moonshine_behavior/
transition.rs1use std::collections::VecDeque;
6use std::fmt::Debug;
7
8use bevy_ecs::event::EntityTrigger;
9use bevy_ecs::prelude::*;
10use bevy_log::prelude::*;
11use bevy_reflect::prelude::*;
12use moonshine_kind::prelude::*;
13use moonshine_util::prelude::*;
14
15use crate::events::Start;
16use crate::{
17 Behavior, BehaviorHooks, BehaviorIndex, BehaviorItem, BehaviorMut, BehaviorMutItem, Memory,
18};
19
20pub use self::Transition::{Interrupt, Next, Previous};
21
22#[derive(Component, Default, Clone, Debug, Reflect)]
27#[require(Expect<T>, Memory<T>)]
28#[reflect(Component)]
29pub enum Transition<T: Behavior> {
30 #[doc(hidden)]
31 #[default]
32 None,
33 Next(T),
35 Interrupt(Interruption<T>),
37 Previous,
39}
40
41impl<T: Behavior> Transition<T> {
42 pub fn is_none(&self) -> bool {
44 matches!(self, Self::None)
45 }
46
47 fn take(&mut self) -> Self {
48 std::mem::replace(self, Transition::None)
49 }
50}
51
52#[allow(clippy::type_complexity)]
54pub fn transition<T: Behavior>(
55 mut query: Query<
56 (Instance<T>, BehaviorMut<T>, Option<&mut TransitionQueue<T>>),
57 Or<(Changed<Transition<T>>, With<TransitionQueue<T>>)>,
58 >,
59 mut commands: Commands,
60) {
61 for (instance, mut behavior, queue_opt) in &mut query {
62 let entity = instance.entity();
63
64 if behavior.is_added() {
65 let mut stack: VecDeque<T> = behavior
66 .memory
67 .bypass_change_detection()
68 .drain(..)
69 .collect();
70
71 if !stack.is_empty() {
72 let mut initial = stack.pop_front().unwrap();
73 let current = {
74 std::mem::swap(behavior.current.as_mut(), &mut initial);
75 initial
76 };
77
78 stack.push_back(current);
79 }
80
81 behavior.invoke_start(None, commands.instance(instance));
82 commands.queue(move |world: &mut World| {
83 world.trigger_with(
84 Start {
85 instance,
86 index: BehaviorIndex::initial(),
87 initial: true,
88 },
89 EntityTrigger,
90 );
91 });
92
93 for state in stack {
94 let _ = behavior.push(instance, state, true, &mut commands);
95 }
96 }
97
98 let mut stop_index = None;
100
101 let mut interrupt_queue = false;
102
103 match behavior.transition.take() {
104 Next(next) => {
105 interrupt_queue = !behavior.push(instance, next, false, &mut commands);
106 }
107 Previous => {
108 stop_index = Some(behavior.current_index());
109 interrupt_queue = !behavior.pop(instance, &mut commands);
110 }
111 Interrupt(Interruption::Start(next)) => {
112 behavior.interrupt(instance, next, &mut commands);
113 interrupt_queue = true;
114 }
115 Interrupt(Interruption::Resume(index)) => {
116 behavior.clear(instance, index, &mut commands);
117 interrupt_queue = true;
118 }
119 _ => {}
120 }
121
122 let Some(queue) = queue_opt else {
123 continue;
124 };
125
126 if interrupt_queue {
127 debug!("{instance:?}: queue interrupted");
128 commands.entity(entity).remove::<TransitionQueue<T>>();
129 } else if queue.is_empty() {
130 debug!("{instance:?}: queue finished");
131 commands.entity(entity).remove::<TransitionQueue<T>>();
132 } else {
133 TransitionQueue::update(queue, instance, behavior, stop_index);
134 }
135 }
136}
137
138#[derive(Debug, Clone, Reflect)]
140pub enum Interruption<T: Behavior> {
141 Start(T),
144 Resume(BehaviorIndex),
146}
147
148#[doc(hidden)]
149#[deprecated(since = "0.2.1", note = "use `Changed<Transition<T>>` instead")]
150pub type TransitionChanged<T> = Or<(Changed<Transition<T>>, With<TransitionQueue<T>>)>;
151
152#[derive(Debug, PartialEq, Reflect)]
154pub enum TransitionError<T: Behavior> {
155 RejectedNext(T),
157 NoPrevious,
159}
160
161#[doc(hidden)]
162#[deprecated(since = "0.3.1", note = "use `TransitionQueue` instead")]
163pub type TransitionSequence<T> = TransitionQueue<T>;
164
165#[derive(Component, Reflect)]
167#[reflect(Component)]
168#[require(Expect<Transition<T>>)]
169pub struct TransitionQueue<T: Behavior> {
170 queue: VecDeque<TransitionQueueItem<T>>,
171 wait_for: Option<BehaviorIndex>,
172}
173
174impl<T: Behavior> TransitionQueue<T> {
175 pub fn chain(items: impl IntoIterator<Item = T>) -> Self {
184 Self {
185 queue: VecDeque::from_iter(items.into_iter().map(TransitionQueueItem::Start)),
186 wait_for: None,
187 }
188 }
189
190 pub fn sequence(items: impl IntoIterator<Item = T>) -> Self {
200 Self {
201 queue: VecDeque::from_iter(items.into_iter().map(TransitionQueueItem::StartWait)),
202 wait_for: None,
203 }
204 }
205
206 pub fn empty() -> Self {
208 Self {
209 queue: VecDeque::new(),
210 wait_for: None,
211 }
212 }
213
214 pub fn start(next: T) -> Self {
216 let mut sequence = Self::empty();
217 sequence.push(TransitionQueueItem::Start(next));
218 sequence
219 }
220
221 pub fn stop() -> Self {
223 let mut sequence = Self::empty();
224 sequence.push(TransitionQueueItem::Stop);
225 sequence
226 }
227
228 pub fn wait_for(next: T) -> Self {
230 Self::empty().then_wait_for(next)
231 }
232
233 pub fn is_empty(&self) -> bool {
235 self.queue.is_empty()
236 }
237
238 pub fn len(&self) -> usize {
240 self.queue.len()
241 }
242
243 pub fn then(mut self, next: T) -> Self {
245 self.push(TransitionQueueItem::Start(next));
246 self
247 }
248
249 pub fn then_if(self, condition: bool, next: T) -> Self {
269 if condition {
270 return self.then(next);
271 }
272 self
273 }
274
275 pub fn then_wait_for(mut self, next: T) -> Self {
277 self.push(TransitionQueueItem::StartWait(next));
278 self
279 }
280
281 pub fn then_stop(mut self) -> Self {
283 self.push(TransitionQueueItem::Stop);
284 self
285 }
286
287 fn push(&mut self, next: TransitionQueueItem<T>) {
288 self.queue.push_back(next);
289 }
290}
291
292impl<T: Behavior> TransitionQueue<T> {
293 pub(crate) fn update(
294 mut this: Mut<Self>,
295 instance: Instance<T>,
296 mut behavior: BehaviorMutItem<T>,
297 stop_index: Option<BehaviorIndex>,
298 ) {
299 debug_assert!(!this.is_empty());
300
301 if let Some(wait_index) = this.wait_for {
302 if let Some(stop_index) = stop_index {
303 if wait_index != stop_index {
304 return;
305 }
306 } else {
307 return;
308 }
309 }
310
311 debug!("{instance:?}: queue length = {:?}", this.len());
312
313 if let Some(element) = this.queue.pop_front() {
314 use TransitionQueueItem::*;
315 match element {
316 Start(next) => {
317 this.wait_for = None;
318 behavior.start(next);
319 }
320 StartWait(next) => {
321 this.wait_for = Some(behavior.current_index().next());
322 behavior.start(next);
323 }
324 Stop => {
325 this.wait_for = None;
326 behavior.stop();
327 }
328 }
329 }
330 }
331}
332
333#[derive(Debug, Reflect)]
334enum TransitionQueueItem<T: Behavior> {
335 Start(T),
336 StartWait(T),
337 Stop,
338}