@archivist-gg/dnd5e 0.3.13 → 0.3.17
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 +5 -5
- package/dist/background/background.parser.js +4 -4
- package/dist/background/background.schema.js +3 -3
- package/dist/{chunk-ZMJQUZQH.js → chunk-2JSQE3DD.js} +11 -0
- package/dist/{chunk-Q6UOHCCT.js → chunk-2UDSN6VW.js} +1 -1
- package/dist/chunk-3LWMFYKS.js +57 -0
- package/dist/{chunk-MUIU7RZ3.js → chunk-3MGQFFNE.js} +1 -1
- package/dist/{chunk-T5GZBKIQ.js → chunk-3VZZDKRA.js} +114 -27
- package/dist/{chunk-BRGLDWUP.js → chunk-5MXFW4O3.js} +7 -3
- package/dist/{chunk-RQNJXADQ.js → chunk-6EI737IO.js} +1 -1
- package/dist/{chunk-EL6QPF6I.js → chunk-7ATNGKBS.js} +1 -1
- package/dist/{chunk-XY6X2APM.js → chunk-7Q2VPKCD.js} +17 -0
- package/dist/{chunk-4SLOJXJ4.js → chunk-7QN7XSAK.js} +1 -1
- package/dist/{chunk-MC5YJ4PB.js → chunk-AZRGFUSR.js} +1 -1
- package/dist/{chunk-HJQNK3J5.js → chunk-B3DP42OM.js} +1 -4
- package/dist/{chunk-JNNNBEUH.js → chunk-DOXSFYLH.js} +7 -2
- package/dist/{chunk-X7VZY4ON.js → chunk-FANQVQ3D.js} +2 -2
- package/dist/{chunk-PX25R3DK.js → chunk-G6PBI2BU.js} +1 -1
- package/dist/chunk-HTYMW5CH.js +228 -0
- package/dist/{chunk-VDPRBUU5.js → chunk-JCMUVZIK.js} +1 -1
- package/dist/{chunk-Q5XTEYHD.js → chunk-JSM6JFZR.js} +15 -8
- package/dist/chunk-KR6JF3BF.js +14 -0
- package/dist/{chunk-J53ERF6K.js → chunk-LF5AGJA4.js} +1 -1
- package/dist/{chunk-XJRNEQPD.js → chunk-MBX25B6D.js} +1 -1
- package/dist/{chunk-5KGV6TF7.js → chunk-OFCSP6MT.js} +1 -1
- package/dist/{chunk-QEO7LYTG.js → chunk-ONTGZR5T.js} +127 -71
- package/dist/{chunk-MWCCGPG3.js → chunk-OOSRUJBI.js} +3 -2
- package/dist/{chunk-LVX6TQ3R.js → chunk-PKX6TGRE.js} +2 -2
- package/dist/{chunk-QSBJ6NEM.js → chunk-PQZ4LM4Z.js} +1 -1
- package/dist/chunk-PXVNAY7T.js +84 -0
- package/dist/chunk-QH36K2PU.js +158 -0
- package/dist/{chunk-M2OWVAZG.js → chunk-QZSWN2KT.js} +1 -1
- package/dist/{chunk-6ZY3U6BQ.js → chunk-RYLXAYIQ.js} +1 -1
- package/dist/{chunk-UE6LKRXQ.js → chunk-T6QIF5XR.js} +16 -0
- package/dist/{chunk-4BZ47YJ4.js → chunk-TULQ4FFX.js} +32 -11
- package/dist/{chunk-PLOSP4XQ.js → chunk-UUBJCNQU.js} +11 -2
- package/dist/{chunk-DPM6V7YX.js → chunk-VRV3NNTA.js} +1 -1
- package/dist/{chunk-R2V5LN3I.js → chunk-Y5VVP73C.js} +1 -1
- package/dist/{chunk-FPGXFD2M.js → chunk-YD2VAHA6.js} +2 -2
- package/dist/{chunk-G7ED5H2G.js → chunk-YKL6SHRJ.js} +1 -1
- package/dist/class/class.codec.js +6 -6
- package/dist/class/class.parser.js +5 -5
- package/dist/class/class.schema.d.ts +8 -0
- package/dist/class/class.schema.js +4 -4
- package/dist/class/class.standard-array.d.ts +16 -0
- package/dist/class/class.standard-array.js +20 -0
- package/dist/class/class.types.d.ts +3 -0
- package/dist/dnd/amount-text.d.ts +38 -0
- package/dist/dnd/amount-text.js +13 -0
- package/dist/dnd/resource-words.d.ts +4 -0
- package/dist/dnd/resource-words.js +30 -0
- package/dist/feat/feat.codec.js +5 -5
- package/dist/feat/feat.parser.js +4 -4
- package/dist/feat/feat.schema.d.ts +25 -0
- package/dist/feat/feat.schema.js +3 -3
- package/dist/index.js +332 -70
- package/dist/monster/monster.parser.js +5 -5
- package/dist/monster/monster.schema.js +4 -4
- package/dist/optional-feature/optional-feature.codec.js +5 -4
- package/dist/optional-feature/optional-feature.parser.js +4 -3
- package/dist/optional-feature/optional-feature.schema.d.ts +26 -0
- package/dist/optional-feature/optional-feature.schema.js +3 -2
- package/dist/pc/pc.additional-spells.js +2 -2
- package/dist/pc/pc.amount-bindings.d.ts +32 -0
- package/dist/pc/pc.amount-bindings.js +23 -0
- package/dist/pc/pc.decision-engine.d.ts +3 -0
- package/dist/pc/pc.decision-engine.js +14 -13
- package/dist/pc/pc.entry-overrides.js +3 -3
- package/dist/pc/pc.equipment.js +6 -5
- package/dist/pc/pc.feature-effects.d.ts +12 -0
- package/dist/pc/pc.feature-effects.js +1 -1
- package/dist/pc/pc.infusions.d.ts +9 -1
- package/dist/pc/pc.infusions.js +3 -3
- package/dist/pc/pc.member-choices.d.ts +90 -0
- package/dist/pc/pc.member-choices.js +32 -0
- package/dist/pc/pc.parser.js +1 -1
- package/dist/pc/pc.pools.d.ts +7 -0
- package/dist/pc/pc.pools.js +5 -3
- package/dist/pc/pc.proficiencies.js +13 -12
- package/dist/pc/pc.proficiency-query.js +6 -0
- package/dist/pc/pc.recalc.js +75 -58
- package/dist/pc/pc.resolver.d.ts +2 -1
- package/dist/pc/pc.resolver.js +13 -12
- package/dist/pc/pc.rest.js +4 -4
- package/dist/pc/pc.schema.d.ts +29 -2
- package/dist/pc/pc.schema.js +1 -1
- package/dist/pc/pc.types.d.ts +45 -2
- package/dist/pc/pc.weapon-grant.d.ts +15 -0
- package/dist/pc/pool-layout.d.ts +3 -9
- package/dist/pc/pool-layout.js +1 -3
- package/dist/pc/resource-equivalents.d.ts +44 -0
- package/dist/pc/spell-source.js +4 -9
- package/dist/race/race.codec.js +6 -6
- package/dist/race/race.parser.js +5 -5
- package/dist/race/race.schema.js +4 -4
- package/dist/schemas/choice-schema.js +2 -2
- package/dist/schemas/feature-effect-schema.d.ts +25 -0
- package/dist/schemas/feature-effect-schema.js +1 -1
- package/dist/schemas/feature-schema.js +3 -3
- package/dist/spell/spell.codec.js +2 -2
- package/dist/spell/spell.parser.d.ts +6 -1
- package/dist/spell/spell.parser.js +5 -3
- package/dist/spell/spell.types.d.ts +10 -0
- package/dist/subclass/subclass.codec.js +6 -6
- package/dist/subclass/subclass.parser.js +5 -5
- package/dist/subclass/subclass.schema.js +4 -4
- package/dist/types/choice.d.ts +5 -0
- package/dist/types/feature-effect.d.ts +6 -0
- package/dist/types/optional-feature.types.d.ts +5 -0
- package/dist/types/selection-pool.d.ts +2 -2
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
backgroundCodec
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-2UDSN6VW.js";
|
|
4
|
+
import "../chunk-6EI737IO.js";
|
|
5
|
+
import "../chunk-3MGQFFNE.js";
|
|
6
6
|
import "../chunk-IN4M4X7P.js";
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-5MXFW4O3.js";
|
|
8
|
+
import "../chunk-2JSQE3DD.js";
|
|
9
9
|
import "../chunk-UA5KS2PL.js";
|
|
10
10
|
export {
|
|
11
11
|
backgroundCodec
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseBackground
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-6EI737IO.js";
|
|
4
|
+
import "../chunk-3MGQFFNE.js";
|
|
5
5
|
import "../chunk-IN4M4X7P.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-5MXFW4O3.js";
|
|
7
|
+
import "../chunk-2JSQE3DD.js";
|
|
8
8
|
import "../chunk-UA5KS2PL.js";
|
|
9
9
|
export {
|
|
10
10
|
parseBackground
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
backgroundEntitySchema,
|
|
3
3
|
langProfSchema
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-3MGQFFNE.js";
|
|
5
5
|
import "../chunk-IN4M4X7P.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-5MXFW4O3.js";
|
|
7
|
+
import "../chunk-2JSQE3DD.js";
|
|
8
8
|
import "../chunk-UA5KS2PL.js";
|
|
9
9
|
export {
|
|
10
10
|
backgroundEntitySchema,
|
|
@@ -7,6 +7,17 @@ var weaponScopeEnum = z.enum(["weapon", "melee", "ranged", "unarmed", "finesse",
|
|
|
7
7
|
var featureEffectSchema = z.discriminatedUnion("kind", [
|
|
8
8
|
z.object({ kind: z.literal("initiative-bonus"), value: z.number().int(), condition: conditionField, subject: subjectField }),
|
|
9
9
|
z.object({ kind: z.literal("skill-bonus"), skills: z.array(z.string().min(1)).nonempty(), ability: abilityEnum, minimum: z.number().int().optional(), condition: conditionField, subject: subjectField }),
|
|
10
|
+
// SL (2026-09-27): the `skill-bonus` twin for saving throws (Aura of Protection). `saves` absent = every save; `inactive_while` turns
|
|
11
|
+
// the bonus off while the character is incapacitated (any condition that disables actions) or unconscious.
|
|
12
|
+
z.object({
|
|
13
|
+
kind: z.literal("save-bonus"),
|
|
14
|
+
ability: abilityEnum,
|
|
15
|
+
minimum: z.number().int().optional(),
|
|
16
|
+
saves: z.array(abilityEnum).nonempty().optional(),
|
|
17
|
+
inactive_while: z.enum(["incapacitated", "unconscious"]).optional(),
|
|
18
|
+
condition: conditionField,
|
|
19
|
+
subject: subjectField
|
|
20
|
+
}),
|
|
10
21
|
// Jack of All Trades / Remarkable Athlete: half the proficiency bonus, and ONLY on a skill the character is
|
|
11
22
|
// not already proficient in — the "doesn't otherwise use your Proficiency Bonus" clause is the whole mechanic,
|
|
12
23
|
// so it is the kind's semantics, not a field. `skills` omitted = every skill (Jack of All Trades); a list
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyWeaponRange
|
|
3
|
+
} from "./chunk-YDGMBMYI.js";
|
|
4
|
+
|
|
5
|
+
// src/pc/pc.weapon-grant.ts
|
|
6
|
+
var PROPERTY_KEY = {
|
|
7
|
+
"2h": "two_handed",
|
|
8
|
+
"two-handed": "two_handed",
|
|
9
|
+
"two handed": "two_handed",
|
|
10
|
+
h: "heavy",
|
|
11
|
+
l: "light",
|
|
12
|
+
f: "finesse",
|
|
13
|
+
t: "thrown",
|
|
14
|
+
v: "versatile",
|
|
15
|
+
r: "reach",
|
|
16
|
+
a: "ammunition",
|
|
17
|
+
ld: "loading",
|
|
18
|
+
s: "special"
|
|
19
|
+
};
|
|
20
|
+
var propertyKey = (word) => {
|
|
21
|
+
var _a;
|
|
22
|
+
const w = word.trim().toLowerCase();
|
|
23
|
+
return (_a = PROPERTY_KEY[w]) != null ? _a : w.replace(/[\s-]+/g, "_");
|
|
24
|
+
};
|
|
25
|
+
var GRANT_RE = new RegExp(
|
|
26
|
+
"^(?:all\\s+)?(?:(melee|ranged)\\s+)?(simple|martial)(?:\\s+(melee|ranged))?\\s+weapons?\\s+(?:that|which)\\s+(have|has|don'?t\\s+have|do\\s+not\\s+have|doesn'?t\\s+have|does\\s+not\\s+have)\\s+the\\s+(.+?)\\s+propert(?:y|ies)\\.?$",
|
|
27
|
+
"i"
|
|
28
|
+
);
|
|
29
|
+
function parseQualifiedWeaponGrant(text) {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
const m = GRANT_RE.exec(text.replace(/’/g, "'").trim().replace(/\s+/g, " "));
|
|
32
|
+
if (!m) return null;
|
|
33
|
+
const range = (_b = (_a = m[1]) != null ? _a : m[3]) == null ? void 0 : _b.toLowerCase();
|
|
34
|
+
const properties = m[5].split(/\s*,\s*|\s+or\s+|\s+and\s+/i).map((w) => w.trim()).filter(Boolean).map(propertyKey);
|
|
35
|
+
if (properties.length === 0) return null;
|
|
36
|
+
return {
|
|
37
|
+
category: m[2].toLowerCase(),
|
|
38
|
+
...range ? { range } : {},
|
|
39
|
+
properties,
|
|
40
|
+
without: /n'?t|not/i.test(m[4])
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function qualifiedGrantCovers(text, weapon) {
|
|
44
|
+
const g = parseQualifiedWeaponGrant(text);
|
|
45
|
+
if (!g || typeof weapon.category !== "string") return false;
|
|
46
|
+
if (!new RegExp(`^${g.category}\\b`).test(weapon.category)) return false;
|
|
47
|
+
if (g.range) {
|
|
48
|
+
const melee = classifyWeaponRange(weapon).melee !== null;
|
|
49
|
+
if (g.range === "melee" !== melee) return false;
|
|
50
|
+
}
|
|
51
|
+
const has = g.properties.some((p) => weapon.properties.some((x) => x === p));
|
|
52
|
+
return g.without ? !has : has;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
qualifiedGrantCovers
|
|
57
|
+
};
|
|
@@ -3,14 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-W6GC6OKK.js";
|
|
4
4
|
import {
|
|
5
5
|
derivePoolLayout
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-B3DP42OM.js";
|
|
7
7
|
import {
|
|
8
8
|
readTableColumn
|
|
9
9
|
} from "./chunk-IUAAXXNO.js";
|
|
10
|
-
import {
|
|
11
|
-
findItemAction,
|
|
12
|
-
resolveItemAction
|
|
13
|
-
} from "./chunk-ZUQW5VBZ.js";
|
|
14
10
|
import {
|
|
15
11
|
requiresAttunement
|
|
16
12
|
} from "./chunk-IHE6KVC3.js";
|
|
@@ -22,6 +18,10 @@ import {
|
|
|
22
18
|
resolveEntityForEntry,
|
|
23
19
|
slotAcTerms
|
|
24
20
|
} from "./chunk-LHZXYFBU.js";
|
|
21
|
+
import {
|
|
22
|
+
findItemAction,
|
|
23
|
+
resolveItemAction
|
|
24
|
+
} from "./chunk-ZUQW5VBZ.js";
|
|
25
25
|
import {
|
|
26
26
|
warnOnce
|
|
27
27
|
} from "./chunk-LVQLWRCU.js";
|
|
@@ -41,29 +41,93 @@ import {
|
|
|
41
41
|
isCanonicalDamageType
|
|
42
42
|
} from "./chunk-PDJSUH24.js";
|
|
43
43
|
|
|
44
|
+
// src/pc/resource-equivalents.ts
|
|
45
|
+
var RESOURCE_EQUIVALENTS = [
|
|
46
|
+
{ name: "Superiority Dice", ids: ["fighter:superiority-dice", "fighter-2024:superiority-dice"] },
|
|
47
|
+
{ name: "Sorcery Points", ids: ["sorcerer:sorcery-point", "sorcerer:sorcery-points", "sorcerer-2024:sorcery-point"] },
|
|
48
|
+
{ name: "Ki / Focus Points", ids: ["monk:ki", "monk:ki-points", "monk:focus-point", "monk:focus-points"] },
|
|
49
|
+
{ name: "Channel Divinity (Cleric)", ids: ["cleric:channel-divinity", "cleric-2024:channel-divinity", "channel-divinity"] },
|
|
50
|
+
{ name: "Channel Divinity (Paladin)", ids: ["paladin:channel-divinity", "paladin-2024:channel-divinity", "channel-divinity"] },
|
|
51
|
+
{ name: "Bardic Inspiration", ids: ["bard:bardic-inspiration", "bard:bardic-die"] },
|
|
52
|
+
{ name: "Rage", ids: ["barbarian:rage", "barbarian:rages", "barbarian-2024:rages"] },
|
|
53
|
+
{ name: "Wild Shape", ids: ["druid:wild-shape", "druid-2024:wild-shape"] },
|
|
54
|
+
{ name: "Soul Trinkets", ids: ["rogue:soul-trinket", "rogue-2024:soul-trinket"] }
|
|
55
|
+
];
|
|
56
|
+
function equivalentResourceIds(id, groups = RESOURCE_EQUIVALENTS) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const g of groups) {
|
|
59
|
+
if (!g.ids.includes(id)) continue;
|
|
60
|
+
for (const other of g.ids) if (other !== id && !out.includes(other)) out.push(other);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function ownedByClass(res, rc) {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
const s = (_a = res == null ? void 0 : res.owner) == null ? void 0 : _a.source;
|
|
67
|
+
if (!s) return false;
|
|
68
|
+
if (s.kind === "class") return s.slug === ((_b = rc.entity) == null ? void 0 : _b.slug);
|
|
69
|
+
if (s.kind === "subclass") return !!rc.subclass && s.slug === rc.subclass.slug;
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
function payingResourceId(id, index, owner, groups = RESOURCE_EQUIVALENTS) {
|
|
73
|
+
const candidates = [id, ...equivalentResourceIds(id, groups)].filter((c) => index.has(c));
|
|
74
|
+
if (candidates.length === 0) return void 0;
|
|
75
|
+
if (owner) {
|
|
76
|
+
const own = candidates.find((c) => ownedByClass(index.get(c), owner));
|
|
77
|
+
if (own) return own;
|
|
78
|
+
}
|
|
79
|
+
return candidates[0];
|
|
80
|
+
}
|
|
81
|
+
function classOfSource(source, classes) {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
if (source.kind === "class") return (_a = classes.find((c) => {
|
|
84
|
+
var _a2;
|
|
85
|
+
return ((_a2 = c.entity) == null ? void 0 : _a2.slug) === source.slug;
|
|
86
|
+
})) != null ? _a : null;
|
|
87
|
+
if (source.kind === "subclass") return (_b = classes.find((c) => {
|
|
88
|
+
var _a2;
|
|
89
|
+
return ((_a2 = c.subclass) == null ? void 0 : _a2.slug) === source.slug;
|
|
90
|
+
})) != null ? _b : null;
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function bindFeatureConsumers(features, index, classes) {
|
|
94
|
+
return features.map((rf) => {
|
|
95
|
+
const consumes = rf.feature.consumes;
|
|
96
|
+
const id = consumes == null ? void 0 : consumes.resource;
|
|
97
|
+
if (!consumes || typeof id !== "string" || id.length === 0) return rf;
|
|
98
|
+
const payer = payingResourceId(id, index, classOfSource(rf.source, classes));
|
|
99
|
+
if (payer === void 0 || payer === id) return rf;
|
|
100
|
+
return { ...rf, feature: { ...rf.feature, consumes: { ...consumes, resource: payer } } };
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
44
104
|
// src/pc/pc.pools.ts
|
|
45
105
|
function levelPrereqMax(d) {
|
|
46
106
|
var _a;
|
|
47
107
|
return ((_a = d.prerequisites) != null ? _a : []).filter((p) => p.kind === "level").reduce((m, p) => Math.max(m, p.min), 0);
|
|
48
108
|
}
|
|
49
|
-
function
|
|
50
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
51
|
-
const entity = rc.entity;
|
|
52
|
-
const level = rc.level;
|
|
53
|
-
const ownerBare = bareEntitySlug(entity.slug);
|
|
54
|
-
const ownerPrefix = entity.slug.split("_")[0];
|
|
55
|
-
const col = pool.count.column;
|
|
56
|
-
const count = (_a = readTableColumn(entity.table, level, [col])) != null ? _a : 0;
|
|
109
|
+
function poolAnchorLevel(entity, pool) {
|
|
57
110
|
let anchorLevel = 1;
|
|
58
111
|
let columnSeen = false;
|
|
59
112
|
for (let l = 1; l <= 20; l++) {
|
|
60
|
-
const c = readTableColumn(entity.table, l, [
|
|
113
|
+
const c = readTableColumn(entity.table, l, [pool.count.column]);
|
|
61
114
|
if (c != null) columnSeen = true;
|
|
62
115
|
if (c != null && c >= 1) {
|
|
63
116
|
anchorLevel = l;
|
|
64
117
|
break;
|
|
65
118
|
}
|
|
66
119
|
}
|
|
120
|
+
return { anchorLevel, columnSeen };
|
|
121
|
+
}
|
|
122
|
+
function resolvePool(rc, classIndex, pool, registry, index, edition) {
|
|
123
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
124
|
+
const entity = rc.entity;
|
|
125
|
+
const level = rc.level;
|
|
126
|
+
const ownerBare = bareEntitySlug(entity.slug);
|
|
127
|
+
const ownerPrefix = entity.slug.split("_")[0];
|
|
128
|
+
const col = pool.count.column;
|
|
129
|
+
const count = (_a = readTableColumn(entity.table, level, [col])) != null ? _a : 0;
|
|
130
|
+
const { anchorLevel, columnSeen } = poolAnchorLevel(entity, pool);
|
|
67
131
|
if (!columnSeen) {
|
|
68
132
|
warnOnce(
|
|
69
133
|
`pool-column:${pool.id}`,
|
|
@@ -87,10 +151,10 @@ function resolvePool(rc, classIndex, pool, registry, index, edition) {
|
|
|
87
151
|
if (group) group.push(entry);
|
|
88
152
|
else candidates.set(bare, [entry]);
|
|
89
153
|
}
|
|
90
|
-
const
|
|
154
|
+
const collapsed = [];
|
|
91
155
|
for (const [bare, group] of candidates) {
|
|
92
156
|
if (group.length === 1) {
|
|
93
|
-
|
|
157
|
+
collapsed.push(group[0]);
|
|
94
158
|
continue;
|
|
95
159
|
}
|
|
96
160
|
const owned = group.find((e) => e.slug.split("_")[0] === ownerPrefix);
|
|
@@ -100,7 +164,7 @@ function resolvePool(rc, classIndex, pool, registry, index, edition) {
|
|
|
100
164
|
`Archivist: pool "${pool.id}" holds ${group.length} cross-edition entries for "${bare}" and none shares the owner's compendium prefix "${ownerPrefix}"; keeping "${group[0].slug}"`
|
|
101
165
|
);
|
|
102
166
|
}
|
|
103
|
-
|
|
167
|
+
collapsed.push(owned != null ? owned : group[0]);
|
|
104
168
|
}
|
|
105
169
|
const grantDecls = [
|
|
106
170
|
...(_d = (_c = rc.entity) == null ? void 0 : _c.pool_grants) != null ? _d : [],
|
|
@@ -114,6 +178,7 @@ function resolvePool(rc, classIndex, pool, registry, index, edition) {
|
|
|
114
178
|
grantedBare.add(key);
|
|
115
179
|
return true;
|
|
116
180
|
});
|
|
181
|
+
const available = collapsed.filter((e) => !grantedBare.has(bareEntitySlug(e.slug)));
|
|
117
182
|
const bySlugAvailable = new Map(available.map((e) => [e.slug, e]));
|
|
118
183
|
const byBareAvailable = new Map(available.map((e) => [bareEntitySlug(e.slug), e]));
|
|
119
184
|
const resolveStored = (s) => {
|
|
@@ -152,8 +217,9 @@ function resolvePool(rc, classIndex, pool, registry, index, edition) {
|
|
|
152
217
|
const votes = /* @__PURE__ */ new Map();
|
|
153
218
|
for (const e of members) {
|
|
154
219
|
const id = (_h = e.entity.consumes) == null ? void 0 : _h.resource;
|
|
155
|
-
|
|
156
|
-
|
|
220
|
+
const payer = id ? payingResourceId(id, index, rc) : void 0;
|
|
221
|
+
if (!payer) continue;
|
|
222
|
+
votes.set(payer, ((_i = votes.get(payer)) != null ? _i : 0) + 1);
|
|
157
223
|
}
|
|
158
224
|
let resource;
|
|
159
225
|
let bestN = 0;
|
|
@@ -161,7 +227,7 @@ function resolvePool(rc, classIndex, pool, registry, index, edition) {
|
|
|
161
227
|
resource = id;
|
|
162
228
|
bestN = n;
|
|
163
229
|
}
|
|
164
|
-
const bind =
|
|
230
|
+
const bind = poolRebinder(rc, pool.id, resource, index);
|
|
165
231
|
return {
|
|
166
232
|
id: pool.id,
|
|
167
233
|
label: pool.label,
|
|
@@ -184,14 +250,20 @@ function poolRebinder(rc, poolId, vote, index) {
|
|
|
184
250
|
return (e) => {
|
|
185
251
|
const consumes = e.entity.consumes;
|
|
186
252
|
const id = consumes == null ? void 0 : consumes.resource;
|
|
187
|
-
if (!consumes || typeof id !== "string" || id.length === 0
|
|
253
|
+
if (!consumes || typeof id !== "string" || id.length === 0) return e;
|
|
254
|
+
const payer = payingResourceId(id, index, rc);
|
|
255
|
+
if (payer === id) return e;
|
|
256
|
+
const target = payer != null ? payer : vote !== void 0 && !declared.has(id) ? vote : void 0;
|
|
257
|
+
if (target === void 0) return e;
|
|
188
258
|
const done = copies.get(e.key);
|
|
189
259
|
if (done) return done;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
260
|
+
if (payer === void 0) {
|
|
261
|
+
warnOnce(
|
|
262
|
+
`pool-rebind:${poolId}:${id}`,
|
|
263
|
+
`Archivist: pool "${poolId}" members consume "${id}", which this character does not own; bound to the pool's resource "${target}"`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
const copy = { ...e, entity: { ...e.entity, consumes: { ...consumes, resource: target } } };
|
|
195
267
|
copies.set(e.key, copy);
|
|
196
268
|
return copy;
|
|
197
269
|
};
|
|
@@ -334,10 +406,23 @@ function resolveOptionRows(note, registry, opts = {}) {
|
|
|
334
406
|
...row.attunement !== void 0 ? { attunement: row.attunement } : {},
|
|
335
407
|
arm: row.creates.item ? "item" : row.creates.variant ? "variant" : "filter",
|
|
336
408
|
candidates,
|
|
337
|
-
availability: anyVisible ? "installed" : installed ? "hidden-only" : "not-installed"
|
|
409
|
+
availability: anyVisible ? "installed" : installed ? "hidden-only" : "not-installed",
|
|
410
|
+
...rowItemWords(row, candidates[0] ? registry.getByTypeAndSlug("item", candidates[0]) : rowItems[0])
|
|
338
411
|
};
|
|
339
412
|
});
|
|
340
413
|
}
|
|
414
|
+
function rowItemWords(row, item) {
|
|
415
|
+
var _a, _b, _c, _d;
|
|
416
|
+
const f = row.creates.filter;
|
|
417
|
+
if (f) return { type: ((_a = f.types) == null ? void 0 : _a[0]) ? lc(f.types[0]) : null, rarity: f.rarity[0] ? lc(f.rarity[0]) : null, charges: null };
|
|
418
|
+
const data = (_b = item == null ? void 0 : item.data) != null ? _b : {};
|
|
419
|
+
const max = (_c = data.charges) == null ? void 0 : _c.max;
|
|
420
|
+
return {
|
|
421
|
+
type: lc(data.type) || null,
|
|
422
|
+
rarity: lc(data.rarity) || null,
|
|
423
|
+
charges: typeof max === "number" && max > 0 ? { max, ...typeof ((_d = data.charges) == null ? void 0 : _d.recharge) === "string" ? { recharge: data.charges.recharge } : {} } : null
|
|
424
|
+
};
|
|
425
|
+
}
|
|
341
426
|
function resolveCopyOption(note, param, ctx) {
|
|
342
427
|
var _a, _b, _c;
|
|
343
428
|
const rows = (_a = note.options) != null ? _a : [];
|
|
@@ -920,7 +1005,9 @@ export {
|
|
|
920
1005
|
infusionsEndedByClassEdit,
|
|
921
1006
|
reconcileClassEdit,
|
|
922
1007
|
eligibleTargets,
|
|
1008
|
+
bindFeatureConsumers,
|
|
923
1009
|
levelPrereqMax,
|
|
1010
|
+
poolAnchorLevel,
|
|
924
1011
|
resolvePool,
|
|
925
1012
|
resolveAllPools,
|
|
926
1013
|
strandedPicks
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
featureEffectSchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2JSQE3DD.js";
|
|
4
4
|
|
|
5
5
|
// src/schemas/choice-schema.ts
|
|
6
6
|
import { z } from "zod";
|
|
@@ -16,7 +16,9 @@ var entityFilterSchema = z.object({
|
|
|
16
16
|
mundane: z.boolean().optional(),
|
|
17
17
|
list: z.string().min(1).optional(),
|
|
18
18
|
level: z.number().int().min(0).max(9).optional(),
|
|
19
|
-
edition: z.string().min(1).optional()
|
|
19
|
+
edition: z.string().min(1).optional(),
|
|
20
|
+
// D2b-A (design §2.2 step 1): Pact of the Tome's "level 1 ritual spells".
|
|
21
|
+
ritual: z.boolean().optional()
|
|
20
22
|
}).strict();
|
|
21
23
|
var inlineOptionSchema = z.lazy(() => z.object({
|
|
22
24
|
value: z.string().min(1),
|
|
@@ -40,7 +42,9 @@ var choiceSchema = z.lazy(() => z.discriminatedUnion("kind", [
|
|
|
40
42
|
count: z.number().int().positive().optional(),
|
|
41
43
|
entity_type: z.string().min(1),
|
|
42
44
|
from: z.array(z.string().min(1)).nonempty().optional(),
|
|
43
|
-
where: entityFilterSchema.optional()
|
|
45
|
+
where: entityFilterSchema.optional(),
|
|
46
|
+
// D2b-A: a pick that names a target (a blast's cantrip), never a grant.
|
|
47
|
+
target: z.literal(true).optional()
|
|
44
48
|
}).strict(),
|
|
45
49
|
z.object({
|
|
46
50
|
kind: z.literal("select-proficiency"),
|
|
@@ -59,6 +59,18 @@ function normalizeSpellComponents(value) {
|
|
|
59
59
|
if (c.r === true) parts.push("R");
|
|
60
60
|
return parts.join(", ");
|
|
61
61
|
}
|
|
62
|
+
function spellMaterialOf(value) {
|
|
63
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
64
|
+
const m = value.m;
|
|
65
|
+
if (m == null || typeof m !== "object" || Array.isArray(m)) return void 0;
|
|
66
|
+
const { text, cost, consume } = m;
|
|
67
|
+
if (typeof text !== "string") return void 0;
|
|
68
|
+
return {
|
|
69
|
+
text,
|
|
70
|
+
...typeof cost === "number" ? { cost } : {},
|
|
71
|
+
...typeof consume === "boolean" || typeof consume === "string" ? { consume } : {}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
62
74
|
function normalizeSpellDurationEntry(entry) {
|
|
63
75
|
if (entry == null || typeof entry !== "object") return toStringSafe(entry);
|
|
64
76
|
const e = entry;
|
|
@@ -84,9 +96,11 @@ function normalizeSpellDuration(value) {
|
|
|
84
96
|
return value.map((entry) => normalizeSpellDurationEntry(entry)).join("; ");
|
|
85
97
|
}
|
|
86
98
|
function mirrorSpellShapes(entity) {
|
|
99
|
+
const material = spellMaterialOf(entity.components);
|
|
87
100
|
return {
|
|
88
101
|
...entity,
|
|
89
102
|
...entity.components != null ? { components: normalizeSpellComponents(entity.components) } : {},
|
|
103
|
+
...material ? { material } : {},
|
|
90
104
|
...entity.duration != null ? { duration: normalizeSpellDuration(entity.duration) } : {}
|
|
91
105
|
};
|
|
92
106
|
}
|
|
@@ -116,6 +130,8 @@ function parseSpell(source) {
|
|
|
116
130
|
if (raw.range != null) spell.range = toStringSafe(raw.range);
|
|
117
131
|
if (raw.components != null) spell.components = normalizeSpellComponents(raw.components);
|
|
118
132
|
if (raw.duration != null) spell.duration = normalizeSpellDuration(raw.duration);
|
|
133
|
+
const material = spellMaterialOf(raw.components);
|
|
134
|
+
if (material) spell.material = material;
|
|
119
135
|
if (raw.concentration != null) spell.concentration = Boolean(raw.concentration);
|
|
120
136
|
if (raw.ritual != null) spell.ritual = Boolean(raw.ritual);
|
|
121
137
|
if (raw.classes) spell.classes = raw.classes.map(String);
|
|
@@ -158,6 +174,7 @@ function parseSpell(source) {
|
|
|
158
174
|
|
|
159
175
|
export {
|
|
160
176
|
normalizeSpellComponents,
|
|
177
|
+
spellMaterialOf,
|
|
161
178
|
normalizeSpellDuration,
|
|
162
179
|
mirrorSpellShapes,
|
|
163
180
|
parseSpell
|
|
@@ -24,11 +24,9 @@ function derivePoolLayout(entries) {
|
|
|
24
24
|
}
|
|
25
25
|
return tied ? void 0 : best;
|
|
26
26
|
}
|
|
27
|
-
var DEFAULT_POOL_ROWS = "blocks";
|
|
28
27
|
function poolPresentation(authored, derived) {
|
|
29
28
|
const head = authored === "point-pool" ? "points" : authored === "dice-pool" ? "dice" : derived === "point-pool" ? "points" : "dice";
|
|
30
|
-
|
|
31
|
-
return { head, rows };
|
|
29
|
+
return { head };
|
|
32
30
|
}
|
|
33
31
|
var RENDERING_HINT_AFFORDANCE = {
|
|
34
32
|
"granted-die-to-ally": "granted-die"
|
|
@@ -49,7 +47,6 @@ function formatAffordanceCaption(affordance, values) {
|
|
|
49
47
|
export {
|
|
50
48
|
RENDERING_HINT_LAYOUT,
|
|
51
49
|
derivePoolLayout,
|
|
52
|
-
DEFAULT_POOL_ROWS,
|
|
53
50
|
poolPresentation,
|
|
54
51
|
RENDERING_HINT_AFFORDANCE,
|
|
55
52
|
deriveEntryAffordance,
|
|
@@ -6,9 +6,12 @@ import {
|
|
|
6
6
|
resetTriggerEnum,
|
|
7
7
|
resourceConsumptionSchema
|
|
8
8
|
} from "./chunk-ZJXSXVFP.js";
|
|
9
|
+
import {
|
|
10
|
+
choiceSchema
|
|
11
|
+
} from "./chunk-5MXFW4O3.js";
|
|
9
12
|
import {
|
|
10
13
|
featureEffectSchema
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-2JSQE3DD.js";
|
|
12
15
|
import {
|
|
13
16
|
additionalSpellsEntrySchema,
|
|
14
17
|
imageField,
|
|
@@ -138,7 +141,9 @@ var optionalFeatureEntitySchema = z.object({
|
|
|
138
141
|
repeatable: z.literal(true).optional(),
|
|
139
142
|
item_choice: z.enum(["learn", "create"]).optional(),
|
|
140
143
|
options: z.array(optionRowSchema).optional(),
|
|
141
|
-
infuse: infuseBlockSchema.optional()
|
|
144
|
+
infuse: infuseBlockSchema.optional(),
|
|
145
|
+
// D2b-A (design §2.2 step 1): a pool member's own choices, the vocabulary features and feats use.
|
|
146
|
+
choices: z.array(choiceSchema).optional()
|
|
142
147
|
}).superRefine((d, ctx) => {
|
|
143
148
|
var _a;
|
|
144
149
|
if (d.repeatable && (!d.options || !d.item_choice)) ctx.addIssue({ code: "custom", message: "a repeatable note needs options and item_choice" });
|