mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory extension with 28 bank slots + 7 bank bags, and UI windows for personal bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid). Fix Classic gossip parser to omit boxMoney/boxText fields not present in Vanilla protocol, fix gossip icon labels with text-based NPC type detection, and add Turtle WoW opcode mappings for bank and auction interactions.
This commit is contained in:
parent
0d4a9c38f7
commit
381d896348
14 changed files with 1839 additions and 15 deletions
|
|
@ -3480,5 +3480,316 @@ bool PacketParsers::parseMailList(network::Packet& packet, std::vector<MailMessa
|
|||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Bank System
|
||||
// ============================================================
|
||||
|
||||
network::Packet BankerActivatePacket::build(uint64_t guid) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_BANKER_ACTIVATE));
|
||||
p.writeUInt64(guid);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet BuyBankSlotPacket::build(uint64_t guid) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_BUY_BANK_SLOT));
|
||||
p.writeUInt64(guid);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet AutoBankItemPacket::build(uint8_t srcBag, uint8_t srcSlot) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUTOBANK_ITEM));
|
||||
p.writeUInt8(srcBag);
|
||||
p.writeUInt8(srcSlot);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet AutoStoreBankItemPacket::build(uint8_t srcBag, uint8_t srcSlot) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUTOSTORE_BANK_ITEM));
|
||||
p.writeUInt8(srcBag);
|
||||
p.writeUInt8(srcSlot);
|
||||
return p;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Guild Bank System
|
||||
// ============================================================
|
||||
|
||||
network::Packet GuildBankerActivatePacket::build(uint64_t guid) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_GUILD_BANKER_ACTIVATE));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt8(0); // full slots update
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet GuildBankQueryTabPacket::build(uint64_t guid, uint8_t tabId, bool fullUpdate) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_GUILD_BANK_QUERY_TAB));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt8(tabId);
|
||||
p.writeUInt8(fullUpdate ? 1 : 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet GuildBankBuyTabPacket::build(uint64_t guid, uint8_t tabId) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_GUILD_BANK_BUY_TAB));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt8(tabId);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet GuildBankDepositMoneyPacket::build(uint64_t guid, uint32_t amount) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_GUILD_BANK_DEPOSIT_MONEY));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt32(amount);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet GuildBankWithdrawMoneyPacket::build(uint64_t guid, uint32_t amount) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_GUILD_BANK_WITHDRAW_MONEY));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt32(amount);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet GuildBankSwapItemsPacket::buildBankToInventory(
|
||||
uint64_t guid, uint8_t tabId, uint8_t bankSlot,
|
||||
uint8_t destBag, uint8_t destSlot, uint32_t splitCount)
|
||||
{
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_GUILD_BANK_SWAP_ITEMS));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt8(0); // bankToCharacter = false -> bank source
|
||||
p.writeUInt8(tabId);
|
||||
p.writeUInt8(bankSlot);
|
||||
p.writeUInt32(0); // itemEntry (unused client side)
|
||||
p.writeUInt8(0); // autoStore = false
|
||||
if (splitCount > 0) {
|
||||
p.writeUInt8(splitCount);
|
||||
}
|
||||
p.writeUInt8(destBag);
|
||||
p.writeUInt8(destSlot);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet GuildBankSwapItemsPacket::buildInventoryToBank(
|
||||
uint64_t guid, uint8_t tabId, uint8_t bankSlot,
|
||||
uint8_t srcBag, uint8_t srcSlot, uint32_t splitCount)
|
||||
{
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_GUILD_BANK_SWAP_ITEMS));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt8(1); // bankToCharacter = true -> char to bank
|
||||
p.writeUInt8(tabId);
|
||||
p.writeUInt8(bankSlot);
|
||||
p.writeUInt32(0); // itemEntry
|
||||
p.writeUInt8(0); // autoStore
|
||||
if (splitCount > 0) {
|
||||
p.writeUInt8(splitCount);
|
||||
}
|
||||
p.writeUInt8(srcBag);
|
||||
p.writeUInt8(srcSlot);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool GuildBankListParser::parse(network::Packet& packet, GuildBankData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 14) return false;
|
||||
|
||||
data.money = packet.readUInt64();
|
||||
data.tabId = packet.readUInt8();
|
||||
data.withdrawAmount = static_cast<int32_t>(packet.readUInt32());
|
||||
uint8_t fullUpdate = packet.readUInt8();
|
||||
|
||||
if (fullUpdate) {
|
||||
uint8_t tabCount = packet.readUInt8();
|
||||
data.tabs.resize(tabCount);
|
||||
for (uint8_t i = 0; i < tabCount; ++i) {
|
||||
data.tabs[i].tabName = packet.readString();
|
||||
data.tabs[i].tabIcon = packet.readString();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t numSlots = packet.readUInt8();
|
||||
data.tabItems.clear();
|
||||
for (uint8_t i = 0; i < numSlots; ++i) {
|
||||
GuildBankItemSlot slot;
|
||||
slot.slotId = packet.readUInt8();
|
||||
slot.itemEntry = packet.readUInt32();
|
||||
if (slot.itemEntry != 0) {
|
||||
// Enchant info
|
||||
uint32_t enchantMask = packet.readUInt32();
|
||||
for (int bit = 0; bit < 10; ++bit) {
|
||||
if (enchantMask & (1u << bit)) {
|
||||
uint32_t enchId = packet.readUInt32();
|
||||
uint32_t enchDur = packet.readUInt32();
|
||||
uint32_t enchCharges = packet.readUInt32();
|
||||
if (bit == 0) slot.enchantId = enchId;
|
||||
(void)enchDur; (void)enchCharges;
|
||||
}
|
||||
}
|
||||
slot.stackCount = packet.readUInt32();
|
||||
/*spare=*/ packet.readUInt32();
|
||||
slot.randomPropertyId = packet.readUInt32();
|
||||
if (slot.randomPropertyId) {
|
||||
/*suffixFactor=*/ packet.readUInt32();
|
||||
}
|
||||
}
|
||||
data.tabItems.push_back(slot);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Auction House System
|
||||
// ============================================================
|
||||
|
||||
network::Packet AuctionHelloPacket::build(uint64_t guid) {
|
||||
network::Packet p(wireOpcode(Opcode::MSG_AUCTION_HELLO));
|
||||
p.writeUInt64(guid);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool AuctionHelloParser::parse(network::Packet& packet, AuctionHelloData& data) {
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
if (remaining < 12) {
|
||||
LOG_WARNING("AuctionHelloParser: too small, remaining=", remaining);
|
||||
return false;
|
||||
}
|
||||
data.auctioneerGuid = packet.readUInt64();
|
||||
data.auctionHouseId = packet.readUInt32();
|
||||
// WotLK has an extra uint8 enabled field; Vanilla does not
|
||||
if (packet.getReadPos() < packet.getSize()) {
|
||||
data.enabled = packet.readUInt8();
|
||||
} else {
|
||||
data.enabled = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
network::Packet AuctionListItemsPacket::build(
|
||||
uint64_t guid, uint32_t offset,
|
||||
const std::string& searchName,
|
||||
uint8_t levelMin, uint8_t levelMax,
|
||||
uint32_t invTypeMask, uint32_t itemClass,
|
||||
uint32_t itemSubClass, uint32_t quality,
|
||||
uint8_t usableOnly, uint8_t exactMatch)
|
||||
{
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUCTION_LIST_ITEMS));
|
||||
p.writeUInt64(guid);
|
||||
p.writeUInt32(offset);
|
||||
p.writeString(searchName);
|
||||
p.writeUInt8(levelMin);
|
||||
p.writeUInt8(levelMax);
|
||||
p.writeUInt32(invTypeMask);
|
||||
p.writeUInt32(itemClass);
|
||||
p.writeUInt32(itemSubClass);
|
||||
p.writeUInt32(quality);
|
||||
p.writeUInt8(usableOnly);
|
||||
p.writeUInt8(0); // getAll (0 = normal search)
|
||||
// Sort columns (0 = none)
|
||||
p.writeUInt8(0);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet AuctionSellItemPacket::build(
|
||||
uint64_t auctioneerGuid, uint64_t itemGuid,
|
||||
uint32_t stackCount, uint32_t bid,
|
||||
uint32_t buyout, uint32_t duration)
|
||||
{
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUCTION_SELL_ITEM));
|
||||
p.writeUInt64(auctioneerGuid);
|
||||
p.writeUInt32(1); // item count (WotLK supports multiple, we send 1)
|
||||
p.writeUInt64(itemGuid);
|
||||
p.writeUInt32(stackCount);
|
||||
p.writeUInt32(bid);
|
||||
p.writeUInt32(buyout);
|
||||
p.writeUInt32(duration);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet AuctionPlaceBidPacket::build(uint64_t auctioneerGuid, uint32_t auctionId, uint32_t amount) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUCTION_PLACE_BID));
|
||||
p.writeUInt64(auctioneerGuid);
|
||||
p.writeUInt32(auctionId);
|
||||
p.writeUInt32(amount);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet AuctionRemoveItemPacket::build(uint64_t auctioneerGuid, uint32_t auctionId) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUCTION_REMOVE_ITEM));
|
||||
p.writeUInt64(auctioneerGuid);
|
||||
p.writeUInt32(auctionId);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet AuctionListOwnerItemsPacket::build(uint64_t auctioneerGuid, uint32_t offset) {
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUCTION_LIST_OWNER_ITEMS));
|
||||
p.writeUInt64(auctioneerGuid);
|
||||
p.writeUInt32(offset);
|
||||
return p;
|
||||
}
|
||||
|
||||
network::Packet AuctionListBidderItemsPacket::build(
|
||||
uint64_t auctioneerGuid, uint32_t offset,
|
||||
const std::vector<uint32_t>& outbiddedIds)
|
||||
{
|
||||
network::Packet p(wireOpcode(Opcode::CMSG_AUCTION_LIST_BIDDER_ITEMS));
|
||||
p.writeUInt64(auctioneerGuid);
|
||||
p.writeUInt32(offset);
|
||||
p.writeUInt32(static_cast<uint32_t>(outbiddedIds.size()));
|
||||
for (uint32_t id : outbiddedIds)
|
||||
p.writeUInt32(id);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool AuctionListResultParser::parse(network::Packet& packet, AuctionListResult& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 4) return false;
|
||||
|
||||
uint32_t count = packet.readUInt32();
|
||||
data.auctions.clear();
|
||||
data.auctions.reserve(count);
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (packet.getReadPos() + 64 > packet.getSize()) break;
|
||||
AuctionEntry e;
|
||||
e.auctionId = packet.readUInt32();
|
||||
e.itemEntry = packet.readUInt32();
|
||||
// 3 enchant slots: enchantId, duration, charges
|
||||
e.enchantId = packet.readUInt32();
|
||||
packet.readUInt32(); // enchant duration
|
||||
packet.readUInt32(); // enchant charges
|
||||
packet.readUInt32(); // enchant2 id
|
||||
packet.readUInt32(); // enchant2 duration
|
||||
packet.readUInt32(); // enchant2 charges
|
||||
packet.readUInt32(); // enchant3 id
|
||||
packet.readUInt32(); // enchant3 duration
|
||||
packet.readUInt32(); // enchant3 charges
|
||||
e.randomPropertyId = packet.readUInt32();
|
||||
e.suffixFactor = packet.readUInt32();
|
||||
e.stackCount = packet.readUInt32();
|
||||
packet.readUInt32(); // item charges
|
||||
packet.readUInt32(); // item flags (unused)
|
||||
e.ownerGuid = packet.readUInt64();
|
||||
e.startBid = packet.readUInt32();
|
||||
e.minBidIncrement = packet.readUInt32();
|
||||
e.buyoutPrice = packet.readUInt32();
|
||||
e.timeLeftMs = packet.readUInt32();
|
||||
e.bidderGuid = packet.readUInt64();
|
||||
e.currentBid = packet.readUInt32();
|
||||
data.auctions.push_back(e);
|
||||
}
|
||||
|
||||
data.totalCount = packet.readUInt32();
|
||||
data.searchDelay = packet.readUInt32();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuctionCommandResultParser::parse(network::Packet& packet, AuctionCommandResult& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 12) return false;
|
||||
data.auctionId = packet.readUInt32();
|
||||
data.action = packet.readUInt32();
|
||||
data.errorCode = packet.readUInt32();
|
||||
if (data.errorCode != 0 && data.action == 2 && packet.getReadPos() + 4 <= packet.getSize()) {
|
||||
data.bidError = packet.readUInt32();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue