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>
Point --game at a directory that is not itself a game but contains game
folders, and the GUI boots into a title screen: the collection's name in
gold (optional collection.json {"name": ...}, else the folder name), a
shelf of games sorted by folder with display names from each game.json,
a dimmed live preview of the selected game's start room baked by the
headless renderer, arrow-key selection, Enter to play, and Esc from
inside a game returning to the shelf instead of the desktop.
Shelf names strip a shared "Series — " prefix when every game has one —
the collection title already names the series, and it also sidesteps the
em-dash glyph missing from macroquad's default font.
Core grows world::is_game_dir / scan_collection / collection_name with a
unit test; the GUI grows Mode::Title. --shot pointed at a collection
captures the title screen (that is how the screenshots in this commit's
review were taken). Sample/kit scaffolding is skipped for collection
dirs so a shelf never gets a stray sprites/ folder written into it.
Closes the "title screen / world picker" roadmap item in MANUAL §20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two general-purpose additions, both needed by an external game folder and
useful to any other:
- World::ego_view() — a game folder with sprites/ego.png uses it as the
player avatar instead of the hardcoded party robot (optional ego1.png
gives the walk bob a second cel). Refreshed on LoadGame and ReloadSprites.
- GameManifest.death_text — the death banner was hardcoded to morp2's
"Morp is not cruel; Morp is accurate", which leaked that game's flavour
into every other one. Now a manifest field; the default keeps the old
wording minus the morp2 reference, so existing games are unaffected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
New authoring surface (all serde-defaulted; old games load unchanged):
- RoomDoc: name + enter_text (announced on entry), exit_flags/exit_blocked
(per-direction flag gates with authored refusal lines, latched per attempt)
- ObjDef: points (once per room+name), consumes, kills, visible_flag,
hidden_by_flag; DlgChoice: points (latched on sets_flag flip), kills
(goto node's says = death text)
- GameManifest: max_score enables scoring; GUI shows the classic
'Score: X of Y' in the menu bar; score_changed + died on the bus
- Death restarts the day, keeping the cause at the top of the fresh transcript
Reviewed by an adversarial multi-agent pass; confirmed findings fixed:
corner push-back no longer slingshots through a perpendicular open exit
(and checks the landing pixel), blocked messages latch per attempt,
kill choices pay no points/flags, new_game re-zeroes the score on the bus,
AI-lane prompts respect object visibility. Deliberate behavior change:
drop/consumes now emit inventory_changed (was a silent client desync).
Built for MORPQUEST (games/… ssh://…/monster/mrpquest.git), which uses all
of it: 250-point budget, four gated exits, two deaths, vanishing Beckies.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- mrpgi-vault (standalone crate): tiny signup/login/world-save service.
Argon2 password hashes, random session cookies, JSON-file storage,
naive per-IP rate limit, 4MB body cap. tiny_http, no framework, no db.
Runs as a container on the dealgod network; nginx proxies
/engine/api/ to it.
- core: WorldBundle — a whole world (manifest + every room) as one JSON
document, with World::to_bundle/apply_bundle.
- web bridge: mrpgi_request_save / mrpgi_alloc / mrpgi_world_in exports
+ an mrpgi_world_saved JS plugin import; the page's SAVE button pulls
the live world out of the running engine, LOAD swaps it back in.
SAVE_REQUESTED is an AtomicBool on purpose — wasm's thread-less
Mutex<bool> is a plain static to LLVM and release builds const-folded
the read (the only writer is JS-visible, not IR-visible).
- web UI: SIGN IN tab → create account (own username + password) /
sign in / Save world / Load world / sign out. Session restores on
revisit.
Verified live end-to-end through Cloudflare: signup → save → reload →
anonymous rejected; throwaway account removed after.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- mrpgi-core: ureq/tiny_http/threads are native-only deps; the AI lane
compiles to an always-off stub on wasm32 (deterministic parser stays).
- World::from_memory + assets::cel_from_bytes — boot a game with no
filesystem.
- mrpgi GUI on wasm: embeds games/lost-fuse via include_dir, boots
straight into Play (Tab still opens the editor; saving needs a disk,
so web is play + paint-only for now).
- web/: index.html shell + macroquad JS loader + build.sh producing a
self-contained static web/dist (1.1 MB total, game included).
- .cargo/config.toml: --allow-undefined for miniquad's JS imports.
Verified in-browser: renders, typed commands reach the parser,
click-to-walk works, zero console errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>