@dreamboard-games/sdk 0.5.0-alpha.2 → 0.5.0-alpha.4
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/README.md +186 -45
- package/dist/board-target-B0QG6OYx.d.ts +8 -0
- package/dist/chunk-3JXUL4VN.js +139 -0
- package/dist/chunk-3JXUL4VN.js.map +1 -0
- package/dist/{chunk-S2JEDVWH.js → chunk-C77SYIVT.js} +6102 -6505
- package/dist/chunk-C77SYIVT.js.map +1 -0
- package/dist/chunk-DMFVAZI2.js +16 -0
- package/dist/chunk-DMFVAZI2.js.map +1 -0
- package/dist/chunk-OOFMBCFF.js +1338 -0
- package/dist/chunk-OOFMBCFF.js.map +1 -0
- package/dist/chunk-UG2Q4NLG.js +590 -0
- package/dist/chunk-UG2Q4NLG.js.map +1 -0
- package/dist/chunk-XU6WJW6K.js +847 -0
- package/dist/chunk-XU6WJW6K.js.map +1 -0
- package/dist/diagnostics-B8d5aLad.d.ts +65 -0
- package/dist/extract-gVKcB8Og.d.ts +4736 -0
- package/dist/index.d.ts +3782 -1
- package/dist/index.js +1096 -6
- package/dist/index.js.map +1 -1
- package/dist/model-vdpLHrxh.d.ts +432 -0
- package/dist/react.d.ts +60 -0
- package/dist/react.js +208 -0
- package/dist/react.js.map +1 -0
- package/dist/reducer.d.ts +1244 -78
- package/dist/reducer.js +4204 -121
- package/dist/reducer.js.map +1 -1
- package/dist/testing.d.ts +520 -564
- package/dist/testing.js +2451 -915
- package/dist/testing.js.map +1 -1
- package/dist/types-Bj5ZLTUV.d.ts +114 -0
- package/package.json +40 -142
- package/dist/HandView-Bi2lxzQP.d.ts +0 -79
- package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
- package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
- package/dist/attributes-DbvyMbXw.d.ts +0 -68
- package/dist/browser-interaction.d.ts +0 -910
- package/dist/browser-interaction.js +0 -119
- package/dist/browser-interaction.js.map +0 -1
- package/dist/chunk-32PFKDV7.js +0 -11705
- package/dist/chunk-32PFKDV7.js.map +0 -1
- package/dist/chunk-3BAD4VC2.js +0 -263
- package/dist/chunk-3BAD4VC2.js.map +0 -1
- package/dist/chunk-3SKDNDPG.js +0 -104
- package/dist/chunk-3SKDNDPG.js.map +0 -1
- package/dist/chunk-5YV2WHJ4.js +0 -25
- package/dist/chunk-5YV2WHJ4.js.map +0 -1
- package/dist/chunk-ANRB4XKA.js +0 -90
- package/dist/chunk-ANRB4XKA.js.map +0 -1
- package/dist/chunk-BI4G3PM2.js +0 -1864
- package/dist/chunk-BI4G3PM2.js.map +0 -1
- package/dist/chunk-EVCLTY4Q.js +0 -935
- package/dist/chunk-EVCLTY4Q.js.map +0 -1
- package/dist/chunk-GDPKTBUR.js +0 -564
- package/dist/chunk-GDPKTBUR.js.map +0 -1
- package/dist/chunk-H6VDGFL5.js +0 -2903
- package/dist/chunk-H6VDGFL5.js.map +0 -1
- package/dist/chunk-LR3ZTWQF.js +0 -5960
- package/dist/chunk-LR3ZTWQF.js.map +0 -1
- package/dist/chunk-PF7L4BMG.js +0 -117
- package/dist/chunk-PF7L4BMG.js.map +0 -1
- package/dist/chunk-PN5O6GG2.js +0 -32
- package/dist/chunk-PN5O6GG2.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-S2JEDVWH.js.map +0 -1
- package/dist/chunk-T3ZKNUZ7.js +0 -1
- package/dist/chunk-T3ZKNUZ7.js.map +0 -1
- package/dist/chunk-UGH54WZ2.js +0 -223
- package/dist/chunk-UGH54WZ2.js.map +0 -1
- package/dist/chunk-VCDWP5VL.js +0 -746
- package/dist/chunk-VCDWP5VL.js.map +0 -1
- package/dist/chunk-VDXOF4FW.js +0 -69
- package/dist/chunk-VDXOF4FW.js.map +0 -1
- package/dist/chunk-WHR5UW3F.js +0 -1988
- package/dist/chunk-WHR5UW3F.js.map +0 -1
- package/dist/chunk-Y75CFE77.js +0 -17
- package/dist/chunk-Y75CFE77.js.map +0 -1
- package/dist/chunk-Z7QCREWI.js +0 -3435
- package/dist/chunk-Z7QCREWI.js.map +0 -1
- package/dist/chunk-ZZGN2ATS.js +0 -1941
- package/dist/chunk-ZZGN2ATS.js.map +0 -1
- package/dist/components-gAbIz7hK.d.ts +0 -1284
- package/dist/definitions-BgmV_GhC.d.ts +0 -300
- package/dist/diagnostics-DIi3ina7.d.ts +0 -50
- package/dist/digest.d.ts +0 -8
- package/dist/game-CsZScpcn.d.ts +0 -763
- package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
- package/dist/index-CcT9Q7N7.d.ts +0 -193
- package/dist/index.d-DjzoK7zn.d.ts +0 -2101
- package/dist/package-set.d.ts +0 -13
- package/dist/package-set.js +0 -12
- package/dist/package-set.js.map +0 -1
- package/dist/player-state-Cqpyeql0.d.ts +0 -371
- package/dist/plugin-runtime-contract.d.ts +0 -17
- package/dist/plugin-runtime-contract.js +0 -92
- package/dist/plugin-runtime-contract.js.map +0 -1
- package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
- package/dist/protocol-dYgafTYY.d.ts +0 -314
- package/dist/reducer/advanced.d.ts +0 -82
- package/dist/reducer/advanced.js +0 -51
- package/dist/reducer/advanced.js.map +0 -1
- package/dist/reducer-contract.d.ts +0 -11
- package/dist/reducer-contract.js +0 -16
- package/dist/reducer-contract.js.map +0 -1
- package/dist/reference-games/index.d.ts +0 -31
- package/dist/reference-games/index.js +0 -48
- package/dist/reference-games/index.js.map +0 -1
- package/dist/runtime/primitives.d.ts +0 -250
- package/dist/runtime/primitives.js +0 -189
- package/dist/runtime/primitives.js.map +0 -1
- package/dist/runtime/runtime-api.d.ts +0 -2
- package/dist/runtime/runtime-api.js +0 -1
- package/dist/runtime/runtime-api.js.map +0 -1
- package/dist/runtime/workspace-contract.d.ts +0 -392
- package/dist/runtime/workspace-contract.js +0 -28
- package/dist/runtime/workspace-contract.js.map +0 -1
- package/dist/runtime-RJ5orDJU.d.ts +0 -1699
- package/dist/runtime-api-Bz5pwNU_.d.ts +0 -296
- package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
- package/dist/runtime.d.ts +0 -83
- package/dist/runtime.js +0 -242
- package/dist/runtime.js.map +0 -1
- package/dist/schema.d.ts +0 -379
- package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
- package/dist/testing-compiler.d.ts +0 -37
- package/dist/testing-compiler.js +0 -272
- package/dist/testing-compiler.js.map +0 -1
- package/dist/testing-runtime.d.ts +0 -119
- package/dist/testing-runtime.js +0 -212
- package/dist/testing-runtime.js.map +0 -1
- package/dist/types-DJj5MJkl.d.ts +0 -256
- package/dist/types-DR7DoB1x.d.ts +0 -28
- package/dist/types-JWCYHmu7.d.ts +0 -122
- package/dist/types.d.ts +0 -1958
- package/dist/types.js +0 -14
- package/dist/types.js.map +0 -1
- package/dist/ui/components.d.ts +0 -17
- package/dist/ui/components.js +0 -216
- package/dist/ui/components.js.map +0 -1
- package/dist/ui/defaults.d.ts +0 -19
- package/dist/ui/defaults.js +0 -104
- package/dist/ui/defaults.js.map +0 -1
- package/dist/ui/player-state.d.ts +0 -2
- package/dist/ui/player-state.js +0 -1
- package/dist/ui/player-state.js.map +0 -1
- package/dist/ui/plugin-styles.css +0 -2
- package/dist/ui-contract-GTUPkf8z.d.ts +0 -1168
- package/dist/ui.d.ts +0 -317
- package/dist/ui.js +0 -277
- package/dist/ui.js.map +0 -1
- package/dist/views-BpuWcOyN.d.ts +0 -1641
package/dist/chunk-Z7QCREWI.js
DELETED
|
@@ -1,3435 +0,0 @@
|
|
|
1
|
-
// src/reducer/manifest/hex-geometry.ts
|
|
2
|
-
var HEX_CORNERS = ["ne-e", "e-se", "se-sw", "sw-w", "w-nw", "nw-ne"];
|
|
3
|
-
var HEX_CORNER_OFFSETS = {
|
|
4
|
-
"ne-e": [2, -1, -1],
|
|
5
|
-
"e-se": [1, -2, 1],
|
|
6
|
-
"se-sw": [-1, -1, 2],
|
|
7
|
-
"sw-w": [-2, 1, 1],
|
|
8
|
-
"w-nw": [-1, 2, -1],
|
|
9
|
-
"nw-ne": [1, 1, -2]
|
|
10
|
-
};
|
|
11
|
-
function cubeFromAxial(space) {
|
|
12
|
-
const x = space.q;
|
|
13
|
-
const z = space.r;
|
|
14
|
-
return [x, -x - z, z];
|
|
15
|
-
}
|
|
16
|
-
function cornerGeometryKey(space, corner) {
|
|
17
|
-
const [x, y, z] = cubeFromAxial(space);
|
|
18
|
-
const [dx, dy, dz] = HEX_CORNER_OFFSETS[corner];
|
|
19
|
-
return `${3 * x + dx},${3 * y + dy},${3 * z + dz}`;
|
|
20
|
-
}
|
|
21
|
-
function resolveHexVertexGeometryKey(ref, spacesById) {
|
|
22
|
-
const resolvedSpaces = [...ref.spaces].sort((left, right) => left.localeCompare(right)).map((spaceId) => {
|
|
23
|
-
const space = spacesById.get(spaceId);
|
|
24
|
-
if (!space) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
`Hex vertex ref references unknown space '${spaceId}'.`
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
return space;
|
|
30
|
-
});
|
|
31
|
-
const keyCounts = /* @__PURE__ */ new Map();
|
|
32
|
-
for (const space of resolvedSpaces) {
|
|
33
|
-
for (const corner of HEX_CORNERS) {
|
|
34
|
-
const key = cornerGeometryKey(space, corner);
|
|
35
|
-
keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const candidates = [...keyCounts.entries()].filter(([, count]) => count === resolvedSpaces.length).map(([key]) => key).sort((left, right) => left.localeCompare(right));
|
|
39
|
-
if (candidates.length !== 1) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`Hex vertex ref spaces '${ref.spaces.join(", ")}' do not resolve to exactly one shared vertex.`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
const vertexKey = candidates[0];
|
|
45
|
-
if (vertexKey === void 0) {
|
|
46
|
-
throw new Error("internal: expected exactly one hex vertex geometry key");
|
|
47
|
-
}
|
|
48
|
-
return vertexKey;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// src/reducer/manifest/preset-card-sets.ts
|
|
52
|
-
var STANDARD_DECK_ID = "standard_52_deck";
|
|
53
|
-
function createEnumPropertySchema(enums, description) {
|
|
54
|
-
return {
|
|
55
|
-
type: "enum",
|
|
56
|
-
description,
|
|
57
|
-
enums
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function createStringPropertySchema(description) {
|
|
61
|
-
return {
|
|
62
|
-
type: "string",
|
|
63
|
-
description
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function createStandard52CardDeck(defaultHome = { type: "detached" }) {
|
|
67
|
-
const suits = ["SPADES", "HEARTS", "CLUBS", "DIAMONDS"];
|
|
68
|
-
const ranks = [
|
|
69
|
-
"3",
|
|
70
|
-
"4",
|
|
71
|
-
"5",
|
|
72
|
-
"6",
|
|
73
|
-
"7",
|
|
74
|
-
"8",
|
|
75
|
-
"9",
|
|
76
|
-
"10",
|
|
77
|
-
"J",
|
|
78
|
-
"Q",
|
|
79
|
-
"K",
|
|
80
|
-
"A",
|
|
81
|
-
"2"
|
|
82
|
-
];
|
|
83
|
-
const cards = [];
|
|
84
|
-
for (const suit of suits) {
|
|
85
|
-
for (const rank of ranks) {
|
|
86
|
-
cards.push({
|
|
87
|
-
type: `${suit}_${rank}`,
|
|
88
|
-
name: `${rank} of ${suit}`,
|
|
89
|
-
imageUrl: `/cards/${suit.toLowerCase()}_${rank.toLowerCase()}.png`,
|
|
90
|
-
text: `A playing card: ${rank} of ${suit}. Value: ${getBigTwoCardValue(rank)}, Suit value: ${getSuitValue(suit)}`,
|
|
91
|
-
count: 1,
|
|
92
|
-
cardType: `${suit}_${rank}`,
|
|
93
|
-
properties: {
|
|
94
|
-
suit,
|
|
95
|
-
rank
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
id: STANDARD_DECK_ID,
|
|
102
|
-
name: "Standard 52-Card Deck",
|
|
103
|
-
type: "manual",
|
|
104
|
-
defaultHome,
|
|
105
|
-
cards,
|
|
106
|
-
cardSchema: {
|
|
107
|
-
properties: {
|
|
108
|
-
suit: createEnumPropertySchema(
|
|
109
|
-
["SPADES", "HEARTS", "CLUBS", "DIAMONDS"],
|
|
110
|
-
"The suit of the card"
|
|
111
|
-
),
|
|
112
|
-
rank: createStringPropertySchema("The rank of the card")
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
function addStandardDecksIfNeeded(manifest) {
|
|
118
|
-
return {
|
|
119
|
-
...manifest,
|
|
120
|
-
cardSets: manifest.cardSets.map(
|
|
121
|
-
(cardSet) => cardSet.type === "preset" && cardSet.presetId === STANDARD_DECK_ID ? {
|
|
122
|
-
...createStandard52CardDeck(cardSet.defaultHome),
|
|
123
|
-
id: cardSet.id,
|
|
124
|
-
name: cardSet.name
|
|
125
|
-
} : cardSet
|
|
126
|
-
)
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
function materializePresetCardSet(presetCardSet) {
|
|
130
|
-
if (presetCardSet.presetId !== STANDARD_DECK_ID) {
|
|
131
|
-
throw new Error(`Unsupported preset deck: ${presetCardSet.presetId}`);
|
|
132
|
-
}
|
|
133
|
-
const standardDeck = createStandard52CardDeck(presetCardSet.defaultHome);
|
|
134
|
-
return {
|
|
135
|
-
...standardDeck,
|
|
136
|
-
id: presetCardSet.id,
|
|
137
|
-
name: presetCardSet.name
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
function materializeCardSet(cardSet) {
|
|
141
|
-
return cardSet.type === "manual" ? cardSet : materializePresetCardSet(cardSet);
|
|
142
|
-
}
|
|
143
|
-
function getBigTwoCardValue(rank) {
|
|
144
|
-
switch (rank) {
|
|
145
|
-
case "3":
|
|
146
|
-
return 3;
|
|
147
|
-
case "4":
|
|
148
|
-
return 4;
|
|
149
|
-
case "5":
|
|
150
|
-
return 5;
|
|
151
|
-
case "6":
|
|
152
|
-
return 6;
|
|
153
|
-
case "7":
|
|
154
|
-
return 7;
|
|
155
|
-
case "8":
|
|
156
|
-
return 8;
|
|
157
|
-
case "9":
|
|
158
|
-
return 9;
|
|
159
|
-
case "10":
|
|
160
|
-
return 10;
|
|
161
|
-
case "J":
|
|
162
|
-
return 11;
|
|
163
|
-
case "Q":
|
|
164
|
-
return 12;
|
|
165
|
-
case "K":
|
|
166
|
-
return 13;
|
|
167
|
-
case "A":
|
|
168
|
-
return 14;
|
|
169
|
-
case "2":
|
|
170
|
-
return 15;
|
|
171
|
-
default:
|
|
172
|
-
return 0;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
function getSuitValue(suit) {
|
|
176
|
-
switch (suit) {
|
|
177
|
-
case "DIAMONDS":
|
|
178
|
-
return 1;
|
|
179
|
-
case "CLUBS":
|
|
180
|
-
return 2;
|
|
181
|
-
case "HEARTS":
|
|
182
|
-
return 3;
|
|
183
|
-
case "SPADES":
|
|
184
|
-
return 4;
|
|
185
|
-
default:
|
|
186
|
-
return 0;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// src/reducer/manifest/manifest-validation.ts
|
|
191
|
-
function isHexBoardTemplateSpec(boardTemplate) {
|
|
192
|
-
return boardTemplate.layout === "hex";
|
|
193
|
-
}
|
|
194
|
-
function isHexBoardSpec(board) {
|
|
195
|
-
return board.layout === "hex";
|
|
196
|
-
}
|
|
197
|
-
function collectDuplicateIdIssues(options) {
|
|
198
|
-
const issues = [];
|
|
199
|
-
const pathsById = /* @__PURE__ */ new Map();
|
|
200
|
-
for (const entry of options.entries) {
|
|
201
|
-
const id = entry.id?.trim();
|
|
202
|
-
if (!id) {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
const paths = pathsById.get(id) ?? [];
|
|
206
|
-
paths.push(entry.path);
|
|
207
|
-
pathsById.set(id, paths);
|
|
208
|
-
}
|
|
209
|
-
for (const [id, paths] of pathsById.entries()) {
|
|
210
|
-
if (paths.length > 1) {
|
|
211
|
-
issues.push(`${paths.join(", ")}: Duplicate ${options.label} '${id}'.`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return issues;
|
|
215
|
-
}
|
|
216
|
-
var PROTOTYPE_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
217
|
-
"__proto__",
|
|
218
|
-
"prototype",
|
|
219
|
-
"constructor"
|
|
220
|
-
]);
|
|
221
|
-
function validateRecordKey(value, path) {
|
|
222
|
-
if (!value || !PROTOTYPE_SENSITIVE_KEYS.has(value)) {
|
|
223
|
-
return [];
|
|
224
|
-
}
|
|
225
|
-
return [
|
|
226
|
-
`${path}: '${value}' is reserved and cannot be used as a generated record key.`
|
|
227
|
-
];
|
|
228
|
-
}
|
|
229
|
-
function collectKeyIssues(entries) {
|
|
230
|
-
return entries.flatMap(({ value, path }) => validateRecordKey(value, path));
|
|
231
|
-
}
|
|
232
|
-
function toHandleKey(input) {
|
|
233
|
-
if (!/[_-]/.test(input)) {
|
|
234
|
-
return input.charAt(0).toLowerCase() + input.slice(1);
|
|
235
|
-
}
|
|
236
|
-
const [first = "", ...rest] = input.split(/[_-]/g).filter(Boolean);
|
|
237
|
-
return [
|
|
238
|
-
first.toLowerCase(),
|
|
239
|
-
...rest.map((part) => {
|
|
240
|
-
const lower = part.toLowerCase();
|
|
241
|
-
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
242
|
-
})
|
|
243
|
-
].join("");
|
|
244
|
-
}
|
|
245
|
-
function collectHandleKeyCollisions(values, label) {
|
|
246
|
-
const idsByHandle = /* @__PURE__ */ new Map();
|
|
247
|
-
for (const value of values) {
|
|
248
|
-
const handle = toHandleKey(value);
|
|
249
|
-
idsByHandle.set(handle, [...idsByHandle.get(handle) ?? [], value]);
|
|
250
|
-
}
|
|
251
|
-
return [...idsByHandle.entries()].filter(([, ids]) => ids.length > 1).map(
|
|
252
|
-
([handle, ids]) => `${label} values ${ids.join(", ")} all generate handle '${handle}'.`
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
function dedupeSorted(values) {
|
|
256
|
-
return Array.from(new Set(values)).sort();
|
|
257
|
-
}
|
|
258
|
-
function renderCardInstanceIds(card) {
|
|
259
|
-
return card.count > 1 ? Array.from(
|
|
260
|
-
{ length: card.count },
|
|
261
|
-
(_, index) => `${card.type}-${index + 1}`
|
|
262
|
-
) : [card.type];
|
|
263
|
-
}
|
|
264
|
-
function collectPropertySchemaKeyIssues(schema, path) {
|
|
265
|
-
if (!schema) {
|
|
266
|
-
return [];
|
|
267
|
-
}
|
|
268
|
-
const issues = [];
|
|
269
|
-
if (schema.type === "object") {
|
|
270
|
-
for (const [key, property] of Object.entries(schema.properties ?? {})) {
|
|
271
|
-
issues.push(
|
|
272
|
-
...validateRecordKey(key, `${path}.properties.${key}`),
|
|
273
|
-
...collectPropertySchemaKeyIssues(
|
|
274
|
-
property,
|
|
275
|
-
`${path}.properties.${key}`
|
|
276
|
-
)
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
if (schema.type === "array") {
|
|
281
|
-
issues.push(
|
|
282
|
-
...collectPropertySchemaKeyIssues(schema.items, `${path}.items`)
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
if (schema.type === "record") {
|
|
286
|
-
issues.push(
|
|
287
|
-
...collectPropertySchemaKeyIssues(schema.values, `${path}.values`)
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
return issues;
|
|
291
|
-
}
|
|
292
|
-
function collectObjectSchemaKeyIssues(schema, path) {
|
|
293
|
-
const issues = [];
|
|
294
|
-
for (const [key, property] of Object.entries(schema?.properties ?? {})) {
|
|
295
|
-
issues.push(
|
|
296
|
-
...validateRecordKey(key, `${path}.properties.${key}`),
|
|
297
|
-
...collectPropertySchemaKeyIssues(property, `${path}.properties.${key}`)
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
return issues;
|
|
301
|
-
}
|
|
302
|
-
function collectCardSchemaKeyIssues(cardSet, path) {
|
|
303
|
-
if (cardSet.type !== "manual") {
|
|
304
|
-
return [];
|
|
305
|
-
}
|
|
306
|
-
const schema = cardSet.cardSchema;
|
|
307
|
-
if ("variants" in schema) {
|
|
308
|
-
return [
|
|
309
|
-
...Object.entries(schema.shared ?? {}).flatMap(([key, property]) => [
|
|
310
|
-
...validateRecordKey(key, `${path}.cardSchema.shared.${key}`),
|
|
311
|
-
...collectPropertySchemaKeyIssues(
|
|
312
|
-
property,
|
|
313
|
-
`${path}.cardSchema.shared.${key}`
|
|
314
|
-
)
|
|
315
|
-
]),
|
|
316
|
-
...Object.entries(schema.variants).flatMap(
|
|
317
|
-
([variantKey, variantSchema]) => [
|
|
318
|
-
...validateRecordKey(
|
|
319
|
-
variantKey,
|
|
320
|
-
`${path}.cardSchema.variants.${variantKey}`
|
|
321
|
-
),
|
|
322
|
-
...collectObjectSchemaKeyIssues(
|
|
323
|
-
variantSchema,
|
|
324
|
-
`${path}.cardSchema.variants.${variantKey}`
|
|
325
|
-
)
|
|
326
|
-
]
|
|
327
|
-
)
|
|
328
|
-
];
|
|
329
|
-
}
|
|
330
|
-
return collectObjectSchemaKeyIssues(schema, `${path}.cardSchema`);
|
|
331
|
-
}
|
|
332
|
-
function expandSeedIds(seeds) {
|
|
333
|
-
return seeds.flatMap((seed) => {
|
|
334
|
-
const count = seed.count ?? 1;
|
|
335
|
-
const baseId = seed.id ?? seed.typeId;
|
|
336
|
-
return count > 1 ? Array.from({ length: count }, (_, index) => `${baseId}-${index + 1}`) : [baseId];
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
function validateTypeSlotDuplicates(options) {
|
|
340
|
-
const issues = [];
|
|
341
|
-
for (const [index, pieceType] of options.pieceTypes.entries()) {
|
|
342
|
-
issues.push(
|
|
343
|
-
...collectDuplicateIdIssues({
|
|
344
|
-
entries: (pieceType.slots ?? []).map((slot, slotIndex) => ({
|
|
345
|
-
id: slot.id,
|
|
346
|
-
path: `manifest.pieceTypes[${index}].slots[${slotIndex}].id`
|
|
347
|
-
})),
|
|
348
|
-
label: "piece slot id"
|
|
349
|
-
})
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
for (const [index, dieType] of options.dieTypes.entries()) {
|
|
353
|
-
issues.push(
|
|
354
|
-
...collectDuplicateIdIssues({
|
|
355
|
-
entries: (dieType.slots ?? []).map((slot, slotIndex) => ({
|
|
356
|
-
id: slot.id,
|
|
357
|
-
path: `manifest.dieTypes[${index}].slots[${slotIndex}].id`
|
|
358
|
-
})),
|
|
359
|
-
label: "die slot id"
|
|
360
|
-
})
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
return issues;
|
|
364
|
-
}
|
|
365
|
-
function validateSlotHostsAndHomes(manifest) {
|
|
366
|
-
const issues = [];
|
|
367
|
-
const pieceTypesById = new Map(
|
|
368
|
-
(manifest.pieceTypes ?? []).map(
|
|
369
|
-
(pieceType) => [pieceType.id, pieceType]
|
|
370
|
-
)
|
|
371
|
-
);
|
|
372
|
-
const dieTypesById = new Map(
|
|
373
|
-
(manifest.dieTypes ?? []).map((dieType) => [dieType.id, dieType])
|
|
374
|
-
);
|
|
375
|
-
const slotIdsByHostKey = /* @__PURE__ */ new Map();
|
|
376
|
-
for (const [index, seed] of (manifest.pieceSeeds ?? []).entries()) {
|
|
377
|
-
const pieceType = pieceTypesById.get(seed.typeId);
|
|
378
|
-
const slotIds = (pieceType?.slots ?? []).map((slot) => slot.id);
|
|
379
|
-
if (slotIds.length === 0) {
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
if (typeof seed.id !== "string" || seed.id.length === 0) {
|
|
383
|
-
issues.push(
|
|
384
|
-
`manifest.pieceSeeds[${index}].id: Piece seed for slot-bearing type '${seed.typeId}' must declare an explicit id.`
|
|
385
|
-
);
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
if ((seed.count ?? 1) !== 1) {
|
|
389
|
-
issues.push(
|
|
390
|
-
`manifest.pieceSeeds[${index}].count: Piece seed '${seed.id}' for slot-bearing type '${seed.typeId}' must omit count or set it to 1.`
|
|
391
|
-
);
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
slotIdsByHostKey.set(`piece:${seed.id}`, new Set(slotIds));
|
|
395
|
-
}
|
|
396
|
-
for (const [index, seed] of (manifest.dieSeeds ?? []).entries()) {
|
|
397
|
-
const dieType = dieTypesById.get(seed.typeId);
|
|
398
|
-
const slotIds = (dieType?.slots ?? []).map((slot) => slot.id);
|
|
399
|
-
if (slotIds.length === 0) {
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
if (typeof seed.id !== "string" || seed.id.length === 0) {
|
|
403
|
-
issues.push(
|
|
404
|
-
`manifest.dieSeeds[${index}].id: Die seed for slot-bearing type '${seed.typeId}' must declare an explicit id.`
|
|
405
|
-
);
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
if ((seed.count ?? 1) !== 1) {
|
|
409
|
-
issues.push(
|
|
410
|
-
`manifest.dieSeeds[${index}].count: Die seed '${seed.id}' for slot-bearing type '${seed.typeId}' must omit count or set it to 1.`
|
|
411
|
-
);
|
|
412
|
-
continue;
|
|
413
|
-
}
|
|
414
|
-
slotIdsByHostKey.set(`die:${seed.id}`, new Set(slotIds));
|
|
415
|
-
}
|
|
416
|
-
const validateHome = (home, path) => {
|
|
417
|
-
if (home?.type !== "slot") {
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
const hostKey = `${home.host.kind}:${home.host.id}`;
|
|
421
|
-
const slotIds = slotIdsByHostKey.get(hostKey);
|
|
422
|
-
if (!slotIds) {
|
|
423
|
-
issues.push(
|
|
424
|
-
`${path}.host: Unknown strict slot host '${home.host.kind}:${home.host.id}'. Hosts must be singleton piece/die seeds whose type declares slots.`
|
|
425
|
-
);
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
if (!slotIds.has(home.slotId)) {
|
|
429
|
-
issues.push(
|
|
430
|
-
`${path}.slotId: Unknown slot '${home.slotId}' for host '${home.host.kind}:${home.host.id}'.`
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
};
|
|
434
|
-
for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
|
|
435
|
-
if (cardSet.type !== "manual") {
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
validateHome(
|
|
439
|
-
cardSet.defaultHome,
|
|
440
|
-
`manifest.cardSets[${cardSetIndex}].defaultHome`
|
|
441
|
-
);
|
|
442
|
-
for (const [cardIndex, card] of cardSet.cards.entries()) {
|
|
443
|
-
validateHome(
|
|
444
|
-
card.home,
|
|
445
|
-
`manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
for (const [index, seed] of (manifest.pieceSeeds ?? []).entries()) {
|
|
450
|
-
validateHome(seed.home, `manifest.pieceSeeds[${index}].home`);
|
|
451
|
-
}
|
|
452
|
-
for (const [index, seed] of (manifest.dieSeeds ?? []).entries()) {
|
|
453
|
-
validateHome(seed.home, `manifest.dieSeeds[${index}].home`);
|
|
454
|
-
}
|
|
455
|
-
return issues;
|
|
456
|
-
}
|
|
457
|
-
function validateSetupProfileReferences(manifest) {
|
|
458
|
-
const issues = [];
|
|
459
|
-
const optionChoiceIdsByOptionId = new Map(
|
|
460
|
-
(manifest.setupOptions ?? []).map((option, optionIndex) => {
|
|
461
|
-
const choiceIds = /* @__PURE__ */ new Set();
|
|
462
|
-
for (const choice of option.choices ?? []) {
|
|
463
|
-
choiceIds.add(choice.id);
|
|
464
|
-
}
|
|
465
|
-
if (!option.id) {
|
|
466
|
-
issues.push(
|
|
467
|
-
`manifest.setupOptions[${optionIndex}].id: Missing option id.`
|
|
468
|
-
);
|
|
469
|
-
}
|
|
470
|
-
return [option.id, choiceIds];
|
|
471
|
-
})
|
|
472
|
-
);
|
|
473
|
-
for (const [profileIndex, profile] of (manifest.setupProfiles ?? []).entries()) {
|
|
474
|
-
const optionValues = profile.optionValues ?? {};
|
|
475
|
-
for (const [optionId, choiceId] of Object.entries(optionValues)) {
|
|
476
|
-
const allowedChoices = optionChoiceIdsByOptionId.get(optionId);
|
|
477
|
-
if (!allowedChoices) {
|
|
478
|
-
issues.push(
|
|
479
|
-
`manifest.setupProfiles[${profileIndex}].optionValues.${optionId}: Unknown setup option '${optionId}'.`
|
|
480
|
-
);
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
if (!allowedChoices.has(choiceId)) {
|
|
484
|
-
issues.push(
|
|
485
|
-
`manifest.setupProfiles[${profileIndex}].optionValues.${optionId}: Unknown choice '${choiceId}' for setup option '${optionId}'.`
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return issues;
|
|
491
|
-
}
|
|
492
|
-
function validatePlayerScopedSeedHomes(manifest) {
|
|
493
|
-
const issues = [];
|
|
494
|
-
const boardScopeById = new Map(
|
|
495
|
-
(manifest.boards ?? []).map((board) => [board.id, board.scope])
|
|
496
|
-
);
|
|
497
|
-
const zoneScopeById = new Map(
|
|
498
|
-
(manifest.zones ?? []).map((zone) => [zone.id, zone.scope])
|
|
499
|
-
);
|
|
500
|
-
const validateSeedHome = (seed, path, label) => {
|
|
501
|
-
const authoredId = seed.id ?? seed.typeId;
|
|
502
|
-
if (seed.ownerId) {
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
if (homeTargetsBoard(seed.home) && boardScopeById.get(seed.home.boardId) === "perPlayer") {
|
|
506
|
-
issues.push(
|
|
507
|
-
`${path}.boardId: ${label} '${authoredId}' requires ownerId because board '${seed.home.boardId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
|
|
508
|
-
);
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
if (seed.home?.type === "zone" && zoneScopeById.get(seed.home.zoneId) === "perPlayer") {
|
|
512
|
-
issues.push(
|
|
513
|
-
`${path}.zoneId: ${label} '${authoredId}' requires ownerId because zone '${seed.home.zoneId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
for (const [index, seed] of (manifest.pieceSeeds ?? []).entries()) {
|
|
518
|
-
validateSeedHome(seed, `manifest.pieceSeeds[${index}].home`, "Piece seed");
|
|
519
|
-
}
|
|
520
|
-
for (const [index, seed] of (manifest.dieSeeds ?? []).entries()) {
|
|
521
|
-
validateSeedHome(seed, `manifest.dieSeeds[${index}].home`, "Die seed");
|
|
522
|
-
}
|
|
523
|
-
return issues;
|
|
524
|
-
}
|
|
525
|
-
function validateCardHomes(manifest) {
|
|
526
|
-
const issues = [];
|
|
527
|
-
const boardScopeById = new Map(
|
|
528
|
-
(manifest.boards ?? []).map((board) => [board.id, board.scope])
|
|
529
|
-
);
|
|
530
|
-
const zoneScopeById = new Map(
|
|
531
|
-
(manifest.zones ?? []).map((zone) => [zone.id, zone.scope])
|
|
532
|
-
);
|
|
533
|
-
for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
|
|
534
|
-
if (!cardSet.defaultHome) {
|
|
535
|
-
issues.push(
|
|
536
|
-
`manifest.cardSets[${cardSetIndex}].defaultHome: ${cardSet.type === "manual" ? "Manual" : "Preset"} card sets must declare defaultHome.`
|
|
537
|
-
);
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
const validateCardHome = (home, path, label) => {
|
|
541
|
-
if (home?.type === "zone" && zoneScopeById.get(home.zoneId) === "perPlayer") {
|
|
542
|
-
issues.push(
|
|
543
|
-
`${path}.zoneId: ${label} cannot target per-player zone '${home.zoneId}' because card inventory has no ownerId. Place it during reducer setup instead.`
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
if (homeTargetsBoard(home) && boardScopeById.get(home.boardId) === "perPlayer") {
|
|
547
|
-
issues.push(
|
|
548
|
-
`${path}.boardId: ${label} cannot target per-player board '${home.boardId}' because card inventory has no ownerId. Place it during reducer setup instead.`
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
validateCardHome(
|
|
553
|
-
cardSet.defaultHome,
|
|
554
|
-
`manifest.cardSets[${cardSetIndex}].defaultHome`,
|
|
555
|
-
`Card set '${cardSet.id}' defaultHome`
|
|
556
|
-
);
|
|
557
|
-
for (const [cardIndex, card] of (cardSet.type === "manual" ? cardSet.cards : []).entries()) {
|
|
558
|
-
const path = `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`;
|
|
559
|
-
validateCardHome(card.home, path, `Card '${card.type}'`);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
return issues;
|
|
563
|
-
}
|
|
564
|
-
function homeTargetsBoard(home) {
|
|
565
|
-
return home?.type === "space" || home?.type === "container" || home?.type === "edge" || home?.type === "vertex";
|
|
566
|
-
}
|
|
567
|
-
function validateBoardTemplateDuplicates(boardTemplates) {
|
|
568
|
-
const issues = [];
|
|
569
|
-
for (const [index, boardTemplate] of boardTemplates.entries()) {
|
|
570
|
-
if (isHexBoardTemplateSpec(boardTemplate)) {
|
|
571
|
-
issues.push(
|
|
572
|
-
...collectDuplicateIdIssues({
|
|
573
|
-
entries: (boardTemplate.spaces ?? []).map((space, spaceIndex) => ({
|
|
574
|
-
id: space.id,
|
|
575
|
-
path: `manifest.boardTemplates[${index}].spaces[${spaceIndex}].id`
|
|
576
|
-
})),
|
|
577
|
-
label: "space id"
|
|
578
|
-
})
|
|
579
|
-
);
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
issues.push(
|
|
583
|
-
...collectDuplicateIdIssues({
|
|
584
|
-
entries: (boardTemplate.spaces ?? []).map((space, spaceIndex) => ({
|
|
585
|
-
id: space.id,
|
|
586
|
-
path: `manifest.boardTemplates[${index}].spaces[${spaceIndex}].id`
|
|
587
|
-
})),
|
|
588
|
-
label: "space id"
|
|
589
|
-
})
|
|
590
|
-
);
|
|
591
|
-
issues.push(
|
|
592
|
-
...collectDuplicateIdIssues({
|
|
593
|
-
entries: (boardTemplate.containers ?? []).map(
|
|
594
|
-
(container, containerIndex) => ({
|
|
595
|
-
id: container.id,
|
|
596
|
-
path: `manifest.boardTemplates[${index}].containers[${containerIndex}].id`
|
|
597
|
-
})
|
|
598
|
-
),
|
|
599
|
-
label: "container id"
|
|
600
|
-
})
|
|
601
|
-
);
|
|
602
|
-
issues.push(
|
|
603
|
-
...collectDuplicateIdIssues({
|
|
604
|
-
entries: (boardTemplate.relations ?? []).map(
|
|
605
|
-
(relation, relationIndex) => ({
|
|
606
|
-
id: relation.id,
|
|
607
|
-
path: `manifest.boardTemplates[${index}].relations[${relationIndex}].id`
|
|
608
|
-
})
|
|
609
|
-
),
|
|
610
|
-
label: "relation id"
|
|
611
|
-
})
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
return issues;
|
|
615
|
-
}
|
|
616
|
-
function validateBoardDuplicates(boards) {
|
|
617
|
-
const issues = [];
|
|
618
|
-
for (const [index, board] of boards.entries()) {
|
|
619
|
-
if (isHexBoardSpec(board)) {
|
|
620
|
-
issues.push(
|
|
621
|
-
...collectDuplicateIdIssues({
|
|
622
|
-
entries: (board.spaces ?? []).map((space, spaceIndex) => ({
|
|
623
|
-
id: space.id,
|
|
624
|
-
path: `manifest.boards[${index}].spaces[${spaceIndex}].id`
|
|
625
|
-
})),
|
|
626
|
-
label: "space id"
|
|
627
|
-
})
|
|
628
|
-
);
|
|
629
|
-
continue;
|
|
630
|
-
}
|
|
631
|
-
issues.push(
|
|
632
|
-
...collectDuplicateIdIssues({
|
|
633
|
-
entries: (board.spaces ?? []).map((space, spaceIndex) => ({
|
|
634
|
-
id: space.id,
|
|
635
|
-
path: `manifest.boards[${index}].spaces[${spaceIndex}].id`
|
|
636
|
-
})),
|
|
637
|
-
label: "space id"
|
|
638
|
-
})
|
|
639
|
-
);
|
|
640
|
-
issues.push(
|
|
641
|
-
...collectDuplicateIdIssues({
|
|
642
|
-
entries: (board.containers ?? []).map((container, containerIndex) => ({
|
|
643
|
-
id: container.id,
|
|
644
|
-
path: `manifest.boards[${index}].containers[${containerIndex}].id`
|
|
645
|
-
})),
|
|
646
|
-
label: "container id"
|
|
647
|
-
})
|
|
648
|
-
);
|
|
649
|
-
issues.push(
|
|
650
|
-
...collectDuplicateIdIssues({
|
|
651
|
-
entries: (board.relations ?? []).map((relation, relationIndex) => ({
|
|
652
|
-
id: relation.id,
|
|
653
|
-
path: `manifest.boards[${index}].relations[${relationIndex}].id`
|
|
654
|
-
})),
|
|
655
|
-
label: "relation id"
|
|
656
|
-
})
|
|
657
|
-
);
|
|
658
|
-
}
|
|
659
|
-
return issues;
|
|
660
|
-
}
|
|
661
|
-
function validateSetupOptionChoiceDuplicates(options) {
|
|
662
|
-
const issues = [];
|
|
663
|
-
for (const [optionIndex, option] of options.entries()) {
|
|
664
|
-
issues.push(
|
|
665
|
-
...collectDuplicateIdIssues({
|
|
666
|
-
entries: (option.choices ?? []).map((choice, choiceIndex) => ({
|
|
667
|
-
id: choice.id,
|
|
668
|
-
path: `manifest.setupOptions[${optionIndex}].choices[${choiceIndex}].id`
|
|
669
|
-
})),
|
|
670
|
-
label: "setup option choice id"
|
|
671
|
-
})
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
return issues;
|
|
675
|
-
}
|
|
676
|
-
function resolveHexSpaces(board, template) {
|
|
677
|
-
if (!template) {
|
|
678
|
-
return [...board.spaces ?? []].sort(
|
|
679
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
const templateSpacesById = new Map(
|
|
683
|
-
(template.spaces ?? []).map((space) => [space.id, space])
|
|
684
|
-
);
|
|
685
|
-
const overridesById = new Map(
|
|
686
|
-
(board.spaces ?? []).map((space) => [space.id, space])
|
|
687
|
-
);
|
|
688
|
-
for (const overrideId of overridesById.keys()) {
|
|
689
|
-
if (!templateSpacesById.has(overrideId)) {
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
return (template.spaces ?? []).map((templateSpace) => {
|
|
694
|
-
const override = overridesById.get(templateSpace.id);
|
|
695
|
-
if (!override) {
|
|
696
|
-
return templateSpace;
|
|
697
|
-
}
|
|
698
|
-
return {
|
|
699
|
-
...templateSpace,
|
|
700
|
-
...override,
|
|
701
|
-
id: templateSpace.id,
|
|
702
|
-
q: templateSpace.q,
|
|
703
|
-
r: templateSpace.r
|
|
704
|
-
};
|
|
705
|
-
}).sort((left, right) => left.id.localeCompare(right.id));
|
|
706
|
-
}
|
|
707
|
-
function validateHexVertexRefs(options) {
|
|
708
|
-
const issues = [];
|
|
709
|
-
const spacesById = new Map(
|
|
710
|
-
options.spaces.map((space) => [space.id, space])
|
|
711
|
-
);
|
|
712
|
-
for (const [vertexIndex, vertex] of options.vertices.entries()) {
|
|
713
|
-
try {
|
|
714
|
-
resolveHexVertexGeometryKey(vertex.ref, spacesById);
|
|
715
|
-
} catch (error) {
|
|
716
|
-
const message = error instanceof Error ? error.message : "Unknown hex vertex error.";
|
|
717
|
-
issues.push(
|
|
718
|
-
`${options.ownerPath}.vertices[${vertexIndex}].ref: ${options.ownerLabel} with spaces '${vertex.ref.spaces.join(", ")}' failed validation. ${message}`
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
return issues;
|
|
723
|
-
}
|
|
724
|
-
function validateHexBoardVertexRefs(manifest) {
|
|
725
|
-
const issues = [];
|
|
726
|
-
const hexTemplatesById = new Map(
|
|
727
|
-
(manifest.boardTemplates ?? []).filter(isHexBoardTemplateSpec).map((boardTemplate) => [boardTemplate.id, boardTemplate])
|
|
728
|
-
);
|
|
729
|
-
for (const [templateIndex, template] of (manifest.boardTemplates ?? []).entries()) {
|
|
730
|
-
if (!isHexBoardTemplateSpec(template)) {
|
|
731
|
-
continue;
|
|
732
|
-
}
|
|
733
|
-
issues.push(
|
|
734
|
-
...validateHexVertexRefs({
|
|
735
|
-
ownerPath: `manifest.boardTemplates[${templateIndex}]`,
|
|
736
|
-
ownerLabel: `Hex board template '${template.id}'`,
|
|
737
|
-
spaces: template.spaces ?? [],
|
|
738
|
-
vertices: template.vertices ?? []
|
|
739
|
-
})
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
for (const [boardIndex, board] of (manifest.boards ?? []).entries()) {
|
|
743
|
-
if (!isHexBoardSpec(board)) {
|
|
744
|
-
continue;
|
|
745
|
-
}
|
|
746
|
-
issues.push(
|
|
747
|
-
...validateHexVertexRefs({
|
|
748
|
-
ownerPath: `manifest.boards[${boardIndex}]`,
|
|
749
|
-
ownerLabel: `Hex board '${board.id}'`,
|
|
750
|
-
spaces: resolveHexSpaces(
|
|
751
|
-
board,
|
|
752
|
-
hexTemplatesById.get(board.templateId ?? "")
|
|
753
|
-
),
|
|
754
|
-
vertices: board.vertices ?? []
|
|
755
|
-
})
|
|
756
|
-
);
|
|
757
|
-
}
|
|
758
|
-
return issues;
|
|
759
|
-
}
|
|
760
|
-
function collectAmbiguousBoardTypeWarnings(manifest) {
|
|
761
|
-
const warnings = [];
|
|
762
|
-
const boardTemplatesById = new Map(
|
|
763
|
-
(manifest.boardTemplates ?? []).map(
|
|
764
|
-
(template) => [template.id, template]
|
|
765
|
-
)
|
|
766
|
-
);
|
|
767
|
-
const boardsBySpaceType = /* @__PURE__ */ new Map();
|
|
768
|
-
const boardsByEdgeType = /* @__PURE__ */ new Map();
|
|
769
|
-
const boardsByVertexType = /* @__PURE__ */ new Map();
|
|
770
|
-
const addBoardUsage = (target, typeId, boardId) => {
|
|
771
|
-
if (!typeId) {
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
const boardIds = target.get(typeId) ?? /* @__PURE__ */ new Set();
|
|
775
|
-
boardIds.add(boardId);
|
|
776
|
-
target.set(typeId, boardIds);
|
|
777
|
-
};
|
|
778
|
-
for (const board of manifest.boards ?? []) {
|
|
779
|
-
const template = board.templateId ? boardTemplatesById.get(board.templateId) : void 0;
|
|
780
|
-
for (const space of [
|
|
781
|
-
...(template?.layout === board.layout ? template.spaces : void 0) ?? [],
|
|
782
|
-
...board.spaces ?? []
|
|
783
|
-
]) {
|
|
784
|
-
addBoardUsage(boardsBySpaceType, space.typeId, board.id);
|
|
785
|
-
}
|
|
786
|
-
if (board.layout === "hex" || board.layout === "square") {
|
|
787
|
-
for (const edge of [
|
|
788
|
-
...(template?.layout === board.layout ? template.edges : void 0) ?? [],
|
|
789
|
-
...board.edges ?? []
|
|
790
|
-
]) {
|
|
791
|
-
addBoardUsage(boardsByEdgeType, edge.typeId, board.id);
|
|
792
|
-
}
|
|
793
|
-
for (const vertex of [
|
|
794
|
-
...(template?.layout === board.layout ? template.vertices : void 0) ?? [],
|
|
795
|
-
...board.vertices ?? []
|
|
796
|
-
]) {
|
|
797
|
-
addBoardUsage(boardsByVertexType, vertex.typeId, board.id);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
const pushWarnings = (kind, boardsByType, helperName) => {
|
|
802
|
-
for (const [typeId, boardIds] of boardsByType.entries()) {
|
|
803
|
-
if (boardIds.size < 2) {
|
|
804
|
-
continue;
|
|
805
|
-
}
|
|
806
|
-
warnings.push(
|
|
807
|
-
`Ambiguous ${kind}.typeId '${typeId}' is authored on multiple boards (${Array.from(boardIds).sort().join(", ")}). Prefer ${helperName} for board-scoped lookups.`
|
|
808
|
-
);
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
pushWarnings(
|
|
812
|
-
"space",
|
|
813
|
-
boardsBySpaceType,
|
|
814
|
-
"boardHelpers.spaceIdsByBoardId / boardHelpers.spaceTypeIdByBoardId"
|
|
815
|
-
);
|
|
816
|
-
pushWarnings(
|
|
817
|
-
"edge",
|
|
818
|
-
boardsByEdgeType,
|
|
819
|
-
"boardHelpers.edgeIdsByBoardIdAndTypeId"
|
|
820
|
-
);
|
|
821
|
-
pushWarnings(
|
|
822
|
-
"vertex",
|
|
823
|
-
boardsByVertexType,
|
|
824
|
-
"boardHelpers.vertexIdsByBoardIdAndTypeId"
|
|
825
|
-
);
|
|
826
|
-
return warnings;
|
|
827
|
-
}
|
|
828
|
-
function collectBoardRecordKeyIssues(manifest) {
|
|
829
|
-
const issues = [];
|
|
830
|
-
const maxPlayers = manifest.players.maxPlayers;
|
|
831
|
-
const playerIds = Array.from(
|
|
832
|
-
{ length: maxPlayers },
|
|
833
|
-
(_, index) => `player-${index + 1}`
|
|
834
|
-
);
|
|
835
|
-
for (const [templateIndex, boardTemplate] of (manifest.boardTemplates ?? []).entries()) {
|
|
836
|
-
const templatePath = `manifest.boardTemplates[${templateIndex}]`;
|
|
837
|
-
issues.push(
|
|
838
|
-
...collectKeyIssues([
|
|
839
|
-
{ value: boardTemplate.id, path: `${templatePath}.id` },
|
|
840
|
-
{ value: boardTemplate.typeId, path: `${templatePath}.typeId` }
|
|
841
|
-
]),
|
|
842
|
-
...collectObjectSchemaKeyIssues(
|
|
843
|
-
boardTemplate.boardFieldsSchema,
|
|
844
|
-
`${templatePath}.boardFieldsSchema`
|
|
845
|
-
),
|
|
846
|
-
...collectObjectSchemaKeyIssues(
|
|
847
|
-
boardTemplate.spaceFieldsSchema,
|
|
848
|
-
`${templatePath}.spaceFieldsSchema`
|
|
849
|
-
)
|
|
850
|
-
);
|
|
851
|
-
if (boardTemplate.layout !== "hex") {
|
|
852
|
-
issues.push(
|
|
853
|
-
...collectObjectSchemaKeyIssues(
|
|
854
|
-
boardTemplate.relationFieldsSchema,
|
|
855
|
-
`${templatePath}.relationFieldsSchema`
|
|
856
|
-
),
|
|
857
|
-
...collectObjectSchemaKeyIssues(
|
|
858
|
-
boardTemplate.containerFieldsSchema,
|
|
859
|
-
`${templatePath}.containerFieldsSchema`
|
|
860
|
-
),
|
|
861
|
-
...collectKeyIssues([
|
|
862
|
-
...(boardTemplate.containers ?? []).flatMap(
|
|
863
|
-
(container, containerIndex) => [
|
|
864
|
-
{
|
|
865
|
-
value: container.id,
|
|
866
|
-
path: `${templatePath}.containers[${containerIndex}].id`
|
|
867
|
-
},
|
|
868
|
-
{
|
|
869
|
-
value: container.host.type === "space" ? container.host.spaceId : void 0,
|
|
870
|
-
path: `${templatePath}.containers[${containerIndex}].host.spaceId`
|
|
871
|
-
}
|
|
872
|
-
]
|
|
873
|
-
),
|
|
874
|
-
...(boardTemplate.relations ?? []).flatMap(
|
|
875
|
-
(relation, relationIndex) => [
|
|
876
|
-
{
|
|
877
|
-
value: relation.id,
|
|
878
|
-
path: `${templatePath}.relations[${relationIndex}].id`
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
value: relation.typeId,
|
|
882
|
-
path: `${templatePath}.relations[${relationIndex}].typeId`
|
|
883
|
-
},
|
|
884
|
-
{
|
|
885
|
-
value: relation.fromSpaceId,
|
|
886
|
-
path: `${templatePath}.relations[${relationIndex}].fromSpaceId`
|
|
887
|
-
},
|
|
888
|
-
{
|
|
889
|
-
value: relation.toSpaceId,
|
|
890
|
-
path: `${templatePath}.relations[${relationIndex}].toSpaceId`
|
|
891
|
-
}
|
|
892
|
-
]
|
|
893
|
-
)
|
|
894
|
-
])
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
if (boardTemplate.layout !== "generic") {
|
|
898
|
-
issues.push(
|
|
899
|
-
...collectObjectSchemaKeyIssues(
|
|
900
|
-
boardTemplate.edgeFieldsSchema,
|
|
901
|
-
`${templatePath}.edgeFieldsSchema`
|
|
902
|
-
),
|
|
903
|
-
...collectObjectSchemaKeyIssues(
|
|
904
|
-
boardTemplate.vertexFieldsSchema,
|
|
905
|
-
`${templatePath}.vertexFieldsSchema`
|
|
906
|
-
),
|
|
907
|
-
...collectKeyIssues([
|
|
908
|
-
...(boardTemplate.edges ?? []).map((edge, edgeIndex) => ({
|
|
909
|
-
value: edge.typeId,
|
|
910
|
-
path: `${templatePath}.edges[${edgeIndex}].typeId`
|
|
911
|
-
})),
|
|
912
|
-
...(boardTemplate.vertices ?? []).map((vertex, vertexIndex) => ({
|
|
913
|
-
value: vertex.typeId,
|
|
914
|
-
path: `${templatePath}.vertices[${vertexIndex}].typeId`
|
|
915
|
-
}))
|
|
916
|
-
])
|
|
917
|
-
);
|
|
918
|
-
}
|
|
919
|
-
issues.push(
|
|
920
|
-
...collectKeyIssues(
|
|
921
|
-
(boardTemplate.spaces ?? []).flatMap((space, spaceIndex) => [
|
|
922
|
-
{
|
|
923
|
-
value: space.id,
|
|
924
|
-
path: `${templatePath}.spaces[${spaceIndex}].id`
|
|
925
|
-
},
|
|
926
|
-
{
|
|
927
|
-
value: space.typeId,
|
|
928
|
-
path: `${templatePath}.spaces[${spaceIndex}].typeId`
|
|
929
|
-
}
|
|
930
|
-
])
|
|
931
|
-
)
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
for (const [boardIndex, board] of (manifest.boards ?? []).entries()) {
|
|
935
|
-
const boardPath = `manifest.boards[${boardIndex}]`;
|
|
936
|
-
const runtimeBoardIds = board.scope === "perPlayer" ? playerIds.map((playerId) => `${board.id}:${playerId}`) : [board.id];
|
|
937
|
-
issues.push(
|
|
938
|
-
...collectKeyIssues([
|
|
939
|
-
{ value: board.id, path: `${boardPath}.id` },
|
|
940
|
-
{ value: board.typeId, path: `${boardPath}.typeId` },
|
|
941
|
-
{ value: board.templateId, path: `${boardPath}.templateId` },
|
|
942
|
-
...runtimeBoardIds.map((runtimeBoardId) => ({
|
|
943
|
-
value: runtimeBoardId,
|
|
944
|
-
path: `${boardPath}.runtimeBoardId`
|
|
945
|
-
}))
|
|
946
|
-
]),
|
|
947
|
-
...collectObjectSchemaKeyIssues(
|
|
948
|
-
board.boardFieldsSchema,
|
|
949
|
-
`${boardPath}.boardFieldsSchema`
|
|
950
|
-
),
|
|
951
|
-
...collectObjectSchemaKeyIssues(
|
|
952
|
-
board.spaceFieldsSchema,
|
|
953
|
-
`${boardPath}.spaceFieldsSchema`
|
|
954
|
-
)
|
|
955
|
-
);
|
|
956
|
-
if (board.layout !== "hex") {
|
|
957
|
-
issues.push(
|
|
958
|
-
...collectObjectSchemaKeyIssues(
|
|
959
|
-
board.relationFieldsSchema,
|
|
960
|
-
`${boardPath}.relationFieldsSchema`
|
|
961
|
-
),
|
|
962
|
-
...collectObjectSchemaKeyIssues(
|
|
963
|
-
board.containerFieldsSchema,
|
|
964
|
-
`${boardPath}.containerFieldsSchema`
|
|
965
|
-
),
|
|
966
|
-
...collectKeyIssues([
|
|
967
|
-
...(board.containers ?? []).flatMap((container, containerIndex) => [
|
|
968
|
-
{
|
|
969
|
-
value: container.id,
|
|
970
|
-
path: `${boardPath}.containers[${containerIndex}].id`
|
|
971
|
-
},
|
|
972
|
-
{
|
|
973
|
-
value: container.host.type === "space" ? container.host.spaceId : void 0,
|
|
974
|
-
path: `${boardPath}.containers[${containerIndex}].host.spaceId`
|
|
975
|
-
}
|
|
976
|
-
]),
|
|
977
|
-
...(board.relations ?? []).flatMap((relation, relationIndex) => [
|
|
978
|
-
{
|
|
979
|
-
value: relation.id,
|
|
980
|
-
path: `${boardPath}.relations[${relationIndex}].id`
|
|
981
|
-
},
|
|
982
|
-
{
|
|
983
|
-
value: relation.typeId,
|
|
984
|
-
path: `${boardPath}.relations[${relationIndex}].typeId`
|
|
985
|
-
},
|
|
986
|
-
{
|
|
987
|
-
value: relation.fromSpaceId,
|
|
988
|
-
path: `${boardPath}.relations[${relationIndex}].fromSpaceId`
|
|
989
|
-
},
|
|
990
|
-
{
|
|
991
|
-
value: relation.toSpaceId,
|
|
992
|
-
path: `${boardPath}.relations[${relationIndex}].toSpaceId`
|
|
993
|
-
}
|
|
994
|
-
])
|
|
995
|
-
])
|
|
996
|
-
);
|
|
997
|
-
}
|
|
998
|
-
if (board.layout !== "generic") {
|
|
999
|
-
issues.push(
|
|
1000
|
-
...collectObjectSchemaKeyIssues(
|
|
1001
|
-
board.edgeFieldsSchema,
|
|
1002
|
-
`${boardPath}.edgeFieldsSchema`
|
|
1003
|
-
),
|
|
1004
|
-
...collectObjectSchemaKeyIssues(
|
|
1005
|
-
board.vertexFieldsSchema,
|
|
1006
|
-
`${boardPath}.vertexFieldsSchema`
|
|
1007
|
-
),
|
|
1008
|
-
...collectKeyIssues([
|
|
1009
|
-
...(board.edges ?? []).map((edge, edgeIndex) => ({
|
|
1010
|
-
value: edge.typeId,
|
|
1011
|
-
path: `${boardPath}.edges[${edgeIndex}].typeId`
|
|
1012
|
-
})),
|
|
1013
|
-
...(board.vertices ?? []).map((vertex, vertexIndex) => ({
|
|
1014
|
-
value: vertex.typeId,
|
|
1015
|
-
path: `${boardPath}.vertices[${vertexIndex}].typeId`
|
|
1016
|
-
}))
|
|
1017
|
-
])
|
|
1018
|
-
);
|
|
1019
|
-
}
|
|
1020
|
-
issues.push(
|
|
1021
|
-
...collectKeyIssues(
|
|
1022
|
-
(board.spaces ?? []).flatMap((space, spaceIndex) => [
|
|
1023
|
-
{
|
|
1024
|
-
value: space.id,
|
|
1025
|
-
path: `${boardPath}.spaces[${spaceIndex}].id`
|
|
1026
|
-
},
|
|
1027
|
-
{
|
|
1028
|
-
value: space.typeId,
|
|
1029
|
-
path: `${boardPath}.spaces[${spaceIndex}].typeId`
|
|
1030
|
-
}
|
|
1031
|
-
])
|
|
1032
|
-
)
|
|
1033
|
-
);
|
|
1034
|
-
}
|
|
1035
|
-
return issues;
|
|
1036
|
-
}
|
|
1037
|
-
function collectManifestRecordKeyIssues(manifest) {
|
|
1038
|
-
const cardSets = manifest.cardSets;
|
|
1039
|
-
const manualCards = cardSets.flatMap(
|
|
1040
|
-
(cardSet, cardSetIndex) => cardSet.type === "manual" ? cardSet.cards.flatMap(
|
|
1041
|
-
(card, cardIndex) => renderCardInstanceIds(card).map((cardId) => ({
|
|
1042
|
-
card,
|
|
1043
|
-
cardId,
|
|
1044
|
-
cardIndex,
|
|
1045
|
-
cardSetIndex
|
|
1046
|
-
}))
|
|
1047
|
-
) : []
|
|
1048
|
-
);
|
|
1049
|
-
return [
|
|
1050
|
-
...collectKeyIssues([
|
|
1051
|
-
...cardSets.map((cardSet, index) => ({
|
|
1052
|
-
value: cardSet.id,
|
|
1053
|
-
path: `manifest.cardSets[${index}].id`
|
|
1054
|
-
})),
|
|
1055
|
-
...manualCards.flatMap(({ card, cardId, cardIndex, cardSetIndex }) => [
|
|
1056
|
-
{
|
|
1057
|
-
value: card.type,
|
|
1058
|
-
path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].type`
|
|
1059
|
-
},
|
|
1060
|
-
{
|
|
1061
|
-
value: card.cardType,
|
|
1062
|
-
path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].cardType`
|
|
1063
|
-
},
|
|
1064
|
-
{
|
|
1065
|
-
value: cardId,
|
|
1066
|
-
path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].runtimeId`
|
|
1067
|
-
}
|
|
1068
|
-
]),
|
|
1069
|
-
...(manifest.zones ?? []).map((zone, index) => ({
|
|
1070
|
-
value: zone.id,
|
|
1071
|
-
path: `manifest.zones[${index}].id`
|
|
1072
|
-
})),
|
|
1073
|
-
...(manifest.resources ?? []).map((resource, index) => ({
|
|
1074
|
-
value: resource.id,
|
|
1075
|
-
path: `manifest.resources[${index}].id`
|
|
1076
|
-
})),
|
|
1077
|
-
...(manifest.pieceTypes ?? []).flatMap((pieceType, typeIndex) => [
|
|
1078
|
-
{
|
|
1079
|
-
value: pieceType.id,
|
|
1080
|
-
path: `manifest.pieceTypes[${typeIndex}].id`
|
|
1081
|
-
},
|
|
1082
|
-
...(pieceType.slots ?? []).map((slot, slotIndex) => ({
|
|
1083
|
-
value: slot.id,
|
|
1084
|
-
path: `manifest.pieceTypes[${typeIndex}].slots[${slotIndex}].id`
|
|
1085
|
-
}))
|
|
1086
|
-
]),
|
|
1087
|
-
...(manifest.dieTypes ?? []).flatMap((dieType, typeIndex) => [
|
|
1088
|
-
{
|
|
1089
|
-
value: dieType.id,
|
|
1090
|
-
path: `manifest.dieTypes[${typeIndex}].id`
|
|
1091
|
-
},
|
|
1092
|
-
...(dieType.slots ?? []).map((slot, slotIndex) => ({
|
|
1093
|
-
value: slot.id,
|
|
1094
|
-
path: `manifest.dieTypes[${typeIndex}].slots[${slotIndex}].id`
|
|
1095
|
-
}))
|
|
1096
|
-
]),
|
|
1097
|
-
...expandSeedIds(manifest.pieceSeeds ?? []).map((pieceId, index) => ({
|
|
1098
|
-
value: pieceId,
|
|
1099
|
-
path: `manifest.pieceSeeds[*][${index}]`
|
|
1100
|
-
})),
|
|
1101
|
-
...expandSeedIds(manifest.dieSeeds ?? []).map((dieId, index) => ({
|
|
1102
|
-
value: dieId,
|
|
1103
|
-
path: `manifest.dieSeeds[*][${index}]`
|
|
1104
|
-
})),
|
|
1105
|
-
...(manifest.setupOptions ?? []).flatMap((option, optionIndex) => [
|
|
1106
|
-
{
|
|
1107
|
-
value: option.id,
|
|
1108
|
-
path: `manifest.setupOptions[${optionIndex}].id`
|
|
1109
|
-
},
|
|
1110
|
-
...(option.choices ?? []).map((choice, choiceIndex) => ({
|
|
1111
|
-
value: choice.id,
|
|
1112
|
-
path: `manifest.setupOptions[${optionIndex}].choices[${choiceIndex}].id`
|
|
1113
|
-
}))
|
|
1114
|
-
]),
|
|
1115
|
-
...(manifest.setupProfiles ?? []).flatMap((profile, profileIndex) => [
|
|
1116
|
-
{
|
|
1117
|
-
value: profile.id,
|
|
1118
|
-
path: `manifest.setupProfiles[${profileIndex}].id`
|
|
1119
|
-
},
|
|
1120
|
-
...(profile.guidance?.steps ?? []).map((step, stepIndex) => ({
|
|
1121
|
-
value: step.id,
|
|
1122
|
-
path: `manifest.setupProfiles[${profileIndex}].guidance.steps[${stepIndex}].id`
|
|
1123
|
-
})),
|
|
1124
|
-
...Object.entries(profile.optionValues ?? {}).flatMap(
|
|
1125
|
-
([optionId, choiceId]) => [
|
|
1126
|
-
{
|
|
1127
|
-
value: optionId,
|
|
1128
|
-
path: `manifest.setupProfiles[${profileIndex}].optionValues.${optionId}`
|
|
1129
|
-
},
|
|
1130
|
-
{
|
|
1131
|
-
value: choiceId,
|
|
1132
|
-
path: `manifest.setupProfiles[${profileIndex}].optionValues.${optionId}`
|
|
1133
|
-
}
|
|
1134
|
-
]
|
|
1135
|
-
)
|
|
1136
|
-
])
|
|
1137
|
-
]),
|
|
1138
|
-
...cardSets.flatMap(
|
|
1139
|
-
(cardSet, cardSetIndex) => collectCardSchemaKeyIssues(cardSet, `manifest.cardSets[${cardSetIndex}]`)
|
|
1140
|
-
),
|
|
1141
|
-
...(manifest.pieceTypes ?? []).flatMap(
|
|
1142
|
-
(pieceType, typeIndex) => collectObjectSchemaKeyIssues(
|
|
1143
|
-
pieceType.fieldsSchema,
|
|
1144
|
-
`manifest.pieceTypes[${typeIndex}].fieldsSchema`
|
|
1145
|
-
)
|
|
1146
|
-
),
|
|
1147
|
-
...(manifest.dieTypes ?? []).flatMap(
|
|
1148
|
-
(dieType, typeIndex) => collectObjectSchemaKeyIssues(
|
|
1149
|
-
dieType.fieldsSchema,
|
|
1150
|
-
`manifest.dieTypes[${typeIndex}].fieldsSchema`
|
|
1151
|
-
)
|
|
1152
|
-
),
|
|
1153
|
-
...collectBoardRecordKeyIssues(manifest),
|
|
1154
|
-
...collectHandleKeyCollisions(
|
|
1155
|
-
dedupeSorted(manualCards.map(({ card }) => card.cardType ?? card.type)),
|
|
1156
|
-
"Card type"
|
|
1157
|
-
),
|
|
1158
|
-
...collectHandleKeyCollisions(
|
|
1159
|
-
dedupeSorted((manifest.zones ?? []).map((zone) => zone.id)),
|
|
1160
|
-
"Zone"
|
|
1161
|
-
)
|
|
1162
|
-
];
|
|
1163
|
-
}
|
|
1164
|
-
function validateManifestAuthoring(manifest) {
|
|
1165
|
-
const errors = [];
|
|
1166
|
-
errors.push(...collectManifestRecordKeyIssues(manifest));
|
|
1167
|
-
errors.push(
|
|
1168
|
-
...collectDuplicateIdIssues({
|
|
1169
|
-
entries: manifest.cardSets.map((cardSet, index) => ({
|
|
1170
|
-
id: cardSet.id,
|
|
1171
|
-
path: `manifest.cardSets[${index}].id`
|
|
1172
|
-
})),
|
|
1173
|
-
label: "card set id"
|
|
1174
|
-
})
|
|
1175
|
-
);
|
|
1176
|
-
errors.push(
|
|
1177
|
-
...collectDuplicateIdIssues({
|
|
1178
|
-
entries: manifest.cardSets.flatMap(
|
|
1179
|
-
(cardSet, cardSetIndex) => cardSet.type === "manual" ? cardSet.cards.flatMap(
|
|
1180
|
-
(card, cardIndex) => renderCardInstanceIds(card).map((cardId) => ({
|
|
1181
|
-
id: cardId,
|
|
1182
|
-
path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].type`
|
|
1183
|
-
}))
|
|
1184
|
-
) : []
|
|
1185
|
-
),
|
|
1186
|
-
label: "card runtime id"
|
|
1187
|
-
})
|
|
1188
|
-
);
|
|
1189
|
-
errors.push(
|
|
1190
|
-
...collectDuplicateIdIssues({
|
|
1191
|
-
entries: (manifest.zones ?? []).map((zone, index) => ({
|
|
1192
|
-
id: zone.id,
|
|
1193
|
-
path: `manifest.zones[${index}].id`
|
|
1194
|
-
})),
|
|
1195
|
-
label: "zone id"
|
|
1196
|
-
})
|
|
1197
|
-
);
|
|
1198
|
-
errors.push(
|
|
1199
|
-
...collectDuplicateIdIssues({
|
|
1200
|
-
entries: (manifest.boardTemplates ?? []).map((boardTemplate, index) => ({
|
|
1201
|
-
id: boardTemplate.id,
|
|
1202
|
-
path: `manifest.boardTemplates[${index}].id`
|
|
1203
|
-
})),
|
|
1204
|
-
label: "board template id"
|
|
1205
|
-
})
|
|
1206
|
-
);
|
|
1207
|
-
errors.push(
|
|
1208
|
-
...validateBoardTemplateDuplicates(manifest.boardTemplates ?? [])
|
|
1209
|
-
);
|
|
1210
|
-
errors.push(
|
|
1211
|
-
...collectDuplicateIdIssues({
|
|
1212
|
-
entries: (manifest.boards ?? []).map((board, index) => ({
|
|
1213
|
-
id: board.id,
|
|
1214
|
-
path: `manifest.boards[${index}].id`
|
|
1215
|
-
})),
|
|
1216
|
-
label: "board id"
|
|
1217
|
-
})
|
|
1218
|
-
);
|
|
1219
|
-
errors.push(...validateBoardDuplicates(manifest.boards ?? []));
|
|
1220
|
-
errors.push(
|
|
1221
|
-
...validateTypeSlotDuplicates({
|
|
1222
|
-
pieceTypes: manifest.pieceTypes ?? [],
|
|
1223
|
-
dieTypes: manifest.dieTypes ?? []
|
|
1224
|
-
})
|
|
1225
|
-
);
|
|
1226
|
-
errors.push(
|
|
1227
|
-
...collectDuplicateIdIssues({
|
|
1228
|
-
entries: (manifest.pieceTypes ?? []).map((pieceType, index) => ({
|
|
1229
|
-
id: pieceType.id,
|
|
1230
|
-
path: `manifest.pieceTypes[${index}].id`
|
|
1231
|
-
})),
|
|
1232
|
-
label: "piece type id"
|
|
1233
|
-
})
|
|
1234
|
-
);
|
|
1235
|
-
errors.push(
|
|
1236
|
-
...collectDuplicateIdIssues({
|
|
1237
|
-
entries: expandSeedIds(manifest.pieceSeeds ?? []).map(
|
|
1238
|
-
(pieceId, index) => ({
|
|
1239
|
-
id: pieceId,
|
|
1240
|
-
path: `manifest.pieceSeeds[*][${index}]`
|
|
1241
|
-
})
|
|
1242
|
-
),
|
|
1243
|
-
label: "piece runtime id"
|
|
1244
|
-
})
|
|
1245
|
-
);
|
|
1246
|
-
errors.push(
|
|
1247
|
-
...collectDuplicateIdIssues({
|
|
1248
|
-
entries: (manifest.dieTypes ?? []).map((dieType, index) => ({
|
|
1249
|
-
id: dieType.id,
|
|
1250
|
-
path: `manifest.dieTypes[${index}].id`
|
|
1251
|
-
})),
|
|
1252
|
-
label: "die type id"
|
|
1253
|
-
})
|
|
1254
|
-
);
|
|
1255
|
-
errors.push(
|
|
1256
|
-
...collectDuplicateIdIssues({
|
|
1257
|
-
entries: expandSeedIds(manifest.dieSeeds ?? []).map((dieId, index) => ({
|
|
1258
|
-
id: dieId,
|
|
1259
|
-
path: `manifest.dieSeeds[*][${index}]`
|
|
1260
|
-
})),
|
|
1261
|
-
label: "die runtime id"
|
|
1262
|
-
})
|
|
1263
|
-
);
|
|
1264
|
-
errors.push(
|
|
1265
|
-
...collectDuplicateIdIssues({
|
|
1266
|
-
entries: (manifest.resources ?? []).map((resource, index) => ({
|
|
1267
|
-
id: resource.id,
|
|
1268
|
-
path: `manifest.resources[${index}].id`
|
|
1269
|
-
})),
|
|
1270
|
-
label: "resource id"
|
|
1271
|
-
})
|
|
1272
|
-
);
|
|
1273
|
-
errors.push(
|
|
1274
|
-
...collectDuplicateIdIssues({
|
|
1275
|
-
entries: (manifest.setupOptions ?? []).map((option, index) => ({
|
|
1276
|
-
id: option.id,
|
|
1277
|
-
path: `manifest.setupOptions[${index}].id`
|
|
1278
|
-
})),
|
|
1279
|
-
label: "setup option id"
|
|
1280
|
-
})
|
|
1281
|
-
);
|
|
1282
|
-
errors.push(
|
|
1283
|
-
...validateSetupOptionChoiceDuplicates(manifest.setupOptions ?? [])
|
|
1284
|
-
);
|
|
1285
|
-
errors.push(
|
|
1286
|
-
...collectDuplicateIdIssues({
|
|
1287
|
-
entries: (manifest.setupProfiles ?? []).map((profile, index) => ({
|
|
1288
|
-
id: profile.id,
|
|
1289
|
-
path: `manifest.setupProfiles[${index}].id`
|
|
1290
|
-
})),
|
|
1291
|
-
label: "setup profile id"
|
|
1292
|
-
})
|
|
1293
|
-
);
|
|
1294
|
-
for (const [profileIndex, profile] of (manifest.setupProfiles ?? []).entries()) {
|
|
1295
|
-
errors.push(
|
|
1296
|
-
...collectDuplicateIdIssues({
|
|
1297
|
-
entries: (profile.guidance?.steps ?? []).map((step, stepIndex) => ({
|
|
1298
|
-
id: step.id,
|
|
1299
|
-
path: `manifest.setupProfiles[${profileIndex}].guidance.steps[${stepIndex}].id`
|
|
1300
|
-
})),
|
|
1301
|
-
label: `setup profile '${profile.id}' guidance step id`
|
|
1302
|
-
})
|
|
1303
|
-
);
|
|
1304
|
-
}
|
|
1305
|
-
errors.push(...validateSlotHostsAndHomes(manifest));
|
|
1306
|
-
errors.push(...validatePlayerScopedSeedHomes(manifest));
|
|
1307
|
-
errors.push(...validateCardHomes(manifest));
|
|
1308
|
-
errors.push(...validateSetupProfileReferences(manifest));
|
|
1309
|
-
errors.push(...validateHexBoardVertexRefs(manifest));
|
|
1310
|
-
return {
|
|
1311
|
-
errors,
|
|
1312
|
-
warnings: collectAmbiguousBoardTypeWarnings(manifest)
|
|
1313
|
-
};
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
// src/reducer/manifest/materialize.ts
|
|
1317
|
-
function isCardPropertySchemaVariants(schema) {
|
|
1318
|
-
return Boolean(schema && "variants" in schema);
|
|
1319
|
-
}
|
|
1320
|
-
function mergeSharedCardProperties(schema, cardType) {
|
|
1321
|
-
const variant = schema.variants[cardType];
|
|
1322
|
-
if (!variant) {
|
|
1323
|
-
return null;
|
|
1324
|
-
}
|
|
1325
|
-
return {
|
|
1326
|
-
properties: {
|
|
1327
|
-
...schema.shared ?? {},
|
|
1328
|
-
...variant.properties
|
|
1329
|
-
}
|
|
1330
|
-
};
|
|
1331
|
-
}
|
|
1332
|
-
function hasPropertySchemaDefault(property) {
|
|
1333
|
-
return Boolean(property && "default" in property);
|
|
1334
|
-
}
|
|
1335
|
-
function dedupeSorted2(values) {
|
|
1336
|
-
return Array.from(new Set(values)).sort();
|
|
1337
|
-
}
|
|
1338
|
-
function sortedObject(entries) {
|
|
1339
|
-
return Object.fromEntries(
|
|
1340
|
-
Array.from(entries).sort(([left], [right]) => left.localeCompare(right))
|
|
1341
|
-
);
|
|
1342
|
-
}
|
|
1343
|
-
function createRecord() {
|
|
1344
|
-
return /* @__PURE__ */ Object.create(null);
|
|
1345
|
-
}
|
|
1346
|
-
function renderCardInstanceIds2(card) {
|
|
1347
|
-
if (card.count > 1) {
|
|
1348
|
-
return Array.from(
|
|
1349
|
-
{ length: card.count },
|
|
1350
|
-
(_, index) => `${card.type}-${index + 1}`
|
|
1351
|
-
);
|
|
1352
|
-
}
|
|
1353
|
-
return [card.type];
|
|
1354
|
-
}
|
|
1355
|
-
function expandSeedIds2(seeds) {
|
|
1356
|
-
const expanded = [];
|
|
1357
|
-
for (const seed of seeds) {
|
|
1358
|
-
const count = seed.count ?? 1;
|
|
1359
|
-
const baseId = seed.id ?? seed.typeId;
|
|
1360
|
-
if (count <= 1) {
|
|
1361
|
-
expanded.push(baseId);
|
|
1362
|
-
continue;
|
|
1363
|
-
}
|
|
1364
|
-
for (let index = 1; index <= count; index += 1) {
|
|
1365
|
-
expanded.push(`${baseId}-${index}`);
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
return expanded;
|
|
1369
|
-
}
|
|
1370
|
-
function isHexBoardTemplateSpec2(boardTemplate) {
|
|
1371
|
-
return boardTemplate.layout === "hex";
|
|
1372
|
-
}
|
|
1373
|
-
function isSquareBoardTemplateSpec(boardTemplate) {
|
|
1374
|
-
return boardTemplate.layout === "square";
|
|
1375
|
-
}
|
|
1376
|
-
function isGenericBoardTemplateSpec(boardTemplate) {
|
|
1377
|
-
return boardTemplate.layout === "generic";
|
|
1378
|
-
}
|
|
1379
|
-
function isHexBoardSpec2(board) {
|
|
1380
|
-
return board.layout === "hex";
|
|
1381
|
-
}
|
|
1382
|
-
function isSquareBoardSpec(board) {
|
|
1383
|
-
return board.layout === "square";
|
|
1384
|
-
}
|
|
1385
|
-
var HEX_SIDES = ["e", "ne", "nw", "w", "sw", "se"];
|
|
1386
|
-
var HEX_CORNERS2 = ["ne-e", "e-se", "se-sw", "sw-w", "w-nw", "nw-ne"];
|
|
1387
|
-
var HEX_SIDE_OFFSETS = {
|
|
1388
|
-
e: [1, 0],
|
|
1389
|
-
ne: [1, -1],
|
|
1390
|
-
nw: [0, -1],
|
|
1391
|
-
w: [-1, 0],
|
|
1392
|
-
sw: [-1, 1],
|
|
1393
|
-
se: [0, 1]
|
|
1394
|
-
};
|
|
1395
|
-
var HEX_CORNER_OFFSETS2 = {
|
|
1396
|
-
"ne-e": [2, -1, -1],
|
|
1397
|
-
"e-se": [1, -2, 1],
|
|
1398
|
-
"se-sw": [-1, -1, 2],
|
|
1399
|
-
"sw-w": [-2, 1, 1],
|
|
1400
|
-
"w-nw": [-1, 2, -1],
|
|
1401
|
-
"nw-ne": [1, 1, -2]
|
|
1402
|
-
};
|
|
1403
|
-
var HEX_SIDE_CORNERS = {
|
|
1404
|
-
e: ["ne-e", "e-se"],
|
|
1405
|
-
ne: ["nw-ne", "ne-e"],
|
|
1406
|
-
nw: ["w-nw", "nw-ne"],
|
|
1407
|
-
w: ["sw-w", "w-nw"],
|
|
1408
|
-
sw: ["se-sw", "sw-w"],
|
|
1409
|
-
se: ["e-se", "se-sw"]
|
|
1410
|
-
};
|
|
1411
|
-
var HEX_CORNER_SIDES = {
|
|
1412
|
-
"ne-e": ["ne", "e"],
|
|
1413
|
-
"e-se": ["e", "se"],
|
|
1414
|
-
"se-sw": ["se", "sw"],
|
|
1415
|
-
"sw-w": ["sw", "w"],
|
|
1416
|
-
"w-nw": ["w", "nw"],
|
|
1417
|
-
"nw-ne": ["nw", "ne"]
|
|
1418
|
-
};
|
|
1419
|
-
function cubeFromAxial2(space) {
|
|
1420
|
-
const x = space.q;
|
|
1421
|
-
const z = space.r;
|
|
1422
|
-
return [x, -x - z, z];
|
|
1423
|
-
}
|
|
1424
|
-
function cornerGeometryKey2(space, corner) {
|
|
1425
|
-
const [x, y, z] = cubeFromAxial2(space);
|
|
1426
|
-
const [dx, dy, dz] = HEX_CORNER_OFFSETS2[corner];
|
|
1427
|
-
return `${3 * x + dx},${3 * y + dy},${3 * z + dz}`;
|
|
1428
|
-
}
|
|
1429
|
-
function edgeGeometryKey(space, side) {
|
|
1430
|
-
const [leftCorner, rightCorner] = HEX_SIDE_CORNERS[side];
|
|
1431
|
-
return [
|
|
1432
|
-
cornerGeometryKey2(space, leftCorner),
|
|
1433
|
-
cornerGeometryKey2(space, rightCorner)
|
|
1434
|
-
].sort((left, right) => left.localeCompare(right)).join("::");
|
|
1435
|
-
}
|
|
1436
|
-
function edgeIdFromGeometryKey(key) {
|
|
1437
|
-
return `hex-edge:${key}`;
|
|
1438
|
-
}
|
|
1439
|
-
function vertexIdFromGeometryKey(key) {
|
|
1440
|
-
return `hex-vertex:${key}`;
|
|
1441
|
-
}
|
|
1442
|
-
var SQUARE_SIDES = ["north", "east", "south", "west"];
|
|
1443
|
-
var SQUARE_CORNERS = ["nw", "ne", "se", "sw"];
|
|
1444
|
-
function squareEdgeIdFromGeometryKey(key) {
|
|
1445
|
-
return `square-edge:${key}`;
|
|
1446
|
-
}
|
|
1447
|
-
function squareVertexIdFromGeometryKey(key) {
|
|
1448
|
-
return `square-vertex:${key}`;
|
|
1449
|
-
}
|
|
1450
|
-
function squareCornerGeometryKey(space, corner) {
|
|
1451
|
-
switch (corner) {
|
|
1452
|
-
case "nw":
|
|
1453
|
-
return `${space.col},${space.row}`;
|
|
1454
|
-
case "ne":
|
|
1455
|
-
return `${space.col + 1},${space.row}`;
|
|
1456
|
-
case "se":
|
|
1457
|
-
return `${space.col + 1},${space.row + 1}`;
|
|
1458
|
-
case "sw":
|
|
1459
|
-
return `${space.col},${space.row + 1}`;
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
function squareEdgeGeometryKey(space, side) {
|
|
1463
|
-
const endpoints = side === "north" ? [`${space.col},${space.row}`, `${space.col + 1},${space.row}`] : side === "east" ? [`${space.col + 1},${space.row}`, `${space.col + 1},${space.row + 1}`] : side === "south" ? [
|
|
1464
|
-
`${space.col},${space.row + 1}`,
|
|
1465
|
-
`${space.col + 1},${space.row + 1}`
|
|
1466
|
-
] : [`${space.col},${space.row}`, `${space.col},${space.row + 1}`];
|
|
1467
|
-
return endpoints.sort((left, right) => left.localeCompare(right)).join("::");
|
|
1468
|
-
}
|
|
1469
|
-
function geometryKeyFromSquareEdgeRef(ref, spacesById) {
|
|
1470
|
-
const resolvedSpaces = [...ref.spaces].sort((a, b) => a.localeCompare(b)).map((spaceId) => {
|
|
1471
|
-
const space = spacesById.get(spaceId);
|
|
1472
|
-
if (!space) {
|
|
1473
|
-
throw new Error(
|
|
1474
|
-
`Square edge ref references unknown space '${spaceId}'.`
|
|
1475
|
-
);
|
|
1476
|
-
}
|
|
1477
|
-
return space;
|
|
1478
|
-
});
|
|
1479
|
-
const keyCounts = /* @__PURE__ */ new Map();
|
|
1480
|
-
for (const space of resolvedSpaces) {
|
|
1481
|
-
for (const side of SQUARE_SIDES) {
|
|
1482
|
-
const key = squareEdgeGeometryKey(space, side);
|
|
1483
|
-
keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
const candidates = [...keyCounts.entries()].filter(([, count]) => count === resolvedSpaces.length).map(([key]) => key).sort((left, right) => left.localeCompare(right));
|
|
1487
|
-
if (candidates.length !== 1) {
|
|
1488
|
-
throw new Error(
|
|
1489
|
-
`Square edge ref spaces '${ref.spaces.join(", ")}' do not resolve to exactly one shared edge.`
|
|
1490
|
-
);
|
|
1491
|
-
}
|
|
1492
|
-
const [only] = candidates;
|
|
1493
|
-
if (only === void 0) {
|
|
1494
|
-
throw new Error(
|
|
1495
|
-
"unreachable: candidates.length === 1 but first is undefined"
|
|
1496
|
-
);
|
|
1497
|
-
}
|
|
1498
|
-
return only;
|
|
1499
|
-
}
|
|
1500
|
-
function geometryKeyFromSquareVertexRef(ref, spacesById) {
|
|
1501
|
-
const resolvedSpaces = [...ref.spaces].sort((a, b) => a.localeCompare(b)).map((spaceId) => {
|
|
1502
|
-
const space = spacesById.get(spaceId);
|
|
1503
|
-
if (!space) {
|
|
1504
|
-
throw new Error(
|
|
1505
|
-
`Square vertex ref references unknown space '${spaceId}'.`
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
return space;
|
|
1509
|
-
});
|
|
1510
|
-
const keyCounts = /* @__PURE__ */ new Map();
|
|
1511
|
-
for (const space of resolvedSpaces) {
|
|
1512
|
-
for (const corner of SQUARE_CORNERS) {
|
|
1513
|
-
const key = squareCornerGeometryKey(space, corner);
|
|
1514
|
-
keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
const candidates = [...keyCounts.entries()].filter(([, count]) => count === resolvedSpaces.length).map(([key]) => key).sort((left, right) => left.localeCompare(right));
|
|
1518
|
-
if (candidates.length !== 1) {
|
|
1519
|
-
throw new Error(
|
|
1520
|
-
`Square vertex ref spaces '${ref.spaces.join(", ")}' do not resolve to exactly one shared vertex.`
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
const [only] = candidates;
|
|
1524
|
-
if (only === void 0) {
|
|
1525
|
-
throw new Error(
|
|
1526
|
-
"unreachable: candidates.length === 1 but first is undefined"
|
|
1527
|
-
);
|
|
1528
|
-
}
|
|
1529
|
-
return only;
|
|
1530
|
-
}
|
|
1531
|
-
function geometryKeyFromEdgeRef(ref, spacesById) {
|
|
1532
|
-
const [leftId, rightId] = [...ref.spaces].sort((a, b) => a.localeCompare(b));
|
|
1533
|
-
if (leftId === void 0 || rightId === void 0) {
|
|
1534
|
-
throw new Error("Hex edge ref must reference exactly two spaces.");
|
|
1535
|
-
}
|
|
1536
|
-
const leftSpace = spacesById.get(leftId);
|
|
1537
|
-
const rightSpace = spacesById.get(rightId);
|
|
1538
|
-
if (!leftSpace || !rightSpace) {
|
|
1539
|
-
throw new Error(
|
|
1540
|
-
`Hex edge ref references unknown spaces: ${ref.spaces.join(", ")}.`
|
|
1541
|
-
);
|
|
1542
|
-
}
|
|
1543
|
-
const [dq, dr] = [rightSpace.q - leftSpace.q, rightSpace.r - leftSpace.r];
|
|
1544
|
-
const side = Object.entries(HEX_SIDE_OFFSETS).find(([, [sideQ, sideR]]) => sideQ === dq && sideR === dr)?.[0];
|
|
1545
|
-
if (!side) {
|
|
1546
|
-
throw new Error(
|
|
1547
|
-
`Hex edge ref spaces '${leftId}' and '${rightId}' are not adjacent.`
|
|
1548
|
-
);
|
|
1549
|
-
}
|
|
1550
|
-
return edgeGeometryKey(leftSpace, side);
|
|
1551
|
-
}
|
|
1552
|
-
function geometryKeyFromVertexRef(ref, spacesById) {
|
|
1553
|
-
return resolveHexVertexGeometryKey(ref, spacesById);
|
|
1554
|
-
}
|
|
1555
|
-
function deriveHexEdges(spaces) {
|
|
1556
|
-
const spacesByCoordinate = /* @__PURE__ */ new Map();
|
|
1557
|
-
for (const space of spaces) {
|
|
1558
|
-
spacesByCoordinate.set(`${space.q},${space.r}`, space);
|
|
1559
|
-
}
|
|
1560
|
-
const edgeMap = /* @__PURE__ */ new Map();
|
|
1561
|
-
for (const space of spaces) {
|
|
1562
|
-
for (const side of HEX_SIDES) {
|
|
1563
|
-
const [dq, dr] = HEX_SIDE_OFFSETS[side];
|
|
1564
|
-
const neighbor = spacesByCoordinate.get(
|
|
1565
|
-
`${space.q + dq},${space.r + dr}`
|
|
1566
|
-
);
|
|
1567
|
-
const geometryKey = edgeGeometryKey(space, side);
|
|
1568
|
-
const existing = edgeMap.get(geometryKey);
|
|
1569
|
-
const nextSpaceIds = dedupeSorted2([
|
|
1570
|
-
...existing?.spaceIds ?? [],
|
|
1571
|
-
space.id,
|
|
1572
|
-
...neighbor ? [neighbor.id] : []
|
|
1573
|
-
]);
|
|
1574
|
-
edgeMap.set(geometryKey, {
|
|
1575
|
-
id: edgeIdFromGeometryKey(geometryKey),
|
|
1576
|
-
geometryKey,
|
|
1577
|
-
spaceIds: nextSpaceIds,
|
|
1578
|
-
typeId: existing?.typeId ?? null,
|
|
1579
|
-
label: existing?.label ?? null,
|
|
1580
|
-
fields: existing?.fields ?? null
|
|
1581
|
-
});
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
return [...edgeMap.values()].sort(
|
|
1585
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1586
|
-
);
|
|
1587
|
-
}
|
|
1588
|
-
function deriveHexVertices(spaces) {
|
|
1589
|
-
const spacesByCoordinate = /* @__PURE__ */ new Map();
|
|
1590
|
-
for (const space of spaces) {
|
|
1591
|
-
spacesByCoordinate.set(`${space.q},${space.r}`, space);
|
|
1592
|
-
}
|
|
1593
|
-
const vertexMap = /* @__PURE__ */ new Map();
|
|
1594
|
-
for (const space of spaces) {
|
|
1595
|
-
for (const corner of HEX_CORNERS2) {
|
|
1596
|
-
const [leftSide, rightSide] = HEX_CORNER_SIDES[corner];
|
|
1597
|
-
const [leftQ, leftR] = HEX_SIDE_OFFSETS[leftSide];
|
|
1598
|
-
const [rightQ, rightR] = HEX_SIDE_OFFSETS[rightSide];
|
|
1599
|
-
const leftNeighbor = spacesByCoordinate.get(
|
|
1600
|
-
`${space.q + leftQ},${space.r + leftR}`
|
|
1601
|
-
);
|
|
1602
|
-
const rightNeighbor = spacesByCoordinate.get(
|
|
1603
|
-
`${space.q + rightQ},${space.r + rightR}`
|
|
1604
|
-
);
|
|
1605
|
-
const geometryKey = cornerGeometryKey2(space, corner);
|
|
1606
|
-
const existing = vertexMap.get(geometryKey);
|
|
1607
|
-
const nextSpaceIds = dedupeSorted2([
|
|
1608
|
-
...existing?.spaceIds ?? [],
|
|
1609
|
-
space.id,
|
|
1610
|
-
...leftNeighbor ? [leftNeighbor.id] : [],
|
|
1611
|
-
...rightNeighbor ? [rightNeighbor.id] : []
|
|
1612
|
-
]);
|
|
1613
|
-
vertexMap.set(geometryKey, {
|
|
1614
|
-
id: vertexIdFromGeometryKey(geometryKey),
|
|
1615
|
-
geometryKey,
|
|
1616
|
-
spaceIds: nextSpaceIds,
|
|
1617
|
-
typeId: existing?.typeId ?? null,
|
|
1618
|
-
label: existing?.label ?? null,
|
|
1619
|
-
fields: existing?.fields ?? null
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
return [...vertexMap.values()].sort(
|
|
1624
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1625
|
-
);
|
|
1626
|
-
}
|
|
1627
|
-
function mergeBoardSpaces(templateSpaces, boardSpaces) {
|
|
1628
|
-
return Array.from(
|
|
1629
|
-
[...templateSpaces, ...boardSpaces].reduce((accumulator, space) => {
|
|
1630
|
-
accumulator.set(space.id, space);
|
|
1631
|
-
return accumulator;
|
|
1632
|
-
}, /* @__PURE__ */ new Map()).values()
|
|
1633
|
-
).sort((left, right) => left.id.localeCompare(right.id));
|
|
1634
|
-
}
|
|
1635
|
-
function mergeBoardContainers(templateContainers, boardContainers) {
|
|
1636
|
-
return Array.from(
|
|
1637
|
-
[...templateContainers, ...boardContainers].reduce((accumulator, container) => {
|
|
1638
|
-
accumulator.set(container.id, container);
|
|
1639
|
-
return accumulator;
|
|
1640
|
-
}, /* @__PURE__ */ new Map()).values()
|
|
1641
|
-
).sort((left, right) => left.id.localeCompare(right.id));
|
|
1642
|
-
}
|
|
1643
|
-
function resolveHexSpaces2(board, template) {
|
|
1644
|
-
if (!template) {
|
|
1645
|
-
return [...board.spaces ?? []].sort(
|
|
1646
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1647
|
-
);
|
|
1648
|
-
}
|
|
1649
|
-
const templateSpacesById = new Map(
|
|
1650
|
-
(template.spaces ?? []).map((space) => [space.id, space])
|
|
1651
|
-
);
|
|
1652
|
-
const overridesById = new Map(
|
|
1653
|
-
(board.spaces ?? []).map((space) => [space.id, space])
|
|
1654
|
-
);
|
|
1655
|
-
for (const overrideId of overridesById.keys()) {
|
|
1656
|
-
if (!templateSpacesById.has(overrideId)) {
|
|
1657
|
-
throw new Error(
|
|
1658
|
-
`Hex board '${board.id}' overrides unknown space '${overrideId}' from template '${template.id}'.`
|
|
1659
|
-
);
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
return (template.spaces ?? []).map((templateSpace) => {
|
|
1663
|
-
const override = overridesById.get(templateSpace.id);
|
|
1664
|
-
if (!override) {
|
|
1665
|
-
return templateSpace;
|
|
1666
|
-
}
|
|
1667
|
-
const templateMatch = templateSpacesById.get(override.id);
|
|
1668
|
-
if (!templateMatch) {
|
|
1669
|
-
throw new Error(
|
|
1670
|
-
`Hex board '${board.id}' overrides unknown space '${override.id}' from template '${template.id}'.`
|
|
1671
|
-
);
|
|
1672
|
-
}
|
|
1673
|
-
if (templateMatch.q !== override.q || templateMatch.r !== override.r) {
|
|
1674
|
-
throw new Error(
|
|
1675
|
-
`Hex board '${board.id}' cannot change coordinates for space '${override.id}' from template '${template.id}'.`
|
|
1676
|
-
);
|
|
1677
|
-
}
|
|
1678
|
-
return {
|
|
1679
|
-
...templateSpace,
|
|
1680
|
-
...override,
|
|
1681
|
-
id: templateSpace.id,
|
|
1682
|
-
q: templateSpace.q,
|
|
1683
|
-
r: templateSpace.r
|
|
1684
|
-
};
|
|
1685
|
-
}).sort((left, right) => left.id.localeCompare(right.id));
|
|
1686
|
-
}
|
|
1687
|
-
function resolveAuthoredHexEdges(board, template, spaces) {
|
|
1688
|
-
const spacesById = new Map(spaces.map((space) => [space.id, space]));
|
|
1689
|
-
const derivedByGeometryKey = new Map(
|
|
1690
|
-
deriveHexEdges(spaces).map((edge) => [edge.geometryKey, edge])
|
|
1691
|
-
);
|
|
1692
|
-
const collectSpecsByGeometryKey = (specs, ownerLabel) => {
|
|
1693
|
-
const specsByGeometryKey = /* @__PURE__ */ new Map();
|
|
1694
|
-
for (const spec of specs) {
|
|
1695
|
-
const geometryKey = geometryKeyFromEdgeRef(spec.ref, spacesById);
|
|
1696
|
-
if (specsByGeometryKey.has(geometryKey)) {
|
|
1697
|
-
throw new Error(`${ownerLabel} contains duplicate hex edge refs.`);
|
|
1698
|
-
}
|
|
1699
|
-
specsByGeometryKey.set(geometryKey, spec);
|
|
1700
|
-
}
|
|
1701
|
-
return specsByGeometryKey;
|
|
1702
|
-
};
|
|
1703
|
-
const templateSpecsByGeometryKey = template ? collectSpecsByGeometryKey(
|
|
1704
|
-
template.edges ?? [],
|
|
1705
|
-
`Hex board template '${template.id}'`
|
|
1706
|
-
) : /* @__PURE__ */ new Map();
|
|
1707
|
-
const overrideSpecsByGeometryKey = collectSpecsByGeometryKey(
|
|
1708
|
-
board.edges ?? [],
|
|
1709
|
-
`Hex board '${board.id}'`
|
|
1710
|
-
);
|
|
1711
|
-
if (template) {
|
|
1712
|
-
for (const overrideKey of overrideSpecsByGeometryKey.keys()) {
|
|
1713
|
-
if (!templateSpecsByGeometryKey.has(overrideKey)) {
|
|
1714
|
-
throw new Error(
|
|
1715
|
-
`Hex board '${board.id}' overrides unknown edge ref from template '${template.id}'.`
|
|
1716
|
-
);
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
const mergedSpecs = template ? Array.from(templateSpecsByGeometryKey.entries()).map(
|
|
1721
|
-
([geometryKey, templateSpec]) => [
|
|
1722
|
-
geometryKey,
|
|
1723
|
-
overrideSpecsByGeometryKey.get(geometryKey) ?? templateSpec
|
|
1724
|
-
]
|
|
1725
|
-
) : Array.from(overrideSpecsByGeometryKey.entries());
|
|
1726
|
-
return mergedSpecs.map(([geometryKey, spec]) => {
|
|
1727
|
-
const derivedEdge = derivedByGeometryKey.get(geometryKey);
|
|
1728
|
-
if (!derivedEdge) {
|
|
1729
|
-
throw new Error(
|
|
1730
|
-
`Hex edge ref on board '${board.id}' does not resolve to a derived edge.`
|
|
1731
|
-
);
|
|
1732
|
-
}
|
|
1733
|
-
return {
|
|
1734
|
-
id: derivedEdge.id,
|
|
1735
|
-
ref: cloneJson(spec.ref),
|
|
1736
|
-
typeId: spec.typeId ?? null,
|
|
1737
|
-
label: spec.label ?? null,
|
|
1738
|
-
fields: spec.fields ?? null
|
|
1739
|
-
};
|
|
1740
|
-
});
|
|
1741
|
-
}
|
|
1742
|
-
function resolveAuthoredHexVertices(board, template, spaces) {
|
|
1743
|
-
const spacesById = new Map(spaces.map((space) => [space.id, space]));
|
|
1744
|
-
const derivedByGeometryKey = new Map(
|
|
1745
|
-
deriveHexVertices(spaces).map(
|
|
1746
|
-
(vertex) => [vertex.geometryKey, vertex]
|
|
1747
|
-
)
|
|
1748
|
-
);
|
|
1749
|
-
const collectSpecsByGeometryKey = (specs, ownerLabel) => {
|
|
1750
|
-
const specsByGeometryKey = /* @__PURE__ */ new Map();
|
|
1751
|
-
for (const spec of specs) {
|
|
1752
|
-
const geometryKey = geometryKeyFromVertexRef(spec.ref, spacesById);
|
|
1753
|
-
if (specsByGeometryKey.has(geometryKey)) {
|
|
1754
|
-
throw new Error(`${ownerLabel} contains duplicate hex vertex refs.`);
|
|
1755
|
-
}
|
|
1756
|
-
specsByGeometryKey.set(geometryKey, spec);
|
|
1757
|
-
}
|
|
1758
|
-
return specsByGeometryKey;
|
|
1759
|
-
};
|
|
1760
|
-
const templateSpecsByGeometryKey = template ? collectSpecsByGeometryKey(
|
|
1761
|
-
template.vertices ?? [],
|
|
1762
|
-
`Hex board template '${template.id}'`
|
|
1763
|
-
) : /* @__PURE__ */ new Map();
|
|
1764
|
-
const overrideSpecsByGeometryKey = collectSpecsByGeometryKey(
|
|
1765
|
-
board.vertices ?? [],
|
|
1766
|
-
`Hex board '${board.id}'`
|
|
1767
|
-
);
|
|
1768
|
-
if (template) {
|
|
1769
|
-
for (const overrideKey of overrideSpecsByGeometryKey.keys()) {
|
|
1770
|
-
if (!templateSpecsByGeometryKey.has(overrideKey)) {
|
|
1771
|
-
throw new Error(
|
|
1772
|
-
`Hex board '${board.id}' overrides unknown vertex ref from template '${template.id}'.`
|
|
1773
|
-
);
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
const mergedSpecs = template ? Array.from(templateSpecsByGeometryKey.entries()).map(
|
|
1778
|
-
([geometryKey, templateSpec]) => [
|
|
1779
|
-
geometryKey,
|
|
1780
|
-
overrideSpecsByGeometryKey.get(geometryKey) ?? templateSpec
|
|
1781
|
-
]
|
|
1782
|
-
) : Array.from(overrideSpecsByGeometryKey.entries());
|
|
1783
|
-
return mergedSpecs.map(([geometryKey, spec]) => {
|
|
1784
|
-
const derivedVertex = derivedByGeometryKey.get(geometryKey);
|
|
1785
|
-
if (!derivedVertex) {
|
|
1786
|
-
throw new Error(
|
|
1787
|
-
`Hex vertex ref on board '${board.id}' does not resolve to a derived vertex.`
|
|
1788
|
-
);
|
|
1789
|
-
}
|
|
1790
|
-
return {
|
|
1791
|
-
id: derivedVertex.id,
|
|
1792
|
-
ref: cloneJson(spec.ref),
|
|
1793
|
-
typeId: spec.typeId ?? null,
|
|
1794
|
-
label: spec.label ?? null,
|
|
1795
|
-
fields: spec.fields ?? null
|
|
1796
|
-
};
|
|
1797
|
-
});
|
|
1798
|
-
}
|
|
1799
|
-
function resolveHexEdges(board, template, spaces) {
|
|
1800
|
-
const derived = deriveHexEdges(spaces);
|
|
1801
|
-
const edgesById = new Map(derived.map((edge) => [edge.id, edge]));
|
|
1802
|
-
for (const authoredEdge of resolveAuthoredHexEdges(board, template, spaces)) {
|
|
1803
|
-
const edge = edgesById.get(authoredEdge.id);
|
|
1804
|
-
if (!edge) {
|
|
1805
|
-
throw new Error(
|
|
1806
|
-
`Hex edge ref on board '${board.id}' does not resolve to a derived edge.`
|
|
1807
|
-
);
|
|
1808
|
-
}
|
|
1809
|
-
edgesById.set(authoredEdge.id, {
|
|
1810
|
-
...edge,
|
|
1811
|
-
typeId: authoredEdge.typeId ?? null,
|
|
1812
|
-
label: authoredEdge.label ?? null,
|
|
1813
|
-
fields: authoredEdge.fields ?? null
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1816
|
-
return [...edgesById.values()].sort(
|
|
1817
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1818
|
-
);
|
|
1819
|
-
}
|
|
1820
|
-
function resolveHexVertices(board, template, spaces) {
|
|
1821
|
-
const derived = deriveHexVertices(spaces);
|
|
1822
|
-
const verticesById = new Map(
|
|
1823
|
-
derived.map((vertex) => [vertex.id, vertex])
|
|
1824
|
-
);
|
|
1825
|
-
for (const authoredVertex of resolveAuthoredHexVertices(
|
|
1826
|
-
board,
|
|
1827
|
-
template,
|
|
1828
|
-
spaces
|
|
1829
|
-
)) {
|
|
1830
|
-
const vertex = verticesById.get(authoredVertex.id);
|
|
1831
|
-
if (!vertex) {
|
|
1832
|
-
throw new Error(
|
|
1833
|
-
`Hex vertex ref on board '${board.id}' does not resolve to a derived vertex.`
|
|
1834
|
-
);
|
|
1835
|
-
}
|
|
1836
|
-
verticesById.set(authoredVertex.id, {
|
|
1837
|
-
...vertex,
|
|
1838
|
-
typeId: authoredVertex.typeId ?? null,
|
|
1839
|
-
label: authoredVertex.label ?? null,
|
|
1840
|
-
fields: authoredVertex.fields ?? null
|
|
1841
|
-
});
|
|
1842
|
-
}
|
|
1843
|
-
return [...verticesById.values()].sort(
|
|
1844
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1845
|
-
);
|
|
1846
|
-
}
|
|
1847
|
-
function resolveSquareSpaces(board, template) {
|
|
1848
|
-
if (!template) {
|
|
1849
|
-
return [...board.spaces ?? []].sort(
|
|
1850
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1851
|
-
);
|
|
1852
|
-
}
|
|
1853
|
-
const templateSpacesById = new Map(
|
|
1854
|
-
(template.spaces ?? []).map((space) => [space.id, space])
|
|
1855
|
-
);
|
|
1856
|
-
const overridesById = new Map(
|
|
1857
|
-
(board.spaces ?? []).map((space) => [space.id, space])
|
|
1858
|
-
);
|
|
1859
|
-
for (const overrideId of overridesById.keys()) {
|
|
1860
|
-
if (!templateSpacesById.has(overrideId)) {
|
|
1861
|
-
throw new Error(
|
|
1862
|
-
`Square board '${board.id}' overrides unknown space '${overrideId}' from template '${template.id}'.`
|
|
1863
|
-
);
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1866
|
-
return (template.spaces ?? []).map((templateSpace) => {
|
|
1867
|
-
const override = overridesById.get(templateSpace.id);
|
|
1868
|
-
if (!override) {
|
|
1869
|
-
return templateSpace;
|
|
1870
|
-
}
|
|
1871
|
-
const templateMatch = templateSpacesById.get(override.id);
|
|
1872
|
-
if (!templateMatch) {
|
|
1873
|
-
throw new Error(
|
|
1874
|
-
`Square board '${board.id}' overrides unknown space '${override.id}' from template '${template.id}'.`
|
|
1875
|
-
);
|
|
1876
|
-
}
|
|
1877
|
-
if (templateMatch.row !== override.row || templateMatch.col !== override.col) {
|
|
1878
|
-
throw new Error(
|
|
1879
|
-
`Square board '${board.id}' cannot change coordinates for space '${override.id}' from template '${template.id}'.`
|
|
1880
|
-
);
|
|
1881
|
-
}
|
|
1882
|
-
return {
|
|
1883
|
-
...templateSpace,
|
|
1884
|
-
...override,
|
|
1885
|
-
id: templateSpace.id,
|
|
1886
|
-
row: templateSpace.row,
|
|
1887
|
-
col: templateSpace.col
|
|
1888
|
-
};
|
|
1889
|
-
}).sort((left, right) => left.id.localeCompare(right.id));
|
|
1890
|
-
}
|
|
1891
|
-
function deriveSquareEdges(spaces) {
|
|
1892
|
-
const edgeMap = /* @__PURE__ */ new Map();
|
|
1893
|
-
for (const space of spaces) {
|
|
1894
|
-
for (const side of SQUARE_SIDES) {
|
|
1895
|
-
const geometryKey = squareEdgeGeometryKey(space, side);
|
|
1896
|
-
const existing = edgeMap.get(geometryKey);
|
|
1897
|
-
const nextSpaceIds = dedupeSorted2([
|
|
1898
|
-
...existing?.spaceIds ?? [],
|
|
1899
|
-
space.id
|
|
1900
|
-
]);
|
|
1901
|
-
edgeMap.set(geometryKey, {
|
|
1902
|
-
id: squareEdgeIdFromGeometryKey(geometryKey),
|
|
1903
|
-
geometryKey,
|
|
1904
|
-
spaceIds: nextSpaceIds,
|
|
1905
|
-
typeId: existing?.typeId ?? null,
|
|
1906
|
-
label: existing?.label ?? null,
|
|
1907
|
-
fields: existing?.fields ?? null
|
|
1908
|
-
});
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
return [...edgeMap.values()].sort(
|
|
1912
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1913
|
-
);
|
|
1914
|
-
}
|
|
1915
|
-
function deriveSquareVertices(spaces) {
|
|
1916
|
-
const vertexMap = /* @__PURE__ */ new Map();
|
|
1917
|
-
for (const space of spaces) {
|
|
1918
|
-
for (const corner of SQUARE_CORNERS) {
|
|
1919
|
-
const geometryKey = squareCornerGeometryKey(space, corner);
|
|
1920
|
-
const existing = vertexMap.get(geometryKey);
|
|
1921
|
-
const nextSpaceIds = dedupeSorted2([
|
|
1922
|
-
...existing?.spaceIds ?? [],
|
|
1923
|
-
space.id
|
|
1924
|
-
]);
|
|
1925
|
-
vertexMap.set(geometryKey, {
|
|
1926
|
-
id: squareVertexIdFromGeometryKey(geometryKey),
|
|
1927
|
-
geometryKey,
|
|
1928
|
-
spaceIds: nextSpaceIds,
|
|
1929
|
-
typeId: existing?.typeId ?? null,
|
|
1930
|
-
label: existing?.label ?? null,
|
|
1931
|
-
fields: existing?.fields ?? null
|
|
1932
|
-
});
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
return [...vertexMap.values()].sort(
|
|
1936
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
1937
|
-
);
|
|
1938
|
-
}
|
|
1939
|
-
function indexSquareEdgeMetadata(specs, spacesById, ownerLabel) {
|
|
1940
|
-
const metadataByKey = /* @__PURE__ */ new Map();
|
|
1941
|
-
for (const spec of specs) {
|
|
1942
|
-
const geometryKey = geometryKeyFromSquareEdgeRef(spec.ref, spacesById);
|
|
1943
|
-
if (metadataByKey.has(geometryKey)) {
|
|
1944
|
-
throw new Error(`${ownerLabel} contains duplicate square edge refs.`);
|
|
1945
|
-
}
|
|
1946
|
-
metadataByKey.set(geometryKey, {
|
|
1947
|
-
geometryKey,
|
|
1948
|
-
typeId: spec.typeId ?? null,
|
|
1949
|
-
label: spec.label ?? null,
|
|
1950
|
-
fields: spec.fields ?? null
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
1953
|
-
return metadataByKey;
|
|
1954
|
-
}
|
|
1955
|
-
function indexSquareVertexMetadata(specs, spacesById, ownerLabel) {
|
|
1956
|
-
const metadataByKey = /* @__PURE__ */ new Map();
|
|
1957
|
-
for (const spec of specs) {
|
|
1958
|
-
const geometryKey = geometryKeyFromSquareVertexRef(spec.ref, spacesById);
|
|
1959
|
-
if (metadataByKey.has(geometryKey)) {
|
|
1960
|
-
throw new Error(`${ownerLabel} contains duplicate square vertex refs.`);
|
|
1961
|
-
}
|
|
1962
|
-
metadataByKey.set(geometryKey, {
|
|
1963
|
-
geometryKey,
|
|
1964
|
-
typeId: spec.typeId ?? null,
|
|
1965
|
-
label: spec.label ?? null,
|
|
1966
|
-
fields: spec.fields ?? null
|
|
1967
|
-
});
|
|
1968
|
-
}
|
|
1969
|
-
return metadataByKey;
|
|
1970
|
-
}
|
|
1971
|
-
function resolveSquareEdges(board, template, spaces) {
|
|
1972
|
-
const spacesById = new Map(spaces.map((space) => [space.id, space]));
|
|
1973
|
-
const derived = deriveSquareEdges(spaces);
|
|
1974
|
-
const edgesByGeometryKey = new Map(
|
|
1975
|
-
derived.map((edge) => [edge.geometryKey, edge])
|
|
1976
|
-
);
|
|
1977
|
-
const templateMetadata = template ? indexSquareEdgeMetadata(
|
|
1978
|
-
template.edges ?? [],
|
|
1979
|
-
spacesById,
|
|
1980
|
-
`Square board template '${template.id}'`
|
|
1981
|
-
) : /* @__PURE__ */ new Map();
|
|
1982
|
-
const overrideMetadata = indexSquareEdgeMetadata(
|
|
1983
|
-
board.edges ?? [],
|
|
1984
|
-
spacesById,
|
|
1985
|
-
`Square board '${board.id}'`
|
|
1986
|
-
);
|
|
1987
|
-
if (template) {
|
|
1988
|
-
for (const overrideKey of overrideMetadata.keys()) {
|
|
1989
|
-
if (!templateMetadata.has(overrideKey)) {
|
|
1990
|
-
throw new Error(
|
|
1991
|
-
`Square board '${board.id}' overrides unknown edge ref from template '${template.id}'.`
|
|
1992
|
-
);
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
const mergedMetadata = template ? new Map(
|
|
1997
|
-
[...templateMetadata.entries()].map(([key, metadata]) => [
|
|
1998
|
-
key,
|
|
1999
|
-
{
|
|
2000
|
-
...metadata,
|
|
2001
|
-
...overrideMetadata.get(key) ?? {},
|
|
2002
|
-
geometryKey: key
|
|
2003
|
-
}
|
|
2004
|
-
])
|
|
2005
|
-
) : overrideMetadata;
|
|
2006
|
-
for (const [geometryKey, metadata] of mergedMetadata.entries()) {
|
|
2007
|
-
const edge = edgesByGeometryKey.get(geometryKey);
|
|
2008
|
-
if (!edge) {
|
|
2009
|
-
throw new Error(
|
|
2010
|
-
`Square edge ref on board '${board.id}' does not resolve to a derived edge.`
|
|
2011
|
-
);
|
|
2012
|
-
}
|
|
2013
|
-
edgesByGeometryKey.set(geometryKey, {
|
|
2014
|
-
...edge,
|
|
2015
|
-
typeId: metadata.typeId ?? null,
|
|
2016
|
-
label: metadata.label ?? null,
|
|
2017
|
-
fields: metadata.fields ?? null
|
|
2018
|
-
});
|
|
2019
|
-
}
|
|
2020
|
-
return [...edgesByGeometryKey.values()].sort(
|
|
2021
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
2022
|
-
);
|
|
2023
|
-
}
|
|
2024
|
-
function resolveSquareVertices(board, template, spaces) {
|
|
2025
|
-
const spacesById = new Map(spaces.map((space) => [space.id, space]));
|
|
2026
|
-
const derived = deriveSquareVertices(spaces);
|
|
2027
|
-
const verticesByGeometryKey = new Map(
|
|
2028
|
-
derived.map((vertex) => [vertex.geometryKey, vertex])
|
|
2029
|
-
);
|
|
2030
|
-
const templateMetadata = template ? indexSquareVertexMetadata(
|
|
2031
|
-
template.vertices ?? [],
|
|
2032
|
-
spacesById,
|
|
2033
|
-
`Square board template '${template.id}'`
|
|
2034
|
-
) : /* @__PURE__ */ new Map();
|
|
2035
|
-
const overrideMetadata = indexSquareVertexMetadata(
|
|
2036
|
-
board.vertices ?? [],
|
|
2037
|
-
spacesById,
|
|
2038
|
-
`Square board '${board.id}'`
|
|
2039
|
-
);
|
|
2040
|
-
if (template) {
|
|
2041
|
-
for (const overrideKey of overrideMetadata.keys()) {
|
|
2042
|
-
if (!templateMetadata.has(overrideKey)) {
|
|
2043
|
-
throw new Error(
|
|
2044
|
-
`Square board '${board.id}' overrides unknown vertex ref from template '${template.id}'.`
|
|
2045
|
-
);
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
const mergedMetadata = template ? new Map(
|
|
2050
|
-
[...templateMetadata.entries()].map(([key, metadata]) => [
|
|
2051
|
-
key,
|
|
2052
|
-
{
|
|
2053
|
-
...metadata,
|
|
2054
|
-
...overrideMetadata.get(key) ?? {},
|
|
2055
|
-
geometryKey: key
|
|
2056
|
-
}
|
|
2057
|
-
])
|
|
2058
|
-
) : overrideMetadata;
|
|
2059
|
-
for (const [geometryKey, metadata] of mergedMetadata.entries()) {
|
|
2060
|
-
const vertex = verticesByGeometryKey.get(geometryKey);
|
|
2061
|
-
if (!vertex) {
|
|
2062
|
-
throw new Error(
|
|
2063
|
-
`Square vertex ref on board '${board.id}' does not resolve to a derived vertex.`
|
|
2064
|
-
);
|
|
2065
|
-
}
|
|
2066
|
-
verticesByGeometryKey.set(geometryKey, {
|
|
2067
|
-
...vertex,
|
|
2068
|
-
typeId: metadata.typeId ?? null,
|
|
2069
|
-
label: metadata.label ?? null,
|
|
2070
|
-
fields: metadata.fields ?? null
|
|
2071
|
-
});
|
|
2072
|
-
}
|
|
2073
|
-
return [...verticesByGeometryKey.values()].sort(
|
|
2074
|
-
(left, right) => left.id.localeCompare(right.id)
|
|
2075
|
-
);
|
|
2076
|
-
}
|
|
2077
|
-
function analyzeBoards(manifest, playerIds) {
|
|
2078
|
-
const boardTemplates = manifest.boardTemplates ?? [];
|
|
2079
|
-
const genericTemplateById = new Map(
|
|
2080
|
-
boardTemplates.filter(isGenericBoardTemplateSpec).map((boardTemplate) => [boardTemplate.id, boardTemplate])
|
|
2081
|
-
);
|
|
2082
|
-
const hexTemplateById = new Map(
|
|
2083
|
-
boardTemplates.filter(isHexBoardTemplateSpec2).map((boardTemplate) => [boardTemplate.id, boardTemplate])
|
|
2084
|
-
);
|
|
2085
|
-
const squareTemplateById = new Map(
|
|
2086
|
-
boardTemplates.filter(isSquareBoardTemplateSpec).map((boardTemplate) => [boardTemplate.id, boardTemplate])
|
|
2087
|
-
);
|
|
2088
|
-
return (manifest.boards ?? []).map((board) => {
|
|
2089
|
-
const runtimeBoardIds = board.scope === "perPlayer" ? playerIds.map((playerId) => `${board.id}:${playerId}`) : [board.id];
|
|
2090
|
-
if (isHexBoardSpec2(board)) {
|
|
2091
|
-
const hexBoard = board;
|
|
2092
|
-
const template2 = board.templateId ? hexTemplateById.get(board.templateId) : void 0;
|
|
2093
|
-
const spaces = resolveHexSpaces2(hexBoard, template2);
|
|
2094
|
-
return {
|
|
2095
|
-
layout: "hex",
|
|
2096
|
-
board: hexBoard,
|
|
2097
|
-
template: template2,
|
|
2098
|
-
boardTypeId: hexBoard.typeId ?? template2?.typeId,
|
|
2099
|
-
runtimeBoardIds,
|
|
2100
|
-
boardFieldsSchema: hexBoard.boardFieldsSchema ?? template2?.boardFieldsSchema,
|
|
2101
|
-
spaceFieldsSchema: hexBoard.spaceFieldsSchema ?? template2?.spaceFieldsSchema,
|
|
2102
|
-
edgeFieldsSchema: hexBoard.edgeFieldsSchema ?? template2?.edgeFieldsSchema,
|
|
2103
|
-
vertexFieldsSchema: hexBoard.vertexFieldsSchema ?? template2?.vertexFieldsSchema,
|
|
2104
|
-
spaces,
|
|
2105
|
-
authoredEdges: resolveAuthoredHexEdges(hexBoard, template2, spaces),
|
|
2106
|
-
authoredVertices: resolveAuthoredHexVertices(
|
|
2107
|
-
hexBoard,
|
|
2108
|
-
template2,
|
|
2109
|
-
spaces
|
|
2110
|
-
),
|
|
2111
|
-
edges: resolveHexEdges(hexBoard, template2, spaces),
|
|
2112
|
-
vertices: resolveHexVertices(hexBoard, template2, spaces)
|
|
2113
|
-
};
|
|
2114
|
-
}
|
|
2115
|
-
if (isSquareBoardSpec(board)) {
|
|
2116
|
-
const squareBoard = board;
|
|
2117
|
-
const template2 = board.templateId ? squareTemplateById.get(board.templateId) : void 0;
|
|
2118
|
-
const spaces = resolveSquareSpaces(squareBoard, template2);
|
|
2119
|
-
return {
|
|
2120
|
-
layout: "square",
|
|
2121
|
-
board: squareBoard,
|
|
2122
|
-
template: template2,
|
|
2123
|
-
boardTypeId: squareBoard.typeId ?? template2?.typeId,
|
|
2124
|
-
runtimeBoardIds,
|
|
2125
|
-
boardFieldsSchema: squareBoard.boardFieldsSchema ?? template2?.boardFieldsSchema,
|
|
2126
|
-
spaceFieldsSchema: squareBoard.spaceFieldsSchema ?? template2?.spaceFieldsSchema,
|
|
2127
|
-
relationFieldsSchema: squareBoard.relationFieldsSchema ?? template2?.relationFieldsSchema,
|
|
2128
|
-
containerFieldsSchema: squareBoard.containerFieldsSchema ?? template2?.containerFieldsSchema,
|
|
2129
|
-
edgeFieldsSchema: squareBoard.edgeFieldsSchema ?? template2?.edgeFieldsSchema,
|
|
2130
|
-
vertexFieldsSchema: squareBoard.vertexFieldsSchema ?? template2?.vertexFieldsSchema,
|
|
2131
|
-
spaces,
|
|
2132
|
-
relations: [
|
|
2133
|
-
...template2?.relations ?? [],
|
|
2134
|
-
...squareBoard.relations ?? []
|
|
2135
|
-
],
|
|
2136
|
-
containers: mergeBoardContainers(
|
|
2137
|
-
template2?.containers ?? [],
|
|
2138
|
-
squareBoard.containers ?? []
|
|
2139
|
-
),
|
|
2140
|
-
edges: resolveSquareEdges(squareBoard, template2, spaces),
|
|
2141
|
-
vertices: resolveSquareVertices(squareBoard, template2, spaces)
|
|
2142
|
-
};
|
|
2143
|
-
}
|
|
2144
|
-
const genericBoard = board;
|
|
2145
|
-
const template = board.templateId ? genericTemplateById.get(board.templateId) : void 0;
|
|
2146
|
-
return {
|
|
2147
|
-
layout: "generic",
|
|
2148
|
-
board: genericBoard,
|
|
2149
|
-
template,
|
|
2150
|
-
boardTypeId: genericBoard.typeId ?? template?.typeId,
|
|
2151
|
-
runtimeBoardIds,
|
|
2152
|
-
boardFieldsSchema: genericBoard.boardFieldsSchema ?? template?.boardFieldsSchema,
|
|
2153
|
-
spaceFieldsSchema: genericBoard.spaceFieldsSchema ?? template?.spaceFieldsSchema,
|
|
2154
|
-
relationFieldsSchema: genericBoard.relationFieldsSchema ?? template?.relationFieldsSchema,
|
|
2155
|
-
containerFieldsSchema: genericBoard.containerFieldsSchema ?? template?.containerFieldsSchema,
|
|
2156
|
-
spaces: mergeBoardSpaces(
|
|
2157
|
-
template?.spaces ?? [],
|
|
2158
|
-
genericBoard.spaces ?? []
|
|
2159
|
-
),
|
|
2160
|
-
relations: [
|
|
2161
|
-
...template?.relations ?? [],
|
|
2162
|
-
...genericBoard.relations ?? []
|
|
2163
|
-
],
|
|
2164
|
-
containers: mergeBoardContainers(
|
|
2165
|
-
template?.containers ?? [],
|
|
2166
|
-
genericBoard.containers ?? []
|
|
2167
|
-
)
|
|
2168
|
-
};
|
|
2169
|
-
});
|
|
2170
|
-
}
|
|
2171
|
-
function isSingletonExplicitSeed(seed) {
|
|
2172
|
-
return typeof seed.id === "string" && seed.id.length > 0 && (seed.count ?? 1) === 1;
|
|
2173
|
-
}
|
|
2174
|
-
function analyzeManifest(inputManifest, runtimePlayerIds) {
|
|
2175
|
-
const manifest = addStandardDecksIfNeeded(inputManifest);
|
|
2176
|
-
const playerIds = runtimePlayerIds ? [...runtimePlayerIds] : Array.from(
|
|
2177
|
-
{ length: manifest.players.maxPlayers },
|
|
2178
|
-
(_, index) => `player-${index + 1}`
|
|
2179
|
-
);
|
|
2180
|
-
const sharedZones = (manifest.zones ?? []).filter(
|
|
2181
|
-
(zone) => zone.scope === "shared"
|
|
2182
|
-
);
|
|
2183
|
-
const playerZones = (manifest.zones ?? []).filter(
|
|
2184
|
-
(zone) => zone.scope === "perPlayer"
|
|
2185
|
-
);
|
|
2186
|
-
const zoneIds = dedupeSorted2((manifest.zones ?? []).map((zone) => zone.id));
|
|
2187
|
-
const cardSets = manifest.cardSets.map(materializeCardSet);
|
|
2188
|
-
const cardSetIds = dedupeSorted2(cardSets.map((cardSet) => cardSet.id));
|
|
2189
|
-
const cardTypes = dedupeSorted2(
|
|
2190
|
-
cardSets.flatMap((cardSet) => cardSet.cards.map((card) => card.type))
|
|
2191
|
-
);
|
|
2192
|
-
const cardIds = dedupeSorted2(
|
|
2193
|
-
cardSets.flatMap((cardSet) => cardSet.cards.flatMap(renderCardInstanceIds2))
|
|
2194
|
-
);
|
|
2195
|
-
const cardSetIdByCardId = /* @__PURE__ */ new Map();
|
|
2196
|
-
const cardTypeByCardId = /* @__PURE__ */ new Map();
|
|
2197
|
-
for (const cardSet of cardSets) {
|
|
2198
|
-
for (const card of cardSet.cards) {
|
|
2199
|
-
for (const cardId of renderCardInstanceIds2(card)) {
|
|
2200
|
-
cardSetIdByCardId.set(cardId, cardSet.id);
|
|
2201
|
-
cardTypeByCardId.set(cardId, card.cardType ?? card.type);
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
const sharedZoneCardSetIds = /* @__PURE__ */ new Map();
|
|
2206
|
-
for (const zone of sharedZones) {
|
|
2207
|
-
sharedZoneCardSetIds.set(
|
|
2208
|
-
zone.id,
|
|
2209
|
-
dedupeSorted2(zone.allowedCardSetIds ?? [])
|
|
2210
|
-
);
|
|
2211
|
-
}
|
|
2212
|
-
const playerZoneCardSetIds = /* @__PURE__ */ new Map();
|
|
2213
|
-
for (const zone of playerZones) {
|
|
2214
|
-
playerZoneCardSetIds.set(
|
|
2215
|
-
zone.id,
|
|
2216
|
-
dedupeSorted2(zone.allowedCardSetIds ?? [])
|
|
2217
|
-
);
|
|
2218
|
-
}
|
|
2219
|
-
const zoneCardSetIdsById = /* @__PURE__ */ new Map();
|
|
2220
|
-
for (const [zoneId, cardSetIds2] of sharedZoneCardSetIds.entries()) {
|
|
2221
|
-
zoneCardSetIdsById.set(zoneId, cardSetIds2);
|
|
2222
|
-
}
|
|
2223
|
-
for (const [zoneId, cardSetIds2] of playerZoneCardSetIds.entries()) {
|
|
2224
|
-
zoneCardSetIdsById.set(zoneId, cardSetIds2);
|
|
2225
|
-
}
|
|
2226
|
-
const sharedZoneIdsByCardSetId = new Map(
|
|
2227
|
-
cardSetIds.map((cardSetId) => [cardSetId, []])
|
|
2228
|
-
);
|
|
2229
|
-
for (const [zoneId, allowedCardSetIds] of sharedZoneCardSetIds.entries()) {
|
|
2230
|
-
for (const cardSetId of allowedCardSetIds) {
|
|
2231
|
-
const zoneIds2 = sharedZoneIdsByCardSetId.get(cardSetId) ?? [];
|
|
2232
|
-
zoneIds2.push(zoneId);
|
|
2233
|
-
sharedZoneIdsByCardSetId.set(cardSetId, zoneIds2);
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
for (const [cardSetId, zoneIds2] of sharedZoneIdsByCardSetId.entries()) {
|
|
2237
|
-
sharedZoneIdsByCardSetId.set(cardSetId, dedupeSorted2(zoneIds2));
|
|
2238
|
-
}
|
|
2239
|
-
const sharedZoneIdSet = new Set(sharedZones.map((zone) => zone.id));
|
|
2240
|
-
const homeSharedZoneIdsByCardType = new Map(
|
|
2241
|
-
cardTypes.map((cardType) => [cardType, []])
|
|
2242
|
-
);
|
|
2243
|
-
for (const cardSet of cardSets) {
|
|
2244
|
-
for (const card of cardSet.cards) {
|
|
2245
|
-
if (card.home?.type !== "zone" || !sharedZoneIdSet.has(card.home.zoneId)) {
|
|
2246
|
-
continue;
|
|
2247
|
-
}
|
|
2248
|
-
const zoneIds2 = homeSharedZoneIdsByCardType.get(card.type) ?? [];
|
|
2249
|
-
zoneIds2.push(card.home.zoneId);
|
|
2250
|
-
homeSharedZoneIdsByCardType.set(card.type, zoneIds2);
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
const homeSharedZoneIdByCardType = /* @__PURE__ */ new Map();
|
|
2254
|
-
for (const [cardType, zoneIds2] of homeSharedZoneIdsByCardType.entries()) {
|
|
2255
|
-
const uniqueZoneIds = dedupeSorted2(zoneIds2);
|
|
2256
|
-
homeSharedZoneIdsByCardType.set(cardType, uniqueZoneIds);
|
|
2257
|
-
const [homeZoneId] = uniqueZoneIds;
|
|
2258
|
-
if (uniqueZoneIds.length === 1 && homeZoneId !== void 0) {
|
|
2259
|
-
homeSharedZoneIdByCardType.set(cardType, homeZoneId);
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
const zoneVisibilityById = new Map(
|
|
2263
|
-
(manifest.zones ?? []).map((zone) => [
|
|
2264
|
-
zone.id,
|
|
2265
|
-
zone.visibility ?? "public"
|
|
2266
|
-
])
|
|
2267
|
-
);
|
|
2268
|
-
const resourceIds = dedupeSorted2(
|
|
2269
|
-
(manifest.resources ?? []).map((resource) => resource.id)
|
|
2270
|
-
);
|
|
2271
|
-
const resourcePresentationById = sortedObject(
|
|
2272
|
-
(manifest.resources ?? []).map((resource) => [
|
|
2273
|
-
resource.id,
|
|
2274
|
-
{
|
|
2275
|
-
label: resource.name,
|
|
2276
|
-
...resource.icon ? { icon: resource.icon } : {}
|
|
2277
|
-
}
|
|
2278
|
-
])
|
|
2279
|
-
);
|
|
2280
|
-
const setupOptionIds = dedupeSorted2(
|
|
2281
|
-
(manifest.setupOptions ?? []).map((option) => option.id)
|
|
2282
|
-
);
|
|
2283
|
-
const setupProfileIds = dedupeSorted2(
|
|
2284
|
-
(manifest.setupProfiles ?? []).map((profile) => profile.id)
|
|
2285
|
-
);
|
|
2286
|
-
const setupChoiceIdsByOptionId = new Map(
|
|
2287
|
-
(manifest.setupOptions ?? []).map((option) => [
|
|
2288
|
-
option.id,
|
|
2289
|
-
dedupeSorted2((option.choices ?? []).map((choice) => choice.id))
|
|
2290
|
-
])
|
|
2291
|
-
);
|
|
2292
|
-
const setupOptionsById = Object.fromEntries(
|
|
2293
|
-
(manifest.setupOptions ?? []).slice().sort((left, right) => left.id.localeCompare(right.id)).map((option) => [
|
|
2294
|
-
option.id,
|
|
2295
|
-
{
|
|
2296
|
-
id: option.id,
|
|
2297
|
-
name: option.name,
|
|
2298
|
-
description: option.description ?? null,
|
|
2299
|
-
choices: (option.choices ?? []).map((choice) => ({
|
|
2300
|
-
id: choice.id,
|
|
2301
|
-
label: choice.label,
|
|
2302
|
-
description: choice.description ?? null
|
|
2303
|
-
}))
|
|
2304
|
-
}
|
|
2305
|
-
])
|
|
2306
|
-
);
|
|
2307
|
-
const setupProfilesById = Object.fromEntries(
|
|
2308
|
-
(manifest.setupProfiles ?? []).slice().sort((left, right) => left.id.localeCompare(right.id)).map((profile) => [
|
|
2309
|
-
profile.id,
|
|
2310
|
-
{
|
|
2311
|
-
id: profile.id,
|
|
2312
|
-
name: profile.name,
|
|
2313
|
-
description: profile.description ?? null,
|
|
2314
|
-
optionValues: profile.optionValues ? Object.fromEntries(
|
|
2315
|
-
Object.entries(profile.optionValues).filter(
|
|
2316
|
-
(entry) => typeof entry[1] === "string"
|
|
2317
|
-
)
|
|
2318
|
-
) : null,
|
|
2319
|
-
guidance: profile.guidance ? {
|
|
2320
|
-
summary: profile.guidance.summary ?? null,
|
|
2321
|
-
steps: (profile.guidance.steps ?? []).map((step) => ({
|
|
2322
|
-
id: step.id,
|
|
2323
|
-
label: step.label,
|
|
2324
|
-
description: step.description ?? null
|
|
2325
|
-
}))
|
|
2326
|
-
} : null
|
|
2327
|
-
}
|
|
2328
|
-
])
|
|
2329
|
-
);
|
|
2330
|
-
const pieceTypeIds = dedupeSorted2(
|
|
2331
|
-
(manifest.pieceTypes ?? []).map((pieceType) => pieceType.id)
|
|
2332
|
-
);
|
|
2333
|
-
const pieceTypeSchemasById = new Map(
|
|
2334
|
-
(manifest.pieceTypes ?? []).map((pieceType) => [
|
|
2335
|
-
pieceType.id,
|
|
2336
|
-
pieceType.fieldsSchema
|
|
2337
|
-
])
|
|
2338
|
-
);
|
|
2339
|
-
const pieceTypeSlotIdsById = new Map(
|
|
2340
|
-
(manifest.pieceTypes ?? []).map((pieceType) => [
|
|
2341
|
-
pieceType.id,
|
|
2342
|
-
dedupeSorted2((pieceType.slots ?? []).map((slot) => slot.id))
|
|
2343
|
-
])
|
|
2344
|
-
);
|
|
2345
|
-
const pieceTypeIdByPieceId = /* @__PURE__ */ new Map();
|
|
2346
|
-
for (const seed of manifest.pieceSeeds ?? []) {
|
|
2347
|
-
for (const pieceId of expandSeedIds2([seed])) {
|
|
2348
|
-
pieceTypeIdByPieceId.set(pieceId, seed.typeId);
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
const pieceIds = dedupeSorted2(pieceTypeIdByPieceId.keys());
|
|
2352
|
-
const dieTypeIds = dedupeSorted2(
|
|
2353
|
-
(manifest.dieTypes ?? []).map((dieType) => dieType.id)
|
|
2354
|
-
);
|
|
2355
|
-
const dieTypeSchemasById = new Map(
|
|
2356
|
-
(manifest.dieTypes ?? []).map((dieType) => [
|
|
2357
|
-
dieType.id,
|
|
2358
|
-
dieType.fieldsSchema
|
|
2359
|
-
])
|
|
2360
|
-
);
|
|
2361
|
-
const dieTypeSlotIdsById = new Map(
|
|
2362
|
-
(manifest.dieTypes ?? []).map((dieType) => [
|
|
2363
|
-
dieType.id,
|
|
2364
|
-
dedupeSorted2((dieType.slots ?? []).map((slot) => slot.id))
|
|
2365
|
-
])
|
|
2366
|
-
);
|
|
2367
|
-
const dieTypeIdByDieId = /* @__PURE__ */ new Map();
|
|
2368
|
-
for (const seed of manifest.dieSeeds ?? []) {
|
|
2369
|
-
for (const dieId of expandSeedIds2([seed])) {
|
|
2370
|
-
dieTypeIdByDieId.set(dieId, seed.typeId);
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
const dieIds = dedupeSorted2(dieTypeIdByDieId.keys());
|
|
2374
|
-
const strictSlotHosts = [
|
|
2375
|
-
...(manifest.pieceSeeds ?? []).flatMap((seed) => {
|
|
2376
|
-
if (!isSingletonExplicitSeed(seed)) {
|
|
2377
|
-
return [];
|
|
2378
|
-
}
|
|
2379
|
-
const slotIds = pieceTypeSlotIdsById.get(seed.typeId) ?? [];
|
|
2380
|
-
return slotIds.length > 0 ? [
|
|
2381
|
-
{
|
|
2382
|
-
kind: "piece",
|
|
2383
|
-
id: seed.id,
|
|
2384
|
-
slotIds
|
|
2385
|
-
}
|
|
2386
|
-
] : [];
|
|
2387
|
-
}),
|
|
2388
|
-
...(manifest.dieSeeds ?? []).flatMap((seed) => {
|
|
2389
|
-
if (!isSingletonExplicitSeed(seed)) {
|
|
2390
|
-
return [];
|
|
2391
|
-
}
|
|
2392
|
-
const slotIds = dieTypeSlotIdsById.get(seed.typeId) ?? [];
|
|
2393
|
-
return slotIds.length > 0 ? [
|
|
2394
|
-
{
|
|
2395
|
-
kind: "die",
|
|
2396
|
-
id: seed.id,
|
|
2397
|
-
slotIds
|
|
2398
|
-
}
|
|
2399
|
-
] : [];
|
|
2400
|
-
})
|
|
2401
|
-
].sort(
|
|
2402
|
-
(left, right) => left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
|
|
2403
|
-
);
|
|
2404
|
-
const analyzedBoards = analyzeBoards(manifest, playerIds);
|
|
2405
|
-
const boardTemplateIds = dedupeSorted2(
|
|
2406
|
-
(manifest.boardTemplates ?? []).map((boardTemplate) => boardTemplate.id)
|
|
2407
|
-
);
|
|
2408
|
-
const boardBaseIds = dedupeSorted2(
|
|
2409
|
-
analyzedBoards.map(({ board }) => board.id)
|
|
2410
|
-
);
|
|
2411
|
-
const boardIds = dedupeSorted2(
|
|
2412
|
-
analyzedBoards.flatMap((entry) => entry.runtimeBoardIds)
|
|
2413
|
-
);
|
|
2414
|
-
const boardTypeIds = dedupeSorted2(
|
|
2415
|
-
analyzedBoards.map((board) => board.boardTypeId).filter((typeId) => typeof typeId === "string")
|
|
2416
|
-
);
|
|
2417
|
-
const boardLayoutById = /* @__PURE__ */ new Map();
|
|
2418
|
-
const boardIdsByLayout = /* @__PURE__ */ new Map();
|
|
2419
|
-
const boardBaseIdsByLayout = /* @__PURE__ */ new Map();
|
|
2420
|
-
const boardIdsByBaseId = /* @__PURE__ */ new Map();
|
|
2421
|
-
const boardBaseIdsByTemplateId = /* @__PURE__ */ new Map();
|
|
2422
|
-
const boardTemplateLayoutById = /* @__PURE__ */ new Map();
|
|
2423
|
-
const boardIdsByTypeId = /* @__PURE__ */ new Map();
|
|
2424
|
-
const spaceIdsByBoardId = /* @__PURE__ */ new Map();
|
|
2425
|
-
const spaceTypeIdByBoardId = /* @__PURE__ */ new Map();
|
|
2426
|
-
const spaceIdsByTypeId = /* @__PURE__ */ new Map();
|
|
2427
|
-
const containerIdsByBoardId = /* @__PURE__ */ new Map();
|
|
2428
|
-
const containerHostByBoardId = /* @__PURE__ */ new Map();
|
|
2429
|
-
const relationTypeIdsByBoardId = /* @__PURE__ */ new Map();
|
|
2430
|
-
const edgeIdsByTypeId = /* @__PURE__ */ new Map();
|
|
2431
|
-
const edgeIdsByBoardIdAndTypeId = /* @__PURE__ */ new Map();
|
|
2432
|
-
const vertexIdsByTypeId = /* @__PURE__ */ new Map();
|
|
2433
|
-
const vertexIdsByBoardIdAndTypeId = /* @__PURE__ */ new Map();
|
|
2434
|
-
for (const boardTemplate of manifest.boardTemplates ?? []) {
|
|
2435
|
-
boardTemplateLayoutById.set(boardTemplate.id, boardTemplate.layout);
|
|
2436
|
-
boardBaseIdsByLayout.set(
|
|
2437
|
-
boardTemplate.layout,
|
|
2438
|
-
dedupeSorted2([...boardBaseIdsByLayout.get(boardTemplate.layout) ?? []])
|
|
2439
|
-
);
|
|
2440
|
-
}
|
|
2441
|
-
for (const analyzedBoard of analyzedBoards) {
|
|
2442
|
-
boardIdsByBaseId.set(analyzedBoard.board.id, analyzedBoard.runtimeBoardIds);
|
|
2443
|
-
boardIdsByLayout.set(
|
|
2444
|
-
analyzedBoard.board.layout,
|
|
2445
|
-
dedupeSorted2([
|
|
2446
|
-
...boardIdsByLayout.get(analyzedBoard.board.layout) ?? [],
|
|
2447
|
-
...analyzedBoard.runtimeBoardIds
|
|
2448
|
-
])
|
|
2449
|
-
);
|
|
2450
|
-
boardBaseIdsByLayout.set(
|
|
2451
|
-
analyzedBoard.board.layout,
|
|
2452
|
-
dedupeSorted2([
|
|
2453
|
-
...boardBaseIdsByLayout.get(analyzedBoard.board.layout) ?? [],
|
|
2454
|
-
analyzedBoard.board.id
|
|
2455
|
-
])
|
|
2456
|
-
);
|
|
2457
|
-
if (analyzedBoard.board.templateId) {
|
|
2458
|
-
boardBaseIdsByTemplateId.set(
|
|
2459
|
-
analyzedBoard.board.templateId,
|
|
2460
|
-
dedupeSorted2([
|
|
2461
|
-
...boardBaseIdsByTemplateId.get(analyzedBoard.board.templateId) ?? [],
|
|
2462
|
-
analyzedBoard.board.id
|
|
2463
|
-
])
|
|
2464
|
-
);
|
|
2465
|
-
}
|
|
2466
|
-
const runtimeSpaceIds = analyzedBoard.spaces.map((space) => space.id);
|
|
2467
|
-
const runtimeSpaceTypeIds = sortedObject(
|
|
2468
|
-
analyzedBoard.spaces.map(
|
|
2469
|
-
(space) => [space.id, space.typeId ?? null]
|
|
2470
|
-
)
|
|
2471
|
-
);
|
|
2472
|
-
const runtimeContainerIds = analyzedBoard.layout === "hex" ? [] : analyzedBoard.containers.map((container) => container.id);
|
|
2473
|
-
const runtimeContainerHosts = analyzedBoard.layout === "hex" ? {} : sortedObject(
|
|
2474
|
-
analyzedBoard.containers.map((container) => [
|
|
2475
|
-
container.id,
|
|
2476
|
-
container.host.type === "space" ? { type: "space", spaceId: container.host.spaceId } : { type: "board" }
|
|
2477
|
-
])
|
|
2478
|
-
);
|
|
2479
|
-
const runtimeRelationTypeIds = analyzedBoard.layout === "hex" ? ["adjacent"] : analyzedBoard.layout === "square" ? dedupeSorted2([
|
|
2480
|
-
"adjacent",
|
|
2481
|
-
...analyzedBoard.relations.map((relation) => relation.typeId)
|
|
2482
|
-
]) : dedupeSorted2(
|
|
2483
|
-
analyzedBoard.relations.map((relation) => relation.typeId)
|
|
2484
|
-
);
|
|
2485
|
-
for (const runtimeBoardId of analyzedBoard.runtimeBoardIds) {
|
|
2486
|
-
boardLayoutById.set(runtimeBoardId, analyzedBoard.board.layout);
|
|
2487
|
-
spaceIdsByBoardId.set(runtimeBoardId, runtimeSpaceIds);
|
|
2488
|
-
spaceTypeIdByBoardId.set(runtimeBoardId, runtimeSpaceTypeIds);
|
|
2489
|
-
containerIdsByBoardId.set(runtimeBoardId, runtimeContainerIds);
|
|
2490
|
-
containerHostByBoardId.set(runtimeBoardId, runtimeContainerHosts);
|
|
2491
|
-
relationTypeIdsByBoardId.set(runtimeBoardId, runtimeRelationTypeIds);
|
|
2492
|
-
}
|
|
2493
|
-
if (analyzedBoard.boardTypeId) {
|
|
2494
|
-
boardIdsByTypeId.set(
|
|
2495
|
-
analyzedBoard.boardTypeId,
|
|
2496
|
-
dedupeSorted2([
|
|
2497
|
-
...boardIdsByTypeId.get(analyzedBoard.boardTypeId) ?? [],
|
|
2498
|
-
...analyzedBoard.runtimeBoardIds
|
|
2499
|
-
])
|
|
2500
|
-
);
|
|
2501
|
-
}
|
|
2502
|
-
for (const space of analyzedBoard.spaces) {
|
|
2503
|
-
if (!space.typeId) {
|
|
2504
|
-
continue;
|
|
2505
|
-
}
|
|
2506
|
-
spaceIdsByTypeId.set(
|
|
2507
|
-
space.typeId,
|
|
2508
|
-
dedupeSorted2([...spaceIdsByTypeId.get(space.typeId) ?? [], space.id])
|
|
2509
|
-
);
|
|
2510
|
-
}
|
|
2511
|
-
if (analyzedBoard.layout !== "generic") {
|
|
2512
|
-
const edgeIdsForBoardByType = {};
|
|
2513
|
-
for (const edge of analyzedBoard.edges) {
|
|
2514
|
-
if (!edge.typeId) {
|
|
2515
|
-
continue;
|
|
2516
|
-
}
|
|
2517
|
-
edgeIdsByTypeId.set(
|
|
2518
|
-
edge.typeId,
|
|
2519
|
-
dedupeSorted2([...edgeIdsByTypeId.get(edge.typeId) ?? [], edge.id])
|
|
2520
|
-
);
|
|
2521
|
-
edgeIdsForBoardByType[edge.typeId] = dedupeSorted2([
|
|
2522
|
-
...edgeIdsForBoardByType[edge.typeId] ?? [],
|
|
2523
|
-
edge.id
|
|
2524
|
-
]);
|
|
2525
|
-
}
|
|
2526
|
-
const vertexIdsForBoardByType = {};
|
|
2527
|
-
for (const vertex of analyzedBoard.vertices) {
|
|
2528
|
-
if (!vertex.typeId) {
|
|
2529
|
-
continue;
|
|
2530
|
-
}
|
|
2531
|
-
vertexIdsByTypeId.set(
|
|
2532
|
-
vertex.typeId,
|
|
2533
|
-
dedupeSorted2([
|
|
2534
|
-
...vertexIdsByTypeId.get(vertex.typeId) ?? [],
|
|
2535
|
-
vertex.id
|
|
2536
|
-
])
|
|
2537
|
-
);
|
|
2538
|
-
vertexIdsForBoardByType[vertex.typeId] = dedupeSorted2([
|
|
2539
|
-
...vertexIdsForBoardByType[vertex.typeId] ?? [],
|
|
2540
|
-
vertex.id
|
|
2541
|
-
]);
|
|
2542
|
-
}
|
|
2543
|
-
for (const runtimeBoardId of analyzedBoard.runtimeBoardIds) {
|
|
2544
|
-
edgeIdsByBoardIdAndTypeId.set(runtimeBoardId, edgeIdsForBoardByType);
|
|
2545
|
-
vertexIdsByBoardIdAndTypeId.set(
|
|
2546
|
-
runtimeBoardId,
|
|
2547
|
-
vertexIdsForBoardByType
|
|
2548
|
-
);
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
const relationTypeIds = dedupeSorted2(
|
|
2553
|
-
Array.from(relationTypeIdsByBoardId.values()).flat()
|
|
2554
|
-
);
|
|
2555
|
-
const edgeIds = dedupeSorted2(
|
|
2556
|
-
analyzedBoards.flatMap(
|
|
2557
|
-
(board) => board.layout === "generic" ? [] : board.edges.map((edge) => edge.id)
|
|
2558
|
-
)
|
|
2559
|
-
);
|
|
2560
|
-
const edgeTypeIds = dedupeSorted2(edgeIdsByTypeId.keys());
|
|
2561
|
-
const vertexIds = dedupeSorted2(
|
|
2562
|
-
analyzedBoards.flatMap(
|
|
2563
|
-
(board) => board.layout === "generic" ? [] : board.vertices.map((vertex) => vertex.id)
|
|
2564
|
-
)
|
|
2565
|
-
);
|
|
2566
|
-
const vertexTypeIds = dedupeSorted2(vertexIdsByTypeId.keys());
|
|
2567
|
-
const boardContainerIds = dedupeSorted2(
|
|
2568
|
-
analyzedBoards.flatMap(
|
|
2569
|
-
(board) => board.layout === "hex" ? [] : board.containers.map((container) => container.id)
|
|
2570
|
-
)
|
|
2571
|
-
);
|
|
2572
|
-
const spaceIds = dedupeSorted2(
|
|
2573
|
-
analyzedBoards.flatMap((board) => board.spaces.map((space) => space.id))
|
|
2574
|
-
);
|
|
2575
|
-
const spaceTypeIds = dedupeSorted2(
|
|
2576
|
-
analyzedBoards.flatMap(
|
|
2577
|
-
(board) => board.spaces.map((space) => space.typeId).filter((typeId) => typeof typeId === "string")
|
|
2578
|
-
)
|
|
2579
|
-
);
|
|
2580
|
-
return {
|
|
2581
|
-
manifest,
|
|
2582
|
-
playerIds,
|
|
2583
|
-
sharedZones,
|
|
2584
|
-
playerZones,
|
|
2585
|
-
zoneIds,
|
|
2586
|
-
cardSets,
|
|
2587
|
-
cardSetIds,
|
|
2588
|
-
cardTypes,
|
|
2589
|
-
cardIds,
|
|
2590
|
-
cardSetIdByCardId,
|
|
2591
|
-
cardTypeByCardId,
|
|
2592
|
-
sharedZoneCardSetIds,
|
|
2593
|
-
sharedZoneIdsByCardSetId,
|
|
2594
|
-
homeSharedZoneIdsByCardType,
|
|
2595
|
-
homeSharedZoneIdByCardType,
|
|
2596
|
-
playerZoneCardSetIds,
|
|
2597
|
-
zoneCardSetIdsById,
|
|
2598
|
-
zoneVisibilityById,
|
|
2599
|
-
resourceIds,
|
|
2600
|
-
resourcePresentationById,
|
|
2601
|
-
setupOptionIds,
|
|
2602
|
-
setupProfileIds,
|
|
2603
|
-
setupChoiceIdsByOptionId,
|
|
2604
|
-
setupOptionsById,
|
|
2605
|
-
setupProfilesById,
|
|
2606
|
-
pieceTypeIds,
|
|
2607
|
-
pieceIds,
|
|
2608
|
-
pieceTypeIdByPieceId,
|
|
2609
|
-
dieTypeIds,
|
|
2610
|
-
dieIds,
|
|
2611
|
-
dieTypeIdByDieId,
|
|
2612
|
-
strictSlotHosts,
|
|
2613
|
-
boardTemplateIds,
|
|
2614
|
-
boardBaseIds,
|
|
2615
|
-
boardIds,
|
|
2616
|
-
boardContainerIds,
|
|
2617
|
-
boardTypeIds,
|
|
2618
|
-
boardLayoutById,
|
|
2619
|
-
boardIdsByLayout,
|
|
2620
|
-
boardBaseIdsByLayout,
|
|
2621
|
-
boardIdsByBaseId,
|
|
2622
|
-
boardBaseIdsByTemplateId,
|
|
2623
|
-
boardTemplateLayoutById,
|
|
2624
|
-
boardIdsByTypeId,
|
|
2625
|
-
spaceIdsByBoardId,
|
|
2626
|
-
spaceTypeIdByBoardId,
|
|
2627
|
-
spaceIdsByTypeId,
|
|
2628
|
-
containerIdsByBoardId,
|
|
2629
|
-
containerHostByBoardId,
|
|
2630
|
-
relationTypeIds,
|
|
2631
|
-
relationTypeIdsByBoardId,
|
|
2632
|
-
edgeIds,
|
|
2633
|
-
edgeTypeIds,
|
|
2634
|
-
edgeIdsByTypeId,
|
|
2635
|
-
edgeIdsByBoardIdAndTypeId,
|
|
2636
|
-
vertexIds,
|
|
2637
|
-
vertexTypeIds,
|
|
2638
|
-
vertexIdsByTypeId,
|
|
2639
|
-
vertexIdsByBoardIdAndTypeId,
|
|
2640
|
-
spaceIds,
|
|
2641
|
-
spaceTypeIds,
|
|
2642
|
-
analyzedBoards,
|
|
2643
|
-
pieceTypeSchemasById,
|
|
2644
|
-
dieTypeSchemasById
|
|
2645
|
-
};
|
|
2646
|
-
}
|
|
2647
|
-
function cloneJson(value) {
|
|
2648
|
-
return JSON.parse(JSON.stringify(value));
|
|
2649
|
-
}
|
|
2650
|
-
function boardSpaceRefKey(spaceIds) {
|
|
2651
|
-
return [...spaceIds].sort((left, right) => left.localeCompare(right)).join("$$");
|
|
2652
|
-
}
|
|
2653
|
-
function materializePropertySchemaDefault(property, analysis, runtimeBoardId) {
|
|
2654
|
-
if (hasPropertySchemaDefault(property)) {
|
|
2655
|
-
return cloneJson(property.default);
|
|
2656
|
-
}
|
|
2657
|
-
if (property.optional) {
|
|
2658
|
-
return void 0;
|
|
2659
|
-
}
|
|
2660
|
-
if (property.nullable) {
|
|
2661
|
-
return null;
|
|
2662
|
-
}
|
|
2663
|
-
switch (property.type) {
|
|
2664
|
-
case "string":
|
|
2665
|
-
return "";
|
|
2666
|
-
case "integer":
|
|
2667
|
-
case "number":
|
|
2668
|
-
return 0;
|
|
2669
|
-
case "boolean":
|
|
2670
|
-
return false;
|
|
2671
|
-
case "enum":
|
|
2672
|
-
return property.enums?.[0] ?? "";
|
|
2673
|
-
case "array":
|
|
2674
|
-
return [];
|
|
2675
|
-
case "object":
|
|
2676
|
-
return materializeObjectSchemaDefaults(
|
|
2677
|
-
property.properties ? { properties: property.properties } : void 0,
|
|
2678
|
-
analysis,
|
|
2679
|
-
runtimeBoardId
|
|
2680
|
-
);
|
|
2681
|
-
case "record":
|
|
2682
|
-
return {};
|
|
2683
|
-
case "zoneId":
|
|
2684
|
-
return analysis.zoneIds[0] ?? "";
|
|
2685
|
-
case "cardId":
|
|
2686
|
-
return analysis.cardIds[0] ?? "";
|
|
2687
|
-
case "playerId":
|
|
2688
|
-
return analysis.playerIds[0] ?? "";
|
|
2689
|
-
case "boardId":
|
|
2690
|
-
return runtimeBoardId ?? analysis.boardIds[0] ?? "";
|
|
2691
|
-
case "edgeId":
|
|
2692
|
-
return analysis.edgeIds[0] ?? "";
|
|
2693
|
-
case "vertexId":
|
|
2694
|
-
return analysis.vertexIds[0] ?? "";
|
|
2695
|
-
case "spaceId":
|
|
2696
|
-
return (runtimeBoardId ? analysis.spaceIdsByBoardId.get(runtimeBoardId)?.[0] : void 0) ?? analysis.spaceIds[0] ?? "";
|
|
2697
|
-
case "pieceId":
|
|
2698
|
-
return analysis.pieceIds[0] ?? "";
|
|
2699
|
-
case "dieId":
|
|
2700
|
-
return analysis.dieIds[0] ?? "";
|
|
2701
|
-
case "resourceId":
|
|
2702
|
-
return analysis.resourceIds[0] ?? "";
|
|
2703
|
-
}
|
|
2704
|
-
}
|
|
2705
|
-
function materializeObjectSchemaDefaults(schema, analysis, runtimeBoardId) {
|
|
2706
|
-
if (!schema?.properties) {
|
|
2707
|
-
return {};
|
|
2708
|
-
}
|
|
2709
|
-
return Object.fromEntries(
|
|
2710
|
-
Object.entries(schema.properties).flatMap(([key, property]) => {
|
|
2711
|
-
const value = materializePropertySchemaDefault(
|
|
2712
|
-
property,
|
|
2713
|
-
analysis,
|
|
2714
|
-
runtimeBoardId
|
|
2715
|
-
);
|
|
2716
|
-
return value === void 0 ? [] : [[key, value]];
|
|
2717
|
-
})
|
|
2718
|
-
);
|
|
2719
|
-
}
|
|
2720
|
-
function materializeCardPropertiesDefaults(schema, cardType, analysis) {
|
|
2721
|
-
const objectSchema = isCardPropertySchemaVariants(schema) ? mergeSharedCardProperties(schema, cardType) : schema;
|
|
2722
|
-
return materializeObjectSchemaDefaults(objectSchema, analysis);
|
|
2723
|
-
}
|
|
2724
|
-
function materializeManifestTable(options) {
|
|
2725
|
-
const validation = validateManifestAuthoring(options.manifest);
|
|
2726
|
-
if (validation.errors.length > 0) {
|
|
2727
|
-
throw new Error(
|
|
2728
|
-
`Cannot materialize invalid topology manifest:
|
|
2729
|
-
${validation.errors.join("\n")}`
|
|
2730
|
-
);
|
|
2731
|
-
}
|
|
2732
|
-
const analysis = analyzeManifest(options.manifest, options.playerIds);
|
|
2733
|
-
const manifest = analysis.manifest;
|
|
2734
|
-
const playerIds = [...options.playerIds];
|
|
2735
|
-
const cards = createRecord();
|
|
2736
|
-
const pieces = createRecord();
|
|
2737
|
-
const dice = createRecord();
|
|
2738
|
-
const componentLocations = createRecord();
|
|
2739
|
-
const locationOrder = /* @__PURE__ */ new Map();
|
|
2740
|
-
const boardAnalysisByBaseId = new Map(
|
|
2741
|
-
analysis.analyzedBoards.map((board) => [board.board.id, board])
|
|
2742
|
-
);
|
|
2743
|
-
const edgeIdByBoardBaseIdAndSpaces = /* @__PURE__ */ new Map();
|
|
2744
|
-
const vertexIdByBoardBaseIdAndSpaces = /* @__PURE__ */ new Map();
|
|
2745
|
-
for (const analyzedBoard of analysis.analyzedBoards) {
|
|
2746
|
-
if (analyzedBoard.layout === "generic") {
|
|
2747
|
-
continue;
|
|
2748
|
-
}
|
|
2749
|
-
edgeIdByBoardBaseIdAndSpaces.set(
|
|
2750
|
-
analyzedBoard.board.id,
|
|
2751
|
-
new Map(
|
|
2752
|
-
analyzedBoard.edges.map((edge) => [
|
|
2753
|
-
boardSpaceRefKey(edge.spaceIds),
|
|
2754
|
-
edge.id
|
|
2755
|
-
])
|
|
2756
|
-
)
|
|
2757
|
-
);
|
|
2758
|
-
vertexIdByBoardBaseIdAndSpaces.set(
|
|
2759
|
-
analyzedBoard.board.id,
|
|
2760
|
-
new Map(
|
|
2761
|
-
analyzedBoard.vertices.map((vertex) => [
|
|
2762
|
-
boardSpaceRefKey(vertex.spaceIds),
|
|
2763
|
-
vertex.id
|
|
2764
|
-
])
|
|
2765
|
-
)
|
|
2766
|
-
);
|
|
2767
|
-
}
|
|
2768
|
-
const nextLocationPosition = (location) => {
|
|
2769
|
-
const key = JSON.stringify(location);
|
|
2770
|
-
const position = locationOrder.get(key) ?? 0;
|
|
2771
|
-
locationOrder.set(key, position + 1);
|
|
2772
|
-
return position;
|
|
2773
|
-
};
|
|
2774
|
-
const zoneScopeById = new Map([
|
|
2775
|
-
...analysis.sharedZones.map((zone) => [zone.id, "shared"]),
|
|
2776
|
-
...analysis.playerZones.map((zone) => [zone.id, "perPlayer"])
|
|
2777
|
-
]);
|
|
2778
|
-
const resolveRuntimeBoardId = (boardBaseId, ownerId, context) => {
|
|
2779
|
-
const analyzedBoard = boardAnalysisByBaseId.get(boardBaseId);
|
|
2780
|
-
if (!analyzedBoard) {
|
|
2781
|
-
throw new Error(
|
|
2782
|
-
`${context ?? "Component home"}.boardId: Unknown board '${boardBaseId}'.`
|
|
2783
|
-
);
|
|
2784
|
-
}
|
|
2785
|
-
if (analyzedBoard?.board.scope === "perPlayer") {
|
|
2786
|
-
if (!ownerId) {
|
|
2787
|
-
throw new Error(
|
|
2788
|
-
`${context ?? `Home on board '${boardBaseId}'`} requires ownerId because board '${boardBaseId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
|
|
2789
|
-
);
|
|
2790
|
-
}
|
|
2791
|
-
return `${boardBaseId}:${ownerId}`;
|
|
2792
|
-
}
|
|
2793
|
-
return boardBaseId;
|
|
2794
|
-
};
|
|
2795
|
-
const resolveBoardEdgeId = (boardBaseId, spaceIds) => {
|
|
2796
|
-
const edgeId = edgeIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds));
|
|
2797
|
-
if (!edgeId) {
|
|
2798
|
-
throw new Error(
|
|
2799
|
-
`Unknown edge on board '${boardBaseId}' for spaces [${spaceIds.join(", ")}].`
|
|
2800
|
-
);
|
|
2801
|
-
}
|
|
2802
|
-
return edgeId;
|
|
2803
|
-
};
|
|
2804
|
-
const resolveBoardVertexId = (boardBaseId, spaceIds) => {
|
|
2805
|
-
const vertexId = vertexIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds));
|
|
2806
|
-
if (!vertexId) {
|
|
2807
|
-
throw new Error(
|
|
2808
|
-
`Unknown vertex on board '${boardBaseId}' for spaces [${spaceIds.join(", ")}].`
|
|
2809
|
-
);
|
|
2810
|
-
}
|
|
2811
|
-
return vertexId;
|
|
2812
|
-
};
|
|
2813
|
-
const materializeComponentLocation = (home, context) => {
|
|
2814
|
-
if (home.type === "detached") {
|
|
2815
|
-
return { type: "Detached" };
|
|
2816
|
-
}
|
|
2817
|
-
if (home.type === "slot") {
|
|
2818
|
-
return {
|
|
2819
|
-
type: "InSlot",
|
|
2820
|
-
host: home.host,
|
|
2821
|
-
slotId: home.slotId,
|
|
2822
|
-
position: nextLocationPosition({
|
|
2823
|
-
type: "InSlot",
|
|
2824
|
-
host: home.host,
|
|
2825
|
-
slotId: home.slotId
|
|
2826
|
-
})
|
|
2827
|
-
};
|
|
2828
|
-
}
|
|
2829
|
-
if (home.type === "zone") {
|
|
2830
|
-
const zoneScope = zoneScopeById.get(home.zoneId);
|
|
2831
|
-
if (!zoneScope) {
|
|
2832
|
-
throw new Error(
|
|
2833
|
-
`${context.path}.zoneId: ${context.label} targets unknown zone '${home.zoneId}'.`
|
|
2834
|
-
);
|
|
2835
|
-
}
|
|
2836
|
-
if (zoneScope === "perPlayer") {
|
|
2837
|
-
throw new Error(
|
|
2838
|
-
`${context.path}.zoneId: ${context.label} cannot target per-player zone '${home.zoneId}' because card inventory has no ownerId. Place it during reducer setup instead.`
|
|
2839
|
-
);
|
|
2840
|
-
}
|
|
2841
|
-
return {
|
|
2842
|
-
type: "InDeck",
|
|
2843
|
-
deckId: home.zoneId,
|
|
2844
|
-
playedBy: null,
|
|
2845
|
-
position: nextLocationPosition({
|
|
2846
|
-
type: "InDeck",
|
|
2847
|
-
deckId: home.zoneId,
|
|
2848
|
-
playedBy: null
|
|
2849
|
-
})
|
|
2850
|
-
};
|
|
2851
|
-
}
|
|
2852
|
-
if (home.type === "space") {
|
|
2853
|
-
const boardId = resolveRuntimeBoardId(
|
|
2854
|
-
home.boardId,
|
|
2855
|
-
null,
|
|
2856
|
-
`${context.path}.boardId: ${context.label}`
|
|
2857
|
-
);
|
|
2858
|
-
if (!analysis.spaceIdsByBoardId.get(home.boardId)?.includes(home.spaceId)) {
|
|
2859
|
-
throw new Error(
|
|
2860
|
-
`${context.path}.spaceId: ${context.label} targets unknown space '${home.spaceId}' on board '${home.boardId}'.`
|
|
2861
|
-
);
|
|
2862
|
-
}
|
|
2863
|
-
return {
|
|
2864
|
-
type: "OnSpace",
|
|
2865
|
-
boardId,
|
|
2866
|
-
spaceId: home.spaceId,
|
|
2867
|
-
position: nextLocationPosition({
|
|
2868
|
-
type: "OnSpace",
|
|
2869
|
-
boardId,
|
|
2870
|
-
spaceId: home.spaceId
|
|
2871
|
-
})
|
|
2872
|
-
};
|
|
2873
|
-
}
|
|
2874
|
-
if (home.type === "container") {
|
|
2875
|
-
const boardId = resolveRuntimeBoardId(
|
|
2876
|
-
home.boardId,
|
|
2877
|
-
null,
|
|
2878
|
-
`${context.path}.boardId: ${context.label}`
|
|
2879
|
-
);
|
|
2880
|
-
if (!analysis.containerIdsByBoardId.get(home.boardId)?.includes(home.containerId)) {
|
|
2881
|
-
throw new Error(
|
|
2882
|
-
`${context.path}.containerId: ${context.label} targets unknown container '${home.containerId}' on board '${home.boardId}'.`
|
|
2883
|
-
);
|
|
2884
|
-
}
|
|
2885
|
-
return {
|
|
2886
|
-
type: "InContainer",
|
|
2887
|
-
boardId,
|
|
2888
|
-
containerId: home.containerId,
|
|
2889
|
-
position: nextLocationPosition({
|
|
2890
|
-
type: "InContainer",
|
|
2891
|
-
boardId,
|
|
2892
|
-
containerId: home.containerId
|
|
2893
|
-
})
|
|
2894
|
-
};
|
|
2895
|
-
}
|
|
2896
|
-
if (home.type === "edge") {
|
|
2897
|
-
const boardId = resolveRuntimeBoardId(
|
|
2898
|
-
home.boardId,
|
|
2899
|
-
null,
|
|
2900
|
-
`${context.path}.boardId: ${context.label}`
|
|
2901
|
-
);
|
|
2902
|
-
const edgeId = resolveBoardEdgeId(home.boardId, home.ref.spaces);
|
|
2903
|
-
return {
|
|
2904
|
-
type: "OnEdge",
|
|
2905
|
-
boardId,
|
|
2906
|
-
edgeId,
|
|
2907
|
-
position: nextLocationPosition({
|
|
2908
|
-
type: "OnEdge",
|
|
2909
|
-
boardId,
|
|
2910
|
-
edgeId
|
|
2911
|
-
})
|
|
2912
|
-
};
|
|
2913
|
-
}
|
|
2914
|
-
if (home.type === "vertex") {
|
|
2915
|
-
const boardId = resolveRuntimeBoardId(
|
|
2916
|
-
home.boardId,
|
|
2917
|
-
null,
|
|
2918
|
-
`${context.path}.boardId: ${context.label}`
|
|
2919
|
-
);
|
|
2920
|
-
const vertexId = resolveBoardVertexId(home.boardId, home.ref.spaces);
|
|
2921
|
-
return {
|
|
2922
|
-
type: "OnVertex",
|
|
2923
|
-
boardId,
|
|
2924
|
-
vertexId,
|
|
2925
|
-
position: nextLocationPosition({
|
|
2926
|
-
type: "OnVertex",
|
|
2927
|
-
boardId,
|
|
2928
|
-
vertexId
|
|
2929
|
-
})
|
|
2930
|
-
};
|
|
2931
|
-
}
|
|
2932
|
-
const exhaustive = home;
|
|
2933
|
-
throw new Error(
|
|
2934
|
-
`Unsupported component home: ${JSON.stringify(exhaustive)}`
|
|
2935
|
-
);
|
|
2936
|
-
};
|
|
2937
|
-
for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
|
|
2938
|
-
const materializedCardSet = materializeCardSet(cardSet);
|
|
2939
|
-
for (const [cardIndex, card] of materializedCardSet.cards.entries()) {
|
|
2940
|
-
const cardInstanceIds = renderCardInstanceIds2(card);
|
|
2941
|
-
for (const [instanceIndex, cardId] of cardInstanceIds.entries()) {
|
|
2942
|
-
cards[cardId] = {
|
|
2943
|
-
id: cardId,
|
|
2944
|
-
cardSetId: materializedCardSet.id,
|
|
2945
|
-
cardType: card.type,
|
|
2946
|
-
name: card.name,
|
|
2947
|
-
text: card.text,
|
|
2948
|
-
properties: {
|
|
2949
|
-
...materializeCardPropertiesDefaults(
|
|
2950
|
-
materializedCardSet.cardSchema,
|
|
2951
|
-
card.type,
|
|
2952
|
-
analysis
|
|
2953
|
-
),
|
|
2954
|
-
...card.properties ?? {}
|
|
2955
|
-
}
|
|
2956
|
-
};
|
|
2957
|
-
const resolvedHome = card.home ?? materializedCardSet.defaultHome;
|
|
2958
|
-
const path = card.home === void 0 ? `manifest.cardSets[${cardSetIndex}].defaultHome` : `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`;
|
|
2959
|
-
componentLocations[cardId] = materializeComponentLocation(
|
|
2960
|
-
resolvedHome,
|
|
2961
|
-
{
|
|
2962
|
-
path,
|
|
2963
|
-
label: `Card '${card.type}' instance ${instanceIndex + 1}`
|
|
2964
|
-
}
|
|
2965
|
-
);
|
|
2966
|
-
}
|
|
2967
|
-
}
|
|
2968
|
-
}
|
|
2969
|
-
if (Object.keys(cards).length !== analysis.cardIds.length) {
|
|
2970
|
-
throw new Error(
|
|
2971
|
-
"Materialized card record cardinality drifted from analysis."
|
|
2972
|
-
);
|
|
2973
|
-
}
|
|
2974
|
-
if (Object.keys(componentLocations).length !== analysis.cardIds.length) {
|
|
2975
|
-
throw new Error(
|
|
2976
|
-
"Materialized card locations drifted from analysis before seed placement."
|
|
2977
|
-
);
|
|
2978
|
-
}
|
|
2979
|
-
const pieceTypesById = new Map(
|
|
2980
|
-
(manifest.pieceTypes ?? []).map((pieceType) => [pieceType.id, pieceType])
|
|
2981
|
-
);
|
|
2982
|
-
const dieTypesById = new Map(
|
|
2983
|
-
(manifest.dieTypes ?? []).map((dieType) => [dieType.id, dieType])
|
|
2984
|
-
);
|
|
2985
|
-
const assignSeedLocation = (componentId, ownerId, home, context) => {
|
|
2986
|
-
if (!home) {
|
|
2987
|
-
componentLocations[componentId] = { type: "Detached" };
|
|
2988
|
-
return;
|
|
2989
|
-
}
|
|
2990
|
-
if (home.type === "slot") {
|
|
2991
|
-
componentLocations[componentId] = {
|
|
2992
|
-
type: "InSlot",
|
|
2993
|
-
host: home.host,
|
|
2994
|
-
slotId: home.slotId,
|
|
2995
|
-
position: nextLocationPosition({
|
|
2996
|
-
type: "InSlot",
|
|
2997
|
-
host: home.host,
|
|
2998
|
-
slotId: home.slotId
|
|
2999
|
-
})
|
|
3000
|
-
};
|
|
3001
|
-
return;
|
|
3002
|
-
}
|
|
3003
|
-
if (home.type === "zone") {
|
|
3004
|
-
if (zoneScopeById.get(home.zoneId) === "perPlayer") {
|
|
3005
|
-
if (!ownerId) {
|
|
3006
|
-
throw new Error(
|
|
3007
|
-
`${context.path}.zoneId: ${context.label} requires ownerId because zone '${home.zoneId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
|
|
3008
|
-
);
|
|
3009
|
-
}
|
|
3010
|
-
componentLocations[componentId] = {
|
|
3011
|
-
type: "InHand",
|
|
3012
|
-
handId: home.zoneId,
|
|
3013
|
-
playerId: ownerId,
|
|
3014
|
-
position: nextLocationPosition({
|
|
3015
|
-
type: "InHand",
|
|
3016
|
-
handId: home.zoneId,
|
|
3017
|
-
playerId: ownerId
|
|
3018
|
-
})
|
|
3019
|
-
};
|
|
3020
|
-
return;
|
|
3021
|
-
}
|
|
3022
|
-
componentLocations[componentId] = {
|
|
3023
|
-
type: "InZone",
|
|
3024
|
-
zoneId: home.zoneId,
|
|
3025
|
-
playedBy: null,
|
|
3026
|
-
position: nextLocationPosition({
|
|
3027
|
-
type: "InZone",
|
|
3028
|
-
zoneId: home.zoneId
|
|
3029
|
-
})
|
|
3030
|
-
};
|
|
3031
|
-
return;
|
|
3032
|
-
}
|
|
3033
|
-
if (home.type === "space") {
|
|
3034
|
-
const boardId = resolveRuntimeBoardId(
|
|
3035
|
-
home.boardId,
|
|
3036
|
-
ownerId,
|
|
3037
|
-
`${context.path}.boardId: ${context.label}`
|
|
3038
|
-
);
|
|
3039
|
-
componentLocations[componentId] = {
|
|
3040
|
-
type: "OnSpace",
|
|
3041
|
-
boardId,
|
|
3042
|
-
spaceId: home.spaceId,
|
|
3043
|
-
position: nextLocationPosition({
|
|
3044
|
-
type: "OnSpace",
|
|
3045
|
-
boardId,
|
|
3046
|
-
spaceId: home.spaceId
|
|
3047
|
-
})
|
|
3048
|
-
};
|
|
3049
|
-
return;
|
|
3050
|
-
}
|
|
3051
|
-
if (home.type === "container") {
|
|
3052
|
-
const boardId = resolveRuntimeBoardId(
|
|
3053
|
-
home.boardId,
|
|
3054
|
-
ownerId,
|
|
3055
|
-
`${context.path}.boardId: ${context.label}`
|
|
3056
|
-
);
|
|
3057
|
-
componentLocations[componentId] = {
|
|
3058
|
-
type: "InContainer",
|
|
3059
|
-
boardId,
|
|
3060
|
-
containerId: home.containerId,
|
|
3061
|
-
position: nextLocationPosition({
|
|
3062
|
-
type: "InContainer",
|
|
3063
|
-
boardId,
|
|
3064
|
-
containerId: home.containerId
|
|
3065
|
-
})
|
|
3066
|
-
};
|
|
3067
|
-
return;
|
|
3068
|
-
}
|
|
3069
|
-
if (home.type === "edge") {
|
|
3070
|
-
const boardId = resolveRuntimeBoardId(
|
|
3071
|
-
home.boardId,
|
|
3072
|
-
ownerId,
|
|
3073
|
-
`${context.path}.boardId: ${context.label}`
|
|
3074
|
-
);
|
|
3075
|
-
const edgeId = resolveBoardEdgeId(home.boardId, home.ref.spaces);
|
|
3076
|
-
componentLocations[componentId] = {
|
|
3077
|
-
type: "OnEdge",
|
|
3078
|
-
boardId,
|
|
3079
|
-
edgeId,
|
|
3080
|
-
position: nextLocationPosition({
|
|
3081
|
-
type: "OnEdge",
|
|
3082
|
-
boardId,
|
|
3083
|
-
edgeId
|
|
3084
|
-
})
|
|
3085
|
-
};
|
|
3086
|
-
return;
|
|
3087
|
-
}
|
|
3088
|
-
if (home.type === "vertex") {
|
|
3089
|
-
const boardId = resolveRuntimeBoardId(
|
|
3090
|
-
home.boardId,
|
|
3091
|
-
ownerId,
|
|
3092
|
-
`${context.path}.boardId: ${context.label}`
|
|
3093
|
-
);
|
|
3094
|
-
const vertexId = resolveBoardVertexId(home.boardId, home.ref.spaces);
|
|
3095
|
-
componentLocations[componentId] = {
|
|
3096
|
-
type: "OnVertex",
|
|
3097
|
-
boardId,
|
|
3098
|
-
vertexId,
|
|
3099
|
-
position: nextLocationPosition({
|
|
3100
|
-
type: "OnVertex",
|
|
3101
|
-
boardId,
|
|
3102
|
-
vertexId
|
|
3103
|
-
})
|
|
3104
|
-
};
|
|
3105
|
-
return;
|
|
3106
|
-
}
|
|
3107
|
-
componentLocations[componentId] = { type: "Detached" };
|
|
3108
|
-
};
|
|
3109
|
-
for (const [seedIndex, seed] of (manifest.pieceSeeds ?? []).entries()) {
|
|
3110
|
-
const pieceType = pieceTypesById.get(seed.typeId);
|
|
3111
|
-
for (const componentId of expandSeedIds2([seed])) {
|
|
3112
|
-
pieces[componentId] = {
|
|
3113
|
-
componentType: "piece",
|
|
3114
|
-
id: componentId,
|
|
3115
|
-
pieceTypeId: seed.typeId,
|
|
3116
|
-
pieceName: pieceType?.name ?? seed.typeId,
|
|
3117
|
-
ownerId: seed.ownerId ?? null,
|
|
3118
|
-
properties: {
|
|
3119
|
-
...materializeObjectSchemaDefaults(
|
|
3120
|
-
analysis.pieceTypeSchemasById.get(seed.typeId),
|
|
3121
|
-
analysis
|
|
3122
|
-
),
|
|
3123
|
-
...seed.fields ?? {}
|
|
3124
|
-
}
|
|
3125
|
-
};
|
|
3126
|
-
assignSeedLocation(componentId, seed.ownerId, seed.home, {
|
|
3127
|
-
path: `manifest.pieceSeeds[${seedIndex}].home`,
|
|
3128
|
-
label: `Piece seed '${seed.id ?? seed.typeId}'`
|
|
3129
|
-
});
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
|
-
for (const [seedIndex, seed] of (manifest.dieSeeds ?? []).entries()) {
|
|
3133
|
-
const dieType = dieTypesById.get(seed.typeId);
|
|
3134
|
-
for (const componentId of expandSeedIds2([seed])) {
|
|
3135
|
-
dice[componentId] = {
|
|
3136
|
-
componentType: "die",
|
|
3137
|
-
id: componentId,
|
|
3138
|
-
dieTypeId: seed.typeId,
|
|
3139
|
-
dieName: dieType?.name ?? seed.typeId,
|
|
3140
|
-
ownerId: seed.ownerId ?? null,
|
|
3141
|
-
sides: dieType?.sides ?? 6,
|
|
3142
|
-
value: null,
|
|
3143
|
-
properties: {
|
|
3144
|
-
...materializeObjectSchemaDefaults(
|
|
3145
|
-
analysis.dieTypeSchemasById.get(seed.typeId),
|
|
3146
|
-
analysis
|
|
3147
|
-
),
|
|
3148
|
-
...seed.fields ?? {}
|
|
3149
|
-
}
|
|
3150
|
-
};
|
|
3151
|
-
assignSeedLocation(componentId, seed.ownerId, seed.home, {
|
|
3152
|
-
path: `manifest.dieSeeds[${seedIndex}].home`,
|
|
3153
|
-
label: `Die seed '${seed.id ?? seed.typeId}'`
|
|
3154
|
-
});
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
|
-
const boardStatesById = createRecord();
|
|
3158
|
-
const hexBoardStatesById = createRecord();
|
|
3159
|
-
const squareBoardStatesById = createRecord();
|
|
3160
|
-
for (const analyzedBoard of analysis.analyzedBoards) {
|
|
3161
|
-
const sharedBoardState = {
|
|
3162
|
-
baseId: analyzedBoard.board.id,
|
|
3163
|
-
layout: analyzedBoard.layout,
|
|
3164
|
-
typeId: analyzedBoard.boardTypeId ?? null,
|
|
3165
|
-
scope: analyzedBoard.board.scope,
|
|
3166
|
-
templateId: analyzedBoard.board.templateId ?? null,
|
|
3167
|
-
fields: {
|
|
3168
|
-
...materializeObjectSchemaDefaults(
|
|
3169
|
-
analyzedBoard.boardFieldsSchema,
|
|
3170
|
-
analysis
|
|
3171
|
-
),
|
|
3172
|
-
...analyzedBoard.board.fields ?? {}
|
|
3173
|
-
}
|
|
3174
|
-
};
|
|
3175
|
-
const buildSpaces = () => Object.fromEntries(
|
|
3176
|
-
analyzedBoard.spaces.map((space) => {
|
|
3177
|
-
const spaceId = space.id;
|
|
3178
|
-
const baseSpaceState = {
|
|
3179
|
-
id: spaceId,
|
|
3180
|
-
name: "name" in space ? space.name ?? null : null,
|
|
3181
|
-
typeId: space?.typeId ?? null,
|
|
3182
|
-
fields: {
|
|
3183
|
-
...materializeObjectSchemaDefaults(
|
|
3184
|
-
analyzedBoard.spaceFieldsSchema,
|
|
3185
|
-
analysis
|
|
3186
|
-
),
|
|
3187
|
-
...space.fields ?? {}
|
|
3188
|
-
},
|
|
3189
|
-
zoneId: "zoneId" in space ? space.zoneId ?? null : null
|
|
3190
|
-
};
|
|
3191
|
-
if (analyzedBoard.layout === "hex") {
|
|
3192
|
-
const hexSpace = space;
|
|
3193
|
-
return [
|
|
3194
|
-
spaceId,
|
|
3195
|
-
{
|
|
3196
|
-
...baseSpaceState,
|
|
3197
|
-
q: hexSpace.q,
|
|
3198
|
-
r: hexSpace.r
|
|
3199
|
-
}
|
|
3200
|
-
];
|
|
3201
|
-
}
|
|
3202
|
-
if (analyzedBoard.layout === "square") {
|
|
3203
|
-
const squareSpace = space;
|
|
3204
|
-
return [
|
|
3205
|
-
spaceId,
|
|
3206
|
-
{
|
|
3207
|
-
...baseSpaceState,
|
|
3208
|
-
row: squareSpace.row,
|
|
3209
|
-
col: squareSpace.col
|
|
3210
|
-
}
|
|
3211
|
-
];
|
|
3212
|
-
}
|
|
3213
|
-
return [spaceId, baseSpaceState];
|
|
3214
|
-
})
|
|
3215
|
-
);
|
|
3216
|
-
const relations = analyzedBoard.layout === "hex" ? [] : analyzedBoard.relations.map((relation) => ({
|
|
3217
|
-
id: relation.id ?? null,
|
|
3218
|
-
typeId: relation.typeId,
|
|
3219
|
-
fromSpaceId: relation.fromSpaceId,
|
|
3220
|
-
toSpaceId: relation.toSpaceId,
|
|
3221
|
-
directed: relation.directed ?? false,
|
|
3222
|
-
fields: {
|
|
3223
|
-
...materializeObjectSchemaDefaults(
|
|
3224
|
-
analyzedBoard.relationFieldsSchema,
|
|
3225
|
-
analysis
|
|
3226
|
-
),
|
|
3227
|
-
...relation.fields ?? {}
|
|
3228
|
-
}
|
|
3229
|
-
}));
|
|
3230
|
-
const buildContainers = (runtimeBoardId) => analyzedBoard.layout === "hex" ? {} : Object.fromEntries(
|
|
3231
|
-
analyzedBoard.containers.map((container) => {
|
|
3232
|
-
const containerId = container.id;
|
|
3233
|
-
return [
|
|
3234
|
-
containerId,
|
|
3235
|
-
{
|
|
3236
|
-
id: containerId,
|
|
3237
|
-
name: container.name ?? containerId,
|
|
3238
|
-
host: container.host.type === "space" ? { type: "space", spaceId: container.host.spaceId } : { type: "board" },
|
|
3239
|
-
allowedCardSetIds: container.allowedCardSetIds,
|
|
3240
|
-
zoneId: `board:${runtimeBoardId}:container:${containerId}`,
|
|
3241
|
-
fields: {
|
|
3242
|
-
...materializeObjectSchemaDefaults(
|
|
3243
|
-
analyzedBoard.containerFieldsSchema,
|
|
3244
|
-
analysis,
|
|
3245
|
-
runtimeBoardId
|
|
3246
|
-
),
|
|
3247
|
-
...container.fields ?? {}
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
];
|
|
3251
|
-
})
|
|
3252
|
-
);
|
|
3253
|
-
const edges = analyzedBoard.layout === "generic" ? [] : analyzedBoard.edges.map((edge) => ({
|
|
3254
|
-
id: edge.id,
|
|
3255
|
-
spaceIds: [...edge.spaceIds],
|
|
3256
|
-
typeId: edge.typeId ?? null,
|
|
3257
|
-
label: edge.label ?? null,
|
|
3258
|
-
ownerId: null,
|
|
3259
|
-
fields: {
|
|
3260
|
-
...materializeObjectSchemaDefaults(
|
|
3261
|
-
analyzedBoard.edgeFieldsSchema,
|
|
3262
|
-
analysis
|
|
3263
|
-
),
|
|
3264
|
-
...edge.fields ?? {}
|
|
3265
|
-
}
|
|
3266
|
-
}));
|
|
3267
|
-
const vertices = analyzedBoard.layout === "generic" ? [] : analyzedBoard.vertices.map((vertex) => ({
|
|
3268
|
-
id: vertex.id,
|
|
3269
|
-
spaceIds: [...vertex.spaceIds],
|
|
3270
|
-
typeId: vertex.typeId ?? null,
|
|
3271
|
-
label: vertex.label ?? null,
|
|
3272
|
-
ownerId: null,
|
|
3273
|
-
fields: {
|
|
3274
|
-
...materializeObjectSchemaDefaults(
|
|
3275
|
-
analyzedBoard.vertexFieldsSchema,
|
|
3276
|
-
analysis
|
|
3277
|
-
),
|
|
3278
|
-
...vertex.fields ?? {}
|
|
3279
|
-
}
|
|
3280
|
-
}));
|
|
3281
|
-
for (const runtimeBoardId of analyzedBoard.runtimeBoardIds) {
|
|
3282
|
-
const playerId = analyzedBoard.board.scope === "perPlayer" ? runtimeBoardId.slice(analyzedBoard.board.id.length + 1) : null;
|
|
3283
|
-
const boardState = {
|
|
3284
|
-
id: runtimeBoardId,
|
|
3285
|
-
...sharedBoardState,
|
|
3286
|
-
playerId,
|
|
3287
|
-
spaces: cloneJson(buildSpaces()),
|
|
3288
|
-
relations: cloneJson(relations),
|
|
3289
|
-
containers: cloneJson(buildContainers(runtimeBoardId)),
|
|
3290
|
-
...analyzedBoard.layout === "hex" ? {
|
|
3291
|
-
orientation: analyzedBoard.board.orientation ?? "pointy-top",
|
|
3292
|
-
edges: cloneJson(edges),
|
|
3293
|
-
vertices: cloneJson(vertices)
|
|
3294
|
-
} : analyzedBoard.layout === "square" ? {
|
|
3295
|
-
edges: cloneJson(edges),
|
|
3296
|
-
vertices: cloneJson(vertices)
|
|
3297
|
-
} : {}
|
|
3298
|
-
};
|
|
3299
|
-
boardStatesById[runtimeBoardId] = boardState;
|
|
3300
|
-
if (analyzedBoard.layout === "hex") {
|
|
3301
|
-
hexBoardStatesById[runtimeBoardId] = boardState;
|
|
3302
|
-
}
|
|
3303
|
-
if (analyzedBoard.layout === "square") {
|
|
3304
|
-
squareBoardStatesById[runtimeBoardId] = boardState;
|
|
3305
|
-
}
|
|
3306
|
-
}
|
|
3307
|
-
}
|
|
3308
|
-
const sharedZones = Object.fromEntries(
|
|
3309
|
-
analysis.sharedZones.map((zone) => [zone.id, []])
|
|
3310
|
-
);
|
|
3311
|
-
const perPlayerZones = Object.fromEntries(
|
|
3312
|
-
analysis.playerZones.map((zone) => [
|
|
3313
|
-
zone.id,
|
|
3314
|
-
Object.fromEntries(
|
|
3315
|
-
playerIds.map((playerId) => [playerId, []])
|
|
3316
|
-
)
|
|
3317
|
-
])
|
|
3318
|
-
);
|
|
3319
|
-
const zoneVisibility = Object.fromEntries(
|
|
3320
|
-
(manifest.zones ?? []).map((zone) => [
|
|
3321
|
-
zone.id,
|
|
3322
|
-
zone.visibility ?? "public"
|
|
3323
|
-
])
|
|
3324
|
-
);
|
|
3325
|
-
const zoneCardSetIdsByZoneId = Object.fromEntries(
|
|
3326
|
-
Array.from(analysis.zoneCardSetIdsById.entries()).sort(
|
|
3327
|
-
([left], [right]) => left.localeCompare(right)
|
|
3328
|
-
)
|
|
3329
|
-
);
|
|
3330
|
-
const handVisibility = Object.fromEntries(
|
|
3331
|
-
analysis.playerZones.map((zone) => [
|
|
3332
|
-
zone.id,
|
|
3333
|
-
zone.visibility ?? "ownerOnly"
|
|
3334
|
-
])
|
|
3335
|
-
);
|
|
3336
|
-
const componentSortPosition = (position) => typeof position === "number" ? position : Number.MAX_SAFE_INTEGER;
|
|
3337
|
-
const pushSharedComponent = (zoneId, componentId) => {
|
|
3338
|
-
const zone = sharedZones[zoneId];
|
|
3339
|
-
if (!zone) {
|
|
3340
|
-
return;
|
|
3341
|
-
}
|
|
3342
|
-
zone.push(componentId);
|
|
3343
|
-
zone.sort(
|
|
3344
|
-
(left, right) => componentSortPosition(componentLocations[left]?.position) - componentSortPosition(componentLocations[right]?.position)
|
|
3345
|
-
);
|
|
3346
|
-
};
|
|
3347
|
-
const pushPlayerComponent = (zoneId, playerId, componentId) => {
|
|
3348
|
-
const zone = perPlayerZones[zoneId]?.[playerId];
|
|
3349
|
-
if (!zone) {
|
|
3350
|
-
return;
|
|
3351
|
-
}
|
|
3352
|
-
zone.push(componentId);
|
|
3353
|
-
zone.sort(
|
|
3354
|
-
(left, right) => componentSortPosition(componentLocations[left]?.position) - componentSortPosition(componentLocations[right]?.position)
|
|
3355
|
-
);
|
|
3356
|
-
};
|
|
3357
|
-
for (const [componentId, location] of Object.entries(componentLocations)) {
|
|
3358
|
-
switch (location.type) {
|
|
3359
|
-
case "InDeck":
|
|
3360
|
-
pushSharedComponent(location.deckId, componentId);
|
|
3361
|
-
break;
|
|
3362
|
-
case "InHand":
|
|
3363
|
-
pushPlayerComponent(
|
|
3364
|
-
location.handId,
|
|
3365
|
-
location.playerId,
|
|
3366
|
-
componentId
|
|
3367
|
-
);
|
|
3368
|
-
break;
|
|
3369
|
-
case "InZone":
|
|
3370
|
-
pushSharedComponent(location.zoneId, componentId);
|
|
3371
|
-
break;
|
|
3372
|
-
default:
|
|
3373
|
-
break;
|
|
3374
|
-
}
|
|
3375
|
-
}
|
|
3376
|
-
const ownerOfCard = Object.fromEntries(
|
|
3377
|
-
Object.keys(cards).map((cardId) => [cardId, null])
|
|
3378
|
-
);
|
|
3379
|
-
const visibility = Object.fromEntries(
|
|
3380
|
-
Object.keys(cards).map((cardId) => [cardId, { faceUp: true }])
|
|
3381
|
-
);
|
|
3382
|
-
const resourcesByPlayer = Object.fromEntries(
|
|
3383
|
-
playerIds.map((playerId) => [
|
|
3384
|
-
playerId,
|
|
3385
|
-
Object.fromEntries(
|
|
3386
|
-
analysis.resourceIds.map((resourceId) => [resourceId, 0])
|
|
3387
|
-
)
|
|
3388
|
-
])
|
|
3389
|
-
);
|
|
3390
|
-
const toPerPlayerWireFormat = (entries) => ({
|
|
3391
|
-
__perPlayer: true,
|
|
3392
|
-
entries: playerIds.map(
|
|
3393
|
-
(playerId) => [playerId, entries[playerId]]
|
|
3394
|
-
)
|
|
3395
|
-
});
|
|
3396
|
-
const perPlayerZonesWire = Object.fromEntries(
|
|
3397
|
-
Object.entries(perPlayerZones).map(([zoneId, entries]) => [
|
|
3398
|
-
zoneId,
|
|
3399
|
-
toPerPlayerWireFormat(entries)
|
|
3400
|
-
])
|
|
3401
|
-
);
|
|
3402
|
-
const resourcesWire = toPerPlayerWireFormat(resourcesByPlayer);
|
|
3403
|
-
return cloneJson({
|
|
3404
|
-
playerOrder: playerIds,
|
|
3405
|
-
zones: {
|
|
3406
|
-
shared: sharedZones,
|
|
3407
|
-
perPlayer: perPlayerZonesWire,
|
|
3408
|
-
visibility: zoneVisibility,
|
|
3409
|
-
cardSetIdsByZoneId: zoneCardSetIdsByZoneId
|
|
3410
|
-
},
|
|
3411
|
-
decks: cloneJson(sharedZones),
|
|
3412
|
-
hands: cloneJson(perPlayerZonesWire),
|
|
3413
|
-
handVisibility,
|
|
3414
|
-
cards,
|
|
3415
|
-
pieces,
|
|
3416
|
-
componentLocations,
|
|
3417
|
-
ownerOfCard,
|
|
3418
|
-
visibility,
|
|
3419
|
-
resources: resourcesWire,
|
|
3420
|
-
boards: {
|
|
3421
|
-
byId: boardStatesById,
|
|
3422
|
-
hex: hexBoardStatesById,
|
|
3423
|
-
square: squareBoardStatesById,
|
|
3424
|
-
network: {},
|
|
3425
|
-
track: {}
|
|
3426
|
-
},
|
|
3427
|
-
dice
|
|
3428
|
-
});
|
|
3429
|
-
}
|
|
3430
|
-
|
|
3431
|
-
export {
|
|
3432
|
-
analyzeManifest,
|
|
3433
|
-
materializeManifestTable
|
|
3434
|
-
};
|
|
3435
|
-
//# sourceMappingURL=chunk-Z7QCREWI.js.map
|