use std::collections::VecDeque;
use std::fmt::Debug;
use bevy_ecs::event::EntityTrigger;
use bevy_ecs::prelude::*;
use bevy_log::prelude::*;
use bevy_reflect::prelude::*;
use moonshine_kind::prelude::*;
use moonshine_util::prelude::*;
use crate::events::Start;
use crate::{
Behavior, BehaviorHooks, BehaviorIndex, BehaviorItem, BehaviorMut, BehaviorMutItem, Memory,
};
pub use self::Transition::{Interrupt, Next, Previous};
#[derive(Component, Default, Clone, Debug, Reflect)]
#[require(Expect<T>, Memory<T>)]
#[reflect(Component)]
pub enum Transition<T: Behavior> {
#[doc(hidden)]
#[default]
None,
Next(T),
Interrupt(Interruption<T>),
Previous,
}
impl<T: Behavior> Transition<T> {
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
fn take(&mut self) -> Self {
std::mem::replace(self, Transition::None)
}
}
#[allow(clippy::type_complexity)]
pub fn transition<T: Behavior>(
mut query: Query<
(Instance<T>, BehaviorMut<T>, Option<&mut TransitionQueue<T>>),
Or<(Changed<Transition<T>>, With<TransitionQueue<T>>)>,
>,
mut commands: Commands,
) {
for (instance, mut behavior, queue_opt) in &mut query {
let entity = instance.entity();
if behavior.is_added() {
let mut stack: VecDeque<T> = behavior
.memory
.bypass_change_detection()
.drain(..)
.collect();
if !stack.is_empty() {
let mut initial = stack.pop_front().unwrap();
let current = {
std::mem::swap(behavior.current.as_mut(), &mut initial);
initial
};
stack.push_back(current);
}
behavior.invoke_start(None, commands.instance(instance));
commands.queue(move |world: &mut World| {
world.trigger_with(
Start {
instance,
index: BehaviorIndex::initial(),
initial: true,
},
EntityTrigger,
);
});
for state in stack {
let _ = behavior.push(instance, state, true, &mut commands);
}
}
let mut stop_index = None;
let mut interrupt_queue = false;
match behavior.transition.take() {
Next(next) => {
interrupt_queue = !behavior.push(instance, next, false, &mut commands);
}
Previous => {
stop_index = Some(behavior.current_index());
interrupt_queue = !behavior.pop(instance, &mut commands);
}
Interrupt(Interruption::Start(next)) => {
behavior.interrupt(instance, next, &mut commands);
interrupt_queue = true;
}
Interrupt(Interruption::Resume(index)) => {
behavior.clear(instance, index, &mut commands);
interrupt_queue = true;
}
_ => {}
}
let Some(queue) = queue_opt else {
continue;
};
if interrupt_queue {
debug!("{instance:?}: queue interrupted");
commands.entity(entity).remove::<TransitionQueue<T>>();
} else if queue.is_empty() {
debug!("{instance:?}: queue finished");
commands.entity(entity).remove::<TransitionQueue<T>>();
} else {
TransitionQueue::update(queue, instance, behavior, stop_index);
}
}
}
#[derive(Debug, Clone, Reflect)]
pub enum Interruption<T: Behavior> {
Start(T),
Resume(BehaviorIndex),
}
#[doc(hidden)]
#[deprecated(since = "0.2.1", note = "use `Changed<Transition<T>>` instead")]
pub type TransitionChanged<T> = Or<(Changed<Transition<T>>, With<TransitionQueue<T>>)>;
#[derive(Debug, PartialEq, Reflect)]
pub enum TransitionError<T: Behavior> {
RejectedNext(T),
NoPrevious,
}
#[doc(hidden)]
#[deprecated(since = "0.3.1", note = "use `TransitionQueue` instead")]
pub type TransitionSequence<T> = TransitionQueue<T>;
#[derive(Component, Reflect)]
#[reflect(Component)]
#[require(Expect<Transition<T>>)]
pub struct TransitionQueue<T: Behavior> {
queue: VecDeque<TransitionQueueItem<T>>,
wait_for: Option<BehaviorIndex>,
}
impl<T: Behavior> TransitionQueue<T> {
pub fn chain(items: impl IntoIterator<Item = T>) -> Self {
Self {
queue: VecDeque::from_iter(items.into_iter().map(TransitionQueueItem::Start)),
wait_for: None,
}
}
pub fn sequence(items: impl IntoIterator<Item = T>) -> Self {
Self {
queue: VecDeque::from_iter(items.into_iter().map(TransitionQueueItem::StartWait)),
wait_for: None,
}
}
pub fn empty() -> Self {
Self {
queue: VecDeque::new(),
wait_for: None,
}
}
pub fn start(next: T) -> Self {
let mut sequence = Self::empty();
sequence.push(TransitionQueueItem::Start(next));
sequence
}
pub fn stop() -> Self {
let mut sequence = Self::empty();
sequence.push(TransitionQueueItem::Stop);
sequence
}
pub fn wait_for(next: T) -> Self {
Self::empty().then_wait_for(next)
}
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn len(&self) -> usize {
self.queue.len()
}
pub fn then(mut self, next: T) -> Self {
self.push(TransitionQueueItem::Start(next));
self
}
pub fn then_if(self, condition: bool, next: T) -> Self {
if condition {
return self.then(next);
}
self
}
pub fn then_wait_for(mut self, next: T) -> Self {
self.push(TransitionQueueItem::StartWait(next));
self
}
pub fn then_stop(mut self) -> Self {
self.push(TransitionQueueItem::Stop);
self
}
fn push(&mut self, next: TransitionQueueItem<T>) {
self.queue.push_back(next);
}
}
impl<T: Behavior> TransitionQueue<T> {
pub(crate) fn update(
mut this: Mut<Self>,
instance: Instance<T>,
mut behavior: BehaviorMutItem<T>,
stop_index: Option<BehaviorIndex>,
) {
debug_assert!(!this.is_empty());
if let Some(wait_index) = this.wait_for {
if let Some(stop_index) = stop_index {
if wait_index != stop_index {
return;
}
} else {
return;
}
}
debug!("{instance:?}: queue length = {:?}", this.len());
if let Some(element) = this.queue.pop_front() {
use TransitionQueueItem::*;
match element {
Start(next) => {
this.wait_for = None;
behavior.start(next);
}
StartWait(next) => {
this.wait_for = Some(behavior.current_index().next());
behavior.start(next);
}
Stop => {
this.wait_for = None;
behavior.stop();
}
}
}
}
}
#[derive(Debug, Reflect)]
enum TransitionQueueItem<T: Behavior> {
Start(T),
StartWait(T),
Stop,
}