1use crate::components::InputKey;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum Bindable {
16 Forward,
18 Backward,
20 Left,
22 Right,
24 Sprint,
26 Jump,
28 Interact,
30}
31
32impl Bindable {
33 pub const ALL: [Bindable; 7] = [
35 Bindable::Forward,
36 Bindable::Backward,
37 Bindable::Left,
38 Bindable::Right,
39 Bindable::Sprint,
40 Bindable::Jump,
41 Bindable::Interact,
42 ];
43
44 pub fn setting_key(self) -> &'static str {
47 match self {
48 Bindable::Forward => "key_forward",
49 Bindable::Backward => "key_backward",
50 Bindable::Left => "key_left",
51 Bindable::Right => "key_right",
52 Bindable::Sprint => "key_sprint",
53 Bindable::Jump => "key_jump",
54 Bindable::Interact => "key_interact",
55 }
56 }
57
58 pub fn from_setting_key(key: &str) -> Option<Bindable> {
60 Bindable::ALL.into_iter().find(|b| b.setting_key() == key)
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69pub struct KeyMap {
70 #[serde(default = "def_forward")]
71 pub forward: InputKey,
73 #[serde(default = "def_backward")]
74 pub backward: InputKey,
76 #[serde(default = "def_left")]
77 pub left: InputKey,
79 #[serde(default = "def_right")]
80 pub right: InputKey,
82 #[serde(default = "def_sprint")]
83 pub sprint: InputKey,
85 #[serde(default = "def_jump")]
86 pub jump: InputKey,
88 #[serde(default = "def_interact")]
89 pub interact: InputKey,
91}
92
93impl KeyMap {
94 pub const DEFAULT: KeyMap = KeyMap {
96 forward: InputKey::W,
97 backward: InputKey::S,
98 left: InputKey::A,
99 right: InputKey::D,
100 sprint: InputKey::Shift,
101 jump: InputKey::Space,
102 interact: InputKey::E,
103 };
104
105 pub fn get(self, action: Bindable) -> InputKey {
107 match action {
108 Bindable::Forward => self.forward,
109 Bindable::Backward => self.backward,
110 Bindable::Left => self.left,
111 Bindable::Right => self.right,
112 Bindable::Sprint => self.sprint,
113 Bindable::Jump => self.jump,
114 Bindable::Interact => self.interact,
115 }
116 }
117
118 pub fn set(&mut self, action: Bindable, key: InputKey) {
120 match action {
121 Bindable::Forward => self.forward = key,
122 Bindable::Backward => self.backward = key,
123 Bindable::Left => self.left = key,
124 Bindable::Right => self.right = key,
125 Bindable::Sprint => self.sprint = key,
126 Bindable::Jump => self.jump = key,
127 Bindable::Interact => self.interact = key,
128 }
129 }
130
131 pub fn action_for_key(self, key: InputKey) -> Option<Bindable> {
135 Bindable::ALL.into_iter().find(|&b| self.get(b) == key)
136 }
137
138 pub fn rebind(&mut self, action: Bindable, new_key: InputKey) {
142 let old_key = self.get(action);
143 if old_key == new_key {
144 return;
145 }
146 if let Some(other) = self.action_for_key(new_key)
147 && other != action
148 {
149 self.set(other, old_key);
150 }
151 self.set(action, new_key);
152 }
153}
154
155impl Default for KeyMap {
156 fn default() -> Self {
157 Self::DEFAULT
158 }
159}
160
161fn def_forward() -> InputKey {
162 KeyMap::DEFAULT.forward
163}
164fn def_backward() -> InputKey {
165 KeyMap::DEFAULT.backward
166}
167fn def_left() -> InputKey {
168 KeyMap::DEFAULT.left
169}
170fn def_right() -> InputKey {
171 KeyMap::DEFAULT.right
172}
173fn def_sprint() -> InputKey {
174 KeyMap::DEFAULT.sprint
175}
176fn def_jump() -> InputKey {
177 KeyMap::DEFAULT.jump
178}
179fn def_interact() -> InputKey {
180 KeyMap::DEFAULT.interact
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 use alloc::string::String;
188 use alloc::vec::Vec;
189 #[test]
190 fn default_is_wasd_shift_space_e() {
191 let m = KeyMap::default();
192 assert_eq!(m.forward, InputKey::W);
193 assert_eq!(m.backward, InputKey::S);
194 assert_eq!(m.left, InputKey::A);
195 assert_eq!(m.right, InputKey::D);
196 assert_eq!(m.sprint, InputKey::Shift);
197 assert_eq!(m.jump, InputKey::Space);
198 assert_eq!(m.interact, InputKey::E);
199 }
200
201 #[test]
202 fn setting_key_round_trips() {
203 for b in Bindable::ALL {
204 assert_eq!(Bindable::from_setting_key(b.setting_key()), Some(b));
205 }
206 assert_eq!(Bindable::from_setting_key("vsync"), None);
207 assert_eq!(Bindable::from_setting_key("key_nope"), None);
208 }
209
210 #[test]
211 fn get_set_round_trip() {
212 let mut m = KeyMap::default();
213 m.set(Bindable::Forward, InputKey::Up);
214 assert_eq!(m.get(Bindable::Forward), InputKey::Up);
215 }
216
217 #[test]
218 fn set_get_cover_every_action_arm() {
219 let keys = [
222 InputKey::Up,
223 InputKey::Down,
224 InputKey::Left,
225 InputKey::Right,
226 InputKey::Q,
227 InputKey::R,
228 InputKey::T,
229 ];
230 let mut m = KeyMap::default();
231 for (b, k) in Bindable::ALL.into_iter().zip(keys) {
232 m.set(b, k);
233 }
234 for (b, k) in Bindable::ALL.into_iter().zip(keys) {
235 assert_eq!(m.get(b), k);
236 }
237 }
238
239 #[test]
240 fn empty_cbor_map_uses_all_defaults() {
241 let empty: alloc::collections::BTreeMap<String, InputKey> =
244 alloc::collections::BTreeMap::new();
245 let mut bytes = Vec::new();
246 ciborium::into_writer(&empty, &mut bytes).unwrap();
247 let loaded: KeyMap = ciborium::from_reader(&bytes[..]).unwrap();
248 assert_eq!(loaded, KeyMap::DEFAULT);
249 }
250
251 #[test]
252 fn action_for_key_finds_the_holder() {
253 let m = KeyMap::default();
254 assert_eq!(m.action_for_key(InputKey::W), Some(Bindable::Forward));
255 assert_eq!(m.action_for_key(InputKey::Space), Some(Bindable::Jump));
256 assert_eq!(m.action_for_key(InputKey::Q), None);
258 }
259
260 #[test]
261 fn rebind_to_free_key_just_sets_it() {
262 let mut m = KeyMap::default();
263 m.rebind(Bindable::Forward, InputKey::Q);
264 assert_eq!(m.forward, InputKey::Q);
265 assert_eq!(m.backward, InputKey::S);
267 }
268
269 #[test]
270 fn rebind_to_own_key_is_a_noop() {
271 let mut m = KeyMap::default();
272 m.rebind(Bindable::Forward, InputKey::W);
273 assert_eq!(m, KeyMap::default());
274 }
275
276 #[test]
277 fn rebind_to_occupied_key_swaps() {
278 let mut m = KeyMap::default();
281 m.rebind(Bindable::Forward, InputKey::S);
282 assert_eq!(m.forward, InputKey::S);
283 assert_eq!(m.backward, InputKey::W);
284 for b in Bindable::ALL {
286 assert_eq!(m.action_for_key(m.get(b)), Some(b));
287 }
288 }
289
290 #[test]
291 fn cbor_round_trip_and_missing_field_defaults() {
292 let m = KeyMap {
294 forward: InputKey::Up,
295 ..KeyMap::default()
296 };
297 let mut bytes = Vec::new();
298 ciborium::into_writer(&m, &mut bytes).unwrap();
299 let back: KeyMap = ciborium::from_reader(&bytes[..]).unwrap();
300 assert_eq!(back, m);
301
302 #[derive(Serialize)]
305 struct Partial {
306 forward: InputKey,
307 backward: InputKey,
308 left: InputKey,
309 right: InputKey,
310 sprint: InputKey,
311 jump: InputKey,
312 }
314 let partial = Partial {
315 forward: InputKey::Up,
316 backward: InputKey::S,
317 left: InputKey::A,
318 right: InputKey::D,
319 sprint: InputKey::Shift,
320 jump: InputKey::Space,
321 };
322 let mut bytes = Vec::new();
323 ciborium::into_writer(&partial, &mut bytes).unwrap();
324 let loaded: KeyMap = ciborium::from_reader(&bytes[..]).unwrap();
325 assert_eq!(loaded.forward, InputKey::Up);
326 assert_eq!(loaded.interact, InputKey::E);
327 }
328}