use std::{thread::sleep, time::Duration};
use flax::{
component, components::name, entity_ids, CommandBuffer, Entity, FetchExt, Query, QueryBorrow,
Schedule, System, World,
};
use itertools::Itertools;
use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng};
use tracing::info_span;
use tracing_subscriber::prelude::*;
fn main() {
tracing_subscriber::registry()
.with(tracing_tree::HierarchicalLayer::new(4))
.init();
let mut world = World::new();
component! {
health: f32,
poison: f32,
armor: f32,
player: (),
}
let query = Query::new((name(), health().modified()));
let health_changes = System::builder()
.with_query(query)
.build(|mut query: QueryBorrow<_>| {
info_span!("health_changes");
for (name, health) in &mut query {
tracing::info!("{name:?}: is now at {health} health");
}
});
let player_id = Entity::builder()
.set(name(), "player".into())
.set(health(), 100.0)
.set_default(player())
.spawn(&mut world);
let enemies = (0..10)
.map(|i| {
Entity::builder()
.set(name(), format!("enemy.{i}"))
.set(health(), 50.0)
.spawn(&mut world)
})
.collect_vec();
let all = enemies.iter().copied().chain([player_id]).collect_vec();
let mut rng = StdRng::from_entropy();
all.choose_multiple(&mut rng, all.len() / 5)
.for_each(|&id| {
world.set(id, poison(), 10.0).unwrap();
});
let damage_random = System::builder()
.with_world_mut()
.build(move |world: &mut World| {
let count = rng.gen_range(0..enemies.len());
let targets = all.choose_multiple(&mut rng, count);
for &enemy in targets {
if let Ok(mut health) = world.get_mut(enemy, health()) {
*health -= 1.0;
}
}
});
let update_poison = System::builder()
.with_query(Query::new((name().opt(), health().as_mut(), poison())))
.for_each(|(name, health, poison)| {
*health -= poison;
tracing::info!("{name:?} suffered {poison} in poison damage");
});
let query = Query::new((name().opt(), entity_ids(), player().satisfied()))
.filter(health().le(0.0).modified());
let cleanup = System::builder()
.with_name("cleanup")
.with_query(query)
.with_cmd_mut()
.build(|mut q: QueryBorrow<_, _>, cmd: &mut CommandBuffer| {
for (name, id, is_player) in &mut q {
if is_player {
tracing::info!("Player died");
}
tracing::info!(name, is_player, "Despawning {id}");
cmd.despawn(id);
}
});
let mut schedule = Schedule::new()
.with_system(damage_random)
.with_system(update_poison)
.with_system(health_changes)
.flush()
.with_system(cleanup)
.flush();
while world.is_alive(player_id) {
schedule
.execute_par(&mut world)
.expect("Failed to run schedule");
sleep(Duration::from_millis(1000));
}
}