diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 7a11d97b..67a459da 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -295,6 +295,13 @@ public: // Server-authoritative armor (UNIT_FIELD_RESISTANCES[0]) int32_t getArmorRating() const { return playerArmorRating_; } + // Server-authoritative elemental resistances (UNIT_FIELD_RESISTANCES[1-6]). + // school: 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane. Returns 0 if not received. + int32_t getResistance(int school) const { + if (school < 1 || school > 6) return 0; + return playerResistances_[school - 1]; + } + // Server-authoritative primary stats (UNIT_FIELD_STAT0-4: STR, AGI, STA, INT, SPI). // Returns -1 if the server hasn't sent the value yet. int32_t getPlayerStat(int idx) const { @@ -2436,6 +2443,7 @@ private: std::unordered_map recentLootMoneyAnnounceCooldowns_; uint64_t playerMoneyCopper_ = 0; int32_t playerArmorRating_ = 0; + int32_t playerResistances_[6] = {}; // [0]=Holy,[1]=Fire,[2]=Nature,[3]=Frost,[4]=Shadow,[5]=Arcane // Server-authoritative primary stats: [0]=STR [1]=AGI [2]=STA [3]=INT [4]=SPI; -1 = not received yet int32_t playerStats_[5] = {-1, -1, -1, -1, -1}; // Some servers/custom clients shift update field indices. We can auto-detect coinage by correlating diff --git a/include/ui/inventory_screen.hpp b/include/ui/inventory_screen.hpp index 31cae856..7a40f43b 100644 --- a/include/ui/inventory_screen.hpp +++ b/include/ui/inventory_screen.hpp @@ -149,7 +149,7 @@ private: void renderEquipmentPanel(game::Inventory& inventory); void renderBackpackPanel(game::Inventory& inventory, bool collapseEmptySections = false); void renderStatsPanel(game::Inventory& inventory, uint32_t playerLevel, int32_t serverArmor = 0, - const int32_t* serverStats = nullptr); + const int32_t* serverStats = nullptr, const int32_t* serverResists = nullptr); void renderReputationPanel(game::GameHandler& gameHandler); void renderItemSlot(game::Inventory& inventory, const game::ItemSlot& slot, diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index df1da0df..7cd7bc2b 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -6703,6 +6703,7 @@ void GameHandler::selectCharacter(uint64_t characterGuid) { onlineEquipDirty_ = false; playerMoneyCopper_ = 0; playerArmorRating_ = 0; + std::fill(std::begin(playerResistances_), std::end(playerResistances_), 0); std::fill(std::begin(playerStats_), std::end(playerStats_), -1); knownSpells.clear(); spellCooldowns.clear(); @@ -8807,6 +8808,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { playerArmorRating_ = static_cast(val); LOG_DEBUG("Armor rating from update fields: ", playerArmorRating_); } + else if (ufArmor != 0xFFFF && key > ufArmor && key <= ufArmor + 6) { + playerResistances_[key - ufArmor - 1] = static_cast(val); + } else if (ufPBytes2 != 0xFFFF && key == ufPBytes2) { uint8_t bankBagSlots = static_cast((val >> 16) & 0xFF); LOG_WARNING("PLAYER_BYTES_2 (CREATE): raw=0x", std::hex, val, std::dec, @@ -9147,6 +9151,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { else if (ufArmor != 0xFFFF && key == ufArmor) { playerArmorRating_ = static_cast(val); } + else if (ufArmor != 0xFFFF && key > ufArmor && key <= ufArmor + 6) { + playerResistances_[key - ufArmor - 1] = static_cast(val); + } else if (ufPBytes2v != 0xFFFF && key == ufPBytes2v) { uint8_t bankBagSlots = static_cast((val >> 16) & 0xFF); LOG_WARNING("PLAYER_BYTES_2 (VALUES): raw=0x", std::hex, val, std::dec, diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index 661d27fe..fc9bd564 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -1159,7 +1159,9 @@ void InventoryScreen::renderCharacterScreen(game::GameHandler& gameHandler) { int32_t stats[5]; for (int i = 0; i < 5; ++i) stats[i] = gameHandler.getPlayerStat(i); const int32_t* serverStats = (stats[0] >= 0) ? stats : nullptr; - renderStatsPanel(inventory, gameHandler.getPlayerLevel(), gameHandler.getArmorRating(), serverStats); + int32_t resists[6]; + for (int i = 0; i < 6; ++i) resists[i] = gameHandler.getResistance(i + 1); + renderStatsPanel(inventory, gameHandler.getPlayerLevel(), gameHandler.getArmorRating(), serverStats, resists); // Played time (shown if available, fetched on character screen open) uint32_t totalSec = gameHandler.getTotalTimePlayed(); @@ -1557,7 +1559,8 @@ void InventoryScreen::renderEquipmentPanel(game::Inventory& inventory) { // ============================================================ void InventoryScreen::renderStatsPanel(game::Inventory& inventory, uint32_t playerLevel, - int32_t serverArmor, const int32_t* serverStats) { + int32_t serverArmor, const int32_t* serverStats, + const int32_t* serverResists) { // Sum equipment stats for item-query bonus display int32_t itemStr = 0, itemAgi = 0, itemSta = 0, itemInt = 0, itemSpi = 0; // Secondary stat sums from extraStats @@ -1665,6 +1668,28 @@ void InventoryScreen::renderStatsPanel(game::Inventory& inventory, uint32_t play renderSecondary("Mana per 5 sec", itemMp5); renderSecondary("Health per 5 sec",itemHp5); } + + // Elemental resistances from server update fields + if (serverResists) { + static const char* kResistNames[6] = { + "Holy Resistance", "Fire Resistance", "Nature Resistance", + "Frost Resistance", "Shadow Resistance", "Arcane Resistance" + }; + bool hasResist = false; + for (int i = 0; i < 6; ++i) { + if (serverResists[i] > 0) { hasResist = true; break; } + } + if (hasResist) { + ImGui::Spacing(); + ImGui::Separator(); + for (int i = 0; i < 6; ++i) { + if (serverResists[i] > 0) { + ImGui::TextColored(ImVec4(0.7f, 0.85f, 1.0f, 1.0f), + "%s: %d", kResistNames[i], serverResists[i]); + } + } + } + } } void InventoryScreen::renderBackpackPanel(game::Inventory& inventory, bool collapseEmptySections) {