feat(tabletop): seed state from ordered setup placements
Apply each setup placement in order, moving its parts to its path, then index each path's parts contiguously so stacking stays valid.
This commit is contained in:
@@ -36,5 +36,6 @@ role: setup
|
||||
type: game
|
||||
id: main
|
||||
setup:
|
||||
/deck: harbor:token
|
||||
- path: /deck
|
||||
parts: harbor:token
|
||||
```
|
||||
@@ -24,7 +24,7 @@ const tree = resolveMountTree(
|
||||
new Set(Object.keys(seeded.surfaces)),
|
||||
);
|
||||
|
||||
export const seededPaths = Object.keys(seeded.paths);
|
||||
export const seededPaths = Object.keys(seeded.parts);
|
||||
export const expanded = expandSetupValue(pkg as never, 'harbor:token');
|
||||
export const placementCount = placements.length;
|
||||
export const matched = matchRoute({ route: '/dock/:seat', x: 0, y: 0, rotation: 0 }, '/dock/0');
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('seedFromSetup', () => {
|
||||
type: 'game',
|
||||
id: 'main',
|
||||
surfaces: ['board#harbor'],
|
||||
setup: { '/deck': 'harbor:card#fleet' },
|
||||
setup: [{ path: '/deck', parts: 'harbor:card#fleet' }],
|
||||
};
|
||||
const state = seedFromSetup(pkg, setup);
|
||||
expect(state.surfaces).toEqual({ 'board#harbor': true });
|
||||
@@ -48,8 +48,24 @@ describe('seedFromSetup', () => {
|
||||
});
|
||||
|
||||
it('enables all surfaces when omitted', () => {
|
||||
const setup = { type: 'game', id: 'main', setup: {} };
|
||||
const setup = { type: 'game', id: 'main', setup: [] };
|
||||
const state = seedFromSetup(pkg, setup);
|
||||
expect(state.surfaces).toEqual({ 'board#harbor': true, 'hud#hand': true });
|
||||
});
|
||||
|
||||
it('applies placements in order, last path wins', () => {
|
||||
// `harbor:card#fleet` is placed on /deck first, then moved to /hand. Each
|
||||
// path's indices stay contiguous 0..n-1 so stacking stays valid.
|
||||
const setup = {
|
||||
type: 'game',
|
||||
id: 'main',
|
||||
setup: [
|
||||
{ path: '/deck', parts: ['harbor:card#fleet', 'harbor:token#wood'] },
|
||||
{ path: '/hand', parts: 'harbor:card#fleet' },
|
||||
],
|
||||
};
|
||||
const state = seedFromSetup(pkg, setup);
|
||||
expect(state.parts['harbor:card#fleet']).toEqual({ path: '/hand', index: 0, face: true });
|
||||
expect(state.parts['harbor:token#wood']).toEqual({ path: '/deck', index: 0, face: true });
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
* `type` (no id) expands to all parts of that type during initialization.
|
||||
*/
|
||||
import { useEffect } from 'react';
|
||||
import type { Package, Setup } from '@tts/bgm';
|
||||
import type { Package, Setup, SetupValue } from '@tts/bgm';
|
||||
import { useTabletopStore, type PartState } from './state.js';
|
||||
|
||||
/**
|
||||
@@ -15,7 +15,7 @@ import { useTabletopStore, type PartState } from './state.js';
|
||||
*/
|
||||
export function expandSetupValue(
|
||||
pkg: Package,
|
||||
value: string | string[],
|
||||
value: SetupValue,
|
||||
): string[] {
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
const out: string[] = [];
|
||||
@@ -48,11 +48,23 @@ export function seedFromSetup(pkg: Package, setup: Setup): {
|
||||
for (const key of pkg.surfaces.keys()) surfaces[key] = true;
|
||||
}
|
||||
|
||||
// Apply placements in order: each moves its parts to its path. A part listed
|
||||
// in a later placement ends up on that placement's path. Then index each
|
||||
// path's parts contiguously, in placement order, so `index` is always a
|
||||
// valid position within its path's stack.
|
||||
const parts: Record<string, PartState> = {};
|
||||
for (const [path, value] of Object.entries(setup.setup)) {
|
||||
const ids = expandSetupValue(pkg, value);
|
||||
for (const placement of setup.setup) {
|
||||
for (const id of expandSetupValue(pkg, placement.parts)) {
|
||||
parts[id] = { path: placement.path, index: 0, face: true };
|
||||
}
|
||||
}
|
||||
const byPath: Record<string, string[]> = {};
|
||||
for (const [id, ps] of Object.entries(parts)) {
|
||||
(byPath[ps.path] ??= []).push(id);
|
||||
}
|
||||
for (const [path, ids] of Object.entries(byPath)) {
|
||||
ids.forEach((id, index) => {
|
||||
parts[id] = { path, index, face: true };
|
||||
parts[id] = { ...parts[id]!, index };
|
||||
});
|
||||
}
|
||||
return { surfaces, parts };
|
||||
|
||||
Reference in New Issue
Block a user