Files
coa-exporter/CoaExporter/Collectors/MysticScrolls.lua
T
florian.berthold 2b97a68317 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).
2026-05-07 10:43:16 +02:00

181 lines
5.4 KiB
Lua

-- CoaExporter / Collectors / MysticScrolls.lua
--
-- Async tooltip scraper for all known mystic enchant scrolls.
-- Iterates AE.ScrollCatalog (generated from AtlasLootAscension) and dumps
-- tooltip text + the spell taught by each scroll.
--
-- Usage: /coae scrolls scan → start scan (one-time; dumps progress to chat)
-- /coae scrolls export → print accumulated JSON of everything scanned
-- /coae scrolls reset → clear cache
--
-- Design notes:
-- - WoW lazy-loads item data. First GetItemInfo() for an unseen item
-- returns nil and triggers a server query. We retry with delay.
-- - Results accumulate in CoaExporterScrollCache SavedVariable so a
-- single run across sessions builds up the full DB.
-- - A "scan" pass fires a batch of GetItemInfo requests, then waits,
-- then re-scans and records whatever resolved. Unresolved entries are
-- retried on the next scan.
CoaExporter = _G.CoaExporter or {}
_G.CoaExporter = CoaExporter
local AE = CoaExporter
CoaExporterScrollCache = CoaExporterScrollCache or {
entries = {},
meta = {
lastScanAt = nil,
totalCatalog = 0,
resolved = 0,
unresolved = {},
},
}
local function EnsureScanner()
if not AE._scrollScanner then
AE._scrollScanner = CreateFrame("GameTooltip", "CoaExpScrollsTT", nil, "GameTooltipTemplate")
AE._scrollScanner:SetOwner(WorldFrame, "ANCHOR_NONE")
end
return AE._scrollScanner
end
local function TipLines(tt)
local out = {}
for i = 1, tt:NumLines() do
local left = _G[tt:GetName() .. "TextLeft" .. i]
if left then
local t = left:GetText()
if t and t ~= "" then out[#out+1] = t end
end
end
return out
end
local function ScanItemTooltip(itemID)
local tt = EnsureScanner()
tt:ClearLines()
tt:SetHyperlink("item:" .. itemID)
return TipLines(tt)
end
local function FlatCatalog()
local out = {}
if not AE.ScrollCatalog then return out end
for class, list in pairs(AE.ScrollCatalog) do
for _, e in ipairs(list) do
out[#out+1] = { itemID = e.itemID, name = e.name, class = class }
end
end
return out
end
local function TryResolve(entry)
local cache = CoaExporterScrollCache.entries
if cache[entry.itemID] and cache[entry.itemID].itemTooltip then
return true
end
local name, _, quality = GetItemInfo(entry.itemID)
if not name then
GameTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
GameTooltip:SetHyperlink("item:" .. entry.itemID)
GameTooltip:Hide()
return false
end
local itemLines = ScanItemTooltip(entry.itemID)
local spellName, spellRank
if GetItemSpell then
spellName, spellRank = GetItemSpell(entry.itemID)
end
cache[entry.itemID] = {
itemID = entry.itemID,
class = entry.class,
scrollName = entry.name,
itemName = name,
quality = quality,
spellName = spellName,
spellRank = spellRank,
itemTooltip = table.concat(itemLines, "\n"),
fetchedAt = time(),
}
return #itemLines > 0
end
function AE.ScrollsScanPass()
local catalog = FlatCatalog()
local total = #catalog
CoaExporterScrollCache.meta.totalCatalog = total
local newlyResolved = 0
local unresolved = {}
for _, entry in ipairs(catalog) do
if TryResolve(entry) then
newlyResolved = newlyResolved + 1
else
unresolved[#unresolved+1] = entry.itemID
end
end
CoaExporterScrollCache.meta.lastScanAt = time()
CoaExporterScrollCache.meta.resolved = newlyResolved
CoaExporterScrollCache.meta.unresolved = unresolved
return { total = total, resolved = newlyResolved, unresolved = #unresolved }
end
function AE.ScrollsStartScan(callback)
local attempts = 0
local maxAttempts = 4
local function step()
attempts = attempts + 1
local stats = AE.ScrollsScanPass()
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"CoaExporter scrolls: pass %d/%d - resolved %d/%d (still unresolved: %d)",
attempts, maxAttempts, stats.resolved, stats.total, stats.unresolved))
if stats.unresolved > 0 and attempts < maxAttempts then
local t = CreateFrame("Frame")
local elapsed = 0
t:SetScript("OnUpdate", function(self, dt)
elapsed = elapsed + dt
if elapsed > 5 then
self:SetScript("OnUpdate", nil)
step()
end
end)
else
if callback then callback(stats) end
end
end
step()
end
function AE.ScrollsExport()
local out = {
schemaVersion = 1,
exportedAt = date("!%Y-%m-%dT%H:%M:%SZ"),
catalogTotal = AE.ScrollCatalogTotal or 0,
cacheMeta = CoaExporterScrollCache.meta,
entries = {},
}
for _, rec in pairs(CoaExporterScrollCache.entries) do
table.insert(out.entries, rec)
end
return out
end
function AE.ScrollsReset()
CoaExporterScrollCache = {
entries = {},
meta = { lastScanAt = nil, totalCatalog = 0, resolved = 0, unresolved = {} },
}
end
-- Compute total once on load (used in /coae catalog status etc.)
do
local total = 0
if AE.ScrollCatalog then
for _, list in pairs(AE.ScrollCatalog) do
total = total + #list
end
end
AE.ScrollCatalogTotal = total
end
AE._loadedMysticScrolls = true