use std::{collections::VecDeque, time::Duration}; use bevy::prelude::*; use crate::{ pathfinding::find_path, rng::SimRng, sim::{SimPos, sim_pos}, world::{TILE_SIZE, WorldMap}, worldgen::{WorldGenerated, spawn_tiles}, }; pub struct ColonistPlugin; impl Plugin for ColonistPlugin { fn build(&self, app: &mut App) { app.insert_resource(SimRng::new(0x5EED)) .add_message::() .add_systems(Update, spawn_colonists.run_if(on_message::)) .add_systems(Update, handle_move_orders) .add_systems(FixedUpdate, (wander, follow_path).chain()); } } const TILES_PER_SECOND: f32 = 4.0; const WANDER_RADIUS: i32 = 8; const WANDER_PAUSE_SECS: std::ops::Range = 1.0..4.0; const STARTING_COLONISTS: [(&str, Color); 3] = [ ("Ada", Color::srgb(0.9, 0.3, 0.3)), ("Bram", Color::srgb(0.3, 0.5, 0.95)), ("Cleo", Color::srgb(0.95, 0.75, 0.3)), ]; #[derive(Component, Debug)] #[require(Transform, SimPos, TilePos, Path, Wander)] pub struct Colonist { pub name: String, } #[derive(Component, Default, Debug, Clone, Copy, PartialEq)] pub struct TilePos(pub UVec2); #[derive(Component, Default, Debug)] pub struct Path(pub VecDeque); #[derive(Component, Debug)] pub struct Wander(Timer); impl Default for Wander { fn default() -> Self { Self(Timer::from_seconds( WANDER_PAUSE_SECS.start, TimerMode::Once, )) } } #[derive(Message, Debug, Clone, Copy)] pub struct MoveOrder { pub colonist: Entity, pub goal: UVec2, } fn spawn_colonists( mut commands: Commands, map: Res, existing: Query>, mut meshes: ResMut>, mut materials: ResMut>, ) { for entity in &existing { commands.entity(entity).despawn(); } let mesh = meshes.add(Circle::new(TILE_SIZE * 0.35)); let tiles = spawn_tiles(&map, STARTING_COLONISTS.len()); for ((name, color), tile) in STARTING_COLONISTS.into_iter().zip(tiles) { commands.spawn(( Name::new(name), Colonist { name: name.into() }, TilePos(tile), Mesh2d(mesh.clone()), MeshMaterial2d(materials.add(color)), sim_pos(WorldMap::tile_center(tile)), Transform::from_translation(WorldMap::tile_center(tile).extend(1.0)), )); } } fn handle_move_orders( mut orders: MessageReader, map: Res, mut colonists: Query<(&TilePos, &mut Path)>, ) { for order in orders.read() { let Ok((tile, mut path)) = colonists.get_mut(order.colonist) else { continue; }; if let Some(new_path) = repath(&map, tile.0, &path.0, order.goal) { path.0 = new_path; } } } pub fn repath( map: &WorldMap, tile: UVec2, current: &VecDeque, goal: UVec2, ) -> Option> { match current.front() { Some(&next) => { let mut path: VecDeque<_> = find_path(map, next, goal)?.into(); path.push_front(next); Some(path) } None => find_path(map, tile, goal).map(VecDeque::from), } } fn wander( time: Res