Add per-character spellbook collector
New Collectors/Spellbook.lua walks GetNumSpellTabs() and emits one entry
per tab with name, texture, offset, numSpells, and a spells[] array of
{ slot, name, rank, spellID, icon }.
3.3.5/Ascension API differences are handled defensively: name comes
from GetSpellBookItemInfo or vanilla GetSpellName; spellID is parsed
out of GetSpellLink (3.3.5 doesn't return it from GetSpellBookItemInfo);
icon falls back to GetSpellTexture if GetSpellInfo doesn't have it yet.
Wiring:
- CoaExporter.toc: load Collectors/Spellbook.lua after Talents
- Core.lua: AssembleExport() includes spellbook for all + spellbook
- Core.lua: HandleExport() accepts /coae export spellbook
- Core.lua: debug output shows tab + spell counts
- UI/ExportFrame.lua: "Spellbook" button in the Character section
This commit is contained in:
@@ -9,6 +9,7 @@ Util\Json.lua
|
||||
Data\ScrollCatalog.lua
|
||||
|
||||
Collectors\Talents.lua
|
||||
Collectors\Spellbook.lua
|
||||
Collectors\Gear.lua
|
||||
Collectors\Enchants.lua
|
||||
Collectors\MysticScrolls.lua
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
-- CoaExporter - Spellbook collector
|
||||
--
|
||||
-- Walks the player's spell book (BOOKTYPE_SPELL) tab by tab and emits
|
||||
-- name + rank + spellID + icon for every learned spell. Pet book is
|
||||
-- skipped — it's not part of the build/guide use-case.
|
||||
--
|
||||
-- 3.3.5 / Ascension API quirks handled defensively:
|
||||
-- - GetSpellBookItemInfo(slot, "spell") returns (name, subName) on
|
||||
-- vanilla 3.3.5 and (name, rank, [stuff]) on newer/private servers.
|
||||
-- We treat the second return as a rank string regardless.
|
||||
-- - GetSpellBookItemInfo doesn't return a spellID on 3.3.5. The reliable
|
||||
-- way to get the ID is to parse it out of GetSpellLink(slot, "spell")
|
||||
-- which yields "|cff71d5ff|Hspell:NNNN|h[Name]|h|r".
|
||||
-- - Some entries can be category headers / flyouts; if no link or
|
||||
-- spellID, we still record the name (it might be a known-spell that
|
||||
-- just doesn't link, e.g. some passives).
|
||||
|
||||
CoaExporter = _G.CoaExporter or {}
|
||||
_G.CoaExporter = CoaExporter
|
||||
local AE = CoaExporter
|
||||
|
||||
local BOOKTYPE = (BOOKTYPE_SPELL or "spell")
|
||||
|
||||
local function safeCall(fn, ...)
|
||||
if type(fn) ~= "function" then return nil end
|
||||
local ok, a, b, c = pcall(fn, ...)
|
||||
if not ok then return nil end
|
||||
return a, b, c
|
||||
end
|
||||
|
||||
local function spellIdFromLink(link)
|
||||
if not link or link == "" then return 0 end
|
||||
local id = string.match(link, "spell:(%d+)")
|
||||
return tonumber(id) or 0
|
||||
end
|
||||
|
||||
local function iconPath(icon)
|
||||
if not icon or icon == "" then return "" end
|
||||
local p = icon:match("Interface\\Icons\\(.+)") or icon
|
||||
return tostring(p):lower()
|
||||
end
|
||||
|
||||
function AE.CollectSpellbook()
|
||||
local out = { tabs = {}, totalSpells = 0 }
|
||||
|
||||
if type(GetNumSpellTabs) ~= "function" then
|
||||
out.error = "GetNumSpellTabs not available"
|
||||
return out
|
||||
end
|
||||
|
||||
local numTabs = GetNumSpellTabs() or 0
|
||||
for tabIndex = 1, numTabs do
|
||||
local tabName, tabTexture, offset, numSpells = safeCall(GetSpellTabInfo, tabIndex)
|
||||
offset = offset or 0
|
||||
numSpells = numSpells or 0
|
||||
|
||||
local tab = {
|
||||
index = tabIndex,
|
||||
name = tabName or ("Tab " .. tabIndex),
|
||||
texture = iconPath(tabTexture),
|
||||
offset = offset,
|
||||
numSpells = numSpells,
|
||||
spells = {},
|
||||
}
|
||||
|
||||
for slot = offset + 1, offset + numSpells do
|
||||
local name, subOrRank = safeCall(GetSpellBookItemInfo, slot, BOOKTYPE)
|
||||
if (not name or name == "") and type(GetSpellName) == "function" then
|
||||
-- vanilla 3.3.5 fallback
|
||||
name, subOrRank = safeCall(GetSpellName, slot, BOOKTYPE)
|
||||
end
|
||||
|
||||
local link = safeCall(GetSpellLink, slot, BOOKTYPE)
|
||||
local spellID = spellIdFromLink(link)
|
||||
|
||||
local icon
|
||||
if spellID > 0 then
|
||||
local _, _, ic = GetSpellInfo(spellID)
|
||||
icon = ic
|
||||
end
|
||||
if (not icon) and type(GetSpellTexture) == "function" then
|
||||
icon = safeCall(GetSpellTexture, slot, BOOKTYPE)
|
||||
end
|
||||
|
||||
if name and name ~= "" then
|
||||
table.insert(tab.spells, {
|
||||
slot = slot,
|
||||
name = name,
|
||||
rank = subOrRank or "",
|
||||
spellID = spellID,
|
||||
icon = iconPath(icon),
|
||||
})
|
||||
out.totalSpells = out.totalSpells + 1
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(out.tabs, tab)
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
AE._loadedSpellbook = true
|
||||
+16
-6
@@ -47,6 +47,9 @@ function AE:AssembleExport(which)
|
||||
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
|
||||
@@ -331,7 +334,7 @@ end
|
||||
local HELP = [[
|
||||
CoaExporter commands:
|
||||
|
||||
/coae export all|talents|gear|enchants
|
||||
/coae export all|talents|spellbook|gear|enchants
|
||||
/coae export mdgear|mdenchants|md (full wiki)
|
||||
/coae catalog all|skills|talents
|
||||
/coae catalog dispels [class]|passives [class]|status
|
||||
@@ -349,7 +352,7 @@ local function HandleExport(rest)
|
||||
AE:ShowExport(AE:GenerateMarkdownEnchants(), "CoaExporter - Markdown Enchants (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 == "gear" or rest == "enchants" then
|
||||
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) .. "'")
|
||||
@@ -442,10 +445,10 @@ local function HandleDebug()
|
||||
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 gear=%s enchants=%s scrolls=%s probe=%s catCommon=%s catSkills=%s catTalents=%s",
|
||||
tostring(AE._loadedTalents or false), tostring(AE._loadedGear or false),
|
||||
tostring(AE._loadedEnchants or false), tostring(AE._loadedMysticScrolls or false),
|
||||
tostring(AE._loadedMysticScrollProbe or false),
|
||||
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)))
|
||||
|
||||
@@ -456,6 +459,13 @@ local function HandleDebug()
|
||||
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 {})))
|
||||
|
||||
@@ -28,6 +28,7 @@ local function buildSections()
|
||||
{ title = "Character", items = {
|
||||
{ "All", function() local ae = ce(); if ae then ae:Export("all") end end },
|
||||
{ "Talents", function() local ae = ce(); if ae then ae:Export("talents") end end },
|
||||
{ "Spellbook", function() local ae = ce(); if ae then ae:Export("spellbook") end end },
|
||||
{ "Gear", function() local ae = ce(); if ae then ae:Export("gear") end end },
|
||||
{ "Enchants", function() local ae = ce(); if ae then ae:Export("enchants") end end },
|
||||
}},
|
||||
|
||||
@@ -44,8 +44,9 @@ Primary slash: `/coae`. Aliases: `/coaexp`, `/ascx`, `/asxc`.
|
||||
### Per-character export
|
||||
|
||||
```
|
||||
/coae export all JSON of talents + gear + mystic enchants
|
||||
/coae export all JSON of talents + spellbook + gear + mystic enchants
|
||||
/coae export talents JSON, talents only
|
||||
/coae export spellbook JSON, full spellbook (per tab, with spellID + rank + icon)
|
||||
/coae export gear JSON, gear only
|
||||
/coae export enchants JSON, mystic enchants only
|
||||
|
||||
@@ -127,6 +128,7 @@ CoaExporter/
|
||||
├── Data/ScrollCatalog.lua (auto-generated from AtlasLootAscension)
|
||||
├── Collectors/ (per-character data)
|
||||
│ ├── Talents.lua
|
||||
│ ├── Spellbook.lua
|
||||
│ ├── Gear.lua
|
||||
│ ├── Enchants.lua
|
||||
│ ├── MysticScrolls.lua
|
||||
|
||||
Reference in New Issue
Block a user