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::{BuildChildrenTransformExt, Command, EntityWorldMut, Reflect},
15};
16
17#[derive(Component, Reflect)]
24#[reflect(from_reflect = false)]
25pub struct InputSequence<Act, I: SystemInput + 'static> {
26 pub system_id: SystemId<I>,
28 pub acts: Vec<Act>,
30 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 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
74pub struct InputSequenceBuilder<Act, S, I> {
76 pub system: S,
78 pub acts: Vec<Act>,
80 pub time_limit: Option<TimeLimit>,
82 input: PhantomData<I>,
83}
84
85impl<Act> InputSequenceBuilder<Act, (), ()> {
86 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 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 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 #[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
174pub type KeySequence = InputSequence<KeyChord, ()>;
176pub type KeySequenceBuilder = InputSequenceBuilder<KeyChord, (), ()>;
178
179pub type ButtonSequence = InputSequence<GamepadButton, In<Entity>>;