use bevy::{
ecs::component::{ComponentHooks, StorageType},
prelude::*,
};
use rand::{seq::IteratorRandom, RngCore};
use crate::{
ecs::{CommandsExt, TriggerGetEntity},
event::{OnPick, OnPicked},
picking::Picker,
};
pub struct PickRandom {
pub rng: Box<dyn RngCore + Send + Sync + 'static>,
}
impl PickRandom {
pub fn new(rng: impl RngCore + Send + Sync + 'static) -> Self {
Self { rng: Box::new(rng) }
}
pub fn rng(&mut self) -> &mut (impl RngCore + Send + Sync + 'static) {
&mut self.rng
}
pub fn set_rng(&mut self, rng: impl RngCore + Send + Sync + 'static) {
self.rng = Box::new(rng);
}
fn observer(
trigger: Trigger<OnPick>,
mut commands: Commands,
mut targets: Query<(Entity, &mut Picker, &mut PickRandom)>,
) {
fn run(target: Entity, mut commands: Commands, mut picker: Mut<Picker>, settings: &mut PickRandom) {
let random = picker.choices.keys().choose(&mut *settings.rng()).copied();
let action = picker.pick(random);
commands.trigger_targets(OnPicked { action }, target);
}
if let Some(target) = trigger.get_entity() {
let Ok((target, picker, settings)) = targets.get_mut(target) else {
return;
};
run(target, commands.reborrow(), picker, settings.into_inner());
} else {
for (target, picker, settings) in targets.iter_mut() {
run(target, commands.reborrow(), picker, settings.into_inner());
}
}
}
}
impl<R: RngCore + Send + Sync + 'static> From<R> for PickRandom {
fn from(rng: R) -> Self {
Self::new(rng)
}
}
impl Component for PickRandom {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut ComponentHooks) {
hooks.on_add(|mut world, _entity, _component| {
#[derive(Resource, Default)]
struct RandomObserverSpawned;
world.commands().once::<RandomObserverSpawned>().observe(Self::observer);
});
}
}