Merge branch 'jimmy/controller-layers' into 'master'

controller layers

See merge request veloren/veloren!3988
This commit is contained in:
Joshua Barretto 2023-07-31 16:20:01 +00:00
commit bcbfbbb274
3 changed files with 474 additions and 25 deletions

View File

@ -1,7 +1,9 @@
//! Module containing controller-specific abstractions allowing complex
//! keybindings
use crate::{game_input::GameInput, window::MenuInput};
use crate::{
game_input::GameInput, settings::gamepad::con_settings::LayerEntry, window::MenuInput,
};
use gilrs::{ev::Code as GilCode, Axis as GilAxis, Button as GilButton};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
@ -16,6 +18,8 @@ pub struct ControllerSettings {
pub menu_analog_button_map: HashMap<AnalogButton, Vec<AnalogButtonMenuAction>>,
pub game_axis_map: HashMap<Axis, Vec<AxisGameAction>>,
pub menu_axis_map: HashMap<Axis, Vec<AxisMenuAction>>,
pub layer_button_map: HashMap<LayerEntry, Vec<GameInput>>,
pub modifier_buttons: Vec<Button>,
pub pan_sensitivity: u32,
pub pan_invert_y: bool,
pub axis_deadzones: HashMap<Axis, f32>,
@ -293,6 +297,171 @@ impl From<&crate::settings::GamepadSettings> for ControllerSettings {
.push(AxisMenuAction::ScrollY);
map
},
layer_button_map: {
let mut map: HashMap<_, Vec<_>> = HashMap::new();
map.entry(settings.game_layer_buttons.primary)
.or_default()
.push(GameInput::Primary);
map.entry(settings.game_layer_buttons.secondary)
.or_default()
.push(GameInput::Secondary);
map.entry(settings.game_layer_buttons.block)
.or_default()
.push(GameInput::Block);
map.entry(settings.game_layer_buttons.slot1)
.or_default()
.push(GameInput::Slot1);
map.entry(settings.game_layer_buttons.slot2)
.or_default()
.push(GameInput::Slot2);
map.entry(settings.game_layer_buttons.slot3)
.or_default()
.push(GameInput::Slot3);
map.entry(settings.game_layer_buttons.slot4)
.or_default()
.push(GameInput::Slot4);
map.entry(settings.game_layer_buttons.slot5)
.or_default()
.push(GameInput::Slot5);
map.entry(settings.game_layer_buttons.slot6)
.or_default()
.push(GameInput::Slot6);
map.entry(settings.game_layer_buttons.slot7)
.or_default()
.push(GameInput::Slot7);
map.entry(settings.game_layer_buttons.slot8)
.or_default()
.push(GameInput::Slot8);
map.entry(settings.game_layer_buttons.slot9)
.or_default()
.push(GameInput::Slot9);
map.entry(settings.game_layer_buttons.slot10)
.or_default()
.push(GameInput::Slot10);
map.entry(settings.game_layer_buttons.toggle_cursor)
.or_default()
.push(GameInput::ToggleCursor);
map.entry(settings.game_layer_buttons.escape)
.or_default()
.push(GameInput::Escape);
map.entry(settings.game_layer_buttons.enter)
.or_default()
.push(GameInput::Chat);
map.entry(settings.game_layer_buttons.command)
.or_default()
.push(GameInput::Command);
map.entry(settings.game_layer_buttons.move_forward)
.or_default()
.push(GameInput::MoveForward);
map.entry(settings.game_layer_buttons.move_left)
.or_default()
.push(GameInput::MoveLeft);
map.entry(settings.game_layer_buttons.move_back)
.or_default()
.push(GameInput::MoveBack);
map.entry(settings.game_layer_buttons.move_right)
.or_default()
.push(GameInput::MoveRight);
map.entry(settings.game_layer_buttons.jump)
.or_default()
.push(GameInput::Jump);
map.entry(settings.game_layer_buttons.sit)
.or_default()
.push(GameInput::Sit);
map.entry(settings.game_layer_buttons.dance)
.or_default()
.push(GameInput::Dance);
map.entry(settings.game_layer_buttons.glide)
.or_default()
.push(GameInput::Glide);
map.entry(settings.game_layer_buttons.climb)
.or_default()
.push(GameInput::Climb);
map.entry(settings.game_layer_buttons.climb_down)
.or_default()
.push(GameInput::ClimbDown);
map.entry(settings.game_layer_buttons.swimup)
.or_default()
.push(GameInput::SwimUp);
map.entry(settings.game_layer_buttons.swimdown)
.or_default()
.push(GameInput::SwimDown);
map.entry(settings.game_layer_buttons.sneak)
.or_default()
.push(GameInput::Sneak);
map.entry(settings.game_layer_buttons.toggle_lantern)
.or_default()
.push(GameInput::ToggleLantern);
map.entry(settings.game_layer_buttons.mount)
.or_default()
.push(GameInput::Mount);
map.entry(settings.game_layer_buttons.map)
.or_default()
.push(GameInput::Map);
map.entry(settings.game_layer_buttons.bag)
.or_default()
.push(GameInput::Bag);
map.entry(settings.game_layer_buttons.social)
.or_default()
.push(GameInput::Social);
map.entry(settings.game_layer_buttons.crafting)
.or_default()
.push(GameInput::Crafting);
map.entry(settings.game_layer_buttons.spellbook)
.or_default()
.push(GameInput::Spellbook);
map.entry(settings.game_layer_buttons.settings)
.or_default()
.push(GameInput::Settings);
map.entry(settings.game_layer_buttons.help)
.or_default()
.push(GameInput::Help);
map.entry(settings.game_layer_buttons.toggle_interface)
.or_default()
.push(GameInput::ToggleInterface);
map.entry(settings.game_layer_buttons.toggle_debug)
.or_default()
.push(GameInput::ToggleDebug);
#[cfg(feature = "egui-ui")]
map.entry(settings.game_layer_buttons.toggle_debug)
.or_default()
.push(GameInput::ToggleEguiDebug);
map.entry(settings.game_layer_buttons.toggle_chat)
.or_default()
.push(GameInput::ToggleChat);
map.entry(settings.game_layer_buttons.fullscreen)
.or_default()
.push(GameInput::Fullscreen);
map.entry(settings.game_layer_buttons.screenshot)
.or_default()
.push(GameInput::Screenshot);
map.entry(settings.game_layer_buttons.toggle_ingame_ui)
.or_default()
.push(GameInput::ToggleIngameUi);
map.entry(settings.game_layer_buttons.roll)
.or_default()
.push(GameInput::Roll);
map.entry(settings.game_layer_buttons.respawn)
.or_default()
.push(GameInput::Respawn);
map.entry(settings.game_layer_buttons.interact)
.or_default()
.push(GameInput::Interact);
map.entry(settings.game_layer_buttons.toggle_wield)
.or_default()
.push(GameInput::ToggleWield);
map.entry(settings.game_layer_buttons.swap_loadout)
.or_default()
.push(GameInput::SwapLoadout);
map
},
modifier_buttons: {
let vec: Vec<Button> = vec![
Button::Simple(GilButton::RightTrigger),
Button::Simple(GilButton::LeftTrigger),
];
vec
},
pan_sensitivity: settings.pan_sensitivity,
pan_invert_y: settings.pan_invert_y,
axis_deadzones: settings.axis_deadzones.clone(),

View File

@ -10,6 +10,7 @@ pub struct GamepadSettings {
pub menu_axis: con_settings::MenuAxis,
pub game_analog_buttons: con_settings::GameAnalogButton,
pub menu_analog_buttons: con_settings::MenuAnalogButton,
pub game_layer_buttons: con_settings::GameLayerEntries,
pub pan_sensitivity: u32,
pub pan_invert_y: bool,
pub axis_deadzones: HashMap<crate::controller::Axis, f32>,
@ -27,6 +28,7 @@ impl Default for GamepadSettings {
menu_axis: con_settings::MenuAxis::default(),
game_analog_buttons: con_settings::GameAnalogButton::default(),
menu_analog_buttons: con_settings::MenuAnalogButton::default(),
game_layer_buttons: con_settings::GameLayerEntries::default(),
pan_sensitivity: 10,
pan_invert_y: false,
axis_deadzones: HashMap::new(),
@ -42,6 +44,247 @@ pub mod con_settings {
use gilrs::{Axis as GilAxis, Button as GilButton};
use serde::{Deserialize, Serialize};
// represents a controller button to fire a GameInput on
// includes two modifier buttons to determine what layer is active
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(default)]
pub struct LayerEntry {
pub button: Button,
pub mod1: Button,
pub mod2: Button,
}
impl Default for LayerEntry {
fn default() -> Self {
// binding to unknown = getting skipped from processing
Self {
button: Button::Simple(GilButton::Unknown),
mod1: Button::Simple(GilButton::Unknown),
mod2: Button::Simple(GilButton::Unknown),
}
}
}
// struct to associate each available GameInput with a LayerEntry
// similar in function to the GameButtons struct
// nothing prevents mapping a GameInput in both GameLayerEntries and GameButtons
// it's likely not desirable to double map a GameInput
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct GameLayerEntries {
pub primary: LayerEntry,
pub secondary: LayerEntry,
pub block: LayerEntry,
pub slot1: LayerEntry,
pub slot2: LayerEntry,
pub slot3: LayerEntry,
pub slot4: LayerEntry,
pub slot5: LayerEntry,
pub slot6: LayerEntry,
pub slot7: LayerEntry,
pub slot8: LayerEntry,
pub slot9: LayerEntry,
pub slot10: LayerEntry,
pub toggle_cursor: LayerEntry,
pub escape: LayerEntry,
pub enter: LayerEntry,
pub command: LayerEntry,
pub move_forward: LayerEntry,
pub move_left: LayerEntry,
pub move_back: LayerEntry,
pub move_right: LayerEntry,
pub jump: LayerEntry,
pub sit: LayerEntry,
pub dance: LayerEntry,
pub glide: LayerEntry,
pub climb: LayerEntry,
pub climb_down: LayerEntry,
pub swimup: LayerEntry,
pub swimdown: LayerEntry,
pub sneak: LayerEntry,
pub toggle_lantern: LayerEntry,
pub mount: LayerEntry,
pub map: LayerEntry,
pub bag: LayerEntry,
pub quest_log: LayerEntry,
pub character_window: LayerEntry,
pub social: LayerEntry,
pub crafting: LayerEntry,
pub spellbook: LayerEntry,
pub settings: LayerEntry,
pub help: LayerEntry,
pub toggle_interface: LayerEntry,
pub toggle_debug: LayerEntry,
#[cfg(feature = "egui-ui")]
pub toggle_egui_debug: LayerEntry,
pub toggle_chat: LayerEntry,
pub fullscreen: LayerEntry,
pub screenshot: LayerEntry,
pub toggle_ingame_ui: LayerEntry,
pub roll: LayerEntry,
pub respawn: LayerEntry,
pub interact: LayerEntry,
pub toggle_wield: LayerEntry,
pub swap_loadout: LayerEntry,
}
impl Default for GameLayerEntries {
fn default() -> Self {
Self {
primary: LayerEntry::default(),
secondary: LayerEntry::default(),
block: LayerEntry::default(),
slot1: LayerEntry {
button: Button::Simple(GilButton::DPadRight),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
slot2: LayerEntry {
button: Button::Simple(GilButton::DPadDown),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
slot3: LayerEntry {
button: Button::Simple(GilButton::DPadUp),
mod1: Button::Simple(GilButton::LeftTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
slot4: LayerEntry {
button: Button::Simple(GilButton::DPadLeft),
mod1: Button::Simple(GilButton::LeftTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
slot5: LayerEntry {
button: Button::Simple(GilButton::DPadRight),
mod1: Button::Simple(GilButton::LeftTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
slot6: LayerEntry {
button: Button::Simple(GilButton::DPadDown),
mod1: Button::Simple(GilButton::LeftTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
slot7: LayerEntry {
button: Button::Simple(GilButton::DPadUp),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::LeftTrigger),
},
slot8: LayerEntry {
button: Button::Simple(GilButton::DPadLeft),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::LeftTrigger),
},
slot9: LayerEntry {
button: Button::Simple(GilButton::DPadRight),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::LeftTrigger),
},
slot10: LayerEntry {
button: Button::Simple(GilButton::DPadDown),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::LeftTrigger),
},
toggle_cursor: LayerEntry {
button: Button::Simple(GilButton::Start),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::LeftTrigger),
},
escape: LayerEntry {
button: Button::Simple(GilButton::Start),
mod1: Button::Simple(GilButton::Unknown),
mod2: Button::Simple(GilButton::Unknown),
},
enter: LayerEntry::default(),
command: LayerEntry::default(),
move_forward: LayerEntry::default(),
move_left: LayerEntry::default(),
move_back: LayerEntry::default(),
move_right: LayerEntry::default(),
jump: LayerEntry::default(),
sit: LayerEntry {
button: Button::Simple(GilButton::Select),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::LeftTrigger),
},
dance: LayerEntry {
button: Button::Simple(GilButton::Select),
mod1: Button::Simple(GilButton::LeftTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
glide: LayerEntry {
button: Button::Simple(GilButton::DPadUp),
mod1: Button::Simple(GilButton::Unknown),
mod2: Button::Simple(GilButton::Unknown),
},
climb: LayerEntry::default(),
climb_down: LayerEntry::default(),
swimup: LayerEntry::default(),
swimdown: LayerEntry::default(),
sneak: LayerEntry::default(),
toggle_lantern: LayerEntry {
button: Button::Simple(GilButton::DPadLeft),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
mount: LayerEntry::default(),
map: LayerEntry {
button: Button::Simple(GilButton::DPadLeft),
mod1: Button::Simple(GilButton::Unknown),
mod2: Button::Simple(GilButton::Unknown),
},
bag: LayerEntry::default(),
quest_log: LayerEntry {
button: Button::Simple(GilButton::DPadRight),
mod1: Button::Simple(GilButton::Unknown),
mod2: Button::Simple(GilButton::Unknown),
},
character_window: LayerEntry::default(),
social: LayerEntry {
button: Button::Simple(GilButton::DPadUp),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
crafting: LayerEntry {
button: Button::Simple(GilButton::Select),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
spellbook: LayerEntry {
button: Button::Simple(GilButton::Select),
mod1: Button::Simple(GilButton::Unknown),
mod2: Button::Simple(GilButton::Unknown),
},
settings: LayerEntry {
button: Button::Simple(GilButton::Start),
mod1: Button::Simple(GilButton::RightTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
help: LayerEntry {
button: Button::Simple(GilButton::Start),
mod1: Button::Simple(GilButton::LeftTrigger),
mod2: Button::Simple(GilButton::Unknown),
},
toggle_interface: LayerEntry::default(),
toggle_debug: LayerEntry::default(),
#[cfg(feature = "egui-ui")]
toggle_egui_debug: LayerEntry::default(),
toggle_chat: LayerEntry::default(),
fullscreen: LayerEntry::default(),
screenshot: LayerEntry::default(),
toggle_ingame_ui: LayerEntry::default(),
roll: LayerEntry::default(),
respawn: LayerEntry::default(),
interact: LayerEntry::default(),
toggle_wield: LayerEntry::default(),
swap_loadout: LayerEntry {
button: Button::Simple(GilButton::DPadDown),
mod1: Button::Simple(GilButton::Unknown),
mod2: Button::Simple(GilButton::Unknown),
},
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct GameButtons {
@ -151,8 +394,8 @@ pub mod con_settings {
Self {
primary: Button::Simple(GilButton::RightTrigger2),
secondary: Button::Simple(GilButton::LeftTrigger2),
block: Button::Simple(GilButton::LeftThumb),
slot1: Button::Simple(GilButton::RightThumb),
block: Button::Simple(GilButton::North),
slot1: Button::Simple(GilButton::Unknown),
slot2: Button::Simple(GilButton::Unknown),
slot3: Button::Simple(GilButton::Unknown),
slot4: Button::Simple(GilButton::Unknown),
@ -161,9 +404,9 @@ pub mod con_settings {
slot7: Button::Simple(GilButton::Unknown),
slot8: Button::Simple(GilButton::Unknown),
slot9: Button::Simple(GilButton::Unknown),
slot10: Button::Simple(GilButton::DPadDown),
toggle_cursor: Button::Simple(GilButton::DPadRight),
escape: Button::Simple(GilButton::Select),
slot10: Button::Simple(GilButton::Unknown),
toggle_cursor: Button::Simple(GilButton::Unknown),
escape: Button::Simple(GilButton::Unknown),
enter: Button::Simple(GilButton::Unknown),
command: Button::Simple(GilButton::Unknown),
move_forward: Button::Simple(GilButton::Unknown),
@ -173,16 +416,16 @@ pub mod con_settings {
jump: Button::Simple(GilButton::South),
sit: Button::Simple(GilButton::Unknown),
dance: Button::Simple(GilButton::Unknown),
glide: Button::Simple(GilButton::LeftTrigger),
glide: Button::Simple(GilButton::Unknown),
climb: Button::Simple(GilButton::South),
climb_down: Button::Simple(GilButton::East),
climb_down: Button::Simple(GilButton::West),
swimup: Button::Simple(GilButton::South),
swimdown: Button::Simple(GilButton::East),
sneak: Button::Simple(GilButton::East),
toggle_lantern: Button::Simple(GilButton::DPadLeft),
mount: Button::Simple(GilButton::North),
map: Button::Simple(GilButton::Start),
bag: Button::Simple(GilButton::Unknown),
swimdown: Button::Simple(GilButton::West),
sneak: Button::Simple(GilButton::LeftThumb),
toggle_lantern: Button::Simple(GilButton::Unknown),
mount: Button::Simple(GilButton::South),
map: Button::Simple(GilButton::Unknown),
bag: Button::Simple(GilButton::East),
quest_log: Button::Simple(GilButton::Unknown),
character_window: Button::Simple(GilButton::Unknown),
social: Button::Simple(GilButton::Unknown),
@ -198,11 +441,11 @@ pub mod con_settings {
fullscreen: Button::Simple(GilButton::Unknown),
screenshot: Button::Simple(GilButton::Unknown),
toggle_ingame_ui: Button::Simple(GilButton::Unknown),
roll: Button::Simple(GilButton::RightTrigger),
roll: Button::Simple(GilButton::RightThumb),
respawn: Button::Simple(GilButton::South),
interact: Button::Simple(GilButton::North),
toggle_wield: Button::Simple(GilButton::West),
swap_loadout: Button::Simple(GilButton::DPadUp),
interact: Button::Simple(GilButton::West),
toggle_wield: Button::Simple(GilButton::Unknown),
swap_loadout: Button::Simple(GilButton::Unknown),
}
}
}
@ -210,16 +453,16 @@ pub mod con_settings {
impl Default for MenuButtons {
fn default() -> Self {
Self {
up: Button::Simple(GilButton::Unknown),
down: Button::Simple(GilButton::Unknown),
left: Button::Simple(GilButton::Unknown),
right: Button::Simple(GilButton::Unknown),
up: Button::Simple(GilButton::DPadUp),
down: Button::Simple(GilButton::DPadDown),
left: Button::Simple(GilButton::DPadLeft),
right: Button::Simple(GilButton::DPadRight),
scroll_up: Button::Simple(GilButton::Unknown),
scroll_down: Button::Simple(GilButton::Unknown),
scroll_left: Button::Simple(GilButton::Unknown),
scroll_right: Button::Simple(GilButton::Unknown),
home: Button::Simple(GilButton::DPadUp),
end: Button::Simple(GilButton::DPadDown),
home: Button::Simple(GilButton::Unknown),
end: Button::Simple(GilButton::Unknown),
apply: Button::Simple(GilButton::South),
back: Button::Simple(GilButton::East),
exit: Button::Simple(GilButton::Mode),

View File

@ -3,7 +3,7 @@ use crate::{
error::Error,
game_input::GameInput,
render::Renderer,
settings::{ControlSettings, Settings},
settings::{gamepad::con_settings::LayerEntry, ControlSettings, Settings},
ui,
};
use common_base::span;
@ -399,6 +399,7 @@ pub struct Window {
pub focused: bool,
gilrs: Option<Gilrs>,
pub controller_settings: ControllerSettings,
pub controller_modifiers: Vec<Button>,
cursor_position: winit::dpi::PhysicalPosition<f64>,
mouse_emulation_vec: Vec2<f32>,
// Currently used to send and receive screenshot result messages
@ -501,6 +502,7 @@ impl Window {
focused: true,
gilrs,
controller_settings,
controller_modifiers: Vec::new(),
cursor_position: winit::dpi::PhysicalPosition::new(0.0, 0.0),
mouse_emulation_vec: Vec2::zero(),
// Currently used to send and receive screenshot result messages
@ -592,10 +594,43 @@ impl Window {
while let Some(event) = gilrs.next_event() {
fn handle_buttons(
settings: &ControllerSettings,
modifiers: &mut Vec<Button>,
events: &mut Vec<Event>,
button: &Button,
is_pressed: bool,
) {
if settings.modifier_buttons.contains(button) {
if is_pressed {
modifiers.push(*button);
} else {
let index = modifiers.iter().position(|x| x == button).unwrap();
modifiers.remove(index);
}
}
// have to make two LayerEntries so LB+RB can be treated equivalent to RB+LB
let l_entry1 = LayerEntry {
button: *button,
mod1: modifiers.get(0).copied().unwrap_or_default(),
mod2: modifiers.get(1).copied().unwrap_or_default(),
};
let l_entry2 = LayerEntry {
button: *button,
mod1: modifiers.get(1).copied().unwrap_or_default(),
mod2: modifiers.get(0).copied().unwrap_or_default(),
};
// have to check l_entry1 and then l_entry2 so LB+RB can be treated equivalent
// to RB+LB
if let Some(evs) = settings.layer_button_map.get(&l_entry1) {
for ev in evs {
events.push(Event::InputUpdate(*ev, is_pressed));
}
} else if let Some(evs) = settings.layer_button_map.get(&l_entry2) {
for ev in evs {
events.push(Event::InputUpdate(*ev, is_pressed));
}
}
if let Some(evs) = settings.game_button_map.get(button) {
for ev in evs {
events.push(Event::InputUpdate(*ev, is_pressed));
@ -613,6 +648,7 @@ impl Window {
| EventType::ButtonRepeated(button, code) => {
handle_buttons(
&self.controller_settings,
&mut self.controller_modifiers,
&mut self.events,
&Button::from((button, code)),
true,
@ -621,6 +657,7 @@ impl Window {
EventType::ButtonReleased(button, code) => {
handle_buttons(
&self.controller_settings,
&mut self.controller_modifiers,
&mut self.events,
&Button::from((button, code)),
false,