cogs_gamedev/controls/
polling.rs

1use std::{collections::HashSet, hash::Hash};
2
3use ahash::AHashMap;
4use enum_map::{Enum, EnumMap};
5
6use super::InputHandler;
7
8/// Polling-based input handler.
9/// See module-level documentation for more.
10pub struct PollingInputHandler<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + 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}
18
19impl<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + Clone> PollingInputHandler<I, C> {
20    /// Create a new PollingInputHandler without any controls.
21    pub fn new_empty() -> Self {
22        Self {
23            control_config: AHashMap::new(),
24            // conveniently, the default value for u32 is 0!
25            // and we want the map to start full of zeros.
26            // (zeroes?)
27            input_time: EnumMap::default(),
28            listening_for_input: None,
29        }
30    }
31
32    /// Create a new PollingInputHandler with the specified controls.
33    /// The HashMap in should map inputs to the controls you want them to actuate.
34    pub fn new(control_config: AHashMap<I, C>) -> Self {
35        Self {
36            control_config,
37            input_time: EnumMap::default(),
38            listening_for_input: None,
39        }
40    }
41
42    /// Update the input handler. You MUST CALL THIS FIRST THING in your game loop.
43    /// Otherwise things won't get updated correctly.
44    pub fn update(&mut self, new_inputs: &HashSet<I>) {
45        match &self.listening_for_input {
46            None => {
47                for (input, control) in self.control_config.iter() {
48                    if new_inputs.contains(input) {
49                        // this input is getting pressed!
50                        // increment our timer
51                        self.input_time[control.to_owned()] += 1;
52                    } else {
53                        // this input is not getting pressed
54                        // reset our timer
55                        self.input_time[control.to_owned()] = 0;
56                    }
57                }
58            }
59            Some(ctrl) => {
60                if let Some(input) = new_inputs.iter().next() {
61                    // we're pressing something!
62                    self.control_config
63                        .insert(input.to_owned(), ctrl.to_owned());
64                    self.listening_for_input = None;
65                }
66            }
67        }
68    }
69}
70
71// there's gotta be a better way to do these generics
72impl<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + Clone> InputHandler<I, C>
73    for PollingInputHandler<I, C>
74{
75    /// Is this input pressed down?
76    /// i.e. is the player pressing the button?
77    fn pressed(&self, control: C) -> bool {
78        self.input_time[control] >= 1
79    }
80
81    /// Is this input released?
82    /// i.e. is the player *not* pressing the button?
83    fn released(&self, control: C) -> bool {
84        self.input_time[control] == 0
85    }
86
87    /// Is this input being clicked down?
88    /// i.e. was it up last frame, but down this frame?
89    fn clicked_down(&self, control: C) -> bool {
90        self.input_time[control] == 1
91    }
92}
93
94/// EnumMap doesn't implement Clone so we do it ourselves
95impl<I: Hash + Eq + PartialEq + Clone, C: Enum<u32> + Clone> Clone for PollingInputHandler<I, C> {
96    fn clone(&self) -> Self {
97        let control_config = self.control_config.clone();
98        let listening_for_input = self.listening_for_input.clone();
99
100        let mut input_time = EnumMap::default();
101        for (k, v) in self.input_time.iter() {
102            input_time[k] = *v;
103        }
104
105        Self {
106            control_config,
107            input_time,
108            listening_for_input,
109        }
110    }
111}