MRPCI/README.md
type-two ce42d69a3a v0.5 — playtest-driven fixes + engine/UX upgrades
Bugs found by playing the game over the bus:
- hotspots now have positions: 'walk to precinct door' pathfinds there,
  and touch verbs walk over to hotspots like they do to props/NPCs
- GameState::settle(): the MCP surface resolves walk-then-act in one call
  instead of returning zero events on a distant verb
- typed 'use X on Y' walks over like a clicked use (was unlimited range)
- taking/deleting a solid prop rebakes — no more lingering invisible walls
- sergeant dialogue no longer loops back to its greeting
- serde-facing maps are BTreeMap: room JSON and --sample are byte-stable

Engine:
- sprite-sheet actor views: <name>-sheet<C>x<R>.png slices into C frames x
  R direction loops (vend-bot now rolls on animated treads)
- pixel-accurate click targeting with 1px slop; typed commands click
  mid-sprite (a feet click lands between an actor's legs)
- parser: compass walking (go east / n), 'bye' ends conversations,
  'look' lists exits

GUI:
- hover hot-text: cursor reads the sentence a click would say
- death banner + '+N' score toast

Tests: full-playthrough regression suite (win path, death rewind,
walk-over dialogue, solid-prop footprint) + sheet slicing unit test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 13:38:33 +10:00

145 lines
8.2 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.

# 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, F1F4 pick
one, `I` inventory, **Enter types a command** (the parser is always alive),
F5/F7 save/restore, F9 new game, M mute, N scanlines. The cursor shows the
sentence a click would say — hover anything and it reads `look neon sign`.
## 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
# <name>-sheet<C>x<R>.png = animated actor view:
# C frames across x R direction rows (front/back/left/right,
# trailing rows optional — right mirrors left)
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))
- [x] v0.5 — sprite-sheet actor views (`-sheetCxR`), pixel-accurate click
targeting, hotspots as walk/touch targets, MCP auto-settle (distant
verbs act synchronously), hover hot-text + death banner + score toast
in the GUI, compass walking & `bye`, deterministic room JSON
(BTreeMap), solid-prop footprints cleared on take, full-playthrough
regression tests
- [ ] 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)