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(); }