@xforge-dev/forge-kit 0.24.0 → 0.24.1

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 CHANGED
@@ -133,10 +133,10 @@ the lowercased name without spaces, so a stored board never holds names.
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
135
  `legionPoints` (the automatic raid's ranks, B 1 … SSS 5, as many members as the Legion rank holds), stat levels
136
- (`LegionStats`, one point a level, `statCap` 15 for basic stats and up to 40 for expanded ones by Legion level),
136
+ (`LegionStats`, one point a level, what a grid square was: same values, caps 15 inner and 40 outer),
137
137
  `allocationProblem`, `statTotals`, `suggestStats`, the two highest event blocks (`appliedEventBlocks`), 10 named
138
138
  presets (`SharedPoints`, stored under `points` in the shared row so an older board write leaves it alone) and the 5th
139
- Champion slot (`championSlotsOpen`). Values not yet confirmed sit in one marked block in `src/legion.ts`.
139
+ Champion slot (`championSlotsOpen`).
140
140
 
141
141
  ## Exports
142
142
 
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, open from the start (the patch notes' order). */
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, opening together at Legion level 2,000 (the patch notes' order). */
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
- /** Each stat's highest level. */
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
- /** Points an applied event block gives. */
205
- export declare const eventBlockPoints: (_b: EventBlock) => number;
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: basic stats 15, expanded stats as the Legion level allows. */
210
- export declare const statCap: (k: GridKey, legionLevel: number) => number;
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). */
@@ -222,8 +213,8 @@ export type PointsMember = {
222
213
  export declare function raidComposition<M extends PointsMember>(members: readonly M[], legionLevel: number): M[];
223
214
  /** The two event blocks that apply: the highest values among those owned. */
224
215
  export declare function appliedEventBlocks(owned: readonly EventBlock[]): EventBlock[];
225
- /** Legion Points to spend: the raid's ranks (and the applied event blocks', none as far as known). */
226
- export declare function legionPoints(members: readonly PointsMember[], legionLevel: number, owned?: readonly EventBlock[]): number;
216
+ /** Legion Points to spend: the ranks of the members the raid counts. Event blocks add ATT, not points. */
217
+ export declare function legionPoints(members: readonly PointsMember[], legionLevel: number): number;
227
218
  /** Points the stat levels spend. */
228
219
  export declare const pointsSpent: (stats: LegionStats) => number;
229
220
  /** 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, open from the start (the patch notes' order). */
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, opening together at Legion level 2,000 (the patch notes' order). */
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,16 @@ 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
- // Confirmed from KMS 1.2.416 (the same revamp, 2026-06-18; maplestory.nexon.com News/Update/806 and the official game
230
- // guide, Guide/N23GameInformation/Articles/407): basic stats go to level 15, expanded stats to 40; the 8 expanded stats
231
- // open together, their cap rising with Legion level (below); points are only the raid's ranks (a KMST screenshot:
232
- // Supreme I, 41 members, 202 points).
233
- /** Each stat's highest level. */
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), and only the members the raid
231
+ // counts (the top members by Legion level, as the board's slots were) give points and their effects.
232
+ /** What one level of each stat gives: a grid square's value. */
254
233
  export const STAT_PER_LEVEL = { ...GRID_PER_SQUARE };
255
- /** Points an applied event block gives. */
256
- export const eventBlockPoints = (_b) => 0;
234
+ /** Each stat's highest level: its grid area's size. */
235
+ export const STAT_MAX_LEVEL = Object.fromEntries(GRID_KEYS.map((k) => [k, gridMax(k)]));
257
236
  export const isExpandedStat = (k) => EXPANDED_STATS.includes(k);
258
- /** A stat's level cap at this Legion level: basic stats 15, expanded stats as the Legion level allows. */
259
- export const statCap = (k, legionLevel) => (isExpandedStat(k) ? Math.min(expandedCap(legionLevel), STAT_MAX_LEVEL[k]) : STAT_MAX_LEVEL[k]);
237
+ /** A stat's level cap at this Legion level: its grid area's size at every Legion level, as the board had it. */
238
+ export const statCap = (k, _legionLevel) => STAT_MAX_LEVEL[k];
260
239
  /** Whether a stat is open at this Legion level. */
261
240
  export const statUnlocked = (k, legionLevel) => statCap(k, legionLevel) > 0;
262
241
  /**
@@ -270,10 +249,9 @@ export function raidComposition(members, legionLevel) {
270
249
  export function appliedEventBlocks(owned) {
271
250
  return [...owned].sort((a, b) => b.value - a.value).slice(0, MAX_EVENT_BLOCKS);
272
251
  }
273
- /** Legion Points to spend: the raid's ranks (and the applied event blocks', none as far as known). */
274
- export function legionPoints(members, legionLevel, owned = []) {
275
- return raidComposition(members, legionLevel).reduce((t, m) => t + RANK_POINTS[m.rank], 0)
276
- + appliedEventBlocks(owned).reduce((t, b) => t + eventBlockPoints(b), 0);
252
+ /** Legion Points to spend: the ranks of the members the raid counts. Event blocks add ATT, not points. */
253
+ export function legionPoints(members, legionLevel) {
254
+ return raidComposition(members, legionLevel).reduce((t, m) => t + RANK_POINTS[m.rank], 0);
277
255
  }
278
256
  /** Points the stat levels spend. */
279
257
  export const pointsSpent = (stats) => Object.values(stats).reduce((t, n) => t + (n ?? 0), 0);
@@ -292,11 +270,8 @@ export function allocationProblem(a) {
292
270
  if (!n)
293
271
  continue;
294
272
  const cap = statCap(k, a.legionLevel);
295
- if (n > cap) {
296
- if (!cap)
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
- }
273
+ if (n > cap)
274
+ return `${STAT_LABELS[k]} goes up to level ${cap}.`;
300
275
  }
301
276
  const spent = pointsSpent(a.stats);
302
277
  if (spent > a.points)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xforge-dev/forge-kit",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "description": "Shared auth, app registry and UI for xForge apps",
5
5
  "license": "MIT",
6
6
  "repository": {