@archivist-gg/dnd5e 0.3.1 → 0.3.5
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-2VDTW7QO.js +1580 -0
- package/dist/{chunk-ZZGEVJKX.js → chunk-3JOTQW7A.js} +1 -1
- package/dist/{chunk-5ZBE4CWV.js → chunk-3PZHBLFG.js} +1 -1
- 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-5RKLL2AD.js} +5 -1
- package/dist/{chunk-SE7AZDR3.js → chunk-63XQXPZL.js} +29 -3
- package/dist/{chunk-4F6AIZ36.js → chunk-6BFWZW6T.js} +1 -1
- package/dist/{chunk-GJOI2GHP.js → chunk-6GNXYAYP.js} +1 -1
- 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-TLYDBT5F.js → chunk-CGRLQ34U.js} +64 -7
- package/dist/chunk-CJYCVNW2.js +7 -0
- package/dist/{chunk-XLSVNUTK.js → chunk-CV5XDJKU.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-GE4TLPWM.js → chunk-EHDATXP7.js} +1 -1
- package/dist/chunk-EVMUJYUD.js +195 -0
- package/dist/{chunk-K6Q5OUGK.js → chunk-GFZ7PITJ.js} +86 -10
- package/dist/{chunk-6NK2MUD3.js → chunk-HZA7OE7N.js} +9 -2
- package/dist/{chunk-TN6ZLTNU.js → chunk-I2NDWFDD.js} +51 -4
- package/dist/{chunk-3DZ74AKK.js → chunk-IN4M4X7P.js} +8 -2
- package/dist/chunk-IUAAXXNO.js +58 -0
- package/dist/{chunk-NXSWH3AE.js → chunk-IWRVOCT5.js} +1 -1
- package/dist/{chunk-XJNECUPT.js → chunk-JC5UNIVM.js} +19 -0
- package/dist/{chunk-24GFQU7T.js → chunk-JSZ4H6AQ.js} +1 -1
- package/dist/{chunk-XTIQWSFM.js → chunk-KADJZGZY.js} +1 -1
- package/dist/{chunk-L7E2UT5A.js → chunk-KK33PIDN.js} +5 -1
- package/dist/chunk-KPEJMNRD.js +302 -0
- package/dist/chunk-KU3YLF6D.js +106 -0
- package/dist/{chunk-5INR4NSL.js → chunk-LDDCNPHD.js} +1 -1
- package/dist/chunk-LVQLWRCU.js +15 -0
- package/dist/chunk-MI7P3NX3.js +161 -0
- package/dist/{chunk-PHQRGDRQ.js → chunk-N3A5ICBO.js} +5 -1
- package/dist/chunk-O4SRD3J5.js +165 -0
- package/dist/chunk-OAXWSVJB.js +81 -0
- 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-RSDRMH54.js → chunk-RF4TYCA2.js} +1 -1
- package/dist/chunk-RLGQ5PZZ.js +16 -0
- package/dist/chunk-RVXC6ZHY.js +89 -0
- package/dist/chunk-S7REMWW4.js +21 -0
- package/dist/{chunk-7VJ6BOA5.js → chunk-SMLXZUBH.js} +9 -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-BIK4DFWK.js → chunk-ULCQ73LA.js} +1 -1
- package/dist/{chunk-2BWAID3Y.js → chunk-UMWIPLLM.js} +29 -3
- package/dist/chunk-UQ3ZFCWD.js +178 -0
- package/dist/chunk-UXPGW4TI.js +101 -0
- package/dist/chunk-V2MJE6NC.js +133 -0
- package/dist/{chunk-D3CLOBE3.js → chunk-V373TRJE.js} +48 -10
- package/dist/chunk-VQAXTIBW.js +100 -0
- package/dist/chunk-W74BCA7V.js +159 -0
- package/dist/{chunk-WP2CX2JO.js → chunk-WELIKLZF.js} +22 -1
- package/dist/{chunk-MDEJHMKK.js → chunk-WHVLNFTG.js} +26 -5
- package/dist/{chunk-GZ46OVGZ.js → chunk-WSVMLNTO.js} +1 -1
- package/dist/{chunk-4LZJXJJS.js → chunk-WXWPK3LI.js} +6 -2
- package/dist/chunk-X3D6LXUU.js +185 -0
- package/dist/chunk-XJ2F2WO6.js +8 -0
- package/dist/{chunk-73HOFQQ5.js → chunk-Y4AGLCY6.js} +1 -1
- package/dist/chunk-YBLDE5CG.js +42 -0
- package/dist/{chunk-JEYQU3JT.js → chunk-YHYTW5QA.js} +2 -18
- package/dist/{chunk-PZOWBI3B.js → chunk-YLL2QXY2.js} +7 -2
- 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 +239 -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 +185 -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 +31 -2
- package/dist/pc/pc.equipment.js +12 -8
- package/dist/pc/pc.feature-effects.d.ts +234 -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 +133 -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 +85 -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,165 @@
|
|
|
1
|
+
import {
|
|
2
|
+
derivePoolLayout
|
|
3
|
+
} from "./chunk-PP2UHLBA.js";
|
|
4
|
+
import {
|
|
5
|
+
readTableColumn
|
|
6
|
+
} from "./chunk-IUAAXXNO.js";
|
|
7
|
+
import {
|
|
8
|
+
warnOnce
|
|
9
|
+
} from "./chunk-LVQLWRCU.js";
|
|
10
|
+
import {
|
|
11
|
+
bareEntitySlug,
|
|
12
|
+
wikilinkTailSlug
|
|
13
|
+
} from "./chunk-6X4KUDMU.js";
|
|
14
|
+
|
|
15
|
+
// src/pc/pc.pools.ts
|
|
16
|
+
function levelPrereqMax(d) {
|
|
17
|
+
var _a;
|
|
18
|
+
return ((_a = d.prerequisites) != null ? _a : []).filter((p) => p.kind === "level").reduce((m, p) => Math.max(m, p.min), 0);
|
|
19
|
+
}
|
|
20
|
+
function resolvePool(rc, classIndex, pool, registry, index) {
|
|
21
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
22
|
+
const entity = rc.entity;
|
|
23
|
+
const level = rc.level;
|
|
24
|
+
const ownerBare = bareEntitySlug(entity.slug);
|
|
25
|
+
const ownerPrefix = entity.slug.split("_")[0];
|
|
26
|
+
const col = pool.count.column;
|
|
27
|
+
const count = (_a = readTableColumn(entity.table, level, [col])) != null ? _a : 0;
|
|
28
|
+
let anchorLevel = 1;
|
|
29
|
+
let columnSeen = false;
|
|
30
|
+
for (let l = 1; l <= 20; l++) {
|
|
31
|
+
const c = readTableColumn(entity.table, l, [col]);
|
|
32
|
+
if (c != null) columnSeen = true;
|
|
33
|
+
if (c != null && c >= 1) {
|
|
34
|
+
anchorLevel = l;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (!columnSeen) {
|
|
39
|
+
warnOnce(
|
|
40
|
+
`pool-column:${pool.id}`,
|
|
41
|
+
`Archivist: pool "${pool.id}" declares count column "${col}", which "${entity.slug}"'s class table carries at no level; the pool resolves count 0 at anchor level 1`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const ft = pool.source.where.feature_type;
|
|
45
|
+
const all = registry.search("", "optional-feature", Number.POSITIVE_INFINITY).slice().sort((a, b) => a.name.localeCompare(b.name) || a.slug.localeCompare(b.slug));
|
|
46
|
+
const byBare = /* @__PURE__ */ new Map();
|
|
47
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
48
|
+
for (const e of all) {
|
|
49
|
+
const d = e.data;
|
|
50
|
+
const entry = { slug: e.slug, entity: d, ...e.compendium ? { compendium: e.compendium } : {} };
|
|
51
|
+
const bare = bareEntitySlug(e.slug);
|
|
52
|
+
byBare.set(bare, entry);
|
|
53
|
+
if (d.feature_type !== ft) continue;
|
|
54
|
+
if (!((_b = d.available_to) != null ? _b : []).some((l) => wikilinkTailSlug(l) === ownerBare)) continue;
|
|
55
|
+
if (levelPrereqMax(d) > level) continue;
|
|
56
|
+
const group = candidates.get(bare);
|
|
57
|
+
if (group) group.push(entry);
|
|
58
|
+
else candidates.set(bare, [entry]);
|
|
59
|
+
}
|
|
60
|
+
const available = [];
|
|
61
|
+
for (const [bare, group] of candidates) {
|
|
62
|
+
if (group.length === 1) {
|
|
63
|
+
available.push(group[0]);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const owned = group.find((e) => e.slug.split("_")[0] === ownerPrefix);
|
|
67
|
+
if (!owned) {
|
|
68
|
+
warnOnce(
|
|
69
|
+
`pool-collapse:${bare}`,
|
|
70
|
+
`Archivist: pool "${pool.id}" holds ${group.length} cross-edition entries for "${bare}" and none shares the owner's compendium prefix "${ownerPrefix}"; keeping "${group[0].slug}"`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
available.push(owned != null ? owned : group[0]);
|
|
74
|
+
}
|
|
75
|
+
const grantDecls = [
|
|
76
|
+
...(_d = (_c = rc.entity) == null ? void 0 : _c.pool_grants) != null ? _d : [],
|
|
77
|
+
...(_f = (_e = rc.subclass) == null ? void 0 : _e.pool_grants) != null ? _f : []
|
|
78
|
+
];
|
|
79
|
+
const grantsRaw = grantDecls.filter((g) => g.pool === pool.id).flatMap((g) => g.grants).filter((g) => g.at_level <= level).map((g) => byBare.get(wikilinkTailSlug(g.feature))).filter((e) => e != null);
|
|
80
|
+
const grantedBare = /* @__PURE__ */ new Set();
|
|
81
|
+
const grants = grantsRaw.filter((e) => {
|
|
82
|
+
const key = bareEntitySlug(e.slug);
|
|
83
|
+
if (grantedBare.has(key)) return false;
|
|
84
|
+
grantedBare.add(key);
|
|
85
|
+
return true;
|
|
86
|
+
});
|
|
87
|
+
const bySlugAvailable = new Map(available.map((e) => [e.slug, e]));
|
|
88
|
+
const byBareAvailable = new Map(available.map((e) => [bareEntitySlug(e.slug), e]));
|
|
89
|
+
const resolveStored = (s) => {
|
|
90
|
+
const exact = bySlugAvailable.get(s);
|
|
91
|
+
if (exact) return exact;
|
|
92
|
+
const survivor = byBareAvailable.get(bareEntitySlug(s));
|
|
93
|
+
if (survivor) return survivor;
|
|
94
|
+
const full = registry.getByTypeAndSlug("optional-feature", s);
|
|
95
|
+
if (full) {
|
|
96
|
+
const d = full.data;
|
|
97
|
+
return { slug: full.slug, entity: d, ...full.compendium ? { compendium: full.compendium } : {} };
|
|
98
|
+
}
|
|
99
|
+
return byBare.get(bareEntitySlug(s));
|
|
100
|
+
};
|
|
101
|
+
const raw = (_g = rc.choices[anchorLevel]) == null ? void 0 : _g[pool.id];
|
|
102
|
+
const selectedSlugs = Array.isArray(raw) ? raw : typeof raw === "string" ? [raw] : [];
|
|
103
|
+
const selected = [];
|
|
104
|
+
const seenBare = /* @__PURE__ */ new Set();
|
|
105
|
+
for (const s of selectedSlugs) {
|
|
106
|
+
const entry = resolveStored(s);
|
|
107
|
+
if (!entry) continue;
|
|
108
|
+
const bare = bareEntitySlug(entry.slug);
|
|
109
|
+
if (grantedBare.has(bare) || seenBare.has(bare)) continue;
|
|
110
|
+
seenBare.add(bare);
|
|
111
|
+
selected.push(entry);
|
|
112
|
+
}
|
|
113
|
+
const members = [...available, ...grants];
|
|
114
|
+
const layout = derivePoolLayout(members);
|
|
115
|
+
const votes = /* @__PURE__ */ new Map();
|
|
116
|
+
for (const e of members) {
|
|
117
|
+
const id = (_h = e.entity.consumes) == null ? void 0 : _h.resource;
|
|
118
|
+
if (!id || !index.has(id)) continue;
|
|
119
|
+
votes.set(id, ((_i = votes.get(id)) != null ? _i : 0) + 1);
|
|
120
|
+
}
|
|
121
|
+
let resource;
|
|
122
|
+
let bestN = 0;
|
|
123
|
+
for (const [id, n] of votes) if (n > bestN) {
|
|
124
|
+
resource = id;
|
|
125
|
+
bestN = n;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
id: pool.id,
|
|
129
|
+
label: pool.label,
|
|
130
|
+
classIndex,
|
|
131
|
+
count,
|
|
132
|
+
anchorLevel,
|
|
133
|
+
selected,
|
|
134
|
+
available,
|
|
135
|
+
grants,
|
|
136
|
+
...layout ? { layout } : {},
|
|
137
|
+
...resource ? { resource } : {}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function resolveAllPools(resolved, registry, index) {
|
|
141
|
+
const out = [];
|
|
142
|
+
resolved.classes.forEach((rc, classIndex) => {
|
|
143
|
+
var _a, _b, _c;
|
|
144
|
+
if (!rc.entity) return;
|
|
145
|
+
const decls = [...(_a = rc.entity.selection_pools) != null ? _a : [], ...(_c = (_b = rc.subclass) == null ? void 0 : _b.selection_pools) != null ? _c : []];
|
|
146
|
+
const seen = /* @__PURE__ */ new Set();
|
|
147
|
+
for (const pool of decls) {
|
|
148
|
+
if (seen.has(pool.id)) continue;
|
|
149
|
+
seen.add(pool.id);
|
|
150
|
+
out.push(resolvePool(rc, classIndex, pool, registry, index));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
function strandedPicks(pool) {
|
|
156
|
+
const inAvailable = new Set(pool.available.map((e) => e.slug));
|
|
157
|
+
return pool.selected.filter((e) => !inAvailable.has(e.slug));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export {
|
|
161
|
+
levelPrereqMax,
|
|
162
|
+
resolvePool,
|
|
163
|
+
resolveAllPools,
|
|
164
|
+
strandedPicks
|
|
165
|
+
};
|
|
@@ -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-W74BCA7V.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
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
spellEntitySchema
|
|
3
|
+
} from "./chunk-VQAXTIBW.js";
|
|
4
|
+
|
|
5
|
+
// src/spell/spell.parser.ts
|
|
6
|
+
import { parseYaml, toStringSafe } from "@archivist-gg/core";
|
|
7
|
+
var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
8
|
+
"name",
|
|
9
|
+
"level",
|
|
10
|
+
"school",
|
|
11
|
+
"casting_time",
|
|
12
|
+
"range",
|
|
13
|
+
"components",
|
|
14
|
+
"duration",
|
|
15
|
+
"concentration",
|
|
16
|
+
"ritual",
|
|
17
|
+
"classes",
|
|
18
|
+
"description",
|
|
19
|
+
"at_higher_levels",
|
|
20
|
+
"damage",
|
|
21
|
+
"saving_throw",
|
|
22
|
+
"casting_options",
|
|
23
|
+
// §2 · the fourteen converter keys. This gate and `spellEntitySchema` are independent: a key
|
|
24
|
+
// missing here is refused BEFORE zod ever sees it, so both must learn every new key.
|
|
25
|
+
"rendering_hint",
|
|
26
|
+
"misc_tags",
|
|
27
|
+
"area_tags",
|
|
28
|
+
"condition_inflict",
|
|
29
|
+
"affects_creature_type",
|
|
30
|
+
"spell_attack",
|
|
31
|
+
"ability_check",
|
|
32
|
+
"damage_resist",
|
|
33
|
+
"damage_immune",
|
|
34
|
+
"condition_immune",
|
|
35
|
+
"damage_vulnerable",
|
|
36
|
+
"has_fluff",
|
|
37
|
+
"image",
|
|
38
|
+
"has_fluff_images",
|
|
39
|
+
// Body-only metadata that's emitted to YAML but ignored at runtime — accept silently.
|
|
40
|
+
"slug",
|
|
41
|
+
"edition",
|
|
42
|
+
"source"
|
|
43
|
+
]);
|
|
44
|
+
var DURATION_END_WORDS = { dispel: "dispelled", trigger: "triggered" };
|
|
45
|
+
function normalizeSpellComponents(value) {
|
|
46
|
+
if (typeof value === "string") return value;
|
|
47
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) return toStringSafe(value);
|
|
48
|
+
const c = value;
|
|
49
|
+
const parts = [];
|
|
50
|
+
if (c.v === true) parts.push("V");
|
|
51
|
+
if (c.s === true) parts.push("S");
|
|
52
|
+
if (typeof c.m === "string") parts.push(`M (${c.m})`);
|
|
53
|
+
else if (c.m === true) parts.push("M");
|
|
54
|
+
else if (c.m != null && typeof c.m === "object") {
|
|
55
|
+
const text = c.m.text;
|
|
56
|
+
parts.push(typeof text === "string" ? `M (${text})` : "M");
|
|
57
|
+
}
|
|
58
|
+
if (c.r === true) parts.push("R");
|
|
59
|
+
return parts.join(", ");
|
|
60
|
+
}
|
|
61
|
+
function normalizeSpellDurationEntry(entry) {
|
|
62
|
+
if (entry == null || typeof entry !== "object") return toStringSafe(entry);
|
|
63
|
+
const e = entry;
|
|
64
|
+
if (e.type === "permanent" && Array.isArray(e.ends)) {
|
|
65
|
+
return "until " + e.ends.map((w) => {
|
|
66
|
+
var _a;
|
|
67
|
+
return (_a = DURATION_END_WORDS[String(w)]) != null ? _a : String(w);
|
|
68
|
+
}).join(" or ");
|
|
69
|
+
}
|
|
70
|
+
if (e.type === "timed" && e.duration != null) {
|
|
71
|
+
const unit = e.duration.type;
|
|
72
|
+
const amount = e.duration.amount;
|
|
73
|
+
if (typeof unit === "string" && typeof amount === "number") {
|
|
74
|
+
const prefix = e.duration.up_to === true ? "up to " : "";
|
|
75
|
+
return `${prefix}${amount} ${unit}${amount > 1 ? "s" : ""}`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return toStringSafe(entry);
|
|
79
|
+
}
|
|
80
|
+
function normalizeSpellDuration(value) {
|
|
81
|
+
if (typeof value === "string") return value;
|
|
82
|
+
if (!Array.isArray(value)) return toStringSafe(value);
|
|
83
|
+
return value.map((entry) => normalizeSpellDurationEntry(entry)).join("; ");
|
|
84
|
+
}
|
|
85
|
+
function mirrorSpellShapes(entity) {
|
|
86
|
+
return {
|
|
87
|
+
...entity,
|
|
88
|
+
...entity.components != null ? { components: normalizeSpellComponents(entity.components) } : {},
|
|
89
|
+
...entity.duration != null ? { duration: normalizeSpellDuration(entity.duration) } : {}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function parseSpell(source) {
|
|
93
|
+
const result = parseYaml(source, ["name"]);
|
|
94
|
+
if (!result.success) return result;
|
|
95
|
+
const meta = {};
|
|
96
|
+
if (typeof result.data.source === "string") meta.source = result.data.source;
|
|
97
|
+
if (typeof result.data.edition === "string") meta.edition = result.data.edition;
|
|
98
|
+
const filtered = {};
|
|
99
|
+
for (const [k, v] of Object.entries(result.data)) {
|
|
100
|
+
if (k === "slug" || k === "edition" || k === "source") continue;
|
|
101
|
+
if (!KNOWN_KEYS.has(k)) {
|
|
102
|
+
return { success: false, error: `Unknown spell field: ${k}` };
|
|
103
|
+
}
|
|
104
|
+
filtered[k] = v;
|
|
105
|
+
}
|
|
106
|
+
const parsed = spellEntitySchema.safeParse(filtered);
|
|
107
|
+
if (!parsed.success) {
|
|
108
|
+
return { success: false, error: `spell schema validation failed: ${parsed.error.message}` };
|
|
109
|
+
}
|
|
110
|
+
const raw = parsed.data;
|
|
111
|
+
const spell = { name: raw.name };
|
|
112
|
+
if (raw.level != null) spell.level = raw.level;
|
|
113
|
+
if (raw.school != null) spell.school = toStringSafe(raw.school);
|
|
114
|
+
if (raw.casting_time != null) spell.casting_time = toStringSafe(raw.casting_time);
|
|
115
|
+
if (raw.range != null) spell.range = toStringSafe(raw.range);
|
|
116
|
+
if (raw.components != null) spell.components = normalizeSpellComponents(raw.components);
|
|
117
|
+
if (raw.duration != null) spell.duration = normalizeSpellDuration(raw.duration);
|
|
118
|
+
if (raw.concentration != null) spell.concentration = Boolean(raw.concentration);
|
|
119
|
+
if (raw.ritual != null) spell.ritual = Boolean(raw.ritual);
|
|
120
|
+
if (raw.classes) spell.classes = raw.classes.map(String);
|
|
121
|
+
if (raw.description) spell.description = raw.description;
|
|
122
|
+
if (raw.at_higher_levels) spell.at_higher_levels = raw.at_higher_levels.map(String);
|
|
123
|
+
if (raw.damage) spell.damage = { types: raw.damage.types.map(String) };
|
|
124
|
+
if (raw.saving_throw) spell.saving_throw = { ability: raw.saving_throw.ability };
|
|
125
|
+
if (raw.casting_options) {
|
|
126
|
+
spell.casting_options = raw.casting_options.map((opt) => {
|
|
127
|
+
const co = { type: opt.type };
|
|
128
|
+
if (opt.damage_roll !== void 0) co.damage_roll = opt.damage_roll;
|
|
129
|
+
if (opt.target_count !== void 0) co.target_count = opt.target_count;
|
|
130
|
+
if (opt.duration !== void 0) co.duration = opt.duration;
|
|
131
|
+
if (opt.range !== void 0) co.range = opt.range;
|
|
132
|
+
if (opt.concentration !== void 0) co.concentration = opt.concentration;
|
|
133
|
+
if (opt.shape_size !== void 0) co.shape_size = opt.shape_size;
|
|
134
|
+
if (opt.desc !== void 0) co.desc = opt.desc;
|
|
135
|
+
return co;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (raw.rendering_hint != null) spell.rendering_hint = raw.rendering_hint;
|
|
139
|
+
if (raw.misc_tags != null) spell.misc_tags = raw.misc_tags;
|
|
140
|
+
if (raw.area_tags != null) spell.area_tags = raw.area_tags;
|
|
141
|
+
if (raw.condition_inflict != null) spell.condition_inflict = raw.condition_inflict;
|
|
142
|
+
if (raw.affects_creature_type != null) spell.affects_creature_type = raw.affects_creature_type;
|
|
143
|
+
if (raw.spell_attack != null) spell.spell_attack = raw.spell_attack;
|
|
144
|
+
if (raw.ability_check != null) spell.ability_check = raw.ability_check;
|
|
145
|
+
if (raw.damage_resist != null) spell.damage_resist = raw.damage_resist;
|
|
146
|
+
if (raw.damage_immune != null) spell.damage_immune = raw.damage_immune;
|
|
147
|
+
if (raw.condition_immune != null) spell.condition_immune = raw.condition_immune;
|
|
148
|
+
if (raw.damage_vulnerable != null) spell.damage_vulnerable = raw.damage_vulnerable;
|
|
149
|
+
if (raw.has_fluff != null) spell.has_fluff = raw.has_fluff;
|
|
150
|
+
if (raw.image != null) spell.image = raw.image;
|
|
151
|
+
if (raw.has_fluff_images != null) spell.has_fluff_images = raw.has_fluff_images;
|
|
152
|
+
if (meta.source) spell.source = meta.source;
|
|
153
|
+
if (meta.edition) spell.edition = meta.edition;
|
|
154
|
+
return { success: true, data: spell };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
normalizeSpellComponents,
|
|
159
|
+
normalizeSpellDuration,
|
|
160
|
+
mirrorSpellShapes,
|
|
161
|
+
parseSpell
|
|
162
|
+
};
|
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
ABILITY_KEYS,
|
|
3
3
|
CR_PROFICIENCY,
|
|
4
4
|
CR_XP,
|
|
5
|
+
DAMAGE_TYPES,
|
|
5
6
|
SIZE_HIT_DICE
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WELIKLZF.js";
|
|
7
8
|
|
|
8
9
|
// src/dnd/math.ts
|
|
9
10
|
function abilityModifier(score) {
|
|
@@ -68,6 +69,22 @@ function abilityNameToKey(name) {
|
|
|
68
69
|
}
|
|
69
70
|
return null;
|
|
70
71
|
}
|
|
72
|
+
var CANONICAL_DAMAGE_TYPES = new Set(DAMAGE_TYPES.map((t) => t.toLowerCase()));
|
|
73
|
+
function isCanonicalDamageType(type) {
|
|
74
|
+
return CANONICAL_DAMAGE_TYPES.has(type.trim().toLowerCase());
|
|
75
|
+
}
|
|
76
|
+
var CHOSEN_DAMAGE_TYPE = "chosen";
|
|
77
|
+
var CHOSEN_DAMAGE_TYPE_NOTE = "damage type of your choice";
|
|
78
|
+
function isChosenDamageType(type) {
|
|
79
|
+
return type !== void 0 && type.trim().toLowerCase() === CHOSEN_DAMAGE_TYPE;
|
|
80
|
+
}
|
|
81
|
+
function isRenderableDamageText(text) {
|
|
82
|
+
var _a;
|
|
83
|
+
const m = /^(?:\d*d\d+(?:\s*[+-]\s*\d+)?|[+-]?\d+)(?:\s+(.*))?$/i.exec(text.trim());
|
|
84
|
+
if (!m) return false;
|
|
85
|
+
const type = ((_a = m[1]) != null ? _a : "").trim();
|
|
86
|
+
return type === "" || isCanonicalDamageType(type);
|
|
87
|
+
}
|
|
71
88
|
|
|
72
89
|
export {
|
|
73
90
|
abilityModifier,
|
|
@@ -84,5 +101,10 @@ export {
|
|
|
84
101
|
passivePerception,
|
|
85
102
|
attackBonus,
|
|
86
103
|
saveDC,
|
|
87
|
-
abilityNameToKey
|
|
104
|
+
abilityNameToKey,
|
|
105
|
+
isCanonicalDamageType,
|
|
106
|
+
CHOSEN_DAMAGE_TYPE,
|
|
107
|
+
CHOSEN_DAMAGE_TYPE_NOTE,
|
|
108
|
+
isChosenDamageType,
|
|
109
|
+
isRenderableDamageText
|
|
88
110
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/pc/pool-layout.ts
|
|
2
|
+
var RENDERING_HINT_LAYOUT = {
|
|
3
|
+
"dice-pool": "dice-pool",
|
|
4
|
+
"point-pool": "point-pool"
|
|
5
|
+
};
|
|
6
|
+
function derivePoolLayout(entries) {
|
|
7
|
+
var _a, _b;
|
|
8
|
+
const votes = /* @__PURE__ */ new Map();
|
|
9
|
+
for (const e of entries) {
|
|
10
|
+
const hint = (_a = e.entity.rendering_hint) != null ? _a : "";
|
|
11
|
+
if (!Object.prototype.hasOwnProperty.call(RENDERING_HINT_LAYOUT, hint)) continue;
|
|
12
|
+
const layout = RENDERING_HINT_LAYOUT[hint];
|
|
13
|
+
votes.set(layout, ((_b = votes.get(layout)) != null ? _b : 0) + 1);
|
|
14
|
+
}
|
|
15
|
+
let best;
|
|
16
|
+
let bestN = 0;
|
|
17
|
+
let tied = false;
|
|
18
|
+
for (const [layout, n] of votes) {
|
|
19
|
+
if (n > bestN) {
|
|
20
|
+
best = layout;
|
|
21
|
+
bestN = n;
|
|
22
|
+
tied = false;
|
|
23
|
+
} else if (n === bestN) tied = true;
|
|
24
|
+
}
|
|
25
|
+
return tied ? void 0 : best;
|
|
26
|
+
}
|
|
27
|
+
var RENDERING_HINT_AFFORDANCE = {
|
|
28
|
+
"granted-die-to-ally": "granted-die"
|
|
29
|
+
};
|
|
30
|
+
function deriveEntryAffordance(entity) {
|
|
31
|
+
var _a;
|
|
32
|
+
const hint = (_a = entity.rendering_hint) != null ? _a : "";
|
|
33
|
+
if (!Object.prototype.hasOwnProperty.call(RENDERING_HINT_AFFORDANCE, hint)) return void 0;
|
|
34
|
+
return RENDERING_HINT_AFFORDANCE[hint];
|
|
35
|
+
}
|
|
36
|
+
var AFFORDANCE_CAPTIONS = {
|
|
37
|
+
"granted-die": "{amount}{die} to an ally"
|
|
38
|
+
};
|
|
39
|
+
function formatAffordanceCaption(affordance, values) {
|
|
40
|
+
return AFFORDANCE_CAPTIONS[affordance].replace(/\{(\w+)\}/g, (whole, key) => Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : whole);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
RENDERING_HINT_LAYOUT,
|
|
45
|
+
derivePoolLayout,
|
|
46
|
+
RENDERING_HINT_AFFORDANCE,
|
|
47
|
+
deriveEntryAffordance,
|
|
48
|
+
AFFORDANCE_CAPTIONS,
|
|
49
|
+
formatAffordanceCaption
|
|
50
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/pc/pc.proficiency-normalize.ts
|
|
2
|
+
var normKey = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean).map((w) => w.endsWith("s") ? w.slice(0, -1) : w).sort().join(" ");
|
|
3
|
+
var toProfSlug = (s) => s.toLowerCase().replace(/’/g, "'").trim().replace(/\s+/g, "-");
|
|
4
|
+
var humanizeProficiency = (s) => s.replace(/-/g, " ").replace(/(^|\s)\w/g, (c) => c.toUpperCase());
|
|
5
|
+
var proficiencyLabel = (raw) => {
|
|
6
|
+
const text = raw.replace(/’/g, "'").trim().replace(/\s+/g, " ");
|
|
7
|
+
if (!/\s/.test(text) || text === text.toLowerCase()) return humanizeProficiency(toProfSlug(raw));
|
|
8
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
normKey,
|
|
13
|
+
toProfSlug,
|
|
14
|
+
humanizeProficiency,
|
|
15
|
+
proficiencyLabel
|
|
16
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
actionCostEnum,
|
|
3
|
+
resourceSchema
|
|
4
|
+
} from "./chunk-YLL2QXY2.js";
|
|
5
|
+
import {
|
|
6
|
+
choiceSchema
|
|
7
|
+
} from "./chunk-IWRVOCT5.js";
|
|
8
|
+
import {
|
|
9
|
+
featureEffectSchema
|
|
10
|
+
} from "./chunk-X3D6LXUU.js";
|
|
11
|
+
import {
|
|
12
|
+
additionalSpellsEntrySchema,
|
|
13
|
+
imageField,
|
|
14
|
+
progressionSchema
|
|
15
|
+
} from "./chunk-UA5KS2PL.js";
|
|
16
|
+
|
|
17
|
+
// src/feat/feat.schema.ts
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
|
|
20
|
+
var editionEnum = z.enum(["2014", "2024"]);
|
|
21
|
+
var categoryEnum = z.enum(["origin", "general", "fighting-style", "epic-boon"]);
|
|
22
|
+
var prerequisiteSchema = z.discriminatedUnion("kind", [
|
|
23
|
+
z.object({ kind: z.literal("ability"), ability: abilityEnum, min: z.number().int().positive() }),
|
|
24
|
+
z.object({ kind: z.literal("level"), min: z.number().int().positive() }),
|
|
25
|
+
z.object({ kind: z.literal("spellcaster") }),
|
|
26
|
+
z.object({
|
|
27
|
+
kind: z.literal("proficiency"),
|
|
28
|
+
proficiency_type: z.enum(["armor", "weapon", "tool", "skill", "saving-throw"]),
|
|
29
|
+
value: z.string().min(1)
|
|
30
|
+
}),
|
|
31
|
+
z.object({ kind: z.literal("race"), slug: z.string().min(1) }),
|
|
32
|
+
z.object({ kind: z.literal("class"), slug: z.string().min(1) }),
|
|
33
|
+
// The exclusive-feat-category and feat-category arms carry a raw 5etools category code, not a
|
|
34
|
+
// slug; featCategoryLabel names it. The rest carry slugs the renderer humanizes, except other,
|
|
35
|
+
// which carries free prose that prereqText returns verbatim.
|
|
36
|
+
z.object({ kind: z.literal("feat"), slug: z.string().min(1) }),
|
|
37
|
+
z.object({ kind: z.literal("campaign"), slug: z.string().min(1) }),
|
|
38
|
+
z.object({ kind: z.literal("exclusive-feat-category"), slug: z.string().min(1) }),
|
|
39
|
+
z.object({ kind: z.literal("feature"), slug: z.string().min(1) }),
|
|
40
|
+
z.object({ kind: z.literal("other"), detail: z.string().min(1) }),
|
|
41
|
+
z.object({ kind: z.literal("feat-category"), slug: z.string().min(1) }),
|
|
42
|
+
z.object({ kind: z.literal("background"), slug: z.string().min(1) })
|
|
43
|
+
]);
|
|
44
|
+
var featEntitySchema = z.object({
|
|
45
|
+
slug: z.string().min(1),
|
|
46
|
+
name: z.string().min(1),
|
|
47
|
+
edition: editionEnum,
|
|
48
|
+
source: z.string().min(1),
|
|
49
|
+
category: categoryEnum,
|
|
50
|
+
description: z.string(),
|
|
51
|
+
prerequisites: z.array(prerequisiteSchema),
|
|
52
|
+
benefits: z.array(z.string()),
|
|
53
|
+
effects: z.array(featureEffectSchema),
|
|
54
|
+
grants_asi: z.object({
|
|
55
|
+
amount: z.number().int().positive(),
|
|
56
|
+
pool: z.array(abilityEnum).optional()
|
|
57
|
+
}).nullable(),
|
|
58
|
+
repeatable: z.boolean(),
|
|
59
|
+
choices: z.array(choiceSchema),
|
|
60
|
+
rendering_hint: z.string().optional(),
|
|
61
|
+
additional_spells: z.array(additionalSpellsEntrySchema).optional(),
|
|
62
|
+
// 71
|
|
63
|
+
has_fluff: z.boolean().optional(),
|
|
64
|
+
// 0 carriers — symmetry, §9.4
|
|
65
|
+
has_fluff_images: z.boolean().optional(),
|
|
66
|
+
// 41
|
|
67
|
+
trait_tags: z.unknown().optional(),
|
|
68
|
+
// 7, nullable measured — same passthrough family
|
|
69
|
+
optionalfeature_progression: z.array(progressionSchema).optional(),
|
|
70
|
+
// 4
|
|
71
|
+
resources: z.array(resourceSchema).optional(),
|
|
72
|
+
// closes the feat.types.ts `Feat.resources` schema/type asymmetry (C);
|
|
73
|
+
// ZERO converter/bundle carriers (no census row) — the only behaviour delta is AUTHORED feats,
|
|
74
|
+
// where the type has promised the field all along. Import resourceSchema.
|
|
75
|
+
action_cost: actionCostEnum.optional(),
|
|
76
|
+
// ruling R-G1b-5 (gate1-r1 f1): a TOP-LEVEL bundle key on ONE
|
|
77
|
+
// feat (`SRD 2024/Feats/Boon of the Night Spirit.md`, `action_cost: bonus-action` at entity top
|
|
78
|
+
// level) — the ninth SRD-control non-kept row: top-level on the ENTITY, not featureSchema-shaped
|
|
79
|
+
// (finding 14's nine rows split 4 featureSchema / 4 §2.8-arity / this one, which needed its own
|
|
80
|
+
// declaration — gate1-r4 R4-1). `actionCostEnum` is exported from
|
|
81
|
+
// `schemas/resource-schema.ts` (optionalFeatureEntitySchema already imports it). MAPPED since
|
|
82
|
+
// R4-G3a §10.2.3: the resolver aliases it onto the resolved feature's `action`, which is what
|
|
83
|
+
// draws the feat note's Action line and files the row under that economy.
|
|
84
|
+
image: imageField
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
featEntitySchema
|
|
89
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/pc/conditions.constants.ts
|
|
2
|
+
var CONDITION_SLUGS = [
|
|
3
|
+
"blinded",
|
|
4
|
+
"charmed",
|
|
5
|
+
"deafened",
|
|
6
|
+
"frightened",
|
|
7
|
+
"grappled",
|
|
8
|
+
"incapacitated",
|
|
9
|
+
"invisible",
|
|
10
|
+
"paralyzed",
|
|
11
|
+
"petrified",
|
|
12
|
+
"poisoned",
|
|
13
|
+
"prone",
|
|
14
|
+
"restrained",
|
|
15
|
+
"stunned",
|
|
16
|
+
"unconscious"
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
CONDITION_SLUGS
|
|
21
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
itemEntitySchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GFZ7PITJ.js";
|
|
4
4
|
|
|
5
5
|
// src/item/item.parser.ts
|
|
6
6
|
import { parseYaml } from "@archivist-gg/core";
|
|
@@ -41,6 +41,14 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
41
41
|
"scroll_level",
|
|
42
42
|
"unidentified",
|
|
43
43
|
"masked_category",
|
|
44
|
+
// Modelled top-level converter keys (spec §4). The schema declaring them is NOT
|
|
45
|
+
// enough: this set is the second, independent gate — a key missing here is
|
|
46
|
+
// copied into `raw` as well, so the two lists must move together.
|
|
47
|
+
"modify_speed",
|
|
48
|
+
"rendering_hint",
|
|
49
|
+
"has_fluff",
|
|
50
|
+
"has_fluff_images",
|
|
51
|
+
"image",
|
|
44
52
|
// Legacy fields kept (removed in Slice 7 after grep verifies no consumer)
|
|
45
53
|
"damage_dice",
|
|
46
54
|
"damage_type",
|