ferravex_render/
demo.rs

1//! Shared native smoke-run support for small interactive examples.
2//! Each game supplies its own autoplay inputs and uses the same simulation as a human.
3use crate::prelude::*;
4
5/// Native flags: `--autoplay`, `--smoke-frames N`, `--screenshot path.png`.
6/// Browser and mobile launches default to interactive play.
7#[derive(Default)]
8pub struct Demo {
9    pub autoplay: bool,
10    pub frame: u64,
11    limit: Option<u64>,
12    #[cfg(not(target_arch = "wasm32"))]
13    screenshot: Option<String>,
14}
15
16impl Demo {
17    pub fn new() -> Self {
18        #[cfg(not(target_arch = "wasm32"))]
19        {
20            let mut demo = Self::default();
21            let mut args = std::env::args().skip(1);
22            while let Some(arg) = args.next() {
23                match arg.as_str() {
24                    "--autoplay" => demo.autoplay = true,
25                    "--smoke-frames" => {
26                        demo.limit = Some(
27                            args.next()
28                                .and_then(|s| s.parse::<u64>().ok())
29                                .filter(|n| *n > 0)
30                                .expect("--smoke-frames requires a positive integer"),
31                        );
32                    }
33                    "--screenshot" => {
34                        demo.screenshot = Some(args.next().expect("--screenshot requires a path"))
35                    }
36                    "--help" => {
37                        println!("Options: --autoplay --smoke-frames N --screenshot path.png");
38                        std::process::exit(0);
39                    }
40                    _ => panic!("Unknown option: {arg}"),
41                }
42            }
43            demo
44        }
45        #[cfg(target_arch = "wasm32")]
46        Self {
47            autoplay: crate::autoplay_enabled(),
48            ..Self::default()
49        }
50    }
51
52    /// Smoke runs use a reproducible render timestep; simulation should still use FixedClock.
53    pub fn dt(&self) -> f32 {
54        if self.limit.is_some() {
55            1.0 / 60.0
56        } else {
57            get_frame_time().clamp(0.0, 0.1)
58        }
59    }
60
61    pub fn time(&self) -> f32 {
62        if self.limit.is_some() {
63            self.frame as f32 / 60.0
64        } else {
65            get_time() as f32
66        }
67    }
68
69    /// Call after all drawing and before next_frame. Returns true when the run is finished.
70    pub fn finish_frame(&mut self) -> bool {
71        self.frame += 1;
72        let finished = self.limit.is_some_and(|limit| self.frame >= limit);
73        #[cfg(not(target_arch = "wasm32"))]
74        if finished && let Some(path) = &self.screenshot {
75            get_screen_data().export_png(path);
76        }
77        finished
78    }
79}