@archivist-gg/dnd5e 0.1.0 → 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-537BHVSH.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-4SSDYJS2.js → chunk-7VJ6BOA5.js} +5 -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-4C2KOKLF.js → chunk-K6Q5OUGK.js} +12 -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-QQ5W2AZP.js → chunk-TQZB4OCJ.js} +67 -12
- package/dist/{chunk-A7T5L7SS.js → chunk-TUIFSVLI.js} +1 -1
- package/dist/{chunk-XURS6Q6Q.js → chunk-V6SOFUJW.js} +19 -5
- 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 +622 -118
- 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 +13 -0
- package/dist/item/item.schema.js +1 -1
- package/dist/item/item.types.d.ts +17 -0
- package/dist/pc/pc.decision-engine.js +8 -1
- package/dist/pc/pc.equipment.d.ts +2 -1
- package/dist/pc/pc.equipment.js +9 -1
- package/dist/pc/pc.feature-effects.d.ts +31 -9
- package/dist/pc/pc.feature-effects.js +11 -1
- package/dist/pc/pc.parser.js +1 -1
- package/dist/pc/pc.pools.js +8 -3
- package/dist/pc/pc.recalc.js +80 -62
- package/dist/pc/pc.resolver.d.ts +71 -1
- package/dist/pc/pc.resolver.js +21 -254
- package/dist/pc/pc.schema.d.ts +21 -0
- package/dist/pc/pc.schema.js +1 -1
- package/dist/pc/pc.types.d.ts +80 -1
- 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 +12 -0
- package/package.json +1 -1
- package/dist/chunk-6PXE7T7C.js +0 -79
- package/dist/chunk-TJCNX6UZ.js +0 -521
|
@@ -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;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const MASTERY: Record<string, {
|
|
2
|
+
label: string;
|
|
3
|
+
description: string;
|
|
4
|
+
gist: string;
|
|
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;
|
|
9
|
+
export declare function masteryDerived(slug: string, abilityMod: number, proficiencyBonus: number): {
|
|
10
|
+
label: string;
|
|
11
|
+
value: number;
|
|
12
|
+
} | undefined;
|
package/package.json
CHANGED
package/dist/chunk-6PXE7T7C.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
readTableColumn
|
|
3
|
-
} from "./chunk-5XUXXAQJ.js";
|
|
4
|
-
import {
|
|
5
|
-
bareEntitySlug,
|
|
6
|
-
wikilinkTailSlug
|
|
7
|
-
} from "./chunk-TJCNX6UZ.js";
|
|
8
|
-
|
|
9
|
-
// src/pc/pc.pools.ts
|
|
10
|
-
function levelPrereqMax(d) {
|
|
11
|
-
var _a;
|
|
12
|
-
return ((_a = d.prerequisites) != null ? _a : []).filter((p) => p.kind === "level").reduce((m, p) => Math.max(m, p.min), 0);
|
|
13
|
-
}
|
|
14
|
-
function resolvePool(rc, classIndex, pool, registry) {
|
|
15
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
16
|
-
const entity = rc.entity;
|
|
17
|
-
const level = rc.level;
|
|
18
|
-
const ownerBare = bareEntitySlug(entity.slug);
|
|
19
|
-
const col = pool.count.column;
|
|
20
|
-
const count = (_a = readTableColumn(entity.table, level, [col])) != null ? _a : 0;
|
|
21
|
-
let anchorLevel = 1;
|
|
22
|
-
for (let l = 1; l <= 20; l++) {
|
|
23
|
-
const c = readTableColumn(entity.table, l, [col]);
|
|
24
|
-
if (c != null && c >= 1) {
|
|
25
|
-
anchorLevel = l;
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
const ft = pool.source.where.feature_type;
|
|
30
|
-
const all = registry.search("", "optional-feature", Number.POSITIVE_INFINITY);
|
|
31
|
-
const byBare = /* @__PURE__ */ new Map();
|
|
32
|
-
const available = [];
|
|
33
|
-
for (const e of all) {
|
|
34
|
-
const d = e.data;
|
|
35
|
-
const entry = { slug: e.slug, entity: d };
|
|
36
|
-
byBare.set(bareEntitySlug(e.slug), entry);
|
|
37
|
-
if (d.feature_type !== ft) continue;
|
|
38
|
-
if (!((_b = d.available_to) != null ? _b : []).some((l) => wikilinkTailSlug(l) === ownerBare)) continue;
|
|
39
|
-
if (levelPrereqMax(d) > level) continue;
|
|
40
|
-
available.push(entry);
|
|
41
|
-
}
|
|
42
|
-
const grantDecls = [
|
|
43
|
-
...(_d = (_c = rc.entity) == null ? void 0 : _c.pool_grants) != null ? _d : [],
|
|
44
|
-
...(_f = (_e = rc.subclass) == null ? void 0 : _e.pool_grants) != null ? _f : []
|
|
45
|
-
];
|
|
46
|
-
const grantsRaw = grantDecls.filter((g) => g.pool === pool.id).flatMap((g) => g.grants).filter((g) => g.at_level <= level).map((g) => byBare.get(wikilinkTailSlug(g.feature))).filter((e) => e != null);
|
|
47
|
-
const grantedBare = /* @__PURE__ */ new Set();
|
|
48
|
-
const grants = grantsRaw.filter((e) => {
|
|
49
|
-
const key = bareEntitySlug(e.slug);
|
|
50
|
-
if (grantedBare.has(key)) return false;
|
|
51
|
-
grantedBare.add(key);
|
|
52
|
-
return true;
|
|
53
|
-
});
|
|
54
|
-
const raw = (_g = rc.choices[anchorLevel]) == null ? void 0 : _g[pool.id];
|
|
55
|
-
const selectedSlugs = Array.isArray(raw) ? raw : typeof raw === "string" ? [raw] : [];
|
|
56
|
-
const selected = selectedSlugs.map((s) => byBare.get(bareEntitySlug(s))).filter((e) => e != null).filter((e) => !grantedBare.has(bareEntitySlug(e.slug)));
|
|
57
|
-
return { id: pool.id, label: pool.label, classIndex, count, anchorLevel, selected, available, grants };
|
|
58
|
-
}
|
|
59
|
-
function resolveAllPools(resolved, registry) {
|
|
60
|
-
const out = [];
|
|
61
|
-
resolved.classes.forEach((rc, classIndex) => {
|
|
62
|
-
var _a, _b, _c;
|
|
63
|
-
if (!rc.entity) return;
|
|
64
|
-
const decls = [...(_a = rc.entity.selection_pools) != null ? _a : [], ...(_c = (_b = rc.subclass) == null ? void 0 : _b.selection_pools) != null ? _c : []];
|
|
65
|
-
const seen = /* @__PURE__ */ new Set();
|
|
66
|
-
for (const pool of decls) {
|
|
67
|
-
if (seen.has(pool.id)) continue;
|
|
68
|
-
seen.add(pool.id);
|
|
69
|
-
out.push(resolvePool(rc, classIndex, pool, registry));
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
return out;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export {
|
|
76
|
-
levelPrereqMax,
|
|
77
|
-
resolvePool,
|
|
78
|
-
resolveAllPools
|
|
79
|
-
};
|
package/dist/chunk-TJCNX6UZ.js
DELETED
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ALL_SKILL_SLUGS
|
|
3
|
-
} from "./chunk-BH6AMZVY.js";
|
|
4
|
-
import {
|
|
5
|
-
recognizeDecision
|
|
6
|
-
} from "./chunk-JEYQU3JT.js";
|
|
7
|
-
import {
|
|
8
|
-
ABILITY_KEYS
|
|
9
|
-
} from "./chunk-WP2CX2JO.js";
|
|
10
|
-
|
|
11
|
-
// src/pc/pc.decision-engine.ts
|
|
12
|
-
var warnedDegradedEquipment = /* @__PURE__ */ new Set();
|
|
13
|
-
function wikilinkTailSlug(link) {
|
|
14
|
-
var _a;
|
|
15
|
-
const inner = link.replace(/^\[\[/, "").replace(/\]\]$/, "");
|
|
16
|
-
const tail = (_a = inner.split("/").pop()) != null ? _a : inner;
|
|
17
|
-
return tail.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
18
|
-
}
|
|
19
|
-
function bareEntitySlug(slug) {
|
|
20
|
-
if (!slug) return "";
|
|
21
|
-
const i = slug.indexOf("_");
|
|
22
|
-
return i === -1 ? slug : slug.slice(i + 1);
|
|
23
|
-
}
|
|
24
|
-
function matchesFilter(e, where, ownerBare) {
|
|
25
|
-
var _a, _b, _c;
|
|
26
|
-
const d = e.data;
|
|
27
|
-
if (where.feature_type && d.feature_type !== where.feature_type) return false;
|
|
28
|
-
if (where.category && d.category !== where.category) return false;
|
|
29
|
-
if (where.weapon_category) {
|
|
30
|
-
const cat = ((_a = d.category) != null ? _a : "").toLowerCase();
|
|
31
|
-
if (!cat.startsWith(`${where.weapon_category}-`) && cat !== where.weapon_category) return false;
|
|
32
|
-
}
|
|
33
|
-
if (where.armor_category) {
|
|
34
|
-
if (((_b = d.category) != null ? _b : "").toLowerCase() !== where.armor_category) return false;
|
|
35
|
-
}
|
|
36
|
-
if (where.parent_class === "self") {
|
|
37
|
-
if (!d.parent_class || wikilinkTailSlug(d.parent_class) !== ownerBare) return false;
|
|
38
|
-
}
|
|
39
|
-
if (where.available_to === "self") {
|
|
40
|
-
const list = (_c = d.available_to) != null ? _c : [];
|
|
41
|
-
if (!list.some((l) => wikilinkTailSlug(l) === ownerBare)) return false;
|
|
42
|
-
}
|
|
43
|
-
return true;
|
|
44
|
-
}
|
|
45
|
-
var __matchesFilterForTest = matchesFilter;
|
|
46
|
-
function categoryToEntitySelect(category, id) {
|
|
47
|
-
const c = category.toLowerCase();
|
|
48
|
-
if (c === "shield") {
|
|
49
|
-
return { kind: "select-entity", id, label: "Choose a shield", count: 1, entity_type: "armor", where: { armor_category: "shield" } };
|
|
50
|
-
}
|
|
51
|
-
if (c.includes("armor")) {
|
|
52
|
-
const klass = c.includes("light") ? "light" : c.includes("medium") ? "medium" : c.includes("heavy") ? "heavy" : null;
|
|
53
|
-
return klass ? { kind: "select-entity", id, label: `Choose ${klass} armor`, count: 1, entity_type: "armor", where: { armor_category: klass } } : { kind: "select-entity", id, label: "Choose armor", count: 1, entity_type: "armor" };
|
|
54
|
-
}
|
|
55
|
-
const wc = c.includes("martial") ? "martial" : "simple";
|
|
56
|
-
return { kind: "select-entity", id, label: `Choose a ${c.replace(/-/g, " ")}`, count: 1, entity_type: "weapon", where: { weapon_category: wc } };
|
|
57
|
-
}
|
|
58
|
-
function enumerateOptions(choice, ctx, ownerBare) {
|
|
59
|
-
var _a, _b;
|
|
60
|
-
switch (choice.kind) {
|
|
61
|
-
case "select-inline":
|
|
62
|
-
return choice.options.map((o) => ({ value: o.value, label: o.label, description: o.description }));
|
|
63
|
-
case "select-entity": {
|
|
64
|
-
if (choice.from) {
|
|
65
|
-
const byBare = /* @__PURE__ */ new Map();
|
|
66
|
-
for (const e of ctx.registry.search("", choice.entity_type, Number.POSITIVE_INFINITY)) {
|
|
67
|
-
byBare.set(bareEntitySlug(e.slug), e);
|
|
68
|
-
}
|
|
69
|
-
return choice.from.map((slug) => {
|
|
70
|
-
var _a2;
|
|
71
|
-
const e = (_a2 = ctx.registry.getByTypeAndSlug(choice.entity_type, slug)) != null ? _a2 : byBare.get(slug);
|
|
72
|
-
return e ? { value: e.slug, label: e.name, entity: e } : { value: slug, label: slug, missing: true };
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const all = ctx.registry.search("", choice.entity_type, Number.POSITIVE_INFINITY);
|
|
76
|
-
const filtered = choice.where ? all.filter((e) => matchesFilter(e, choice.where, ownerBare)) : all;
|
|
77
|
-
return filtered.map((e) => ({ value: e.slug, label: e.name, entity: e }));
|
|
78
|
-
}
|
|
79
|
-
case "select-proficiency": {
|
|
80
|
-
const pool = (_a = choice.from) != null ? _a : choice.domain === "skill" ? [...ALL_SKILL_SLUGS] : [];
|
|
81
|
-
return pool.map((v) => ({ value: v, label: v.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) }));
|
|
82
|
-
}
|
|
83
|
-
case "ability-points": {
|
|
84
|
-
const pool = (_b = choice.pool) != null ? _b : ["str", "dex", "con", "int", "wis", "cha"];
|
|
85
|
-
return pool.map((a) => ({ value: a, label: a.toUpperCase() }));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function selectionCount(choice, selected) {
|
|
90
|
-
if (selected === void 0) return 0;
|
|
91
|
-
if (choice.kind === "ability-points") {
|
|
92
|
-
if (typeof selected !== "object" || Array.isArray(selected)) return 0;
|
|
93
|
-
return Object.values(selected).reduce((s, v) => s + (typeof v === "number" ? v : 0), 0);
|
|
94
|
-
}
|
|
95
|
-
if (Array.isArray(selected)) return selected.length;
|
|
96
|
-
return typeof selected === "string" && selected.length > 0 ? 1 : 0;
|
|
97
|
-
}
|
|
98
|
-
function requiredCount(choice) {
|
|
99
|
-
var _a;
|
|
100
|
-
if (choice.kind === "ability-points") return choice.points;
|
|
101
|
-
if (choice.kind === "select-proficiency") return choice.count;
|
|
102
|
-
return (_a = choice.count) != null ? _a : 1;
|
|
103
|
-
}
|
|
104
|
-
function statusOf(choice, selected) {
|
|
105
|
-
const n = selectionCount(choice, selected);
|
|
106
|
-
const need = requiredCount(choice);
|
|
107
|
-
if (n === 0) return "unresolved";
|
|
108
|
-
return n >= need ? "resolved" : "partial";
|
|
109
|
-
}
|
|
110
|
-
function resolveEntityRef(ctx, entityType, value) {
|
|
111
|
-
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
112
|
-
const raw = value.replace(/^\[\[/, "").replace(/\]\]$/, "");
|
|
113
|
-
const direct = ctx.registry.getByTypeAndSlug(entityType, raw);
|
|
114
|
-
if (direct) return direct;
|
|
115
|
-
for (const e of ctx.registry.search("", entityType, Number.POSITIVE_INFINITY)) {
|
|
116
|
-
if (e.slug === raw || bareEntitySlug(e.slug) === raw) return e;
|
|
117
|
-
}
|
|
118
|
-
return void 0;
|
|
119
|
-
}
|
|
120
|
-
function buildItem(choice, source, level, featureName, readValue, ctx, ownerBare, opts) {
|
|
121
|
-
var _a, _b, _c, _d;
|
|
122
|
-
const keyPrefix = (_a = opts == null ? void 0 : opts.keyPrefix) != null ? _a : "";
|
|
123
|
-
const expandFeatChildren = (_b = opts == null ? void 0 : opts.expandFeatChildren) != null ? _b : true;
|
|
124
|
-
const key = keyPrefix + choice.id;
|
|
125
|
-
const selected = readValue(key);
|
|
126
|
-
const item = {
|
|
127
|
-
key,
|
|
128
|
-
source,
|
|
129
|
-
level,
|
|
130
|
-
featureName,
|
|
131
|
-
choice,
|
|
132
|
-
description: opts == null ? void 0 : opts.description,
|
|
133
|
-
options: enumerateOptions(choice, ctx, ownerBare),
|
|
134
|
-
selected,
|
|
135
|
-
status: statusOf(choice, selected)
|
|
136
|
-
};
|
|
137
|
-
if (choice.kind === "select-inline" && typeof selected === "string") {
|
|
138
|
-
const branch = choice.options.find((o) => o.value === selected);
|
|
139
|
-
if ((_c = branch == null ? void 0 : branch.choices) == null ? void 0 : _c.length) {
|
|
140
|
-
item.children = branch.choices.map((c) => buildItem(c, source, level, featureName, readValue, ctx, ownerBare, { keyPrefix, expandFeatChildren }));
|
|
141
|
-
if (item.status === "resolved" && item.children.some((c) => c.status !== "resolved")) {
|
|
142
|
-
item.status = "partial";
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (choice.kind === "select-entity" && choice.entity_type === "feat" && expandFeatChildren && typeof selected === "string") {
|
|
147
|
-
const entity = resolveEntityRef(ctx, "feat", selected);
|
|
148
|
-
const rawChoices = (_d = entity == null ? void 0 : entity.data) == null ? void 0 : _d.choices;
|
|
149
|
-
const featChoices = Array.isArray(rawChoices) ? rawChoices : [];
|
|
150
|
-
if (featChoices.length) {
|
|
151
|
-
const childPrefix = `${keyPrefix}feat:`;
|
|
152
|
-
item.children = featChoices.map((c) => buildItem(
|
|
153
|
-
c,
|
|
154
|
-
source,
|
|
155
|
-
level,
|
|
156
|
-
featureName,
|
|
157
|
-
readValue,
|
|
158
|
-
ctx,
|
|
159
|
-
ownerBare,
|
|
160
|
-
{ keyPrefix: childPrefix, expandFeatChildren: false }
|
|
161
|
-
));
|
|
162
|
-
if (item.status === "resolved" && item.children.some((c) => c.status !== "resolved")) {
|
|
163
|
-
item.status = "partial";
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return item;
|
|
168
|
-
}
|
|
169
|
-
function buildSubclassItem(choice, source, level, featureName, c, ctx, ownerBare, description) {
|
|
170
|
-
const selected = c.subclass ? c.subclass.slug : void 0;
|
|
171
|
-
return {
|
|
172
|
-
key: "subclass",
|
|
173
|
-
source,
|
|
174
|
-
level,
|
|
175
|
-
featureName,
|
|
176
|
-
description,
|
|
177
|
-
choice,
|
|
178
|
-
options: enumerateOptions(choice, ctx, ownerBare),
|
|
179
|
-
selected,
|
|
180
|
-
status: selected ? "resolved" : "unresolved"
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
function collectChosenProficiencies(resolved) {
|
|
184
|
-
var _a, _b;
|
|
185
|
-
const out = { skills: [], expertise: [], languages: [], tools: [] };
|
|
186
|
-
const apply = (choice, selected) => {
|
|
187
|
-
if (choice.kind !== "select-proficiency") return;
|
|
188
|
-
const vals = Array.isArray(selected) ? selected : typeof selected === "string" ? [selected] : [];
|
|
189
|
-
const pool = choice.from;
|
|
190
|
-
const valid = pool ? vals.filter((v) => pool.includes(v)) : vals;
|
|
191
|
-
const bucket = choice.domain === "skill" ? choice.expertise ? out.expertise : out.skills : choice.domain === "language" ? out.languages : choice.domain === "tool" ? out.tools : null;
|
|
192
|
-
if (bucket) {
|
|
193
|
-
for (const v of valid) if (!bucket.includes(v)) bucket.push(v);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
const walk = (choices, read) => {
|
|
197
|
-
for (const ch of choices != null ? choices : []) {
|
|
198
|
-
apply(ch, read(ch.id));
|
|
199
|
-
if (ch.kind === "select-inline") {
|
|
200
|
-
const sel = read(ch.id);
|
|
201
|
-
const branch = typeof sel === "string" ? ch.options.find((o) => o.value === sel) : void 0;
|
|
202
|
-
if (branch == null ? void 0 : branch.choices) walk(branch.choices, read);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
resolved.classes.forEach((c, i) => {
|
|
207
|
-
var _a2, _b2;
|
|
208
|
-
if (!c.entity) return;
|
|
209
|
-
const entity = c.entity;
|
|
210
|
-
const readAt = (lvl) => (id) => {
|
|
211
|
-
var _a3;
|
|
212
|
-
return (_a3 = c.choices[lvl]) == null ? void 0 : _a3[id];
|
|
213
|
-
};
|
|
214
|
-
if (i === 0 && ((_b2 = (_a2 = entity.skill_choices) == null ? void 0 : _a2.from) == null ? void 0 : _b2.length)) {
|
|
215
|
-
apply({
|
|
216
|
-
kind: "select-proficiency",
|
|
217
|
-
id: "skills",
|
|
218
|
-
count: entity.skill_choices.count,
|
|
219
|
-
domain: "skill",
|
|
220
|
-
from: entity.skill_choices.from
|
|
221
|
-
}, readAt(1)("skills"));
|
|
222
|
-
}
|
|
223
|
-
for (const rf of resolved.features) {
|
|
224
|
-
if (rf.source.kind !== "class" && rf.source.kind !== "subclass") continue;
|
|
225
|
-
const belongs = rf.source.kind === "class" ? rf.source.slug === entity.slug : c.subclass != null && rf.source.slug === c.subclass.slug;
|
|
226
|
-
if (!belongs) continue;
|
|
227
|
-
walk(rf.feature.choices, readAt(rf.source.level));
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
const oc = (_a = resolved.definition.origin_choices) != null ? _a : {};
|
|
231
|
-
const originRead = (ns) => (id) => oc[`${ns}:${id}`];
|
|
232
|
-
if (resolved.race) {
|
|
233
|
-
walk(resolved.race.choices, originRead("race"));
|
|
234
|
-
for (const t of (_b = resolved.race.traits) != null ? _b : []) walk(t.choices, originRead("race"));
|
|
235
|
-
}
|
|
236
|
-
if (resolved.background) {
|
|
237
|
-
walk(resolved.background.choices, originRead("background"));
|
|
238
|
-
if (resolved.background.feature) {
|
|
239
|
-
walk(resolved.background.feature.choices, originRead("background"));
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return out;
|
|
243
|
-
}
|
|
244
|
-
function collectChosenAbilityPoints(resolved) {
|
|
245
|
-
var _a, _b, _c;
|
|
246
|
-
const oc = (_a = resolved.definition.origin_choices) != null ? _a : {};
|
|
247
|
-
const out = { race: {}, background: {} };
|
|
248
|
-
const fold = (ns, choices) => {
|
|
249
|
-
var _a2;
|
|
250
|
-
for (const ch of choices != null ? choices : []) {
|
|
251
|
-
if (ch.kind !== "ability-points") continue;
|
|
252
|
-
const raw = oc[`${ns}:${ch.id}`];
|
|
253
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
254
|
-
let left = ch.points;
|
|
255
|
-
for (const ab of ABILITY_KEYS) {
|
|
256
|
-
if (ch.pool && !ch.pool.includes(ab)) continue;
|
|
257
|
-
const v = raw[ab];
|
|
258
|
-
if (typeof v !== "number" || v <= 0 || left <= 0) continue;
|
|
259
|
-
const take = Math.min(v, ch.max_per, left);
|
|
260
|
-
out[ns][ab] = ((_a2 = out[ns][ab]) != null ? _a2 : 0) + take;
|
|
261
|
-
left -= take;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
const race = resolved.race;
|
|
266
|
-
if (race) {
|
|
267
|
-
fold("race", race.choices);
|
|
268
|
-
for (const t of (_b = race.traits) != null ? _b : []) fold("race", t.choices);
|
|
269
|
-
}
|
|
270
|
-
const bg = resolved.background;
|
|
271
|
-
if (bg) {
|
|
272
|
-
fold("background", bg.choices);
|
|
273
|
-
fold("background", (_c = bg.feature) == null ? void 0 : _c.choices);
|
|
274
|
-
}
|
|
275
|
-
return out;
|
|
276
|
-
}
|
|
277
|
-
function buildDecisionLedger(resolved, ctx) {
|
|
278
|
-
var _a, _b, _c, _d;
|
|
279
|
-
const classes = [];
|
|
280
|
-
resolved.classes.forEach((c, classIndex) => {
|
|
281
|
-
var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
282
|
-
if (!c.entity) return;
|
|
283
|
-
const entity = c.entity;
|
|
284
|
-
const ownerBare = bareEntitySlug(entity.slug);
|
|
285
|
-
const byLevel = /* @__PURE__ */ new Map();
|
|
286
|
-
const push = (lvl, item) => {
|
|
287
|
-
var _a3;
|
|
288
|
-
const arr = (_a3 = byLevel.get(lvl)) != null ? _a3 : [];
|
|
289
|
-
arr.push(item);
|
|
290
|
-
byLevel.set(lvl, arr);
|
|
291
|
-
};
|
|
292
|
-
const readAt = (lvl) => (id) => {
|
|
293
|
-
var _a3;
|
|
294
|
-
return (_a3 = c.choices[lvl]) == null ? void 0 : _a3[id];
|
|
295
|
-
};
|
|
296
|
-
if (classIndex === 0 && ((_b2 = (_a2 = entity.skill_choices) == null ? void 0 : _a2.from) == null ? void 0 : _b2.length)) {
|
|
297
|
-
const skillChoice = {
|
|
298
|
-
kind: "select-proficiency",
|
|
299
|
-
id: "skills",
|
|
300
|
-
label: "Skill Proficiencies",
|
|
301
|
-
count: entity.skill_choices.count,
|
|
302
|
-
domain: "skill",
|
|
303
|
-
from: entity.skill_choices.from
|
|
304
|
-
};
|
|
305
|
-
push(1, buildItem(
|
|
306
|
-
skillChoice,
|
|
307
|
-
{ kind: "class", slug: entity.slug, level: 1 },
|
|
308
|
-
1,
|
|
309
|
-
"Proficiencies",
|
|
310
|
-
readAt(1),
|
|
311
|
-
ctx,
|
|
312
|
-
ownerBare
|
|
313
|
-
));
|
|
314
|
-
}
|
|
315
|
-
if (classIndex === 0) {
|
|
316
|
-
((_c2 = entity.starting_equipment) != null ? _c2 : []).forEach((eq, i) => {
|
|
317
|
-
var _a3;
|
|
318
|
-
if (eq.kind !== "choice") return;
|
|
319
|
-
const options = (_a3 = eq.options) != null ? _a3 : [];
|
|
320
|
-
let degraded = false;
|
|
321
|
-
const ch = {
|
|
322
|
-
kind: "select-inline",
|
|
323
|
-
id: `equipment-${i}`,
|
|
324
|
-
label: "Starting Equipment",
|
|
325
|
-
count: 1,
|
|
326
|
-
options: options.map((opt, j) => {
|
|
327
|
-
var _a4;
|
|
328
|
-
const label = typeof opt === "string" ? opt : (_a4 = opt == null ? void 0 : opt.label) != null ? _a4 : "";
|
|
329
|
-
const grants = opt && typeof opt === "object" && Array.isArray(opt.grants) ? opt.grants : [];
|
|
330
|
-
if (typeof opt === "string" || !(opt && typeof opt === "object" && Array.isArray(opt.grants))) {
|
|
331
|
-
degraded = true;
|
|
332
|
-
}
|
|
333
|
-
return {
|
|
334
|
-
value: `option-${j}`,
|
|
335
|
-
label,
|
|
336
|
-
choices: grants.flatMap((g, k) => g && typeof g === "object" && "category" in g && g.category ? [categoryToEntitySelect(g.category, `equipment-${i}-opt-${j}-cat-${k}`)] : [])
|
|
337
|
-
};
|
|
338
|
-
})
|
|
339
|
-
};
|
|
340
|
-
if (degraded && !warnedDegradedEquipment.has(entity.slug)) {
|
|
341
|
-
warnedDegradedEquipment.add(entity.slug);
|
|
342
|
-
console.warn(
|
|
343
|
-
`[archivist] Starting equipment for "${entity.slug}" is in an outdated/unstructured format; its nested picks and seeding are skipped. Re-sync the compendium (delete _compendium.md and reload) to fix.`
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
push(1, buildItem(
|
|
347
|
-
ch,
|
|
348
|
-
{ kind: "class", slug: entity.slug, level: 1 },
|
|
349
|
-
1,
|
|
350
|
-
"Starting Equipment",
|
|
351
|
-
readAt(1),
|
|
352
|
-
ctx,
|
|
353
|
-
ownerBare
|
|
354
|
-
));
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
let sawAuthoredSubclass = false;
|
|
358
|
-
for (const rf of resolved.features) {
|
|
359
|
-
const src = rf.source;
|
|
360
|
-
if (src.kind !== "class" && src.kind !== "subclass") continue;
|
|
361
|
-
const belongs = src.kind === "class" ? src.slug === entity.slug : c.subclass != null && src.slug === c.subclass.slug;
|
|
362
|
-
if (!belongs) continue;
|
|
363
|
-
const lvl = src.level;
|
|
364
|
-
let choices = rf.feature.choices;
|
|
365
|
-
if (!(choices == null ? void 0 : choices.length)) {
|
|
366
|
-
const recognized = recognizeDecision(rf.feature);
|
|
367
|
-
if (recognized === "informational") {
|
|
368
|
-
push(lvl, {
|
|
369
|
-
key: (_d2 = rf.feature.id) != null ? _d2 : rf.feature.name,
|
|
370
|
-
source: src,
|
|
371
|
-
level: lvl,
|
|
372
|
-
featureName: rf.feature.name,
|
|
373
|
-
description: (_f = rf.feature.description) != null ? _f : (_e = rf.feature.entries) == null ? void 0 : _e.join("\n\n"),
|
|
374
|
-
choice: { kind: "select-inline", id: (_g = rf.feature.id) != null ? _g : rf.feature.name, options: [{ value: "_", label: "_" }] },
|
|
375
|
-
options: [],
|
|
376
|
-
selected: void 0,
|
|
377
|
-
status: "informational"
|
|
378
|
-
});
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
|
-
choices = recognized != null ? recognized : void 0;
|
|
382
|
-
}
|
|
383
|
-
if (!(choices == null ? void 0 : choices.length)) {
|
|
384
|
-
if (rf.feature.description || ((_h = rf.feature.entries) == null ? void 0 : _h.length)) {
|
|
385
|
-
push(lvl, {
|
|
386
|
-
key: (_i = rf.feature.id) != null ? _i : rf.feature.name,
|
|
387
|
-
source: src,
|
|
388
|
-
level: lvl,
|
|
389
|
-
featureName: rf.feature.name,
|
|
390
|
-
description: (_k = rf.feature.description) != null ? _k : (_j = rf.feature.entries) == null ? void 0 : _j.join("\n\n"),
|
|
391
|
-
choice: { kind: "select-inline", id: (_l = rf.feature.id) != null ? _l : rf.feature.name, options: [{ value: "_", label: "_" }] },
|
|
392
|
-
options: [],
|
|
393
|
-
selected: void 0,
|
|
394
|
-
status: "informational"
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
continue;
|
|
398
|
-
}
|
|
399
|
-
for (const ch of choices) {
|
|
400
|
-
if (ch.kind === "select-entity" && ch.entity_type === "subclass") {
|
|
401
|
-
sawAuthoredSubclass = true;
|
|
402
|
-
push(lvl, buildSubclassItem(ch, src, lvl, rf.feature.name, c, ctx, ownerBare, rf.feature.description));
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
push(lvl, buildItem(
|
|
406
|
-
ch,
|
|
407
|
-
src,
|
|
408
|
-
lvl,
|
|
409
|
-
rf.feature.name,
|
|
410
|
-
readAt(lvl),
|
|
411
|
-
ctx,
|
|
412
|
-
ownerBare,
|
|
413
|
-
{ description: rf.feature.description }
|
|
414
|
-
));
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
const subclassLevel = (_m = entity.subclass_level) != null ? _m : null;
|
|
418
|
-
if (subclassLevel != null && subclassLevel <= c.level && !sawAuthoredSubclass) {
|
|
419
|
-
const featureName = (_n = entity.subclass_feature_name) != null ? _n : "Subclass";
|
|
420
|
-
const synthChoice = {
|
|
421
|
-
kind: "select-entity",
|
|
422
|
-
id: "subclass",
|
|
423
|
-
label: featureName,
|
|
424
|
-
count: 1,
|
|
425
|
-
entity_type: "subclass",
|
|
426
|
-
where: { parent_class: "self" }
|
|
427
|
-
};
|
|
428
|
-
push(subclassLevel, buildSubclassItem(
|
|
429
|
-
synthChoice,
|
|
430
|
-
{ kind: "class", slug: entity.slug, level: subclassLevel },
|
|
431
|
-
subclassLevel,
|
|
432
|
-
featureName,
|
|
433
|
-
c,
|
|
434
|
-
ctx,
|
|
435
|
-
ownerBare
|
|
436
|
-
));
|
|
437
|
-
}
|
|
438
|
-
for (const pool of resolved.pools) {
|
|
439
|
-
if (pool.classIndex !== classIndex || pool.count < 1) continue;
|
|
440
|
-
const synth = {
|
|
441
|
-
kind: "select-entity",
|
|
442
|
-
id: pool.id,
|
|
443
|
-
label: pool.label,
|
|
444
|
-
count: pool.count,
|
|
445
|
-
entity_type: "optional-feature",
|
|
446
|
-
from: pool.available.map((e) => e.slug)
|
|
447
|
-
};
|
|
448
|
-
push(pool.anchorLevel, buildItem(
|
|
449
|
-
synth,
|
|
450
|
-
{ kind: "class", slug: entity.slug, level: pool.anchorLevel },
|
|
451
|
-
pool.anchorLevel,
|
|
452
|
-
pool.label,
|
|
453
|
-
readAt(pool.anchorLevel),
|
|
454
|
-
ctx,
|
|
455
|
-
ownerBare
|
|
456
|
-
));
|
|
457
|
-
}
|
|
458
|
-
const levels = [...byLevel.entries()].sort(([a], [b]) => a - b).map(([level, items]) => ({ level, items }));
|
|
459
|
-
classes.push({ classIndex, levels });
|
|
460
|
-
});
|
|
461
|
-
const origin = [];
|
|
462
|
-
const oc = (_a = resolved.definition.origin_choices) != null ? _a : {};
|
|
463
|
-
const originRead = (ns) => (id) => oc[`${ns}:${id}`];
|
|
464
|
-
const pushOrigin = (choices, ns, source, featureName, ownerBare, description) => {
|
|
465
|
-
for (const ch of choices != null ? choices : []) {
|
|
466
|
-
origin.push(buildItem(ch, source, 0, featureName, originRead(ns), ctx, ownerBare, { description }));
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
if (resolved.race) {
|
|
470
|
-
const bare = bareEntitySlug(resolved.race.slug);
|
|
471
|
-
pushOrigin(
|
|
472
|
-
resolved.race.choices,
|
|
473
|
-
"race",
|
|
474
|
-
{ kind: "race", slug: resolved.race.slug },
|
|
475
|
-
(_b = resolved.race.name) != null ? _b : "Race",
|
|
476
|
-
bare,
|
|
477
|
-
resolved.race.description
|
|
478
|
-
);
|
|
479
|
-
for (const t of (_c = resolved.race.traits) != null ? _c : []) {
|
|
480
|
-
pushOrigin(
|
|
481
|
-
t.choices,
|
|
482
|
-
"race",
|
|
483
|
-
{ kind: "race", slug: resolved.race.slug },
|
|
484
|
-
t.name,
|
|
485
|
-
bare,
|
|
486
|
-
t.description
|
|
487
|
-
);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
if (resolved.background) {
|
|
491
|
-
const bare = bareEntitySlug(resolved.background.slug);
|
|
492
|
-
pushOrigin(
|
|
493
|
-
resolved.background.choices,
|
|
494
|
-
"background",
|
|
495
|
-
{ kind: "background", slug: resolved.background.slug },
|
|
496
|
-
(_d = resolved.background.name) != null ? _d : "Background",
|
|
497
|
-
bare,
|
|
498
|
-
resolved.background.description
|
|
499
|
-
);
|
|
500
|
-
if (resolved.background.feature) {
|
|
501
|
-
pushOrigin(
|
|
502
|
-
resolved.background.feature.choices,
|
|
503
|
-
"background",
|
|
504
|
-
{ kind: "background", slug: resolved.background.slug },
|
|
505
|
-
resolved.background.feature.name,
|
|
506
|
-
bare,
|
|
507
|
-
resolved.background.feature.description
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
return { classes, origin };
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
export {
|
|
515
|
-
wikilinkTailSlug,
|
|
516
|
-
bareEntitySlug,
|
|
517
|
-
__matchesFilterForTest,
|
|
518
|
-
collectChosenProficiencies,
|
|
519
|
-
collectChosenAbilityPoints,
|
|
520
|
-
buildDecisionLedger
|
|
521
|
-
};
|