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:
2026-05-07 10:43:16 +02:00
commit 2b97a68317
17 changed files with 2988 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail
# Installs/syncs the CoaExporter addon into your WoW Ascension AddOns folder.
# Default target: /srv/add01/wow-ascension/Interface/AddOns
# Usage:
# scripts/install_addon_sub.sh [TARGET_ADDONS_DIR]
# Example:
# scripts/install_addon_sub.sh /srv/add01/wow-ascension/Interface/AddOns
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="${SCRIPT_DIR}/.."
SRC="${REPO_ROOT}/CoaExporter"
DEST_BASE="${1:-/srv/add01/wow-ascension/Interface/AddOns}"
DEST="${DEST_BASE}/CoaExporter"
if [[ ! -d "${SRC}" ]]; then
echo "ERROR: Source addon folder not found at: ${SRC}" >&2
exit 1
fi
echo "Installing CoaExporter from: ${SRC}"
echo "Target AddOns directory: ${DEST_BASE}"
mkdir -p "${DEST_BASE}"
if command -v rsync >/dev/null 2>&1; then
echo "Using rsync to copy files..."
rsync -a --delete "${SRC}/" "${DEST}/"
else
echo "rsync not found; using cp -a"
mkdir -p "${DEST}"
# Copy contents of SRC into DEST, preserving attributes
cp -a "${SRC}/." "${DEST}/"
fi
echo "Done. Addon installed to: ${DEST}"
echo "If needed, enable it on the character select screen and type /reload in-game."
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""
regen_scroll_catalog.py — re-generate CoaExporter/Data/ScrollCatalog.lua
from the AtlasLootAscension MysticEnchants.lua source of truth.
Usage:
python3 scripts/regen_scroll_catalog.py [--source PATH] [--out PATH]
"""
from __future__ import annotations
import argparse
import collections
import re
from pathlib import Path
DEFAULT_SOURCE = Path('/home/sub/public-repos/AtlasLootAscension/'
'AtlasLoot_OriginalWoW/MysticEnchants.lua')
DEFAULT_OUT = Path(__file__).parent.parent / \
'CoaExporter' / 'Data' / 'ScrollCatalog.lua'
def extract(source: Path):
text = source.read_text()
sections = re.findall(
r'\["(MysticEnchants\w+)"\]\s*=\s*\{(.*?)^\s*\}\s*,',
text, re.DOTALL | re.MULTILINE)
out = collections.defaultdict(list)
for cls, body in sections:
class_name = cls.replace('MysticEnchants', '')
for iid, name in re.findall(
r'\{\s*itemID\s*=\s*(\d+)(?:[^}]*?)?\}\s*,\s*(?://|--)\s*(.+?)(?:\n|$)',
body):
out[class_name].append((int(iid), name.strip()))
return out
def render_lua(by_class) -> str:
total = sum(len(v) for v in by_class.values())
lines = [
'-- CoaExporter / Data / ScrollCatalog.lua',
'-- Auto-generated from AtlasLootAscension MysticEnchants.lua',
f'-- {total} mystic scroll item IDs across {len(by_class)} classes',
'-- Re-generate: scripts/regen_scroll_catalog.py',
'',
'CoaExporter = _G.CoaExporter or {}',
'local AE = CoaExporter',
'',
'AE.ScrollCatalog = {',
]
for cls in sorted(by_class):
lines.append(f' {cls} = {{')
for iid, name in sorted(by_class[cls], key=lambda x: x[1]):
esc = name.replace('"', '\\"')
lines.append(f' {{ itemID = {iid}, name = "{esc}" }},')
lines.append(' },')
lines.extend([
'}',
'',
f'AE.ScrollCatalogTotal = {total}',
'AE._loadedScrollCatalog = true',
'',
])
return '\n'.join(lines)
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--source', type=Path, default=DEFAULT_SOURCE)
ap.add_argument('--out', type=Path, default=DEFAULT_OUT)
args = ap.parse_args()
by_class = extract(args.source)
args.out.parent.mkdir(parents=True, exist_ok=True)
args.out.write_text(render_lua(by_class))
total = sum(len(v) for v in by_class.values())
print(f'wrote {args.out} ({total} scrolls, {len(by_class)} classes)')
if __name__ == '__main__':
main()