@archivist-gg/dnd5e 0.3.11 → 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/LICENSES/SRD.md +38 -3
- package/dist/armor/armor.codec.js +3 -3
- package/dist/armor/armor.parser.js +2 -2
- package/dist/armor/armor.schema.d.ts +1 -0
- package/dist/armor/armor.schema.js +1 -1
- package/dist/armor/armor.types.d.ts +2 -0
- 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-XTRDCSJB.js → chunk-2JSQE3DD.js} +15 -1
- package/dist/chunk-2LULMQTF.js +14 -0
- package/dist/{chunk-MAH6DCRI.js → chunk-2UDSN6VW.js} +1 -1
- package/dist/chunk-3LWMFYKS.js +57 -0
- package/dist/{chunk-JCEV7KYN.js → chunk-3MGQFFNE.js} +1 -1
- package/dist/chunk-3VZZDKRA.js +1014 -0
- package/dist/{chunk-2DFENQIJ.js → chunk-5GKJAUMX.js} +2 -0
- package/dist/{chunk-2M34FLAY.js → chunk-5MXFW4O3.js} +9 -3
- package/dist/{chunk-4DOMDEBZ.js → chunk-6EI737IO.js} +1 -1
- package/dist/{chunk-G247QXWW.js → chunk-7ATNGKBS.js} +1 -1
- package/dist/{chunk-XY6X2APM.js → chunk-7Q2VPKCD.js} +17 -0
- package/dist/{chunk-T2SNEJOE.js → chunk-7QN7XSAK.js} +1 -1
- package/dist/{chunk-DNAJRMNN.js → chunk-A54HHCWW.js} +6 -7
- package/dist/{chunk-7YU4NPKR.js → chunk-AZRGFUSR.js} +1 -1
- package/dist/{chunk-PP2UHLBA.js → chunk-B3DP42OM.js} +5 -0
- package/dist/chunk-CM5CWO4K.js +735 -0
- package/dist/{chunk-MQ747UCR.js → chunk-DOXSFYLH.js} +70 -2
- package/dist/chunk-EJP263OP.js +186 -0
- package/dist/{chunk-SMLXZUBH.js → chunk-EYX4JMAU.js} +4 -1
- package/dist/{chunk-V6LJFP4R.js → chunk-FANQVQ3D.js} +2 -2
- package/dist/{chunk-AW3EHF3L.js → chunk-FC4IGH5Y.js} +1 -1
- package/dist/{chunk-23LZLBJK.js → chunk-G6MHJDVT.js} +6 -2
- package/dist/{chunk-JANNRGJC.js → chunk-G6PBI2BU.js} +1 -1
- package/dist/chunk-H5PTKYXL.js +101 -0
- package/dist/{chunk-LWFTHJIE.js → chunk-HHBD4SQ6.js} +1 -1
- package/dist/{chunk-JC5UNIVM.js → chunk-HS6FIOLP.js} +2 -0
- package/dist/chunk-HTYMW5CH.js +228 -0
- package/dist/chunk-IHE6KVC3.js +13 -0
- package/dist/{chunk-QHMWA3SZ.js → chunk-JCMUVZIK.js} +1 -1
- package/dist/{chunk-V373TRJE.js → chunk-JSM6JFZR.js} +198 -125
- package/dist/{chunk-LDDCNPHD.js → chunk-K5NJBTE2.js} +1 -1
- package/dist/chunk-KR6JF3BF.js +14 -0
- package/dist/{chunk-4ISR76DH.js → chunk-KSTV7J2U.js} +9 -3
- package/dist/chunk-LBEXLN6I.js +91 -0
- package/dist/{chunk-BLRW2UU6.js → chunk-LF5AGJA4.js} +2 -2
- package/dist/{chunk-UBBKSYBY.js → chunk-LHZXYFBU.js} +30 -1
- package/dist/{chunk-BIA32MQ5.js → chunk-MBX25B6D.js} +1 -1
- package/dist/{chunk-R2ZV233X.js → chunk-MYY2C5MB.js} +3 -3
- package/dist/{chunk-J77CKYV3.js → chunk-OFCSP6MT.js} +1 -1
- package/dist/{chunk-2INMRR3Q.js → chunk-ONTGZR5T.js} +326 -149
- package/dist/{chunk-MWCCGPG3.js → chunk-OOSRUJBI.js} +3 -2
- package/dist/{chunk-U3BABVJF.js → chunk-PKX6TGRE.js} +2 -2
- package/dist/{chunk-PJMG5XV3.js → chunk-PQZ4LM4Z.js} +1 -1
- package/dist/chunk-PXVNAY7T.js +84 -0
- package/dist/chunk-QH36K2PU.js +158 -0
- package/dist/{chunk-R747YKS7.js → chunk-QZSWN2KT.js} +1 -1
- package/dist/{chunk-CHUALUIX.js → chunk-RYLXAYIQ.js} +1 -1
- package/dist/{chunk-GFZ7PITJ.js → chunk-S3YUXDSC.js} +6 -1
- package/dist/{chunk-PZCE7DJM.js → chunk-T6QIF5XR.js} +31 -1
- package/dist/{chunk-CBEKVAX4.js → chunk-TULQ4FFX.js} +41 -13
- package/dist/{chunk-S56D6B6C.js → chunk-UUBJCNQU.js} +12 -3
- package/dist/{chunk-5N2GW72F.js → chunk-UZXG6QSY.js} +2 -1
- package/dist/{chunk-4XTNPNQB.js → chunk-VPYBK2MG.js} +1 -1
- package/dist/{chunk-3HX5VLWP.js → chunk-VRV3NNTA.js} +2 -2
- package/dist/{chunk-HZA7OE7N.js → chunk-XCFC7LGF.js} +2 -1
- package/dist/{chunk-BL3VELKP.js → chunk-Y5VVP73C.js} +1 -1
- package/dist/{chunk-PI4LLJG4.js → chunk-YD2VAHA6.js} +2 -2
- package/dist/{chunk-UIRMA5KM.js → chunk-YDGMBMYI.js} +1 -1
- package/dist/{chunk-NWDF5HG3.js → chunk-YKL6SHRJ.js} +1 -1
- package/dist/{chunk-6X4KUDMU.js → chunk-ZCTUYGNV.js} +7 -2
- package/dist/chunk-ZUQW5VBZ.js +89 -0
- package/dist/class/class.codec.js +7 -7
- package/dist/class/class.parser.js +6 -6
- package/dist/class/class.schema.d.ts +18 -0
- package/dist/class/class.schema.js +5 -5
- 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/entities/base-item-resolver.js +2 -2
- package/dist/entities/slug.d.ts +18 -8
- package/dist/entities/slug.js +3 -1
- package/dist/feat/feat.codec.js +5 -5
- package/dist/feat/feat.parser.js +4 -4
- package/dist/feat/feat.schema.d.ts +41 -3
- package/dist/feat/feat.schema.js +3 -3
- package/dist/index.js +7513 -4177
- package/dist/item/item.actions-map.d.ts +40 -5
- package/dist/item/item.actions-map.js +5 -84
- package/dist/item/item.attunement.js +3 -9
- package/dist/item/item.bonuses.js +5 -2
- package/dist/item/item.codec.js +3 -3
- package/dist/item/item.conditions.js +4 -1
- package/dist/item/item.conditions.types.d.ts +4 -0
- package/dist/item/item.cost.d.ts +3 -0
- package/dist/item/item.cost.js +29 -0
- package/dist/item/item.icon.d.ts +15 -0
- package/dist/item/item.icon.js +211 -0
- package/dist/item/item.parser.js +2 -2
- package/dist/item/item.schema.d.ts +16 -1
- package/dist/item/item.schema.js +3 -1
- package/dist/item/item.types.d.ts +9 -2
- 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 +255 -3
- package/dist/optional-feature/optional-feature.schema.js +7 -2
- package/dist/pc/pc.additional-spells.js +3 -3
- package/dist/pc/pc.amount-bindings.d.ts +32 -0
- package/dist/pc/pc.amount-bindings.js +23 -0
- package/dist/pc/pc.asi-flatten.d.ts +62 -1
- package/dist/pc/pc.asi-flatten.js +16 -0
- package/dist/pc/pc.conditions.js +1 -1
- package/dist/pc/pc.containment.d.ts +45 -0
- package/dist/pc/pc.containment.js +24 -0
- package/dist/pc/pc.decision-engine.d.ts +24 -3
- package/dist/pc/pc.decision-engine.js +26 -16
- package/dist/pc/pc.entry-overrides.d.ts +79 -0
- package/dist/pc/pc.entry-overrides.js +40 -0
- package/dist/pc/pc.equipment.d.ts +2 -2
- package/dist/pc/pc.equipment.js +19 -7
- package/dist/pc/pc.feature-effects.d.ts +12 -0
- package/dist/pc/pc.feature-effects.js +2 -2
- package/dist/pc/pc.infusions.d.ts +228 -0
- package/dist/pc/pc.infusions.js +64 -0
- 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 +18 -1
- package/dist/pc/pc.pools.d.ts +8 -1
- package/dist/pc/pc.pools.js +13 -3
- package/dist/pc/pc.proficiencies.js +23 -15
- package/dist/pc/pc.proficiency-query.js +6 -0
- package/dist/pc/pc.recalc.d.ts +39 -36
- package/dist/pc/pc.recalc.js +115 -70
- package/dist/pc/pc.resolver.d.ts +2 -1
- package/dist/pc/pc.resolver.js +22 -14
- package/dist/pc/pc.resource-seed.js +1 -1
- package/dist/pc/pc.resources.d.ts +6 -1
- package/dist/pc/pc.resources.js +3 -1
- package/dist/pc/pc.rest.d.ts +21 -1
- package/dist/pc/pc.rest.js +52 -19
- package/dist/pc/pc.schema.d.ts +43 -2
- package/dist/pc/pc.schema.js +1 -1
- package/dist/pc/pc.slotting.d.ts +36 -3
- package/dist/pc/pc.slotting.js +13 -5
- package/dist/pc/pc.tool-picks.d.ts +51 -0
- package/dist/pc/pc.tool-picks.js +18 -0
- package/dist/pc/pc.types.d.ts +115 -14
- package/dist/pc/pc.weapon-grant.d.ts +15 -0
- package/dist/pc/pool-layout.d.ts +8 -0
- package/dist/pc/pool-layout.js +5 -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/attack-schema.d.ts +1 -1
- package/dist/schemas/choice-schema.js +2 -2
- package/dist/schemas/feature-effect-schema.d.ts +41 -3
- package/dist/schemas/feature-effect-schema.js +1 -1
- package/dist/schemas/feature-schema.js +3 -3
- package/dist/schemas/selection-pool-schema.d.ts +10 -0
- package/dist/schemas/selection-pool-schema.js +1 -1
- 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/srd/retired-items.d.ts +14 -0
- package/dist/srd/retired-items.js +11 -0
- package/dist/subclass/subclass.codec.js +7 -7
- package/dist/subclass/subclass.parser.js +6 -6
- package/dist/subclass/subclass.schema.d.ts +10 -0
- package/dist/subclass/subclass.schema.js +5 -5
- package/dist/types/choice.d.ts +13 -0
- package/dist/types/equipment-grant.d.ts +18 -1
- package/dist/types/equipment-grant.js +12 -0
- package/dist/types/feature-effect.d.ts +12 -1
- package/dist/types/optional-feature.types.d.ts +84 -1
- package/dist/types/selection-pool.d.ts +18 -3
- package/dist/weapon/weapon.classify.d.ts +10 -8
- package/dist/weapon/weapon.classify.js +2 -2
- package/dist/weapon/weapon.codec.js +3 -3
- package/dist/weapon/weapon.parser.js +2 -2
- package/dist/weapon/weapon.schema.d.ts +1 -0
- package/dist/weapon/weapon.schema.js +1 -1
- package/dist/weapon/weapon.types.d.ts +2 -0
- package/package.json +1 -1
- package/dist/chunk-O4SRD3J5.js +0 -165
- package/dist/chunk-WXWPK3LI.js +0 -55
|
@@ -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,
|
|
@@ -46,6 +49,58 @@ var usesSchema = z.object({
|
|
|
46
49
|
restores: z.enum(["uses", "spell-slots"]).optional()
|
|
47
50
|
})).optional()
|
|
48
51
|
});
|
|
52
|
+
var filterSpecSchema = z.object({
|
|
53
|
+
rarity: z.array(z.string().min(1)).min(1),
|
|
54
|
+
types: z.array(z.string().min(1)).optional(),
|
|
55
|
+
exclude_types: z.array(z.string().min(1)).optional(),
|
|
56
|
+
cursed: z.literal(false).optional()
|
|
57
|
+
}).strict();
|
|
58
|
+
var optionCreatesSchema = z.object({
|
|
59
|
+
item: z.string().regex(wikilinkRegex).optional(),
|
|
60
|
+
variant: z.object({ candidates: z.array(z.string().regex(wikilinkRegex)).min(1) }).strict().optional(),
|
|
61
|
+
filter: filterSpecSchema.optional()
|
|
62
|
+
}).strict().refine((c) => [c.item, c.variant, c.filter].filter((a) => a !== void 0).length === 1, { message: "creates needs exactly one of item, variant, filter" });
|
|
63
|
+
var optionRowSchema = z.object({
|
|
64
|
+
key: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/, "an option key is lower-case, with no '#' and no whitespace"),
|
|
65
|
+
name: z.string().min(1),
|
|
66
|
+
min_level: z.number().int().min(1).max(20),
|
|
67
|
+
attunement: z.boolean().optional(),
|
|
68
|
+
creates: optionCreatesSchema
|
|
69
|
+
}).strict();
|
|
70
|
+
var infuseItemSchema = z.object({
|
|
71
|
+
bonuses: z.object({
|
|
72
|
+
ac: z.number().int().optional(),
|
|
73
|
+
weapon_attack: z.number().int().optional(),
|
|
74
|
+
weapon_damage: z.number().int().optional(),
|
|
75
|
+
spell_attack: z.number().int().optional(),
|
|
76
|
+
saving_throws: z.number().int().optional(),
|
|
77
|
+
speed: z.object({ walk: z.number().int().optional(), fly: z.number().int().optional(), swim: z.number().int().optional(), climb: z.number().int().optional() }).strict().optional()
|
|
78
|
+
}).strict().optional(),
|
|
79
|
+
resist: z.array(z.string().min(1)).optional(),
|
|
80
|
+
charges: z.object({ max: z.number().int().min(1), dice: z.string().optional(), recharge: z.string().optional(), recharge_amount: z.string().optional() }).strict().optional(),
|
|
81
|
+
action: actionCostEnum.optional(),
|
|
82
|
+
// ART-c1 (R-3 a): the Actions row's range, as data (B-8.4 draws "Self").
|
|
83
|
+
range: z.string().min(1).optional()
|
|
84
|
+
}).strict();
|
|
85
|
+
var infuseBlockSchema = z.object({
|
|
86
|
+
targets: z.array(z.object({
|
|
87
|
+
kind: z.enum(["armor", "shield", "weapon", "gear"]),
|
|
88
|
+
category: z.array(z.string().min(1)).min(1).optional(),
|
|
89
|
+
property: z.string().min(1).optional(),
|
|
90
|
+
name_any: z.array(z.string().min(1)).min(1).optional()
|
|
91
|
+
}).strict()).min(1),
|
|
92
|
+
targets_label: z.string().min(1).optional(),
|
|
93
|
+
new_object: z.string().min(1).optional(),
|
|
94
|
+
rarity: z.string().min(1).optional(),
|
|
95
|
+
attunement: z.object({ required: z.literal(true) }).strict().optional(),
|
|
96
|
+
// ART-c1 (R-3 b): the damage types the choice offers (v1 U-4 draws the TCE ten); absent keeps dnd5e's canonical list.
|
|
97
|
+
choice: z.object({ kind: z.literal("damage-type"), options: z.array(z.string().min(1)).min(1).optional() }).strict().optional(),
|
|
98
|
+
item: infuseItemSchema.optional(),
|
|
99
|
+
scales_at: z.array(z.object({ level: z.number().int().min(1).max(20), item: infuseItemSchema }).strict()).optional()
|
|
100
|
+
}).strict().refine((b) => {
|
|
101
|
+
var _a;
|
|
102
|
+
return ((_a = b.scales_at) != null ? _a : []).every((r, i, a) => i === 0 || a[i - 1].level < r.level);
|
|
103
|
+
}, { message: "scales_at levels ascend" });
|
|
49
104
|
var optionalFeatureEntitySchema = z.object({
|
|
50
105
|
slug: z.string().min(1),
|
|
51
106
|
name: z.string().min(1),
|
|
@@ -82,9 +137,22 @@ var optionalFeatureEntitySchema = z.object({
|
|
|
82
137
|
// 1
|
|
83
138
|
optionalfeature_progression: z.array(progressionSchema).optional(),
|
|
84
139
|
// 1
|
|
85
|
-
image: imageField
|
|
140
|
+
image: imageField,
|
|
141
|
+
repeatable: z.literal(true).optional(),
|
|
142
|
+
item_choice: z.enum(["learn", "create"]).optional(),
|
|
143
|
+
options: z.array(optionRowSchema).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()
|
|
147
|
+
}).superRefine((d, ctx) => {
|
|
148
|
+
var _a;
|
|
149
|
+
if (d.repeatable && (!d.options || !d.item_choice)) ctx.addIssue({ code: "custom", message: "a repeatable note needs options and item_choice" });
|
|
150
|
+
const keys = ((_a = d.options) != null ? _a : []).map((o) => o.key);
|
|
151
|
+
if (new Set(keys).size !== keys.length) ctx.addIssue({ code: "custom", message: "option keys are unique" });
|
|
86
152
|
});
|
|
87
153
|
|
|
88
154
|
export {
|
|
155
|
+
optionRowSchema,
|
|
156
|
+
infuseBlockSchema,
|
|
89
157
|
optionalFeatureEntitySchema
|
|
90
158
|
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveBaseItem
|
|
3
|
+
} from "./chunk-CM5CWO4K.js";
|
|
4
|
+
import {
|
|
5
|
+
bareEntitySlug,
|
|
6
|
+
slugify,
|
|
7
|
+
wikilinkTailSlug
|
|
8
|
+
} from "./chunk-ZCTUYGNV.js";
|
|
9
|
+
|
|
10
|
+
// src/pc/pc.containment.ts
|
|
11
|
+
function nonEmptyRef(v) {
|
|
12
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
13
|
+
}
|
|
14
|
+
function isContainerItem(entity) {
|
|
15
|
+
if (!entity || typeof entity !== "object") return false;
|
|
16
|
+
const c = entity.container;
|
|
17
|
+
if (!c || typeof c !== "object") return false;
|
|
18
|
+
const container = c;
|
|
19
|
+
if (typeof container.holds_items === "boolean") return container.holds_items;
|
|
20
|
+
if (typeof container.capacity_weight === "number" && container.capacity_weight > 0) return true;
|
|
21
|
+
const cap = container.capacity;
|
|
22
|
+
if (!cap || typeof cap !== "object") return false;
|
|
23
|
+
const k = cap;
|
|
24
|
+
if (Array.isArray(k.weight) && k.weight.length > 0) return true;
|
|
25
|
+
if (Array.isArray(k.item) && k.item.length > 0) return true;
|
|
26
|
+
return k.weightless === true;
|
|
27
|
+
}
|
|
28
|
+
function idOwners(equipment) {
|
|
29
|
+
const ownerOfId = /* @__PURE__ */ new Map();
|
|
30
|
+
const duplicateIds = [];
|
|
31
|
+
equipment.forEach((entry, i) => {
|
|
32
|
+
const id = nonEmptyRef(entry.id);
|
|
33
|
+
if (id === null) return;
|
|
34
|
+
if (ownerOfId.has(id)) duplicateIds.push(i);
|
|
35
|
+
else ownerOfId.set(id, i);
|
|
36
|
+
});
|
|
37
|
+
return { ownerOfId, duplicateIds };
|
|
38
|
+
}
|
|
39
|
+
function entityOfEntry(entry, registry) {
|
|
40
|
+
var _a, _b;
|
|
41
|
+
if (!registry) return null;
|
|
42
|
+
return (_b = (_a = resolveBaseItem(entry.item, registry)) == null ? void 0 : _a.data) != null ? _b : null;
|
|
43
|
+
}
|
|
44
|
+
function resolveContainment(equipment, registry) {
|
|
45
|
+
const n = equipment.length;
|
|
46
|
+
const { ownerOfId, duplicateIds } = idOwners(equipment);
|
|
47
|
+
const entities = equipment.map((entry) => entityOfEntry(entry, registry));
|
|
48
|
+
const isContainer = entities.map((entity) => isContainerItem(entity));
|
|
49
|
+
const parentOf = new Array(n).fill(null);
|
|
50
|
+
const broken = [];
|
|
51
|
+
const closesCycle = (child, owner) => {
|
|
52
|
+
for (let cur = owner; cur !== null; cur = parentOf[cur]) if (cur === child) return true;
|
|
53
|
+
return false;
|
|
54
|
+
};
|
|
55
|
+
for (let i = 0; i < n; i++) {
|
|
56
|
+
const target = nonEmptyRef(equipment[i].in);
|
|
57
|
+
if (target === null) continue;
|
|
58
|
+
const owner = ownerOfId.get(target);
|
|
59
|
+
let reason = null;
|
|
60
|
+
if (equipment[i].equipped === true) reason = "equipped";
|
|
61
|
+
else if (owner === void 0) reason = "missing";
|
|
62
|
+
else if (owner === i) reason = "self";
|
|
63
|
+
else if (entities[owner] === null) reason = "unresolved";
|
|
64
|
+
else if (!isContainer[owner]) reason = "not-container";
|
|
65
|
+
else if (closesCycle(i, owner)) reason = "cycle";
|
|
66
|
+
if (reason !== null) {
|
|
67
|
+
broken.push({ index: i, reason });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
parentOf[i] = owner;
|
|
71
|
+
}
|
|
72
|
+
const childrenOf = Array.from({ length: n }, () => []);
|
|
73
|
+
parentOf.forEach((p, i) => {
|
|
74
|
+
if (p !== null) childrenOf[p].push(i);
|
|
75
|
+
});
|
|
76
|
+
return { parentOf, childrenOf, isContainer, ownerOfId, duplicateIds, broken };
|
|
77
|
+
}
|
|
78
|
+
function mintBase(entry, registry) {
|
|
79
|
+
var _a, _b, _c;
|
|
80
|
+
const found = registry ? resolveBaseItem(entry.item, registry) : null;
|
|
81
|
+
const byName = found ? slugify((_a = found.name) != null ? _a : "") : "";
|
|
82
|
+
if (byName) return byName;
|
|
83
|
+
const inner = (_c = (_b = /^\[\[(.+?)\]\]$/.exec(entry.item.trim())) == null ? void 0 : _b[1]) != null ? _c : entry.item.trim();
|
|
84
|
+
const ref = inner.split("|")[0].trim();
|
|
85
|
+
const byRef = ref.includes("/") ? wikilinkTailSlug(ref) : slugify(bareEntitySlug(ref));
|
|
86
|
+
return byRef || "item";
|
|
87
|
+
}
|
|
88
|
+
function ensureEntryId(equipment, index, registry) {
|
|
89
|
+
const { ownerOfId } = idOwners(equipment);
|
|
90
|
+
const own = nonEmptyRef(equipment[index].id);
|
|
91
|
+
if (own !== null && ownerOfId.get(own) === index) return own;
|
|
92
|
+
const taken = /* @__PURE__ */ new Set();
|
|
93
|
+
for (const entry of equipment) {
|
|
94
|
+
const a = nonEmptyRef(entry.id);
|
|
95
|
+
if (a !== null) taken.add(a);
|
|
96
|
+
const b = nonEmptyRef(entry.in);
|
|
97
|
+
if (b !== null) taken.add(b);
|
|
98
|
+
}
|
|
99
|
+
const base = mintBase(equipment[index], registry);
|
|
100
|
+
let id = base;
|
|
101
|
+
for (let k = 2; taken.has(id); k++) id = `${base}-${k}`;
|
|
102
|
+
equipment[index].id = id;
|
|
103
|
+
return id;
|
|
104
|
+
}
|
|
105
|
+
function canMoveInto(equipment, index, target, registry) {
|
|
106
|
+
var _a;
|
|
107
|
+
const inRange = (i) => Number.isInteger(i) && i >= 0 && i < equipment.length;
|
|
108
|
+
if (!inRange(index) || target !== null && !inRange(target)) return { ok: false, reason: "out-of-range" };
|
|
109
|
+
if (target === null) return { ok: true };
|
|
110
|
+
if (equipment[index].equipped === true) return { ok: false, reason: "equipped" };
|
|
111
|
+
if (target === index) return { ok: false, reason: "self" };
|
|
112
|
+
const view = resolveContainment(equipment, registry);
|
|
113
|
+
const unresolvedLink = new Set(view.broken.filter((b) => b.reason === "unresolved").map((b) => b.index));
|
|
114
|
+
const seen = /* @__PURE__ */ new Set();
|
|
115
|
+
for (let cur = target; cur !== null && !seen.has(cur); ) {
|
|
116
|
+
if (cur === index) return { ok: false, reason: "descendant" };
|
|
117
|
+
seen.add(cur);
|
|
118
|
+
const up = view.parentOf[cur];
|
|
119
|
+
if (up !== null) {
|
|
120
|
+
cur = up;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (unresolvedLink.has(cur)) {
|
|
124
|
+
const raw = nonEmptyRef(equipment[cur].in);
|
|
125
|
+
cur = raw === null ? null : (_a = view.ownerOfId.get(raw)) != null ? _a : null;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
cur = null;
|
|
129
|
+
}
|
|
130
|
+
const entity = entityOfEntry(equipment[target], registry);
|
|
131
|
+
if (entity === null) return { ok: false, reason: "unresolved" };
|
|
132
|
+
if (!isContainerItem(entity)) return { ok: false, reason: "not-container" };
|
|
133
|
+
return { ok: true };
|
|
134
|
+
}
|
|
135
|
+
function moveInto(equipment, index, target, registry) {
|
|
136
|
+
const check = canMoveInto(equipment, index, target, registry);
|
|
137
|
+
if (!check.ok) return check;
|
|
138
|
+
const entry = equipment[index];
|
|
139
|
+
if (target === null) {
|
|
140
|
+
const had = entry.in !== void 0;
|
|
141
|
+
delete entry.in;
|
|
142
|
+
return { ok: true, changed: had };
|
|
143
|
+
}
|
|
144
|
+
const wasHonoured = resolveContainment(equipment, registry).parentOf[index] === target;
|
|
145
|
+
const idBefore = equipment[target].id;
|
|
146
|
+
const id = ensureEntryId(equipment, target, registry);
|
|
147
|
+
entry.in = id;
|
|
148
|
+
return { ok: true, changed: idBefore !== id || !wasHonoured };
|
|
149
|
+
}
|
|
150
|
+
function repairContainment(equipment, registry) {
|
|
151
|
+
if (!registry) return 0;
|
|
152
|
+
let removed = 0;
|
|
153
|
+
for (const entry of equipment) {
|
|
154
|
+
if (entry.id === "") {
|
|
155
|
+
delete entry.id;
|
|
156
|
+
removed++;
|
|
157
|
+
}
|
|
158
|
+
if (entry.in === "") {
|
|
159
|
+
delete entry.in;
|
|
160
|
+
removed++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const view = resolveContainment(equipment, registry);
|
|
164
|
+
for (const b of view.broken) {
|
|
165
|
+
if (b.reason === "unresolved") continue;
|
|
166
|
+
delete equipment[b.index].in;
|
|
167
|
+
removed++;
|
|
168
|
+
}
|
|
169
|
+
for (const i of view.duplicateIds) {
|
|
170
|
+
delete equipment[i].id;
|
|
171
|
+
removed++;
|
|
172
|
+
}
|
|
173
|
+
return removed;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
nonEmptyRef,
|
|
178
|
+
isContainerItem,
|
|
179
|
+
idOwners,
|
|
180
|
+
entityOfEntry,
|
|
181
|
+
resolveContainment,
|
|
182
|
+
ensureEntryId,
|
|
183
|
+
canMoveInto,
|
|
184
|
+
moveInto,
|
|
185
|
+
repairContainment
|
|
186
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
itemEntitySchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-S3YUXDSC.js";
|
|
4
4
|
|
|
5
5
|
// src/item/item.parser.ts
|
|
6
6
|
import { parseYaml } from "@archivist-gg/core";
|
|
@@ -25,6 +25,8 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
25
25
|
"sentient",
|
|
26
26
|
"focus",
|
|
27
27
|
"tier",
|
|
28
|
+
"tattoo",
|
|
29
|
+
// declared I2a; the converter emits it (27 carriers)
|
|
28
30
|
"damage",
|
|
29
31
|
"weapon_category",
|
|
30
32
|
"armor_category",
|
|
@@ -46,6 +48,7 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
46
48
|
// copied into `raw` as well, so the two lists must move together.
|
|
47
49
|
"modify_speed",
|
|
48
50
|
"rendering_hint",
|
|
51
|
+
"icon",
|
|
49
52
|
"has_fluff",
|
|
50
53
|
"has_fluff_images",
|
|
51
54
|
"image",
|
|
@@ -11,8 +11,12 @@ var selectionPoolSchema = z.object({
|
|
|
11
11
|
available_to: z.literal("self")
|
|
12
12
|
})
|
|
13
13
|
}),
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// ART-a (spec §4.5): `count.label`, `active` and `head_resource` carry NO zod default: the defaults are applied at
|
|
15
|
+
// read by `poolCountLabel` / `poolActiveWords`, so a parse never adds a key (the importer's round trip).
|
|
16
|
+
count: z.object({ column: z.string().min(1), label: z.string().min(1).optional() }),
|
|
17
|
+
replaceable: z.boolean().optional(),
|
|
18
|
+
active: z.object({ column: z.string().min(1), label: z.string().min(1), verb: z.string().min(1), counter_label: z.string().min(1).optional(), create_label: z.string().min(1).optional(), create_verb: z.string().min(1).optional() }).optional(),
|
|
19
|
+
head_resource: z.string().min(1).optional()
|
|
16
20
|
});
|
|
17
21
|
var poolGrantSchema = z.object({
|
|
18
22
|
pool: z.string().min(1),
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ALL_TOOLS,
|
|
3
|
+
ARTISANS_TOOLS,
|
|
4
|
+
GAMING_SETS,
|
|
5
|
+
MUSICAL_INSTRUMENTS
|
|
6
|
+
} from "./chunk-KU3YLF6D.js";
|
|
7
|
+
import {
|
|
8
|
+
toProfSlug
|
|
9
|
+
} from "./chunk-RLGQ5PZZ.js";
|
|
10
|
+
|
|
11
|
+
// src/pc/pc.tool-picks.ts
|
|
12
|
+
var TOOL_CATEGORIES = {
|
|
13
|
+
"musical-instrument": { pool: MUSICAL_INSTRUMENTS, itemType: "instrument", label: "a musical instrument" },
|
|
14
|
+
"artisan's-tools": { pool: ARTISANS_TOOLS, itemType: "artisan's tools", label: "artisan's tools" },
|
|
15
|
+
"gaming-set": { pool: GAMING_SETS, itemType: "gaming set", label: "a gaming set" }
|
|
16
|
+
};
|
|
17
|
+
function categoryWords(token) {
|
|
18
|
+
return token.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/['’ʼ]/g, "").split(/[\s_-]+/).filter(Boolean).map((w) => w.length > 3 && w.endsWith("s") && !w.endsWith("ss") ? w.slice(0, -1) : w);
|
|
19
|
+
}
|
|
20
|
+
var CATEGORY_BY_WORDS = /* @__PURE__ */ new Map([
|
|
21
|
+
["musical instrument", "musical-instrument"],
|
|
22
|
+
["artisan tool", "artisan's-tools"],
|
|
23
|
+
["gaming set", "gaming-set"]
|
|
24
|
+
]);
|
|
25
|
+
function toolCategoriesOf(token) {
|
|
26
|
+
const parts = [[]];
|
|
27
|
+
for (const w of categoryWords(token)) {
|
|
28
|
+
if (w === "or") parts.push([]);
|
|
29
|
+
else parts[parts.length - 1].push(w);
|
|
30
|
+
}
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const p of parts) {
|
|
33
|
+
const c = CATEGORY_BY_WORDS.get(p.filter((w) => w !== "any" && w !== "one").join(" "));
|
|
34
|
+
if (!c) return void 0;
|
|
35
|
+
if (!out.includes(c)) out.push(c);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function expandToolPool(from) {
|
|
40
|
+
const out = [];
|
|
41
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42
|
+
const add = (v) => {
|
|
43
|
+
const k = toProfSlug(v);
|
|
44
|
+
if (seen.has(k)) return;
|
|
45
|
+
seen.add(k);
|
|
46
|
+
out.push(v);
|
|
47
|
+
};
|
|
48
|
+
for (const v of from) {
|
|
49
|
+
const cats = toolCategoriesOf(v);
|
|
50
|
+
if (cats) for (const c of cats) for (const t of TOOL_CATEGORIES[c].pool) add(t);
|
|
51
|
+
else add(v);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
var EMPTY = { decisions: [], grants: [] };
|
|
56
|
+
var poolOf = (from) => Array.isArray(from) ? from.filter((v) => typeof v === "string" && v.length > 0) : typeof from === "string" && from.length > 0 ? [from] : [];
|
|
57
|
+
var countOf = (count) => {
|
|
58
|
+
const n = Number(count != null ? count : 1);
|
|
59
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 1;
|
|
60
|
+
};
|
|
61
|
+
var KNOWN_TOOLS = new Set(ALL_TOOLS.map(toProfSlug));
|
|
62
|
+
var namesToolsOutright = (from) => from.every((v) => !toolCategoriesOf(v) && KNOWN_TOOLS.has(toProfSlug(v)));
|
|
63
|
+
var authorsToolPick = (choices) => Array.isArray(choices) && choices.some((c) => !!c && typeof c === "object" && c.kind === "select-proficiency" && c.domain === "tool");
|
|
64
|
+
function toolPick(count, from, id, allowDecision) {
|
|
65
|
+
const authored = poolOf(from);
|
|
66
|
+
const pool = expandToolPool(authored);
|
|
67
|
+
const n = countOf(count);
|
|
68
|
+
if (pool.length === 0) return EMPTY;
|
|
69
|
+
if (n >= pool.length && namesToolsOutright(authored)) return { decisions: [], grants: pool };
|
|
70
|
+
if (!allowDecision) return EMPTY;
|
|
71
|
+
return { decisions: [{ kind: "select-proficiency", id, count: n, domain: "tool", from: pool }], grants: [] };
|
|
72
|
+
}
|
|
73
|
+
function classToolPicks(entity) {
|
|
74
|
+
var _a;
|
|
75
|
+
const tools = (_a = entity == null ? void 0 : entity.proficiencies) == null ? void 0 : _a.tools;
|
|
76
|
+
const choice = tools == null ? void 0 : tools.choice;
|
|
77
|
+
if (!choice || typeof choice !== "object") return EMPTY;
|
|
78
|
+
return toolPick(choice.count, choice.from, "tool", !authorsToolPick(entity == null ? void 0 : entity.choices));
|
|
79
|
+
}
|
|
80
|
+
function backgroundToolPicks(bg) {
|
|
81
|
+
const entries = Array.isArray(bg == null ? void 0 : bg.tool_proficiencies) ? bg.tool_proficiencies : [];
|
|
82
|
+
const allowDecision = !authorsToolPick(bg == null ? void 0 : bg.choices);
|
|
83
|
+
const out = { decisions: [], grants: [] };
|
|
84
|
+
for (const e of entries) {
|
|
85
|
+
if (!e || typeof e !== "object" || e.kind !== "choice") continue;
|
|
86
|
+
const id = out.decisions.length === 0 ? "tool" : `tool-${out.decisions.length + 1}`;
|
|
87
|
+
const r = toolPick(e.count, e.from, id, allowDecision);
|
|
88
|
+
out.decisions.push(...r.decisions);
|
|
89
|
+
out.grants.push(...r.grants);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
TOOL_CATEGORIES,
|
|
96
|
+
categoryWords,
|
|
97
|
+
toolCategoriesOf,
|
|
98
|
+
expandToolPool,
|
|
99
|
+
classToolPicks,
|
|
100
|
+
backgroundToolPicks
|
|
101
|
+
};
|
|
@@ -32,6 +32,8 @@ var armorEntitySchema = z.object({
|
|
|
32
32
|
// string today, so `.min(1)` is forbidden here.
|
|
33
33
|
rendering_hint: z.string().optional(),
|
|
34
34
|
// 27
|
|
35
|
+
// The icon key (I3): a key of the renderer's icon set or a Lucide name; itemIconKey reads it first.
|
|
36
|
+
icon: z.string().optional(),
|
|
35
37
|
has_fluff: z.boolean().optional(),
|
|
36
38
|
// 0 carriers — symmetry with weapon
|
|
37
39
|
has_fluff_images: z.boolean().optional(),
|