feat(TankMode): CoA custom-class role detection via active spec
release / release (push) Successful in 3s

Add CoAClassSpecData.lua (copied from coa-db/data) and wire
GetPlayerCoARole() into TankMode so CoA tokens resolve TANK/HEALER/DAMAGER
from their active spec instead of defaulting to DAMAGER.

- CoAClassSpecData.lua: defines CoAClassSpec keyed by UnitClass token,
  loaded in .toc before Core.xml/Modules.xml
- GetPlayerCoARole(): pcall-guarded helper that calls
  SpecializationUtil.GetActiveSpecialization() (1-based index) to look up
  the current spec in CoAClassSpec[token].specs; TANK > HEALER > DAMAGER
  precedence when a spec carries multiple roles; returns nil for vanilla
  classes so the existing IsTank/IsHealer path is unchanged
- mod:Update(): try GetPlayerCoARole() first; fall through to vanilla
  logic only when nil (vanilla class or unresolvable spec)
- Re-evaluation on spec change already covered: PLAYER_TALENT_UPDATE
  fires on both spec and talent changes, driving mod:Update()

luac -p: TankMode.lua OK, CoAClassSpecData.lua OK
This commit is contained in:
2026-05-30 01:28:59 +02:00
parent 7b124be04b
commit 68701d7d42
3 changed files with 212 additions and 2 deletions
+64 -2
View File
@@ -106,6 +106,62 @@ do
end
end
end
--------------------------------------------------------- CoA role detection --
-- Returns "TANK", "HEALER", or "DAMAGER" for the player's current spec when
-- the player is a CoA custom class. Returns nil if the class is not in
-- CoAClassSpec (vanilla path) or if the active spec cannot be resolved (caller
-- falls back to existing logic).
--
-- API rationale: SpecializationUtil.GetActiveSpecialization() is the
-- confirmed per-spec API on the CoA/Ascension 3.3.5a client. It returns
-- a 1-based integer matching the in-game tab order, which is the same order
-- as CoAClassSpec[token].specs. This is the same API used by coa-clique
-- (Clique.lua:700) and coa-weakauras (WeakAuras.lua:1608) on this client.
-- We guard with pcall so a missing or broken API degrades silently to nil.
local function GetPlayerCoARole()
if not CoAClassSpec then return nil end
local token = select(2, UnitClass("player"))
if not token then return nil end
local classData = CoAClassSpec[token]
if not classData then return nil end -- vanilla class; use vanilla path
-- Resolve the active spec index via SpecializationUtil.
local specIndex
local ok, result = pcall(function()
if SpecializationUtil and SpecializationUtil.GetActiveSpecialization then
return SpecializationUtil.GetActiveSpecialization()
end
return nil
end)
if ok and result and result >= 1 then
specIndex = result
end
local specData
if specIndex and classData.specs[specIndex] then
specData = classData.specs[specIndex]
end
if not specData then return nil end
-- Map CoAClassSpec roles → tank-mode role.
-- TANK and HEALER take precedence over DAMAGER roles when both present.
local roles = specData.roles
if not roles then return nil end
local isTank, isHealer = false, false
for _, r in ipairs(roles) do
if r == "TANK" then isTank = true end
if r == "HEALER" then isHealer = true end
end
if isTank then return "TANK" end
if isHealer then return "HEALER" end
return "DAMAGER"
end
--------------------------------------------------------- tank mode functions --
do
local function getTalentpointsSpent(spellID)
@@ -162,10 +218,16 @@ do
function mod:Update()
if profile_tankmode.enabled == 1 then
-- smart - judge by spec
local spec = GetActiveTalentGroup()
local role
if class == "WARRIOR" and GetShapeshiftForm() ~= 2 then
-- For CoA custom classes, derive role from the active spec via
-- CoAClassSpec. GetPlayerCoARole() returns nil when the class is
-- vanilla (not in CoAClassSpec) or when the spec cannot be resolved,
-- so vanilla classes always fall through to the IsTank/IsHealer path.
local coaRole = GetPlayerCoARole()
if coaRole then
role = coaRole
elseif class == "WARRIOR" and GetShapeshiftForm() ~= 2 then
-- no tank for gladiator stance
role = nil
elseif IsTank() then