@archivist-gg/dnd5e 0.3.5 → 0.3.7

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 (63) hide show
  1. package/dist/{chunk-UMWIPLLM.js → chunk-2BI5NAXK.js} +1 -1
  2. package/dist/{chunk-V2MJE6NC.js → chunk-3BMVVNCS.js} +1 -1
  3. package/dist/{chunk-UXPGW4TI.js → chunk-4ISR76DH.js} +5 -0
  4. package/dist/{chunk-N3A5ICBO.js → chunk-7MRW4RTH.js} +1 -1
  5. package/dist/{chunk-WHVLNFTG.js → chunk-7WNQJAZW.js} +19 -2
  6. package/dist/{chunk-UQ3ZFCWD.js → chunk-A6YL53NC.js} +1 -1
  7. package/dist/{chunk-KK33PIDN.js → chunk-ABQ6ZWAF.js} +1 -1
  8. package/dist/{chunk-CV5XDJKU.js → chunk-B4TQTJVC.js} +1 -1
  9. package/dist/{chunk-RVXC6ZHY.js → chunk-BJFL4KYG.js} +1 -1
  10. package/dist/{chunk-EHDATXP7.js → chunk-BS355LND.js} +1 -1
  11. package/dist/{chunk-3JOTQW7A.js → chunk-CQAL67R3.js} +1 -1
  12. package/dist/{chunk-CGRLQ34U.js → chunk-DTCR4DBF.js} +2 -2
  13. package/dist/{chunk-2VDTW7QO.js → chunk-EYZCCWG2.js} +50 -1
  14. package/dist/{chunk-5RKLL2AD.js → chunk-H4VEMVOO.js} +1 -1
  15. package/dist/{chunk-Y4AGLCY6.js → chunk-JKLRBILX.js} +1 -1
  16. package/dist/{chunk-W74BCA7V.js → chunk-JOVMV2X5.js} +1 -1
  17. package/dist/{chunk-OAXWSVJB.js → chunk-MT4RWROQ.js} +2 -2
  18. package/dist/{chunk-RF4TYCA2.js → chunk-MYW7AOUE.js} +1 -1
  19. package/dist/{chunk-6BFWZW6T.js → chunk-NNAQ4KQF.js} +1 -1
  20. package/dist/{chunk-I2NDWFDD.js → chunk-PZCE7DJM.js} +18 -0
  21. package/dist/{chunk-KADJZGZY.js → chunk-RWZTGMSN.js} +1 -1
  22. package/dist/{chunk-YLL2QXY2.js → chunk-UA3I2YSO.js} +4 -0
  23. package/dist/class/class.codec.js +5 -5
  24. package/dist/class/class.parser.js +4 -4
  25. package/dist/class/class.schema.d.ts +5 -0
  26. package/dist/class/class.schema.js +3 -3
  27. package/dist/feat/feat.codec.js +4 -4
  28. package/dist/feat/feat.parser.js +3 -3
  29. package/dist/feat/feat.schema.d.ts +5 -0
  30. package/dist/feat/feat.schema.js +2 -2
  31. package/dist/index.js +19 -19
  32. package/dist/monster/monster.parser.js +4 -4
  33. package/dist/monster/monster.schema.js +3 -3
  34. package/dist/optional-feature/optional-feature.codec.js +4 -4
  35. package/dist/optional-feature/optional-feature.parser.js +3 -3
  36. package/dist/optional-feature/optional-feature.schema.d.ts +23 -0
  37. package/dist/optional-feature/optional-feature.schema.js +2 -2
  38. package/dist/pc/pc.decision-engine.js +2 -2
  39. package/dist/pc/pc.parser.js +1 -1
  40. package/dist/pc/pc.proficiencies.js +2 -2
  41. package/dist/pc/pc.recalc.js +2 -2
  42. package/dist/pc/pc.resolver.d.ts +14 -0
  43. package/dist/pc/pc.resolver.js +4 -2
  44. package/dist/pc/pc.resource-seed.js +1 -1
  45. package/dist/pc/pc.resources.d.ts +6 -0
  46. package/dist/pc/pc.resources.js +1 -1
  47. package/dist/pc/pc.rest.js +18 -12
  48. package/dist/pc/pc.schema.d.ts +2 -0
  49. package/dist/pc/pc.schema.js +1 -1
  50. package/dist/pc/pc.types.d.ts +11 -0
  51. package/dist/race/race.codec.js +5 -5
  52. package/dist/race/race.parser.js +4 -4
  53. package/dist/race/race.schema.js +3 -3
  54. package/dist/schemas/feature-schema.js +2 -2
  55. package/dist/schemas/resource-schema.d.ts +5 -0
  56. package/dist/schemas/resource-schema.js +1 -1
  57. package/dist/subclass/subclass.codec.js +5 -5
  58. package/dist/subclass/subclass.parser.js +4 -4
  59. package/dist/subclass/subclass.schema.d.ts +5 -0
  60. package/dist/subclass/subclass.schema.js +3 -3
  61. package/dist/types/optional-feature.types.d.ts +2 -0
  62. package/dist/types/resource.d.ts +5 -0
  63. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  featureSchema
3
- } from "./chunk-W74BCA7V.js";
3
+ } from "./chunk-JOVMV2X5.js";
4
4
  import {
5
5
  choiceSchema
6
6
  } from "./chunk-IWRVOCT5.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MONSTER_KNOWN_KEYS,
3
3
  monsterSchema
4
- } from "./chunk-UQ3ZFCWD.js";
4
+ } from "./chunk-A6YL53NC.js";
5
5
 
6
6
  // src/monster/monster.parser.ts
7
7
  import { parseYaml } from "@archivist-gg/core";
@@ -42,6 +42,8 @@ function toResolvedResource(r, fallbackName, owner) {
42
42
  ...r.die ? { die: r.die } : {},
43
43
  ...r.scales_at ? { scalesAt: r.scales_at } : {},
44
44
  ...((_b = r.recovery) == null ? void 0 : _b.length) ? { recovery: r.recovery.map(resolveRecovery) } : {},
45
+ ...r.rendering_hint ? { renderingHint: r.rendering_hint } : {},
46
+ ...r.surface ? { surface: r.surface } : {},
45
47
  owner
46
48
  };
47
49
  }
@@ -72,11 +74,14 @@ function resolveResourceIndex(resolved) {
72
74
  warnOnce(`pool-uses:${entry.slug}`, `optional feature "${entry.slug}" has a prose uses.max (${JSON.stringify(uses.max)}); no tracker`);
73
75
  continue;
74
76
  }
77
+ const pres = entry.entity;
75
78
  out.set(entry.slug, {
76
79
  id: entry.slug,
77
80
  name: entry.entity.name,
78
81
  reset: uses.recharge,
79
82
  maxFormula,
83
+ ...pres.rendering_hint ? { renderingHint: pres.rendering_hint } : {},
84
+ ...pres.surface ? { surface: pres.surface } : {},
80
85
  owner: { kind: "pool", poolId: pool.id, poolLabel: pool.label, source: { kind: "class", slug: classSlug, level: pool.anchorLevel } }
81
86
  });
82
87
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raceEntitySchema
3
- } from "./chunk-UMWIPLLM.js";
3
+ } from "./chunk-2BI5NAXK.js";
4
4
  import {
5
5
  aliasFeatureListActionCost
6
6
  } from "./chunk-4U55VNC7.js";
@@ -5,7 +5,7 @@ import {
5
5
  actionCostEnum,
6
6
  resetTriggerEnum,
7
7
  resourceConsumptionSchema
8
- } from "./chunk-YLL2QXY2.js";
8
+ } from "./chunk-UA3I2YSO.js";
9
9
  import {
10
10
  featureEffectSchema
11
11
  } from "./chunk-X3D6LXUU.js";
@@ -33,7 +33,18 @@ var prerequisiteSchema = z.discriminatedUnion("kind", [
33
33
  ]);
34
34
  var usesSchema = z.object({
35
35
  max: z.union([z.number(), z.string()]),
36
- recharge: resetTriggerEnum
36
+ recharge: resetTriggerEnum,
37
+ /** Partial recovery, for a pool a rest does NOT fully refill (a sanity track that regains 1 per long
38
+ * rest). Same shape a feature's `resources[].recovery` uses, so `ResolvedResource.recovery` — which
39
+ * has carried the field since R4-G4 — is populated identically from either side. Optional: all 35
40
+ * shipped `uses` carriers omit it and parse unchanged. */
41
+ recovery: z.array(z.object({
42
+ id: z.string().min(1),
43
+ name: z.string().min(1),
44
+ amount: z.union([z.number(), z.string()]),
45
+ reset: resetTriggerEnum,
46
+ restores: z.enum(["uses", "spell-slots"]).optional()
47
+ })).optional()
37
48
  });
38
49
  var optionalFeatureEntitySchema = z.object({
39
50
  slug: z.string().min(1),
@@ -55,6 +66,12 @@ var optionalFeatureEntitySchema = z.object({
55
66
  activatable: z.boolean().optional(),
56
67
  rendering_hint: z.string().optional(),
57
68
  // the ONE root with load-bearing VALUES (88 records, G4's)
69
+ /** WHERE this resource is drawn. `band` puts it in the header strip beside HP; anything else, and the
70
+ * default, leaves it to the Resources tab. The tab ALWAYS lists every resource — it is the complete
71
+ * inventory of what the character can spend, and a row missing from it would read as a bug — so this
72
+ * key only ever ADDS a second, curated home. Opt-in on purpose: the band's whole value is being short,
73
+ * which an opt-out flag would erode one new grant at a time. */
74
+ surface: z.enum(["band", "tab"]).optional(),
58
75
  additional_spells: z.array(additionalSpellsEntrySchema).optional(),
59
76
  // 54
60
77
  is_class_feature_variant: z.boolean().optional(),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  featureObjectSchema
3
- } from "./chunk-W74BCA7V.js";
3
+ } from "./chunk-JOVMV2X5.js";
4
4
  import {
5
5
  imageField
6
6
  } from "./chunk-UA5KS2PL.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  subclassEntitySchema
3
- } from "./chunk-OAXWSVJB.js";
3
+ } from "./chunk-MT4RWROQ.js";
4
4
  import {
5
5
  aliasFeaturesByLevelActionCost
6
6
  } from "./chunk-4U55VNC7.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseSubclass
3
- } from "./chunk-KK33PIDN.js";
3
+ } from "./chunk-ABQ6ZWAF.js";
4
4
 
5
5
  // src/subclass/subclass.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  actionCostEnum,
3
3
  resourceSchema
4
- } from "./chunk-YLL2QXY2.js";
4
+ } from "./chunk-UA3I2YSO.js";
5
5
  import {
6
6
  choiceSchema
7
7
  } from "./chunk-IWRVOCT5.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseClass
3
- } from "./chunk-5RKLL2AD.js";
3
+ } from "./chunk-H4VEMVOO.js";
4
4
 
5
5
  // src/class/class.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseRace
3
- } from "./chunk-N3A5ICBO.js";
3
+ } from "./chunk-7MRW4RTH.js";
4
4
 
5
5
  // src/race/race.codec.ts
6
6
  import yaml from "js-yaml";
@@ -8,10 +8,10 @@ import {
8
8
  } from "./chunk-CJYCVNW2.js";
9
9
  import {
10
10
  featureSchema
11
- } from "./chunk-W74BCA7V.js";
11
+ } from "./chunk-JOVMV2X5.js";
12
12
  import {
13
13
  resourceSchema
14
- } from "./chunk-YLL2QXY2.js";
14
+ } from "./chunk-UA3I2YSO.js";
15
15
  import {
16
16
  startingEquipmentEntrySchema,
17
17
  startingGoldSchema
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  resolveFeatureResources,
3
3
  resolveResourceIndex
4
- } from "./chunk-UXPGW4TI.js";
4
+ } from "./chunk-4ISR76DH.js";
5
5
  import {
6
6
  attributeUnclassedSpell,
7
7
  effectiveSpellcastingAbility,
@@ -51,6 +51,9 @@ import {
51
51
  FEATURE_TYPE_FOR_CATEGORY,
52
52
  featProgressionCategory
53
53
  } from "./chunk-YBLDE5CG.js";
54
+ import {
55
+ isValidMaxFormula
56
+ } from "./chunk-EVMUJYUD.js";
54
57
  import {
55
58
  mirrorSpellShapes
56
59
  } from "./chunk-OZP6JFGT.js";
@@ -1089,6 +1092,7 @@ var PCResolver = class {
1089
1092
  const features = collectResolvedFeatures(race, classes, background, feats, featVia);
1090
1093
  const extraFeatures = collectChosenGrantedFeatures(character, classes, this.entities, race, background);
1091
1094
  features.push(...extraFeatures);
1095
+ features.push(...collectAdditionalFeatures(character, this.entities, warnings));
1092
1096
  const casters = [];
1093
1097
  classes.forEach((c, i) => {
1094
1098
  const profile = c.entity ? resolveSpellcasting(c) : null;
@@ -1349,6 +1353,50 @@ function collectChosenGrantedFeatures(character, classes, registry, race = null,
1349
1353
  }
1350
1354
  return out;
1351
1355
  }
1356
+ function collectAdditionalFeatures(character, entities, warnings) {
1357
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1358
+ const out = [];
1359
+ for (const entry of (_a = character.additional_features) != null ? _a : []) {
1360
+ const slug = (_b = stripSlug(entry)) != null ? _b : entry;
1361
+ const reg = entities.getByTypeAndSlug("optional-feature", slug);
1362
+ if (!reg) {
1363
+ warnings.push(`Granted feature [[${slug}]] not found in compendium as optional-feature.`);
1364
+ continue;
1365
+ }
1366
+ const d = reg.data;
1367
+ const feature = {
1368
+ id: (_c = d.slug) != null ? _c : slug,
1369
+ name: (_d = d.name) != null ? _d : slug,
1370
+ description: d.description,
1371
+ ...((_e = d.effects) == null ? void 0 : _e.length) ? { effects: d.effects } : {},
1372
+ ...d.action_cost ? { action: d.action_cost } : {},
1373
+ ...d.passive !== void 0 ? { passive: d.passive } : {},
1374
+ ...d.activatable !== void 0 ? { activatable: d.activatable } : {},
1375
+ ...d.duration ? { duration: d.duration } : {},
1376
+ ...d.consumes ? { consumes: d.consumes } : {}
1377
+ };
1378
+ const uses = d.uses;
1379
+ if (uses) {
1380
+ const maxFormula = String(uses.max);
1381
+ if (isValidMaxFormula(maxFormula)) {
1382
+ feature.resources = [{
1383
+ id: (_f = d.slug) != null ? _f : slug,
1384
+ name: (_g = d.name) != null ? _g : slug,
1385
+ max_formula: maxFormula,
1386
+ reset: uses.recharge,
1387
+ ...((_h = uses.recovery) == null ? void 0 : _h.length) ? { recovery: uses.recovery } : {},
1388
+ // Presentation rides the resource, so the band and the tab read one object.
1389
+ ...d.rendering_hint ? { rendering_hint: d.rendering_hint } : {},
1390
+ ...d.surface ? { surface: d.surface } : {}
1391
+ }];
1392
+ } else {
1393
+ warnings.push(`Granted feature "${slug}" has a prose uses.max (${JSON.stringify(uses.max)}); no tracker.`);
1394
+ }
1395
+ }
1396
+ out.push({ feature, source: { kind: "campaign", slug } });
1397
+ }
1398
+ return out;
1399
+ }
1352
1400
  function walkChoiceGrants(choices, atLevel, emit, registry) {
1353
1401
  var _a, _b, _c, _d, _e;
1354
1402
  for (const ch of choices != null ? choices : []) {
@@ -1570,6 +1618,7 @@ export {
1570
1618
  collectItemGrantedSpells,
1571
1619
  dedupeResolvedSpells,
1572
1620
  collectChosenGrantedFeatures,
1621
+ collectAdditionalFeatures,
1573
1622
  collectResolvedFeatures,
1574
1623
  collectProficiencyGrants,
1575
1624
  __matchesFilterForTest,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-4U55VNC7.js";
4
4
  import {
5
5
  classEntitySchema
6
- } from "./chunk-CGRLQ34U.js";
6
+ } from "./chunk-DTCR4DBF.js";
7
7
 
8
8
  // src/class/class.parser.ts
9
9
  import { parseYaml } from "@archivist-gg/core";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFeat
3
- } from "./chunk-KADJZGZY.js";
3
+ } from "./chunk-RWZTGMSN.js";
4
4
 
5
5
  // src/feat/feat.codec.ts
6
6
  import yaml from "js-yaml";
@@ -5,7 +5,7 @@ import {
5
5
  actionCostEnum,
6
6
  resourceConsumptionSchema,
7
7
  resourceSchema
8
- } from "./chunk-YLL2QXY2.js";
8
+ } from "./chunk-UA3I2YSO.js";
9
9
  import {
10
10
  choiceSchema
11
11
  } from "./chunk-IWRVOCT5.js";
@@ -8,10 +8,10 @@ import {
8
8
  } from "./chunk-CJYCVNW2.js";
9
9
  import {
10
10
  featureSchema
11
- } from "./chunk-W74BCA7V.js";
11
+ } from "./chunk-JOVMV2X5.js";
12
12
  import {
13
13
  resourceSchema
14
- } from "./chunk-YLL2QXY2.js";
14
+ } from "./chunk-UA3I2YSO.js";
15
15
  import {
16
16
  additionalSpellsEntrySchema,
17
17
  imageField,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseOptionalFeature
3
- } from "./chunk-6BFWZW6T.js";
3
+ } from "./chunk-NNAQ4KQF.js";
4
4
 
5
5
  // src/optional-feature/optional-feature.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  optionalFeatureEntitySchema
3
- } from "./chunk-WHVLNFTG.js";
3
+ } from "./chunk-7WNQJAZW.js";
4
4
 
5
5
  // src/optional-feature/optional-feature.parser.ts
6
6
  import { parseYaml } from "@archivist-gg/core";
@@ -193,6 +193,15 @@ var characterStateSchema = z.object({
193
193
  used: z.number().int().nonnegative(),
194
194
  max: z.number().int().nonnegative()
195
195
  })).default({}),
196
+ // R4-G5 G8 (research D §3.3, the booked design): banked PREROLLED dice, keyed by the SAME id as
197
+ // feature_uses (the resource id, e.g. "wizard-2024:foretelling-roll" for Portent). SIGN IS STATE
198
+ // (the G8 shuffle fix): a value is stored NEGATIVE while spent, positive while live, so positions
199
+ // are STABLE and "spend the right-hand roll" never reorders the bank (the earlier spent-prefix
200
+ // shape moved a spent value into a prefix and visibly shuffled its neighbours). The count axis
201
+ // stays on feature_uses (the tracker remains the single source of how many are left); the
202
+ // writers on the plugin edit state keep the sign set and `used` in step. Without this line a
203
+ // non-strict parse STRIPS the key and the rolls never survive a load.
204
+ feature_rolls: z.record(z.string(), z.array(z.number().int().refine((n) => n !== 0 && Math.abs(n) <= 999, "banked rolls are nonzero, |value| 1..999"))).default({}),
196
205
  // Phase 3 activatable buffs: ids/slugs of activatable features/boons that are
197
206
  // currently toggled on. Their effects fold into recalc only while listed here.
198
207
  active_buffs: z.array(z.string()).optional(),
@@ -284,6 +293,15 @@ var characterSchema = z.object({
284
293
  gp: z.number().int().nonnegative(),
285
294
  pp: z.number().int().nonnegative()
286
295
  }).optional(),
296
+ /** Features granted directly to this character by the DM — a campaign boon, a sanity track, hero
297
+ * points. Each entry names an `optional-feature` entity; the engine resolves it exactly the way a
298
+ * pool pick resolves, but with no pool and no class choice behind it.
299
+ *
300
+ * WHERE a grant is drawn is the declaring NOTE's business, not the character's: the note's
301
+ * `surface: band` opts it into the header strip. A character-side opt-OUT flag lived here briefly
302
+ * and was removed — two mechanisms for one job, and the opt-out eroded the band's shortness one
303
+ * grant at a time. */
304
+ additional_features: z.array(z.string().min(1)).default([]),
287
305
  notes: z.string().optional(),
288
306
  defenses: z.object({
289
307
  resistances: z.array(z.string()).default([]),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  featEntitySchema
3
- } from "./chunk-RVXC6ZHY.js";
3
+ } from "./chunk-BJFL4KYG.js";
4
4
 
5
5
  // src/feat/feat.parser.ts
6
6
  import { parseYaml } from "@archivist-gg/core";
@@ -19,6 +19,10 @@ var resourceSchema = z.object({
19
19
  resource: z.string().min(1),
20
20
  amount: z.number().int().positive()
21
21
  }).optional(),
22
+ /** Presentation, carried and never interpreted by the engine: which control draws this resource, and
23
+ * whether it also appears in the header band. The renderer owns the hint → component table. */
24
+ rendering_hint: z.string().optional(),
25
+ surface: z.enum(["band", "tab"]).optional(),
22
26
  recovery: z.array(z.object({
23
27
  id: z.string().min(1),
24
28
  name: z.string().min(1),
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  classCodec
3
- } from "../chunk-EHDATXP7.js";
4
- import "../chunk-5RKLL2AD.js";
3
+ } from "../chunk-BS355LND.js";
4
+ import "../chunk-H4VEMVOO.js";
5
5
  import "../chunk-4U55VNC7.js";
6
- import "../chunk-CGRLQ34U.js";
6
+ import "../chunk-DTCR4DBF.js";
7
7
  import "../chunk-23LZLBJK.js";
8
8
  import "../chunk-CJYCVNW2.js";
9
- import "../chunk-W74BCA7V.js";
9
+ import "../chunk-JOVMV2X5.js";
10
10
  import "../chunk-WYGD2UGN.js";
11
- import "../chunk-YLL2QXY2.js";
11
+ import "../chunk-UA3I2YSO.js";
12
12
  import "../chunk-IN4M4X7P.js";
13
13
  import "../chunk-IWRVOCT5.js";
14
14
  import "../chunk-X3D6LXUU.js";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  parseClass
3
- } from "../chunk-5RKLL2AD.js";
3
+ } from "../chunk-H4VEMVOO.js";
4
4
  import "../chunk-4U55VNC7.js";
5
- import "../chunk-CGRLQ34U.js";
5
+ import "../chunk-DTCR4DBF.js";
6
6
  import "../chunk-23LZLBJK.js";
7
7
  import "../chunk-CJYCVNW2.js";
8
- import "../chunk-W74BCA7V.js";
8
+ import "../chunk-JOVMV2X5.js";
9
9
  import "../chunk-WYGD2UGN.js";
10
- import "../chunk-YLL2QXY2.js";
10
+ import "../chunk-UA3I2YSO.js";
11
11
  import "../chunk-IN4M4X7P.js";
12
12
  import "../chunk-IWRVOCT5.js";
13
13
  import "../chunk-X3D6LXUU.js";
@@ -146,6 +146,11 @@ export declare const classEntitySchema: z.ZodObject<{
146
146
  resource: z.ZodString;
147
147
  amount: z.ZodNumber;
148
148
  }, z.core.$strip>>;
149
+ rendering_hint: z.ZodOptional<z.ZodString>;
150
+ surface: z.ZodOptional<z.ZodEnum<{
151
+ band: "band";
152
+ tab: "tab";
153
+ }>>;
149
154
  recovery: z.ZodOptional<z.ZodArray<z.ZodObject<{
150
155
  id: z.ZodString;
151
156
  name: z.ZodString;
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  classEntitySchema
3
- } from "../chunk-CGRLQ34U.js";
3
+ } from "../chunk-DTCR4DBF.js";
4
4
  import "../chunk-23LZLBJK.js";
5
5
  import "../chunk-CJYCVNW2.js";
6
- import "../chunk-W74BCA7V.js";
6
+ import "../chunk-JOVMV2X5.js";
7
7
  import "../chunk-WYGD2UGN.js";
8
- import "../chunk-YLL2QXY2.js";
8
+ import "../chunk-UA3I2YSO.js";
9
9
  import "../chunk-IN4M4X7P.js";
10
10
  import "../chunk-IWRVOCT5.js";
11
11
  import "../chunk-X3D6LXUU.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  featCodec
3
- } from "../chunk-Y4AGLCY6.js";
4
- import "../chunk-KADJZGZY.js";
5
- import "../chunk-RVXC6ZHY.js";
6
- import "../chunk-YLL2QXY2.js";
3
+ } from "../chunk-JKLRBILX.js";
4
+ import "../chunk-RWZTGMSN.js";
5
+ import "../chunk-BJFL4KYG.js";
6
+ import "../chunk-UA3I2YSO.js";
7
7
  import "../chunk-IWRVOCT5.js";
8
8
  import "../chunk-X3D6LXUU.js";
9
9
  import "../chunk-UA5KS2PL.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  parseFeat
3
- } from "../chunk-KADJZGZY.js";
4
- import "../chunk-RVXC6ZHY.js";
5
- import "../chunk-YLL2QXY2.js";
3
+ } from "../chunk-RWZTGMSN.js";
4
+ import "../chunk-BJFL4KYG.js";
5
+ import "../chunk-UA3I2YSO.js";
6
6
  import "../chunk-IWRVOCT5.js";
7
7
  import "../chunk-X3D6LXUU.js";
8
8
  import "../chunk-UA5KS2PL.js";
@@ -394,6 +394,11 @@ export declare const featEntitySchema: z.ZodObject<{
394
394
  resource: z.ZodString;
395
395
  amount: z.ZodNumber;
396
396
  }, z.core.$strip>>;
397
+ rendering_hint: z.ZodOptional<z.ZodString>;
398
+ surface: z.ZodOptional<z.ZodEnum<{
399
+ band: "band";
400
+ tab: "tab";
401
+ }>>;
397
402
  recovery: z.ZodOptional<z.ZodArray<z.ZodObject<{
398
403
  id: z.ZodString;
399
404
  name: z.ZodString;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  featEntitySchema
3
- } from "../chunk-RVXC6ZHY.js";
4
- import "../chunk-YLL2QXY2.js";
3
+ } from "../chunk-BJFL4KYG.js";
4
+ import "../chunk-UA3I2YSO.js";
5
5
  import "../chunk-IWRVOCT5.js";
6
6
  import "../chunk-X3D6LXUU.js";
7
7
  import "../chunk-UA5KS2PL.js";
package/dist/index.js CHANGED
@@ -5,9 +5,9 @@ import "./chunk-HZA7OE7N.js";
5
5
  import "./chunk-2DFENQIJ.js";
6
6
  import {
7
7
  subclassCodec
8
- } from "./chunk-CV5XDJKU.js";
9
- import "./chunk-KK33PIDN.js";
10
- import "./chunk-OAXWSVJB.js";
8
+ } from "./chunk-B4TQTJVC.js";
9
+ import "./chunk-ABQ6ZWAF.js";
10
+ import "./chunk-MT4RWROQ.js";
11
11
  import {
12
12
  spellInputSchema
13
13
  } from "./chunk-656QCX4U.js";
@@ -19,9 +19,9 @@ import {
19
19
  } from "./chunk-WSVMLNTO.js";
20
20
  import {
21
21
  raceCodec
22
- } from "./chunk-3JOTQW7A.js";
23
- import "./chunk-N3A5ICBO.js";
24
- import "./chunk-UMWIPLLM.js";
22
+ } from "./chunk-CQAL67R3.js";
23
+ import "./chunk-7MRW4RTH.js";
24
+ import "./chunk-2BI5NAXK.js";
25
25
  import "./chunk-OZP6JFGT.js";
26
26
  import "./chunk-VQAXTIBW.js";
27
27
  import {
@@ -34,14 +34,14 @@ import {
34
34
  } from "./chunk-6PXTNCBP.js";
35
35
  import {
36
36
  parseMonster
37
- } from "./chunk-V2MJE6NC.js";
38
- import "./chunk-UQ3ZFCWD.js";
37
+ } from "./chunk-3BMVVNCS.js";
38
+ import "./chunk-A6YL53NC.js";
39
39
  import "./chunk-IKSMX4TN.js";
40
40
  import {
41
41
  optionalFeatureCodec
42
- } from "./chunk-RF4TYCA2.js";
43
- import "./chunk-6BFWZW6T.js";
44
- import "./chunk-WHVLNFTG.js";
42
+ } from "./chunk-MYW7AOUE.js";
43
+ import "./chunk-NNAQ4KQF.js";
44
+ import "./chunk-7WNQJAZW.js";
45
45
  import {
46
46
  itemCodec
47
47
  } from "./chunk-4XTNPNQB.js";
@@ -64,23 +64,23 @@ import {
64
64
  } from "./chunk-LSLSG67V.js";
65
65
  import {
66
66
  featCodec
67
- } from "./chunk-Y4AGLCY6.js";
68
- import "./chunk-KADJZGZY.js";
69
- import "./chunk-RVXC6ZHY.js";
67
+ } from "./chunk-JKLRBILX.js";
68
+ import "./chunk-RWZTGMSN.js";
69
+ import "./chunk-BJFL4KYG.js";
70
70
  import "./chunk-DEEHHLAP.js";
71
71
  import "./chunk-PDJSUH24.js";
72
72
  import "./chunk-WELIKLZF.js";
73
73
  import {
74
74
  classCodec
75
- } from "./chunk-EHDATXP7.js";
76
- import "./chunk-5RKLL2AD.js";
75
+ } from "./chunk-BS355LND.js";
76
+ import "./chunk-H4VEMVOO.js";
77
77
  import "./chunk-4U55VNC7.js";
78
- import "./chunk-CGRLQ34U.js";
78
+ import "./chunk-DTCR4DBF.js";
79
79
  import "./chunk-23LZLBJK.js";
80
80
  import "./chunk-CJYCVNW2.js";
81
- import "./chunk-W74BCA7V.js";
81
+ import "./chunk-JOVMV2X5.js";
82
82
  import "./chunk-WYGD2UGN.js";
83
- import "./chunk-YLL2QXY2.js";
83
+ import "./chunk-UA3I2YSO.js";
84
84
  import {
85
85
  parseCondition
86
86
  } from "./chunk-6X2NV665.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  migrateLegacy,
3
3
  parseMonster
4
- } from "../chunk-V2MJE6NC.js";
5
- import "../chunk-UQ3ZFCWD.js";
6
- import "../chunk-W74BCA7V.js";
4
+ } from "../chunk-3BMVVNCS.js";
5
+ import "../chunk-A6YL53NC.js";
6
+ import "../chunk-JOVMV2X5.js";
7
7
  import "../chunk-WYGD2UGN.js";
8
- import "../chunk-YLL2QXY2.js";
8
+ import "../chunk-UA3I2YSO.js";
9
9
  import "../chunk-IWRVOCT5.js";
10
10
  import "../chunk-X3D6LXUU.js";
11
11
  import "../chunk-UA5KS2PL.js";
@@ -11,10 +11,10 @@ import {
11
11
  monsterSpeedSchema,
12
12
  monsterSpellcastingSchema,
13
13
  monsterTypeSchema
14
- } from "../chunk-UQ3ZFCWD.js";
15
- import "../chunk-W74BCA7V.js";
14
+ } from "../chunk-A6YL53NC.js";
15
+ import "../chunk-JOVMV2X5.js";
16
16
  import "../chunk-WYGD2UGN.js";
17
- import "../chunk-YLL2QXY2.js";
17
+ import "../chunk-UA3I2YSO.js";
18
18
  import "../chunk-IWRVOCT5.js";
19
19
  import "../chunk-X3D6LXUU.js";
20
20
  import "../chunk-UA5KS2PL.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  optionalFeatureCodec
3
- } from "../chunk-RF4TYCA2.js";
4
- import "../chunk-6BFWZW6T.js";
5
- import "../chunk-WHVLNFTG.js";
3
+ } from "../chunk-MYW7AOUE.js";
4
+ import "../chunk-NNAQ4KQF.js";
5
+ import "../chunk-7WNQJAZW.js";
6
6
  import "../chunk-WYGD2UGN.js";
7
- import "../chunk-YLL2QXY2.js";
7
+ import "../chunk-UA3I2YSO.js";
8
8
  import "../chunk-X3D6LXUU.js";
9
9
  import "../chunk-UA5KS2PL.js";
10
10
  export {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseOptionalFeature
3
- } from "../chunk-6BFWZW6T.js";
4
- import "../chunk-WHVLNFTG.js";
3
+ } from "../chunk-NNAQ4KQF.js";
4
+ import "../chunk-7WNQJAZW.js";
5
5
  import "../chunk-WYGD2UGN.js";
6
- import "../chunk-YLL2QXY2.js";
6
+ import "../chunk-UA3I2YSO.js";
7
7
  import "../chunk-X3D6LXUU.js";
8
8
  import "../chunk-UA5KS2PL.js";
9
9
  export {
@@ -342,6 +342,25 @@ export declare const optionalFeatureEntitySchema: z.ZodObject<{
342
342
  round: "round";
343
343
  custom: "custom";
344
344
  }>;
345
+ recovery: z.ZodOptional<z.ZodArray<z.ZodObject<{
346
+ id: z.ZodString;
347
+ name: z.ZodString;
348
+ amount: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
349
+ reset: z.ZodEnum<{
350
+ "short-rest": "short-rest";
351
+ "long-rest": "long-rest";
352
+ either: "either";
353
+ dawn: "dawn";
354
+ dusk: "dusk";
355
+ turn: "turn";
356
+ round: "round";
357
+ custom: "custom";
358
+ }>;
359
+ restores: z.ZodOptional<z.ZodEnum<{
360
+ uses: "uses";
361
+ "spell-slots": "spell-slots";
362
+ }>>;
363
+ }, z.core.$strip>>>;
345
364
  }, z.core.$strip>>>;
346
365
  consumes: z.ZodOptional<z.ZodNullable<z.ZodObject<{
347
366
  source: z.ZodOptional<z.ZodEnum<{
@@ -386,6 +405,10 @@ export declare const optionalFeatureEntitySchema: z.ZodObject<{
386
405
  passive: z.ZodOptional<z.ZodBoolean>;
387
406
  activatable: z.ZodOptional<z.ZodBoolean>;
388
407
  rendering_hint: z.ZodOptional<z.ZodString>;
408
+ surface: z.ZodOptional<z.ZodEnum<{
409
+ band: "band";
410
+ tab: "tab";
411
+ }>>;
389
412
  additional_spells: z.ZodOptional<z.ZodArray<z.ZodObject<{
390
413
  name: z.ZodOptional<z.ZodString>;
391
414
  ability: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  optionalFeatureEntitySchema
3
- } from "../chunk-WHVLNFTG.js";
3
+ } from "../chunk-7WNQJAZW.js";
4
4
  import "../chunk-WYGD2UGN.js";
5
- import "../chunk-YLL2QXY2.js";
5
+ import "../chunk-UA3I2YSO.js";
6
6
  import "../chunk-X3D6LXUU.js";
7
7
  import "../chunk-UA5KS2PL.js";
8
8
  export {
@@ -4,8 +4,8 @@ import {
4
4
  collectChosenAbilityPoints,
5
5
  collectChosenProficiencies,
6
6
  computeEffectiveProficiencies
7
- } from "../chunk-2VDTW7QO.js";
8
- import "../chunk-UXPGW4TI.js";
7
+ } from "../chunk-EYZCCWG2.js";
8
+ import "../chunk-4ISR76DH.js";
9
9
  import "../chunk-DPUJDOKV.js";
10
10
  import "../chunk-O4SRD3J5.js";
11
11
  import "../chunk-PP2UHLBA.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  characterSchema
3
- } from "../chunk-I2NDWFDD.js";
3
+ } from "../chunk-PZCE7DJM.js";
4
4
  import "../chunk-S7REMWW4.js";
5
5
 
6
6
  // src/pc/pc.parser.ts
@@ -2,8 +2,8 @@ import {
2
2
  collectChosenProficiencies,
3
3
  collectProficiencyGrants,
4
4
  computeEffectiveProficiencies
5
- } from "../chunk-2VDTW7QO.js";
6
- import "../chunk-UXPGW4TI.js";
5
+ } from "../chunk-EYZCCWG2.js";
6
+ import "../chunk-4ISR76DH.js";
7
7
  import "../chunk-DPUJDOKV.js";
8
8
  import "../chunk-O4SRD3J5.js";
9
9
  import "../chunk-PP2UHLBA.js";
@@ -4,10 +4,10 @@ import {
4
4
  import {
5
5
  collectChosenAbilityPoints,
6
6
  collectChosenProficiencies
7
- } from "../chunk-2VDTW7QO.js";
7
+ } from "../chunk-EYZCCWG2.js";
8
8
  import {
9
9
  resourceLevelFor
10
- } from "../chunk-UXPGW4TI.js";
10
+ } from "../chunk-4ISR76DH.js";
11
11
  import {
12
12
  computeSpellLimits,
13
13
  deriveSpellSlots,
@@ -127,4 +127,18 @@ export declare function collectChosenGrantedFeatures(character: Character, class
127
127
  data: Record<string, unknown>;
128
128
  } | undefined;
129
129
  }, race?: RaceEntity | null, background?: BackgroundEntity | null): ResolvedFeature[];
130
+ /**
131
+ * `character.additional_features` → ResolvedFeatures (the DM-grant path).
132
+ *
133
+ * Deliberately NOT routed through `walkChoiceGrants`: that converter emits
134
+ * `{id, name, description, effects, action}` and DROPS `uses`, which is correct there because a pool
135
+ * pick's tracker is built separately by `resolveResourceIndex`'s pools loop. A campaign grant has no
136
+ * pool, so nothing would ever build its tracker — the feature would render as a card with no counter.
137
+ * This walk therefore converts `uses` into the feature's own `resources[]`, which
138
+ * `resolveFeatureResources` already indexes, and the grant gets a tracker with no pool involved.
139
+ *
140
+ * A prose `uses.max` (the 4 shipped carriers that have one) yields no resource rather than a broken
141
+ * tracker, matching the pools loop's rule; the feature still renders.
142
+ */
143
+ export declare function collectAdditionalFeatures(character: Character, entities: EntityRegistry, warnings: string[]): ResolvedFeature[];
130
144
  export declare function collectResolvedFeatures(race: RaceEntity | null, classes: ResolvedClass[], background: BackgroundEntity | null, feats: FeatEntity[], featVia?: ReadonlyMap<string, FeatVia>): ResolvedFeature[];
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  PCResolver,
3
+ collectAdditionalFeatures,
3
4
  collectChosenGrantedFeatures,
4
5
  collectChosenWeaponMasteries,
5
6
  collectFeatGrantedSpells,
@@ -9,8 +10,8 @@ import {
9
10
  dedupeResolvedSpells,
10
11
  resolveOriginFeat,
11
12
  stripSlug
12
- } from "../chunk-2VDTW7QO.js";
13
- import "../chunk-UXPGW4TI.js";
13
+ } from "../chunk-EYZCCWG2.js";
14
+ import "../chunk-4ISR76DH.js";
14
15
  import "../chunk-DPUJDOKV.js";
15
16
  import "../chunk-O4SRD3J5.js";
16
17
  import "../chunk-PP2UHLBA.js";
@@ -37,6 +38,7 @@ import "../chunk-4U55VNC7.js";
37
38
  import "../chunk-UA5KS2PL.js";
38
39
  export {
39
40
  PCResolver,
41
+ collectAdditionalFeatures,
40
42
  collectChosenGrantedFeatures,
41
43
  collectChosenWeaponMasteries,
42
44
  collectFeatGrantedSpells,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resourceLevelFor
3
- } from "../chunk-UXPGW4TI.js";
3
+ } from "../chunk-4ISR76DH.js";
4
4
  import "../chunk-DPUJDOKV.js";
5
5
  import {
6
6
  numericColumnsAt
@@ -50,6 +50,12 @@ export interface ResolvedResource {
50
50
  scalesAt?: ResourceScaleStep[];
51
51
  recovery?: ResolvedRecovery[];
52
52
  owner: ResourceOwner;
53
+ /** Which control draws this resource — the declaring note's `rendering_hint`, carried verbatim and
54
+ * never interpreted here. The renderer owns the hint → component table; the engine only delivers the
55
+ * string, so adding a component is a change in ONE table and adding a resource is no code at all. */
56
+ renderingHint?: string;
57
+ /** `band` = also drawn in the header strip. Absent = the Resources tab alone. */
58
+ surface?: "band" | "tab";
53
59
  }
54
60
  export type ResourceIndex = ReadonlyMap<string, ResolvedResource>;
55
61
  /** KIND first (the schema key; absent = `uses`, UR2), FLAVOUR second (`action` / `reset`): a
@@ -4,7 +4,7 @@ import {
4
4
  resolveRecovery,
5
5
  resolveResourceIndex,
6
6
  resourceLevelFor
7
- } from "../chunk-UXPGW4TI.js";
7
+ } from "../chunk-4ISR76DH.js";
8
8
  import "../chunk-DPUJDOKV.js";
9
9
  import "../chunk-IUAAXXNO.js";
10
10
  import "../chunk-LVQLWRCU.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveResourceIndex
3
- } from "../chunk-UXPGW4TI.js";
3
+ } from "../chunk-4ISR76DH.js";
4
4
  import "../chunk-DPUJDOKV.js";
5
5
  import "../chunk-IUAAXXNO.js";
6
6
  import "../chunk-LVQLWRCU.js";
@@ -65,7 +65,7 @@ function pushBuffEnds(cats, character, resolved) {
65
65
  }
66
66
  }
67
67
  function computeRestPlan(character, resolved, derived, registry, type) {
68
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
68
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
69
69
  const cats = [];
70
70
  let hdRegainDist;
71
71
  const index = resolveResourceIndex(resolved);
@@ -128,14 +128,17 @@ function computeRestPlan(character, resolved, derived, registry, type) {
128
128
  }
129
129
  }
130
130
  for (const [key, fu] of Object.entries((_f = character.state.feature_uses) != null ? _f : {})) {
131
- if (fu.used <= 0) continue;
131
+ const banked = (_g = character.state.feature_rolls) == null ? void 0 : _g[key];
132
+ const bankedRolls = banked ? banked.filter((v) => v > 0).length : 0;
133
+ if (fu.used <= 0 && bankedRolls <= 0) continue;
132
134
  const res = index.get(key);
133
- const reset = (_g = res == null ? void 0 : res.reset) != null ? _g : "long-rest";
135
+ const reset = (_h = res == null ? void 0 : res.reset) != null ? _h : "long-rest";
134
136
  if (reset !== "short-rest" && reset !== "long-rest" && reset !== "either" && reset !== "dawn" && reset !== "dusk") continue;
137
+ const rollsNote = bankedRolls > 0 ? ` \xB7 ${bankedRolls} roll${bankedRolls === 1 ? "" : "s"} cleared` : "";
135
138
  cats.push({
136
139
  id: `feature:${key}`,
137
- label: (_h = res == null ? void 0 : res.name) != null ? _h : key,
138
- preview: `${fu.used}/${fu.max} restored`
140
+ label: (_i = res == null ? void 0 : res.name) != null ? _i : key,
141
+ preview: `${fu.used}/${fu.max} restored${rollsNote}`
139
142
  });
140
143
  }
141
144
  pushPartialRecoveries(cats, character, index, "long");
@@ -155,15 +158,18 @@ function computeRestPlan(character, resolved, derived, registry, type) {
155
158
  });
156
159
  }
157
160
  if (type === "short") {
158
- for (const [key, fu] of Object.entries((_i = character.state.feature_uses) != null ? _i : {})) {
159
- if (fu.used <= 0) continue;
161
+ for (const [key, fu] of Object.entries((_j = character.state.feature_uses) != null ? _j : {})) {
162
+ const banked = (_k = character.state.feature_rolls) == null ? void 0 : _k[key];
163
+ const bankedRolls = banked ? banked.filter((v) => v > 0).length : 0;
164
+ if (fu.used <= 0 && bankedRolls <= 0) continue;
160
165
  const res = index.get(key);
161
- const r = (_j = res == null ? void 0 : res.reset) != null ? _j : "long-rest";
166
+ const r = (_l = res == null ? void 0 : res.reset) != null ? _l : "long-rest";
162
167
  if (r !== "short-rest" && r !== "either") continue;
168
+ const rollsNote = bankedRolls > 0 ? ` \xB7 ${bankedRolls} roll${bankedRolls === 1 ? "" : "s"} cleared` : "";
163
169
  cats.push({
164
170
  id: `feature:${key}`,
165
- label: (_k = res == null ? void 0 : res.name) != null ? _k : key,
166
- preview: `${fu.used}/${fu.max} restored`
171
+ label: (_m = res == null ? void 0 : res.name) != null ? _m : key,
172
+ preview: `${fu.used}/${fu.max} restored${rollsNote}`
167
173
  });
168
174
  }
169
175
  pushPartialRecoveries(cats, character, index, "short");
@@ -183,7 +189,7 @@ function computeRestPlan(character, resolved, derived, registry, type) {
183
189
  });
184
190
  }
185
191
  pushBuffEnds(cats, character, resolved);
186
- const hdAvailable = type === "short" ? Object.entries((_l = character.state.hit_dice) != null ? _l : {}).filter(([, hd]) => hd.used < hd.total).map(([die, hd]) => ({ die, remaining: hd.total - hd.used })) : [];
192
+ const hdAvailable = type === "short" ? Object.entries((_n = character.state.hit_dice) != null ? _n : {}).filter(([, hd]) => hd.used < hd.total).map(([die, hd]) => ({ die, remaining: hd.total - hd.used })) : [];
187
193
  return { type, categories: cats, hdAvailable, hdRegainDist };
188
194
  }
189
195
  export {
@@ -389,6 +389,7 @@ export declare const characterSchema: z.ZodObject<{
389
389
  gp: z.ZodNumber;
390
390
  pp: z.ZodNumber;
391
391
  }, z.core.$strip>>;
392
+ additional_features: z.ZodDefault<z.ZodArray<z.ZodString>>;
392
393
  notes: z.ZodOptional<z.ZodString>;
393
394
  defenses: z.ZodOptional<z.ZodObject<{
394
395
  resistances: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
@@ -449,6 +450,7 @@ export declare const characterSchema: z.ZodObject<{
449
450
  used: z.ZodNumber;
450
451
  max: z.ZodNumber;
451
452
  }, z.core.$strip>>>;
453
+ feature_rolls: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodNumber>>>;
452
454
  active_buffs: z.ZodOptional<z.ZodArray<z.ZodString>>;
453
455
  attuned_items: z.ZodOptional<z.ZodArray<z.ZodString>>;
454
456
  }, z.core.$strip>;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  characterOverridesShape,
3
3
  characterSchema
4
- } from "../chunk-I2NDWFDD.js";
4
+ } from "../chunk-PZCE7DJM.js";
5
5
  import "../chunk-S7REMWW4.js";
6
6
  export {
7
7
  characterOverridesShape,
@@ -217,6 +217,9 @@ export interface CharacterState {
217
217
  used: number;
218
218
  max: number;
219
219
  }>;
220
+ /** R4-G5 G8: banked prerolled dice, keyed by the SAME resource id as feature_uses.
221
+ * SIGN IS STATE: negative = spent, positive = live; positions never move. */
222
+ feature_rolls: Record<string, number[]>;
220
223
  /** Phase 3 activatable buffs: ids/slugs of activatable features/boons currently
221
224
  * toggled on. Their effects fold into recalc only while listed here. */
222
225
  active_buffs?: string[];
@@ -279,6 +282,9 @@ export interface Character {
279
282
  gp: number;
280
283
  pp: number;
281
284
  };
285
+ /** DM grants: optional-feature slugs resolved onto the character with no class, level or pool behind
286
+ * them. Placement is the declaring note's `surface`, never a flag here. */
287
+ additional_features?: string[];
282
288
  notes?: string;
283
289
  defenses?: {
284
290
  resistances?: string[];
@@ -317,6 +323,11 @@ export type FeatureSource = {
317
323
  kind: "feat";
318
324
  slug: string;
319
325
  via?: FeatVia;
326
+ }
327
+ /** A DM grant listed in `character.additional_features`. No class, no level, no pool behind it. */
328
+ | {
329
+ kind: "campaign";
330
+ slug: string;
320
331
  };
321
332
  export interface ResolvedFeature {
322
333
  feature: Feature;
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  raceCodec
3
- } from "../chunk-3JOTQW7A.js";
4
- import "../chunk-N3A5ICBO.js";
5
- import "../chunk-UMWIPLLM.js";
3
+ } from "../chunk-CQAL67R3.js";
4
+ import "../chunk-7MRW4RTH.js";
5
+ import "../chunk-2BI5NAXK.js";
6
6
  import "../chunk-4U55VNC7.js";
7
- import "../chunk-W74BCA7V.js";
7
+ import "../chunk-JOVMV2X5.js";
8
8
  import "../chunk-WYGD2UGN.js";
9
- import "../chunk-YLL2QXY2.js";
9
+ import "../chunk-UA3I2YSO.js";
10
10
  import "../chunk-IWRVOCT5.js";
11
11
  import "../chunk-X3D6LXUU.js";
12
12
  import "../chunk-UA5KS2PL.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  parseRace
3
- } from "../chunk-N3A5ICBO.js";
4
- import "../chunk-UMWIPLLM.js";
3
+ } from "../chunk-7MRW4RTH.js";
4
+ import "../chunk-2BI5NAXK.js";
5
5
  import "../chunk-4U55VNC7.js";
6
- import "../chunk-W74BCA7V.js";
6
+ import "../chunk-JOVMV2X5.js";
7
7
  import "../chunk-WYGD2UGN.js";
8
- import "../chunk-YLL2QXY2.js";
8
+ import "../chunk-UA3I2YSO.js";
9
9
  import "../chunk-IWRVOCT5.js";
10
10
  import "../chunk-X3D6LXUU.js";
11
11
  import "../chunk-UA5KS2PL.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  fixedAsiSchema,
3
3
  raceEntitySchema
4
- } from "../chunk-UMWIPLLM.js";
5
- import "../chunk-W74BCA7V.js";
4
+ } from "../chunk-2BI5NAXK.js";
5
+ import "../chunk-JOVMV2X5.js";
6
6
  import "../chunk-WYGD2UGN.js";
7
- import "../chunk-YLL2QXY2.js";
7
+ import "../chunk-UA3I2YSO.js";
8
8
  import "../chunk-IWRVOCT5.js";
9
9
  import "../chunk-X3D6LXUU.js";
10
10
  import "../chunk-UA5KS2PL.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  featureObjectSchema,
3
3
  featureSchema
4
- } from "../chunk-W74BCA7V.js";
4
+ } from "../chunk-JOVMV2X5.js";
5
5
  import "../chunk-WYGD2UGN.js";
6
- import "../chunk-YLL2QXY2.js";
6
+ import "../chunk-UA3I2YSO.js";
7
7
  import "../chunk-IWRVOCT5.js";
8
8
  import "../chunk-X3D6LXUU.js";
9
9
  export {
@@ -42,6 +42,11 @@ export declare const resourceSchema: z.ZodObject<{
42
42
  resource: z.ZodString;
43
43
  amount: z.ZodNumber;
44
44
  }, z.core.$strip>>;
45
+ rendering_hint: z.ZodOptional<z.ZodString>;
46
+ surface: z.ZodOptional<z.ZodEnum<{
47
+ band: "band";
48
+ tab: "tab";
49
+ }>>;
45
50
  recovery: z.ZodOptional<z.ZodArray<z.ZodObject<{
46
51
  id: z.ZodString;
47
52
  name: z.ZodString;
@@ -3,7 +3,7 @@ import {
3
3
  resetTriggerEnum,
4
4
  resourceConsumptionSchema,
5
5
  resourceSchema
6
- } from "../chunk-YLL2QXY2.js";
6
+ } from "../chunk-UA3I2YSO.js";
7
7
  export {
8
8
  actionCostEnum,
9
9
  resetTriggerEnum,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  subclassCodec
3
- } from "../chunk-CV5XDJKU.js";
4
- import "../chunk-KK33PIDN.js";
5
- import "../chunk-OAXWSVJB.js";
3
+ } from "../chunk-B4TQTJVC.js";
4
+ import "../chunk-ABQ6ZWAF.js";
5
+ import "../chunk-MT4RWROQ.js";
6
6
  import "../chunk-4U55VNC7.js";
7
7
  import "../chunk-23LZLBJK.js";
8
8
  import "../chunk-CJYCVNW2.js";
9
- import "../chunk-W74BCA7V.js";
9
+ import "../chunk-JOVMV2X5.js";
10
10
  import "../chunk-WYGD2UGN.js";
11
- import "../chunk-YLL2QXY2.js";
11
+ import "../chunk-UA3I2YSO.js";
12
12
  import "../chunk-IWRVOCT5.js";
13
13
  import "../chunk-X3D6LXUU.js";
14
14
  import "../chunk-UA5KS2PL.js";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  parseSubclass
3
- } from "../chunk-KK33PIDN.js";
4
- import "../chunk-OAXWSVJB.js";
3
+ } from "../chunk-ABQ6ZWAF.js";
4
+ import "../chunk-MT4RWROQ.js";
5
5
  import "../chunk-4U55VNC7.js";
6
6
  import "../chunk-23LZLBJK.js";
7
7
  import "../chunk-CJYCVNW2.js";
8
- import "../chunk-W74BCA7V.js";
8
+ import "../chunk-JOVMV2X5.js";
9
9
  import "../chunk-WYGD2UGN.js";
10
- import "../chunk-YLL2QXY2.js";
10
+ import "../chunk-UA3I2YSO.js";
11
11
  import "../chunk-IWRVOCT5.js";
12
12
  import "../chunk-X3D6LXUU.js";
13
13
  import "../chunk-UA5KS2PL.js";
@@ -61,6 +61,11 @@ export declare const subclassEntitySchema: z.ZodObject<{
61
61
  resource: z.ZodString;
62
62
  amount: z.ZodNumber;
63
63
  }, z.core.$strip>>;
64
+ rendering_hint: z.ZodOptional<z.ZodString>;
65
+ surface: z.ZodOptional<z.ZodEnum<{
66
+ band: "band";
67
+ tab: "tab";
68
+ }>>;
64
69
  recovery: z.ZodOptional<z.ZodArray<z.ZodObject<{
65
70
  id: z.ZodString;
66
71
  name: z.ZodString;
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  subclassEntitySchema
3
- } from "../chunk-OAXWSVJB.js";
3
+ } from "../chunk-MT4RWROQ.js";
4
4
  import "../chunk-23LZLBJK.js";
5
5
  import "../chunk-CJYCVNW2.js";
6
- import "../chunk-W74BCA7V.js";
6
+ import "../chunk-JOVMV2X5.js";
7
7
  import "../chunk-WYGD2UGN.js";
8
- import "../chunk-YLL2QXY2.js";
8
+ import "../chunk-UA3I2YSO.js";
9
9
  import "../chunk-IWRVOCT5.js";
10
10
  import "../chunk-X3D6LXUU.js";
11
11
  import "../chunk-UA5KS2PL.js";
@@ -78,6 +78,8 @@ export interface OptionalFeatureEntity {
78
78
  * `rendering_hint` is the ONE of these whose VALUES are load-bearing (88 records carry a
79
79
  * real marker such as `dice-pool`); reading it is G4's row, declaring it is this one's. */
80
80
  rendering_hint?: string;
81
+ /** `band` = also drawn in the header strip. Absent/`tab` = the Resources tab only. */
82
+ surface?: "band" | "tab";
81
83
  additional_spells?: AdditionalSpellsEntry[];
82
84
  is_class_feature_variant?: boolean;
83
85
  has_fluff_images?: boolean;
@@ -41,6 +41,11 @@ export interface Resource {
41
41
  scales_at?: ResourceScaleStep[];
42
42
  die?: ResourceDie;
43
43
  reset: ResetTrigger;
44
+ /** Presentation, carried and never interpreted by the engine: which control draws this resource.
45
+ * The renderer owns the hint → component table. */
46
+ rendering_hint?: string;
47
+ /** `band` = also drawn in the header strip; absent = the Resources tab alone. */
48
+ surface?: "band" | "tab";
44
49
  consumes?: ResourceConsumesLink;
45
50
  recovery?: ResourceRecovery[];
46
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archivist-gg/dnd5e",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "license": "MIT AND CC-BY-4.0",
5
5
  "type": "module",
6
6
  "publishConfig": {