monster robot party game interpreter
Go to file
type-two 827797ad1f Web: bake any game into the wasm; never block first paint on audio
The title screen / world picker now works in the browser, not just natively.
`./web/build.sh <folder-of-game-folders>` stages every game inside it plus
collection.json and bakes the lot into one wasm; the engine boots into the
shelf exactly as it does on the desktop. Sixteen cases of Police Squad Quest
come to 2.1 MB total.

- embedded_world() becomes embedded_world_at(prefix), so one bundle can hold
  many games; embedded_games() / embedded_collection_name() are the browser
  twins of world::scan_collection / collection_name.
- The picker is now keyed by String on both platforms and resolved through one
  load_by_key() — a filesystem path natively, an embedded subdirectory in the
  browser — so Mode::Title has a single implementation.
- Esc is a no-op on the web when there is nowhere to back out to. Breaking the
  main loop in a browser just leaves a dead black canvas; found by pressing it.
- ascii_fold() on every game-text draw. macroquad's built-in font has no em
  dash, curly quote, star or dagger, so room names like "— Squad Room —" and
  every death card were drawing tofu boxes in both builds. Folding at the draw
  layer fixes the whole engine without making game authors type worse prose.
- web/build.sh uses the rustup toolchain explicitly: cargo/rustc on PATH may be
  Homebrew's, which ships no wasm32 std even though `rustup target list` shows
  the target installed, and the resulting "can't find crate for core" is a
  thoroughly misleading error.

Verified in a real browser: shelf renders, arrows move the selection and swap
the live room preview, Enter loads and plays the case, Esc returns to the shelf
with the selection preserved. Native --shot and all 13 core tests still pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 18:39:48 +10:00
.cargo Web: bake any game into the wasm; never block first paint on audio 2026-07-05 15:53:22 +10:00
assets Initial commit: MRPGI — Apple-Silicon AGI adventure engine 2026-06-17 17:26:55 +10:00
docs Sierra soul for game authors: scoring, deaths, gated exits, room prose, visibility flags 2026-07-05 15:30:32 +10:00
games Add the updated manual and a teaching game: The DJ's Lost Fuse 2026-07-05 09:18:50 +10:00
kit Initial commit: MRPGI — Apple-Silicon AGI adventure engine 2026-06-17 17:26:55 +10:00
mrpgi Web: bake any game into the wasm; never block first paint on audio 2026-07-27 18:39:48 +10:00
mrpgi-core Title screen / world picker for game collections 2026-07-26 21:50:13 +10:00
mrpgi-vault Untrack mrpgi-vault build artifacts (254 compiled files; also carried stray game-name strings) 2026-07-26 10:44:35 +10:00
palettes Initial commit: MRPGI — Apple-Silicon AGI adventure engine 2026-06-17 17:26:55 +10:00
rooms Initial commit: MRPGI — Apple-Silicon AGI adventure engine 2026-06-17 17:26:55 +10:00
sprites Initial commit: MRPGI — Apple-Silicon AGI adventure engine 2026-06-17 17:26:55 +10:00
web Web: bake any game into the wasm; never block first paint on audio 2026-07-27 18:39:48 +10:00
.gitignore Untrack mrpgi-vault build artifacts (254 compiled files; also carried stray game-name strings) 2026-07-26 10:44:35 +10:00
Cargo.lock GUI: enable macroquad audio feature (cues and music were silently disabled) 2026-07-26 10:30:38 +10:00
Cargo.toml Carve the engine into a headless core behind a command/event bus 2026-07-05 09:18:33 +10:00
CLAUDE.md CLAUDE.md: this repo is the engine; games (morpquest, policesquadquest) are separate repos 2026-07-26 10:39:13 +10:00
EDITOR_GUIDE.md Initial commit: MRPGI — Apple-Silicon AGI adventure engine 2026-06-17 17:26:55 +10:00
MANUAL.md Web: bake any game into the wasm; never block first paint on audio 2026-07-27 18:39:48 +10:00
README.md THE MRPGI BOOK: deep combined user/dev manual, linked from the live menus 2026-07-05 11:49:24 +10:00
run.sh Carve the engine into a headless core behind a command/event bus 2026-07-05 09:18:33 +10:00

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 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

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:

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 for the full guide (commands, events, HTTP routes, MCP tools, authoring JSON reference).

📖 The deep read: THE MRPGI BOOK — the combined user/dev manual with the authoring cookbook, AI prompt patterns with real outputs, and engine internals. Live at monsterrobot.games/engine/manual.html and under Game → Manual in the engine itself.

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

  • v0.1 — rendering core: vector room, priority depth, walkable ego, click-to-walk, CRT toggle
  • v0.2 — asset pipeline: PNG → EGA quantize + dither, tile / pour-into-shape / object insertion, hot-reload (R)
  • 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
  • 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
  • Text parser: data-driven verb table with synonyms, verb-noun matching, on-screen input line
  • Room transitions, inventory, flags (with requires_flag/sets_flag puzzle gating)
  • 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)
  • Loadable game folders (game.json + JSON rooms + PNGs) so games ship without recompiling the engine
  • Headless core + control surfaces: JSONL stdio, script replay, HTTP, MCP, headless PNG rendering — see 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)