@xforge-dev/forge-kit 0.24.0 → 0.24.2
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/README.md +4 -3
- package/dist/legion.d.ts +19 -18
- package/dist/legion.js +27 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,11 +132,12 @@ the lowercased name without spaces, so a stored board never holds names.
|
|
|
132
132
|
### Legion Points (GMS v.272)
|
|
133
133
|
|
|
134
134
|
v.272 removes the board. The points model sits beside it (the board stays for app versions still reading it):
|
|
135
|
-
`legionPoints` (the automatic raid's ranks, B 1 … SSS 5, as many members as the Legion rank holds
|
|
136
|
-
|
|
135
|
+
`legionPoints` (the automatic raid's ranks, B 1 … SSS 5, as many members as the Legion rank holds; `raidSplit` gives
|
|
136
|
+
those and the bonus-only rest, since every member at rank B or higher gives its effect: `effectMembers`), stat levels
|
|
137
|
+
(`LegionStats`, one point a level, what a grid square was: same values, caps 15 inner and 40 outer),
|
|
137
138
|
`allocationProblem`, `statTotals`, `suggestStats`, the two highest event blocks (`appliedEventBlocks`), 10 named
|
|
138
139
|
presets (`SharedPoints`, stored under `points` in the shared row so an older board write leaves it alone) and the 5th
|
|
139
|
-
Champion slot (`championSlotsOpen`).
|
|
140
|
+
Champion slot (`championSlotsOpen`).
|
|
140
141
|
|
|
141
142
|
## Exports
|
|
142
143
|
|
package/dist/legion.d.ts
CHANGED
|
@@ -166,9 +166,9 @@ export declare function sharedBoardFrom(raw: unknown): SharedBoard | null;
|
|
|
166
166
|
export declare function crystalsFrom(raw: unknown): ArtifactCrystal[];
|
|
167
167
|
/** The Legion's 16 stats, keyed as the grid's areas were. */
|
|
168
168
|
export type LegionStatKey = GridKey;
|
|
169
|
-
/** Basic stats
|
|
169
|
+
/** Basic stats: the board's inner areas (the patch notes' order). */
|
|
170
170
|
export declare const BASIC_STATS: readonly ["str", "dex", "int", "luk", "att", "matt", "hp", "mp"];
|
|
171
|
-
/** Expanded stats
|
|
171
|
+
/** Expanded stats: the board's outer areas (the patch notes' order). */
|
|
172
172
|
export declare const EXPANDED_STATS: readonly ["critDamage", "boss", "ied", "critRate", "buffDuration", "normal", "exp", "statusResist"];
|
|
173
173
|
export type ExpandedStat = (typeof EXPANDED_STATS)[number];
|
|
174
174
|
export declare const LEGION_STATS: readonly GridKey[];
|
|
@@ -190,24 +190,15 @@ export declare const LEGION_LEVEL_RANKS: readonly LegionLevelRank[];
|
|
|
190
190
|
export declare function legionLevelRank(level: number): LegionLevelRank | null;
|
|
191
191
|
/** Members the automatic Legion Raid takes at this Legion level (Nameless I's 9 below it). */
|
|
192
192
|
export declare const raidMembers: (level: number) => number;
|
|
193
|
-
/**
|
|
194
|
-
export declare const STAT_MAX_LEVEL: Record<GridKey, number>;
|
|
195
|
-
/** The expanded stats' shared level cap by Legion level (the guide's 유니온 레벨별 확장 스탯 최대 레벨). */
|
|
196
|
-
export declare const EXPANDED_CAPS: readonly {
|
|
197
|
-
level: number;
|
|
198
|
-
cap: number;
|
|
199
|
-
}[];
|
|
200
|
-
/** The expanded stats' level cap at this Legion level: 0 below 2,000. */
|
|
201
|
-
export declare function expandedCap(legionLevel: number): number;
|
|
202
|
-
/** What one level of each stat gives. */
|
|
193
|
+
/** What one level of each stat gives: a grid square's value. */
|
|
203
194
|
export declare const STAT_PER_LEVEL: Record<GridKey, number>;
|
|
204
|
-
/**
|
|
205
|
-
export declare const
|
|
195
|
+
/** Each stat's highest level: its grid area's size. */
|
|
196
|
+
export declare const STAT_MAX_LEVEL: Record<GridKey, number>;
|
|
206
197
|
/** Stat levels, one per point: absent or 0 when nothing is spent. */
|
|
207
198
|
export type LegionStats = Partial<Record<GridKey, number>>;
|
|
208
199
|
export declare const isExpandedStat: (k: GridKey) => k is ExpandedStat;
|
|
209
|
-
/** A stat's level cap at this Legion level:
|
|
210
|
-
export declare const statCap: (k: GridKey,
|
|
200
|
+
/** A stat's level cap at this Legion level: its grid area's size at every Legion level, as the board had it. */
|
|
201
|
+
export declare const statCap: (k: GridKey, _legionLevel: number) => number;
|
|
211
202
|
/** Whether a stat is open at this Legion level. */
|
|
212
203
|
export declare const statUnlocked: (k: GridKey, legionLevel: number) => boolean;
|
|
213
204
|
/** One member as the points model reads it: `rank` null below B (no points, no effect). */
|
|
@@ -220,10 +211,20 @@ export type PointsMember = {
|
|
|
220
211
|
* The caller works out ranks (Zero's own levels) and Legion level.
|
|
221
212
|
*/
|
|
222
213
|
export declare function raidComposition<M extends PointsMember>(members: readonly M[], legionLevel: number): M[];
|
|
214
|
+
/**
|
|
215
|
+
* The Legion split in two: `counted`, the members the raid takes (their ranks are the Legion Points), and `bonus`, the
|
|
216
|
+
* other members at rank B or higher, whose effects apply too but who give no points. Members below B are in neither.
|
|
217
|
+
*/
|
|
218
|
+
export declare function raidSplit<M extends PointsMember>(members: readonly M[], legionLevel: number): {
|
|
219
|
+
counted: M[];
|
|
220
|
+
bonus: M[];
|
|
221
|
+
};
|
|
222
|
+
/** Members whose effects apply: every member at rank B or higher, counted by the raid or not. */
|
|
223
|
+
export declare const effectMembers: <M extends PointsMember>(members: readonly M[]) => M[];
|
|
223
224
|
/** The two event blocks that apply: the highest values among those owned. */
|
|
224
225
|
export declare function appliedEventBlocks(owned: readonly EventBlock[]): EventBlock[];
|
|
225
|
-
/** Legion Points to spend: the
|
|
226
|
-
export declare function legionPoints(members: readonly PointsMember[], legionLevel: number
|
|
226
|
+
/** Legion Points to spend: the ranks of the members the raid counts. Event blocks add ATT, not points. */
|
|
227
|
+
export declare function legionPoints(members: readonly PointsMember[], legionLevel: number): number;
|
|
227
228
|
/** Points the stat levels spend. */
|
|
228
229
|
export declare const pointsSpent: (stats: LegionStats) => number;
|
|
229
230
|
/** What each stat gives at its level (only stats with a level). */
|
package/dist/legion.js
CHANGED
|
@@ -195,9 +195,9 @@ export function crystalsFrom(raw) {
|
|
|
195
195
|
return { level, lines };
|
|
196
196
|
});
|
|
197
197
|
}
|
|
198
|
-
/** Basic stats
|
|
198
|
+
/** Basic stats: the board's inner areas (the patch notes' order). */
|
|
199
199
|
export const BASIC_STATS = ['str', 'dex', 'int', 'luk', 'att', 'matt', 'hp', 'mp'];
|
|
200
|
-
/** Expanded stats
|
|
200
|
+
/** Expanded stats: the board's outer areas (the patch notes' order). */
|
|
201
201
|
export const EXPANDED_STATS = [
|
|
202
202
|
'critDamage', 'boss', 'ied', 'critRate', 'buffDuration', 'normal', 'exp', 'statusResist',
|
|
203
203
|
];
|
|
@@ -226,37 +226,17 @@ export function legionLevelRank(level) {
|
|
|
226
226
|
}
|
|
227
227
|
/** Members the automatic Legion Raid takes at this Legion level (Nameless I's 9 below it). */
|
|
228
228
|
export const raidMembers = (level) => (legionLevelRank(level) ?? LEGION_LEVEL_RANKS[0]).members;
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
/**
|
|
234
|
-
export const STAT_MAX_LEVEL = Object.fromEntries(GRID_KEYS.map((k) => [k, gridMax(k)]));
|
|
235
|
-
/** The expanded stats' shared level cap by Legion level (the guide's 유니온 레벨별 확장 스탯 최대 레벨). */
|
|
236
|
-
export const EXPANDED_CAPS = [
|
|
237
|
-
{ level: 2000, cap: 5 }, { level: 3000, cap: 10 }, { level: 4000, cap: 20 }, { level: 5000, cap: 30 }, { level: 6000, cap: 40 },
|
|
238
|
-
];
|
|
239
|
-
/** The expanded stats' level cap at this Legion level: 0 below 2,000. */
|
|
240
|
-
export function expandedCap(legionLevel) {
|
|
241
|
-
let cap = 0;
|
|
242
|
-
for (const c of EXPANDED_CAPS)
|
|
243
|
-
if (legionLevel >= c.level)
|
|
244
|
-
cap = c.cap;
|
|
245
|
-
return cap;
|
|
246
|
-
}
|
|
247
|
-
// ┌─ ASSUMED VALUES ────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
248
|
-
// │ Not in the v.272 notes nor any KMS source found: what a stat level gives. Each is taken as one old grid square │
|
|
249
|
-
// │ (KMS says the stats are "the same types as the old occupation effects", and the caps and 440 total match the │
|
|
250
|
-
// │ board). Nor whether event blocks give points: taken as none (the notes only say they apply automatically and │
|
|
251
|
-
// │ don't count as members). Change them here only; everything below reads them. │
|
|
252
|
-
// └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
253
|
-
/** What one level of each stat gives. */
|
|
229
|
+
// The rules are the board's, without the board: a stat level is what a grid square was (same value per level, the
|
|
230
|
+
// same caps: inner stats 15, outer stats 40, open from the start as every area was). Every member at rank B or higher
|
|
231
|
+
// gives its effect; only the members the raid counts (the top members by level, as many as the board's slots were)
|
|
232
|
+
// give points.
|
|
233
|
+
/** What one level of each stat gives: a grid square's value. */
|
|
254
234
|
export const STAT_PER_LEVEL = { ...GRID_PER_SQUARE };
|
|
255
|
-
/**
|
|
256
|
-
export const
|
|
235
|
+
/** Each stat's highest level: its grid area's size. */
|
|
236
|
+
export const STAT_MAX_LEVEL = Object.fromEntries(GRID_KEYS.map((k) => [k, gridMax(k)]));
|
|
257
237
|
export const isExpandedStat = (k) => EXPANDED_STATS.includes(k);
|
|
258
|
-
/** A stat's level cap at this Legion level:
|
|
259
|
-
export const statCap = (k,
|
|
238
|
+
/** A stat's level cap at this Legion level: its grid area's size at every Legion level, as the board had it. */
|
|
239
|
+
export const statCap = (k, _legionLevel) => STAT_MAX_LEVEL[k];
|
|
260
240
|
/** Whether a stat is open at this Legion level. */
|
|
261
241
|
export const statUnlocked = (k, legionLevel) => statCap(k, legionLevel) > 0;
|
|
262
242
|
/**
|
|
@@ -266,14 +246,24 @@ export const statUnlocked = (k, legionLevel) => statCap(k, legionLevel) > 0;
|
|
|
266
246
|
export function raidComposition(members, legionLevel) {
|
|
267
247
|
return members.filter((m) => m.rank).sort((a, b) => b.level - a.level).slice(0, raidMembers(legionLevel));
|
|
268
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* The Legion split in two: `counted`, the members the raid takes (their ranks are the Legion Points), and `bonus`, the
|
|
251
|
+
* other members at rank B or higher, whose effects apply too but who give no points. Members below B are in neither.
|
|
252
|
+
*/
|
|
253
|
+
export function raidSplit(members, legionLevel) {
|
|
254
|
+
const counted = raidComposition(members, legionLevel);
|
|
255
|
+
const taken = new Set(counted);
|
|
256
|
+
return { counted, bonus: members.filter((m) => m.rank && !taken.has(m)).sort((a, b) => b.level - a.level) };
|
|
257
|
+
}
|
|
258
|
+
/** Members whose effects apply: every member at rank B or higher, counted by the raid or not. */
|
|
259
|
+
export const effectMembers = (members) => members.filter((m) => m.rank);
|
|
269
260
|
/** The two event blocks that apply: the highest values among those owned. */
|
|
270
261
|
export function appliedEventBlocks(owned) {
|
|
271
262
|
return [...owned].sort((a, b) => b.value - a.value).slice(0, MAX_EVENT_BLOCKS);
|
|
272
263
|
}
|
|
273
|
-
/** Legion Points to spend: the
|
|
274
|
-
export function legionPoints(members, legionLevel
|
|
275
|
-
return raidComposition(members, legionLevel).reduce((t, m) => t + RANK_POINTS[m.rank], 0)
|
|
276
|
-
+ appliedEventBlocks(owned).reduce((t, b) => t + eventBlockPoints(b), 0);
|
|
264
|
+
/** Legion Points to spend: the ranks of the members the raid counts. Event blocks add ATT, not points. */
|
|
265
|
+
export function legionPoints(members, legionLevel) {
|
|
266
|
+
return raidComposition(members, legionLevel).reduce((t, m) => t + RANK_POINTS[m.rank], 0);
|
|
277
267
|
}
|
|
278
268
|
/** Points the stat levels spend. */
|
|
279
269
|
export const pointsSpent = (stats) => Object.values(stats).reduce((t, n) => t + (n ?? 0), 0);
|
|
@@ -292,11 +282,8 @@ export function allocationProblem(a) {
|
|
|
292
282
|
if (!n)
|
|
293
283
|
continue;
|
|
294
284
|
const cap = statCap(k, a.legionLevel);
|
|
295
|
-
if (n > cap)
|
|
296
|
-
|
|
297
|
-
return `${STAT_LABELS[k]} opens at Legion level ${EXPANDED_CAPS[0].level.toLocaleString('en-US')}.`;
|
|
298
|
-
return `${STAT_LABELS[k]} goes up to level ${cap}${cap < STAT_MAX_LEVEL[k] ? ' at this Legion level' : ''}.`;
|
|
299
|
-
}
|
|
285
|
+
if (n > cap)
|
|
286
|
+
return `${STAT_LABELS[k]} goes up to level ${cap}.`;
|
|
300
287
|
}
|
|
301
288
|
const spent = pointsSpent(a.stats);
|
|
302
289
|
if (spent > a.points)
|