bevy_enhanced_input/input_condition/
block_by.rs

1use core::{any, marker::PhantomData};
2
3use bevy::prelude::*;
4
5use super::{ConditionKind, InputCondition};
6use crate::{
7    action_map::{ActionMap, ActionState},
8    action_value::ActionValue,
9    input_action::InputAction,
10};
11
12/// Requires another action to not be fired within the same context.
13#[derive(Debug)]
14pub struct BlockBy<A: InputAction> {
15    /// Action that blocks this condition when active.
16    marker: PhantomData<A>,
17
18    /// Whether to block the state or only the events.
19    ///
20    /// By default set to false.
21    pub events_only: bool,
22}
23
24impl<A: InputAction> BlockBy<A> {
25    /// Block only events.
26    ///
27    /// For details, see [`ConditionKind::Blocker::events_only`].
28    ///
29    /// This can be used for chords to avoid triggering required actions.
30    /// Otherwise, the chord will register the release and cancel itself.
31    pub fn events_only() -> Self {
32        Self {
33            marker: PhantomData,
34            events_only: true,
35        }
36    }
37}
38
39impl<A: InputAction> Default for BlockBy<A> {
40    fn default() -> Self {
41        Self {
42            marker: PhantomData,
43            events_only: false,
44        }
45    }
46}
47
48impl<A: InputAction> Clone for BlockBy<A> {
49    fn clone(&self) -> Self {
50        *self
51    }
52}
53
54impl<A: InputAction> Copy for BlockBy<A> {}
55
56impl<A: InputAction> InputCondition for BlockBy<A> {
57    fn evaluate(
58        &mut self,
59        action_map: &ActionMap,
60        _time: &Time<Virtual>,
61        _value: ActionValue,
62    ) -> ActionState {
63        if let Some(action) = action_map.action::<A>() {
64            if action.state() == ActionState::Fired {
65                return ActionState::None;
66            }
67        } else {
68            warn_once!(
69                "action `{}` is not present in context",
70                any::type_name::<A>()
71            );
72        }
73
74        ActionState::Fired
75    }
76
77    fn kind(&self) -> ConditionKind {
78        ConditionKind::Blocker {
79            events_only: self.events_only,
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use bevy_enhanced_input_macros::InputAction;
87
88    use super::*;
89    use crate::action_map::Action;
90
91    #[test]
92    fn block() {
93        let mut condition = BlockBy::<DummyAction>::default();
94        let mut action = Action::new::<DummyAction>();
95        let time = Time::default();
96        action.update(&time, ActionState::Fired, true);
97        let mut action_map = ActionMap::default();
98        action_map.insert_action::<DummyAction>(action);
99
100        assert_eq!(
101            condition.evaluate(&action_map, &time, true.into()),
102            ActionState::None,
103        );
104    }
105
106    #[test]
107    fn missing_action() {
108        let mut condition = BlockBy::<DummyAction>::default();
109        let action_map = ActionMap::default();
110        let time = Time::default();
111
112        assert_eq!(
113            condition.evaluate(&action_map, &time, true.into()),
114            ActionState::Fired,
115        );
116    }
117
118    #[derive(Debug, InputAction)]
119    #[input_action(output = bool)]
120    struct DummyAction;
121}