@archivist-gg/dnd5e 0.3.8 → 0.3.10

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 (65) 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-UYBCC473.js → chunk-22MOUU2G.js} +1 -1
  5. package/dist/{chunk-LEHDXPY6.js → chunk-2M34FLAY.js} +1 -1
  6. package/dist/{chunk-YRTI2FLD.js → chunk-3UNP3G4D.js} +1 -1
  7. package/dist/{chunk-2Q374PZ3.js → chunk-4DOMDEBZ.js} +1 -1
  8. package/dist/{chunk-HRG4URUK.js → chunk-7YU4NPKR.js} +1 -1
  9. package/dist/{chunk-DIAYWWHG.js → chunk-BIA32MQ5.js} +1 -1
  10. package/dist/{chunk-5VTDYZIW.js → chunk-BL3VELKP.js} +1 -1
  11. package/dist/{chunk-GD2CFJ3T.js → chunk-BLRW2UU6.js} +1 -1
  12. package/dist/{chunk-IXMS2HHP.js → chunk-CBEKVAX4.js} +14 -6
  13. package/dist/{chunk-IVLA24R3.js → chunk-G247QXWW.js} +1 -1
  14. package/dist/{chunk-TEKRIRNZ.js → chunk-HLQAFZKQ.js} +1 -1
  15. package/dist/{chunk-5SMMRAO2.js → chunk-J77CKYV3.js} +1 -1
  16. package/dist/{chunk-5QM7YNGV.js → chunk-JANNRGJC.js} +1 -1
  17. package/dist/{chunk-O5WXS3G5.js → chunk-JCEV7KYN.js} +1 -1
  18. package/dist/{chunk-VMIFD2RB.js → chunk-MAH6DCRI.js} +1 -1
  19. package/dist/{chunk-FYM3ROGA.js → chunk-MQ747UCR.js} +1 -1
  20. package/dist/{chunk-EISRDJUV.js → chunk-NWDF5HG3.js} +1 -1
  21. package/dist/{chunk-47SCRXGM.js → chunk-OYUEUKKF.js} +2 -2
  22. package/dist/{chunk-N4JJNGLZ.js → chunk-PI4LLJG4.js} +2 -2
  23. package/dist/{chunk-RW42SHFM.js → chunk-PJMG5XV3.js} +1 -1
  24. package/dist/{chunk-WNQ2O2IS.js → chunk-QHMWA3SZ.js} +1 -1
  25. package/dist/{chunk-U3DNWCEI.js → chunk-R747YKS7.js} +1 -1
  26. package/dist/{chunk-Q6L2RASW.js → chunk-S56D6B6C.js} +2 -2
  27. package/dist/{chunk-QCKOVD3K.js → chunk-V6LJFP4R.js} +2 -2
  28. package/dist/chunk-W6GC6OKK.js +16 -0
  29. package/dist/{chunk-VTKJOAJD.js → chunk-XTRDCSJB.js} +6 -0
  30. package/dist/class/class.codec.js +6 -6
  31. package/dist/class/class.parser.js +5 -5
  32. package/dist/class/class.schema.js +4 -4
  33. package/dist/feat/feat.codec.js +5 -5
  34. package/dist/feat/feat.parser.js +4 -4
  35. package/dist/feat/feat.schema.d.ts +9 -0
  36. package/dist/feat/feat.schema.js +3 -3
  37. package/dist/index.js +23 -23
  38. package/dist/monster/monster.parser.js +5 -5
  39. package/dist/monster/monster.schema.js +4 -4
  40. package/dist/optional-feature/optional-feature.codec.js +4 -4
  41. package/dist/optional-feature/optional-feature.parser.js +3 -3
  42. package/dist/optional-feature/optional-feature.schema.d.ts +9 -0
  43. package/dist/optional-feature/optional-feature.schema.js +2 -2
  44. package/dist/pc/pc.decision-engine.js +2 -2
  45. package/dist/pc/pc.feature-effects.d.ts +12 -0
  46. package/dist/pc/pc.feature-effects.js +1 -1
  47. package/dist/pc/pc.proficiencies.js +2 -2
  48. package/dist/pc/pc.recalc.js +29 -31
  49. package/dist/pc/pc.resolver.js +2 -2
  50. package/dist/race/race.codec.js +6 -6
  51. package/dist/race/race.parser.js +5 -5
  52. package/dist/race/race.schema.js +4 -4
  53. package/dist/schemas/choice-schema.js +2 -2
  54. package/dist/schemas/feature-effect-schema.d.ts +9 -0
  55. package/dist/schemas/feature-effect-schema.js +1 -1
  56. package/dist/schemas/feature-schema.js +3 -3
  57. package/dist/spell/casting-time.d.ts +22 -0
  58. package/dist/spell/casting-time.js +6 -0
  59. package/dist/spell/spell.filter.d.ts +2 -2
  60. package/dist/spell/spell.filter.js +7 -19
  61. package/dist/subclass/subclass.codec.js +6 -6
  62. package/dist/subclass/subclass.parser.js +5 -5
  63. package/dist/subclass/subclass.schema.js +4 -4
  64. package/dist/types/feature-effect.d.ts +4 -0
  65. package/package.json +1 -1
@@ -11,12 +11,12 @@ import {
11
11
  monsterSpeedSchema,
12
12
  monsterSpellcastingSchema,
13
13
  monsterTypeSchema
14
- } from "../chunk-IVLA24R3.js";
15
- import "../chunk-N4JJNGLZ.js";
14
+ } from "../chunk-G247QXWW.js";
15
+ import "../chunk-PI4LLJG4.js";
16
16
  import "../chunk-WYGD2UGN.js";
17
17
  import "../chunk-ZJXSXVFP.js";
18
- import "../chunk-LEHDXPY6.js";
19
- import "../chunk-VTKJOAJD.js";
18
+ import "../chunk-2M34FLAY.js";
19
+ import "../chunk-XTRDCSJB.js";
20
20
  import "../chunk-UA5KS2PL.js";
21
21
  export {
22
22
  MONSTER_KNOWN_KEYS,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  optionalFeatureCodec
3
- } from "../chunk-5QM7YNGV.js";
4
- import "../chunk-RW42SHFM.js";
5
- import "../chunk-FYM3ROGA.js";
3
+ } from "../chunk-JANNRGJC.js";
4
+ import "../chunk-PJMG5XV3.js";
5
+ import "../chunk-MQ747UCR.js";
6
6
  import "../chunk-WYGD2UGN.js";
7
7
  import "../chunk-ZJXSXVFP.js";
8
- import "../chunk-VTKJOAJD.js";
8
+ import "../chunk-XTRDCSJB.js";
9
9
  import "../chunk-UA5KS2PL.js";
10
10
  export {
11
11
  optionalFeatureCodec
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  parseOptionalFeature
3
- } from "../chunk-RW42SHFM.js";
4
- import "../chunk-FYM3ROGA.js";
3
+ } from "../chunk-PJMG5XV3.js";
4
+ import "../chunk-MQ747UCR.js";
5
5
  import "../chunk-WYGD2UGN.js";
6
6
  import "../chunk-ZJXSXVFP.js";
7
- import "../chunk-VTKJOAJD.js";
7
+ import "../chunk-XTRDCSJB.js";
8
8
  import "../chunk-UA5KS2PL.js";
9
9
  export {
10
10
  parseOptionalFeature
@@ -79,6 +79,15 @@ export declare const optionalFeatureEntitySchema: z.ZodObject<{
79
79
  minimum: z.ZodOptional<z.ZodNumber>;
80
80
  condition: z.ZodOptional<z.ZodString>;
81
81
  subject: z.ZodOptional<z.ZodString>;
82
+ }, z.core.$strip>, z.ZodObject<{
83
+ kind: z.ZodLiteral<"half-proficiency">;
84
+ skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
85
+ round: z.ZodOptional<z.ZodEnum<{
86
+ down: "down";
87
+ up: "up";
88
+ }>>;
89
+ condition: z.ZodOptional<z.ZodString>;
90
+ subject: z.ZodOptional<z.ZodString>;
82
91
  }, z.core.$strip>, z.ZodObject<{
83
92
  kind: z.ZodLiteral<"immune-condition">;
84
93
  condition: z.ZodString;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  optionalFeatureEntitySchema
3
- } from "../chunk-FYM3ROGA.js";
3
+ } from "../chunk-MQ747UCR.js";
4
4
  import "../chunk-WYGD2UGN.js";
5
5
  import "../chunk-ZJXSXVFP.js";
6
- import "../chunk-VTKJOAJD.js";
6
+ import "../chunk-XTRDCSJB.js";
7
7
  import "../chunk-UA5KS2PL.js";
8
8
  export {
9
9
  optionalFeatureEntitySchema
@@ -4,7 +4,7 @@ import {
4
4
  collectChosenAbilityPoints,
5
5
  collectChosenProficiencies,
6
6
  computeEffectiveProficiencies
7
- } from "../chunk-TEKRIRNZ.js";
7
+ } from "../chunk-HLQAFZKQ.js";
8
8
  import "../chunk-4ISR76DH.js";
9
9
  import "../chunk-DPUJDOKV.js";
10
10
  import "../chunk-O4SRD3J5.js";
@@ -14,7 +14,7 @@ import "../chunk-YHYTW5QA.js";
14
14
  import "../chunk-MI7P3NX3.js";
15
15
  import "../chunk-PVCEA4HZ.js";
16
16
  import "../chunk-UBBKSYBY.js";
17
- import "../chunk-IXMS2HHP.js";
17
+ import "../chunk-CBEKVAX4.js";
18
18
  import "../chunk-TOM66P2P.js";
19
19
  import "../chunk-KU3YLF6D.js";
20
20
  import "../chunk-RLGQ5PZZ.js";
@@ -34,6 +34,18 @@ export interface FeatureEffectTotals {
34
34
  minimum: number;
35
35
  label: string;
36
36
  }[];
37
+ /**
38
+ * Half-proficiency terms (Jack of All Trades, Remarkable Athlete). Resolved in recalc against the
39
+ * proficiency bonus, and ONLY for a skill whose effective tri is "none" — that gate is the mechanic
40
+ * ("a skill proficiency you lack"), so it lives at the consumer, not here. Empty `skills` = every skill.
41
+ * recalc takes the MAX across terms, never the sum: "add half your Proficiency Bonus" is one grant, so two
42
+ * features (or a duplicated homebrew note) must not double it.
43
+ */
44
+ half_proficiency_terms: {
45
+ skills: string[];
46
+ round: "down" | "up";
47
+ label: string;
48
+ }[];
37
49
  hp_per_level_bonus: number;
38
50
  /** One term per hp-per-level-bonus effect, labeled with the owning feature's name. */
39
51
  hp_per_level_terms: {
@@ -7,7 +7,7 @@ import {
7
7
  foldsNow,
8
8
  foldsOnSelf,
9
9
  selfEffectsOf
10
- } from "../chunk-IXMS2HHP.js";
10
+ } from "../chunk-CBEKVAX4.js";
11
11
  import "../chunk-TOM66P2P.js";
12
12
  import "../chunk-KU3YLF6D.js";
13
13
  import "../chunk-RLGQ5PZZ.js";
@@ -2,7 +2,7 @@ import {
2
2
  collectChosenProficiencies,
3
3
  collectProficiencyGrants,
4
4
  computeEffectiveProficiencies
5
- } from "../chunk-TEKRIRNZ.js";
5
+ } from "../chunk-HLQAFZKQ.js";
6
6
  import "../chunk-4ISR76DH.js";
7
7
  import "../chunk-DPUJDOKV.js";
8
8
  import "../chunk-O4SRD3J5.js";
@@ -12,7 +12,7 @@ import "../chunk-YHYTW5QA.js";
12
12
  import "../chunk-MI7P3NX3.js";
13
13
  import "../chunk-PVCEA4HZ.js";
14
14
  import "../chunk-UBBKSYBY.js";
15
- import "../chunk-IXMS2HHP.js";
15
+ import "../chunk-CBEKVAX4.js";
16
16
  import "../chunk-TOM66P2P.js";
17
17
  import "../chunk-KU3YLF6D.js";
18
18
  import {
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  collectChosenAbilityPoints,
6
6
  collectChosenProficiencies
7
- } from "../chunk-TEKRIRNZ.js";
7
+ } from "../chunk-HLQAFZKQ.js";
8
8
  import {
9
9
  resourceLevelFor
10
10
  } from "../chunk-4ISR76DH.js";
@@ -39,7 +39,7 @@ import {
39
39
  computeFeatureEffects,
40
40
  foldsNow,
41
41
  selfEffectsOf
42
- } from "../chunk-IXMS2HHP.js";
42
+ } from "../chunk-CBEKVAX4.js";
43
43
  import "../chunk-TOM66P2P.js";
44
44
  import "../chunk-KU3YLF6D.js";
45
45
  import "../chunk-RLGQ5PZZ.js";
@@ -61,8 +61,6 @@ import {
61
61
  attackBonus,
62
62
  isCanonicalDamageType,
63
63
  isChosenDamageType,
64
- passive,
65
- passivePerception,
66
64
  proficiencyFromLevel,
67
65
  saveDC,
68
66
  savingThrow,
@@ -516,7 +514,7 @@ function computeProficiencies(resolved) {
516
514
  };
517
515
  }
518
516
  function recalc(resolved, registry) {
519
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y;
517
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z;
520
518
  const warnings = [];
521
519
  const overrides = (_a = resolved.definition.overrides) != null ? _a : {};
522
520
  const chosenProfs = collectChosenProficiencies(resolved);
@@ -623,11 +621,14 @@ function recalc(resolved, registry) {
623
621
  const override = (_k = overrides.skills) == null ? void 0 : _k[skillKey];
624
622
  const tri = (_l = override == null ? void 0 : override.proficiency) != null ? _l : expSet.has(skillKey) ? "expertise" : profSet.has(skillKey) ? "proficient" : "none";
625
623
  const skillTerms = featureEffects.skill_bonus_terms.filter((t) => t.skills.includes(skillKey)).map((t) => ({ source: t.label, amount: Math.max(t.minimum, mods[t.ability]) }));
626
- const bonus = (_m = override == null ? void 0 : override.bonus) != null ? _m : skillBonus(scores[ab], tri, proficiencyBonus) + skillTerms.reduce((sum, t) => sum + t.amount, 0) + conditionEffects.d20_test_penalty;
624
+ const halfProfTerms = tri !== "none" ? [] : featureEffects.half_proficiency_terms.filter((t) => t.skills.length === 0 || t.skills.includes(skillKey)).map((t) => ({ source: t.label, amount: t.round === "up" ? Math.ceil(proficiencyBonus / 2) : Math.floor(proficiencyBonus / 2) }));
625
+ const halfProfBest = halfProfTerms.length ? halfProfTerms.reduce((best, t) => t.amount > best.amount ? t : best) : null;
626
+ const bonus = (_n = override == null ? void 0 : override.bonus) != null ? _n : skillBonus(scores[ab], tri, proficiencyBonus) + skillTerms.reduce((sum, t) => sum + t.amount, 0) + ((_m = halfProfBest == null ? void 0 : halfProfBest.amount) != null ? _m : 0) + conditionEffects.d20_test_penalty;
627
627
  skillBreakdowns[skillKey] = [
628
628
  { source: ab.toUpperCase(), amount: mods[ab] },
629
629
  ...tri !== "none" ? [{ source: tri === "expertise" ? "Expertise" : "Proficiency", amount: proficiencyBonus * (tri === "expertise" ? 2 : 1) }] : [],
630
630
  ...skillTerms,
631
+ ...halfProfBest ? [halfProfBest] : [],
631
632
  ...conditionEffects.d20_test_penalty ? [{ source: "Conditions", amount: conditionEffects.d20_test_penalty }] : []
632
633
  ];
633
634
  skills[skillKey] = {
@@ -636,16 +637,13 @@ function recalc(resolved, registry) {
636
637
  ability: ab
637
638
  };
638
639
  }
639
- const perceptionTri = skills.perception.proficiency;
640
- const investigationTri = skills.investigation.proficiency;
641
- const insightTri = skills.insight.proficiency;
642
640
  const passives = {
643
- perception: (_o = (_n = overrides.passives) == null ? void 0 : _n.perception) != null ? _o : passivePerception(scores.wis, perceptionTri, proficiencyBonus),
644
- investigation: (_q = (_p = overrides.passives) == null ? void 0 : _p.investigation) != null ? _q : passive(scores.int, investigationTri, proficiencyBonus),
645
- insight: (_s = (_r = overrides.passives) == null ? void 0 : _r.insight) != null ? _s : passive(scores.wis, insightTri, proficiencyBonus)
641
+ perception: (_p = (_o = overrides.passives) == null ? void 0 : _o.perception) != null ? _p : 10 + skills.perception.bonus,
642
+ investigation: (_r = (_q = overrides.passives) == null ? void 0 : _q.investigation) != null ? _r : 10 + skills.investigation.bonus,
643
+ insight: (_t = (_s = overrides.passives) == null ? void 0 : _s.insight) != null ? _t : 10 + skills.insight.bonus
646
644
  };
647
- const hpRolled = (_u = (_t = overrides.hp) == null ? void 0 : _t.rolled) != null ? _u : null;
648
- const hpModifier = (_w = (_v = overrides.hp) == null ? void 0 : _v.modifier) != null ? _w : null;
645
+ const hpRolled = (_v = (_u = overrides.hp) == null ? void 0 : _u.rolled) != null ? _v : null;
646
+ const hpModifier = (_x = (_w = overrides.hp) == null ? void 0 : _w.modifier) != null ? _x : null;
649
647
  const averageDiceSum = hitDiceAverageSum(resolved.classes);
650
648
  const conLevels = hpLevelCount(resolved.classes);
651
649
  const diceSum = hpRolled != null ? hpRolled : averageDiceSum;
@@ -653,7 +651,7 @@ function recalc(resolved, registry) {
653
651
  const diceConClamped = Math.max(1, diceConRaw);
654
652
  const hpMaxDerived = diceConClamped + featureEffects.hp_per_level_bonus * totalLevel + (hpModifier != null ? hpModifier : 0);
655
653
  const hpMaxAfterConditions = Math.max(0, Math.floor(hpMaxDerived * conditionEffects.hp_max_multiplier));
656
- const hpMax = (_y = (_x = overrides.hp) == null ? void 0 : _x.max) != null ? _y : hpMaxAfterConditions;
654
+ const hpMax = (_z = (_y = overrides.hp) == null ? void 0 : _y.max) != null ? _z : hpMaxAfterConditions;
657
655
  const hpBreakdown = {
658
656
  diceSum,
659
657
  diceSource: hpRolled != null ? "rolled" : "average",
@@ -671,7 +669,7 @@ function recalc(resolved, registry) {
671
669
  exhaustionMultiplier: conditionEffects.hp_max_multiplier,
672
670
  exhaustionLevel: conditionEffects.exhaustion_level,
673
671
  derivedMax: hpMaxAfterConditions,
674
- override: (_A = (_z = overrides.hp) == null ? void 0 : _z.max) != null ? _A : null,
672
+ override: (_B = (_A = overrides.hp) == null ? void 0 : _A.max) != null ? _B : null,
675
673
  final: hpMax
676
674
  };
677
675
  const featureAcTermsFor = (hasArmor) => featureEffects.ac_terms.filter((t) => !t.requires_armor || hasArmor).map((t) => ({ source: t.label, amount: t.value, kind: "feature", condition: t.condition }));
@@ -695,7 +693,7 @@ function recalc(resolved, registry) {
695
693
  let acBreakdownDerived = [];
696
694
  let acInformationalDerived = [];
697
695
  if (registry) {
698
- derivedEquipment = computeSlotsAndAttacks(resolved, mods, profsForApply, registry, warnings, proficiencyBonus, weaponAbilities, (_B = resolved.weaponMasteries) != null ? _B : [], featureEffects.attunement_set);
696
+ derivedEquipment = computeSlotsAndAttacks(resolved, mods, profsForApply, registry, warnings, proficiencyBonus, weaponAbilities, (_C = resolved.weaponMasteries) != null ? _C : [], featureEffects.attunement_set);
699
697
  if (derivedEquipment.equippedSlots.armor) {
700
698
  const featTerms = featureAcTermsFor(true);
701
699
  acDerived = derivedEquipment.ac + sumTerms(featTerms);
@@ -719,13 +717,13 @@ function recalc(resolved, registry) {
719
717
  acDerived = total + sumTerms(featTerms);
720
718
  acBreakdownDerived = [...terms, ...featTerms];
721
719
  }
722
- const ac = (_C = overrides.ac) != null ? _C : acDerived;
720
+ const ac = (_D = overrides.ac) != null ? _D : acDerived;
723
721
  const additiveSpeed = speedFromRace(resolved) + applied.speed_bonuses.walk + featureEffects.speed_walk_bonus;
724
722
  const baseSpeed = Math.max(featureEffects.speed_walk_set, additiveSpeed);
725
723
  const adjustedSpeed = baseSpeed * conditionEffects.speed_multiplier - conditionEffects.speed_reduction_ft;
726
724
  const conditionSpeed = conditionEffects.speed_floor_zero ? 0 : Math.max(0, Math.floor(adjustedSpeed));
727
- const speed = (_D = overrides.speed) != null ? _D : conditionSpeed;
728
- const raceSpeed = (_G = (_F = (_E = resolved.race) == null ? void 0 : _E.speed) == null ? void 0 : _F.walk) != null ? _G : 30;
725
+ const speed = (_E = overrides.speed) != null ? _E : conditionSpeed;
726
+ const raceSpeed = (_H = (_G = (_F = resolved.race) == null ? void 0 : _F.speed) == null ? void 0 : _G.walk) != null ? _H : 30;
729
727
  const speedBreakdown = [
730
728
  { source: "Base speed", amount: raceSpeed },
731
729
  ...speedFromRace(resolved) !== raceSpeed ? [{ source: "Feats", amount: speedFromRace(resolved) - raceSpeed }] : [],
@@ -735,14 +733,14 @@ function recalc(resolved, registry) {
735
733
  ...conditionSpeed !== baseSpeed ? [{ source: "Conditions", amount: conditionSpeed - baseSpeed }] : []
736
734
  ];
737
735
  if (!resolved.race) warnings.push("No race resolved; speed defaulted to 30.");
738
- const init = (_H = overrides.initiative) != null ? _H : initiativeBonus(mods.dex, resolved.feats, resolved.definition.edition) + featureEffects.initiative_bonus;
736
+ const init = (_I = overrides.initiative) != null ? _I : initiativeBonus(mods.dex, resolved.feats, resolved.definition.edition) + featureEffects.initiative_bonus;
739
737
  const initiativeBreakdown = [
740
738
  { source: "DEX", amount: mods.dex },
741
739
  ...initiativeBonus(mods.dex, resolved.feats, resolved.definition.edition) !== mods.dex ? [{ source: "Feats", amount: initiativeBonus(mods.dex, resolved.feats, resolved.definition.edition) - mods.dex }] : [],
742
740
  ...featureEffects.initiative_terms.map((term) => ({ source: term.label, amount: term.value }))
743
741
  ];
744
742
  const senses = {
745
- darkvision: Math.max((_K = (_J = (_I = resolved.race) == null ? void 0 : _I.vision) == null ? void 0 : _J.darkvision) != null ? _K : 0, featureEffects.senses.darkvision, applied.senses.darkvision),
743
+ darkvision: Math.max((_L = (_K = (_J = resolved.race) == null ? void 0 : _J.vision) == null ? void 0 : _K.darkvision) != null ? _L : 0, featureEffects.senses.darkvision, applied.senses.darkvision),
746
744
  blindsight: Math.max(featureEffects.senses.blindsight, applied.senses.blindsight),
747
745
  tremorsense: Math.max(featureEffects.senses.tremorsense, applied.senses.tremorsense),
748
746
  truesight: Math.max(featureEffects.senses.truesight, applied.senses.truesight)
@@ -789,27 +787,27 @@ function recalc(resolved, registry) {
789
787
  const spellLimits = computeSpellLimits(limitInputs);
790
788
  const defenses = {
791
789
  resistances: suppress(resolved, "resistances", composeDefenseEntries(
792
- (_M = (_L = resolved.definition.defenses) == null ? void 0 : _L.resistances) != null ? _M : [],
790
+ (_N = (_M = resolved.definition.defenses) == null ? void 0 : _M.resistances) != null ? _N : [],
793
791
  applied.defenses.resistances,
794
792
  featureEffects.resistances
795
793
  )),
796
794
  immunities: suppress(resolved, "immunities", composeDefenseEntries(
797
- (_O = (_N = resolved.definition.defenses) == null ? void 0 : _N.immunities) != null ? _O : [],
795
+ (_P = (_O = resolved.definition.defenses) == null ? void 0 : _O.immunities) != null ? _P : [],
798
796
  applied.defenses.immunities,
799
797
  featureEffects.immunities
800
798
  )),
801
799
  vulnerabilities: suppress(resolved, "vulnerabilities", composeDefenseEntries(
802
- (_Q = (_P = resolved.definition.defenses) == null ? void 0 : _P.vulnerabilities) != null ? _Q : [],
800
+ (_R = (_Q = resolved.definition.defenses) == null ? void 0 : _Q.vulnerabilities) != null ? _R : [],
803
801
  applied.defenses.vulnerabilities,
804
802
  featureEffects.vulnerabilities
805
803
  )),
806
804
  condition_immunities: suppress(resolved, "condition_immunities", composeDefenseEntries(
807
- (_S = (_R = resolved.definition.defenses) == null ? void 0 : _R.condition_immunities) != null ? _S : [],
805
+ (_T = (_S = resolved.definition.defenses) == null ? void 0 : _S.condition_immunities) != null ? _T : [],
808
806
  applied.defenses.condition_immunities,
809
807
  featureEffects.condition_immunities
810
808
  ))
811
809
  };
812
- const attackRows = [...(_T = derivedEquipment == null ? void 0 : derivedEquipment.attacks) != null ? _T : [], buildUnarmedRow(mods, proficiencyBonus, resolveUnarmedStrike(resolved))];
810
+ const attackRows = [...(_U = derivedEquipment == null ? void 0 : derivedEquipment.attacks) != null ? _U : [], buildUnarmedRow(mods, proficiencyBonus, resolveUnarmedStrike(resolved))];
813
811
  const riderBindings = {
814
812
  level: totalLevel,
815
813
  class_level: totalLevel,
@@ -873,10 +871,10 @@ function recalc(resolved, registry) {
873
871
  ...riders.length ? { damageRiders: riders } : {}
874
872
  };
875
873
  }),
876
- equippedSlots: (_U = derivedEquipment == null ? void 0 : derivedEquipment.equippedSlots) != null ? _U : {},
877
- carriedWeight: (_V = derivedEquipment == null ? void 0 : derivedEquipment.carriedWeight) != null ? _V : 0,
878
- attunementUsed: (_W = derivedEquipment == null ? void 0 : derivedEquipment.attunementUsed) != null ? _W : 0,
879
- attunementLimit: (_Y = derivedEquipment == null ? void 0 : derivedEquipment.attunementLimit) != null ? _Y : (_X = overrides.attunement_limit) != null ? _X : Math.max(3, featureEffects.attunement_set),
874
+ equippedSlots: (_V = derivedEquipment == null ? void 0 : derivedEquipment.equippedSlots) != null ? _V : {},
875
+ carriedWeight: (_W = derivedEquipment == null ? void 0 : derivedEquipment.carriedWeight) != null ? _W : 0,
876
+ attunementUsed: (_X = derivedEquipment == null ? void 0 : derivedEquipment.attunementUsed) != null ? _X : 0,
877
+ attunementLimit: (_Z = derivedEquipment == null ? void 0 : derivedEquipment.attunementLimit) != null ? _Z : (_Y = overrides.attunement_limit) != null ? _Y : Math.max(3, featureEffects.attunement_set),
880
878
  conditionEffects,
881
879
  rollModifiers: featureEffects.rollModifiers,
882
880
  saveOutcomes: featureEffects.saveOutcomes
@@ -10,7 +10,7 @@ import {
10
10
  dedupeResolvedSpells,
11
11
  resolveOriginFeat,
12
12
  stripSlug
13
- } from "../chunk-TEKRIRNZ.js";
13
+ } from "../chunk-HLQAFZKQ.js";
14
14
  import "../chunk-4ISR76DH.js";
15
15
  import "../chunk-DPUJDOKV.js";
16
16
  import "../chunk-O4SRD3J5.js";
@@ -20,7 +20,7 @@ import "../chunk-YHYTW5QA.js";
20
20
  import "../chunk-MI7P3NX3.js";
21
21
  import "../chunk-PVCEA4HZ.js";
22
22
  import "../chunk-UBBKSYBY.js";
23
- import "../chunk-IXMS2HHP.js";
23
+ import "../chunk-CBEKVAX4.js";
24
24
  import "../chunk-TOM66P2P.js";
25
25
  import "../chunk-KU3YLF6D.js";
26
26
  import "../chunk-RLGQ5PZZ.js";
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  raceCodec
3
- } from "../chunk-HRG4URUK.js";
4
- import "../chunk-5VTDYZIW.js";
5
- import "../chunk-QCKOVD3K.js";
3
+ } from "../chunk-7YU4NPKR.js";
4
+ import "../chunk-BL3VELKP.js";
5
+ import "../chunk-V6LJFP4R.js";
6
6
  import "../chunk-4U55VNC7.js";
7
- import "../chunk-N4JJNGLZ.js";
7
+ import "../chunk-PI4LLJG4.js";
8
8
  import "../chunk-WYGD2UGN.js";
9
9
  import "../chunk-ZJXSXVFP.js";
10
- import "../chunk-LEHDXPY6.js";
11
- import "../chunk-VTKJOAJD.js";
10
+ import "../chunk-2M34FLAY.js";
11
+ import "../chunk-XTRDCSJB.js";
12
12
  import "../chunk-UA5KS2PL.js";
13
13
  export {
14
14
  raceCodec
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  parseRace
3
- } from "../chunk-5VTDYZIW.js";
4
- import "../chunk-QCKOVD3K.js";
3
+ } from "../chunk-BL3VELKP.js";
4
+ import "../chunk-V6LJFP4R.js";
5
5
  import "../chunk-4U55VNC7.js";
6
- import "../chunk-N4JJNGLZ.js";
6
+ import "../chunk-PI4LLJG4.js";
7
7
  import "../chunk-WYGD2UGN.js";
8
8
  import "../chunk-ZJXSXVFP.js";
9
- import "../chunk-LEHDXPY6.js";
10
- import "../chunk-VTKJOAJD.js";
9
+ import "../chunk-2M34FLAY.js";
10
+ import "../chunk-XTRDCSJB.js";
11
11
  import "../chunk-UA5KS2PL.js";
12
12
  export {
13
13
  parseRace
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  fixedAsiSchema,
3
3
  raceEntitySchema
4
- } from "../chunk-QCKOVD3K.js";
5
- import "../chunk-N4JJNGLZ.js";
4
+ } from "../chunk-V6LJFP4R.js";
5
+ import "../chunk-PI4LLJG4.js";
6
6
  import "../chunk-WYGD2UGN.js";
7
7
  import "../chunk-ZJXSXVFP.js";
8
- import "../chunk-LEHDXPY6.js";
9
- import "../chunk-VTKJOAJD.js";
8
+ import "../chunk-2M34FLAY.js";
9
+ import "../chunk-XTRDCSJB.js";
10
10
  import "../chunk-UA5KS2PL.js";
11
11
  export {
12
12
  fixedAsiSchema,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  choiceSchema
3
- } from "../chunk-LEHDXPY6.js";
4
- import "../chunk-VTKJOAJD.js";
3
+ } from "../chunk-2M34FLAY.js";
4
+ import "../chunk-XTRDCSJB.js";
5
5
  export {
6
6
  choiceSchema
7
7
  };
@@ -18,6 +18,15 @@ export declare const featureEffectSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
18
18
  minimum: z.ZodOptional<z.ZodNumber>;
19
19
  condition: z.ZodOptional<z.ZodString>;
20
20
  subject: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strip>, z.ZodObject<{
22
+ kind: z.ZodLiteral<"half-proficiency">;
23
+ skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
+ round: z.ZodOptional<z.ZodEnum<{
25
+ down: "down";
26
+ up: "up";
27
+ }>>;
28
+ condition: z.ZodOptional<z.ZodString>;
29
+ subject: z.ZodOptional<z.ZodString>;
21
30
  }, z.core.$strip>, z.ZodObject<{
22
31
  kind: z.ZodLiteral<"immune-condition">;
23
32
  condition: z.ZodString;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  featureEffectSchema
3
- } from "../chunk-VTKJOAJD.js";
3
+ } from "../chunk-XTRDCSJB.js";
4
4
  export {
5
5
  featureEffectSchema
6
6
  };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  featureObjectSchema,
3
3
  featureSchema
4
- } from "../chunk-N4JJNGLZ.js";
4
+ } from "../chunk-PI4LLJG4.js";
5
5
  import "../chunk-WYGD2UGN.js";
6
6
  import "../chunk-ZJXSXVFP.js";
7
- import "../chunk-LEHDXPY6.js";
8
- import "../chunk-VTKJOAJD.js";
7
+ import "../chunk-2M34FLAY.js";
8
+ import "../chunk-XTRDCSJB.js";
9
9
  export {
10
10
  featureObjectSchema,
11
11
  featureSchema
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The ONE reader of a spell's `casting_time` string. Every consumer (the plugin's compact Cast-table label,
3
+ * the add-drawer's filter bucket and its sort rank) goes through here, so a spelling that one of them
4
+ * understands the others understand too. Before this, each kept its own exact-string switch over the bundle's
5
+ * spellings (`action`, `bonus-action`, `1minute`), and they drifted: a converted or AI-generated spell's
6
+ * `1 action` / `10 minute` printed raw in the Cast table, and every vault spell spelled `bonus action`,
7
+ * `1 minute` or `1 hour` filtered as "special".
8
+ *
9
+ * The LEADING token decides. Prose after it (a reaction's trigger, Plant Growth's "or 8 hours") is left for
10
+ * the caller's tooltip and never matched, and the trailing word boundary stops a word that merely starts with
11
+ * a unit (`reactionary`, `bonuses`, `minuteman`) from reading as one. A count is optional and only meaningful
12
+ * for minutes and hours; `bonus` may drop the word `action`, which hand-authored spells do.
13
+ */
14
+ export type CastingTimeKind = "action" | "bonus" | "reaction" | "time";
15
+ export interface ParsedCastingTime {
16
+ kind: CastingTimeKind;
17
+ /** 1 for the action economy; the stated number of minutes / hours for `time` (1 when omitted). */
18
+ count: number;
19
+ /** Present only for `time`. */
20
+ unit?: "minute" | "hour";
21
+ }
22
+ export declare function parseCastingTime(token: string | undefined): ParsedCastingTime | null;
@@ -0,0 +1,6 @@
1
+ import {
2
+ parseCastingTime
3
+ } from "../chunk-W6GC6OKK.js";
4
+ export {
5
+ parseCastingTime
6
+ };
@@ -1,8 +1,8 @@
1
1
  import type { SpellCandidate } from "./spell.access.js";
2
2
  export type CastTimeCat = "action" | "bonus" | "reaction" | "long" | "special";
3
3
  export type RangeCat = "self" | "touch" | "ranged" | "special";
4
- /** Cast-time bucket from the stored token (mirrors compactCastingTime's set).
5
- * Rounds/minutes/hours fold into "long"; null/unknown → "special". */
4
+ /** Cast-time bucket, read through `parseCastingTime` (the same reader the Cast table's label uses, so the two
5
+ * cannot drift). Minutes/hours fold into "long"; unreadable or absent → "special". */
6
6
  export declare function castTimeCategory(token: string | undefined): CastTimeCat;
7
7
  /** Range bucket from the human range string. Honest read, documented buckets:
8
8
  * "Self…"→self, "Touch…"→touch, "<n> feet|ft"→ranged, else (mile/Sight/…)→special. */
@@ -1,24 +1,12 @@
1
+ import {
2
+ parseCastingTime
3
+ } from "../chunk-W6GC6OKK.js";
4
+
1
5
  // src/spell/spell.filter.ts
2
6
  function castTimeCategory(token) {
3
- switch (token) {
4
- case "action":
5
- return "action";
6
- case "bonus-action":
7
- return "bonus";
8
- case "reaction":
9
- return "reaction";
10
- case "minute":
11
- case "1minute":
12
- case "10minutes":
13
- case "hour":
14
- case "1hour":
15
- case "8hours":
16
- case "12hours":
17
- case "24hours":
18
- return "long";
19
- default:
20
- return "special";
21
- }
7
+ const parsed = parseCastingTime(token);
8
+ if (!parsed) return "special";
9
+ return parsed.kind === "time" ? "long" : parsed.kind;
22
10
  }
23
11
  function rangeCategory(range) {
24
12
  if (!range) return "special";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  subclassCodec
3
- } from "../chunk-U3DNWCEI.js";
4
- import "../chunk-5SMMRAO2.js";
5
- import "../chunk-GD2CFJ3T.js";
3
+ } from "../chunk-R747YKS7.js";
4
+ import "../chunk-J77CKYV3.js";
5
+ import "../chunk-BLRW2UU6.js";
6
6
  import "../chunk-4U55VNC7.js";
7
7
  import "../chunk-23LZLBJK.js";
8
8
  import "../chunk-CJYCVNW2.js";
9
- import "../chunk-N4JJNGLZ.js";
9
+ import "../chunk-PI4LLJG4.js";
10
10
  import "../chunk-WYGD2UGN.js";
11
11
  import "../chunk-ZJXSXVFP.js";
12
- import "../chunk-LEHDXPY6.js";
13
- import "../chunk-VTKJOAJD.js";
12
+ import "../chunk-2M34FLAY.js";
13
+ import "../chunk-XTRDCSJB.js";
14
14
  import "../chunk-UA5KS2PL.js";
15
15
  export {
16
16
  subclassCodec
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  parseSubclass
3
- } from "../chunk-5SMMRAO2.js";
4
- import "../chunk-GD2CFJ3T.js";
3
+ } from "../chunk-J77CKYV3.js";
4
+ import "../chunk-BLRW2UU6.js";
5
5
  import "../chunk-4U55VNC7.js";
6
6
  import "../chunk-23LZLBJK.js";
7
7
  import "../chunk-CJYCVNW2.js";
8
- import "../chunk-N4JJNGLZ.js";
8
+ import "../chunk-PI4LLJG4.js";
9
9
  import "../chunk-WYGD2UGN.js";
10
10
  import "../chunk-ZJXSXVFP.js";
11
- import "../chunk-LEHDXPY6.js";
12
- import "../chunk-VTKJOAJD.js";
11
+ import "../chunk-2M34FLAY.js";
12
+ import "../chunk-XTRDCSJB.js";
13
13
  import "../chunk-UA5KS2PL.js";
14
14
  export {
15
15
  parseSubclass
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  subclassEntitySchema
3
- } from "../chunk-GD2CFJ3T.js";
3
+ } from "../chunk-BLRW2UU6.js";
4
4
  import "../chunk-23LZLBJK.js";
5
5
  import "../chunk-CJYCVNW2.js";
6
- import "../chunk-N4JJNGLZ.js";
6
+ import "../chunk-PI4LLJG4.js";
7
7
  import "../chunk-WYGD2UGN.js";
8
8
  import "../chunk-ZJXSXVFP.js";
9
- import "../chunk-LEHDXPY6.js";
10
- import "../chunk-VTKJOAJD.js";
9
+ import "../chunk-2M34FLAY.js";
10
+ import "../chunk-XTRDCSJB.js";
11
11
  import "../chunk-UA5KS2PL.js";
12
12
  export {
13
13
  subclassEntitySchema
@@ -22,6 +22,10 @@ export type FeatureEffect = ({
22
22
  skills: string[];
23
23
  ability: Ability;
24
24
  minimum?: number;
25
+ } & Qualified) | ({
26
+ kind: "half-proficiency";
27
+ skills?: string[];
28
+ round?: "down" | "up";
25
29
  } & Qualified) | ({
26
30
  kind: "immune-condition";
27
31
  condition: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archivist-gg/dnd5e",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "license": "MIT AND CC-BY-4.0",
5
5
  "type": "module",
6
6
  "publishConfig": {