mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: implement WotLK glyph display in talent screen
Store glyph IDs from SMSG_TALENTS_INFO (previously discarded) in learnedGlyphs_[2][6] per talent spec. Load GlyphProperties.dbc to map glyphId to spellId and major/minor type. Add a Glyphs tab to the talent screen showing all 6 slots with spell icons and names. Also clear vehicleId_ on SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA.
This commit is contained in:
parent
b7c1aa39a9
commit
882cb1bae3
4 changed files with 130 additions and 1 deletions
|
|
@ -706,6 +706,14 @@ public:
|
|||
static std::unordered_map<uint32_t, uint8_t> empty;
|
||||
return spec < 2 ? learnedTalents_[spec] : empty;
|
||||
}
|
||||
|
||||
// Glyphs (WotLK): up to 6 glyph slots per spec (3 major + 3 minor)
|
||||
static constexpr uint8_t MAX_GLYPH_SLOTS = 6;
|
||||
const std::array<uint16_t, MAX_GLYPH_SLOTS>& getGlyphs() const { return learnedGlyphs_[activeTalentSpec_]; }
|
||||
const std::array<uint16_t, MAX_GLYPH_SLOTS>& getGlyphs(uint8_t spec) const {
|
||||
static std::array<uint16_t, MAX_GLYPH_SLOTS> empty{};
|
||||
return spec < 2 ? learnedGlyphs_[spec] : empty;
|
||||
}
|
||||
uint8_t getTalentRank(uint32_t talentId) const {
|
||||
auto it = learnedTalents_[activeTalentSpec_].find(talentId);
|
||||
return (it != learnedTalents_[activeTalentSpec_].end()) ? it->second : 0;
|
||||
|
|
@ -2308,6 +2316,7 @@ private:
|
|||
uint8_t activeTalentSpec_ = 0; // Currently active spec (0 or 1)
|
||||
uint8_t unspentTalentPoints_[2] = {0, 0}; // Unspent points per spec
|
||||
std::unordered_map<uint32_t, uint8_t> learnedTalents_[2]; // Learned talents per spec
|
||||
std::array<std::array<uint16_t, MAX_GLYPH_SLOTS>, 2> learnedGlyphs_{}; // Glyphs per spec
|
||||
std::unordered_map<uint32_t, TalentEntry> talentCache_; // talentId -> entry
|
||||
std::unordered_map<uint32_t, TalentTabEntry> talentTabCache_; // tabId -> entry
|
||||
bool talentDbcLoaded_ = false;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ private:
|
|||
|
||||
void loadSpellDBC(pipeline::AssetManager* assetManager);
|
||||
void loadSpellIconDBC(pipeline::AssetManager* assetManager);
|
||||
void loadGlyphPropertiesDBC(pipeline::AssetManager* assetManager);
|
||||
void renderGlyphs(game::GameHandler& gameHandler);
|
||||
VkDescriptorSet getSpellIcon(uint32_t iconId, pipeline::AssetManager* assetManager);
|
||||
|
||||
bool open = false;
|
||||
|
|
@ -36,11 +38,16 @@ private:
|
|||
// DBC caches
|
||||
bool spellDbcLoaded = false;
|
||||
bool iconDbcLoaded = false;
|
||||
bool glyphDbcLoaded = false;
|
||||
std::unordered_map<uint32_t, uint32_t> spellIconIds; // spellId -> iconId
|
||||
std::unordered_map<uint32_t, std::string> spellIconPaths; // iconId -> path
|
||||
std::unordered_map<uint32_t, VkDescriptorSet> spellIconCache; // iconId -> texture
|
||||
std::unordered_map<uint32_t, std::string> spellTooltips; // spellId -> description
|
||||
std::unordered_map<uint32_t, VkDescriptorSet> bgTextureCache_; // tabId -> bg texture
|
||||
|
||||
// GlyphProperties.dbc cache: glyphId -> { spellId, isMajor }
|
||||
struct GlyphInfo { uint32_t spellId = 0; bool isMajor = false; };
|
||||
std::unordered_map<uint32_t, GlyphInfo> glyphProperties_; // glyphId -> info
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
|||
|
|
@ -6252,6 +6252,9 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
handleQuestPoiQueryResponse(packet);
|
||||
break;
|
||||
case Opcode::SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA:
|
||||
vehicleId_ = 0; // Vehicle ride cancelled; clear UI
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_RESET_RANGED_COMBAT_TIMER:
|
||||
case Opcode::SMSG_PROFILEDATA_RESPONSE:
|
||||
packet.setReadPos(packet.getSize());
|
||||
|
|
@ -6891,6 +6894,8 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) {
|
|||
talentsInitialized_ = false;
|
||||
learnedTalents_[0].clear();
|
||||
learnedTalents_[1].clear();
|
||||
learnedGlyphs_[0].fill(0);
|
||||
learnedGlyphs_[1].fill(0);
|
||||
unspentTalentPoints_[0] = 0;
|
||||
unspentTalentPoints_[1] = 0;
|
||||
activeTalentSpec_ = 0;
|
||||
|
|
@ -11338,10 +11343,12 @@ void GameHandler::handleInspectResults(network::Packet& packet) {
|
|||
learnedTalents_[g][talentId] = rank;
|
||||
}
|
||||
if (packet.getSize() - packet.getReadPos() < 1) break;
|
||||
learnedGlyphs_[g].fill(0);
|
||||
uint8_t glyphCount = packet.readUInt8();
|
||||
for (uint8_t gl = 0; gl < glyphCount; ++gl) {
|
||||
if (packet.getSize() - packet.getReadPos() < 2) break;
|
||||
packet.readUInt16(); // glyphId (skip)
|
||||
uint16_t glyphId = packet.readUInt16();
|
||||
if (gl < MAX_GLYPH_SLOTS) learnedGlyphs_[g][gl] = glyphId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ void TalentScreen::renderTalentTrees(game::GameHandler& gameHandler) {
|
|||
gameHandler.loadTalentDbc();
|
||||
loadSpellDBC(assetManager);
|
||||
loadSpellIconDBC(assetManager);
|
||||
loadGlyphPropertiesDBC(assetManager);
|
||||
}
|
||||
|
||||
uint8_t playerClass = gameHandler.getPlayerClass();
|
||||
|
|
@ -161,6 +162,18 @@ void TalentScreen::renderTalentTrees(game::GameHandler& gameHandler) {
|
|||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
// Glyphs tab (WotLK only — visible when any glyph slot is populated or DBC data loaded)
|
||||
if (!glyphProperties_.empty() || [&]() {
|
||||
const auto& g = gameHandler.getGlyphs();
|
||||
for (auto id : g) if (id != 0) return true;
|
||||
return false; }()) {
|
||||
if (ImGui::BeginTabItem("Glyphs")) {
|
||||
renderGlyphs(gameHandler);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
|
|
@ -616,6 +629,99 @@ void TalentScreen::loadSpellIconDBC(pipeline::AssetManager* assetManager) {
|
|||
}
|
||||
}
|
||||
|
||||
void TalentScreen::loadGlyphPropertiesDBC(pipeline::AssetManager* assetManager) {
|
||||
if (glyphDbcLoaded) return;
|
||||
glyphDbcLoaded = true;
|
||||
|
||||
if (!assetManager || !assetManager->isInitialized()) return;
|
||||
|
||||
auto dbc = assetManager->loadDBC("GlyphProperties.dbc");
|
||||
if (!dbc || !dbc->isLoaded()) return;
|
||||
|
||||
// GlyphProperties.dbc: field 0=ID, field 1=SpellID, field 2=GlyphSlotFlags (1=minor), field 3=SpellIconID
|
||||
for (uint32_t i = 0; i < dbc->getRecordCount(); i++) {
|
||||
uint32_t id = dbc->getUInt32(i, 0);
|
||||
uint32_t spellId = dbc->getUInt32(i, 1);
|
||||
uint32_t flags = dbc->getUInt32(i, 2);
|
||||
if (id == 0) continue;
|
||||
GlyphInfo info;
|
||||
info.spellId = spellId;
|
||||
info.isMajor = (flags == 0); // flag 0 = major, flag 1 = minor
|
||||
glyphProperties_[id] = info;
|
||||
}
|
||||
}
|
||||
|
||||
void TalentScreen::renderGlyphs(game::GameHandler& gameHandler) {
|
||||
auto* assetManager = core::Application::getInstance().getAssetManager();
|
||||
const auto& glyphs = gameHandler.getGlyphs();
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.0f, 1.0f), "Major Glyphs");
|
||||
ImGui::Separator();
|
||||
|
||||
// WotLK: 6 glyph slots total. Slots 0,2,4 are major by convention from the server,
|
||||
// but we check GlyphProperties.dbc flags when available.
|
||||
// Display all 6 slots grouped: show major (non-minor) first, then minor.
|
||||
std::vector<std::pair<int, bool>> majorSlots, minorSlots;
|
||||
for (int i = 0; i < game::GameHandler::MAX_GLYPH_SLOTS; i++) {
|
||||
uint16_t glyphId = glyphs[i];
|
||||
bool isMajor = true;
|
||||
if (glyphId != 0) {
|
||||
auto git = glyphProperties_.find(glyphId);
|
||||
if (git != glyphProperties_.end()) isMajor = git->second.isMajor;
|
||||
else isMajor = (i % 2 == 0); // fallback: even slots = major
|
||||
} else {
|
||||
isMajor = (i % 2 == 0); // empty slots follow same pattern
|
||||
}
|
||||
if (isMajor) majorSlots.push_back({i, true});
|
||||
else minorSlots.push_back({i, false});
|
||||
}
|
||||
|
||||
auto renderGlyphSlot = [&](int slotIdx) {
|
||||
uint16_t glyphId = glyphs[slotIdx];
|
||||
char label[64];
|
||||
if (glyphId == 0) {
|
||||
snprintf(label, sizeof(label), "Slot %d [Empty]", slotIdx + 1);
|
||||
ImGui::TextDisabled("%s", label);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t spellId = 0;
|
||||
uint32_t iconId = 0;
|
||||
auto git = glyphProperties_.find(glyphId);
|
||||
if (git != glyphProperties_.end()) {
|
||||
spellId = git->second.spellId;
|
||||
auto iit = spellIconIds.find(spellId);
|
||||
if (iit != spellIconIds.end()) iconId = iit->second;
|
||||
}
|
||||
|
||||
// Icon (24x24)
|
||||
VkDescriptorSet icon = getSpellIcon(iconId, assetManager);
|
||||
if (icon != VK_NULL_HANDLE) {
|
||||
ImGui::Image((ImTextureID)(uintptr_t)icon, ImVec2(24, 24));
|
||||
ImGui::SameLine(0, 6);
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(24, 24));
|
||||
ImGui::SameLine(0, 6);
|
||||
}
|
||||
|
||||
// Spell name
|
||||
const std::string& name = spellId ? gameHandler.getSpellName(spellId) : "";
|
||||
if (!name.empty()) {
|
||||
ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "%s", name.c_str());
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Glyph #%u", (uint32_t)glyphId);
|
||||
}
|
||||
};
|
||||
|
||||
for (auto& [idx, major] : majorSlots) renderGlyphSlot(idx);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextColored(ImVec4(0.6f, 0.8f, 1.0f, 1.0f), "Minor Glyphs");
|
||||
ImGui::Separator();
|
||||
for (auto& [idx, major] : minorSlots) renderGlyphSlot(idx);
|
||||
}
|
||||
|
||||
VkDescriptorSet TalentScreen::getSpellIcon(uint32_t iconId, pipeline::AssetManager* assetManager) {
|
||||
if (iconId == 0 || !assetManager) return VK_NULL_HANDLE;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue