F8 toggles edit mode. Tools: brush/line/rect/ellipse/flood/gradient/picker; ink meanings write LOOK/FLOOR/WALL/WATER/DEPTH across the four screens; V overlays priority/control/hotspot; hotspot rects, prop placement, spawn, and a :colon command line (save/name/enter/music/weather/exit/room/hotmsg). Every editor action is a bus Command (paint_op, undo_op, clear_ops, set_spawn/exit/music/weather/room_name/enter_text, upsert/delete hotspot+prop) so MCP clients wield identical powers. PicOp::Brush added; docs/EDITOR.md. Verified: 5 suites green, playthrough events byte-identical to pre-editor build, GUI boots. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
135 lines
7.5 KiB
Markdown
135 lines
7.5 KiB
Markdown
# MRPCI — Monster Robot Party Creative Interpreter
|
||
|
||
The **SCI generation** of the Monster Robot Party engine family. Where
|
||
[MRPGI](../mrpgi) reimagines Sierra's 1980s AGI (16 colors, text parser,
|
||
one merged priority buffer), MRPCI reimagines the tech behind *King's Quest V*
|
||
and *Space Quest IV* — and then fixes everything Sierra never could:
|
||
|
||
| Sierra SCI (1990) | MRPCI (now) |
|
||
|---|---|
|
||
| 256-color VGA, hand-cut palettes | 256-color palettes **quantized live from any PNG** (median cut), EGA/gray/ember ranges kept stable for sprites & cycles |
|
||
| Palette cycling (waterfalls, neon) | Same trick, `cycles` are room data + script-toggleable |
|
||
| Priority + control screens | **Four** screens: visual / priority / control / hotspot — hit-testing any pixel is one array read |
|
||
| Polygon avoidance that wedged egos into corners | Real **A\*** with LOS-verified edges + string-pulling; actors walk the exact verified line, so a legal path can never clip |
|
||
| Scale tables (SCI1.1) | Per-room perspective: actors shrink toward the horizon, walk speed scales to match |
|
||
| CPU-speed timers → Error 52, instant Sequel Police | Fixed **30Hz deterministic cycles**; `after()` timers count ticks; the only RNG is seeded xorshift — replays are byte-identical |
|
||
| `Error 47: Not an object` → crash to DOS | Missing resources = placeholders + an `error` event. A compile-error script = a log line, not a hang |
|
||
| Die 3 hours after the dead-end | **Checkpoint at every room entry; death rewinds to it.** Save-anywhere slots on top |
|
||
| OOP bytecode scripts, priest-tier tooling | **rhai** room scripts: `on_enter`, `on_verb(v,n)`, `on_trigger`, `after(ticks,"fn")` — hot-swappable over the bus |
|
||
| Icon bar (SCI1) *or* parser (SCI0) | **Both.** Clicks and typed commands converge on one verb pipeline |
|
||
|
||
The architecture is inherited from MRPGI and kept sacred: **`mrpci-core` is
|
||
headless** (zero windowing deps — a compile error is the guardrail). The
|
||
whole sim is a `GameState` behind a typed Command/Event bus, and every
|
||
control surface — GUI, JSONL stdio, HTTP, MCP — is a thin adapter.
|
||
|
||
## Run it
|
||
|
||
```sh
|
||
cargo run -p mrpci-core --bin mrpci-headless -- --sample # write the demo game
|
||
cargo run -p mrpci # play Neon Precinct
|
||
```
|
||
|
||
**Neon Precinct**: you are Officer Morp-9. Rain, neon that actually cycles,
|
||
a vend-bot with a tip, a keycard behind crates (watch the A\* go around),
|
||
a lethal fusebox (watch death *not* ruin your evening), and a case to close.
|
||
25 points.
|
||
|
||
Controls: left-click applies the verb, right-click cycles verbs, F1–F4 pick
|
||
one, `I` inventory, **Enter types a command** (the parser is always alive),
|
||
F5/F7 save/restore, F9 new game, M mute, N scanlines.
|
||
|
||
## Drive it headless (the point)
|
||
|
||
```sh
|
||
cargo build -p mrpci-core
|
||
target/debug/mrpci-headless --game games/neon-precinct # JSONL stdio
|
||
target/debug/mrpci-headless --game games/neon-precinct --serve 8093 # HTTP
|
||
target/debug/mrpci-headless --game games/neon-precinct --mcp # MCP for Claude
|
||
target/debug/mrpci-headless --game games/neon-precinct --script play.jsonl # golden replay
|
||
target/debug/mrpci-headless --game games/neon-precinct --room 2 --ticks 45 --render-room out.png
|
||
target/debug/mrpci-headless --game games/neon-precinct --screens shots/ # all four screens
|
||
```
|
||
|
||
Register with Claude:
|
||
|
||
```sh
|
||
claude mcp add mrpci -- $PWD/target/debug/mrpci-headless --mcp --game $PWD/games/neon-precinct
|
||
```
|
||
|
||
Claude can then *play* the game (verbs, walks, dialogue), *see* it
|
||
(`mrpci_render_frame`, plus the invisible priority/control/hotspot screens),
|
||
and *author* it (upsert rooms and rhai scripts live, validated before commit).
|
||
See [docs/CONTROL.md](docs/CONTROL.md) for the full bus reference.
|
||
|
||
## A game is a folder
|
||
|
||
```
|
||
games/mygame/
|
||
game.json # manifest: name, start_room, intro, verbs, flags, max_score
|
||
rooms/room0.json # RoomDoc: ops (paint), background, palette, cycles, scale,
|
||
# spawn, exits, hotspots, props, npcs, music, script
|
||
pics/street.png # optional backgrounds (+ street-pri.png / street-ctl.png masks)
|
||
sprites/*.png # quantized per-room; filename = sprite name
|
||
scripts/*.rhai # main.rhai + room scripts
|
||
sfx/ music/ # optional audio overrides (else the built-in chip synth plays)
|
||
saves/ # save-anywhere slots (gitignored)
|
||
```
|
||
|
||
Rooms paint with `PicOp`s — rects, polygons, floods, **dithered gradients**,
|
||
plus procedural fills: **fBm noise** (clouds, asphalt, grime), **Voronoi
|
||
cells** (cobbles, slabs) and **scatter** (stars, glints) — all seeded, all
|
||
deterministic — through an `Ink` that writes any subset of the four screens
|
||
at once. Or drop a PNG in `pics/` and let the quantizer fold it into the
|
||
palette. Rooms can also set `weather` (rain / snow / embers): a particle
|
||
layer on its own RNG stream, so visuals never perturb gameplay replays.
|
||
|
||
## Source map
|
||
|
||
| Crate / file | Role |
|
||
|---|---|
|
||
| `mrpci-core/screens.rs` | the four aligned screens + depth bands |
|
||
| `mrpci-core/palette.rs` | 256-color palettes, cycling LUTs |
|
||
| `mrpci-core/pic.rs` | vector paint ops, Bayer-dithered gradients |
|
||
| `mrpci-core/assets.rs` | median-cut quantizer, masks, sprite pipeline |
|
||
| `mrpci-core/view.rs` | loops/cels, ASCII sprites, mirroring |
|
||
| `mrpci-core/actor.rs` | ego + NPCs: scaling, line-exact path following |
|
||
| `mrpci-core/path.rs` | A\*, LOS smoothing, `walk_line` |
|
||
| `mrpci-core/room.rs` | RoomDoc / World / game folders / validation |
|
||
| `mrpci-core/script.rs` | rhai host: hooks, Fx airlock, tick timers |
|
||
| `mrpci-core/state.rs` | **the bus**: GameState, Command/Event, checkpoints |
|
||
| `mrpci-core/render.rs` | headless compositing → RGBA/PNG |
|
||
| `mrpci-core/audio.rs` | multi-voice chip synth (square/pulse/tri/noise) |
|
||
| `mrpci-core/bin/mrpci-headless/` | stdio / HTTP / MCP / replay / sample |
|
||
| `mrpci/` | the macroquad GUI (icon bar, dialogue windows, sound) |
|
||
|
||
Headless frames are **annotated**: `--render-room`, HTTP `/frame.png` and
|
||
MCP `mrpci_render_frame` stamp the room name, score, open dialogue window
|
||
and transcript strip into the frame with the built-in 5x7 font — an LLM
|
||
sees exactly what a player reads (add `--raw` / `/frame-raw.png` to skip).
|
||
The GUI renders via a **GPU palette shader**: the frame ships to the card
|
||
as indices + a 256x1 palette strip, and the fragment shader does the lookup
|
||
plus the whole CRT (curvature, scanlines, vignette, room fades).
|
||
|
||
## The AI parser lane
|
||
|
||
When the deterministic parser shrugs at a typed sentence, a small local LLM
|
||
(Ollama by default; OpenRouter optional) translates it into ONE command from
|
||
the *already-legal* verb/noun tables — then that command runs through the
|
||
real game logic. The AI can suggest; only the sim decides. Configure with
|
||
`MRPCI_AI` (`ollama`/`openrouter`/`off`), `MRPCI_AI_MODEL`, `MRPCI_AI_URL`.
|
||
`--script` replays force the lane off, so golden tests stay byte-identical.
|
||
|
||
## Roadmap
|
||
|
||
- [x] v0.1 — the engine above + Neon Precinct
|
||
- [x] v0.2 — procedural fills (fBm/Voronoi/scatter), weather particles,
|
||
in-core font + annotated frames, GPU palette/CRT shader
|
||
- [x] v0.3 — world bundles carry art (`--export-bundle`/`--bundle`, the
|
||
wasm path), AI parser lane, dialogue talker portraits
|
||
- [x] v0.4 — in-engine room editor: F8, four-screen paint meanings,
|
||
overlay views, hotspot/prop placement, `:` commands — all over the
|
||
bus, so MCP clients wield identical powers ([docs/EDITOR.md](docs/EDITOR.md))
|
||
- [ ] Priority-mask art workflow polish (paint depth in any image editor)
|
||
- [ ] wasm build → see the [MRP3GI](../mrp3gi) sibling (three.js + rigged GLBs over this core)
|