@archivist-gg/dnd5e 0.3.4 → 0.3.6

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 (81) 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-FDBWYMZK.js → chunk-2BI5NAXK.js} +2 -2
  5. package/dist/{chunk-COEOOQX2.js → chunk-3BMVVNCS.js} +1 -1
  6. package/dist/{chunk-LMBAEIP6.js → chunk-3PZHBLFG.js} +1 -1
  7. package/dist/{chunk-UXPGW4TI.js → chunk-4ISR76DH.js} +5 -0
  8. package/dist/{chunk-JRDPUDT5.js → chunk-63XQXPZL.js} +1 -1
  9. package/dist/{chunk-XGOFP7AQ.js → chunk-7MRW4RTH.js} +1 -1
  10. package/dist/{chunk-ZONJ66NE.js → chunk-7WNQJAZW.js} +20 -3
  11. package/dist/{chunk-6PKV5V2H.js → chunk-A6YL53NC.js} +1 -1
  12. package/dist/{chunk-HB7XZXDK.js → chunk-ABQ6ZWAF.js} +1 -1
  13. package/dist/{chunk-R3VAKWKW.js → chunk-B4TQTJVC.js} +1 -1
  14. package/dist/{chunk-RIXI5D3R.js → chunk-BJFL4KYG.js} +3 -3
  15. package/dist/{chunk-US5IKYCW.js → chunk-BS355LND.js} +1 -1
  16. package/dist/{chunk-XBQMCEI2.js → chunk-CQAL67R3.js} +1 -1
  17. package/dist/{chunk-YJJTISBN.js → chunk-DTCR4DBF.js} +3 -3
  18. package/dist/{chunk-I2NDWFDD.js → chunk-E4KG7HXO.js} +9 -0
  19. package/dist/{chunk-XQCSGNXE.js → chunk-EYZCCWG2.js} +51 -2
  20. package/dist/{chunk-5U5FFFA3.js → chunk-H4VEMVOO.js} +1 -1
  21. package/dist/{chunk-T26ZVCL5.js → chunk-IWRVOCT5.js} +1 -1
  22. package/dist/{chunk-ESS46GR4.js → chunk-JKLRBILX.js} +1 -1
  23. package/dist/{chunk-4FWNK4FD.js → chunk-JOVMV2X5.js} +3 -3
  24. package/dist/{chunk-JJ46T5XX.js → chunk-KPEJMNRD.js} +5 -0
  25. package/dist/{chunk-H7KBLR4X.js → chunk-MT4RWROQ.js} +2 -2
  26. package/dist/{chunk-IMZFOOVL.js → chunk-MYW7AOUE.js} +1 -1
  27. package/dist/{chunk-5XMQPTHD.js → chunk-NNAQ4KQF.js} +1 -1
  28. package/dist/{chunk-D5U7GMPX.js → chunk-RWZTGMSN.js} +1 -1
  29. package/dist/{chunk-YLL2QXY2.js → chunk-UA3I2YSO.js} +4 -0
  30. package/dist/{chunk-HJ5EEZ47.js → chunk-ULCQ73LA.js} +1 -1
  31. package/dist/{chunk-OSQFPGLM.js → chunk-V373TRJE.js} +4 -2
  32. package/dist/{chunk-WPWOHGPI.js → chunk-X3D6LXUU.js} +13 -0
  33. package/dist/class/class.codec.js +7 -7
  34. package/dist/class/class.parser.js +6 -6
  35. package/dist/class/class.schema.d.ts +5 -0
  36. package/dist/class/class.schema.js +5 -5
  37. package/dist/feat/feat.codec.js +6 -6
  38. package/dist/feat/feat.parser.js +5 -5
  39. package/dist/feat/feat.schema.d.ts +14 -0
  40. package/dist/feat/feat.schema.js +4 -4
  41. package/dist/index.js +24 -24
  42. package/dist/monster/monster.parser.js +6 -6
  43. package/dist/monster/monster.schema.js +5 -5
  44. package/dist/optional-feature/optional-feature.codec.js +5 -5
  45. package/dist/optional-feature/optional-feature.parser.js +4 -4
  46. package/dist/optional-feature/optional-feature.schema.d.ts +32 -0
  47. package/dist/optional-feature/optional-feature.schema.js +3 -3
  48. package/dist/pc/pc.decision-engine.js +3 -3
  49. package/dist/pc/pc.equipment.d.ts +1 -1
  50. package/dist/pc/pc.equipment.js +1 -1
  51. package/dist/pc/pc.feature-effects.d.ts +9 -0
  52. package/dist/pc/pc.feature-effects.js +1 -1
  53. package/dist/pc/pc.parser.js +1 -1
  54. package/dist/pc/pc.proficiencies.js +3 -3
  55. package/dist/pc/pc.recalc.js +6 -6
  56. package/dist/pc/pc.resolver.d.ts +14 -0
  57. package/dist/pc/pc.resolver.js +5 -3
  58. package/dist/pc/pc.resource-seed.js +1 -1
  59. package/dist/pc/pc.resources.d.ts +6 -0
  60. package/dist/pc/pc.resources.js +1 -1
  61. package/dist/pc/pc.rest.js +1 -1
  62. package/dist/pc/pc.schema.d.ts +1 -0
  63. package/dist/pc/pc.schema.js +1 -1
  64. package/dist/pc/pc.types.d.ts +8 -0
  65. package/dist/race/race.codec.js +7 -7
  66. package/dist/race/race.parser.js +6 -6
  67. package/dist/race/race.schema.js +5 -5
  68. package/dist/schemas/choice-schema.js +2 -2
  69. package/dist/schemas/feature-effect-schema.d.ts +9 -0
  70. package/dist/schemas/feature-effect-schema.js +1 -1
  71. package/dist/schemas/feature-schema.js +4 -4
  72. package/dist/schemas/resource-schema.d.ts +5 -0
  73. package/dist/schemas/resource-schema.js +1 -1
  74. package/dist/subclass/subclass.codec.js +7 -7
  75. package/dist/subclass/subclass.parser.js +6 -6
  76. package/dist/subclass/subclass.schema.d.ts +5 -0
  77. package/dist/subclass/subclass.schema.js +5 -5
  78. package/dist/types/feature-effect.d.ts +7 -0
  79. package/dist/types/optional-feature.types.d.ts +2 -0
  80. package/dist/types/resource.d.ts +5 -0
  81. package/package.json +1 -1
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  backgroundCodec
3
- } from "../chunk-HJ5EEZ47.js";
4
- import "../chunk-LMBAEIP6.js";
5
- import "../chunk-JRDPUDT5.js";
3
+ } from "../chunk-ULCQ73LA.js";
4
+ import "../chunk-3PZHBLFG.js";
5
+ import "../chunk-63XQXPZL.js";
6
6
  import "../chunk-IN4M4X7P.js";
7
- import "../chunk-T26ZVCL5.js";
8
- import "../chunk-WPWOHGPI.js";
7
+ import "../chunk-IWRVOCT5.js";
8
+ import "../chunk-X3D6LXUU.js";
9
9
  import "../chunk-UA5KS2PL.js";
10
10
  export {
11
11
  backgroundCodec
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  parseBackground
3
- } from "../chunk-LMBAEIP6.js";
4
- import "../chunk-JRDPUDT5.js";
3
+ } from "../chunk-3PZHBLFG.js";
4
+ import "../chunk-63XQXPZL.js";
5
5
  import "../chunk-IN4M4X7P.js";
6
- import "../chunk-T26ZVCL5.js";
7
- import "../chunk-WPWOHGPI.js";
6
+ import "../chunk-IWRVOCT5.js";
7
+ import "../chunk-X3D6LXUU.js";
8
8
  import "../chunk-UA5KS2PL.js";
9
9
  export {
10
10
  parseBackground
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  backgroundEntitySchema,
3
3
  langProfSchema
4
- } from "../chunk-JRDPUDT5.js";
4
+ } from "../chunk-63XQXPZL.js";
5
5
  import "../chunk-IN4M4X7P.js";
6
- import "../chunk-T26ZVCL5.js";
7
- import "../chunk-WPWOHGPI.js";
6
+ import "../chunk-IWRVOCT5.js";
7
+ import "../chunk-X3D6LXUU.js";
8
8
  import "../chunk-UA5KS2PL.js";
9
9
  export {
10
10
  backgroundEntitySchema,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  featureSchema
3
- } from "./chunk-4FWNK4FD.js";
3
+ } from "./chunk-JOVMV2X5.js";
4
4
  import {
5
5
  choiceSchema
6
- } from "./chunk-T26ZVCL5.js";
6
+ } from "./chunk-IWRVOCT5.js";
7
7
  import {
8
8
  additionalSpellsEntrySchema,
9
9
  imageField
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MONSTER_KNOWN_KEYS,
3
3
  monsterSchema
4
- } from "./chunk-6PKV5V2H.js";
4
+ } from "./chunk-A6YL53NC.js";
5
5
 
6
6
  // src/monster/monster.parser.ts
7
7
  import { parseYaml } from "@archivist-gg/core";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  backgroundEntitySchema
3
- } from "./chunk-JRDPUDT5.js";
3
+ } from "./chunk-63XQXPZL.js";
4
4
 
5
5
  // src/background/background.parser.ts
6
6
  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
  }
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-IN4M4X7P.js";
4
4
  import {
5
5
  choiceSchema
6
- } from "./chunk-T26ZVCL5.js";
6
+ } from "./chunk-IWRVOCT5.js";
7
7
  import {
8
8
  additionalSpellsEntrySchema,
9
9
  imageField
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raceEntitySchema
3
- } from "./chunk-FDBWYMZK.js";
3
+ } from "./chunk-2BI5NAXK.js";
4
4
  import {
5
5
  aliasFeatureListActionCost
6
6
  } from "./chunk-4U55VNC7.js";
@@ -5,10 +5,10 @@ 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
- } from "./chunk-WPWOHGPI.js";
11
+ } from "./chunk-X3D6LXUU.js";
12
12
  import {
13
13
  additionalSpellsEntrySchema,
14
14
  imageField,
@@ -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-4FWNK4FD.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-H7KBLR4X.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-HB7XZXDK.js";
3
+ } from "./chunk-ABQ6ZWAF.js";
4
4
 
5
5
  // src/subclass/subclass.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,13 +1,13 @@
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
- } from "./chunk-T26ZVCL5.js";
7
+ } from "./chunk-IWRVOCT5.js";
8
8
  import {
9
9
  featureEffectSchema
10
- } from "./chunk-WPWOHGPI.js";
10
+ } from "./chunk-X3D6LXUU.js";
11
11
  import {
12
12
  additionalSpellsEntrySchema,
13
13
  imageField,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseClass
3
- } from "./chunk-5U5FFFA3.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-XGOFP7AQ.js";
3
+ } from "./chunk-7MRW4RTH.js";
4
4
 
5
5
  // src/race/race.codec.ts
6
6
  import yaml from "js-yaml";
@@ -8,17 +8,17 @@ import {
8
8
  } from "./chunk-CJYCVNW2.js";
9
9
  import {
10
10
  featureSchema
11
- } from "./chunk-4FWNK4FD.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
18
18
  } from "./chunk-IN4M4X7P.js";
19
19
  import {
20
20
  choiceSchema
21
- } from "./chunk-T26ZVCL5.js";
21
+ } from "./chunk-IWRVOCT5.js";
22
22
  import {
23
23
  additionalSpellsEntrySchema,
24
24
  imageField,
@@ -284,6 +284,15 @@ var characterSchema = z.object({
284
284
  gp: z.number().int().nonnegative(),
285
285
  pp: z.number().int().nonnegative()
286
286
  }).optional(),
287
+ /** Features granted directly to this character by the DM — a campaign boon, a sanity track, hero
288
+ * points. Each entry names an `optional-feature` entity; the engine resolves it exactly the way a
289
+ * pool pick resolves, but with no pool and no class choice behind it.
290
+ *
291
+ * WHERE a grant is drawn is the declaring NOTE's business, not the character's: the note's
292
+ * `surface: band` opts it into the header strip. A character-side opt-OUT flag lived here briefly
293
+ * and was removed — two mechanisms for one job, and the opt-out eroded the band's shortness one
294
+ * grant at a time. */
295
+ additional_features: z.array(z.string().min(1)).default([]),
287
296
  notes: z.string().optional(),
288
297
  defenses: z.object({
289
298
  resistances: z.array(z.string()).default([]),
@@ -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,
@@ -29,7 +29,7 @@ import {
29
29
  import {
30
30
  assembleEffectFeatures,
31
31
  collectProficiencyEffectGrants
32
- } from "./chunk-JJ46T5XX.js";
32
+ } from "./chunk-KPEJMNRD.js";
33
33
  import {
34
34
  ALL_LANGUAGES,
35
35
  ALL_SKILL_SLUGS,
@@ -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-YJJTISBN.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
  featureEffectSchema
3
- } from "./chunk-WPWOHGPI.js";
3
+ } from "./chunk-X3D6LXUU.js";
4
4
 
5
5
  // src/schemas/choice-schema.ts
6
6
  import { z } from "zod";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFeat
3
- } from "./chunk-D5U7GMPX.js";
3
+ } from "./chunk-RWZTGMSN.js";
4
4
 
5
5
  // src/feat/feat.codec.ts
6
6
  import yaml from "js-yaml";
@@ -5,13 +5,13 @@ 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
- } from "./chunk-T26ZVCL5.js";
11
+ } from "./chunk-IWRVOCT5.js";
12
12
  import {
13
13
  featureEffectSchema
14
- } from "./chunk-WPWOHGPI.js";
14
+ } from "./chunk-X3D6LXUU.js";
15
15
 
16
16
  // src/schemas/feature-schema.ts
17
17
  import { z as z2 } from "zod";
@@ -22,6 +22,7 @@ function emptyFeatureEffectTotals() {
22
22
  hp_per_level_terms: [],
23
23
  speed_walk_bonus: 0,
24
24
  speed_walk_set: 0,
25
+ attunement_set: 0,
25
26
  ability_bonus: {},
26
27
  senses: { darkvision: 0, blindsight: 0, tremorsense: 0, truesight: 0 },
27
28
  ac_terms: [],
@@ -195,6 +196,10 @@ function applyEffect(out, eff, label, level) {
195
196
  }
196
197
  break;
197
198
  }
199
+ case "attunement-limit": {
200
+ out.attunement_set = Math.max(out.attunement_set, scaled(eff.value, "value", eff.scales_at, level));
201
+ break;
202
+ }
198
203
  case "sense":
199
204
  out.senses[eff.type] = Math.max(out.senses[eff.type], eff.range);
200
205
  break;
@@ -8,10 +8,10 @@ import {
8
8
  } from "./chunk-CJYCVNW2.js";
9
9
  import {
10
10
  featureSchema
11
- } from "./chunk-4FWNK4FD.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-5XMQPTHD.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-ZONJ66NE.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";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  featEntitySchema
3
- } from "./chunk-RIXI5D3R.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,6 +1,6 @@
1
1
  import {
2
2
  parseBackground
3
- } from "./chunk-LMBAEIP6.js";
3
+ } from "./chunk-3PZHBLFG.js";
4
4
 
5
5
  // src/background/background.codec.ts
6
6
  import yaml from "js-yaml";
@@ -605,7 +605,7 @@ function computeCarriedWeight(resolved, registry) {
605
605
  }
606
606
  return total;
607
607
  }
608
- function computeSlotsAndAttacks(resolved, mods, profs, registry, warnings, proficiencyBonus, weaponAbilities = [], weaponMasteries = []) {
608
+ function computeSlotsAndAttacks(resolved, mods, profs, registry, warnings, proficiencyBonus, weaponAbilities = [], weaponMasteries = [], attunementFloor = 0) {
609
609
  var _a, _b, _c;
610
610
  const equippedSlots = assignSlots(resolved, registry, warnings);
611
611
  const overrides = (_a = resolved.definition.overrides) != null ? _a : {};
@@ -621,7 +621,9 @@ function computeSlotsAndAttacks(resolved, mods, profs, registry, warnings, profi
621
621
  carriedWeight: computeCarriedWeight(resolved, registry),
622
622
  // Attunement is persistent: an attuned item still occupies a slot even when unequipped (SRD).
623
623
  attunementUsed: ((_b = resolved.definition.equipment) != null ? _b : []).filter((e) => e.attuned).length,
624
- attunementLimit: (_c = overrides.attunement_limit) != null ? _c : 3
624
+ // PRECEDENCE: an explicit character override wins outright (a number the player typed is intent);
625
+ // otherwise the baseline 3 is raised — never lowered — by whatever features granted.
626
+ attunementLimit: (_c = overrides.attunement_limit) != null ? _c : Math.max(3, attunementFloor)
625
627
  };
626
628
  }
627
629
 
@@ -25,6 +25,19 @@ var featureEffectSchema = z.discriminatedUnion("kind", [
25
25
  condition: conditionField,
26
26
  subject: subjectField
27
27
  }),
28
+ z.object({
29
+ // The attunement CAP, as data. `value` is an absolute cap ("you can now attune to up to four"),
30
+ // never an increment, so two grants MAX rather than sum and a grant can never lower the cap:
31
+ // recalc folds it as Math.max(3, granted). `scales_at` carries a whole progression on ONE effect —
32
+ // the `speed-bonus` / `extra-attack` shape (R4-G7 §7.3) — because the overlay has no level axis and
33
+ // the Artificer's three steps (10 -> 4, 14 -> 5, 18 -> 6) would otherwise need three separate
34
+ // features, one of which is embedded prose inside another feature's description.
35
+ kind: z.literal("attunement-limit"),
36
+ value: z.number().int().positive(),
37
+ scales_at: z.array(z.object({ level: z.number().int().min(1).max(20), value: z.number().int().positive() })).optional(),
38
+ condition: conditionField,
39
+ subject: subjectField
40
+ }),
28
41
  z.object({
29
42
  kind: z.literal("sense"),
30
43
  type: z.enum(["darkvision", "blindsight", "tremorsense", "truesight"]),
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  classCodec
3
- } from "../chunk-US5IKYCW.js";
4
- import "../chunk-5U5FFFA3.js";
3
+ } from "../chunk-BS355LND.js";
4
+ import "../chunk-H4VEMVOO.js";
5
5
  import "../chunk-4U55VNC7.js";
6
- import "../chunk-YJJTISBN.js";
6
+ import "../chunk-DTCR4DBF.js";
7
7
  import "../chunk-23LZLBJK.js";
8
8
  import "../chunk-CJYCVNW2.js";
9
- import "../chunk-4FWNK4FD.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
- import "../chunk-T26ZVCL5.js";
14
- import "../chunk-WPWOHGPI.js";
13
+ import "../chunk-IWRVOCT5.js";
14
+ import "../chunk-X3D6LXUU.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-5U5FFFA3.js";
3
+ } from "../chunk-H4VEMVOO.js";
4
4
  import "../chunk-4U55VNC7.js";
5
- import "../chunk-YJJTISBN.js";
5
+ import "../chunk-DTCR4DBF.js";
6
6
  import "../chunk-23LZLBJK.js";
7
7
  import "../chunk-CJYCVNW2.js";
8
- import "../chunk-4FWNK4FD.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
- import "../chunk-T26ZVCL5.js";
13
- import "../chunk-WPWOHGPI.js";
12
+ import "../chunk-IWRVOCT5.js";
13
+ import "../chunk-X3D6LXUU.js";
14
14
  import "../chunk-UA5KS2PL.js";
15
15
  export {
16
16
  parseClass
@@ -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,14 +1,14 @@
1
1
  import {
2
2
  classEntitySchema
3
- } from "../chunk-YJJTISBN.js";
3
+ } from "../chunk-DTCR4DBF.js";
4
4
  import "../chunk-23LZLBJK.js";
5
5
  import "../chunk-CJYCVNW2.js";
6
- import "../chunk-4FWNK4FD.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
- import "../chunk-T26ZVCL5.js";
11
- import "../chunk-WPWOHGPI.js";
10
+ import "../chunk-IWRVOCT5.js";
11
+ import "../chunk-X3D6LXUU.js";
12
12
  import "../chunk-UA5KS2PL.js";
13
13
  export {
14
14
  classEntitySchema
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  featCodec
3
- } from "../chunk-ESS46GR4.js";
4
- import "../chunk-D5U7GMPX.js";
5
- import "../chunk-RIXI5D3R.js";
6
- import "../chunk-YLL2QXY2.js";
7
- import "../chunk-T26ZVCL5.js";
8
- import "../chunk-WPWOHGPI.js";
3
+ } from "../chunk-JKLRBILX.js";
4
+ import "../chunk-RWZTGMSN.js";
5
+ import "../chunk-BJFL4KYG.js";
6
+ import "../chunk-UA3I2YSO.js";
7
+ import "../chunk-IWRVOCT5.js";
8
+ import "../chunk-X3D6LXUU.js";
9
9
  import "../chunk-UA5KS2PL.js";
10
10
  export {
11
11
  featCodec