From bc0d98adae7bdcc44e20dbea59bed01e8530d3cc Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 12:43:53 -0700 Subject: [PATCH] feat: show item set piece count and active bonuses in item tooltip --- src/ui/inventory_screen.cpp | 94 +++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index 7c705d9d..519b2592 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -2560,32 +2560,98 @@ void InventoryScreen::renderItemTooltip(const game::ItemQueryResponseData& info, // Item set membership if (info.itemSetId != 0) { - // Lazy-load ItemSet.dbc name table - static std::unordered_map s_setNames; - static bool s_setNamesLoaded = false; - if (!s_setNamesLoaded && assetManager_) { - s_setNamesLoaded = true; + // Lazy-load full ItemSet.dbc data (name + item IDs + bonus spells/thresholds) + struct SetEntry { + std::string name; + std::array itemIds{}; + std::array spellIds{}; + std::array thresholds{}; + }; + static std::unordered_map s_setData; + static bool s_setDataLoaded = false; + if (!s_setDataLoaded && assetManager_) { + s_setDataLoaded = true; auto dbc = assetManager_->loadDBC("ItemSet.dbc"); if (dbc && dbc->isLoaded()) { const auto* layout = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("ItemSet") : nullptr; - uint32_t idF = layout ? (*layout)["ID"] : 0; - uint32_t nameF = layout ? (*layout)["Name"] : 1; + auto lf = [&](const char* k, uint32_t def) -> uint32_t { + return layout ? (*layout)[k] : def; + }; + uint32_t idF = lf("ID", 0), nameF = lf("Name", 1); + static const char* itemKeys[10] = { + "Item0","Item1","Item2","Item3","Item4", + "Item5","Item6","Item7","Item8","Item9" + }; + static const char* spellKeys[10] = { + "Spell0","Spell1","Spell2","Spell3","Spell4", + "Spell5","Spell6","Spell7","Spell8","Spell9" + }; + static const char* thrKeys[10] = { + "Threshold0","Threshold1","Threshold2","Threshold3","Threshold4", + "Threshold5","Threshold6","Threshold7","Threshold8","Threshold9" + }; + uint32_t itemFallback[10], spellFallback[10], thrFallback[10]; + for (int i = 0; i < 10; ++i) { + itemFallback[i] = 18 + i; + spellFallback[i] = 28 + i; + thrFallback[i] = 38 + i; + } for (uint32_t r = 0; r < dbc->getRecordCount(); ++r) { uint32_t id = dbc->getUInt32(r, idF); if (!id) continue; - std::string nm = dbc->getString(r, nameF); - if (!nm.empty()) s_setNames[id] = std::move(nm); + SetEntry e; + e.name = dbc->getString(r, nameF); + for (int i = 0; i < 10; ++i) { + e.itemIds[i] = dbc->getUInt32(r, layout ? (*layout)[itemKeys[i]] : itemFallback[i]); + e.spellIds[i] = dbc->getUInt32(r, layout ? (*layout)[spellKeys[i]] : spellFallback[i]); + e.thresholds[i] = dbc->getUInt32(r, layout ? (*layout)[thrKeys[i]] : thrFallback[i]); + } + s_setData[id] = std::move(e); } } } - auto setIt = s_setNames.find(info.itemSetId); - const char* setName = (setIt != s_setNames.end()) ? setIt->second.c_str() : nullptr; + + auto setIt = s_setData.find(info.itemSetId); ImGui::Spacing(); - if (setName) - ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "%s", setName); - else + if (setIt != s_setData.end()) { + const SetEntry& se = setIt->second; + // Count equipped pieces + int equipped = 0, total = 0; + for (int i = 0; i < 10; ++i) { + if (se.itemIds[i] == 0) continue; + ++total; + if (inventory) { + for (int s = 0; s < game::Inventory::NUM_EQUIP_SLOTS; s++) { + const auto& eSlot = inventory->getEquipSlot(static_cast(s)); + if (!eSlot.empty() && eSlot.item.itemId == se.itemIds[i]) { ++equipped; break; } + } + } + } + if (total > 0) { + ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), + "%s (%d/%d)", se.name.empty() ? "Set" : se.name.c_str(), equipped, total); + } else { + if (!se.name.empty()) + ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "%s", se.name.c_str()); + } + // Show set bonuses: gray if not reached, green if active + if (gameHandler_) { + for (int i = 0; i < 10; ++i) { + if (se.spellIds[i] == 0 || se.thresholds[i] == 0) continue; + const std::string& bname = gameHandler_->getSpellName(se.spellIds[i]); + bool active = (equipped >= static_cast(se.thresholds[i])); + ImVec4 col = active ? ImVec4(0.5f, 1.0f, 0.5f, 1.0f) + : ImVec4(0.55f, 0.55f, 0.55f, 1.0f); + if (!bname.empty()) + ImGui::TextColored(col, "(%u) %s", se.thresholds[i], bname.c_str()); + else + ImGui::TextColored(col, "(%u) Set Bonus", se.thresholds[i]); + } + } + } else { ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Set (id %u)", info.itemSetId); + } } if (info.startQuestId != 0) {