use std::marker::PhantomData;
use bevy::ecs::query::{QueryData, QueryFilter, ReadOnlyQueryData};
use bevy::prelude::*;
use crate::query_data_effect::QueryDataEffect;
use crate::{Effect, EffectOut, effect_out};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct QueryAffect<QueryDataE, Filter = ()>
where
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
{
pub query_data_effect: QueryDataE,
filter: PhantomData<Filter>,
}
impl<QueryDataE, Filter> QueryAffect<QueryDataE, Filter>
where
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
{
pub fn new(query_data_effect: QueryDataE) -> Self {
QueryAffect {
query_data_effect,
filter: PhantomData,
}
}
}
pub fn query_affect<QueryDataE, Filter>(
query_data_effect: QueryDataE,
) -> QueryAffect<QueryDataE, Filter>
where
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
{
QueryAffect::new(query_data_effect)
}
impl<QueryDataE, Filter> Default for QueryAffect<QueryDataE, Filter>
where
QueryDataE: QueryDataEffect + Default,
Filter: QueryFilter,
{
fn default() -> Self {
query_affect(default())
}
}
impl<QueryDataE, Filter> Effect for QueryAffect<QueryDataE, Filter>
where
QueryDataE: QueryDataEffect + Clone,
QueryDataE::MutQueryData: 'static,
Filter: QueryFilter + 'static,
{
type MutParam = Query<'static, 'static, QueryDataE::MutQueryData, Filter>;
fn affect(self, param: &mut <Self::MutParam as bevy::ecs::system::SystemParam>::Item<'_, '_>) {
param
.iter_mut()
.for_each(|mut query_data| self.query_data_effect.clone().affect(&mut query_data));
}
}
pub type BoxedQueryMapFn<QueryDataIn, QueryDataE> =
Box<dyn for<'w, 's> Fn(<QueryDataIn as QueryData>::Item<'w, 's>) -> QueryDataE>;
#[derive(derive_more::Debug)]
pub struct QueryMap<QueryDataIn, QueryDataE, Filter = ()>
where
QueryDataIn: ReadOnlyQueryData,
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
{
#[debug("{0} -> {1}", std::any::type_name::<QueryDataIn>(), std::any::type_name::<QueryDataE>())]
pub f: BoxedQueryMapFn<QueryDataIn, QueryDataE>,
filter: PhantomData<Filter>,
}
impl<QueryDataIn, QueryDataE, Filter> QueryMap<QueryDataIn, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData,
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
{
pub fn new(f: BoxedQueryMapFn<QueryDataIn, QueryDataE>) -> Self {
QueryMap {
f,
filter: PhantomData,
}
}
}
pub fn query_map<QueryDataIn, QueryDataE, Filter, F>(
f: F,
) -> QueryMap<QueryDataIn, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData,
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
F: for<'w, 's> Fn(QueryDataIn::Item<'w, 's>) -> QueryDataE + 'static,
{
QueryMap::new(Box::new(f))
}
impl<QueryDataIn, QueryDataE, Filter> Default for QueryMap<QueryDataIn, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData,
QueryDataE: QueryDataEffect + Default,
Filter: QueryFilter,
{
fn default() -> Self {
query_map(|_| default())
}
}
impl<QueryDataIn, QueryDataE, Filter> Effect for QueryMap<QueryDataIn, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData + 'static,
QueryDataE: QueryDataEffect,
QueryDataE::MutQueryData: 'static,
QueryDataE::Filter: 'static,
Filter: QueryFilter + 'static,
{
type MutParam = ParamSet<
'static,
'static,
(
Query<'static, 'static, (Entity, QueryDataIn), (QueryDataE::Filter, Filter)>,
Query<'static, 'static, QueryDataE::MutQueryData, Filter>,
),
>;
fn affect(self, param: &mut <Self::MutParam as bevy::ecs::system::SystemParam>::Item<'_, '_>) {
let query_data_effects = param
.p0()
.iter()
.map(|(entity, data_in)| (entity, (self.f)(data_in)))
.collect::<Vec<_>>();
query_data_effects
.into_iter()
.for_each(|(entity, query_data_effect)| {
query_data_effect.affect(&mut param.p1().get_mut(entity).expect("The entities in the first query are guaranteed to be a subset of the entities in the second query due to filters"));
})
}
}
pub type BoxedQueryMapAndFn<QueryDataIn, E, QueryDataE> =
Box<dyn for<'w, 's> Fn(<QueryDataIn as QueryData>::Item<'w, 's>) -> EffectOut<E, QueryDataE>>;
#[derive(derive_more::Debug)]
pub struct QueryMapAnd<QueryDataIn, E, QueryDataE, Filter = ()>
where
QueryDataIn: ReadOnlyQueryData,
E: Effect,
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
{
#[debug("{0} -> {1}", std::any::type_name::<QueryDataIn>(), std::any::type_name::<EffectOut<E, QueryDataE>>())]
pub f: BoxedQueryMapAndFn<QueryDataIn, E, QueryDataE>,
filter: PhantomData<Filter>,
}
impl<QueryDataIn, E, QueryDataE, Filter> QueryMapAnd<QueryDataIn, E, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData,
E: Effect,
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
{
pub fn new(f: BoxedQueryMapAndFn<QueryDataIn, E, QueryDataE>) -> Self {
QueryMapAnd {
f,
filter: PhantomData,
}
}
}
pub fn query_map_and<QueryDataIn, E, QueryDataE, Filter, F>(
f: F,
) -> QueryMapAnd<QueryDataIn, E, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData,
E: Effect,
QueryDataE: QueryDataEffect,
Filter: QueryFilter,
F: for<'w, 's> Fn(QueryDataIn::Item<'w, 's>) -> EffectOut<E, QueryDataE> + 'static,
{
QueryMapAnd::new(Box::new(f))
}
impl<QueryDataIn, E, QueryDataE, Filter> Default for QueryMapAnd<QueryDataIn, E, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData,
E: Effect + Default,
QueryDataE: QueryDataEffect + Default,
Filter: QueryFilter,
{
fn default() -> Self {
query_map_and(|_| default())
}
}
impl<QueryDataIn, E, QueryDataE, Filter> Effect for QueryMapAnd<QueryDataIn, E, QueryDataE, Filter>
where
QueryDataIn: ReadOnlyQueryData + 'static,
E: Effect,
QueryDataE: QueryDataEffect,
QueryDataE::MutQueryData: 'static,
QueryDataE::Filter: 'static,
Filter: QueryFilter + 'static,
{
type MutParam = ParamSet<
'static,
'static,
(
Query<'static, 'static, (Entity, QueryDataIn), (QueryDataE::Filter, Filter)>,
Query<'static, 'static, QueryDataE::MutQueryData, Filter>,
<Vec<E> as Effect>::MutParam,
),
>;
fn affect(self, param: &mut <Self::MutParam as bevy::ecs::system::SystemParam>::Item<'_, '_>) {
let EffectOut {
effect: effects,
out: query_data_effects,
}: EffectOut<Vec<E>, Vec<(Entity, QueryDataE)>> = param
.p0()
.iter()
.map(|(entity, data_in)| {
let EffectOut {
effect,
out: query_data_effect,
} = (self.f)(data_in);
effect_out(effect, (entity, query_data_effect))
})
.collect();
query_data_effects
.into_iter()
.for_each(|(entity, query_data_effect)| {
query_data_effect.affect(&mut param.p1().get_mut(entity).expect("The entities in the first query are guaranteed to be a subset of the entities in the second query due to filters"));
});
effects.affect(&mut param.p2())
}
}