diff --git a/libretrogd/src/base/mod.rs b/libretrogd/src/base/mod.rs new file mode 100644 index 0000000..68eb302 --- /dev/null +++ b/libretrogd/src/base/mod.rs @@ -0,0 +1,96 @@ +//! Optional, extra types and helpers that can be used to get game's main loop boilerplate up and +//! running quicker. + +use thiserror::Error; + +use crate::events::*; +use crate::states::*; +use crate::system::*; + +pub trait AppState { + fn system(&self) -> &System; + fn system_mut(&mut self) -> &mut System; +} + +pub trait AppStateWithFrameTiming: AppState { + fn delta(&self) -> f32; + fn set_delta(&mut self, delta: f32); + + fn update_frame_delta(&mut self, last_ticks: u64) -> u64 { + let ticks = self.system().ticks(); + let tick_frequency = self.system().tick_frequency(); + let elapsed = ticks - last_ticks; + self.set_delta((elapsed as f64 / tick_frequency as f64) as f32); + ticks + } +} + +pub trait AppStateWithEvents: AppState { + fn event_publisher(&mut self) -> &mut EventPublisher; +} + +pub trait SupportSystems {} + +pub trait SupportSystemsWithEvents: SupportSystems +where + ContextType: AppStateWithEvents, +{ + fn event_listeners(&mut self) -> &mut EventListeners; + + fn do_events(&mut self, context: &mut ContextType) { + self.event_listeners().take_queue_from(context.event_publisher()); + self.event_listeners().dispatch_queue(context); + } +} + +pub struct App { + pub state: StateType, + pub support: SupportType, +} + +impl App { + pub fn new(state: StateType, support: SupportType) -> Self { + App { state, support } + } +} + +#[derive(Error, Debug)] +pub enum MainLoopError { + #[error("States error: {0}")] + StateError(#[from] StateError), + + #[error("System error: {0}")] + SystemError(#[from] SystemError), +} + +pub fn main_loop( + mut app: App, + initial_state: State, +) -> Result<(), MainLoopError> +where + StateType: AppStateWithFrameTiming, + SupportType: SupportSystems, + State: GameState + 'static, +{ + let mut states = States::new(); + states.push(initial_state)?; + + let mut is_running = true; + let mut last_ticks = app.state.system().ticks(); + + while is_running && !states.is_empty() { + app.state.system_mut().do_events_with(|event| match event { + SystemEvent::Quit => { + is_running = false; + } + _ => {} + }); + + last_ticks = app.state.update_frame_delta(last_ticks); + states.update(&mut app.state)?; + states.render(&mut app.state); + app.state.system_mut().display()?; + } + + Ok(()) +} diff --git a/libretrogd/src/lib.rs b/libretrogd/src/lib.rs index 4e15a29..3f89495 100644 --- a/libretrogd/src/lib.rs +++ b/libretrogd/src/lib.rs @@ -2,6 +2,7 @@ extern crate core; extern crate sdl2; pub mod audio; +pub mod base; pub mod entities; pub mod events; pub mod graphics;