use alloc::vec::Vec;
use core::{fmt::Debug, marker::PhantomData};
use bevy_ecs::{
error::Result,
event::EntityEvent,
lifecycle::Insert,
observer::On,
prelude::{Commands, Component, Entity, With},
system::{Query, Single},
};
use bevy_prng::EntropySource;
use crate::{
global::GlobalRng, params::RngEntity, prelude::RngEntityCommandsExt, traits::ForkableAsSeed,
};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
#[cfg(feature = "bevy_reflect")]
use bevy_ecs::reflect::ReflectComponent;
#[derive(Debug, Component)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component))]
#[relationship_target(relationship = RngSource<Source, Target>)]
pub struct RngLinks<Source: EntropySource, Target: EntropySource> {
#[relationship]
related: Vec<Entity>,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
_source: PhantomData<Source>,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
_target: PhantomData<Target>,
}
impl<Source: EntropySource, Target: EntropySource> Default for RngLinks<Source, Target> {
#[inline]
fn default() -> Self {
Self {
related: Vec::new(),
_source: PhantomData,
_target: PhantomData,
}
}
}
#[derive(Debug, Component)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component))]
#[relationship(relationship_target = RngLinks<Source, Target>)]
pub struct RngSource<Source: EntropySource, Target: EntropySource> {
#[relationship]
linked: Entity,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
_source: PhantomData<Source>,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
_target: PhantomData<Target>,
}
impl<Source: EntropySource, Target: EntropySource> RngSource<Source, Target> {
#[inline]
pub fn new(parent: Entity) -> Self {
Self {
linked: parent,
_source: PhantomData,
_target: PhantomData,
}
}
#[inline]
pub fn entity(&self) -> Entity {
self.linked
}
}
#[derive(Debug, EntityEvent)]
pub struct SeedFromGlobal<Source: EntropySource, Target: EntropySource> {
#[event_target]
target: Entity,
_source: PhantomData<Source>,
_target: PhantomData<Target>,
}
impl<Source: EntropySource, Target: EntropySource> SeedFromGlobal<Source, Target> {
#[inline]
pub fn new(entity: Entity) -> Self {
Self {
target: entity,
_source: PhantomData,
_target: PhantomData,
}
}
}
#[derive(Debug, EntityEvent)]
pub struct SeedLinked<Source: EntropySource, Target: EntropySource> {
#[event_target]
source: Entity,
_source: PhantomData<Source>,
_target: PhantomData<Target>,
}
impl<Source: EntropySource, Target: EntropySource> SeedLinked<Source, Target> {
#[inline]
pub fn new(entity: Entity) -> Self {
Self {
source: entity,
_source: PhantomData,
_target: PhantomData,
}
}
}
#[derive(Debug, EntityEvent)]
pub struct SeedFromSource<Source: EntropySource, Target: EntropySource> {
#[event_target]
target: Entity,
_source: PhantomData<Source>,
_target: PhantomData<Target>,
}
impl<Source: EntropySource, Target: EntropySource> SeedFromSource<Source, Target> {
#[inline]
pub fn new(entity: Entity) -> Self {
Self {
target: entity,
_source: PhantomData,
_target: PhantomData,
}
}
}
pub fn seed_from_global<Source: EntropySource, Target: EntropySource>(
event: On<SeedFromGlobal<Source, Target>>,
mut source: Single<&mut Source, With<GlobalRng>>,
mut commands: Commands,
) -> Result {
let mut entity = commands.get_entity(event.target)?;
entity.insert(source.fork_as_seed::<Target>());
Ok(())
}
pub fn seed_from_parent<Source: EntropySource, Target: EntropySource>(
event: On<SeedFromSource<Source, Target>>,
q_linked: Query<&RngSource<Source, Target>>,
mut q_parents: Query<&mut Source, With<RngLinks<Source, Target>>>,
mut commands: Commands,
) -> Result {
let target = event.target;
let rng = q_linked
.get(target)
.and_then(|parent| q_parents.get_mut(parent.entity()))
.map(|mut rng| rng.fork_as_seed::<Target>())?;
commands.entity(target).insert(rng);
Ok(())
}
pub fn seed_linked<Source: EntropySource, Target: EntropySource>(
event: On<SeedLinked<Source, Target>>,
mut q_source: Query<(&mut Source, &RngLinks<Source, Target>)>,
mut commands: Commands,
) -> Result {
let target = event.source;
let (mut rng, targets) = q_source.get_mut(target)?;
let batched: Vec<_> = targets
.related
.iter()
.copied()
.map(|target| (target, rng.fork_as_seed::<Target>()))
.collect();
commands.insert_batch(batched);
Ok(())
}
pub fn trigger_seed_linked<Source: EntropySource, Target: EntropySource>(
event: On<Insert, Source>,
q_source: Query<RngEntity<Source>, With<RngLinks<Source, Target>>>,
mut commands: Commands,
) {
let target = event.entity;
if let Ok(mut rng_source) = q_source
.get(target)
.map(|source| commands.rng_entity(&source))
{
rng_source.reseed_linked_as::<Target>();
}
}