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' statemaster
| @@ -100,6 +100,15 @@ impl_op_ex!(/ <T: Numeric> |a: &Angle<T>, b: &T| -> Angle<T> { a._mul(T::one() / | |||||
| impl_op_ex!(+= <T: Numeric> |a: &mut Angle<T>, b: &Angle<T>| { *a = a._add(b); }); | impl_op_ex!(+= <T: Numeric> |a: &mut Angle<T>, b: &Angle<T>| { *a = a._add(b); }); | ||||
| impl_op_ex!(-= <T: Numeric> |a: &mut Angle<T>, b: &Angle<T>| { *a = a._add(&b._neg()); }); | impl_op_ex!(-= <T: Numeric> |a: &mut Angle<T>, b: &Angle<T>| { *a = a._add(&b._neg()); }); | ||||
| impl<T> Default for Angle<T> | |||||
| where | |||||
| T: Numeric, | |||||
| { | |||||
| fn default() -> Self { | |||||
| Self::Deg(T::zero()) | |||||
| } | |||||
| } | |||||
| impl<T, S> PartialEq<Angle<S>> for Angle<T> | impl<T, S> PartialEq<Angle<S>> for Angle<T> | ||||
| where | where | ||||
| T: Numeric + PartialEq<S>, | T: Numeric + PartialEq<S>, | ||||
| @@ -1,28 +1,31 @@ | |||||
| x -> needed | x -> needed | ||||
| o -> optional | 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 | | |||||
| +--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |||||
| @@ -1,8 +1,5 @@ | |||||
| use glc::vector::{Angle, Vector2f, Vector4f}; | 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 specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | ||||
| use crate::resources::Geometry; | use crate::resources::Geometry; | ||||
| @@ -40,13 +37,6 @@ impl<'a> System<'a> for MeetingPoints { | |||||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | Vector4f::new(0.5, 0.5, 0.5, 0.05), | ||||
| &create_circle(position.get(), meeting_point.max()), | &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); | gl::blend_equation(gl::FUNC_ADD); | ||||
| @@ -1,5 +1,7 @@ | |||||
| use glc::vector::Vector4f; | 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 specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | ||||
| use crate::resources::Geometry; | use crate::resources::Geometry; | ||||
| @@ -14,6 +16,8 @@ pub struct ShipsData<'a> { | |||||
| player_owned: ReadStorage<'a, PlayerOwned>, | player_owned: ReadStorage<'a, PlayerOwned>, | ||||
| players: ReadStorage<'a, Player>, | players: ReadStorage<'a, Player>, | ||||
| ships: ReadStorage<'a, Ship>, | ships: ReadStorage<'a, Ship>, | ||||
| ships_orbiting: ReadStorage<'a, ShipOrbiting>, | |||||
| ships_moving: ReadStorage<'a, ShipMoving>, | |||||
| } | } | ||||
| impl<'a> System<'a> for Ships { | impl<'a> System<'a> for Ships { | ||||
| @@ -26,12 +30,22 @@ impl<'a> System<'a> for Ships { | |||||
| player_owned, | player_owned, | ||||
| players, | players, | ||||
| ships, | ships, | ||||
| ships_orbiting, | |||||
| ships_moving, | |||||
| } = data; | } = data; | ||||
| gl::enable(gl::BLEND); | gl::enable(gl::BLEND); | ||||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE); | 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 ship_pos = position.get(); | ||||
| let type_ = ship.type_(); | let type_ = ship.type_(); | ||||
| let player_id = player_owned.owner(); | 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), | Vector4f::new(0.0, 0.0, 1.0, 0.2), | ||||
| &[*ship_pos, ship_pos + ship.dir() * ship_data.speed], | &[*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( | 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], | |||||
| ); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -94,7 +94,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| let fleet_id = Fleet::builder() | let fleet_id = Fleet::builder() | ||||
| .player(player_id) | .player(player_id) | ||||
| .meeting_point(planets[1]) | |||||
| .orbiting(planets[1]) | |||||
| .build(world) | .build(world) | ||||
| .unwrap(); | .unwrap(); | ||||
| @@ -84,9 +84,7 @@ impl<'a> System<'a> for FleetMove { | |||||
| Some(target) => target, | Some(target) => target, | ||||
| None => continue, | None => continue, | ||||
| }; | }; | ||||
| let player = game_state.player_id(); | |||||
| let event = FleetControlEvent::MoveToMeetingPoint { | let event = FleetControlEvent::MoveToMeetingPoint { | ||||
| player, | |||||
| fleet, | fleet, | ||||
| count, | count, | ||||
| target, | target, | ||||
| @@ -11,7 +11,6 @@ use glc::{ | |||||
| use shrev::{EventChannel, ReaderId}; | use shrev::{EventChannel, ReaderId}; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{Fleet, FleetOrbiting, MeetingPoint, Position, Shape, ShipCount}, | components::{Fleet, FleetOrbiting, MeetingPoint, Position, Shape, ShipCount}, | ||||
| constants::VECTOR_2F_POS_X, | |||||
| misc::{LogResult, WorldHelper as _}, | misc::{LogResult, WorldHelper as _}, | ||||
| resources::Global, | resources::Global, | ||||
| }; | }; | ||||
| @@ -260,10 +259,12 @@ impl FleetSelect { | |||||
| self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom; | self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom; | ||||
| self.ring1 = self.ring0 + FLEET_SELECT_WIDTH / 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 is_simple = self.select_mode.is_simple(); | ||||
| let angle = self | let angle = self | ||||
| .marker | .marker | ||||
| .angle2(&VECTOR_2F_POS_X) | |||||
| .angle2(&VECTOR2F_POS_X) | |||||
| .normalize() | .normalize() | ||||
| .into_deg() | .into_deg() | ||||
| .into_inner(); | .into_inner(); | ||||
| @@ -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::{ | use crate::{ | ||||
| components::{Fleet, FleetOrbiting, PlayerOwned}, | |||||
| components::{Fleet, FleetMoving, FleetOrbiting, PlayerOwned}, | |||||
| misc::{Persistence, WorldPersistence}, | misc::{Persistence, WorldPersistence}, | ||||
| }; | }; | ||||
| @@ -10,28 +13,71 @@ use super::Error; | |||||
| #[derive(Default, Clone)] | #[derive(Default, Clone)] | ||||
| pub struct FleetBuilder { | pub struct FleetBuilder { | ||||
| player: Option<Entity>, | player: Option<Entity>, | ||||
| meeting_point: Option<Entity>, | |||||
| state: Option<State>, | |||||
| } | |||||
| #[derive(Clone)] | |||||
| enum State { | |||||
| Orbiting(Entity), | |||||
| Moving(Entity), | |||||
| } | } | ||||
| impl FleetBuilder { | impl FleetBuilder { | ||||
| pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | ||||
| let player = self.player.ok_or(Error::MissingValue("player"))?; | 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 player_owned = PlayerOwned::new(player); | ||||
| let fleet_orbiting = FleetOrbiting::new(meeting_point); | |||||
| let fleet = Fleet::new(); | let fleet = Fleet::new(); | ||||
| let entity = world | |||||
| let builder = world | |||||
| .create_entity() | .create_entity() | ||||
| .marked::<<WorldPersistence as Persistence>::Marker>() | .marked::<<WorldPersistence as Persistence>::Marker>() | ||||
| .with(player_owned) | .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<Entity, Error> { | |||||
| 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::<<WorldPersistence as Persistence>::Marker>() | |||||
| .build(); | .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) | Ok(entity) | ||||
| } | } | ||||
| @@ -41,8 +87,14 @@ impl FleetBuilder { | |||||
| self | 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 | self | ||||
| } | } | ||||
| @@ -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<Vector2f>, | |||||
| } | |||||
| impl MeetingPointBuilder { | |||||
| pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | |||||
| 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::<<WorldPersistence as Persistence>::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<Entity, Error> { | |||||
| 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::<<WorldPersistence as Persistence>::Marker>() | |||||
| .build(); | |||||
| positions.insert(entity, position)?; | |||||
| meeting_points.insert(entity, meeting_point)?; | |||||
| Ok(entity) | |||||
| } | |||||
| pub fn position<V: Into<Vector2f>>(mut self, value: V) -> Self { | |||||
| self.position = Some(value.into()); | |||||
| self | |||||
| } | |||||
| } | |||||
| @@ -1,11 +1,13 @@ | |||||
| mod asteroid; | mod asteroid; | ||||
| mod fleet; | mod fleet; | ||||
| mod meeting_point; | |||||
| mod planet; | mod planet; | ||||
| mod player; | mod player; | ||||
| mod ship; | mod ship; | ||||
| pub use asteroid::AsteroidBuilder; | pub use asteroid::AsteroidBuilder; | ||||
| pub use fleet::FleetBuilder; | pub use fleet::FleetBuilder; | ||||
| pub use meeting_point::MeetingPointBuilder; | |||||
| pub use planet::PlanetBuilder; | pub use planet::PlanetBuilder; | ||||
| pub use player::PlayerBuilder; | pub use player::PlayerBuilder; | ||||
| pub use ship::ShipBuilder; | pub use ship::ShipBuilder; | ||||
| @@ -3,7 +3,7 @@ use rand::random; | |||||
| use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | ||||
| use crate::{ | use crate::{ | ||||
| components::{FleetOwned, PlayerOwned, Position, Ship, ShipType}, | |||||
| components::{FleetOwned, PlayerOwned, Position, Ship, ShipOrbiting, ShipType}, | |||||
| misc::{Persistence, WorldPersistence}, | misc::{Persistence, WorldPersistence}, | ||||
| }; | }; | ||||
| @@ -30,6 +30,7 @@ impl ShipBuilder { | |||||
| let fleet_owned = FleetOwned::new(fleet); | let fleet_owned = FleetOwned::new(fleet); | ||||
| let position = Position::new(position); | let position = Position::new(position); | ||||
| let ship = Ship::new(type_, direction); | let ship = Ship::new(type_, direction); | ||||
| let ship_orbiting = ShipOrbiting::new(); | |||||
| let entity = world | let entity = world | ||||
| .create_entity() | .create_entity() | ||||
| @@ -38,6 +39,7 @@ impl ShipBuilder { | |||||
| .with(fleet_owned) | .with(fleet_owned) | ||||
| .with(position) | .with(position) | ||||
| .with(ship) | .with(ship) | ||||
| .with(ship_orbiting) | |||||
| .build(); | .build(); | ||||
| Ok(entity) | Ok(entity) | ||||
| @@ -30,6 +30,11 @@ pub struct FleetOrbiting { | |||||
| meeting_point: Entity, | meeting_point: Entity, | ||||
| } | } | ||||
| #[derive(Debug, Clone)] | |||||
| pub struct FleetMoving { | |||||
| target: Entity, | |||||
| } | |||||
| #[derive(Serialize, Deserialize)] | #[derive(Serialize, Deserialize)] | ||||
| pub struct FleetOwnedData<M> { | pub struct FleetOwnedData<M> { | ||||
| pub owner: M, | pub owner: M, | ||||
| @@ -40,6 +45,11 @@ pub struct FleetOrbitingData<M> { | |||||
| pub meeting_point: M, | pub meeting_point: M, | ||||
| } | } | ||||
| #[derive(Serialize, Deserialize)] | |||||
| pub struct FleetMovingData<M> { | |||||
| pub target: M, | |||||
| } | |||||
| /* Fleet */ | /* Fleet */ | ||||
| impl Fleet { | impl Fleet { | ||||
| @@ -57,6 +67,11 @@ impl Fleet { | |||||
| pub fn count(&self) -> &ShipCount { | pub fn count(&self) -> &ShipCount { | ||||
| &self.count | &self.count | ||||
| } | } | ||||
| #[inline] | |||||
| pub fn is_empty(&self) -> bool { | |||||
| self.count.total() == 0 | |||||
| } | |||||
| } | } | ||||
| impl Fleet { | impl Fleet { | ||||
| @@ -177,3 +192,47 @@ where | |||||
| Ok(FleetOrbiting { meeting_point }) | 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<Self, HashMapStorage<Self>>; | |||||
| } | |||||
| impl<M> ConvertSaveload<M> for FleetMoving | |||||
| where | |||||
| for<'de> M: Marker + Serialize + Deserialize<'de>, | |||||
| { | |||||
| type Data = FleetMovingData<M>; | |||||
| type Error = NoError; | |||||
| fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||||
| where | |||||
| F: FnMut(Entity) -> Option<M>, | |||||
| { | |||||
| let target = ids(self.target).unwrap(); | |||||
| Ok(FleetMovingData { target }) | |||||
| } | |||||
| fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||||
| where | |||||
| F: FnMut(M) -> Option<Entity>, | |||||
| { | |||||
| let target = ids(data.target).unwrap(); | |||||
| Ok(FleetMoving { target }) | |||||
| } | |||||
| } | |||||
| @@ -7,7 +7,7 @@ use specs::{ | |||||
| Component, Entity, HashMapStorage, | Component, Entity, HashMapStorage, | ||||
| }; | }; | ||||
| use crate::misc::FlaggedStorage; | |||||
| use crate::{builder::MeetingPointBuilder, misc::FlaggedStorage}; | |||||
| /// Point in the universe fleets can meet | /// Point in the universe fleets can meet | ||||
| #[derive(Clone, Debug, Default)] | #[derive(Clone, Debug, Default)] | ||||
| @@ -40,6 +40,11 @@ type Fleets = SmallVec<[Option<Entity>; 8]>; | |||||
| /* MeetingPoint */ | /* MeetingPoint */ | ||||
| impl MeetingPoint { | impl MeetingPoint { | ||||
| #[inline] | |||||
| pub fn builder() -> MeetingPointBuilder { | |||||
| MeetingPointBuilder::default() | |||||
| } | |||||
| #[inline] | #[inline] | ||||
| pub fn min(&self) -> f32 { | pub fn min(&self) -> f32 { | ||||
| self.min | self.min | ||||
| @@ -9,11 +9,13 @@ mod shape; | |||||
| mod ship; | mod ship; | ||||
| pub use asteroid::{Asteroid, Type as AsteroidType}; | 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 meeting_point::MeetingPoint; | ||||
| pub use obstacle::Obstacle; | pub use obstacle::Obstacle; | ||||
| pub use planet::Planet; | pub use planet::Planet; | ||||
| pub use player::{Player, PlayerOwned, Race}; | pub use player::{Player, PlayerOwned, Race}; | ||||
| pub use position::Position; | pub use position::Position; | ||||
| pub use shape::Shape; | pub use shape::Shape; | ||||
| pub use ship::{Ship, ShipCount, ShipData, ShipObstacle, ShipType, ShipsData}; | |||||
| pub use ship::{ | |||||
| Ship, ShipCount, ShipData, ShipMoving, ShipObstacle, ShipOrbiting, ShipType, ShipsData, | |||||
| }; | |||||
| @@ -61,6 +61,11 @@ impl Player { | |||||
| pub fn ship_data(&self, type_: ShipType) -> &ShipData { | pub fn ship_data(&self, type_: ShipType) -> &ShipData { | ||||
| &self.ships_data[type_] | &self.ships_data[type_] | ||||
| } | } | ||||
| #[inline] | |||||
| pub fn ships_data(&self) -> &ShipsData { | |||||
| &self.ships_data | |||||
| } | |||||
| } | } | ||||
| impl Player { | impl Player { | ||||
| @@ -1,7 +1,7 @@ | |||||
| use std::cmp::min; | use std::cmp::min; | ||||
| use std::ops::{Index, IndexMut, Mul}; | use std::ops::{Index, IndexMut, Mul}; | ||||
| use glc::vector::Vector2f; | |||||
| use glc::vector::{Angle, Vector2f}; | |||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||
| use specs::{Component, Entity, VecStorage}; | use specs::{Component, Entity, VecStorage}; | ||||
| @@ -11,13 +11,18 @@ use crate::{builder::ShipBuilder, misc::FlaggedStorage}; | |||||
| pub struct Ship { | pub struct Ship { | ||||
| type_: ShipType, | type_: ShipType, | ||||
| dir: Vector2f, | dir: Vector2f, | ||||
| } | |||||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||||
| pub struct ShipMoving { | |||||
| #[serde(skip)] | #[serde(skip)] | ||||
| obstacle: ShipObstacle, | obstacle: ShipObstacle, | ||||
| } | |||||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||||
| pub struct ShipOrbiting { | |||||
| #[serde(skip)] | #[serde(skip)] | ||||
| target_pos: Vector2f, | target_pos: Vector2f, | ||||
| #[serde(skip)] | #[serde(skip)] | ||||
| target_dir: Vector2f, | target_dir: Vector2f, | ||||
| } | } | ||||
| @@ -46,6 +51,7 @@ pub struct ShipsData { | |||||
| #[derive(Clone, Default, Debug)] | #[derive(Clone, Default, Debug)] | ||||
| pub struct ShipData { | pub struct ShipData { | ||||
| pub speed: f32, | pub speed: f32, | ||||
| pub agility: Angle<f32>, | |||||
| } | } | ||||
| #[derive(Copy, Clone, Debug, Serialize, Deserialize)] | #[derive(Copy, Clone, Debug, Serialize, Deserialize)] | ||||
| @@ -72,30 +78,65 @@ impl Ship { | |||||
| pub fn dir(&self) -> &Vector2f { | pub fn dir(&self) -> &Vector2f { | ||||
| &self.dir | &self.dir | ||||
| } | } | ||||
| } | |||||
| impl Ship { | |||||
| #[inline] | #[inline] | ||||
| pub fn target_pos(&self) -> &Vector2f { | |||||
| &self.target_pos | |||||
| pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { | |||||
| Self { type_, dir } | |||||
| } | } | ||||
| #[inline] | #[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<Self, VecStorage<Self>>; | |||||
| } | |||||
| /* ShipMoving */ | |||||
| impl ShipMoving { | |||||
| pub fn obstacle(&self) -> &ShipObstacle { | |||||
| &self.obstacle | |||||
| } | |||||
| } | |||||
| impl ShipMoving { | |||||
| #[inline] | #[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<Self>; | |||||
| } | |||||
| /* ShipOrbiting */ | |||||
| impl ShipOrbiting { | |||||
| pub fn target_pos(&self) -> &Vector2f { | |||||
| &self.target_pos | |||||
| } | |||||
| pub fn target_dir(&self) -> &Vector2f { | |||||
| &self.target_dir | |||||
| } | |||||
| } | |||||
| impl ShipOrbiting { | |||||
| #[inline] | #[inline] | ||||
| pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { | |||||
| pub(crate) fn new() -> Self { | |||||
| Self { | Self { | ||||
| type_, | |||||
| dir, | |||||
| obstacle: Default::default(), | |||||
| target_pos: Default::default(), | target_pos: Default::default(), | ||||
| target_dir: Default::default(), | target_dir: Default::default(), | ||||
| } | } | ||||
| @@ -106,20 +147,10 @@ impl Ship { | |||||
| self.target_pos = pos; | self.target_pos = pos; | ||||
| self.target_dir = dir; | 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<Self, VecStorage<Self>>; | |||||
| impl Component for ShipOrbiting { | |||||
| type Storage = VecStorage<Self>; | |||||
| } | } | ||||
| /* Obstacle */ | /* Obstacle */ | ||||
| @@ -1,35 +1,61 @@ | |||||
| use glc::{matrix::Angle, vector::Vector2f}; | |||||
| use glc::matrix::Angle; | |||||
| use crate::components::{ShipData, ShipsData}; | 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<f32> = Angle::Deg(5000.0); | pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle<f32> = Angle::Deg(5000.0); | ||||
| /// Random angle between old and new target position in orbit | |||||
| pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(5000.0); | pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(5000.0); | ||||
| pub const SHIP_ORBIT_DISTANCE_MAX: f32 = 1.10; // % | |||||
| /// Agility of ships inside orbit | |||||
| pub const SHIP_ORBIT_AGILITY: Angle<f32> = 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 { | 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 { | 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 { | 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), | |||||
| }, | |||||
| }; | }; | ||||
| @@ -3,7 +3,10 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||||
| use crate::{ | use crate::{ | ||||
| components::Player, | components::Player, | ||||
| resources::Global, | resources::Global, | ||||
| systems::{FleetControl, FleetOrbitingUpdate, FleetOwnedUpdate, Process, ShipMovement, Ships}, | |||||
| systems::{ | |||||
| FleetControl, FleetOrbitingUpdate, FleetOwnedUpdate, Process, ShipControl, ShipsMovement, | |||||
| ShipsMoving, ShipsOrbiting, | |||||
| }, | |||||
| Error, | Error, | ||||
| }; | }; | ||||
| @@ -19,8 +22,10 @@ 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(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(FleetControl::new(world)?, "fleet_control", &[]) | ||||
| .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | ||||
| .with(FleetOrbitingUpdate::new(world), "fleet_orbiting", &[]) | .with(FleetOrbitingUpdate::new(world), "fleet_orbiting", &[]) | ||||
| @@ -8,7 +8,7 @@ use specs::{saveload::SimpleMarker, World}; | |||||
| use crate::{ | use crate::{ | ||||
| components::{ | components::{ | ||||
| Asteroid, FleetOrbiting, FleetOwned, MeetingPoint, Obstacle, Planet, Player, PlayerOwned, | Asteroid, FleetOrbiting, FleetOwned, MeetingPoint, Obstacle, Planet, Player, PlayerOwned, | ||||
| Position, Ship, | |||||
| Position, Ship, ShipMoving, ShipOrbiting, | |||||
| }, | }, | ||||
| Error, | Error, | ||||
| }; | }; | ||||
| @@ -96,6 +96,8 @@ impl Persistence for WorldPersistence { | |||||
| FleetOwned, | FleetOwned, | ||||
| FleetOrbiting, | FleetOrbiting, | ||||
| Ship, | Ship, | ||||
| ShipMoving, | |||||
| ShipOrbiting, | |||||
| Obstacle, | Obstacle, | ||||
| MeetingPoint, | MeetingPoint, | ||||
| Planet, | Planet, | ||||
| @@ -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 shrev::{EventChannel, ReaderId}; | ||||
| use specs::{ | use specs::{ | ||||
| prelude::*, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, System, World, WriteStorage, | prelude::*, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, System, World, WriteStorage, | ||||
| @@ -11,9 +6,11 @@ use specs::{ | |||||
| use crate::{ | use crate::{ | ||||
| components::{ | 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, | Error, | ||||
| }; | }; | ||||
| @@ -24,24 +21,50 @@ pub struct FleetControl { | |||||
| #[derive(Debug)] | #[derive(Debug)] | ||||
| pub enum FleetControlEvent { | pub enum FleetControlEvent { | ||||
| MoveToMeetingPoint { | MoveToMeetingPoint { | ||||
| player: Entity, | |||||
| fleet: Entity, | fleet: Entity, | ||||
| count: ShipCount, | count: ShipCount, | ||||
| target: Entity, | target: Entity, | ||||
| }, | }, | ||||
| MoveToPosition { | |||||
| fleet: Entity, | |||||
| count: ShipCount, | |||||
| target: Vector2f, | |||||
| }, | |||||
| } | } | ||||
| #[derive(SystemData)] | #[derive(SystemData)] | ||||
| pub struct FleetControlData<'a> { | pub struct FleetControlData<'a> { | ||||
| fleet_control_events: ReadExpect<'a, EventChannel<FleetControlEvent>>, | |||||
| lazy: Read<'a, LazyUpdate>, | lazy: Read<'a, LazyUpdate>, | ||||
| entities: Entities<'a>, | entities: Entities<'a>, | ||||
| fleet_control_events: ReadExpect<'a, EventChannel<FleetControlEvent>>, | |||||
| player_owned: WriteStorage<'a, PlayerOwned>, | player_owned: WriteStorage<'a, PlayerOwned>, | ||||
| fleet_orbiting: WriteStorage<'a, FleetOrbiting>, | |||||
| fleet_owned: WriteStorage<'a, FleetOwned>, | |||||
| ship_control: WriteExpect<'a, EventChannel<ShipControlEvent>>, | |||||
| fleets: WriteStorage<'a, Fleet>, | |||||
| fleets_moving: WriteStorage<'a, FleetMoving>, | |||||
| fleets_orbiting: WriteStorage<'a, FleetOrbiting>, | |||||
| positions: WriteStorage<'a, Position>, | |||||
| meeting_points: WriteStorage<'a, MeetingPoint>, | 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<ShipControlEvent>>, | |||||
| fleets: WriteStorage<'a, Fleet>, | 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>, | ships: ReadStorage<'a, Ship>, | ||||
| } | } | ||||
| @@ -62,77 +85,114 @@ impl<'a> System<'a> for FleetControl { | |||||
| fn run(&mut self, data: Self::SystemData) { | fn run(&mut self, data: Self::SystemData) { | ||||
| let FleetControlData { | let FleetControlData { | ||||
| fleet_control_events, | |||||
| lazy, | lazy, | ||||
| entities, | 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, | ships, | ||||
| } = data; | } = 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); | let events = fleet_control_events.read(&mut self.fleet_control_event_id); | ||||
| for event in events { | for event in events { | ||||
| match event { | 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::<<WorldPersistence as Persistence>::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") | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,8 +4,8 @@ use shrev::ReaderId; | |||||
| use specs::{prelude::*, world::Index, Entities, ReadStorage, System, World, WriteStorage}; | use specs::{prelude::*, world::Index, Entities, ReadStorage, System, World, WriteStorage}; | ||||
| use crate::{ | use crate::{ | ||||
| components::{Fleet, FleetOwned, Ship}, | |||||
| misc::{ComponentEvent, StorageHelper, StorageHelperMut}, | |||||
| components::{Fleet, FleetMoving, FleetOwned, Ship}, | |||||
| misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, | |||||
| }; | }; | ||||
| pub struct FleetOwnedUpdate { | pub struct FleetOwnedUpdate { | ||||
| @@ -40,6 +40,7 @@ pub struct FleetOwnedUpdateData<'a> { | |||||
| entities: Entities<'a>, | entities: Entities<'a>, | ||||
| ships: ReadStorage<'a, Ship>, | ships: ReadStorage<'a, Ship>, | ||||
| fleets: WriteStorage<'a, Fleet>, | fleets: WriteStorage<'a, Fleet>, | ||||
| fleets_moving: ReadStorage<'a, FleetMoving>, | |||||
| fleet_owned: ReadStorage<'a, FleetOwned>, | fleet_owned: ReadStorage<'a, FleetOwned>, | ||||
| } | } | ||||
| @@ -51,6 +52,7 @@ impl<'a> System<'a> for FleetOwnedUpdate { | |||||
| entities, | entities, | ||||
| ships, | ships, | ||||
| mut fleets, | mut fleets, | ||||
| fleets_moving, | |||||
| fleet_owned, | fleet_owned, | ||||
| } = data; | } = data; | ||||
| @@ -103,6 +105,11 @@ impl<'a> System<'a> for FleetOwnedUpdate { | |||||
| if old_match && !new_match { | if old_match && !new_match { | ||||
| fleet.remove_ship(ship_id, ship); | 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 { | } else if !old_match && new_match { | ||||
| fleet.add_ship(ship_id, ship); | fleet.add_ship(ship_id, ship); | ||||
| } | } | ||||
| @@ -2,12 +2,16 @@ mod fleet_control; | |||||
| mod fleet_orbiting_update; | mod fleet_orbiting_update; | ||||
| mod fleet_owned_update; | mod fleet_owned_update; | ||||
| mod process; | 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_control::{FleetControl, FleetControlEvent}; | ||||
| pub use fleet_orbiting_update::FleetOrbitingUpdate; | pub use fleet_orbiting_update::FleetOrbitingUpdate; | ||||
| pub use fleet_owned_update::FleetOwnedUpdate; | pub use fleet_owned_update::FleetOwnedUpdate; | ||||
| pub use process::Process; | 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; | |||||
| @@ -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<ShipControlEvent>, | |||||
| } | |||||
| #[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<ShipControlEvent>>, | |||||
| 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<Self, Error> { | |||||
| world.insert(EventChannel::<ShipControlEvent>::default()); | |||||
| let ship_control_event_id = world.register_event_reader::<ShipControlEvent>()?; | |||||
| 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'"); | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| }, | |||||
| ); | |||||
| } | |||||
| } | |||||
| @@ -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<ComponentEvent<FleetOwned>>, | |||||
| } | |||||
| #[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::<FleetOwned>::setup(world); | |||||
| let need_update = BitSet::new(); | |||||
| let fleet_owned_id = world | |||||
| .system_data::<WriteStorage<FleetOwned>>() | |||||
| .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::<f32>(); | |||||
| 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::<f32>(); | |||||
| 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); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| }); | |||||
| } | |||||
| } | |||||
| @@ -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<ShipControlEvent>>, | |||||
| 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<Entity> { | |||||
| 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 | |||||
| } | |||||
| } | |||||
| @@ -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::<f32>(); | |||||
| 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::<f32>(); | |||||
| 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); | |||||
| } | |||||
| } | |||||
| } | |||||