mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: capture and display all item stat types in tooltips
Previously only the 5 primary stats (Str/Agi/Sta/Int/Spi) were stored, discarding hit rating, crit, haste, attack power, spell power, resilience, expertise, armor penetration, MP5, and many others. Changes: - Add ItemDef::ExtraStat and ItemQueryResponseData::ExtraStat arrays - All three expansion parsers (WotLK/TBC/Classic) now capture non-primary stat type/value pairs into extraStats instead of silently dropping them - All 5 rebuildOnlineInventory paths propagate extraStats to ItemDef - Tooltip now renders each extra stat on its own line with a name lookup covering all common WotLK stat types (hit, crit, haste, AP, SP, etc.) - Also fix Classic/TBC bag-content and bank-bag paths that were missing bindType, description propagation from previous commits
This commit is contained in:
parent
6075207d94
commit
321aaeae54
7 changed files with 84 additions and 3 deletions
|
|
@ -3,6 +3,7 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
|
@ -52,6 +53,9 @@ struct ItemDef {
|
|||
uint32_t requiredLevel = 0;
|
||||
uint32_t bindType = 0; // 0=none, 1=BoP, 2=BoE, 3=BoU, 4=BoQ
|
||||
std::string description; // Flavor/lore text shown in tooltip (italic yellow)
|
||||
// Generic stat pairs for non-primary stats (hit, crit, haste, AP, SP, etc.)
|
||||
struct ExtraStat { uint32_t statType = 0; int32_t statValue = 0; };
|
||||
std::vector<ExtraStat> extraStats;
|
||||
};
|
||||
|
||||
struct ItemSlot {
|
||||
|
|
|
|||
|
|
@ -1563,6 +1563,9 @@ struct ItemQueryResponseData {
|
|||
std::array<ItemSpell, 5> spells{};
|
||||
uint32_t bindType = 0; // 0=none, 1=BoP, 2=BoE, 3=BoU, 4=BoQ
|
||||
std::string description; // Flavor/lore text
|
||||
// Generic stat pairs for non-primary stats (hit, crit, haste, AP, SP, etc.)
|
||||
struct ExtraStat { uint32_t statType = 0; int32_t statValue = 0; };
|
||||
std::vector<ExtraStat> extraStats;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10763,6 +10763,9 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.requiredLevel = infoIt->second.requiredLevel;
|
||||
def.bindType = infoIt->second.bindType;
|
||||
def.description = infoIt->second.description;
|
||||
def.extraStats.clear();
|
||||
for (const auto& es : infoIt->second.extraStats)
|
||||
def.extraStats.push_back({es.statType, es.statValue});
|
||||
} else {
|
||||
def.name = "Item " + std::to_string(def.itemId);
|
||||
queryItemInfo(def.itemId, guid);
|
||||
|
|
@ -10808,6 +10811,9 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.requiredLevel = infoIt->second.requiredLevel;
|
||||
def.bindType = infoIt->second.bindType;
|
||||
def.description = infoIt->second.description;
|
||||
def.extraStats.clear();
|
||||
for (const auto& es : infoIt->second.extraStats)
|
||||
def.extraStats.push_back({es.statType, es.statValue});
|
||||
} else {
|
||||
def.name = "Item " + std::to_string(def.itemId);
|
||||
queryItemInfo(def.itemId, guid);
|
||||
|
|
@ -10886,6 +10892,11 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.sellPrice = infoIt->second.sellPrice;
|
||||
def.itemLevel = infoIt->second.itemLevel;
|
||||
def.requiredLevel = infoIt->second.requiredLevel;
|
||||
def.bindType = infoIt->second.bindType;
|
||||
def.description = infoIt->second.description;
|
||||
def.extraStats.clear();
|
||||
for (const auto& es : infoIt->second.extraStats)
|
||||
def.extraStats.push_back({es.statType, es.statValue});
|
||||
def.bagSlots = infoIt->second.containerSlots;
|
||||
} else {
|
||||
def.name = "Item " + std::to_string(def.itemId);
|
||||
|
|
@ -10932,6 +10943,9 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.requiredLevel = infoIt->second.requiredLevel;
|
||||
def.bindType = infoIt->second.bindType;
|
||||
def.description = infoIt->second.description;
|
||||
def.extraStats.clear();
|
||||
for (const auto& es : infoIt->second.extraStats)
|
||||
def.extraStats.push_back({es.statType, es.statValue});
|
||||
def.sellPrice = infoIt->second.sellPrice;
|
||||
def.bagSlots = infoIt->second.containerSlots;
|
||||
} else {
|
||||
|
|
@ -11018,6 +11032,11 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.itemLevel = infoIt->second.itemLevel;
|
||||
def.requiredLevel = infoIt->second.requiredLevel;
|
||||
def.sellPrice = infoIt->second.sellPrice;
|
||||
def.bindType = infoIt->second.bindType;
|
||||
def.description = infoIt->second.description;
|
||||
def.extraStats.clear();
|
||||
for (const auto& es : infoIt->second.extraStats)
|
||||
def.extraStats.push_back({es.statType, es.statValue});
|
||||
def.bagSlots = infoIt->second.containerSlots;
|
||||
} else {
|
||||
def.name = "Item " + std::to_string(def.itemId);
|
||||
|
|
|
|||
|
|
@ -1266,7 +1266,10 @@ bool ClassicPacketParsers::parseItemQueryResponse(network::Packet& packet, ItemQ
|
|||
case 5: data.intellect = statValue; break;
|
||||
case 6: data.spirit = statValue; break;
|
||||
case 7: data.stamina = statValue; break;
|
||||
default: break;
|
||||
default:
|
||||
if (statValue != 0)
|
||||
data.extraStats.push_back({statType, statValue});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -931,7 +931,10 @@ bool TbcPacketParsers::parseItemQueryResponse(network::Packet& packet, ItemQuery
|
|||
case 5: data.intellect = statValue; break;
|
||||
case 6: data.spirit = statValue; break;
|
||||
case 7: data.stamina = statValue; break;
|
||||
default: break;
|
||||
default:
|
||||
if (statValue != 0)
|
||||
data.extraStats.push_back({statType, statValue});
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TBC: NO ScalingStatDistribution, NO ScalingStatValue (WotLK-only)
|
||||
|
|
|
|||
|
|
@ -2474,7 +2474,10 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
case 5: data.intellect = statValue; break;
|
||||
case 6: data.spirit = statValue; break;
|
||||
case 7: data.stamina = statValue; break;
|
||||
default: break;
|
||||
default:
|
||||
if (statValue != 0)
|
||||
data.extraStats.push_back({statType, statValue});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1833,6 +1833,52 @@ void InventoryScreen::renderItemTooltip(const game::ItemDef& item, const game::I
|
|||
if (!bonusLine.empty()) {
|
||||
ImGui::TextColored(green, "%s", bonusLine.c_str());
|
||||
}
|
||||
|
||||
// Extra stats (hit, crit, haste, AP, SP, etc.) — one line each
|
||||
for (const auto& es : item.extraStats) {
|
||||
const char* statName = nullptr;
|
||||
switch (es.statType) {
|
||||
case 0: statName = "Mana"; break;
|
||||
case 1: statName = "Health"; break;
|
||||
case 12: statName = "Defense Rating"; break;
|
||||
case 13: statName = "Dodge Rating"; break;
|
||||
case 14: statName = "Parry Rating"; break;
|
||||
case 15: statName = "Block Rating"; break;
|
||||
case 16: statName = "Hit Rating"; break;
|
||||
case 17: statName = "Hit Rating"; break;
|
||||
case 18: statName = "Hit Rating"; break;
|
||||
case 19: statName = "Crit Rating"; break;
|
||||
case 20: statName = "Crit Rating"; break;
|
||||
case 21: statName = "Crit Rating"; break;
|
||||
case 28: statName = "Haste Rating"; break;
|
||||
case 29: statName = "Haste Rating"; break;
|
||||
case 30: statName = "Haste Rating"; break;
|
||||
case 31: statName = "Hit Rating"; break;
|
||||
case 32: statName = "Crit Rating"; break;
|
||||
case 35: statName = "Resilience"; break;
|
||||
case 36: statName = "Haste Rating"; break;
|
||||
case 37: statName = "Expertise Rating"; break;
|
||||
case 38: statName = "Attack Power"; break;
|
||||
case 39: statName = "Ranged Attack Power"; break;
|
||||
case 41: statName = "Healing Power"; break;
|
||||
case 42: statName = "Spell Damage"; break;
|
||||
case 43: statName = "Mana per 5 sec"; break;
|
||||
case 44: statName = "Armor Penetration"; break;
|
||||
case 45: statName = "Spell Power"; break;
|
||||
case 46: statName = "Health per 5 sec"; break;
|
||||
case 47: statName = "Spell Penetration"; break;
|
||||
case 48: statName = "Block Value"; break;
|
||||
default: statName = nullptr; break;
|
||||
}
|
||||
char buf[64];
|
||||
if (statName) {
|
||||
std::snprintf(buf, sizeof(buf), "%+d %s", es.statValue, statName);
|
||||
} else {
|
||||
std::snprintf(buf, sizeof(buf), "%+d (stat %u)", es.statValue, es.statType);
|
||||
}
|
||||
ImGui::TextColored(green, "%s", buf);
|
||||
}
|
||||
|
||||
if (item.requiredLevel > 1) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.5f, 1.0f), "Requires Level %u", item.requiredLevel);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue