diff --git a/mrpgi-core/src/world.rs b/mrpgi-core/src/world.rs index 57335d4..3ee3775 100644 --- a/mrpgi-core/src/world.rs +++ b/mrpgi-core/src/world.rs @@ -498,16 +498,15 @@ impl World { /// dropping `sprites/ego.png` (feet on the bottom edge, like any prop); /// `ego1.png` is an optional second cel for the walk bob. pub fn ego_view(&self) -> view::View { - let cel = |f: &str| { - let p = self.base.join("sprites").join(f); - p.exists().then(|| assets::load_cel(&p.to_string_lossy(), false)) - }; - match cel("ego.png") { + // Read the already-loaded sprite table rather than stat'ing the folder: + // a wasm build is baked into the binary and has no `sprites/` to look at, + // so the filesystem version silently fell back to the robot on the web. + match self.sprite_cel("ego") { Some(c0) => { - let cels = match cel("ego1.png") { - Some(c1) => vec![c0, c1], - None => vec![c0], - }; + let mut cels = vec![c0.clone()]; + if let Some(c1) = self.sprite_cel("ego1") { + cels.push(c1.clone()); + } view::View { loops: vec![view::ViewLoop { cels }] } } None => view::default_robot_view(), diff --git a/mrpgi/src/main.rs b/mrpgi/src/main.rs index 3a7a84b..135fa6d 100644 --- a/mrpgi/src/main.rs +++ b/mrpgi/src/main.rs @@ -26,7 +26,8 @@ fn window_conf() -> Conf { } } -const STATUS_H: f32 = 150.0; +// 7 text rows: wrapped room descriptions routinely run to three or four. +const STATUS_H: f32 = 186.0; /// The classic Sierra menu bar across the top of the play screen. const MENU_H: f32 = 22.0; const MENU_ITEM_H: f32 = 20.0; @@ -49,17 +50,18 @@ enum MenuAct { About, } -const MANUAL_URL: &str = "https://monsterrobot.games/engine/manual.html"; - /// Open the Book: a new tab on the web, the default browser natively. fn open_manual() { #[cfg(target_arch = "wasm32")] unsafe { web_bridge::mrpgi_open_manual(); } + // The repo's own copy, so it always matches this build. The old + // monsterrobot.games/engine/ URL now falls through to the arcade index. #[cfg(not(target_arch = "wasm32"))] { - let _ = std::process::Command::new("open").arg(MANUAL_URL).spawn(); + let p = concat!(env!("CARGO_MANIFEST_DIR"), "/../web/manual.html"); + let _ = std::process::Command::new("open").arg(p).spawn(); } } @@ -758,6 +760,24 @@ fn draw_scanlines(l: &Layout) { } } +/// Greedy word-wrap to a pixel width. A word longer than the line is left to +/// overhang rather than hyphenated — that only happens to URLs. +fn wrap(s: &str, size: f32, max_w: f32) -> Vec { + let mut out: Vec = Vec::new(); + let mut cur = String::new(); + for w in s.split_whitespace() { + let trial = if cur.is_empty() { w.to_string() } else { format!("{} {}", cur, w) }; + if !cur.is_empty() && measure_text(&trial, None, size as u16, 1.0).width > max_w { + out.push(std::mem::take(&mut cur)); + cur = w.to_string(); + } else { + cur = trial; + } + } + out.push(cur); + out +} + fn draw_play_panel(gs: &GameState, command: &str, crt: bool, ai: &str, sound_on: bool) { let sw = screen_width(); let sh = screen_height(); @@ -765,15 +785,23 @@ fn draw_play_panel(gs: &GameState, command: &str, crt: bool, ai: &str, sound_on: draw_rectangle(0.0, top, sw, STATUS_H, color_u8!(12, 14, 18, 255)); draw_line(0.0, top, sw, top, 1.0, color_u8!(60, 70, 85, 255)); + // Room descriptions are whole paragraphs, so wrap to the panel before + // drawing — draw_text will otherwise run a long line off the right edge. + let rows = (((sh - 38.0) - (top + 20.0)) / 18.0).floor().max(1.0) as usize; let mut yy = top + 20.0; - let start = gs.transcript.len().saturating_sub(5); + let start = gs.transcript.len().saturating_sub(rows); + let mut vis: Vec<(String, bool)> = Vec::new(); for line in &gs.transcript[start..] { - let col = if line.starts_with('>') { + let cmd = line.starts_with('>'); + vis.extend(wrap(&ascii_fold(line), 18.0, sw - 24.0).into_iter().map(|l| (l, cmd))); + } + for (line, cmd) in vis.iter().skip(vis.len().saturating_sub(rows)) { + let col = if *cmd { color_u8!(255, 135, 215, 255) } else { color_u8!(205, 213, 225, 255) }; - draw_text(&ascii_fold(line), 12.0, yy, 18.0, col); + draw_text(line, 12.0, yy, 18.0, col); yy += 18.0; }