moonshine_behavior/
transition.rs

1//! A [`Behavior`] is controlled using [`Transition`].
2//!
3//! Transitions are processed by the [`transition`] system.
4
5use std::collections::VecDeque;
6use std::fmt::Debug;
7
8use bevy_ecs::component::Components;
9use bevy_ecs::prelude::*;
10use bevy_log::prelude::*;
11use bevy_reflect::prelude::*;
12use moonshine_kind::prelude::*;
13
14use crate::events::{OnActivate, OnStart};
15use crate::{Behavior, BehaviorHooks, BehaviorIndex, BehaviorMut, BehaviorMutItem, Memory};
16
17pub use self::Transition::{Interrupt, Next, Previous};
18
19/// A [`Component`] which controls transitions between [`Behavior`] states.
20///
21/// This component is automatically registered as a required component for all types
22/// which implement the [`Behavior`] trait and and have their [`BehaviorPlugin`](crate::plugin::BehaviorPlugin) added.
23#[derive(Component, Clone, Debug, Reflect)]
24#[require(Memory<T>)]
25#[reflect(Component)]
26pub enum Transition<T: Behavior> {
27    #[doc(hidden)]
28    None,
29    /// Starts the next behavior.
30    Next(T),
31    /// Starts an [`Interruption`].
32    Interrupt(Interruption<T>),
33    /// Stops the current behavior and resumes the previous one.
34    Previous,
35}
36
37impl<T: Behavior> Transition<T> {
38    /// Returns `true` if there are no pending transitions.
39    pub fn is_none(&self) -> bool {
40        matches!(self, Self::None)
41    }
42
43    fn take(&mut self) -> Self {
44        std::mem::replace(self, Transition::None)
45    }
46}
47
48impl<T: Behavior> Default for Transition<T> {
49    fn default() -> Self {
50        Self::None
51    }
52}
53
54/// A system which processes [`Behavior`] [`Transitions`](Transition).
55pub fn transition<T: Behavior>(
56    components: &Components,
57    mut query: Query<
58        (
59            Instance<T>,
60            BehaviorMut<T>,
61            Option<&mut TransitionSequence<T>>,
62        ),
63        Or<(Changed<Transition<T>>, With<TransitionSequence<T>>)>,
64    >,
65    mut commands: Commands,
66) {
67    for (instance, mut behavior, sequence_opt) in &mut query {
68        if behavior.current.is_added() {
69            // Memory must be empty when the component is added
70            debug_assert!(behavior.memory.is_empty());
71
72            // Send start event for the initial behavior
73            behavior.invoke_start(None, commands.instance(instance));
74            let id = components.valid_component_id::<T>().unwrap();
75            commands.trigger_targets(
76                OnStart {
77                    index: BehaviorIndex::initial(),
78                },
79                (*instance, id),
80            );
81            commands.trigger_targets(
82                OnActivate {
83                    index: BehaviorIndex::initial(),
84                    resume: false,
85                },
86                (*instance, id),
87            );
88        }
89
90        // Index of the stopped behavior, if applicable.
91        let mut stop_index = None;
92
93        let mut interrupt_sequence = false;
94
95        match behavior.transition.take() {
96            Next(next) => {
97                interrupt_sequence = !behavior.push(instance, next, components, &mut commands);
98            }
99            Previous => {
100                stop_index = Some(behavior.index());
101                interrupt_sequence = !behavior.pop(instance, components, &mut commands);
102            }
103            Interrupt(Interruption::Start(next)) => {
104                behavior.interrupt(instance, next, components, &mut commands);
105                interrupt_sequence = true;
106            }
107            Interrupt(Interruption::Resume(index)) => {
108                behavior.clear(instance, index, components, &mut commands);
109                interrupt_sequence = true;
110            }
111            _ => {}
112        }
113
114        let Some(sequence) = sequence_opt else {
115            continue;
116        };
117
118        if interrupt_sequence {
119            debug!("{instance:?}: sequence interrupted");
120            commands.entity(*instance).remove::<TransitionSequence<T>>();
121        } else if sequence.is_empty() {
122            debug!("{instance:?}: sequence finished");
123            commands.entity(*instance).remove::<TransitionSequence<T>>();
124        } else {
125            TransitionSequence::update(sequence, instance, behavior, stop_index);
126        }
127    }
128}
129
130/// A specific kind of [`Transition`] which may stop active behaviors before activating a new one.
131#[derive(Debug, Clone, Reflect)]
132pub enum Interruption<T: Behavior> {
133    /// An interruption which stops any behavior which [yields](Behavior::filter_yield)
134    /// to the given behavior, and then starts it.
135    Start(T),
136    /// An interruption which resumes a behavior at the given index by stopping all other behaviors above it in the stack.
137    Resume(BehaviorIndex),
138}
139
140#[doc(hidden)]
141#[deprecated(since = "0.2.1", note = "use `Changed<Transition<T>>` instead")]
142pub type TransitionChanged<T> = Or<(Changed<Transition<T>>, With<TransitionSequence<T>>)>;
143
144/// Represents an error during [`transition`].
145#[derive(Debug, PartialEq, Reflect)]
146pub enum TransitionError<T: Behavior> {
147    /// The given behavior was rejected by [`filter_next`](Behavior::filter_next).
148    RejectedNext(T),
149    /// Initial behavior may not be stopped.
150    NoPrevious,
151}
152
153/// A queue of transitions to start automated behavior sequences.
154#[derive(Component, Reflect)]
155#[reflect(Component)]
156pub struct TransitionSequence<T: Behavior> {
157    queue: VecDeque<TransitionSequenceElement<T>>,
158    wait_index: Option<BehaviorIndex>,
159}
160
161impl<T: Behavior> TransitionSequence<T> {
162    /// Creates a new transition sequence which starts all the given behaviors in given order.
163    pub fn new(items: impl IntoIterator<Item = T>) -> Self {
164        Self {
165            queue: VecDeque::from_iter(items.into_iter().map(TransitionSequenceElement::Start)),
166            wait_index: None,
167        }
168    }
169
170    /// Creates an empty transition sequence.
171    pub fn empty() -> Self {
172        Self {
173            queue: VecDeque::new(),
174            wait_index: None,
175        }
176    }
177
178    /// Creates a new transition sequence which starts with the given behavior.
179    pub fn start(next: T) -> Self {
180        let mut sequence = Self::empty();
181        sequence.push(TransitionSequenceElement::Start(next));
182        sequence
183    }
184
185    /// Creates a new transition sequence which starts with the given behavior and waits for it to finish.
186    pub fn wait_for(next: T) -> Self {
187        Self::empty().then_wait_for(next)
188    }
189
190    /// Returns `true` if the sequence is empty.
191    pub fn is_empty(&self) -> bool {
192        self.queue.is_empty()
193    }
194
195    /// Returns the number of transitions in the sequence.
196    pub fn len(&self) -> usize {
197        self.queue.len()
198    }
199
200    /// Starts the next behavior in the sequence.
201    pub fn then(mut self, next: T) -> Self {
202        self.push(TransitionSequenceElement::Start(next));
203        self
204    }
205
206    /// Starts the next behavior in the sequence and waits for it to finish.
207    pub fn then_wait_for(mut self, next: T) -> Self {
208        self.push(TransitionSequenceElement::StartWait(next));
209        self
210    }
211
212    /// Stops the current behavior.
213    pub fn then_stop(mut self) -> Self {
214        self.push(TransitionSequenceElement::Stop);
215        self
216    }
217
218    fn push(&mut self, next: TransitionSequenceElement<T>) {
219        self.queue.push_back(next);
220    }
221}
222
223impl<T: Behavior> TransitionSequence<T> {
224    pub(crate) fn update(
225        mut this: Mut<Self>,
226        instance: Instance<T>,
227        mut behavior: BehaviorMutItem<T>,
228        stop_index: Option<BehaviorIndex>,
229    ) {
230        debug_assert!(!this.queue.is_empty());
231
232        if let Some(wait_index) = this.wait_index {
233            if let Some(stop_index) = stop_index {
234                if wait_index != stop_index {
235                    return;
236                }
237            } else {
238                return;
239            }
240        }
241
242        debug!("{instance:?}: sequence steps = {:?}", this.queue.len());
243
244        if let Some(element) = this.queue.pop_front() {
245            use TransitionSequenceElement::*;
246            match element {
247                Start(next) => {
248                    this.wait_index = None;
249                    behavior.start(next);
250                }
251                StartWait(next) => {
252                    this.wait_index = Some(behavior.index().next());
253                    behavior.start(next);
254                }
255                Stop => {
256                    this.wait_index = None;
257                    behavior.stop();
258                }
259            }
260        }
261    }
262}
263
264#[derive(Debug, Reflect)]
265enum TransitionSequenceElement<T: Behavior> {
266    Start(T),
267    StartWait(T),
268    Stop,
269}