Scaffold Action
Use when adding a new action to the game's event system.
Action Taxonomy
All actions are strongly-typed discriminated unions. The four top-level categories are:
type InputAction = /* keyboard / mouse / touch / gamepad events, normalised */
type GameAction = /* simulation-driven state transitions */
type SystemEvent = /* lifecycle events: tick, init, pause, resume */
type RenderCommand = /* draw instructions produced by projectRenderCommands(state) */
Steps
- Identify the category — ask which of the four types the new action belongs to.
- Add the variant — append a new branch to the correct discriminated union in
packages/game/lib/core/actions.ts. - Handle it exhaustively — add a
caseto everyswitchthat covers that union; the compiler will error on missing cases (nevercheck at the end). - Write a unit test — pure function test in the co-located
*.test.tsfile that exercises the new branch.
Conventions
- Tag field is always
type(notkind, notaction). - Payload fields are
Readonly<{…}>inline — no separate payload type unless reused. - No classes. No
this. No mutation. - Branded IDs for any entity reference:
Brand<string, "EnemyId">.
Template
// packages/game/lib/core/actions.ts (add to the correct union)
| { readonly type: "your-action"; readonly payload: Readonly<{ /* … */ }> }
Exhaustiveness guard (add to every switch that covers this union):
default: {
const _exhaustive: never = action;
return _exhaustive;
}
For RenderCommand variants, also update packages/game/lib/render/project-render-commands.ts, packages/game/lib/effects/execute-render-command.ts, and their co-located tests.