Replace the per-part lift with a local Y-axis tilt that fans the stack, and add zStart/zEnd to ramp the stack's height across the curve so it arches in 3D. Update the poker deck and docs accordingly.
49 lines
2.9 KiB
Markdown
49 lines
2.9 KiB
Markdown
# bgm-tabletop
|
|
|
|
a r3f based interactive component library to work with [bgm](./bgm-format.md) board games. will be used somewhere in the `web` app's bgm inspector routes.
|
|
|
|
## 1. stack
|
|
|
|
`react` - react, react router, tailwindv4
|
|
`r3f` - r3f, drei, postprocessing
|
|
`zustand` - for state management
|
|
|
|
## 2. states
|
|
|
|
source-of-truth game state:
|
|
|
|
```ts
|
|
{
|
|
surfaces: Record<string, boolean>, // enabled per surface id
|
|
paths: Record<string, string[]>, // path -> part list
|
|
}
|
|
```
|
|
|
|
**assumption:** each piece on the board has a unique id, even tokens of the same type. so each entry in a path's list is a unique piece id, and a piece id never appears twice in the same path. 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 }` 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.
|
|
|
|
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.
|
|
|
|
## 3. components
|
|
|
|
- `SetupLoader` side effect only component that seeds the game state with setup (enabled surfaces + part placement).
|
|
- `WorldSurfaceView` mounts a surface to world space.
|
|
- `HudSurfaceView` mounts a surface to hud space.
|
|
- `PartPlacement` a stable per-part component that positions a part on a surface location. uses the stacking hook (below) to apply the route's stacking strategy.
|
|
- `PartView` used in `PartPlacement`, creates a mesh from part definition. a standalone component library, so it reuses geometry/shape code from `@tts/mesh` rather than the web app's viewers.
|
|
|
|
## 4. stacking
|
|
|
|
the format's stacking strategy (`curve` / `limit` / `align` / `steps` / `tilt` / `zStart` / `zEnd`, see `bgm-format.md` §4) is implemented as a hook, e.g. `useStacking(route.stacking, index, stackSize)`, returning the offset/rotation to apply to a piece: `{ x, y, rotation, z, tilt }`. `x`/`y`/`rotation` come from the `curve`; `z` is the surface-normal height ramped from `zStart` to `zEnd`; `tilt` is the per-part fan about the card's local Y (long) axis. `PartPlacement` consumes it.
|
|
|
|
## 5. usage
|
|
|
|
- we will inspect individual parts with `PartView` in the web app's part inspection route.
|
|
- as a library, the public surface is the components above: mount a surface with `WorldSurfaceView`/`HudSurfaceView`, seed state with `SetupLoader`, and let `PartPlacement`/`PartView` render the pieces. the web app is one consumer; the library should not assume the web app's routes or store.
|
|
- a surface is mounted only when enabled; a disabled surface is not rendered. |