diff --git a/Data/expansions/wotlk/dbc_layouts.json b/Data/expansions/wotlk/dbc_layouts.json index 859f7542..5b500741 100644 --- a/Data/expansions/wotlk/dbc_layouts.json +++ b/Data/expansions/wotlk/dbc_layouts.json @@ -28,6 +28,7 @@ "ReputationBase0": 10, "ReputationBase1": 11, "ReputationBase2": 12, "ReputationBase3": 13 }, + "Achievement": { "ID": 0, "Title": 4, "Description": 21 }, "AreaTable": { "ID": 0, "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 e3b297a7..a20f8568 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1979,6 +1979,11 @@ private: struct SpellNameEntry { std::string name; std::string rank; uint32_t schoolMask = 0; }; std::unordered_map spellNameCache_; bool spellNameCacheLoaded_ = false; + + // Achievement name cache (lazy-loaded from Achievement.dbc on first earned event) + std::unordered_map achievementNameCache_; + bool achievementNameCacheLoaded_ = false; + void loadAchievementNameCache(); std::vector trainerTabs_; void handleTrainerList(network::Packet& packet); void loadSpellNameCache(); diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index bf90d0d9..e97ff85b 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -17094,6 +17094,30 @@ void GameHandler::sendLootRoll(uint64_t objectGuid, uint32_t slot, uint8_t rollT // PackedTime date — uint32 bitfield (seconds since epoch) // uint32 realmFirst — how many on realm also got it (0 = realm first) // --------------------------------------------------------------------------- +void GameHandler::loadAchievementNameCache() { + if (achievementNameCacheLoaded_) return; + achievementNameCacheLoaded_ = true; + + auto* am = core::Application::getInstance().getAssetManager(); + if (!am || !am->isInitialized()) return; + + auto dbc = am->loadDBC("Achievement.dbc"); + if (!dbc || !dbc->isLoaded() || dbc->getFieldCount() < 22) return; + + const auto* achL = pipeline::getActiveDBCLayout() + ? pipeline::getActiveDBCLayout()->getLayout("Achievement") : nullptr; + uint32_t titleField = achL ? achL->field("Title") : 4; + if (titleField == 0xFFFFFFFF) titleField = 4; + + 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); + } + LOG_INFO("Achievement: loaded ", achievementNameCache_.size(), " names from Achievement.dbc"); +} + void GameHandler::handleAchievementEarned(network::Packet& packet) { size_t remaining = packet.getSize() - packet.getReadPos(); if (remaining < 16) return; // guid(8) + id(4) + date(4) @@ -17102,12 +17126,20 @@ void GameHandler::handleAchievementEarned(network::Packet& packet) { uint32_t achievementId = packet.readUInt32(); /*uint32_t date =*/ packet.readUInt32(); // PackedTime — not displayed + loadAchievementNameCache(); + auto nameIt = achievementNameCache_.find(achievementId); + const std::string& achName = (nameIt != achievementNameCache_.end()) + ? nameIt->second : std::string(); + // Show chat notification bool isSelf = (guid == playerGuid); if (isSelf) { - char buf[128]; - std::snprintf(buf, sizeof(buf), - "Achievement earned! (ID %u)", achievementId); + char buf[256]; + if (!achName.empty()) { + std::snprintf(buf, sizeof(buf), "Achievement earned: %s", achName.c_str()); + } else { + std::snprintf(buf, sizeof(buf), "Achievement earned! (ID %u)", achievementId); + } addSystemChatMessage(buf); if (achievementEarnedCallback_) { @@ -17127,13 +17159,19 @@ void GameHandler::handleAchievementEarned(network::Packet& packet) { senderName = tmp; } char buf[256]; - std::snprintf(buf, sizeof(buf), - "%s has earned an achievement! (ID %u)", senderName.c_str(), achievementId); + if (!achName.empty()) { + std::snprintf(buf, sizeof(buf), "%s has earned the achievement: %s", + senderName.c_str(), achName.c_str()); + } else { + std::snprintf(buf, sizeof(buf), "%s has earned an achievement! (ID %u)", + senderName.c_str(), achievementId); + } addSystemChatMessage(buf); } LOG_INFO("SMSG_ACHIEVEMENT_EARNED: guid=0x", std::hex, guid, std::dec, - " achievementId=", achievementId, " self=", isSelf); + " achievementId=", achievementId, " self=", isSelf, + achName.empty() ? "" : " name=", achName); } // ---------------------------------------------------------------------------