use std::marker::PhantomData;
use std::time::Duration;
use bevy::prelude::*;
use std::collections::HashSet;
use crate::{guards::Guards, EnterState, Transition, active::Active, StateChildOf, StateMachine, ExitState, Parallel};
use crate::state_component::Reset;
#[derive(Component, Default, Debug, PartialEq, Eq, Reflect)]
#[relationship_target(relationship = Source, linked_spawn)]
#[reflect(Component, FromWorld, Default)]
pub struct Transitions(Vec<Entity>);
impl<'a> IntoIterator for &'a Transitions {
type Item = <Self::IntoIter as Iterator>::Item;
type IntoIter = std::slice::Iter<'a, Entity>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl Transitions {
pub fn new() -> Self {
Self(Vec::new())
}
}
#[derive(Component, Clone, PartialEq, Eq, Debug, Reflect)]
#[relationship(relationship_target = Transitions)]
#[reflect(Component, PartialEq, Debug, FromWorld, Clone)]
pub struct Source(#[entities] pub Entity);
impl FromWorld for Source {
#[inline(always)]
fn from_world(_world: &mut World) -> Self {
Source(Entity::PLACEHOLDER)
}
}
#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct Target(#[entities] pub Entity);
#[derive(Component, Reflect, Default, Clone, Copy)]
#[reflect(Component, Default)]
pub enum EdgeKind {
#[default]
External,
Internal,
}
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
#[require(EdgeKind)]
pub struct AlwaysEdge;
#[derive(Component, Reflect, Default)]
#[reflect(Component, Default)]
pub struct After {
pub duration: Duration,
}
impl After {
#[inline]
pub fn new(duration: Duration) -> Self { Self { duration } }
}
#[derive(Component)]
pub struct EdgeTimer(pub Timer);
#[derive(Reflect, Component)]
#[reflect(Component)]
#[require(EdgeKind)]
pub struct EventEdge<E: Event> {
#[reflect(ignore)]
_marker: PhantomData<E>,
}
impl<E: Event> Default for EventEdge<E> {
fn default() -> Self {
Self { _marker: PhantomData }
}
}
#[derive(Component)]
pub struct DeferEvent<E: Event> {
pub deferred: Option<E>,
}
impl<E: Event> Default for DeferEvent<E> {
fn default() -> Self {
Self { deferred: None }
}
}
impl<E: Event> DeferEvent<E> {
pub fn new() -> Self {
Self::default()
}
pub fn defer_event(&mut self, event: E) {
self.deferred = Some(event);
}
pub fn take_deferred(&mut self) -> Option<E> {
std::mem::take(&mut self.deferred)
}
}
#[derive(Component, Reflect, Default)]
#[reflect(Component, Default)]
pub struct ResetEdge(pub ResetScope);
#[derive(Reflect, Default, Clone, Copy)]
pub enum ResetScope { #[default] Source, Target, Both }
pub fn always_edge_listener(
trigger: Trigger<EnterState>,
transitions_query: Query<&Transitions>,
always_query: Query<(), With<AlwaysEdge>>,
edge_target_query: Query<&Target>,
guards_query: Query<&Guards>,
after_query: Query<&After>,
child_of_query: Query<&StateChildOf>,
mut commands: Commands,
){
let source = trigger.target();
let Ok(transitions) = transitions_query.get(source) else { return; };
for edge in transitions.into_iter().copied() {
if always_query.get(edge).is_err() { continue; }
if after_query.get(edge).is_ok() { continue; }
if edge_target_query.get(edge).is_err() { continue; }
if let Ok(guards) = guards_query.get(edge) {
if !guards.check() { continue; }
}
let root = child_of_query.root_ancestor(source);
commands.trigger_targets(Transition { source, edge }, root);
break;
}
}
fn find_parallel_region_root(
state: Entity,
child_of_query: &Query<&StateChildOf>,
parallel_query: &Query<&Parallel>,
) -> Entity {
let mut previous_ancestor = state;
for ancestor in child_of_query.iter_ancestors(state) {
if parallel_query.contains(ancestor) {
return previous_ancestor;
}
previous_ancestor = ancestor;
}
state
}
pub fn edge_event_listener<E: Event + Clone>(
trigger: Trigger<E>,
transitions_query: Query<&Transitions>,
listener_query: Query<&EventEdge<E>>,
edge_target_query: Query<&Target>,
guards_query: Query<&Guards>,
child_of_query: Query<&StateChildOf>,
current_state_query: Query<&StateMachine>,
mut defer_query: Query<&mut DeferEvent<E>>,
active_query: Query<(), With<Active>>,
parallel_query: Query<&Parallel>,
mut commands: Commands,
){
let event = trigger.event();
if let Ok(current) = current_state_query.get(trigger.target()) {
let machine_root = trigger.target();
let mut visited: HashSet<Entity> = HashSet::new();
let mut fired_regions: HashSet<Entity> = HashSet::new();
for &leaf in current.active_leaves.iter() {
let region_root = find_parallel_region_root(
leaf,
&child_of_query,
¶llel_query,
);
if fired_regions.contains(®ion_root) {
continue;
}
if try_fire_first_matching_edge_on_branch(
leaf,
event,
machine_root,
&transitions_query,
&listener_query,
&edge_target_query,
&guards_query,
&child_of_query,
&mut defer_query,
&active_query,
&mut visited,
&mut commands,
) {
fired_regions.insert(region_root);
}
}
return;
}
let source = trigger.target();
try_fire_first_matching_edge(
source,
event,
&transitions_query,
&listener_query,
&edge_target_query,
&guards_query,
&child_of_query,
&mut defer_query,
&active_query,
&mut commands,
);
}
fn try_fire_first_matching_edge<E: Event + Clone>(
source: Entity,
event: &E,
transitions_query: &Query<&Transitions>,
listener_query: &Query<&EventEdge<E>>,
edge_target_query: &Query<&Target>,
guards_query: &Query<&Guards>,
child_of_query: &Query<&StateChildOf>,
defer_query: &mut Query<&mut DeferEvent<E>>,
active_query: &Query<(), With<Active>>,
commands: &mut Commands,
) -> bool {
if let Ok(mut defer_event) = defer_query.get_mut(source) {
if active_query.get(source).is_ok() {
defer_event.defer_event(event.clone());
return false; }
}
let Ok(transitions) = transitions_query.get(source) else { return false; };
for edge in transitions.into_iter().copied() {
if listener_query.get(edge).is_err() { continue; }
if let Ok(guards) = guards_query.get(edge) {
if !guards.check() { continue; }
}
if edge_target_query.get(edge).is_err() { continue; }
let root = child_of_query.root_ancestor(source);
commands.trigger_targets(Transition { source, edge }, root);
return true;
}
false
}
fn try_fire_first_matching_edge_on_branch<E: Event + Clone>(
start: Entity,
event: &E,
machine_root: Entity,
transitions_query: &Query<&Transitions>,
listener_query: &Query<&EventEdge<E>>,
edge_target_query: &Query<&Target>,
guards_query: &Query<&Guards>,
child_of_query: &Query<&StateChildOf>,
defer_query: &mut Query<&mut DeferEvent<E>>,
active_query: &Query<(), With<Active>>,
visited: &mut HashSet<Entity>,
commands: &mut Commands,
) -> bool {
let mut current = Some(start);
while let Some(state) = current {
if !visited.insert(state) {
if state == machine_root { break; }
current = child_of_query.get(state).ok().map(|rel| rel.0);
continue;
}
if try_fire_first_matching_edge(
state,
event,
transitions_query,
listener_query,
edge_target_query,
guards_query,
child_of_query,
defer_query,
active_query,
commands,
) {
return true;
}
if state == machine_root { break; }
current = child_of_query.get(state).ok().map(|rel| rel.0);
}
false
}
pub fn check_always_on_guards_changed(
guards_changed_query: Query<(Entity, &Guards, &Source, Has<Target>), (Changed<Guards>, With<AlwaysEdge>)>,
transitions_query: Query<&Transitions>,
child_of_query: Query<&StateChildOf>,
active_query: Query<(), With<Active>>,
mut commands: Commands,
) {
for (edge, guards, source, edge_target) in guards_changed_query.iter() {
let source = source.0;
if !active_query.contains(source) { continue; }
if !guards.check() { continue; }
let Ok(transitions) = transitions_query.get(source) else { continue; };
if !transitions.into_iter().any(|&e| e == edge) { continue; }
if !edge_target { continue; }
let root = child_of_query.root_ancestor(source);
commands.trigger_targets(Transition { source, edge }, root);
}
}
pub fn start_after_on_enter(
trigger: Trigger<EnterState>,
transitions_query: Query<&Transitions>,
after_query: Query<&After>,
mut commands: Commands,
) {
let source = trigger.target();
let Ok(transitions) = transitions_query.get(source) else { return; };
for edge in transitions.into_iter().copied() {
if let Ok(after) = after_query.get(edge) {
commands.entity(edge).insert(EdgeTimer(Timer::new(after.duration, TimerMode::Once)));
}
}
}
pub fn cancel_after_on_exit(
trigger: Trigger<crate::ExitState>,
transitions_query: Query<&Transitions>,
after_query: Query<&After>,
mut commands: Commands,
) {
let source = trigger.target();
let Ok(transitions) = transitions_query.get(source) else { return; };
for edge in transitions.into_iter().copied() {
if after_query.get(edge).is_ok() {
commands.entity(edge).remove::<EdgeTimer>();
}
}
}
pub(crate) fn reset_on_transition_actions(
trigger: Trigger<crate::TransitionActions>,
reset_edge_q: Query<&ResetEdge>,
edge_q: Query<(&Source, &Target)>,
children_q: Query<&crate::StateChildren>,
mut commands: Commands,
) {
let edge = trigger.target();
let Ok(reset) = reset_edge_q.get(edge) else { return; };
let Ok((Source(source), Target(target))) = edge_q.get(edge) else { return; };
let mut entities = vec![];
match reset.0 {
ResetScope::Source => {
entities.push(*source);
entities.extend(children_q.iter_descendants(*source));
}
ResetScope::Target => {
entities.push(*target);
entities.extend(children_q.iter_descendants(*target));
}
ResetScope::Both => {
entities.push(*source);
entities.push(*target);
entities.extend(children_q.iter_descendants(*source));
entities.extend(children_q.iter_descendants(*target));
}
}
for entity in entities {
println!("Resetting entity: {:?}", entity);
commands.trigger_targets(Reset, entity);
}
}
pub fn tick_after_system(
time: Res<Time>,
sources_with_transitions: Query<(Entity, &Transitions), With<Active>>, mut timer_query: Query<&mut EdgeTimer>,
after_query: Query<&After>,
guards_query: Query<&Guards>,
edge_target_query: Query<&Target>,
child_of_query: Query<&StateChildOf>,
mut commands: Commands,
) {
for (source, transitions) in sources_with_transitions.iter() {
for edge in transitions.into_iter().copied() {
if after_query.get(edge).is_err() { continue; }
let Ok(mut timer) = timer_query.get_mut(edge) else { continue; };
timer.0.tick(time.delta());
if !timer.0.just_finished() { continue; }
if let Ok(guards) = guards_query.get(edge) {
if !guards.check() { continue; }
}
if edge_target_query.get(edge).is_err() { continue; }
commands.entity(edge).remove::<EdgeTimer>();
let root = child_of_query.root_ancestor(source);
commands.trigger_targets(Transition { source, edge }, root);
break; }
}
}
pub fn replay_deferred_event<E: Event + Clone>(
trigger: Trigger<ExitState>,
mut defer_query: Query<&mut DeferEvent<E>>,
child_of_query: Query<&StateChildOf>,
mut commands: Commands,
) {
let exited_state = trigger.target();
if let Ok(mut defer_event) = defer_query.get_mut(exited_state) {
let deferred = defer_event.take_deferred();
if let Some(deferred) = deferred {
let root_entity = child_of_query.root_ancestor(exited_state);
commands.trigger_targets(deferred, root_entity);
}
}
}