From 804b947203d85302262adc5d7d2daee2c0dadbf7 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 26 Feb 2026 11:12:34 -0800 Subject: [PATCH] Fix bank for all expansions: add missing field indices and per-expansion slot counts --- Data/expansions/classic/update_fields.json | 2 ++ Data/expansions/tbc/update_fields.json | 2 ++ Data/expansions/turtle/update_fields.json | 2 ++ Data/expansions/wotlk/update_fields.json | 2 ++ include/game/game_handler.hpp | 4 +++ src/game/game_handler.cpp | 42 ++++++++++++++++------ src/ui/game_screen.cpp | 10 +++--- 7 files changed, 49 insertions(+), 15 deletions(-) diff --git a/Data/expansions/classic/update_fields.json b/Data/expansions/classic/update_fields.json index f0a21619..5f97f29f 100644 --- a/Data/expansions/classic/update_fields.json +++ b/Data/expansions/classic/update_fields.json @@ -26,6 +26,8 @@ "PLAYER_QUEST_LOG_START": 198, "PLAYER_FIELD_INV_SLOT_HEAD": 486, "PLAYER_FIELD_PACK_SLOT_1": 532, + "PLAYER_FIELD_BANK_SLOT_1": 564, + "PLAYER_FIELD_BANKBAG_SLOT_1": 612, "PLAYER_SKILL_INFO_START": 718, "PLAYER_EXPLORED_ZONES_START": 1111, "PLAYER_END": 1282, diff --git a/Data/expansions/tbc/update_fields.json b/Data/expansions/tbc/update_fields.json index 2a1aea0e..bbcedec5 100644 --- a/Data/expansions/tbc/update_fields.json +++ b/Data/expansions/tbc/update_fields.json @@ -26,6 +26,8 @@ "PLAYER_QUEST_LOG_START": 244, "PLAYER_FIELD_INV_SLOT_HEAD": 650, "PLAYER_FIELD_PACK_SLOT_1": 696, + "PLAYER_FIELD_BANK_SLOT_1": 728, + "PLAYER_FIELD_BANKBAG_SLOT_1": 784, "PLAYER_SKILL_INFO_START": 928, "PLAYER_EXPLORED_ZONES_START": 1312, "GAMEOBJECT_DISPLAYID": 8, diff --git a/Data/expansions/turtle/update_fields.json b/Data/expansions/turtle/update_fields.json index f0a21619..5f97f29f 100644 --- a/Data/expansions/turtle/update_fields.json +++ b/Data/expansions/turtle/update_fields.json @@ -26,6 +26,8 @@ "PLAYER_QUEST_LOG_START": 198, "PLAYER_FIELD_INV_SLOT_HEAD": 486, "PLAYER_FIELD_PACK_SLOT_1": 532, + "PLAYER_FIELD_BANK_SLOT_1": 564, + "PLAYER_FIELD_BANKBAG_SLOT_1": 612, "PLAYER_SKILL_INFO_START": 718, "PLAYER_EXPLORED_ZONES_START": 1111, "PLAYER_END": 1282, diff --git a/Data/expansions/wotlk/update_fields.json b/Data/expansions/wotlk/update_fields.json index 12f92ceb..1ea25b2b 100644 --- a/Data/expansions/wotlk/update_fields.json +++ b/Data/expansions/wotlk/update_fields.json @@ -26,6 +26,8 @@ "PLAYER_QUEST_LOG_START": 158, "PLAYER_FIELD_INV_SLOT_HEAD": 324, "PLAYER_FIELD_PACK_SLOT_1": 370, + "PLAYER_FIELD_BANK_SLOT_1": 402, + "PLAYER_FIELD_BANKBAG_SLOT_1": 458, "PLAYER_SKILL_INFO_START": 636, "PLAYER_EXPLORED_ZONES_START": 1041, "GAMEOBJECT_DISPLAYID": 8, diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 265b2fe9..e21c8085 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -907,6 +907,8 @@ public: void withdrawItem(uint8_t srcBag, uint8_t srcSlot); bool isBankOpen() const { return bankOpen_; } uint64_t getBankerGuid() const { return bankerGuid_; } + int getEffectiveBankSlots() const { return effectiveBankSlots_; } + int getEffectiveBankBagSlots() const { return effectiveBankBagSlots_; } // Guild Bank void openGuildBank(uint64_t guid); @@ -1619,6 +1621,8 @@ private: uint64_t bankerGuid_ = 0; std::array bankSlotGuids_{}; std::array bankBagSlotGuids_{}; + int effectiveBankSlots_ = 28; // 24 for Classic, 28 for TBC/WotLK + int effectiveBankBagSlots_ = 7; // 6 for Classic, 7 for TBC/WotLK // Guild Bank bool guildBankOpen_ = false; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 2450ec8d..555e31e2 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -5213,6 +5213,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { const uint16_t ufPlayerLevel = fieldIndex(UF::UNIT_FIELD_LEVEL); const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE); const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES); + const uint16_t ufPBytes2 = fieldIndex(UF::PLAYER_BYTES_2); for (const auto& [key, val] : block.fields) { if (key == ufPlayerXp) { playerXp_ = val; } else if (key == ufPlayerNextXp) { playerNextLevelXp_ = val; } @@ -5230,6 +5231,10 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { playerArmorRating_ = static_cast(val); LOG_DEBUG("Armor rating from update fields: ", playerArmorRating_); } + else if (ufPBytes2 != 0xFFFF && key == ufPBytes2) { + uint8_t bankBagSlots = static_cast((val >> 16) & 0xFF); + inventory.setPurchasedBankBagSlots(bankBagSlots); + } // Do not synthesize quest-log entries from raw update-field slots. // Slot layouts differ on some classic-family realms and can produce // phantom "already accepted" quests that block quest acceptance. @@ -5501,6 +5506,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE); const uint16_t ufPlayerFlags = fieldIndex(UF::PLAYER_FLAGS); const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES); + const uint16_t ufPBytes2v = fieldIndex(UF::PLAYER_BYTES_2); for (const auto& [key, val] : block.fields) { if (key == ufPlayerXp) { playerXp_ = val; @@ -5527,6 +5533,10 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { else if (ufArmor != 0xFFFF && key == ufArmor) { playerArmorRating_ = static_cast(val); } + else if (ufPBytes2v != 0xFFFF && key == ufPBytes2v) { + uint8_t bankBagSlots = static_cast((val >> 16) & 0xFF); + inventory.setPurchasedBankBagSlots(bankBagSlots); + } else if (key == ufPlayerFlags) { constexpr uint32_t PLAYER_FLAGS_GHOST = 0x00000010; bool wasGhost = releasedSpirit_; @@ -7632,10 +7642,16 @@ bool GameHandler::applyInventoryFields(const std::map& field } } - // Bank slots: 28 slots × 2 fields = 56 fields starting at PLAYER_FIELD_BANK_SLOT_1 + // Bank slots starting at PLAYER_FIELD_BANK_SLOT_1 int bankBase = static_cast(fieldIndex(UF::PLAYER_FIELD_BANK_SLOT_1)); + int bankBagBase = static_cast(fieldIndex(UF::PLAYER_FIELD_BANKBAG_SLOT_1)); + // Derive slot counts from field gap (Classic=24/6, TBC/WotLK=28/7) + if (bankBase != 0xFFFF && bankBagBase != 0xFFFF) { + effectiveBankSlots_ = std::min((bankBagBase - bankBase) / 2, 28); + effectiveBankBagSlots_ = (effectiveBankSlots_ <= 24) ? 6 : 7; + } if (bankBase != 0xFFFF && key >= static_cast(bankBase) && - key <= static_cast(bankBase) + (game::Inventory::BANK_SLOTS * 2 - 1)) { + key <= static_cast(bankBase) + (effectiveBankSlots_ * 2 - 1)) { int slotIndex = (key - bankBase) / 2; bool isLow = ((key - bankBase) % 2 == 0); if (slotIndex < static_cast(bankSlotGuids_.size())) { @@ -7646,10 +7662,9 @@ bool GameHandler::applyInventoryFields(const std::map& field } } - // Bank bag slots: 7 slots × 2 fields = 14 fields starting at PLAYER_FIELD_BANKBAG_SLOT_1 - int bankBagBase = static_cast(fieldIndex(UF::PLAYER_FIELD_BANKBAG_SLOT_1)); + // Bank bag slots starting at PLAYER_FIELD_BANKBAG_SLOT_1 if (bankBagBase != 0xFFFF && key >= static_cast(bankBagBase) && - key <= static_cast(bankBagBase) + (game::Inventory::BANK_BAG_SLOTS * 2 - 1)) { + key <= static_cast(bankBagBase) + (effectiveBankBagSlots_ * 2 - 1)) { int slotIndex = (key - bankBagBase) / 2; bool isLow = ((key - bankBagBase) % 2 == 0); if (slotIndex < static_cast(bankBagSlotGuids_.size())) { @@ -7845,8 +7860,8 @@ void GameHandler::rebuildOnlineInventory() { } } - // Bank slots (28 main slots) - for (int i = 0; i < 28; i++) { + // Bank slots (24 for Classic, 28 for TBC/WotLK) + for (int i = 0; i < effectiveBankSlots_; i++) { uint64_t guid = bankSlotGuids_[i]; if (guid == 0) { inventory.clearBankSlot(i); continue; } @@ -7885,8 +7900,8 @@ void GameHandler::rebuildOnlineInventory() { inventory.setBankSlot(i, def); } - // Bank bag contents (7 bank bag slots) - for (int bagIdx = 0; bagIdx < 7; bagIdx++) { + // Bank bag contents (6 for Classic, 7 for TBC/WotLK) + for (int bagIdx = 0; bagIdx < effectiveBankBagSlots_; bagIdx++) { uint64_t bagGuid = bankBagSlotGuids_[bagIdx]; if (bagGuid == 0) { inventory.setBankBagSize(bagIdx, 0); continue; } @@ -9275,9 +9290,14 @@ void GameHandler::cancelAura(uint32_t spellId) { } void GameHandler::handlePetSpells(network::Packet& packet) { - if (packet.getSize() - packet.getReadPos() < 8) return; + if (packet.getSize() - packet.getReadPos() < 8) { + // Empty packet = pet dismissed/died + petGuid_ = 0; + LOG_INFO("SMSG_PET_SPELLS: pet cleared (empty packet)"); + return; + } petGuid_ = packet.readUInt64(); - LOG_DEBUG("SMSG_PET_SPELLS: petGuid=0x", std::hex, petGuid_, std::dec); + LOG_INFO("SMSG_PET_SPELLS: petGuid=0x", std::hex, petGuid_, std::dec); } void GameHandler::dismissPet() { diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index d0b31c50..3298680c 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -7765,11 +7765,13 @@ void GameScreen::renderBankWindow(game::GameHandler& gameHandler) { auto& inv = gameHandler.getInventory(); - // Main bank slots (28 = 7 columns × 4 rows) + // Main bank slots (24 for Classic, 28 for TBC/WotLK) + int bankSlotCount = gameHandler.getEffectiveBankSlots(); + int bankBagCount = gameHandler.getEffectiveBankBagSlots(); ImGui::Text("Bank Slots"); ImGui::Separator(); bool isHolding = inventoryScreen.isHoldingItem(); - for (int i = 0; i < game::Inventory::BANK_SLOTS; i++) { + for (int i = 0; i < bankSlotCount; i++) { if (i % 7 != 0) ImGui::SameLine(); const auto& slot = inv.getBankSlot(i); @@ -7817,7 +7819,7 @@ void GameScreen::renderBankWindow(game::GameHandler& gameHandler) { ImGui::Separator(); ImGui::Text("Bank Bags"); uint8_t purchased = inv.getPurchasedBankBagSlots(); - for (int i = 0; i < game::Inventory::BANK_BAG_SLOTS; i++) { + for (int i = 0; i < bankBagCount; i++) { if (i > 0) ImGui::SameLine(); ImGui::PushID(i + 2000); @@ -7835,7 +7837,7 @@ void GameScreen::renderBankWindow(game::GameHandler& gameHandler) { } // Show expanded bank bag contents - for (int bagIdx = 0; bagIdx < game::Inventory::BANK_BAG_SLOTS; bagIdx++) { + for (int bagIdx = 0; bagIdx < bankBagCount; bagIdx++) { int bagSize = inv.getBankBagSize(bagIdx); if (bagSize <= 0) continue;