menu_accept/
menu_accept.rs1use std::path::{Path, PathBuf};
23
24use dotzuki_engine::overworld::types::Direction;
25use dotzuki_engine::render::{FrameBuffer, Rgba};
26use dotzuki_engine::render_config::RenderConfig;
27use dotzuki_renderer::input::{GbButton, InputState};
28use dotzuki_runner::headless::save_png;
29use dotzuki_runner::{LoadedProject, RunnerGame, RunnerOptions, SCREEN_H, SCREEN_W};
30
31fn snap(game: &mut RunnerGame, dir: &Option<PathBuf>, name: &str) {
33 let Some(dir) = dir else { return };
34 let mut fb = FrameBuffer::new(
35 RenderConfig::new(SCREEN_W as u32, SCREEN_H as u32),
36 Rgba::BLACK,
37 );
38 game.draw(&mut fb);
39 let path = dir.join(format!("{name}.png"));
40 save_png(&fb, &path).expect("screenshot");
41 println!(" [shot] {}", path.display());
42}
43
44fn frame(game: &mut RunnerGame, mask: u8) {
45 let mut input = InputState::new();
46 input.set_from_bitmask(mask);
47 game.update(&input);
48}
49
50fn idle(game: &mut RunnerGame, n: u32) {
51 for _ in 0..n {
52 frame(game, 0);
53 }
54}
55
56fn press(game: &mut RunnerGame, button: GbButton) {
57 frame(game, button.bit_mask());
58 idle(game, 1);
59}
60
61fn press_a(game: &mut RunnerGame) {
62 press(game, GbButton::A);
63}
64
65fn dismiss(game: &mut RunnerGame) {
67 for _ in 0..40 {
68 if game.dialogue_text().is_none() && game.choice_options().is_none() {
69 return;
70 }
71 press_a(game);
72 }
73 panic!("dialogue did not close");
74}
75
76fn main() {
77 let mut args = std::env::args().skip(1);
78 let dir = args
79 .next()
80 .expect("usage: menu_accept <project-dir> [shot-dir]");
81 let shot_dir = args.next().map(PathBuf::from);
82
83 let project = LoadedProject::load(Path::new(&dir)).expect("load project");
84 let mut game = RunnerGame::new(
85 project,
86 RunnerOptions {
87 headless: true,
88 fresh: true,
89 rng_script: Some(vec![50, 100, 1]),
91 ..RunnerOptions::default()
92 },
93 )
94 .expect("boot game");
95
96 dismiss(&mut game); println!("== 1. the shop ==");
98 println!("money at boot: {} (manifest shop.startMoney)", game.money());
99
100 game.debug_place(12, 10, Direction::Right);
102 press_a(&mut game);
103 let rows = game.shop_lines().expect("shop open");
104 println!("shop shelf: {rows:?}");
105 snap(&mut game, &shot_dir, "shop");
106
107 let before = game.money();
109 press_a(&mut game);
110 let rows = game.shop_lines().expect("shop still open");
111 println!(
112 "bought a Potion: money {before} → {} G; inventory {:?}",
113 game.money(),
114 game.inventory().expect("inventory materialized")
115 );
116 println!("shop note: {:?}", rows.last());
117 snap(&mut game, &shot_dir, "shop-bought");
118 assert_eq!(game.money(), 80, "20 G charged");
119 assert_eq!(game.inventory().unwrap().get("potion"), Some(&4));
120 press_a(&mut game); press(&mut game, GbButton::B);
124 let page = game.dialogue_text().expect("scene resumed after the shop");
125 println!("scene resumed with: {page:?}");
126 assert!(page.contains("Come again!"), "page: {page:?}");
127 dismiss(&mut game);
128
129 println!("\n== 2. the whiteout ==");
130 game.debug_place(12, 10, Direction::Left);
132 press_a(&mut game); press_a(&mut game); let mut last_log: Vec<String> = Vec::new();
135 for _ in 0..120 {
136 let Some(battle) = game.battle() else { break };
137 last_log = battle.log().to_vec();
138 press_a(&mut game);
139 }
140 println!("lost battle log:");
141 for line in &last_log {
142 println!(" {line}");
143 }
144 assert_eq!(
145 last_log.last().map(String::as_str),
146 Some("You lost the battle…")
147 );
148
149 let page = game.dialogue_text().expect("post-lose text");
151 println!("post-lose text: {page:?}");
152 assert!(page.contains("no match"), "page: {page:?}");
153 press_a(&mut game); assert!(game.whiteout_active(), "whiteout armed");
157 idle(&mut game, 10);
158 snap(&mut game, &shot_dir, "whiteout-blackout");
159 idle(&mut game, 25);
160 let page = game.dialogue_text().expect("whiteout message");
161 println!("whiteout message: {page:?}");
162 assert!(page.contains("collapsed"), "page: {page:?}");
163 snap(&mut game, &shot_dir, "whiteout-message");
164
165 press_a(&mut game);
167 assert!(!game.whiteout_active());
168 println!("after the whiteout:");
169 println!(
170 " map: {:?} tile: {:?}",
171 game.current_map_id(),
172 game.player_tile()
173 );
174 for m in game.party_state().expect("party state") {
175 println!(" {} hp {} mp {} status {:?}", m.id, m.hp, m.mp, m.status);
176 }
177 println!(
178 " __played_main_StartTown flag kept: {}",
179 game.flag("__played_main_StartTown")
180 );
181 assert_eq!(game.current_map_id(), Some("StartTown"));
182 assert!(game
183 .party_state()
184 .unwrap()
185 .iter()
186 .all(|m| m.hp > 0 && m.status.is_none()));
187 snap(&mut game, &shot_dir, "whiteout-after");
188
189 println!("\nacceptance OK");
190}