@archivist-gg/dnd5e 0.3.13 → 0.3.17

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.
Files changed (111) hide show
  1. package/dist/background/background.codec.js +5 -5
  2. package/dist/background/background.parser.js +4 -4
  3. package/dist/background/background.schema.js +3 -3
  4. package/dist/{chunk-ZMJQUZQH.js → chunk-2JSQE3DD.js} +11 -0
  5. package/dist/{chunk-Q6UOHCCT.js → chunk-2UDSN6VW.js} +1 -1
  6. package/dist/chunk-3LWMFYKS.js +57 -0
  7. package/dist/{chunk-MUIU7RZ3.js → chunk-3MGQFFNE.js} +1 -1
  8. package/dist/{chunk-T5GZBKIQ.js → chunk-3VZZDKRA.js} +114 -27
  9. package/dist/{chunk-BRGLDWUP.js → chunk-5MXFW4O3.js} +7 -3
  10. package/dist/{chunk-RQNJXADQ.js → chunk-6EI737IO.js} +1 -1
  11. package/dist/{chunk-EL6QPF6I.js → chunk-7ATNGKBS.js} +1 -1
  12. package/dist/{chunk-XY6X2APM.js → chunk-7Q2VPKCD.js} +17 -0
  13. package/dist/{chunk-4SLOJXJ4.js → chunk-7QN7XSAK.js} +1 -1
  14. package/dist/{chunk-MC5YJ4PB.js → chunk-AZRGFUSR.js} +1 -1
  15. package/dist/{chunk-HJQNK3J5.js → chunk-B3DP42OM.js} +1 -4
  16. package/dist/{chunk-JNNNBEUH.js → chunk-DOXSFYLH.js} +7 -2
  17. package/dist/{chunk-X7VZY4ON.js → chunk-FANQVQ3D.js} +2 -2
  18. package/dist/{chunk-PX25R3DK.js → chunk-G6PBI2BU.js} +1 -1
  19. package/dist/chunk-HTYMW5CH.js +228 -0
  20. package/dist/{chunk-VDPRBUU5.js → chunk-JCMUVZIK.js} +1 -1
  21. package/dist/{chunk-Q5XTEYHD.js → chunk-JSM6JFZR.js} +15 -8
  22. package/dist/chunk-KR6JF3BF.js +14 -0
  23. package/dist/{chunk-J53ERF6K.js → chunk-LF5AGJA4.js} +1 -1
  24. package/dist/{chunk-XJRNEQPD.js → chunk-MBX25B6D.js} +1 -1
  25. package/dist/{chunk-5KGV6TF7.js → chunk-OFCSP6MT.js} +1 -1
  26. package/dist/{chunk-QEO7LYTG.js → chunk-ONTGZR5T.js} +127 -71
  27. package/dist/{chunk-MWCCGPG3.js → chunk-OOSRUJBI.js} +3 -2
  28. package/dist/{chunk-LVX6TQ3R.js → chunk-PKX6TGRE.js} +2 -2
  29. package/dist/{chunk-QSBJ6NEM.js → chunk-PQZ4LM4Z.js} +1 -1
  30. package/dist/chunk-PXVNAY7T.js +84 -0
  31. package/dist/chunk-QH36K2PU.js +158 -0
  32. package/dist/{chunk-M2OWVAZG.js → chunk-QZSWN2KT.js} +1 -1
  33. package/dist/{chunk-6ZY3U6BQ.js → chunk-RYLXAYIQ.js} +1 -1
  34. package/dist/{chunk-UE6LKRXQ.js → chunk-T6QIF5XR.js} +16 -0
  35. package/dist/{chunk-4BZ47YJ4.js → chunk-TULQ4FFX.js} +32 -11
  36. package/dist/{chunk-PLOSP4XQ.js → chunk-UUBJCNQU.js} +11 -2
  37. package/dist/{chunk-DPM6V7YX.js → chunk-VRV3NNTA.js} +1 -1
  38. package/dist/{chunk-R2V5LN3I.js → chunk-Y5VVP73C.js} +1 -1
  39. package/dist/{chunk-FPGXFD2M.js → chunk-YD2VAHA6.js} +2 -2
  40. package/dist/{chunk-G7ED5H2G.js → chunk-YKL6SHRJ.js} +1 -1
  41. package/dist/class/class.codec.js +6 -6
  42. package/dist/class/class.parser.js +5 -5
  43. package/dist/class/class.schema.d.ts +8 -0
  44. package/dist/class/class.schema.js +4 -4
  45. package/dist/class/class.standard-array.d.ts +16 -0
  46. package/dist/class/class.standard-array.js +20 -0
  47. package/dist/class/class.types.d.ts +3 -0
  48. package/dist/dnd/amount-text.d.ts +38 -0
  49. package/dist/dnd/amount-text.js +13 -0
  50. package/dist/dnd/resource-words.d.ts +4 -0
  51. package/dist/dnd/resource-words.js +30 -0
  52. package/dist/feat/feat.codec.js +5 -5
  53. package/dist/feat/feat.parser.js +4 -4
  54. package/dist/feat/feat.schema.d.ts +25 -0
  55. package/dist/feat/feat.schema.js +3 -3
  56. package/dist/index.js +332 -70
  57. package/dist/monster/monster.parser.js +5 -5
  58. package/dist/monster/monster.schema.js +4 -4
  59. package/dist/optional-feature/optional-feature.codec.js +5 -4
  60. package/dist/optional-feature/optional-feature.parser.js +4 -3
  61. package/dist/optional-feature/optional-feature.schema.d.ts +26 -0
  62. package/dist/optional-feature/optional-feature.schema.js +3 -2
  63. package/dist/pc/pc.additional-spells.js +2 -2
  64. package/dist/pc/pc.amount-bindings.d.ts +32 -0
  65. package/dist/pc/pc.amount-bindings.js +23 -0
  66. package/dist/pc/pc.decision-engine.d.ts +3 -0
  67. package/dist/pc/pc.decision-engine.js +14 -13
  68. package/dist/pc/pc.entry-overrides.js +3 -3
  69. package/dist/pc/pc.equipment.js +6 -5
  70. package/dist/pc/pc.feature-effects.d.ts +12 -0
  71. package/dist/pc/pc.feature-effects.js +1 -1
  72. package/dist/pc/pc.infusions.d.ts +9 -1
  73. package/dist/pc/pc.infusions.js +3 -3
  74. package/dist/pc/pc.member-choices.d.ts +90 -0
  75. package/dist/pc/pc.member-choices.js +32 -0
  76. package/dist/pc/pc.parser.js +1 -1
  77. package/dist/pc/pc.pools.d.ts +7 -0
  78. package/dist/pc/pc.pools.js +5 -3
  79. package/dist/pc/pc.proficiencies.js +13 -12
  80. package/dist/pc/pc.proficiency-query.js +6 -0
  81. package/dist/pc/pc.recalc.js +75 -58
  82. package/dist/pc/pc.resolver.d.ts +2 -1
  83. package/dist/pc/pc.resolver.js +13 -12
  84. package/dist/pc/pc.rest.js +4 -4
  85. package/dist/pc/pc.schema.d.ts +29 -2
  86. package/dist/pc/pc.schema.js +1 -1
  87. package/dist/pc/pc.types.d.ts +45 -2
  88. package/dist/pc/pc.weapon-grant.d.ts +15 -0
  89. package/dist/pc/pool-layout.d.ts +3 -9
  90. package/dist/pc/pool-layout.js +1 -3
  91. package/dist/pc/resource-equivalents.d.ts +44 -0
  92. package/dist/pc/spell-source.js +4 -9
  93. package/dist/race/race.codec.js +6 -6
  94. package/dist/race/race.parser.js +5 -5
  95. package/dist/race/race.schema.js +4 -4
  96. package/dist/schemas/choice-schema.js +2 -2
  97. package/dist/schemas/feature-effect-schema.d.ts +25 -0
  98. package/dist/schemas/feature-effect-schema.js +1 -1
  99. package/dist/schemas/feature-schema.js +3 -3
  100. package/dist/spell/spell.codec.js +2 -2
  101. package/dist/spell/spell.parser.d.ts +6 -1
  102. package/dist/spell/spell.parser.js +5 -3
  103. package/dist/spell/spell.types.d.ts +10 -0
  104. package/dist/subclass/subclass.codec.js +6 -6
  105. package/dist/subclass/subclass.parser.js +5 -5
  106. package/dist/subclass/subclass.schema.js +4 -4
  107. package/dist/types/choice.d.ts +5 -0
  108. package/dist/types/feature-effect.d.ts +6 -0
  109. package/dist/types/optional-feature.types.d.ts +5 -0
  110. package/dist/types/selection-pool.d.ts +2 -2
  111. package/package.json +1 -1
@@ -251,6 +251,21 @@ var knownSpellObjectSchema = z.object({
251
251
  always_prepared: z.boolean().optional()
252
252
  }).strict();
253
253
  var knownSpellEntrySchema = z.union([z.string().min(1), knownSpellObjectSchema]);
254
+ var sheetViewFilterSchema = z.object({
255
+ show: z.array(z.string().min(1)).optional(),
256
+ hide: z.array(z.string().min(1)).optional()
257
+ });
258
+ var sheetPoolViewSchema = z.object({
259
+ show: z.enum(["known", "granted", "not-known"]).optional().catch(void 0),
260
+ off: z.record(z.string(), z.array(z.string())).optional(),
261
+ folded: z.array(z.string().min(1)).optional()
262
+ });
263
+ var sheetStateSchema = z.object({
264
+ layout: z.object({ saves: z.enum(["section"]).optional().catch(void 0) }).optional(),
265
+ favorites: z.array(z.string().min(1)).optional(),
266
+ filters: z.object({ actions: sheetViewFilterSchema.optional(), passive: sheetViewFilterSchema.optional() }).optional(),
267
+ pools: z.record(z.string(), sheetPoolViewSchema).optional()
268
+ });
254
269
  var characterSchema = z.object({
255
270
  name: z.string().min(1),
256
271
  edition: z.enum(["2014", "2024"]),
@@ -323,6 +338,7 @@ var characterSchema = z.object({
323
338
  vulnerabilities: z.array(z.string()).default([]),
324
339
  condition_immunities: z.array(z.string()).default([])
325
340
  }).partial().optional(),
341
+ sheet: sheetStateSchema.optional(),
326
342
  state: characterStateSchema
327
343
  });
328
344
 
@@ -20,6 +20,7 @@ function emptyFeatureEffectTotals() {
20
20
  initiative_bonus: 0,
21
21
  initiative_terms: [],
22
22
  skill_bonus_terms: [],
23
+ save_bonus_terms: [],
23
24
  half_proficiency_terms: [],
24
25
  hp_per_level_bonus: 0,
25
26
  hp_per_level_terms: [],
@@ -118,7 +119,7 @@ function computeFeatureEffects(features, opts) {
118
119
  if (!foldsNow(rf, (_a = opts == null ? void 0 : opts.activeBuffs) != null ? _a : /* @__PURE__ */ new Set())) continue;
119
120
  const level = (_b = opts == null ? void 0 : opts.levelFor) == null ? void 0 : _b.call(opts, rf.source);
120
121
  for (const eff of selfEffectsOf(rf.feature)) {
121
- applyEffect(out, eff, (_c = rf.feature.name) != null ? _c : "Feature", level);
122
+ applyEffect(out, columnScaledRider(eff, rf, level, opts == null ? void 0 : opts.tableDiceFor), (_c = rf.feature.name) != null ? _c : "Feature", level, rf.source);
122
123
  }
123
124
  }
124
125
  return out;
@@ -182,13 +183,23 @@ function scaled(base, key, scalesAt, level) {
182
183
  }
183
184
  return best;
184
185
  }
186
+ function columnScaledRider(eff, rf, level, tableDiceFor) {
187
+ if (eff.kind !== "damage-bonus" || level === void 0 || !tableDiceFor) return eff;
188
+ const src = rf.source;
189
+ const column = rf.feature.name;
190
+ if (src.kind !== "class" && src.kind !== "subclass" || !column) return eff;
191
+ const atGain = tableDiceFor(src, column, src.level);
192
+ if (!atGain || atGain.toLowerCase() !== eff.amount.trim().toLowerCase()) return eff;
193
+ const now = tableDiceFor(src, column, level);
194
+ return now ? { ...eff, amount: now } : eff;
195
+ }
185
196
  function riderScope(appliesTo) {
186
197
  if (appliesTo === void 0 || appliesTo === "all" || appliesTo === "spell") return void 0;
187
198
  const words = Array.isArray(appliesTo) ? appliesTo : [appliesTo];
188
199
  return words.includes("weapon") ? void 0 : [...words];
189
200
  }
190
- function applyEffect(out, eff, label, level) {
191
- var _a, _b, _c, _d, _e, _f, _g, _h;
201
+ function applyEffect(out, eff, label, level, owner) {
202
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
192
203
  switch (eff.kind) {
193
204
  case "initiative-bonus":
194
205
  out.initiative_bonus += eff.value;
@@ -202,10 +213,19 @@ function applyEffect(out, eff, label, level) {
202
213
  label
203
214
  });
204
215
  break;
216
+ case "save-bonus":
217
+ out.save_bonus_terms.push({
218
+ saves: [...(_b = eff.saves) != null ? _b : []],
219
+ ability: eff.ability,
220
+ minimum: (_c = eff.minimum) != null ? _c : 0,
221
+ ...eff.inactive_while ? { inactiveWhile: eff.inactive_while } : {},
222
+ label
223
+ });
224
+ break;
205
225
  case "half-proficiency":
206
226
  out.half_proficiency_terms.push({
207
- skills: ((_b = eff.skills) != null ? _b : []).map(toProfSlug),
208
- round: (_c = eff.round) != null ? _c : "down",
227
+ skills: ((_d = eff.skills) != null ? _d : []).map(toProfSlug),
228
+ round: (_e = eff.round) != null ? _e : "down",
209
229
  label
210
230
  });
211
231
  break;
@@ -272,7 +292,7 @@ function applyEffect(out, eff, label, level) {
272
292
  }
273
293
  case "roll-modifier": {
274
294
  const rolls = eff.roll === "any" ? ["ability-check", "saving-throw", "attack"] : [eff.roll];
275
- const scopes = (_d = normalizeRollScope(eff.scope, rolls[0])) != null ? _d : [eff.scope];
295
+ const scopes = (_f = normalizeRollScope(eff.scope, rolls[0])) != null ? _f : [eff.scope];
276
296
  for (const roll of rolls) for (const scope of scopes)
277
297
  out.rollModifiers.push({ mode: eff.mode, roll, scope, condition: eff.condition, label });
278
298
  break;
@@ -282,13 +302,13 @@ function applyEffect(out, eff, label, level) {
282
302
  ability: eff.ability === "any" ? void 0 : eff.ability,
283
303
  on_success: eff.on_success,
284
304
  on_failure: eff.on_failure,
285
- appliesTo: (_e = eff.applies_to) != null ? _e : void 0,
305
+ appliesTo: (_g = eff.applies_to) != null ? _g : void 0,
286
306
  condition: eff.condition,
287
307
  label
288
308
  });
289
309
  break;
290
310
  case "crit-range":
291
- if (((_f = eff.applies_to) != null ? _f : "weapon") !== "spell") out.critRange = Math.min(out.critRange, eff.min_roll);
311
+ if (((_h = eff.applies_to) != null ? _h : "weapon") !== "spell") out.critRange = Math.min(out.critRange, eff.min_roll);
292
312
  break;
293
313
  case "extra-attack":
294
314
  out.extraAttack = Math.max(out.extraAttack, scaled(eff.count, "count", eff.scales_at, level));
@@ -300,20 +320,21 @@ function applyEffect(out, eff, label, level) {
300
320
  if (eff.flag === "no-ranged-in-melee-disadvantage") out.attackNotes.push("No disadvantage firing in melee");
301
321
  break;
302
322
  case "damage-bonus":
303
- if (((_g = eff.applies_to) != null ? _g : "weapon") !== "spell") {
323
+ if (((_i = eff.applies_to) != null ? _i : "weapon") !== "spell") {
304
324
  const scope = riderScope(eff.applies_to);
305
325
  out.damageBonuses.push({
306
326
  amount: eff.amount,
307
327
  damage_type: eff.damage_type,
308
328
  source: label,
309
329
  ...eff.condition ? { condition: eff.condition } : {},
310
- ...scope ? { scope } : {}
330
+ ...scope ? { scope } : {},
331
+ ...owner ? { owner } : {}
311
332
  });
312
333
  }
313
334
  break;
314
335
  case "ability-score-increase":
315
336
  if (eff.abilities === "chosen") break;
316
- for (const ab of eff.abilities) out.ability_bonus[ab] = ((_h = out.ability_bonus[ab]) != null ? _h : 0) + eff.amount;
337
+ for (const ab of eff.abilities) out.ability_bonus[ab] = ((_j = out.ability_bonus[ab]) != null ? _j : 0) + eff.amount;
317
338
  break;
318
339
  default:
319
340
  break;
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-CJYCVNW2.js";
9
9
  import {
10
10
  featureSchema
11
- } from "./chunk-FPGXFD2M.js";
11
+ } from "./chunk-YD2VAHA6.js";
12
12
  import {
13
13
  resourceSchema
14
14
  } from "./chunk-ZJXSXVFP.js";
@@ -18,7 +18,7 @@ import {
18
18
  } from "./chunk-IN4M4X7P.js";
19
19
  import {
20
20
  choiceSchema
21
- } from "./chunk-BRGLDWUP.js";
21
+ } from "./chunk-5MXFW4O3.js";
22
22
  import {
23
23
  additionalSpellsEntrySchema,
24
24
  imageField,
@@ -110,6 +110,15 @@ var classEntitySchema = z.object({
110
110
  description: z.string(),
111
111
  hit_die: hitDieEnum,
112
112
  primary_abilities: z.array(abilityEnum).nonempty(),
113
+ // D2b-A (A-1): the class's recommended Standard Array (SRD 5.2 "Standard Array by Class"); absent on 2014 classes.
114
+ standard_array: z.strictObject({
115
+ str: z.number().int(),
116
+ dex: z.number().int(),
117
+ con: z.number().int(),
118
+ int: z.number().int(),
119
+ wis: z.number().int(),
120
+ cha: z.number().int()
121
+ }).optional(),
113
122
  saving_throws: z.array(abilityEnum).length(2),
114
123
  proficiencies: z.object({
115
124
  armor: z.array(armorCategoryEnum),
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-ZCTUYGNV.js";
10
10
  import {
11
11
  mirrorSpellShapes
12
- } from "./chunk-XY6X2APM.js";
12
+ } from "./chunk-7Q2VPKCD.js";
13
13
  import {
14
14
  ABILITY_KEYS
15
15
  } from "./chunk-WELIKLZF.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raceEntitySchema
3
- } from "./chunk-X7VZY4ON.js";
3
+ } from "./chunk-FANQVQ3D.js";
4
4
  import {
5
5
  aliasFeatureListActionCost
6
6
  } from "./chunk-4U55VNC7.js";
@@ -8,10 +8,10 @@ import {
8
8
  } from "./chunk-ZJXSXVFP.js";
9
9
  import {
10
10
  choiceSchema
11
- } from "./chunk-BRGLDWUP.js";
11
+ } from "./chunk-5MXFW4O3.js";
12
12
  import {
13
13
  featureEffectSchema
14
- } from "./chunk-ZMJQUZQH.js";
14
+ } from "./chunk-2JSQE3DD.js";
15
15
 
16
16
  // src/schemas/feature-schema.ts
17
17
  import { z as z2 } from "zod";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-4U55VNC7.js";
4
4
  import {
5
5
  classEntitySchema
6
- } from "./chunk-PLOSP4XQ.js";
6
+ } from "./chunk-UUBJCNQU.js";
7
7
 
8
8
  // src/class/class.parser.ts
9
9
  import { parseYaml } from "@archivist-gg/core";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  classCodec
3
- } from "../chunk-XJRNEQPD.js";
4
- import "../chunk-G7ED5H2G.js";
3
+ } from "../chunk-MBX25B6D.js";
4
+ import "../chunk-YKL6SHRJ.js";
5
5
  import "../chunk-4U55VNC7.js";
6
- import "../chunk-PLOSP4XQ.js";
6
+ import "../chunk-UUBJCNQU.js";
7
7
  import "../chunk-G6MHJDVT.js";
8
8
  import "../chunk-CJYCVNW2.js";
9
- import "../chunk-FPGXFD2M.js";
9
+ import "../chunk-YD2VAHA6.js";
10
10
  import "../chunk-WYGD2UGN.js";
11
11
  import "../chunk-ZJXSXVFP.js";
12
12
  import "../chunk-IN4M4X7P.js";
13
- import "../chunk-BRGLDWUP.js";
14
- import "../chunk-ZMJQUZQH.js";
13
+ import "../chunk-5MXFW4O3.js";
14
+ import "../chunk-2JSQE3DD.js";
15
15
  import "../chunk-UA5KS2PL.js";
16
16
  export {
17
17
  classCodec
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  parseClass
3
- } from "../chunk-G7ED5H2G.js";
3
+ } from "../chunk-YKL6SHRJ.js";
4
4
  import "../chunk-4U55VNC7.js";
5
- import "../chunk-PLOSP4XQ.js";
5
+ import "../chunk-UUBJCNQU.js";
6
6
  import "../chunk-G6MHJDVT.js";
7
7
  import "../chunk-CJYCVNW2.js";
8
- import "../chunk-FPGXFD2M.js";
8
+ import "../chunk-YD2VAHA6.js";
9
9
  import "../chunk-WYGD2UGN.js";
10
10
  import "../chunk-ZJXSXVFP.js";
11
11
  import "../chunk-IN4M4X7P.js";
12
- import "../chunk-BRGLDWUP.js";
13
- import "../chunk-ZMJQUZQH.js";
12
+ import "../chunk-5MXFW4O3.js";
13
+ import "../chunk-2JSQE3DD.js";
14
14
  import "../chunk-UA5KS2PL.js";
15
15
  export {
16
16
  parseClass
@@ -22,6 +22,14 @@ export declare const classEntitySchema: z.ZodObject<{
22
22
  wis: "wis";
23
23
  cha: "cha";
24
24
  }>>;
25
+ standard_array: z.ZodOptional<z.ZodObject<{
26
+ str: z.ZodNumber;
27
+ dex: z.ZodNumber;
28
+ con: z.ZodNumber;
29
+ int: z.ZodNumber;
30
+ wis: z.ZodNumber;
31
+ cha: z.ZodNumber;
32
+ }, z.core.$strict>>;
25
33
  saving_throws: z.ZodArray<z.ZodEnum<{
26
34
  str: "str";
27
35
  dex: "dex";
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  classEntitySchema
3
- } from "../chunk-PLOSP4XQ.js";
3
+ } from "../chunk-UUBJCNQU.js";
4
4
  import "../chunk-G6MHJDVT.js";
5
5
  import "../chunk-CJYCVNW2.js";
6
- import "../chunk-FPGXFD2M.js";
6
+ import "../chunk-YD2VAHA6.js";
7
7
  import "../chunk-WYGD2UGN.js";
8
8
  import "../chunk-ZJXSXVFP.js";
9
9
  import "../chunk-IN4M4X7P.js";
10
- import "../chunk-BRGLDWUP.js";
11
- import "../chunk-ZMJQUZQH.js";
10
+ import "../chunk-5MXFW4O3.js";
11
+ import "../chunk-2JSQE3DD.js";
12
12
  import "../chunk-UA5KS2PL.js";
13
13
  export {
14
14
  classEntitySchema
@@ -0,0 +1,16 @@
1
+ /**
2
+ * D2b-A (A-1, user pick A) · the class's recommended Standard Array, which the Builder's Abilities step suggests.
3
+ */
4
+ import type { Ability } from "@archivist-gg/dnd5e/types/choice";
5
+ import type { ClassEntity } from "./class.types.js";
6
+ /** Structural subset of the registry this reads. */
7
+ export interface StandardArrayRegistry {
8
+ search(query: string, entityType: string, limit: number): Array<{
9
+ slug: string;
10
+ data: Record<string, unknown>;
11
+ }>;
12
+ }
13
+ /** The class's own `standard_array`; else, for a 2024 class that carries none (a book class imported before the table
14
+ * was data), the table of its 2024 twin in the registry (the same bare slug, e.g. the SRD 2024 Fighter for the PHB
15
+ * 2024 Fighter). Null for a 2014 class and for a class with no 2024 twin (homebrew): the Builder then suggests nothing. */
16
+ export declare function standardArrayFor(entity: Pick<ClassEntity, "slug" | "edition" | "standard_array">, registry: StandardArrayRegistry | null | undefined): Record<Ability, number> | null;
@@ -0,0 +1,20 @@
1
+ import {
2
+ bareEntitySlug
3
+ } from "../chunk-ZCTUYGNV.js";
4
+
5
+ // src/class/class.standard-array.ts
6
+ var isArray = (v) => !!v && typeof v === "object" && ["str", "dex", "con", "int", "wis", "cha"].every((k) => typeof v[k] === "number");
7
+ function standardArrayFor(entity, registry) {
8
+ if (isArray(entity.standard_array)) return entity.standard_array;
9
+ if (entity.edition !== "2024" || !registry) return null;
10
+ const bare = bareEntitySlug(entity.slug);
11
+ for (const e of registry.search("", "class", Number.POSITIVE_INFINITY)) {
12
+ if (e.slug === entity.slug || bareEntitySlug(e.slug) !== bare) continue;
13
+ const d = e.data;
14
+ if (d.edition === "2024" && isArray(d.standard_array)) return d.standard_array;
15
+ }
16
+ return null;
17
+ }
18
+ export {
19
+ standardArrayFor
20
+ };
@@ -69,6 +69,9 @@ export interface ClassEntity {
69
69
  description: string;
70
70
  hit_die: string;
71
71
  primary_abilities: Ability[];
72
+ /** D2b-A (A-1): the recommended Standard Array for the class, one score per ability (SRD 5.2 "Standard Array by
73
+ * Class"). Absent on 2014 classes and homebrew; the Builder then suggests nothing. */
74
+ standard_array?: Record<Ability, number>;
72
75
  saving_throws: Ability[];
73
76
  proficiencies: ClassProficiencies;
74
77
  skill_choices: SkillChoices;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * D2b-A · an AMOUNT that names a modifier prints the number (design §1.2 A, user rulings 2026-09-27).
3
+ *
4
+ * An effect amount, a rider amount or a save DC formula is written in the book's words ("1d8 plus your Wisdom
5
+ * modifier", "your spell save DC") or with a `{token}` ("2d4 + {spell_mod}"). Nothing rendered it as a number: the
6
+ * resource DSL (`resource-formula.ts`) has no dice production and no spellcasting idents. This module reads both at
7
+ * READ time, so existing notes and homebrew need no re-import:
8
+ * 1. `normalizeAmountPhrases` turns the closed phrase table below into tokens;
9
+ * 2. `resolveAmountText` substitutes each `{ident}` with its number and folds: a pure numeric result is evaluated by
10
+ * the resource DSL (so `floor`, `max`, `ceil` keep working) and a trailing "(minimum of N)" is applied;
11
+ * `<dice> +/- <numbers>` folds to one roll ("1d8+3"); anything else keeps its words with the numbers in place
12
+ * ("one roll of your Martial Arts die + 3").
13
+ * The phrase table is the ONE place the book's English is read; an unmapped phrase stays as written.
14
+ */
15
+ import { type FormulaBindings } from "./resource-formula.js";
16
+ /** The DSL bindings plus the owner's spellcasting modifier and save DC (absent when the owner casts no spells). */
17
+ export interface AmountBindings extends FormulaBindings {
18
+ spell_mod?: number;
19
+ spell_dc?: number;
20
+ }
21
+ /** The book phrases of `text` as tokens, "plus" as "+"; text with no mapped phrase comes back unchanged. */
22
+ export declare function normalizeAmountPhrases(text: string): string;
23
+ /**
24
+ * The amount as the sheet prints it: the phrases and tokens of `text` resolved against `b`. A pure number is
25
+ * evaluated ("13"); a roll plus numbers folds ("1d8+3"); other words keep their place with the numbers in ("the roll + 2
26
+ * (minimum of 1)"). An UNBOUND token prints its short label when the note wrote the token, and a book phrase whose
27
+ * token is unbound keeps the note's own words. Text with no phrase and no token comes back unchanged.
28
+ */
29
+ export declare function resolveAmountText(text: string, b: AmountBindings): string;
30
+ /** The leading dice roll of a resolved amount ("1d8+3" of "1d8+3 (minimum of 1)"), which the sheet makes rollable; null
31
+ * when the amount does not open with a roll. */
32
+ export declare function diceRollOf(resolved: string): string | null;
33
+ /** The leading roll of `text` as the RAW characters it spans (`raw`, which the caller cuts the text by) and as a notation
34
+ * with no spaces (`notation`, which the roller reads): "2d4 + 2 more" is { raw: "2d4 + 2", notation: "2d4+2" }. */
35
+ export declare function diceRollPrefix(text: string): {
36
+ raw: string;
37
+ notation: string;
38
+ } | null;
@@ -0,0 +1,13 @@
1
+ import {
2
+ diceRollOf,
3
+ diceRollPrefix,
4
+ normalizeAmountPhrases,
5
+ resolveAmountText
6
+ } from "../chunk-QH36K2PU.js";
7
+ import "../chunk-EVMUJYUD.js";
8
+ export {
9
+ diceRollOf,
10
+ diceRollPrefix,
11
+ normalizeAmountPhrases,
12
+ resolveAmountText
13
+ };
@@ -0,0 +1,4 @@
1
+ /** The resource's name worded for `amount`: the last word singular for one, plural otherwise, when it is a count noun. */
2
+ export declare function resourceCountWord(name: string, amount: number): string;
3
+ /** "<amount> <name worded for it>" ("1 Superiority Die"). */
4
+ export declare function countedResource(amount: number, name: string): string;
@@ -0,0 +1,30 @@
1
+ // src/dnd/resource-words.ts
2
+ var COUNT_NOUNS = [
3
+ ["Die", "Dice"],
4
+ ["Point", "Points"],
5
+ ["Use", "Uses"],
6
+ ["Seal", "Seals"],
7
+ ["Rage", "Rages"],
8
+ ["Cast", "Casts"],
9
+ ["Casting", "Castings"],
10
+ ["Scroll", "Scrolls"],
11
+ ["Charge", "Charges"],
12
+ ["Token", "Tokens"],
13
+ ["Slot", "Slots"]
14
+ ];
15
+ var BY_WORD = new Map(
16
+ COUNT_NOUNS.flatMap((pair) => [[pair[0], pair], [pair[1], pair]])
17
+ );
18
+ function resourceCountWord(name, amount) {
19
+ const at = name.lastIndexOf(" ") + 1;
20
+ const pair = BY_WORD.get(name.slice(at));
21
+ if (!pair) return name;
22
+ return name.slice(0, at) + (amount === 1 ? pair[0] : pair[1]);
23
+ }
24
+ function countedResource(amount, name) {
25
+ return `${amount} ${resourceCountWord(name, amount)}`;
26
+ }
27
+ export {
28
+ countedResource,
29
+ resourceCountWord
30
+ };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  featCodec
3
- } from "../chunk-6ZY3U6BQ.js";
4
- import "../chunk-4SLOJXJ4.js";
5
- import "../chunk-LVX6TQ3R.js";
3
+ } from "../chunk-RYLXAYIQ.js";
4
+ import "../chunk-7QN7XSAK.js";
5
+ import "../chunk-PKX6TGRE.js";
6
6
  import "../chunk-ZJXSXVFP.js";
7
- import "../chunk-BRGLDWUP.js";
8
- import "../chunk-ZMJQUZQH.js";
7
+ import "../chunk-5MXFW4O3.js";
8
+ import "../chunk-2JSQE3DD.js";
9
9
  import "../chunk-UA5KS2PL.js";
10
10
  export {
11
11
  featCodec
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  parseFeat
3
- } from "../chunk-4SLOJXJ4.js";
4
- import "../chunk-LVX6TQ3R.js";
3
+ } from "../chunk-7QN7XSAK.js";
4
+ import "../chunk-PKX6TGRE.js";
5
5
  import "../chunk-ZJXSXVFP.js";
6
- import "../chunk-BRGLDWUP.js";
7
- import "../chunk-ZMJQUZQH.js";
6
+ import "../chunk-5MXFW4O3.js";
7
+ import "../chunk-2JSQE3DD.js";
8
8
  import "../chunk-UA5KS2PL.js";
9
9
  export {
10
10
  parseFeat
@@ -88,6 +88,31 @@ export declare const featEntitySchema: z.ZodObject<{
88
88
  minimum: z.ZodOptional<z.ZodNumber>;
89
89
  condition: z.ZodOptional<z.ZodString>;
90
90
  subject: z.ZodOptional<z.ZodString>;
91
+ }, z.core.$strip>, z.ZodObject<{
92
+ kind: z.ZodLiteral<"save-bonus">;
93
+ ability: z.ZodEnum<{
94
+ str: "str";
95
+ dex: "dex";
96
+ con: "con";
97
+ int: "int";
98
+ wis: "wis";
99
+ cha: "cha";
100
+ }>;
101
+ minimum: z.ZodOptional<z.ZodNumber>;
102
+ saves: z.ZodOptional<z.ZodArray<z.ZodEnum<{
103
+ str: "str";
104
+ dex: "dex";
105
+ con: "con";
106
+ int: "int";
107
+ wis: "wis";
108
+ cha: "cha";
109
+ }>>>;
110
+ inactive_while: z.ZodOptional<z.ZodEnum<{
111
+ incapacitated: "incapacitated";
112
+ unconscious: "unconscious";
113
+ }>>;
114
+ condition: z.ZodOptional<z.ZodString>;
115
+ subject: z.ZodOptional<z.ZodString>;
91
116
  }, z.core.$strip>, z.ZodObject<{
92
117
  kind: z.ZodLiteral<"half-proficiency">;
93
118
  skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  featEntitySchema
3
- } from "../chunk-LVX6TQ3R.js";
3
+ } from "../chunk-PKX6TGRE.js";
4
4
  import "../chunk-ZJXSXVFP.js";
5
- import "../chunk-BRGLDWUP.js";
6
- import "../chunk-ZMJQUZQH.js";
5
+ import "../chunk-5MXFW4O3.js";
6
+ import "../chunk-2JSQE3DD.js";
7
7
  import "../chunk-UA5KS2PL.js";
8
8
  export {
9
9
  featEntitySchema