@poe2-toolkit/tree-extractor 0.1.0
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/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/atlas.d.ts +30 -0
- package/dist/atlas.d.ts.map +1 -0
- package/dist/atlas.js +48 -0
- package/dist/atlas.js.map +1 -0
- package/dist/buildCentre.d.ts +17 -0
- package/dist/buildCentre.d.ts.map +1 -0
- package/dist/buildCentre.js +71 -0
- package/dist/buildCentre.js.map +1 -0
- package/dist/buildGraphics.d.ts +42 -0
- package/dist/buildGraphics.d.ts.map +1 -0
- package/dist/buildGraphics.js +124 -0
- package/dist/buildGraphics.js.map +1 -0
- package/dist/buildTree.d.ts +77 -0
- package/dist/buildTree.d.ts.map +1 -0
- package/dist/buildTree.js +299 -0
- package/dist/buildTree.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +67 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/psg.d.ts +52 -0
- package/dist/psg.d.ts.map +1 -0
- package/dist/psg.js +83 -0
- package/dist/psg.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the passive-tree `data.json` from GGPK tables + the `.psg` graph, in
|
|
3
|
+
* the shape `@poe2-toolkit/tree-core`'s `normalizeGggTree` expects. Source of truth:
|
|
4
|
+
* GGPK only.
|
|
5
|
+
*
|
|
6
|
+
* Linkage (verified): a node's `skill` = PassiveSkillGraphId; the `.psg` keys
|
|
7
|
+
* nodes by the same id; PassiveSkills rows join by PassiveSkillGraphId.
|
|
8
|
+
* Positions are computed from group centre + orbit radius + orbit index (radii
|
|
9
|
+
* derived from the live tree, exact). Stats render via the passive + base
|
|
10
|
+
* stat-description files.
|
|
11
|
+
*/
|
|
12
|
+
import { buildStatIndex, renderBlock } from '@poe2-toolkit/ggpk';
|
|
13
|
+
import { parsePsg } from './psg.js';
|
|
14
|
+
// Orbit radii derived from the live tree (zero spread per orbit; angle formula
|
|
15
|
+
// matched 5150/5150 baked positions). Index = orbit.
|
|
16
|
+
const ORBIT_RADII = [0, 82, 164, 334, 488, 657, 839, 250, 1076, 1320];
|
|
17
|
+
const ASC_RADIUS = 1332;
|
|
18
|
+
const PSG_PATH = 'metadata/passiveskillgraph.psg';
|
|
19
|
+
const BASE_STATS_PATH = 'data/statdescriptions/stat_descriptions.csd';
|
|
20
|
+
const PASSIVE_STATS_PATH = 'data/statdescriptions/passive_skill_stat_descriptions.csd';
|
|
21
|
+
/** Read a UTF-16 `.csd` stat-description file from the source into a string. */
|
|
22
|
+
async function readCsd(source, path) {
|
|
23
|
+
const bytes = await source.file(path);
|
|
24
|
+
if (!bytes) {
|
|
25
|
+
throw new Error(`stat descriptions not found: ${path}`);
|
|
26
|
+
}
|
|
27
|
+
return Buffer.from(bytes).toString('utf16le');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the passive-tree export. All data comes from the supplied
|
|
31
|
+
* {@link GgpkSource}; this function performs no I/O of its own beyond what the
|
|
32
|
+
* source serves.
|
|
33
|
+
*/
|
|
34
|
+
export async function buildTree(source) {
|
|
35
|
+
const PassiveSkills = (await source.table('PassiveSkills'));
|
|
36
|
+
const Stats = (await source.table('Stats'));
|
|
37
|
+
const MasteryGroups = (await source.table('PassiveSkillMasteryGroups'));
|
|
38
|
+
const MasteryArt = (await source.table('PassiveSkillTreeMasteryArt'));
|
|
39
|
+
const Characters = (await source.table('Characters'));
|
|
40
|
+
const Ascendancy = (await source.table('Ascendancy'));
|
|
41
|
+
const JewelSlots = (await source.table('PassiveJewelSlots'));
|
|
42
|
+
const ClassOverrides = (await source.table('ClassPassiveSkillOverrides'));
|
|
43
|
+
// Combined stat index: the passive file `include`s the base one, but the
|
|
44
|
+
// parser doesn't follow includes — merge both, passive entries win on conflict.
|
|
45
|
+
const baseIdx = buildStatIndex(await readCsd(source, BASE_STATS_PATH));
|
|
46
|
+
const passiveIdx = buildStatIndex(await readCsd(source, PASSIVE_STATS_PATH));
|
|
47
|
+
const statIdx = { byStat: new Map([...baseIdx.byStat, ...passiveIdx.byStat]) };
|
|
48
|
+
const psgBytes = await source.file(PSG_PATH);
|
|
49
|
+
if (!psgBytes) {
|
|
50
|
+
throw new Error(`passive skill graph not found: ${PSG_PATH}`);
|
|
51
|
+
}
|
|
52
|
+
const psg = parsePsg(psgBytes);
|
|
53
|
+
// --- joins -----------------------------------------------------------------
|
|
54
|
+
const byGraphId = new Map();
|
|
55
|
+
for (const row of PassiveSkills) {
|
|
56
|
+
if (row.PassiveSkillGraphId != null) {
|
|
57
|
+
byGraphId.set(row.PassiveSkillGraphId, row);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Render a passive's stat lines from its Stats foreign rows + Stat1..5Value. */
|
|
61
|
+
function renderStats(row) {
|
|
62
|
+
const statIds = (row.Stats ?? []).map((i) => Stats[i]?.Id).filter((id) => Boolean(id));
|
|
63
|
+
if (!statIds.length) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const vals = [row.Stat1Value, row.Stat2Value, row.Stat3Value, row.Stat4Value, row.Stat5Value];
|
|
67
|
+
// GGG templates carry literal "\n" for multi-line stats; make it a real newline.
|
|
68
|
+
return renderBlock(statIdx, statIds, vals).lines.map((line) => line.replace(/\\n/g, '\n'));
|
|
69
|
+
}
|
|
70
|
+
// A mastery's cluster all carry MasteryGroup; the mastery node itself grants
|
|
71
|
+
// nothing (no stats) and isn't a notable/keystone.
|
|
72
|
+
function isMasteryNode(row) {
|
|
73
|
+
return row.MasteryGroup != null && (!row.Stats || row.Stats.length === 0) && !row.IsNotable && !row.IsKeystone;
|
|
74
|
+
}
|
|
75
|
+
/** Mastery node's background pattern image (extensionless, like GGG ships it). */
|
|
76
|
+
function activeEffectImage(row) {
|
|
77
|
+
if (row.MasteryGroup == null) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const group = MasteryGroups[row.MasteryGroup];
|
|
81
|
+
const art = group?.Art != null ? MasteryArt[group.Art] : undefined;
|
|
82
|
+
return art?.ActiveEffectImage || undefined;
|
|
83
|
+
}
|
|
84
|
+
// Bidirectional adjacency (the `.psg` stores each edge once, directed, so
|
|
85
|
+
// mirror it). `incoming` keeps the raw direction: the nodes that point INTO a
|
|
86
|
+
// given node — that's what activates a mastery.
|
|
87
|
+
const adjacency = new Map();
|
|
88
|
+
const incoming = new Map();
|
|
89
|
+
const addEdge = (a, b) => {
|
|
90
|
+
if (!adjacency.has(a)) {
|
|
91
|
+
adjacency.set(a, new Set());
|
|
92
|
+
}
|
|
93
|
+
adjacency.get(a).add(b);
|
|
94
|
+
};
|
|
95
|
+
for (const node of psg.nodes) {
|
|
96
|
+
for (const target of node.connections) {
|
|
97
|
+
addEdge(node.skillId, target);
|
|
98
|
+
addEdge(target, node.skillId);
|
|
99
|
+
if (!incoming.has(target)) {
|
|
100
|
+
incoming.set(target, new Set());
|
|
101
|
+
}
|
|
102
|
+
incoming.get(target).add(node.skillId);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// --- nodes -----------------------------------------------------------------
|
|
106
|
+
function position(node) {
|
|
107
|
+
const group = psg.groups[node.group];
|
|
108
|
+
const radius = ORBIT_RADII[node.orbit] ?? 0;
|
|
109
|
+
const segments = psg.passivesPerOrbit[node.orbit] || 1;
|
|
110
|
+
const angle = (2 * Math.PI * node.orbitIndex) / segments;
|
|
111
|
+
return { x: group.x + radius * Math.sin(angle), y: group.y - radius * Math.cos(angle) };
|
|
112
|
+
}
|
|
113
|
+
// Real PoE2 classes only: GGPK still carries 4 PoE1 placeholder classes whose
|
|
114
|
+
// ascendancies are all [DNT-UNUSED]. Keep classes with a released ascendancy;
|
|
115
|
+
// remap the class-start indices a node lists onto the filtered class array.
|
|
116
|
+
const DNT = (s) => typeof s === 'string' && s.includes('[DNT');
|
|
117
|
+
const isReleasedAscendancy = (a) => Boolean(a.Name) && !DNT(a.Name) && !a.Disabled;
|
|
118
|
+
const realClassIndices = [...Characters.keys()].filter((index) => Ascendancy.some((a) => a.Character === index && isReleasedAscendancy(a)));
|
|
119
|
+
const classRemap = new Map(realClassIndices.map((oldIndex, newIndex) => [oldIndex, newIndex]));
|
|
120
|
+
const nodes = {};
|
|
121
|
+
for (const pnode of psg.nodes) {
|
|
122
|
+
const row = byGraphId.get(pnode.skillId);
|
|
123
|
+
if (!row) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const { x, y } = position(pnode);
|
|
127
|
+
const ascId = row.Ascendancy != null ? Ascendancy[row.Ascendancy]?.Id : undefined;
|
|
128
|
+
const characters = Array.isArray(row.Characters) ? row.Characters : [];
|
|
129
|
+
const classStart = characters.filter((i) => classRemap.has(i)).map((i) => classRemap.get(i));
|
|
130
|
+
const mastery = isMasteryNode(row);
|
|
131
|
+
const effect = mastery ? activeEffectImage(row) : undefined;
|
|
132
|
+
// A mastery's effect lights when any node it shares a `.psg` edge with (either
|
|
133
|
+
// direction) is allocated. Masteries nothing points into never light, so they
|
|
134
|
+
// get no activators. Pure data: when GGG re-wires the graph this just follows.
|
|
135
|
+
const connections = mastery
|
|
136
|
+
? (incoming.has(pnode.skillId) ? [...(adjacency.get(pnode.skillId) ?? [])] : [])
|
|
137
|
+
: [...(adjacency.get(pnode.skillId) ?? [])];
|
|
138
|
+
nodes[pnode.skillId] = {
|
|
139
|
+
skill: pnode.skillId,
|
|
140
|
+
name: row.Name ?? '',
|
|
141
|
+
icon: row.Icon_DDSFile ?? '',
|
|
142
|
+
stats: renderStats(row),
|
|
143
|
+
group: pnode.group,
|
|
144
|
+
orbit: pnode.orbit,
|
|
145
|
+
orbitIndex: pnode.orbitIndex,
|
|
146
|
+
x: Number(x.toFixed(3)),
|
|
147
|
+
y: Number(y.toFixed(3)),
|
|
148
|
+
out: connections,
|
|
149
|
+
in: [],
|
|
150
|
+
...(row.IsNotable ? { isNotable: true } : {}),
|
|
151
|
+
...(row.IsKeystone ? { isKeystone: true } : {}),
|
|
152
|
+
...(row.IsJewelSocket ? { isJewelSocket: true } : {}),
|
|
153
|
+
...(mastery ? { isMastery: true } : {}),
|
|
154
|
+
...(row.IsAttribute ? { isGenericAttribute: true } : {}),
|
|
155
|
+
...(row.IsAscendancyStartingNode ? { isAscendancyStart: true } : {}),
|
|
156
|
+
...(effect ? { activeEffectImage: effect } : {}),
|
|
157
|
+
...(ascId ? { ascendancyId: ascId } : {}),
|
|
158
|
+
...(classStart.length ? { classStartIndex: classStart } : {}),
|
|
159
|
+
...(row.FlavourText ? { flavourText: row.FlavourText } : {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// --- groups ----------------------------------------------------------------
|
|
163
|
+
const groups = {};
|
|
164
|
+
psg.groups.forEach((group, index) => {
|
|
165
|
+
const orbits = new Set();
|
|
166
|
+
for (const id of group.nodes) {
|
|
167
|
+
const node = nodes[id];
|
|
168
|
+
if (node && node.orbit !== undefined) {
|
|
169
|
+
orbits.add(node.orbit);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
groups[index] = {
|
|
173
|
+
x: Number(group.x.toFixed(3)),
|
|
174
|
+
y: Number(group.y.toFixed(3)),
|
|
175
|
+
orbits: [...orbits].sort((a, b) => a - b),
|
|
176
|
+
nodes: group.nodes,
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
// --- classes + ascendancies ------------------------------------------------
|
|
180
|
+
// Hub centre = centroid of the class-start nodes; ascendancy discs sit 1332u
|
|
181
|
+
// out along each class's axis (derived from GGPK geometry, matches GGG layout).
|
|
182
|
+
const startNodes = Object.values(nodes).filter((n) => n.classStartIndex);
|
|
183
|
+
const hub = {
|
|
184
|
+
x: startNodes.reduce((s, n) => s + n.x, 0) / startNodes.length,
|
|
185
|
+
y: startNodes.reduce((s, n) => s + n.y, 0) / startNodes.length,
|
|
186
|
+
};
|
|
187
|
+
/** Class-start node for a class index (each start serves two classes). */
|
|
188
|
+
function classStartFor(classIndex) {
|
|
189
|
+
return startNodes.find((n) => n.classStartIndex?.includes(classIndex));
|
|
190
|
+
}
|
|
191
|
+
function ascendancyOffset(classIndex) {
|
|
192
|
+
const start = classStartFor(classIndex);
|
|
193
|
+
if (!start) {
|
|
194
|
+
return { x: 0, y: 0 };
|
|
195
|
+
}
|
|
196
|
+
const dx = start.x - hub.x;
|
|
197
|
+
const dy = start.y - hub.y;
|
|
198
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
199
|
+
// The renderer relocates the disc so its start diamond lands at `centre - offset`.
|
|
200
|
+
// To put that diamond on the class quatrefoil (out along +direction), the offset
|
|
201
|
+
// must point the opposite way: -direction * radius.
|
|
202
|
+
return { x: -(dx / len) * ASC_RADIUS, y: -(dy / len) * ASC_RADIUS };
|
|
203
|
+
}
|
|
204
|
+
// Per-class node overrides: GGG swaps some nodes for a class-specific variant
|
|
205
|
+
// (e.g. the Witch sees the generic "Spell Damage" as "Spell and Minion
|
|
206
|
+
// Damage"). Keyed by the base node's graph id -> the override node's, per class.
|
|
207
|
+
const overrideByClass = new Map();
|
|
208
|
+
for (const row of ClassOverrides) {
|
|
209
|
+
const base = PassiveSkills[row.SkillToOverride]?.PassiveSkillGraphId;
|
|
210
|
+
const target = PassiveSkills[row.Override]?.PassiveSkillGraphId;
|
|
211
|
+
if (base == null || target == null) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!overrideByClass.has(row.CharacterToOverrideFor)) {
|
|
215
|
+
overrideByClass.set(row.CharacterToOverrideFor, {});
|
|
216
|
+
}
|
|
217
|
+
overrideByClass.get(row.CharacterToOverrideFor)[base] = target;
|
|
218
|
+
}
|
|
219
|
+
const classes = realClassIndices.map((oldIndex, newIndex) => {
|
|
220
|
+
const character = Characters[oldIndex];
|
|
221
|
+
const offset = ascendancyOffset(newIndex);
|
|
222
|
+
const ascendancies = Ascendancy
|
|
223
|
+
.filter((a) => a.Character === oldIndex && isReleasedAscendancy(a))
|
|
224
|
+
.map((a) => ({ id: a.Id, name: a.Name, offsetX: offset.x, offsetY: offset.y }));
|
|
225
|
+
return {
|
|
226
|
+
name: character.Name,
|
|
227
|
+
base_str: character.BaseStrength,
|
|
228
|
+
base_dex: character.BaseDexterity,
|
|
229
|
+
base_int: character.BaseIntelligence,
|
|
230
|
+
ascendancies,
|
|
231
|
+
overridePairs: overrideByClass.get(oldIndex) ?? {},
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
// Override target nodes aren't in the graph — they only supply display data
|
|
235
|
+
// when a class override swaps them in at the base node's position. Add them as
|
|
236
|
+
// data-only entries (no geometry) so the renderer can resolve their fields.
|
|
237
|
+
for (const pairs of overrideByClass.values()) {
|
|
238
|
+
for (const target of Object.values(pairs)) {
|
|
239
|
+
if (nodes[target]) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const row = byGraphId.get(target);
|
|
243
|
+
if (!row) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
nodes[target] = { skill: target, name: row.Name ?? '', icon: row.Icon_DDSFile ?? '', stats: renderStats(row) };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// --- skillOverrides: the +attribute choices --------------------------------
|
|
250
|
+
//
|
|
251
|
+
// A generic +attribute node shows "+N to any Attribute" (the default state
|
|
252
|
+
// planners use). Allocating it in-game forces Str/Dex/Int. GGG's
|
|
253
|
+
// PassiveSkillOverrides table is empty in PoE2, so synthesise the three here.
|
|
254
|
+
const skillOverrides = {};
|
|
255
|
+
const anyAttribute = Object.values(nodes).find((node) => node.isGenericAttribute)?.stats?.[0] ?? '';
|
|
256
|
+
const attributeValue = Number(anyAttribute.match(/\d+/)?.[0] ?? 5);
|
|
257
|
+
const ATTRIBUTE_CHOICES = [
|
|
258
|
+
{ skill: 26297, key: 'strength', name: 'Strength', grant: 'grantedStrength', icon: 'plusstrength' },
|
|
259
|
+
{ skill: 14927, key: 'dexterity', name: 'Dexterity', grant: 'grantedDexterity', icon: 'plusdexterity' },
|
|
260
|
+
{ skill: 57022, key: 'intelligence', name: 'Intelligence', grant: 'grantedIntelligence', icon: 'plusintelligence' },
|
|
261
|
+
];
|
|
262
|
+
for (const choice of ATTRIBUTE_CHOICES) {
|
|
263
|
+
skillOverrides[choice.skill] = {
|
|
264
|
+
id: `generic_attribute_${choice.key}`,
|
|
265
|
+
skill: choice.skill,
|
|
266
|
+
name: choice.name,
|
|
267
|
+
icon: `Art/2DArt/SkillIcons/passives/${choice.icon}.dds`,
|
|
268
|
+
[choice.grant]: attributeValue,
|
|
269
|
+
stats: [`+${attributeValue} to ${choice.name}`],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
// --- jewel slots + bounds --------------------------------------------------
|
|
273
|
+
const jewelSlots = JewelSlots
|
|
274
|
+
.map((s) => (s.Slot != null ? PassiveSkills[s.Slot]?.PassiveSkillGraphId : undefined))
|
|
275
|
+
.filter((v) => v != null);
|
|
276
|
+
// KNOWN BUG, preserved for parity: the data-only override nodes appended above
|
|
277
|
+
// carry no x/y, so these collapse to NaN and serialize to null.
|
|
278
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
279
|
+
for (const n of Object.values(nodes)) {
|
|
280
|
+
minX = Math.min(minX, n.x);
|
|
281
|
+
minY = Math.min(minY, n.y);
|
|
282
|
+
maxX = Math.max(maxX, n.x);
|
|
283
|
+
maxY = Math.max(maxY, n.y);
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
tree: 'Default',
|
|
287
|
+
classes,
|
|
288
|
+
groups,
|
|
289
|
+
nodes,
|
|
290
|
+
edges: [],
|
|
291
|
+
skillOverrides,
|
|
292
|
+
jewelSlots,
|
|
293
|
+
min_x: Math.round(minX),
|
|
294
|
+
min_y: Math.round(minY),
|
|
295
|
+
max_x: Math.round(maxX),
|
|
296
|
+
max_y: Math.round(maxY),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=buildTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildTree.js","sourceRoot":"","sources":["../src/buildTree.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Command-line entry: extract the passive tree from the patch CDN and write the
|
|
4
|
+
* bundle (data.json + sprite atlases + centre art) to an output directory.
|
|
5
|
+
*
|
|
6
|
+
* Output is PNG + JSON, mirroring the renderer's atlas layout. Converting the
|
|
7
|
+
* PNGs to WebP for the web is a separate publish step, left to the consumer.
|
|
8
|
+
*
|
|
9
|
+
* poe2-tree-extract --patch 4.5.3.1.7 --tables ./tables/English \
|
|
10
|
+
* --cache ./.cache --out ./out/tree
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Command-line entry: extract the passive tree from the patch CDN and write the
|
|
4
|
+
* bundle (data.json + sprite atlases + centre art) to an output directory.
|
|
5
|
+
*
|
|
6
|
+
* Output is PNG + JSON, mirroring the renderer's atlas layout. Converting the
|
|
7
|
+
* PNGs to WebP for the web is a separate publish step, left to the consumer.
|
|
8
|
+
*
|
|
9
|
+
* poe2-tree-extract --patch 4.5.3.1.7 --tables ./tables/English \
|
|
10
|
+
* --cache ./.cache --out ./out/tree
|
|
11
|
+
*/
|
|
12
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { createCdnSource } from '@poe2-toolkit/ggpk';
|
|
15
|
+
import { extractTree } from './index.js';
|
|
16
|
+
/** Parse `--flag value` pairs; throws with usage on a missing required flag. */
|
|
17
|
+
function parseArgs(argv) {
|
|
18
|
+
const flags = new Map();
|
|
19
|
+
for (let i = 0; i < argv.length; i += 2) {
|
|
20
|
+
const key = argv[i];
|
|
21
|
+
const value = argv[i + 1];
|
|
22
|
+
if (key?.startsWith('--') && value !== undefined) {
|
|
23
|
+
flags.set(key.slice(2), value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const required = (name) => {
|
|
27
|
+
const value = flags.get(name);
|
|
28
|
+
if (!value) {
|
|
29
|
+
throw new Error(`missing --${name}\nusage: poe2-tree-extract --patch <v> --tables <dir> --cache <dir> --out <dir>`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
patch: required('patch'),
|
|
35
|
+
tablesDir: required('tables'),
|
|
36
|
+
cacheDir: required('cache'),
|
|
37
|
+
outDir: required('out'),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function main() {
|
|
41
|
+
const { patch, tablesDir, cacheDir, outDir } = parseArgs(process.argv.slice(2));
|
|
42
|
+
const source = await createCdnSource({ patch, cacheDir, tablesDir });
|
|
43
|
+
const { data, graphics, centre } = await extractTree(source);
|
|
44
|
+
const assetsDir = join(outDir, 'assets');
|
|
45
|
+
const centreDir = join(outDir, 'centre');
|
|
46
|
+
mkdirSync(assetsDir, { recursive: true });
|
|
47
|
+
mkdirSync(centreDir, { recursive: true });
|
|
48
|
+
writeFileSync(join(outDir, 'data.json'), JSON.stringify(data));
|
|
49
|
+
for (const [name, atlas] of Object.entries(graphics.atlases)) {
|
|
50
|
+
writeFileSync(join(assetsDir, `${name}.png`), atlas.png);
|
|
51
|
+
writeFileSync(join(assetsDir, `${name}.json`), JSON.stringify({ frames: atlas.frames }));
|
|
52
|
+
}
|
|
53
|
+
for (const [name, png] of Object.entries(centre)) {
|
|
54
|
+
writeFileSync(join(centreDir, `${name}.png`), png);
|
|
55
|
+
}
|
|
56
|
+
const { skills, masteryEffects } = graphics.report;
|
|
57
|
+
process.stdout.write(`tree: ${Object.keys(data.nodes).length} nodes, ${data.classes.length} classes\n` +
|
|
58
|
+
`atlases: ${skills.packed} icons (${skills.missing} missing), ` +
|
|
59
|
+
`${masteryEffects.packed} mastery effects\n` +
|
|
60
|
+
`centre: ${Object.keys(centre).length} images\n` +
|
|
61
|
+
`written to ${outDir}\n`);
|
|
62
|
+
}
|
|
63
|
+
main().catch((error) => {
|
|
64
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @poe2-toolkit/tree-extractor — builds the Path of Exile 2 passive-tree data and sprite
|
|
3
|
+
* atlases from a {@link GgpkSource}, in the shape `@poe2-toolkit/tree-core` consumes.
|
|
4
|
+
*
|
|
5
|
+
* The package is source-agnostic: it never downloads anything itself. Pass any
|
|
6
|
+
* `@poe2-toolkit/ggpk` source (the CDN-backed `createCdnSource`, or your own) and it
|
|
7
|
+
* returns the `data.json` payload, the four sprite atlases, and the centre art —
|
|
8
|
+
* all as plain data, ready to write wherever you publish them.
|
|
9
|
+
*/
|
|
10
|
+
import type { GgpkSource } from '@poe2-toolkit/ggpk';
|
|
11
|
+
import type { CentreSource } from './buildCentre.js';
|
|
12
|
+
import type { GraphicsResult, GraphicsSource } from './buildGraphics.js';
|
|
13
|
+
import type { TreeExport } from './buildTree.js';
|
|
14
|
+
export { parsePsg } from './psg.js';
|
|
15
|
+
export type { Psg, PsgNode, PsgGroup } from './psg.js';
|
|
16
|
+
export { packAtlas, desaturate } from './atlas.js';
|
|
17
|
+
export type { AtlasSprite, AtlasFrame, PackedAtlas } from './atlas.js';
|
|
18
|
+
export { buildTree } from './buildTree.js';
|
|
19
|
+
export type { TreeExport, ExportNode, ExportClass, ExportGroup, ExportAscendancy, } from './buildTree.js';
|
|
20
|
+
export { buildGraphics } from './buildGraphics.js';
|
|
21
|
+
export type { TreeAtlases, GraphicsResult, GraphicsReport, GraphicsSource } from './buildGraphics.js';
|
|
22
|
+
export { buildCentre } from './buildCentre.js';
|
|
23
|
+
export type { CentreSource } from './buildCentre.js';
|
|
24
|
+
/** Everything the extractor produces for one tree version. */
|
|
25
|
+
export interface TreeBundle {
|
|
26
|
+
/** The `data.json` payload (`@poe2-toolkit/tree-core`'s normalize input). */
|
|
27
|
+
data: TreeExport;
|
|
28
|
+
/** The four sprite atlases plus a report of what packed or was skipped. */
|
|
29
|
+
graphics: GraphicsResult;
|
|
30
|
+
/** Centre art keyed by output name (e.g. `portrait-ranger`), PNG bytes. */
|
|
31
|
+
centre: Record<string, Buffer>;
|
|
32
|
+
}
|
|
33
|
+
/** A source that can serve both tables/files and images (tree + graphics). */
|
|
34
|
+
export type TreeSource = GgpkSource & GraphicsSource & CentreSource;
|
|
35
|
+
/**
|
|
36
|
+
* Run the full passive-tree extraction against a source: tree data, atlases and
|
|
37
|
+
* centre art in one pass. The source is queried for everything; no other I/O is
|
|
38
|
+
* performed (writing the result is the caller's concern).
|
|
39
|
+
*/
|
|
40
|
+
export declare function extractTree(source: TreeSource): Promise<TreeBundle>;
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAEnD,OAAO,KAAK,EAAC,cAAc,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAEvE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEvE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEtG,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,IAAI,EAAE,UAAU,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,EAAE,cAAc,CAAC;IACzB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CAAC;AAEpE;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAMzE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @poe2-toolkit/tree-extractor — builds the Path of Exile 2 passive-tree data and sprite
|
|
3
|
+
* atlases from a {@link GgpkSource}, in the shape `@poe2-toolkit/tree-core` consumes.
|
|
4
|
+
*
|
|
5
|
+
* The package is source-agnostic: it never downloads anything itself. Pass any
|
|
6
|
+
* `@poe2-toolkit/ggpk` source (the CDN-backed `createCdnSource`, or your own) and it
|
|
7
|
+
* returns the `data.json` payload, the four sprite atlases, and the centre art —
|
|
8
|
+
* all as plain data, ready to write wherever you publish them.
|
|
9
|
+
*/
|
|
10
|
+
import { buildCentre } from './buildCentre.js';
|
|
11
|
+
import { buildGraphics } from './buildGraphics.js';
|
|
12
|
+
import { buildTree } from './buildTree.js';
|
|
13
|
+
export { parsePsg } from './psg.js';
|
|
14
|
+
export { packAtlas, desaturate } from './atlas.js';
|
|
15
|
+
export { buildTree } from './buildTree.js';
|
|
16
|
+
export { buildGraphics } from './buildGraphics.js';
|
|
17
|
+
export { buildCentre } from './buildCentre.js';
|
|
18
|
+
/**
|
|
19
|
+
* Run the full passive-tree extraction against a source: tree data, atlases and
|
|
20
|
+
* centre art in one pass. The source is queried for everything; no other I/O is
|
|
21
|
+
* performed (writing the result is the caller's concern).
|
|
22
|
+
*/
|
|
23
|
+
export async function extractTree(source) {
|
|
24
|
+
const data = await buildTree(source);
|
|
25
|
+
const graphics = await buildGraphics(source, data);
|
|
26
|
+
const centre = await buildCentre(source);
|
|
27
|
+
return { data, graphics, centre };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,WAAW,EAAG,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAI,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,SAAS,EAAG,MAAM,gBAAgB,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGnD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAS3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAgB/C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAkB;IAClD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAEzC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC"}
|
package/dist/psg.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for GGG's `metadata/passiveskillgraph.psg` — the passive skill graph
|
|
3
|
+
* holding every node's geometry (group, orbit, orbit index) and its outgoing
|
|
4
|
+
* connections.
|
|
5
|
+
*
|
|
6
|
+
* A node's `skillId` here is the PassiveSkillGraphId, the same number the tree
|
|
7
|
+
* uses as `skill` (verified against the live tree: every id matches the graph
|
|
8
|
+
* id, not the table row index). The binary layout is documented in
|
|
9
|
+
* `docs/reference/PSG_FORMAT.md`.
|
|
10
|
+
*/
|
|
11
|
+
/** One node in the passive graph. */
|
|
12
|
+
export interface PsgNode {
|
|
13
|
+
/** PassiveSkillGraphId — joins to the passive tables and the tree's `skill`. */
|
|
14
|
+
skillId: number;
|
|
15
|
+
/** Index of the owning group in {@link Psg.groups}. */
|
|
16
|
+
group: number;
|
|
17
|
+
/** Orbit ring within the group. */
|
|
18
|
+
orbit: number;
|
|
19
|
+
/** Slot index along the orbit. */
|
|
20
|
+
orbitIndex: number;
|
|
21
|
+
/** Skill ids this node points to (the edge is stored once, directed). */
|
|
22
|
+
connections: number[];
|
|
23
|
+
}
|
|
24
|
+
/** One orbit cluster: a world anchor plus its member nodes. */
|
|
25
|
+
export interface PsgGroup {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
/** Proxy groups exist only to anchor geometry and hold no real passives. */
|
|
29
|
+
isProxy: boolean;
|
|
30
|
+
/** Member skill ids, in file order. */
|
|
31
|
+
nodes: number[];
|
|
32
|
+
}
|
|
33
|
+
/** The fully parsed passive skill graph. */
|
|
34
|
+
export interface Psg {
|
|
35
|
+
version: number;
|
|
36
|
+
graphType: number;
|
|
37
|
+
/** Slot count per orbit ring; index = orbit. */
|
|
38
|
+
passivesPerOrbit: number[];
|
|
39
|
+
/** Root node ids (class starts and similar anchors). */
|
|
40
|
+
roots: number[];
|
|
41
|
+
groups: PsgGroup[];
|
|
42
|
+
/** Every node, flattened across all groups, in file order. */
|
|
43
|
+
nodes: PsgNode[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse the raw bytes of a `.psg` file.
|
|
47
|
+
*
|
|
48
|
+
* @throws if the declared root length is implausible or the byte cursor does
|
|
49
|
+
* not land exactly on the end of the buffer (a corrupt or misread file).
|
|
50
|
+
*/
|
|
51
|
+
export declare function parsePsg(buf: Uint8Array): Psg;
|
|
52
|
+
//# sourceMappingURL=psg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"psg.d.ts","sourceRoot":"","sources":["../src/psg.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qCAAqC;AACrC,MAAM,WAAW,OAAO;IACtB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,+DAA+D;AAC/D,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,4CAA4C;AAC5C,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,wDAAwD;IACxD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,8DAA8D;IAC9D,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,GAAG,CAkF7C"}
|
package/dist/psg.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for GGG's `metadata/passiveskillgraph.psg` — the passive skill graph
|
|
3
|
+
* holding every node's geometry (group, orbit, orbit index) and its outgoing
|
|
4
|
+
* connections.
|
|
5
|
+
*
|
|
6
|
+
* A node's `skillId` here is the PassiveSkillGraphId, the same number the tree
|
|
7
|
+
* uses as `skill` (verified against the live tree: every id matches the graph
|
|
8
|
+
* id, not the table row index). The binary layout is documented in
|
|
9
|
+
* `docs/reference/PSG_FORMAT.md`.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Parse the raw bytes of a `.psg` file.
|
|
13
|
+
*
|
|
14
|
+
* @throws if the declared root length is implausible or the byte cursor does
|
|
15
|
+
* not land exactly on the end of the buffer (a corrupt or misread file).
|
|
16
|
+
*/
|
|
17
|
+
export function parsePsg(buf) {
|
|
18
|
+
const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
19
|
+
let o = 0;
|
|
20
|
+
const u8 = () => buf[o++];
|
|
21
|
+
const u32 = () => {
|
|
22
|
+
const v = dv.getUint32(o, true);
|
|
23
|
+
o += 4;
|
|
24
|
+
return v;
|
|
25
|
+
};
|
|
26
|
+
const i32 = () => {
|
|
27
|
+
const v = dv.getInt32(o, true);
|
|
28
|
+
o += 4;
|
|
29
|
+
return v;
|
|
30
|
+
};
|
|
31
|
+
const f32 = () => {
|
|
32
|
+
const v = dv.getFloat32(o, true);
|
|
33
|
+
o += 4;
|
|
34
|
+
return v;
|
|
35
|
+
};
|
|
36
|
+
const version = u8();
|
|
37
|
+
const graphType = u8();
|
|
38
|
+
const orbitCount = u8();
|
|
39
|
+
const passivesPerOrbit = [];
|
|
40
|
+
for (let i = 0; i < orbitCount; i++) {
|
|
41
|
+
passivesPerOrbit.push(u8());
|
|
42
|
+
}
|
|
43
|
+
const rootLength = u32();
|
|
44
|
+
if (rootLength > 1000) {
|
|
45
|
+
throw new Error(`unrealistic root_length ${rootLength}`);
|
|
46
|
+
}
|
|
47
|
+
const roots = [];
|
|
48
|
+
for (let i = 0; i < rootLength; i++) {
|
|
49
|
+
roots.push(u32());
|
|
50
|
+
u32(); // curvature, unused
|
|
51
|
+
}
|
|
52
|
+
const groupLength = u32();
|
|
53
|
+
const groups = [];
|
|
54
|
+
const nodes = [];
|
|
55
|
+
for (let g = 0; g < groupLength; g++) {
|
|
56
|
+
const x = f32();
|
|
57
|
+
const y = f32();
|
|
58
|
+
u32(); // flag
|
|
59
|
+
u32(); // unknown1
|
|
60
|
+
const isProxy = u8() === 1;
|
|
61
|
+
const passiveLength = u32();
|
|
62
|
+
const groupNodeIds = [];
|
|
63
|
+
for (let n = 0; n < passiveLength; n++) {
|
|
64
|
+
const skillId = u32();
|
|
65
|
+
const orbit = u32();
|
|
66
|
+
const orbitIndex = u32();
|
|
67
|
+
const connectionsLength = u32();
|
|
68
|
+
const connections = [];
|
|
69
|
+
for (let c = 0; c < connectionsLength; c++) {
|
|
70
|
+
connections.push(u32());
|
|
71
|
+
i32(); // orbit of the connection, unused
|
|
72
|
+
}
|
|
73
|
+
nodes.push({ skillId, group: g, orbit, orbitIndex, connections });
|
|
74
|
+
groupNodeIds.push(skillId);
|
|
75
|
+
}
|
|
76
|
+
groups.push({ x, y, isProxy, nodes: groupNodeIds });
|
|
77
|
+
}
|
|
78
|
+
if (o !== buf.byteLength) {
|
|
79
|
+
throw new Error(`psg: ${o} bytes consumed, expected ${buf.byteLength}`);
|
|
80
|
+
}
|
|
81
|
+
return { version, graphType, passivesPerOrbit, roots, groups, nodes };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=psg.js.map
|
package/dist/psg.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"psg.js","sourceRoot":"","sources":["../src/psg.ts"],"names":[],"mappings":"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"}
|