Files
ElvUI_CoA/Modules/DispelHighlight.lua
T
Narcasung d2005c4725 Detect the dispel talents instead of asking for them
The three talents that grant an extra dispel type were gated behind
manual checkboxes because the talent state looked unreadable on this
server. It isn't: C_CharacterAdvancement.GetTalentRankByID reports a
live rank for the player, entry IDs are shared across every class tree
so querying a foreign one just reports rank 0, and
CHARACTER_ADVANCEMENT_UPDATE_ENTRIES_RESULT fires when a build is
confirmed.

Key the talents to their entry IDs and read them instead. The lookup is
cached, since PostUpdate_DebuffHighlight runs once per aura per frame,
and refreshed on build confirmation plus the existing delayed refresh
after login. Every call is guarded, so a missing API reads as "talent
not taken" and behaves as it did before.

The three checkboxes are gone, replaced by a single Detect Talents
switch that turns the whole lookup off.
2026-08-18 15:41:13 +02:00

126 lines
4.8 KiB
Lua

local E, L, V, P, G = unpack(ElvUI)
local UF = E:GetModule("UnitFrames")
local CoA = E:GetModule("CoA")
-- Base dispel types each custom class can remove, independent of talents.
-- CHRONOMANCER is a special case: its dispel removes the last debuff applied
-- to the target regardless of type, so it's never filtered out here -- we
-- can only approximate this as "treat every typed debuff as dispellable",
-- since the underlying oUF scan only ever surfaces typed debuffs anyway.
local CLASS_DISPEL_TYPES = {
CHRONOMANCER = true,
SUNCLERIC = {Magic = true, Poison = true, Disease = true},
STARCALLER = {Poison = true, Disease = true},
PROPHET = {Poison = true},
WITCHDOCTOR = {Curse = true},
CULTIST = {Magic = true},
PYROMANCER = {},
}
-- 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 = {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]
if baseTypes == true then return true end
if baseTypes and baseTypes[debuffType] then return true end
local talent = TALENT_DISPEL_TYPES[class]
if talent and hasTalentDispel and CoA.db.profile.dispelHighlightDetectTalents and talent.types[debuffType] then
return true
end
return false
end
local function SuppressHighlight(object)
if object.DebuffHighlightBackdrop and object.DBHGlow then
object.DBHGlow:Hide()
elseif object.DebuffHighlightUseTexture then
object.DebuffHighlight:SetTexture(nil)
else
object.DebuffHighlight:SetVertexColor(0, 0, 0, 0)
end
end
local origPostUpdate = UF.PostUpdate_DebuffHighlight
local function DispelAwarePostUpdate(dbh, object, debuffType, texture, wasFiltered, style, color)
origPostUpdate(dbh, object, debuffType, texture, wasFiltered, style, color)
if CoA.db.profile.dispelHighlightOnlyMine and debuffType and not wasFiltered and not CoA:CanDispel(debuffType) then
SuppressHighlight(object)
end
end
UF.PostUpdate_DebuffHighlight = DispelAwarePostUpdate
hooksecurefunc(UF, "Configure_DebuffHighlight", function(_, frame)
local dbh = frame.DebuffHighlight
if dbh then
dbh.PostUpdate = UF.PostUpdate_DebuffHighlight
end
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, 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