Files
coa-exporter/CoaExporter/Catalogs/Icons.lua
T

207 lines
9.1 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- CoaExporter / Catalogs / Icons.lua
--
-- Captures the realm-server-resolved icon paths the MoA UI actually
-- renders, which neither CharacterAdvancementData.json nor any client
-- DBC carries:
--
-- 1. Per-entry icon (`entry.Icon`) — the talent-grid node icon. The
-- Skills/Talents collectors above use GetSpellInfo's icon, which
-- is empty for CoA-custom entries that don't back to a real spell
-- (every spec-root Talent like ID 31202 "Fortitude Venomancer" =
-- the Venomancer beetle node, plus ~3800 other CoA-custom rows).
-- `entry.Icon` is what the realm gives the UI; we capture both.
--
-- 2. Per-(class, spec) sidebar tab icon — the small icon on each
-- vertical spec tab in the Character Advancement window, fetched
-- via `C_ClassInfo.GetSpecInfo(classFile, specFile).SpecFilename`.
-- Same source the UI itself uses (see Ascension's
-- Ascension_CharacterAdvancement/Templates/CAGate.lua:179).
--
-- Output (in CoaExporterCatalog):
-- iconByEntryId = { [entryId] = "icon-name" }
-- specInfoByFileString = { [<FILE_STRING>] = {
-- [<TabName>] = { Name=..., SpecFilename=... } } }
--
-- Class-keyspace: every spec is keyed by the WoW classFile (UPPERCASE,
-- the second return of UnitClass) like "PROPHET", "MONK", "FLESHWARDEN" —
-- the same key C_ClassInfo.GetSpecInfo wants. Avoids the entry.Class →
-- file_string alias mess (Venomancer → PROPHET, Templar → MONK, …) by
-- never recording entry.Class as a primary key.
--
-- For sidebar icons we also brute-force `KNOWN_CLASS_FILES × KNOWN_TABS`
-- regardless of what the entry-walk found this session — Ascension's
-- C_CharacterAdvancement.GetAllEntries() is class-scoped (only returns
-- entries the active character can reach), so a Venomancer alt may not
-- see Templar entries and vice-versa. C_ClassInfo.GetSpecInfo is
-- keyed (class, spec) directly though, so we can ask about every CoA
-- combination without the entry-walk needing to have surfaced it.
--
-- iconByEntryId is still entry-walk gated (we need entry.ID + entry.Icon),
-- so per-talent icons accumulate across runs as you rotate characters.
CoaExporter = _G.CoaExporter or {}
_G.CoaExporter = CoaExporter
local AE = CoaExporter
local C = AE.Catalog
-- All file_strings in coa-db `class.file_string`. Vanilla 10 + DK + 21
-- Ascension custom. The "HERO" pseudo-class is intentionally omitted —
-- it's not a real class and GetSpecInfo wouldn't have data for it.
local KNOWN_CLASS_FILES = {
-- vanilla
"WARRIOR", "PALADIN", "HUNTER", "ROGUE", "PRIEST",
"DEATHKNIGHT", "SHAMAN", "MAGE", "WARLOCK", "DRUID",
-- Ascension CoA custom (class.file_string)
"BARBARIAN", "WITCHDOCTOR", "DEMONHUNTER", "WITCHHUNTER", "STORMBRINGER",
"FLESHWARDEN", "GUARDIAN", "MONK", "SONOFARUGAL", "RANGER",
"CHRONOMANCER", "NECROMANCER", "PYROMANCER", "CULTIST", "STARCALLER",
"SUNCLERIC", "TINKER", "PROPHET", "REAPER", "WILDWALKER", "SPIRITMAGE",
}
-- Every internal Tab name seen in CharacterAdvancementTabTypes.dbc
-- (94 unique values). Hardcoding lets us probe specs that the current
-- character's GetAllEntries doesn't surface — Venomancer's
-- Fortitude/Stalking/Vizier/Venom in particular.
local KNOWN_TABS = {
"Affliction", "Ancestry", "Animation", "Arcane", "Archery", "Arms",
"Assassination", "AstralWarfare", "Balance", "BeastMastery",
"Blessings", "Blood", "Boltslinger", "Brewing", "Brutality", "Bulwark",
"Combat", "Corruption", "Darkness", "Death", "Defiance", "Demonology",
"Destruction", "Discipline", "Displacement", "Domination", "Draconic",
"Duality", "Dueling", "Elemental", "Enhancement", "Felblood", "Feral",
"Ferocity", "Fighting", "Fire", "Firearms", "Fleshweaver", "Fortitude",
"Frost", "Fury", "Geomancy", "Gifts", "Gladiator", "Godblade",
"Hellfire", "Holy", "Hydromancy", "Incineration", "Influence",
"Inquisition", "Inspiration", "Invention", "Life", "Lightning",
"Marksmanship", "Mechanics", "Moonbow", "MountainKing", "Packleader",
"Piety", "Primal", "Protection", "Reaping", "Restoration",
"Retribution", "Riftblade", "Rime", "Runes", "Runic", "Seraphim",
"Shadow", "Shadowhunting", "Slaying", "Soul", "Stalking", "Subtlety",
"Survival", "Tactics", "Tides", "Time", "Unholy", "Valkyr", "Venom",
"Vizier", "Voodoo", "War", "Wind", "WitchKnight",
}
-- Map entry.Class (the in-game advancement string) -> classFile, for the
-- entry-walk stream. Mirrors load_moa_talents.py's CLASS_NAME_TO_FILE_STRING.
-- Used only to bucket entry icons; the spec-info brute force iterates
-- KNOWN_CLASS_FILES directly so this only needs to cover what we actually
-- see in entry.Class strings.
local CLASS_NAME_TO_FILE_STRING = {
Warrior = "WARRIOR",
Paladin = "PALADIN",
Hunter = "HUNTER",
Rogue = "ROGUE",
Priest = "PRIEST",
DeathKnight = "DEATHKNIGHT",
Shaman = "SHAMAN",
Mage = "MAGE",
Warlock = "WARLOCK",
Druid = "DRUID",
Barbarian = "BARBARIAN",
WitchDoctor = "WITCHDOCTOR",
DemonHunter = "DEMONHUNTER",
Felsworn = "DEMONHUNTER",
WitchHunter = "WITCHHUNTER",
Stormbringer = "STORMBRINGER",
KnightOfXoroth = "FLESHWARDEN",
Fleshwarden = "FLESHWARDEN",
Guardian = "GUARDIAN",
Monk = "MONK",
Templar = "MONK",
SonOfArugal = "SONOFARUGAL",
Bloodmage = "SONOFARUGAL",
Ranger = "RANGER",
Chronomancer = "CHRONOMANCER",
Necromancer = "NECROMANCER",
Pyromancer = "PYROMANCER",
Cultist = "CULTIST",
Starcaller = "STARCALLER",
SunCleric = "SUNCLERIC",
Tinker = "TINKER",
Prophet = "PROPHET",
Venomancer = "PROPHET",
Reaper = "REAPER",
Wildwalker = "WILDWALKER",
Primalist = "WILDWALKER",
SpiritMage = "SPIRITMAGE",
Runemaster = "SPIRITMAGE",
}
local function tablen(t)
if not t then return 0 end
local n = 0
for _ in pairs(t) do n = n + 1 end
return n
end
C.Register({
name = "icons",
onStart = function(_)
-- Don't wipe — accumulate across characters/runs so the user can
-- /coae catalog icons after each Reborn class swap and end up with
-- a complete catalog without re-collecting what was already done.
CoaExporterCatalog.iconByEntryId = CoaExporterCatalog.iconByEntryId or {}
CoaExporterCatalog.specInfoByFileString = CoaExporterCatalog.specInfoByFileString or {}
-- Drop the legacy field if it exists from earlier runs — single
-- source of truth keeps the loader simple.
CoaExporterCatalog.specInfoByClassSpec = nil
end,
onEntry = function(_, entry, info)
local id = tonumber(entry.ID) or entry.ID
local serverIcon = (type(entry.Icon) == "string" and entry.Icon ~= "") and entry.Icon or nil
local fallback = (info.icon and info.icon ~= "") and info.icon or nil
local pick = serverIcon or fallback
if id and pick then
CoaExporterCatalog.iconByEntryId[id] = pick
end
end,
onFinish = function(_)
local resolved, attempted = 0, 0
if C_ClassInfo and C_ClassInfo.GetSpecInfo then
for _, classFile in ipairs(KNOWN_CLASS_FILES) do
for _, tab in ipairs(KNOWN_TABS) do
attempted = attempted + 1
local upperSpec = string.upper(tab)
local ok, r = pcall(C_ClassInfo.GetSpecInfo, classFile, upperSpec)
if ok and r and r.SpecFilename and r.SpecFilename ~= "" then
CoaExporterCatalog.specInfoByFileString[classFile] =
CoaExporterCatalog.specInfoByFileString[classFile] or {}
CoaExporterCatalog.specInfoByFileString[classFile][tab] = {
Name = r.Name or tab,
SpecFilename = r.SpecFilename,
}
resolved = resolved + 1
end
end
end
end
local totalIcons = tablen(CoaExporterCatalog.iconByEntryId)
local totalClasses, totalSpecs = 0, 0
for _, tabs in pairs(CoaExporterCatalog.specInfoByFileString) do
totalClasses = totalClasses + 1
totalSpecs = totalSpecs + tablen(tabs)
end
if C_ClassInfo and C_ClassInfo.GetSpecInfo then
C._log(string.format(
"icons: brute-forced %d (class,spec) probes, %d resolved this run; "
.. "cumulative: %d entry icons, %d specs across %d classes",
attempted, resolved, totalIcons, totalSpecs, totalClasses))
else
C._log(string.format(
"icons: %d entry icons cumulatively (C_ClassInfo.GetSpecInfo unavailable - sidebar icons skipped)",
totalIcons))
end
end,
})
-- Exposed for the loader (load_moa_icons.py) to keep the entry.Class
-- alias table in lockstep with this addon. Not used in-game.
AE._iconsClassNameToFileString = CLASS_NAME_TO_FILE_STRING
AE._loadedCatalogIcons = true