MRPGI/README.md
type-two 96b45561d0 Carve the engine into a headless core behind a command/event bus
The handover plan (docs/HANDOVER.md), P0-P6, in one pass. One insight
powers it: CLI/HTTP/MCP/Python is one feature, not four — a headless
core driven by Commands in, Events out, with every surface a thin
adapter.

- mrpgi-core: the whole sim as a library with zero macroquad (the
  workspace boundary is the guardrail). GameState::apply/tick, fixed
  1/20s cycles, deterministic replays, headless RGBA/PNG rendering.
- mrpgi-headless: JSONL stdio REPL, --script replay (AI lane off =
  byte-identical), --render-room PNG, --serve HTTP (state/command/
  events cursor/frame.png), --mcp JSON-RPC stdio server whose
  render_frame returns a real PNG so a model sees its own moves.
- Games are folders: game.json manifest (all fields default — legacy
  dirs still load), data-driven verb table feeding both the parser and
  the AI whitelist, flags with requires_flag/sets_flag gating on
  objects and dialogue choices, live UpsertRoom lore ingestion with
  validation.
- mrpgi (GUI): now one consumer of the bus; editor commits through the
  same commands MCP uses; audio became core events with per-game
  sfx/ music/ file overrides beating the chiptune synth.
- Tests: parser goldens + a full win-path playthrough asserting
  byte-identical transcripts across runs.

Deleted: the old fused src/ (root is now a virtual workspace), the
dead demo Scene/Assets path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 09:18:33 +10:00

111 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MRPGI — Monster Robot Party Game Interpreter
A **new-school, Apple-Silicon-native reimagining of Sierra's 1980s AGI engine**
the tech behind *King's Quest*, *Space Quest*, and *Police Quest* — rebuilt in
Rust + [macroquad](https://macroquad.rs) so you can make cool, fun adventure games.
It keeps the *soul* of AGI (vector rooms, the invisible priority/depth screen,
loops-and-cels sprites, a verb-noun parser, flags & vars, the ego) and bridges
it forward (Retina-crisp rendering, hand-editable text resources, a real
embedded scripting language, and an optional AI parser).
## Run it
```sh
cargo run -p mrpgi # the GUI (paint + play) — or ./run.sh
cargo run -p mrpgi --release # smooth, optimized build
```
A window opens in the editor. Paint a room (or launch with `--sample` for the
3-room demo game), then press **Tab** to walk it — arrows or click to move,
type verbs to play. `F1` toggles CRT scanlines, `F2` sound, `Esc` quits.
## Drive it from anywhere (headless)
The engine is a headless core behind a typed command/event bus; the GUI is
just one consumer. The same bus is exposed as JSONL stdio, HTTP, and MCP:
```sh
cargo run -p mrpgi-core --bin mrpgi-headless -- --sample --game games/demo
echo '{"cmd":"parse","text":"look"}' | cargo run -p mrpgi-core --bin mrpgi-headless -- --game games/demo
mrpgi-headless --serve 127.0.0.1:7777 --game games/demo # HTTP + /frame.png
claude mcp add mrpgi -- mrpgi-headless --mcp --game games/demo # Claude plays & authors
```
A game is a folder of JSON + PNGs — an LLM expansion is just JSON that
deserializes. See **[docs/CONTROL.md](docs/CONTROL.md)** for the full guide
(commands, events, HTTP routes, MCP tools, authoring JSON reference).
## How it works (the AGI model, faithfully)
Every room is drawn into **two buffers at once**:
| Buffer | What it holds | Why |
|--------|---------------|-----|
| **visual** | palette indices (015, EGA) | what you see |
| **priority** | depth bands (415) + control lines (03) | depth sorting & collision |
Lower on screen ⇒ higher priority ⇒ drawn nearer the camera. The ego is drawn a
pixel at a time, *only where its priority ≥ the scenery's* — that's the whole
"walk behind the tree" trick, with zero per-object Z bookkeeping. Control lines
(priority `0`) are walls the ego can't cross.
## Source map
Two crates: **`mrpgi-core`** (the headless engine — zero windowing deps, a
compile error is the guardrail) and **`mrpgi`** (the macroquad GUI).
| File | Role |
|------|------|
| `mrpgi-core/src/state.rs` | **the spine**: `GameState`, `Command`/`Event` bus, fixed-step `tick` |
| `mrpgi-core/src/world.rs` | rooms/objects/dialogue data model (all serde), `World`, game folders |
| `mrpgi-core/src/parser.rs` | data-driven verb table, verb-noun parser, dialogue state |
| `mrpgi-core/src/render.rs` | headless compositing → RGBA / PNG (the priority-buffer trick) |
| `mrpgi-core/src/framebuffer.rs` | the visual + priority buffers and the `band(y)` depth table |
| `mrpgi-core/src/picture.rs` | vector room painting (rects / lines / fills / stamps) |
| `mrpgi-core/src/view.rs` | sprites as loops + cels, authored from tiny ASCII grids |
| `mrpgi-core/src/sprite.rs` | the ego: movement, click-to-walk, collision, walk animation |
| `mrpgi-core/src/assets.rs` | PNG → EGA quantize + dither; sprite folders; kit art |
| `mrpgi-core/src/audio.rs` | chiptune synth + audio cues (played by front-ends, or files) |
| `mrpgi-core/src/ai.rs` | AI parser lane (Ollama / OpenRouter / off) on a worker thread |
| `mrpgi-core/src/kit.rs` | sample game + fantasy starter kit, built from the real types |
| `mrpgi-core/src/bin/mrpgi-headless/` | JSONL stdio, `--script` replay, `--render-room`, HTTP, MCP |
| `mrpgi/src/main.rs` | window, play loop over the bus, HUD |
| `mrpgi/src/editor.rs` | the in-engine paint tool (edits go through the same bus) |
| `mrpgi/src/sound.rs` | audio out: game-folder files override the built-in synth |
## Asset pipeline (the new-school bit)
Images don't sit *on top* of the game — they're folded *into* it. Any PNG is
quantized to the EGA palette (optional Bayer dithering for smooth retro
gradients) and becomes indexed pixels, so from that moment it depth-sorts, gets
occluded, and walks-behind exactly like hand-authored art. Three ways in:
- **Tile** an image across a region (textures — e.g. the floor)
- **Pour** an image into a shape via `stamp_cel_fit` with a fit mode
(stretch / tile / center) — *the shape also stamps depth & collision.*
This is the "draw a shape, load an image into it" idea, realized.
- **Objects** — a PNG becomes a `Prop` that sorts with the ego
Drop your own art into `assets/`, then press **R** in-game to hot-reload it.
## Roadmap
- [x] **v0.1 — rendering core**: vector room, priority depth, walkable ego, click-to-walk, CRT toggle
- [x] **v0.2 — asset pipeline**: PNG → EGA quantize + dither, tile / pour-into-shape / object insertion, hot-reload (`R`)
- [x] **v0.3 — room editor**: in-engine paint tool with edit/play modes, dual-layer LOOK+MEANING painting, flood-fill, tools (brush/line/rect/fill/pick/erase), undo, and JSON room save/load — see [EDITOR_GUIDE.md](EDITOR_GUIDE.md)
- [ ] **Priority-mask backgrounds**: a full-room art PNG + a companion mask PNG whose colors encode depth bands & control lines (the pro art workflow)
- [ ] **True-color "HD" layer mode**: skip quantization for painted backdrops; the priority buffer stays indexed
- [ ] **Polygon shapes** for `stamp_cel_fit` (not just rectangles)
- [ ] **Directional views** (4 loops + horizontal mirroring) for the robot
- [x] **Text parser**: data-driven verb table with synonyms, verb-noun matching, on-screen input line
- [x] **Room transitions**, inventory, flags (with `requires_flag`/`sets_flag` puzzle gating)
- [x] **AI parser adapter**: when the classic parse misses, an LLM maps free text onto an *already-allowed* command (deterministic core stays authoritative; works offline without it)
- [x] **Loadable game folders** (`game.json` + JSON rooms + PNGs) so games ship without recompiling the engine
- [x] **Headless core + control surfaces**: JSONL stdio, script replay, HTTP, MCP, headless PNG rendering — see [docs/CONTROL.md](docs/CONTROL.md)
- [ ] **Rhai room logic**: rooms scripted in an AGI-flavored embedded language (`if said(...) { print(...) }`), hot-reloadable
- [ ] **3-voice + noise chiptune synth** (per-game audio files already override the square-wave synth)
- [ ] **Strudel sink**: browser sidecar over the HTTP surface, driven by the same audio events
- [ ] **Vars & conditions on dialogue/objects** beyond boolean flags; cross-room NPC state
- [ ] **Pathfinding** for click-to-walk (today: greedy steering)