use bevy::{ecs::schedule::ScheduleLabel, prelude::*};
pub trait AppExt {
fn trigger_on<'a, O: Event<Trigger<'a>: Default>>(
&mut self,
schedule: impl ScheduleLabel,
event: impl Fn() -> O + 'static + Send + Sync,
) -> &mut Self;
fn add_propagator<'a, T: Event, O: Event<Trigger<'a>: Default>>(
&mut self,
event: impl Fn(On<T>) -> O + 'static + Send + Sync,
) -> &mut Self;
fn add_multicondition_propagator(
&mut self,
builder: impl Fn(Conditions) -> PropagatorResult,
) -> &mut Self;
}
#[derive(Component, Deref, DerefMut)]
struct PropagatorTodo(Vec<bool>);
pub struct Conditions<'a> {
todo: PropagatorTodo,
todo_ent: Entity,
app: &'a mut App,
}
pub struct PropagatorResult {
_marker: (),
}
impl<'a> Conditions<'a> {
fn new(app: &'a mut App) -> Self {
Self {
todo_ent: app.world_mut().spawn_empty().id(),
app,
todo: PropagatorTodo(vec![]),
}
}
pub fn add<T: Event>(mut self) -> Self {
let index = self.todo.0.len();
let entity = self.todo_ent.clone();
self.app
.add_observer(move |_: On<T>, mut todo_q: Query<&mut PropagatorTodo>| {
if let Ok(mut todo) = todo_q.get_mut(entity) {
todo.0[index] = true;
}
});
self.todo.push(false);
self
}
pub fn then_call<'b, E: Event<Trigger<'b>: Default> + Clone>(
self,
event: E,
) -> PropagatorResult {
let entity = self.todo_ent;
let mut todo = Some(self.todo);
self.app
.add_systems(Startup, move |mut commands: Commands| {
if let Some(t) = todo.take() {
commands.entity(entity).insert(t);
}
})
.add_systems(
PreUpdate,
move |mut todo_q: Query<&mut PropagatorTodo>, mut commands: Commands| {
if let Ok(mut todo) = todo_q.get_mut(entity) {
if todo.iter().all(|done| *done) {
todo.fill_with(|| false);
commands.trigger(event.clone());
}
}
},
);
PropagatorResult { _marker: () }
}
}
impl AppExt for App {
fn trigger_on<'a, O: Event<Trigger<'a>: Default>>(
&mut self,
schedule: impl ScheduleLabel,
event: impl Fn() -> O + 'static + Send + Sync,
) -> &mut Self {
self.add_systems(schedule, move |mut commands: Commands| {
commands.trigger(event())
});
self
}
fn add_propagator<'a, T: Event, O: Event<Trigger<'a>: Default>>(
&mut self,
event: impl Fn(On<T>) -> O + 'static + Send + Sync,
) -> &mut Self {
self.add_observer(move |t: On<T>, mut commands: Commands| commands.trigger(event(t)));
self
}
fn add_multicondition_propagator(
&mut self,
builder: impl Fn(Conditions) -> PropagatorResult,
) -> &mut Self {
builder(Conditions::new(self));
self
}
}