ego_view() decided whether a game overrode the default party robot by stat'ing sprites/ego.png on disk. A wasm build has no disk — the game is baked into the binary with include_dir — so the check always failed and every web player was a party robot regardless of what the game shipped. Read the already-loaded sprite table instead, which both loaders populate by file stem, so native and web now agree and native does one less read. The transcript was drawn one draw_text per line with no wrapping, so any line wider than the window ran off the right edge and was simply lost. Room descriptions here are whole paragraphs, so this ate the end of most of them. Greedy word-wrap on measured width, and the visible row count is derived from the panel geometry rather than hardcoded at 5. Panel grows 150 -> 186 so a wrapped paragraph fits without pushing its own opening line off the top. Also: Game > Manual opened monsterrobot.games/engine/manual.html, which has not served the manual since that domain moved hosts — it silently returns the arcade index (200, so nothing looked wrong). Point native at the repo's own web/manual.html, which always matches the build. Verified: all 16 policesquadquest win paths still replay to max score, and both fixes confirmed rendering in the native --shot and on the live deploy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .cargo | ||
| assets | ||
| docs | ||
| games | ||
| kit | ||
| mrpgi | ||
| mrpgi-core | ||
| mrpgi-vault | ||
| palettes | ||
| rooms | ||
| sprites | ||
| web | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| EDITOR_GUIDE.md | ||
| MANUAL.md | ||
| README.md | ||
| run.sh | ||
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 (0–15, EGA) | what you see |
| priority | depth bands (4–15) + control lines (0–3) | 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_fitwith 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
Propthat 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_flagpuzzle 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)