use rand::prelude::*;
#[cfg(feature = "parallel")]
use rayon::iter::ParallelIterator;
use specs::{prelude::*, storage::HashMapStorage, WorldExt};
const TAU: f32 = 2. * std::f32::consts::PI;
#[derive(Debug)]
struct ClusterBomb {
fuse: usize,
}
impl Component for ClusterBomb {
type Storage = HashMapStorage<Self>;
}
#[derive(Debug)]
struct Shrapnel {
durability: usize,
}
impl Component for Shrapnel {
type Storage = HashMapStorage<Self>;
}
#[derive(Debug, Clone)]
struct Pos(f32, f32);
impl Component for Pos {
type Storage = VecStorage<Self>;
}
#[derive(Debug)]
struct Vel(f32, f32);
impl Component for Vel {
type Storage = DenseVecStorage<Self>;
}
struct ClusterBombSystem;
impl<'a> System<'a> for ClusterBombSystem {
type SystemData = (
Entities<'a>,
WriteStorage<'a, ClusterBomb>,
ReadStorage<'a, Pos>,
Read<'a, LazyUpdate>,
);
fn run(&mut self, (entities, mut bombs, positions, updater): Self::SystemData) {
use rand::distributions::Uniform;
let durability_range = Uniform::new(10, 20);
let update_position = |(entity, bomb, position): (Entity, &mut ClusterBomb, &Pos)| {
let mut rng = rand::thread_rng();
if bomb.fuse == 0 {
let _ = entities.delete(entity);
for _ in 0..9 {
let shrapnel = entities.create();
updater.insert(
shrapnel,
Shrapnel {
durability: durability_range.sample(&mut rng),
},
);
updater.insert(shrapnel, position.clone());
let angle: f32 = rng.gen::<f32>() * TAU;
updater.insert(shrapnel, Vel(angle.sin(), angle.cos()));
}
} else {
bomb.fuse -= 1;
}
};
#[cfg(not(feature = "parallel"))]
{
(&entities, &mut bombs, &positions)
.join()
.for_each(update_position);
}
#[cfg(feature = "parallel")]
{
(&entities, &mut bombs, &positions)
.par_join()
.for_each(update_position);
}
}
}
struct PhysicsSystem;
impl<'a> System<'a> for PhysicsSystem {
type SystemData = (WriteStorage<'a, Pos>, ReadStorage<'a, Vel>);
fn run(&mut self, (mut pos, vel): Self::SystemData) {
#[cfg(not(feature = "parallel"))]
(&mut pos, &vel).join().for_each(|(pos, vel)| {
pos.0 += vel.0;
pos.1 += vel.1;
});
#[cfg(feature = "parallel")]
(&mut pos, &vel).par_join().for_each(|(pos, vel)| {
pos.0 += vel.0;
pos.1 += vel.1;
});
}
}
struct ShrapnelSystem;
impl<'a> System<'a> for ShrapnelSystem {
type SystemData = (Entities<'a>, WriteStorage<'a, Shrapnel>);
fn run(&mut self, (entities, mut shrapnels): Self::SystemData) {
#[cfg(not(feature = "parallel"))]
(&entities, &mut shrapnels)
.join()
.for_each(|(entity, shrapnel)| {
if shrapnel.durability == 0 {
let _ = entities.delete(entity);
} else {
shrapnel.durability -= 1;
}
});
#[cfg(feature = "parallel")]
(&entities, &mut shrapnels)
.par_join()
.for_each(|(entity, shrapnel)| {
if shrapnel.durability == 0 {
let _ = entities.delete(entity);
} else {
shrapnel.durability -= 1;
}
});
}
}
fn main() {
let mut world = World::new();
let mut dispatcher = DispatcherBuilder::new()
.with(PhysicsSystem, "physics", &[])
.with(ClusterBombSystem, "cluster_bombs", &[])
.with(ShrapnelSystem, "shrapnels", &[])
.build();
dispatcher.setup(&mut world);
world
.create_entity()
.with(Pos(0., 0.))
.with(ClusterBomb { fuse: 3 })
.build();
let mut step = 0;
loop {
step += 1;
let mut entities = 0;
{
let positions = world.read_storage::<Pos>();
const WIDTH: usize = 10;
const HEIGHT: usize = 10;
const SCALE: f32 = 1. / 4.;
let mut screen = [[0; WIDTH]; HEIGHT];
for entity in world.entities().join() {
if let Some(pos) = positions.get(entity) {
let x = (pos.0 * SCALE + WIDTH as f32 / 2.).floor() as usize;
let y = (pos.1 * SCALE + HEIGHT as f32 / 2.).floor() as usize;
if x < WIDTH && y < HEIGHT {
screen[x][y] += 1;
}
}
entities += 1;
}
println!("Step: {}, Entities: {}", step, entities);
for row in &screen {
for cell in row {
print!("{}", cell);
}
println!();
}
println!();
}
if entities == 0 {
break;
}
dispatcher.dispatch(&world);
world.maintain();
}
}