use crate::pico8::Error;
use ::rand::Rng;
use bevy::{ecs::system::SystemParam, prelude::*};
use bevy_mod_scripting::bindings::InteropError;
#[cfg(feature = "scripting")]
use bevy_mod_scripting::bindings::ScriptValue;
use bevy_prng::WyRand;
use bevy_rand::prelude::{Entropy, EntropyPlugin, RngSeed};
use rand::RngCore;
#[derive(Debug, Component)]
struct Source;
#[derive(SystemParam)]
pub struct Rand8<'w> {
rand: Single<'w, (&'static mut Entropy<WyRand>, &'static RngSeed<WyRand>), With<Source>>,
}
impl Rand8<'_> {
pub fn rnd<T>(&mut self, max: T) -> T
where
T: ::rand::distributions::uniform::SampleUniform + PartialOrd + num_traits::Zero + Copy,
{
let (rng, _seed) = &mut *self.rand;
rng.gen_range(T::zero()..max)
}
#[cfg(feature = "scripting")]
pub fn rnd_value(&mut self, value: Option<ScriptValue>) -> ScriptValue {
let value = value.unwrap_or(ScriptValue::Unit);
let (rng, _seed) = &mut *self.rand;
match value {
ScriptValue::Integer(x) => {
ScriptValue::from(
x as f64 * (rng.next_u64().saturating_sub(1) as f64) / (u64::MAX as f64),
)
}
ScriptValue::Float(x) => {
ScriptValue::from(x * (rng.next_u64().saturating_sub(1) as f64) / (u64::MAX as f64))
}
ScriptValue::Unit => {
ScriptValue::from((rng.next_u64().saturating_sub(1) as f64) / (u64::MAX as f64))
}
ScriptValue::List(mut x) => {
if x.is_empty() {
ScriptValue::Unit
} else {
let index = rng.next_u64() as usize % x.len();
x.swap_remove(index)
}
}
_ => ScriptValue::Error(InteropError::external(Box::new(Error::InvalidArgument(
"rng expects integer, float, or list".into(),
)))),
}
}
#[allow(deprecated)]
pub fn srand(&mut self, new_seed: u64) {
let (rng, _seed) = &mut *self.rand;
rng.reseed(new_seed.to_ne_bytes());
}
}
pub(crate) fn plugin(app: &mut App) {
app.add_plugins(EntropyPlugin::<WyRand>::default())
.add_systems(PreStartup, setup);
}
fn setup(mut commands: Commands) {
commands.spawn((Source, RngSeed::<WyRand>::default()));
}