mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: enhance item tooltips with binding, description, speed, and spell effects
- Parse Bonding and Description fields from SMSG_ITEM_QUERY_SINGLE_RESPONSE
(read after the 5 spell slots: bindType uint32, then description cstring)
- Add bindType and description to ItemQueryResponseData and ItemDef
- Propagate bindType and description through all 5 rebuildOnlineInventory paths
- Tooltip now shows: "Binds when picked up/equipped/used/quest item"
- Tooltip now shows weapon damage range ("X - Y Damage") and speed ("Speed 2.60")
on same line, plus DPS in parentheses below
- Tooltip now shows spell effects ("Use: <SpellName>", "Equip: <SpellName>",
"Chance on Hit: ...") using existing getSpellName() lookup
- Tooltip now shows item flavor/lore description in italic-style yellow text
This commit is contained in:
parent
f53f16a59b
commit
76bd6b409e
5 changed files with 64 additions and 3 deletions
|
|
@ -50,6 +50,8 @@ struct ItemDef {
|
|||
uint32_t maxDurability = 0;
|
||||
uint32_t itemLevel = 0;
|
||||
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)
|
||||
};
|
||||
|
||||
struct ItemSlot {
|
||||
|
|
|
|||
|
|
@ -1561,6 +1561,8 @@ struct ItemQueryResponseData {
|
|||
uint32_t spellTrigger = 0; // 0=Use, 1=Equip, 2=ChanceOnHit, 5=Learn
|
||||
};
|
||||
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
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10761,6 +10761,8 @@ 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;
|
||||
} else {
|
||||
def.name = "Item " + std::to_string(def.itemId);
|
||||
queryItemInfo(def.itemId, guid);
|
||||
|
|
@ -10804,6 +10806,8 @@ 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;
|
||||
} else {
|
||||
def.name = "Item " + std::to_string(def.itemId);
|
||||
queryItemInfo(def.itemId, guid);
|
||||
|
|
@ -10926,6 +10930,8 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.spirit = infoIt->second.spirit;
|
||||
def.itemLevel = infoIt->second.itemLevel;
|
||||
def.requiredLevel = infoIt->second.requiredLevel;
|
||||
def.bindType = infoIt->second.bindType;
|
||||
def.description = infoIt->second.description;
|
||||
def.sellPrice = infoIt->second.sellPrice;
|
||||
def.bagSlots = infoIt->second.containerSlots;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2518,6 +2518,14 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
packet.readUInt32(); // SpellCategoryCooldown
|
||||
}
|
||||
|
||||
// Bonding type (0=none, 1=BoP, 2=BoE, 3=BoU, 4=BoQ)
|
||||
if (packet.getReadPos() + 4 <= packet.getSize())
|
||||
data.bindType = packet.readUInt32();
|
||||
|
||||
// Flavor/lore text (Description cstring)
|
||||
if (packet.getReadPos() < packet.getSize())
|
||||
data.description = packet.readString();
|
||||
|
||||
data.valid = !data.name.empty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1718,6 +1718,15 @@ void InventoryScreen::renderItemTooltip(const game::ItemDef& item, const game::I
|
|||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.7f), "Item Level %u", item.itemLevel);
|
||||
}
|
||||
|
||||
// Binding type
|
||||
switch (item.bindType) {
|
||||
case 1: ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Binds when picked up"); break;
|
||||
case 2: ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Binds when equipped"); break;
|
||||
case 3: ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Binds when used"); break;
|
||||
case 4: ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Quest Item"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (item.itemId == 6948 && gameHandler_) {
|
||||
uint32_t mapId = 0;
|
||||
glm::vec3 pos;
|
||||
|
|
@ -1793,13 +1802,15 @@ void InventoryScreen::renderItemTooltip(const game::ItemDef& item, const game::I
|
|||
};
|
||||
const bool isWeapon = isWeaponInventoryType(item.inventoryType);
|
||||
|
||||
// Compact stats view for weapons: DPS + condensed stat bonuses.
|
||||
// Non-weapons keep armor/sell info visible.
|
||||
// Compact stats view for weapons: damage range + speed + DPS
|
||||
ImVec4 green(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
if (isWeapon && item.damageMax > 0.0f && item.delayMs > 0) {
|
||||
float speed = static_cast<float>(item.delayMs) / 1000.0f;
|
||||
float dps = ((item.damageMin + item.damageMax) * 0.5f) / speed;
|
||||
ImGui::Text("%.1f DPS", dps);
|
||||
ImGui::Text("%.0f - %.0f Damage", item.damageMin, item.damageMax);
|
||||
ImGui::SameLine(160.0f);
|
||||
ImGui::TextDisabled("Speed %.2f", speed);
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "(%.1f damage per second)", dps);
|
||||
}
|
||||
|
||||
// Armor appears before stat bonuses — matches WoW tooltip order
|
||||
|
|
@ -1834,6 +1845,38 @@ void InventoryScreen::renderItemTooltip(const game::ItemDef& item, const game::I
|
|||
ImGui::TextColored(durColor, "Durability %u / %u",
|
||||
item.curDurability, item.maxDurability);
|
||||
}
|
||||
// Item spell effects (Use/Equip/Chance on Hit)
|
||||
if (gameHandler_) {
|
||||
auto* info = gameHandler_->getItemInfo(item.itemId);
|
||||
if (info) {
|
||||
for (const auto& sp : info->spells) {
|
||||
if (sp.spellId == 0) continue;
|
||||
const char* trigger = nullptr;
|
||||
switch (sp.spellTrigger) {
|
||||
case 0: trigger = "Use"; break;
|
||||
case 1: trigger = "Equip"; break;
|
||||
case 2: trigger = "Chance on Hit"; break;
|
||||
case 6: trigger = "Soulstone"; break;
|
||||
default: break;
|
||||
}
|
||||
if (!trigger) continue;
|
||||
const std::string& spName = gameHandler_->getSpellName(sp.spellId);
|
||||
if (!spName.empty()) {
|
||||
ImGui::TextColored(ImVec4(0.0f, 0.8f, 1.0f, 1.0f),
|
||||
"%s: %s", trigger, spName.c_str());
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.0f, 0.8f, 1.0f, 1.0f),
|
||||
"%s: Spell #%u", trigger, sp.spellId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flavor / lore text (italic yellow in WoW, just yellow here)
|
||||
if (!item.description.empty()) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.9f, 0.5f, 0.9f), "\"%s\"", item.description.c_str());
|
||||
}
|
||||
|
||||
if (item.sellPrice > 0) {
|
||||
uint32_t g = item.sellPrice / 10000;
|
||||
uint32_t s = (item.sellPrice / 100) % 100;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue