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); | |||||
} | |||||
} | |||||
} |