Headless core (mrpci-core) + macroquad GUI (mrpci), inheriting MRPGI's Command/Event bus architecture and jumping a Sierra generation: - 256-color palettes with live median-cut quantization + palette cycling - four screens: visual / priority / control / hotspot - A* pathfinding with LOS-verified edges and line-exact path following - perspective scale tables (actors shrink toward the horizon) - point-and-click verb bar AND a text parser, one shared verb pipeline - rhai room scripts (on_enter/on_verb/on_trigger + after() tick timers) - deterministic 30Hz cycles, seeded RNG — byte-identical script replays - checkpoint at room entry; death rewinds instead of restarting - save-anywhere slots; multi-voice chip synth; JSONL/HTTP/MCP surfaces - demo game: Neon Precinct (3 rooms, 25 points, one merciful fusebox) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
45 lines
1.5 KiB
Rust
45 lines
1.5 KiB
Rust
//! Golden-path integration tests against the generated demo game.
|
|
//! (Run `mrpci-headless --sample` first; CI does.)
|
|
|
|
use mrpci_core::path::{find_path, nearest_open, Passable};
|
|
use mrpci_core::{Command, GameState, World};
|
|
|
|
fn game_dir() -> Option<String> {
|
|
for p in ["games/neon-precinct", "../games/neon-precinct"] {
|
|
if std::path::Path::new(p).join("game.json").exists() {
|
|
return Some(p.to_string());
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
#[test]
|
|
fn ego_can_cross_neon_row() {
|
|
let Some(dir) = game_dir() else {
|
|
eprintln!("demo game not generated; skipping");
|
|
return;
|
|
};
|
|
let world = World::load_game(dir);
|
|
let mut gs = GameState::new(world);
|
|
gs.apply(Command::NewGame { room: None });
|
|
|
|
let p = Passable {
|
|
s: &gs.world.screens,
|
|
half_w: 8,
|
|
water_ok: false,
|
|
min: (4, 8),
|
|
max: (316, 188),
|
|
};
|
|
let start = (gs.ego.x, gs.ego.y);
|
|
eprintln!("start {:?} open={:?}", start, nearest_open(&p, start));
|
|
eprintln!("goal open={:?}", nearest_open(&p, (316, 150)));
|
|
let path = find_path(&p, start, (316, 150));
|
|
eprintln!("path: {:?}", path);
|
|
assert!(!path.is_empty(), "no path across Neon Row");
|
|
|
|
let evs = gs.apply(Command::WalkTo { x: 316, y: 150 });
|
|
let moved = format!("{:?}", (gs.ego.x, gs.ego.y, gs.world.current));
|
|
eprintln!("after walk: {} events={}", moved, evs.len());
|
|
assert_eq!(gs.world.current, 2, "edge exit east should reach Rain Alley (ego at {})", moved);
|
|
}
|