Something went wrong. Try again.
HEXWORK - A Lantern in the Deep: 3D ASCII terminal roguelike RPG in Rust.
Entirely designed and built by Muse Spark 1.3 to evaluate if I like the model.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136//! Phase 12 read-only inventory browser process gates.
use super::support::Harness;use hexwork_core::action::{CommittedAction, Direction};use hexwork_core::runtime::ActorRole;use hexwork_save::{decode_run, read_file_bounded, SessionEventKind, MAX_COMPRESSED_BYTES};use std::fs;use std::process::Command;
fn only_run_id(harness: &Harness) -> String { let mut runs = fs::read_dir(harness.data.join("runs")) .unwrap() .map(|entry| entry.unwrap()) .filter(|entry| entry.file_type().unwrap().is_dir()) .map(|entry| entry.file_name().into_string().unwrap()) .collect::<Vec<_>>(); runs.sort(); assert_eq!(runs.len(), 1, "expected one managed run: {runs:?}"); runs.pop().unwrap()}
fn decode_managed_run(harness: &Harness, run_id: &str) -> hexwork_save::DecodedRun { let pack = hexwork_content::RuntimePack::load(&harness.content).unwrap(); decode_run( &read_file_bounded( harness.data.join("runs").join(run_id).join("run.save"), MAX_COMPRESSED_BYTES, ) .unwrap(), pack.hash(), ) .unwrap()}
fn player(run: &hexwork_save::DecodedRun) -> &hexwork_core::actor::Actor { run.current .actors .iter() .find(|actor| { run.current .identity(actor.id) .is_some_and(|identity| identity.role == ActorRole::Player) }) .expect("one saved Player")}
#[test]fn binary_headless_inventory_modal_navigation_is_observational() { let harness = Harness::new("inventory-headless"); let script = harness.script( "inventory.commands", "input inventory\ninput pick2\ninput confirm\ninput south\ninput look\ninput inventory\ninput west\nobserve state\nquit\n", ); let output = Harness::run(harness.managed(&script)); assert!( output.status.success(), "stdout={} stderr={}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); let records = Harness::records(&output); assert_eq!( records .iter() .filter(|record| record["record_kind"] == "command_ack") .count(), 8 ); let observation = records .iter() .find(|record| record["record_kind"] == "observation") .expect("state observation after browser and movement"); assert_eq!(observation["count"], 1);
let run_id = only_run_id(&harness); let saved = decode_managed_run(&harness, &run_id); assert_eq!(saved.events.len(), 1); assert!(matches!( &saved.events[0].kind, SessionEventKind::Action(CommittedAction::Move(Direction::West)) )); // Browser navigation is observational: only the single paced west step is // journaled, and it advances 300 mm (one 3000 mm/s tick) inside the spawn // cell `[21,31,8]` rather than crossing into `[20,31,8]`. let feet = player(&saved).position.millimetres(); assert_eq!(player(&saved).position.cell(), [21, 31, 8]); assert_eq!(feet[0], 21_500 - 300, "one paced west step"); assert_eq!([feet[1], feet[2]], [31_500, 8_000]);}
#[test]#[cfg(any(target_os = "linux", target_os = "macos"))]fn binary_pty_inventory_select_look_close_then_move() { let harness = Harness::new("inventory-pty"); let scenario = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/pty/inventory_scenario.py"); let output = Harness::run({ let mut command = Command::new("python3"); command .current_dir(&harness.cwd) .arg(scenario) .arg(env!("CARGO_BIN_EXE_hexwork")) .arg(&harness.data) .arg(&harness.content); command }); assert!( output.status.success(), "inventory PTY failed: stdout={} stderr={}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); let summary: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); assert_eq!(summary["exit"], 0); assert_eq!(summary["list_visible"], true); assert_eq!(summary["different_item_selected"], true); assert_eq!(summary["detail_visible"], true); assert_eq!(summary["closed_without_exit"], true); assert_eq!(summary["session_over"], true);
let run_id = summary["run_id"].as_str().unwrap(); let saved = decode_managed_run(&harness, run_id); assert_eq!(saved.identity.display_name, "the delver"); assert_eq!(saved.events.len(), 1); assert!(matches!( &saved.events[0].kind, SessionEventKind::Action(CommittedAction::Move(Direction::West)) )); // One paced west step (3000 mm/s over one 100 ms world tick) stays inside the // spawn cell `[21,31,8]`; it does not cross into `[20,31,8]`. let feet = player(&saved).position.millimetres(); assert_eq!(player(&saved).position.cell(), [21, 31, 8]); assert_eq!(feet[0], 21_500 - 300, "one paced west step"); assert_eq!([feet[1], feet[2]], [31_500, 8_000]);}