| @@ -142,15 +142,15 @@ impl<'a> System<'a> for FleetInfoUpdate { | |||||
| None => false, | None => false, | ||||
| Some(modified) => match (modified.old_fleet_id, modified.old_player_id) { | Some(modified) => match (modified.old_fleet_id, modified.old_player_id) { | ||||
| (Some(old_fleet_id), Some(old_player_id)) => { | (Some(old_fleet_id), Some(old_player_id)) => { | ||||
| old_fleet_id != fleet_id && old_player_id == player_id | |||||
| old_fleet_id == fleet_id && old_player_id == player_id | |||||
| } | } | ||||
| (Some(old_fleet_id), None) => { | (Some(old_fleet_id), None) => { | ||||
| old_fleet_id != fleet_id && player_owned.owner == player_id | |||||
| old_fleet_id == fleet_id && player_owned.owner == player_id | |||||
| } | } | ||||
| (None, Some(old_player_id)) => { | (None, Some(old_player_id)) => { | ||||
| fleet_owned.owner != fleet_id && old_player_id == player_id | |||||
| fleet_owned.owner == fleet_id && old_player_id == player_id | |||||
| } | } | ||||
| (None, None) => false, | |||||
| (None, None) => unreachable!(), | |||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -3,7 +3,7 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||||
| use crate::{ | use crate::{ | ||||
| components::Player, | components::Player, | ||||
| resources::Global, | resources::Global, | ||||
| systems::{Fleets, Movement, Process}, | |||||
| systems::{Movement, Process, Ships}, | |||||
| }; | }; | ||||
| pub struct Dispatcher<'a, 'b> { | pub struct Dispatcher<'a, 'b> { | ||||
| @@ -19,7 +19,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(Process::default(), "process", &[]) | .with(Process::default(), "process", &[]) | ||||
| .with(Movement::default(), "movement", &[]) | .with(Movement::default(), "movement", &[]) | ||||
| .with(Fleets::default(), "fleets", &[]) | |||||
| .with(Ships::new(world), "ships", &[]) | |||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -1,134 +0,0 @@ | |||||
| 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::<f32>(); | |||||
| 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); | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,7 @@ | |||||
| mod fleets; | |||||
| mod movement; | mod movement; | ||||
| mod process; | mod process; | ||||
| mod ships; | |||||
| pub use fleets::Fleets; | |||||
| pub use movement::Movement; | pub use movement::Movement; | ||||
| pub use process::Process; | pub use process::Process; | ||||
| pub use ships::Ships; | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| } | |||||