wuira 0.2.4 → 0.2.6
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.
- package/CHANGELOG.md +33 -0
- package/README.md +62 -831
- package/data/icons/forever.json +90051 -0
- package/docs/icons.md +28 -0
- package/docs/source-converters.md +27 -0
- package/lib/datasources/DataSource.d.ts +8 -0
- package/lib/datasources/DataSource.lua +7 -0
- package/lib/datasources/SourceConverter.d.ts +30 -0
- package/lib/datasources/SourceConverter.lua +475 -0
- package/lib/datasources/UnitInfo.d.ts +6 -0
- package/lib/datasources/UnitInfo.lua +347 -15
- package/lib/datasources/index.d.ts +3 -0
- package/lib/datasources/index.lua +4 -0
- package/lib/icons/index.d.ts +45634 -0
- package/lib/icons/index.lua +3 -0
- package/lib/index.d.ts +1 -0
- package/lib/store/index.lua +20 -18
- package/package.json +20 -4
- package/tools/build-library.mjs +1 -1
- package/tools/generate-icons.mjs +10 -0
- package/tools/icon-data.mjs +59 -0
- package/tools/update-icons.mjs +36 -0
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";
|
package/lib/store/index.lua
CHANGED
|
@@ -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
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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:
|
|
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:
|
|
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
|
|
788
|
-
|
|
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:
|
|
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:
|
|
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
|
-
"
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "git+https://github.com/KerimCETINBAS/wuira.git"
|
|
6
|
+
},
|
|
7
|
+
"version": "0.2.6",
|
|
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"
|
package/tools/build-library.mjs
CHANGED
|
@@ -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);
|