use std::fmt::Debug;
use bevy::prelude::*;
use rand::RngCore;
use crate::backend::SpatialBackend;
use crate::target::Target;
pub trait CollisionFilter: Component + Clone + Debug + Send + Sync + 'static {
type Lookup: Component;
fn can_target(
&self,
invoker_data: Option<&Self::Lookup>,
target_data: Option<&Self::Lookup>,
) -> bool;
}
#[derive(Component, Clone, Debug, Default)]
pub struct Collides;
#[derive(Clone, Debug)]
pub enum NumberType {
Fixed(usize),
Random(usize, usize),
}
impl NumberType {
pub fn resolve(&self, rng: &mut dyn RngCore) -> usize {
match self {
NumberType::Fixed(n) => *n,
NumberType::Random(min, max) => {
if min >= max {
return *min;
}
let range = max - min + 1;
let r = (rng.next_u64() as usize) % range;
min + r
}
}
}
}
pub fn limit_count<P: Clone + Copy + Send + Sync + Default + Debug + 'static>(
targets: Vec<Target<P>>,
number: &NumberType,
rng: &mut dyn RngCore,
) -> Vec<Target<P>> {
let max_count = number.resolve(rng);
if targets.len() <= max_count {
return targets;
}
let mut selected = Vec::with_capacity(max_count);
for (i, target) in targets.into_iter().enumerate() {
if selected.len() < max_count {
selected.push(target);
} else {
let r = (rng.next_u64() as usize) % (i + 1);
if r < max_count {
selected[r] = target;
}
}
}
selected
}
pub fn sort_by_distance<B: SpatialBackend>(
targets: &mut [Target<B::Pos>],
origin: &B::Pos,
) {
targets.sort_by(|a, b| {
let dist_a = B::distance(&a.position, origin);
let dist_b = B::distance(&b.position, origin);
dist_a.partial_cmp(&dist_b).unwrap_or(std::cmp::Ordering::Equal)
});
}