From 40a98f2436305050ac1bf0c4e2821293e36484c5 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 03:15:56 -0700 Subject: [PATCH] Fix talent tab crash and missing talent points at level 10 Unique child window and button IDs per tab prevent ImGui state corruption when switching talent tabs. Parse SMSG_INSPECT_TALENT type=0 properly to populate unspentTalentPoints_ and learnedTalents_ from the server response. --- src/game/game_handler.cpp | 47 +++++++++++++++++++++++++++++++++++++-- src/ui/talent_screen.cpp | 9 +++++--- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 163b44c6..b3e57ccc 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -11192,8 +11192,51 @@ void GameHandler::handleInspectResults(network::Packet& packet) { uint8_t talentType = packet.readUInt8(); if (talentType == 0) { - // Own talent info — silently consume (sent on login, talent changes, respecs) - LOG_DEBUG("SMSG_TALENTS_INFO: received own talent data, ignoring"); + // Own talent info (type 0): uint32 unspentTalents, uint8 groupCount, uint8 activeGroup + // Per group: uint8 talentCount, [talentId(4)+rank(1)]..., uint8 glyphCount, [glyphId(2)]... + if (packet.getSize() - packet.getReadPos() < 6) { + LOG_DEBUG("SMSG_TALENTS_INFO type=0: too short"); + return; + } + uint32_t unspentTalents = packet.readUInt32(); + uint8_t talentGroupCount = packet.readUInt8(); + uint8_t activeTalentGroup = packet.readUInt8(); + + if (activeTalentGroup > 1) activeTalentGroup = 0; + activeTalentSpec_ = activeTalentGroup; + + for (uint8_t g = 0; g < talentGroupCount && g < 2; ++g) { + if (packet.getSize() - packet.getReadPos() < 1) break; + uint8_t talentCount = packet.readUInt8(); + learnedTalents_[g].clear(); + for (uint8_t t = 0; t < talentCount; ++t) { + if (packet.getSize() - packet.getReadPos() < 5) break; + uint32_t talentId = packet.readUInt32(); + uint8_t rank = packet.readUInt8(); + learnedTalents_[g][talentId] = rank; + } + if (packet.getSize() - packet.getReadPos() < 1) break; + uint8_t glyphCount = packet.readUInt8(); + for (uint8_t gl = 0; gl < glyphCount; ++gl) { + if (packet.getSize() - packet.getReadPos() < 2) break; + packet.readUInt16(); // glyphId (skip) + } + } + + unspentTalentPoints_[activeTalentGroup] = static_cast( + unspentTalents > 255 ? 255 : unspentTalents); + + if (!talentsInitialized_) { + talentsInitialized_ = true; + if (unspentTalents > 0) { + addSystemChatMessage("You have " + std::to_string(unspentTalents) + + " unspent talent point" + (unspentTalents != 1 ? "s" : "") + "."); + } + } + + LOG_INFO("SMSG_TALENTS_INFO type=0: unspent=", unspentTalents, + " groups=", (int)talentGroupCount, " active=", (int)activeTalentGroup, + " learned=", learnedTalents_[activeTalentGroup].size()); return; } diff --git a/src/ui/talent_screen.cpp b/src/ui/talent_screen.cpp index d1ee6627..5c6bdaf9 100644 --- a/src/ui/talent_screen.cpp +++ b/src/ui/talent_screen.cpp @@ -216,7 +216,9 @@ void TalentScreen::renderTalentTree(game::GameHandler& gameHandler, uint32_t tab float availW = ImGui::GetContentRegionAvail().x; float offsetX = std::max(0.0f, (availW - gridWidth) * 0.5f); - ImGui::BeginChild("TalentGrid", ImVec2(0, 0), false); + char childId[32]; + snprintf(childId, sizeof(childId), "TalentGrid_%u", tabId); + ImGui::BeginChild(childId, ImVec2(0, 0), false); ImVec2 gridOrigin = ImGui::GetCursorScreenPos(); gridOrigin.x += offsetX; @@ -326,8 +328,9 @@ void TalentScreen::renderTalentTree(game::GameHandler& gameHandler, uint32_t tab renderTalent(gameHandler, *talent, pointsInTree); } else { // Empty cell — invisible placeholder - ImGui::InvisibleButton(("e_" + std::to_string(row) + "_" + std::to_string(col)).c_str(), - ImVec2(iconSize, iconSize)); + char emptyId[32]; + snprintf(emptyId, sizeof(emptyId), "e_%u_%u_%u", tabId, row, col); + ImGui::InvisibleButton(emptyId, ImVec2(iconSize, iconSize)); } } }