feat: add facing orientation to parts

Replace the face/back boolean with a three-state facing (face, back,
standing) seeded from setup placements. Parts now orient about the
anchor at the resting face/edge, so back-down parts sit on the table
instead of below it and standing parts rest on their bottom edge.
This commit is contained in:
2026-08-10 11:47:58 +08:00
parent aa23e93b3f
commit d663f4afea
13 changed files with 126 additions and 36 deletions
+14
View File
@@ -272,6 +272,10 @@ setup:
parts: harbor:boat#fleet
- path: /deck
parts: harbor:card
facing: back
- path: /table
parts: harbor:token#wood
facing: standing
```
`surfaces` lists the surfaces enabled at the start. A surface not listed is
@@ -287,6 +291,16 @@ the deck, then move these cards to the flop".
either. A bare type expands to all parts of that type during game state
initialization.
`facing` sets how the placed parts are oriented on the board, defaulting to
`face`:
- `face` — lay flat, front up, resting on the bottom face.
- `back` — lay flat, front down (flipped over), resting on the top face.
- `standing` — stand upright on the bottom edge, front texture still showing.
A part's `facing` is seeded into the game state and can change at runtime; it
only affects orientation, never the part's texture.
---
## 4. Concepts
+1 -1
View File
@@ -86,7 +86,7 @@ interface GameState {
interface PartState {
path: string; // the path key this part is on
index: number; // the part's position in its path's stack
face: boolean; // whether the part's face is up
facing: 'face' | 'back' | 'standing'; // how the part is oriented on the board
}
```
+3 -3
View File
@@ -21,19 +21,19 @@ source-of-truth game state:
interface PartState {
path: string, // the path key this part is on
index: number, // the part's position in its path's stack
face: boolean, // whether the part's face is up
facing: 'face' | 'back' | 'standing', // how the part is oriented on the board
}
```
**assumption:** each piece on the board has a unique id, even tokens of the same type. so a part id appears at most once, and a path's ordered children (for stacking) are derived from the map by sorting on `index`. this makes the render list keyed by piece id stable and unambiguous.
derived surface render state: game state + surface routes => map of piece id to `{ surface, route, candidate, index, stackSize, face }` for rendering on a surface. keys of this map makes a stable render list.
derived surface render state: game state + surface routes => map of piece id to `{ surface, route, candidate, index, stackSize, facing }` for rendering on a surface. keys of this map makes a stable render list.
- `route` - the matched route.
- `candidate` - the matched candidate for a `:param` route, carrying its anchor `x`/`y`/`rotation`. absent for routes without candidates.
- `index` - the piece's position in its path's stack.
- `stackSize` - the number of pieces on the path.
- `face` - whether the piece's face is up.
- `facing` - how the piece is oriented on the board (`face` / `back` / `standing`).
the render map is per enabled surface: a piece may appear on more than one enabled surface (e.g. an expansion path and the main board), and each is rendered independently.