@datamoc/mw_games 0.5.0 → 0.5.2

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.
Files changed (52) hide show
  1. package/dist/actors/EntityTemplate.d.ts +127 -0
  2. package/dist/actors/EntityTemplate.d.ts.map +1 -0
  3. package/dist/actors/EntityTemplate.js +117 -0
  4. package/dist/actors/EntityTemplate.js.map +1 -0
  5. package/dist/actors/index.d.ts +2 -0
  6. package/dist/actors/index.d.ts.map +1 -1
  7. package/dist/actors/index.js +1 -0
  8. package/dist/actors/index.js.map +1 -1
  9. package/dist/board/HexSkirmish.d.ts +213 -0
  10. package/dist/board/HexSkirmish.d.ts.map +1 -0
  11. package/dist/board/HexSkirmish.js +271 -0
  12. package/dist/board/HexSkirmish.js.map +1 -0
  13. package/dist/board/index.d.ts +2 -0
  14. package/dist/board/index.d.ts.map +1 -1
  15. package/dist/board/index.js +1 -0
  16. package/dist/board/index.js.map +1 -1
  17. package/dist/core/Csv.d.ts +64 -0
  18. package/dist/core/Csv.d.ts.map +1 -0
  19. package/dist/core/Csv.js +164 -0
  20. package/dist/core/Csv.js.map +1 -0
  21. package/dist/core/Registry.d.ts +31 -0
  22. package/dist/core/Registry.d.ts.map +1 -0
  23. package/dist/core/Registry.js +46 -0
  24. package/dist/core/Registry.js.map +1 -0
  25. package/dist/core/SceneComponents.d.ts +67 -0
  26. package/dist/core/SceneComponents.d.ts.map +1 -0
  27. package/dist/core/SceneComponents.js +76 -0
  28. package/dist/core/SceneComponents.js.map +1 -0
  29. package/dist/core/index.d.ts +5 -0
  30. package/dist/core/index.d.ts.map +1 -1
  31. package/dist/core/index.js +3 -0
  32. package/dist/core/index.js.map +1 -1
  33. package/dist/mw_games.global.js +91 -90
  34. package/dist/rpg/Quest.d.ts +45 -0
  35. package/dist/rpg/Quest.d.ts.map +1 -1
  36. package/dist/rpg/Quest.js +76 -0
  37. package/dist/rpg/Quest.js.map +1 -1
  38. package/dist/rpg/index.d.ts +2 -2
  39. package/dist/rpg/index.d.ts.map +1 -1
  40. package/dist/rpg/index.js +1 -1
  41. package/dist/rpg/index.js.map +1 -1
  42. package/dist/two-d/render/Shape2D.d.ts +23 -1
  43. package/dist/two-d/render/Shape2D.d.ts.map +1 -1
  44. package/dist/two-d/render/Shape2D.js +23 -1
  45. package/dist/two-d/render/Shape2D.js.map +1 -1
  46. package/dist/two-d/render/index.d.ts +1 -1
  47. package/dist/two-d/render/index.d.ts.map +1 -1
  48. package/dist/two-d/render/index.js +1 -1
  49. package/dist/two-d/render/index.js.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,271 @@
1
+ import { hexNeighbors, hexDistance } from '../core/Hex.js';
2
+ import * as Random from '../core/Random.js';
3
+ /**
4
+ * @example
5
+ * ```ts
6
+ * import { startingSkirmish } from '@datamoc/mw_games/board';
7
+ *
8
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
9
+ * console.log(state.width, state.round); // 6 1
10
+ * ```
11
+ */
12
+ export function startingSkirmish(width, height, terrainTable, defaultTerrain) {
13
+ if (width < 1 || height < 1)
14
+ throw new Error('a skirmish map needs positive dimensions');
15
+ const terrain = defaultTerrain ?? Object.keys(terrainTable)[0];
16
+ if (!terrain || !terrainTable[terrain])
17
+ throw new Error('a skirmish map needs at least one terrain kind');
18
+ return {
19
+ width,
20
+ height,
21
+ terrainTable,
22
+ cells: Array.from({ length: width * height }, () => ({ terrain })),
23
+ units: [],
24
+ turn: '',
25
+ round: 1,
26
+ };
27
+ }
28
+ /**
29
+ * @example
30
+ * ```ts
31
+ * import { startingSkirmish, setSkirmishTerrain } from '@datamoc/mw_games/board';
32
+ *
33
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 }, forest: { moveCost: 2, defense: 0.3 } });
34
+ * setSkirmishTerrain(state, 2, 2, 'forest', true);
35
+ * console.log(state.cells[2 * 6 + 2]); // { terrain: 'forest', village: true }
36
+ * ```
37
+ */
38
+ export function setSkirmishTerrain(state, x, y, terrain, village = false) {
39
+ if (!inside(state, x, y))
40
+ throw new Error('cell is outside the skirmish map');
41
+ if (!state.terrainTable[terrain])
42
+ throw new Error(`unknown skirmish terrain "${terrain}"`);
43
+ state.cells[y * state.width + x] = village ? { terrain, village: true } : { terrain };
44
+ }
45
+ /**
46
+ * whether a unit could be placed at (x, y) right now - on the map, passable, and unoccupied
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import { startingSkirmish, addSkirmishUnit, canPlaceSkirmishUnit } from '@datamoc/mw_games/board';
51
+ *
52
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
53
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 0.7 });
54
+ *
55
+ * console.log(canPlaceSkirmishUnit(state, 1, 0)); // true
56
+ * console.log(canPlaceSkirmishUnit(state, 0, 0)); // false - axeman is already there
57
+ * ```
58
+ */
59
+ export function canPlaceSkirmishUnit(state, x, y) {
60
+ return inside(state, x, y) && moveCostOf(state, x, y) < Infinity && !occupied(state, x, y);
61
+ }
62
+ /**
63
+ * Places a unit at full remaining movement; the first unit added decides whose turn it is.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * import { startingSkirmish, addSkirmishUnit } from '@datamoc/mw_games/board';
68
+ *
69
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
70
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 0.7 });
71
+ * console.log(state.turn); // 'blue'
72
+ * ```
73
+ */
74
+ export function addSkirmishUnit(state, unit) {
75
+ if (!canPlaceSkirmishUnit(state, unit.x, unit.y))
76
+ throw new Error('skirmish unit cannot be placed there');
77
+ state.units.push({ ...unit, remainingMoves: unit.moves });
78
+ if (!state.turn)
79
+ state.turn = unit.owner;
80
+ }
81
+ /**
82
+ * Every cell a unit can reach with its remaining movement points this turn, weighted by each
83
+ * terrain kind's own `moveCost` - no zone of control, unlike `board.Tactics`.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * import { startingSkirmish, addSkirmishUnit, skirmishMoves } from '@datamoc/mw_games/board';
88
+ *
89
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
90
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 2, attack: 4, hitChance: 0.7 });
91
+ *
92
+ * console.log(skirmishMoves(state, 'axeman').length); // 6 - every hex within 2 plain steps
93
+ * ```
94
+ */
95
+ export function skirmishMoves(state, unitId) {
96
+ const unit = getUnit(state, unitId);
97
+ const budget = unit.remainingMoves ?? unit.moves;
98
+ if (unit.owner !== state.turn || budget <= 0)
99
+ return [];
100
+ const out = [];
101
+ for (let y = 0; y < state.height; y++)
102
+ for (let x = 0; x < state.width; x++) {
103
+ if ((x === unit.x && y === unit.y) || moveCostOf(state, x, y) === Infinity || occupied(state, x, y))
104
+ continue;
105
+ const cost = pathCost(state, unit, x, y, budget);
106
+ if (cost !== null)
107
+ out.push({ unit: unitId, x, y, cost });
108
+ }
109
+ return out;
110
+ }
111
+ /**
112
+ * @example
113
+ * ```ts
114
+ * import { startingSkirmish, addSkirmishUnit, skirmishMoves, moveSkirmishUnit } from '@datamoc/mw_games/board';
115
+ *
116
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
117
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 2, attack: 4, hitChance: 0.7 });
118
+ *
119
+ * moveSkirmishUnit(state, skirmishMoves(state, 'axeman')[0]);
120
+ * console.log(state.units[0].remainingMoves); // 1 - one plain hex spent
121
+ * ```
122
+ */
123
+ export function moveSkirmishUnit(state, move) {
124
+ const legal = skirmishMoves(state, move.unit).find((candidate) => candidate.x === move.x && candidate.y === move.y);
125
+ if (!legal)
126
+ throw new Error('illegal skirmish move');
127
+ const unit = getUnit(state, move.unit);
128
+ unit.x = move.x;
129
+ unit.y = move.y;
130
+ unit.remainingMoves = (unit.remainingMoves ?? unit.moves) - legal.cost;
131
+ const cell = state.cells[move.y * state.width + move.x];
132
+ if (cell.village)
133
+ cell.owner = unit.owner;
134
+ }
135
+ /**
136
+ * An attacker's strike against an adjacent enemy, followed by the defender's own retaliation
137
+ * if it survives - both use the same formula: `hitChance` reduced by the defender's terrain
138
+ * defence, `Random.chance` deciding whether it lands, `attack` as the flat damage on a hit.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * import { startingSkirmish, addSkirmishUnit, skirmishAttack } from '@datamoc/mw_games/board';
143
+ * import { Random } from '@datamoc/mw_games/core';
144
+ *
145
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
146
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 1 });
147
+ * addSkirmishUnit(state, { id: 'raider', owner: 'red', x: 1, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 3, hitChance: 1 });
148
+ *
149
+ * const exchange = Random.withSeed(1, () => skirmishAttack(state, 'axeman', 'raider'));
150
+ * console.log(exchange.strikes.map((strike: { attacker: string }) => strike.attacker)); // ['axeman', 'raider'] - raider survived to retaliate
151
+ * ```
152
+ */
153
+ export function skirmishAttack(state, attackerId, defenderId) {
154
+ const attacker = getUnit(state, attackerId);
155
+ const defender = getUnit(state, defenderId);
156
+ if (attacker.owner !== state.turn || attacker.owner === defender.owner)
157
+ throw new Error('invalid skirmish attack');
158
+ if (hexDistance(attacker, defender) !== 1)
159
+ throw new Error('skirmish combat is adjacency-only');
160
+ const strikes = [strike(state, attacker, defender)];
161
+ if (defender.hp > 0)
162
+ strikes.push(strike(state, defender, attacker));
163
+ state.units = state.units.filter((unit) => unit.hp > 0);
164
+ return { strikes };
165
+ }
166
+ function strike(state, attacker, defender) {
167
+ const defense = terrainOf(state, defender.x, defender.y).defense;
168
+ const hit = Random.chance(Math.max(0, Math.min(1, attacker.hitChance * (1 - defense))));
169
+ const damage = hit ? Math.min(attacker.attack, defender.hp) : 0;
170
+ defender.hp -= damage;
171
+ return { attacker: attacker.id, defender: defender.id, hit, damage, killed: defender.hp <= 0 };
172
+ }
173
+ /**
174
+ * Net income for one side, from a flat base plus every village it currently owns - purely a
175
+ * function of the map's own village ownership, no formula beyond that belongs here, the same
176
+ * policy `board.Army`'s `armyIncome` already follows.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * import { startingSkirmish, setSkirmishTerrain, skirmishIncome } from '@datamoc/mw_games/board';
181
+ *
182
+ * const state = startingSkirmish(4, 1, { plain: { moveCost: 1, defense: 0 } });
183
+ * setSkirmishTerrain(state, 0, 0, 'plain', true);
184
+ * state.cells[0].owner = 'blue';
185
+ *
186
+ * console.log(skirmishIncome(state, 'blue', 2, 1)); // 3 - base 2, plus 1 for the one village
187
+ * ```
188
+ */
189
+ export function skirmishIncome(state, owner, baseIncome, perVillage) {
190
+ const villages = state.cells.filter((cell) => cell.village && cell.owner === owner).length;
191
+ return baseIncome + villages * perVillage;
192
+ }
193
+ /**
194
+ * Hands the turn to the next owner with units on the board, refilling their movement and
195
+ * healing every one of their units standing on a village they own.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * import { startingSkirmish, addSkirmishUnit, endSkirmishTurn } from '@datamoc/mw_games/board';
200
+ *
201
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
202
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 0.7 });
203
+ * addSkirmishUnit(state, { id: 'raider', owner: 'red', x: 3, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 3, hitChance: 0.6 });
204
+ *
205
+ * endSkirmishTurn(state, 2);
206
+ * console.log(state.turn, state.round); // 'red' 1
207
+ * ```
208
+ */
209
+ export function endSkirmishTurn(state, healPerVillage = 0) {
210
+ const owners = [...new Set(state.units.map((unit) => unit.owner))];
211
+ if (owners.length === 0)
212
+ return;
213
+ const next = owners.indexOf(state.turn) + 1;
214
+ if (next >= owners.length)
215
+ state.round++;
216
+ state.turn = owners[next % owners.length];
217
+ for (const unit of state.units) {
218
+ if (unit.owner !== state.turn)
219
+ continue;
220
+ unit.remainingMoves = unit.moves;
221
+ const cell = state.cells[unit.y * state.width + unit.x];
222
+ if (cell.village && cell.owner === unit.owner && healPerVillage > 0) {
223
+ unit.hp = Math.min(unit.maxHp, unit.hp + healPerVillage);
224
+ }
225
+ }
226
+ }
227
+ function getUnit(state, id) {
228
+ const unit = state.units.find((candidate) => candidate.id === id);
229
+ if (!unit)
230
+ throw new Error(`unknown skirmish unit "${id}"`);
231
+ return unit;
232
+ }
233
+ function inside(state, x, y) { return x >= 0 && y >= 0 && x < state.width && y < state.height; }
234
+ function occupied(state, x, y) { return state.units.some((unit) => unit.x === x && unit.y === y); }
235
+ function terrainOf(state, x, y) { return state.terrainTable[state.cells[y * state.width + x].terrain]; }
236
+ function moveCostOf(state, x, y) { return terrainOf(state, x, y).moveCost; }
237
+ //the real weighted walking cost to (targetX, targetY), never exceeding `budget`, or null if it
238
+ //cannot be reached within that budget - Dijkstra over a small grid rather than BFS, since
239
+ //terrain move cost varies per cell unlike board.Tactics' uniform one-step cost
240
+ function pathCost(state, unit, targetX, targetY, budget) {
241
+ const best = new Map([[`${unit.x},${unit.y}`, 0]]);
242
+ const todo = [{ x: unit.x, y: unit.y, cost: 0 }];
243
+ while (todo.length > 0) {
244
+ todo.sort((a, b) => a.cost - b.cost);
245
+ const current = todo.shift();
246
+ const key = `${current.x},${current.y}`;
247
+ if (current.cost > (best.get(key) ?? Infinity))
248
+ continue;
249
+ if (current.x === targetX && current.y === targetY)
250
+ return current.cost;
251
+ for (const next of hexNeighbors(current.x, current.y)) {
252
+ if (!inside(state, next.x, next.y))
253
+ continue;
254
+ const stepCost = moveCostOf(state, next.x, next.y);
255
+ if (stepCost === Infinity)
256
+ continue;
257
+ if (occupied(state, next.x, next.y) && !(next.x === targetX && next.y === targetY))
258
+ continue;
259
+ const total = current.cost + stepCost;
260
+ if (total > budget)
261
+ continue;
262
+ const nextKey = `${next.x},${next.y}`;
263
+ if (total < (best.get(nextKey) ?? Infinity)) {
264
+ best.set(nextKey, total);
265
+ todo.push({ x: next.x, y: next.y, cost: total });
266
+ }
267
+ }
268
+ }
269
+ return null;
270
+ }
271
+ //# sourceMappingURL=HexSkirmish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HexSkirmish.js","sourceRoot":"","sources":["../../src/board/HexSkirmish.ts"],"names":[],"mappings":"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"}
@@ -10,4 +10,6 @@ export { FactionFog } from './FogOfWar.ts';
10
10
  export type { VisionCell } from './FogOfWar.ts';
11
11
  export { startingArmy, recruit, recall, bankUnit, armyIncome, applyUpkeep } from './Army.ts';
12
12
  export type { UnitTemplate, ArmyState, UpkeepRates } from './Army.ts';
13
+ export { startingSkirmish, setSkirmishTerrain, canPlaceSkirmishUnit, addSkirmishUnit, skirmishMoves, moveSkirmishUnit, skirmishAttack, skirmishIncome, endSkirmishTurn, } from './HexSkirmish.ts';
14
+ export type { SkirmishTerrain, SkirmishCell, SkirmishUnit, SkirmishState, SkirmishMove, SkirmishStrike, SkirmishExchange, } from './HexSkirmish.ts';
13
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,OAAO,EACP,SAAS,GACT,MAAM,eAAe,CAAC;AACvB,YAAY,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,SAAS,EACT,eAAe,EACf,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxM,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC3H,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,OAAO,EACP,SAAS,GACT,MAAM,eAAe,CAAC;AACvB,YAAY,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,SAAS,EACT,eAAe,EACf,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxM,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC3H,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,EACd,gBAAgB,GAChB,MAAM,kBAAkB,CAAC"}
@@ -4,4 +4,5 @@ export { BoardGrid, startingCheckers, checkersMoves, applyCheckersMove, starting
4
4
  export { startingTactics, addTacticalUnit, canPlaceTacticalUnit, tacticalMoves, moveTacticalUnit, setTacticalOverwatch, triggerTacticalOverwatch, tacticalAttack, endTacticalTurn } from './Tactics.js';
5
5
  export { FactionFog } from './FogOfWar.js';
6
6
  export { startingArmy, recruit, recall, bankUnit, armyIncome, applyUpkeep } from './Army.js';
7
+ export { startingSkirmish, setSkirmishTerrain, canPlaceSkirmishUnit, addSkirmishUnit, skirmishMoves, moveSkirmishUnit, skirmishAttack, skirmishIncome, endSkirmishTurn, } from './HexSkirmish.js';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,OAAO,EACP,SAAS,GACT,MAAM,eAAe,CAAC;AAmBvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAExM,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,OAAO,EACP,SAAS,GACT,MAAM,eAAe,CAAC;AAmBvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAExM,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7F,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A header-row CSV into an array of typed row objects - so a content designer edits a
3
+ * spreadsheet instead of a `.ts` object literal for a table shaped like `actors.AffixTable`,
4
+ * `actors.LootTable`, or any other array-of-records a game defines. The same precedent
5
+ * `i18n`'s `parseFTL` already set: this takes a raw string and has no opinion on how that
6
+ * string reached the game (a small table can sit in a `.ts` template literal; a larger one
7
+ * loads through a bundler's raw-text import - Vite resolves a `?raw`-suffixed path to its
8
+ * file's contents as a plain string - or through a game's own asset pipeline) - `mwg`
9
+ * supplies the parsing, not the loading.
10
+ *
11
+ * Every cell is a string until a column says otherwise: `columns` names which fields are
12
+ * `'number'`/`'boolean'`/`'list'` (semicolon-separated by default, for a field shaped like
13
+ * `AffixDef.kinds`) /`'map'` (semicolon-separated `key=value` pairs, for a per-row property
14
+ * bag such as `{ str: '2', dex: '-1' }` a game reads its own way) - and an undeclared column
15
+ * stays a plain string. An **empty cell omits that field from the row entirely**, the same as
16
+ * never setting an optional property - so a blank `curse` column reads exactly like the
17
+ * `curse?: boolean` it fills never being set.
18
+ *
19
+ * Follows RFC 4180 far enough for real game-data text: a quoted field may contain the
20
+ * delimiter, a newline, or an escaped `""` for a literal quote, which a naive `split(',')`
21
+ * cannot survive the moment a description contains a comma.
22
+ *
23
+ * What this cannot hold: a `core.ReactionTable` rule's `when`/`action` are executable code,
24
+ * not data, so no cell format expresses them - the same boundary `AffixDef.id` already draws
25
+ * (a string the game interprets, `mwg` never does). A CSV row can drive *which* reactions a
26
+ * game builds at startup (an id, a comparison string like `'hp<=0.25'`, a threshold number a
27
+ * `'number'` column already coerces) with the game's own code turning each row into a real
28
+ * `ReactionRule`; it cannot hold the rule's logic itself.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { parseCSV } from '@datamoc/mw_games/core';
33
+ * import type { AffixDef, AffixTable } from '@datamoc/mw_games/actors';
34
+ *
35
+ * const csv = `id,trigger,weight,curse,description
36
+ * blazing,strike,3,,Ignites the victim
37
+ * wayward,strike,1,true,"Cursed: -3 accuracy"`;
38
+ *
39
+ * const table: AffixTable = {
40
+ * entries: parseCSV<AffixDef>(csv, { columns: { weight: 'number', curse: 'boolean' } }),
41
+ * };
42
+ * console.log(table.entries[1]); // { id: 'wayward', trigger: 'strike', weight: 1, curse: true, description: 'Cursed: -3 accuracy' }
43
+ * ```
44
+ */
45
+ export type CsvColumnType = 'string' | 'number' | 'boolean' | 'list' | 'map';
46
+ export interface CsvOptions {
47
+ /** how to interpret each named column beyond a plain string; an undeclared column stays one */
48
+ columns?: Record<string, CsvColumnType>;
49
+ /** splits a `'list'`/`'map'` column's cell into individual entries; default `;` */
50
+ listDelimiter?: string;
51
+ /** splits a `'map'` entry into its key and value; default `=` */
52
+ mapDelimiter?: string;
53
+ }
54
+ /**
55
+ * @example
56
+ * ```ts
57
+ * import { parseCSV } from '@datamoc/mw_games/core';
58
+ *
59
+ * const rows = parseCSV('id,weight\nblazing,3\nchilling,3', { columns: { weight: 'number' } });
60
+ * console.log(rows); // [{ id: 'blazing', weight: 3 }, { id: 'chilling', weight: 3 }]
61
+ * ```
62
+ */
63
+ export declare function parseCSV<T = Record<string, string>>(source: string, options?: CsvOptions): T[];
64
+ //# sourceMappingURL=Csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Csv.d.ts","sourceRoot":"","sources":["../../src/core/Csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7E,MAAM,WAAW,UAAU;IAC1B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAExC,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,CAAC,EAAE,CAiBlG"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * A header-row CSV into an array of typed row objects - so a content designer edits a
3
+ * spreadsheet instead of a `.ts` object literal for a table shaped like `actors.AffixTable`,
4
+ * `actors.LootTable`, or any other array-of-records a game defines. The same precedent
5
+ * `i18n`'s `parseFTL` already set: this takes a raw string and has no opinion on how that
6
+ * string reached the game (a small table can sit in a `.ts` template literal; a larger one
7
+ * loads through a bundler's raw-text import - Vite resolves a `?raw`-suffixed path to its
8
+ * file's contents as a plain string - or through a game's own asset pipeline) - `mwg`
9
+ * supplies the parsing, not the loading.
10
+ *
11
+ * Every cell is a string until a column says otherwise: `columns` names which fields are
12
+ * `'number'`/`'boolean'`/`'list'` (semicolon-separated by default, for a field shaped like
13
+ * `AffixDef.kinds`) /`'map'` (semicolon-separated `key=value` pairs, for a per-row property
14
+ * bag such as `{ str: '2', dex: '-1' }` a game reads its own way) - and an undeclared column
15
+ * stays a plain string. An **empty cell omits that field from the row entirely**, the same as
16
+ * never setting an optional property - so a blank `curse` column reads exactly like the
17
+ * `curse?: boolean` it fills never being set.
18
+ *
19
+ * Follows RFC 4180 far enough for real game-data text: a quoted field may contain the
20
+ * delimiter, a newline, or an escaped `""` for a literal quote, which a naive `split(',')`
21
+ * cannot survive the moment a description contains a comma.
22
+ *
23
+ * What this cannot hold: a `core.ReactionTable` rule's `when`/`action` are executable code,
24
+ * not data, so no cell format expresses them - the same boundary `AffixDef.id` already draws
25
+ * (a string the game interprets, `mwg` never does). A CSV row can drive *which* reactions a
26
+ * game builds at startup (an id, a comparison string like `'hp<=0.25'`, a threshold number a
27
+ * `'number'` column already coerces) with the game's own code turning each row into a real
28
+ * `ReactionRule`; it cannot hold the rule's logic itself.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { parseCSV } from '@datamoc/mw_games/core';
33
+ * import type { AffixDef, AffixTable } from '@datamoc/mw_games/actors';
34
+ *
35
+ * const csv = `id,trigger,weight,curse,description
36
+ * blazing,strike,3,,Ignites the victim
37
+ * wayward,strike,1,true,"Cursed: -3 accuracy"`;
38
+ *
39
+ * const table: AffixTable = {
40
+ * entries: parseCSV<AffixDef>(csv, { columns: { weight: 'number', curse: 'boolean' } }),
41
+ * };
42
+ * console.log(table.entries[1]); // { id: 'wayward', trigger: 'strike', weight: 1, curse: true, description: 'Cursed: -3 accuracy' }
43
+ * ```
44
+ */
45
+ /**
46
+ * @example
47
+ * ```ts
48
+ * import { parseCSV } from '@datamoc/mw_games/core';
49
+ *
50
+ * const rows = parseCSV('id,weight\nblazing,3\nchilling,3', { columns: { weight: 'number' } });
51
+ * console.log(rows); // [{ id: 'blazing', weight: 3 }, { id: 'chilling', weight: 3 }]
52
+ * ```
53
+ */
54
+ export function parseCSV(source, options = {}) {
55
+ const rows = tokenizeCsv(source);
56
+ if (rows.length === 0)
57
+ return [];
58
+ const [header, ...body] = rows;
59
+ const listDelimiter = options.listDelimiter ?? ';';
60
+ const mapDelimiter = options.mapDelimiter ?? '=';
61
+ return body.map((cells, rowIndex) => {
62
+ const row = {};
63
+ header.forEach((name, columnIndex) => {
64
+ const raw = cells[columnIndex] ?? '';
65
+ if (raw === '')
66
+ return; //an empty cell omits the field, the same as an optional property never set
67
+ row[name] = coerceCsvCell(raw, options.columns?.[name] ?? 'string', name, rowIndex, listDelimiter, mapDelimiter);
68
+ });
69
+ return row;
70
+ });
71
+ }
72
+ function coerceCsvCell(raw, type, column, rowIndex, listDelimiter, mapDelimiter) {
73
+ switch (type) {
74
+ case 'string':
75
+ return raw;
76
+ case 'number': {
77
+ const value = Number(raw);
78
+ if (!Number.isFinite(value))
79
+ throw new Error(`CSV row ${rowIndex + 1}, column "${column}": "${raw}" is not a number`);
80
+ return value;
81
+ }
82
+ case 'boolean': {
83
+ const lower = raw.toLowerCase();
84
+ if (lower === 'true')
85
+ return true;
86
+ if (lower === 'false')
87
+ return false;
88
+ throw new Error(`CSV row ${rowIndex + 1}, column "${column}": "${raw}" is not "true" or "false"`);
89
+ }
90
+ case 'list':
91
+ return raw.split(listDelimiter).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
92
+ case 'map': {
93
+ const map = {};
94
+ for (const entry of raw.split(listDelimiter).map((piece) => piece.trim()).filter((piece) => piece.length > 0)) {
95
+ const at = entry.indexOf(mapDelimiter);
96
+ if (at === -1)
97
+ throw new Error(`CSV row ${rowIndex + 1}, column "${column}": "${entry}" has no "${mapDelimiter}" to split a key from its value`);
98
+ map[entry.slice(0, at).trim()] = entry.slice(at + mapDelimiter.length).trim();
99
+ }
100
+ return map;
101
+ }
102
+ }
103
+ }
104
+ //RFC 4180-shaped tokenizer, char by char rather than split(',')/split('\n'): a quoted field may
105
+ //itself contain the delimiter, a bare newline, or an escaped `""` for a literal quote, none of
106
+ //which a naive split survives the moment a description column contains a comma
107
+ function tokenizeCsv(source) {
108
+ const rows = [];
109
+ let row = [];
110
+ let field = '';
111
+ let inQuotes = false;
112
+ let i = 0;
113
+ while (i < source.length) {
114
+ const char = source[i];
115
+ if (inQuotes) {
116
+ if (char === '"') {
117
+ if (source[i + 1] === '"') {
118
+ field += '"';
119
+ i += 2;
120
+ continue;
121
+ }
122
+ inQuotes = false;
123
+ i++;
124
+ continue;
125
+ }
126
+ field += char;
127
+ i++;
128
+ continue;
129
+ }
130
+ if (char === '"') {
131
+ inQuotes = true;
132
+ i++;
133
+ continue;
134
+ }
135
+ if (char === ',') {
136
+ row.push(field);
137
+ field = '';
138
+ i++;
139
+ continue;
140
+ }
141
+ if (char === '\r') {
142
+ i++;
143
+ continue;
144
+ } //normalize CRLF, and swallow a bare CR too
145
+ if (char === '\n') {
146
+ row.push(field);
147
+ rows.push(row);
148
+ row = [];
149
+ field = '';
150
+ i++;
151
+ continue;
152
+ }
153
+ field += char;
154
+ i++;
155
+ }
156
+ if (field.length > 0 || row.length > 0) {
157
+ row.push(field);
158
+ rows.push(row);
159
+ }
160
+ //a trailing newline (the common case for a hand-edited file) would otherwise flush one
161
+ //more, entirely blank row after the real data
162
+ return rows.filter((cells) => !(cells.length === 1 && cells[0] === ''));
163
+ }
164
+ //# sourceMappingURL=Csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Csv.js","sourceRoot":"","sources":["../../src/core/Csv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * A named lookup of values, registered once and read back by name - the shape underneath a
3
+ * catalog of factories, handlers, or scene sections, so a game can grow that catalog by
4
+ * registering another entry instead of adding another branch to a hand-written `if`/`switch`.
5
+ *
6
+ * Deliberately does not discover or import anything on its own: a game still writes the
7
+ * import and the `register` call for each entry itself, the same explicit-import style
8
+ * `core`/`two-d` already use throughout - this only replaces the dispatch step after that,
9
+ * not the wiring step before it.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { Registry } from '@datamoc/mw_games/core';
14
+ *
15
+ * const commands = new Registry<(args: string[]) => void>();
16
+ * commands.register('heal', (args) => console.log('healing', args[0]));
17
+ *
18
+ * commands.get('heal')(['fireling']); // "healing fireling"
19
+ * commands.get('missing'); // throws: no registration named "missing"
20
+ * ```
21
+ */
22
+ export declare class Registry<T> {
23
+ private items;
24
+ /** throws if `name` is already registered - a duplicate is always an authoring error */
25
+ register(name: string, value: T): void;
26
+ get(name: string): T;
27
+ has(name: string): boolean;
28
+ /** every registered name, in registration order */
29
+ list(): string[];
30
+ }
31
+ //# sourceMappingURL=Registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Registry.d.ts","sourceRoot":"","sources":["../../src/core/Registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,QAAQ,CAAC,CAAC;IACtB,OAAO,CAAC,KAAK,CAAwB;IAErC,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAGrC;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAInB;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzB;IAED,mDAAmD;IACnD,IAAI,IAAI,MAAM,EAAE,CAEf;CACD"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * A named lookup of values, registered once and read back by name - the shape underneath a
3
+ * catalog of factories, handlers, or scene sections, so a game can grow that catalog by
4
+ * registering another entry instead of adding another branch to a hand-written `if`/`switch`.
5
+ *
6
+ * Deliberately does not discover or import anything on its own: a game still writes the
7
+ * import and the `register` call for each entry itself, the same explicit-import style
8
+ * `core`/`two-d` already use throughout - this only replaces the dispatch step after that,
9
+ * not the wiring step before it.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { Registry } from '@datamoc/mw_games/core';
14
+ *
15
+ * const commands = new Registry<(args: string[]) => void>();
16
+ * commands.register('heal', (args) => console.log('healing', args[0]));
17
+ *
18
+ * commands.get('heal')(['fireling']); // "healing fireling"
19
+ * commands.get('missing'); // throws: no registration named "missing"
20
+ * ```
21
+ */
22
+ export class Registry {
23
+ constructor() {
24
+ this.items = new Map();
25
+ }
26
+ /** throws if `name` is already registered - a duplicate is always an authoring error */
27
+ register(name, value) {
28
+ if (this.items.has(name))
29
+ throw new Error(`"${name}" is already registered`);
30
+ this.items.set(name, value);
31
+ }
32
+ get(name) {
33
+ const value = this.items.get(name);
34
+ if (value === undefined)
35
+ throw new Error(`no registration named "${name}"`);
36
+ return value;
37
+ }
38
+ has(name) {
39
+ return this.items.has(name);
40
+ }
41
+ /** every registered name, in registration order */
42
+ list() {
43
+ return [...this.items.keys()];
44
+ }
45
+ }
46
+ //# sourceMappingURL=Registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Registry.js","sourceRoot":"","sources":["../../src/core/Registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,QAAQ;IAArB;QACS,UAAK,GAAG,IAAI,GAAG,EAAa,CAAC;IAsBtC,CAAC;IApBA,wFAAwF;IACxF,QAAQ,CAAC,IAAY,EAAE,KAAQ;QAC9B,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,yBAAyB,CAAC,CAAC;QAC7E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,IAAY;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,GAAG,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,mDAAmD;IACnD,IAAI;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/B,CAAC;CACD"}