diff --git a/docs/bgm-format.md b/docs/bgm-format.md index 2718671..35a9f08 100644 --- a/docs/bgm-format.md +++ b/docs/bgm-format.md @@ -341,7 +341,7 @@ string,number,number,number The router should select only the first candidate with all params matched against its props — the fields in the candidate's CSV row (e.g. `:seat` matches the candidate's `seat` value). -When no candidates match, the whole route fails to match. +A candidate inherits the route's `x`, `y`, `rotation`, and `stacking`, and may override any of them with its own values. When no candidates match, the whole route fails to match. ### Stacking diff --git a/games/poker/poker.md b/games/poker/poker.md index 11fbf66..d65c7b8 100644 --- a/games/poker/poker.md +++ b/games/poker/poker.md @@ -106,7 +106,6 @@ layout: align: center zStart: 0 #zEnd: 100 - tilt: 1 curve: M -50 -200 C 50 -150 450 -150 550 -200 - route: /community/:slot candidates: diff --git a/packages/bgm/src/types.ts b/packages/bgm/src/types.ts index 832b40c..e7bd2ac 100644 --- a/packages/bgm/src/types.ts +++ b/packages/bgm/src/types.ts @@ -84,6 +84,8 @@ export interface Candidate { x?: number; y?: number; rotation?: number; + /** Stacking strategy; overrides the route's when set, else inherits it. */ + stacking?: Stacking; } export interface Route { diff --git a/packages/tabletop/src/placement.tsx b/packages/tabletop/src/placement.tsx index 59e6b0a..859cd15 100644 --- a/packages/tabletop/src/placement.tsx +++ b/packages/tabletop/src/placement.tsx @@ -17,7 +17,9 @@ export function PartPlacement({ pkg, placement }: { pkg: Package; placement: Pla const part = pkg.parts.get(piece.split(':').slice(1).join(':')); if (!part) return null; - const { x, y, rotation, z, tilt } = useStacking(route.stacking, index, stackSize); + // A candidate inherits the route's stacking unless it overrides it. + const stacking = candidate?.stacking ?? route.stacking; + const { x, y, rotation, z, tilt } = useStacking(stacking, index, stackSize); // Route anchors and stacking offsets are in mm; convert to world units so // parts land on the (world-scaled) surface. `z` raises the part along the diff --git a/packages/tabletop/src/state.test.ts b/packages/tabletop/src/state.test.ts index d06e6b0..04da27a 100644 --- a/packages/tabletop/src/state.test.ts +++ b/packages/tabletop/src/state.test.ts @@ -98,6 +98,22 @@ describe('computeSurfacePlacements', () => { expect(placements[0]!.candidate).toEqual({ seat: '0', x: 40, y: 5, rotation: 1 }); }); + it('keeps the candidate stacking alongside its anchor', () => { + const surface = makeSurface({ + layout: [ + { + route: '/dock/:seat', + x: 0, + y: 0, + rotation: 0, + candidates: [{ seat: '0', x: 40, y: 5, stacking: { tilt: 2 } }], + }, + ], + }); + const placements = computeSurfacePlacements(surface, { '/dock/0': ['harbor:boat#fleet'] }); + expect(placements[0]!.candidate).toEqual({ seat: '0', x: 40, y: 5, stacking: { tilt: 2 } }); + }); + it('keeps the same piece on two paths as distinct placements', () => { const surface = makeSurface({ layout: [