use bevy::{input::InputSystems, prelude::*}; use crate::sim::SimSpeed; pub struct ActionsPlugin; impl Plugin for ActionsPlugin { fn build(&self, app: &mut App) { app.add_message::() .add_systems(PreUpdate, keyboard_actions.after(InputSystems)); } } #[derive(Message, Debug, Clone, Copy, PartialEq, Eq)] pub enum Action { TogglePause, SetSpeed(SimSpeed), ToggleStockpileTool, ToggleFarmTool, Cancel, NewWorld, ToggleUi, } pub const KEYBINDS: [(KeyCode, Action); 9] = [ (KeyCode::Space, Action::TogglePause), (KeyCode::Digit1, Action::SetSpeed(SimSpeed::Normal)), (KeyCode::Digit2, Action::SetSpeed(SimSpeed::Fast)), (KeyCode::Digit3, Action::SetSpeed(SimSpeed::Fastest)), (KeyCode::KeyZ, Action::ToggleStockpileTool), (KeyCode::KeyF, Action::ToggleFarmTool), (KeyCode::Escape, Action::Cancel), (KeyCode::KeyR, Action::NewWorld), (KeyCode::KeyH, Action::ToggleUi), ]; impl Action { pub fn label(self) -> &'static str { match self { Action::TogglePause => "Pause", Action::SetSpeed(SimSpeed::Normal) => "1x", Action::SetSpeed(SimSpeed::Fast) => "3x", Action::SetSpeed(SimSpeed::Fastest) => "6x", Action::ToggleStockpileTool => "Stockpile", Action::ToggleFarmTool => "Farm", Action::Cancel => "Cancel", Action::NewWorld => "New world", Action::ToggleUi => "Hide UI", } } pub fn key(self) -> Option { KEYBINDS .iter() .find(|(_, action)| *action == self) .map(|(key, _)| *key) } pub fn key_label(self) -> String { match self.key() { Some(KeyCode::Space) => "Space".into(), Some(KeyCode::Escape) => "Esc".into(), Some(key) => format!("{key:?}") .trim_start_matches("Digit") .trim_start_matches("Key") .to_string(), None => String::new(), } } } fn keyboard_actions(keys: Res>, mut actions: MessageWriter) { for (key, action) in KEYBINDS { if keys.just_pressed(key) { actions.write(action); } } } #[cfg(test)] mod tests { use super::*; #[test] fn pressing_a_bound_key_triggers_its_action() { let mut app = App::new(); app.init_resource::>() .add_message::() .add_systems(Update, keyboard_actions); app.world_mut() .resource_mut::>() .press(KeyCode::Digit3); app.update(); let actions: Vec = app .world_mut() .resource_mut::>() .drain() .collect(); assert_eq!(actions, vec![Action::SetSpeed(SimSpeed::Fastest)]); } #[test] fn every_action_has_exactly_one_key() { for (_, action) in KEYBINDS { let bound = KEYBINDS.iter().filter(|(_, a)| *a == action).count(); assert_eq!(bound, 1, "{action:?}"); } let mut keys: Vec<_> = KEYBINDS.iter().map(|(k, _)| format!("{k:?}")).collect(); keys.sort(); keys.dedup(); assert_eq!(keys.len(), KEYBINDS.len()); } #[test] fn key_labels_are_short() { assert_eq!(Action::SetSpeed(SimSpeed::Fast).key_label(), "2"); assert_eq!(Action::ToggleStockpileTool.key_label(), "Z"); assert_eq!(Action::Cancel.key_label(), "Esc"); } }