use glc::{ math::{linear_step, sqr}, matrix::Matrix3f, vector::{Vector2f, Vector3f}, }; use rand::random; use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; use crate::{ components::{Fleet, FleetOwned, Position, Ship, Velocity}, constants::{ SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, }, resources::Global, }; #[derive(Default)] pub struct Fleets; #[derive(SystemData)] pub struct FleetsData<'a> { ship: WriteStorage<'a, Ship>, velocity: WriteStorage<'a, Velocity>, fleet_owned: ReadStorage<'a, FleetOwned>, position: ReadStorage<'a, Position>, fleet: ReadStorage<'a, Fleet>, global: Read<'a, Global>, } impl<'a> System<'a> for Fleets { type SystemData = FleetsData<'a>; fn run(&mut self, data: Self::SystemData) { let FleetsData { mut ship, mut velocity, fleet_owned, position, fleet, global, } = data; (&mut ship, &mut velocity, &fleet_owned, &position) .par_join() .for_each(|(ship, vel, fleet_owned, pos)| { progress_ship( &position, &fleet, ship, vel, fleet_owned.owner, pos, global.delta, ) }); } } fn progress_ship<'a>( positions: &ReadStorage<'a, Position>, fleets: &ReadStorage<'a, Fleet>, ship: &mut Ship, velocity: &mut Velocity, fleet_id: Entity, position: &Position, delta: f32, ) { let (orbit_pos, orbit_min, orbit_max): (Vector2f, f32, f32) = match (positions.get(fleet_id), fleets.get(fleet_id)) { (Some(position), Some(fleet)) => (position.pos, fleet.orbit_min, fleet.orbit_max), (_, _) => return, }; let orbit_to_target = ship.target_pos - orbit_pos; let orbit_to_ship = position.pos - orbit_pos; let ship_to_target = ship.target_pos - position.pos; let target_radius = orbit_to_target.length_sqr(); let ship_radius = orbit_to_ship.length_sqr(); let target_in_orbit = (target_radius <= sqr(orbit_max)) && (target_radius >= sqr(orbit_min)); let ship_in_orbit = ship_radius < sqr(SHIP_ORBIT_DISTANCE_MAX * orbit_max); /* check and update target posistion */ if ship.target_dir.length_sqr() == 0.0 || ship_in_orbit != target_in_orbit || ship.target_dir * ship_to_target <= 0.0 { if ship_in_orbit && orbit_max > 0.0 { let orbit_to_ship_vec3 = Vector3f::new(orbit_to_ship.x, orbit_to_ship.y, 0.0); let ship_dir_vec3 = Vector3f::new(velocity.dir.x, velocity.dir.y, 0.0); let dir = if orbit_to_ship_vec3.cross(&ship_dir_vec3).z > 0.0 { 1.0 } else { -1.0 }; let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X); let angle = angle + (SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random()) * dir / orbit_max; let radius = orbit_min + (orbit_max - orbit_min) * random::(); ship.target_pos.x = orbit_pos.x + radius * angle.cos(); ship.target_pos.y = orbit_pos.y + radius * angle.sin(); } else { ship.target_pos = orbit_pos; } ship.target_dir = (ship.target_pos - position.pos).normalize(); } /* update ship direction */ let angle = ship_to_target.angle2(&velocity.dir); if angle.into_inner().abs() > 0.0001 { let dir = angle.into_inner() / angle.abs().into_inner(); let agility = if ship_in_orbit { SHIP_ORBIT_AGILITY } else { ship.agility }; let rot_speed = agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner()); let m = Matrix3f::new( Vector3f::new(velocity.dir.y, -velocity.dir.x, 0.0), Vector3f::new(velocity.dir.x, velocity.dir.y, 0.0), Vector3f::new(0.0, 0.0, 1.0), ); let m = m * Matrix3f::rotate(rot_speed * -dir * delta); velocity.dir = Vector2f::new(m.axis_y.x, m.axis_y.y); } }