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>
84 lines
3.3 KiB
Bash
Executable File
84 lines
3.3 KiB
Bash
Executable File
#!/bin/sh
|
|
# Build the browser version of MRPGI into web/dist/ (self-contained, static).
|
|
# Requires the wasm target: rustup target add wasm32-unknown-unknown
|
|
#
|
|
# ./web/build.sh # the default game (games/lost-fuse)
|
|
# ./web/build.sh /path/to/game # bake any game folder into the wasm;
|
|
# # its music/ and web/index.html ride along
|
|
# ./web/build.sh /path/to/cases # a folder of game folders bakes the whole
|
|
# # shelf and boots the title screen picker
|
|
#
|
|
# Deploy (engine live at https://monsterrobot.games/engine/):
|
|
# scp web/dist/* root@100.94.195.115:/opt/dealgod/images/engine/
|
|
# Served by the dealgod-nginx container via a `location /engine/` alias in
|
|
# /opt/dealgod/nginx/dealgod-servers.conf. NOTE: edit that file IN PLACE
|
|
# (python open("w") / tee), never `sed -i` — it's a single-file bind mount
|
|
# and an inode swap desyncs the container until a `docker restart`.
|
|
set -e
|
|
cd "$(dirname "$0")/.."
|
|
|
|
# rustc/cargo on PATH may be Homebrew's, which ships no wasm32 std even when
|
|
# `rustup target list` shows it installed. Use the rustup toolchain outright.
|
|
if command -v rustup >/dev/null 2>&1; then
|
|
TC_BIN="$(dirname "$(rustup which cargo)")"
|
|
CARGO="$TC_BIN/cargo"
|
|
RUSTC="$TC_BIN/rustc"
|
|
export RUSTC
|
|
PATH="$TC_BIN:$PATH"
|
|
export PATH
|
|
else
|
|
CARGO=cargo
|
|
fi
|
|
|
|
# Stage only what the engine reads — include_dir! embeds the whole tree, and a
|
|
# game repo also carries raw art, tools, tests, .git.
|
|
stage_game() { # $1 = source game dir, $2 = destination
|
|
mkdir -p "$2"
|
|
cp "$1/game.json" "$2/" 2>/dev/null || true
|
|
cp -R "$1/rooms" "$1/sprites" "$2/" 2>/dev/null || true
|
|
}
|
|
|
|
GAME="${1:-}"
|
|
if [ -n "$GAME" ]; then
|
|
SRC="$(cd "$GAME" && pwd)"
|
|
STAGE="$(mktemp -d)/game"
|
|
mkdir -p "$STAGE"
|
|
if [ -f "$SRC/game.json" ] || [ -d "$SRC/rooms" ]; then
|
|
stage_game "$SRC" "$STAGE"
|
|
echo "baking game: $SRC"
|
|
else
|
|
# A COLLECTION: a folder of game folders. Bakes the whole shelf, and
|
|
# the engine boots into its title screen / world picker.
|
|
n=0
|
|
for d in "$SRC"/*/; do
|
|
[ -f "$d/game.json" ] || [ -d "$d/rooms" ] || continue
|
|
stage_game "$d" "$STAGE/$(basename "$d")"
|
|
n=$((n + 1))
|
|
done
|
|
[ -f "$SRC/collection.json" ] && cp "$SRC/collection.json" "$STAGE/"
|
|
[ "$n" -gt 0 ] || { echo "no games found in $SRC"; exit 1; }
|
|
echo "baking collection: $SRC ($n games)"
|
|
fi
|
|
MRPGI_WEB_GAME="$STAGE"
|
|
export MRPGI_WEB_GAME
|
|
fi
|
|
# include_dir! reads the env var at compile time; make sure the crate recompiles.
|
|
touch mrpgi/src/main.rs
|
|
|
|
"$CARGO" build -p mrpgi --release --target wasm32-unknown-unknown
|
|
rm -rf web/dist
|
|
mkdir -p web/dist
|
|
cp target/wasm32-unknown-unknown/release/mrpgi.wasm web/dist/
|
|
cp web/index.html web/manual.html web/mq_js_bundle.js web/dist/
|
|
|
|
if [ -n "$GAME" ]; then
|
|
# A game can ship its own page shell and music; both are optional.
|
|
[ -f "$SRC/web/index.html" ] && cp "$SRC/web/index.html" web/dist/
|
|
if [ -d "$SRC/music" ]; then
|
|
mkdir -p web/dist/music
|
|
cp "$SRC"/music/*.wav web/dist/music/ 2>/dev/null || true
|
|
cp "$SRC"/music/*.ogg "$SRC"/music/*.mp3 web/dist/music/ 2>/dev/null || true
|
|
fi
|
|
fi
|
|
echo "web build ready: $(du -sh web/dist | cut -f1) in web/dist/"
|