f9a7ab5beb
- New AE:GenerateMarkdownSpellbook(): one section per spell tab
(General, class, pet) with icon + name + db.ascension.gg link + rank.
Skips empty tabs.
- /coae export mdspellbook new slash subcommand
- /coae export md now appends a Spellbook section after
Talents in the full wiki page
- UI sidebar: "MD Spellbook" button added under Markdown (Wiki)
596 lines
24 KiB
Lua
596 lines
24 KiB
Lua
-- CoaExporter - Core
|
|
local ADDON_NAME = ...
|
|
CoaExporter = CoaExporter or {}
|
|
local AE = CoaExporter
|
|
|
|
CoaExporterConfig = CoaExporterConfig or {}
|
|
CoaExporterConfig.enableSavedVariables = CoaExporterConfig.enableSavedVariables == true and true or false
|
|
CoaExporterSaved = CoaExporterSaved or {}
|
|
|
|
local function Norm(s)
|
|
if s == nil then return nil end
|
|
s = tostring(s):lower()
|
|
s = s:match("^%s*(.-)%s*$") or s
|
|
if s == "" then return nil end
|
|
return s
|
|
end
|
|
|
|
local function SafeCall(fn, ...)
|
|
local ok, r = pcall(fn, ...)
|
|
if ok then return r end
|
|
return nil
|
|
end
|
|
|
|
-- ===== Per-character export =====
|
|
|
|
function AE:AssembleExport(which)
|
|
which = Norm(which)
|
|
local nowIso = date("!%Y-%m-%dT%H:%M:%SZ")
|
|
local version, build, buildDate, toc = GetBuildInfo()
|
|
|
|
local name = UnitName("player") or ""
|
|
local level = UnitLevel("player") or 0
|
|
local _, class = UnitClass("player")
|
|
local _, race = UnitRace("player")
|
|
local faction = UnitFactionGroup("player") or ""
|
|
local realm = GetRealmName and GetRealmName() or (GetCVar and GetCVar("realmName")) or ""
|
|
|
|
local out = {
|
|
schemaVersion = 1,
|
|
exportedAt = nowIso,
|
|
client = { interface = toc or 30300, version = version, build = build, buildDate = buildDate },
|
|
character = { name = name or "", realm = realm or "", level = level or 0, class = class or "", race = race or "", faction = faction or "" },
|
|
}
|
|
|
|
local wantAll = (which == nil) or (which == "all")
|
|
|
|
if wantAll or which == "talents" then
|
|
if AE.CollectTalents then out.talents = SafeCall(AE.CollectTalents) or {} end
|
|
end
|
|
if wantAll or which == "spellbook" then
|
|
if AE.CollectSpellbook then out.spellbook = SafeCall(AE.CollectSpellbook) or {} end
|
|
end
|
|
if wantAll or which == "gear" then
|
|
if AE.CollectGear then out.gear = SafeCall(AE.CollectGear) or {} end
|
|
end
|
|
if wantAll or which == "enchants" then
|
|
if AE.CollectMysticEnchants then out.mysticEnchants = SafeCall(AE.CollectMysticEnchants) or {} end
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
function AE:ShowExport(text, titleText)
|
|
local show = _G.CoaExporter_ShowExportFrame
|
|
if type(show) == "function" then
|
|
show(text, titleText)
|
|
return
|
|
end
|
|
|
|
if not self._fallbackFrame then
|
|
local f = CreateFrame("Frame", "CoaExporterFallbackFrame", UIParent, "DialogBoxFrame")
|
|
f:SetSize(700, 500)
|
|
f:SetPoint("CENTER")
|
|
f:SetMovable(true)
|
|
f:EnableMouse(true)
|
|
f:RegisterForDrag("LeftButton")
|
|
f:SetScript("OnDragStart", f.StartMoving)
|
|
f:SetScript("OnDragStop", f.StopMovingOrSizing)
|
|
|
|
local title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
|
|
title:SetPoint("TOP", 0, -8)
|
|
f.title = title
|
|
|
|
local scroll = CreateFrame("ScrollFrame", "CoaExporterFallbackScroll", f, "UIPanelScrollFrameTemplate")
|
|
scroll:SetPoint("TOPLEFT", 16, -36)
|
|
scroll:SetPoint("BOTTOMRIGHT", -32, 16)
|
|
|
|
local edit = CreateFrame("EditBox", "CoaExporterFallbackEdit", scroll)
|
|
edit:SetMultiLine(true)
|
|
edit:SetAutoFocus(true)
|
|
edit:SetFontObject(ChatFontNormal)
|
|
edit:SetWidth(640)
|
|
edit:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
|
|
edit:SetScript("OnEditFocusGained", function(self) self:HighlightText() end)
|
|
scroll:SetScrollChild(edit)
|
|
|
|
f.editBox = edit
|
|
f.scrollFrame = scroll
|
|
|
|
local close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
|
|
close:SetPoint("TOPRIGHT", -4, -4)
|
|
|
|
self._fallbackFrame = f
|
|
|
|
if type(_G.CoaExporter_ShowExportFrame) ~= "function" then
|
|
_G.CoaExporter_ShowExportFrame = function(t, ti)
|
|
local ff = AE._fallbackFrame
|
|
if not ff then return end
|
|
ff:Show()
|
|
ff.editBox:SetText(t or "")
|
|
ff.title:SetText(ti or "CoaExporter (Ctrl+C)")
|
|
ff.editBox:HighlightText()
|
|
ff.editBox:SetFocus()
|
|
end
|
|
end
|
|
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: using fallback export window (UI module not loaded).")
|
|
end
|
|
|
|
local f = self._fallbackFrame
|
|
f:Show()
|
|
f.editBox:SetText(text or "")
|
|
f.title:SetText(titleText or "CoaExporter (Ctrl+C)")
|
|
f.editBox:HighlightText()
|
|
f.editBox:SetFocus()
|
|
end
|
|
|
|
function AE:Export(which)
|
|
local normWhich = Norm(which)
|
|
local data = self:AssembleExport(normWhich)
|
|
local function tiny_json_encode(v)
|
|
local tv = type(v)
|
|
if tv == 'nil' then return 'null' end
|
|
if tv == 'boolean' then return v and 'true' or 'false' end
|
|
if tv == 'number' then return tostring(v) end
|
|
if tv == 'string' then
|
|
local s = v:gsub('\\', '\\\\'):gsub('"', '\\"'):gsub('\n', '\\n'):gsub('\r', '\\r'):gsub('\t', '\\t')
|
|
return '"' .. s .. '"'
|
|
end
|
|
if tv == 'table' then
|
|
local n = 0
|
|
for k,_ in pairs(v) do if type(k) ~= 'number' then n = -1 break else n = math.max(n, k) end end
|
|
if n >= 1 then
|
|
local parts = {}
|
|
for i=1,n do parts[#parts+1] = tiny_json_encode(v[i]) end
|
|
return '[' .. table.concat(parts, ',') .. ']'
|
|
else
|
|
local parts = {}
|
|
for k,val in pairs(v) do parts[#parts+1] = tiny_json_encode(tostring(k)) .. ':' .. tiny_json_encode(val) end
|
|
return '{' .. table.concat(parts, ',') .. '}'
|
|
end
|
|
end
|
|
return 'null'
|
|
end
|
|
local encoder = _G.CoaExporter_Json_Encode or tiny_json_encode
|
|
local json = encoder(data)
|
|
|
|
local title = "CoaExporter"
|
|
if normWhich and normWhich ~= "all" then title = title .. " - " .. normWhich end
|
|
self:ShowExport(json, title .. " (Ctrl+C)")
|
|
|
|
if CoaExporterConfig.enableSavedVariables then
|
|
local key = (data.character.realm or "") .. ":" .. (data.character.name or "")
|
|
CoaExporterSaved[key] = data
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: export saved to SavedVariables for " .. key)
|
|
end
|
|
end
|
|
|
|
-- ===== Markdown generators =====
|
|
|
|
function AE:GenerateMarkdownGear()
|
|
local gear = self.CollectGear and self.CollectGear() or { slots = {} }
|
|
local ench = self.CollectMysticEnchants and self.CollectMysticEnchants() or { perSlot = {}, active = {} }
|
|
|
|
local bySlot = {}
|
|
for _, s in ipairs(gear.slots or {}) do bySlot[s.slot] = s end
|
|
local mystPer = {}
|
|
for _, e in ipairs(ench.perSlot or {}) do mystPer[e.slot] = e.name end
|
|
|
|
local ORDER = {
|
|
{1,"Head"},{2,"Neck"},{3,"Shoulders"},{15,"Back"},{5,"Chest"},
|
|
{9,"Wrist"},{16,"Mainhand"},{17,"Offhand"},{18,"Wand"},{10,"Gloves"},
|
|
{6,"Belt"},{7,"Legs"},{8,"Boots"},{11,"Ring 1"},{12,"Ring 2"},
|
|
{13,"Trinket 1"},{14,"Trinket 2"},
|
|
}
|
|
|
|
local function md_link(name, itemId)
|
|
if (itemId or 0) > 0 and name and name ~= "" then
|
|
return string.format("[%s](https://db.ascension.gg/?item=%d)", name, itemId)
|
|
elseif name and name ~= "" then
|
|
return name
|
|
else
|
|
return "-"
|
|
end
|
|
end
|
|
|
|
local lines = {}
|
|
table.insert(lines, "| Slot | Item | Location | Enchant | Alternative |")
|
|
table.insert(lines, "|------|------|----------|---------|-------------|")
|
|
for _, ent in ipairs(ORDER) do
|
|
local slotId, label = ent[1], ent[2]
|
|
local s = bySlot[slotId]
|
|
local itemCell, enchantCell = "-", "-"
|
|
if s then
|
|
itemCell = md_link(s.name, s.itemId)
|
|
local gearEnch = s.enchantName
|
|
if gearEnch and gearEnch ~= "" then
|
|
enchantCell = gearEnch
|
|
else
|
|
local myst = mystPer[slotId]
|
|
if myst and myst ~= "" then enchantCell = "Mystic: " .. myst end
|
|
end
|
|
end
|
|
table.insert(lines, string.format("| **%s** | %s | - | %s | - |", label, itemCell, enchantCell))
|
|
end
|
|
return table.concat(lines, "\n") .. "\n"
|
|
end
|
|
|
|
function AE:GenerateMarkdownEnchants()
|
|
local ench = self.CollectMysticEnchants and self.CollectMysticEnchants() or { enchants = {} }
|
|
|
|
local lines = {}
|
|
table.insert(lines, "## Mystic Enchants\n")
|
|
if #(ench.enchants or {}) == 0 then
|
|
table.insert(lines, "*No Mystic Enchants equipped.*\n")
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
local enchantRows = {}
|
|
for _, e in ipairs(ench.enchants or {}) do
|
|
local slot = e.slot or 0
|
|
local quality, qualityOrder
|
|
if slot >= 11 then
|
|
quality = '<span class="artifact">Artifact</span>'; qualityOrder = 1
|
|
elseif slot == 1 then
|
|
quality = '<span class="legendary">Legendary</span>'; qualityOrder = 2
|
|
elseif slot >= 2 and slot <= 4 then
|
|
quality = '<span class="epic">Epic</span>'; qualityOrder = 3
|
|
else
|
|
quality = '<span class="rare">Rare</span>'; qualityOrder = 4
|
|
end
|
|
|
|
local spellID = e.spellID or 0
|
|
local name = e.name or "Unknown"
|
|
local icon = e.icon or ""
|
|
local iconImg = icon ~= "" and string.format('<img src="/icons/spells/%s.png" width="20" height="20" style="vertical-align: middle;" /> ', icon) or ""
|
|
local link = spellID > 0 and string.format("[%s](https://db.ascension.gg/?spell=%d)", name, spellID) or name
|
|
table.insert(enchantRows, {
|
|
quality = quality, qualityOrder = qualityOrder, slot = slot,
|
|
text = string.format("| %s | %s**%s** |", quality, iconImg, link),
|
|
})
|
|
end
|
|
|
|
table.sort(enchantRows, function(a, b)
|
|
if a.qualityOrder ~= b.qualityOrder then return a.qualityOrder < b.qualityOrder end
|
|
return a.slot < b.slot
|
|
end)
|
|
|
|
table.insert(lines, "| Quality | Enchant |")
|
|
table.insert(lines, "|---------|---------|")
|
|
for _, row in ipairs(enchantRows) do table.insert(lines, row.text) end
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
function AE:GenerateMarkdownSpellbook()
|
|
local sb = self.CollectSpellbook and self.CollectSpellbook() or { tabs = {} }
|
|
|
|
local lines = {}
|
|
table.insert(lines, "## Spellbook\n")
|
|
|
|
if not sb.tabs or #sb.tabs == 0 then
|
|
table.insert(lines, "*Spellbook not available - run `/coae debug` to check collector.*\n")
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
table.insert(lines, string.format("Total: **%d** spells across %d tab(s).\n",
|
|
sb.totalSpells or 0, #sb.tabs))
|
|
|
|
for _, tab in ipairs(sb.tabs) do
|
|
if tab.spells and #tab.spells > 0 then
|
|
table.insert(lines, string.format("### %s (%d)", tab.name or "Tab", #tab.spells))
|
|
table.insert(lines, "")
|
|
table.insert(lines, "| Icon | Spell | Rank |")
|
|
table.insert(lines, "|------|-------|------|")
|
|
for _, sp in ipairs(tab.spells) do
|
|
local icon = sp.icon or ""
|
|
local iconImg = icon ~= "" and string.format(
|
|
'<img src="/icons/spells/%s.png" width="20" height="20" style="vertical-align: middle;" />',
|
|
icon) or ""
|
|
local link = (sp.spellID or 0) > 0
|
|
and string.format("[%s](https://db.ascension.gg/?spell=%d)", sp.name or "", sp.spellID)
|
|
or (sp.name or "")
|
|
local rank = (sp.rank ~= nil and sp.rank ~= "") and tostring(sp.rank) or "-"
|
|
table.insert(lines, string.format("| %s | %s | %s |", iconImg, link, rank))
|
|
end
|
|
table.insert(lines, "")
|
|
end
|
|
end
|
|
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
function AE:GenerateMarkdownFull()
|
|
local data = self:AssembleExport("all") or {}
|
|
local char = data.character or {}
|
|
local cls = (char.class or ""):lower()
|
|
local classIcon = string.format("/icons/classes/classicon_%s.png", cls ~= "" and cls or "unknown")
|
|
|
|
local lines = {}
|
|
table.insert(lines, string.format("", char.class or "Class", classIcon))
|
|
table.insert(lines, string.format("By %s", char.name or "?"))
|
|
table.insert(lines, "")
|
|
table.insert(lines, string.format("Character: **%s** - Level %d %s %s on %s",
|
|
char.name or "?", char.level or 0, char.race or "?", char.class or "?", char.realm or "?"))
|
|
table.insert(lines, "")
|
|
table.insert(lines, "<!-- Optional intro paragraph: describe the build, content target, philosophy. -->")
|
|
table.insert(lines, "")
|
|
table.insert(lines, "> **Note:** Exported by CoaExporter - gear, enchants, and talents are auto-populated.")
|
|
table.insert(lines, "> Items marked with unknown db.ascension.gg IDs are custom Ascension loot.")
|
|
table.insert(lines, "> {.is-info}")
|
|
table.insert(lines, "")
|
|
|
|
table.insert(lines, "## Gear")
|
|
table.insert(lines, "")
|
|
table.insert(lines, self:GenerateMarkdownGear())
|
|
|
|
table.insert(lines, self:GenerateMarkdownEnchants())
|
|
table.insert(lines, "")
|
|
|
|
table.insert(lines, "## Talents")
|
|
table.insert(lines, "")
|
|
local talents = data.talents or {}
|
|
local selected = talents.selected or {}
|
|
local buildString = talents.buildString
|
|
local list = talents.talents or selected
|
|
if buildString and buildString ~= "" then
|
|
table.insert(lines, string.format("**Ascension ExportBuild:** `%s`", buildString))
|
|
table.insert(lines, "")
|
|
table.insert(lines, string.format(
|
|
"<!-- The exil.es talent-calc uses a different build format. " ..
|
|
"Until the converter is in place, generate the iframe URL from " ..
|
|
"the exil.es calc UI by importing the ExportBuild string above, " ..
|
|
"then paste the resulting src= here:"))
|
|
table.insert(lines, string.format(
|
|
" <iframe src=\"/static/talents/?build=%s.XX_YY_ZZ..&embed=true\" " ..
|
|
"width=\"100%%\" height=\"840\" style=\"border:0;border-radius:8px;\"></iframe>",
|
|
(cls ~= "" and cls or "druid")))
|
|
table.insert(lines, "-->")
|
|
table.insert(lines, "")
|
|
end
|
|
|
|
if list and #list > 0 then
|
|
table.insert(lines, string.format("Known talents: **%d** entries.", #list))
|
|
table.insert(lines, "")
|
|
table.insert(lines, "| ID | Name | Rank |")
|
|
table.insert(lines, "|----|------|------|")
|
|
for _, t in ipairs(list) do
|
|
local nm = (t.name and t.name ~= "") and t.name or "*(unresolved)*"
|
|
local rk = (t.rank or 0) > 0 and tostring(t.rank) or "?"
|
|
local mr = (t.maxRank or 0) > 0 and tostring(t.maxRank) or "?"
|
|
table.insert(lines, string.format("| %d | %s | %s/%s |", t.id or 0, nm, rk, mr))
|
|
end
|
|
else
|
|
table.insert(lines, "*No talent data - run `/coae debug` to check collector.*")
|
|
end
|
|
|
|
-- Spellbook section
|
|
table.insert(lines, "")
|
|
table.insert(lines, self:GenerateMarkdownSpellbook())
|
|
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
-- ===== Slash command =====
|
|
|
|
local HELP = [[
|
|
CoaExporter commands:
|
|
|
|
/coae export all|talents|spellbook|gear|enchants
|
|
/coae export mdgear|mdenchants|mdspellbook|md (full wiki)
|
|
/coae catalog all|skills|talents
|
|
/coae catalog dispels [class]|passives [class]|status
|
|
/coae scrolls scan|export|reset|status
|
|
/coae sv on|off (SavedVariables for character export)
|
|
/coae debug
|
|
/coae help
|
|
]]
|
|
|
|
local function HandleExport(rest)
|
|
rest = Norm(rest) or "all"
|
|
if rest == "mdgear" then
|
|
AE:ShowExport(AE:GenerateMarkdownGear(), "CoaExporter - Markdown Gear (Ctrl+C)")
|
|
elseif rest == "mdenchants" then
|
|
AE:ShowExport(AE:GenerateMarkdownEnchants(), "CoaExporter - Markdown Enchants (Ctrl+C)")
|
|
elseif rest == "mdspellbook" then
|
|
AE:ShowExport(AE:GenerateMarkdownSpellbook(), "CoaExporter - Markdown Spellbook (Ctrl+C)")
|
|
elseif rest == "md" or rest == "mdfull" or rest == "wiki" then
|
|
AE:ShowExport(AE:GenerateMarkdownFull(), "CoaExporter - Wiki Markdown (Ctrl+C)")
|
|
elseif rest == "all" or rest == "talents" or rest == "spellbook" or rest == "gear" or rest == "enchants" then
|
|
AE:Export(rest)
|
|
else
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: unknown export target '" .. tostring(rest) .. "'")
|
|
end
|
|
end
|
|
|
|
local function HandleCatalog(rest)
|
|
rest = Norm(rest) or "status"
|
|
local sub, arg = rest:match("^(%S+)%s*(.*)$")
|
|
sub = sub or rest
|
|
|
|
if sub == "all" or sub == "skills" or sub == "talents" then
|
|
if not AE.Catalog or not AE.Catalog.Run then
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: catalog module not loaded")
|
|
return
|
|
end
|
|
AE.Catalog.Run(sub == "all" and "all" or sub)
|
|
elseif sub == "dispels" then
|
|
if AE.CatalogListDispels then AE.CatalogListDispels(arg) end
|
|
elseif sub == "passives" then
|
|
if AE.CatalogListPassives then AE.CatalogListPassives(arg) end
|
|
elseif sub == "status" then
|
|
local meta = (CoaExporterCatalog and CoaExporterCatalog._meta) or {}
|
|
local nSkills, nDispels, nPassives, nTalents = 0, 0, 0, 0
|
|
if CoaExporterCatalog then
|
|
for _ in pairs(CoaExporterCatalog.skills or {}) do nSkills = nSkills + 1 end
|
|
for _ in pairs(CoaExporterCatalog.dispels or {}) do nDispels = nDispels + 1 end
|
|
for _ in pairs(CoaExporterCatalog.levelPassives or {}) do nPassives = nPassives + 1 end
|
|
for _ in pairs(CoaExporterCatalog.talents or {}) do nTalents = nTalents + 1 end
|
|
end
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(
|
|
"CoaExporter catalog: lastScan=%s filter=%s | skills=%d cls dispels=%d cls passives=%d cls talents=%d cls",
|
|
tostring(meta.lastScanAt or "never"), tostring(meta.filter or "-"),
|
|
nSkills, nDispels, nPassives, nTalents))
|
|
else
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: /coae catalog [all|skills|talents|dispels|passives|status]")
|
|
end
|
|
end
|
|
|
|
local function HandleScrolls(rest)
|
|
rest = Norm(rest) or "status"
|
|
if rest == "scan" then
|
|
if not AE.ScrollsStartScan then
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: MysticScrolls collector not loaded")
|
|
return
|
|
end
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(
|
|
"CoaExporter scrolls: scanning %d scrolls, ~20s...",
|
|
AE.ScrollCatalogTotal or 0))
|
|
AE.ScrollsStartScan(function(stats)
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(
|
|
"CoaExporter scrolls: scan complete - %d resolved, %d unresolved",
|
|
stats.total - stats.unresolved, stats.unresolved))
|
|
end)
|
|
elseif rest == "export" then
|
|
if not AE.ScrollsExport then
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: MysticScrolls collector not loaded")
|
|
return
|
|
end
|
|
local data = AE.ScrollsExport()
|
|
local encoder = _G.CoaExporter_Json_Encode
|
|
if not encoder then
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: JSON encoder missing")
|
|
return
|
|
end
|
|
AE:ShowExport(encoder(data), "CoaExporter - Scrolls (Ctrl+C)")
|
|
elseif rest == "reset" then
|
|
if AE.ScrollsReset then AE.ScrollsReset() end
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter scrolls: cache cleared")
|
|
elseif rest == "status" then
|
|
local meta = CoaExporterScrollCache and CoaExporterScrollCache.meta or {}
|
|
local resolved = 0
|
|
if CoaExporterScrollCache then
|
|
for _ in pairs(CoaExporterScrollCache.entries or {}) do resolved = resolved + 1 end
|
|
end
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(
|
|
"CoaExporter scrolls: %d/%d resolved (last scan: %s)",
|
|
resolved, AE.ScrollCatalogTotal or 0,
|
|
meta.lastScanAt and date("%Y-%m-%d %H:%M:%S", meta.lastScanAt) or "never"))
|
|
else
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: /coae scrolls [scan|export|reset|status]")
|
|
end
|
|
end
|
|
|
|
local function HandleDebug()
|
|
local lines = {}
|
|
local function add(m) table.insert(lines, m) end
|
|
|
|
add("CoaExporter debug:")
|
|
add(string.format("- AddOn: %s", tostring(ADDON_NAME)))
|
|
add(string.format("- UI: %s", type(_G.CoaExporter_ShowExportFrame) == "function" and "yes" or "no"))
|
|
add(string.format("- JSON: %s", type(_G.CoaExporter_Json_Encode) == "function" and "yes" or "no"))
|
|
add(string.format("- Loaded: talents=%s spellbook=%s gear=%s enchants=%s scrolls=%s probe=%s catCommon=%s catSkills=%s catTalents=%s",
|
|
tostring(AE._loadedTalents or false), tostring(AE._loadedSpellbook or false),
|
|
tostring(AE._loadedGear or false), tostring(AE._loadedEnchants or false),
|
|
tostring(AE._loadedMysticScrolls or false), tostring(AE._loadedMysticScrollProbe or false),
|
|
tostring(AE._loadedCatalogCommon or false), tostring(AE._loadedCatalogSkills or false),
|
|
tostring(AE._loadedCatalogTalents or false)))
|
|
|
|
if type(AE.CollectTalents) == "function" then
|
|
local t = SafeCall(AE.CollectTalents) or {}
|
|
add(string.format("Talents: OK, selected=%d, build=%s",
|
|
#(t.selected or {}), tostring(t.buildString or "-")))
|
|
else
|
|
add("Talents: MISSING")
|
|
end
|
|
if type(AE.CollectSpellbook) == "function" then
|
|
local sb = SafeCall(AE.CollectSpellbook) or {}
|
|
add(string.format("Spellbook: OK, tabs=%d, spells=%d",
|
|
#(sb.tabs or {}), sb.totalSpells or 0))
|
|
else
|
|
add("Spellbook: MISSING")
|
|
end
|
|
if type(AE.CollectGear) == "function" then
|
|
local g = SafeCall(AE.CollectGear) or {}
|
|
add(string.format("Gear: OK, slots=%d", #(g.slots or {})))
|
|
else
|
|
add("Gear: MISSING")
|
|
end
|
|
if type(AE.CollectMysticEnchants) == "function" then
|
|
local e = SafeCall(AE.CollectMysticEnchants) or {}
|
|
add(string.format("MysticEnchants: OK, count=%d", #(e.enchants or {})))
|
|
else
|
|
add("MysticEnchants: MISSING")
|
|
end
|
|
add(string.format("ScrollCatalog: %d entries", AE.ScrollCatalogTotal or 0))
|
|
|
|
local meta = (CoaExporterCatalog and CoaExporterCatalog._meta) or {}
|
|
add(string.format("Catalog: lastScan=%s filter=%s",
|
|
tostring(meta.lastScanAt or "never"), tostring(meta.filter or "-")))
|
|
|
|
AE:ShowExport(table.concat(lines, "\n"), "CoaExporter - Debug")
|
|
end
|
|
|
|
local function HandleSv(rest)
|
|
if rest == "on" then
|
|
CoaExporterConfig.enableSavedVariables = true
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: SavedVariables export ENABLED")
|
|
elseif rest == "off" then
|
|
CoaExporterConfig.enableSavedVariables = false
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: SavedVariables export DISABLED")
|
|
else
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: /coae sv on|off")
|
|
end
|
|
end
|
|
|
|
local function Dispatch(msg)
|
|
msg = (msg or ""):lower()
|
|
if msg == "" or msg == "help" then
|
|
AE:ShowExport(HELP, "CoaExporter - Help")
|
|
return
|
|
end
|
|
local cmd, rest = msg:match("^(%S+)%s*(.*)$")
|
|
if cmd == "export" then return HandleExport(rest) end
|
|
if cmd == "catalog" then return HandleCatalog(rest) end
|
|
if cmd == "scrolls" then return HandleScrolls(rest) end
|
|
if cmd == "debug" then return HandleDebug() end
|
|
if cmd == "sv" then return HandleSv(Norm(rest)) end
|
|
DEFAULT_CHAT_FRAME:AddMessage("CoaExporter: unknown command. Type /coae help")
|
|
end
|
|
|
|
SLASH_COAE1 = "/coae"
|
|
SLASH_COAE2 = "/coaexp"
|
|
SLASH_COAE3 = "/ascx" -- back-compat: AscensionExporter users
|
|
SLASH_COAE4 = "/asxc"
|
|
SlashCmdList["COAE"] = Dispatch
|
|
|
|
-- Legacy CoA_*Exporter slash compatibility shims
|
|
SLASH_COAESKILLDUMP1 = "/skilldump"
|
|
SlashCmdList["COAESKILLDUMP"] = function()
|
|
if AE.Catalog and AE.Catalog.Run then AE.Catalog.Run("skills") end
|
|
end
|
|
|
|
SLASH_COAEDISPELS1 = "/dispels"
|
|
SlashCmdList["COAEDISPELS"] = function(arg)
|
|
if AE.CatalogListDispels then AE.CatalogListDispels(arg) end
|
|
end
|
|
|
|
SLASH_COAEPASSIVES1 = "/passives"
|
|
SlashCmdList["COAEPASSIVES"] = function(arg)
|
|
if AE.CatalogListPassives then AE.CatalogListPassives(arg) end
|
|
end
|
|
|
|
SLASH_COAETALENTDUMP1 = "/talentdumpall"
|
|
SLASH_COAETALENTDUMP2 = "/talentdump"
|
|
SlashCmdList["COAETALENTDUMP"] = function()
|
|
if AE.Catalog and AE.Catalog.Run then AE.Catalog.Run("talents") end
|
|
end
|
|
|
|
local f = CreateFrame("Frame")
|
|
f:RegisterEvent("ADDON_LOADED")
|
|
f:SetScript("OnEvent", function(_, event, addon)
|
|
if event == "ADDON_LOADED" and addon == "CoaExporter" then
|
|
CoaExporterConfig.enableSavedVariables = CoaExporterConfig.enableSavedVariables and true or false
|
|
end
|
|
end)
|