From d817e4144c438fc5c1dc5640778b85521f48f507 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 06:55:16 -0700 Subject: [PATCH] feat: debuff dispel-type border coloring in buff bar Read DispelType from Spell.dbc (new field in all expansion DBC layouts) and use it to color debuff icon borders: magic=blue, curse=purple, disease=brown, poison=green, other=red. Buffs remain green-bordered. Adds getSpellDispelType() to GameHandler for lazy cache lookup. --- Data/expansions/classic/dbc_layouts.json | 3 ++- Data/expansions/tbc/dbc_layouts.json | 3 ++- Data/expansions/turtle/dbc_layouts.json | 3 ++- Data/expansions/wotlk/dbc_layouts.json | 3 ++- include/game/game_handler.hpp | 4 +++- src/game/game_handler.cpp | 19 ++++++++++++++++++- src/ui/game_screen.cpp | 17 ++++++++++++++++- 7 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Data/expansions/classic/dbc_layouts.json b/Data/expansions/classic/dbc_layouts.json index ca8c8a50..5c550c81 100644 --- a/Data/expansions/classic/dbc_layouts.json +++ b/Data/expansions/classic/dbc_layouts.json @@ -2,7 +2,8 @@ "Spell": { "ID": 0, "Attributes": 5, "IconID": 117, "Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1, - "CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33 + "CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33, + "DispelType": 4 }, "SpellRange": { "MaxRange": 2 }, "ItemDisplayInfo": { diff --git a/Data/expansions/tbc/dbc_layouts.json b/Data/expansions/tbc/dbc_layouts.json index fdc9e07d..b99159a6 100644 --- a/Data/expansions/tbc/dbc_layouts.json +++ b/Data/expansions/tbc/dbc_layouts.json @@ -2,7 +2,8 @@ "Spell": { "ID": 0, "Attributes": 5, "IconID": 124, "Name": 127, "Tooltip": 154, "Rank": 136, "SchoolMask": 215, - "CastingTimeIndex": 22, "PowerType": 35, "ManaCost": 36, "RangeIndex": 40 + "CastingTimeIndex": 22, "PowerType": 35, "ManaCost": 36, "RangeIndex": 40, + "DispelType": 3 }, "SpellRange": { "MaxRange": 4 }, "ItemDisplayInfo": { diff --git a/Data/expansions/turtle/dbc_layouts.json b/Data/expansions/turtle/dbc_layouts.json index a2482e0d..3b06971d 100644 --- a/Data/expansions/turtle/dbc_layouts.json +++ b/Data/expansions/turtle/dbc_layouts.json @@ -2,7 +2,8 @@ "Spell": { "ID": 0, "Attributes": 5, "IconID": 117, "Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1, - "CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33 + "CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33, + "DispelType": 4 }, "SpellRange": { "MaxRange": 2 }, "ItemDisplayInfo": { diff --git a/Data/expansions/wotlk/dbc_layouts.json b/Data/expansions/wotlk/dbc_layouts.json index 0d1667a1..94f7df4d 100644 --- a/Data/expansions/wotlk/dbc_layouts.json +++ b/Data/expansions/wotlk/dbc_layouts.json @@ -2,7 +2,8 @@ "Spell": { "ID": 0, "Attributes": 4, "IconID": 133, "Name": 136, "Tooltip": 139, "Rank": 153, "SchoolMask": 225, - "PowerType": 14, "ManaCost": 39, "CastingTimeIndex": 47, "RangeIndex": 49 + "PowerType": 14, "ManaCost": 39, "CastingTimeIndex": 47, "RangeIndex": 49, + "DispelType": 2 }, "SpellRange": { "MaxRange": 4 }, "ItemDisplayInfo": { diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 8dbebe1d..e3840fe2 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1586,6 +1586,8 @@ public: const std::string& getSpellName(uint32_t spellId) const; const std::string& getSpellRank(uint32_t spellId) const; const std::string& getSkillLineName(uint32_t spellId) const; + /// Returns the DispelType for a spell (0=none,1=magic,2=curse,3=disease,4=poison,5+=other) + uint8_t getSpellDispelType(uint32_t spellId) const; struct TrainerTab { std::string name; @@ -2486,7 +2488,7 @@ private: // Trainer bool trainerWindowOpen_ = false; TrainerListData currentTrainerList_; - struct SpellNameEntry { std::string name; std::string rank; uint32_t schoolMask = 0; }; + struct SpellNameEntry { std::string name; std::string rank; uint32_t schoolMask = 0; uint8_t dispelType = 0; }; std::unordered_map spellNameCache_; bool spellNameCacheLoaded_ = false; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index a0f274a7..cd373518 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -16983,6 +16983,14 @@ void GameHandler::loadSpellNameCache() { if (f != 0xFFFFFFFF && f < dbc->getFieldCount()) { schoolEnumField = f; hasSchoolEnum = true; } } + // DispelType field (0=none,1=magic,2=curse,3=disease,4=poison,5=stealth,…) + uint32_t dispelField = 0xFFFFFFFF; + bool hasDispelField = false; + if (spellL) { + uint32_t f = spellL->field("DispelType"); + if (f != 0xFFFFFFFF && f < dbc->getFieldCount()) { dispelField = f; hasDispelField = true; } + } + uint32_t count = dbc->getRecordCount(); for (uint32_t i = 0; i < count; ++i) { uint32_t id = dbc->getUInt32(i, spellL ? (*spellL)["ID"] : 0); @@ -16990,7 +16998,7 @@ void GameHandler::loadSpellNameCache() { std::string name = dbc->getString(i, spellL ? (*spellL)["Name"] : 136); std::string rank = dbc->getString(i, spellL ? (*spellL)["Rank"] : 153); if (!name.empty()) { - SpellNameEntry entry{std::move(name), std::move(rank), 0}; + SpellNameEntry entry{std::move(name), std::move(rank), 0, 0}; if (hasSchoolMask) { entry.schoolMask = dbc->getUInt32(i, schoolMaskField); } else if (hasSchoolEnum) { @@ -16999,6 +17007,9 @@ void GameHandler::loadSpellNameCache() { uint32_t e = dbc->getUInt32(i, schoolEnumField); entry.schoolMask = (e < 7) ? enumToBitmask[e] : 0; } + if (hasDispelField) { + entry.dispelType = static_cast(dbc->getUInt32(i, dispelField)); + } spellNameCache_[id] = std::move(entry); } } @@ -17192,6 +17203,12 @@ const std::string& GameHandler::getSpellRank(uint32_t spellId) const { return (it != spellNameCache_.end()) ? it->second.rank : EMPTY_STRING; } +uint8_t GameHandler::getSpellDispelType(uint32_t spellId) const { + const_cast(this)->loadSpellNameCache(); + auto it = spellNameCache_.find(spellId); + return (it != spellNameCache_.end()) ? it->second.dispelType : 0; +} + const std::string& GameHandler::getSkillLineName(uint32_t spellId) const { auto slIt = spellToSkillLine_.find(spellId); if (slIt == spellToSkillLine_.end()) return EMPTY_STRING; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index bd1bcaed..8f2bd977 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -9503,7 +9503,22 @@ void GameScreen::renderBuffBar(game::GameHandler& gameHandler) { ImGui::PushID(static_cast(i) + (pass * 256)); - ImVec4 borderColor = isBuff ? ImVec4(0.2f, 0.8f, 0.2f, 0.9f) : ImVec4(0.8f, 0.2f, 0.2f, 0.9f); + // Determine border color: buffs = green; debuffs use WoW dispel-type colors + ImVec4 borderColor; + if (isBuff) { + borderColor = ImVec4(0.2f, 0.8f, 0.2f, 0.9f); // green + } else { + // Debuff: color by dispel type (0=none/red, 1=magic/blue, 2=curse/purple, + // 3=disease/brown, 4=poison/green, other=dark-red) + uint8_t dt = gameHandler.getSpellDispelType(aura.spellId); + switch (dt) { + case 1: borderColor = ImVec4(0.15f, 0.50f, 1.00f, 0.9f); break; // magic: blue + case 2: borderColor = ImVec4(0.70f, 0.20f, 0.90f, 0.9f); break; // curse: purple + case 3: borderColor = ImVec4(0.55f, 0.30f, 0.10f, 0.9f); break; // disease: brown + case 4: borderColor = ImVec4(0.10f, 0.70f, 0.10f, 0.9f); break; // poison: green + default: borderColor = ImVec4(0.80f, 0.20f, 0.20f, 0.9f); break; // other: red + } + } // Try to get spell icon VkDescriptorSet iconTex = VK_NULL_HANDLE;