2b97a68317
Folds three previously-separate Lua addons into one for guild-member use:
- ascension-char-exporter (per-character JSON/Wiki.js Markdown via /ascx)
- CoA_SkillExporter (skills/dispels/passives catalog via /skilldump)
- CoA_TalentExporter (talent-tree catalog via /talentdumpall)
The two CoA catalog dumpers were ~90% identical entry-walkers. Pulled the
shared C_CharacterAdvancement.GetAllEntries() loop into Catalogs/Common.lua
and have Skills.lua / Talents.lua register collectors that share a single
scan pass (so /coae catalog all walks the entry list once, not twice).
Per-character collectors (Talents, Gear, Enchants, MysticScrolls,
MysticScrollProbe) and the AtlasLootAscension-derived ScrollCatalog data
are kept verbatim, just rebranded.
Slash interface:
/coae export {all|talents|gear|enchants|mdgear|mdenchants|md}
/coae catalog {all|skills|talents|dispels [class]|passives [class]|status}
/coae scrolls {scan|export|reset|status}
/coae sv on|off | debug | help
Aliases: /coaexp, /ascx, /asxc, plus legacy /skilldump /talentdumpall
/dispels /passives that map to the catalog subcommands.
SavedVariables: CoaExporterSaved, CoaExporterConfig, CoaExporterScrollCache,
CoaExporterCatalog (skills/dispels/levelPassives/talents/_meta).
207 lines
7.4 KiB
Lua
207 lines
7.4 KiB
Lua
-- CoaExporter / Catalogs / Common.lua
|
|
--
|
|
-- Shared engine for catalog dumps. Walks
|
|
-- C_CharacterAdvancement.GetAllEntries() in 100-entry batches via OnUpdate
|
|
-- and dispatches each entry to every registered collector. Used by
|
|
-- Catalogs/Skills.lua and Catalogs/Talents.lua so we only walk the (large)
|
|
-- entry list once.
|
|
|
|
CoaExporter = _G.CoaExporter or {}
|
|
_G.CoaExporter = CoaExporter
|
|
local AE = CoaExporter
|
|
|
|
AE.Catalog = AE.Catalog or {}
|
|
local C = AE.Catalog
|
|
|
|
C._collectors = C._collectors or {}
|
|
C._isRunning = false
|
|
|
|
-- Shared SavedVariable for catalog output. Each collector writes its own
|
|
-- top-level key (skills, dispels, levelPassives, talents, ...).
|
|
CoaExporterCatalog = CoaExporterCatalog or {}
|
|
|
|
local TalentScanner
|
|
local function GetCatalogTooltip(spellId)
|
|
if not TalentScanner then
|
|
TalentScanner = CreateFrame("GameTooltip", "CoaExpCatalogScanner", nil, "GameTooltipTemplate")
|
|
end
|
|
local scanner = TalentScanner
|
|
scanner:SetOwner(UIParent, "ANCHOR_NONE")
|
|
scanner:ClearLines()
|
|
local ok = pcall(function() scanner:SetHyperlink("spell:" .. spellId) end)
|
|
if not ok or scanner:NumLines() == 0 then return "No description" end
|
|
local text = ""
|
|
for i = 1, scanner:NumLines() do
|
|
local line = _G["CoaExpCatalogScannerTextLeft"..i]
|
|
if line then
|
|
local t = line:GetText()
|
|
if t and t ~= "" then text = text .. t .. "\n" end
|
|
end
|
|
end
|
|
return text
|
|
end
|
|
|
|
C.GetCatalogTooltip = GetCatalogTooltip
|
|
|
|
local function log(msg)
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExp catalog: " .. tostring(msg))
|
|
end
|
|
C._log = log
|
|
|
|
-- Collectors register a handler that gets called for each valid entry.
|
|
-- spec = {
|
|
-- name = "skills", -- identifier (also used as SV key)
|
|
-- onStart = function(ctx) end, -- reset state
|
|
-- onEntry = function(ctx, entry, info) end,
|
|
-- onFinish = function(ctx) end, -- write into CoaExporterCatalog[name]
|
|
-- }
|
|
function C.Register(spec)
|
|
assert(spec and spec.name, "catalog collector needs a name")
|
|
C._collectors[spec.name] = spec
|
|
end
|
|
|
|
local function activeCollectors(filter)
|
|
local out = {}
|
|
if filter == nil or filter == "all" then
|
|
for _, c in pairs(C._collectors) do out[#out+1] = c end
|
|
elseif type(filter) == "string" then
|
|
local one = C._collectors[filter]
|
|
if one then out[#out+1] = one end
|
|
end
|
|
table.sort(out, function(a, b) return (a.name or "") < (b.name or "") end)
|
|
return out
|
|
end
|
|
|
|
-- One-pass scan. filter: "all", "skills", "talents", or nil.
|
|
function C.Run(filter, callback)
|
|
if C._isRunning then
|
|
log("already running, ignoring")
|
|
return false
|
|
end
|
|
if not C_CharacterAdvancement or not C_CharacterAdvancement.GetAllEntries then
|
|
log("ERROR: C_CharacterAdvancement.GetAllEntries missing - are you on Ascension 3.3.5?")
|
|
return false
|
|
end
|
|
|
|
local collectors = activeCollectors(filter)
|
|
if #collectors == 0 then
|
|
log("no collectors matched filter: " .. tostring(filter))
|
|
return false
|
|
end
|
|
|
|
local ctx = { startedAt = date(), filter = filter or "all" }
|
|
for _, col in ipairs(collectors) do
|
|
if col.onStart then col.onStart(ctx) end
|
|
end
|
|
|
|
local allEntries = C_CharacterAdvancement.GetAllEntries()
|
|
local entryKeys = {}
|
|
for k in pairs(allEntries) do table.insert(entryKeys, k) end
|
|
local total = #entryKeys
|
|
local idx = 1
|
|
|
|
log(string.format("scanning %d entries for [%s]", total, ctx.filter))
|
|
C._isRunning = true
|
|
|
|
local frame = CreateFrame("Frame")
|
|
frame:SetScript("OnUpdate", function(self)
|
|
if not C._isRunning then
|
|
self:SetScript("OnUpdate", nil)
|
|
return
|
|
end
|
|
|
|
local batch = 100
|
|
local processed = 0
|
|
while processed < batch and idx <= total do
|
|
local k = entryKeys[idx]
|
|
local v = allEntries[k]
|
|
|
|
local entry
|
|
if type(v) == "table" and v.Class then
|
|
entry = v
|
|
else
|
|
local id = tonumber(k) or (type(v) == "table" and v.ID)
|
|
if id then entry = C_CharacterAdvancement.GetEntryByInternalID(id) end
|
|
end
|
|
|
|
if entry and entry.Class then
|
|
-- Build a normalized info bag every collector can use.
|
|
local spellId = 0
|
|
local allSpells = {}
|
|
if entry.Spells then
|
|
if type(entry.Spells) == "table" then
|
|
spellId = tonumber(entry.Spells[1]) or 0
|
|
for _, sp in ipairs(entry.Spells) do
|
|
table.insert(allSpells, tonumber(sp) or 0)
|
|
end
|
|
else
|
|
spellId = tonumber(entry.Spells) or 0
|
|
table.insert(allSpells, spellId)
|
|
end
|
|
end
|
|
|
|
local name, _, icon
|
|
if spellId > 0 then name, _, icon = GetSpellInfo(spellId) end
|
|
local tooltip = GetCatalogTooltip(spellId > 0 and spellId or entry.ID)
|
|
if not name or name == "" then
|
|
name = tooltip:match("^([^\n]+)") or ("ID:" .. tostring(entry.ID))
|
|
end
|
|
|
|
local tierVal = tonumber(entry.PositionY) or tonumber(entry.Row) or tonumber(entry.y) or 0
|
|
local colVal = tonumber(entry.PositionX) or tonumber(entry.Column) or tonumber(entry.x) or tonumber(entry.Col) or 0
|
|
|
|
-- Garbage filter: tier=0/col=0/no-icon AND no-spell entries
|
|
local valid = true
|
|
if tierVal == 0 and colVal == 0 and (not icon or icon == "") then valid = false end
|
|
if spellId == 0 and (not icon or icon == "") then valid = false end
|
|
|
|
if valid then
|
|
local info = {
|
|
cls = tostring(entry.Class),
|
|
tab = tostring(entry.Tab or "General"),
|
|
entryType = tostring(entry.Type or "Talent"),
|
|
name = name,
|
|
spellId = spellId,
|
|
allSpells = allSpells,
|
|
icon = icon or "",
|
|
tier = tierVal,
|
|
col = colVal,
|
|
tooltip = tooltip,
|
|
}
|
|
for _, col in ipairs(collectors) do
|
|
if col.onEntry then col.onEntry(ctx, entry, info) end
|
|
end
|
|
end
|
|
end
|
|
|
|
idx = idx + 1
|
|
processed = processed + 1
|
|
end
|
|
|
|
if idx > total then
|
|
C._isRunning = false
|
|
self:SetScript("OnUpdate", nil)
|
|
|
|
for _, col in ipairs(collectors) do
|
|
if col.onFinish then col.onFinish(ctx) end
|
|
end
|
|
CoaExporterCatalog._meta = {
|
|
lastScanAt = ctx.startedAt,
|
|
filter = ctx.filter,
|
|
totalEntries = total,
|
|
}
|
|
log(string.format("done. %d entries processed; /reload to flush SavedVariables.", total))
|
|
if callback then callback(ctx) end
|
|
else
|
|
local pct = math.floor((idx / total) * 100)
|
|
if pct > 0 and pct % 10 == 0 and (ctx._lastPct or -1) ~= pct then
|
|
ctx._lastPct = pct
|
|
log(string.format(" %d%% (%d/%d)", pct, idx, total))
|
|
end
|
|
end
|
|
end)
|
|
return true
|
|
end
|
|
|
|
AE._loadedCatalogCommon = true
|