//! This module helps us deal with controls and events. use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::rc::Rc; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use crate::engine::vector::Vector; use crate::{log, Result}; /// The different events that can be triggered. #[derive(Debug, Copy, Clone)] pub enum Event { /// A key has been pressed. KeyPressed(Key), /// A key has been released. KeyReleased(Key), /// A gamepad has been connected. GamepadConnected(u32), /// A button has been pressed on a certain gamepad. ButtonPressed(u32, Button), /// A button has been released on a certain gamepad. ButtonReleased(u32, Button), } /// The different keyboard keys we support. #[derive(Debug, Hash, Copy, Clone, PartialEq, Eq)] pub enum Key { /// The left arrow key. ArrowLeft, /// The right arrow key. ArrowRight, /// The up arrow key. ArrowUp, /// The down arrow key. ArrowDown, /// The space key. Space, } /// The different actions a user can do. #[derive(Debug, Copy, Clone)] pub enum Action { /// A certain button has been pressed. ButtonPressed(Button), /// A certain button has been released. ButtonReleased(Button), } /// The different actions that a user can do. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Button { /// The user asks to go to the left. Left, /// The user asks to go to the right. Right, /// The user asks to go up. Up, /// The user asks to go down. Down, /// The main action or validatation button. Button0, /// The user asks to do the secondary action. Button1, /// The user asks to do the tertiary action. Button2, /// The user asks to do the 4th action or cancel. Button3, } /// This structure holds what button are pressed or released, and stores the events. pub struct InputManager { /// The state of the keyboard. keyboard: HashMap, /// All the connected gamepads. gamepads: Vec, /// The events that have occur. events: VecDeque, } impl InputManager { /// Creates a new input manager. pub fn new() -> InputManager { InputManager { keyboard: HashMap::new(), gamepads: vec![], events: VecDeque::new(), } } /// Gets the gamepad from its id. pub fn gamepad(&self, id: u32) -> Option<&Gamepad> { self.gamepads.iter().find(|x| x.inner.index() == id) } /// Parses a keyboard event to the key. pub fn key(event: web_sys::KeyboardEvent) -> Option { match event.code().as_str() { "ArrowDown" => Some(Key::ArrowDown), "ArrowUp" => Some(Key::ArrowUp), "ArrowLeft" => Some(Key::ArrowLeft), "ArrowRight" => Some(Key::ArrowRight), "Space" => Some(Key::Space), _ => None, } } /// Adds a key pressed event to the list of events. pub fn key_pressed_event(&mut self, event: web_sys::KeyboardEvent) { if let Some(key) = InputManager::key(event) { self.keyboard.insert(key, true); self.events.push_back(Event::KeyPressed(key)); } } /// Adds a key released event to the list of events. pub fn key_released_event(&mut self, event: web_sys::KeyboardEvent) { if let Some(key) = InputManager::key(event) { self.keyboard.insert(key, false); self.events.push_back(Event::KeyReleased(key)); } } /// Adds a gamepad to the input manager. pub fn add_gamepad(&mut self, gamepad: web_sys::Gamepad) { log!("Gamepad added {}", gamepad.id()); self.events .push_back(Event::GamepadConnected(gamepad.index())); self.gamepads.push(Gamepad::new(gamepad)); } /// Updates the gamepad states. pub fn update(&mut self) { for gamepad in &mut self.gamepads { gamepad.update(&mut self.events); } } /// Checks if a specific key is pressed. pub fn is_key_pressed(&self, key: Key) -> bool { *self.keyboard.get(&key).unwrap_or(&false) } } /// Holds the gamepad information. #[derive(Clone)] pub struct Gamepad { /// The javascript gamepad object. inner: web_sys::Gamepad, /// The main joystick. main_joystick: Joystick, /// What buttons are pressed or released. buttons: HashMap, } /// The joysticks. #[derive(Clone)] pub struct Joystick { /// The id of the X axis of the joystick. pub x_id: u32, /// The id of the Y axis of the joystick. pub y_id: u32, } impl Gamepad { /// Creates a new gamepad from its javascript gamepad. pub fn new(inner: web_sys::Gamepad) -> Gamepad { Gamepad { inner, main_joystick: Joystick { x_id: 0, y_id: 1 }, buttons: HashMap::new(), } } /// Returns the direction of the main joystick. pub fn main_direction(&self) -> Vector { let axes = self.inner.axes(); let x = axes.get(self.main_joystick.x_id).as_f64().unwrap(); let y = axes.get(self.main_joystick.y_id).as_f64().unwrap(); Vector::new(x, y) } /// Updates the state of the gamepad and adds the corresponding events to the deque. pub fn update(&mut self, events: &mut VecDeque) { const BUTTONS: [Button; 4] = [ Button::Button0, Button::Button1, Button::Button2, Button::Button3, ]; for button in BUTTONS { // Finds the real state of the button let is_pressed = self.is_js_pressed(button); // Updates the map and returns the old state of the button let was_pressed = self.buttons.insert(button, is_pressed).unwrap_or(false); if was_pressed && !is_pressed { // Button was released events.push_back(Event::ButtonReleased(self.inner.index(), button)); } if is_pressed && !was_pressed { // Button was pressed events.push_back(Event::ButtonPressed(self.inner.index(), button)); } } } /// Checks if a button is pressed. pub fn is_pressed(&self, button: Button) -> bool { *self.buttons.get(&button).unwrap_or(&false) } /// Utility function to really check the state of the button. fn is_js_pressed(&self, button: Button) -> bool { let buttons = self.inner.buttons(); match button { Button::Left => false, Button::Right => false, Button::Up => false, Button::Down => false, Button::Button0 => Into::::into(buttons.get(0)).pressed(), Button::Button1 => Into::::into(buttons.get(1)).pressed(), Button::Button2 => Into::::into(buttons.get(2)).pressed(), Button::Button3 => Into::::into(buttons.get(3)).pressed(), } } } /// A helper to easily deal with inputs. #[derive(Clone)] pub struct Inputs(pub Rc>); impl Inputs { /// Creates a new inputs object. pub fn new(window: &web_sys::Window) -> Result { let inputs = Inputs(Rc::new(RefCell::new(InputManager::new()))); let clone = inputs.clone(); let cb = Closure::::new(move |event: web_sys::GamepadEvent| { let mut inner = clone.0.borrow_mut(); inner.add_gamepad(event.gamepad().unwrap()); }); window.add_event_listener_with_callback("gamepadconnected", cb.as_ref().unchecked_ref())?; cb.forget(); let clone = inputs.clone(); let cb = Closure::::new(move |event: web_sys::KeyboardEvent| { let mut inner = clone.0.borrow_mut(); inner.key_pressed_event(event); }); window.add_event_listener_with_callback("keydown", cb.as_ref().unchecked_ref())?; cb.forget(); let clone = inputs.clone(); let cb = Closure::::new(move |event: web_sys::KeyboardEvent| { let mut inner = clone.0.borrow_mut(); inner.key_released_event(event); }); window.add_event_listener_with_callback("keyup", cb.as_ref().unchecked_ref())?; cb.forget(); Ok(inputs) } /// Updates the inputs. /// /// This function needs to be called at each frame. pub fn update(&mut self) { let mut inner = self.0.borrow_mut(); inner.update(); } /// Returns the next event. pub fn next(&mut self) -> Option { let mut inner = self.0.borrow_mut(); inner.events.pop_front() } }