use super::*; use common::{ event::{EventBus, ServerEvent}, terrain::TerrainGrid, state::DeltaTime, comp, }; use specs::{Join, Read, ReadStorage, WriteStorage, System, WriteExpect, ReadExpect}; use std::sync::Arc; const ENTITY_TICK_PERIOD: u64 = 30; pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( Read<'a, DeltaTime>, Read<'a, EventBus>, WriteExpect<'a, RtSim>, ReadExpect<'a, TerrainGrid>, ReadExpect<'a, Arc>, ReadExpect<'a, world::IndexOwned>, ReadStorage<'a, comp::Pos>, ReadStorage<'a, RtSimEntity>, WriteStorage<'a, comp::Agent>, ); fn run( &mut self, ( dt, server_event_bus, mut rtsim, terrain, world, index, positions, rtsim_entities, mut agents, ): Self::SystemData, ) { let rtsim = &mut *rtsim; rtsim.tick += 1; // Update rtsim entities // TODO: don't update all of them each tick let mut to_reify = Vec::new(); for (id, entity) in rtsim.entities.iter_mut() { if entity.is_loaded { // No load-specific behaviour yet } else if rtsim.chunks.chunk_at(entity.pos.xy()).map(|c| c.is_loaded).unwrap_or(false) { to_reify.push(id); } else { // Simulate behaviour if let Some(travel_to) = entity.controller.travel_to { // Move towards target at approximate character speed entity.pos += Vec3::from((travel_to.xy() - entity.pos.xy()) .try_normalized() .unwrap_or_else(Vec2::zero) * entity.get_body().max_speed_approx() * entity.controller.speed_factor) * dt.0; } if let Some(alt) = world.sim().get_alt_approx(entity.pos.xy().map(|e| e.floor() as i32)) { entity.pos.z = alt; } } // Tick entity AI if entity.last_tick + ENTITY_TICK_PERIOD <= rtsim.tick { entity.tick(&terrain, &world); entity.last_tick = rtsim.tick; } } let mut server_emitter = server_event_bus.emitter(); for id in to_reify { rtsim.reify_entity(id); let entity = &rtsim.entities[id]; let spawn_pos = terrain.find_space(entity.pos.map(|e| e.floor() as i32)).map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); let body = entity.get_body(); server_emitter.emit(ServerEvent::CreateNpc { pos: comp::Pos(spawn_pos), stats: comp::Stats::new("Traveller [rt]".to_string(), body), health: comp::Health::new(body, 10), loadout: entity.get_loadout(), body, agent: Some(comp::Agent::new(None, true, &body)), alignment: comp::Alignment::Npc, scale: comp::Scale(1.0), drop_item: None, home_chunk: None, rtsim_entity: Some(RtSimEntity(id)), }); } // Update rtsim with real entity data for (pos, rtsim_entity, agent) in (&positions, &rtsim_entities, &mut agents).join() { rtsim.entities.get_mut(rtsim_entity.0).map(|entity| { entity.pos = pos.0; agent.rtsim_controller = entity.controller.clone(); }); } } }