cogs_gamedev/controls/
event.rs

1use std::hash::Hash;
2
3use ahash::AHashMap;
4use enum_map::{Enum, EnumMap};
5
6use super::InputHandler;
7
8/// Event-based input handler
9/// See module-level documentation for more detail.
10pub struct EventInputHandler<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + Enum<bool> + Clone> {
11    /// Maps inputs to the controls they activate
12    control_config: AHashMap<I, C>,
13    /// How long each control has been pressed
14    input_time: EnumMap<C, u32>,
15    /// If this is Some, we're waiting for a new control config.
16    listening_for_input: Option<C>,
17    /// The set of all the control events we've gotten since we last called `update`
18    pressed_controls: EnumMap<C, bool>,
19}
20
21impl<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + Enum<bool> + Clone> EventInputHandler<I, C> {
22    /// Create a new EventInputHandler without any controls.
23    pub fn new_empty() -> Self {
24        Self::new(AHashMap::new())
25    }
26
27    /// Create a new EventInputHandler with the specified controls.
28    /// The HashMap in should map inputs to the controls you want them to actuate.
29    pub fn new(control_config: AHashMap<I, C>) -> Self {
30        Self {
31            control_config,
32            input_time: EnumMap::default(),
33            listening_for_input: None,
34            pressed_controls: EnumMap::default(),
35        }
36    }
37
38    /// Call this function when your game engine gives you a KeyDown event,
39    /// or any event signaling that an input is newly pressed down.
40    pub fn input_down(&mut self, input: I) {
41        match &self.listening_for_input {
42            None => {
43                if let Some(control) = self.control_config.get(&input) {
44                    self.pressed_controls[control.to_owned()] = true;
45                }
46            }
47            Some(ctrl) => {
48                // Update the control ...
49                self.control_config.insert(input, ctrl.to_owned());
50                // and stop listening for inputs
51                self.listening_for_input = None;
52            }
53        }
54    }
55
56    /// Call this function when your game engine gives you a KeyUp event,
57    /// or any event signaling that an input has been released.
58    pub fn input_up(&mut self, input: I) {
59        if let Some(control) = self.control_config.get(&input) {
60            self.pressed_controls[control.to_owned()] = false;
61        }
62    }
63
64    /// Manually clear all the inputs the handler has received.
65    ///
66    /// (I'm not sure why you would want to do this, but hey, might as well
67    /// expose the functionality.)
68    pub fn clear_inputs(&mut self) {
69        self.pressed_controls.clear();
70    }
71
72    /// Update the input handler. You MUST CALL THIS FIRST THING in your game loop.
73    /// Otherwise things won't get updated correctly.
74    pub fn update(&mut self) {
75        if self.listening_for_input.is_none() {
76            for (control, pressed) in self.pressed_controls.iter() {
77                if *pressed {
78                    // this input is getting pressed!
79                    // increment our timer
80                    self.input_time[control] += 1;
81                } else {
82                    // this input is not getting pressed
83                    // reset our timer
84                    self.input_time[control] = 0;
85                }
86            }
87        }
88    }
89}
90
91// there's gotta be a better way to do these generics
92impl<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + Enum<bool> + Clone> InputHandler<I, C>
93    for EventInputHandler<I, C>
94{
95    /// Is this input pressed down?
96    /// i.e. is the player pressing the button?
97    fn pressed(&self, control: C) -> bool {
98        self.input_time[control] >= 1
99    }
100
101    /// Is this input released?
102    /// i.e. is the player *not* pressing the button?
103    fn released(&self, control: C) -> bool {
104        self.input_time[control] == 0
105    }
106
107    /// Is this input being clicked down?
108    /// i.e. was it up last frame, but down this frame?
109    fn clicked_down(&self, control: C) -> bool {
110        self.input_time[control] == 1
111    }
112}
113
114/// EnumMap doesn't implement Clone so we do it ourselves
115impl<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + Enum<bool> + Clone> Clone
116    for EventInputHandler<I, C>
117{
118    fn clone(&self) -> Self {
119        let control_config = self.control_config.clone();
120        let listening_for_input = self.listening_for_input.clone();
121
122        let mut pressed_controls = EnumMap::default();
123        for (k, v) in self.pressed_controls.iter() {
124            pressed_controls[k] = *v;
125        }
126
127        let mut input_time = EnumMap::default();
128        for (k, v) in self.input_time.iter() {
129            input_time[k] = *v;
130        }
131
132        Self {
133            control_config,
134            input_time,
135            listening_for_input,
136            pressed_controls,
137        }
138    }
139}