//! Module containing controller-specific abstractions allowing complex //! keybindings use crate::{game_input::GameInput, window::MenuInput}; use gilrs::{ev::Code as GilCode, Axis as GilAxis, Button as GilButton}; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; /// Contains all controller related settings and keymaps #[derive(Clone, Debug, Serialize, Deserialize, Default)] #[serde(default)] pub struct ControllerSettings { pub game_button_map: HashMap>, pub menu_button_map: HashMap>, pub game_analog_button_map: HashMap>, pub menu_analog_button_map: HashMap>, pub game_axis_map: HashMap>, pub menu_axis_map: HashMap>, pub pan_sensitivity: u32, pub pan_invert_y: bool, pub axis_deadzones: HashMap, pub button_deadzones: HashMap, pub mouse_emulation_sensitivity: u32, pub inverted_axes: Vec, } impl ControllerSettings { pub fn apply_axis_deadzone(&self, k: &Axis, input: f32) -> f32 { let threshold = *self.axis_deadzones.get(k).unwrap_or(&0.2); // This could be one comparison per handled event faster if threshold was // guaranteed to fall into <0, 1) range let input_abs = input.abs(); if input_abs <= threshold || threshold >= 1.0 { 0.0 } else if threshold <= 0.0 { input } else { (input_abs - threshold) / (1.0 - threshold) * input.signum() } } pub fn apply_button_deadzone(&self, k: &AnalogButton, input: f32) -> f32 { let threshold = *self.button_deadzones.get(k).unwrap_or(&0.2); // This could be one comparison per handled event faster if threshold was // guaranteed to fall into <0, 1) range if input <= threshold || threshold >= 1.0 { 0.0 } else if threshold <= 0.0 { input } else { (input - threshold) / (1.0 - threshold) } } } impl From<&crate::settings::GamepadSettings> for ControllerSettings { fn from(settings: &crate::settings::GamepadSettings) -> Self { Self { game_button_map: { let mut map: HashMap<_, Vec<_>> = HashMap::new(); map.entry(settings.game_buttons.primary) .or_default() .push(GameInput::Primary); map.entry(settings.game_buttons.secondary) .or_default() .push(GameInput::Secondary); map.entry(settings.game_buttons.block) .or_default() .push(GameInput::Block); map.entry(settings.game_buttons.slot1) .or_default() .push(GameInput::Slot1); map.entry(settings.game_buttons.slot2) .or_default() .push(GameInput::Slot2); map.entry(settings.game_buttons.slot3) .or_default() .push(GameInput::Slot3); map.entry(settings.game_buttons.slot4) .or_default() .push(GameInput::Slot4); map.entry(settings.game_buttons.slot5) .or_default() .push(GameInput::Slot5); map.entry(settings.game_buttons.slot6) .or_default() .push(GameInput::Slot6); map.entry(settings.game_buttons.slot7) .or_default() .push(GameInput::Slot7); map.entry(settings.game_buttons.slot8) .or_default() .push(GameInput::Slot8); map.entry(settings.game_buttons.slot9) .or_default() .push(GameInput::Slot9); map.entry(settings.game_buttons.slot10) .or_default() .push(GameInput::Slot10); map.entry(settings.game_buttons.toggle_cursor) .or_default() .push(GameInput::ToggleCursor); map.entry(settings.game_buttons.escape) .or_default() .push(GameInput::Escape); map.entry(settings.game_buttons.enter) .or_default() .push(GameInput::Chat); map.entry(settings.game_buttons.command) .or_default() .push(GameInput::Command); map.entry(settings.game_buttons.move_forward) .or_default() .push(GameInput::MoveForward); map.entry(settings.game_buttons.move_left) .or_default() .push(GameInput::MoveLeft); map.entry(settings.game_buttons.move_back) .or_default() .push(GameInput::MoveBack); map.entry(settings.game_buttons.move_right) .or_default() .push(GameInput::MoveRight); map.entry(settings.game_buttons.jump) .or_default() .push(GameInput::Jump); map.entry(settings.game_buttons.sit) .or_default() .push(GameInput::Sit); map.entry(settings.game_buttons.dance) .or_default() .push(GameInput::Dance); map.entry(settings.game_buttons.glide) .or_default() .push(GameInput::Glide); map.entry(settings.game_buttons.climb) .or_default() .push(GameInput::Climb); map.entry(settings.game_buttons.climb_down) .or_default() .push(GameInput::ClimbDown); map.entry(settings.game_buttons.swimup) .or_default() .push(GameInput::SwimUp); map.entry(settings.game_buttons.swimdown) .or_default() .push(GameInput::SwimDown); map.entry(settings.game_buttons.sneak) .or_default() .push(GameInput::Sneak); map.entry(settings.game_buttons.toggle_lantern) .or_default() .push(GameInput::ToggleLantern); map.entry(settings.game_buttons.mount) .or_default() .push(GameInput::Mount); map.entry(settings.game_buttons.map) .or_default() .push(GameInput::Map); map.entry(settings.game_buttons.bag) .or_default() .push(GameInput::Bag); map.entry(settings.game_buttons.social) .or_default() .push(GameInput::Social); map.entry(settings.game_buttons.crafting) .or_default() .push(GameInput::Crafting); map.entry(settings.game_buttons.spellbook) .or_default() .push(GameInput::Spellbook); map.entry(settings.game_buttons.settings) .or_default() .push(GameInput::Settings); map.entry(settings.game_buttons.help) .or_default() .push(GameInput::Help); map.entry(settings.game_buttons.toggle_interface) .or_default() .push(GameInput::ToggleInterface); map.entry(settings.game_buttons.toggle_debug) .or_default() .push(GameInput::ToggleDebug); #[cfg(feature = "egui-ui")] map.entry(settings.game_buttons.toggle_debug) .or_default() .push(GameInput::ToggleEguiDebug); map.entry(settings.game_buttons.toggle_chat) .or_default() .push(GameInput::ToggleChat); map.entry(settings.game_buttons.fullscreen) .or_default() .push(GameInput::Fullscreen); map.entry(settings.game_buttons.screenshot) .or_default() .push(GameInput::Screenshot); map.entry(settings.game_buttons.toggle_ingame_ui) .or_default() .push(GameInput::ToggleIngameUi); map.entry(settings.game_buttons.roll) .or_default() .push(GameInput::Roll); map.entry(settings.game_buttons.respawn) .or_default() .push(GameInput::Respawn); map.entry(settings.game_buttons.interact) .or_default() .push(GameInput::Interact); map.entry(settings.game_buttons.toggle_wield) .or_default() .push(GameInput::ToggleWield); map.entry(settings.game_buttons.swap_loadout) .or_default() .push(GameInput::SwapLoadout); map }, menu_button_map: { let mut map: HashMap<_, Vec<_>> = HashMap::new(); map.entry(settings.menu_buttons.up) .or_default() .push(MenuInput::Up); map.entry(settings.menu_buttons.down) .or_default() .push(MenuInput::Down); map.entry(settings.menu_buttons.left) .or_default() .push(MenuInput::Left); map.entry(settings.menu_buttons.right) .or_default() .push(MenuInput::Right); map.entry(settings.menu_buttons.scroll_up) .or_default() .push(MenuInput::ScrollUp); map.entry(settings.menu_buttons.scroll_down) .or_default() .push(MenuInput::ScrollDown); map.entry(settings.menu_buttons.scroll_left) .or_default() .push(MenuInput::ScrollLeft); map.entry(settings.menu_buttons.scroll_right) .or_default() .push(MenuInput::ScrollRight); map.entry(settings.menu_buttons.home) .or_default() .push(MenuInput::Home); map.entry(settings.menu_buttons.end) .or_default() .push(MenuInput::End); map.entry(settings.menu_buttons.apply) .or_default() .push(MenuInput::Apply); map.entry(settings.menu_buttons.back) .or_default() .push(MenuInput::Back); map.entry(settings.menu_buttons.exit) .or_default() .push(MenuInput::Exit); map }, game_analog_button_map: HashMap::new(), menu_analog_button_map: HashMap::new(), game_axis_map: { let mut map: HashMap<_, Vec<_>> = HashMap::new(); map.entry(settings.game_axis.movement_x) .or_default() .push(AxisGameAction::MovementX); map.entry(settings.game_axis.movement_y) .or_default() .push(AxisGameAction::MovementY); map.entry(settings.game_axis.camera_x) .or_default() .push(AxisGameAction::CameraX); map.entry(settings.game_axis.camera_y) .or_default() .push(AxisGameAction::CameraY); map }, menu_axis_map: { let mut map: HashMap<_, Vec<_>> = HashMap::new(); map.entry(settings.menu_axis.move_x) .or_default() .push(AxisMenuAction::MoveX); map.entry(settings.menu_axis.move_y) .or_default() .push(AxisMenuAction::MoveY); map.entry(settings.menu_axis.scroll_x) .or_default() .push(AxisMenuAction::ScrollX); map.entry(settings.menu_axis.scroll_y) .or_default() .push(AxisMenuAction::ScrollY); map }, pan_sensitivity: settings.pan_sensitivity, pan_invert_y: settings.pan_invert_y, axis_deadzones: settings.axis_deadzones.clone(), button_deadzones: settings.button_deadzones.clone(), mouse_emulation_sensitivity: settings.mouse_emulation_sensitivity, inverted_axes: settings.inverted_axes.clone(), } } } /// All the menu actions you can bind to an Axis #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum AxisMenuAction { MoveX, MoveY, ScrollX, ScrollY, } /// All the game actions you can bind to an Axis #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum AxisGameAction { MovementX, MovementY, CameraX, CameraY, } /// All the menu actions you can bind to an analog button #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum AnalogButtonMenuAction {} /// All the game actions you can bind to an analog button #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum AnalogButtonGameAction {} /// Button::Simple(GilButton::Unknown) is invalid and equal to mapping an action /// to nothing #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum Button { Simple(GilButton), EventCode(u32), } impl Default for Button { fn default() -> Self { Button::Simple(GilButton::Unknown) } } /// AnalogButton::Simple(GilButton::Unknown) is invalid and equal to mapping an /// action to nothing #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum AnalogButton { Simple(GilButton), EventCode(u32), } impl Default for AnalogButton { fn default() -> Self { AnalogButton::Simple(GilButton::Unknown) } } /// Axis::Simple(GilAxis::Unknown) is invalid and equal to mapping an action to /// nothing #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum Axis { Simple(GilAxis), EventCode(u32), } impl Default for Axis { fn default() -> Self { Axis::Simple(GilAxis::Unknown) } } impl From<(GilAxis, GilCode)> for Axis { fn from((axis, code): (GilAxis, GilCode)) -> Self { match axis { GilAxis::Unknown => Self::EventCode(code.into_u32()), _ => Self::Simple(axis), } } } impl From<(GilButton, GilCode)> for Button { fn from((button, code): (GilButton, GilCode)) -> Self { match button { GilButton::Unknown => Self::EventCode(code.into_u32()), _ => Self::Simple(button), } } } impl From<(GilButton, GilCode)> for AnalogButton { fn from((button, code): (GilButton, GilCode)) -> Self { match button { GilButton::Unknown => Self::EventCode(code.into_u32()), _ => Self::Simple(button), } } }