diff --git a/AGENTS.md b/AGENTS.md index 7017eae..e3157a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,10 +2,12 @@ ## Project Overview -A Phaser 3 framework for building web board games, built on top of `boardgame-core` (state management with Preact Signals + Mutative). Uses a pnpm monorepo with two packages: +A Phaser 3 framework for building web board games, built on top of `boardgame-core` (state management with Preact Signals + Mutative). Uses a pnpm monorepo with four packages: - **`packages/framework`** (`boardgame-phaser`) — Reusable library: reactive scenes, signal→Phaser bindings, input/command bridge, Preact UI components - **`packages/sample-game`** — Demo Tic-Tac-Toe game using the framework +- **`packages/boop-game`** — Boop sample game +- **`packages/regicide-game`** — Regicide game ## boardgame-core Usage @@ -75,18 +77,15 @@ cd ../boardgame-core && npm build ``` ### Testing -No test framework is configured yet. When adding tests, use **Vitest** (consistent with boardgame-core): +Framework and regicide-game have **Vitest** configured: ```bash # In packages/framework: -pnpm add -D vitest -# Run all tests: -pnpm vitest run -# Run a single test file: -pnpm vitest run src/bindings/index.test.ts -# Run tests matching a pattern: -pnpm vitest run -t "bindRegion" -# Watch mode: -pnpm vitest +pnpm --filter boardgame-phaser test # Run all tests +pnpm --filter boardgame-phaser test:watch # Watch mode + +# In packages/regicide-game: +pnpm --filter regicide-game test # Run all tests +pnpm --filter regicide-game test:watch # Watch mode ``` ## Code Style @@ -111,24 +110,24 @@ pnpm vitest - Use `as any` sparingly; prefer `as unknown as Record` for type narrowing ### Naming conventions -- **Classes**: PascalCase (`ReactiveScene`, `InputMapper`, `PromptHandler`) -- **Interfaces**: PascalCase with descriptive names (`ReactiveSceneOptions`, `BindRegionOptions`) -- **Functions**: camelCase (`bindSignal`, `createInputMapper`, `createPromptHandler`) +- **Classes**: PascalCase (`GameHostScene`, `PhaserGame`, `GameUI`) +- **Interfaces**: PascalCase with descriptive names (`GameHostSceneOptions`, `PhaserGameProps`) +- **Functions**: camelCase (`spawnEffect`, `bindRegion`) - **Constants**: UPPER_SNAKE_CASE (`CELL_SIZE`, `BOARD_OFFSET`) - **Type aliases**: PascalCase (`DisposeFn`, `CommandResult`) -- **Factory functions**: `create*` prefix (`createInputMapper`, `createPromptHandler`) +- **Factory functions**: `create*` prefix (`createRegion`, `createRNG`) ### Architecture patterns -- **ReactiveScene**: Abstract base class extending `Phaser.Scene`. Subclasses implement `onStateReady()` and `setupBindings()`. Effects are auto-cleaned on scene shutdown via `this.events.on('shutdown', ...)`. -- **Bindings**: Use `effect()` from `@preact/signals-core` to reactively sync signal state to Phaser objects. Always return cleanup functions. -- **Input/Command bridge**: Maps Phaser pointer events to `boardgame-core` command strings. Uses `create*` factory pattern. +- **GameHostScene**: Abstract base class extending `Phaser.Scene`. Subclasses implement game-specific logic. Provides `gameHost` property for state access and `addInterruption()`/`addTweenInterruption()` for async flow control. Disposables are auto-cleaned on scene shutdown via `this.events.on('shutdown', ...)`. +- **Spawner**: Use `spawnEffect()` for data-driven object creation with signal-based configuration. - **UI components**: Preact functional components with hooks. Use `className` (not `class`) for CSS classes. +- **Phaser Bridge**: Use `PhaserGame`, `PhaserScene`, and `GameUI` components from `boardgame-phaser` for React/Phaser integration. ### Error handling -- Command results use `{ success: true, result } | { success: false, error }` pattern -- Scene effects are cleaned up via the `shutdown` event, not by overriding `shutdown()` +- Use `DisposableBag` for managing multiple disposables in scenes +- Scene effects are cleaned up via the `shutdown` event - Use `as any` casts only when Phaser types are incomplete (e.g., `setInteractive`) -- Prompt handlers return `string | null` for validation errors +- Game commands return validation errors as `string | null` ### JSX - `jsxImportSource: "preact"` — no React import needed diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75a63db..677ec14 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,9 @@ importers: vite: specifier: ^5.1.0 version: 5.4.21(lightningcss@1.32.0) + vitest: + specifier: ^3.2.4 + version: 3.2.4(lightningcss@1.32.0) packages/sample-game: dependencies: