aboutsummaryrefslogtreecommitdiff
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md193
1 files changed, 193 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..fb196e5
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,193 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## What this is
+
+**Downstroke** is a 2D tile-driven game engine for Chicken Scheme, built on SDL2. API inspired by Phaser 2: a minimal game is ~20 lines of Scheme.
+
+The engine is being extracted from the testbed game **macroknight** (`/home/gene/src/macroknight`). Milestones 1–6 are pure refactoring (no behavior changes); Milestone 7 is the design pivot where the public API stabilizes.
+
+**Detailed extraction plan**: `/home/gene/src/macroknight/TODO-engine.org`
+**Project milestones**: `/home/gene/Documents/Perso/Projects/downstroke.org` (also in README.org)
+
+## Target API
+
+```scheme
+(define my-game
+ (make-game
+ title: "My Game" width: 600 height: 400
+ preload: (lambda (game) ...) ; load assets
+ create: (lambda (game) ...) ; init scene
+ update: (lambda (game dt) ...))) ; game-specific logic (physics runs first)
+
+(game-run! my-game)
+```
+
+Built-in physics pipeline (runs before user `update:` hook):
+```
+input → acceleration → gravity → velocity-x → tile-collision-x →
+velocity-y → tile-collision-y → ground-detection → entity-collisions
+```
+
+## Build & Test (macroknight — source of truth)
+
+All engine code currently lives in `/home/gene/src/macroknight`. Until Milestone 1 is complete, build and test from there.
+
+```bash
+cd /home/gene/src/macroknight
+
+make # compile all modules + link bin/game
+make test # run all 8 SRFI-64 test suites via csi
+make clean # remove bin/ and .import.scm files
+
+# Run a single test module:
+csi -s tests/physics-test.scm
+csi -s tests/entity-test.scm
+# etc.
+```
+
+Once extraction begins, the downstroke Makefile must also build all demos:
+
+```bash
+make # compile engine + all demos in demo/
+make test # run all SRFI-64 test suites
+make demos # build demo games only (verify they compile)
+```
+
+**Module compile order** (dependency order, must be respected in Makefile):
+`tilemap → entity → world → animation → physics → ai → input → prefabs → mixer → sound`
+
+Modules are compiled as **units** (`csc -c -J -unit $*`) to avoid C toplevel name collisions when linking multiple `.o` files. Each module generates both a `.o` and a `.import.scm` in `bin/`.
+
+## Test-Driven Development
+
+**Tests are mandatory for all engine code.** Write tests before or alongside implementation — never after. The test suite is the primary correctness guarantee for the engine, since behavior regressions are easy to introduce during extraction.
+
+- Test files live in `tests/`, named `<module>-test.scm`
+- Framework: SRFI-64 (`test-begin`, `test-equal`, `test-assert`, `test-end`)
+- Tests run via `csi -s` (interpreter, not compiled) and must not require SDL2 — mock or stub any SDL2-dependent code
+- Each engine module must have a corresponding test module before the module is considered done
+
+## Documentation
+
+End-user documentation lives in `docs/` as **org-mode files** and must be kept up to date as the API evolves. This is not optional — docs ship with the egg.
+
+- `docs/api.org` — public API reference (`make-game`, `game-run!`, all accessors and hooks)
+- `docs/guide.org` — getting started guide with the minimal ~20-line game example
+- `docs/entities.org` — entity model, plist keys, prefab/mixin system
+- `docs/physics.org` — physics pipeline, collision model, gravity/velocity API
+
+When adding or changing any public-facing function or keyword argument, update the relevant doc file in the same commit.
+
+## Demo Games
+
+`demo/` contains small self-contained example games that exercise the engine API. They serve as living documentation and integration tests.
+
+- Each demo is a single `.scm` file (plus any assets in `demo/<name>/`)
+- The Makefile must build all demos as part of `make` or `make demos` — a demo that fails to compile is a build failure
+- Demos should be minimal: one mechanic per demo (gravity+jump, tilemap rendering, animation, etc.)
+- Do not add game-specific logic to the engine to make a demo work; if a demo needs something, it belongs in the engine's public API
+
+## Engine Module Architecture
+
+| Module | File | Responsibility |
+|---|---|---|
+| `engine` | engine.scm | `make-game`, `game-run!`, lifecycle orchestration |
+| `world` | world.scm | Scene struct, entity list ops, camera |
+| `entity` | entity.scm | Entity plist accessors (`entity-ref`, `entity-set`, `entity-type`) |
+| `physics` | physics.scm | Gravity, velocity, AABB tile + entity collisions, ground detection |
+| `tilemap` | tilemap.scm | TMX/TSX XML parsing (expat), tileset loading, tile rect calculations |
+| `input` | input.scm | SDL2 event → action mapping, keyboard/joystick/controller |
+| `animation` | animation.scm | Frame/tick tracking, sprite ID mapping, animation state machine |
+| `prefabs` | prefabs.scm | Mixin composition, prefab data loading, entity instantiation + hooks |
+| `ai` | ai.scm | FSM-based enemy AI (idle/patrol/chase) via `states` egg |
+| `renderer` | renderer.scm | SDL2 drawing abstraction: `draw-sprite`, `draw-tilemap-layer`, `draw-text` |
+| `assets` | assets.scm | Asset registry for `preload:` lifecycle hook |
+| `scene-loader` | scene-loader.scm | `game-load-scene!`, `instantiate-prefab` |
+| `sound` | sound.scm | Sound registry, music playback |
+| `mixer` | mixer.scm | SDL_mixer FFI bindings (no Scheme dependencies) |
+
+## Entity Model
+
+Entities are **plists** (property lists) — no classes, pure data + functions:
+
+```scheme
+(list #:type 'player
+ #:x 100 #:y 200
+ #:width 16 #:height 16
+ #:vx 0 #:vy 0
+ #:gravity? #t
+ #:on-ground? #f
+ #:tile-id 29 ; sprite index in tileset
+ #:tags '(player)
+ #:anim-name 'walk
+ #:animations ((idle #:frames (28) #:duration 10)
+ (walk #:frames (27 28) #:duration 10)))
+```
+
+Key shared entity keys: `#:type`, `#:x`, `#:y`, `#:width`, `#:height`, `#:vx`, `#:vy`, `#:tile-id`, `#:tags`.
+
+Access via `entity-ref`, `entity-set` (returns new plist — functional/immutable), `entity-type`.
+
+## Scene & Camera
+
+```scheme
+(make-scene
+ entities: (list ...)
+ tilemap: tm
+ camera: (make-camera x: 0 y: 0)
+ tileset-texture: tex)
+```
+
+## Prefab / Mixin System
+
+```scheme
+(mixins
+ (physics-body #:vx 0 #:vy 0 ...)
+ (has-facing #:facing 1))
+
+(prefabs
+ (player physics-body has-facing #:type player #:tile-id 29 ...))
+```
+
+Prefabs are loaded from a data file (`assets/prefabs.scm`). `instantiate-prefab` merges mixins + overrides into a fresh entity plist.
+
+## Tile Collision Model
+
+Tiles come from TMX maps (Tiled editor). The tilemap module parses TMX XML via expat into a struct with layers, tile GIDs, and a tileset. Collision tiles are identified by metadata in the TSX tileset. The physics module checks all tile cells overlapping an entity's AABB and snaps the entity to the nearest edge (velocity-direction-aware).
+
+## AI State Machine
+
+Uses the `states` egg. States: `idle → patrol → chase → patrol` (cycles back). Guards: `player-in-range?`, `player-in-attack-range?`, `chase-give-up?`. After Milestone 10, hardcoded `'player` type checks are replaced with `(scene-find-tagged scene 'player)`.
+
+## Dependencies
+
+System: `SDL2`, `SDL2_mixer`, `SDL2_ttf`, `SDL2_image`
+
+Chicken eggs: `sdl2`, `sdl2-image`, `expat`, `matchable`, `defstruct`, `states`, `srfi-64` (tests), `srfi-197`
+
+## Egg Packaging
+
+Downstroke is distributed as a **Chicken egg** named `downstroke`. The egg spec (`downstroke.egg`) declares all modules as installable units. Once published, games depend on it via `chicken-install downstroke`.
+
+Module namespacing follows the egg convention: `downstroke/physics`, `downstroke/world`, etc. All module source files live at the project root; the egg spec maps them to the namespaced identifiers.
+
+## Macroknight Port
+
+As soon as the first installable egg exists (Milestone 11), macroknight must be ported to depend on it. This is an ongoing obligation — macroknight is the primary validation that the engine API is usable. Any engine change that requires macroknight to be updated should update macroknight in the same work session.
+
+The ported macroknight `game.scm` is the definition of "Milestone 12 done": it should be ~20–30 lines, containing only `make-game` + lifecycle hooks + `game-run!`.
+
+## Tracking Progress
+
+As milestones and tasks are completed, update the TODO items in `/home/gene/Documents/Perso/Projects/downstroke.org`. Mark tasks with `DONE` as they are finished — this file is the authoritative project tracker.
+
+## Milestone Status
+
+Milestones 1–6: pure refactoring — extract modules into the project root, no behavior changes.
+Milestone 7: design pivot — `make-game` + `game-run!` public API becomes stable.
+Milestones 8–10: camera follow, scene state machine, AI tag lookup.
+Milestones 11–12: package as Chicken egg, macroknight uses it as dependency.
+
+Current status: all milestones at 0/N — extraction has not yet begun.