use crate::runner::ApiState;
use nightshade::prelude::*;
use nightshade::run::pump::{PumpShell, pump_frame, pump_shell_new, pump_shell_ready};
pub struct Window {
pub title: String,
pub size: Option<(u32, u32)>,
}
impl Default for Window {
fn default() -> Self {
Self {
title: "nightshade".to_string(),
size: None,
}
}
}
pub struct App {
pub world: World,
pub frame_limit: Option<u32>,
frames_rendered: u32,
shell: PumpShell,
}
pub fn open() -> App {
open_with(Window::default())
}
pub fn open_with(window: Window) -> App {
let state = ApiState::<()> {
setup: None,
update: None,
data: None,
clears_draw_pools: false,
frame_limit: None,
frames_rendered: 0,
};
let mut shell =
pump_shell_new(Box::new(state)).expect("failed to create the engine event loop");
shell.context.world.resources.window.title = window.title;
shell.context.world.resources.window.initial_size = window.size;
let mut pumps_without_window = 0;
while !pump_shell_ready(&shell) {
if !pump_frame(&mut shell) {
break;
}
let window_exists = shell.context.world.resources.window.handle.is_some();
if window_exists && shell.context.initialized && shell.context.renderer.is_none() {
panic!("failed to create the renderer, see the log for the error");
}
if !window_exists {
pumps_without_window += 1;
if pumps_without_window > 10000 {
panic!("failed to create the window, see the log for the error");
}
}
}
let mut world = World::default();
std::mem::swap(&mut world, &mut shell.context.world);
schedule_remove(
&mut world.resources.schedules.frame,
system_names::RESET_MOUSE,
);
schedule_remove(
&mut world.resources.schedules.frame,
system_names::RESET_KEYBOARD,
);
schedule_remove(
&mut world.resources.schedules.frame,
system_names::RESET_TOUCH,
);
let frame_limit = crate::runner::frame_limit_from_environment();
App {
world,
frame_limit,
frames_rendered: 0,
shell,
}
}
pub fn render_image(
width: u32,
height: u32,
path: impl Into<std::path::PathBuf>,
setup: impl FnOnce(&mut World),
) {
let state = ApiState::<()> {
setup: None,
update: None,
data: None,
clears_draw_pools: false,
frame_limit: None,
frames_rendered: 0,
};
let mut shell =
pump_shell_new(Box::new(state)).expect("failed to create the engine event loop");
shell.context.world.resources.window.start_hidden = true;
shell.context.world.resources.window.initial_size = Some((width, height));
while !pump_shell_ready(&shell) {
if !pump_frame(&mut shell) {
return;
}
}
setup(&mut shell.context.world);
for _ in 0..30 {
tick_hidden(&mut shell);
}
crate::environment::screenshot(&mut shell.context.world, path.into());
for _ in 0..10 {
tick_hidden(&mut shell);
}
}
fn tick_hidden(shell: &mut PumpShell) {
let context = &mut shell.context;
let Some(renderer) = context.renderer.as_mut() else {
return;
};
if let Some(next_state) = tick_offscreen(&mut context.world, context.state.as_mut(), renderer) {
context.state = next_state;
}
}
pub fn frame(app: &mut App) -> bool {
if let Some(limit) = app.frame_limit
&& app.frames_rendered >= limit
{
return false;
}
nightshade::ecs::input::systems::reset_mouse_system(&mut app.world);
nightshade::ecs::input::systems::reset_keyboard_system(&mut app.world);
nightshade::ecs::input::systems::reset_touch_system(&mut app.world);
std::mem::swap(&mut app.world, &mut app.shell.context.world);
let alive = pump_frame(&mut app.shell);
std::mem::swap(&mut app.world, &mut app.shell.context.world);
crate::draw::clear_draw_pools(&mut app.world);
app.frames_rendered += 1;
alive
}