|
|
|
@@ -0,0 +1,205 @@ |
|
|
|
use glc::{ |
|
|
|
math::{linear_step, sqr}, |
|
|
|
matrix::Matrix3f, |
|
|
|
vector::{Vector2f, Vector3f}, |
|
|
|
}; |
|
|
|
use rand::random; |
|
|
|
use shrev::ReaderId; |
|
|
|
use specs::{ |
|
|
|
hibitset::BitSet, prelude::*, Entities, 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, |
|
|
|
}, |
|
|
|
misc::ComponentEvent, |
|
|
|
resources::Global, |
|
|
|
return_if_none, |
|
|
|
}; |
|
|
|
|
|
|
|
pub struct Ships { |
|
|
|
need_update: BitSet, |
|
|
|
fleet_owned_id: ReaderId<ComponentEvent<FleetOwned>>, |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(SystemData)] |
|
|
|
pub struct ShipsData<'a> { |
|
|
|
global: Read<'a, Global>, |
|
|
|
entities: Entities<'a>, |
|
|
|
ships: WriteStorage<'a, Ship>, |
|
|
|
velocities: WriteStorage<'a, Velocity>, |
|
|
|
fleet_owned: ReadStorage<'a, FleetOwned>, |
|
|
|
positions: ReadStorage<'a, Position>, |
|
|
|
fleets: ReadStorage<'a, Fleet>, |
|
|
|
} |
|
|
|
|
|
|
|
struct Processor<'a> { |
|
|
|
need_update: &'a BitSet, |
|
|
|
positions: &'a ReadStorage<'a, Position>, |
|
|
|
fleets: &'a ReadStorage<'a, Fleet>, |
|
|
|
delta: f32, |
|
|
|
} |
|
|
|
|
|
|
|
impl Ships { |
|
|
|
pub fn new(world: &mut World) -> Self { |
|
|
|
let need_update = BitSet::new(); |
|
|
|
let fleet_owned_id = unsafe { |
|
|
|
WriteStorage::<FleetOwned>::setup(world); |
|
|
|
|
|
|
|
let mut fleet_owned = world.system_data::<WriteStorage<FleetOwned>>(); |
|
|
|
fleet_owned |
|
|
|
.unprotected_storage_mut() |
|
|
|
.channel_mut() |
|
|
|
.register_reader() |
|
|
|
}; |
|
|
|
|
|
|
|
Self { |
|
|
|
need_update, |
|
|
|
fleet_owned_id, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn progress_events(&mut self, fleet_owned: &ReadStorage<'_, FleetOwned>) { |
|
|
|
self.need_update.clear(); |
|
|
|
let events = fleet_owned |
|
|
|
.unprotected_storage() |
|
|
|
.channel() |
|
|
|
.read(&mut self.fleet_owned_id); |
|
|
|
for event in events { |
|
|
|
let id = match event { |
|
|
|
ComponentEvent::Inserted(id) => id, |
|
|
|
ComponentEvent::Modified(id, _) => id, |
|
|
|
ComponentEvent::Removed(id, _) => id, |
|
|
|
}; |
|
|
|
|
|
|
|
self.need_update.add(*id); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl<'a> System<'a> for Ships { |
|
|
|
type SystemData = ShipsData<'a>; |
|
|
|
|
|
|
|
fn run(&mut self, data: Self::SystemData) { |
|
|
|
let ShipsData { |
|
|
|
global, |
|
|
|
entities, |
|
|
|
mut ships, |
|
|
|
mut velocities, |
|
|
|
fleet_owned, |
|
|
|
positions, |
|
|
|
fleets, |
|
|
|
} = data; |
|
|
|
|
|
|
|
self.progress_events(&fleet_owned); |
|
|
|
|
|
|
|
/* update ships */ |
|
|
|
let processor = Processor { |
|
|
|
need_update: &self.need_update, |
|
|
|
positions: &positions, |
|
|
|
fleets: &fleets, |
|
|
|
delta: global.delta, |
|
|
|
}; |
|
|
|
( |
|
|
|
&entities, |
|
|
|
&mut ships, |
|
|
|
&mut velocities, |
|
|
|
&positions, |
|
|
|
&fleet_owned, |
|
|
|
) |
|
|
|
.par_join() |
|
|
|
.for_each(|(entity, ship, velocity, position, fleet_owned)| { |
|
|
|
processor.progress_ship(entity, ship, velocity, position, fleet_owned); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Processor<'_> { |
|
|
|
fn progress_ship( |
|
|
|
&self, |
|
|
|
entity: Entity, |
|
|
|
ship: &mut Ship, |
|
|
|
velocity: &mut Velocity, |
|
|
|
position: &Position, |
|
|
|
fleet_owned: &FleetOwned, |
|
|
|
) { |
|
|
|
let fleet_id = fleet_owned.owner; |
|
|
|
let fleet = return_if_none!(self.fleets.get(fleet_id)); |
|
|
|
let orbit_pos = return_if_none!(self.positions.get(fleet_id)).pos; |
|
|
|
let ship_pos = position.pos; |
|
|
|
let target_pos = ship.target_pos; |
|
|
|
let target_dir = ship.target_dir; |
|
|
|
|
|
|
|
let orbit_to_target = target_pos - orbit_pos; |
|
|
|
let orbit_to_ship = position.pos - orbit_pos; |
|
|
|
let ship_to_target = target_pos - position.pos; |
|
|
|
|
|
|
|
let r_ship = orbit_to_ship.length_sqr(); |
|
|
|
let r_target = orbit_to_target.length_sqr(); |
|
|
|
|
|
|
|
let orbit_min = fleet.orbit_min; |
|
|
|
let orbit_max = fleet.orbit_max; |
|
|
|
|
|
|
|
let target_in_orbit = (r_target <= sqr(fleet.orbit_max)) && (r_target >= sqr(orbit_min)); |
|
|
|
let ship_in_orbit = r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * orbit_max); |
|
|
|
|
|
|
|
let need_update = self.need_update.contains(entity.id()); |
|
|
|
let has_target = target_dir.length_sqr() != 0.0; |
|
|
|
let passed_target = target_dir * ship_to_target <= 0.0; |
|
|
|
|
|
|
|
/* check and update target posistion */ |
|
|
|
if need_update || !has_target || passed_target || ship_in_orbit != target_in_orbit { |
|
|
|
let target_pos = if ship_in_orbit && fleet.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 |
|
|
|
/ fleet.orbit_max; |
|
|
|
let radius = |
|
|
|
fleet.orbit_min + (fleet.orbit_max - fleet.orbit_min) * random::<f32>(); |
|
|
|
|
|
|
|
Vector2f::new( |
|
|
|
orbit_pos.x + radius * angle.cos(), |
|
|
|
orbit_pos.y + radius * angle.sin(), |
|
|
|
) |
|
|
|
} else { |
|
|
|
orbit_pos |
|
|
|
}; |
|
|
|
|
|
|
|
ship.target_pos = target_pos; |
|
|
|
ship.target_dir = (target_pos - ship_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 * self.delta); |
|
|
|
|
|
|
|
velocity.dir = Vector2f::new(m.axis_y.x, m.axis_y.y); |
|
|
|
} |
|
|
|
} |
|
|
|
} |