mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: resolve title names from CharTitles.dbc in SMSG_TITLE_EARNED
Previously SMSG_TITLE_EARNED only showed the numeric bit index. Now it lazy-loads CharTitles.dbc and formats the full title string with the player's name (e.g. "Title earned: Commander Kelsi!"). - Add CharTitles layout to WotLK (TitleBit=36) and TBC (TitleBit=20) layouts - loadTitleNameCache() maps each titleBit to its English title string - SMSG_TITLE_EARNED substitutes %s placeholder with local player's name - Falls back to "Title earned (bit N)!" if DBC is unavailable
This commit is contained in:
parent
cd01d07a91
commit
81b95b4af7
4 changed files with 69 additions and 6 deletions
|
|
@ -31,6 +31,7 @@
|
|||
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||
"ReputationBase2": 12, "ReputationBase3": 13
|
||||
},
|
||||
"CharTitles": { "ID": 0, "Title": 2, "TitleBit": 20 },
|
||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||
"CreatureDisplayInfoExtra": {
|
||||
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||
"ReputationBase2": 12, "ReputationBase3": 13
|
||||
},
|
||||
"CharTitles": { "ID": 0, "Title": 2, "TitleBit": 36 },
|
||||
"Achievement": { "ID": 0, "Title": 4, "Description": 21, "Points": 39 },
|
||||
"AchievementCriteria": { "ID": 0, "AchievementID": 1, "Quantity": 4, "Description": 9 },
|
||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||
|
|
|
|||
|
|
@ -2694,6 +2694,12 @@ private:
|
|||
std::unordered_map<uint32_t, SpellNameEntry> spellNameCache_;
|
||||
bool spellNameCacheLoaded_ = false;
|
||||
|
||||
// Title cache: maps titleBit → title string (lazy-loaded from CharTitles.dbc)
|
||||
// The strings use "%s" as a player-name placeholder (e.g. "Commander %s", "%s the Explorer").
|
||||
std::unordered_map<uint32_t, std::string> titleNameCache_;
|
||||
bool titleNameCacheLoaded_ = false;
|
||||
void loadTitleNameCache();
|
||||
|
||||
// Achievement caches (lazy-loaded from Achievement.dbc on first earned event)
|
||||
std::unordered_map<uint32_t, std::string> achievementNameCache_;
|
||||
std::unordered_map<uint32_t, std::string> achievementDescCache_;
|
||||
|
|
|
|||
|
|
@ -2095,12 +2095,40 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
if (packet.getSize() - packet.getReadPos() < 8) break;
|
||||
uint32_t titleBit = packet.readUInt32();
|
||||
uint32_t isLost = packet.readUInt32();
|
||||
char buf[128];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
isLost ? "Title removed (ID %u)." : "Title earned (ID %u)!",
|
||||
titleBit);
|
||||
addSystemChatMessage(buf);
|
||||
LOG_INFO("SMSG_TITLE_EARNED: id=", titleBit, " lost=", isLost);
|
||||
loadTitleNameCache();
|
||||
|
||||
// Format the title string using the player's own name
|
||||
std::string titleStr;
|
||||
auto tit = titleNameCache_.find(titleBit);
|
||||
if (tit != titleNameCache_.end() && !tit->second.empty()) {
|
||||
// Title strings contain "%s" as a player-name placeholder.
|
||||
// Replace it with the local player's name if known.
|
||||
auto nameIt = playerNameCache.find(playerGuid);
|
||||
const std::string& pName = (nameIt != playerNameCache.end())
|
||||
? nameIt->second : "you";
|
||||
const std::string& fmt = tit->second;
|
||||
size_t pos = fmt.find("%s");
|
||||
if (pos != std::string::npos) {
|
||||
titleStr = fmt.substr(0, pos) + pName + fmt.substr(pos + 2);
|
||||
} else {
|
||||
titleStr = fmt;
|
||||
}
|
||||
}
|
||||
|
||||
std::string msg;
|
||||
if (!titleStr.empty()) {
|
||||
msg = isLost ? ("Title removed: " + titleStr + ".")
|
||||
: ("Title earned: " + titleStr + "!");
|
||||
} else {
|
||||
char buf[64];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
isLost ? "Title removed (bit %u)." : "Title earned (bit %u)!",
|
||||
titleBit);
|
||||
msg = buf;
|
||||
}
|
||||
addSystemChatMessage(msg);
|
||||
LOG_INFO("SMSG_TITLE_EARNED: bit=", titleBit, " lost=", isLost,
|
||||
" title='", titleStr, "'");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -20539,6 +20567,33 @@ 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::loadTitleNameCache() {
|
||||
if (titleNameCacheLoaded_) return;
|
||||
titleNameCacheLoaded_ = true;
|
||||
|
||||
auto* am = core::Application::getInstance().getAssetManager();
|
||||
if (!am || !am->isInitialized()) return;
|
||||
|
||||
auto dbc = am->loadDBC("CharTitles.dbc");
|
||||
if (!dbc || !dbc->isLoaded() || dbc->getFieldCount() < 5) return;
|
||||
|
||||
const auto* layout = pipeline::getActiveDBCLayout()
|
||||
? pipeline::getActiveDBCLayout()->getLayout("CharTitles") : nullptr;
|
||||
|
||||
uint32_t titleField = layout ? layout->field("Title") : 2;
|
||||
uint32_t bitField = layout ? layout->field("TitleBit") : 36;
|
||||
if (titleField == 0xFFFFFFFF) titleField = 2;
|
||||
if (bitField == 0xFFFFFFFF) bitField = static_cast<uint32_t>(dbc->getFieldCount() - 1);
|
||||
|
||||
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
|
||||
uint32_t bit = dbc->getUInt32(i, bitField);
|
||||
if (bit == 0) continue;
|
||||
std::string name = dbc->getString(i, titleField);
|
||||
if (!name.empty()) titleNameCache_[bit] = std::move(name);
|
||||
}
|
||||
LOG_INFO("CharTitles: loaded ", titleNameCache_.size(), " title names from DBC");
|
||||
}
|
||||
|
||||
void GameHandler::loadAchievementNameCache() {
|
||||
if (achievementNameCacheLoaded_) return;
|
||||
achievementNameCacheLoaded_ = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue