From 0a2cd213dcdaa8dbd3087033f86a59e0c443c3d3 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 12:15:08 -0700 Subject: [PATCH] feat: display gem socket slots and socket bonus in item tooltips --- include/game/world_packets.hpp | 3 +++ src/game/world_packets.cpp | 20 ++++++++++++++++++++ src/ui/inventory_screen.cpp | 29 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index 6e5721fd..2cb47fe1 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -1594,6 +1594,9 @@ struct ItemQueryResponseData { struct ExtraStat { uint32_t statType = 0; int32_t statValue = 0; }; std::vector extraStats; uint32_t startQuestId = 0; // Non-zero: item begins a quest + // Gem socket slots (WotLK/TBC): 0=no socket; color mask: 1=Meta,2=Red,4=Yellow,8=Blue + std::array socketColor{}; + uint32_t socketBonus = 0; // enchantmentId of socket bonus; 0=none bool valid = false; }; diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 5a9a77ec..abfa5929 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2930,6 +2930,26 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa data.startQuestId = packet.readUInt32(); // StartQuest } + // WotLK 3.3.5a: additional fields after StartQuest (read up to socket data) + // LockID(4), Material(4), Sheath(4), RandomProperty(4), RandomSuffix(4), + // Block(4), ItemSet(4), MaxDurability(4), Area(4), Map(4), BagFamily(4), + // TotemCategory(4) = 48 bytes before sockets + constexpr size_t kPreSocketSkip = 48; + if (packet.getReadPos() + kPreSocketSkip + 28 <= packet.getSize()) { + for (size_t i = 0; i < kPreSocketSkip / 4; ++i) + packet.readUInt32(); + // 3 socket slots: socketColor (4 bytes each) + data.socketColor[0] = packet.readUInt32(); + data.socketColor[1] = packet.readUInt32(); + data.socketColor[2] = packet.readUInt32(); + // 3 socket content (gem enchantment IDs — skip, not currently displayed) + packet.readUInt32(); + packet.readUInt32(); + packet.readUInt32(); + // socketBonus (enchantmentId) + data.socketBonus = packet.readUInt32(); + } + data.valid = !data.name.empty(); return true; } diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index e2075f09..661d27fe 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -2482,6 +2482,35 @@ void InventoryScreen::renderItemTooltip(const game::ItemQueryResponseData& info, } } + // Gem socket slots + { + static const struct { uint32_t mask; const char* label; ImVec4 col; } kSocketTypes[] = { + { 1, "Meta Socket", { 0.7f, 0.7f, 0.9f, 1.0f } }, + { 2, "Red Socket", { 1.0f, 0.3f, 0.3f, 1.0f } }, + { 4, "Yellow Socket", { 1.0f, 0.9f, 0.3f, 1.0f } }, + { 8, "Blue Socket", { 0.3f, 0.6f, 1.0f, 1.0f } }, + }; + bool hasSocket = false; + for (int i = 0; i < 3; ++i) { + if (info.socketColor[i] == 0) continue; + if (!hasSocket) { ImGui::Spacing(); hasSocket = true; } + for (const auto& st : kSocketTypes) { + if (info.socketColor[i] & st.mask) { + ImGui::TextColored(st.col, "%s", st.label); + break; + } + } + } + if (hasSocket && info.socketBonus != 0 && gameHandler_) { + // Socket bonus is an enchantment ID — show its name if known + const std::string& bonusName = gameHandler_->getSpellName(info.socketBonus); + if (!bonusName.empty()) + ImGui::TextColored(ImVec4(0.5f, 0.8f, 0.5f, 1.0f), "Socket Bonus: %s", bonusName.c_str()); + else + ImGui::TextColored(ImVec4(0.5f, 0.8f, 0.5f, 1.0f), "Socket Bonus: (id %u)", info.socketBonus); + } + } + if (info.startQuestId != 0) { ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Begins a Quest"); }