Apply the same tilt to every stacked part and rotate it about its local Y axis instead of Z. Default tilt to 1 degree when a stacking strategy is present, and arc the poker deck along the surface's bottom edge.
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 rotation about the card's local Y (long) axis, applied to every part. `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. |