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