// Library use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; use vek::*; use rand::Rng; // Crate use crate::{ comp::{phys::Pos, Agent, Control, Jumping, Attacking}, state::Time, }; // Basic ECS AI agent system pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( Read<'a, Time>, Entities<'a>, WriteStorage<'a, Agent>, ReadStorage<'a, Pos>, WriteStorage<'a, Control>, WriteStorage<'a, Jumping>, WriteStorage<'a, Attacking>, ); fn run( &mut self, (time, entities, mut agents, positions, mut controls, mut jumpings, mut attackings): Self::SystemData, ) { for (entity, agent, pos, control) in (&entities, &mut agents, &positions, &mut controls).join() { match agent { Agent::Wanderer(bearing) => { *bearing += Vec2::new(rand::random::() - 0.5, rand::random::() - 0.5) * 0.1 - *bearing * 0.01 - pos.0 * 0.0002; if bearing.magnitude_squared() != 0.0 { control.move_dir = bearing.normalized(); } } Agent::Pet { target, offset } => { // Run towards target match positions.get(*target) { Some(tgt_pos) => { let tgt_pos = tgt_pos.0 + *offset; if tgt_pos.z > pos.0.z + 1.0 { jumpings.insert(entity, Jumping); } // Move towards the target. let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude(); control.move_dir = if dist > 5.0 { Vec2::from(tgt_pos - pos.0).normalized() } else if dist < 1.5 && dist > 0.0 { Vec2::from(pos.0 - tgt_pos).normalized() } else { Vec2::zero() }; } _ => control.move_dir = Vec2::zero(), } // Change offset occasionally. if rand::random::() < 0.003 { *offset = Vec2::new(rand::random::() - 0.5, rand::random::() - 0.5) * 10.0; } }, Agent::Enemy { target } => { let choose_new = match target.map(|tgt| positions.get(tgt)).flatten() { Some(tgt_pos) => { let dist = Vec2::::from(tgt_pos.0 - pos.0).magnitude(); if dist < 2.0 { control.move_dir = Vec2::zero(); if rand::random::() < 0.2 { attackings.insert(entity, Attacking::start()); } false } else if dist < 60.0 { control.move_dir = Vec2::::from(tgt_pos.0 - pos.0).normalized() * 0.96; false } else { true } }, None => { control.move_dir = Vec2::one(); rand::random::().fract() < 0.25 }, }; if choose_new { let entities = (&entities, &positions) .join() .filter(|(_, e_pos)| Vec2::::from(e_pos.0 - pos.0).magnitude() < 30.0) .map(|(e, _)| e) .collect::>(); *target = rand::thread_rng().choose(&entities).cloned(); } }, } } } }