feat: display detailed honor/arena/token costs for vendor items

Load ItemExtendedCost.dbc and show specific costs (e.g. "2000 Honor",
"200 Arena", "30x Badge of Justice") instead of generic "[Tokens]" for
vendor items with extended costs. Items with both gold and token costs
now show both. Token item names are resolved from item info cache.
This commit is contained in:
Kelsi 2026-03-20 05:28:45 -07:00
parent 595ea466c2
commit 5230815353
2 changed files with 76 additions and 2 deletions

View file

@ -423,6 +423,18 @@ private:
bool spellIconDbLoaded_ = false;
VkDescriptorSet getSpellIcon(uint32_t spellId, pipeline::AssetManager* am);
// ItemExtendedCost.dbc cache: extendedCostId -> cost details
struct ExtendedCostEntry {
uint32_t honorPoints = 0;
uint32_t arenaPoints = 0;
uint32_t itemId[5] = {};
uint32_t itemCount[5] = {};
};
std::unordered_map<uint32_t, ExtendedCostEntry> extendedCostCache_;
bool extendedCostDbLoaded_ = false;
void loadExtendedCostDBC();
std::string formatExtendedCost(uint32_t extendedCostId, game::GameHandler& gameHandler);
// Death Knight rune bar: client-predicted fill (0.0=depleted, 1.0=ready) for smooth animation
float runeClientFill_[6] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};

View file

@ -16020,6 +16020,60 @@ void GameScreen::renderQuestOfferRewardWindow(game::GameHandler& gameHandler) {
}
}
// ============================================================
// ItemExtendedCost.dbc loader
// ============================================================
void GameScreen::loadExtendedCostDBC() {
if (extendedCostDbLoaded_) return;
extendedCostDbLoaded_ = true;
auto* am = core::Application::getInstance().getAssetManager();
if (!am || !am->isInitialized()) return;
auto dbc = am->loadDBC("ItemExtendedCost.dbc");
if (!dbc || !dbc->isLoaded()) return;
// WotLK ItemExtendedCost.dbc: field 0=ID, 1=honorPoints, 2=arenaPoints,
// 3=arenaSlotRestrictions, 4-8=itemId[5], 9-13=itemCount[5], 14=reqRating, 15=purchaseGroup
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
uint32_t id = dbc->getUInt32(i, 0);
if (id == 0) continue;
ExtendedCostEntry e;
e.honorPoints = dbc->getUInt32(i, 1);
e.arenaPoints = dbc->getUInt32(i, 2);
for (int j = 0; j < 5; ++j) {
e.itemId[j] = dbc->getUInt32(i, 4 + j);
e.itemCount[j] = dbc->getUInt32(i, 9 + j);
}
extendedCostCache_[id] = e;
}
LOG_INFO("ItemExtendedCost.dbc: loaded ", extendedCostCache_.size(), " entries");
}
std::string GameScreen::formatExtendedCost(uint32_t extendedCostId, game::GameHandler& gameHandler) {
loadExtendedCostDBC();
auto it = extendedCostCache_.find(extendedCostId);
if (it == extendedCostCache_.end()) return "[Tokens]";
const auto& e = it->second;
std::string result;
if (e.honorPoints > 0) {
result += std::to_string(e.honorPoints) + " Honor";
}
if (e.arenaPoints > 0) {
if (!result.empty()) result += ", ";
result += std::to_string(e.arenaPoints) + " Arena";
}
for (int j = 0; j < 5; ++j) {
if (e.itemId[j] == 0 || e.itemCount[j] == 0) continue;
if (!result.empty()) result += ", ";
const auto* itemInfo = gameHandler.getItemInfo(e.itemId[j]);
if (itemInfo && itemInfo->valid && !itemInfo->name.empty()) {
result += std::to_string(e.itemCount[j]) + "x " + itemInfo->name;
} else {
result += std::to_string(e.itemCount[j]) + "x Item#" + std::to_string(e.itemId[j]);
}
}
return result.empty() ? "[Tokens]" : result;
}
// ============================================================
// Vendor Window (Phase 5)
// ============================================================
@ -16272,8 +16326,9 @@ void GameScreen::renderVendorWindow(game::GameHandler& gameHandler) {
ImGui::TableSetColumnIndex(2);
if (item.buyPrice == 0 && item.extendedCost != 0) {
// Token-only item (no gold cost)
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "[Tokens]");
// Token-only item — show detailed cost from ItemExtendedCost.dbc
std::string costStr = formatExtendedCost(item.extendedCost, gameHandler);
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "%s", costStr.c_str());
} else {
uint32_t g = item.buyPrice / 10000;
uint32_t s = (item.buyPrice / 100) % 100;
@ -16284,6 +16339,13 @@ void GameScreen::renderVendorWindow(game::GameHandler& gameHandler) {
} else {
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%ug %us %uc", g, s, c);
}
// Show additional token cost if both gold and tokens are required
if (item.extendedCost != 0) {
std::string costStr = formatExtendedCost(item.extendedCost, gameHandler);
if (costStr != "[Tokens]") {
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 0.8f), "+ %s", costStr.c_str());
}
}
}
ImGui::TableSetColumnIndex(3);