@archivist-gg/dnd5e 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/background/background.codec.js +3 -3
  2. package/dist/background/background.parser.js +2 -2
  3. package/dist/background/background.schema.d.ts +8 -0
  4. package/dist/background/background.schema.js +5 -3
  5. package/dist/chunk-353WXS6S.js +25 -0
  6. package/dist/{chunk-DC74TZNA.js → chunk-4LZJXJJS.js} +7 -2
  7. package/dist/{chunk-DZXSWSBP.js → chunk-5ZBE4CWV.js} +1 -1
  8. package/dist/{chunk-IXZXFFFW.js → chunk-6IRYTM5V.js} +6 -1
  9. package/dist/chunk-7TEK5KK7.js +49 -0
  10. package/dist/{chunk-V6SOFUJW.js → chunk-AIRFC5NQ.js} +3 -1
  11. package/dist/{chunk-VTYUBINW.js → chunk-BIK4DFWK.js} +1 -1
  12. package/dist/{chunk-TQZB4OCJ.js → chunk-D3CLOBE3.js} +49 -21
  13. package/dist/{chunk-3TRGRSCH.js → chunk-LV4SNWZQ.js} +7 -1
  14. package/dist/{chunk-UC4FE5X3.js → chunk-N6N74K6X.js} +1 -1
  15. package/dist/chunk-O5EYMKFH.js +6 -0
  16. package/dist/{chunk-TUIFSVLI.js → chunk-SE7AZDR3.js} +1 -0
  17. package/dist/{chunk-5WQD3WNZ.js → chunk-TN6ZLTNU.js} +6 -5
  18. package/dist/{chunk-R5SQHP2X.js → chunk-TNUFPIXI.js} +48 -30
  19. package/dist/{chunk-J2RYSZMQ.js → chunk-VTTA2ALZ.js} +2 -2
  20. package/dist/class/class.slug.d.ts +7 -5
  21. package/dist/class/class.slug.js +1 -1
  22. package/dist/entities/base-item-resolver.d.ts +9 -0
  23. package/dist/entities/base-item-resolver.js +6 -4
  24. package/dist/entities/slug.d.ts +7 -0
  25. package/dist/entities/slug.js +3 -1
  26. package/dist/index.js +3902 -3623
  27. package/dist/item/item.actions-map.d.ts +8 -7
  28. package/dist/item/item.actions-map.js +3 -4
  29. package/dist/item/item.ai-schema.d.ts +1 -1
  30. package/dist/pc/pc.decision-engine.d.ts +13 -3
  31. package/dist/pc/pc.decision-engine.js +9 -9
  32. package/dist/pc/pc.equipment.js +6 -12
  33. package/dist/pc/pc.feature-effects.d.ts +5 -0
  34. package/dist/pc/pc.feature-effects.js +2 -11
  35. package/dist/pc/pc.parser.js +1 -1
  36. package/dist/pc/pc.pools.js +7 -7
  37. package/dist/pc/pc.proficiencies.d.ts +31 -0
  38. package/dist/pc/pc.proficiencies.js +96 -23
  39. package/dist/pc/pc.proficiency-normalize.d.ts +5 -0
  40. package/dist/pc/pc.proficiency-query.js +5 -1
  41. package/dist/pc/pc.recalc.d.ts +7 -0
  42. package/dist/pc/pc.recalc.js +112 -58
  43. package/dist/pc/pc.resolver.js +7 -7
  44. package/dist/pc/pc.rest.d.ts +1 -1
  45. package/dist/pc/pc.rest.js +20 -11
  46. package/dist/pc/pc.schema.d.ts +10 -4
  47. package/dist/pc/pc.schema.js +1 -1
  48. package/dist/pc/pc.slotting.js +3 -3
  49. package/dist/pc/pc.spellcasting.d.ts +5 -1
  50. package/dist/pc/pc.spellcasting.js +4 -2
  51. package/dist/pc/pc.types.d.ts +30 -4
  52. package/dist/spell/spell.access.js +1 -1
  53. package/dist/types/choice.d.ts +5 -0
  54. package/dist/types/choice.js +9 -3
  55. package/dist/weapon/weapon.classify.d.ts +21 -0
  56. package/dist/weapon/weapon.classify.js +7 -0
  57. package/package.json +1 -1
  58. package/dist/chunk-BH6AMZVY.js +0 -25
@@ -21,14 +21,15 @@ export declare const ITEM_ACTIONS: Record<string, ItemAction>;
21
21
  * (`srd-5e_wand-of-fireballs`, `srd-2024_wand-of-fireballs`,
22
22
  * `homebrew_wand-of-fireballs`).
23
23
  *
24
- * Compendium-prefixed slugs follow the shape `<prefix>_<name-slug>` where
25
- * both prefix and name-slug are kebab-case (slugify strips underscores).
26
- * The first underscore is therefore the unambiguous prefix separator: we
27
- * try the bare slug after the first underscore first, then fall back to
28
- * the original slug for legacy bare-slug callers.
24
+ * Type-namespaced slugs follow the shape `<prefix>_<entity_type>_<name-slug>`
25
+ * (all kebab-case; slugify never emits `_`), so a slug splits on `_` into
26
+ * exactly 3 parts and the bare name is `parts.slice(2).join("_")`. This is
27
+ * arity-robust: it also recovers the bare name from a legacy 2-part
28
+ * `<prefix>_<name>` slug and an already-bare name. `ITEM_ACTIONS` is keyed by
29
+ * bare name.
29
30
  *
30
- * Returns `undefined` if no map entry matches either form. Does not crash
31
- * on slugs without an underscore — those are treated as already-bare.
31
+ * Returns `undefined` if no map entry matches. Does not crash on slugs
32
+ * without an underscore — those are treated as already-bare.
32
33
  */
33
34
  export declare function findItemAction(slug: string): ItemAction | undefined;
34
35
  /**
@@ -63,10 +63,9 @@ var ITEM_ACTIONS = {
63
63
  };
64
64
  function findItemAction(slug) {
65
65
  if (slug in ITEM_ACTIONS) return ITEM_ACTIONS[slug];
66
- const underscore = slug.indexOf("_");
67
- if (underscore < 0) return void 0;
68
- const bare = slug.slice(underscore + 1);
69
- return ITEM_ACTIONS[bare];
66
+ const p = slug.split("_");
67
+ const bare = p.length >= 3 ? p.slice(2).join("_") : p[p.length - 1];
68
+ return bare === slug ? void 0 : ITEM_ACTIONS[bare];
70
69
  }
71
70
  function resolveItemAction(slug, entry, itemType) {
72
71
  var _a, _b;
@@ -14,8 +14,8 @@ export declare const itemInputSchema: z.ZodObject<{
14
14
  "wondrous item": "wondrous item";
15
15
  }>;
16
16
  rarity: z.ZodEnum<{
17
- legendary: "legendary";
18
17
  common: "common";
18
+ legendary: "legendary";
19
19
  uncommon: "uncommon";
20
20
  rare: "rare";
21
21
  "very rare": "very rare";
@@ -68,9 +68,6 @@ export interface DecisionContext {
68
68
  }
69
69
  /** "[[SRD 2024/Classes/Fighter]]" → "fighter" (tail segment, slugified). */
70
70
  export declare function wikilinkTailSlug(link: string): string;
71
- /** "srd-2024_fighter" → "fighter". Tolerates a nullish slug (degrades to "") so a
72
- * malformed entity can't hard-crash the builder; the resolver backfills real slugs. */
73
- export declare function bareEntitySlug(slug: string | null | undefined): string;
74
71
  declare function matchesFilter(e: RegisteredEntity, where: EntityFilter, ownerBare: string): boolean;
75
72
  /** Test-only export of {@link matchesFilter} (Task B2). */
76
73
  export declare const __matchesFilterForTest: typeof matchesFilter;
@@ -83,6 +80,19 @@ export declare function collectChosenProficiencies(resolved: ResolvedCharacter):
83
80
  languages: string[];
84
81
  tools: string[];
85
82
  };
83
+ export interface ChoiceStatus {
84
+ id: string;
85
+ count: number;
86
+ selected: number;
87
+ }
88
+ /** Per-choice status for language/tool select-proficiency decisions, using the
89
+ * SAME walk as collectChosenProficiencies so required/selected counts cannot
90
+ * diverge from the picks. `selected` counts persisted picks validated against
91
+ * the choice's `from` pool (stale slugs ignored, mirroring the pick fold). */
92
+ export declare function collectLanguageToolChoiceStatus(resolved: ResolvedCharacter): {
93
+ languages: ChoiceStatus[];
94
+ tools: ChoiceStatus[];
95
+ };
86
96
  export interface OriginAbilityPoints {
87
97
  race: Partial<Record<Ability, number>>;
88
98
  background: Partial<Record<Ability, number>>;
@@ -1,26 +1,26 @@
1
1
  import {
2
2
  __matchesFilterForTest,
3
- bareEntitySlug,
4
3
  buildDecisionLedger,
5
4
  collectChosenAbilityPoints,
6
5
  collectChosenProficiencies,
6
+ collectLanguageToolChoiceStatus,
7
7
  wikilinkTailSlug
8
- } from "../chunk-R5SQHP2X.js";
9
- import "../chunk-BH6AMZVY.js";
10
- import "../chunk-UC4FE5X3.js";
11
- import "../chunk-IXZXFFFW.js";
8
+ } from "../chunk-TNUFPIXI.js";
9
+ import "../chunk-7TEK5KK7.js";
10
+ import "../chunk-6IRYTM5V.js";
12
11
  import "../chunk-5XUXXAQJ.js";
13
12
  import "../chunk-JEYQU3JT.js";
14
- import "../chunk-DC74TZNA.js";
15
- import "../chunk-3TRGRSCH.js";
16
- import "../chunk-J2RYSZMQ.js";
13
+ import "../chunk-N6N74K6X.js";
14
+ import "../chunk-4LZJXJJS.js";
15
+ import "../chunk-LV4SNWZQ.js";
16
+ import "../chunk-VTTA2ALZ.js";
17
17
  import "../chunk-NAEPSZAM.js";
18
18
  import "../chunk-WP2CX2JO.js";
19
19
  export {
20
20
  __matchesFilterForTest,
21
- bareEntitySlug,
22
21
  buildDecisionLedger,
23
22
  collectChosenAbilityPoints,
24
23
  collectChosenProficiencies,
24
+ collectLanguageToolChoiceStatus,
25
25
  wikilinkTailSlug
26
26
  };
@@ -2,20 +2,14 @@ import {
2
2
  computeAppliedBonuses,
3
3
  computeSlotsAndAttacks,
4
4
  emptyAppliedBonuses
5
- } from "../chunk-TQZB4OCJ.js";
6
- import "../chunk-R5SQHP2X.js";
7
- import "../chunk-BH6AMZVY.js";
8
- import "../chunk-UC4FE5X3.js";
9
- import "../chunk-IXZXFFFW.js";
10
- import "../chunk-5XUXXAQJ.js";
11
- import "../chunk-JEYQU3JT.js";
5
+ } from "../chunk-D3CLOBE3.js";
6
+ import "../chunk-353WXS6S.js";
7
+ import "../chunk-O5EYMKFH.js";
8
+ import "../chunk-N6N74K6X.js";
12
9
  import "../chunk-LWFTHJIE.js";
13
10
  import "../chunk-DNAJRMNN.js";
14
- import "../chunk-DC74TZNA.js";
15
- import "../chunk-3TRGRSCH.js";
16
- import "../chunk-J2RYSZMQ.js";
17
- import "../chunk-NAEPSZAM.js";
18
- import "../chunk-WP2CX2JO.js";
11
+ import "../chunk-4LZJXJJS.js";
12
+ import "../chunk-LV4SNWZQ.js";
19
13
  export {
20
14
  computeAppliedBonuses,
21
15
  computeSlotsAndAttacks,
@@ -24,6 +24,11 @@ export interface WeaponAbilityOverride {
24
24
  export interface FeatureEffectTotals {
25
25
  initiative_bonus: number;
26
26
  hp_per_level_bonus: number;
27
+ /** One term per hp-per-level-bonus effect, labeled with the owning feature's name. */
28
+ hp_per_level_terms: {
29
+ label: string;
30
+ value: number;
31
+ }[];
27
32
  speed_walk_bonus: number;
28
33
  /**
29
34
  * Absolute walk-speed FLOOR from `speed-bonus` effects with `set:true` (e.g.
@@ -1,17 +1,8 @@
1
1
  import {
2
2
  computeFeatureEffects,
3
3
  emptyFeatureEffectTotals
4
- } from "../chunk-V6SOFUJW.js";
5
- import "../chunk-R5SQHP2X.js";
6
- import "../chunk-BH6AMZVY.js";
7
- import "../chunk-UC4FE5X3.js";
8
- import "../chunk-IXZXFFFW.js";
9
- import "../chunk-5XUXXAQJ.js";
10
- import "../chunk-JEYQU3JT.js";
11
- import "../chunk-DC74TZNA.js";
12
- import "../chunk-3TRGRSCH.js";
13
- import "../chunk-J2RYSZMQ.js";
14
- import "../chunk-NAEPSZAM.js";
4
+ } from "../chunk-AIRFC5NQ.js";
5
+ import "../chunk-LV4SNWZQ.js";
15
6
  import "../chunk-WP2CX2JO.js";
16
7
  export {
17
8
  computeFeatureEffects,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  characterSchema
3
- } from "../chunk-5WQD3WNZ.js";
3
+ } from "../chunk-TN6ZLTNU.js";
4
4
  import "../chunk-TZK36BU7.js";
5
5
 
6
6
  // src/pc/pc.parser.ts
@@ -2,15 +2,15 @@ import {
2
2
  levelPrereqMax,
3
3
  resolveAllPools,
4
4
  resolvePool
5
- } from "../chunk-R5SQHP2X.js";
6
- import "../chunk-BH6AMZVY.js";
7
- import "../chunk-UC4FE5X3.js";
8
- import "../chunk-IXZXFFFW.js";
5
+ } from "../chunk-TNUFPIXI.js";
6
+ import "../chunk-7TEK5KK7.js";
7
+ import "../chunk-6IRYTM5V.js";
9
8
  import "../chunk-5XUXXAQJ.js";
10
9
  import "../chunk-JEYQU3JT.js";
11
- import "../chunk-DC74TZNA.js";
12
- import "../chunk-3TRGRSCH.js";
13
- import "../chunk-J2RYSZMQ.js";
10
+ import "../chunk-N6N74K6X.js";
11
+ import "../chunk-4LZJXJJS.js";
12
+ import "../chunk-LV4SNWZQ.js";
13
+ import "../chunk-VTTA2ALZ.js";
14
14
  import "../chunk-NAEPSZAM.js";
15
15
  import "../chunk-WP2CX2JO.js";
16
16
  export {
@@ -1,8 +1,39 @@
1
1
  import type { ResolvedCharacter } from "./pc.types.js";
2
+ import { type ChoiceStatus } from "./pc.decision-engine.js";
2
3
  export interface ProficiencyAggregate {
3
4
  armor: string[];
4
5
  weapons: string[];
5
6
  tools: string[];
6
7
  languages: string[];
8
+ /** Unresolved language/tool decisions as "choose N" placeholders (N = count -
9
+ * selected, only when > 0). Additive; consumed by the plugin proficiency panel. */
10
+ choices: {
11
+ languages: string[];
12
+ tools: string[];
13
+ };
7
14
  }
15
+ /** Normalized intermediate of every proficiency/language source the DISPLAY path
16
+ * reads, in one place. Only the class branch is a live proficiency source today
17
+ * (race/bg/feat `.proficiencies` object fields do not exist on those entities);
18
+ * race languages, bg tool/lang, and feat grants read their real fields. Chosen
19
+ * picks + per-choice status come from the single decision walk in
20
+ * pc.decision-engine so display can never diverge from the persisted picks. */
21
+ export interface ProficiencySources {
22
+ classArmor: string[];
23
+ classWeaponFixed: string[];
24
+ classWeaponCategories: string[];
25
+ classToolFixed: string[];
26
+ raceLangFixed: string[];
27
+ bgToolFixed: string[];
28
+ bgLangFixed: string[];
29
+ featArmor: string[];
30
+ featWeapons: string[];
31
+ featTools: string[];
32
+ featLanguages: string[];
33
+ chosenLanguages: string[];
34
+ chosenTools: string[];
35
+ languageChoices: ChoiceStatus[];
36
+ toolChoices: ChoiceStatus[];
37
+ }
38
+ export declare function collectProficiencySources(resolved: ResolvedCharacter): ProficiencySources;
8
39
  export declare function aggregateProficiencies(resolved: ResolvedCharacter): ProficiencyAggregate;
@@ -1,47 +1,120 @@
1
+ import {
2
+ collectChosenProficiencies,
3
+ collectLanguageToolChoiceStatus
4
+ } from "../chunk-TNUFPIXI.js";
5
+ import "../chunk-7TEK5KK7.js";
6
+ import "../chunk-6IRYTM5V.js";
7
+ import "../chunk-5XUXXAQJ.js";
8
+ import "../chunk-JEYQU3JT.js";
9
+ import "../chunk-N6N74K6X.js";
10
+ import "../chunk-4LZJXJJS.js";
11
+ import "../chunk-LV4SNWZQ.js";
12
+ import "../chunk-VTTA2ALZ.js";
13
+ import "../chunk-NAEPSZAM.js";
14
+ import "../chunk-WP2CX2JO.js";
15
+
1
16
  // src/pc/pc.proficiencies.ts
2
- function aggregateProficiencies(resolved) {
3
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
4
- const armor = /* @__PURE__ */ new Set();
5
- const weapons = /* @__PURE__ */ new Set();
6
- const tools = /* @__PURE__ */ new Set();
7
- const languages = /* @__PURE__ */ new Set();
17
+ function collectProficiencySources(resolved) {
18
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
19
+ const classArmor = [];
20
+ const classWeaponFixed = [];
21
+ const classWeaponCategories = [];
22
+ const classToolFixed = [];
8
23
  for (const c of resolved.classes) {
9
24
  const prof = (_a = c.entity) == null ? void 0 : _a.proficiencies;
10
25
  if (!prof) continue;
11
- for (const a of (_b = prof.armor) != null ? _b : []) armor.add(prettyName(a));
12
- if ((_c = prof.weapons) == null ? void 0 : _c.fixed) for (const w of prof.weapons.fixed) weapons.add(prettyName(w));
13
- if ((_d = prof.weapons) == null ? void 0 : _d.categories) for (const w of prof.weapons.categories) weapons.add(prettyName(w));
14
- if ((_e = prof.tools) == null ? void 0 : _e.fixed) for (const t of prof.tools.fixed) tools.add(prettyName(t));
26
+ for (const a of (_b = prof.armor) != null ? _b : []) classArmor.push(a);
27
+ for (const w of (_d = (_c = prof.weapons) == null ? void 0 : _c.fixed) != null ? _d : []) classWeaponFixed.push(w);
28
+ for (const w of (_f = (_e = prof.weapons) == null ? void 0 : _e.categories) != null ? _f : []) classWeaponCategories.push(w);
29
+ for (const t of (_h = (_g = prof.tools) == null ? void 0 : _g.fixed) != null ? _h : []) classToolFixed.push(t);
15
30
  }
16
- const raceLangs = (_f = resolved.race) == null ? void 0 : _f.languages;
17
- if (raceLangs == null ? void 0 : raceLangs.fixed) for (const l of raceLangs.fixed) languages.add(prettyName(l));
31
+ const raceLangFixed = [...(_k = (_j = (_i = resolved.race) == null ? void 0 : _i.languages) == null ? void 0 : _j.fixed) != null ? _k : []];
32
+ const bgToolFixed = [];
33
+ const bgLangFixed = [];
18
34
  const bg = resolved.background;
19
35
  if (bg) {
20
- for (const entry of (_g = bg.tool_proficiencies) != null ? _g : []) {
21
- if (entry.kind === "fixed") for (const t of entry.items) tools.add(prettyName(t));
36
+ for (const entry of (_l = bg.tool_proficiencies) != null ? _l : []) {
37
+ if (entry.kind === "fixed") bgToolFixed.push(...entry.items);
22
38
  }
23
- for (const entry of (_h = bg.language_proficiencies) != null ? _h : []) {
24
- if (entry.kind === "fixed") for (const l of entry.languages) languages.add(prettyName(l));
39
+ for (const entry of (_m = bg.language_proficiencies) != null ? _m : []) {
40
+ if (entry.kind === "fixed") bgLangFixed.push(...entry.languages);
25
41
  }
26
42
  }
43
+ const featArmor = [];
44
+ const featWeapons = [];
45
+ const featTools = [];
46
+ const featLanguages = [];
27
47
  for (const f of resolved.feats) {
28
- const grants = f.proficiencies;
48
+ const featView = f;
49
+ const grants = featView.proficiencies;
29
50
  if (!grants) continue;
30
- for (const a of (_i = grants.armor) != null ? _i : []) armor.add(prettyName(a));
31
- for (const w of (_j = grants.weapons) != null ? _j : []) weapons.add(prettyName(w));
32
- for (const t of (_k = grants.tools) != null ? _k : []) tools.add(prettyName(t));
33
- for (const l of (_l = grants.languages) != null ? _l : []) languages.add(prettyName(l));
51
+ featArmor.push(...(_n = grants.armor) != null ? _n : []);
52
+ featWeapons.push(...(_o = grants.weapons) != null ? _o : []);
53
+ featTools.push(...(_p = grants.tools) != null ? _p : []);
54
+ featLanguages.push(...(_q = grants.languages) != null ? _q : []);
34
55
  }
56
+ const chosen = collectChosenProficiencies(resolved);
57
+ const status = collectLanguageToolChoiceStatus(resolved);
58
+ return {
59
+ classArmor,
60
+ classWeaponFixed,
61
+ classWeaponCategories,
62
+ classToolFixed,
63
+ raceLangFixed,
64
+ bgToolFixed,
65
+ bgLangFixed,
66
+ featArmor,
67
+ featWeapons,
68
+ featTools,
69
+ featLanguages,
70
+ chosenLanguages: chosen.languages,
71
+ chosenTools: chosen.tools,
72
+ languageChoices: status.languages,
73
+ toolChoices: status.tools
74
+ };
75
+ }
76
+ function aggregateProficiencies(resolved) {
77
+ const src = collectProficiencySources(resolved);
78
+ const armor = /* @__PURE__ */ new Set();
79
+ const weapons = /* @__PURE__ */ new Set();
80
+ const tools = /* @__PURE__ */ new Set();
81
+ const languages = /* @__PURE__ */ new Set();
82
+ for (const a of src.classArmor) armor.add(prettyName(a));
83
+ for (const w of src.classWeaponFixed) weapons.add(prettyName(w));
84
+ for (const w of src.classWeaponCategories) weapons.add(prettyName(w));
85
+ for (const t of src.classToolFixed) tools.add(prettyName(t));
86
+ for (const l of src.raceLangFixed) languages.add(prettyName(l));
87
+ for (const t of src.bgToolFixed) tools.add(prettyName(t));
88
+ for (const l of src.bgLangFixed) languages.add(prettyName(l));
89
+ for (const a of src.featArmor) armor.add(prettyName(a));
90
+ for (const w of src.featWeapons) weapons.add(prettyName(w));
91
+ for (const t of src.featTools) tools.add(prettyName(t));
92
+ for (const l of src.featLanguages) languages.add(prettyName(l));
93
+ for (const l of src.chosenLanguages) languages.add(prettyName(l));
94
+ for (const t of src.chosenTools) tools.add(prettyName(t));
35
95
  return {
36
96
  armor: [...armor].sort(),
37
97
  weapons: [...weapons].sort(),
38
98
  tools: [...tools].sort(),
39
- languages: [...languages].sort()
99
+ languages: [...languages].sort(),
100
+ choices: {
101
+ languages: placeholders(src.languageChoices),
102
+ tools: placeholders(src.toolChoices)
103
+ }
40
104
  };
41
105
  }
106
+ function placeholders(choices) {
107
+ const out = [];
108
+ for (const c of choices) {
109
+ const remaining = c.count - c.selected;
110
+ if (remaining > 0) out.push("choose " + remaining);
111
+ }
112
+ return out;
113
+ }
42
114
  function prettyName(slug) {
43
115
  return slug.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
44
116
  }
45
117
  export {
46
- aggregateProficiencies
118
+ aggregateProficiencies,
119
+ collectProficiencySources
47
120
  };
@@ -0,0 +1,5 @@
1
+ /** Normalized token-set key for matching class weapon DISPLAY names ("hand crossbows")
2
+ * against weapon-entity names ("Crossbow, hand"). EXACT equality on the key; verified
3
+ * collision-free across the SRD weapon set. Strip-"s" singularizer does not handle
4
+ * irregular plurals; no SRD weapon triggers that. */
5
+ export declare const normKey: (s: string) => string;
@@ -1,6 +1,10 @@
1
+ import {
2
+ normKey
3
+ } from "../chunk-O5EYMKFH.js";
4
+
1
5
  // src/pc/pc.proficiency-query.ts
2
6
  function isProficientWithWeapon(weapon, profs) {
3
- if (profs.weapons.specific.includes(weapon.slug)) return true;
7
+ if (profs.weapons.specific.some((e) => e === weapon.slug || normKey(e) === normKey(weapon.name))) return true;
4
8
  const baseCategory = weapon.category.split("-")[0];
5
9
  return profs.weapons.categories.includes(baseCategory);
6
10
  }
@@ -45,11 +45,18 @@ export declare function abilityBonusBreakdown(resolved: ResolvedCharacter): Reco
45
45
  class: number;
46
46
  feat: number;
47
47
  }>;
48
+ /** Dice-only portion of PC max HP: first VALID level anywhere = max(die),
49
+ * every later valid level = PHB average. Skip rule identical to hpLevelCount. */
50
+ export declare function hitDiceAverageSum(classes: ResolvedClass[]): number;
51
+ /** Count of levels that contribute to HP (valid class entries only). */
52
+ export declare function hpLevelCount(classes: ResolvedClass[]): number;
48
53
  /**
49
54
  * PC-oriented multiclass HP.
50
55
  * - First class's first level: max(hit_die) + conMod.
51
56
  * - Every subsequent level (in any class): phbAverageForDie(thatLevel'sClass) + conMod.
52
57
  * CON mod contributes once per total level.
58
+ * Decomposed (P5 T2) into hitDiceAverageSum (dice-only) + hpLevelCount (level count),
59
+ * so a later popup can render the two components separately.
53
60
  */
54
61
  export declare function multiclassMaxHP(classes: ResolvedClass[], conMod: number): number;
55
62
  /**