@archivist-gg/dnd5e 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background/background.codec.js +4 -4
- package/dist/background/background.parser.js +3 -3
- package/dist/background/background.schema.js +2 -2
- package/dist/{chunk-FFULAF2P.js → chunk-2BWAID3Y.js} +2 -2
- package/dist/{chunk-FUK7QSN5.js → chunk-42Y5IJKT.js} +1 -1
- package/dist/{chunk-TPZSPU4L.js → chunk-4JHOHWBI.js} +1 -1
- package/dist/{chunk-ZLG6FQFB.js → chunk-5WQD3WNZ.js} +16 -1
- package/dist/{chunk-FEOB24KP.js → chunk-73HOFQQ5.js} +1 -1
- package/dist/{chunk-YDPWD2T7.js → chunk-7VJ6BOA5.js} +4 -1
- package/dist/{chunk-DABBCYTT.js → chunk-CZPMTQOY.js} +1 -1
- package/dist/{chunk-DRNZJNJP.js → chunk-DZXSWSBP.js} +1 -1
- package/dist/{chunk-RVYYKYFL.js → chunk-GE4TLPWM.js} +1 -1
- package/dist/{chunk-VLKIB33F.js → chunk-K6Q5OUGK.js} +6 -0
- package/dist/{chunk-PMADYMUI.js → chunk-L7E2UT5A.js} +1 -1
- package/dist/{chunk-CDW7R2QT.js → chunk-NXSWH3AE.js} +4 -1
- package/dist/{chunk-IDTDACM6.js → chunk-PHQRGDRQ.js} +1 -1
- package/dist/{chunk-BAXWWGFJ.js → chunk-Q5K6LWBZ.js} +1 -1
- package/dist/chunk-R5SQHP2X.js +1092 -0
- package/dist/{chunk-Z6GXV2GO.js → chunk-TLYDBT5F.js} +1 -1
- package/dist/{chunk-ADLTSHOR.js → chunk-TQZB4OCJ.js} +16 -12
- package/dist/{chunk-A7T5L7SS.js → chunk-TUIFSVLI.js} +1 -1
- package/dist/{chunk-GEJSJP6E.js → chunk-V6SOFUJW.js} +1 -1
- package/dist/{chunk-Z4TL2V7S.js → chunk-VTYUBINW.js} +1 -1
- package/dist/{chunk-B7UXKNLD.js → chunk-W7HWMMBF.js} +1 -1
- package/dist/{chunk-SSXBYF3P.js → chunk-XLSVNUTK.js} +1 -1
- package/dist/{chunk-GIOH5JB5.js → chunk-XTIQWSFM.js} +1 -1
- package/dist/{chunk-PKC77VZP.js → chunk-ZZGEVJKX.js} +1 -1
- package/dist/class/class.codec.js +5 -5
- package/dist/class/class.parser.js +4 -4
- package/dist/class/class.schema.js +3 -3
- package/dist/feat/feat.codec.js +4 -4
- package/dist/feat/feat.parser.js +3 -3
- package/dist/feat/feat.schema.js +2 -2
- package/dist/index.js +321 -75
- package/dist/item/item.actions-map.d.ts +10 -3
- package/dist/item/item.actions-map.js +13 -8
- package/dist/item/item.codec.js +3 -3
- package/dist/item/item.parser.js +2 -2
- package/dist/item/item.schema.d.ts +3 -0
- package/dist/item/item.schema.js +1 -1
- package/dist/item/item.types.d.ts +8 -0
- package/dist/pc/pc.decision-engine.js +8 -1
- package/dist/pc/pc.equipment.js +7 -3
- package/dist/pc/pc.feature-effects.js +9 -2
- package/dist/pc/pc.parser.js +1 -1
- package/dist/pc/pc.pools.js +8 -3
- package/dist/pc/pc.recalc.js +20 -10
- package/dist/pc/pc.resolver.d.ts +59 -1
- package/dist/pc/pc.resolver.js +20 -342
- package/dist/pc/pc.schema.d.ts +21 -0
- package/dist/pc/pc.schema.js +1 -1
- package/dist/pc/pc.types.d.ts +37 -5
- package/dist/race/race.codec.js +5 -5
- package/dist/race/race.parser.js +4 -4
- package/dist/race/race.schema.js +3 -3
- package/dist/schemas/choice-schema.js +1 -1
- package/dist/schemas/feature-schema.js +2 -2
- package/dist/subclass/subclass.codec.js +5 -5
- package/dist/subclass/subclass.parser.js +4 -4
- package/dist/subclass/subclass.schema.js +3 -3
- package/dist/types/choice.d.ts +5 -0
- package/dist/weapon/weapon-mastery.d.ts +3 -0
- package/package.json +1 -1
- package/dist/chunk-6PXE7T7C.js +0 -79
- package/dist/chunk-TJCNX6UZ.js +0 -521
package/dist/pc/pc.resolver.js
CHANGED
|
@@ -1,356 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
PCResolver,
|
|
3
|
+
collectChosenGrantedFeatures,
|
|
4
|
+
collectChosenWeaponMasteries,
|
|
5
|
+
collectFeatGrantedSpells,
|
|
6
|
+
collectFeatSlugs,
|
|
7
|
+
collectItemGrantedSpells,
|
|
8
|
+
collectResolvedFeatures,
|
|
9
|
+
dedupeResolvedSpells,
|
|
10
|
+
resolveOriginFeat,
|
|
11
|
+
stripSlug
|
|
12
|
+
} from "../chunk-R5SQHP2X.js";
|
|
12
13
|
import "../chunk-BH6AMZVY.js";
|
|
14
|
+
import "../chunk-UC4FE5X3.js";
|
|
15
|
+
import "../chunk-IXZXFFFW.js";
|
|
16
|
+
import "../chunk-5XUXXAQJ.js";
|
|
13
17
|
import "../chunk-JEYQU3JT.js";
|
|
18
|
+
import "../chunk-DC74TZNA.js";
|
|
19
|
+
import "../chunk-3TRGRSCH.js";
|
|
14
20
|
import "../chunk-J2RYSZMQ.js";
|
|
15
21
|
import "../chunk-NAEPSZAM.js";
|
|
16
22
|
import "../chunk-WP2CX2JO.js";
|
|
17
|
-
|
|
18
|
-
// src/pc/pc.resolver.ts
|
|
19
|
-
var SLUG_RE = /^\[\[(.+?)\]\]$/;
|
|
20
|
-
function stripSlug(ref) {
|
|
21
|
-
if (!ref) return null;
|
|
22
|
-
const m = ref.match(SLUG_RE);
|
|
23
|
-
return m ? m[1] : ref;
|
|
24
|
-
}
|
|
25
|
-
var PCResolver = class {
|
|
26
|
-
constructor(entities) {
|
|
27
|
-
this.entities = entities;
|
|
28
|
-
}
|
|
29
|
-
/** Exposed so recalc() can apply equipment bonuses (Pass A / Pass B). */
|
|
30
|
-
get registry() {
|
|
31
|
-
return this.entities;
|
|
32
|
-
}
|
|
33
|
-
resolve(character) {
|
|
34
|
-
var _a, _b, _c, _d, _e;
|
|
35
|
-
const warnings = [];
|
|
36
|
-
const lookup = (rawRef, type) => {
|
|
37
|
-
const slug = stripSlug(rawRef);
|
|
38
|
-
if (!slug) return null;
|
|
39
|
-
const reg = this.entities.getByTypeAndSlug(type, slug);
|
|
40
|
-
if (!reg) {
|
|
41
|
-
warnings.push(`Slug [[${slug}]] not found in compendium as ${type}.`);
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
const data = reg.data;
|
|
45
|
-
return data.slug == null ? { ...data, slug: reg.slug } : data;
|
|
46
|
-
};
|
|
47
|
-
const race = lookup(character.race, "race");
|
|
48
|
-
const background = lookup(character.background, "background");
|
|
49
|
-
const classes = character.class.map((c) => ({
|
|
50
|
-
entity: lookup(c.name, "class"),
|
|
51
|
-
level: c.level,
|
|
52
|
-
subclass: lookup(c.subclass, "subclass"),
|
|
53
|
-
choices: c.choices
|
|
54
|
-
}));
|
|
55
|
-
const featSlugs = collectFeatSlugs(character);
|
|
56
|
-
const feats = [];
|
|
57
|
-
for (const slug of featSlugs) {
|
|
58
|
-
const f = lookup(`[[${slug}]]`, "feat");
|
|
59
|
-
if (f) feats.push(f);
|
|
60
|
-
}
|
|
61
|
-
const totalLevel = classes.reduce((sum, c) => sum + c.level, 0);
|
|
62
|
-
const features = collectResolvedFeatures(race, classes, background, feats);
|
|
63
|
-
const extraFeatures = collectChosenGrantedFeatures(character, classes, this.entities, race, background);
|
|
64
|
-
features.push(...extraFeatures);
|
|
65
|
-
const primaryCasterSlug = (_a = classes.map((c, i) => c.entity && resolveSpellcasting(c) ? stripSlug(character.class[i].name) : null).find((slug) => slug != null)) != null ? _a : null;
|
|
66
|
-
const spells = [];
|
|
67
|
-
for (const raw of (_b = character.spells.known) != null ? _b : []) {
|
|
68
|
-
const n = normalizeKnownSpell(raw);
|
|
69
|
-
const reg = this.entities.getByTypeAndSlug("spell", n.slug);
|
|
70
|
-
if (!reg) {
|
|
71
|
-
warnings.push(`Spell [[${n.slug}]] not found in compendium.`);
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
const entity = reg.data;
|
|
75
|
-
const isCantrip = ((_c = entity.level) != null ? _c : 0) === 0;
|
|
76
|
-
const classSlug = (_d = n.classSlug) != null ? _d : primaryCasterSlug;
|
|
77
|
-
const prep = isCantrip || n.alwaysPrepared ? true : (_e = n.preparedFlag) != null ? _e : false;
|
|
78
|
-
spells.push({ entity, slug: n.slug, classSlug, source: n.source, prepared: prep, alwaysPrepared: n.alwaysPrepared });
|
|
79
|
-
}
|
|
80
|
-
const chosenMasteries = collectChosenWeaponMasteries(character);
|
|
81
|
-
if (chosenMasteries.full.length > 0) {
|
|
82
|
-
const target = features.find(
|
|
83
|
-
(rf) => {
|
|
84
|
-
var _a2;
|
|
85
|
-
return rf.feature.id === "weapon-mastery" || ((_a2 = rf.feature.choices) == null ? void 0 : _a2.some(
|
|
86
|
-
(ch) => ch.kind === "select-entity" && ch.id === "weapon-mastery" && ch.entity_type === "weapon"
|
|
87
|
-
));
|
|
88
|
-
}
|
|
89
|
-
);
|
|
90
|
-
if (target) {
|
|
91
|
-
const names = chosenMasteries.full.map(
|
|
92
|
-
(fullSlug) => {
|
|
93
|
-
var _a2, _b2;
|
|
94
|
-
return (_b2 = (_a2 = this.entities.getByTypeAndSlug("weapon", fullSlug)) == null ? void 0 : _a2.name) != null ? _b2 : fullSlug;
|
|
95
|
-
}
|
|
96
|
-
);
|
|
97
|
-
const entry = { label: "Mastered weapons", description: names.join(", ") };
|
|
98
|
-
target.chosenInline = target.chosenInline ? [...target.chosenInline, entry] : [entry];
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
const resolvedCharacter = {
|
|
102
|
-
definition: character,
|
|
103
|
-
race,
|
|
104
|
-
classes,
|
|
105
|
-
background,
|
|
106
|
-
feats,
|
|
107
|
-
totalLevel,
|
|
108
|
-
features,
|
|
109
|
-
spells,
|
|
110
|
-
pools: [],
|
|
111
|
-
weaponMasteries: chosenMasteries.bare,
|
|
112
|
-
state: character.state
|
|
113
|
-
};
|
|
114
|
-
resolvedCharacter.pools = resolveAllPools(resolvedCharacter, this.entities);
|
|
115
|
-
return { character: resolvedCharacter, warnings };
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
function collectChosenWeaponMasteries(character) {
|
|
119
|
-
var _a, _b;
|
|
120
|
-
const full = /* @__PURE__ */ new Set();
|
|
121
|
-
for (const cls of (_a = character.class) != null ? _a : []) {
|
|
122
|
-
for (const lvl of Object.values((_b = cls.choices) != null ? _b : {})) {
|
|
123
|
-
const pick = lvl["weapon-mastery"];
|
|
124
|
-
if (Array.isArray(pick)) {
|
|
125
|
-
for (const s of pick) if (typeof s === "string") full.add(s);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return { full: [...full], bare: [...new Set([...full].map(bareEntitySlug))] };
|
|
130
|
-
}
|
|
131
|
-
function collectFeatSlugs(character) {
|
|
132
|
-
const slugs = /* @__PURE__ */ new Set();
|
|
133
|
-
for (const c of character.class) {
|
|
134
|
-
for (const [, choiceBlock] of Object.entries(c.choices)) {
|
|
135
|
-
const feat = choiceBlock == null ? void 0 : choiceBlock.feat;
|
|
136
|
-
if (typeof feat === "string") {
|
|
137
|
-
const s = stripSlug(feat);
|
|
138
|
-
if (s) slugs.add(s);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return [...slugs];
|
|
143
|
-
}
|
|
144
|
-
function collectChosenGrantedFeatures(character, classes, registry, race = null, background = null) {
|
|
145
|
-
var _a, _b;
|
|
146
|
-
const out = [];
|
|
147
|
-
classes.forEach((c, i) => {
|
|
148
|
-
var _a2, _b2, _c, _d;
|
|
149
|
-
if (!c.entity) return;
|
|
150
|
-
const entity = c.entity;
|
|
151
|
-
const entry = character.class[i];
|
|
152
|
-
if (!entry) return;
|
|
153
|
-
for (const [lvlStr, atLevel] of Object.entries(entry.choices)) {
|
|
154
|
-
const lvl = Number(lvlStr);
|
|
155
|
-
if (!Number.isFinite(lvl) || lvl > entry.level) continue;
|
|
156
|
-
const features = (_b2 = ((_a2 = entity.features_by_level) != null ? _a2 : {})[lvl]) != null ? _b2 : [];
|
|
157
|
-
const subFeatures = c.subclass ? (_d = ((_c = c.subclass.features_by_level) != null ? _c : {})[lvl]) != null ? _d : [] : [];
|
|
158
|
-
for (const feature of [...features, ...subFeatures]) {
|
|
159
|
-
walkChoiceGrants(feature.choices, atLevel, (granted, suppress) => {
|
|
160
|
-
out.push({
|
|
161
|
-
feature: granted,
|
|
162
|
-
source: { kind: "class", slug: entity.slug, level: lvl },
|
|
163
|
-
...suppress ? { renderSuppressed: true } : {}
|
|
164
|
-
});
|
|
165
|
-
}, registry);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
const oc = (_a = character.origin_choices) != null ? _a : {};
|
|
170
|
-
const originAt = (ns) => {
|
|
171
|
-
const at = {};
|
|
172
|
-
for (const [key, val] of Object.entries(oc)) {
|
|
173
|
-
if (key.startsWith(`${ns}:`)) at[key.slice(ns.length + 1)] = val;
|
|
174
|
-
}
|
|
175
|
-
return at;
|
|
176
|
-
};
|
|
177
|
-
if (race) {
|
|
178
|
-
const atRace = originAt("race");
|
|
179
|
-
const emitRace = (granted, suppress) => {
|
|
180
|
-
out.push({
|
|
181
|
-
feature: granted,
|
|
182
|
-
source: { kind: "race", slug: race.slug },
|
|
183
|
-
...suppress ? { renderSuppressed: true } : {}
|
|
184
|
-
});
|
|
185
|
-
};
|
|
186
|
-
walkChoiceGrants(race.choices, atRace, emitRace, registry);
|
|
187
|
-
for (const trait of (_b = race.traits) != null ? _b : []) {
|
|
188
|
-
walkChoiceGrants(trait.choices, atRace, emitRace, registry);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (background) {
|
|
192
|
-
const atBg = originAt("background");
|
|
193
|
-
const emitBg = (granted, suppress) => {
|
|
194
|
-
out.push({
|
|
195
|
-
feature: granted,
|
|
196
|
-
source: { kind: "background", slug: background.slug },
|
|
197
|
-
...suppress ? { renderSuppressed: true } : {}
|
|
198
|
-
});
|
|
199
|
-
};
|
|
200
|
-
walkChoiceGrants(background.choices, atBg, emitBg, registry);
|
|
201
|
-
if (background.feature) {
|
|
202
|
-
walkChoiceGrants(background.feature.choices, atBg, emitBg, registry);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return out;
|
|
206
|
-
}
|
|
207
|
-
function walkChoiceGrants(choices, atLevel, emit, registry) {
|
|
208
|
-
var _a, _b, _c, _d, _e;
|
|
209
|
-
for (const ch of choices != null ? choices : []) {
|
|
210
|
-
const sel = atLevel[ch.id];
|
|
211
|
-
if (ch.kind === "select-entity" && ch.entity_type === "optional-feature") {
|
|
212
|
-
const slugs = Array.isArray(sel) ? sel : typeof sel === "string" ? [sel] : [];
|
|
213
|
-
for (const slug of slugs) {
|
|
214
|
-
const reg = registry.getByTypeAndSlug("optional-feature", (_a = stripSlug(String(slug))) != null ? _a : String(slug));
|
|
215
|
-
if (!reg) continue;
|
|
216
|
-
const d = reg.data;
|
|
217
|
-
emit({
|
|
218
|
-
id: (_b = d.slug) != null ? _b : String(slug),
|
|
219
|
-
name: (_c = d.name) != null ? _c : String(slug),
|
|
220
|
-
description: d.description,
|
|
221
|
-
effects: d.effects,
|
|
222
|
-
...d.action_cost ? { action: d.action_cost } : {}
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (ch.kind === "select-inline") {
|
|
227
|
-
const branch = typeof sel === "string" ? ch.options.find((o) => o.value === sel) : void 0;
|
|
228
|
-
if ((_d = branch == null ? void 0 : branch.effects) == null ? void 0 : _d.length) {
|
|
229
|
-
const wc = (_e = branch.choices) == null ? void 0 : _e.find(
|
|
230
|
-
(c) => c.kind === "select-entity" && c.entity_type === "weapon"
|
|
231
|
-
);
|
|
232
|
-
const picked = wc ? atLevel == null ? void 0 : atLevel[wc.id] : void 0;
|
|
233
|
-
const effects = branch.effects.map(
|
|
234
|
-
(e) => e.kind === "weapon-ability" && e.weapons === "chosen" && typeof picked === "string" ? { ...e, weapons: [bareEntitySlug(picked)] } : e
|
|
235
|
-
);
|
|
236
|
-
emit({ id: `${ch.id}-${branch.value}`, name: branch.label, description: branch.description, effects }, true);
|
|
237
|
-
}
|
|
238
|
-
if (branch == null ? void 0 : branch.choices) walkChoiceGrants(branch.choices, atLevel, emit, registry);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
function resolveChosenInline(feature, atLevel) {
|
|
243
|
-
var _a;
|
|
244
|
-
if (!atLevel) return void 0;
|
|
245
|
-
const picks = [];
|
|
246
|
-
for (const ch of (_a = feature.choices) != null ? _a : []) {
|
|
247
|
-
if (ch.kind !== "select-inline") continue;
|
|
248
|
-
const sel = atLevel[ch.id];
|
|
249
|
-
if (typeof sel !== "string") continue;
|
|
250
|
-
const opt = ch.options.find((o) => o.value === sel);
|
|
251
|
-
if (!opt) continue;
|
|
252
|
-
picks.push({ label: opt.label, description: opt.description });
|
|
253
|
-
}
|
|
254
|
-
return picks.length ? picks : void 0;
|
|
255
|
-
}
|
|
256
|
-
function isAsiSlotFeature(feature) {
|
|
257
|
-
var _a, _b, _c, _d;
|
|
258
|
-
return feature.id === "ability-score-improvement" && !((_a = feature.effects) == null ? void 0 : _a.length) && !((_b = feature.resources) == null ? void 0 : _b.length) && !feature.action && !((_c = feature.sub_features) == null ? void 0 : _c.length) && !((_d = feature.attacks) == null ? void 0 : _d.length);
|
|
259
|
-
}
|
|
260
|
-
function collectResolvedFeatures(race, classes, background, feats) {
|
|
261
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
262
|
-
const out = [];
|
|
263
|
-
for (const c of classes) {
|
|
264
|
-
if (!c.entity) continue;
|
|
265
|
-
const slug = c.entity.slug;
|
|
266
|
-
const byLevel = (_a = c.entity.features_by_level) != null ? _a : {};
|
|
267
|
-
for (const [lvlStr, feats0] of Object.entries(byLevel)) {
|
|
268
|
-
const lvl = parseInt(lvlStr, 10);
|
|
269
|
-
if (Number.isNaN(lvl) || lvl > c.level) continue;
|
|
270
|
-
for (const feat of feats0) {
|
|
271
|
-
const chosenInline = resolveChosenInline(feat, (_b = c.choices) == null ? void 0 : _b[lvl]);
|
|
272
|
-
out.push({
|
|
273
|
-
feature: feat,
|
|
274
|
-
source: { kind: "class", slug, level: lvl },
|
|
275
|
-
...chosenInline ? { chosenInline } : {},
|
|
276
|
-
...isAsiSlotFeature(feat) ? { buildOnly: true } : {}
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
if ((_c = c.entity.resources) == null ? void 0 : _c.length) {
|
|
281
|
-
out.push({
|
|
282
|
-
feature: { name: c.entity.name, resources: c.entity.resources },
|
|
283
|
-
source: { kind: "class", slug, level: 1 }
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
if (c.subclass) {
|
|
287
|
-
const sSlug = c.subclass.slug;
|
|
288
|
-
const sByLevel = (_d = c.subclass.features_by_level) != null ? _d : {};
|
|
289
|
-
for (const [lvlStr, feats0] of Object.entries(sByLevel)) {
|
|
290
|
-
const lvl = parseInt(lvlStr, 10);
|
|
291
|
-
if (Number.isNaN(lvl) || lvl > c.level) continue;
|
|
292
|
-
for (const feat of feats0) {
|
|
293
|
-
const chosenInline = resolveChosenInline(feat, (_e = c.choices) == null ? void 0 : _e[lvl]);
|
|
294
|
-
out.push({
|
|
295
|
-
feature: feat,
|
|
296
|
-
source: { kind: "subclass", slug: sSlug, level: lvl },
|
|
297
|
-
...chosenInline ? { chosenInline } : {},
|
|
298
|
-
...isAsiSlotFeature(feat) ? { buildOnly: true } : {}
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if ((_f = c.subclass.resources) == null ? void 0 : _f.length) {
|
|
303
|
-
out.push({
|
|
304
|
-
feature: { name: c.subclass.name, resources: c.subclass.resources },
|
|
305
|
-
source: { kind: "subclass", slug: sSlug, level: 1 }
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
if (race) {
|
|
311
|
-
const traits = (_g = race.traits) != null ? _g : [];
|
|
312
|
-
for (const feat of traits) {
|
|
313
|
-
out.push({ feature: feat, source: { kind: "race", slug: race.slug } });
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (background) {
|
|
317
|
-
const bgFeature = background.feature;
|
|
318
|
-
if (bgFeature) {
|
|
319
|
-
out.push({ feature: bgFeature, source: { kind: "background", slug: background.slug } });
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
for (const feat of feats) {
|
|
323
|
-
const bundled = (_h = feat.features) != null ? _h : [];
|
|
324
|
-
const entityEffects = (_i = feat.effects) != null ? _i : [];
|
|
325
|
-
if (bundled.length > 0) {
|
|
326
|
-
bundled.forEach((f, i) => {
|
|
327
|
-
var _a2;
|
|
328
|
-
const feature = i === 0 && entityEffects.length > 0 ? { ...f, effects: [...(_a2 = f.effects) != null ? _a2 : [], ...entityEffects] } : f;
|
|
329
|
-
out.push({ feature, source: { kind: "feat", slug: feat.slug } });
|
|
330
|
-
});
|
|
331
|
-
} else {
|
|
332
|
-
const name = (_j = feat.name) != null ? _j : feat.slug;
|
|
333
|
-
const foldedDesc = [feat.description, ...(_k = feat.benefits) != null ? _k : []].filter(Boolean).join("\n\n") || void 0;
|
|
334
|
-
const buildOnly = ((_l = feat.choices) != null ? _l : []).some((c) => c.kind === "ability-points" && c.points >= 2) && !((_m = feat.effects) == null ? void 0 : _m.length) && !((_n = feat.resources) == null ? void 0 : _n.length) && bundled.length === 0;
|
|
335
|
-
out.push({
|
|
336
|
-
feature: {
|
|
337
|
-
name,
|
|
338
|
-
...foldedDesc ? { description: foldedDesc } : {},
|
|
339
|
-
...feat.resources ? { resources: feat.resources } : {},
|
|
340
|
-
...entityEffects.length > 0 ? { effects: entityEffects } : {}
|
|
341
|
-
},
|
|
342
|
-
source: { kind: "feat", slug: feat.slug },
|
|
343
|
-
...buildOnly ? { buildOnly: true } : {}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return out;
|
|
348
|
-
}
|
|
349
23
|
export {
|
|
350
24
|
PCResolver,
|
|
351
25
|
collectChosenGrantedFeatures,
|
|
352
26
|
collectChosenWeaponMasteries,
|
|
27
|
+
collectFeatGrantedSpells,
|
|
353
28
|
collectFeatSlugs,
|
|
29
|
+
collectItemGrantedSpells,
|
|
354
30
|
collectResolvedFeatures,
|
|
31
|
+
dedupeResolvedSpells,
|
|
32
|
+
resolveOriginFeat,
|
|
355
33
|
stripSlug
|
|
356
34
|
};
|
package/dist/pc/pc.schema.d.ts
CHANGED
|
@@ -130,6 +130,19 @@ export declare const characterSchema: z.ZodObject<{
|
|
|
130
130
|
special: "special";
|
|
131
131
|
}>>;
|
|
132
132
|
range: z.ZodOptional<z.ZodString>;
|
|
133
|
+
resist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
134
|
+
immune: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
135
|
+
vulnerable: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
136
|
+
condition_immune: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
137
|
+
spell: z.ZodOptional<z.ZodString>;
|
|
138
|
+
spell_ability: z.ZodOptional<z.ZodEnum<{
|
|
139
|
+
str: "str";
|
|
140
|
+
dex: "dex";
|
|
141
|
+
con: "con";
|
|
142
|
+
int: "int";
|
|
143
|
+
wis: "wis";
|
|
144
|
+
cha: "cha";
|
|
145
|
+
}>>;
|
|
133
146
|
}, z.core.$strict>>;
|
|
134
147
|
state: z.ZodOptional<z.ZodObject<{
|
|
135
148
|
charges: z.ZodOptional<z.ZodObject<{
|
|
@@ -216,6 +229,14 @@ export declare const characterSchema: z.ZodObject<{
|
|
|
216
229
|
saveDC: z.ZodOptional<z.ZodNumber>;
|
|
217
230
|
attackBonus: z.ZodOptional<z.ZodNumber>;
|
|
218
231
|
}, z.core.$strip>>;
|
|
232
|
+
spellcasting_ability: z.ZodOptional<z.ZodEnum<{
|
|
233
|
+
str: "str";
|
|
234
|
+
dex: "dex";
|
|
235
|
+
con: "con";
|
|
236
|
+
int: "int";
|
|
237
|
+
wis: "wis";
|
|
238
|
+
cha: "cha";
|
|
239
|
+
}>>;
|
|
219
240
|
spell_slots: z.ZodOptional<z.ZodRecord<z.ZodCoercedNumber<unknown>, z.ZodNumber>>;
|
|
220
241
|
attunement_limit: z.ZodOptional<z.ZodNumber>;
|
|
221
242
|
}, z.core.$strip>>;
|
package/dist/pc/pc.schema.js
CHANGED
package/dist/pc/pc.types.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export interface EquipmentEntryOverrides {
|
|
|
35
35
|
immune?: string[];
|
|
36
36
|
vulnerable?: string[];
|
|
37
37
|
condition_immune?: string[];
|
|
38
|
+
/** Per-instance spell payload (e.g. the spell a Spell Scroll casts). */
|
|
39
|
+
spell?: string;
|
|
40
|
+
/** Ability governing this instance's spell save DC / attack when cast. */
|
|
41
|
+
spell_ability?: Ability;
|
|
38
42
|
}
|
|
39
43
|
export interface EquipmentEntryState {
|
|
40
44
|
charges?: {
|
|
@@ -92,6 +96,10 @@ export interface KnownSpellObject {
|
|
|
92
96
|
source?: "class" | "feat" | "item" | "race" | "domain";
|
|
93
97
|
prepared?: boolean;
|
|
94
98
|
always_prepared?: boolean;
|
|
99
|
+
/** The spell's own spellcasting ability (e.g. WIS for a Magic Initiate (Cleric)
|
|
100
|
+
* pick). Belongs on the persisted shape for symmetry with ResolvedSpell.ability;
|
|
101
|
+
* feat spells arrive via origin_choices, but a known-spell entry may carry it. */
|
|
102
|
+
ability?: Ability;
|
|
95
103
|
}
|
|
96
104
|
export type KnownSpellEntry = string | KnownSpellObject;
|
|
97
105
|
export type PassiveKind = "perception" | "investigation" | "insight";
|
|
@@ -120,6 +128,10 @@ export interface CharacterOverrides {
|
|
|
120
128
|
saveDC?: number;
|
|
121
129
|
attackBonus?: number;
|
|
122
130
|
};
|
|
131
|
+
/** Character-level spellcasting ability for spells that carry no ability of
|
|
132
|
+
* their own (e.g. a Spell Scroll cast by a non-caster): the DC-ability fallback
|
|
133
|
+
* after a per-instance override and the character's own class ability. */
|
|
134
|
+
spellcasting_ability?: Ability;
|
|
123
135
|
spell_slots?: Record<number, number>;
|
|
124
136
|
attunement_limit?: number;
|
|
125
137
|
}
|
|
@@ -267,6 +279,14 @@ export interface ResolvedSpell {
|
|
|
267
279
|
source: "class" | "feat" | "item" | "race" | "domain";
|
|
268
280
|
prepared: boolean;
|
|
269
281
|
alwaysPrepared: boolean;
|
|
282
|
+
/** The spell's OWN spellcasting ability. For a feat spell this carries the
|
|
283
|
+
* chosen ability (e.g. WIS for Magic Initiate (Cleric)) while classSlug stays
|
|
284
|
+
* null (a feat spell is not owned by a class for DC/ability). Absent/undefined
|
|
285
|
+
* for class spells, which derive their ability from classSlug. */
|
|
286
|
+
ability?: Ability | null;
|
|
287
|
+
/** Index of the originating equipment entry when this spell was granted by a
|
|
288
|
+
* per-instance item (e.g. a Spell Scroll). Absent for class/feat/race spells. */
|
|
289
|
+
entryIndex?: number;
|
|
270
290
|
}
|
|
271
291
|
export interface ResolvedClass {
|
|
272
292
|
entity: ClassEntity | null;
|
|
@@ -376,15 +396,17 @@ export interface AttackRow {
|
|
|
376
396
|
attackNotes?: string[];
|
|
377
397
|
/** 2024 Weapon Mastery for this attack, present only when the character has
|
|
378
398
|
* chosen mastery of this weapon AND is proficient with it AND the weapon
|
|
379
|
-
* declares a mastery property. `slug`/`label`/`description` come from
|
|
380
|
-
* mastery glossary
|
|
381
|
-
*
|
|
382
|
-
* for the
|
|
383
|
-
*
|
|
399
|
+
* declares a mastery property. `slug`/`label`/`description`/`gist` come from
|
|
400
|
+
* the mastery glossary (`gist` = a one-line effect summary for the Actions-tab
|
|
401
|
+
* mastery column, e.g. "on fail: Prone"); `derived` carries the computed
|
|
402
|
+
* number for the two numeric masteries (topple → Save DC, graze → On-miss
|
|
403
|
+
* damage) and is absent for the rest. Additive + display-only, absent
|
|
404
|
+
* (undefined) on untouched rows so nothing else changes. */
|
|
384
405
|
mastery?: {
|
|
385
406
|
slug: string;
|
|
386
407
|
label: string;
|
|
387
408
|
description: string;
|
|
409
|
+
gist?: string;
|
|
388
410
|
derived?: {
|
|
389
411
|
label: string;
|
|
390
412
|
value: number;
|
|
@@ -480,6 +502,16 @@ export interface DerivedStats {
|
|
|
480
502
|
} | null;
|
|
481
503
|
/** One entry per casting class (multiclass-aware). Empty for non-casters. */
|
|
482
504
|
spellcastingClasses: SpellcastingClassInfo[];
|
|
505
|
+
/** Save DC / attack bonus for spells that carry their OWN spellcasting
|
|
506
|
+
* ability rather than a class (feat-granted spells, e.g. Magic Initiate).
|
|
507
|
+
* Keyed by that ability; populated for every ability a feat spell uses.
|
|
508
|
+
* Computed independently of class casters, so a NON-caster (empty
|
|
509
|
+
* spellcastingClasses / null spellcasting) still gets a real DC/attack.
|
|
510
|
+
* Empty object when the character has no such spell. */
|
|
511
|
+
abilitySpellcasting: Partial<Record<Ability, {
|
|
512
|
+
saveDC: number;
|
|
513
|
+
attackBonus: number;
|
|
514
|
+
}>>;
|
|
483
515
|
/** Derived standard slot totals by spell level (before user override). */
|
|
484
516
|
derivedSpellSlots: Record<number, number>;
|
|
485
517
|
/** Warlock Pact Magic, or null. */
|
package/dist/race/race.codec.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
raceCodec
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-ZZGEVJKX.js";
|
|
4
|
+
import "../chunk-PHQRGDRQ.js";
|
|
5
|
+
import "../chunk-2BWAID3Y.js";
|
|
6
|
+
import "../chunk-CZPMTQOY.js";
|
|
7
7
|
import "../chunk-WYGD2UGN.js";
|
|
8
8
|
import "../chunk-PZOWBI3B.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-NXSWH3AE.js";
|
|
10
10
|
import "../chunk-NXR3BD2V.js";
|
|
11
11
|
export {
|
|
12
12
|
raceCodec
|
package/dist/race/race.parser.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseRace
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-PHQRGDRQ.js";
|
|
4
|
+
import "../chunk-2BWAID3Y.js";
|
|
5
|
+
import "../chunk-CZPMTQOY.js";
|
|
6
6
|
import "../chunk-WYGD2UGN.js";
|
|
7
7
|
import "../chunk-PZOWBI3B.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-NXSWH3AE.js";
|
|
9
9
|
import "../chunk-NXR3BD2V.js";
|
|
10
10
|
export {
|
|
11
11
|
parseRace
|
package/dist/race/race.schema.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
raceEntitySchema
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-2BWAID3Y.js";
|
|
4
|
+
import "../chunk-CZPMTQOY.js";
|
|
5
5
|
import "../chunk-WYGD2UGN.js";
|
|
6
6
|
import "../chunk-PZOWBI3B.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-NXSWH3AE.js";
|
|
8
8
|
import "../chunk-NXR3BD2V.js";
|
|
9
9
|
export {
|
|
10
10
|
raceEntitySchema
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
featureSchema
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-CZPMTQOY.js";
|
|
4
4
|
import "../chunk-WYGD2UGN.js";
|
|
5
5
|
import "../chunk-PZOWBI3B.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-NXSWH3AE.js";
|
|
7
7
|
import "../chunk-NXR3BD2V.js";
|
|
8
8
|
export {
|
|
9
9
|
featureSchema
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
subclassCodec
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-XLSVNUTK.js";
|
|
4
|
+
import "../chunk-L7E2UT5A.js";
|
|
5
|
+
import "../chunk-W7HWMMBF.js";
|
|
6
6
|
import "../chunk-4TIJKN6O.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-CZPMTQOY.js";
|
|
8
8
|
import "../chunk-WYGD2UGN.js";
|
|
9
9
|
import "../chunk-PZOWBI3B.js";
|
|
10
|
-
import "../chunk-
|
|
10
|
+
import "../chunk-NXSWH3AE.js";
|
|
11
11
|
import "../chunk-NXR3BD2V.js";
|
|
12
12
|
export {
|
|
13
13
|
subclassCodec
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseSubclass
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-L7E2UT5A.js";
|
|
4
|
+
import "../chunk-W7HWMMBF.js";
|
|
5
5
|
import "../chunk-4TIJKN6O.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-CZPMTQOY.js";
|
|
7
7
|
import "../chunk-WYGD2UGN.js";
|
|
8
8
|
import "../chunk-PZOWBI3B.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-NXSWH3AE.js";
|
|
10
10
|
import "../chunk-NXR3BD2V.js";
|
|
11
11
|
export {
|
|
12
12
|
parseSubclass
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
subclassEntitySchema
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-W7HWMMBF.js";
|
|
4
4
|
import "../chunk-4TIJKN6O.js";
|
|
5
|
-
import "../chunk-
|
|
5
|
+
import "../chunk-CZPMTQOY.js";
|
|
6
6
|
import "../chunk-WYGD2UGN.js";
|
|
7
7
|
import "../chunk-PZOWBI3B.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-NXSWH3AE.js";
|
|
9
9
|
import "../chunk-NXR3BD2V.js";
|
|
10
10
|
export {
|
|
11
11
|
subclassEntitySchema
|
package/dist/types/choice.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export interface EntityFilter {
|
|
|
19
19
|
/** Armor proficiency class. Matched against an armor entity's `category`
|
|
20
20
|
* field exactly; "shield" selects shields. */
|
|
21
21
|
armor_category?: "light" | "medium" | "heavy" | "shield";
|
|
22
|
+
/** Spell axis: filter spells by class list, level, and edition. Set together on a
|
|
23
|
+
* select-entity{entity_type:"spell"} choice (e.g. Magic Initiate). */
|
|
24
|
+
list?: string;
|
|
25
|
+
level?: number;
|
|
26
|
+
edition?: string;
|
|
22
27
|
}
|
|
23
28
|
export interface InlineOption {
|
|
24
29
|
value: string;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export declare const MASTERY: Record<string, {
|
|
2
2
|
label: string;
|
|
3
3
|
description: string;
|
|
4
|
+
gist: string;
|
|
4
5
|
}>;
|
|
5
6
|
export declare function masteryLabel(slug: string): string | undefined;
|
|
7
|
+
/** One-line effect gist for a mastery slug (or undefined for an unknown slug). */
|
|
8
|
+
export declare function masteryGist(slug: string): string | undefined;
|
|
6
9
|
export declare function masteryDerived(slug: string, abilityMod: number, proficiencyBonus: number): {
|
|
7
10
|
label: string;
|
|
8
11
|
value: number;
|