diff --git a/Modules/DispelHighlight.lua b/Modules/DispelHighlight.lua index b7e1aca..ca96008 100644 --- a/Modules/DispelHighlight.lua +++ b/Modules/DispelHighlight.lua @@ -17,15 +17,40 @@ local CLASS_DISPEL_TYPES = { PYROMANCER = {}, } --- Extra dispel types unlocked by a talent choice. There's no reliable way to --- auto-detect the talent on this server, so these are gated by a manual --- checkbox in the options panel instead. +-- Extra dispel types unlocked by a talent choice, keyed to the Character +-- Advancement entry that grants them. The IDs are stable and shared across +-- every class tree, so querying one from a class that can't take it simply +-- reports rank 0 rather than erroring. local TALENT_DISPEL_TYPES = { - PROPHET = {flag = "hasBlightAntidote", types = {Curse = true}}, - CULTIST = {flag = "hasDevourCurse", types = {Curse = true}}, - PYROMANCER = {flag = "hasBurnImpurities", types = {Magic = true, Disease = true, Bleed = true}}, + PROPHET = {talentID = 6324, types = {Curse = true}}, -- Blight Antidote + CULTIST = {talentID = 12982, types = {Curse = true}}, -- Devour Curse + PYROMANCER = {talentID = 31276, types = {Magic = true, Disease = true, Bleed = true}}, -- Burn Impurities } +-- Cached result of the talent lookup for the player's current build. +-- PostUpdate_DebuffHighlight runs once per aura per frame, so the query is +-- hoisted out of that path and refreshed only when the build can change. +local hasTalentDispel = false + +-- C_CharacterAdvancement is a server addition with no API documentation, so +-- every entry point is guarded and a failed call reads as "talent not taken", +-- which degrades to the same behaviour as before auto-detection existed. +local function HasTalent(talentID) + local api = C_CharacterAdvancement + if not (api and type(api.GetTalentRankByID) == "function") then return false end + + local ok, rank = pcall(api.GetTalentRankByID, talentID) + + return ok and type(rank) == "number" and rank > 0 +end + +local function RefreshTalentState() + local _, class = UnitClass("player") + local talent = TALENT_DISPEL_TYPES[class] + + hasTalentDispel = (talent and HasTalent(talent.talentID)) or false +end + function CoA:CanDispel(debuffType) local _, class = UnitClass("player") local baseTypes = CLASS_DISPEL_TYPES[class] @@ -34,7 +59,9 @@ function CoA:CanDispel(debuffType) if baseTypes and baseTypes[debuffType] then return true end local talent = TALENT_DISPEL_TYPES[class] - if talent and CoA.db.profile[talent.flag] and talent.types[debuffType] then return true end + if talent and hasTalentDispel and CoA.db.profile.dispelHighlightDetectTalents and talent.types[debuffType] then + return true + end return false end @@ -69,18 +96,30 @@ hooksecurefunc(UF, "Configure_DebuffHighlight", function(_, frame) end) function CoA:UpdateDispelHighlight() + RefreshTalentState() UF:Update_AllFrames() end -- On Ascension, UnitClass("player")'s second return only reliably reports the -- real custom class (CULTIST, PYROMANCER, ...) a short while after login -- -- immediately at ADDON_LOADED/PLAYER_LOGIN it can still read back the generic --- "HERO" base class. If a debuff highlight gets evaluated before that data --- syncs, CoA:CanDispel wrongly returns false and the highlight stays wrongly --- suppressed until the next aura change. Force one extra refresh shortly --- after entering the world so the very first debuff isn't judged too early. +-- "HERO" base class, and the advancement data lags in the same way. If a +-- debuff highlight gets evaluated before that data syncs, CoA:CanDispel +-- wrongly returns false and the highlight stays wrongly suppressed until the +-- next aura change. Force one extra refresh shortly after entering the world +-- so the very first debuff isn't judged too early. +-- +-- CHARACTER_ADVANCEMENT_UPDATE_ENTRIES_RESULT fires when a build is confirmed, +-- which is the point the talent ranks actually change. The advancement UI also +-- fires CHARACTER_ADVANCEMENT_PENDING_BUILD_UPDATED while talents are being +-- clicked around, but ranks don't move until the build is saved, so listening +-- to it would only cost refreshes that read back the unchanged state. function CoA:InitializeDispelHighlight() CoA:RegisterEvent("PLAYER_ENTERING_WORLD", function() CoA:ScheduleTimer("UpdateDispelHighlight", 2) end) + + CoA:RegisterEvent("CHARACTER_ADVANCEMENT_UPDATE_ENTRIES_RESULT", function() + CoA:UpdateDispelHighlight() + end) end diff --git a/README.md b/README.md index 35544c7..e629a5c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ElvUI plugin that hides and skins the custom CoA (3.3.5) frames not covered by s - Class resource frames: hiding - Extra action button frame: skin, anchor, size - Instance swap frame: skin, anchor, font settings -- Only highlight unitframes if dispellable by your CoA class, with manual talent switch. +- Only highlight unitframes if dispellable by your CoA class. ## Installation diff --git a/core.lua b/core.lua index 1b9f486..e88f96e 100644 --- a/core.lua +++ b/core.lua @@ -32,9 +32,7 @@ local defaults = { instanceButtonFontSize = 12, instanceButtonFontOutline = "OUTLINE", dispelHighlightOnlyMine = false, - hasBlightAntidote = false, - hasDevourCurse = false, - hasBurnImpurities = false, + dispelHighlightDetectTalents = true, hideResourceSegmentBar = false, hideResourceOrb = false, hideResourceBar = false, @@ -405,55 +403,20 @@ local function getOptions() end end, }, - talents = { + detectTalents = { order = 3, - type = "group", - inline = true, - name = "Talents", - args = { - hasBlightAntidote = { - order = 1, - type = "toggle", - name = string.format("Blight Antidote (%s)", LOCALIZED_CLASS_NAMES_MALE.PROPHET), - desc = "Grants Curse dispel.", - get = function() return CoA.db.profile.hasBlightAntidote end, - set = function(_, value) - CoA.db.profile.hasBlightAntidote = value + type = "toggle", + name = "Detect Talents", + desc = "Read the talents that grant extra dispel types from your current build. Turn this off to fall back to what your class can dispel without any talent.", + disabled = function() return not CoA.db.profile.dispelHighlightOnlyMine end, + get = function() return CoA.db.profile.dispelHighlightDetectTalents end, + set = function(_, value) + CoA.db.profile.dispelHighlightDetectTalents = value - if CoA.UpdateDispelHighlight then - CoA:UpdateDispelHighlight() - end - end, - }, - hasDevourCurse = { - order = 2, - type = "toggle", - name = string.format("Devour Curse (%s)", LOCALIZED_CLASS_NAMES_MALE.CULTIST), - desc = "Grants Curse dispel.", - get = function() return CoA.db.profile.hasDevourCurse end, - set = function(_, value) - CoA.db.profile.hasDevourCurse = value - - if CoA.UpdateDispelHighlight then - CoA:UpdateDispelHighlight() - end - end, - }, - hasBurnImpurities = { - order = 3, - type = "toggle", - name = string.format("Burn Impurities (%s)", LOCALIZED_CLASS_NAMES_MALE.PYROMANCER), - desc = "Grants Magic, Disease, and Bleed dispel.", - get = function() return CoA.db.profile.hasBurnImpurities end, - set = function(_, value) - CoA.db.profile.hasBurnImpurities = value - - if CoA.UpdateDispelHighlight then - CoA:UpdateDispelHighlight() - end - end, - }, - }, + if CoA.UpdateDispelHighlight then + CoA:UpdateDispelHighlight() + end + end, }, }, },