Initial CoaExporter: merge AscensionExporter + CoA skill/talent dumpers
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).
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
-- 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
|
||||
Reference in New Issue
Block a user