mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 00:03:50 +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,
|
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||||
"ReputationBase2": 12, "ReputationBase3": 13
|
"ReputationBase2": 12, "ReputationBase3": 13
|
||||||
},
|
},
|
||||||
|
"CharTitles": { "ID": 0, "Title": 2, "TitleBit": 20 },
|
||||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||||
"CreatureDisplayInfoExtra": {
|
"CreatureDisplayInfoExtra": {
|
||||||
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
"ReputationBase0": 10, "ReputationBase1": 11,
|
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||||
"ReputationBase2": 12, "ReputationBase3": 13
|
"ReputationBase2": 12, "ReputationBase3": 13
|
||||||
},
|
},
|
||||||
|
"CharTitles": { "ID": 0, "Title": 2, "TitleBit": 36 },
|
||||||
"Achievement": { "ID": 0, "Title": 4, "Description": 21, "Points": 39 },
|
"Achievement": { "ID": 0, "Title": 4, "Description": 21, "Points": 39 },
|
||||||
"AchievementCriteria": { "ID": 0, "AchievementID": 1, "Quantity": 4, "Description": 9 },
|
"AchievementCriteria": { "ID": 0, "AchievementID": 1, "Quantity": 4, "Description": 9 },
|
||||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||||
|
|
|
||||||
|
|
@ -2694,6 +2694,12 @@ private:
|
||||||
std::unordered_map<uint32_t, SpellNameEntry> spellNameCache_;
|
std::unordered_map<uint32_t, SpellNameEntry> spellNameCache_;
|
||||||
bool spellNameCacheLoaded_ = false;
|
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)
|
// 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> achievementNameCache_;
|
||||||
std::unordered_map<uint32_t, std::string> achievementDescCache_;
|
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;
|
if (packet.getSize() - packet.getReadPos() < 8) break;
|
||||||
uint32_t titleBit = packet.readUInt32();
|
uint32_t titleBit = packet.readUInt32();
|
||||||
uint32_t isLost = packet.readUInt32();
|
uint32_t isLost = packet.readUInt32();
|
||||||
char buf[128];
|
loadTitleNameCache();
|
||||||
std::snprintf(buf, sizeof(buf),
|
|
||||||
isLost ? "Title removed (ID %u)." : "Title earned (ID %u)!",
|
// Format the title string using the player's own name
|
||||||
titleBit);
|
std::string titleStr;
|
||||||
addSystemChatMessage(buf);
|
auto tit = titleNameCache_.find(titleBit);
|
||||||
LOG_INFO("SMSG_TITLE_EARNED: id=", titleBit, " lost=", isLost);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20539,6 +20567,33 @@ void GameHandler::sendLootRoll(uint64_t objectGuid, uint32_t slot, uint8_t rollT
|
||||||
// PackedTime date — uint32 bitfield (seconds since epoch)
|
// PackedTime date — uint32 bitfield (seconds since epoch)
|
||||||
// uint32 realmFirst — how many on realm also got it (0 = realm first)
|
// 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() {
|
void GameHandler::loadAchievementNameCache() {
|
||||||
if (achievementNameCacheLoaded_) return;
|
if (achievementNameCacheLoaded_) return;
|
||||||
achievementNameCacheLoaded_ = true;
|
achievementNameCacheLoaded_ = true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue