use super::*; pub(crate) struct GameActionsPlugin; impl Plugin for GameActionsPlugin { fn build(&self, app: &mut App) { app.add_plugins(( ActionsPlugin::::new() .bind(SimAction::Toggle, [Binding::Key(KeyCode::Space)]) .bind(SimAction::Step, [Binding::Key(KeyCode::KeyN)]) .bind(SimAction::Reset, [Binding::Key(KeyCode::KeyR)]), ActionsPlugin::::new() .bind(AudioAction::Toggle, [Binding::Key(KeyCode::KeyM)]), ActionsPlugin::::new() .bind(Action::ZoomIn, [Binding::Key(KeyCode::Equal)]) .bind(Action::ZoomOut, [Binding::Key(KeyCode::Minus)]) .bind(Action::Quit, [Binding::Key(KeyCode::Escape)]), )); } } #[derive(Copy, Clone, Eq, PartialEq, Hash, GameAction, Default)] pub(crate) enum SimAction { #[action_handler(SimAction::toggle)] Toggle, #[action_handler(SimAction::step)] Step, #[action_handler(SimAction::reset)] Reset, #[action_handler(SimAction::wipe)] Wipe, #[default] None, } impl SimAction { /// When the user toggles between Play/Pause simulation, just update the state fn toggle( curr: Res>, mut next: ResMut>, mut last_board_state: ResMut, coordinates: Query<&Coordinates, With>, ) { next.set(match curr.get() { SimulationPlayback::Run | SimulationPlayback::Step => { // Run | Step -> Pause SimulationPlayback::Pause } SimulationPlayback::Pause => { // Capture current state before running last_board_state.coordinates = coordinates.iter().cloned().collect(); // Pause -> Run SimulationPlayback::Run } }); } // When the user wants to step the simulation, just enter the SimulationStep state // TODO: Should switch back to Pause automatically after step if Step is the current state fn step(mut next: ResMut>) { next.set(SimulationPlayback::Step); } // Reset board to last saved state (before running, or before wiping) fn reset( last_board_state: Res, mut lifecycle: MessageWriter, cells: Query>, mut commands: Commands, ) { // TODO: reconcile board instead of despawn/spawning the world cells.iter().for_each(|e| { commands.entity(e).despawn(); }); last_board_state.coordinates.iter().for_each(|c| { lifecycle.write(Lifecycle::Alive(c.clone())); }); } // Wipe all cells from the board fn wipe(cells: Query>, mut commands: Commands) { cells.iter().for_each(|e| { commands.entity(e).despawn(); }); } } #[derive(Copy, Clone, Eq, PartialEq, Hash, GameAction, Default)] pub(crate) enum Action { #[action_handler(Action::zoom_in)] ZoomIn, #[action_handler(Action::zoom_out)] ZoomOut, #[action_handler(Action::quit)] Quit, #[default] None, } impl Action { fn zoom_in(mut projection: Query<&mut Projection>) { projection.iter_mut().for_each(|mut p| match *p { Projection::Orthographic(ref mut o) => { o.scale *= 0.5; } _ => todo!(), }); } fn zoom_out(mut projection: Query<&mut Projection>) { projection.iter_mut().for_each(|mut p| match *p { Projection::Orthographic(ref mut o) => { o.scale /= 0.5; } _ => todo!(), }); } fn quit(mut writer: MessageWriter) { writer.write(AppExit::Success); } } #[derive(Copy, Clone, Eq, PartialEq, Hash, GameAction, Default)] pub(crate) enum AudioAction { #[action_handler(AudioAction::toggle)] Toggle, #[default] None, } impl AudioAction { fn toggle(curr: Res>, mut next: ResMut>) { next.set(match curr.get() { AudioPlayback::Play => AudioPlayback::Mute, AudioPlayback::Mute => AudioPlayback::Play, }); } }