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>
- MRPGI_WEB_GAME env (default games/lost-fuse via .cargo/config.toml)
selects the game include_dir! embeds; web/build.sh <game-dir> stages
just game.json+rooms+sprites (a repo's raw art was ballooning the wasm
to 41MB; now 1.9MB) and ships the game's web/index.html and music/.
- sound.rs: file overrides load through macroquad's loader (fs on native,
HTTP fetch on web), and AudioOut now loads inside a coroutine behind
LazyAudio — browsers gate audio decoding behind a user gesture, and
blocking boot on it was a black screen. First frame draws immediately;
music starts when ready, remembering the requested mood/mute.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- web/manual.html — 17 chapters: playing, the editor deeply, the
authoring cookbook (full JSON reference + the five load-bearing
puzzle patterns), dialogue trees, flags, the art pipeline with
sprite-generation prompts, sound, driving the engine with code
(real JSONL transcripts), AIs + MCP, prompt patterns with actual
outputs (the roof room is the real shipped expansion), the web
build & cloud saves, engine internals, extension recipes,
troubleshooting, and full command/event/palette reference.
- Game menu gains "Manual": window.open on the web (via a JS plugin
import), the default browser natively.
- README tab links the Book; repo README points at it.
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>
- The classic dropdown menus across the top of the play screen: Game
(New Game / Edit Mode / CRT / Sound / About) and Action (Look Around /
Inventory / Help). Pure bus consumers — menu items inject the same
Commands every other surface uses. Sliding along the bar switches
menus, clicks outside close, clicks never leak into click-to-walk.
- web: README tab pinned top-right with the short guide — controls,
editor shortcuts (undo is just Z, no Ctrl), the two-layer paint model,
and why the lake is scenery for now.
- --shot out.png: render a few Play frames and save the real backbuffer
to PNG — ground-truth GPU screenshots for debugging and CI. (Used to
prove the menu colors were right when a preview browser's auto-dark
filter was inverting light neutrals on canvas.)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live at https://monsterrobot.games/engine/ (dealgod VPS, dealgod-nginx
/engine alias over /opt/dealgod/images/engine/).
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>