feat: parse and display item level and required level in tooltips

- Add itemLevel/requiredLevel fields to ItemQueryResponseData (parsed
  from SMSG_ITEM_QUERY_SINGLE_RESPONSE) and ItemDef
- Propagate through all 5 rebuildOnlineInventory() paths
- Show "Item Level N" and "Requires Level N" in item tooltip in
  standard WoW order (below item name, above required level/stats)
This commit is contained in:
Kelsi 2026-03-10 16:26:20 -07:00
parent 67db7383ad
commit 1793549550
5 changed files with 22 additions and 2 deletions

View file

@ -48,6 +48,8 @@ struct ItemDef {
uint32_t sellPrice = 0; uint32_t sellPrice = 0;
uint32_t curDurability = 0; uint32_t curDurability = 0;
uint32_t maxDurability = 0; uint32_t maxDurability = 0;
uint32_t itemLevel = 0;
uint32_t requiredLevel = 0;
}; };
struct ItemSlot { struct ItemSlot {

View file

@ -1552,6 +1552,8 @@ struct ItemQueryResponseData {
int32_t intellect = 0; int32_t intellect = 0;
int32_t spirit = 0; int32_t spirit = 0;
uint32_t sellPrice = 0; uint32_t sellPrice = 0;
uint32_t itemLevel = 0;
uint32_t requiredLevel = 0;
std::string subclassName; std::string subclassName;
// Item spells (up to 5) // Item spells (up to 5)
struct ItemSpell { struct ItemSpell {

View file

@ -10758,6 +10758,8 @@ void GameHandler::rebuildOnlineInventory() {
def.agility = infoIt->second.agility; def.agility = infoIt->second.agility;
def.intellect = infoIt->second.intellect; def.intellect = infoIt->second.intellect;
def.spirit = infoIt->second.spirit; def.spirit = infoIt->second.spirit;
def.itemLevel = infoIt->second.itemLevel;
def.requiredLevel = infoIt->second.requiredLevel;
} else { } else {
def.name = "Item " + std::to_string(def.itemId); def.name = "Item " + std::to_string(def.itemId);
queryItemInfo(def.itemId, guid); queryItemInfo(def.itemId, guid);
@ -10798,6 +10800,8 @@ void GameHandler::rebuildOnlineInventory() {
def.agility = infoIt->second.agility; def.agility = infoIt->second.agility;
def.intellect = infoIt->second.intellect; def.intellect = infoIt->second.intellect;
def.spirit = infoIt->second.spirit; def.spirit = infoIt->second.spirit;
def.itemLevel = infoIt->second.itemLevel;
def.requiredLevel = infoIt->second.requiredLevel;
} else { } else {
def.name = "Item " + std::to_string(def.itemId); def.name = "Item " + std::to_string(def.itemId);
queryItemInfo(def.itemId, guid); queryItemInfo(def.itemId, guid);
@ -10873,6 +10877,8 @@ void GameHandler::rebuildOnlineInventory() {
def.agility = infoIt->second.agility; def.agility = infoIt->second.agility;
def.intellect = infoIt->second.intellect; def.intellect = infoIt->second.intellect;
def.spirit = infoIt->second.spirit; def.spirit = infoIt->second.spirit;
def.itemLevel = infoIt->second.itemLevel;
def.requiredLevel = infoIt->second.requiredLevel;
def.bagSlots = infoIt->second.containerSlots; def.bagSlots = infoIt->second.containerSlots;
} else { } else {
def.name = "Item " + std::to_string(def.itemId); def.name = "Item " + std::to_string(def.itemId);
@ -10915,6 +10921,8 @@ void GameHandler::rebuildOnlineInventory() {
def.agility = infoIt->second.agility; def.agility = infoIt->second.agility;
def.intellect = infoIt->second.intellect; def.intellect = infoIt->second.intellect;
def.spirit = infoIt->second.spirit; def.spirit = infoIt->second.spirit;
def.itemLevel = infoIt->second.itemLevel;
def.requiredLevel = infoIt->second.requiredLevel;
def.sellPrice = infoIt->second.sellPrice; def.sellPrice = infoIt->second.sellPrice;
def.bagSlots = infoIt->second.containerSlots; def.bagSlots = infoIt->second.containerSlots;
} else { } else {
@ -10998,6 +11006,8 @@ void GameHandler::rebuildOnlineInventory() {
def.agility = infoIt->second.agility; def.agility = infoIt->second.agility;
def.intellect = infoIt->second.intellect; def.intellect = infoIt->second.intellect;
def.spirit = infoIt->second.spirit; def.spirit = infoIt->second.spirit;
def.itemLevel = infoIt->second.itemLevel;
def.requiredLevel = infoIt->second.requiredLevel;
def.sellPrice = infoIt->second.sellPrice; def.sellPrice = infoIt->second.sellPrice;
def.bagSlots = infoIt->second.containerSlots; def.bagSlots = infoIt->second.containerSlots;
} else { } else {

View file

@ -2449,8 +2449,8 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
packet.readUInt32(); // AllowableClass packet.readUInt32(); // AllowableClass
packet.readUInt32(); // AllowableRace packet.readUInt32(); // AllowableRace
packet.readUInt32(); // ItemLevel data.itemLevel = packet.readUInt32();
packet.readUInt32(); // RequiredLevel data.requiredLevel = packet.readUInt32();
packet.readUInt32(); // RequiredSkill packet.readUInt32(); // RequiredSkill
packet.readUInt32(); // RequiredSkillRank packet.readUInt32(); // RequiredSkillRank
packet.readUInt32(); // RequiredSpell packet.readUInt32(); // RequiredSpell

View file

@ -1714,6 +1714,9 @@ void InventoryScreen::renderItemTooltip(const game::ItemDef& item, const game::I
ImVec4 qColor = getQualityColor(item.quality); ImVec4 qColor = getQualityColor(item.quality);
ImGui::TextColored(qColor, "%s", item.name.c_str()); ImGui::TextColored(qColor, "%s", item.name.c_str());
if (item.itemLevel > 0) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.7f), "Item Level %u", item.itemLevel);
}
if (item.itemId == 6948 && gameHandler_) { if (item.itemId == 6948 && gameHandler_) {
uint32_t mapId = 0; uint32_t mapId = 0;
@ -1819,6 +1822,9 @@ void InventoryScreen::renderItemTooltip(const game::ItemDef& item, const game::I
if (!bonusLine.empty()) { if (!bonusLine.empty()) {
ImGui::TextColored(green, "%s", bonusLine.c_str()); ImGui::TextColored(green, "%s", bonusLine.c_str());
} }
if (item.requiredLevel > 1) {
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.5f, 1.0f), "Requires Level %u", item.requiredLevel);
}
if (item.maxDurability > 0) { if (item.maxDurability > 0) {
float durPct = static_cast<float>(item.curDurability) / static_cast<float>(item.maxDurability); float durPct = static_cast<float>(item.curDurability) / static_cast<float>(item.maxDurability);
ImVec4 durColor; ImVec4 durColor;