@archivist-gg/dnd5e 0.2.0 → 0.3.1
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 +3 -3
- package/dist/background/background.parser.js +2 -2
- package/dist/background/background.schema.d.ts +8 -0
- package/dist/background/background.schema.js +5 -3
- package/dist/chunk-353WXS6S.js +25 -0
- package/dist/{chunk-DC74TZNA.js → chunk-4LZJXJJS.js} +7 -2
- package/dist/{chunk-DZXSWSBP.js → chunk-5ZBE4CWV.js} +1 -1
- package/dist/{chunk-IXZXFFFW.js → chunk-6IRYTM5V.js} +6 -1
- package/dist/chunk-7TEK5KK7.js +49 -0
- package/dist/{chunk-V6SOFUJW.js → chunk-AIRFC5NQ.js} +3 -1
- package/dist/{chunk-VTYUBINW.js → chunk-BIK4DFWK.js} +1 -1
- package/dist/{chunk-TQZB4OCJ.js → chunk-D3CLOBE3.js} +49 -21
- package/dist/{chunk-3TRGRSCH.js → chunk-LV4SNWZQ.js} +7 -1
- package/dist/{chunk-UC4FE5X3.js → chunk-N6N74K6X.js} +1 -1
- package/dist/chunk-O5EYMKFH.js +6 -0
- package/dist/{chunk-TUIFSVLI.js → chunk-SE7AZDR3.js} +1 -0
- package/dist/{chunk-5WQD3WNZ.js → chunk-TN6ZLTNU.js} +6 -5
- package/dist/{chunk-R5SQHP2X.js → chunk-TNUFPIXI.js} +48 -30
- package/dist/{chunk-J2RYSZMQ.js → chunk-VTTA2ALZ.js} +2 -2
- package/dist/class/class.slug.d.ts +7 -5
- package/dist/class/class.slug.js +1 -1
- package/dist/entities/base-item-resolver.d.ts +9 -0
- package/dist/entities/base-item-resolver.js +6 -4
- package/dist/entities/slug.d.ts +7 -0
- package/dist/entities/slug.js +3 -1
- package/dist/index.js +3902 -3623
- package/dist/item/item.actions-map.d.ts +8 -7
- package/dist/item/item.actions-map.js +3 -4
- package/dist/item/item.ai-schema.d.ts +1 -1
- package/dist/pc/pc.decision-engine.d.ts +13 -3
- package/dist/pc/pc.decision-engine.js +9 -9
- package/dist/pc/pc.equipment.js +6 -12
- package/dist/pc/pc.feature-effects.d.ts +5 -0
- package/dist/pc/pc.feature-effects.js +2 -11
- package/dist/pc/pc.parser.js +1 -1
- package/dist/pc/pc.pools.js +7 -7
- package/dist/pc/pc.proficiencies.d.ts +31 -0
- package/dist/pc/pc.proficiencies.js +96 -23
- package/dist/pc/pc.proficiency-normalize.d.ts +5 -0
- package/dist/pc/pc.proficiency-query.js +5 -1
- package/dist/pc/pc.recalc.d.ts +7 -0
- package/dist/pc/pc.recalc.js +112 -58
- package/dist/pc/pc.resolver.js +7 -7
- package/dist/pc/pc.rest.d.ts +1 -1
- package/dist/pc/pc.rest.js +20 -11
- package/dist/pc/pc.schema.d.ts +10 -4
- package/dist/pc/pc.schema.js +1 -1
- package/dist/pc/pc.slotting.js +3 -3
- package/dist/pc/pc.spellcasting.d.ts +5 -1
- package/dist/pc/pc.spellcasting.js +4 -2
- package/dist/pc/pc.types.d.ts +30 -4
- package/dist/spell/spell.access.js +1 -1
- package/dist/types/choice.d.ts +5 -0
- package/dist/types/choice.js +9 -3
- package/dist/weapon/weapon.classify.d.ts +21 -0
- package/dist/weapon/weapon.classify.js +7 -0
- package/package.json +1 -1
- package/dist/chunk-BH6AMZVY.js +0 -25
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
backgroundCodec
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-BIK4DFWK.js";
|
|
4
|
+
import "../chunk-5ZBE4CWV.js";
|
|
5
|
+
import "../chunk-SE7AZDR3.js";
|
|
6
6
|
import "../chunk-3DZ74AKK.js";
|
|
7
7
|
import "../chunk-NXSWH3AE.js";
|
|
8
8
|
import "../chunk-NXR3BD2V.js";
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export declare const langProfSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
3
|
+
kind: z.ZodLiteral<"fixed">;
|
|
4
|
+
languages: z.ZodArray<z.ZodString>;
|
|
5
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
6
|
+
kind: z.ZodLiteral<"choice">;
|
|
7
|
+
count: z.ZodNumber;
|
|
8
|
+
from: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
|
|
9
|
+
}, z.core.$strip>], "kind">;
|
|
2
10
|
export declare const backgroundEntitySchema: z.ZodObject<{
|
|
3
11
|
slug: z.ZodString;
|
|
4
12
|
name: z.ZodString;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
backgroundEntitySchema
|
|
3
|
-
|
|
2
|
+
backgroundEntitySchema,
|
|
3
|
+
langProfSchema
|
|
4
|
+
} from "../chunk-SE7AZDR3.js";
|
|
4
5
|
import "../chunk-3DZ74AKK.js";
|
|
5
6
|
import "../chunk-NXSWH3AE.js";
|
|
6
7
|
import "../chunk-NXR3BD2V.js";
|
|
7
8
|
export {
|
|
8
|
-
backgroundEntitySchema
|
|
9
|
+
backgroundEntitySchema,
|
|
10
|
+
langProfSchema
|
|
9
11
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bareEntitySlug
|
|
3
|
+
} from "./chunk-LV4SNWZQ.js";
|
|
4
|
+
|
|
5
|
+
// src/weapon/weapon.classify.ts
|
|
6
|
+
var PURE_RANGED_THROWN_SLUGS = /* @__PURE__ */ new Set(["dart", "net"]);
|
|
7
|
+
function classifyWeaponRange(weapon) {
|
|
8
|
+
const has = (p) => weapon.properties.some((x) => x === p);
|
|
9
|
+
const isCatRanged = /ranged/.test(weapon.category);
|
|
10
|
+
const pureRanged = isCatRanged && (has("ammunition") || has("loading") || PURE_RANGED_THROWN_SLUGS.has(bareEntitySlug(weapon.slug)));
|
|
11
|
+
if (pureRanged) {
|
|
12
|
+
return {
|
|
13
|
+
melee: null,
|
|
14
|
+
ranged: weapon.range ? { normal: weapon.range.normal, long: weapon.range.long, thrown: false } : null
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
melee: { reach: 5 + (has("reach") ? 5 : 0) },
|
|
19
|
+
ranged: weapon.range ? { normal: weapon.range.normal, long: weapon.range.long, thrown: true } : null
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
classifyWeaponRange
|
|
25
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
slugify
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LV4SNWZQ.js";
|
|
4
4
|
|
|
5
5
|
// src/entities/base-item-resolver.ts
|
|
6
6
|
function unwrapWikilink(input) {
|
|
@@ -10,6 +10,7 @@ function unwrapWikilink(input) {
|
|
|
10
10
|
const pipe = inner.indexOf("|");
|
|
11
11
|
return pipe === -1 ? inner : inner.slice(0, pipe);
|
|
12
12
|
}
|
|
13
|
+
var FOLDER_TO_TYPE = { Weapons: "weapon", Armor: "armor" };
|
|
13
14
|
function vaultPathToSlug(target) {
|
|
14
15
|
const segments = target.split("/").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
15
16
|
if (segments.length === 0) return "";
|
|
@@ -17,10 +18,13 @@ function vaultPathToSlug(target) {
|
|
|
17
18
|
return slugify(segments[0]);
|
|
18
19
|
}
|
|
19
20
|
const compendiumName = segments[0];
|
|
21
|
+
const typeFolder = segments[segments.length - 2];
|
|
20
22
|
const basename = segments[segments.length - 1];
|
|
21
23
|
const prefix = slugify(compendiumName);
|
|
22
24
|
const nameSlug = slugify(basename);
|
|
23
|
-
|
|
25
|
+
if (!prefix || !nameSlug) return nameSlug;
|
|
26
|
+
const type = FOLDER_TO_TYPE[typeFolder];
|
|
27
|
+
return type ? `${prefix}_${type}_${nameSlug}` : `${prefix}_${nameSlug}`;
|
|
24
28
|
}
|
|
25
29
|
function resolveBaseItem(baseItem, registry) {
|
|
26
30
|
var _a, _b;
|
|
@@ -41,6 +45,7 @@ function resolveBaseItemOfType(baseItem, entityType, registry) {
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
export {
|
|
48
|
+
vaultPathToSlug,
|
|
44
49
|
resolveBaseItem,
|
|
45
50
|
resolveBaseItemOfType
|
|
46
51
|
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-5XUXXAQJ.js";
|
|
4
4
|
import {
|
|
5
5
|
bareSlug
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VTTA2ALZ.js";
|
|
7
7
|
import {
|
|
8
8
|
abilityModifier
|
|
9
9
|
} from "./chunk-NAEPSZAM.js";
|
|
@@ -24,6 +24,10 @@ function resolveSpellcasting(rc) {
|
|
|
24
24
|
table
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
+
function effectiveSpellcastingAbility(classSlug, dataAbility, overrides) {
|
|
28
|
+
var _a, _b;
|
|
29
|
+
return (_b = (_a = overrides.spellcasting_ability_by_class) == null ? void 0 : _a[classSlug]) != null ? _b : dataAbility;
|
|
30
|
+
}
|
|
27
31
|
function normalizeKnownSpell(entry) {
|
|
28
32
|
var _a, _b;
|
|
29
33
|
if (typeof entry === "string") {
|
|
@@ -263,6 +267,7 @@ function computeSpellLimits(inputs) {
|
|
|
263
267
|
|
|
264
268
|
export {
|
|
265
269
|
resolveSpellcasting,
|
|
270
|
+
effectiveSpellcastingAbility,
|
|
266
271
|
normalizeKnownSpell,
|
|
267
272
|
deriveSpellSlots,
|
|
268
273
|
computeSpellLimits
|
|
@@ -0,0 +1,49 @@
|
|
|
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 ALL_LANGUAGES = [...STANDARD_LANGUAGES, ...EXOTIC_LANGUAGES];
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
ALL_SKILL_SLUGS,
|
|
46
|
+
STANDARD_LANGUAGES,
|
|
47
|
+
EXOTIC_LANGUAGES,
|
|
48
|
+
ALL_LANGUAGES
|
|
49
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bareEntitySlug
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LV4SNWZQ.js";
|
|
4
4
|
import {
|
|
5
5
|
ABILITY_KEYS
|
|
6
6
|
} from "./chunk-WP2CX2JO.js";
|
|
@@ -10,6 +10,7 @@ function emptyFeatureEffectTotals() {
|
|
|
10
10
|
return {
|
|
11
11
|
initiative_bonus: 0,
|
|
12
12
|
hp_per_level_bonus: 0,
|
|
13
|
+
hp_per_level_terms: [],
|
|
13
14
|
speed_walk_bonus: 0,
|
|
14
15
|
speed_walk_set: 0,
|
|
15
16
|
senses: { darkvision: 0, blindsight: 0, tremorsense: 0, truesight: 0 },
|
|
@@ -66,6 +67,7 @@ function applyEffect(out, eff, label) {
|
|
|
66
67
|
break;
|
|
67
68
|
case "hp-per-level-bonus":
|
|
68
69
|
out.hp_per_level_bonus += eff.value;
|
|
70
|
+
out.hp_per_level_terms.push({ label, value: eff.value });
|
|
69
71
|
break;
|
|
70
72
|
case "speed-bonus":
|
|
71
73
|
if (eff.mode === "walk") {
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
classifyWeaponRange
|
|
3
|
+
} from "./chunk-353WXS6S.js";
|
|
4
|
+
import {
|
|
5
|
+
normKey
|
|
6
|
+
} from "./chunk-O5EYMKFH.js";
|
|
4
7
|
import {
|
|
5
8
|
defaultSlotForType,
|
|
6
9
|
effectiveArmor,
|
|
7
10
|
isItemEntity,
|
|
8
11
|
isWeaponEntity,
|
|
9
12
|
resolveEntityForEntry
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-N6N74K6X.js";
|
|
11
14
|
import {
|
|
12
15
|
readNumericBonus
|
|
13
16
|
} from "./chunk-LWFTHJIE.js";
|
|
14
17
|
import {
|
|
15
18
|
resolveBaseItem
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4LZJXJJS.js";
|
|
20
|
+
import {
|
|
21
|
+
bareEntitySlug
|
|
22
|
+
} from "./chunk-LV4SNWZQ.js";
|
|
17
23
|
|
|
18
24
|
// src/weapon/weapon-mastery.ts
|
|
19
25
|
var MASTERY = {
|
|
@@ -245,7 +251,14 @@ function assignSlots(resolved, registry, warnings) {
|
|
|
245
251
|
const { entity, entityType } = resolveEntityForEntry(entry.item, registry);
|
|
246
252
|
if (!entity || !entityType) continue;
|
|
247
253
|
const defaultSlot = defaultSlotForType(entityType, entity, registry);
|
|
248
|
-
if (!defaultSlot)
|
|
254
|
+
if (!defaultSlot) {
|
|
255
|
+
if (isItemEntity(entity) && typeof entity.base_item === "string" && entity.base_item.length > 0) {
|
|
256
|
+
warnings.push(
|
|
257
|
+
`Magic item "${entity.name}" base_item ${entity.base_item} did not resolve to a base weapon or armor; not slotted (AC/attack not applied).`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
249
262
|
const placed = { index: i, entity, entityType, entry };
|
|
250
263
|
if (defaultSlot === "mainhand") {
|
|
251
264
|
if (!slots.mainhand) slots.mainhand = placed;
|
|
@@ -265,12 +278,17 @@ function assignSlots(resolved, registry, warnings) {
|
|
|
265
278
|
}
|
|
266
279
|
return slots;
|
|
267
280
|
}
|
|
268
|
-
function computeAC(equippedSlots, resolved, mods, registry) {
|
|
281
|
+
function computeAC(equippedSlots, resolved, mods, registry, warnings) {
|
|
269
282
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
270
283
|
const breakdown = [];
|
|
271
284
|
const armorSlot = equippedSlots.armor;
|
|
272
285
|
const armorEntity = (_a = armorSlot == null ? void 0 : armorSlot.entity) != null ? _a : null;
|
|
273
286
|
const armor = effectiveArmor(armorEntity, registry);
|
|
287
|
+
if (isItemEntity(armorEntity) && typeof armorEntity.base_item === "string" && !armor) {
|
|
288
|
+
warnings.push(
|
|
289
|
+
`Magic armor "${armorEntity.name}" base_item ${armorEntity.base_item} did not resolve to a base armor; AC not applied.`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
274
292
|
let ac = 10;
|
|
275
293
|
if (armor) {
|
|
276
294
|
const armorBase = armor.ac.base + ((_b = armor.ac.flat) != null ? _b : 0);
|
|
@@ -329,8 +347,9 @@ function hasProperty(weapon, prop) {
|
|
|
329
347
|
return weapon.properties.some((p) => p === prop);
|
|
330
348
|
}
|
|
331
349
|
function attackAbility(weapon, mods, weaponAbilities) {
|
|
332
|
-
const
|
|
333
|
-
|
|
350
|
+
const modes = classifyWeaponRange(weapon);
|
|
351
|
+
const finesse = hasProperty(weapon, "finesse");
|
|
352
|
+
if (modes.melee) {
|
|
334
353
|
const scoped = weaponAbilities.find((o2) => {
|
|
335
354
|
var _a;
|
|
336
355
|
return (_a = o2.weaponSlugs) == null ? void 0 : _a.includes(bareEntitySlug(weapon.slug));
|
|
@@ -338,14 +357,14 @@ function attackAbility(weapon, mods, weaponAbilities) {
|
|
|
338
357
|
const glob = weaponAbilities.find((o2) => !o2.weaponSlugs);
|
|
339
358
|
const o = scoped != null ? scoped : glob;
|
|
340
359
|
if (o) return o.ability;
|
|
360
|
+
if (finesse) return mods.dex >= mods.str ? "dex" : "str";
|
|
361
|
+
return "str";
|
|
341
362
|
}
|
|
342
|
-
|
|
343
|
-
if (hasProperty(weapon, "finesse")) return mods.dex >= mods.str ? "dex" : "str";
|
|
344
|
-
return "str";
|
|
363
|
+
return finesse && mods.str > mods.dex ? "str" : "dex";
|
|
345
364
|
}
|
|
346
365
|
function isWeaponSlugProficient(weapon, weaponSlug, profs) {
|
|
347
366
|
const wp = profs.weapons;
|
|
348
|
-
if (wp.specific.
|
|
367
|
+
if (wp.specific.some((e) => e === weaponSlug || normKey(e) === normKey(weapon.name))) return true;
|
|
349
368
|
const cat = weapon.category;
|
|
350
369
|
if (typeof cat === "string") {
|
|
351
370
|
if (/^simple\b/.test(cat) && wp.categories.includes("simple")) return true;
|
|
@@ -420,8 +439,10 @@ function buildAttackRow(args) {
|
|
|
420
439
|
{ source: `${ability.toUpperCase()} modifier`, amount: abilityMod, kind: "ability" },
|
|
421
440
|
{ source: magicSource, amount: magic.dmg, kind: "item" }
|
|
422
441
|
];
|
|
423
|
-
const
|
|
424
|
-
const
|
|
442
|
+
const modes = classifyWeaponRange(weapon);
|
|
443
|
+
const fmtRanged = (r) => `${r.normal}/${r.long} ft`;
|
|
444
|
+
const range = modes.melee ? `${modes.melee.reach} ft` : modes.ranged ? fmtRanged(modes.ranged) : void 0;
|
|
445
|
+
const thrownRange = modes.melee && modes.ranged ? fmtRanged(modes.ranged) : void 0;
|
|
425
446
|
const stringProps = weapon.properties.filter(
|
|
426
447
|
(p) => typeof p === "string"
|
|
427
448
|
);
|
|
@@ -429,7 +450,8 @@ function buildAttackRow(args) {
|
|
|
429
450
|
const row = {
|
|
430
451
|
id,
|
|
431
452
|
name,
|
|
432
|
-
range,
|
|
453
|
+
...range !== void 0 ? { range } : {},
|
|
454
|
+
...thrownRange !== void 0 ? { thrownRange } : {},
|
|
433
455
|
toHit,
|
|
434
456
|
damageDice,
|
|
435
457
|
damageType: (_b = magic.damageTypeOverride) != null ? _b : weapon.damage.type,
|
|
@@ -446,7 +468,7 @@ function buildAttackRow(args) {
|
|
|
446
468
|
breakdown: { toHit: toHitBreakdown, damage: damageBreakdown },
|
|
447
469
|
informational: args.magic.informational.length > 0 ? args.magic.informational : void 0,
|
|
448
470
|
slotKey: args.slotKey,
|
|
449
|
-
subLabel: formatWeaponSubLabel(weapon, finalProps),
|
|
471
|
+
subLabel: formatWeaponSubLabel(weapon, finalProps, modes),
|
|
450
472
|
actionCost: args.actionCost,
|
|
451
473
|
versatile: args.versatileDice ? { damageDice: formatDice(args.versatileDice) } : void 0
|
|
452
474
|
};
|
|
@@ -460,10 +482,16 @@ function buildAttackRow(args) {
|
|
|
460
482
|
}
|
|
461
483
|
return row;
|
|
462
484
|
}
|
|
463
|
-
function
|
|
485
|
+
function humanizeWeaponToken(s) {
|
|
486
|
+
return s.replace(/[-_]/g, " ").replace(/(^|[\s(])\w/g, (c) => c.toUpperCase());
|
|
487
|
+
}
|
|
488
|
+
function formatWeaponSubLabel(weapon, properties, modes) {
|
|
464
489
|
const parts = [];
|
|
465
|
-
if (weapon.category)
|
|
466
|
-
|
|
490
|
+
if (weapon.category) {
|
|
491
|
+
const cat = modes.melee && /ranged/.test(weapon.category) ? weapon.category.replace("ranged", "melee") : weapon.category;
|
|
492
|
+
parts.push(humanizeWeaponToken(cat));
|
|
493
|
+
}
|
|
494
|
+
if (properties.length > 0) parts.push(properties.map(humanizeWeaponToken).join(", "));
|
|
467
495
|
return parts.join(" \xB7 ");
|
|
468
496
|
}
|
|
469
497
|
function computeAttacks(equippedSlots, mods, profs, registry, warnings, proficiencyBonus, ctx, weaponMasteries, weaponAbilities = []) {
|
|
@@ -486,7 +514,7 @@ function computeAttacks(equippedSlots, mods, profs, registry, warnings, proficie
|
|
|
486
514
|
if (found && found.entityType === "weapon" && isWeaponEntity(found.data)) {
|
|
487
515
|
weapon = found.data;
|
|
488
516
|
} else {
|
|
489
|
-
warnings.push(`Magic weapon ${entity.name} references missing base_item
|
|
517
|
+
warnings.push(`Magic weapon ${entity.name} references missing base_item ${baseSlug}.`);
|
|
490
518
|
continue;
|
|
491
519
|
}
|
|
492
520
|
} else {
|
|
@@ -547,7 +575,7 @@ function computeSlotsAndAttacks(resolved, mods, profs, registry, warnings, profi
|
|
|
547
575
|
var _a, _b, _c;
|
|
548
576
|
const equippedSlots = assignSlots(resolved, registry, warnings);
|
|
549
577
|
const overrides = (_a = resolved.definition.overrides) != null ? _a : {};
|
|
550
|
-
const acOut = computeAC(equippedSlots, resolved, mods, registry);
|
|
578
|
+
const acOut = computeAC(equippedSlots, resolved, mods, registry, warnings);
|
|
551
579
|
const ctx = buildConditionContext(resolved, equippedSlots);
|
|
552
580
|
const attacks = computeAttacks(equippedSlots, mods, profs, registry, warnings, proficiencyBonus, ctx, weaponMasteries, weaponAbilities);
|
|
553
581
|
return {
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
function slugify(name) {
|
|
3
3
|
return name.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/[\s]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
4
4
|
}
|
|
5
|
+
function bareEntitySlug(slug) {
|
|
6
|
+
if (!slug) return "";
|
|
7
|
+
const p = slug.split("_");
|
|
8
|
+
return p.length >= 3 ? p.slice(2).join("_") : p[p.length - 1];
|
|
9
|
+
}
|
|
5
10
|
|
|
6
11
|
export {
|
|
7
|
-
slugify
|
|
12
|
+
slugify,
|
|
13
|
+
bareEntitySlug
|
|
8
14
|
};
|
|
@@ -94,14 +94,15 @@ var characterOverridesSchema = z.object({
|
|
|
94
94
|
investigation: z.number().int().optional(),
|
|
95
95
|
insight: z.number().int().optional()
|
|
96
96
|
}).partial().optional(),
|
|
97
|
-
hp: z.object({
|
|
97
|
+
hp: z.object({
|
|
98
|
+
max: z.number().int().positive().optional(),
|
|
99
|
+
rolled: z.number().int().positive().optional(),
|
|
100
|
+
modifier: z.number().int().optional()
|
|
101
|
+
}).optional(),
|
|
98
102
|
ac: z.number().int().optional(),
|
|
99
103
|
speed: z.number().int().optional(),
|
|
100
104
|
initiative: z.number().int().optional(),
|
|
101
|
-
|
|
102
|
-
saveDC: z.number().int().optional(),
|
|
103
|
-
attackBonus: z.number().int().optional()
|
|
104
|
-
}).optional(),
|
|
105
|
+
spellcasting_ability_by_class: z.record(z.string(), abilityEnum).optional(),
|
|
105
106
|
// Character-level DC-ability fallback for own-ability-less spells (e.g. a Spell
|
|
106
107
|
// Scroll cast by a non-caster). Same ability enum as the per-instance spell_ability.
|
|
107
108
|
spellcasting_ability: abilityEnum.optional(),
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ALL_LANGUAGES,
|
|
2
3
|
ALL_SKILL_SLUGS
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
isItemEntity,
|
|
6
|
-
resolveEntityForEntry
|
|
7
|
-
} from "./chunk-UC4FE5X3.js";
|
|
4
|
+
} from "./chunk-7TEK5KK7.js";
|
|
8
5
|
import {
|
|
6
|
+
effectiveSpellcastingAbility,
|
|
9
7
|
normalizeKnownSpell,
|
|
10
8
|
resolveSpellcasting
|
|
11
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6IRYTM5V.js";
|
|
12
10
|
import {
|
|
13
11
|
readTableColumn
|
|
14
12
|
} from "./chunk-5XUXXAQJ.js";
|
|
15
13
|
import {
|
|
16
14
|
recognizeDecision
|
|
17
15
|
} from "./chunk-JEYQU3JT.js";
|
|
16
|
+
import {
|
|
17
|
+
isItemEntity,
|
|
18
|
+
resolveEntityForEntry
|
|
19
|
+
} from "./chunk-N6N74K6X.js";
|
|
20
|
+
import {
|
|
21
|
+
bareEntitySlug
|
|
22
|
+
} from "./chunk-LV4SNWZQ.js";
|
|
18
23
|
import {
|
|
19
24
|
ABILITY_KEYS
|
|
20
25
|
} from "./chunk-WP2CX2JO.js";
|
|
@@ -131,8 +136,9 @@ var PCResolver = class {
|
|
|
131
136
|
}
|
|
132
137
|
}
|
|
133
138
|
const ownSpellcastingAbility = (_h = classes.map((c) => {
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
if (!c.entity) return null;
|
|
140
|
+
const p = resolveSpellcasting(c);
|
|
141
|
+
return p ? effectiveSpellcastingAbility(c.entity.slug, p.ability, character.overrides) : null;
|
|
136
142
|
}).find((a) => a != null)) != null ? _h : null;
|
|
137
143
|
spells.push(...collectItemGrantedSpells(character, ownSpellcastingAbility, this.entities, warnings));
|
|
138
144
|
const chosenMasteries = collectChosenWeaponMasteries(character);
|
|
@@ -483,11 +489,6 @@ function wikilinkTailSlug(link) {
|
|
|
483
489
|
const tail = (_a = inner.split("/").pop()) != null ? _a : inner;
|
|
484
490
|
return tail.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
485
491
|
}
|
|
486
|
-
function bareEntitySlug(slug) {
|
|
487
|
-
if (!slug) return "";
|
|
488
|
-
const i = slug.indexOf("_");
|
|
489
|
-
return i === -1 ? slug : slug.slice(i + 1);
|
|
490
|
-
}
|
|
491
492
|
function matchesFilter(e, where, ownerBare) {
|
|
492
493
|
var _a, _b, _c, _d, _e;
|
|
493
494
|
const d = e.data;
|
|
@@ -547,7 +548,7 @@ function enumerateOptions(choice, ctx, ownerBare) {
|
|
|
547
548
|
return filtered.map((e) => ({ value: e.slug, label: e.name, entity: e }));
|
|
548
549
|
}
|
|
549
550
|
case "select-proficiency": {
|
|
550
|
-
const pool = (_a = choice.from) != null ? _a : choice.domain === "skill" ? [...ALL_SKILL_SLUGS] : [];
|
|
551
|
+
const pool = (_a = choice.from) != null ? _a : choice.domain === "skill" ? [...ALL_SKILL_SLUGS] : choice.domain === "language" ? [...ALL_LANGUAGES] : [];
|
|
551
552
|
return pool.map((v) => ({ value: v, label: v.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) }));
|
|
552
553
|
}
|
|
553
554
|
case "ability-points": {
|
|
@@ -650,22 +651,11 @@ function buildSubclassItem(choice, source, level, featureName, c, ctx, ownerBare
|
|
|
650
651
|
status: selected ? "resolved" : "unresolved"
|
|
651
652
|
};
|
|
652
653
|
}
|
|
653
|
-
function
|
|
654
|
+
function visitProficiencyChoices(resolved, visit) {
|
|
654
655
|
var _a, _b;
|
|
655
|
-
const out = { skills: [], expertise: [], languages: [], tools: [] };
|
|
656
|
-
const apply = (choice, selected) => {
|
|
657
|
-
if (choice.kind !== "select-proficiency") return;
|
|
658
|
-
const vals = Array.isArray(selected) ? selected : typeof selected === "string" ? [selected] : [];
|
|
659
|
-
const pool = choice.from;
|
|
660
|
-
const valid = pool ? vals.filter((v) => pool.includes(v)) : vals;
|
|
661
|
-
const bucket = choice.domain === "skill" ? choice.expertise ? out.expertise : out.skills : choice.domain === "language" ? out.languages : choice.domain === "tool" ? out.tools : null;
|
|
662
|
-
if (bucket) {
|
|
663
|
-
for (const v of valid) if (!bucket.includes(v)) bucket.push(v);
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
656
|
const walk = (choices, read) => {
|
|
667
657
|
for (const ch of choices != null ? choices : []) {
|
|
668
|
-
|
|
658
|
+
visit(ch, read(ch.id));
|
|
669
659
|
if (ch.kind === "select-inline") {
|
|
670
660
|
const sel = read(ch.id);
|
|
671
661
|
const branch = typeof sel === "string" ? ch.options.find((o) => o.value === sel) : void 0;
|
|
@@ -682,7 +672,7 @@ function collectChosenProficiencies(resolved) {
|
|
|
682
672
|
return (_a3 = c.choices[lvl]) == null ? void 0 : _a3[id];
|
|
683
673
|
};
|
|
684
674
|
if (i === 0 && ((_b2 = (_a2 = entity.skill_choices) == null ? void 0 : _a2.from) == null ? void 0 : _b2.length)) {
|
|
685
|
-
|
|
675
|
+
visit({
|
|
686
676
|
kind: "select-proficiency",
|
|
687
677
|
id: "skills",
|
|
688
678
|
count: entity.skill_choices.count,
|
|
@@ -706,11 +696,39 @@ function collectChosenProficiencies(resolved) {
|
|
|
706
696
|
if (resolved.background) {
|
|
707
697
|
walk(resolved.background.choices, originRead("background"));
|
|
708
698
|
if (resolved.background.feature) {
|
|
709
|
-
|
|
699
|
+
const feature = resolved.background.feature;
|
|
700
|
+
walk(feature.choices, originRead("background"));
|
|
710
701
|
}
|
|
711
702
|
}
|
|
703
|
+
}
|
|
704
|
+
function collectChosenProficiencies(resolved) {
|
|
705
|
+
const out = { skills: [], expertise: [], languages: [], tools: [] };
|
|
706
|
+
visitProficiencyChoices(resolved, (choice, selected) => {
|
|
707
|
+
if (choice.kind !== "select-proficiency") return;
|
|
708
|
+
const vals = Array.isArray(selected) ? selected : typeof selected === "string" ? [selected] : [];
|
|
709
|
+
const pool = choice.from;
|
|
710
|
+
const valid = pool ? vals.filter((v) => pool.includes(v)) : vals;
|
|
711
|
+
const bucket = choice.domain === "skill" ? choice.expertise ? out.expertise : out.skills : choice.domain === "language" ? out.languages : choice.domain === "tool" ? out.tools : null;
|
|
712
|
+
if (bucket) {
|
|
713
|
+
for (const v of valid) if (!bucket.includes(v)) bucket.push(v);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
712
716
|
return out;
|
|
713
717
|
}
|
|
718
|
+
function collectLanguageToolChoiceStatus(resolved) {
|
|
719
|
+
const languages = [];
|
|
720
|
+
const tools = [];
|
|
721
|
+
visitProficiencyChoices(resolved, (choice, selected) => {
|
|
722
|
+
if (choice.kind !== "select-proficiency") return;
|
|
723
|
+
if (choice.domain !== "language" && choice.domain !== "tool") return;
|
|
724
|
+
const vals = Array.isArray(selected) ? selected : typeof selected === "string" ? [selected] : [];
|
|
725
|
+
const pool = choice.from;
|
|
726
|
+
const valid = pool ? vals.filter((v) => pool.includes(v)) : vals;
|
|
727
|
+
const status = { id: choice.id, count: choice.count, selected: valid.length };
|
|
728
|
+
(choice.domain === "language" ? languages : tools).push(status);
|
|
729
|
+
});
|
|
730
|
+
return { languages, tools };
|
|
731
|
+
}
|
|
714
732
|
function collectChosenAbilityPoints(resolved) {
|
|
715
733
|
var _a, _b, _c;
|
|
716
734
|
const oc = (_a = resolved.definition.origin_choices) != null ? _a : {};
|
|
@@ -1084,9 +1102,9 @@ export {
|
|
|
1084
1102
|
collectChosenGrantedFeatures,
|
|
1085
1103
|
collectResolvedFeatures,
|
|
1086
1104
|
wikilinkTailSlug,
|
|
1087
|
-
bareEntitySlug,
|
|
1088
1105
|
__matchesFilterForTest,
|
|
1089
1106
|
collectChosenProficiencies,
|
|
1107
|
+
collectLanguageToolChoiceStatus,
|
|
1090
1108
|
collectChosenAbilityPoints,
|
|
1091
1109
|
buildDecisionLedger
|
|
1092
1110
|
};
|
|
@@ -6,8 +6,8 @@ function bareSlug(ref) {
|
|
|
6
6
|
}
|
|
7
7
|
function baseClassName(ref) {
|
|
8
8
|
const bare = bareSlug(ref);
|
|
9
|
-
const
|
|
10
|
-
return
|
|
9
|
+
const p = bare.split("_");
|
|
10
|
+
return p.length >= 3 ? p.slice(2).join("_") : p[p.length - 1];
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export {
|
|
@@ -2,10 +2,12 @@ export declare function bareSlug(ref: string): string;
|
|
|
2
2
|
/**
|
|
3
3
|
* Bare class name for profile / spell-`classes` lookups.
|
|
4
4
|
*
|
|
5
|
-
* Compendium-qualified slugs are `<compendium>_<name>`
|
|
6
|
-
* `srd-
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Compendium-qualified slugs are `<compendium>_<type>_<name>` (3-part, e.g.
|
|
6
|
+
* `srd-2024_class_wizard`) or the legacy `<compendium>_<name>` (2-part, e.g.
|
|
7
|
+
* `srd-5e_wizard`); compendium/type segments are hyphen-cased, so `_` only ever
|
|
8
|
+
* separates the structural parts from the entity name. Strip that prefix to get
|
|
9
|
+
* the canonical class key (`wizard`). Name-slugs never contain `_`, so a 3-part
|
|
10
|
+
* slug's name is `parts.slice(2).join("_")`. Hand-written/test slugs without a
|
|
11
|
+
* `_` pass through unchanged (`wizard` → `wizard`).
|
|
10
12
|
*/
|
|
11
13
|
export declare function baseClassName(ref: string): string;
|