bevy_input_sequence/
input_sequence.rs

1//! Input sequences for keys and gamepad buttons
2use crate::{cond_system::IntoCondSystem, time_limit::TimeLimit, KeyChord};
3use std::{fmt, marker::PhantomData};
4
5use bevy::{
6    ecs::{
7        component::Component,
8        entity::Entity,
9        prelude::In,
10        system::{IntoSystem, System, SystemId, SystemInput},
11        world::World,
12    },
13    input::gamepad::GamepadButton,
14    prelude::{BuildChildrenTransformExt, Command, EntityWorldMut, Reflect},
15};
16
17/// An input sequence is a series of acts that fires an event when matched with
18/// inputs within the given time limit.
19///
20/// `InputSequence<KeyChord, ()>`, a keychord sequence doesn't have an input,
21/// but a gamepad `InputSequence<GamepadButton, In<Entity>>` provides an entity
22/// for the controller the input came from.
23#[derive(Component, Reflect)]
24#[reflect(from_reflect = false)]
25pub struct InputSequence<Act, I: SystemInput + 'static> {
26    /// Event emitted
27    pub system_id: SystemId<I>,
28    /// Sequence of acts that trigger input sequence
29    pub acts: Vec<Act>,
30    /// Optional time limit after first match
31    pub time_limit: Option<TimeLimit>,
32}
33
34impl<Act: Clone> Clone for InputSequence<Act, ()> {
35    fn clone(&self) -> Self {
36        Self {
37            system_id: self.system_id,
38            acts: self.acts.clone(),
39            time_limit: self.time_limit.clone(),
40        }
41    }
42}
43
44impl<Act: Clone> Clone for InputSequence<Act, In<Entity>> {
45    fn clone(&self) -> Self {
46        Self {
47            system_id: self.system_id,
48            acts: self.acts.clone(),
49            time_limit: self.time_limit.clone(),
50        }
51    }
52}
53
54impl<Act: fmt::Debug, In: SystemInput + Clone> fmt::Debug for InputSequence<Act, In> {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
56        #[derive(Debug)]
57        #[allow(dead_code)]
58        struct InputSequence<'a, Act> {
59            // system_id: SystemId<In>,
60            acts: &'a Vec<Act>,
61            time_limit: &'a Option<TimeLimit>,
62        }
63
64        let Self {
65            acts,
66            time_limit,
67            system_id: _,
68        } = self;
69
70        fmt::Debug::fmt(&InputSequence { acts, time_limit }, f)
71    }
72}
73
74/// An input sequence builder.
75pub struct InputSequenceBuilder<Act, S, I> {
76    /// The action when to run when sequence matches
77    pub system: S,
78    /// Sequence of acts that trigger input sequence
79    pub acts: Vec<Act>,
80    /// Optional time limit after first match
81    pub time_limit: Option<TimeLimit>,
82    input: PhantomData<I>,
83}
84
85impl<Act> InputSequenceBuilder<Act, (), ()> {
86    /// Create new input sequence. Not operant until added to an entity.
87    pub fn new<C, I, M>(system: C) -> InputSequenceBuilder<Act, C::System, I>
88    where
89        C: IntoCondSystem<I, (), M> + 'static,
90        I: SystemInput + Send + Sync + 'static,
91    {
92        InputSequenceBuilder {
93            acts: Vec::new(),
94            system: IntoSystem::into_system(system),
95            time_limit: None,
96            input: PhantomData,
97        }
98    }
99}
100
101impl<Act, S, I> InputSequenceBuilder<Act, S, I>
102where
103    S: System<Out = ()>,
104{
105    /// Specify a time limit from the start of the first matching input.
106    pub fn time_limit(mut self, time_limit: impl Into<TimeLimit>) -> Self {
107        self.time_limit = Some(time_limit.into());
108        self
109    }
110
111    /// Build the InputSequence. Requires world to register the system.
112    pub fn build(self, world: &mut World) -> InputSequence<Act, S::In> {
113        InputSequence {
114            system_id: world.register_system(self.system),
115            acts: self.acts,
116            time_limit: self.time_limit,
117        }
118    }
119}
120
121impl<Act, S, I> Command for InputSequenceBuilder<Act, S, I>
122where
123    Act: Send + Sync + 'static,
124    S: System<In = I, Out = ()> + Send + Sync + 'static,
125    I: SystemInput + Send + Sync + 'static,
126{
127    fn apply(self, world: &mut World) {
128        let act = self.build(world);
129        let system_entity = act.system_id.entity();
130        let id = world.spawn(act).id();
131        world.entity_mut(system_entity).set_parent_in_place(id);
132    }
133}
134
135impl<Act, S, I> bevy::ecs::system::EntityCommand for InputSequenceBuilder<Act, S, I>
136where
137    Act: Send + Sync + 'static,
138    S: System<In = I, Out = ()> + Send + Sync + 'static,
139    I: SystemInput + Send + Sync + 'static,
140{
141    fn apply(self, mut entity_world: EntityWorldMut) {
142        let id = entity_world.id();
143        entity_world.world_scope(move |world: &mut World| {
144            let act = self.build(world);
145            let system_entity = act.system_id.entity();
146            let mut entity = world.get_entity_mut(id).unwrap();
147            entity.insert(act);
148            world.entity_mut(system_entity).set_parent_in_place(id);
149        });
150    }
151}
152
153impl<Act, In: SystemInput + Send + Sync + 'static> InputSequence<Act, In>
154where
155    In: 'static,
156{
157    /// Create new input sequence. Not operant until added to an entity.
158    #[inline(always)]
159    #[allow(clippy::new_ret_no_self)]
160    pub fn new<T, C, M>(
161        system: C,
162        acts: impl IntoIterator<Item = T>,
163    ) -> InputSequenceBuilder<Act, C::System, In>
164    where
165        C: IntoCondSystem<In, (), M> + 'static,
166        Act: From<T>,
167    {
168        let mut builder = InputSequenceBuilder::new(system);
169        builder.acts = Vec::from_iter(acts.into_iter().map(Act::from));
170        builder
171    }
172}
173
174/// Represents a key sequence
175pub type KeySequence = InputSequence<KeyChord, ()>;
176/// Represents a key sequence builder
177pub type KeySequenceBuilder = InputSequenceBuilder<KeyChord, (), ()>;
178
179/// Represents a gamepad button sequence
180pub type ButtonSequence = InputSequence<GamepadButton, In<Entity>>;