veloren_voxygen/settings/
control.rs

1use crate::{game_input::GameInput, window::KeyMouse};
2use hashbrown::{HashMap, HashSet};
3use serde::{Deserialize, Serialize};
4use strum::IntoEnumIterator;
5use winit::event::{MouseButton, VirtualKeyCode};
6
7// ControlSetting-like struct used by Serde, to handle not serializing/building
8// post-deserializing the inverse_keybindings hashmap
9#[derive(Serialize, Deserialize)]
10struct ControlSettingsSerde {
11    keybindings: HashMap<GameInput, Option<KeyMouse>>,
12}
13
14impl From<ControlSettings> for ControlSettingsSerde {
15    fn from(control_settings: ControlSettings) -> Self {
16        let mut user_bindings: HashMap<GameInput, Option<KeyMouse>> = HashMap::new();
17        // Do a delta between default() ControlSettings and the argument, and let
18        // keybindings be only the custom keybindings chosen by the user.
19        for (k, v) in control_settings.keybindings {
20            if ControlSettings::default_binding(k) != v {
21                // Keybinding chosen by the user
22                user_bindings.insert(k, v);
23            }
24        }
25        ControlSettingsSerde {
26            keybindings: user_bindings,
27        }
28    }
29}
30
31/// `ControlSettings` contains keybindings.
32#[derive(Clone, Debug, Serialize, Deserialize)]
33#[serde(from = "ControlSettingsSerde", into = "ControlSettingsSerde")]
34pub struct ControlSettings {
35    pub keybindings: HashMap<GameInput, Option<KeyMouse>>,
36    pub inverse_keybindings: HashMap<KeyMouse, HashSet<GameInput>>, // used in event loop
37}
38
39impl From<ControlSettingsSerde> for ControlSettings {
40    fn from(control_serde: ControlSettingsSerde) -> Self {
41        let user_keybindings = control_serde.keybindings;
42        let mut control_settings = ControlSettings::default();
43        for (k, maybe_v) in user_keybindings {
44            match maybe_v {
45                Some(v) => control_settings.modify_binding(k, v),
46                None => control_settings.remove_binding(k),
47            }
48        }
49        control_settings
50    }
51}
52
53/// Since Macbook trackpads lack middle click, on OS X we default to LShift
54/// instead It is an imperfect heuristic, but hopefully it will be a slightly
55/// better default, and the two places we default to middle click currently
56/// (roll and wall jump) are both situations where you cannot glide (the other
57/// default mapping for LShift).
58#[cfg(target_os = "macos")]
59const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Key(VirtualKeyCode::Grave);
60#[cfg(not(target_os = "macos"))]
61const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Mouse(MouseButton::Middle);
62
63impl ControlSettings {
64    pub fn remove_binding(&mut self, game_input: GameInput) {
65        if let Some(inverse) = self
66            .keybindings
67            .insert(game_input, None)
68            .flatten()
69            .and_then(|key_mouse| self.inverse_keybindings.get_mut(&key_mouse))
70        {
71            inverse.remove(&game_input);
72        }
73    }
74
75    pub fn get_binding(&self, game_input: GameInput) -> Option<KeyMouse> {
76        self.keybindings.get(&game_input).copied().flatten()
77    }
78
79    pub fn get_associated_game_inputs(&self, key_mouse: &KeyMouse) -> Option<&HashSet<GameInput>> {
80        self.inverse_keybindings.get(key_mouse)
81    }
82
83    pub fn insert_binding(&mut self, game_input: GameInput, key_mouse: KeyMouse) {
84        self.keybindings.insert(game_input, Some(key_mouse));
85        self.inverse_keybindings
86            .entry(key_mouse)
87            .or_default()
88            .insert(game_input);
89    }
90
91    pub fn modify_binding(&mut self, game_input: GameInput, key_mouse: KeyMouse) {
92        // For the KeyMouse->GameInput hashmap, we first need to remove the GameInput
93        // from the old binding
94        if let Some(old_binding) = self.get_binding(game_input) {
95            self.inverse_keybindings
96                .entry(old_binding)
97                .or_default()
98                .remove(&game_input);
99        }
100        // then we add the GameInput to the proper key
101        self.inverse_keybindings
102            .entry(key_mouse)
103            .or_default()
104            .insert(game_input);
105        // For the GameInput->KeyMouse hashmap, just overwrite the value
106        self.keybindings.insert(game_input, Some(key_mouse));
107    }
108
109    /// Return true if this key is used for multiple GameInputs that aren't
110    /// expected to be safe to have bound to the same key at the same time
111    pub fn has_conflicting_bindings(&self, key_mouse: KeyMouse) -> bool {
112        if let Some(game_inputs) = self.inverse_keybindings.get(&key_mouse) {
113            for a in game_inputs.iter() {
114                for b in game_inputs.iter() {
115                    if !GameInput::can_share_bindings(*a, *b) {
116                        return true;
117                    }
118                }
119            }
120        }
121        false
122    }
123
124    pub fn default_binding(game_input: GameInput) -> Option<KeyMouse> {
125        // If a new GameInput is added, be sure to update GameInput::iterator() too!
126        match game_input {
127            GameInput::Primary => Some(KeyMouse::Mouse(MouseButton::Left)),
128            GameInput::Secondary => Some(KeyMouse::Mouse(MouseButton::Right)),
129            GameInput::Block => Some(KeyMouse::Key(VirtualKeyCode::LAlt)),
130            GameInput::ToggleCursor => Some(KeyMouse::Key(VirtualKeyCode::Comma)),
131            GameInput::Escape => Some(KeyMouse::Key(VirtualKeyCode::Escape)),
132            GameInput::Chat => Some(KeyMouse::Key(VirtualKeyCode::Return)),
133            GameInput::Command => Some(KeyMouse::Key(VirtualKeyCode::Slash)),
134            GameInput::MoveForward => Some(KeyMouse::Key(VirtualKeyCode::W)),
135            GameInput::MoveLeft => Some(KeyMouse::Key(VirtualKeyCode::A)),
136            GameInput::MoveBack => Some(KeyMouse::Key(VirtualKeyCode::S)),
137            GameInput::MoveRight => Some(KeyMouse::Key(VirtualKeyCode::D)),
138            GameInput::Jump => Some(KeyMouse::Key(VirtualKeyCode::Space)),
139            GameInput::Sit => Some(KeyMouse::Key(VirtualKeyCode::K)),
140            GameInput::Crawl => Some(KeyMouse::Key(VirtualKeyCode::Down)),
141            GameInput::Dance => Some(KeyMouse::Key(VirtualKeyCode::J)),
142            GameInput::Greet => Some(KeyMouse::Key(VirtualKeyCode::H)),
143            GameInput::Glide => Some(KeyMouse::Key(VirtualKeyCode::LControl)),
144            GameInput::Climb => Some(KeyMouse::Key(VirtualKeyCode::Space)),
145            GameInput::ClimbDown => Some(KeyMouse::Key(VirtualKeyCode::LShift)),
146            GameInput::SwimUp => Some(KeyMouse::Key(VirtualKeyCode::Space)),
147            GameInput::SwimDown => Some(KeyMouse::Key(VirtualKeyCode::LShift)),
148            GameInput::Fly => Some(KeyMouse::Key(VirtualKeyCode::H)),
149            GameInput::Sneak => Some(KeyMouse::Key(VirtualKeyCode::LShift)),
150            GameInput::ToggleLantern => Some(KeyMouse::Key(VirtualKeyCode::G)),
151            GameInput::Mount => Some(KeyMouse::Key(VirtualKeyCode::F)),
152            GameInput::StayFollow => Some(KeyMouse::Key(VirtualKeyCode::V)),
153            GameInput::Map => Some(KeyMouse::Key(VirtualKeyCode::M)),
154            GameInput::Inventory => Some(KeyMouse::Key(VirtualKeyCode::I)),
155            GameInput::Trade => Some(KeyMouse::Key(VirtualKeyCode::T)),
156            GameInput::Social => Some(KeyMouse::Key(VirtualKeyCode::O)),
157            GameInput::Crafting => Some(KeyMouse::Key(VirtualKeyCode::C)),
158            GameInput::Diary => Some(KeyMouse::Key(VirtualKeyCode::P)),
159            GameInput::Settings => Some(KeyMouse::Key(VirtualKeyCode::F10)),
160            GameInput::Controls => Some(KeyMouse::Key(VirtualKeyCode::F1)),
161            GameInput::ToggleInterface => Some(KeyMouse::Key(VirtualKeyCode::F2)),
162            GameInput::ToggleDebug => Some(KeyMouse::Key(VirtualKeyCode::F3)),
163            #[cfg(feature = "egui-ui")]
164            GameInput::ToggleEguiDebug => Some(KeyMouse::Key(VirtualKeyCode::F7)),
165            GameInput::ToggleChat => Some(KeyMouse::Key(VirtualKeyCode::F5)),
166            GameInput::Fullscreen => Some(KeyMouse::Key(VirtualKeyCode::F11)),
167            GameInput::Screenshot => Some(KeyMouse::Key(VirtualKeyCode::F4)),
168            GameInput::ToggleIngameUi => Some(KeyMouse::Key(VirtualKeyCode::F6)),
169            GameInput::Roll => Some(MIDDLE_CLICK_KEY),
170            GameInput::GiveUp => Some(KeyMouse::Key(VirtualKeyCode::Space)),
171            GameInput::Respawn => Some(KeyMouse::Key(VirtualKeyCode::Space)),
172            GameInput::Interact => Some(KeyMouse::Key(VirtualKeyCode::E)),
173            GameInput::ToggleWield => Some(KeyMouse::Key(VirtualKeyCode::R)),
174            GameInput::FreeLook => Some(KeyMouse::Key(VirtualKeyCode::L)),
175            GameInput::AutoWalk => Some(KeyMouse::Key(VirtualKeyCode::Period)),
176            GameInput::ZoomIn => Some(KeyMouse::Key(VirtualKeyCode::RBracket)),
177            GameInput::ZoomOut => Some(KeyMouse::Key(VirtualKeyCode::LBracket)),
178            GameInput::ZoomLock => None,
179            GameInput::CameraClamp => Some(KeyMouse::Key(VirtualKeyCode::Apostrophe)),
180            GameInput::CycleCamera => Some(KeyMouse::Key(VirtualKeyCode::Key0)),
181            GameInput::Slot1 => Some(KeyMouse::Key(VirtualKeyCode::Key1)),
182            GameInput::Slot2 => Some(KeyMouse::Key(VirtualKeyCode::Key2)),
183            GameInput::Slot3 => Some(KeyMouse::Key(VirtualKeyCode::Key3)),
184            GameInput::Slot4 => Some(KeyMouse::Key(VirtualKeyCode::Key4)),
185            GameInput::Slot5 => Some(KeyMouse::Key(VirtualKeyCode::Key5)),
186            GameInput::Slot6 => Some(KeyMouse::Key(VirtualKeyCode::Key6)),
187            GameInput::Slot7 => Some(KeyMouse::Key(VirtualKeyCode::Key7)),
188            GameInput::Slot8 => Some(KeyMouse::Key(VirtualKeyCode::Key8)),
189            GameInput::Slot9 => Some(KeyMouse::Key(VirtualKeyCode::Key9)),
190            GameInput::Slot10 => Some(KeyMouse::Key(VirtualKeyCode::Q)),
191            GameInput::SwapLoadout => Some(KeyMouse::Key(VirtualKeyCode::Tab)),
192            GameInput::Select => Some(KeyMouse::Key(VirtualKeyCode::X)),
193            GameInput::AcceptGroupInvite => Some(KeyMouse::Key(VirtualKeyCode::Y)),
194            GameInput::DeclineGroupInvite => Some(KeyMouse::Key(VirtualKeyCode::N)),
195            GameInput::MapZoomIn => Some(KeyMouse::Key(VirtualKeyCode::Plus)),
196            GameInput::MapZoomOut => Some(KeyMouse::Key(VirtualKeyCode::Minus)),
197            GameInput::MapSetMarker => Some(KeyMouse::Mouse(MouseButton::Middle)),
198            GameInput::SpectateSpeedBoost => Some(KeyMouse::Key(VirtualKeyCode::LControl)),
199            GameInput::SpectateViewpoint => Some(KeyMouse::Mouse(MouseButton::Middle)),
200            GameInput::MuteMaster => Some(KeyMouse::Key(VirtualKeyCode::Mute)),
201            GameInput::MuteInactiveMaster => None,
202            GameInput::MuteMusic => Some(KeyMouse::Key(VirtualKeyCode::F8)),
203            GameInput::MuteSfx => None,
204            GameInput::MuteAmbience => None,
205            GameInput::ToggleWalk => Some(KeyMouse::Key(VirtualKeyCode::B)),
206        }
207    }
208}
209
210impl Default for ControlSettings {
211    fn default() -> Self {
212        let mut new_settings = Self {
213            keybindings: HashMap::new(),
214            inverse_keybindings: HashMap::new(),
215        };
216        // Sets the initial keybindings for those GameInputs.
217        for game_input in GameInput::iter() {
218            match ControlSettings::default_binding(game_input) {
219                None => {},
220                Some(default) => new_settings.insert_binding(game_input, default),
221            };
222        }
223        new_settings
224    }
225}