use std::{
collections::{HashMap, HashSet},
ops::Deref,
};
use crate::event::{ScriptAttachedEvent, ScriptDetachedEvent};
use ::{
bevy_asset::Handle,
bevy_ecs::{
entity::Entity, prelude::ReflectComponent, resource::Resource, world::DeferredWorld,
},
bevy_reflect::Reflect,
};
mod context_key;
mod script_context;
use bevy_ecs::{component::Component, lifecycle::HookContext};
use bevy_log::trace;
use bevy_mod_scripting_asset::ScriptAsset;
use bevy_mod_scripting_script::ScriptAttachment;
pub use context_key::*;
pub use script_context::*;
pub type ScriptId = Handle<ScriptAsset>;
#[derive(Component, Reflect, Clone, Default, Debug)]
#[reflect(Component)]
#[component(on_remove=Self::on_remove, on_add=Self::on_add)]
pub struct ScriptComponent(pub Vec<Handle<ScriptAsset>>);
impl Deref for ScriptComponent {
type Target = Vec<Handle<ScriptAsset>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ScriptComponent {
pub fn new<S: Into<Handle<ScriptAsset>>, I: IntoIterator<Item = S>>(components: I) -> Self {
Self(components.into_iter().map(Into::into).collect())
}
fn get_context_keys_present(world: &DeferredWorld, entity: Entity) -> Vec<ScriptAttachment> {
let entity_ref = world.entity(entity);
let script_component = entity_ref.components::<&ScriptComponent>();
let mut context_keys = Vec::new();
for script in script_component.iter() {
context_keys.push(ScriptAttachment::EntityScript(entity, script.clone()));
}
context_keys
}
pub fn on_remove(mut world: DeferredWorld, context: HookContext) {
let context_keys = Self::get_context_keys_present(&world, context.entity);
trace!("on remove hook for script components: {context_keys:?}");
world.write_message_batch(context_keys.into_iter().map(ScriptDetachedEvent));
}
pub fn on_add(mut world: DeferredWorld, context: HookContext) {
let context_keys = Self::get_context_keys_present(&world, context.entity);
trace!("on add hook for script components: {context_keys:?}");
world.write_message_batch(context_keys.into_iter().map(ScriptAttachedEvent));
}
}
#[cfg(test)]
mod tests {
use bevy_ecs::{message::Messages, world::World};
use super::*;
#[test]
fn test_component_add() {
let mut world = World::new();
world.init_resource::<Messages<ScriptAttachedEvent>>();
let entity = world.spawn(ScriptComponent::new([Handle::default()])).id();
let mut events = world.resource_mut::<Messages<ScriptAttachedEvent>>();
assert_eq!(
ScriptAttachment::EntityScript(entity, Handle::default()),
events.drain().next().unwrap().0
);
}
}