1use 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#[derive(Component, Reflect)]
25#[reflect(from_reflect = false)]
26pub struct InputSequence<Act, I: SystemInput + 'static> {
27 #[reflect(ignore)]
29 pub system_id: SystemId<I>,
30 pub acts: Vec<Act>,
32 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 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
76pub struct InputSequenceBuilder<Act, S, I> {
78 pub system: S,
80 pub acts: Vec<Act>,
82 pub time_limit: Option<TimeLimit>,
84 input: PhantomData<I>,
85}
86
87impl<Act> InputSequenceBuilder<Act, (), ()> {
88 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 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 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 #[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
176pub type KeySequence = InputSequence<KeyChord, ()>;
178pub type KeySequenceBuilder = InputSequenceBuilder<KeyChord, (), ()>;
180
181pub type ButtonSequence = InputSequence<GamepadButton, In<Entity>>;