1use std::collections::HashMap;
6
7use super::action::InputAction;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum VirtualKey {
13 A,
15 B,
16 C,
17 D,
18 E,
19 F,
20 G,
21 H,
22 I,
23 J,
24 K,
25 L,
26 M,
27 N,
28 O,
29 P,
30 Q,
31 R,
32 S,
33 T,
34 U,
35 V,
36 W,
37 X,
38 Y,
39 Z,
40
41 Num0,
43 Num1,
44 Num2,
45 Num3,
46 Num4,
47 Num5,
48 Num6,
49 Num7,
50 Num8,
51 Num9,
52
53 ArrowUp,
55 ArrowDown,
56 ArrowLeft,
57 ArrowRight,
58
59 ShiftLeft,
61 ShiftRight,
62 CtrlLeft,
63 CtrlRight,
64 AltLeft,
65 AltRight,
66
67 Space,
69 Enter,
70 Escape,
71 Tab,
72 Backspace,
73 Delete,
74 Home,
75 End,
76 PageUp,
77 PageDown,
78 F1,
79 F2,
80 F3,
81 F4,
82 F5,
83 F6,
84 F7,
85 F8,
86 F9,
87 F10,
88 F11,
89 F12,
90
91 MouseLeft,
93 MouseRight,
94 MouseMiddle,
95 MouseButton4,
96 MouseButton5,
97
98 GamepadA,
100 GamepadB,
101 GamepadX,
102 GamepadY,
103 GamepadLB,
104 GamepadRB,
105 GamepadLT,
106 GamepadRT,
107 GamepadStart,
108 GamepadSelect,
109 GamepadDpadUp,
110 GamepadDpadDown,
111 GamepadDpadLeft,
112 GamepadDpadRight,
113 GamepadLeftStickPress,
114 GamepadRightStickPress,
115}
116
117pub struct InputBindingMap {
122 bindings: HashMap<VirtualKey, InputAction>,
123}
124
125impl InputBindingMap {
126 pub fn new() -> Self {
127 Self {
128 bindings: HashMap::new(),
129 }
130 }
131
132 pub fn default_bindings() -> Self {
134 let mut map = Self::new();
135
136 map.bind(VirtualKey::W, InputAction::MoveForward);
138 map.bind(VirtualKey::S, InputAction::MoveBackward);
139 map.bind(VirtualKey::A, InputAction::MoveLeft);
140 map.bind(VirtualKey::D, InputAction::MoveRight);
141
142 map.bind(VirtualKey::ArrowUp, InputAction::MoveForward);
144 map.bind(VirtualKey::ArrowDown, InputAction::MoveBackward);
145 map.bind(VirtualKey::ArrowLeft, InputAction::MoveLeft);
146 map.bind(VirtualKey::ArrowRight, InputAction::MoveRight);
147
148 map.bind(VirtualKey::Space, InputAction::Jump);
150 map.bind(VirtualKey::ShiftLeft, InputAction::Sprint);
151
152 map.bind(VirtualKey::MouseRight, InputAction::CameraRotate);
154 map.bind(VirtualKey::MouseMiddle, InputAction::CameraPan);
155
156 map.bind(VirtualKey::MouseLeft, InputAction::PrimaryAction);
158 map.bind(VirtualKey::E, InputAction::Interact);
159 map.bind(VirtualKey::Escape, InputAction::Cancel);
160 map.bind(VirtualKey::Enter, InputAction::Confirm);
161
162 map.bind(VirtualKey::Q, InputAction::Block);
165
166 map.bind(VirtualKey::I, InputAction::OpenInventory);
168 map.bind(VirtualKey::M, InputAction::OpenMap);
169 map.bind(VirtualKey::T, InputAction::OpenChat);
170
171 map.bind(VirtualKey::Num1, InputAction::QuickSlot(0));
173 map.bind(VirtualKey::Num2, InputAction::QuickSlot(1));
174 map.bind(VirtualKey::Num3, InputAction::QuickSlot(2));
175 map.bind(VirtualKey::Num4, InputAction::QuickSlot(3));
176 map.bind(VirtualKey::Num5, InputAction::QuickSlot(4));
177 map.bind(VirtualKey::Num6, InputAction::QuickSlot(5));
178 map.bind(VirtualKey::Num7, InputAction::QuickSlot(6));
179 map.bind(VirtualKey::Num8, InputAction::QuickSlot(7));
180 map.bind(VirtualKey::Num9, InputAction::QuickSlot(8));
181
182 map.bind(VirtualKey::F11, InputAction::ToggleFullscreen);
184 map.bind(VirtualKey::F12, InputAction::Screenshot);
185
186 map.bind(VirtualKey::GamepadA, InputAction::Confirm);
188 map.bind(VirtualKey::GamepadB, InputAction::Cancel);
189 map.bind(VirtualKey::GamepadX, InputAction::Interact);
190 map.bind(VirtualKey::GamepadY, InputAction::OpenInventory);
191 map.bind(VirtualKey::GamepadRB, InputAction::Attack);
192 map.bind(VirtualKey::GamepadLB, InputAction::Block);
193 map.bind(VirtualKey::GamepadStart, InputAction::Pause);
194
195 map
196 }
197
198 pub fn bind(&mut self, key: VirtualKey, action: InputAction) {
200 self.bindings.insert(key, action);
201 }
202
203 pub fn unbind(&mut self, key: VirtualKey) {
205 self.bindings.remove(&key);
206 }
207
208 pub fn action_for(&self, key: VirtualKey) -> Option<InputAction> {
210 self.bindings.get(&key).copied()
211 }
212
213 pub fn len(&self) -> usize {
215 self.bindings.len()
216 }
217
218 pub fn is_empty(&self) -> bool {
220 self.bindings.is_empty()
221 }
222
223 pub fn iter(&self) -> impl Iterator<Item = (&VirtualKey, &InputAction)> {
225 self.bindings.iter()
226 }
227}
228
229impl Default for InputBindingMap {
230 fn default() -> Self {
231 Self::default_bindings()
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 #[test]
240 fn default_bindings_has_wasd() {
241 let map = InputBindingMap::default_bindings();
242 assert_eq!(map.action_for(VirtualKey::W), Some(InputAction::MoveForward));
243 assert_eq!(map.action_for(VirtualKey::S), Some(InputAction::MoveBackward));
244 assert_eq!(map.action_for(VirtualKey::A), Some(InputAction::MoveLeft));
245 assert_eq!(map.action_for(VirtualKey::D), Some(InputAction::MoveRight));
246 }
247
248 #[test]
249 fn bind_and_unbind() {
250 let mut map = InputBindingMap::new();
251 map.bind(VirtualKey::X, InputAction::Attack);
252 assert_eq!(map.action_for(VirtualKey::X), Some(InputAction::Attack));
253
254 map.unbind(VirtualKey::X);
255 assert_eq!(map.action_for(VirtualKey::X), None);
256 }
257
258 #[test]
259 fn rebind_overwrites() {
260 let mut map = InputBindingMap::new();
261 map.bind(VirtualKey::Space, InputAction::Jump);
262 map.bind(VirtualKey::Space, InputAction::Dodge);
263 assert_eq!(map.action_for(VirtualKey::Space), Some(InputAction::Dodge));
264 }
265
266 #[test]
267 fn default_bindings_nonempty() {
268 let map = InputBindingMap::default_bindings();
269 assert!(!map.is_empty());
270 assert!(map.len() > 20);
271 }
272
273 #[test]
274 fn arrow_keys_bound() {
275 let map = InputBindingMap::default_bindings();
276 assert_eq!(map.action_for(VirtualKey::ArrowUp), Some(InputAction::MoveForward));
277 assert_eq!(map.action_for(VirtualKey::ArrowDown), Some(InputAction::MoveBackward));
278 assert_eq!(map.action_for(VirtualKey::ArrowLeft), Some(InputAction::MoveLeft));
279 assert_eq!(map.action_for(VirtualKey::ArrowRight), Some(InputAction::MoveRight));
280 }
281
282 #[test]
283 fn quick_slots_bound() {
284 let map = InputBindingMap::default_bindings();
285 assert_eq!(map.action_for(VirtualKey::Num1), Some(InputAction::QuickSlot(0)));
286 assert_eq!(map.action_for(VirtualKey::Num9), Some(InputAction::QuickSlot(8)));
287 }
288
289 #[test]
290 fn mouse_left_maps_to_primary_action() {
291 let map = InputBindingMap::default_bindings();
292 assert_eq!(map.action_for(VirtualKey::MouseLeft), Some(InputAction::PrimaryAction));
294 }
295
296 #[test]
297 fn gamepad_bindings() {
298 let map = InputBindingMap::default_bindings();
299 assert_eq!(map.action_for(VirtualKey::GamepadA), Some(InputAction::Confirm));
300 assert_eq!(map.action_for(VirtualKey::GamepadB), Some(InputAction::Cancel));
301 assert_eq!(map.action_for(VirtualKey::GamepadStart), Some(InputAction::Pause));
302 }
303}