@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
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readTableColumn
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IUAAXXNO.js";
|
|
4
4
|
import {
|
|
5
|
-
bareSlug
|
|
5
|
+
bareSlug,
|
|
6
|
+
baseClassName
|
|
6
7
|
} from "./chunk-VTTA2ALZ.js";
|
|
7
8
|
import {
|
|
8
9
|
abilityModifier
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-PDJSUH24.js";
|
|
10
11
|
|
|
11
12
|
// src/pc/pc.spellcasting.ts
|
|
13
|
+
function hasPreparedColumn(table) {
|
|
14
|
+
return Object.values(table).some((row) => PREPARED_COLUMNS.some((k) => {
|
|
15
|
+
var _a;
|
|
16
|
+
return ((_a = row == null ? void 0 : row.columns) == null ? void 0 : _a[k]) !== void 0;
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
12
19
|
function resolveSpellcasting(rc) {
|
|
13
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
20
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
|
|
14
21
|
const sub = (_b = (_a = rc.subclass) == null ? void 0 : _a.spellcasting) != null ? _b : null;
|
|
15
22
|
const cls = (_d = (_c = rc.entity) == null ? void 0 : _c.spellcasting) != null ? _d : null;
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
spellList: sc.spell_list,
|
|
24
|
-
table
|
|
25
|
-
};
|
|
23
|
+
const casterType = (_f = (_e = sub == null ? void 0 : sub.caster_type) != null ? _e : cls == null ? void 0 : cls.caster_type) != null ? _f : null;
|
|
24
|
+
const ability = (_h = (_g = sub == null ? void 0 : sub.ability) != null ? _g : cls == null ? void 0 : cls.ability) != null ? _h : null;
|
|
25
|
+
if (!casterType || !ability) return null;
|
|
26
|
+
const table = (_l = (_k = (sub == null ? void 0 : sub.caster_type) ? (_i = rc.subclass) == null ? void 0 : _i.table : void 0) != null ? _k : (_j = rc.entity) == null ? void 0 : _j.table) != null ? _l : {};
|
|
27
|
+
const preparation = (_n = (_m = sub == null ? void 0 : sub.preparation) != null ? _m : cls == null ? void 0 : cls.preparation) != null ? _n : hasPreparedColumn(table) ? "prepared" : "known";
|
|
28
|
+
const spellList = (_p = (_o = sub == null ? void 0 : sub.spell_list) != null ? _o : cls == null ? void 0 : cls.spell_list) != null ? _p : null;
|
|
29
|
+
return { ability, casterType, preparation, spellList, table };
|
|
26
30
|
}
|
|
27
31
|
function effectiveSpellcastingAbility(classSlug, dataAbility, overrides) {
|
|
28
32
|
var _a, _b;
|
|
@@ -41,6 +45,43 @@ function normalizeKnownSpell(entry) {
|
|
|
41
45
|
alwaysPrepared: (_b = entry.always_prepared) != null ? _b : false
|
|
42
46
|
};
|
|
43
47
|
}
|
|
48
|
+
function firstListedCasterClass(spellClasses, casterClassSlugs) {
|
|
49
|
+
var _a;
|
|
50
|
+
if (!Array.isArray(spellClasses) || spellClasses.length === 0) return null;
|
|
51
|
+
const listed = new Set(spellClasses.map((c) => baseClassName(String(c))));
|
|
52
|
+
return (_a = casterClassSlugs.find((slug) => listed.has(baseClassName(slug)))) != null ? _a : null;
|
|
53
|
+
}
|
|
54
|
+
var LISTED_CLASS_NAMES = /* @__PURE__ */ new WeakMap();
|
|
55
|
+
var NO_NAMES = /* @__PURE__ */ new Set();
|
|
56
|
+
function listedSpellClassNames(registry, compendium) {
|
|
57
|
+
var _a;
|
|
58
|
+
const count = registry.count();
|
|
59
|
+
let cached = LISTED_CLASS_NAMES.get(registry);
|
|
60
|
+
if (!cached || cached.count !== count) {
|
|
61
|
+
const byCompendium = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const e of registry.search("", "spell", Number.POSITIVE_INFINITY)) {
|
|
63
|
+
const classes = e.data.classes;
|
|
64
|
+
if (!Array.isArray(classes)) continue;
|
|
65
|
+
let names = byCompendium.get(e.compendium);
|
|
66
|
+
if (!names) byCompendium.set(e.compendium, names = /* @__PURE__ */ new Set());
|
|
67
|
+
for (const c of classes) names.add(baseClassName(String(c)));
|
|
68
|
+
}
|
|
69
|
+
cached = { count, byCompendium };
|
|
70
|
+
LISTED_CLASS_NAMES.set(registry, cached);
|
|
71
|
+
}
|
|
72
|
+
return (_a = cached.byCompendium.get(compendium)) != null ? _a : NO_NAMES;
|
|
73
|
+
}
|
|
74
|
+
function attributeUnclassedSpell(spellClasses, casters, registry) {
|
|
75
|
+
var _a;
|
|
76
|
+
const first = casters[0];
|
|
77
|
+
if (!first) return null;
|
|
78
|
+
const listed = firstListedCasterClass(spellClasses, casters.map((c) => c.classSlug));
|
|
79
|
+
if (listed == null || listed === first.classSlug) return first.classSlug;
|
|
80
|
+
if (first.spellList == null) return first.classSlug;
|
|
81
|
+
const compendium = (_a = registry.getByTypeAndSlug("class", first.classSlug)) == null ? void 0 : _a.compendium;
|
|
82
|
+
if (compendium == null || !listedSpellClassNames(registry, compendium).has(baseClassName(first.classSlug))) return first.classSlug;
|
|
83
|
+
return listed;
|
|
84
|
+
}
|
|
44
85
|
var FULL_CASTER_SLOTS = [
|
|
45
86
|
/* 1 */
|
|
46
87
|
[2, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
@@ -209,6 +250,24 @@ var PACT_MAGIC = [
|
|
|
209
250
|
/* 20 */
|
|
210
251
|
{ level: 5, total: 4 }
|
|
211
252
|
];
|
|
253
|
+
var ARTIFICER_CASTER_SLOTS = HALF_CASTER_SLOTS.map((row) => [...row]);
|
|
254
|
+
ARTIFICER_CASTER_SLOTS[0] = [2, 0, 0, 0, 0];
|
|
255
|
+
function slotTableFor(caster) {
|
|
256
|
+
switch (caster) {
|
|
257
|
+
case "full":
|
|
258
|
+
return FULL_CASTER_SLOTS;
|
|
259
|
+
case "half":
|
|
260
|
+
return HALF_CASTER_SLOTS;
|
|
261
|
+
case "third":
|
|
262
|
+
return THIRD_CASTER_SLOTS;
|
|
263
|
+
case "artificer":
|
|
264
|
+
return ARTIFICER_CASTER_SLOTS;
|
|
265
|
+
default: {
|
|
266
|
+
const _n = caster;
|
|
267
|
+
throw new Error(`unknown caster type ${String(_n)}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
212
271
|
function rowToRecord(row) {
|
|
213
272
|
const out = {};
|
|
214
273
|
row.forEach((n, i) => {
|
|
@@ -217,7 +276,7 @@ function rowToRecord(row) {
|
|
|
217
276
|
return out;
|
|
218
277
|
}
|
|
219
278
|
function deriveSpellSlots(classes) {
|
|
220
|
-
let fullLevels = 0, halfLevels = 0, thirdLevels = 0, warlockLevel = 0;
|
|
279
|
+
let fullLevels = 0, halfLevels = 0, thirdLevels = 0, artificerLevels = 0, warlockLevel = 0;
|
|
221
280
|
const regular = [];
|
|
222
281
|
for (const c of classes) {
|
|
223
282
|
if (c.casterType === "pact") {
|
|
@@ -225,28 +284,55 @@ function deriveSpellSlots(classes) {
|
|
|
225
284
|
continue;
|
|
226
285
|
}
|
|
227
286
|
regular.push({ caster: c.casterType, level: c.level });
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
287
|
+
switch (c.casterType) {
|
|
288
|
+
case "full":
|
|
289
|
+
fullLevels += c.level;
|
|
290
|
+
break;
|
|
291
|
+
case "half":
|
|
292
|
+
halfLevels += c.level;
|
|
293
|
+
break;
|
|
294
|
+
case "third":
|
|
295
|
+
thirdLevels += c.level;
|
|
296
|
+
break;
|
|
297
|
+
case "artificer":
|
|
298
|
+
artificerLevels += c.level;
|
|
299
|
+
break;
|
|
300
|
+
default: {
|
|
301
|
+
const _n = c.casterType;
|
|
302
|
+
throw new Error(`unknown caster type ${String(_n)}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
231
305
|
}
|
|
232
306
|
let standard = {};
|
|
233
307
|
if (regular.length === 1) {
|
|
234
308
|
const only = regular[0];
|
|
309
|
+
const table = slotTableFor(only.caster);
|
|
235
310
|
const idx = only.level - 1;
|
|
236
|
-
if (
|
|
237
|
-
if (idx >= 0 && idx < HALF_CASTER_SLOTS.length) standard = rowToRecord(HALF_CASTER_SLOTS[idx]);
|
|
238
|
-
} else if (only.caster === "third") {
|
|
239
|
-
if (idx >= 0 && idx < THIRD_CASTER_SLOTS.length) standard = rowToRecord(THIRD_CASTER_SLOTS[idx]);
|
|
240
|
-
} else {
|
|
241
|
-
if (only.level >= 1 && only.level <= FULL_CASTER_SLOTS.length) standard = rowToRecord(FULL_CASTER_SLOTS[only.level - 1]);
|
|
242
|
-
}
|
|
311
|
+
if (idx >= 0 && idx < table.length) standard = rowToRecord(table[idx]);
|
|
243
312
|
} else if (regular.length >= 2) {
|
|
244
|
-
const cl = fullLevels + Math.floor(halfLevels / 2) + Math.floor(thirdLevels / 3);
|
|
313
|
+
const cl = fullLevels + Math.floor(halfLevels / 2) + Math.floor(thirdLevels / 3) + Math.ceil(artificerLevels / 2);
|
|
245
314
|
if (cl >= 1 && cl <= FULL_CASTER_SLOTS.length) standard = rowToRecord(FULL_CASTER_SLOTS[cl - 1]);
|
|
246
315
|
}
|
|
247
316
|
const pact = warlockLevel >= 1 && warlockLevel <= PACT_MAGIC.length ? PACT_MAGIC[warlockLevel - 1] : null;
|
|
248
317
|
return { standard, pact };
|
|
249
318
|
}
|
|
319
|
+
var PREPARED_COLUMNS = ["Prepared Spells", "Spells Prepared"];
|
|
320
|
+
function preparedLevelTerm(caster, level) {
|
|
321
|
+
switch (caster) {
|
|
322
|
+
case "full":
|
|
323
|
+
case "pact":
|
|
324
|
+
return level;
|
|
325
|
+
case "half":
|
|
326
|
+
case "artificer":
|
|
327
|
+
return Math.floor(level / 2);
|
|
328
|
+
case "third":
|
|
329
|
+
return Math.floor(level / 3);
|
|
330
|
+
default: {
|
|
331
|
+
const _n = caster;
|
|
332
|
+
throw new Error(`unknown caster type ${String(_n)}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
250
336
|
function computeSpellLimits(inputs) {
|
|
251
337
|
const out = [];
|
|
252
338
|
for (const i of inputs) {
|
|
@@ -255,8 +341,8 @@ function computeSpellLimits(inputs) {
|
|
|
255
341
|
const mod = abilityModifier(i.abilityScore);
|
|
256
342
|
let preparedOrKnown;
|
|
257
343
|
if (p.preparation === "prepared") {
|
|
258
|
-
const
|
|
259
|
-
preparedOrKnown = Math.max(1, mod +
|
|
344
|
+
const fromTable = readTableColumn(p.table, i.level, PREPARED_COLUMNS);
|
|
345
|
+
preparedOrKnown = fromTable !== null ? fromTable : Math.max(1, mod + preparedLevelTerm(p.casterType, i.level));
|
|
260
346
|
} else {
|
|
261
347
|
preparedOrKnown = readTableColumn(p.table, i.level, ["Spells Known", "Prepared Spells"]);
|
|
262
348
|
}
|
|
@@ -269,6 +355,11 @@ export {
|
|
|
269
355
|
resolveSpellcasting,
|
|
270
356
|
effectiveSpellcastingAbility,
|
|
271
357
|
normalizeKnownSpell,
|
|
358
|
+
listedSpellClassNames,
|
|
359
|
+
attributeUnclassedSpell,
|
|
360
|
+
slotTableFor,
|
|
272
361
|
deriveSpellSlots,
|
|
362
|
+
PREPARED_COLUMNS,
|
|
363
|
+
preparedLevelTerm,
|
|
273
364
|
computeSpellLimits
|
|
274
365
|
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// src/dnd/resource-formula.ts
|
|
2
|
+
var IDENTS = /* @__PURE__ */ new Set([
|
|
3
|
+
"level",
|
|
4
|
+
"class_level",
|
|
5
|
+
"prof",
|
|
6
|
+
"str_mod",
|
|
7
|
+
"dex_mod",
|
|
8
|
+
"con_mod",
|
|
9
|
+
"int_mod",
|
|
10
|
+
"wis_mod",
|
|
11
|
+
"cha_mod"
|
|
12
|
+
]);
|
|
13
|
+
var AT_WILL_MAX = 999;
|
|
14
|
+
var FUNCS = /* @__PURE__ */ new Set(["ceil", "floor", "max", "min"]);
|
|
15
|
+
function tokenize(input) {
|
|
16
|
+
input = input.trim();
|
|
17
|
+
const tokens = [];
|
|
18
|
+
const re = /\s*('[^']*'|\{[a-z_]+\}|[a-z_]+|\d+|[+\-*/(),])/y;
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < input.length) {
|
|
21
|
+
re.lastIndex = i;
|
|
22
|
+
const m = re.exec(input);
|
|
23
|
+
if (!m || m.index !== i) return null;
|
|
24
|
+
const raw = m[1];
|
|
25
|
+
i = re.lastIndex;
|
|
26
|
+
if (/^\d+$/.test(raw)) tokens.push({ kind: "num", value: parseInt(raw, 10) });
|
|
27
|
+
else if (raw.startsWith("'")) tokens.push({ kind: "str", value: raw.slice(1, -1) });
|
|
28
|
+
else if (raw === "(") tokens.push({ kind: "lparen" });
|
|
29
|
+
else if (raw === ")") tokens.push({ kind: "rparen" });
|
|
30
|
+
else if (raw === ",") tokens.push({ kind: "comma" });
|
|
31
|
+
else if (raw === "+" || raw === "-" || raw === "*" || raw === "/") tokens.push({ kind: "op", op: raw });
|
|
32
|
+
else {
|
|
33
|
+
const braced = raw.startsWith("{");
|
|
34
|
+
const name = braced ? raw.slice(1, -1) : raw;
|
|
35
|
+
if (!braced && FUNCS.has(name)) tokens.push({ kind: "fn", name });
|
|
36
|
+
else if (!braced && name === "column") tokens.push({ kind: "col" });
|
|
37
|
+
else if (IDENTS.has(name)) tokens.push({ kind: "id", name });
|
|
38
|
+
else return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return tokens.length > 0 ? tokens : null;
|
|
42
|
+
}
|
|
43
|
+
function evalTokens(tokens, b) {
|
|
44
|
+
let pos = 0;
|
|
45
|
+
const peek = () => tokens[pos];
|
|
46
|
+
const factor = () => {
|
|
47
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
48
|
+
const t = peek();
|
|
49
|
+
if (!t) return null;
|
|
50
|
+
if (t.kind === "num") {
|
|
51
|
+
pos++;
|
|
52
|
+
return t.value;
|
|
53
|
+
}
|
|
54
|
+
if (t.kind === "id") {
|
|
55
|
+
const val = b[t.name];
|
|
56
|
+
if (val === void 0) return null;
|
|
57
|
+
pos++;
|
|
58
|
+
return val;
|
|
59
|
+
}
|
|
60
|
+
if (t.kind === "fn") {
|
|
61
|
+
pos++;
|
|
62
|
+
if (((_a = peek()) == null ? void 0 : _a.kind) !== "lparen") return null;
|
|
63
|
+
pos++;
|
|
64
|
+
const args = [];
|
|
65
|
+
const first = expr();
|
|
66
|
+
if (first === null) return null;
|
|
67
|
+
args.push(first);
|
|
68
|
+
while (((_b = peek()) == null ? void 0 : _b.kind) === "comma") {
|
|
69
|
+
pos++;
|
|
70
|
+
const next = expr();
|
|
71
|
+
if (next === null) return null;
|
|
72
|
+
args.push(next);
|
|
73
|
+
}
|
|
74
|
+
if (((_c = peek()) == null ? void 0 : _c.kind) !== "rparen") return null;
|
|
75
|
+
pos++;
|
|
76
|
+
if (t.name === "ceil" || t.name === "floor") {
|
|
77
|
+
if (args.length !== 1) return null;
|
|
78
|
+
return t.name === "ceil" ? Math.ceil(args[0]) : Math.floor(args[0]);
|
|
79
|
+
}
|
|
80
|
+
if (args.length < 2) return null;
|
|
81
|
+
return t.name === "max" ? Math.max(...args) : Math.min(...args);
|
|
82
|
+
}
|
|
83
|
+
if (t.kind === "col") {
|
|
84
|
+
pos++;
|
|
85
|
+
if (((_d = peek()) == null ? void 0 : _d.kind) !== "lparen") return null;
|
|
86
|
+
pos++;
|
|
87
|
+
const s = peek();
|
|
88
|
+
if (!s || s.kind !== "str") return null;
|
|
89
|
+
pos++;
|
|
90
|
+
if (((_e = peek()) == null ? void 0 : _e.kind) !== "rparen") return null;
|
|
91
|
+
pos++;
|
|
92
|
+
return (_g = (_f = b.columns) == null ? void 0 : _f[s.value]) != null ? _g : 0;
|
|
93
|
+
}
|
|
94
|
+
if (t.kind === "lparen") {
|
|
95
|
+
pos++;
|
|
96
|
+
const inner = expr();
|
|
97
|
+
if (inner === null) return null;
|
|
98
|
+
if (((_h = peek()) == null ? void 0 : _h.kind) !== "rparen") return null;
|
|
99
|
+
pos++;
|
|
100
|
+
return inner;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
};
|
|
104
|
+
const term = () => {
|
|
105
|
+
var _a;
|
|
106
|
+
let v = factor();
|
|
107
|
+
if (v === null) return null;
|
|
108
|
+
while (((_a = peek()) == null ? void 0 : _a.kind) === "op" && (peek().op === "*" || peek().op === "/")) {
|
|
109
|
+
const op = peek().op;
|
|
110
|
+
pos++;
|
|
111
|
+
const r = factor();
|
|
112
|
+
if (r === null) return null;
|
|
113
|
+
v = op === "*" ? v * r : v / r;
|
|
114
|
+
}
|
|
115
|
+
return v;
|
|
116
|
+
};
|
|
117
|
+
const expr = () => {
|
|
118
|
+
var _a;
|
|
119
|
+
let v = term();
|
|
120
|
+
if (v === null) return null;
|
|
121
|
+
while (((_a = peek()) == null ? void 0 : _a.kind) === "op" && (peek().op === "+" || peek().op === "-")) {
|
|
122
|
+
const op = peek().op;
|
|
123
|
+
pos++;
|
|
124
|
+
const r = term();
|
|
125
|
+
if (r === null) return null;
|
|
126
|
+
v = op === "+" ? v + r : v - r;
|
|
127
|
+
}
|
|
128
|
+
return v;
|
|
129
|
+
};
|
|
130
|
+
const result = expr();
|
|
131
|
+
if (result === null || pos !== tokens.length) return null;
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
var ZERO = {
|
|
135
|
+
level: 1,
|
|
136
|
+
class_level: 1,
|
|
137
|
+
prof: 2,
|
|
138
|
+
str_mod: 0,
|
|
139
|
+
dex_mod: 0,
|
|
140
|
+
con_mod: 0,
|
|
141
|
+
int_mod: 0,
|
|
142
|
+
wis_mod: 0,
|
|
143
|
+
cha_mod: 0,
|
|
144
|
+
columns: {}
|
|
145
|
+
};
|
|
146
|
+
function isValidMaxFormula(formula) {
|
|
147
|
+
const toks = tokenize(formula);
|
|
148
|
+
if (!toks) return false;
|
|
149
|
+
return evalTokens(toks, ZERO) !== null;
|
|
150
|
+
}
|
|
151
|
+
function evaluateMaxFormula(formula, bindings) {
|
|
152
|
+
const toks = tokenize(formula);
|
|
153
|
+
if (!toks) throw new Error(`invalid max_formula: ${JSON.stringify(formula)}`);
|
|
154
|
+
const v = evalTokens(toks, bindings);
|
|
155
|
+
if (v === null) throw new Error(`invalid max_formula: ${JSON.stringify(formula)}`);
|
|
156
|
+
return v;
|
|
157
|
+
}
|
|
158
|
+
function resolveMaxAt(totalLevel, resource) {
|
|
159
|
+
var _a;
|
|
160
|
+
let chosen = resource.max_formula;
|
|
161
|
+
let best = 0;
|
|
162
|
+
for (const step of (_a = resource.scales_at) != null ? _a : []) {
|
|
163
|
+
if (step.level <= totalLevel && step.level > best) {
|
|
164
|
+
best = step.level;
|
|
165
|
+
chosen = step.max;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return chosen;
|
|
169
|
+
}
|
|
170
|
+
function resolveMaxCountAt(level, resource, bindings) {
|
|
171
|
+
var _a;
|
|
172
|
+
const tryEval = (s) => {
|
|
173
|
+
const toks = tokenize(s);
|
|
174
|
+
return toks ? evalTokens(toks, bindings) : null;
|
|
175
|
+
};
|
|
176
|
+
let best = 0;
|
|
177
|
+
let chosen = null;
|
|
178
|
+
for (const step of (_a = resource.scales_at) != null ? _a : []) {
|
|
179
|
+
if (step.level > level || step.level <= best) continue;
|
|
180
|
+
const v = tryEval(step.max);
|
|
181
|
+
if (v === null) continue;
|
|
182
|
+
best = step.level;
|
|
183
|
+
chosen = v;
|
|
184
|
+
}
|
|
185
|
+
if (chosen !== null) return chosen;
|
|
186
|
+
return tryEval(resource.max_formula);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export {
|
|
190
|
+
AT_WILL_MAX,
|
|
191
|
+
isValidMaxFormula,
|
|
192
|
+
evaluateMaxFormula,
|
|
193
|
+
resolveMaxAt,
|
|
194
|
+
resolveMaxCountAt
|
|
195
|
+
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
featureSchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4FWNK4FD.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/race/race.schema.ts
|
|
9
13
|
import { z } from "zod";
|
|
@@ -55,9 +59,31 @@ var raceEntitySchema = z.object({
|
|
|
55
59
|
variant_label: z.string().min(1),
|
|
56
60
|
traits: z.array(featureSchema),
|
|
57
61
|
choices: z.array(choiceSchema).optional(),
|
|
58
|
-
subspecies_of: z.string().regex(wikilinkRegex).optional()
|
|
62
|
+
subspecies_of: z.string().regex(wikilinkRegex).optional(),
|
|
63
|
+
rendering_hint: z.string().optional(),
|
|
64
|
+
has_fluff: z.boolean().optional(),
|
|
65
|
+
has_fluff_images: z.boolean().optional(),
|
|
66
|
+
creature_type: z.array(z.string().min(1)).optional(),
|
|
67
|
+
// guarded non-empty at the emitter (finding 24)
|
|
68
|
+
creature_type_tags: z.array(z.string().min(1)).optional(),
|
|
69
|
+
additional_spells: z.union([
|
|
70
|
+
z.array(additionalSpellsEntrySchema),
|
|
71
|
+
// converter form, 109 docs
|
|
72
|
+
additionalSpellsEntrySchema
|
|
73
|
+
// SHIPPED-BUNDLE bare-object form, 3 docs (§2.8)
|
|
74
|
+
]).optional(),
|
|
75
|
+
// The four RAW 5etools PASSTHROUGHS (race-mapper.ts D13 block copies them VERBATIM, no shape
|
|
76
|
+
// guard — finding 24): today's variant set is a snapshot, not a contract (lineage is already
|
|
77
|
+
// string|null|boolean), and NO G row ever reads them. Maximally loose by ruling; the guard
|
|
78
|
+
// test §9.5 pins today's variant classes since the census goes blind under z.unknown().
|
|
79
|
+
trait_tags: z.unknown().optional(),
|
|
80
|
+
lineage: z.unknown().optional(),
|
|
81
|
+
height_and_weight: z.unknown().optional(),
|
|
82
|
+
sound_clip: z.unknown().optional(),
|
|
83
|
+
image: imageField
|
|
59
84
|
});
|
|
60
85
|
|
|
61
86
|
export {
|
|
87
|
+
fixedAsiSchema,
|
|
62
88
|
raceEntitySchema
|
|
63
89
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
imageField
|
|
3
|
+
} from "./chunk-UA5KS2PL.js";
|
|
4
|
+
|
|
1
5
|
// src/item/item.schema.ts
|
|
2
6
|
import { z } from "zod";
|
|
3
7
|
var tier1Conditions = [
|
|
@@ -41,6 +45,7 @@ var conditionalBonusSchema = z.object({
|
|
|
41
45
|
when: z.array(conditionSchema)
|
|
42
46
|
});
|
|
43
47
|
var numberOrConditional = z.union([z.number().int(), conditionalBonusSchema]);
|
|
48
|
+
var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
|
|
44
49
|
var bonusesSchema = z.object({
|
|
45
50
|
ac: numberOrConditional.optional(),
|
|
46
51
|
weapon_attack: numberOrConditional.optional(),
|
|
@@ -48,6 +53,12 @@ var bonusesSchema = z.object({
|
|
|
48
53
|
spell_attack: numberOrConditional.optional(),
|
|
49
54
|
spell_save_dc: numberOrConditional.optional(),
|
|
50
55
|
saving_throws: numberOrConditional.optional(),
|
|
56
|
+
// Converter-measured (spec §4): 24 / 3 / 2 / 1 carriers. Same numberOrConditional
|
|
57
|
+
// shape as the six above, so a conditional arm is legal on these too.
|
|
58
|
+
spell_damage: numberOrConditional.optional(),
|
|
59
|
+
ability_check: numberOrConditional.optional(),
|
|
60
|
+
proficiency_bonus: numberOrConditional.optional(),
|
|
61
|
+
saving_throw_concentration: numberOrConditional.optional(),
|
|
51
62
|
ability_scores: z.object({
|
|
52
63
|
// partial: each ability is independently optional. z.record(enum, …) in
|
|
53
64
|
// Zod v4 treats every enum key as required, which doesn't match the
|
|
@@ -67,7 +78,16 @@ var bonusesSchema = z.object({
|
|
|
67
78
|
int: numberOrConditional.optional(),
|
|
68
79
|
wis: numberOrConditional.optional(),
|
|
69
80
|
cha: numberOrConditional.optional()
|
|
70
|
-
}).optional()
|
|
81
|
+
}).optional(),
|
|
82
|
+
// "choose `count` of `from`, each +`amount`" (6 measured leaves). `amount` is
|
|
83
|
+
// OPTIONAL: Lost Laboratory of Kwalish/Deck of Several Things carries
|
|
84
|
+
// {from: [all six], count: 1} with NO amount, and a required `amount` would
|
|
85
|
+
// refuse that document outright.
|
|
86
|
+
choose: z.array(z.strictObject({
|
|
87
|
+
from: z.array(abilityEnum),
|
|
88
|
+
count: z.number().int().positive(),
|
|
89
|
+
amount: z.number().int().optional()
|
|
90
|
+
})).optional()
|
|
71
91
|
}).optional(),
|
|
72
92
|
speed: z.object({
|
|
73
93
|
walk: numberOrConditional.optional(),
|
|
@@ -78,6 +98,9 @@ var bonusesSchema = z.object({
|
|
|
78
98
|
});
|
|
79
99
|
var chargesSchema = z.object({
|
|
80
100
|
max: z.number().int(),
|
|
101
|
+
// Dice regained per recharge, e.g. "1d4 - 1" (68 carriers). Distinct from
|
|
102
|
+
// `recharge_amount`, which the canonical pipeline also emits as a string.
|
|
103
|
+
dice: z.string().optional(),
|
|
81
104
|
recharge: z.string().optional(),
|
|
82
105
|
recharge_amount: z.string().optional(),
|
|
83
106
|
destroy_on_empty: z.object({
|
|
@@ -96,16 +119,37 @@ var attachedSpellsSchema = z.object({
|
|
|
96
119
|
// Source canonical pipeline emits this for Necklace of Fireballs, Helm of
|
|
97
120
|
// Brilliance, Candle of Invocation, Efreeti Bottle. Key is total available
|
|
98
121
|
// uses (e.g. "9": ["fireball"] for the necklace's 9 beads).
|
|
99
|
-
limited: z.record(z.string(), z.array(z.string())).optional()
|
|
122
|
+
limited: z.record(z.string(), z.array(z.string())).optional(),
|
|
123
|
+
// Converter-measured (spec §4): spells the item lists without a cost model (18),
|
|
124
|
+
// the casting ability for the item's attached spells (10), and ritual-only
|
|
125
|
+
// spells (1).
|
|
126
|
+
other: z.array(z.string()).optional(),
|
|
127
|
+
ability: z.string().optional(),
|
|
128
|
+
ritual: z.array(z.string()).optional()
|
|
100
129
|
});
|
|
101
130
|
var attunementTagSchema = z.union([
|
|
102
|
-
z.object({
|
|
131
|
+
z.object({
|
|
132
|
+
class: z.string(),
|
|
133
|
+
subclass: z.string().optional(),
|
|
134
|
+
alignment: z.union([z.string(), z.array(z.string())]).optional()
|
|
135
|
+
}),
|
|
103
136
|
// alignment can be a single string OR an array of alignment-letter codes
|
|
104
137
|
// (e.g. ["G"] for good, ["L", "G"] for lawful good).
|
|
105
|
-
z.object({
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
138
|
+
z.object({
|
|
139
|
+
alignment: z.union([z.string(), z.array(z.string())]),
|
|
140
|
+
race: z.string().optional()
|
|
141
|
+
}),
|
|
142
|
+
z.object({
|
|
143
|
+
race: z.string(),
|
|
144
|
+
alignment: z.union([z.string(), z.array(z.string())]).optional()
|
|
145
|
+
}),
|
|
146
|
+
z.object({ creature_type: z.string(), size: z.string().optional() }),
|
|
147
|
+
z.object({ spellcasting: z.boolean() }),
|
|
148
|
+
z.object({ background: z.string() }),
|
|
149
|
+
z.object({ psionics: z.boolean() }),
|
|
150
|
+
z.object({ int: z.number().int() }),
|
|
151
|
+
z.object({ skill_proficiency: z.array(z.string()) }),
|
|
152
|
+
z.object({ language_proficiency: z.array(z.string()) })
|
|
109
153
|
]);
|
|
110
154
|
var attunementCanonicalSchema = z.object({
|
|
111
155
|
required: z.boolean(),
|
|
@@ -122,15 +166,27 @@ var grantsSchema = z.object({
|
|
|
122
166
|
blindsight: z.number().optional()
|
|
123
167
|
}).optional()
|
|
124
168
|
});
|
|
169
|
+
var containerCapacitySchema = z.strictObject({
|
|
170
|
+
weight: z.array(z.number()).optional(),
|
|
171
|
+
item: z.array(z.record(z.string(), z.number())).optional(),
|
|
172
|
+
weightless: z.boolean().optional(),
|
|
173
|
+
volume: z.array(z.number()).optional()
|
|
174
|
+
});
|
|
125
175
|
var containerSchema = z.object({
|
|
126
176
|
capacity_weight: z.number().optional(),
|
|
127
177
|
weightless: z.boolean().optional(),
|
|
128
|
-
pack_contents: z.array(z.string()).optional()
|
|
178
|
+
pack_contents: z.array(z.string()).optional(),
|
|
179
|
+
capacity: containerCapacitySchema.optional()
|
|
129
180
|
});
|
|
130
181
|
var lightSchema = z.object({
|
|
131
182
|
bright_radius: z.number(),
|
|
132
183
|
dim_radius: z.number()
|
|
133
184
|
});
|
|
185
|
+
var lightEntrySchema = z.strictObject({
|
|
186
|
+
bright: z.number().optional(),
|
|
187
|
+
dim: z.number().optional(),
|
|
188
|
+
shape: z.string().optional()
|
|
189
|
+
});
|
|
134
190
|
var itemEntitySchema = z.object({
|
|
135
191
|
name: z.string().min(1),
|
|
136
192
|
slug: z.string().optional(),
|
|
@@ -147,10 +203,12 @@ var itemEntitySchema = z.object({
|
|
|
147
203
|
attunement: z.union([attunementCanonicalSchema, z.boolean(), z.string()]).optional(),
|
|
148
204
|
grants: grantsSchema.optional(),
|
|
149
205
|
container: containerSchema.optional(),
|
|
150
|
-
light: lightSchema.optional(),
|
|
206
|
+
light: z.union([lightSchema, z.array(lightEntrySchema).nonempty()]).optional(),
|
|
151
207
|
cursed: z.boolean().optional(),
|
|
152
208
|
sentient: z.boolean().optional(),
|
|
153
|
-
|
|
209
|
+
// 54 docs carry a LIST of classes that may use the item as a focus
|
|
210
|
+
// (e.g. ["Druid", "Ranger"]); 26 carry a single string, 2 a boolean.
|
|
211
|
+
focus: z.union([z.boolean(), z.string(), z.array(z.string())]).optional(),
|
|
154
212
|
tier: z.enum(["major", "minor"]).optional(),
|
|
155
213
|
damage: z.union([
|
|
156
214
|
z.object({
|
|
@@ -182,6 +240,24 @@ var itemEntitySchema = z.object({
|
|
|
182
240
|
scroll_level: z.number().optional(),
|
|
183
241
|
unidentified: z.boolean().optional(),
|
|
184
242
|
masked_category: z.string().optional(),
|
|
243
|
+
// Converter-modelled top-level keys (spec §4). Declared here so they survive
|
|
244
|
+
// parse AND listed in the parser's KNOWN_KEYS so they are not additionally
|
|
245
|
+
// copied into `raw` — the schema and the raw-bag are independent gates.
|
|
246
|
+
// `modify_speed` (124 carriers): equal 101 (string-valued, e.g. {fly: "walk"}) ·
|
|
247
|
+
// static 20 · multiply 3 · bonus 1 (keyed "*").
|
|
248
|
+
modify_speed: z.strictObject({
|
|
249
|
+
equal: z.record(z.string(), z.string()).optional(),
|
|
250
|
+
static: z.record(z.string(), z.number()).optional(),
|
|
251
|
+
multiply: z.record(z.string(), z.number()).optional(),
|
|
252
|
+
bonus: z.record(z.string(), z.number()).optional()
|
|
253
|
+
}).optional(),
|
|
254
|
+
// Carried by all 6,046 converter item docs, ALWAYS as the empty string today:
|
|
255
|
+
// `.min(1)` is forbidden here.
|
|
256
|
+
rendering_hint: z.string().optional(),
|
|
257
|
+
has_fluff: z.boolean().optional(),
|
|
258
|
+
has_fluff_images: z.boolean().optional(),
|
|
259
|
+
// Already `.optional()` at its source — never append a second one.
|
|
260
|
+
image: imageField,
|
|
185
261
|
raw: z.record(z.string(), z.unknown()).optional(),
|
|
186
262
|
// Legacy fields preserved
|
|
187
263
|
damage_dice: z.string().optional(),
|