use std::path::{Path, PathBuf};
use dotzuki_engine::overworld::types::Direction;
use dotzuki_engine::render::{FrameBuffer, Rgba};
use dotzuki_engine::render_config::RenderConfig;
use dotzuki_renderer::input::{GbButton, InputState};
use dotzuki_runner::headless::save_png;
use dotzuki_runner::{LoadedProject, RunnerGame, RunnerOptions, SCREEN_H, SCREEN_W};
fn snap(game: &mut RunnerGame, dir: &Option<PathBuf>, name: &str) {
let Some(dir) = dir else { return };
let mut fb = FrameBuffer::new(
RenderConfig::new(SCREEN_W as u32, SCREEN_H as u32),
Rgba::BLACK,
);
game.draw(&mut fb);
let path = dir.join(format!("{name}.png"));
save_png(&fb, &path).expect("screenshot");
println!(" [shot] {}", path.display());
}
fn frame(game: &mut RunnerGame, mask: u8) {
let mut input = InputState::new();
input.set_from_bitmask(mask);
game.update(&input);
}
fn idle(game: &mut RunnerGame, n: u32) {
for _ in 0..n {
frame(game, 0);
}
}
fn press(game: &mut RunnerGame, button: GbButton) {
frame(game, button.bit_mask());
idle(game, 1);
}
fn press_a(game: &mut RunnerGame) {
press(game, GbButton::A);
}
fn dismiss(game: &mut RunnerGame) {
for _ in 0..40 {
if game.dialogue_text().is_none() && game.choice_options().is_none() {
return;
}
press_a(game);
}
panic!("dialogue did not close");
}
fn main() {
let mut args = std::env::args().skip(1);
let dir = args.next().expect("usage: menu_accept <project-dir> [shot-dir]");
let shot_dir = args.next().map(PathBuf::from);
let project = LoadedProject::load(Path::new(&dir)).expect("load project");
let mut game = RunnerGame::new(
project,
RunnerOptions {
headless: true,
fresh: true,
rng_script: Some(vec![50, 100, 1]),
..RunnerOptions::default()
},
)
.expect("boot game");
dismiss(&mut game); println!("== 1. the shop ==");
println!("money at boot: {} (manifest shop.startMoney)", game.money());
game.debug_place(12, 10, Direction::Right);
press_a(&mut game);
let rows = game.shop_lines().expect("shop open");
println!("shop shelf: {rows:?}");
snap(&mut game, &shot_dir, "shop");
let before = game.money();
press_a(&mut game);
let rows = game.shop_lines().expect("shop still open");
println!(
"bought a Potion: money {before} → {} G; inventory {:?}",
game.money(),
game.inventory().expect("inventory materialized")
);
println!("shop note: {:?}", rows.last());
snap(&mut game, &shot_dir, "shop-bought");
assert_eq!(game.money(), 80, "20 G charged");
assert_eq!(game.inventory().unwrap().get("potion"), Some(&4));
press_a(&mut game);
press(&mut game, GbButton::B);
let page = game.dialogue_text().expect("scene resumed after the shop");
println!("scene resumed with: {page:?}");
assert!(page.contains("Come again!"), "page: {page:?}");
dismiss(&mut game);
println!("\n== 2. the whiteout ==");
game.debug_place(12, 10, Direction::Left);
press_a(&mut game); press_a(&mut game); let mut last_log: Vec<String> = Vec::new();
for _ in 0..120 {
let Some(battle) = game.battle() else { break };
last_log = battle.log().to_vec();
press_a(&mut game);
}
println!("lost battle log:");
for line in &last_log {
println!(" {line}");
}
assert_eq!(last_log.last().map(String::as_str), Some("You lost the battle…"));
let page = game.dialogue_text().expect("post-lose text");
println!("post-lose text: {page:?}");
assert!(page.contains("no match"), "page: {page:?}");
press_a(&mut game);
assert!(game.whiteout_active(), "whiteout armed");
idle(&mut game, 10);
snap(&mut game, &shot_dir, "whiteout-blackout");
idle(&mut game, 25);
let page = game.dialogue_text().expect("whiteout message");
println!("whiteout message: {page:?}");
assert!(page.contains("collapsed"), "page: {page:?}");
snap(&mut game, &shot_dir, "whiteout-message");
press_a(&mut game);
assert!(!game.whiteout_active());
println!("after the whiteout:");
println!(" map: {:?} tile: {:?}", game.current_map_id(), game.player_tile());
for m in game.party_state().expect("party state") {
println!(" {} hp {} mp {} status {:?}", m.id, m.hp, m.mp, m.status);
}
println!(" __played_main_StartTown flag kept: {}", game.flag("__played_main_StartTown"));
assert_eq!(game.current_map_id(), Some("StartTown"));
assert!(game
.party_state()
.unwrap()
.iter()
.all(|m| m.hp > 0 && m.status.is_none()));
snap(&mut game, &shot_dir, "whiteout-after");
println!("\nacceptance OK");
}