use crate::prelude::*;
use beet_core::prelude::*;
pub trait RunEvent: EntityTargetEvent {
type End: EndEvent<Run = Self>;
}
pub trait EndEvent: EntityTargetEvent + Clone {
type Run: RunEvent<End = Self>;
}
#[derive(Debug, Clone, PartialEq, Eq, EntityTargetEvent)]
pub struct ChildEnd<T>
where
T: 'static + Send + Sync,
{
child: Entity,
value: T,
}
impl<T> std::ops::Deref for ChildEnd<T>
where
T: 'static + Send + Sync,
{
type Target = T;
fn deref(&self) -> &Self::Target { &self.value }
}
impl<T> std::ops::DerefMut for ChildEnd<T>
where
T: 'static + Send + Sync,
{
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value }
}
impl<T> ChildEnd<T>
where
T: Clone + Event,
{
pub fn trigger(mut commands: Commands, child: Entity, value: T) {
commands.queue(move |world: &mut World| {
if let Some(parent) = world.entity(child).get::<ChildOf>().clone() {
let parent = parent.parent();
world
.entity_mut(parent)
.trigger_target(ChildEnd { child, value });
}
})
}
pub fn child(&self) -> Entity { self.child }
pub fn value(&self) -> &T { &self.value }
pub fn inner(self) -> T { self.value }
}
#[derive(Debug, Clone, Component, Reflect)]
#[reflect(Component)]
pub struct PreventPropagateEnd<T = Outcome> {
phantom: PhantomData<T>,
}
impl<T> Default for PreventPropagateEnd<T> {
fn default() -> Self { Self { phantom: default() } }
}
pub(crate) fn propagate_end<T: EndEvent>(ev: On<T>, commands: Commands) {
ChildEnd::trigger(commands, ev.target(), ev.event().clone());
}
pub(crate) fn propagate_child_end<T>(
ev: On<ChildEnd<T>>,
mut commands: Commands,
prevent: Query<(), With<PreventPropagateEnd>>,
) where
T: EntityTargetEvent + Clone,
{
let target = ev.target();
if !prevent.contains(target) {
let value = ev.event().clone().inner();
commands.entity(target).trigger_target(value);
}
}
#[cfg(test)]
mod test {
use crate::prelude::*;
use beet_core::prelude::*;
#[action(run_child)]
#[derive(Component)]
struct Parent;
fn run_child(
ev: On<GetOutcome>,
children: Query<&Children>,
mut commands: Commands,
) {
let child = children.get(ev.target()).unwrap()[0];
commands.entity(child).trigger_target(GetOutcome);
}
#[action(succeed)]
#[derive(Component)]
struct Child;
fn succeed(ev: On<GetOutcome>, mut commands: Commands) {
commands.entity(ev.target()).trigger_target(Outcome::Pass);
}
#[test]
fn works() {
let mut world = ControlFlowPlugin::world();
world.insert_resource(Messages::<AppExit>::default());
world
.spawn((Parent, ExitOnEnd, children![Child]))
.trigger_target(GetOutcome)
.flush();
world.should_exit().xpect_eq(Some(AppExit::Success));
}
#[test]
fn prevent_propagate() {
let mut world = ControlFlowPlugin::world();
world.insert_resource(Messages::<AppExit>::default());
world
.spawn((
Parent,
PreventPropagateEnd::<Outcome>::default(),
children![(Child)],
))
.trigger_target(GetOutcome)
.flush();
world.should_exit().xpect_none();
}
}