@archivist-gg/dnd5e 0.1.1 → 0.3.0
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.
- package/dist/background/background.codec.js +4 -4
- package/dist/background/background.parser.js +3 -3
- package/dist/background/background.schema.js +2 -2
- package/dist/{chunk-FFULAF2P.js → chunk-2BWAID3Y.js} +2 -2
- package/dist/chunk-353WXS6S.js +25 -0
- package/dist/{chunk-FUK7QSN5.js → chunk-42Y5IJKT.js} +1 -1
- package/dist/{chunk-TPZSPU4L.js → chunk-4JHOHWBI.js} +1 -1
- package/dist/{chunk-DC74TZNA.js → chunk-4LZJXJJS.js} +7 -2
- package/dist/chunk-5MZSB6OM.js +1089 -0
- package/dist/{chunk-FEOB24KP.js → chunk-73HOFQQ5.js} +1 -1
- package/dist/{chunk-YDPWD2T7.js → chunk-7VJ6BOA5.js} +4 -1
- package/dist/{chunk-ZLG6FQFB.js → chunk-A4WENOUQ.js} +21 -2
- package/dist/{chunk-GEJSJP6E.js → chunk-AIRFC5NQ.js} +3 -1
- package/dist/{chunk-DABBCYTT.js → chunk-CZPMTQOY.js} +1 -1
- package/dist/{chunk-DRNZJNJP.js → chunk-DZXSWSBP.js} +1 -1
- package/dist/{chunk-ADLTSHOR.js → chunk-EMOORSQ7.js} +59 -30
- package/dist/{chunk-RVYYKYFL.js → chunk-GE4TLPWM.js} +1 -1
- package/dist/{chunk-VLKIB33F.js → chunk-K6Q5OUGK.js} +6 -0
- package/dist/{chunk-PMADYMUI.js → chunk-L7E2UT5A.js} +1 -1
- package/dist/{chunk-3TRGRSCH.js → chunk-LV4SNWZQ.js} +7 -1
- package/dist/{chunk-IXZXFFFW.js → chunk-M2EMFD6S.js} +1 -1
- package/dist/{chunk-UC4FE5X3.js → chunk-N6N74K6X.js} +1 -1
- package/dist/{chunk-CDW7R2QT.js → chunk-NXSWH3AE.js} +4 -1
- package/dist/{chunk-IDTDACM6.js → chunk-PHQRGDRQ.js} +1 -1
- package/dist/{chunk-BAXWWGFJ.js → chunk-Q5K6LWBZ.js} +1 -1
- package/dist/{chunk-Z6GXV2GO.js → chunk-TLYDBT5F.js} +1 -1
- package/dist/{chunk-A7T5L7SS.js → chunk-TUIFSVLI.js} +1 -1
- package/dist/{chunk-J2RYSZMQ.js → chunk-VTTA2ALZ.js} +2 -2
- package/dist/{chunk-Z4TL2V7S.js → chunk-VTYUBINW.js} +1 -1
- package/dist/{chunk-B7UXKNLD.js → chunk-W7HWMMBF.js} +1 -1
- package/dist/{chunk-SSXBYF3P.js → chunk-XLSVNUTK.js} +1 -1
- package/dist/{chunk-GIOH5JB5.js → chunk-XTIQWSFM.js} +1 -1
- package/dist/{chunk-PKC77VZP.js → chunk-ZZGEVJKX.js} +1 -1
- package/dist/class/class.codec.js +5 -5
- package/dist/class/class.parser.js +4 -4
- package/dist/class/class.schema.js +3 -3
- package/dist/class/class.slug.d.ts +7 -5
- package/dist/class/class.slug.js +1 -1
- package/dist/entities/base-item-resolver.d.ts +9 -0
- package/dist/entities/base-item-resolver.js +6 -4
- package/dist/entities/slug.d.ts +7 -0
- package/dist/entities/slug.js +3 -1
- package/dist/feat/feat.codec.js +4 -4
- package/dist/feat/feat.parser.js +3 -3
- package/dist/feat/feat.schema.js +2 -2
- package/dist/index.js +4116 -3662
- package/dist/item/item.actions-map.d.ts +18 -10
- package/dist/item/item.actions-map.js +16 -12
- package/dist/item/item.codec.js +3 -3
- package/dist/item/item.parser.js +2 -2
- package/dist/item/item.schema.d.ts +3 -0
- package/dist/item/item.schema.js +1 -1
- package/dist/item/item.types.d.ts +8 -0
- package/dist/pc/pc.decision-engine.d.ts +0 -3
- package/dist/pc/pc.decision-engine.js +8 -3
- package/dist/pc/pc.equipment.js +5 -8
- package/dist/pc/pc.feature-effects.d.ts +5 -0
- package/dist/pc/pc.feature-effects.js +2 -4
- package/dist/pc/pc.parser.js +1 -1
- package/dist/pc/pc.pools.js +8 -3
- package/dist/pc/pc.recalc.d.ts +7 -0
- package/dist/pc/pc.recalc.js +94 -40
- package/dist/pc/pc.resolver.d.ts +59 -1
- package/dist/pc/pc.resolver.js +21 -343
- package/dist/pc/pc.rest.d.ts +1 -1
- package/dist/pc/pc.rest.js +20 -11
- package/dist/pc/pc.schema.d.ts +23 -0
- package/dist/pc/pc.schema.js +1 -1
- package/dist/pc/pc.slotting.js +3 -3
- package/dist/pc/pc.spellcasting.js +2 -2
- package/dist/pc/pc.types.d.ts +65 -5
- package/dist/race/race.codec.js +5 -5
- package/dist/race/race.parser.js +4 -4
- package/dist/race/race.schema.js +3 -3
- package/dist/schemas/choice-schema.js +1 -1
- package/dist/schemas/feature-schema.js +2 -2
- package/dist/spell/spell.access.js +1 -1
- package/dist/subclass/subclass.codec.js +5 -5
- package/dist/subclass/subclass.parser.js +4 -4
- package/dist/subclass/subclass.schema.js +3 -3
- package/dist/types/choice.d.ts +5 -0
- package/dist/weapon/weapon-mastery.d.ts +3 -0
- package/dist/weapon/weapon.classify.d.ts +21 -0
- package/dist/weapon/weapon.classify.js +7 -0
- package/package.json +1 -1
- package/dist/chunk-6PXE7T7C.js +0 -79
- package/dist/chunk-TJCNX6UZ.js +0 -521
|
@@ -21,21 +21,29 @@ 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
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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
|
|
31
|
-
*
|
|
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
|
/**
|
|
35
36
|
* Resolve the ItemAction for an equipped entry.
|
|
36
|
-
* Priority:
|
|
37
|
-
*
|
|
37
|
+
* Priority: per-instance override > curated map > `itemType==="potion"`
|
|
38
|
+
* bonus-action default > null.
|
|
39
|
+
*
|
|
40
|
+
* `itemType` is the entity's `data.type` (from the registry, statically
|
|
41
|
+
* `unknown`); per the 2024 SRD, drinking/administering a potion is a Bonus
|
|
42
|
+
* Action, so an uncurated `type==="potion"` item still surfaces (curated oils
|
|
43
|
+
* mis-typed `"potion"` are pinned to "action" so this default cannot mislabel
|
|
44
|
+
* them). Returns null when no source supplies a cost (non-potion uncurated with
|
|
45
|
+
* no override).
|
|
38
46
|
*
|
|
39
47
|
* Accepts both bare and compendium-prefixed slugs (see `findItemAction`).
|
|
40
48
|
*/
|
|
41
|
-
export declare function resolveItemAction(slug: string, entry: EquipmentEntry): ItemAction | null;
|
|
49
|
+
export declare function resolveItemAction(slug: string, entry: EquipmentEntry, itemType?: unknown): ItemAction | null;
|
|
@@ -46,30 +46,34 @@ var ITEM_ACTIONS = {
|
|
|
46
46
|
// Magic weapons with activation actions (surface in items table only via override; weapons table is primary)
|
|
47
47
|
"sun-blade": { cost: "free", range: "self" },
|
|
48
48
|
// Potions / consumables
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"potion-of-
|
|
52
|
-
"potion-of-
|
|
49
|
+
// 2024 healing potions are a Bonus Action to drink/administer (curation still
|
|
50
|
+
// carries range/max_charges/recovery; the type=potion default covers uncurated ones).
|
|
51
|
+
"potion-of-healing": { cost: "bonus-action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
52
|
+
"potion-of-greater-healing": { cost: "bonus-action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
53
|
+
"potion-of-superior-healing": { cost: "bonus-action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
54
|
+
"potion-of-supreme-healing": { cost: "bonus-action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
53
55
|
"alchemists-fire": { cost: "action", range: "20 ft.", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
54
56
|
"holy-water": { cost: "action", range: "20 ft.", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
57
|
+
// Oils are APPLIED (minutes), not drunk: curated to "action" so the blanket
|
|
58
|
+
// type=potion → bonus-action default never mislabels these mis-typed items.
|
|
55
59
|
"oil-of-sharpness": { cost: "action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
60
|
+
"oil-of-etherealness": { cost: "action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
61
|
+
"oil-of-slipperiness": { cost: "action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } },
|
|
56
62
|
"dust-of-disappearance": { cost: "action", range: "self", max_charges: 1, recovery: { amount: "0", reset: "special" } }
|
|
57
63
|
};
|
|
58
64
|
function findItemAction(slug) {
|
|
59
65
|
if (slug in ITEM_ACTIONS) return ITEM_ACTIONS[slug];
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
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];
|
|
64
69
|
}
|
|
65
|
-
function resolveItemAction(slug, entry) {
|
|
66
|
-
var _a;
|
|
70
|
+
function resolveItemAction(slug, entry, itemType) {
|
|
71
|
+
var _a, _b;
|
|
67
72
|
const curated = (_a = findItemAction(slug)) != null ? _a : null;
|
|
68
73
|
const override = entry.overrides;
|
|
69
74
|
const overrideCost = override == null ? void 0 : override.action;
|
|
70
75
|
const overrideRange = override == null ? void 0 : override.range;
|
|
71
|
-
|
|
72
|
-
const cost = overrideCost != null ? overrideCost : curated == null ? void 0 : curated.cost;
|
|
76
|
+
const cost = (_b = overrideCost != null ? overrideCost : curated == null ? void 0 : curated.cost) != null ? _b : itemType === "potion" ? "bonus-action" : void 0;
|
|
73
77
|
if (!cost) return null;
|
|
74
78
|
return {
|
|
75
79
|
cost,
|
package/dist/item/item.codec.js
CHANGED
package/dist/item/item.parser.js
CHANGED
|
@@ -175,6 +175,9 @@ export declare const itemEntitySchema: z.ZodObject<{
|
|
|
175
175
|
}>>;
|
|
176
176
|
condition: z.ZodOptional<z.ZodString>;
|
|
177
177
|
}, z.core.$strip>>>;
|
|
178
|
+
scroll_level: z.ZodOptional<z.ZodNumber>;
|
|
179
|
+
unidentified: z.ZodOptional<z.ZodBoolean>;
|
|
180
|
+
masked_category: z.ZodOptional<z.ZodString>;
|
|
178
181
|
raw: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
179
182
|
damage_dice: z.ZodOptional<z.ZodString>;
|
|
180
183
|
damage_type: z.ZodOptional<z.ZodString>;
|
package/dist/item/item.schema.js
CHANGED
|
@@ -106,6 +106,14 @@ export interface ItemEntity {
|
|
|
106
106
|
applies_to?: "weapon" | "spell" | "all";
|
|
107
107
|
condition?: string;
|
|
108
108
|
}>;
|
|
109
|
+
/** Spell-scroll casting level (e.g. 3 for a "Spell Scroll (3rd Level)"). */
|
|
110
|
+
scroll_level?: number;
|
|
111
|
+
/** Marks an item the player has not yet identified: the sheet shows the
|
|
112
|
+
* masked_category label instead of the item's true identity. */
|
|
113
|
+
unidentified?: boolean;
|
|
114
|
+
/** Generic category label shown for an unidentified item (e.g. "potion",
|
|
115
|
+
* "scroll", "wand") in place of its real name. */
|
|
116
|
+
masked_category?: string;
|
|
109
117
|
raw?: Record<string, unknown>;
|
|
110
118
|
damage_dice?: string;
|
|
111
119
|
damage_type?: string;
|
|
@@ -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;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__matchesFilterForTest,
|
|
3
|
-
bareEntitySlug,
|
|
4
3
|
buildDecisionLedger,
|
|
5
4
|
collectChosenAbilityPoints,
|
|
6
5
|
collectChosenProficiencies,
|
|
7
6
|
wikilinkTailSlug
|
|
8
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-5MZSB6OM.js";
|
|
9
8
|
import "../chunk-BH6AMZVY.js";
|
|
9
|
+
import "../chunk-M2EMFD6S.js";
|
|
10
|
+
import "../chunk-5XUXXAQJ.js";
|
|
10
11
|
import "../chunk-JEYQU3JT.js";
|
|
12
|
+
import "../chunk-N6N74K6X.js";
|
|
13
|
+
import "../chunk-4LZJXJJS.js";
|
|
14
|
+
import "../chunk-LV4SNWZQ.js";
|
|
15
|
+
import "../chunk-VTTA2ALZ.js";
|
|
16
|
+
import "../chunk-NAEPSZAM.js";
|
|
11
17
|
import "../chunk-WP2CX2JO.js";
|
|
12
18
|
export {
|
|
13
19
|
__matchesFilterForTest,
|
|
14
|
-
bareEntitySlug,
|
|
15
20
|
buildDecisionLedger,
|
|
16
21
|
collectChosenAbilityPoints,
|
|
17
22
|
collectChosenProficiencies,
|
package/dist/pc/pc.equipment.js
CHANGED
|
@@ -2,16 +2,13 @@ import {
|
|
|
2
2
|
computeAppliedBonuses,
|
|
3
3
|
computeSlotsAndAttacks,
|
|
4
4
|
emptyAppliedBonuses
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-BH6AMZVY.js";
|
|
9
|
-
import "../chunk-JEYQU3JT.js";
|
|
5
|
+
} from "../chunk-EMOORSQ7.js";
|
|
6
|
+
import "../chunk-353WXS6S.js";
|
|
7
|
+
import "../chunk-N6N74K6X.js";
|
|
10
8
|
import "../chunk-LWFTHJIE.js";
|
|
11
9
|
import "../chunk-DNAJRMNN.js";
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-WP2CX2JO.js";
|
|
10
|
+
import "../chunk-4LZJXJJS.js";
|
|
11
|
+
import "../chunk-LV4SNWZQ.js";
|
|
15
12
|
export {
|
|
16
13
|
computeAppliedBonuses,
|
|
17
14
|
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,10 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
computeFeatureEffects,
|
|
3
3
|
emptyFeatureEffectTotals
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-BH6AMZVY.js";
|
|
7
|
-
import "../chunk-JEYQU3JT.js";
|
|
4
|
+
} from "../chunk-AIRFC5NQ.js";
|
|
5
|
+
import "../chunk-LV4SNWZQ.js";
|
|
8
6
|
import "../chunk-WP2CX2JO.js";
|
|
9
7
|
export {
|
|
10
8
|
computeFeatureEffects,
|
package/dist/pc/pc.parser.js
CHANGED
package/dist/pc/pc.pools.js
CHANGED
|
@@ -2,11 +2,16 @@ import {
|
|
|
2
2
|
levelPrereqMax,
|
|
3
3
|
resolveAllPools,
|
|
4
4
|
resolvePool
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import "../chunk-5XUXXAQJ.js";
|
|
7
|
-
import "../chunk-TJCNX6UZ.js";
|
|
5
|
+
} from "../chunk-5MZSB6OM.js";
|
|
8
6
|
import "../chunk-BH6AMZVY.js";
|
|
7
|
+
import "../chunk-M2EMFD6S.js";
|
|
8
|
+
import "../chunk-5XUXXAQJ.js";
|
|
9
9
|
import "../chunk-JEYQU3JT.js";
|
|
10
|
+
import "../chunk-N6N74K6X.js";
|
|
11
|
+
import "../chunk-4LZJXJJS.js";
|
|
12
|
+
import "../chunk-LV4SNWZQ.js";
|
|
13
|
+
import "../chunk-VTTA2ALZ.js";
|
|
14
|
+
import "../chunk-NAEPSZAM.js";
|
|
10
15
|
import "../chunk-WP2CX2JO.js";
|
|
11
16
|
export {
|
|
12
17
|
levelPrereqMax,
|
package/dist/pc/pc.recalc.d.ts
CHANGED
|
@@ -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
|
/**
|
package/dist/pc/pc.recalc.js
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeConditionEffects
|
|
3
|
+
} from "../chunk-R2ZV233X.js";
|
|
4
|
+
import {
|
|
5
|
+
collectChosenAbilityPoints,
|
|
6
|
+
collectChosenProficiencies
|
|
7
|
+
} from "../chunk-5MZSB6OM.js";
|
|
8
|
+
import "../chunk-BH6AMZVY.js";
|
|
1
9
|
import {
|
|
2
10
|
computeSpellLimits,
|
|
3
11
|
deriveSpellSlots,
|
|
4
12
|
resolveSpellcasting
|
|
5
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-M2EMFD6S.js";
|
|
6
14
|
import "../chunk-5XUXXAQJ.js";
|
|
7
|
-
import
|
|
8
|
-
computeConditionEffects
|
|
9
|
-
} from "../chunk-R2ZV233X.js";
|
|
15
|
+
import "../chunk-JEYQU3JT.js";
|
|
10
16
|
import {
|
|
11
17
|
computeAppliedBonuses,
|
|
12
18
|
computeSlotsAndAttacks,
|
|
13
19
|
emptyAppliedBonuses
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import "../chunk-
|
|
20
|
+
} from "../chunk-EMOORSQ7.js";
|
|
21
|
+
import "../chunk-353WXS6S.js";
|
|
22
|
+
import "../chunk-N6N74K6X.js";
|
|
16
23
|
import {
|
|
17
24
|
computeFeatureEffects
|
|
18
|
-
} from "../chunk-
|
|
19
|
-
import {
|
|
20
|
-
collectChosenAbilityPoints,
|
|
21
|
-
collectChosenProficiencies
|
|
22
|
-
} from "../chunk-TJCNX6UZ.js";
|
|
23
|
-
import "../chunk-BH6AMZVY.js";
|
|
24
|
-
import "../chunk-JEYQU3JT.js";
|
|
25
|
+
} from "../chunk-AIRFC5NQ.js";
|
|
25
26
|
import "../chunk-LWFTHJIE.js";
|
|
26
27
|
import "../chunk-DNAJRMNN.js";
|
|
27
|
-
import "../chunk-
|
|
28
|
-
import "../chunk-
|
|
29
|
-
import "../chunk-
|
|
28
|
+
import "../chunk-4LZJXJJS.js";
|
|
29
|
+
import "../chunk-LV4SNWZQ.js";
|
|
30
|
+
import "../chunk-VTTA2ALZ.js";
|
|
30
31
|
import {
|
|
31
32
|
abilityModifier,
|
|
32
33
|
attackBonus,
|
|
@@ -117,7 +118,7 @@ function abilityBonusBreakdown(resolved) {
|
|
|
117
118
|
}
|
|
118
119
|
return out;
|
|
119
120
|
}
|
|
120
|
-
function
|
|
121
|
+
function hitDiceAverageSum(classes) {
|
|
121
122
|
var _a;
|
|
122
123
|
let total = 0;
|
|
123
124
|
let firstLevelCounted = false;
|
|
@@ -126,14 +127,27 @@ function multiclassMaxHP(classes, conMod) {
|
|
|
126
127
|
if (!c.entity || die == null || c.level < 1) continue;
|
|
127
128
|
for (let lvl = 1; lvl <= c.level; lvl++) {
|
|
128
129
|
if (!firstLevelCounted) {
|
|
129
|
-
total += die
|
|
130
|
+
total += die;
|
|
130
131
|
firstLevelCounted = true;
|
|
131
132
|
} else {
|
|
132
|
-
total += Math.floor(die / 2) + 1
|
|
133
|
+
total += Math.floor(die / 2) + 1;
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
|
-
return
|
|
137
|
+
return total;
|
|
138
|
+
}
|
|
139
|
+
function hpLevelCount(classes) {
|
|
140
|
+
var _a;
|
|
141
|
+
let count = 0;
|
|
142
|
+
for (const c of classes) {
|
|
143
|
+
const die = parseDieSize((_a = c.entity) == null ? void 0 : _a.hit_die);
|
|
144
|
+
if (!c.entity || die == null || c.level < 1) continue;
|
|
145
|
+
count += c.level;
|
|
146
|
+
}
|
|
147
|
+
return count;
|
|
148
|
+
}
|
|
149
|
+
function multiclassMaxHP(classes, conMod) {
|
|
150
|
+
return Math.max(1, hitDiceAverageSum(classes) + conMod * hpLevelCount(classes));
|
|
137
151
|
}
|
|
138
152
|
function unarmoredAC(resolved, mods, warnings) {
|
|
139
153
|
return unarmoredACBreakdown(resolved, mods, warnings).total;
|
|
@@ -362,7 +376,7 @@ function computeProficiencies(resolved) {
|
|
|
362
376
|
};
|
|
363
377
|
}
|
|
364
378
|
function recalc(resolved, registry) {
|
|
365
|
-
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;
|
|
379
|
+
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, __, _$, _aa;
|
|
366
380
|
const warnings = [];
|
|
367
381
|
const overrides = (_a = resolved.definition.overrides) != null ? _a : {};
|
|
368
382
|
const chosenProfs = collectChosenProficiencies(resolved);
|
|
@@ -484,9 +498,36 @@ function recalc(resolved, registry) {
|
|
|
484
498
|
investigation: (_t = (_s = overrides.passives) == null ? void 0 : _s.investigation) != null ? _t : passive(scores.int, investigationTri, proficiencyBonus),
|
|
485
499
|
insight: (_v = (_u = overrides.passives) == null ? void 0 : _u.insight) != null ? _v : passive(scores.wis, insightTri, proficiencyBonus)
|
|
486
500
|
};
|
|
487
|
-
const
|
|
488
|
-
const
|
|
489
|
-
const
|
|
501
|
+
const hpRolled = (_x = (_w = overrides.hp) == null ? void 0 : _w.rolled) != null ? _x : null;
|
|
502
|
+
const hpModifier = (_z = (_y = overrides.hp) == null ? void 0 : _y.modifier) != null ? _z : null;
|
|
503
|
+
const averageDiceSum = hitDiceAverageSum(resolved.classes);
|
|
504
|
+
const conLevels = hpLevelCount(resolved.classes);
|
|
505
|
+
const diceSum = hpRolled != null ? hpRolled : averageDiceSum;
|
|
506
|
+
const diceConRaw = diceSum + mods.con * conLevels;
|
|
507
|
+
const diceConClamped = Math.max(1, diceConRaw);
|
|
508
|
+
const hpMaxDerived = diceConClamped + featureEffects.hp_per_level_bonus * totalLevel + (hpModifier != null ? hpModifier : 0);
|
|
509
|
+
const hpMaxAfterConditions = Math.max(0, Math.floor(hpMaxDerived * conditionEffects.hp_max_multiplier));
|
|
510
|
+
const hpMax = (_B = (_A = overrides.hp) == null ? void 0 : _A.max) != null ? _B : hpMaxAfterConditions;
|
|
511
|
+
const hpBreakdown = {
|
|
512
|
+
diceSum,
|
|
513
|
+
diceSource: hpRolled != null ? "rolled" : "average",
|
|
514
|
+
averageDiceSum,
|
|
515
|
+
conMod: mods.con,
|
|
516
|
+
conLevels,
|
|
517
|
+
clampApplied: diceConClamped !== diceConRaw,
|
|
518
|
+
perLevelTerms: featureEffects.hp_per_level_terms.map((t) => ({
|
|
519
|
+
label: t.label,
|
|
520
|
+
perLevel: t.value,
|
|
521
|
+
levels: totalLevel,
|
|
522
|
+
total: t.value * totalLevel
|
|
523
|
+
})),
|
|
524
|
+
modifier: hpModifier,
|
|
525
|
+
exhaustionMultiplier: conditionEffects.hp_max_multiplier,
|
|
526
|
+
exhaustionLevel: conditionEffects.exhaustion_level,
|
|
527
|
+
derivedMax: hpMaxAfterConditions,
|
|
528
|
+
override: (_D = (_C = overrides.hp) == null ? void 0 : _C.max) != null ? _D : null,
|
|
529
|
+
final: hpMax
|
|
530
|
+
};
|
|
490
531
|
const featureAcTermsFor = (hasArmor) => featureEffects.ac_terms.filter((t) => !t.requires_armor || hasArmor).map((t) => ({ source: t.label, amount: t.value, kind: "feature" }));
|
|
491
532
|
const sumTerms = (terms) => terms.reduce((s, t) => s + t.amount, 0);
|
|
492
533
|
const weaponAbilities = [...featureEffects.weaponAbilities];
|
|
@@ -511,7 +552,7 @@ function recalc(resolved, registry) {
|
|
|
511
552
|
let acBreakdownDerived = [];
|
|
512
553
|
let acInformationalDerived = [];
|
|
513
554
|
if (registry) {
|
|
514
|
-
derivedEquipment = computeSlotsAndAttacks(resolved, mods, profsForApply, registry, warnings, proficiencyBonus, weaponAbilities, (
|
|
555
|
+
derivedEquipment = computeSlotsAndAttacks(resolved, mods, profsForApply, registry, warnings, proficiencyBonus, weaponAbilities, (_E = resolved.weaponMasteries) != null ? _E : []);
|
|
515
556
|
if (derivedEquipment.equippedSlots.armor) {
|
|
516
557
|
const featTerms = featureAcTermsFor(true);
|
|
517
558
|
acDerived = derivedEquipment.ac + sumTerms(featTerms);
|
|
@@ -534,16 +575,16 @@ function recalc(resolved, registry) {
|
|
|
534
575
|
acDerived = total + sumTerms(featTerms);
|
|
535
576
|
acBreakdownDerived = [...terms, ...featTerms];
|
|
536
577
|
}
|
|
537
|
-
const ac = (
|
|
578
|
+
const ac = (_F = overrides.ac) != null ? _F : acDerived;
|
|
538
579
|
const additiveSpeed = speedFromRace(resolved) + applied.speed_bonuses.walk + featureEffects.speed_walk_bonus;
|
|
539
580
|
const baseSpeed = Math.max(featureEffects.speed_walk_set, additiveSpeed);
|
|
540
581
|
const adjustedSpeed = baseSpeed * conditionEffects.speed_multiplier - conditionEffects.speed_reduction_ft;
|
|
541
582
|
const conditionSpeed = conditionEffects.speed_floor_zero ? 0 : Math.max(0, Math.floor(adjustedSpeed));
|
|
542
|
-
const speed = (
|
|
583
|
+
const speed = (_G = overrides.speed) != null ? _G : conditionSpeed;
|
|
543
584
|
if (!resolved.race) warnings.push("No race resolved; speed defaulted to 30.");
|
|
544
|
-
const init = (
|
|
585
|
+
const init = (_H = overrides.initiative) != null ? _H : initiativeBonus(mods.dex, resolved.feats, resolved.definition.edition) + featureEffects.initiative_bonus;
|
|
545
586
|
const senses = {
|
|
546
|
-
darkvision: Math.max((
|
|
587
|
+
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),
|
|
547
588
|
blindsight: Math.max(featureEffects.senses.blindsight, applied.senses.blindsight),
|
|
548
589
|
tremorsense: Math.max(featureEffects.senses.tremorsense, applied.senses.tremorsense),
|
|
549
590
|
truesight: Math.max(featureEffects.senses.truesight, applied.senses.truesight)
|
|
@@ -561,8 +602,8 @@ function recalc(resolved, registry) {
|
|
|
561
602
|
classSlug: c.entity.slug,
|
|
562
603
|
className: c.entity.name,
|
|
563
604
|
ability: profile.ability,
|
|
564
|
-
saveDC: (
|
|
565
|
-
attackBonus: (
|
|
605
|
+
saveDC: (_M = (_L = overrides.spellcasting) == null ? void 0 : _L.saveDC) != null ? _M : dc,
|
|
606
|
+
attackBonus: (_O = (_N = overrides.spellcasting) == null ? void 0 : _N.attackBonus) != null ? _O : atk,
|
|
566
607
|
casterType: profile.casterType,
|
|
567
608
|
preparation: profile.preparation
|
|
568
609
|
});
|
|
@@ -574,24 +615,33 @@ function recalc(resolved, registry) {
|
|
|
574
615
|
saveDC: spellcastingClasses[0].saveDC,
|
|
575
616
|
attackBonus: spellcastingClasses[0].attackBonus
|
|
576
617
|
} : null;
|
|
618
|
+
const abilitySpellcasting = {};
|
|
619
|
+
for (const s of resolved.spells) {
|
|
620
|
+
const ab = s.ability;
|
|
621
|
+
if (!ab || abilitySpellcasting[ab]) continue;
|
|
622
|
+
abilitySpellcasting[ab] = {
|
|
623
|
+
saveDC: saveDC(scores[ab], proficiencyBonus) + applied.spell_save_dc,
|
|
624
|
+
attackBonus: attackBonus(scores[ab], proficiencyBonus) + applied.spell_attack
|
|
625
|
+
};
|
|
626
|
+
}
|
|
577
627
|
const derivedSlots = deriveSpellSlots(slotInputs);
|
|
578
628
|
const spellLimits = computeSpellLimits(limitInputs);
|
|
579
629
|
const defenses = {
|
|
580
630
|
resistances: dedupeDefenseList([
|
|
581
|
-
(
|
|
631
|
+
(_Q = (_P = resolved.definition.defenses) == null ? void 0 : _P.resistances) != null ? _Q : [],
|
|
582
632
|
applied.defenses.resistances,
|
|
583
633
|
featureEffects.resistances
|
|
584
634
|
]),
|
|
585
635
|
immunities: dedupeDefenseList([
|
|
586
|
-
(
|
|
636
|
+
(_S = (_R = resolved.definition.defenses) == null ? void 0 : _R.immunities) != null ? _S : [],
|
|
587
637
|
applied.defenses.immunities
|
|
588
638
|
]),
|
|
589
639
|
vulnerabilities: dedupeDefenseList([
|
|
590
|
-
(
|
|
640
|
+
(_U = (_T = resolved.definition.defenses) == null ? void 0 : _T.vulnerabilities) != null ? _U : [],
|
|
591
641
|
applied.defenses.vulnerabilities
|
|
592
642
|
]),
|
|
593
643
|
condition_immunities: dedupeDefenseList([
|
|
594
|
-
(
|
|
644
|
+
(_W = (_V = resolved.definition.defenses) == null ? void 0 : _V.condition_immunities) != null ? _W : [],
|
|
595
645
|
applied.defenses.condition_immunities,
|
|
596
646
|
featureEffects.condition_immunities
|
|
597
647
|
])
|
|
@@ -611,11 +661,13 @@ function recalc(resolved, registry) {
|
|
|
611
661
|
current: resolved.state.hp.current,
|
|
612
662
|
temp: resolved.state.hp.temp
|
|
613
663
|
},
|
|
664
|
+
hpBreakdown,
|
|
614
665
|
ac,
|
|
615
666
|
speed,
|
|
616
667
|
initiative: init,
|
|
617
668
|
spellcasting,
|
|
618
669
|
spellcastingClasses,
|
|
670
|
+
abilitySpellcasting,
|
|
619
671
|
derivedSpellSlots: derivedSlots.standard,
|
|
620
672
|
pactMagic: derivedSlots.pact,
|
|
621
673
|
spellLimits,
|
|
@@ -633,7 +685,7 @@ function recalc(resolved, registry) {
|
|
|
633
685
|
// untouched rows keep `critRange`/`attackNotes` ABSENT (not 20 / not []).
|
|
634
686
|
// crit-range: folded weapon crit threshold, only when an effect lowered it.
|
|
635
687
|
// attackNotes: reroll-damage / attack-rule captions, only when non-empty.
|
|
636
|
-
attacks: ((
|
|
688
|
+
attacks: ((_X = derivedEquipment == null ? void 0 : derivedEquipment.attacks) != null ? _X : []).map((a) => {
|
|
637
689
|
var _a2;
|
|
638
690
|
const riders = [...(_a2 = a.damageRiders) != null ? _a2 : [], ...featureEffects.damageBonuses];
|
|
639
691
|
return {
|
|
@@ -644,10 +696,10 @@ function recalc(resolved, registry) {
|
|
|
644
696
|
...riders.length ? { damageRiders: riders } : {}
|
|
645
697
|
};
|
|
646
698
|
}),
|
|
647
|
-
equippedSlots: (
|
|
648
|
-
carriedWeight: (
|
|
649
|
-
attunementUsed: (
|
|
650
|
-
attunementLimit: (
|
|
699
|
+
equippedSlots: (_Y = derivedEquipment == null ? void 0 : derivedEquipment.equippedSlots) != null ? _Y : {},
|
|
700
|
+
carriedWeight: (_Z = derivedEquipment == null ? void 0 : derivedEquipment.carriedWeight) != null ? _Z : 0,
|
|
701
|
+
attunementUsed: (__ = derivedEquipment == null ? void 0 : derivedEquipment.attunementUsed) != null ? __ : 0,
|
|
702
|
+
attunementLimit: (_aa = derivedEquipment == null ? void 0 : derivedEquipment.attunementLimit) != null ? _aa : (_$ = overrides.attunement_limit) != null ? _$ : 3,
|
|
651
703
|
conditionEffects,
|
|
652
704
|
rollModifiers: featureEffects.rollModifiers
|
|
653
705
|
};
|
|
@@ -659,6 +711,8 @@ export {
|
|
|
659
711
|
computeAbilityScores,
|
|
660
712
|
computeProficiencies,
|
|
661
713
|
flattenRaceAsi,
|
|
714
|
+
hitDiceAverageSum,
|
|
715
|
+
hpLevelCount,
|
|
662
716
|
initiativeBonus,
|
|
663
717
|
multiclassMaxHP,
|
|
664
718
|
parseDieSize,
|
package/dist/pc/pc.resolver.d.ts
CHANGED
|
@@ -2,12 +2,28 @@ import type { EntityRegistry } from "@archivist-gg/core";
|
|
|
2
2
|
import type { RaceEntity } from "@archivist-gg/dnd5e/race/race.types";
|
|
3
3
|
import type { BackgroundEntity } from "@archivist-gg/dnd5e/background/background.types";
|
|
4
4
|
import type { FeatEntity } from "@archivist-gg/dnd5e/feat/feat.types";
|
|
5
|
-
import type {
|
|
5
|
+
import type { Ability } from "@archivist-gg/dnd5e";
|
|
6
|
+
import type { Character, ChoiceValue, ResolvedCharacter, ResolvedClass, ResolvedFeature, ResolvedSpell } from "./pc.types.js";
|
|
6
7
|
export interface ResolveResult {
|
|
7
8
|
character: ResolvedCharacter;
|
|
8
9
|
warnings: string[];
|
|
9
10
|
}
|
|
10
11
|
export declare function stripSlug(ref: string | null): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a background's `origin_feat` wikilink into a real FeatEntity + a display
|
|
14
|
+
* name, using ONLY the EntityRegistry (no DOM). Lifted from the builder's
|
|
15
|
+
* background-step so the resolver pipeline and the builder share ONE resolution
|
|
16
|
+
* (R2-m7, [[always-retire-shims-edit-all]]). Handles both path-style refs
|
|
17
|
+
* ("[[SRD 2024/Feats/Alert]]") and bare-slug refs ("[[my-feat]]"), plus the 2024
|
|
18
|
+
* parenthetical-variant refs ("[[SRD 2024/Feats/Magic Initiate (Cleric)]]") that
|
|
19
|
+
* resolve to the BASE feat ("magic-initiate") while keeping the full variant
|
|
20
|
+
* string as the display name. Returns null when the ref is empty or resolves to no
|
|
21
|
+
* feat (an unresolvable ref folds nothing into the pipeline).
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveOriginFeat(entities: EntityRegistry, originFeatRef: string | null): {
|
|
24
|
+
feat: FeatEntity;
|
|
25
|
+
display: string;
|
|
26
|
+
} | null;
|
|
11
27
|
export declare class PCResolver {
|
|
12
28
|
private readonly entities;
|
|
13
29
|
constructor(entities: EntityRegistry);
|
|
@@ -32,6 +48,48 @@ export declare function collectChosenWeaponMasteries(character: Character): {
|
|
|
32
48
|
* Returns bare slugs (no `[[ ]]`).
|
|
33
49
|
*/
|
|
34
50
|
export declare function collectFeatSlugs(character: Character): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Feat→spell application (3d). Walks a feat's OWN `choices` to find its spell
|
|
53
|
+
* picks and turns each into a ResolvedSpell. A feat's spells sit behind a
|
|
54
|
+
* `spell-list` select-inline whose CHOSEN branch nests `select-entity{spell}`
|
|
55
|
+
* picks; the chosen spellcasting ability comes from the feat's
|
|
56
|
+
* `spellcasting-ability` select-inline. `read(childId)` yields the persisted pick
|
|
57
|
+
* for a child choice id (origin feat: `origin_choices["background:feat:<id>"]`;
|
|
58
|
+
* class-slot feat: `choices[lvl]["feat:<id>"]`). Unresolvable spell slugs warn and
|
|
59
|
+
* are skipped, mirroring the known-spell loop. classSlug stays null: a feat spell
|
|
60
|
+
* is not owned by a class for DC/ability; the carried `ability` drives its casting.
|
|
61
|
+
*/
|
|
62
|
+
export declare function collectFeatGrantedSpells(feat: FeatEntity, read: (childId: string) => ChoiceValue | undefined, entities: EntityRegistry, warnings: string[]): ResolvedSpell[];
|
|
63
|
+
/**
|
|
64
|
+
* Item→spell application (P4-T3). Walks the character's equipment for entries whose
|
|
65
|
+
* resolved item is a spell scroll (carries the T1 `scroll_level` marker) AND names a
|
|
66
|
+
* chosen spell via `overrides.spell`, turning each into a
|
|
67
|
+
* ResolvedSpell{ source:"item", classSlug:null, prepared:true, alwaysPrepared:true }.
|
|
68
|
+
* The casting `ability` is the character's OWN spellcasting ability when they have
|
|
69
|
+
* one (`ownAbility`, the first caster class's ability), else the per-instance
|
|
70
|
+
* `overrides.spell_ability`, else undefined (a no-ability scroll is left ability-less:
|
|
71
|
+
* the plugin surfaces it without a DC; an ability is NEVER fabricated here).
|
|
72
|
+
* `entryIndex` records the originating equipment index so the segmented dedupe keeps
|
|
73
|
+
* two scrolls of one spell (and a scroll of a class-known spell) as their own rows.
|
|
74
|
+
* Unresolvable spell slugs warn and are skipped, mirroring the known-spell + feat loops.
|
|
75
|
+
* In-memory only: no KnownSpellObject{source:"item"} is injected (its strict schema
|
|
76
|
+
* omits `ability`).
|
|
77
|
+
*/
|
|
78
|
+
export declare function collectItemGrantedSpells(character: Character, ownAbility: Ability | null, entities: EntityRegistry, warnings: string[]): ResolvedSpell[];
|
|
79
|
+
/**
|
|
80
|
+
* Collapses duplicate resolved spells (3d Minor #2 carry-forward + P4-T3 segmented
|
|
81
|
+
* dedupe). A spell can be emitted more than once: it may be BOTH in
|
|
82
|
+
* `character.spells.known` AND a feat pick, or a feat may be taken as both origin +
|
|
83
|
+
* class-slot. A class-sourced copy owns a real DC via its `classSlug`, so it always
|
|
84
|
+
* wins over a feat copy of the same slug; otherwise first-seen wins. Insertion order
|
|
85
|
+
* is preserved (a later class copy replaces an earlier feat copy in place), so the
|
|
86
|
+
* Spells section never renders duplicate rows while a legitimately class-known copy is
|
|
87
|
+
* never lost. Item-source spells carry INSTANCE identity: they key by
|
|
88
|
+
* `slug + "#" + entryIndex`, so two scrolls of one spell stay two rows and a scroll of
|
|
89
|
+
* a class-known spell keeps its OWN item row, never collapsing into / being collapsed
|
|
90
|
+
* by a class/feat copy. The class/feat slug-only merge is unchanged.
|
|
91
|
+
*/
|
|
92
|
+
export declare function dedupeResolvedSpells(spells: ResolvedSpell[]): ResolvedSpell[];
|
|
35
93
|
/** Selected select-entity values (optional-features) and selected inline
|
|
36
94
|
* options carrying effects[] become synthesized resolved features (SP2 Plan 3 §9).
|
|
37
95
|
* The actions/resources engines consume these synthesized features, and the
|