@archivist-gg/dnd5e 0.3.1 → 0.3.4
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/LICENSES/SRD.md +25 -6
- package/dist/armor/armor.codec.js +4 -3
- package/dist/armor/armor.parser.js +3 -2
- package/dist/armor/armor.schema.d.ts +4 -0
- package/dist/armor/armor.schema.js +2 -1
- package/dist/armor/armor.types.d.ts +8 -0
- package/dist/background/background.codec.js +7 -6
- package/dist/background/background.parser.js +6 -5
- package/dist/background/background.schema.d.ts +26 -0
- package/dist/background/background.schema.js +5 -4
- package/dist/background/background.types.d.ts +22 -0
- package/dist/{chunk-4TIJKN6O.js → chunk-23LZLBJK.js} +1 -1
- package/dist/{chunk-AFKTJXRA.js → chunk-2DFENQIJ.js} +18 -0
- package/dist/chunk-2FRUSVZS.js +489 -0
- package/dist/chunk-4FWNK4FD.js +159 -0
- package/dist/chunk-4U55VNC7.js +22 -0
- package/dist/{chunk-4JHOHWBI.js → chunk-4XTNPNQB.js} +1 -1
- package/dist/{chunk-HCP3GMHS.js → chunk-5N2GW72F.js} +15 -2
- package/dist/{chunk-42Y5IJKT.js → chunk-5U5FFFA3.js} +5 -1
- package/dist/{chunk-4F6AIZ36.js → chunk-5XMQPTHD.js} +1 -1
- package/dist/{chunk-GJOI2GHP.js → chunk-6GNXYAYP.js} +1 -1
- package/dist/chunk-6PKV5V2H.js +178 -0
- package/dist/{chunk-66XXW2Z2.js → chunk-6PXTNCBP.js} +2 -2
- package/dist/chunk-6X2NV665.js +33 -0
- package/dist/{chunk-LV4SNWZQ.js → chunk-6X4KUDMU.js} +8 -1
- package/dist/{chunk-ZMR77DDL.js → chunk-AW3EHF3L.js} +1 -1
- package/dist/chunk-CJYCVNW2.js +7 -0
- package/dist/chunk-COEOOQX2.js +133 -0
- package/dist/{chunk-XTIQWSFM.js → chunk-D5U7GMPX.js} +1 -1
- package/dist/{chunk-RAWJTA45.js → chunk-DEEHHLAP.js} +1 -1
- package/dist/{chunk-6IRYTM5V.js → chunk-DPUJDOKV.js} +119 -28
- package/dist/{chunk-73HOFQQ5.js → chunk-ESS46GR4.js} +1 -1
- package/dist/chunk-EVMUJYUD.js +195 -0
- package/dist/{chunk-2BWAID3Y.js → chunk-FDBWYMZK.js} +29 -3
- package/dist/{chunk-K6Q5OUGK.js → chunk-GFZ7PITJ.js} +86 -10
- package/dist/chunk-H7KBLR4X.js +81 -0
- package/dist/{chunk-L7E2UT5A.js → chunk-HB7XZXDK.js} +5 -1
- package/dist/{chunk-BIK4DFWK.js → chunk-HJ5EEZ47.js} +1 -1
- package/dist/{chunk-6NK2MUD3.js → chunk-HZA7OE7N.js} +9 -2
- package/dist/{chunk-TN6ZLTNU.js → chunk-I2NDWFDD.js} +51 -4
- package/dist/{chunk-RSDRMH54.js → chunk-IMZFOOVL.js} +1 -1
- package/dist/{chunk-3DZ74AKK.js → chunk-IN4M4X7P.js} +8 -2
- package/dist/chunk-IUAAXXNO.js +58 -0
- package/dist/{chunk-XJNECUPT.js → chunk-JC5UNIVM.js} +19 -0
- package/dist/chunk-JJ46T5XX.js +297 -0
- package/dist/{chunk-SE7AZDR3.js → chunk-JRDPUDT5.js} +29 -3
- package/dist/{chunk-24GFQU7T.js → chunk-JSZ4H6AQ.js} +1 -1
- package/dist/chunk-KU3YLF6D.js +106 -0
- package/dist/{chunk-5INR4NSL.js → chunk-LDDCNPHD.js} +1 -1
- package/dist/{chunk-5ZBE4CWV.js → chunk-LMBAEIP6.js} +1 -1
- package/dist/chunk-LVQLWRCU.js +15 -0
- package/dist/chunk-MI7P3NX3.js +161 -0
- package/dist/chunk-O4SRD3J5.js +165 -0
- package/dist/{chunk-D3CLOBE3.js → chunk-OSQFPGLM.js} +44 -8
- package/dist/chunk-OZP6JFGT.js +162 -0
- package/dist/{chunk-NAEPSZAM.js → chunk-PDJSUH24.js} +24 -2
- package/dist/chunk-PP2UHLBA.js +50 -0
- package/dist/chunk-PVCEA4HZ.js +6 -0
- package/dist/{chunk-XLSVNUTK.js → chunk-R3VAKWKW.js} +1 -1
- package/dist/chunk-RIXI5D3R.js +89 -0
- package/dist/chunk-RLGQ5PZZ.js +16 -0
- package/dist/chunk-S7REMWW4.js +21 -0
- package/dist/{chunk-7VJ6BOA5.js → chunk-SMLXZUBH.js} +9 -1
- package/dist/{chunk-NXSWH3AE.js → chunk-T26ZVCL5.js} +1 -1
- package/dist/chunk-TOM66P2P.js +38 -0
- package/dist/chunk-UA5KS2PL.js +26 -0
- package/dist/{chunk-N6N74K6X.js → chunk-UBBKSYBY.js} +1 -1
- package/dist/{chunk-353WXS6S.js → chunk-UIRMA5KM.js} +1 -1
- package/dist/{chunk-GE4TLPWM.js → chunk-US5IKYCW.js} +1 -1
- package/dist/chunk-UXPGW4TI.js +101 -0
- package/dist/chunk-VQAXTIBW.js +100 -0
- package/dist/{chunk-WP2CX2JO.js → chunk-WELIKLZF.js} +22 -1
- package/dist/chunk-WPWOHGPI.js +172 -0
- package/dist/{chunk-GZ46OVGZ.js → chunk-WSVMLNTO.js} +1 -1
- package/dist/{chunk-4LZJXJJS.js → chunk-WXWPK3LI.js} +6 -2
- package/dist/{chunk-ZZGEVJKX.js → chunk-XBQMCEI2.js} +1 -1
- package/dist/{chunk-PHQRGDRQ.js → chunk-XGOFP7AQ.js} +5 -1
- package/dist/chunk-XJ2F2WO6.js +8 -0
- package/dist/chunk-XQCSGNXE.js +1580 -0
- package/dist/chunk-YBLDE5CG.js +42 -0
- package/dist/{chunk-JEYQU3JT.js → chunk-YHYTW5QA.js} +2 -18
- package/dist/{chunk-TLYDBT5F.js → chunk-YJJTISBN.js} +64 -7
- package/dist/{chunk-PZOWBI3B.js → chunk-YLL2QXY2.js} +7 -2
- package/dist/{chunk-MDEJHMKK.js → chunk-ZONJ66NE.js} +26 -5
- package/dist/class/class.codec.js +12 -9
- package/dist/class/class.parser.js +11 -8
- package/dist/class/class.schema.d.ts +55 -3
- package/dist/class/class.schema.js +9 -7
- package/dist/class/class.types.d.ts +48 -3
- package/dist/condition/condition.codec.d.ts +9 -0
- package/dist/condition/condition.entity-type.d.ts +2 -0
- package/dist/condition/condition.parser.d.ts +13 -0
- package/dist/condition/condition.parser.js +7 -0
- package/dist/condition/condition.schema.d.ts +29 -0
- package/dist/condition/condition.types.d.ts +15 -0
- package/dist/condition/condition.types.js +0 -0
- package/dist/dnd/constants.d.ts +22 -0
- package/dist/dnd/constants.js +9 -3
- package/dist/dnd/formula-tags.js +2 -2
- package/dist/dnd/math.d.ts +26 -0
- package/dist/dnd/math.js +12 -2
- package/dist/dnd/prose-tags.js +1 -1
- package/dist/dnd/resource-formula.d.ts +36 -10
- package/dist/dnd/resource-formula.js +10 -155
- package/dist/dnd/srd-tag-converter.js +3 -3
- package/dist/dnd/tag-grammar.js +1 -1
- package/dist/dnd/warn-once.d.ts +2 -0
- package/dist/dnd/warn-once.js +8 -0
- package/dist/entities/base-item-resolver.js +2 -2
- package/dist/entities/slug.d.ts +13 -0
- package/dist/entities/slug.js +5 -3
- package/dist/entities/wikilink-display.d.ts +3 -0
- package/dist/entities/wikilink-display.js +10 -0
- package/dist/feat/feat.category-codes.d.ts +32 -0
- package/dist/feat/feat.category-codes.js +14 -0
- package/dist/feat/feat.codec.js +7 -5
- package/dist/feat/feat.parser.js +6 -4
- package/dist/feat/feat.schema.d.ts +230 -0
- package/dist/feat/feat.schema.js +5 -3
- package/dist/feat/feat.types.d.ts +37 -1
- package/dist/index.js +2033 -6907
- package/dist/item/item.codec.js +4 -3
- package/dist/item/item.enrichment.js +4 -4
- package/dist/item/item.parser.js +3 -2
- package/dist/item/item.schema.d.ts +69 -3
- package/dist/item/item.schema.js +2 -1
- package/dist/item/item.types.d.ts +69 -3
- package/dist/monster/monster.codec.d.ts +7 -6
- package/dist/monster/monster.enrichment.js +4 -4
- package/dist/monster/monster.format.d.ts +135 -0
- package/dist/monster/monster.format.js +78 -0
- package/dist/monster/monster.parser.d.ts +6 -1
- package/dist/monster/monster.parser.js +10 -1
- package/dist/monster/monster.schema.d.ts +369 -0
- package/dist/monster/monster.schema.js +34 -0
- package/dist/monster/monster.types.d.ts +200 -14
- package/dist/optional-feature/optional-feature.codec.js +6 -5
- package/dist/optional-feature/optional-feature.parser.js +5 -4
- package/dist/optional-feature/optional-feature.schema.d.ts +176 -0
- package/dist/optional-feature/optional-feature.schema.js +4 -3
- package/dist/pack.d.ts +1 -1
- package/dist/pc/conditions.constants.d.ts +0 -1
- package/dist/pc/conditions.constants.js +1 -3
- package/dist/pc/decision-recognizer.js +1 -1
- package/dist/pc/pc.additional-spells.d.ts +47 -0
- package/dist/pc/pc.additional-spells.js +23 -0
- package/dist/pc/pc.asi-flatten.d.ts +18 -0
- package/dist/pc/pc.decision-engine.d.ts +105 -17
- package/dist/pc/pc.decision-engine.js +28 -12
- package/dist/pc/pc.defense-normalize.d.ts +11 -0
- package/dist/pc/pc.defense-normalize.js +6 -0
- package/dist/pc/pc.equipment.d.ts +30 -1
- package/dist/pc/pc.equipment.js +12 -8
- package/dist/pc/pc.feature-effects.d.ts +225 -11
- package/dist/pc/pc.feature-effects.js +21 -5
- package/dist/pc/pc.parser.js +2 -2
- package/dist/pc/pc.pools.d.ts +11 -3
- package/dist/pc/pc.pools.js +9 -13
- package/dist/pc/pc.proficiencies.d.ts +54 -29
- package/dist/pc/pc.proficiencies.js +74 -95
- package/dist/pc/pc.proficiency-grants.d.ts +58 -0
- package/dist/pc/pc.proficiency-normalize.d.ts +24 -0
- package/dist/pc/pc.proficiency-normalize.js +12 -0
- package/dist/pc/pc.proficiency-query.d.ts +13 -0
- package/dist/pc/pc.proficiency-query.js +1 -1
- package/dist/pc/pc.recalc.d.ts +69 -14
- package/dist/pc/pc.recalc.js +261 -122
- package/dist/pc/pc.resolver.d.ts +34 -8
- package/dist/pc/pc.resolver.js +27 -12
- package/dist/pc/pc.resource-seed.d.ts +3 -2
- package/dist/pc/pc.resource-seed.js +19 -17
- package/dist/pc/pc.resources.d.ts +87 -0
- package/dist/pc/pc.resources.js +21 -0
- package/dist/pc/pc.rest.d.ts +10 -1
- package/dist/pc/pc.rest.js +66 -12
- package/dist/pc/pc.schema.d.ts +139 -3
- package/dist/pc/pc.schema.js +4 -2
- package/dist/pc/pc.slotting.js +3 -3
- package/dist/pc/pc.spellcasting.d.ts +75 -6
- package/dist/pc/pc.spellcasting.js +16 -6
- package/dist/pc/pc.table-column.d.ts +23 -0
- package/dist/pc/pc.table-column.js +13 -3
- package/dist/pc/pc.types.d.ts +251 -12
- package/dist/pc/pool-layout.d.ts +47 -0
- package/dist/pc/pool-layout.js +16 -0
- package/dist/pc/roll-scope.d.ts +2 -0
- package/dist/pc/roll-scope.js +8 -0
- package/dist/pc/roll-tag-labels.d.ts +40 -0
- package/dist/pc/roll-tag-labels.js +17 -0
- package/dist/pc/spell-source.d.ts +21 -0
- package/dist/pc/spell-source.js +13 -0
- package/dist/race/race.codec.js +9 -7
- package/dist/race/race.parser.js +8 -6
- package/dist/race/race.schema.d.ts +51 -1
- package/dist/race/race.schema.js +8 -5
- package/dist/race/race.structural.d.ts +7 -0
- package/dist/race/race.structural.js +6 -0
- package/dist/race/race.types.d.ts +20 -0
- package/dist/schemas/attack-schema.d.ts +5 -0
- package/dist/schemas/caster-type-schema.d.ts +14 -0
- package/dist/schemas/caster-type-schema.js +6 -0
- package/dist/schemas/choice-schema.js +2 -2
- package/dist/schemas/entity-extras-schema.d.ts +53 -0
- package/dist/schemas/entity-extras-schema.js +10 -0
- package/dist/schemas/equipment-grant-schema.d.ts +2 -0
- package/dist/schemas/equipment-grant-schema.js +1 -1
- package/dist/schemas/feature-alias.d.ts +34 -0
- package/dist/schemas/feature-effect-schema.d.ts +124 -0
- package/dist/schemas/feature-effect-schema.js +1 -1
- package/dist/schemas/feature-schema.d.ts +8 -0
- package/dist/schemas/feature-schema.js +6 -4
- package/dist/schemas/resource-schema.d.ts +43 -0
- package/dist/schemas/resource-schema.js +1 -1
- package/dist/schemas/selection-pool-schema.d.ts +2 -0
- package/dist/schemas/selection-pool-schema.js +1 -1
- package/dist/spell/spell.access.js +23 -3
- package/dist/spell/spell.codec.js +4 -3
- package/dist/spell/spell.enrichment.js +4 -4
- package/dist/spell/spell.parser.d.ts +30 -0
- package/dist/spell/spell.parser.js +9 -2
- package/dist/spell/spell.scaling.d.ts +27 -6
- package/dist/spell/spell.scaling.js +25 -6
- package/dist/spell/spell.schema.d.ts +44 -2
- package/dist/spell/spell.schema.js +2 -1
- package/dist/spell/spell.types.d.ts +14 -0
- package/dist/subclass/subclass.codec.d.ts +1 -1
- package/dist/subclass/subclass.codec.js +11 -8
- package/dist/subclass/subclass.parser.js +10 -7
- package/dist/subclass/subclass.schema.d.ts +54 -7
- package/dist/subclass/subclass.schema.js +8 -6
- package/dist/subclass/subclass.types.d.ts +20 -2
- package/dist/types/choice.d.ts +53 -2
- package/dist/types/choice.js +13 -1
- package/dist/types/equipment-grant.d.ts +8 -1
- package/dist/types/feature-effect.d.ts +78 -23
- package/dist/types/feature.d.ts +70 -1
- package/dist/types/optional-feature.types.d.ts +28 -0
- package/dist/types/resource.d.ts +21 -1
- package/dist/types/selection-pool.d.ts +17 -2
- package/dist/weapon/weapon.classify.js +2 -2
- package/dist/weapon/weapon.codec.js +4 -3
- package/dist/weapon/weapon.parser.js +3 -2
- package/dist/weapon/weapon.schema.d.ts +4 -0
- package/dist/weapon/weapon.schema.js +2 -1
- package/dist/weapon/weapon.types.d.ts +8 -0
- package/package.json +2 -2
- package/dist/chunk-5XUXXAQJ.js +0 -30
- package/dist/chunk-7P65Q426.js +0 -158
- package/dist/chunk-7TEK5KK7.js +0 -49
- package/dist/chunk-AIRFC5NQ.js +0 -143
- package/dist/chunk-CZPMTQOY.js +0 -68
- package/dist/chunk-N2JE7BWL.js +0 -81
- package/dist/chunk-NXR3BD2V.js +0 -90
- package/dist/chunk-O5EYMKFH.js +0 -6
- package/dist/chunk-Q5K6LWBZ.js +0 -45
- package/dist/chunk-TNUFPIXI.js +0 -1110
- package/dist/chunk-TZK36BU7.js +0 -38
- package/dist/chunk-W7HWMMBF.js +0 -46
- package/dist/chunk-Y4I74YEG.js +0 -33
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
poolGrantSchema,
|
|
3
|
+
selectionPoolSchema,
|
|
4
|
+
tabDeclSchema
|
|
5
|
+
} from "./chunk-23LZLBJK.js";
|
|
6
|
+
import {
|
|
7
|
+
casterTypeEnum
|
|
8
|
+
} from "./chunk-CJYCVNW2.js";
|
|
9
|
+
import {
|
|
10
|
+
featureSchema
|
|
11
|
+
} from "./chunk-4FWNK4FD.js";
|
|
12
|
+
import {
|
|
13
|
+
resourceSchema
|
|
14
|
+
} from "./chunk-YLL2QXY2.js";
|
|
15
|
+
import {
|
|
16
|
+
additionalSpellsEntrySchema,
|
|
17
|
+
imageField,
|
|
18
|
+
progressionSchema
|
|
19
|
+
} from "./chunk-UA5KS2PL.js";
|
|
20
|
+
|
|
21
|
+
// src/subclass/subclass.schema.ts
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
var editionEnum = z.enum(["2014", "2024"]);
|
|
24
|
+
var wikilinkRegex = /^\[\[[^[\]]+\]\]$/;
|
|
25
|
+
var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
|
|
26
|
+
var spellcastingSchema = z.object({
|
|
27
|
+
caster_type: casterTypeEnum.optional(),
|
|
28
|
+
ability: abilityEnum,
|
|
29
|
+
preparation: z.enum(["known", "prepared"]).optional(),
|
|
30
|
+
spell_list: z.string().min(1).optional()
|
|
31
|
+
});
|
|
32
|
+
var subclassTableRowSchema = z.object({
|
|
33
|
+
columns: z.record(z.string(), z.union([z.string(), z.number()])).optional()
|
|
34
|
+
});
|
|
35
|
+
var subclassEntitySchema = z.object({
|
|
36
|
+
slug: z.string().min(1),
|
|
37
|
+
name: z.string().min(1),
|
|
38
|
+
parent_class: z.string().regex(wikilinkRegex, "parent_class must be a wikilink like [[rogue]]"),
|
|
39
|
+
edition: editionEnum,
|
|
40
|
+
source: z.string().min(1),
|
|
41
|
+
description: z.string(),
|
|
42
|
+
spellcasting: spellcastingSchema.nullable().optional(),
|
|
43
|
+
table: z.record(z.string(), subclassTableRowSchema).optional(),
|
|
44
|
+
features_by_level: z.record(z.string(), z.array(featureSchema)),
|
|
45
|
+
resources: z.array(resourceSchema),
|
|
46
|
+
selection_pools: z.array(selectionPoolSchema).optional(),
|
|
47
|
+
pool_grants: z.array(poolGrantSchema).optional(),
|
|
48
|
+
tabs: z.array(tabDeclSchema).optional(),
|
|
49
|
+
rendering_hint: z.string().optional(),
|
|
50
|
+
// 346
|
|
51
|
+
short_name: z.string().min(1).optional(),
|
|
52
|
+
// 346
|
|
53
|
+
has_fluff: z.boolean().optional(),
|
|
54
|
+
// 50
|
|
55
|
+
has_fluff_images: z.boolean().optional(),
|
|
56
|
+
// 346
|
|
57
|
+
fluff: z.unknown().optional(),
|
|
58
|
+
// 62 docs, single key-set {_subclassFluff:{...}} — 5etools-internal
|
|
59
|
+
table_col_labels: z.array(z.string().min(1)).optional(),
|
|
60
|
+
// 8
|
|
61
|
+
cantrip_progression: z.array(z.number()).optional(),
|
|
62
|
+
// 6
|
|
63
|
+
prepared_spells_change: z.string().min(1).optional(),
|
|
64
|
+
// 2
|
|
65
|
+
prepared_spells_progression: z.array(z.number()).optional(),
|
|
66
|
+
// 2
|
|
67
|
+
spells_known_progression: z.array(z.number()).optional(),
|
|
68
|
+
// 4
|
|
69
|
+
feat_progression: z.array(progressionSchema).optional(),
|
|
70
|
+
// 1
|
|
71
|
+
optionalfeature_progression: z.array(progressionSchema).optional(),
|
|
72
|
+
// 13 carriers; featureType
|
|
73
|
+
// on all 13, required on 2
|
|
74
|
+
additional_spells: z.array(additionalSpellsEntrySchema).optional(),
|
|
75
|
+
// 212
|
|
76
|
+
image: imageField
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
subclassEntitySchema
|
|
81
|
+
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
subclassEntitySchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-H7KBLR4X.js";
|
|
4
|
+
import {
|
|
5
|
+
aliasFeaturesByLevelActionCost
|
|
6
|
+
} from "./chunk-4U55VNC7.js";
|
|
4
7
|
|
|
5
8
|
// src/subclass/subclass.parser.ts
|
|
6
9
|
import { parseYaml } from "@archivist-gg/core";
|
|
7
10
|
function parseSubclass(source) {
|
|
8
11
|
const raw = parseYaml(source, ["name", "slug", "parent_class"]);
|
|
9
12
|
if (!raw.success) return raw;
|
|
13
|
+
aliasFeaturesByLevelActionCost(raw.data.features_by_level);
|
|
10
14
|
const result = subclassEntitySchema.safeParse({ edition: "2014", ...raw.data });
|
|
11
15
|
if (!result.success) {
|
|
12
16
|
return { success: false, error: `subclass schema validation failed: ${result.error.message}` };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
weaponEntitySchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2DFENQIJ.js";
|
|
4
4
|
|
|
5
5
|
// src/weapon/weapon.parser.ts
|
|
6
6
|
import { parseYaml } from "@archivist-gg/core";
|
|
@@ -21,7 +21,14 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
21
21
|
"page",
|
|
22
22
|
"edition",
|
|
23
23
|
"entries",
|
|
24
|
-
"raw"
|
|
24
|
+
"raw",
|
|
25
|
+
// Root extras declared on weaponEntitySchema (spec §5). The schema declaring them is NOT
|
|
26
|
+
// enough: this set is the second, independent gate — a key missing here is copied into
|
|
27
|
+
// `raw` as well, so the two lists must move together.
|
|
28
|
+
"rendering_hint",
|
|
29
|
+
"has_fluff",
|
|
30
|
+
"has_fluff_images",
|
|
31
|
+
"image"
|
|
25
32
|
]);
|
|
26
33
|
var FLAG_PROPERTIES = /* @__PURE__ */ new Set([
|
|
27
34
|
"finesse",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONDITION_SLUGS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-S7REMWW4.js";
|
|
4
4
|
|
|
5
5
|
// src/pc/pc.schema.ts
|
|
6
6
|
import { z } from "zod";
|
|
@@ -79,7 +79,7 @@ var equipmentEntrySchema = z.object({
|
|
|
79
79
|
// finished file never carries it.
|
|
80
80
|
granted_by: z.string().optional()
|
|
81
81
|
});
|
|
82
|
-
var
|
|
82
|
+
var characterOverridesShape = z.object({
|
|
83
83
|
scores: z.partialRecord(abilityEnum, z.number().int()).optional(),
|
|
84
84
|
saves: z.partialRecord(abilityEnum, z.object({
|
|
85
85
|
bonus: z.number().int().optional(),
|
|
@@ -107,8 +107,54 @@ var characterOverridesSchema = z.object({
|
|
|
107
107
|
// Scroll cast by a non-caster). Same ability enum as the per-instance spell_ability.
|
|
108
108
|
spellcasting_ability: abilityEnum.optional(),
|
|
109
109
|
spell_slots: z.record(z.coerce.number().int(), z.number().int().nonnegative()).optional(),
|
|
110
|
-
attunement_limit: z.number().int().nonnegative().optional()
|
|
111
|
-
|
|
110
|
+
attunement_limit: z.number().int().nonnegative().optional(),
|
|
111
|
+
/** Manual proficiency edits. `remove` SUPPRESSES a rules-granted entry ("a dwarf who doesn't know
|
|
112
|
+
* dwarvish"). NOTHING here may carry a default, at either level, or an untouched note stops being
|
|
113
|
+
* byte-identical:
|
|
114
|
+
* - a `.default({})` on `languages`/`tools` materializes the key on every note that carries any
|
|
115
|
+
* `overrides` block at all;
|
|
116
|
+
* - a `.default([])` on `add`/`remove` materializes the sibling array whenever the parent key IS
|
|
117
|
+
* present, which is every add-only entry the mutators write. (It cannot fire while the parent
|
|
118
|
+
* is absent, but that is the uninteresting half of the case.)
|
|
119
|
+
* That rule covers `tools.proficiency` too: no default at any of its three levels.
|
|
120
|
+
* Every reader uses `?? []` (`?? {}` for the record). */
|
|
121
|
+
languages: z.object({ add: z.array(z.string()).optional(), remove: z.array(z.string()).optional() }).optional(),
|
|
122
|
+
tools: z.object({
|
|
123
|
+
add: z.array(z.string()).optional(),
|
|
124
|
+
remove: z.array(z.string()).optional(),
|
|
125
|
+
/** R4-G4 §9.3 (UR1): a per-tool tri. An OPEN key space (tool names are not an enum, unlike
|
|
126
|
+
* `skillEnum` above); no `bonus` sibling, because tools have no numeric consumer. `expertise`
|
|
127
|
+
* beats a data grant, `none` suppresses one, `proficient` clears a data expertise · applied in
|
|
128
|
+
* `computeEffectiveProficiencies` (pc.decision-engine.ts). A key a WRITER CREATES is
|
|
129
|
+
* `toProfSlug` output with the apostrophe RETAINED ("thieves'-tools"), matching the engine's
|
|
130
|
+
* own tool vocabulary in types/choice.ts. The READER is wider: `computeEffectiveProficiencies`
|
|
131
|
+
* normalises each key with `toProfSlug` on read, so a hand-typed "Thieves' Tools" also
|
|
132
|
+
* matches; and the plugin's two writers (`CharacterEditState.setToolProficiency` and the
|
|
133
|
+
* `addProficiency` clearance) match an existing key by slug too, so a key the note ALREADY
|
|
134
|
+
* carries is written THROUGH in its own spelling rather than duplicated · nothing here
|
|
135
|
+
* re-spells it (review M-4: "repaired in place" read as "normalised", which it is not).
|
|
136
|
+
* `languages` gets no such sibling: languages have no tri (§9.3). */
|
|
137
|
+
proficiency: z.record(z.string(), proficiencyTri).optional()
|
|
138
|
+
}).optional(),
|
|
139
|
+
/** Manual defense edits. SUPPRESSION-ONLY, and the missing `add` channel is a DELIBERATE
|
|
140
|
+
* asymmetry with `languages`/`tools` above · do not "complete the pattern". The additive
|
|
141
|
+
* store is `character.defenses.*`, which already exists and works; a second additive
|
|
142
|
+
* channel would recreate exactly the two-writers divergence R4-P5 exists to remove.
|
|
143
|
+
* The schema persists what the note wrote, VERBATIM · it neither normalizes nor validates
|
|
144
|
+
* the strings. As of this commit the store has NO consumer at all: Task 5 writes the first,
|
|
145
|
+
* and the intent it is to implement is that entries be matched with `toDefenseSlug`
|
|
146
|
+
* (pc.defense-normalize.ts), so a note may author any spelling.
|
|
147
|
+
* The no-defaults rule of the block above applies here at all three levels: a default on
|
|
148
|
+
* `defenses`, on a bucket, or on `remove` rewrites an untouched note. Readers should
|
|
149
|
+
* therefore treat every level as possibly absent (`?? []`). */
|
|
150
|
+
defenses: z.object({
|
|
151
|
+
resistances: z.object({ remove: z.array(z.string()).optional() }).optional(),
|
|
152
|
+
immunities: z.object({ remove: z.array(z.string()).optional() }).optional(),
|
|
153
|
+
vulnerabilities: z.object({ remove: z.array(z.string()).optional() }).optional(),
|
|
154
|
+
condition_immunities: z.object({ remove: z.array(z.string()).optional() }).optional()
|
|
155
|
+
}).optional()
|
|
156
|
+
});
|
|
157
|
+
var characterOverridesSchema = characterOverridesShape.default({});
|
|
112
158
|
var characterStateSchema = z.object({
|
|
113
159
|
hp: z.object({
|
|
114
160
|
current: z.number().int(),
|
|
@@ -249,5 +295,6 @@ var characterSchema = z.object({
|
|
|
249
295
|
});
|
|
250
296
|
|
|
251
297
|
export {
|
|
298
|
+
characterOverridesShape,
|
|
252
299
|
characterSchema
|
|
253
300
|
};
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
// src/schemas/equipment-grant-schema.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
var equipmentGrantSchema = z.union([
|
|
4
|
-
z.object({
|
|
5
|
-
|
|
4
|
+
z.object({
|
|
5
|
+
item: z.string().min(1),
|
|
6
|
+
qty: z.number().int().positive().optional(),
|
|
7
|
+
contains_value: z.number().int().nonnegative().optional(),
|
|
8
|
+
display_name: z.string().min(1).optional(),
|
|
9
|
+
worth_value: z.number().int().nonnegative().optional()
|
|
10
|
+
}).strict(),
|
|
11
|
+
z.object({ category: z.string().min(1), qty: z.number().int().positive().optional(), display_name: z.string().min(1).optional() }).strict(),
|
|
6
12
|
z.object({ gold: z.number().int().positive() }).strict()
|
|
7
13
|
]);
|
|
8
14
|
var equipmentOptionSchema = z.object({
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/pc/pc.table-column.ts
|
|
2
|
+
function readTableColumn(table, level, keys) {
|
|
3
|
+
var _a;
|
|
4
|
+
const cols = (_a = table == null ? void 0 : table[level]) == null ? void 0 : _a.columns;
|
|
5
|
+
if (!cols) return null;
|
|
6
|
+
for (const k of keys) {
|
|
7
|
+
const raw = cols[k];
|
|
8
|
+
if (raw === void 0 || raw === null) continue;
|
|
9
|
+
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
10
|
+
if (!Number.isNaN(n)) return n;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function numericColumnsAt(table, level) {
|
|
15
|
+
var _a;
|
|
16
|
+
const out = {};
|
|
17
|
+
const cols = (_a = table == null ? void 0 : table[level]) == null ? void 0 : _a.columns;
|
|
18
|
+
if (!cols) return out;
|
|
19
|
+
for (const [k, raw] of Object.entries(cols)) {
|
|
20
|
+
if (raw === void 0 || raw === null) continue;
|
|
21
|
+
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
22
|
+
if (!Number.isNaN(n)) out[k] = n;
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
var COUNT_COLUMNS = {
|
|
27
|
+
"weapon-mastery": ["Weapon Mastery"]
|
|
28
|
+
};
|
|
29
|
+
function countColumnsFor(choiceId) {
|
|
30
|
+
if (!Object.prototype.hasOwnProperty.call(COUNT_COLUMNS, choiceId)) return void 0;
|
|
31
|
+
return COUNT_COLUMNS[choiceId];
|
|
32
|
+
}
|
|
33
|
+
function diceColumnAt(table, level, key) {
|
|
34
|
+
var _a;
|
|
35
|
+
const cols = (_a = table == null ? void 0 : table[level]) == null ? void 0 : _a.columns;
|
|
36
|
+
if (!cols || !Object.prototype.hasOwnProperty.call(cols, key)) return null;
|
|
37
|
+
const raw = cols[key];
|
|
38
|
+
if (typeof raw !== "string") return null;
|
|
39
|
+
const cell = raw.trim();
|
|
40
|
+
return /^\d+d\d+$/i.test(cell) ? cell : null;
|
|
41
|
+
}
|
|
42
|
+
var UNARMED_DIE_COLUMNS = {
|
|
43
|
+
"martial-arts": "Martial Arts"
|
|
44
|
+
};
|
|
45
|
+
function unarmedDieColumnFor(featureSlug) {
|
|
46
|
+
if (!Object.prototype.hasOwnProperty.call(UNARMED_DIE_COLUMNS, featureSlug)) return void 0;
|
|
47
|
+
return UNARMED_DIE_COLUMNS[featureSlug];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
readTableColumn,
|
|
52
|
+
numericColumnsAt,
|
|
53
|
+
COUNT_COLUMNS,
|
|
54
|
+
countColumnsFor,
|
|
55
|
+
diceColumnAt,
|
|
56
|
+
UNARMED_DIE_COLUMNS,
|
|
57
|
+
unarmedDieColumnFor
|
|
58
|
+
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
imageField
|
|
3
|
+
} from "./chunk-UA5KS2PL.js";
|
|
4
|
+
|
|
1
5
|
// src/armor/armor.schema.ts
|
|
2
6
|
import { z } from "zod";
|
|
3
7
|
var acSchema = z.object({
|
|
@@ -23,7 +27,22 @@ var armorEntitySchema = z.object({
|
|
|
23
27
|
page: z.number().int().optional(),
|
|
24
28
|
edition: z.string().optional(),
|
|
25
29
|
entries: z.array(z.unknown()).optional(),
|
|
30
|
+
// Root extras the converter and the SRD bundle emit on more than one root (spec §5).
|
|
31
|
+
// Carrier counts measured on the G1b close census. `rendering_hint` is ALWAYS the empty
|
|
32
|
+
// string today, so `.min(1)` is forbidden here.
|
|
33
|
+
rendering_hint: z.string().optional(),
|
|
34
|
+
// 27
|
|
35
|
+
has_fluff: z.boolean().optional(),
|
|
36
|
+
// 0 carriers — symmetry with weapon
|
|
37
|
+
has_fluff_images: z.boolean().optional(),
|
|
38
|
+
// 13
|
|
39
|
+
// Already `.optional()` at its source — never append a second one.
|
|
40
|
+
image: imageField,
|
|
41
|
+
// 0 today (images-OFF tree)
|
|
26
42
|
raw: z.record(z.string(), z.unknown()).optional()
|
|
43
|
+
// `.loose()` is load-bearing for the `strength_required` alias (armor.parser.ts): the alias
|
|
44
|
+
// rides through onto the typed output as an inert extra, which is what keeps its census
|
|
45
|
+
// disposal `kept` instead of minting a `stripped` row. See the parser's comment.
|
|
27
46
|
}).loose();
|
|
28
47
|
|
|
29
48
|
export {
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeRollScope
|
|
3
|
+
} from "./chunk-TOM66P2P.js";
|
|
4
|
+
import {
|
|
5
|
+
toProfSlug
|
|
6
|
+
} from "./chunk-RLGQ5PZZ.js";
|
|
7
|
+
import {
|
|
8
|
+
warnOnce
|
|
9
|
+
} from "./chunk-LVQLWRCU.js";
|
|
10
|
+
import {
|
|
11
|
+
bareEntitySlug
|
|
12
|
+
} from "./chunk-6X4KUDMU.js";
|
|
13
|
+
import {
|
|
14
|
+
normalizeAbility
|
|
15
|
+
} from "./chunk-WELIKLZF.js";
|
|
16
|
+
|
|
17
|
+
// src/pc/pc.feature-effects.ts
|
|
18
|
+
function emptyFeatureEffectTotals() {
|
|
19
|
+
return {
|
|
20
|
+
initiative_bonus: 0,
|
|
21
|
+
hp_per_level_bonus: 0,
|
|
22
|
+
hp_per_level_terms: [],
|
|
23
|
+
speed_walk_bonus: 0,
|
|
24
|
+
speed_walk_set: 0,
|
|
25
|
+
ability_bonus: {},
|
|
26
|
+
senses: { darkvision: 0, blindsight: 0, tremorsense: 0, truesight: 0 },
|
|
27
|
+
ac_terms: [],
|
|
28
|
+
resistances: [],
|
|
29
|
+
immunities: [],
|
|
30
|
+
vulnerabilities: [],
|
|
31
|
+
condition_immunities: [],
|
|
32
|
+
proficiencies: { skills: [], skillExpertise: [], tools: [], languages: [], saves: [], armor: [], weapons: [] },
|
|
33
|
+
weaponAbilities: [],
|
|
34
|
+
rollModifiers: [],
|
|
35
|
+
saveOutcomes: [],
|
|
36
|
+
critRange: 20,
|
|
37
|
+
extraAttack: 0,
|
|
38
|
+
attackNotes: [],
|
|
39
|
+
damageBonuses: []
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function pushUnique(list, value) {
|
|
43
|
+
const key = value.trim().toLowerCase();
|
|
44
|
+
if (!key) return;
|
|
45
|
+
if (!list.some((v) => v.trim().toLowerCase() === key)) list.push(value.trim());
|
|
46
|
+
}
|
|
47
|
+
function pushDefenseGrant(list, value, source, condition) {
|
|
48
|
+
const key = value.trim().toLowerCase();
|
|
49
|
+
if (!key) return;
|
|
50
|
+
const existing = list.find((g) => g.value.trim().toLowerCase() === key);
|
|
51
|
+
if (existing) {
|
|
52
|
+
if (!existing.sources.includes(source)) existing.sources.push(source);
|
|
53
|
+
if (!existing.condition && condition) existing.condition = condition;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
list.push({ value: value.trim(), sources: [source], ...condition ? { condition } : {} });
|
|
57
|
+
}
|
|
58
|
+
function foldsNow(rf, activeBuffs) {
|
|
59
|
+
if (rf.feature.activatable !== true) return true;
|
|
60
|
+
const id = rf.feature.id;
|
|
61
|
+
return !!id && activeBuffs.has(id);
|
|
62
|
+
}
|
|
63
|
+
function assembleEffectFeatures(resolved) {
|
|
64
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
65
|
+
const activeBuffs = new Set((_b = (_a = resolved.state) == null ? void 0 : _a.active_buffs) != null ? _b : []);
|
|
66
|
+
const featureIds = new Set(((_c = resolved.features) != null ? _c : []).map((f) => f.feature.id).filter((id) => !!id));
|
|
67
|
+
const aliases = [];
|
|
68
|
+
for (const pool of (_d = resolved.pools) != null ? _d : []) {
|
|
69
|
+
for (const entry of [...(_e = pool.selected) != null ? _e : [], ...(_f = pool.grants) != null ? _f : []]) {
|
|
70
|
+
if (activeBuffs.has(entry.slug)) continue;
|
|
71
|
+
const bare = bareEntitySlug(entry.slug);
|
|
72
|
+
for (const stored of activeBuffs) {
|
|
73
|
+
if (featureIds.has(stored)) continue;
|
|
74
|
+
if (bareEntitySlug(stored) === bare) {
|
|
75
|
+
aliases.push(entry.slug);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const a of aliases) activeBuffs.add(a);
|
|
82
|
+
const buffFeatures = [];
|
|
83
|
+
const pushBoon = (item, pool) => {
|
|
84
|
+
var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
|
|
85
|
+
const e = item.entity;
|
|
86
|
+
if (!e || ((_b2 = (_a2 = e.effects) == null ? void 0 : _a2.length) != null ? _b2 : 0) === 0) return;
|
|
87
|
+
buffFeatures.push({
|
|
88
|
+
feature: { id: item.slug, name: e.name, activatable: (_c2 = e.activatable) != null ? _c2 : false, effects: e.effects },
|
|
89
|
+
// source is inert for the fold; attribute to the pool's owning class
|
|
90
|
+
// (never a hardcoded class) so the generic engine carries no homebrew name.
|
|
91
|
+
source: { kind: "class", slug: (_g2 = (_f2 = (_e2 = (_d2 = resolved.classes) == null ? void 0 : _d2[pool.classIndex]) == null ? void 0 : _e2.entity) == null ? void 0 : _f2.slug) != null ? _g2 : pool.id, level: pool.anchorLevel }
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
for (const pool of (_g = resolved.pools) != null ? _g : []) {
|
|
95
|
+
for (const sel of (_h = pool.selected) != null ? _h : []) pushBoon(sel, pool);
|
|
96
|
+
for (const g of (_i = pool.grants) != null ? _i : []) pushBoon(g, pool);
|
|
97
|
+
}
|
|
98
|
+
return { features: [...(_j = resolved.features) != null ? _j : [], ...buffFeatures], activeBuffs };
|
|
99
|
+
}
|
|
100
|
+
function foldsOnSelf(eff) {
|
|
101
|
+
if (eff.subject === void 0 || eff.subject === "self") return true;
|
|
102
|
+
warnOnce(eff.subject, `archivist: unrecognised effect subject "${eff.subject}" (only "self" folds)`);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
function selfEffectsOf(f) {
|
|
106
|
+
var _a;
|
|
107
|
+
return ((_a = f.effects) != null ? _a : []).filter(foldsOnSelf);
|
|
108
|
+
}
|
|
109
|
+
function computeFeatureEffects(features, opts) {
|
|
110
|
+
var _a, _b, _c;
|
|
111
|
+
const out = emptyFeatureEffectTotals();
|
|
112
|
+
for (const rf of features) {
|
|
113
|
+
if (!foldsNow(rf, (_a = opts == null ? void 0 : opts.activeBuffs) != null ? _a : /* @__PURE__ */ new Set())) continue;
|
|
114
|
+
const level = (_b = opts == null ? void 0 : opts.levelFor) == null ? void 0 : _b.call(opts, rf.source);
|
|
115
|
+
for (const eff of selfEffectsOf(rf.feature)) {
|
|
116
|
+
applyEffect(out, eff, (_c = rf.feature.name) != null ? _c : "Feature", level);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
function classifyProficiencyEffect(eff) {
|
|
122
|
+
if (eff.kind !== "proficiency") return null;
|
|
123
|
+
const raw = eff.value;
|
|
124
|
+
const expertise = eff.expertise === true;
|
|
125
|
+
switch (eff.proficiency_type) {
|
|
126
|
+
case "skill":
|
|
127
|
+
return { bucket: "skills", value: toProfSlug(raw), raw, expertise };
|
|
128
|
+
case "tool":
|
|
129
|
+
return { bucket: "tools", value: toProfSlug(raw), raw, expertise };
|
|
130
|
+
case "language":
|
|
131
|
+
return { bucket: "languages", value: toProfSlug(raw), raw, expertise };
|
|
132
|
+
case "armor":
|
|
133
|
+
return { bucket: "armor", value: raw.toLowerCase(), raw, expertise };
|
|
134
|
+
case "weapon":
|
|
135
|
+
return { bucket: "weapons", value: raw.toLowerCase(), raw, expertise };
|
|
136
|
+
default: {
|
|
137
|
+
const ab = normalizeAbility(raw);
|
|
138
|
+
return ab ? { bucket: "saves", value: ab, raw, expertise } : null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function collectProficiencyEffectGrants(features, activeBuffs) {
|
|
143
|
+
const out = {
|
|
144
|
+
skills: [],
|
|
145
|
+
tools: [],
|
|
146
|
+
languages: [],
|
|
147
|
+
saves: [],
|
|
148
|
+
armor: [],
|
|
149
|
+
weapons: []
|
|
150
|
+
};
|
|
151
|
+
for (const rf of features) {
|
|
152
|
+
if (!foldsNow(rf, activeBuffs)) continue;
|
|
153
|
+
for (const eff of selfEffectsOf(rf.feature)) {
|
|
154
|
+
const c = classifyProficiencyEffect(eff);
|
|
155
|
+
if (!c) continue;
|
|
156
|
+
out[c.bucket].push({
|
|
157
|
+
value: c.value,
|
|
158
|
+
raw: c.raw,
|
|
159
|
+
sourceKind: rf.source.kind,
|
|
160
|
+
sourceSlug: rf.source.slug,
|
|
161
|
+
...c.expertise ? { expertise: true } : {}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
function scaled(base, key, scalesAt, level) {
|
|
168
|
+
if (level === void 0 || !(scalesAt == null ? void 0 : scalesAt.length)) return base;
|
|
169
|
+
let best = base;
|
|
170
|
+
let bestLevel = -1;
|
|
171
|
+
for (const step of scalesAt) {
|
|
172
|
+
if (step.level > level || step.level <= bestLevel) continue;
|
|
173
|
+
const v = step[key];
|
|
174
|
+
if (typeof v !== "number") continue;
|
|
175
|
+
best = v;
|
|
176
|
+
bestLevel = step.level;
|
|
177
|
+
}
|
|
178
|
+
return best;
|
|
179
|
+
}
|
|
180
|
+
function applyEffect(out, eff, label, level) {
|
|
181
|
+
var _a, _b, _c, _d, _e;
|
|
182
|
+
switch (eff.kind) {
|
|
183
|
+
case "initiative-bonus":
|
|
184
|
+
out.initiative_bonus += eff.value;
|
|
185
|
+
break;
|
|
186
|
+
case "hp-per-level-bonus":
|
|
187
|
+
out.hp_per_level_bonus += eff.value;
|
|
188
|
+
out.hp_per_level_terms.push({ label, value: eff.value });
|
|
189
|
+
break;
|
|
190
|
+
case "speed-bonus": {
|
|
191
|
+
const value = scaled(eff.value, "value", eff.scales_at, level);
|
|
192
|
+
if (eff.mode === "walk") {
|
|
193
|
+
if (eff.set) out.speed_walk_set = Math.max(out.speed_walk_set, value);
|
|
194
|
+
else out.speed_walk_bonus += value;
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case "sense":
|
|
199
|
+
out.senses[eff.type] = Math.max(out.senses[eff.type], eff.range);
|
|
200
|
+
break;
|
|
201
|
+
case "resistance":
|
|
202
|
+
pushDefenseGrant(out.resistances, eff.damage_type, label, eff.condition);
|
|
203
|
+
break;
|
|
204
|
+
case "immunity":
|
|
205
|
+
pushDefenseGrant(out.immunities, eff.damage_type, label, eff.condition);
|
|
206
|
+
break;
|
|
207
|
+
case "vulnerability":
|
|
208
|
+
pushDefenseGrant(out.vulnerabilities, eff.damage_type, label, eff.condition);
|
|
209
|
+
break;
|
|
210
|
+
case "immune-condition":
|
|
211
|
+
if (!eff.while) pushDefenseGrant(out.condition_immunities, eff.condition, label);
|
|
212
|
+
break;
|
|
213
|
+
case "proficiency": {
|
|
214
|
+
const c = classifyProficiencyEffect(eff);
|
|
215
|
+
if (!c) break;
|
|
216
|
+
if (c.bucket === "saves") {
|
|
217
|
+
pushUnique(out.proficiencies.saves, c.value);
|
|
218
|
+
} else {
|
|
219
|
+
pushUnique(out.proficiencies[c.bucket], c.value);
|
|
220
|
+
}
|
|
221
|
+
if (c.expertise && c.bucket === "skills") pushUnique(out.proficiencies.skillExpertise, c.value);
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case "ac-bonus":
|
|
225
|
+
out.ac_terms.push({
|
|
226
|
+
value: eff.value,
|
|
227
|
+
requires_armor: eff.requires_armor === true,
|
|
228
|
+
label,
|
|
229
|
+
condition: eff.condition
|
|
230
|
+
});
|
|
231
|
+
break;
|
|
232
|
+
case "weapon-ability": {
|
|
233
|
+
if (eff.ability !== "spellcasting") {
|
|
234
|
+
const w = eff.weapons;
|
|
235
|
+
const weaponSlugs = !w || w === "chosen" ? void 0 : Array.isArray(w) ? w.map(bareEntitySlug) : [bareEntitySlug(w)];
|
|
236
|
+
out.weaponAbilities.push({ ability: eff.ability, weaponSlugs });
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case "roll-modifier": {
|
|
241
|
+
const rolls = eff.roll === "any" ? ["ability-check", "saving-throw", "attack"] : [eff.roll];
|
|
242
|
+
const scopes = (_a = normalizeRollScope(eff.scope, rolls[0])) != null ? _a : [eff.scope];
|
|
243
|
+
for (const roll of rolls) for (const scope of scopes)
|
|
244
|
+
out.rollModifiers.push({ mode: eff.mode, roll, scope, condition: eff.condition, label });
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case "save-outcome":
|
|
248
|
+
out.saveOutcomes.push({
|
|
249
|
+
ability: eff.ability === "any" ? void 0 : eff.ability,
|
|
250
|
+
on_success: eff.on_success,
|
|
251
|
+
on_failure: eff.on_failure,
|
|
252
|
+
appliesTo: (_b = eff.applies_to) != null ? _b : void 0,
|
|
253
|
+
condition: eff.condition,
|
|
254
|
+
label
|
|
255
|
+
});
|
|
256
|
+
break;
|
|
257
|
+
case "crit-range":
|
|
258
|
+
if (((_c = eff.applies_to) != null ? _c : "weapon") !== "spell") out.critRange = Math.min(out.critRange, eff.min_roll);
|
|
259
|
+
break;
|
|
260
|
+
case "extra-attack":
|
|
261
|
+
out.extraAttack = Math.max(out.extraAttack, scaled(eff.count, "count", eff.scales_at, level));
|
|
262
|
+
break;
|
|
263
|
+
case "reroll-damage":
|
|
264
|
+
out.attackNotes.push(`Reroll ${eff.max_reroll}s${eff.once_per_die ? " (once/die)" : ""}`);
|
|
265
|
+
break;
|
|
266
|
+
case "attack-rule":
|
|
267
|
+
if (eff.flag === "no-ranged-in-melee-disadvantage") out.attackNotes.push("No disadvantage firing in melee");
|
|
268
|
+
break;
|
|
269
|
+
case "damage-bonus":
|
|
270
|
+
if (((_d = eff.applies_to) != null ? _d : "weapon") !== "spell") {
|
|
271
|
+
out.damageBonuses.push({
|
|
272
|
+
amount: eff.amount,
|
|
273
|
+
damage_type: eff.damage_type,
|
|
274
|
+
source: label,
|
|
275
|
+
...eff.condition ? { condition: eff.condition } : {}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
break;
|
|
279
|
+
case "ability-score-increase":
|
|
280
|
+
if (eff.abilities === "chosen") break;
|
|
281
|
+
for (const ab of eff.abilities) out.ability_bonus[ab] = ((_e = out.ability_bonus[ab]) != null ? _e : 0) + eff.amount;
|
|
282
|
+
break;
|
|
283
|
+
default:
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export {
|
|
289
|
+
emptyFeatureEffectTotals,
|
|
290
|
+
foldsNow,
|
|
291
|
+
assembleEffectFeatures,
|
|
292
|
+
foldsOnSelf,
|
|
293
|
+
selfEffectsOf,
|
|
294
|
+
computeFeatureEffects,
|
|
295
|
+
classifyProficiencyEffect,
|
|
296
|
+
collectProficiencyEffectGrants
|
|
297
|
+
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startingEquipmentEntrySchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IN4M4X7P.js";
|
|
4
4
|
import {
|
|
5
5
|
choiceSchema
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-T26ZVCL5.js";
|
|
7
|
+
import {
|
|
8
|
+
additionalSpellsEntrySchema,
|
|
9
|
+
imageField
|
|
10
|
+
} from "./chunk-UA5KS2PL.js";
|
|
7
11
|
|
|
8
12
|
// src/background/background.schema.ts
|
|
9
13
|
import { z } from "zod";
|
|
@@ -75,7 +79,29 @@ var backgroundEntitySchema = z.object({
|
|
|
75
79
|
}).nullable(),
|
|
76
80
|
origin_feat: z.string().regex(wikilinkRegex).nullable(),
|
|
77
81
|
suggested_characteristics: suggestedCharSchema.nullable(),
|
|
78
|
-
choices: z.array(choiceSchema).optional()
|
|
82
|
+
choices: z.array(choiceSchema).optional(),
|
|
83
|
+
rendering_hint: z.string().optional(),
|
|
84
|
+
// 175
|
|
85
|
+
has_fluff_images: z.boolean().optional(),
|
|
86
|
+
// 87
|
|
87
|
+
tables: z.array(z.object({
|
|
88
|
+
// 88 tables / 67 docs; key-sets EXACT (finding 6)
|
|
89
|
+
name: z.string().min(1),
|
|
90
|
+
dice: z.string().min(1),
|
|
91
|
+
rows: z.array(z.object({ roll: z.string(), text: z.string() }))
|
|
92
|
+
})).optional(),
|
|
93
|
+
additional_spells: z.array(additionalSpellsEntrySchema).optional(),
|
|
94
|
+
// 15
|
|
95
|
+
prerequisites: z.array(z.object({
|
|
96
|
+
// 4 docs; kind CLOSED AT THE EMITTER
|
|
97
|
+
kind: z.literal("campaign"),
|
|
98
|
+
slug: z.string().min(1)
|
|
99
|
+
// (background-mapper.ts pushes
|
|
100
|
+
})).optional(),
|
|
101
|
+
// {kind:"campaign", slug} literally — finding 24)
|
|
102
|
+
// The one-arm union STAYS (§2.6 adjudication): it is closed by CONSTRUCTION at the converter's
|
|
103
|
+
// only push site, and a future kind turns into a visible census REFUSAL — the correct loud failure.
|
|
104
|
+
image: imageField
|
|
79
105
|
});
|
|
80
106
|
|
|
81
107
|
export {
|