use std::{collections::VecDeque, iter::FusedIterator, marker::PhantomData};
use bevy::{
ecs::{
component::ComponentId,
entity::EntityHashMap,
observer::TriggerTargets,
query::{QueryData, QueryEntityError, QueryFilter, ReadOnlyQueryData},
system::{IntoObserverSystem, SystemParam},
world::Command,
},
prelude::*,
utils::Entry,
};
pub struct TargetedAction(pub Entity, pub ComponentId);
impl TriggerTargets for TargetedAction {
#[inline]
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
std::iter::once(self.1)
}
#[inline]
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
std::iter::once(self.0)
}
}
pub trait TriggerGetEntity {
fn get_entity(&self) -> Option<Entity>;
}
impl<E, B: Bundle> TriggerGetEntity for Trigger<'_, E, B> {
#[inline]
fn get_entity(&self) -> Option<Entity> {
Some(self.entity()).filter(|e| e != &Entity::PLACEHOLDER)
}
}
#[derive(SystemParam)]
pub struct AncestorQuery<'w, 's, T: ReferenceType> {
check: Query<'w, 's, (<T as ReferenceType>::Has, Option<&'static Parent>)>,
fetch: Query<'w, 's, T>,
cache: Local<'s, EntityHashMap<Entity>>,
}
impl<'w, 's, T: ReferenceType> AncestorQuery<'w, 's, T> {
fn find(&mut self, start: Entity) -> Result<Entity, QueryEntityError> {
let mut current = start;
loop {
match self.check.get(current) {
Ok((true, _)) => {
self.cache.insert(start, current);
return Ok(current);
}
Ok((false, Some(parent))) => {
current = **parent;
}
Ok((false, None)) => {
return Err(QueryEntityError::NoSuchEntity(current));
}
Err(_) => {
return Err(QueryEntityError::NoSuchEntity(current));
}
}
}
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
}
impl<'w, 's, T: Component> AncestorQuery<'w, 's, &'static T> {
pub fn get(&mut self, start: Entity) -> Result<&T, QueryEntityError> {
if let Entry::Occupied(entry) = self.cache.entry(start) {
if self.fetch.contains(*entry.get()) {
return self.fetch.get(*entry.get());
} else {
entry.remove();
}
}
self.find(start).and_then(|found| self.fetch.get(found))
}
}
impl<'w, 's, T: Component> AncestorQuery<'w, 's, &'static mut T> {
pub fn get_mut(&mut self, start: Entity) -> Result<Mut<T>, QueryEntityError> {
if let Entry::Occupied(entry) = self.cache.entry(start) {
if self.fetch.contains(*entry.get()) {
return self.fetch.get_mut(*entry.get());
} else {
entry.remove();
}
}
self.find(start).and_then(|found| self.fetch.get_mut(found))
}
}
pub trait ReferenceType: QueryData + 'static {
type Has: for<'a> ReadOnlyQueryData<Item<'a> = bool>;
}
impl<T: Component> ReferenceType for &'static T {
type Has = Has<T>;
}
impl<T: Component> ReferenceType for &'static mut T {
type Has = Has<T>;
}
pub struct Once<R: Resource + Default, C: Command> {
_type: PhantomData<R>,
command: C,
}
impl<R: Resource + Default, C: Command> Command for Once<R, C> {
fn apply(self, world: &mut World) {
if world.contains_resource::<R>() {
return;
}
world.insert_resource(R::default());
self.command.apply(world);
}
}
pub struct OnceCommands<'w, 's, R: Resource + Default> {
commands: Commands<'w, 's>,
_type: PhantomData<R>,
}
impl<'w, 's, R: Resource + Default> OnceCommands<'w, 's, R> {
fn new(commands: Commands<'w, 's>) -> Self {
Self {
commands,
_type: PhantomData,
}
}
pub fn observe<E: Event, B: Bundle, M>(mut self, observer: impl IntoObserverSystem<E, B, M>) {
self.commands.add(Once::<R, _> {
_type: PhantomData,
command: |world: &mut World| {
world.observe(observer);
},
});
}
}
pub trait CommandsExt {
fn once<R: Resource + Default>(&mut self) -> OnceCommands<'_, '_, R>;
}
impl CommandsExt for Commands<'_, '_> {
fn once<R: Resource + Default>(&mut self) -> OnceCommands<'_, '_, R> {
OnceCommands::new(self.reborrow())
}
}
#[derive(SystemParam)]
pub struct DFSPostTraversal<'w, 's, F: QueryFilter + 'static = ()> {
children: Query<'w, 's, &'static Children, F>,
queue: Local<'s, VecDeque<(usize, Entity)>>,
}
impl<'w, 's, F: QueryFilter + 'static> DFSPostTraversal<'w, 's, F> {
pub fn iter(&mut self, root: Entity) -> DFSPostTraversalIter<'_, 'w, 's, F> {
DFSPostTraversalIter::new(self, root)
}
}
pub struct DFSPostTraversalIter<'a, 'w, 's, F: QueryFilter + 'static> {
param: &'a mut DFSPostTraversal<'w, 's, F>,
visited: usize,
current_depth: usize,
}
impl<'a, 'w, 's, F: QueryFilter + 'static> DFSPostTraversalIter<'a, 'w, 's, F> {
fn new(param: &'a mut DFSPostTraversal<'w, 's, F>, root: Entity) -> Self {
param.queue.clear();
param.queue.push_back((0, root));
Self {
param,
visited: 0,
current_depth: 0,
}
}
}
impl<F: QueryFilter + 'static> Iterator for DFSPostTraversalIter<'_, '_, '_, F> {
type Item = Entity;
fn next(&mut self) -> Option<Self::Item> {
if self.param.queue.is_empty() {
return None;
}
loop {
let i = self.visited;
let Some(&(depth, entity)) = self.param.queue.get(i) else {
break;
};
if self.current_depth > depth {
break;
}
self.visited += 1;
self.current_depth = depth;
let Ok(entity_children) = self.param.children.get(entity) else {
break;
};
for (j, child) in entity_children.into_iter().copied().enumerate() {
self.param.queue.insert(i + j + 1, (depth + 1, child));
}
}
let Some((depth, entity)) = self.param.queue.remove(self.visited - 1) else {
return None;
};
self.visited -= 1;
self.current_depth = depth;
Some(entity)
}
}
impl<F: QueryFilter + 'static> FusedIterator for DFSPostTraversalIter<'_, '_, '_, F> {}