diff options
| author | Gene Pasquet <dev@etenil.net> | 2026-04-05 23:12:54 +0100 |
|---|---|---|
| committer | Gene Pasquet <dev@etenil.net> | 2026-04-05 23:12:54 +0100 |
| commit | b99ada53b715def5492c7d04c0d327fa7048e5d3 (patch) | |
| tree | 9e94dbc8ff863ef09ef18f4be31fb45e085572a4 /docs/superpowers/specs/2026-04-05-demos-design.md | |
| parent | 027053b11a3a5d861ed2fa2db245388bd95ac246 (diff) | |
Complete implementation
Diffstat (limited to 'docs/superpowers/specs/2026-04-05-demos-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-04-05-demos-design.md | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-04-05-demos-design.md b/docs/superpowers/specs/2026-04-05-demos-design.md new file mode 100644 index 0000000..78ed3f4 --- /dev/null +++ b/docs/superpowers/specs/2026-04-05-demos-design.md @@ -0,0 +1,256 @@ +# Downstroke Demo Games Design + +**Date:** 2026-04-05 +**Status:** Approved +**Scope:** `demo/` folder, 5 demo games, Makefile `make demos` target, CLAUDE.md update + +--- + +## Goal + +Provide 5 small self-contained demo games in `demo/` that collectively exercise every engine system. Each demo compiles to its own executable (`bin/demo-*`). They replace the macroknight integration test for Milestone 8 and serve as living documentation of the engine API. + +--- + +## File Layout + +``` +demo/ + assets/ ← copied from macroknight/assets (not symlinked) + monochrome-transparent.png ← tileset spritesheet + monochrome_transparent.tsx ← tileset metadata (TSX) + level-0.tmx ← level used by platformer, topdown, sandbox + DejaVuSans.ttf ← font for audio demo text + jump.wav ← sound effect (platformer jump, shmup shoot) + theme.ogg ← music (audio demo) + platformer.scm + shmup.scm + topdown.scm + audio.scm + sandbox.scm +``` + +**Omitted from copy:** `prefabs.scm`, `macroknight.tiled-project`, `macroknight.tiled-session` — macroknight-specific files not needed by any demo. + +**Audio modules:** `mixer.scm` and `sound.scm` are copied from `macroknight/` into the downstroke repo root and added to the build. They are engine-level modules and are required by any demo that uses audio. + +When copying `sound.scm`, add one function that macroknight's version omits: +```scheme +(define (stop-music!) (mix-halt-music)) +``` +The audio demo uses `stop-music!` to toggle music off. + +--- + +## Build + +### Makefile additions + +```makefile +DEMO_NAMES := platformer shmup topdown audio sandbox +DEMO_BINS := $(patsubst %,bin/demo-%,$(DEMO_NAMES)) + +demos: engine $(DEMO_BINS) + +bin/demo-%: demo/%.scm $(OBJECT_FILES) | bin + csc demo/$*.scm $(OBJECT_FILES) -o bin/demo-$* -I bin +``` + +- `make` — builds engine modules only (unchanged) +- `make demos` — builds all 5 demo executables; depends on engine being built first +- Demos are compiled as programs (not units), linked against all engine `.o` files +- `$(OBJECT_FILES)` includes `mixer` and `sound` once those modules are added to `MODULE_NAMES` + +### `render-scene!` nil guards + +`render-scene!` in `renderer.scm` is updated so that entity drawing is **nested inside** the tilemap guard: + +- Tilemap drawing only fires if `(scene-tilemap scene)` is not `#f` +- Entity drawing only fires if **both** `(scene-tilemap scene)` AND `(scene-tileset-texture scene)` are not `#f` + +```scheme +(when tilemap + (draw-tilemap renderer camera tileset-texture tilemap) + (when tileset-texture + (let ((tileset (tilemap-tileset tilemap))) + (draw-entities renderer camera tileset tileset-texture entities)))) +``` + +**Consequence for shmup:** since shmup has no tilemap, the engine will not draw its entities. Shmup draws its player, bullets, and enemies as **colored SDL rectangles** in its `render:` hook — matching the original "colored rects" intent. This is acceptable because shmup entities are simple geometric shapes, not sprites. + +Audio (no scene at all) works because `engine.scm` guards `render-scene!` with `(when (game-scene game) ...)`. + +### CLAUDE.md update + +Add to the Build & Test section: + +> `make demos` must always succeed. A demo that fails to compile is a build failure. Run `make && make demos` to verify both engine and demos build cleanly. + +--- + +## Demo Code Pattern + +Every demo follows this ~30-line structure: + +```scheme +(import (prefix sdl2 "sdl2:") + (prefix sdl2-ttf "ttf:") + (prefix sdl2-image "img:") + downstroke/engine + downstroke/world + downstroke/tilemap + downstroke/renderer + downstroke/input + downstroke/physics + downstroke/assets) + +(define *game* + (make-game + title: "Demo: <Name>" width: 600 height: 400 + preload: (lambda (game) ...) ;; load tilemap, tileset texture, sounds + create: (lambda (game) ...) ;; build scene, place entities + update: (lambda (game dt) ...) ;; input dispatch, physics calls + render: (lambda (game) ...))) ;; HUD overlay (optional) + +(game-run! *game*) +``` + +Tile IDs in entity plists are placeholder values — to be adjusted visually after first run. + +--- + +## The 5 Demos + +### 1. `demo/platformer.scm` — Platformer + +**Systems exercised:** `input`, `physics` (gravity + tile collision), `renderer` (tilemap + entities), `world`/scene, camera follow, audio (sound effect) + +**Mechanics:** +- Player entity with gravity, left/right movement, jump +- Tile collision via `apply-physics` from `physics.scm` +- Camera follows player horizontally +- Jump sound via `(play-sound 'jump)` (loaded in preload: as `'(jump . "demo/assets/jump.wav")`) +- Level: `demo/assets/level-0.tmx` +- Tile IDs: placeholder (user adjusts) + +**Key entity plist:** +```scheme +(list #:type 'player + #:x 100 #:y 50 + #:width 16 #:height 16 + #:vx 0 #:vy 0 + #:gravity? #t + #:on-ground? #f + #:tile-id 1) +``` + +**Update logic:** read input → set `#:vx` from left/right → jump sets `#:vy` → call physics step → update camera x to follow player x. + +--- + +### 2. `demo/shmup.scm` — Shoot-em-up + +**Systems exercised:** `entity` (spawning/removal), manual AABB entity-entity collision (removal-based, not `physics.scm`), `input`, `renderer` (SDL colored rects), `world`/scene (no tilemap) + +**Mechanics:** +- Player ship at bottom, moves left/right +- Space bar fires bullet upward (new entity added to scene) +- Enemies spawn from top at random x positions every N frames, move downward +- Bullet-enemy collision: both entities removed from scene +- No tilemap — plain background (black/SDL clear) +- No gravity on any entity +- `jump.wav` plays on shoot + +**Key entity plists:** (no `#:tile-id` — entities are drawn as colored rects) +```scheme +;; player +(list #:type 'player #:x 280 #:y 360 #:width 16 #:height 16 #:vx 0 #:vy 0) +;; bullet +(list #:type 'bullet #:x px #:y 340 #:width 4 #:height 8 #:vx 0 #:vy -5) +;; enemy +(list #:type 'enemy #:x rx #:y 0 #:width 16 #:height 16 #:vx 0 #:vy 2) +``` + +**Rendering:** shmup has no tilemap, so `render-scene!` draws nothing for this scene. Shmup implements its own `render:` hook using `sdl2:render-fill-rect!` with distinct colors (player = white, bullet = yellow, enemy = red). The camera is at (0,0) so screen coords == world coords. + +**Update logic:** move entities by vx/vy each frame → manual AABB collision check between bullets and enemies (not `resolve-entity-collisions` — shmup uses removal, not push-apart) → filter removed entities from scene → spawn new enemy every 60 frames → read input for player movement and shoot. + +--- + +### 3. `demo/topdown.scm` — Top-down explorer + +**Systems exercised:** `input` (8-directional), `renderer` (tilemap + entity), `world`/scene, camera follow (both axes), `physics` (no gravity) + +**Mechanics:** +- Player entity moves in 8 directions (WASD or arrows) +- No gravity (`#:gravity? #f`) +- Camera follows player on both x and y axes +- Level: `demo/assets/level-0.tmx` (same tilemap, different movement feel) +- Tileset texture loaded in preload: — required for entity sprite rendering (tilemap + tileset-texture both present so render-scene! draws entities via tileset) +- No audio + +**Update logic:** read input → set `#:vx` and `#:vy` from direction keys → apply tile collision (no gravity component) → update camera to center on player. + +--- + +### 4. `demo/audio.scm` — Audio showcase + +**Systems exercised:** audio (sound effects + music), `renderer` (text via `draw-ui-text`), `input`, `assets` + +**Mechanics:** +- Static screen with text instructions rendered via `draw-ui-text` +- Press **J** → play `jump.wav` +- Press **M** → toggle `theme.ogg` music on/off +- Press **Escape** → quit +- No tilemap, no physics, no moving entities +- Uses `DejaVuSans.ttf` for text +- Audio calls via `sound.scm` functions: `load-sounds!`, `play-sound`, `load-music!`, `play-music!`, `stop-music!` (added when copying sound.scm — see File Layout) + +**Display:** colored rectangle background + text labels for each key binding. + +--- + +### 5. `demo/sandbox.scm` — Physics sandbox + +**Systems exercised:** `physics` (gravity + tile collision + entity-entity collision), `renderer`, `world`/scene, no player input + +**Mechanics:** +- 10 entities spawned at random x positions near the top of the screen +- All have `#:gravity? #t` and `#:solid? #t` (required for `resolve-entity-collisions` to participate) +- Physics step runs each frame (gravity accelerates, tile collision stops them) +- Entities rest on floor tiles or bounce (depending on physics.scm behavior) +- No player — pure observation of physics pipeline +- Level: `demo/assets/level-0.tmx` +- After all entities settle (or after 10 seconds), loop: despawn all, respawn at new random positions + +--- + +## Systems Coverage Matrix + +| System / Module | platformer | shmup | topdown | audio | sandbox | +|---|---|---|---|---|---| +| `engine` (make-game, game-run!) | ✓ | ✓ | ✓ | ✓ | ✓ | +| `input` | ✓ | ✓ | ✓ | ✓ | — | +| `physics` (gravity) | ✓ | — | — | — | ✓ | +| `physics` (tile collision) | ✓ | — | ✓ | — | ✓ | +| `physics` (entity collision) | — | — | — | — | ✓ | +| manual AABB (removal) | — | ✓ | — | — | — | +| `renderer` (tilemap) | ✓ | — | ✓ | — | ✓ | +| `renderer` (entities) | ✓ | — | ✓ | — | ✓ | +| `renderer` (SDL colored rects) | — | ✓ | — | — | — | +| `renderer` (text) | — | — | — | ✓ | — | +| `world` / scene | ✓ | ✓ | ✓ | — | ✓ | +| `assets` registry | ✓ | ✓ | ✓ | ✓ | ✓ | +| audio (sound) | ✓ | ✓ | — | ✓ | — | +| audio (music) | — | — | — | ✓ | — | +| camera follow | ✓ (x) | — | ✓ (xy) | — | — | + +--- + +## Out of Scope + +- Animation state machine (`animation.scm`) — not yet extracted to downstroke +- AI (`ai.scm`) — not yet extracted +- Prefab system — not yet extracted +- Scene transitions — Milestone 9 +- Asset-type-specific load helpers (`game-load-tilemap!` etc.) — Milestone 6 |
