oxygengine_input/resources/
stack.rs1use crate::{component::InputStackInstance, resources::controller::*};
2use core::{
3 ecs::{life_cycle::EntityChanges, Entity},
4 id::ID,
5 Scalar,
6};
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, HashSet};
9
10pub type InputStackListenerId = ID<InputStackListener>;
11pub type InputStackMappings = HashSet<String>;
12pub type InputStackScaledMappings = HashMap<String, Scalar>;
13
14#[derive(Debug, Default, Clone, Serialize, Deserialize)]
15pub struct InputStackTrigger {
16 #[serde(default)]
17 pub consume: bool,
18 #[serde(default)]
19 mappings: InputStackMappings,
20 #[serde(skip)]
21 state: TriggerState,
22}
23
24impl InputStackTrigger {
25 pub fn new(mappings: impl Into<InputStackMappings>) -> Self {
26 Self {
27 consume: false,
28 mappings: mappings.into(),
29 state: Default::default(),
30 }
31 }
32
33 pub fn mappings(&self) -> &InputStackMappings {
34 &self.mappings
35 }
36
37 pub fn state(&self) -> TriggerState {
38 self.state
39 }
40}
41
42#[derive(Debug, Default, Clone, Serialize, Deserialize)]
43pub struct InputStackChannel {
44 #[serde(default)]
45 mappings: InputStackScaledMappings,
46 #[serde(skip)]
47 state: Scalar,
48}
49
50impl InputStackChannel {
51 pub fn new(mappings: impl Into<InputStackScaledMappings>) -> Self {
52 Self {
53 mappings: mappings.into(),
54 state: 0.0,
55 }
56 }
57
58 pub fn mappings(&self) -> &InputStackScaledMappings {
59 &self.mappings
60 }
61
62 pub fn state(&self) -> Scalar {
63 self.state
64 }
65}
66
67#[derive(Debug, Default, Clone, Serialize, Deserialize)]
68pub struct InputStackAxes {
69 #[serde(default)]
70 pub consume: bool,
71 #[serde(default)]
72 channels: Vec<InputStackChannel>,
73}
74
75impl InputStackAxes {
76 pub fn new(channels: impl Iterator<Item = impl Into<InputStackChannel>>) -> Self {
77 Self {
78 consume: false,
79 channels: channels.map(|item| item.into()).collect::<Vec<_>>(),
80 }
81 }
82
83 pub fn new_single(channel: impl Into<InputStackChannel>) -> Self {
84 Self {
85 consume: false,
86 channels: vec![channel.into()],
87 }
88 }
89
90 pub fn channels(&self) -> &[InputStackChannel] {
91 &self.channels
92 }
93
94 pub fn channels_state_or_default<const N: usize>(&self) -> [Scalar; N] {
95 let mut result = [0.0; N];
96 for (channel, result) in self.channels.iter().zip(result.iter_mut()) {
97 *result = channel.state;
98 }
99 result
100 }
101
102 pub fn channel_state_or_default(&self) -> Scalar {
103 self.channels
104 .get(0)
105 .map(|channel| channel.state)
106 .unwrap_or_default()
107 }
108}
109
110#[derive(Debug, Default, Clone, Serialize, Deserialize)]
111pub struct InputStackText {
112 pub consume: bool,
113 state: String,
114}
115
116impl InputStackText {
117 pub fn state(&self) -> &str {
118 &self.state
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
123pub enum InputStackCombinationAction {
124 Trigger(String),
125 AxesMagnitude {
126 name: String,
127 threshold: Scalar,
128 },
129 AxesTargetValues {
130 name: String,
131 target: Vec<Scalar>,
132 threshold: Scalar,
133 },
134}
135
136#[derive(Debug, Default, Clone, Serialize, Deserialize)]
137pub struct InputStackCombination {
138 #[serde(default)]
139 pub continous: bool,
140 #[serde(default)]
141 actions: Vec<InputStackCombinationAction>,
142 #[serde(skip)]
143 state: bool,
144 #[serde(skip)]
145 last_state: bool,
146}
147
148impl InputStackCombination {
149 pub fn new(actions: impl Iterator<Item = impl Into<InputStackCombinationAction>>) -> Self {
150 Self {
151 continous: false,
152 actions: actions.map(|action| action.into()).collect(),
153 state: false,
154 last_state: false,
155 }
156 }
157
158 pub fn actions(&self) -> &[InputStackCombinationAction] {
159 &self.actions
160 }
161
162 pub fn pressed(&self) -> bool {
163 if self.continous {
164 self.state
165 } else {
166 self.state && !self.last_state
167 }
168 }
169
170 pub fn released(&self) -> bool {
171 if self.continous {
172 !self.state
173 } else {
174 !self.state && self.last_state
175 }
176 }
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct InputStackListener {
181 #[serde(default)]
182 pub priority: usize,
183 #[serde(default = "InputStackListener::default_enabled")]
184 pub enabled: bool,
185 #[serde(skip)]
186 pub bound_entity: Option<Entity>,
187 #[serde(default)]
188 triggers: HashMap<String, InputStackTrigger>,
189 #[serde(default)]
190 axes: HashMap<String, InputStackAxes>,
191 #[serde(default)]
192 text: Option<InputStackText>,
193 #[serde(default)]
194 combinations: HashMap<String, InputStackCombination>,
195}
196
197impl Default for InputStackListener {
198 fn default() -> Self {
199 Self {
200 priority: 0,
201 enabled: Self::default_enabled(),
202 bound_entity: None,
203 triggers: Default::default(),
204 axes: Default::default(),
205 text: None,
206 combinations: Default::default(),
207 }
208 }
209}
210
211impl InputStackListener {
212 fn default_enabled() -> bool {
213 true
214 }
215
216 pub fn with_trigger(mut self, name: &str, trigger: InputStackTrigger) -> Self {
217 self.map_trigger(name, trigger);
218 self
219 }
220
221 pub fn map_trigger(&mut self, name: &str, trigger: InputStackTrigger) {
222 self.triggers.insert(name.to_owned(), trigger);
223 }
224
225 pub fn unmap_trigger(&mut self, name: &str) {
226 self.triggers.remove(name);
227 }
228
229 pub fn trigger(&self, name: &str) -> Option<&InputStackTrigger> {
230 self.triggers.get(name)
231 }
232
233 pub fn trigger_state_or_default(&self, name: &str) -> TriggerState {
234 self.trigger(name)
235 .map(|trigger| trigger.state())
236 .unwrap_or_default()
237 }
238
239 pub fn with_axes(mut self, name: &str, axes: InputStackAxes) -> Self {
240 self.map_axes(name, axes);
241 self
242 }
243
244 pub fn map_axes(&mut self, name: &str, axes: InputStackAxes) {
245 self.axes.insert(name.to_owned(), axes);
246 }
247
248 pub fn unmap_axes(&mut self, name: &str) {
249 self.axes.remove(name);
250 }
251
252 pub fn axes(&self, name: &str) -> Option<&InputStackAxes> {
253 self.axes.get(name)
254 }
255
256 pub fn axes_channels_or_default(&self, name: &str) -> &[InputStackChannel] {
257 self.axes(name)
258 .map(|axes| axes.channels())
259 .unwrap_or_default()
260 }
261
262 pub fn axes_state_or_default<const N: usize>(&self, name: &str) -> [Scalar; N] {
263 self.axes(name)
264 .map(|axes| axes.channels_state_or_default())
265 .unwrap_or_else(|| [0.0; N])
266 }
267
268 pub fn channel_state_or_default(&self, name: &str) -> Scalar {
269 self.axes(name)
270 .map(|axes| axes.channel_state_or_default())
271 .unwrap_or_default()
272 }
273
274 pub fn with_text(mut self, text: InputStackText) -> Self {
275 self.map_text(text);
276 self
277 }
278
279 pub fn map_text(&mut self, text: InputStackText) {
280 self.text = Some(text);
281 }
282
283 pub fn unmap_text(&mut self) {
284 self.text = None;
285 }
286
287 pub fn text(&self) -> Option<&InputStackText> {
288 self.text.as_ref()
289 }
290
291 pub fn text_state_or_default(&self) -> &str {
292 self.text().map(|text| text.state()).unwrap_or_default()
293 }
294
295 pub fn with_combination(mut self, name: &str, combination: InputStackCombination) -> Self {
296 self.map_combination(name, combination);
297 self
298 }
299
300 pub fn map_combination(&mut self, name: &str, combination: InputStackCombination) {
301 self.combinations.insert(name.to_owned(), combination);
302 }
303
304 pub fn unmap_combination(&mut self, name: &str) {
305 self.combinations.remove(name);
306 }
307
308 pub fn combination(&self, name: &str) -> Option<&InputStackCombination> {
309 self.combinations.get(name)
310 }
311
312 pub fn combination_pressed_or_default(&self, name: &str) -> bool {
313 self.combination(name)
314 .map(|combination| combination.pressed())
315 .unwrap_or_default()
316 }
317
318 pub fn combination_released_or_default(&self, name: &str) -> bool {
319 self.combination(name)
320 .map(|combination| combination.released())
321 .unwrap_or_default()
322 }
323}
324
325#[derive(Default)]
326pub struct InputStack {
327 listeners: HashMap<InputStackListenerId, InputStackListener>,
328}
329
330impl InputStack {
331 pub fn register(&mut self, listener: InputStackListener) -> InputStackListenerId {
332 let id = InputStackListenerId::new();
333 self.listeners.insert(id, listener);
334 id
335 }
336
337 pub fn unregister(&mut self, id: InputStackListenerId) {
338 self.listeners.remove(&id);
339 }
340
341 pub fn listeners(&self) -> impl Iterator<Item = &InputStackListener> {
342 self.listeners.values()
343 }
344
345 pub fn listener(&self, id: InputStackListenerId) -> Option<&InputStackListener> {
346 self.listeners.get(&id)
347 }
348
349 pub fn listener_by_instance(
350 &self,
351 instance: &InputStackInstance,
352 ) -> Option<&InputStackListener> {
353 instance.as_listener().and_then(|id| self.listener(id))
354 }
355
356 pub fn listeners_by_entity(&self, entity: Entity) -> impl Iterator<Item = &InputStackListener> {
357 self.listeners()
358 .filter(move |listener| listener.bound_entity == Some(entity))
359 }
360
361 pub fn process(&mut self, controller: &InputController, entity_changes: &EntityChanges) {
362 self.listeners.retain(|_, listener| {
363 listener
364 .bound_entity
365 .map(|entity| !entity_changes.has_despawned(entity))
366 .unwrap_or(true)
367 });
368
369 let mut consumed_triggers = HashSet::with_capacity(controller.triggers().count());
370 let mut consumed_axes = HashSet::with_capacity(controller.axes().count());
371 let mut consumed_text = false;
372 let mut stack = self.listeners.iter_mut().collect::<Vec<_>>();
373 stack.sort_by(|a, b| a.1.priority.cmp(&b.1.priority).reverse());
374
375 for (_, listener) in stack {
376 for trigger in listener.triggers.values_mut() {
377 trigger.state = trigger.state.release();
378 if listener.enabled {
379 if let Some((mapping, state)) = trigger
380 .mappings
381 .iter()
382 .filter(|m| !consumed_triggers.contains(m.as_str()))
383 .map(|m| (m, controller.trigger_or_default(m)))
384 .max_by(|a, b| a.1.priority().cmp(&b.1.priority()))
385 {
386 trigger.state = state;
387 if trigger.consume {
388 consumed_triggers.insert(mapping.to_owned());
389 }
390 }
391 }
392 }
393 for axes in listener.axes.values_mut() {
394 for channel in &mut axes.channels {
395 channel.state = 0.0;
396 if listener.enabled {
397 if let Some((mapping, scale, value)) = channel
398 .mappings
399 .iter()
400 .filter(|(m, _)| !consumed_axes.contains(m.as_str()))
401 .map(|(m, s)| (m, s, controller.axis_or_default(m)))
402 .max_by(|a, b| a.2.partial_cmp(&b.2).unwrap())
403 {
404 channel.state = value * scale;
405 if axes.consume {
406 consumed_axes.insert(mapping.to_owned());
407 }
408 }
409 }
410 }
411 }
412 if let Some(text) = listener.text.as_mut() {
413 if !listener.enabled || consumed_text {
414 text.state.clear();
415 } else {
416 text.state = controller.text().to_owned();
417 if text.consume {
418 consumed_text = true;
419 }
420 }
421 }
422 let mut combinations = std::mem::take(&mut listener.combinations);
423 for combination in combinations.values_mut() {
424 combination.last_state = combination.state;
425 combination.state = combination
426 .actions
427 .iter()
428 .map(|action| match action {
429 InputStackCombinationAction::Trigger(name) => {
430 listener.trigger_state_or_default(name).is_pressed()
431 }
432 InputStackCombinationAction::AxesMagnitude { name, threshold } => {
433 let axes = listener.axes_channels_or_default(name);
434 let squared = axes
435 .iter()
436 .map(|channel| channel.state() * channel.state())
437 .sum::<Scalar>();
438 let magnitude = if !axes.is_empty() {
439 squared / axes.len() as Scalar
440 } else {
441 0.0
442 };
443 magnitude > *threshold
444 }
445 InputStackCombinationAction::AxesTargetValues {
446 name,
447 target,
448 threshold,
449 } => {
450 let axes = listener.axes_channels_or_default(name);
451 let squared = target
452 .iter()
453 .zip(axes.iter())
454 .map(|(target, channel)| {
455 let diff = (target - channel.state()).abs();
456 diff * diff
457 })
458 .sum::<Scalar>();
459 let difference = if !axes.is_empty() {
460 squared / axes.len() as Scalar
461 } else {
462 0.0
463 };
464 difference > *threshold
465 }
466 })
467 .all(|v| v);
468 }
469 listener.combinations = combinations;
470 }
471 }
472}