@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,302 @@
|
|
|
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
|
+
attunement_set: 0,
|
|
26
|
+
ability_bonus: {},
|
|
27
|
+
senses: { darkvision: 0, blindsight: 0, tremorsense: 0, truesight: 0 },
|
|
28
|
+
ac_terms: [],
|
|
29
|
+
resistances: [],
|
|
30
|
+
immunities: [],
|
|
31
|
+
vulnerabilities: [],
|
|
32
|
+
condition_immunities: [],
|
|
33
|
+
proficiencies: { skills: [], skillExpertise: [], tools: [], languages: [], saves: [], armor: [], weapons: [] },
|
|
34
|
+
weaponAbilities: [],
|
|
35
|
+
rollModifiers: [],
|
|
36
|
+
saveOutcomes: [],
|
|
37
|
+
critRange: 20,
|
|
38
|
+
extraAttack: 0,
|
|
39
|
+
attackNotes: [],
|
|
40
|
+
damageBonuses: []
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function pushUnique(list, value) {
|
|
44
|
+
const key = value.trim().toLowerCase();
|
|
45
|
+
if (!key) return;
|
|
46
|
+
if (!list.some((v) => v.trim().toLowerCase() === key)) list.push(value.trim());
|
|
47
|
+
}
|
|
48
|
+
function pushDefenseGrant(list, value, source, condition) {
|
|
49
|
+
const key = value.trim().toLowerCase();
|
|
50
|
+
if (!key) return;
|
|
51
|
+
const existing = list.find((g) => g.value.trim().toLowerCase() === key);
|
|
52
|
+
if (existing) {
|
|
53
|
+
if (!existing.sources.includes(source)) existing.sources.push(source);
|
|
54
|
+
if (!existing.condition && condition) existing.condition = condition;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
list.push({ value: value.trim(), sources: [source], ...condition ? { condition } : {} });
|
|
58
|
+
}
|
|
59
|
+
function foldsNow(rf, activeBuffs) {
|
|
60
|
+
if (rf.feature.activatable !== true) return true;
|
|
61
|
+
const id = rf.feature.id;
|
|
62
|
+
return !!id && activeBuffs.has(id);
|
|
63
|
+
}
|
|
64
|
+
function assembleEffectFeatures(resolved) {
|
|
65
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
66
|
+
const activeBuffs = new Set((_b = (_a = resolved.state) == null ? void 0 : _a.active_buffs) != null ? _b : []);
|
|
67
|
+
const featureIds = new Set(((_c = resolved.features) != null ? _c : []).map((f) => f.feature.id).filter((id) => !!id));
|
|
68
|
+
const aliases = [];
|
|
69
|
+
for (const pool of (_d = resolved.pools) != null ? _d : []) {
|
|
70
|
+
for (const entry of [...(_e = pool.selected) != null ? _e : [], ...(_f = pool.grants) != null ? _f : []]) {
|
|
71
|
+
if (activeBuffs.has(entry.slug)) continue;
|
|
72
|
+
const bare = bareEntitySlug(entry.slug);
|
|
73
|
+
for (const stored of activeBuffs) {
|
|
74
|
+
if (featureIds.has(stored)) continue;
|
|
75
|
+
if (bareEntitySlug(stored) === bare) {
|
|
76
|
+
aliases.push(entry.slug);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const a of aliases) activeBuffs.add(a);
|
|
83
|
+
const buffFeatures = [];
|
|
84
|
+
const pushBoon = (item, pool) => {
|
|
85
|
+
var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
|
|
86
|
+
const e = item.entity;
|
|
87
|
+
if (!e || ((_b2 = (_a2 = e.effects) == null ? void 0 : _a2.length) != null ? _b2 : 0) === 0) return;
|
|
88
|
+
buffFeatures.push({
|
|
89
|
+
feature: { id: item.slug, name: e.name, activatable: (_c2 = e.activatable) != null ? _c2 : false, effects: e.effects },
|
|
90
|
+
// source is inert for the fold; attribute to the pool's owning class
|
|
91
|
+
// (never a hardcoded class) so the generic engine carries no homebrew name.
|
|
92
|
+
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 }
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
for (const pool of (_g = resolved.pools) != null ? _g : []) {
|
|
96
|
+
for (const sel of (_h = pool.selected) != null ? _h : []) pushBoon(sel, pool);
|
|
97
|
+
for (const g of (_i = pool.grants) != null ? _i : []) pushBoon(g, pool);
|
|
98
|
+
}
|
|
99
|
+
return { features: [...(_j = resolved.features) != null ? _j : [], ...buffFeatures], activeBuffs };
|
|
100
|
+
}
|
|
101
|
+
function foldsOnSelf(eff) {
|
|
102
|
+
if (eff.subject === void 0 || eff.subject === "self") return true;
|
|
103
|
+
warnOnce(eff.subject, `archivist: unrecognised effect subject "${eff.subject}" (only "self" folds)`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
function selfEffectsOf(f) {
|
|
107
|
+
var _a;
|
|
108
|
+
return ((_a = f.effects) != null ? _a : []).filter(foldsOnSelf);
|
|
109
|
+
}
|
|
110
|
+
function computeFeatureEffects(features, opts) {
|
|
111
|
+
var _a, _b, _c;
|
|
112
|
+
const out = emptyFeatureEffectTotals();
|
|
113
|
+
for (const rf of features) {
|
|
114
|
+
if (!foldsNow(rf, (_a = opts == null ? void 0 : opts.activeBuffs) != null ? _a : /* @__PURE__ */ new Set())) continue;
|
|
115
|
+
const level = (_b = opts == null ? void 0 : opts.levelFor) == null ? void 0 : _b.call(opts, rf.source);
|
|
116
|
+
for (const eff of selfEffectsOf(rf.feature)) {
|
|
117
|
+
applyEffect(out, eff, (_c = rf.feature.name) != null ? _c : "Feature", level);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
function classifyProficiencyEffect(eff) {
|
|
123
|
+
if (eff.kind !== "proficiency") return null;
|
|
124
|
+
const raw = eff.value;
|
|
125
|
+
const expertise = eff.expertise === true;
|
|
126
|
+
switch (eff.proficiency_type) {
|
|
127
|
+
case "skill":
|
|
128
|
+
return { bucket: "skills", value: toProfSlug(raw), raw, expertise };
|
|
129
|
+
case "tool":
|
|
130
|
+
return { bucket: "tools", value: toProfSlug(raw), raw, expertise };
|
|
131
|
+
case "language":
|
|
132
|
+
return { bucket: "languages", value: toProfSlug(raw), raw, expertise };
|
|
133
|
+
case "armor":
|
|
134
|
+
return { bucket: "armor", value: raw.toLowerCase(), raw, expertise };
|
|
135
|
+
case "weapon":
|
|
136
|
+
return { bucket: "weapons", value: raw.toLowerCase(), raw, expertise };
|
|
137
|
+
default: {
|
|
138
|
+
const ab = normalizeAbility(raw);
|
|
139
|
+
return ab ? { bucket: "saves", value: ab, raw, expertise } : null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function collectProficiencyEffectGrants(features, activeBuffs) {
|
|
144
|
+
const out = {
|
|
145
|
+
skills: [],
|
|
146
|
+
tools: [],
|
|
147
|
+
languages: [],
|
|
148
|
+
saves: [],
|
|
149
|
+
armor: [],
|
|
150
|
+
weapons: []
|
|
151
|
+
};
|
|
152
|
+
for (const rf of features) {
|
|
153
|
+
if (!foldsNow(rf, activeBuffs)) continue;
|
|
154
|
+
for (const eff of selfEffectsOf(rf.feature)) {
|
|
155
|
+
const c = classifyProficiencyEffect(eff);
|
|
156
|
+
if (!c) continue;
|
|
157
|
+
out[c.bucket].push({
|
|
158
|
+
value: c.value,
|
|
159
|
+
raw: c.raw,
|
|
160
|
+
sourceKind: rf.source.kind,
|
|
161
|
+
sourceSlug: rf.source.slug,
|
|
162
|
+
...c.expertise ? { expertise: true } : {}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
function scaled(base, key, scalesAt, level) {
|
|
169
|
+
if (level === void 0 || !(scalesAt == null ? void 0 : scalesAt.length)) return base;
|
|
170
|
+
let best = base;
|
|
171
|
+
let bestLevel = -1;
|
|
172
|
+
for (const step of scalesAt) {
|
|
173
|
+
if (step.level > level || step.level <= bestLevel) continue;
|
|
174
|
+
const v = step[key];
|
|
175
|
+
if (typeof v !== "number") continue;
|
|
176
|
+
best = v;
|
|
177
|
+
bestLevel = step.level;
|
|
178
|
+
}
|
|
179
|
+
return best;
|
|
180
|
+
}
|
|
181
|
+
function applyEffect(out, eff, label, level) {
|
|
182
|
+
var _a, _b, _c, _d, _e;
|
|
183
|
+
switch (eff.kind) {
|
|
184
|
+
case "initiative-bonus":
|
|
185
|
+
out.initiative_bonus += eff.value;
|
|
186
|
+
break;
|
|
187
|
+
case "hp-per-level-bonus":
|
|
188
|
+
out.hp_per_level_bonus += eff.value;
|
|
189
|
+
out.hp_per_level_terms.push({ label, value: eff.value });
|
|
190
|
+
break;
|
|
191
|
+
case "speed-bonus": {
|
|
192
|
+
const value = scaled(eff.value, "value", eff.scales_at, level);
|
|
193
|
+
if (eff.mode === "walk") {
|
|
194
|
+
if (eff.set) out.speed_walk_set = Math.max(out.speed_walk_set, value);
|
|
195
|
+
else out.speed_walk_bonus += value;
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case "attunement-limit": {
|
|
200
|
+
out.attunement_set = Math.max(out.attunement_set, scaled(eff.value, "value", eff.scales_at, level));
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case "sense":
|
|
204
|
+
out.senses[eff.type] = Math.max(out.senses[eff.type], eff.range);
|
|
205
|
+
break;
|
|
206
|
+
case "resistance":
|
|
207
|
+
pushDefenseGrant(out.resistances, eff.damage_type, label, eff.condition);
|
|
208
|
+
break;
|
|
209
|
+
case "immunity":
|
|
210
|
+
pushDefenseGrant(out.immunities, eff.damage_type, label, eff.condition);
|
|
211
|
+
break;
|
|
212
|
+
case "vulnerability":
|
|
213
|
+
pushDefenseGrant(out.vulnerabilities, eff.damage_type, label, eff.condition);
|
|
214
|
+
break;
|
|
215
|
+
case "immune-condition":
|
|
216
|
+
if (!eff.while) pushDefenseGrant(out.condition_immunities, eff.condition, label);
|
|
217
|
+
break;
|
|
218
|
+
case "proficiency": {
|
|
219
|
+
const c = classifyProficiencyEffect(eff);
|
|
220
|
+
if (!c) break;
|
|
221
|
+
if (c.bucket === "saves") {
|
|
222
|
+
pushUnique(out.proficiencies.saves, c.value);
|
|
223
|
+
} else {
|
|
224
|
+
pushUnique(out.proficiencies[c.bucket], c.value);
|
|
225
|
+
}
|
|
226
|
+
if (c.expertise && c.bucket === "skills") pushUnique(out.proficiencies.skillExpertise, c.value);
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case "ac-bonus":
|
|
230
|
+
out.ac_terms.push({
|
|
231
|
+
value: eff.value,
|
|
232
|
+
requires_armor: eff.requires_armor === true,
|
|
233
|
+
label,
|
|
234
|
+
condition: eff.condition
|
|
235
|
+
});
|
|
236
|
+
break;
|
|
237
|
+
case "weapon-ability": {
|
|
238
|
+
if (eff.ability !== "spellcasting") {
|
|
239
|
+
const w = eff.weapons;
|
|
240
|
+
const weaponSlugs = !w || w === "chosen" ? void 0 : Array.isArray(w) ? w.map(bareEntitySlug) : [bareEntitySlug(w)];
|
|
241
|
+
out.weaponAbilities.push({ ability: eff.ability, weaponSlugs });
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
case "roll-modifier": {
|
|
246
|
+
const rolls = eff.roll === "any" ? ["ability-check", "saving-throw", "attack"] : [eff.roll];
|
|
247
|
+
const scopes = (_a = normalizeRollScope(eff.scope, rolls[0])) != null ? _a : [eff.scope];
|
|
248
|
+
for (const roll of rolls) for (const scope of scopes)
|
|
249
|
+
out.rollModifiers.push({ mode: eff.mode, roll, scope, condition: eff.condition, label });
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case "save-outcome":
|
|
253
|
+
out.saveOutcomes.push({
|
|
254
|
+
ability: eff.ability === "any" ? void 0 : eff.ability,
|
|
255
|
+
on_success: eff.on_success,
|
|
256
|
+
on_failure: eff.on_failure,
|
|
257
|
+
appliesTo: (_b = eff.applies_to) != null ? _b : void 0,
|
|
258
|
+
condition: eff.condition,
|
|
259
|
+
label
|
|
260
|
+
});
|
|
261
|
+
break;
|
|
262
|
+
case "crit-range":
|
|
263
|
+
if (((_c = eff.applies_to) != null ? _c : "weapon") !== "spell") out.critRange = Math.min(out.critRange, eff.min_roll);
|
|
264
|
+
break;
|
|
265
|
+
case "extra-attack":
|
|
266
|
+
out.extraAttack = Math.max(out.extraAttack, scaled(eff.count, "count", eff.scales_at, level));
|
|
267
|
+
break;
|
|
268
|
+
case "reroll-damage":
|
|
269
|
+
out.attackNotes.push(`Reroll ${eff.max_reroll}s${eff.once_per_die ? " (once/die)" : ""}`);
|
|
270
|
+
break;
|
|
271
|
+
case "attack-rule":
|
|
272
|
+
if (eff.flag === "no-ranged-in-melee-disadvantage") out.attackNotes.push("No disadvantage firing in melee");
|
|
273
|
+
break;
|
|
274
|
+
case "damage-bonus":
|
|
275
|
+
if (((_d = eff.applies_to) != null ? _d : "weapon") !== "spell") {
|
|
276
|
+
out.damageBonuses.push({
|
|
277
|
+
amount: eff.amount,
|
|
278
|
+
damage_type: eff.damage_type,
|
|
279
|
+
source: label,
|
|
280
|
+
...eff.condition ? { condition: eff.condition } : {}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
break;
|
|
284
|
+
case "ability-score-increase":
|
|
285
|
+
if (eff.abilities === "chosen") break;
|
|
286
|
+
for (const ab of eff.abilities) out.ability_bonus[ab] = ((_e = out.ability_bonus[ab]) != null ? _e : 0) + eff.amount;
|
|
287
|
+
break;
|
|
288
|
+
default:
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export {
|
|
294
|
+
emptyFeatureEffectTotals,
|
|
295
|
+
foldsNow,
|
|
296
|
+
assembleEffectFeatures,
|
|
297
|
+
foldsOnSelf,
|
|
298
|
+
selfEffectsOf,
|
|
299
|
+
computeFeatureEffects,
|
|
300
|
+
classifyProficiencyEffect,
|
|
301
|
+
collectProficiencyEffectGrants
|
|
302
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// src/types/choice.ts
|
|
2
|
+
var ALL_SKILL_SLUGS = [
|
|
3
|
+
"acrobatics",
|
|
4
|
+
"animal-handling",
|
|
5
|
+
"arcana",
|
|
6
|
+
"athletics",
|
|
7
|
+
"deception",
|
|
8
|
+
"history",
|
|
9
|
+
"insight",
|
|
10
|
+
"intimidation",
|
|
11
|
+
"investigation",
|
|
12
|
+
"medicine",
|
|
13
|
+
"nature",
|
|
14
|
+
"perception",
|
|
15
|
+
"performance",
|
|
16
|
+
"persuasion",
|
|
17
|
+
"religion",
|
|
18
|
+
"sleight-of-hand",
|
|
19
|
+
"stealth",
|
|
20
|
+
"survival"
|
|
21
|
+
];
|
|
22
|
+
var STANDARD_LANGUAGES = [
|
|
23
|
+
"common",
|
|
24
|
+
"dwarvish",
|
|
25
|
+
"elvish",
|
|
26
|
+
"giant",
|
|
27
|
+
"gnomish",
|
|
28
|
+
"goblin",
|
|
29
|
+
"halfling",
|
|
30
|
+
"orc"
|
|
31
|
+
];
|
|
32
|
+
var EXOTIC_LANGUAGES = [
|
|
33
|
+
"abyssal",
|
|
34
|
+
"celestial",
|
|
35
|
+
"deep-speech",
|
|
36
|
+
"draconic",
|
|
37
|
+
"infernal",
|
|
38
|
+
"primordial",
|
|
39
|
+
"sylvan",
|
|
40
|
+
"undercommon"
|
|
41
|
+
];
|
|
42
|
+
var SECRET_LANGUAGES = ["druidic", "thieves'-cant"];
|
|
43
|
+
var ALL_LANGUAGES = [
|
|
44
|
+
...STANDARD_LANGUAGES,
|
|
45
|
+
...EXOTIC_LANGUAGES,
|
|
46
|
+
...SECRET_LANGUAGES
|
|
47
|
+
];
|
|
48
|
+
var ARTISANS_TOOLS = [
|
|
49
|
+
"alchemist's-supplies",
|
|
50
|
+
"brewer's-supplies",
|
|
51
|
+
"calligrapher's-supplies",
|
|
52
|
+
"carpenter's-tools",
|
|
53
|
+
"cartographer's-tools",
|
|
54
|
+
"cobbler's-tools",
|
|
55
|
+
"cook's-utensils",
|
|
56
|
+
"glassblower's-tools",
|
|
57
|
+
"jeweler's-tools",
|
|
58
|
+
"leatherworker's-tools",
|
|
59
|
+
"mason's-tools",
|
|
60
|
+
"painter's-supplies",
|
|
61
|
+
"potter's-tools",
|
|
62
|
+
"smith's-tools",
|
|
63
|
+
"tinker's-tools",
|
|
64
|
+
"weaver's-tools",
|
|
65
|
+
"woodcarver's-tools"
|
|
66
|
+
];
|
|
67
|
+
var MUSICAL_INSTRUMENTS = [
|
|
68
|
+
"bagpipes",
|
|
69
|
+
"drum",
|
|
70
|
+
"dulcimer",
|
|
71
|
+
"flute",
|
|
72
|
+
"horn",
|
|
73
|
+
"lute",
|
|
74
|
+
"lyre",
|
|
75
|
+
"pan-flute",
|
|
76
|
+
"shawm",
|
|
77
|
+
"viol"
|
|
78
|
+
];
|
|
79
|
+
var GAMING_SETS = ["dice-set", "playing-cards"];
|
|
80
|
+
var OTHER_TOOLS = [
|
|
81
|
+
"disguise-kit",
|
|
82
|
+
"forgery-kit",
|
|
83
|
+
"herbalism-kit",
|
|
84
|
+
"navigator's-tools",
|
|
85
|
+
"poisoner's-kit",
|
|
86
|
+
"thieves'-tools"
|
|
87
|
+
];
|
|
88
|
+
var ALL_TOOLS = [
|
|
89
|
+
...ARTISANS_TOOLS,
|
|
90
|
+
...MUSICAL_INSTRUMENTS,
|
|
91
|
+
...GAMING_SETS,
|
|
92
|
+
...OTHER_TOOLS
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
ALL_SKILL_SLUGS,
|
|
97
|
+
STANDARD_LANGUAGES,
|
|
98
|
+
EXOTIC_LANGUAGES,
|
|
99
|
+
SECRET_LANGUAGES,
|
|
100
|
+
ALL_LANGUAGES,
|
|
101
|
+
ARTISANS_TOOLS,
|
|
102
|
+
MUSICAL_INSTRUMENTS,
|
|
103
|
+
GAMING_SETS,
|
|
104
|
+
OTHER_TOOLS,
|
|
105
|
+
ALL_TOOLS
|
|
106
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/dnd/warn-once.ts
|
|
2
|
+
var seen = /* @__PURE__ */ new Set();
|
|
3
|
+
function warnOnce(key, msg) {
|
|
4
|
+
if (seen.has(key)) return;
|
|
5
|
+
seen.add(key);
|
|
6
|
+
console.warn(msg);
|
|
7
|
+
}
|
|
8
|
+
function __resetWarnOnceForTests() {
|
|
9
|
+
seen.clear();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
warnOnce,
|
|
14
|
+
__resetWarnOnceForTests
|
|
15
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RACE_STRUCTURAL_PSEUDO
|
|
3
|
+
} from "./chunk-PVCEA4HZ.js";
|
|
4
|
+
import {
|
|
5
|
+
warnOnce
|
|
6
|
+
} from "./chunk-LVQLWRCU.js";
|
|
7
|
+
import {
|
|
8
|
+
slugify
|
|
9
|
+
} from "./chunk-6X4KUDMU.js";
|
|
10
|
+
import {
|
|
11
|
+
mirrorSpellShapes
|
|
12
|
+
} from "./chunk-OZP6JFGT.js";
|
|
13
|
+
import {
|
|
14
|
+
ABILITY_KEYS
|
|
15
|
+
} from "./chunk-WELIKLZF.js";
|
|
16
|
+
|
|
17
|
+
// src/pc/pc.additional-spells.ts
|
|
18
|
+
var entriesOf = (a) => Array.isArray(a) ? a : a && typeof a === "object" ? [a] : [];
|
|
19
|
+
var editionOf = (x) => {
|
|
20
|
+
const e = x == null ? void 0 : x.edition;
|
|
21
|
+
return typeof e === "string" ? e : void 0;
|
|
22
|
+
};
|
|
23
|
+
var prefixOf = (slug) => {
|
|
24
|
+
var _a;
|
|
25
|
+
return (_a = slug.split("_")[0]) != null ? _a : slug;
|
|
26
|
+
};
|
|
27
|
+
function normalizeSpellRef(ref) {
|
|
28
|
+
var _a, _b, _c;
|
|
29
|
+
if (typeof ref !== "string") return null;
|
|
30
|
+
let s = ref.trim();
|
|
31
|
+
const wl = /^\[\[(.+)\]\]$/.exec(s);
|
|
32
|
+
if (wl) {
|
|
33
|
+
const target = (_a = wl[1].split("|")[0]) != null ? _a : "";
|
|
34
|
+
const tail = (_b = target.split("/").pop()) != null ? _b : target;
|
|
35
|
+
const w = slugify(tail.replace(/#.*$/, "").trim());
|
|
36
|
+
return w.length > 0 ? w : null;
|
|
37
|
+
}
|
|
38
|
+
s = s.replace(/#.*$/, "");
|
|
39
|
+
s = (_c = s.split("|")[0]) != null ? _c : "";
|
|
40
|
+
const out = slugify(s.trim());
|
|
41
|
+
return out.length > 0 ? out : null;
|
|
42
|
+
}
|
|
43
|
+
function buildSpellNameIndex(entities) {
|
|
44
|
+
var _a;
|
|
45
|
+
const byName = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const e of entities.search("", "spell", Number.POSITIVE_INFINITY)) {
|
|
47
|
+
const raw = e.data.name;
|
|
48
|
+
const key = slugify(typeof raw === "string" ? raw : e.name);
|
|
49
|
+
const list = (_a = byName.get(key)) != null ? _a : [];
|
|
50
|
+
list.push(e);
|
|
51
|
+
byName.set(key, list);
|
|
52
|
+
}
|
|
53
|
+
return { byName };
|
|
54
|
+
}
|
|
55
|
+
function resolveSpellByName(opts) {
|
|
56
|
+
var _a;
|
|
57
|
+
const { entities, index, nameSlug, carrierSlug, carrierEdition, alreadyCollected } = opts;
|
|
58
|
+
const own = alreadyCollected.find((s) => {
|
|
59
|
+
var _a2;
|
|
60
|
+
return slugify(String((_a2 = s.entity.name) != null ? _a2 : "")) === nameSlug;
|
|
61
|
+
});
|
|
62
|
+
if (own) return { entity: own.entity, slug: own.slug };
|
|
63
|
+
const exact = entities.getByTypeAndSlug("spell", `${prefixOf(carrierSlug)}_spell_${nameSlug}`);
|
|
64
|
+
if (exact) return { entity: mirrorSpellShapes(exact.data), slug: exact.slug };
|
|
65
|
+
const cands = (_a = index.byName.get(nameSlug)) != null ? _a : [];
|
|
66
|
+
if (cands.length === 0) {
|
|
67
|
+
warnOnce(`addspells:miss:${nameSlug}`, `archivist: additional_spells ref "${nameSlug}" matches no spell in the compendium`);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const prefix = prefixOf(carrierSlug);
|
|
71
|
+
let pool = cands.filter((e) => prefixOf(e.slug) === prefix);
|
|
72
|
+
if (pool.length === 0) pool = cands;
|
|
73
|
+
if (carrierEdition !== void 0) {
|
|
74
|
+
const same = pool.filter((e) => editionOf(e.data) === carrierEdition);
|
|
75
|
+
if (same.length > 0) pool = same;
|
|
76
|
+
}
|
|
77
|
+
pool = [...pool].sort((a, b) => a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0);
|
|
78
|
+
if (pool.length > 1) warnOnce(`addspells:multi:${nameSlug}`, `archivist: additional_spells ref "${nameSlug}" matches ${cands.length} spells; using "${pool[0].slug}"`);
|
|
79
|
+
return { entity: mirrorSpellShapes(pool[0].data), slug: pool[0].slug };
|
|
80
|
+
}
|
|
81
|
+
var hasContentSelectInline = (choices) => Array.isArray(choices) && choices.some((c) => (c == null ? void 0 : c.kind) === "select-inline" && typeof c.id === "string" && !RACE_STRUCTURAL_PSEUDO.has(c.id) || Array.isArray(c == null ? void 0 : c.options) && c.options.some((o) => hasContentSelectInline(o == null ? void 0 : o.choices)));
|
|
82
|
+
function raceIsUngated(race) {
|
|
83
|
+
var _a;
|
|
84
|
+
const r = race;
|
|
85
|
+
if (hasContentSelectInline(r.choices)) return false;
|
|
86
|
+
return !((_a = r.traits) != null ? _a : []).some((t) => hasContentSelectInline(t == null ? void 0 : t.choices));
|
|
87
|
+
}
|
|
88
|
+
var readAbility = (v, own) => {
|
|
89
|
+
if (v && typeof v === "object" && "choose" in v) return "choose";
|
|
90
|
+
if (typeof v !== "string") return void 0;
|
|
91
|
+
if (v === "inherit") return own != null ? own : void 0;
|
|
92
|
+
return ABILITY_KEYS.includes(v) ? v : void 0;
|
|
93
|
+
};
|
|
94
|
+
var entryHasInSliceLeaf = (e, comparand, own) => readAbility(e.ability, own) !== "choose" && ["known", "prepared"].some((b) => {
|
|
95
|
+
const lv = e[b];
|
|
96
|
+
return !!lv && typeof lv === "object" && !Array.isArray(lv) && Object.entries(lv).some(([k, v]) => (k === "_" || Number(k) <= comparand) && Array.isArray(v) && v.some((x) => typeof x === "string"));
|
|
97
|
+
});
|
|
98
|
+
function collectAdditionalSpells(args) {
|
|
99
|
+
const { race, feats, classes, totalLevel, ownAbility, alreadyCollected, entities, warnings } = args;
|
|
100
|
+
let index = null;
|
|
101
|
+
const indexOf = () => index != null ? index : index = buildSpellNameIndex(entities);
|
|
102
|
+
const out = [];
|
|
103
|
+
const carriers = [];
|
|
104
|
+
const asRaw = (x) => x == null ? void 0 : x.additional_spells;
|
|
105
|
+
if (race && raceIsUngated(race)) carriers.push({ root: "race", slug: race.slug, edition: editionOf(race), entries: entriesOf(asRaw(race)), comparand: totalLevel, classSlug: null });
|
|
106
|
+
for (const f of feats) carriers.push({ root: "feat", slug: f.slug, edition: editionOf(f), entries: entriesOf(asRaw(f)), comparand: totalLevel, classSlug: null });
|
|
107
|
+
for (const c of classes) {
|
|
108
|
+
if (!c.entity) continue;
|
|
109
|
+
carriers.push({ root: "class", slug: c.entity.slug, edition: editionOf(c.entity), entries: entriesOf(asRaw(c.entity)), comparand: c.level, classSlug: c.entity.slug });
|
|
110
|
+
if (c.subclass) carriers.push({ root: "subclass", slug: c.subclass.slug, edition: editionOf(c.subclass), entries: entriesOf(asRaw(c.subclass)), comparand: c.level, classSlug: c.entity.slug });
|
|
111
|
+
}
|
|
112
|
+
for (const car of carriers) {
|
|
113
|
+
if (car.entries.length === 0) continue;
|
|
114
|
+
if (car.entries.length > 1) {
|
|
115
|
+
if (car.entries.some((e) => entryHasInSliceLeaf(e, car.comparand, ownAbility))) warnOnce(`addspells:siblings:${car.slug}`, `archivist: additional_spells on "${car.slug}" has ${car.entries.length} sibling entries (a choose-one grant); skipped`);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const entry = car.entries[0];
|
|
119
|
+
const ability = readAbility(entry.ability, ownAbility);
|
|
120
|
+
if (ability === "choose") continue;
|
|
121
|
+
for (const bucket of ["known", "prepared"]) {
|
|
122
|
+
const levels = entry[bucket];
|
|
123
|
+
if (!levels || typeof levels !== "object" || Array.isArray(levels)) continue;
|
|
124
|
+
for (const [k, v] of Object.entries(levels)) {
|
|
125
|
+
if (!(k === "_" || Number(k) <= car.comparand)) continue;
|
|
126
|
+
if (!Array.isArray(v)) {
|
|
127
|
+
warnOnce(`addspells:usage:${car.slug}:${bucket}:${k}`, `archivist: additional_spells on "${car.slug}" ${bucket}.${k} is a usage economy (skipped; G8)`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const leaf of v) {
|
|
131
|
+
const nameSlug = normalizeSpellRef(leaf);
|
|
132
|
+
if (!nameSlug) continue;
|
|
133
|
+
const hit = resolveSpellByName({ entities, index: indexOf(), nameSlug, carrierSlug: car.slug, carrierEdition: car.edition, alreadyCollected: [...alreadyCollected, ...out] });
|
|
134
|
+
if (!hit) {
|
|
135
|
+
warnings.push(`additional_spells ref "${String(leaf)}" on ${car.slug} not found in compendium.`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
out.push({
|
|
139
|
+
entity: hit.entity,
|
|
140
|
+
slug: hit.slug,
|
|
141
|
+
classSlug: car.classSlug,
|
|
142
|
+
source: car.root === "race" ? "race" : car.root === "feat" ? "feat" : "class",
|
|
143
|
+
prepared: true,
|
|
144
|
+
alwaysPrepared: true,
|
|
145
|
+
...ability ? { ability } : {}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export {
|
|
155
|
+
prefixOf,
|
|
156
|
+
normalizeSpellRef,
|
|
157
|
+
buildSpellNameIndex,
|
|
158
|
+
resolveSpellByName,
|
|
159
|
+
raceIsUngated,
|
|
160
|
+
collectAdditionalSpells
|
|
161
|
+
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
raceEntitySchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UMWIPLLM.js";
|
|
4
|
+
import {
|
|
5
|
+
aliasFeatureListActionCost
|
|
6
|
+
} from "./chunk-4U55VNC7.js";
|
|
4
7
|
|
|
5
8
|
// src/race/race.parser.ts
|
|
6
9
|
import { parseYaml } from "@archivist-gg/core";
|
|
7
10
|
function parseRace(source) {
|
|
8
11
|
const raw = parseYaml(source, ["name", "slug"]);
|
|
9
12
|
if (!raw.success) return raw;
|
|
13
|
+
aliasFeatureListActionCost(raw.data.traits);
|
|
10
14
|
const result = raceEntitySchema.safeParse({ edition: "2014", ...raw.data });
|
|
11
15
|
if (!result.success) {
|
|
12
16
|
return { success: false, error: `race schema validation failed: ${result.error.message}` };
|