From d44f5e6560eacbbfd10474c600b350a76c1b1289 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 12:49:38 -0700 Subject: [PATCH] feat: show achievement description and point value in tooltip Hovering an earned achievement now shows its point value (gold badge), description text from Achievement.dbc field 21, and the earn date. loadAchievementNameCache() also populates achievementDescCache_ and achievementPointsCache_ in a single DBC pass; Points field (39) added to the WotLK Achievement DBC layout. --- Data/expansions/wotlk/dbc_layouts.json | 2 +- include/game/game_handler.hpp | 16 +++++++++++++++- src/game/game_handler.cpp | 11 +++++++++++ src/ui/game_screen.cpp | 19 +++++++++++++++++-- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Data/expansions/wotlk/dbc_layouts.json b/Data/expansions/wotlk/dbc_layouts.json index c8287a29..aefc2216 100644 --- a/Data/expansions/wotlk/dbc_layouts.json +++ b/Data/expansions/wotlk/dbc_layouts.json @@ -31,7 +31,7 @@ "ReputationBase0": 10, "ReputationBase1": 11, "ReputationBase2": 12, "ReputationBase3": 13 }, - "Achievement": { "ID": 0, "Title": 4, "Description": 21 }, + "Achievement": { "ID": 0, "Title": 4, "Description": 21, "Points": 39 }, "AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 }, "CreatureDisplayInfoExtra": { "ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4, diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 67a459da..efc8553d 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1429,6 +1429,18 @@ public: static const std::string kEmpty; return kEmpty; } + /// Returns the description of an achievement by ID, or empty string if unknown. + const std::string& getAchievementDescription(uint32_t id) const { + auto it = achievementDescCache_.find(id); + if (it != achievementDescCache_.end()) return it->second; + static const std::string kEmpty; + return kEmpty; + } + /// Returns the point value of an achievement by ID, or 0 if unknown. + uint32_t getAchievementPoints(uint32_t id) const { + auto it = achievementPointsCache_.find(id); + return (it != achievementPointsCache_.end()) ? it->second : 0u; + } // Server-triggered music callback — fires when SMSG_PLAY_MUSIC is received. // The soundId corresponds to a SoundEntries.dbc record. The receiver is @@ -2590,8 +2602,10 @@ private: std::unordered_map spellNameCache_; bool spellNameCacheLoaded_ = false; - // Achievement name cache (lazy-loaded from Achievement.dbc on first earned event) + // Achievement caches (lazy-loaded from Achievement.dbc on first earned event) std::unordered_map achievementNameCache_; + std::unordered_map achievementDescCache_; + std::unordered_map achievementPointsCache_; bool achievementNameCacheLoaded_ = false; void loadAchievementNameCache(); // Set of achievement IDs earned by the player (populated from SMSG_ALL_ACHIEVEMENT_DATA) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 7cd7bc2b..7576561e 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -20303,12 +20303,23 @@ void GameHandler::loadAchievementNameCache() { ? pipeline::getActiveDBCLayout()->getLayout("Achievement") : nullptr; uint32_t titleField = achL ? achL->field("Title") : 4; if (titleField == 0xFFFFFFFF) titleField = 4; + uint32_t descField = achL ? achL->field("Description") : 0xFFFFFFFF; + uint32_t ptsField = achL ? achL->field("Points") : 0xFFFFFFFF; + uint32_t fieldCount = dbc->getFieldCount(); for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) { uint32_t id = dbc->getUInt32(i, 0); if (id == 0) continue; std::string title = dbc->getString(i, titleField); if (!title.empty()) achievementNameCache_[id] = std::move(title); + if (descField != 0xFFFFFFFF && descField < fieldCount) { + std::string desc = dbc->getString(i, descField); + if (!desc.empty()) achievementDescCache_[id] = std::move(desc); + } + if (ptsField != 0xFFFFFFFF && ptsField < fieldCount) { + uint32_t pts = dbc->getUInt32(i, ptsField); + if (pts > 0) achievementPointsCache_[id] = pts; + } } LOG_INFO("Achievement: loaded ", achievementNameCache_.size(), " names from Achievement.dbc"); } diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 0f4c0b96..30bd6789 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -17559,7 +17559,22 @@ void GameScreen::renderAchievementWindow(game::GameHandler& gameHandler) { ImGui::TextUnformatted(display.c_str()); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("Achievement ID: %u", id); + // Points badge + uint32_t pts = gameHandler.getAchievementPoints(id); + if (pts > 0) { + ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.0f, 1.0f), + "%u Achievement Point%s", pts, pts == 1 ? "" : "s"); + ImGui::Separator(); + } + // Description + const std::string& desc = gameHandler.getAchievementDescription(id); + if (!desc.empty()) { + ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + 320.0f); + ImGui::TextUnformatted(desc.c_str()); + ImGui::PopTextWrapPos(); + ImGui::Spacing(); + } + // Earn date uint32_t packed = gameHandler.getAchievementDate(id); if (packed != 0) { // WoW PackedTime: year[31:25] month[24:21] day[20:17] weekday[16:14] hour[13:9] minute[8:3] @@ -17573,7 +17588,7 @@ void GameScreen::renderAchievementWindow(game::GameHandler& gameHandler) { "Jul","Aug","Sep","Oct","Nov","Dec" }; const char* mname = (month >= 1 && month <= 12) ? kMonths[month - 1] : "?"; - ImGui::Text("Earned: %s %d, %d %02d:%02d", mname, day, year, hour, minute); + ImGui::TextDisabled("Earned: %s %d, %d %02d:%02d", mname, day, year, hour, minute); } ImGui::EndTooltip(); }