wuira 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
+ local ____exports = {}
3
+ return ____exports
package/lib/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export type { CommandCallback } from "./platform/SlashCommands";
6
6
  export { AddonSettings } from "./platform/AddonSettings";
7
7
  export { Encoding } from "./platform/Encoding";
8
8
  export { DruidSpells, GeneralSpells, HunterSpells, MageSpells, PaladinSpells, PetSpells, PriestSpells, ProfessionSpells, RacialSpells, RogueSpells, ShamanSpells, WarlockSpells, WarriorSpells, WorldSpells, BindingActions, } from "./spells/index";
9
+ export { Icons } from "./icons/index";
9
10
  export { BlizzardUI } from "./platform/BlizzardUI";
10
11
  export { ActionBars } from "./platform/ActionBars";
11
12
  export type { ActionSlot, EmptyActionBarsOptions } from "./platform/ActionBars";
@@ -739,10 +739,12 @@ function StoreRegistry.prototype.____constructor(self, options)
739
739
  if string.match(options.variable, "^[%a_][%w_]*$") == nil then
740
740
  error("Invalid SavedVariables name", 0)
741
741
  end
742
- local ____opt_1 = C_AddOns and C_AddOns.IsAddOnLoaded
743
- if ____opt_1 and ____opt_1(options.addon) then
744
- self:Load()
745
- return
742
+ if (C_AddOns and C_AddOns.IsAddOnLoaded) ~= nil then
743
+ local ____, loaded = C_AddOns.IsAddOnLoaded(options.addon)
744
+ if loaded then
745
+ self:Load()
746
+ return
747
+ end
746
748
  end
747
749
  self.listener = CreateFrame("Frame", nil, UIParent)
748
750
  self.listener:RegisterEvent("ADDON_LOADED")
@@ -753,17 +755,17 @@ function StoreRegistry.prototype.____constructor(self, options)
753
755
  return
754
756
  end
755
757
  self:Load()
758
+ local ____opt_3 = self.listener
759
+ if ____opt_3 ~= nil then
760
+ ____opt_3:UnregisterAllEvents()
761
+ end
756
762
  local ____opt_5 = self.listener
757
763
  if ____opt_5 ~= nil then
758
- ____opt_5:UnregisterAllEvents()
764
+ ____opt_5:SetScript("OnEvent", nil)
759
765
  end
760
766
  local ____opt_7 = self.listener
761
767
  if ____opt_7 ~= nil then
762
- ____opt_7:SetScript("OnEvent", nil)
763
- end
764
- local ____opt_9 = self.listener
765
- if ____opt_9 ~= nil then
766
- ____opt_9:Hide()
768
+ ____opt_7:Hide()
767
769
  end
768
770
  self.listener = nil
769
771
  local callbacks = self.readyCallbacks
@@ -784,8 +786,8 @@ function StoreRegistry.prototype.onReady(self, callback)
784
786
  if self.IsReady then
785
787
  callback(nil)
786
788
  else
787
- local ____self_readyCallbacks_11 = self.readyCallbacks
788
- ____self_readyCallbacks_11[#____self_readyCallbacks_11 + 1] = callback
789
+ local ____self_readyCallbacks_9 = self.readyCallbacks
790
+ ____self_readyCallbacks_9[#____self_readyCallbacks_9 + 1] = callback
789
791
  end
790
792
  end
791
793
  function StoreRegistry.prototype.Read(self, schema)
@@ -907,17 +909,17 @@ function StoreRegistry.prototype.subscribe(self, schema, observer)
907
909
  end
908
910
  end
909
911
  function StoreRegistry.prototype.Dispose(self)
912
+ local ____opt_10 = self.listener
913
+ if ____opt_10 ~= nil then
914
+ ____opt_10:UnregisterAllEvents()
915
+ end
910
916
  local ____opt_12 = self.listener
911
917
  if ____opt_12 ~= nil then
912
- ____opt_12:UnregisterAllEvents()
918
+ ____opt_12:SetScript("OnEvent", nil)
913
919
  end
914
920
  local ____opt_14 = self.listener
915
921
  if ____opt_14 ~= nil then
916
- ____opt_14:SetScript("OnEvent", nil)
917
- end
918
- local ____opt_16 = self.listener
919
- if ____opt_16 ~= nil then
920
- ____opt_16:Hide()
922
+ ____opt_14:Hide()
921
923
  end
922
924
  self.listener = nil
923
925
  self.observers:clear()
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "wuira",
3
- "version": "0.2.4",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "git+https://github.com/KerimCETINBAS/wuira.git"
6
+ },
7
+ "version": "0.2.5",
4
8
  "author": {
5
9
  "name": "Kerim Cetinbas",
6
10
  "email": "a.cetinbas@icloud.com"
@@ -42,7 +46,11 @@
42
46
  },
43
47
  "./config/tsconfig.json": "./config/tsconfig.json",
44
48
  "./tools/wow-modules.mjs": "./tools/wow-modules.mjs",
45
- "./package.json": "./package.json"
49
+ "./package.json": "./package.json",
50
+ "./icons": {
51
+ "types": "./lib/icons/index.d.ts",
52
+ "default": "./lib/icons/index.lua"
53
+ }
46
54
  },
47
55
  "typesVersions": {
48
56
  "*": {
@@ -66,6 +74,9 @@
66
74
  ],
67
75
  "console-polyfill": [
68
76
  "lib/console-polyfill.d.ts"
77
+ ],
78
+ "icons": [
79
+ "lib/icons/index.d.ts"
69
80
  ]
70
81
  }
71
82
  },
@@ -81,7 +92,10 @@
81
92
  "tsconfig.lib.json",
82
93
  "README.md",
83
94
  "CHANGELOG.md",
84
- "LICENSE"
95
+ "LICENSE",
96
+ "data/icons/forever.json",
97
+ "docs/icons.md",
98
+ "docs/source-converters.md"
85
99
  ],
86
100
  "engines": {
87
101
  "node": ">=22.13"
@@ -97,7 +111,9 @@
97
111
  "check": "tsc -p tsconfig.json --noEmit",
98
112
  "spells:update": "node tools/update-spells.mjs",
99
113
  "spells:generate": "node tools/generate-spells.mjs",
100
- "test": "node --test tests/*.test.cjs"
114
+ "test": "node --test tests/*.test.cjs",
115
+ "icons:update": "node tools/update-icons.mjs",
116
+ "icons:generate": "node tools/generate-icons.mjs"
101
117
  },
102
118
  "devDependencies": {
103
119
  "fengari-node-cli": "0.1.0"
@@ -12,7 +12,7 @@ const result = spawnSync(process.execPath, [require.resolve("typescript-to-lua/d
12
12
  if (result.status !== 0) process.exit(result.status || 1);
13
13
  fs.mkdirSync(path.join(root, "lib/platform"), { recursive: true });
14
14
  fs.copyFileSync(path.join(root, "src/platform/forever.d.ts"), path.join(root, "lib/platform/forever.d.ts"));
15
- for (const file of ["index.d.ts", "ui/index.d.ts", "datasources/index.d.ts", "store/index.d.ts", "skins/index.d.ts", "spells/index.d.ts"]) {
15
+ for (const file of ["index.d.ts", "ui/index.d.ts", "datasources/index.d.ts", "store/index.d.ts", "skins/index.d.ts", "spells/index.d.ts", "icons/index.d.ts"]) {
16
16
  const target = path.join(root, "lib", file);
17
17
  const reference = file === "index.d.ts" ? "./platform/forever.d.ts" : "../platform/forever.d.ts";
18
18
  fs.writeFileSync(target, `/// <reference path="${reference}" />\n` + fs.readFileSync(target, "utf8"));
@@ -0,0 +1,10 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { iconEnumSource } from "./icon-data.mjs";
4
+ const root = path.resolve(import.meta.dirname, "..");
5
+ const catalog = JSON.parse(fs.readFileSync(path.join(root,"data/icons/forever.json"),"utf8"));
6
+ const spells = JSON.parse(fs.readFileSync(path.join(root, "data/spells/forever.json"), "utf8"));
7
+ if (catalog.build !== spells.build || catalog.product !== spells.product) throw new Error("Icon and spell builds must match");
8
+ const output=path.join(root,"src/icons");fs.mkdirSync(output,{recursive:true});
9
+ fs.writeFileSync(path.join(output, "index.ts"), iconEnumSource(catalog));
10
+ console.log("Generated " + catalog.icons.length + " native icon IDs");
@@ -0,0 +1,59 @@
1
+ /** Build-time catalog data. Nothing in this module runs inside the game. */
2
+ export function collectIcons(spells, tables) {
3
+ const spellNames = new Map(spells.spells.map(row => [row[0], row]));
4
+ const itemNames = new Map(tables.ItemSparse.map(row => [Number(row.ID), row.Display_lang]));
5
+ if (!tables.SpellMisc.length || !tables.SpellMisc.every(row => "SpellIconFileDataID" in row && "ActiveIconFileDataID" in row && "SpellID" in row)
6
+ || !tables.Item.length || !tables.Item.every(row => "IconFileDataID" in row)
7
+ || !tables.ItemSparse.length || !tables.ItemSparse.every(row => "Display_lang" in row)) throw new Error("Unexpected client icon schema");
8
+ const icons = new Map();
9
+ const add = (value, name, kind, priority) => {
10
+ const id = Number(value);
11
+ if (!Number.isSafeInteger(id) || id < 0) throw new Error("Invalid icon FileDataID");
12
+ if (id === 0) return;
13
+ const icon = icons.get(id) ?? { id, names: new Map(), kinds: new Set() };
14
+ const label = name.trim();
15
+ icon.names.set(label, Math.min(icon.names.get(label) ?? Infinity, priority));
16
+ icon.kinds.add(kind); icons.set(id, icon);
17
+ };
18
+ for (const row of tables.SpellMisc) {
19
+ const spell = spellNames.get(Number(row.SpellID));
20
+ const name = spell?.[1]?.trim() || "Spell " + row.SpellID;
21
+ const priority = spell?.[4] ? 0 : spell ? 2 : 4;
22
+ add(row.SpellIconFileDataID, name, "spell", priority);
23
+ add(row.ActiveIconFileDataID, name + " (active)", "spell", priority + 1);
24
+ }
25
+ for (const row of tables.Item) {
26
+ const name = itemNames.get(Number(row.ID))?.trim();
27
+ add(row.IconFileDataID, name || "Item " + row.ID, "item", name ? 1 : 4);
28
+ }
29
+ return [...icons.values()].sort((a, b) => a.id - b.id).map(icon => {
30
+ const names = [...icon.names].sort(([a, pa], [b, pb]) => pa - pb || (a < b ? -1 : a > b ? 1 : 0)).map(([name]) => name);
31
+ return [icon.id, names[0], names, [...icon.kinds].sort()];
32
+ });
33
+ }
34
+
35
+ export function iconEnumSource(catalog) {
36
+ if (catalog.version !== 1 || catalog.product !== "wow_classic_beta" || !/^1\.60\./.test(catalog.build)) throw new Error("Unsupported icon catalog");
37
+ const identifier = name => {
38
+ const value = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/['’]/g, "")
39
+ .split(/[^A-Za-z0-9]+/).filter(Boolean).map(word => word[0].toUpperCase() + word.slice(1)).join("");
40
+ return /^[A-Za-z]/.test(value) ? value : "Icon" + value;
41
+ };
42
+ const ids = new Set(), aliases = new Map(), occupied = new Set(), lines = [];
43
+ for (const [id, name, names] of catalog.icons) {
44
+ if (!Number.isSafeInteger(id) || id < 1 || ids.has(id) || !name || !Array.isArray(names) || !names.includes(name)) throw new Error("Invalid icon record: " + id);
45
+ ids.add(id);
46
+ const key = identifier(name) + "_" + id;
47
+ occupied.add(key); lines.push(" " + key + " = " + id + ",");
48
+ for (const alias of names) {
49
+ const key = identifier(alias);
50
+ if (!aliases.has(key)) aliases.set(key, new Set());
51
+ aliases.get(key).add(id);
52
+ }
53
+ }
54
+ for (const [name, values] of [...aliases].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) if (values.size === 1 && !occupied.has(name)) {
55
+ occupied.add(name); lines.push(" " + name + " = " + [...values][0] + ",");
56
+ }
57
+ return "// Auto generated. Do not edit.\n// Source: Forever " + catalog.build + "; run npm run icons:generate.\n"
58
+ + "/** Native texture FileDataIDs, independent of spell and action IDs. */\nexport const enum Icons {\n" + lines.join("\n") + "\n}\n";
59
+ }
@@ -0,0 +1,36 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { parseCSV } from "./spell-data.mjs";
5
+ import { collectIcons } from "./icon-data.mjs";
6
+ const root = path.resolve(import.meta.dirname, "..");
7
+ const argument = name => process.argv[process.argv.indexOf(name) + 1];
8
+ const build = process.argv.includes("--build") ? argument("--build") : undefined;
9
+ const spells = JSON.parse(fs.readFileSync(path.join(root, "data/spells/forever.json"), "utf8"));
10
+ if (!build || !build.startsWith("1.60.") || build !== spells.build || spells.product !== "wow_classic_beta") throw new Error("Use the same verified Forever build as the spell catalog");
11
+ const cached = process.argv.includes("--cached"), directory = path.join(root, "data/icons/.cache", build);
12
+ fs.mkdirSync(directory, { recursive: true });
13
+ const hash = value => createHash("sha256").update(value).digest("hex"), sources = [];
14
+ async function table(name) {
15
+ const url = "https://wago.tools/db2/" + name + "/csv?build=" + build, file = path.join(directory, name + ".csv");
16
+ let text;
17
+ if (cached) text = fs.readFileSync(file, "utf8");
18
+ else {
19
+ const response = await fetch(url, { signal: AbortSignal.timeout(60000) });
20
+ if (!response.ok || !response.headers.get("content-type")?.includes("text/csv")) throw new Error("Icon data download failed: " + name + " (" + response.status + ")");
21
+ text = await response.text(); fs.writeFileSync(file, text);
22
+ }
23
+ const rows = parseCSV(text); sources.push({ table: name, url, sha256: hash(text), rows: rows.length }); return [name, rows];
24
+ }
25
+ const results = await Promise.allSettled(["SpellMisc", "Item", "ItemSparse"].map(table));
26
+ const failures = results.filter(result => result.status === "rejected");
27
+ if (failures.length) throw new AggregateError(failures.map(result => result.reason), "Icon data download failed");
28
+ const tables = Object.fromEntries(results.map(result => result.value));
29
+ const rows = collectIcons(spells, tables);
30
+ if (rows.length < 500) throw new Error("Icon catalog is unexpectedly small");
31
+ const catalog = { version: 1, product: spells.product, build, locale: spells.locale,
32
+ sources: sources.sort((a,b) => a.table.localeCompare(b.table)), spellCatalogSHA256: hash(JSON.stringify(spells)),
33
+ columns: ["fileDataId","name","aliases","kinds"], icons: rows };
34
+ fs.mkdirSync(path.join(root,"data/icons"),{recursive:true});
35
+ fs.writeFileSync(path.join(root,"data/icons/forever.json"),JSON.stringify(catalog,null,2)+"\n");
36
+ console.log("Saved "+rows.length+" spell/item icons for Forever "+build);