From 9679966f37d731521200f3f0a6c373511254fd8b Mon Sep 17 00:00:00 2001 From: Florian Berthold Date: Sun, 17 May 2026 06:08:34 +0200 Subject: [PATCH 01/23] coa: fix "Show curable only" debuff filter for CoA custom classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UnitAura "HARMFUL|RAID" only honours vanilla class dispel knowledge in the 3.3.5 client — CoA custom classes (Chronomancer, Monk/Templar, Prophet/Venomancer, etc.) were silently filtered to zero debuffs. Fix: in updateGroup(), detect CoA dispellers via C_Player:IsCustomClass() and store their per-class dispel set on group.coaRaidFilter instead of appending |RAID. scan() skips debuffs whose auraType is not in that set, preserving contiguous button layout with no gaps. Class → dispel types (sourced from coa-decursive): CHRONOMANCER Magic/Curse/Disease/Poison (Roll Back 804490) MONK Magic/Disease/Poison (Rebuke 525051) PROPHET Poison (Antivenom 800905; curse-dispel doesn't fire) PYROMANCER Disease/Poison RANGER Disease/Poison CULTIST Curse SONOFARUGAL Curse SPIRITMAGE Magic STARCALLER Magic WITCHHUNTER Curse --- ShadowedUnitFrames/modules/auras.lua | 42 ++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/ShadowedUnitFrames/modules/auras.lua b/ShadowedUnitFrames/modules/auras.lua index f0d98fa..8f95e0d 100644 --- a/ShadowedUnitFrames/modules/auras.lua +++ b/ShadowedUnitFrames/modules/auras.lua @@ -1,6 +1,34 @@ local Auras = {} local stealableColor = {r = 1, g = 1, b = 1} local playerUnits = {player = true, vehicle = true, pet = true} + +-- CoA: UnitAura "HARMFUL|RAID" only honours vanilla class dispels; custom classes +-- need a manual type check. playerCoaDispels is nil until first checked, false if +-- the player is not a dispelling CoA class, or a {type=true} set if they are. +local playerCoaDispels +local COA_CLASS_DISPELS = { + ["CHRONOMANCER"] = { Magic = true, Curse = true, Disease = true, Poison = true }, + ["MONK"] = { Magic = true, Disease = true, Poison = true }, + ["PROPHET"] = { Poison = true }, + ["PYROMANCER"] = { Disease = true, Poison = true }, + ["RANGER"] = { Disease = true, Poison = true }, + ["CULTIST"] = { Curse = true }, + ["SONOFARUGAL"] = { Curse = true }, + ["SPIRITMAGE"] = { Magic = true }, + ["STARCALLER"] = { Magic = true }, + ["WITCHHUNTER"] = { Curse = true }, +} +local function getCoaDispels() + if playerCoaDispels ~= nil then return playerCoaDispels end + local cp = _G.C_Player + if cp and cp.IsCustomClass and cp:IsCustomClass() then + local _, token = UnitClass("player") + playerCoaDispels = token and COA_CLASS_DISPELS[token] or false + else + playerCoaDispels = false + end + return playerCoaDispels +end local mainHand, offHand = {time = 0}, {time = 0} local tempEnchantScan ShadowUF:RegisterModule(Auras, "auras", ShadowUF.L["Auras"]) @@ -309,7 +337,17 @@ local function updateGroup(self, type, config, reverseConfig) -- This is a bit of an odd filter, when used with a HELPFUL filter, it will only return buffs you can cast on group members -- When used with HARMFUL it will only return debuffs you can cure if( config.raid ) then - group.filter = group.filter .. "|RAID" + local coa = group.type == "debuffs" and getCoaDispels() + if coa then + -- CoA custom class: keep filter as plain "HARMFUL" and let scan() + -- apply the per-type check via group.coaRaidFilter. + group.coaRaidFilter = coa + else + group.filter = group.filter .. "|RAID" + group.coaRaidFilter = nil + end + else + group.coaRaidFilter = nil end for id, button in pairs(group.buttons) do @@ -486,7 +524,7 @@ local function scan(parent, frame, type, config, filter) local name, rank, texture, count, auraType, duration, endTime, caster, isStealable = UnitAura(frame.parent.unit, index, filter) if( not name ) then break end - if( ( not config.player or playerUnits[caster] ) and ( not parent.whitelist[type] and not parent.blacklist[type] or parent.whitelist[type] and parent.whitelist[name] or parent.blacklist[type] and not parent.blacklist[name] ) ) then + if( ( not frame.coaRaidFilter or (auraType and frame.coaRaidFilter[auraType]) ) and ( not config.player or playerUnits[caster] ) and ( not parent.whitelist[type] and not parent.blacklist[type] or parent.whitelist[type] and parent.whitelist[name] or parent.blacklist[type] and not parent.blacklist[name] ) ) then -- Create any buttons we need frame.totalAuras = frame.totalAuras + 1 if( #(frame.buttons) < frame.totalAuras ) then From 48ebec985bf9f2c03751b316c9a5868f9a895cbd Mon Sep 17 00:00:00 2001 From: Florian Berthold Date: Sun, 17 May 2026 10:52:50 +0200 Subject: [PATCH 02/23] coa: fix CoA dispel detection timing in SUF aura scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous approach baked playerCoaDispels into group.coaRaidFilter during OnLayoutApplied, which can fire before PLAYER_LOGIN when C_Player isn't ready yet — Venomancer and other CoA classes got cached as false and never re-evaluated. Fix: move all CoA logic into scan() itself so it runs on every PLAYER_ENTERING_WORLD update. getCoaDispels() no longer caches when C_Player is unavailable (returns nil without storing), so the first successful in-world call populates it correctly. updateGroup() is restored to its original form — no group.coaRaidFilter. scan() intercepts filter=="HARMFUL|RAID", calls getCoaDispels(), and if a dispel set is found strips |RAID and applies the per-type check inline. --- ShadowedUnitFrames/modules/auras.lua | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/ShadowedUnitFrames/modules/auras.lua b/ShadowedUnitFrames/modules/auras.lua index 8f95e0d..4ce8168 100644 --- a/ShadowedUnitFrames/modules/auras.lua +++ b/ShadowedUnitFrames/modules/auras.lua @@ -21,7 +21,12 @@ local COA_CLASS_DISPELS = { local function getCoaDispels() if playerCoaDispels ~= nil then return playerCoaDispels end local cp = _G.C_Player - if cp and cp.IsCustomClass and cp:IsCustomClass() then + if not cp or not cp.IsCustomClass then + -- C_Player not yet initialised (called before PLAYER_LOGIN); don't + -- cache — retry on the next scan so we pick it up once in-world. + return nil + end + if cp:IsCustomClass() then local _, token = UnitClass("player") playerCoaDispels = token and COA_CLASS_DISPELS[token] or false else @@ -337,17 +342,7 @@ local function updateGroup(self, type, config, reverseConfig) -- This is a bit of an odd filter, when used with a HELPFUL filter, it will only return buffs you can cast on group members -- When used with HARMFUL it will only return debuffs you can cure if( config.raid ) then - local coa = group.type == "debuffs" and getCoaDispels() - if coa then - -- CoA custom class: keep filter as plain "HARMFUL" and let scan() - -- apply the per-type check via group.coaRaidFilter. - group.coaRaidFilter = coa - else - group.filter = group.filter .. "|RAID" - group.coaRaidFilter = nil - end - else - group.coaRaidFilter = nil + group.filter = group.filter .. "|RAID" end for id, button in pairs(group.buttons) do @@ -517,6 +512,13 @@ end local function scan(parent, frame, type, config, filter) if( frame.totalAuras >= frame.maxAuras or not config.enabled ) then return end + -- CoA: |RAID is not honoured for custom classes; override with manual type check. + local coaFilter + if filter == "HARMFUL|RAID" then + coaFilter = getCoaDispels() + if coaFilter then filter = "HARMFUL" end + end + local isFriendly = UnitIsFriend(frame.parent.unit, "player") local index = 0 while( true ) do @@ -524,7 +526,7 @@ local function scan(parent, frame, type, config, filter) local name, rank, texture, count, auraType, duration, endTime, caster, isStealable = UnitAura(frame.parent.unit, index, filter) if( not name ) then break end - if( ( not frame.coaRaidFilter or (auraType and frame.coaRaidFilter[auraType]) ) and ( not config.player or playerUnits[caster] ) and ( not parent.whitelist[type] and not parent.blacklist[type] or parent.whitelist[type] and parent.whitelist[name] or parent.blacklist[type] and not parent.blacklist[name] ) ) then + if( ( not coaFilter or (auraType and coaFilter[auraType]) ) and ( not config.player or playerUnits[caster] ) and ( not parent.whitelist[type] and not parent.blacklist[type] or parent.whitelist[type] and parent.whitelist[name] or parent.blacklist[type] and not parent.blacklist[name] ) ) then -- Create any buttons we need frame.totalAuras = frame.totalAuras + 1 if( #(frame.buttons) < frame.totalAuras ) then From da942631f0585db8ce7594fc10142cf04f799c80 Mon Sep 17 00:00:00 2001 From: Florian Berthold Date: Sat, 23 May 2026 13:42:19 +0200 Subject: [PATCH 03/23] chore(libs): sync Ace3 to coa-ace3 (WoWUIDev master @ 52e5f2c) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring every embedded Ace3 / CallbackHandler / LibStub copy in line with the canonical Exiles/coa-ace3 bundle so LibStub resolution is predictable across all Exiles forks regardless of which addons are enabled. Libraries updated in this fork: AceConfig-3.0 3 (2 → 3) AceConfigCmd-3.0 14 (12 → 14) AceConfigDialog-3.0 92 (48 → 92) AceConfigRegistry-3.0 22 (12 → 22) AceDB-3.0 33 (21 → 33) AceDBOptions-3.0 15 (12 → 15) AceGUI-3.0 41 (33 → 41) CallbackHandler-1.0 8 (5 → 8) LibStub 2 --- .../libs/AceConfig-3.0/AceConfig-3.0.lua | 115 +- .../libs/AceConfig-3.0/AceConfig-3.0.xml | 14 +- .../AceConfigCmd-3.0/AceConfigCmd-3.0.lua | 1574 +++---- .../AceConfigCmd-3.0/AceConfigCmd-3.0.xml | 6 +- .../AceConfigDialog-3.0.lua | 3937 +++++++++-------- .../AceConfigDialog-3.0.xml | 6 +- .../AceConfigRegistry-3.0.lua | 719 +-- .../AceConfigRegistry-3.0.xml | 6 +- .../AceDBOptions-3.0/AceDBOptions-3.0.lua | 876 ++-- .../AceDBOptions-3.0/AceDBOptions-3.0.xml | 6 +- .../libs/AceGUI-3.0/AceGUI-3.0.lua | 1825 ++++---- .../libs/AceGUI-3.0/AceGUI-3.0.xml | 56 +- .../AceGUIContainer-BlizOptionsGroup.lua | 276 +- .../widgets/AceGUIContainer-DropDownGroup.lua | 335 +- .../widgets/AceGUIContainer-Frame.lua | 606 +-- .../widgets/AceGUIContainer-InlineGroup.lua | 241 +- .../widgets/AceGUIContainer-ScrollFrame.lua | 456 +- .../widgets/AceGUIContainer-SimpleGroup.lua | 168 +- .../widgets/AceGUIContainer-TabGroup.lua | 924 ++-- .../widgets/AceGUIContainer-TreeGroup.lua | 1465 +++--- .../widgets/AceGUIContainer-Window.lua | 667 +-- .../widgets/AceGUIWidget-Button.lua | 195 +- .../widgets/AceGUIWidget-CheckBox.lua | 581 +-- .../widgets/AceGUIWidget-ColorPicker.lua | 416 +- .../widgets/AceGUIWidget-DropDown-Items.lua | 936 ++-- .../widgets/AceGUIWidget-DropDown.lua | 1439 +++--- .../widgets/AceGUIWidget-EditBox.lua | 490 +- .../widgets/AceGUIWidget-Heading.lua | 156 +- .../AceGUI-3.0/widgets/AceGUIWidget-Icon.lua | 284 +- .../widgets/AceGUIWidget-InteractiveLabel.lua | 195 +- .../widgets/AceGUIWidget-Keybinding.lua | 481 +- .../AceGUI-3.0/widgets/AceGUIWidget-Label.lua | 341 +- .../widgets/AceGUIWidget-MultiLineEditBox.lua | 655 +-- .../widgets/AceGUIWidget-Slider.lua | 562 +-- .../libs/AceDB-3.0/AceDB-3.0.lua | 1525 ++++--- .../libs/AceDB-3.0/AceDB-3.0.xml | 6 +- .../CallbackHandler-1.0.lua | 441 +- .../CallbackHandler-1.0.xml | 6 +- .../CallbackHandler-1.0/LibStub/LibStub.lua | 60 +- 39 files changed, 11911 insertions(+), 11136 deletions(-) diff --git a/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.lua b/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.lua index b66022d..1c9454a 100644 --- a/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.lua +++ b/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.lua @@ -1,57 +1,58 @@ ---- AceConfig-3.0 wrapper library. --- Provides an API to register an options table with the config registry, --- as well as associate it with a slash command. --- @class file --- @name AceConfig-3.0 --- @release $Id: AceConfig-3.0.lua 877 2009-11-02 15:56:50Z nevcairiel $ - ---[[ -AceConfig-3.0 - -Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole. - -]] - -local MAJOR, MINOR = "AceConfig-3.0", 2 -local AceConfig = LibStub:NewLibrary(MAJOR, MINOR) - -if not AceConfig then return end - -local cfgreg = LibStub("AceConfigRegistry-3.0") -local cfgcmd = LibStub("AceConfigCmd-3.0") -local cfgdlg = LibStub("AceConfigDialog-3.0") ---TODO: local cfgdrp = LibStub("AceConfigDropdown-3.0") - --- Lua APIs -local pcall, error, type, pairs = pcall, error, type, pairs - --- ------------------------------------------------------------------- --- :RegisterOptionsTable(appName, options, slashcmd, persist) --- --- - appName - (string) application name --- - options - table or function ref, see AceConfigRegistry --- - slashcmd - slash command (string) or table with commands, or nil to NOT create a slash command - ---- Register a option table with the AceConfig registry. --- You can supply a slash command (or a table of slash commands) to register with AceConfigCmd directly. --- @paramsig appName, options [, slashcmd] --- @param appName The application name for the config table. --- @param options The option table (or a function to generate one on demand) --- @param slashcmd A slash command to register for the option table, or a table of slash commands. --- @usage --- local AceConfig = LibStub("AceConfig-3.0") --- AceConfig:RegisterOptionsTable("MyAddon", myOptions, {"/myslash", "/my"}) -function AceConfig:RegisterOptionsTable(appName, options, slashcmd) - local ok,msg = pcall(cfgreg.RegisterOptionsTable, self, appName, options) - if not ok then error(msg, 2) end - - if slashcmd then - if type(slashcmd) == "table" then - for _,cmd in pairs(slashcmd) do - cfgcmd:CreateChatCommand(cmd, appName) - end - else - cfgcmd:CreateChatCommand(slashcmd, appName) - end - end -end +--- AceConfig-3.0 wrapper library. +-- Provides an API to register an options table with the config registry, +-- as well as associate it with a slash command. +-- @class file +-- @name AceConfig-3.0 +-- @release $Id$ + +--[[ +AceConfig-3.0 + +Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole. + +]] + +local cfgreg = LibStub("AceConfigRegistry-3.0") +local cfgcmd = LibStub("AceConfigCmd-3.0") + +local MAJOR, MINOR = "AceConfig-3.0", 3 +local AceConfig = LibStub:NewLibrary(MAJOR, MINOR) + +if not AceConfig then return end + +--TODO: local cfgdlg = LibStub("AceConfigDialog-3.0", true) +--TODO: local cfgdrp = LibStub("AceConfigDropdown-3.0", true) + +-- Lua APIs +local pcall, error, type, pairs = pcall, error, type, pairs + +-- ------------------------------------------------------------------- +-- :RegisterOptionsTable(appName, options, slashcmd) +-- +-- - appName - (string) application name +-- - options - table or function ref, see AceConfigRegistry +-- - slashcmd - slash command (string) or table with commands, or nil to NOT create a slash command + +--- Register a option table with the AceConfig registry. +-- You can supply a slash command (or a table of slash commands) to register with AceConfigCmd directly. +-- @paramsig appName, options [, slashcmd] +-- @param appName The application name for the config table. +-- @param options The option table (or a function to generate one on demand). http://www.wowace.com/addons/ace3/pages/ace-config-3-0-options-tables/ +-- @param slashcmd A slash command to register for the option table, or a table of slash commands. +-- @usage +-- local AceConfig = LibStub("AceConfig-3.0") +-- AceConfig:RegisterOptionsTable("MyAddon", myOptions, {"/myslash", "/my"}) +function AceConfig:RegisterOptionsTable(appName, options, slashcmd) + local ok,msg = pcall(cfgreg.RegisterOptionsTable, self, appName, options) + if not ok then error(msg, 2) end + + if slashcmd then + if type(slashcmd) == "table" then + for _,cmd in pairs(slashcmd) do + cfgcmd:CreateChatCommand(cmd, appName) + end + else + cfgcmd:CreateChatCommand(slashcmd, appName) + end + end +end diff --git a/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.xml b/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.xml index d9eb96a..87972ad 100644 --- a/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.xml +++ b/ShadowedUF_Options/libs/AceConfig-3.0/AceConfig-3.0.xml @@ -1,8 +1,8 @@ - - - - - -