moonshine_behavior/
transition.rs

1use std::collections::VecDeque;
2use std::fmt::Debug;
3
4use bevy_ecs::prelude::*;
5use bevy_log::prelude::*;
6use bevy_reflect::prelude::*;
7use moonshine_kind::prelude::*;
8
9use crate::events::BehaviorEvent;
10use crate::{Behavior, BehaviorEventsMut, BehaviorHooks, BehaviorMut, BehaviorMutItem, Memory};
11
12pub use self::Transition::{Interrupt, Next, Previous, Reset};
13
14#[derive(Component, Debug, Reflect)]
15#[require(Memory<T>)]
16#[reflect(Component)]
17pub enum Transition<T: Behavior> {
18    None,
19    Next(T),
20    Interrupt(T),
21    Previous,
22    Reset,
23}
24
25impl<T: Behavior> Transition<T> {
26    pub fn is_none(&self) -> bool {
27        matches!(self, Self::None)
28    }
29
30    fn take(&mut self) -> Self {
31        std::mem::replace(self, Transition::None)
32    }
33}
34
35impl<T: Behavior> Default for Transition<T> {
36    fn default() -> Self {
37        Self::None
38    }
39}
40
41impl<T: Behavior + Clone> Clone for Transition<T> {
42    fn clone(&self) -> Self {
43        match self {
44            Self::None => Self::None,
45            Next(next) => Next(next.clone()),
46            Interrupt(next) => Interrupt(next.clone()),
47            Previous => Previous,
48            Reset => Reset,
49        }
50    }
51}
52
53pub fn transition<T: Behavior>(
54    mut events: BehaviorEventsMut<T>,
55    mut query: Query<
56        (
57            Instance<T>,
58            BehaviorMut<T>,
59            Option<&mut TransitionSequence<T>>,
60        ),
61        TransitionChanged<T>,
62    >,
63    mut commands: Commands,
64) {
65    for (instance, mut behavior, sequence_opt) in &mut query {
66        if behavior.current.is_added() {
67            // Memory must be empty when the component is added
68            debug_assert!(behavior.memory.is_empty());
69
70            // Send start event for the initial behavior
71            behavior.invoke_start(None, commands.instance(instance));
72            events.write(BehaviorEvent::Start { instance, index: 0 });
73        }
74
75        // Index of the stopped behavior, if applicable.
76        let mut stop_index = None;
77
78        let mut interrupt_sequence = false;
79
80        match behavior.transition.take() {
81            Next(next) => {
82                interrupt_sequence = !behavior.push(instance, next, &mut events, &mut commands);
83            }
84            Previous => {
85                stop_index = Some(behavior.index());
86                interrupt_sequence = !behavior.pop(instance, &mut events, &mut commands);
87            }
88            Interrupt(next) => {
89                behavior.interrupt(instance, next, &mut events, &mut commands);
90                interrupt_sequence = true;
91            }
92            Reset => {
93                behavior.clear(instance, &mut events, &mut commands);
94                interrupt_sequence = true;
95            }
96            _ => {}
97        }
98
99        let Some(sequence) = sequence_opt else {
100            continue;
101        };
102
103        if interrupt_sequence {
104            debug!("{instance:?}: sequence interrupted");
105            commands.entity(*instance).remove::<TransitionSequence<T>>();
106        } else if sequence.is_empty() {
107            debug!("{instance:?}: sequence finished");
108            commands.entity(*instance).remove::<TransitionSequence<T>>();
109        } else {
110            TransitionSequence::update(sequence, instance, behavior, stop_index);
111        }
112    }
113}
114
115// TODO: Can we use `Changed<TransitionSequence<T>>` for this?
116pub type TransitionChanged<T> = Or<(Changed<Transition<T>>, With<TransitionSequence<T>>)>;
117
118#[derive(Debug, PartialEq, Reflect)]
119pub enum TransitionError<T: Behavior> {
120    RejectedNext(T),
121    NoPrevious,
122}
123
124#[derive(Component, Reflect)]
125#[reflect(Component)]
126pub struct TransitionSequence<T: Behavior> {
127    queue: VecDeque<TransitionSequenceElement<T>>,
128    wait_index: Option<usize>,
129}
130
131impl<T: Behavior> TransitionSequence<T> {
132    pub fn new(items: impl IntoIterator<Item = T>) -> Self {
133        Self {
134            queue: VecDeque::from_iter(items.into_iter().map(TransitionSequenceElement::Start)),
135            wait_index: None,
136        }
137    }
138
139    pub fn empty() -> Self {
140        Self {
141            queue: VecDeque::new(),
142            wait_index: None,
143        }
144    }
145
146    pub fn is_empty(&self) -> bool {
147        self.queue.is_empty()
148    }
149
150    pub fn len(&self) -> usize {
151        self.queue.len()
152    }
153
154    pub fn start(next: T) -> Self {
155        let mut sequence = Self::empty();
156        sequence.push(TransitionSequenceElement::Start(next));
157        sequence
158    }
159
160    pub fn wait_for(next: T) -> Self {
161        Self::empty().then_wait_for(next)
162    }
163
164    pub fn then(mut self, next: T) -> Self {
165        self.push(TransitionSequenceElement::Start(next));
166        self
167    }
168
169    pub fn then_wait_for(mut self, next: T) -> Self {
170        self.push(TransitionSequenceElement::StartWait(next));
171        self
172    }
173
174    pub fn then_stop(mut self) -> Self {
175        self.push(TransitionSequenceElement::Stop);
176        self
177    }
178
179    fn push(&mut self, next: TransitionSequenceElement<T>) {
180        self.queue.push_back(next);
181    }
182}
183
184impl<T: Behavior> TransitionSequence<T> {
185    pub(crate) fn update(
186        mut this: Mut<Self>,
187        instance: Instance<T>,
188        mut behavior: BehaviorMutItem<T>,
189        stop_index: Option<usize>,
190    ) {
191        debug_assert!(!this.queue.is_empty());
192
193        if let Some(wait_index) = this.wait_index {
194            if let Some(stop_index) = stop_index {
195                if wait_index != stop_index {
196                    return;
197                }
198            } else {
199                return;
200            }
201        }
202
203        debug!("{instance:?}: sequence steps = {:?}", this.queue.len());
204
205        if let Some(element) = this.queue.pop_front() {
206            use TransitionSequenceElement::*;
207            match element {
208                Start(next) => {
209                    this.wait_index = None;
210                    behavior.start(next);
211                }
212                StartWait(next) => {
213                    this.wait_index = Some(behavior.index() + 1);
214                    behavior.start(next);
215                }
216                Stop => {
217                    this.wait_index = None;
218                    behavior.stop();
219                }
220            }
221        }
222    }
223}
224
225#[derive(Debug, Reflect)]
226enum TransitionSequenceElement<T: Behavior> {
227    Start(T),
228    StartWait(T),
229    Stop,
230}