From a1a30ecf96a1f20fe692458689a47f00456b3095 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Tue, 19 Jan 2021 16:18:34 +0100 Subject: [PATCH] Refactored ship movement Split ship movement in several systems: - ShipControl to handle events (mostly state transitions) of single ships - ShipsMovement to handle the basic movement of the ship (this will update the position of the ship) - ShipsMoving to handle ships that are in the 'moving' state - ShipsOrbiting to handle ships that are in the 'orbiting' state --- glc/src/angle.rs | 9 + overview | 53 ++-- space-crush-app/src/debug/meeting_points.rs | 12 +- space-crush-app/src/debug/ships.rs | 49 ++- space-crush-app/src/main.rs | 2 +- space-crush-app/src/render/fleet_move.rs | 2 - space-crush-app/src/render/fleet_select.rs | 5 +- space-crush-common/src/builder/fleet.rs | 76 ++++- .../src/builder/meeting_point.rs | 65 ++++ space-crush-common/src/builder/mod.rs | 2 + space-crush-common/src/builder/ship.rs | 4 +- space-crush-common/src/components/fleet.rs | 59 ++++ .../src/components/meeting_point.rs | 7 +- space-crush-common/src/components/mod.rs | 6 +- space-crush-common/src/components/player.rs | 5 + space-crush-common/src/components/ship.rs | 81 +++-- space-crush-common/src/constants.rs | 62 ++-- space-crush-common/src/dispatcher.rs | 11 +- space-crush-common/src/misc/world.rs | 4 +- .../src/systems/fleet_control.rs | 206 +++++++----- .../src/systems/fleet_owned_update.rs | 11 +- space-crush-common/src/systems/mod.rs | 12 +- .../src/systems/ship_control.rs | 166 ++++++++++ .../src/systems/ship_movement.rs | 47 --- space-crush-common/src/systems/ships.rs | 297 ------------------ .../src/systems/ships_movement.rs | 82 +++++ .../src/systems/ships_moving.rs | 239 ++++++++++++++ .../src/systems/ships_orbiting.rs | 138 ++++++++ 28 files changed, 1170 insertions(+), 542 deletions(-) create mode 100644 space-crush-common/src/builder/meeting_point.rs create mode 100644 space-crush-common/src/systems/ship_control.rs delete mode 100644 space-crush-common/src/systems/ship_movement.rs delete mode 100644 space-crush-common/src/systems/ships.rs create mode 100644 space-crush-common/src/systems/ships_movement.rs create mode 100644 space-crush-common/src/systems/ships_moving.rs create mode 100644 space-crush-common/src/systems/ships_orbiting.rs diff --git a/glc/src/angle.rs b/glc/src/angle.rs index 124d586..468458c 100644 --- a/glc/src/angle.rs +++ b/glc/src/angle.rs @@ -100,6 +100,15 @@ impl_op_ex!(/ |a: &Angle, b: &T| -> Angle { a._mul(T::one() / impl_op_ex!(+= |a: &mut Angle, b: &Angle| { *a = a._add(b); }); impl_op_ex!(-= |a: &mut Angle, b: &Angle| { *a = a._add(&b._neg()); }); +impl Default for Angle +where + T: Numeric, +{ + fn default() -> Self { + Self::Deg(T::zero()) + } +} + impl PartialEq> for Angle where T: Numeric + PartialEq, diff --git a/overview b/overview index ddd9393..a1bef41 100644 --- a/overview +++ b/overview @@ -1,28 +1,31 @@ x -> needed o -> optional -___________________________________________________________ -WorldPersistence________________________________________ \ -Planet_______________________________________________ \ \ -Asteroid__________________________________________ \ \ \ -MeetingPoint___________________________________ \ \ \ \ -Obstacle____________________________________ \ \ \ \ \ -Shape____________________________________ \ \ \ \ \ \ -MeetingPointOwned_____________________ \ \ \ \ \ \ \ -Fleet______________________________ \ \ \ \ \ \ \ \ -Ship____________________________ \ \ \ \ \ \ \ \ \ -FleetOwned___________________ \ \ \ \ \ \ \ \ \ \ -Position__________________ \ \ \ \ \ \ \ \ \ \ \ -PlayerOwned____________ \ \ \ \ \ \ \ \ \ \ \ \ -Player______________ \ \ \ \ \ \ \ \ \ \ \ \ \ - \ \ \ \ \ \ \ \ \ \ \ \ \ \ -+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+ -| Game Objects | | | | | | | | | | | | | | -+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+ -| Player | x | | | | | | | | | | | | x | -| Ship | | x | x | x | x | | | | | | | | x | -| Fleet | | x | | | | x | x | | | | | | x | -| Asteroid | | o | x | | | | | x | x | x | x | | x | -| Planet | | o | x | | | | | x | x | x | | x | x | -| MeetingPoint | | | x | | | | | | | x | | | x | -+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+ +____________________________________________________________________ +WorldPersistence_________________________________________________ \ +Planet________________________________________________________ \ \ +Asteroid___________________________________________________ \ \ \ +MeetingPoint____________________________________________ \ \ \ \ +Obstacle_____________________________________________ \ \ \ \ \ +Shape_____________________________________________ \ \ \ \ \ \ +FleetOrbiting__________________________________ \ \ \ \ \ \ \ +FleetMoving_________________________________ \ \ \ \ \ \ \ \ +Fleet____________________________________ \ \ \ \ \ \ \ \ \ +ShipOrbiting__________________________ \ \ \ \ \ \ \ \ \ \ +ShipMoving_________________________ \ \ \ \ \ \ \ \ \ \ \ +Ship____________________________ \ \ \ \ \ \ \ \ \ \ \ \ +FleetOwned___________________ \ \ \ \ \ \ \ \ \ \ \ \ \ +Position__________________ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +PlayerOwned____________ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +Player______________ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ + \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ++--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +| Game Objects | | | | | | | | | | | | | | | | | ++--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +| Player | x | | | | | | | | | | | | | | | x | +| Ship | | x | x | x | x | o | o | | | | | | | | | x | +| Fleet | | x | | | | | | x | o | o | | | | | | x | +| Asteroid | | o | x | | | | | | | | x | x | x | x | | x | +| Planet | | o | x | | | | | | | | x | x | x | | x | x | +| MeetingPoint | | | x | | | | | | | | | | x | | | x | ++--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ diff --git a/space-crush-app/src/debug/meeting_points.rs b/space-crush-app/src/debug/meeting_points.rs index 03ef957..fd710ac 100644 --- a/space-crush-app/src/debug/meeting_points.rs +++ b/space-crush-app/src/debug/meeting_points.rs @@ -1,8 +1,5 @@ use glc::vector::{Angle, Vector2f, Vector4f}; -use space_crush_common::{ - components::{MeetingPoint, Position}, - constants::SHIP_ORBIT_DISTANCE_MAX, -}; +use space_crush_common::components::{MeetingPoint, Position}; use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; use crate::resources::Geometry; @@ -40,13 +37,6 @@ impl<'a> System<'a> for MeetingPoints { Vector4f::new(0.5, 0.5, 0.5, 0.05), &create_circle(position.get(), meeting_point.max()), ); - geometry.render_lines( - Vector4f::new(0.5, 0.5, 0.5, 0.05), - &create_circle( - position.get(), - SHIP_ORBIT_DISTANCE_MAX * meeting_point.max(), - ), - ); } gl::blend_equation(gl::FUNC_ADD); diff --git a/space-crush-app/src/debug/ships.rs b/space-crush-app/src/debug/ships.rs index 596d495..e5a4d41 100644 --- a/space-crush-app/src/debug/ships.rs +++ b/space-crush-app/src/debug/ships.rs @@ -1,5 +1,7 @@ use glc::vector::Vector4f; -use space_crush_common::components::{Player, PlayerOwned, Position, Ship, ShipObstacle}; +use space_crush_common::components::{ + Player, PlayerOwned, Position, Ship, ShipMoving, ShipObstacle, ShipOrbiting, +}; use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; use crate::resources::Geometry; @@ -14,6 +16,8 @@ pub struct ShipsData<'a> { player_owned: ReadStorage<'a, PlayerOwned>, players: ReadStorage<'a, Player>, ships: ReadStorage<'a, Ship>, + ships_orbiting: ReadStorage<'a, ShipOrbiting>, + ships_moving: ReadStorage<'a, ShipMoving>, } impl<'a> System<'a> for Ships { @@ -26,12 +30,22 @@ impl<'a> System<'a> for Ships { player_owned, players, ships, + ships_orbiting, + ships_moving, } = data; gl::enable(gl::BLEND); gl::blend_func(gl::SRC_ALPHA, gl::ONE); - for (position, player_owned, ship) in (&positions, &player_owned, &ships).join() { + let data = ( + &positions, + &player_owned, + &ships, + ships_orbiting.maybe(), + ships_moving.maybe(), + ); + + for (position, player_owned, ship, ship_orbiting, ship_moving) in data.join() { let ship_pos = position.get(); let type_ = ship.type_(); let player_id = player_owned.owner(); @@ -42,22 +56,27 @@ impl<'a> System<'a> for Ships { Vector4f::new(0.0, 0.0, 1.0, 0.2), &[*ship_pos, ship_pos + ship.dir() * ship_data.speed], ); - geometry.render_lines( - Vector4f::new(1.0, 0.0, 0.0, 0.2), - &[*ship_pos, ship_pos + ship.target_dir() * 100.0], - ); - geometry.render_lines( - Vector4f::new(1.0, 1.0, 1.0, 0.2), - &[*ship_pos, *ship.target_pos()], - ); - - if let ShipObstacle::Known(obstacle) = ship.obstacle() { - let obstacle_pos = positions.get(obstacle).unwrap().get(); + if let Some(ship_orbiting) = &ship_orbiting { geometry.render_lines( - Vector4f::new(0.0, 1.0, 0.0, 0.2), - &[*ship_pos, *obstacle_pos], + Vector4f::new(1.0, 0.0, 0.0, 0.2), + &[*ship_pos, ship_pos + ship_orbiting.target_dir() * 100.0], ); + geometry.render_lines( + Vector4f::new(1.0, 1.0, 1.0, 0.2), + &[*ship_pos, *ship_orbiting.target_pos()], + ); + } + + if let Some(ship_moving) = ship_moving { + if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() { + let obstacle_pos = positions.get(*obstacle_id).unwrap().get(); + + geometry.render_lines( + Vector4f::new(0.0, 1.0, 0.0, 0.2), + &[*ship_pos, *obstacle_pos], + ); + } } } diff --git a/space-crush-app/src/main.rs b/space-crush-app/src/main.rs index c0c264a..bc136cb 100644 --- a/space-crush-app/src/main.rs +++ b/space-crush-app/src/main.rs @@ -94,7 +94,7 @@ fn create_world(world: &mut World, player_id: Entity) { let fleet_id = Fleet::builder() .player(player_id) - .meeting_point(planets[1]) + .orbiting(planets[1]) .build(world) .unwrap(); diff --git a/space-crush-app/src/render/fleet_move.rs b/space-crush-app/src/render/fleet_move.rs index afac336..d138676 100644 --- a/space-crush-app/src/render/fleet_move.rs +++ b/space-crush-app/src/render/fleet_move.rs @@ -84,9 +84,7 @@ impl<'a> System<'a> for FleetMove { Some(target) => target, None => continue, }; - let player = game_state.player_id(); let event = FleetControlEvent::MoveToMeetingPoint { - player, fleet, count, target, diff --git a/space-crush-app/src/render/fleet_select.rs b/space-crush-app/src/render/fleet_select.rs index 6cb4415..3d5879d 100644 --- a/space-crush-app/src/render/fleet_select.rs +++ b/space-crush-app/src/render/fleet_select.rs @@ -11,7 +11,6 @@ use glc::{ use shrev::{EventChannel, ReaderId}; use space_crush_common::{ components::{Fleet, FleetOrbiting, MeetingPoint, Position, Shape, ShipCount}, - constants::VECTOR_2F_POS_X, misc::{LogResult, WorldHelper as _}, resources::Global, }; @@ -260,10 +259,12 @@ impl FleetSelect { self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom; self.ring1 = self.ring0 + FLEET_SELECT_WIDTH / self.zoom; + const VECTOR2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0); + let is_simple = self.select_mode.is_simple(); let angle = self .marker - .angle2(&VECTOR_2F_POS_X) + .angle2(&VECTOR2F_POS_X) .normalize() .into_deg() .into_inner(); diff --git a/space-crush-common/src/builder/fleet.rs b/space-crush-common/src/builder/fleet.rs index d06ba68..4e3b91b 100644 --- a/space-crush-common/src/builder/fleet.rs +++ b/space-crush-common/src/builder/fleet.rs @@ -1,7 +1,10 @@ -use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; +use specs::{ + saveload::MarkedBuilder, world::EntitiesRes, Builder, Entity, LazyUpdate, World, WorldExt, + WriteStorage, +}; use crate::{ - components::{Fleet, FleetOrbiting, PlayerOwned}, + components::{Fleet, FleetMoving, FleetOrbiting, PlayerOwned}, misc::{Persistence, WorldPersistence}, }; @@ -10,28 +13,71 @@ use super::Error; #[derive(Default, Clone)] pub struct FleetBuilder { player: Option, - meeting_point: Option, + state: Option, +} + +#[derive(Clone)] +enum State { + Orbiting(Entity), + Moving(Entity), } impl FleetBuilder { pub fn build(&self, world: &mut World) -> Result { let player = self.player.ok_or(Error::MissingValue("player"))?; - let meeting_point = self - .meeting_point - .ok_or(Error::MissingValue("meeting_point"))?; + let state = self.state.as_ref().ok_or(Error::MissingValue("state"))?; let player_owned = PlayerOwned::new(player); - let fleet_orbiting = FleetOrbiting::new(meeting_point); let fleet = Fleet::new(); - let entity = world + let builder = world .create_entity() .marked::<::Marker>() .with(player_owned) - .with(fleet_orbiting) - .with(fleet) + .with(fleet); + + let builder = match state { + State::Moving(target) => builder.with(FleetMoving::new(*target)), + State::Orbiting(meeting_point) => builder.with(FleetOrbiting::new(*meeting_point)), + }; + + let entity = builder.build(); + + Ok(entity) + } + + pub fn lazy( + &self, + lazy: &LazyUpdate, + entities: &EntitiesRes, + players_owned: &mut WriteStorage<'_, PlayerOwned>, + fleets: &mut WriteStorage<'_, Fleet>, + fleets_moving: &mut WriteStorage<'_, FleetMoving>, + fleets_orbiting: &mut WriteStorage<'_, FleetOrbiting>, + ) -> Result { + let player = self.player.ok_or(Error::MissingValue("player"))?; + let state = self.state.as_ref().ok_or(Error::MissingValue("state"))?; + + let player_owned = PlayerOwned::new(player); + let fleet = Fleet::new(); + + let entity = lazy + .create_entity(entities) + .marked::<::Marker>() .build(); + players_owned.insert(entity, player_owned)?; + fleets.insert(entity, fleet)?; + + match state { + State::Moving(target) => { + fleets_moving.insert(entity, FleetMoving::new(*target))?; + } + State::Orbiting(meeting_point) => { + fleets_orbiting.insert(entity, FleetOrbiting::new(*meeting_point))?; + } + }; + Ok(entity) } @@ -41,8 +87,14 @@ impl FleetBuilder { self } - pub fn meeting_point(mut self, meeting_point: Entity) -> Self { - self.meeting_point = Some(meeting_point); + pub fn orbiting(mut self, meeting_point: Entity) -> Self { + self.state = Some(State::Orbiting(meeting_point)); + + self + } + + pub fn moving(mut self, target: Entity) -> Self { + self.state = Some(State::Moving(target)); self } diff --git a/space-crush-common/src/builder/meeting_point.rs b/space-crush-common/src/builder/meeting_point.rs new file mode 100644 index 0000000..e9342fb --- /dev/null +++ b/space-crush-common/src/builder/meeting_point.rs @@ -0,0 +1,65 @@ +use specs::{ + saveload::MarkedBuilder, world::EntitiesRes, Builder, Entity, LazyUpdate, World, WorldExt, + WriteStorage, +}; + +use glc::vector::Vector2f; + +use crate::{ + components::{MeetingPoint, Position}, + misc::{Persistence, WorldPersistence}, +}; + +use super::Error; + +#[derive(Default, Clone)] +pub struct MeetingPointBuilder { + position: Option, +} + +impl MeetingPointBuilder { + pub fn build(&self, world: &mut World) -> Result { + let position = self.position.ok_or(Error::MissingValue("position"))?; + + let position = Position::new(position); + let meeting_point = MeetingPoint::new(0.0, 10.0); + + let entity = world + .create_entity() + .marked::<::Marker>() + .with(position) + .with(meeting_point) + .build(); + + Ok(entity) + } + + pub fn lazy( + &self, + lazy: &LazyUpdate, + entities: &EntitiesRes, + positions: &mut WriteStorage<'_, Position>, + meeting_points: &mut WriteStorage<'_, MeetingPoint>, + ) -> Result { + let position = self.position.ok_or(Error::MissingValue("position"))?; + + let position = Position::new(position); + let meeting_point = MeetingPoint::new(0.0, 10.0); + + let entity = lazy + .create_entity(entities) + .marked::<::Marker>() + .build(); + + positions.insert(entity, position)?; + meeting_points.insert(entity, meeting_point)?; + + Ok(entity) + } + + pub fn position>(mut self, value: V) -> Self { + self.position = Some(value.into()); + + self + } +} diff --git a/space-crush-common/src/builder/mod.rs b/space-crush-common/src/builder/mod.rs index 9ff845f..7cb98e7 100644 --- a/space-crush-common/src/builder/mod.rs +++ b/space-crush-common/src/builder/mod.rs @@ -1,11 +1,13 @@ mod asteroid; mod fleet; +mod meeting_point; mod planet; mod player; mod ship; pub use asteroid::AsteroidBuilder; pub use fleet::FleetBuilder; +pub use meeting_point::MeetingPointBuilder; pub use planet::PlanetBuilder; pub use player::PlayerBuilder; pub use ship::ShipBuilder; diff --git a/space-crush-common/src/builder/ship.rs b/space-crush-common/src/builder/ship.rs index 00bccbb..007976b 100644 --- a/space-crush-common/src/builder/ship.rs +++ b/space-crush-common/src/builder/ship.rs @@ -3,7 +3,7 @@ use rand::random; use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; use crate::{ - components::{FleetOwned, PlayerOwned, Position, Ship, ShipType}, + components::{FleetOwned, PlayerOwned, Position, Ship, ShipOrbiting, ShipType}, misc::{Persistence, WorldPersistence}, }; @@ -30,6 +30,7 @@ impl ShipBuilder { let fleet_owned = FleetOwned::new(fleet); let position = Position::new(position); let ship = Ship::new(type_, direction); + let ship_orbiting = ShipOrbiting::new(); let entity = world .create_entity() @@ -38,6 +39,7 @@ impl ShipBuilder { .with(fleet_owned) .with(position) .with(ship) + .with(ship_orbiting) .build(); Ok(entity) diff --git a/space-crush-common/src/components/fleet.rs b/space-crush-common/src/components/fleet.rs index 989844d..35afef3 100644 --- a/space-crush-common/src/components/fleet.rs +++ b/space-crush-common/src/components/fleet.rs @@ -30,6 +30,11 @@ pub struct FleetOrbiting { meeting_point: Entity, } +#[derive(Debug, Clone)] +pub struct FleetMoving { + target: Entity, +} + #[derive(Serialize, Deserialize)] pub struct FleetOwnedData { pub owner: M, @@ -40,6 +45,11 @@ pub struct FleetOrbitingData { pub meeting_point: M, } +#[derive(Serialize, Deserialize)] +pub struct FleetMovingData { + pub target: M, +} + /* Fleet */ impl Fleet { @@ -57,6 +67,11 @@ impl Fleet { pub fn count(&self) -> &ShipCount { &self.count } + + #[inline] + pub fn is_empty(&self) -> bool { + self.count.total() == 0 + } } impl Fleet { @@ -177,3 +192,47 @@ where Ok(FleetOrbiting { meeting_point }) } } + +/* FleetMoving */ + +impl FleetMoving { + pub fn target(&self) -> Entity { + self.target + } +} + +impl FleetMoving { + pub(crate) fn new(target: Entity) -> Self { + Self { target } + } +} + +impl Component for FleetMoving { + type Storage = FlaggedStorage>; +} + +impl ConvertSaveload for FleetMoving +where + for<'de> M: Marker + Serialize + Deserialize<'de>, +{ + type Data = FleetMovingData; + type Error = NoError; + + fn convert_into(&self, mut ids: F) -> Result + where + F: FnMut(Entity) -> Option, + { + let target = ids(self.target).unwrap(); + + Ok(FleetMovingData { target }) + } + + fn convert_from(data: Self::Data, mut ids: F) -> Result + where + F: FnMut(M) -> Option, + { + let target = ids(data.target).unwrap(); + + Ok(FleetMoving { target }) + } +} diff --git a/space-crush-common/src/components/meeting_point.rs b/space-crush-common/src/components/meeting_point.rs index 2bdf3de..69c13e2 100644 --- a/space-crush-common/src/components/meeting_point.rs +++ b/space-crush-common/src/components/meeting_point.rs @@ -7,7 +7,7 @@ use specs::{ Component, Entity, HashMapStorage, }; -use crate::misc::FlaggedStorage; +use crate::{builder::MeetingPointBuilder, misc::FlaggedStorage}; /// Point in the universe fleets can meet #[derive(Clone, Debug, Default)] @@ -40,6 +40,11 @@ type Fleets = SmallVec<[Option; 8]>; /* MeetingPoint */ impl MeetingPoint { + #[inline] + pub fn builder() -> MeetingPointBuilder { + MeetingPointBuilder::default() + } + #[inline] pub fn min(&self) -> f32 { self.min diff --git a/space-crush-common/src/components/mod.rs b/space-crush-common/src/components/mod.rs index bc58b33..b8a7d59 100644 --- a/space-crush-common/src/components/mod.rs +++ b/space-crush-common/src/components/mod.rs @@ -9,11 +9,13 @@ mod shape; mod ship; pub use asteroid::{Asteroid, Type as AsteroidType}; -pub use fleet::{Fleet, FleetOrbiting, FleetOwned}; +pub use fleet::{Fleet, FleetMoving, FleetOrbiting, FleetOwned}; pub use meeting_point::MeetingPoint; pub use obstacle::Obstacle; pub use planet::Planet; pub use player::{Player, PlayerOwned, Race}; pub use position::Position; pub use shape::Shape; -pub use ship::{Ship, ShipCount, ShipData, ShipObstacle, ShipType, ShipsData}; +pub use ship::{ + Ship, ShipCount, ShipData, ShipMoving, ShipObstacle, ShipOrbiting, ShipType, ShipsData, +}; diff --git a/space-crush-common/src/components/player.rs b/space-crush-common/src/components/player.rs index 6410eb0..834e09f 100644 --- a/space-crush-common/src/components/player.rs +++ b/space-crush-common/src/components/player.rs @@ -61,6 +61,11 @@ impl Player { pub fn ship_data(&self, type_: ShipType) -> &ShipData { &self.ships_data[type_] } + + #[inline] + pub fn ships_data(&self) -> &ShipsData { + &self.ships_data + } } impl Player { diff --git a/space-crush-common/src/components/ship.rs b/space-crush-common/src/components/ship.rs index 8afe015..9f6b197 100644 --- a/space-crush-common/src/components/ship.rs +++ b/space-crush-common/src/components/ship.rs @@ -1,7 +1,7 @@ use std::cmp::min; use std::ops::{Index, IndexMut, Mul}; -use glc::vector::Vector2f; +use glc::vector::{Angle, Vector2f}; use serde::{Deserialize, Serialize}; use specs::{Component, Entity, VecStorage}; @@ -11,13 +11,18 @@ use crate::{builder::ShipBuilder, misc::FlaggedStorage}; pub struct Ship { type_: ShipType, dir: Vector2f, +} +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ShipMoving { #[serde(skip)] obstacle: ShipObstacle, +} +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ShipOrbiting { #[serde(skip)] target_pos: Vector2f, - #[serde(skip)] target_dir: Vector2f, } @@ -46,6 +51,7 @@ pub struct ShipsData { #[derive(Clone, Default, Debug)] pub struct ShipData { pub speed: f32, + pub agility: Angle, } #[derive(Copy, Clone, Debug, Serialize, Deserialize)] @@ -72,30 +78,65 @@ impl Ship { pub fn dir(&self) -> &Vector2f { &self.dir } +} +impl Ship { #[inline] - pub fn target_pos(&self) -> &Vector2f { - &self.target_pos + pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { + Self { type_, dir } } #[inline] - pub fn target_dir(&self) -> &Vector2f { - &self.target_dir + pub(crate) fn dir_mut(&mut self) -> &mut Vector2f { + &mut self.dir } +} + +impl Component for Ship { + type Storage = FlaggedStorage>; +} + +/* ShipMoving */ + +impl ShipMoving { + pub fn obstacle(&self) -> &ShipObstacle { + &self.obstacle + } +} +impl ShipMoving { #[inline] - pub fn obstacle(&self) -> ShipObstacle { - self.obstacle + pub(crate) fn new() -> Self { + Self { + obstacle: ShipObstacle::Search, + } + } + + pub(crate) fn obstacle_mut(&mut self) -> &mut ShipObstacle { + &mut self.obstacle } } -impl Ship { +impl Component for ShipMoving { + type Storage = VecStorage; +} + +/* ShipOrbiting */ + +impl ShipOrbiting { + pub fn target_pos(&self) -> &Vector2f { + &self.target_pos + } + + pub fn target_dir(&self) -> &Vector2f { + &self.target_dir + } +} + +impl ShipOrbiting { #[inline] - pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { + pub(crate) fn new() -> Self { Self { - type_, - dir, - obstacle: Default::default(), target_pos: Default::default(), target_dir: Default::default(), } @@ -106,20 +147,10 @@ impl Ship { self.target_pos = pos; self.target_dir = dir; } - - #[inline] - pub(crate) fn dir_mut(&mut self) -> &mut Vector2f { - &mut self.dir - } - - #[inline] - pub(crate) fn obstacle_mut(&mut self) -> &mut ShipObstacle { - &mut self.obstacle - } } -impl Component for Ship { - type Storage = FlaggedStorage>; +impl Component for ShipOrbiting { + type Storage = VecStorage; } /* Obstacle */ diff --git a/space-crush-common/src/constants.rs b/space-crush-common/src/constants.rs index 08cea06..09d104f 100644 --- a/space-crush-common/src/constants.rs +++ b/space-crush-common/src/constants.rs @@ -1,35 +1,61 @@ -use glc::{matrix::Angle, vector::Vector2f}; +use glc::matrix::Angle; use crate::components::{ShipData, ShipsData}; -/// Distance to orbit before ship is handled as "in orbit" in % -pub const SHIP_ORBIT_DISTANCE_MAX: f32 = 1.10; +/* Constants for ships in orbit */ -/// Minimum angle between old and new target position in orbit pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle = Angle::Deg(5000.0); - -/// Random angle between old and new target position in orbit pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle = Angle::Deg(5000.0); +pub const SHIP_ORBIT_DISTANCE_MAX: f32 = 1.10; // % -/// Agility of ships inside orbit -pub const SHIP_ORBIT_AGILITY: Angle = Angle::Deg(90.0); +pub const SHIP_DATA_IN_ORBIT: ShipData = ShipData { + speed: 100.0, + agility: Angle::Deg(60.0), +}; -pub const VECTOR_2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0); +/* Constants for different ship types */ pub const SHIPS_DATA_FIGHTER: ShipsData = ShipsData { - fighter: ShipData { speed: 120.0 }, - bomber: ShipData { speed: 80.0 }, - transporter: ShipData { speed: 100.0 }, + fighter: ShipData { + speed: 120.0, + agility: Angle::Deg(120.0), + }, + bomber: ShipData { + speed: 80.0, + agility: Angle::Deg(80.0), + }, + transporter: ShipData { + speed: 100.0, + agility: Angle::Deg(100.0), + }, }; pub const SHIPS_DATA_TRADER: ShipsData = ShipsData { - fighter: ShipData { speed: 100.0 }, - bomber: ShipData { speed: 70.0 }, - transporter: ShipData { speed: 120.0 }, + fighter: ShipData { + speed: 100.0, + agility: Angle::Deg(100.0), + }, + bomber: ShipData { + speed: 70.0, + agility: Angle::Deg(70.0), + }, + transporter: ShipData { + speed: 120.0, + agility: Angle::Deg(120.0), + }, }; pub const SHIPS_DATA_RESEARCHER: ShipsData = ShipsData { - fighter: ShipData { speed: 90.0 }, - bomber: ShipData { speed: 60.0 }, - transporter: ShipData { speed: 100.0 }, + fighter: ShipData { + speed: 90.0, + agility: Angle::Deg(90.0), + }, + bomber: ShipData { + speed: 60.0, + agility: Angle::Deg(60.0), + }, + transporter: ShipData { + speed: 100.0, + agility: Angle::Deg(100.0), + }, }; diff --git a/space-crush-common/src/dispatcher.rs b/space-crush-common/src/dispatcher.rs index dff3d6e..2c8c6ef 100644 --- a/space-crush-common/src/dispatcher.rs +++ b/space-crush-common/src/dispatcher.rs @@ -3,7 +3,10 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; use crate::{ components::Player, resources::Global, - systems::{FleetControl, FleetOrbitingUpdate, FleetOwnedUpdate, Process, ShipMovement, Ships}, + systems::{ + FleetControl, FleetOrbitingUpdate, FleetOwnedUpdate, Process, ShipControl, ShipsMovement, + ShipsMoving, ShipsOrbiting, + }, Error, }; @@ -19,8 +22,10 @@ impl<'a, 'b> Dispatcher<'a, 'b> { let mut dispatcher = DispatcherBuilder::new() .with(Process::default(), "process", &[]) - .with(ShipMovement::default(), "ship_movement", &[]) - .with(Ships::new(world), "ships", &[]) + .with(ShipControl::new(world)?, "ship_control", &[]) + .with(ShipsOrbiting::default(), "ships_orbiting", &[]) + .with(ShipsMoving::default(), "ships_moving", &[]) + .with(ShipsMovement::default(), "ships_movement", &[]) .with(FleetControl::new(world)?, "fleet_control", &[]) .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) .with(FleetOrbitingUpdate::new(world), "fleet_orbiting", &[]) diff --git a/space-crush-common/src/misc/world.rs b/space-crush-common/src/misc/world.rs index fb28876..d0eb7fc 100644 --- a/space-crush-common/src/misc/world.rs +++ b/space-crush-common/src/misc/world.rs @@ -8,7 +8,7 @@ use specs::{saveload::SimpleMarker, World}; use crate::{ components::{ Asteroid, FleetOrbiting, FleetOwned, MeetingPoint, Obstacle, Planet, Player, PlayerOwned, - Position, Ship, + Position, Ship, ShipMoving, ShipOrbiting, }, Error, }; @@ -96,6 +96,8 @@ impl Persistence for WorldPersistence { FleetOwned, FleetOrbiting, Ship, + ShipMoving, + ShipOrbiting, Obstacle, MeetingPoint, Planet, diff --git a/space-crush-common/src/systems/fleet_control.rs b/space-crush-common/src/systems/fleet_control.rs index 67eab80..7985c19 100644 --- a/space-crush-common/src/systems/fleet_control.rs +++ b/space-crush-common/src/systems/fleet_control.rs @@ -1,9 +1,4 @@ -// TODO -#![allow(dead_code)] -#![allow(unused_mut)] -#![allow(unused_imports)] -#![allow(unused_variables)] - +use glc::vector::Vector2f; use shrev::{EventChannel, ReaderId}; use specs::{ prelude::*, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, System, World, WriteStorage, @@ -11,9 +6,11 @@ use specs::{ use crate::{ components::{ - Fleet, FleetOrbiting, FleetOwned, MeetingPoint, Player, PlayerOwned, Ship, ShipCount, + Fleet, FleetMoving, FleetOrbiting, MeetingPoint, PlayerOwned, Position, Ship, ShipCount, + ShipType, }, - misc::WorldHelper, + misc::{LogResult, WorldHelper}, + systems::ShipControlEvent, Error, }; @@ -24,24 +21,50 @@ pub struct FleetControl { #[derive(Debug)] pub enum FleetControlEvent { MoveToMeetingPoint { - player: Entity, fleet: Entity, count: ShipCount, target: Entity, }, + MoveToPosition { + fleet: Entity, + count: ShipCount, + target: Vector2f, + }, } #[derive(SystemData)] pub struct FleetControlData<'a> { + fleet_control_events: ReadExpect<'a, EventChannel>, + lazy: Read<'a, LazyUpdate>, entities: Entities<'a>, - fleet_control_events: ReadExpect<'a, EventChannel>, player_owned: WriteStorage<'a, PlayerOwned>, - fleet_orbiting: WriteStorage<'a, FleetOrbiting>, - fleet_owned: WriteStorage<'a, FleetOwned>, + ship_control: WriteExpect<'a, EventChannel>, + + fleets: WriteStorage<'a, Fleet>, + fleets_moving: WriteStorage<'a, FleetMoving>, + fleets_orbiting: WriteStorage<'a, FleetOrbiting>, + + positions: WriteStorage<'a, Position>, meeting_points: WriteStorage<'a, MeetingPoint>, + + ships: ReadStorage<'a, Ship>, +} + +struct Processor<'a> { + lazy: Read<'a, LazyUpdate>, + + entities: Entities<'a>, + player_owned: WriteStorage<'a, PlayerOwned>, + ship_control: WriteExpect<'a, EventChannel>, + fleets: WriteStorage<'a, Fleet>, - players: ReadStorage<'a, Player>, + fleets_moving: WriteStorage<'a, FleetMoving>, + fleets_orbiting: WriteStorage<'a, FleetOrbiting>, + + positions: WriteStorage<'a, Position>, + meeting_points: WriteStorage<'a, MeetingPoint>, + ships: ReadStorage<'a, Ship>, } @@ -62,77 +85,114 @@ impl<'a> System<'a> for FleetControl { fn run(&mut self, data: Self::SystemData) { let FleetControlData { + fleet_control_events, lazy, entities, - fleet_control_events, - mut player_owned, - mut fleet_orbiting, - mut fleet_owned, - mut meeting_points, - mut fleets, - players, + player_owned, + ship_control, + fleets, + fleets_moving, + fleets_orbiting, + positions, + meeting_points, ships, } = data; - /* TODO + let mut processor = Processor { + lazy, + entities, + player_owned, + ship_control, + fleets, + fleets_moving, + fleets_orbiting, + positions, + meeting_points, + ships, + }; + let events = fleet_control_events.read(&mut self.fleet_control_event_id); for event in events { match event { - FleetControlEvent::Move(args) if args.count.is_all() => { - let orbit_owned = continue_if_none!(fleet_orbiting.get_mut(args.fleet)); - orbit_owned.set_owner(args.target); + FleetControlEvent::MoveToPosition { + fleet, + count, + target, + } => { + processor.move_to_position(*fleet, *count, *target); + } + FleetControlEvent::MoveToMeetingPoint { + fleet, + count, + target, + } => { + processor.move_to_meeting_point(*fleet, *count, *target); + } + } + } + } +} + +impl Processor<'_> { + fn move_to_position(&mut self, fleet: Entity, count: ShipCount, target: Vector2f) { + let target = self.create_meeting_point(target); + + self.move_to_meeting_point(fleet, count, target); + } + + fn move_to_meeting_point(&mut self, fleet: Entity, mut count: ShipCount, target: Entity) { + let player_owned = self.player_owned.get(fleet).unwrap(); + let player_id = player_owned.owner(); + let target_fleet = self.create_fleet(player_id, target); + let source_fleet = self.fleets.get(fleet).unwrap(); + + let data = (&self.entities, source_fleet.owned(), &self.ships); + for (id, _, ship) in data.join() { + match ship.type_() { + ShipType::Fighter if count.fighter > 0 => { + count.fighter -= 1; + } + ShipType::Bomber if count.bomber > 0 => { + count.bomber -= 1; } - FleetControlEvent::Move(args) => { - let target_orbit = continue_if_none!(meeting_points.get_mut(args.target)); - let player = continue_if_none!(players.get(args.player)); - let mut count = args.count; - - let target_fleet = match target_orbit.fleet_mut(player.index()) { - Some(fleet) => *fleet, - f @ None => { - let fleet = lazy - .create_entity(&entities) - .marked::<::Marker>() - .build(); - - player_owned - .insert(fleet, PlayerOwned::new(args.player)) - .error("Unable to insert component: PlayerOwned"); - fleet_orbiting - .insert(fleet, MeetingPointOwned::new(args.target)) - .error("Unable to insert component: MeetingPointOwned"); - fleets - .insert(fleet, Fleet::default()) - .error("Unable to insert component: Fleet"); - - *f = Some(fleet); - - fleet - } - }; - - let source_fleet = continue_if_none!(fleets.get(args.fleet)); - let data = (&mut fleet_owned, &ships, source_fleet.owned()); - for (fleet_owned, ship, _) in data.join() { - match ship.type_() { - ShipType::Fighter if count.fighter > 0 => { - count.fighter -= 1; - fleet_owned.set_owner(target_fleet); - } - ShipType::Bomber if count.bomber > 0 => { - count.bomber -= 1; - fleet_owned.set_owner(target_fleet); - } - ShipType::Transporter if count.transporter > 0 => { - count.transporter -= 1; - fleet_owned.set_owner(target_fleet); - } - _ => (), - } - } + ShipType::Transporter if count.transporter > 0 => { + count.transporter -= 1; } + _ => continue, } + + let event = ShipControlEvent::SetMoving { + ship: id, + fleet: target_fleet, + }; + self.ship_control.single_write(event); } - */ + } + + fn create_meeting_point(&mut self, pos: Vector2f) -> Entity { + MeetingPoint::builder() + .position(pos) + .lazy( + &self.lazy, + &self.entities, + &mut self.positions, + &mut self.meeting_points, + ) + .panic("Unable to create new entity: MeetingPoint") + } + + fn create_fleet(&mut self, player: Entity, target: Entity) -> Entity { + Fleet::builder() + .player(player) + .moving(target) + .lazy( + &self.lazy, + &self.entities, + &mut self.player_owned, + &mut self.fleets, + &mut self.fleets_moving, + &mut self.fleets_orbiting, + ) + .panic("Unable to create new entitiy: Fleet") } } diff --git a/space-crush-common/src/systems/fleet_owned_update.rs b/space-crush-common/src/systems/fleet_owned_update.rs index c75d819..dc29d56 100644 --- a/space-crush-common/src/systems/fleet_owned_update.rs +++ b/space-crush-common/src/systems/fleet_owned_update.rs @@ -4,8 +4,8 @@ use shrev::ReaderId; use specs::{prelude::*, world::Index, Entities, ReadStorage, System, World, WriteStorage}; use crate::{ - components::{Fleet, FleetOwned, Ship}, - misc::{ComponentEvent, StorageHelper, StorageHelperMut}, + components::{Fleet, FleetMoving, FleetOwned, Ship}, + misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, }; pub struct FleetOwnedUpdate { @@ -40,6 +40,7 @@ pub struct FleetOwnedUpdateData<'a> { entities: Entities<'a>, ships: ReadStorage<'a, Ship>, fleets: WriteStorage<'a, Fleet>, + fleets_moving: ReadStorage<'a, FleetMoving>, fleet_owned: ReadStorage<'a, FleetOwned>, } @@ -51,6 +52,7 @@ impl<'a> System<'a> for FleetOwnedUpdate { entities, ships, mut fleets, + fleets_moving, fleet_owned, } = data; @@ -103,6 +105,11 @@ impl<'a> System<'a> for FleetOwnedUpdate { if old_match && !new_match { fleet.remove_ship(ship_id, ship); + if fleet.is_empty() && fleets_moving.contains(fleet_id) { + entities + .delete(fleet_id) + .panic("Unable to delete entity for moving fleet!"); + } } else if !old_match && new_match { fleet.add_ship(ship_id, ship); } diff --git a/space-crush-common/src/systems/mod.rs b/space-crush-common/src/systems/mod.rs index 75512fc..43936c8 100644 --- a/space-crush-common/src/systems/mod.rs +++ b/space-crush-common/src/systems/mod.rs @@ -2,12 +2,16 @@ mod fleet_control; mod fleet_orbiting_update; mod fleet_owned_update; mod process; -mod ship_movement; -mod ships; +mod ship_control; +mod ships_movement; +mod ships_moving; +mod ships_orbiting; pub use fleet_control::{FleetControl, FleetControlEvent}; pub use fleet_orbiting_update::FleetOrbitingUpdate; pub use fleet_owned_update::FleetOwnedUpdate; pub use process::Process; -pub use ship_movement::ShipMovement; -pub use ships::Ships; +pub use ship_control::{ShipControl, ShipControlEvent}; +pub use ships_movement::ShipsMovement; +pub use ships_moving::ShipsMoving; +pub use ships_orbiting::ShipsOrbiting; diff --git a/space-crush-common/src/systems/ship_control.rs b/space-crush-common/src/systems/ship_control.rs new file mode 100644 index 0000000..0374155 --- /dev/null +++ b/space-crush-common/src/systems/ship_control.rs @@ -0,0 +1,166 @@ +use shrev::{EventChannel, ReaderId}; +use specs::{ + prelude::*, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, System, World, WriteStorage, +}; + +use crate::{ + components::{ + Fleet, FleetMoving, FleetOrbiting, FleetOwned, MeetingPoint, Player, PlayerOwned, + ShipMoving, ShipOrbiting, + }, + misc::{LogResult, WorldHelper}, + Error, +}; + +pub struct ShipControl { + ship_control_event_id: ReaderId, +} + +#[derive(Debug)] +pub enum ShipControlEvent { + SetMoving { ship: Entity, fleet: Entity }, + SetOrbiting { ship: Entity, target: Entity }, +} + +#[derive(SystemData)] +pub struct ShipControlData<'a> { + ship_control_events: ReadExpect<'a, EventChannel>, + + lazy: Read<'a, LazyUpdate>, + entities: Entities<'a>, + + players: ReadStorage<'a, Player>, + player_owned: WriteStorage<'a, PlayerOwned>, + + fleets: WriteStorage<'a, Fleet>, + fleets_moving: WriteStorage<'a, FleetMoving>, + fleets_orbiting: WriteStorage<'a, FleetOrbiting>, + + meeting_points: WriteStorage<'a, MeetingPoint>, + + ships_orbiting: WriteStorage<'a, ShipOrbiting>, + ships_moving: WriteStorage<'a, ShipMoving>, + fleet_owned: WriteStorage<'a, FleetOwned>, +} + +struct Processor<'a> { + lazy: Read<'a, LazyUpdate>, + entities: Entities<'a>, + + players: ReadStorage<'a, Player>, + player_owned: WriteStorage<'a, PlayerOwned>, + + fleets: WriteStorage<'a, Fleet>, + fleets_moving: WriteStorage<'a, FleetMoving>, + fleets_orbiting: WriteStorage<'a, FleetOrbiting>, + + meeting_points: WriteStorage<'a, MeetingPoint>, + + ships_orbiting: WriteStorage<'a, ShipOrbiting>, + ships_moving: WriteStorage<'a, ShipMoving>, + fleet_owned: WriteStorage<'a, FleetOwned>, +} + +impl ShipControl { + pub fn new(world: &mut World) -> Result { + world.insert(EventChannel::::default()); + + let ship_control_event_id = world.register_event_reader::()?; + + Ok(Self { + ship_control_event_id, + }) + } +} + +impl<'a> System<'a> for ShipControl { + type SystemData = ShipControlData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let ShipControlData { + ship_control_events, + lazy, + entities, + players, + player_owned, + fleets, + fleets_moving, + fleets_orbiting, + meeting_points, + ships_orbiting, + ships_moving, + fleet_owned, + } = data; + + let mut processor = Processor { + lazy, + entities, + players, + player_owned, + fleets, + fleets_moving, + fleets_orbiting, + meeting_points, + ships_orbiting, + ships_moving, + fleet_owned, + }; + + let events = ship_control_events.read(&mut self.ship_control_event_id); + for event in events { + match event { + ShipControlEvent::SetMoving { ship, fleet } => processor.set_moving(*ship, *fleet), + ShipControlEvent::SetOrbiting { ship, target } => { + processor.set_orbiting(*ship, *target) + } + } + } + } +} + +impl Processor<'_> { + fn set_moving(&mut self, ship_id: Entity, fleet_id: Entity) { + let fleet_owned = self.fleet_owned.get_mut(ship_id).unwrap(); + fleet_owned.set_owner(fleet_id); + + self.ships_orbiting.remove(ship_id); + self.ships_moving + .insert(ship_id, ShipMoving::new()) + .panic("Unable to create component 'ShipsMoving'"); + } + + fn set_orbiting(&mut self, ship_id: Entity, target_id: Entity) { + let player_owned = self.player_owned.get(ship_id).unwrap(); + let player_id = player_owned.owner(); + let player = self.players.get(player_id).unwrap(); + let meeting_point = self.meeting_points.get_mut(target_id).unwrap(); + let fleet_id = match meeting_point.fleet(player.index()) { + Some(fleet_id) => fleet_id, + None => { + let fleet_id = Fleet::builder() + .player(player_id) + .orbiting(target_id) + .lazy( + &self.lazy, + &self.entities, + &mut self.player_owned, + &mut self.fleets, + &mut self.fleets_moving, + &mut self.fleets_orbiting, + ) + .panic("Unable to create new entitiy: Fleet"); + meeting_point.add_fleet(player.index(), fleet_id); + + fleet_id + } + }; + + let fleet_owned = self.fleet_owned.get_mut(ship_id).unwrap(); + fleet_owned.set_owner(fleet_id); + + self.ships_moving.remove(ship_id); + self.ships_orbiting + .insert(ship_id, ShipOrbiting::new()) + .panic("Unable to create component 'ShipOrbiting'"); + } +} diff --git a/space-crush-common/src/systems/ship_movement.rs b/space-crush-common/src/systems/ship_movement.rs deleted file mode 100644 index f82beff..0000000 --- a/space-crush-common/src/systems/ship_movement.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![allow(dead_code)] - -use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; - -use crate::{ - components::{Player, PlayerOwned, Position, Ship}, - resources::Global, -}; - -#[derive(Default)] -pub struct ShipMovement; - -#[derive(SystemData)] -pub struct ShipMovementData<'a> { - positions: WriteStorage<'a, Position>, - ships: ReadStorage<'a, Ship>, - player_owned: ReadStorage<'a, PlayerOwned>, - players: ReadStorage<'a, Player>, - global: Read<'a, Global>, -} - -impl<'a> System<'a> for ShipMovement { - type SystemData = ShipMovementData<'a>; - - fn run(&mut self, data: Self::SystemData) { - let ShipMovementData { - mut positions, - ships, - player_owned, - players, - global, - } = data; - - (&mut positions, &player_owned, &ships).par_join().for_each( - |(position, player_owned, ship)| { - let delta = global.delta * global.world_speed; - let position = position.get_mut(); - let type_ = ship.type_(); - let player_id = player_owned.owner(); - let player = players.get(player_id).unwrap(); - let ship_data = player.ship_data(type_); - - *position += ship.dir() * ship_data.speed * delta; - }, - ); - } -} diff --git a/space-crush-common/src/systems/ships.rs b/space-crush-common/src/systems/ships.rs deleted file mode 100644 index f8c2526..0000000 --- a/space-crush-common/src/systems/ships.rs +++ /dev/null @@ -1,297 +0,0 @@ -use glc::{ - math::{linear_step, sqr}, - matrix::Matrix3f, - vector::{Angle, Vector2f, Vector3f}, -}; -use rand::random; -use shrev::ReaderId; -use specs::{ - hibitset::BitSet, prelude::*, world::Index, Entities, ParJoin, Read, ReadStorage, System, - WriteStorage, -}; - -use crate::{ - components::{ - FleetOrbiting, FleetOwned, MeetingPoint, Obstacle, Position, Shape, Ship, ShipObstacle, - }, - constants::{ - SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, - SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, - }, - misc::{ComponentEvent, StorageHelper, StorageHelperMut}, - resources::Global, -}; - -pub struct Ships { - need_update: BitSet, - fleet_owned_id: ReaderId>, -} - -#[derive(SystemData)] -pub struct ShipsData<'a> { - global: Read<'a, Global>, - entities: Entities<'a>, - ships: WriteStorage<'a, Ship>, - fleet_owned: ReadStorage<'a, FleetOwned>, - fleet_orbiting: ReadStorage<'a, FleetOrbiting>, - positions: ReadStorage<'a, Position>, - shapes: ReadStorage<'a, Shape>, - obstacles: ReadStorage<'a, Obstacle>, - meeting_points: ReadStorage<'a, MeetingPoint>, -} - -struct Processor<'a> { - need_update: &'a BitSet, - entities: &'a Entities<'a>, - positions: &'a ReadStorage<'a, Position>, - shapes: &'a ReadStorage<'a, Shape>, - obstacles: &'a ReadStorage<'a, Obstacle>, - meeting_points: &'a ReadStorage<'a, MeetingPoint>, - fleet_orbiting: &'a ReadStorage<'a, FleetOrbiting>, - delta: f32, -} - -impl Ships { - pub fn new(world: &mut World) -> Self { - WriteStorage::::setup(world); - - let need_update = BitSet::new(); - let fleet_owned_id = world - .system_data::>() - .register_event_reader(); - - Self { - need_update, - fleet_owned_id, - } - } - - fn progress_events(&mut self, fleet_owned: &ReadStorage<'_, FleetOwned>) { - self.need_update.clear(); - let events = fleet_owned.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, - fleet_owned, - fleet_orbiting, - positions, - shapes, - obstacles, - meeting_points, - } = data; - - self.progress_events(&fleet_owned); - - /* update ships */ - let processor = Processor { - need_update: &self.need_update, - entities: &entities, - positions: &positions, - shapes: &shapes, - obstacles: &obstacles, - fleet_orbiting: &fleet_orbiting, - meeting_points: &meeting_points, - delta: global.delta * global.world_speed, - }; - - ships.set_event_emission(false); - - let data = (positions.mask(), &mut ships, &positions, &fleet_owned); - - data.par_join() - .for_each(|(id, ship, position, fleet_owned)| { - processor.progress_ship(id, ship, position, fleet_owned); - }); - - ships.set_event_emission(true); - } -} - -impl Processor<'_> { - fn progress_ship( - &self, - id: Index, - ship: &mut Ship, - position: &Position, - fleet_owned: &FleetOwned, - ) { - let fleet_id = fleet_owned.owner(); - let fleet_orbiting = self.fleet_orbiting.get(fleet_id).unwrap(); - let meeting_point_id = fleet_orbiting.meeting_point(); - let meeting_point = self.meeting_points.get(meeting_point_id).unwrap(); - let meeting_point_pos = self.positions.get(meeting_point_id).unwrap().get(); - let ship_pos = position.get(); - let target_pos = ship.target_pos(); - let target_dir = ship.target_dir(); - - let meeting_point_to_target = target_pos - meeting_point_pos; - let meeting_point_to_ship = ship_pos - meeting_point_pos; - let mut ship_to_target = target_pos - ship_pos; - - let r_ship = meeting_point_to_ship.length_sqr(); - let r_target = meeting_point_to_target.length_sqr(); - - let meeting_point_min = meeting_point.min(); - let meeting_point_max = meeting_point.max(); - - let target_in_meeting_point = - (r_target <= sqr(meeting_point_max)) && (r_target >= sqr(meeting_point_min)); - let ship_in_meeting_point = r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * meeting_point_max); - - let need_update = self.need_update.contains(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_meeting_point != target_in_meeting_point - { - let target_pos = if ship_in_meeting_point && meeting_point_max > 0.0 { - let meeting_point_to_ship_vec3 = - Vector3f::new(meeting_point_to_ship.x, meeting_point_to_ship.y, 0.0); - let ship_dir_vec3 = ship.dir().into_vec3(); - - let dir = if meeting_point_to_ship_vec3.cross(&ship_dir_vec3).z > 0.0 { - 1.0 - } else { - -1.0 - }; - - let meeting_point_min = meeting_point.min(); - let meeting_point_max = meeting_point.max(); - - let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random::(); - let angle = meeting_point_to_ship.angle2(&VECTOR_2F_POS_X) - + (add * dir / meeting_point_max); - let radius = - meeting_point_min + (meeting_point_max - meeting_point_min) * random::(); - - Vector2f::new( - meeting_point_pos.x + radius * angle.cos(), - meeting_point_pos.y + radius * angle.sin(), - ) - } else { - *ship.obstacle_mut() = ShipObstacle::Search; - - *meeting_point_pos - }; - - ship.set_target(target_pos, (target_pos - ship_pos).normalize()); - - ship_to_target = target_pos - ship_pos; - } - - /* check if obstacle is still valid */ - if ship_in_meeting_point { - *ship.obstacle_mut() = ShipObstacle::Done; - } else if let ShipObstacle::Known(obstacle) = ship.obstacle() { - if let Some(position) = self.positions.get(obstacle) { - let obstacle_meeting_point = self.meeting_points.get(obstacle).unwrap(); - let obstacle_pos = position.get(); - let ship_to_obstacle = obstacle_pos - ship_pos; - - let obstacle_angle = ship_to_target - .angle2(&ship_to_obstacle) - .into_deg() - .into_inner() - .abs(); - - let meeting_point_sqr = obstacle_meeting_point.max() * obstacle_meeting_point.max(); - if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > meeting_point_sqr) - || obstacle_angle > 170.0 - { - *ship.obstacle_mut() = ShipObstacle::Search; - } - } else { - *ship.obstacle_mut() = ShipObstacle::Search; - } - } - - /* find obstacle */ - if !ship_in_meeting_point && ship.obstacle() == ShipObstacle::Search { - let mut dist_sqr = f32::MAX; - for (e, position, _) in (self.entities, self.positions, self.obstacles).join() { - let obstacle_pos = position.get(); - let ship_to_obstacle = obstacle_pos - ship_pos; - - if ship_to_target * ship_to_obstacle < 0.0 { - continue; // obstacle is behind the ship - } - - let len_sqr = ship_to_obstacle.length_sqr(); - if len_sqr < dist_sqr { - dist_sqr = len_sqr; - *ship.obstacle_mut() = ShipObstacle::Known(e); - } - } - - if let ShipObstacle::Known(e) = ship.obstacle() { - if e == fleet_owned.owner() { - *ship.obstacle_mut() = ShipObstacle::Done; - } - } - } - - /* check the obstacle */ - let mut expected_dir = ship_to_target; - if let ShipObstacle::Known(obstacle) = ship.obstacle() { - let obstacle_pos = self.positions.get(obstacle).unwrap(); - let obstacle_shape = self.shapes.get(obstacle).unwrap(); - let obstacle_meeting_point = self.meeting_points.get(obstacle).unwrap(); - let ship_to_obstacle = obstacle_pos.get() - ship_pos; - - let meeting_point_min = obstacle_meeting_point.min(); - let meeting_point_max = obstacle_meeting_point.max(); - - let meeting_point = ship_to_obstacle.length(); - if meeting_point < meeting_point_max { - let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x); - - let radius = obstacle_shape.radius(); - let mut adjust_low = linear_step(meeting_point_min, radius, meeting_point); - let adjust_high = - 1.0 - linear_step(meeting_point_max, meeting_point_min, meeting_point); - - if ship_to_target * tangent < 0.0 { - tangent = -tangent; - } else { - adjust_low = -adjust_low; - } - - let a_low = Angle::Deg(45.0); - let a_high = tangent.angle2(&ship_to_target); - let mat = Matrix3f::rotate(a_low * adjust_low + a_high * adjust_high); - expected_dir = mat.transform(tangent); - } - } - - /* update ship direction */ - let angle = expected_dir.angle2(ship.dir()); - if angle.into_inner().abs() > 0.0001 { - let dir = angle.into_inner() / angle.abs().into_inner(); - let agility = SHIP_ORBIT_AGILITY; - let rot_speed = agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner()); - - *ship.dir_mut() *= Matrix3f::rotate(rot_speed * -dir * self.delta); - } - } -} diff --git a/space-crush-common/src/systems/ships_movement.rs b/space-crush-common/src/systems/ships_movement.rs new file mode 100644 index 0000000..1fc1cb4 --- /dev/null +++ b/space-crush-common/src/systems/ships_movement.rs @@ -0,0 +1,82 @@ +#![allow(dead_code)] + +use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; + +use crate::{ + components::{Fleet, FleetOwned, Player, PlayerOwned, Position, Ship, ShipMoving, ShipType}, + resources::Global, +}; + +#[derive(Default)] +pub struct ShipsMovement; + +#[derive(SystemData)] +pub struct ShipsMovementData<'a> { + positions: WriteStorage<'a, Position>, + ships: ReadStorage<'a, Ship>, + ships_moving: ReadStorage<'a, ShipMoving>, + fleet_owned: ReadStorage<'a, FleetOwned>, + fleets: ReadStorage<'a, Fleet>, + player_owned: ReadStorage<'a, PlayerOwned>, + players: ReadStorage<'a, Player>, + global: Read<'a, Global>, +} + +impl<'a> System<'a> for ShipsMovement { + type SystemData = ShipsMovementData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let ShipsMovementData { + mut positions, + ships, + ships_moving, + fleet_owned, + fleets, + player_owned, + players, + global, + } = data; + + ( + &mut positions, + &player_owned, + &fleet_owned, + &ships, + ships_moving.maybe(), + ) + .par_join() + .for_each(|(position, player_owned, fleet_owned, ship, ship_moving)| { + let delta = global.delta * global.world_speed; + let position = position.get_mut(); + let player_id = player_owned.owner(); + let player = players.get(player_id).unwrap(); + let ships_data = player.ships_data(); + let type_ = ship.type_(); + + let speed = if ship_moving.is_some() { + let fleet_id = fleet_owned.owner(); + let fleet = fleets.get(fleet_id).unwrap(); + + let mut speed = ships_data[type_].speed; + + macro_rules! merge { + ($t:path) => { + if fleet.count()[$t] > 0 { + speed = speed.min(ships_data[$t].speed); + } + }; + } + + merge!(ShipType::Fighter); + merge!(ShipType::Bomber); + merge!(ShipType::Transporter); + + speed + } else { + ships_data[type_].speed + }; + + *position += ship.dir() * speed * delta; + }); + } +} diff --git a/space-crush-common/src/systems/ships_moving.rs b/space-crush-common/src/systems/ships_moving.rs new file mode 100644 index 0000000..f06fd47 --- /dev/null +++ b/space-crush-common/src/systems/ships_moving.rs @@ -0,0 +1,239 @@ +use glc::{ + math::{linear_step, sqr}, + matrix::Matrix3f, + vector::{Angle, Vector2f}, +}; +use shrev::EventChannel; +use specs::{prelude::*, Entities, Read, ReadStorage, System, WriteStorage}; + +use crate::{ + components::{ + FleetMoving, FleetOwned, MeetingPoint, Obstacle, Player, PlayerOwned, Position, Shape, + Ship, ShipMoving, ShipObstacle, + }, + constants::SHIP_ORBIT_DISTANCE_MAX, + misc::StorageHelperMut, + resources::Global, + systems::ShipControlEvent, +}; + +#[derive(Default)] +pub struct ShipsMoving {} + +#[derive(SystemData)] +pub struct ShipsData<'a> { + global: Read<'a, Global>, + entities: Entities<'a>, + + positions: ReadStorage<'a, Position>, + ships: WriteStorage<'a, Ship>, + ships_moving: WriteStorage<'a, ShipMoving>, + ship_control: WriteExpect<'a, EventChannel>, + + fleet_owned: ReadStorage<'a, FleetOwned>, + fleet_moving: ReadStorage<'a, FleetMoving>, + + meeting_points: ReadStorage<'a, MeetingPoint>, + obstacles: ReadStorage<'a, Obstacle>, + shapes: ReadStorage<'a, Shape>, + + players: ReadStorage<'a, Player>, + player_owned: ReadStorage<'a, PlayerOwned>, +} + +struct Processor<'a> { + delta: f32, + entities: Entities<'a>, + positions: ReadStorage<'a, Position>, + shapes: ReadStorage<'a, Shape>, + obstacles: ReadStorage<'a, Obstacle>, + meeting_points: ReadStorage<'a, MeetingPoint>, + fleet_moving: ReadStorage<'a, FleetMoving>, + players: ReadStorage<'a, Player>, +} + +impl<'a> System<'a> for ShipsMoving { + type SystemData = ShipsData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let ShipsData { + global, + entities, + + positions, + mut ships, + mut ships_moving, + mut ship_control, + + fleet_owned, + fleet_moving, + + meeting_points, + obstacles, + shapes, + + players, + player_owned, + } = data; + + /* update ships */ + let processor = Processor { + delta: global.delta * global.world_speed, + entities, + positions, + shapes, + obstacles, + meeting_points, + fleet_moving, + players, + }; + + ships.set_event_emission(false); + + let data = ( + &processor.entities, + &processor.positions, + &mut ships, + &mut ships_moving, + &fleet_owned, + &player_owned, + ); + + for (id, position, ship, ship_moving, fleet_owned, player_owned) in data.join() { + let target = + processor.progress_ship(position, ship, ship_moving, fleet_owned, player_owned); + + if let Some(target) = target { + let event = ShipControlEvent::SetOrbiting { ship: id, target }; + ship_control.single_write(event); + } + } + + ships.set_event_emission(true); + } +} + +impl Processor<'_> { + fn progress_ship( + &self, + position: &Position, + ship: &mut Ship, + ship_moving: &mut ShipMoving, + fleet_owned: &FleetOwned, + player_owned: &PlayerOwned, + ) -> Option { + let player_id = player_owned.owner(); + let player = self.players.get(player_id).unwrap(); + + let fleet_id = fleet_owned.owner(); + let fleet_moving = self.fleet_moving.get(fleet_id).unwrap(); + + let target_id = fleet_moving.target(); + let target_orbit = self.meeting_points.get(target_id).unwrap().max(); + let target_pos = self.positions.get(target_id).unwrap().get(); + + let ship_pos = position.get(); + let ship_data = player.ship_data(ship.type_()); + + let meeting_point_to_ship = ship_pos - target_pos; + let ship_to_target = target_pos - ship_pos; + let r_ship = meeting_point_to_ship.length_sqr(); + + if r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * target_orbit) { + return Some(target_id); + } + + /* check if obstacle is still valid */ + if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() { + let obstacle_id = *obstacle_id; + let obstacle_pos = self.positions.get(obstacle_id).unwrap().get(); + let obstacle_meeting_point = self.meeting_points.get(obstacle_id).unwrap(); + let ship_to_obstacle = obstacle_pos - ship_pos; + + let obstacle_angle = ship_to_target + .angle2(&ship_to_obstacle) + .into_deg() + .into_inner() + .abs(); + + let meeting_point_sqr = obstacle_meeting_point.max() * obstacle_meeting_point.max(); + if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > meeting_point_sqr) + || obstacle_angle > 170.0 + { + *ship_moving.obstacle_mut() = ShipObstacle::Search; + } + } + + /* find obstacle */ + if ship_moving.obstacle() == &ShipObstacle::Search { + let mut dist_sqr = f32::MAX; + for (obstacle_id, position, _) in + (&self.entities, &self.positions, &self.obstacles).join() + { + let obstacle_pos = position.get(); + let ship_to_obstacle = obstacle_pos - ship_pos; + + if ship_to_target * ship_to_obstacle < 0.0 { + continue; // obstacle is behind the ship + } + + let len_sqr = ship_to_obstacle.length_sqr(); + if len_sqr < dist_sqr { + dist_sqr = len_sqr; + *ship_moving.obstacle_mut() = ShipObstacle::Known(obstacle_id); + } + } + + if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() { + if *obstacle_id == fleet_owned.owner() { + *ship_moving.obstacle_mut() = ShipObstacle::Done; + } + } + } + + /* check the obstacle */ + let mut expected_dir = ship_to_target; + if let ShipObstacle::Known(obstacle) = ship_moving.obstacle() { + let obstacle_pos = self.positions.get(*obstacle).unwrap(); + let obstacle_shape = self.shapes.get(*obstacle).unwrap(); + let obstacle_meeting_point = self.meeting_points.get(*obstacle).unwrap(); + let ship_to_obstacle = obstacle_pos.get() - ship_pos; + + let meeting_point_min = obstacle_meeting_point.min(); + let meeting_point_max = obstacle_meeting_point.max(); + + let meeting_point = ship_to_obstacle.length(); + if meeting_point < meeting_point_max { + let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x); + + let radius = obstacle_shape.radius(); + let mut adjust_low = linear_step(meeting_point_min, radius, meeting_point); + let adjust_high = + 1.0 - linear_step(meeting_point_max, meeting_point_min, meeting_point); + + if ship_to_target * tangent < 0.0 { + tangent = -tangent; + } else { + adjust_low = -adjust_low; + } + + let a_low = Angle::Deg(45.0); + let a_high = tangent.angle2(&ship_to_target); + let mat = Matrix3f::rotate(a_low * adjust_low + a_high * adjust_high); + expected_dir = mat.transform(tangent); + } + } + + /* update ship direction */ + let angle = expected_dir.angle2(ship.dir()); + if angle.into_inner().abs() > 0.0001 { + let dir = angle.into_inner() / angle.abs().into_inner(); + let rot_speed = + ship_data.agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner()); + + *ship.dir_mut() *= Matrix3f::rotate(rot_speed * -dir * self.delta); + } + + None + } +} diff --git a/space-crush-common/src/systems/ships_orbiting.rs b/space-crush-common/src/systems/ships_orbiting.rs new file mode 100644 index 0000000..3028985 --- /dev/null +++ b/space-crush-common/src/systems/ships_orbiting.rs @@ -0,0 +1,138 @@ +use glc::{ + math::linear_step, + matrix::Matrix3f, + vector::{Vector2f, Vector3f}, +}; +use rand::random; +use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; + +use crate::{ + components::{FleetOrbiting, FleetOwned, MeetingPoint, Position, Ship, ShipOrbiting}, + constants::{SHIP_DATA_IN_ORBIT, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND}, + misc::StorageHelperMut, + resources::Global, +}; + +#[derive(Default)] +pub struct ShipsOrbiting; + +#[derive(SystemData)] +pub struct ShipsData<'a> { + global: Read<'a, Global>, + positions: ReadStorage<'a, Position>, + fleet_owned: ReadStorage<'a, FleetOwned>, + ships: WriteStorage<'a, Ship>, + ships_orbiting: WriteStorage<'a, ShipOrbiting>, + fleets_orbiting: ReadStorage<'a, FleetOrbiting>, + meeting_points: ReadStorage<'a, MeetingPoint>, +} + +struct Processor<'a> { + delta: f32, + positions: &'a ReadStorage<'a, Position>, + meeting_points: &'a ReadStorage<'a, MeetingPoint>, + fleets_orbiting: &'a ReadStorage<'a, FleetOrbiting>, +} + +impl<'a> System<'a> for ShipsOrbiting { + type SystemData = ShipsData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let ShipsData { + global, + positions, + fleet_owned, + mut ships, + mut ships_orbiting, + fleets_orbiting, + meeting_points, + } = data; + + /* update ships */ + let processor = Processor { + delta: global.delta * global.world_speed, + positions: &positions, + fleets_orbiting: &fleets_orbiting, + meeting_points: &meeting_points, + }; + + ships.set_event_emission(false); + + let data = (&positions, &fleet_owned, &mut ships, &mut ships_orbiting); + + data.par_join() + .for_each(|(position, fleet_owned, ship, ship_orbiting)| { + processor.progress_ship(position, fleet_owned, ship, ship_orbiting); + }); + + ships.set_event_emission(true); + } +} + +impl Processor<'_> { + fn progress_ship( + &self, + position: &Position, + fleet_owned: &FleetOwned, + ship: &mut Ship, + ship_orbiting: &mut ShipOrbiting, + ) { + let fleet_id = fleet_owned.owner(); + let fleet_orbiting = self.fleets_orbiting.get(fleet_id).unwrap(); + let meeting_point_id = fleet_orbiting.meeting_point(); + let meeting_point = self.meeting_points.get(meeting_point_id).unwrap(); + let meeting_point_pos = self.positions.get(meeting_point_id).unwrap().get(); + let ship_pos = position.get(); + let target_pos = *ship_orbiting.target_pos(); + let target_dir = ship_orbiting.target_dir(); + + let meeting_point_to_ship = ship_pos - meeting_point_pos; + let mut ship_to_target = target_pos - ship_pos; + + 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 !has_target || passed_target { + let meeting_point_to_ship_vec3 = + Vector3f::new(meeting_point_to_ship.x, meeting_point_to_ship.y, 0.0); + let ship_dir_vec3 = ship.dir().into_vec3(); + + let dir = if meeting_point_to_ship_vec3.cross(&ship_dir_vec3).z > 0.0 { + 1.0 + } else { + -1.0 + }; + + let meeting_point_min = meeting_point.min(); + let meeting_point_max = meeting_point.max(); + + const VECTOR2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0); + + let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random::(); + let angle = + meeting_point_to_ship.angle2(&VECTOR2F_POS_X) + (add * dir / meeting_point_max); + let radius = + meeting_point_min + (meeting_point_max - meeting_point_min) * random::(); + + let target_pos = Vector2f::new( + meeting_point_pos.x + radius * angle.cos(), + meeting_point_pos.y + radius * angle.sin(), + ); + ship_to_target = target_pos - ship_pos; + let target_dir = ship_to_target.normalize(); + + ship_orbiting.set_target(target_pos, target_dir); + } + + /* update ship direction */ + let angle = ship_to_target.angle2(ship.dir()); + if angle.into_inner().abs() > 0.0001 { + let dir = angle.into_inner() / angle.abs().into_inner(); + let agility = SHIP_DATA_IN_ORBIT.agility; + let rot_speed = agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner()); + + *ship.dir_mut() *= Matrix3f::rotate(rot_speed * -dir * self.delta); + } + } +}