use crate::ctx::BehaveCtx;
use bevy::prelude::*;
use dyn_clone::DynClone;
#[derive(Event, Debug, Clone)]
pub struct BehaveTrigger<T: Clone + Send + Sync> {
pub(crate) inner: T,
pub(crate) ctx: BehaveCtx,
}
impl<T: Clone + Send + Sync> BehaveTrigger<T> {
pub fn ctx(&self) -> &BehaveCtx {
&self.ctx
}
pub fn inner(&self) -> &T {
&self.inner
}
}
fn world_trigger<T: Clone + Send + Sync + 'static>(bundle: T) -> impl DynTriggerCommand {
move |ctx: BehaveCtx, world: &mut World| {
let ev = BehaveTrigger::<T> { inner: bundle, ctx };
world.trigger(ev);
}
}
trait DynTriggerCommand<Marker = ()>: DynClone + Send + Sync + 'static {
fn apply(self: Box<Self>, ctx: BehaveCtx, world: &mut World);
}
impl<F> DynTriggerCommand for F
where
F: FnOnce(BehaveCtx, &mut World) + DynClone + Send + Sync + 'static,
{
fn apply(self: Box<Self>, ctx: BehaveCtx, world: &mut World) {
self(ctx, world);
}
}
struct CommandWrapper {
ctx: BehaveCtx,
cmd: DynamicTrigger,
}
impl Command for CommandWrapper {
fn apply(self, world: &mut World) {
self.cmd.trig_fn.apply(self.ctx, world);
}
}
dyn_clone::clone_trait_object!(DynTriggerCommand);
#[derive(Clone)]
pub struct DynamicTrigger {
#[allow(dead_code)]
trig_fn: Box<dyn DynTriggerCommand>,
type_name: String,
}
impl std::fmt::Debug for DynamicTrigger {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DynamicTrigger({})", self.type_name)
}
}
impl DynamicTrigger {
pub fn type_name(&self) -> &str {
&self.type_name
}
pub fn new<T: Clone + Send + Sync + 'static>(trig: T) -> DynamicTrigger {
DynamicTrigger {
trig_fn: Box::new(world_trigger(trig)),
type_name: std::any::type_name::<T>().to_string(),
}
}
}
#[allow(dead_code)]
pub trait DynamicTriggerCommand {
fn dyn_trigger(&mut self, dyn_trigger: DynamicTrigger, ctx: BehaveCtx);
}
impl DynamicTriggerCommand for Commands<'_, '_> {
fn dyn_trigger(&mut self, dyn_trigger: DynamicTrigger, ctx: BehaveCtx) {
self.queue(CommandWrapper {
ctx,
cmd: dyn_trigger,
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct MyStruct(u32);
#[derive(Resource, Default)]
struct TrigRes {
val: Option<MyStruct>,
}
fn on_trig(trigger: On<BehaveTrigger<MyStruct>>, mut res: ResMut<TrigRes>) {
res.val = Some(*trigger.event().inner());
}
fn send_trigger(mut commands: Commands) {
let dyn_trig = DynamicTrigger::new(MyStruct(123));
let tick_ctx = crate::TickCtx::new(Entity::PLACEHOLDER, Entity::PLACEHOLDER, 0.0);
let ctx = BehaveCtx::new_for_entity(get_node_id(), &tick_ctx, Entity::PLACEHOLDER);
commands.dyn_trigger(dyn_trig, ctx);
}
#[test]
fn dyn_trigger_test() {
let mut app = App::new();
app.init_resource::<TrigRes>()
.add_observer(on_trig)
.add_systems(Startup, send_trigger);
app.update();
assert_eq!(app.world().resource::<TrigRes>().val, Some(MyStruct(123)));
}
fn get_node_id() -> ego_tree::NodeId {
let t = ego_tree::tree! { 1 };
t.root().id()
}
}