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._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> | |||
where | |||
T: Numeric + PartialEq<S>, | |||
@@ -1,28 +1,31 @@ | |||
x -> needed | |||
o -> optional | |||
___________________________________________________________ | |||
WorldPersistence________________________________________ \ | |||
Planet_______________________________________________ \ \ | |||
Asteroid__________________________________________ \ \ \ | |||
MeetingPoint___________________________________ \ \ \ \ | |||
Obstacle____________________________________ \ \ \ \ \ | |||
Shape____________________________________ \ \ \ \ \ \ | |||
MeetingPointOwned_____________________ \ \ \ \ \ \ \ | |||
Fleet______________________________ \ \ \ \ \ \ \ \ | |||
Ship____________________________ \ \ \ \ \ \ \ \ \ | |||
FleetOwned___________________ \ \ \ \ \ \ \ \ \ \ | |||
Position__________________ \ \ \ \ \ \ \ \ \ \ \ | |||
PlayerOwned____________ \ \ \ \ \ \ \ \ \ \ \ \ | |||
Player______________ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |||
| Game Objects | | | | | | | | | | | | | | | |||
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |||
| Player | x | | | | | | | | | | | | x | | |||
| Ship | | x | x | x | x | | | | | | | | x | | |||
| Fleet | | x | | | | x | x | | | | | | x | | |||
| Asteroid | | o | x | | | | | x | x | x | x | | x | | |||
| Planet | | o | x | | | | | x | x | x | | x | x | | |||
| MeetingPoint | | | x | | | | | | | x | | | x | | |||
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |||
____________________________________________________________________ | |||
WorldPersistence_________________________________________________ \ | |||
Planet________________________________________________________ \ \ | |||
Asteroid___________________________________________________ \ \ \ | |||
MeetingPoint____________________________________________ \ \ \ \ | |||
Obstacle_____________________________________________ \ \ \ \ \ | |||
Shape_____________________________________________ \ \ \ \ \ \ | |||
FleetOrbiting__________________________________ \ \ \ \ \ \ \ | |||
FleetMoving_________________________________ \ \ \ \ \ \ \ \ | |||
Fleet____________________________________ \ \ \ \ \ \ \ \ \ | |||
ShipOrbiting__________________________ \ \ \ \ \ \ \ \ \ \ | |||
ShipMoving_________________________ \ \ \ \ \ \ \ \ \ \ \ | |||
Ship____________________________ \ \ \ \ \ \ \ \ \ \ \ \ | |||
FleetOwned___________________ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
Position__________________ \ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
PlayerOwned____________ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
Player______________ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |||
| Game Objects | | | | | | | | | | | | | | | | | | |||
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |||
| Player | x | | | | | | | | | | | | | | | x | | |||
| Ship | | x | x | x | x | o | o | | | | | | | | | x | | |||
| Fleet | | x | | | | | | x | o | o | | | | | | x | | |||
| Asteroid | | o | x | | | | | | | | x | x | x | x | | x | | |||
| Planet | | o | x | | | | | | | | x | x | x | | x | x | | |||
| MeetingPoint | | | x | | | | | | | | | | x | | | x | | |||
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
@@ -1,8 +1,5 @@ | |||
use glc::vector::{Angle, Vector2f, Vector4f}; | |||
use space_crush_common::{ | |||
components::{MeetingPoint, Position}, | |||
constants::SHIP_ORBIT_DISTANCE_MAX, | |||
}; | |||
use space_crush_common::components::{MeetingPoint, Position}; | |||
use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | |||
use crate::resources::Geometry; | |||
@@ -40,13 +37,6 @@ impl<'a> System<'a> for MeetingPoints { | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle(position.get(), meeting_point.max()), | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle( | |||
position.get(), | |||
SHIP_ORBIT_DISTANCE_MAX * meeting_point.max(), | |||
), | |||
); | |||
} | |||
gl::blend_equation(gl::FUNC_ADD); | |||
@@ -1,5 +1,7 @@ | |||
use glc::vector::Vector4f; | |||
use space_crush_common::components::{Player, PlayerOwned, Position, Ship, ShipObstacle}; | |||
use space_crush_common::components::{ | |||
Player, PlayerOwned, Position, Ship, ShipMoving, ShipObstacle, ShipOrbiting, | |||
}; | |||
use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | |||
use crate::resources::Geometry; | |||
@@ -14,6 +16,8 @@ pub struct ShipsData<'a> { | |||
player_owned: ReadStorage<'a, PlayerOwned>, | |||
players: ReadStorage<'a, Player>, | |||
ships: ReadStorage<'a, Ship>, | |||
ships_orbiting: ReadStorage<'a, ShipOrbiting>, | |||
ships_moving: ReadStorage<'a, ShipMoving>, | |||
} | |||
impl<'a> System<'a> for Ships { | |||
@@ -26,12 +30,22 @@ impl<'a> System<'a> for Ships { | |||
player_owned, | |||
players, | |||
ships, | |||
ships_orbiting, | |||
ships_moving, | |||
} = data; | |||
gl::enable(gl::BLEND); | |||
gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||
for (position, player_owned, ship) in (&positions, &player_owned, &ships).join() { | |||
let data = ( | |||
&positions, | |||
&player_owned, | |||
&ships, | |||
ships_orbiting.maybe(), | |||
ships_moving.maybe(), | |||
); | |||
for (position, player_owned, ship, ship_orbiting, ship_moving) in data.join() { | |||
let ship_pos = position.get(); | |||
let type_ = ship.type_(); | |||
let player_id = player_owned.owner(); | |||
@@ -42,22 +56,27 @@ impl<'a> System<'a> for Ships { | |||
Vector4f::new(0.0, 0.0, 1.0, 0.2), | |||
&[*ship_pos, ship_pos + ship.dir() * ship_data.speed], | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(1.0, 0.0, 0.0, 0.2), | |||
&[*ship_pos, ship_pos + ship.target_dir() * 100.0], | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(1.0, 1.0, 1.0, 0.2), | |||
&[*ship_pos, *ship.target_pos()], | |||
); | |||
if let ShipObstacle::Known(obstacle) = ship.obstacle() { | |||
let obstacle_pos = positions.get(obstacle).unwrap().get(); | |||
if let Some(ship_orbiting) = &ship_orbiting { | |||
geometry.render_lines( | |||
Vector4f::new(0.0, 1.0, 0.0, 0.2), | |||
&[*ship_pos, *obstacle_pos], | |||
Vector4f::new(1.0, 0.0, 0.0, 0.2), | |||
&[*ship_pos, ship_pos + ship_orbiting.target_dir() * 100.0], | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(1.0, 1.0, 1.0, 0.2), | |||
&[*ship_pos, *ship_orbiting.target_pos()], | |||
); | |||
} | |||
if let Some(ship_moving) = ship_moving { | |||
if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() { | |||
let obstacle_pos = positions.get(*obstacle_id).unwrap().get(); | |||
geometry.render_lines( | |||
Vector4f::new(0.0, 1.0, 0.0, 0.2), | |||
&[*ship_pos, *obstacle_pos], | |||
); | |||
} | |||
} | |||
} | |||
@@ -94,7 +94,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
let fleet_id = Fleet::builder() | |||
.player(player_id) | |||
.meeting_point(planets[1]) | |||
.orbiting(planets[1]) | |||
.build(world) | |||
.unwrap(); | |||
@@ -84,9 +84,7 @@ impl<'a> System<'a> for FleetMove { | |||
Some(target) => target, | |||
None => continue, | |||
}; | |||
let player = game_state.player_id(); | |||
let event = FleetControlEvent::MoveToMeetingPoint { | |||
player, | |||
fleet, | |||
count, | |||
target, | |||
@@ -11,7 +11,6 @@ use glc::{ | |||
use shrev::{EventChannel, ReaderId}; | |||
use space_crush_common::{ | |||
components::{Fleet, FleetOrbiting, MeetingPoint, Position, Shape, ShipCount}, | |||
constants::VECTOR_2F_POS_X, | |||
misc::{LogResult, WorldHelper as _}, | |||
resources::Global, | |||
}; | |||
@@ -260,10 +259,12 @@ impl FleetSelect { | |||
self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom; | |||
self.ring1 = self.ring0 + FLEET_SELECT_WIDTH / self.zoom; | |||
const VECTOR2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0); | |||
let is_simple = self.select_mode.is_simple(); | |||
let angle = self | |||
.marker | |||
.angle2(&VECTOR_2F_POS_X) | |||
.angle2(&VECTOR2F_POS_X) | |||
.normalize() | |||
.into_deg() | |||
.into_inner(); | |||
@@ -1,7 +1,10 @@ | |||
use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | |||
use specs::{ | |||
saveload::MarkedBuilder, world::EntitiesRes, Builder, Entity, LazyUpdate, World, WorldExt, | |||
WriteStorage, | |||
}; | |||
use crate::{ | |||
components::{Fleet, FleetOrbiting, PlayerOwned}, | |||
components::{Fleet, FleetMoving, FleetOrbiting, PlayerOwned}, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
@@ -10,28 +13,71 @@ use super::Error; | |||
#[derive(Default, Clone)] | |||
pub struct FleetBuilder { | |||
player: Option<Entity>, | |||
meeting_point: Option<Entity>, | |||
state: Option<State>, | |||
} | |||
#[derive(Clone)] | |||
enum State { | |||
Orbiting(Entity), | |||
Moving(Entity), | |||
} | |||
impl FleetBuilder { | |||
pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | |||
let player = self.player.ok_or(Error::MissingValue("player"))?; | |||
let meeting_point = self | |||
.meeting_point | |||
.ok_or(Error::MissingValue("meeting_point"))?; | |||
let state = self.state.as_ref().ok_or(Error::MissingValue("state"))?; | |||
let player_owned = PlayerOwned::new(player); | |||
let fleet_orbiting = FleetOrbiting::new(meeting_point); | |||
let fleet = Fleet::new(); | |||
let entity = world | |||
let builder = world | |||
.create_entity() | |||
.marked::<<WorldPersistence as Persistence>::Marker>() | |||
.with(player_owned) | |||
.with(fleet_orbiting) | |||
.with(fleet) | |||
.with(fleet); | |||
let builder = match state { | |||
State::Moving(target) => builder.with(FleetMoving::new(*target)), | |||
State::Orbiting(meeting_point) => builder.with(FleetOrbiting::new(*meeting_point)), | |||
}; | |||
let entity = builder.build(); | |||
Ok(entity) | |||
} | |||
pub fn lazy( | |||
&self, | |||
lazy: &LazyUpdate, | |||
entities: &EntitiesRes, | |||
players_owned: &mut WriteStorage<'_, PlayerOwned>, | |||
fleets: &mut WriteStorage<'_, Fleet>, | |||
fleets_moving: &mut WriteStorage<'_, FleetMoving>, | |||
fleets_orbiting: &mut WriteStorage<'_, FleetOrbiting>, | |||
) -> Result<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(); | |||
players_owned.insert(entity, player_owned)?; | |||
fleets.insert(entity, fleet)?; | |||
match state { | |||
State::Moving(target) => { | |||
fleets_moving.insert(entity, FleetMoving::new(*target))?; | |||
} | |||
State::Orbiting(meeting_point) => { | |||
fleets_orbiting.insert(entity, FleetOrbiting::new(*meeting_point))?; | |||
} | |||
}; | |||
Ok(entity) | |||
} | |||
@@ -41,8 +87,14 @@ impl FleetBuilder { | |||
self | |||
} | |||
pub fn meeting_point(mut self, meeting_point: Entity) -> Self { | |||
self.meeting_point = Some(meeting_point); | |||
pub fn orbiting(mut self, meeting_point: Entity) -> Self { | |||
self.state = Some(State::Orbiting(meeting_point)); | |||
self | |||
} | |||
pub fn moving(mut self, target: Entity) -> Self { | |||
self.state = Some(State::Moving(target)); | |||
self | |||
} | |||
@@ -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 fleet; | |||
mod meeting_point; | |||
mod planet; | |||
mod player; | |||
mod ship; | |||
pub use asteroid::AsteroidBuilder; | |||
pub use fleet::FleetBuilder; | |||
pub use meeting_point::MeetingPointBuilder; | |||
pub use planet::PlanetBuilder; | |||
pub use player::PlayerBuilder; | |||
pub use ship::ShipBuilder; | |||
@@ -3,7 +3,7 @@ use rand::random; | |||
use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | |||
use crate::{ | |||
components::{FleetOwned, PlayerOwned, Position, Ship, ShipType}, | |||
components::{FleetOwned, PlayerOwned, Position, Ship, ShipOrbiting, ShipType}, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
@@ -30,6 +30,7 @@ impl ShipBuilder { | |||
let fleet_owned = FleetOwned::new(fleet); | |||
let position = Position::new(position); | |||
let ship = Ship::new(type_, direction); | |||
let ship_orbiting = ShipOrbiting::new(); | |||
let entity = world | |||
.create_entity() | |||
@@ -38,6 +39,7 @@ impl ShipBuilder { | |||
.with(fleet_owned) | |||
.with(position) | |||
.with(ship) | |||
.with(ship_orbiting) | |||
.build(); | |||
Ok(entity) | |||
@@ -30,6 +30,11 @@ pub struct FleetOrbiting { | |||
meeting_point: Entity, | |||
} | |||
#[derive(Debug, Clone)] | |||
pub struct FleetMoving { | |||
target: Entity, | |||
} | |||
#[derive(Serialize, Deserialize)] | |||
pub struct FleetOwnedData<M> { | |||
pub owner: M, | |||
@@ -40,6 +45,11 @@ pub struct FleetOrbitingData<M> { | |||
pub meeting_point: M, | |||
} | |||
#[derive(Serialize, Deserialize)] | |||
pub struct FleetMovingData<M> { | |||
pub target: M, | |||
} | |||
/* Fleet */ | |||
impl Fleet { | |||
@@ -57,6 +67,11 @@ impl Fleet { | |||
pub fn count(&self) -> &ShipCount { | |||
&self.count | |||
} | |||
#[inline] | |||
pub fn is_empty(&self) -> bool { | |||
self.count.total() == 0 | |||
} | |||
} | |||
impl Fleet { | |||
@@ -177,3 +192,47 @@ where | |||
Ok(FleetOrbiting { meeting_point }) | |||
} | |||
} | |||
/* FleetMoving */ | |||
impl FleetMoving { | |||
pub fn target(&self) -> Entity { | |||
self.target | |||
} | |||
} | |||
impl FleetMoving { | |||
pub(crate) fn new(target: Entity) -> Self { | |||
Self { target } | |||
} | |||
} | |||
impl Component for FleetMoving { | |||
type Storage = FlaggedStorage<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, | |||
}; | |||
use crate::misc::FlaggedStorage; | |||
use crate::{builder::MeetingPointBuilder, misc::FlaggedStorage}; | |||
/// Point in the universe fleets can meet | |||
#[derive(Clone, Debug, Default)] | |||
@@ -40,6 +40,11 @@ type Fleets = SmallVec<[Option<Entity>; 8]>; | |||
/* MeetingPoint */ | |||
impl MeetingPoint { | |||
#[inline] | |||
pub fn builder() -> MeetingPointBuilder { | |||
MeetingPointBuilder::default() | |||
} | |||
#[inline] | |||
pub fn min(&self) -> f32 { | |||
self.min | |||
@@ -9,11 +9,13 @@ mod shape; | |||
mod ship; | |||
pub use asteroid::{Asteroid, Type as AsteroidType}; | |||
pub use fleet::{Fleet, FleetOrbiting, FleetOwned}; | |||
pub use fleet::{Fleet, FleetMoving, FleetOrbiting, FleetOwned}; | |||
pub use meeting_point::MeetingPoint; | |||
pub use obstacle::Obstacle; | |||
pub use planet::Planet; | |||
pub use player::{Player, PlayerOwned, Race}; | |||
pub use position::Position; | |||
pub use shape::Shape; | |||
pub use ship::{Ship, ShipCount, ShipData, ShipObstacle, ShipType, ShipsData}; | |||
pub use ship::{ | |||
Ship, ShipCount, ShipData, ShipMoving, ShipObstacle, ShipOrbiting, ShipType, ShipsData, | |||
}; |
@@ -61,6 +61,11 @@ impl Player { | |||
pub fn ship_data(&self, type_: ShipType) -> &ShipData { | |||
&self.ships_data[type_] | |||
} | |||
#[inline] | |||
pub fn ships_data(&self) -> &ShipsData { | |||
&self.ships_data | |||
} | |||
} | |||
impl Player { | |||
@@ -1,7 +1,7 @@ | |||
use std::cmp::min; | |||
use std::ops::{Index, IndexMut, Mul}; | |||
use glc::vector::Vector2f; | |||
use glc::vector::{Angle, Vector2f}; | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, Entity, VecStorage}; | |||
@@ -11,13 +11,18 @@ use crate::{builder::ShipBuilder, misc::FlaggedStorage}; | |||
pub struct Ship { | |||
type_: ShipType, | |||
dir: Vector2f, | |||
} | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct ShipMoving { | |||
#[serde(skip)] | |||
obstacle: ShipObstacle, | |||
} | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct ShipOrbiting { | |||
#[serde(skip)] | |||
target_pos: Vector2f, | |||
#[serde(skip)] | |||
target_dir: Vector2f, | |||
} | |||
@@ -46,6 +51,7 @@ pub struct ShipsData { | |||
#[derive(Clone, Default, Debug)] | |||
pub struct ShipData { | |||
pub speed: f32, | |||
pub agility: Angle<f32>, | |||
} | |||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] | |||
@@ -72,30 +78,65 @@ impl Ship { | |||
pub fn dir(&self) -> &Vector2f { | |||
&self.dir | |||
} | |||
} | |||
impl Ship { | |||
#[inline] | |||
pub fn target_pos(&self) -> &Vector2f { | |||
&self.target_pos | |||
pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { | |||
Self { type_, dir } | |||
} | |||
#[inline] | |||
pub fn target_dir(&self) -> &Vector2f { | |||
&self.target_dir | |||
pub(crate) fn dir_mut(&mut self) -> &mut Vector2f { | |||
&mut self.dir | |||
} | |||
} | |||
impl Component for Ship { | |||
type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||
} | |||
/* ShipMoving */ | |||
impl ShipMoving { | |||
pub fn obstacle(&self) -> &ShipObstacle { | |||
&self.obstacle | |||
} | |||
} | |||
impl ShipMoving { | |||
#[inline] | |||
pub fn obstacle(&self) -> ShipObstacle { | |||
self.obstacle | |||
pub(crate) fn new() -> Self { | |||
Self { | |||
obstacle: ShipObstacle::Search, | |||
} | |||
} | |||
pub(crate) fn obstacle_mut(&mut self) -> &mut ShipObstacle { | |||
&mut self.obstacle | |||
} | |||
} | |||
impl Ship { | |||
impl Component for ShipMoving { | |||
type Storage = VecStorage<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] | |||
pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { | |||
pub(crate) fn new() -> Self { | |||
Self { | |||
type_, | |||
dir, | |||
obstacle: Default::default(), | |||
target_pos: Default::default(), | |||
target_dir: Default::default(), | |||
} | |||
@@ -106,20 +147,10 @@ impl Ship { | |||
self.target_pos = pos; | |||
self.target_dir = dir; | |||
} | |||
#[inline] | |||
pub(crate) fn dir_mut(&mut self) -> &mut Vector2f { | |||
&mut self.dir | |||
} | |||
#[inline] | |||
pub(crate) fn obstacle_mut(&mut self) -> &mut ShipObstacle { | |||
&mut self.obstacle | |||
} | |||
} | |||
impl Component for Ship { | |||
type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||
impl Component for ShipOrbiting { | |||
type Storage = VecStorage<Self>; | |||
} | |||
/* Obstacle */ | |||
@@ -1,35 +1,61 @@ | |||
use glc::{matrix::Angle, vector::Vector2f}; | |||
use glc::matrix::Angle; | |||
use crate::components::{ShipData, ShipsData}; | |||
/// Distance to orbit before ship is handled as "in orbit" in % | |||
pub const SHIP_ORBIT_DISTANCE_MAX: f32 = 1.10; | |||
/* Constants for ships in orbit */ | |||
/// Minimum angle between old and new target position in orbit | |||
pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle<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_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 { | |||
fighter: ShipData { speed: 120.0 }, | |||
bomber: ShipData { speed: 80.0 }, | |||
transporter: ShipData { speed: 100.0 }, | |||
fighter: ShipData { | |||
speed: 120.0, | |||
agility: Angle::Deg(120.0), | |||
}, | |||
bomber: ShipData { | |||
speed: 80.0, | |||
agility: Angle::Deg(80.0), | |||
}, | |||
transporter: ShipData { | |||
speed: 100.0, | |||
agility: Angle::Deg(100.0), | |||
}, | |||
}; | |||
pub const SHIPS_DATA_TRADER: ShipsData = ShipsData { | |||
fighter: ShipData { speed: 100.0 }, | |||
bomber: ShipData { speed: 70.0 }, | |||
transporter: ShipData { speed: 120.0 }, | |||
fighter: ShipData { | |||
speed: 100.0, | |||
agility: Angle::Deg(100.0), | |||
}, | |||
bomber: ShipData { | |||
speed: 70.0, | |||
agility: Angle::Deg(70.0), | |||
}, | |||
transporter: ShipData { | |||
speed: 120.0, | |||
agility: Angle::Deg(120.0), | |||
}, | |||
}; | |||
pub const SHIPS_DATA_RESEARCHER: ShipsData = ShipsData { | |||
fighter: ShipData { speed: 90.0 }, | |||
bomber: ShipData { speed: 60.0 }, | |||
transporter: ShipData { speed: 100.0 }, | |||
fighter: ShipData { | |||
speed: 90.0, | |||
agility: Angle::Deg(90.0), | |||
}, | |||
bomber: ShipData { | |||
speed: 60.0, | |||
agility: Angle::Deg(60.0), | |||
}, | |||
transporter: ShipData { | |||
speed: 100.0, | |||
agility: Angle::Deg(100.0), | |||
}, | |||
}; |
@@ -3,7 +3,10 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||
use crate::{ | |||
components::Player, | |||
resources::Global, | |||
systems::{FleetControl, FleetOrbitingUpdate, FleetOwnedUpdate, Process, ShipMovement, Ships}, | |||
systems::{ | |||
FleetControl, FleetOrbitingUpdate, FleetOwnedUpdate, Process, ShipControl, ShipsMovement, | |||
ShipsMoving, ShipsOrbiting, | |||
}, | |||
Error, | |||
}; | |||
@@ -19,8 +22,10 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||
let mut dispatcher = DispatcherBuilder::new() | |||
.with(Process::default(), "process", &[]) | |||
.with(ShipMovement::default(), "ship_movement", &[]) | |||
.with(Ships::new(world), "ships", &[]) | |||
.with(ShipControl::new(world)?, "ship_control", &[]) | |||
.with(ShipsOrbiting::default(), "ships_orbiting", &[]) | |||
.with(ShipsMoving::default(), "ships_moving", &[]) | |||
.with(ShipsMovement::default(), "ships_movement", &[]) | |||
.with(FleetControl::new(world)?, "fleet_control", &[]) | |||
.with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | |||
.with(FleetOrbitingUpdate::new(world), "fleet_orbiting", &[]) | |||
@@ -8,7 +8,7 @@ use specs::{saveload::SimpleMarker, World}; | |||
use crate::{ | |||
components::{ | |||
Asteroid, FleetOrbiting, FleetOwned, MeetingPoint, Obstacle, Planet, Player, PlayerOwned, | |||
Position, Ship, | |||
Position, Ship, ShipMoving, ShipOrbiting, | |||
}, | |||
Error, | |||
}; | |||
@@ -96,6 +96,8 @@ impl Persistence for WorldPersistence { | |||
FleetOwned, | |||
FleetOrbiting, | |||
Ship, | |||
ShipMoving, | |||
ShipOrbiting, | |||
Obstacle, | |||
MeetingPoint, | |||
Planet, | |||
@@ -1,9 +1,4 @@ | |||
// TODO | |||
#![allow(dead_code)] | |||
#![allow(unused_mut)] | |||
#![allow(unused_imports)] | |||
#![allow(unused_variables)] | |||
use glc::vector::Vector2f; | |||
use shrev::{EventChannel, ReaderId}; | |||
use specs::{ | |||
prelude::*, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, System, World, WriteStorage, | |||
@@ -11,9 +6,11 @@ use specs::{ | |||
use crate::{ | |||
components::{ | |||
Fleet, FleetOrbiting, FleetOwned, MeetingPoint, Player, PlayerOwned, Ship, ShipCount, | |||
Fleet, FleetMoving, FleetOrbiting, MeetingPoint, PlayerOwned, Position, Ship, ShipCount, | |||
ShipType, | |||
}, | |||
misc::WorldHelper, | |||
misc::{LogResult, WorldHelper}, | |||
systems::ShipControlEvent, | |||
Error, | |||
}; | |||
@@ -24,24 +21,50 @@ pub struct FleetControl { | |||
#[derive(Debug)] | |||
pub enum FleetControlEvent { | |||
MoveToMeetingPoint { | |||
player: Entity, | |||
fleet: Entity, | |||
count: ShipCount, | |||
target: Entity, | |||
}, | |||
MoveToPosition { | |||
fleet: Entity, | |||
count: ShipCount, | |||
target: Vector2f, | |||
}, | |||
} | |||
#[derive(SystemData)] | |||
pub struct FleetControlData<'a> { | |||
fleet_control_events: ReadExpect<'a, EventChannel<FleetControlEvent>>, | |||
lazy: Read<'a, LazyUpdate>, | |||
entities: Entities<'a>, | |||
fleet_control_events: ReadExpect<'a, EventChannel<FleetControlEvent>>, | |||
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>, | |||
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>, | |||
players: ReadStorage<'a, Player>, | |||
fleets_moving: WriteStorage<'a, FleetMoving>, | |||
fleets_orbiting: WriteStorage<'a, FleetOrbiting>, | |||
positions: WriteStorage<'a, Position>, | |||
meeting_points: WriteStorage<'a, MeetingPoint>, | |||
ships: ReadStorage<'a, Ship>, | |||
} | |||
@@ -62,77 +85,114 @@ impl<'a> System<'a> for FleetControl { | |||
fn run(&mut self, data: Self::SystemData) { | |||
let FleetControlData { | |||
fleet_control_events, | |||
lazy, | |||
entities, | |||
fleet_control_events, | |||
mut player_owned, | |||
mut fleet_orbiting, | |||
mut fleet_owned, | |||
mut meeting_points, | |||
mut fleets, | |||
players, | |||
player_owned, | |||
ship_control, | |||
fleets, | |||
fleets_moving, | |||
fleets_orbiting, | |||
positions, | |||
meeting_points, | |||
ships, | |||
} = data; | |||
/* TODO | |||
let mut processor = Processor { | |||
lazy, | |||
entities, | |||
player_owned, | |||
ship_control, | |||
fleets, | |||
fleets_moving, | |||
fleets_orbiting, | |||
positions, | |||
meeting_points, | |||
ships, | |||
}; | |||
let events = fleet_control_events.read(&mut self.fleet_control_event_id); | |||
for event in events { | |||
match event { | |||
FleetControlEvent::Move(args) if args.count.is_all() => { | |||
let orbit_owned = continue_if_none!(fleet_orbiting.get_mut(args.fleet)); | |||
orbit_owned.set_owner(args.target); | |||
FleetControlEvent::MoveToPosition { | |||
fleet, | |||
count, | |||
target, | |||
} => { | |||
processor.move_to_position(*fleet, *count, *target); | |||
} | |||
FleetControlEvent::MoveToMeetingPoint { | |||
fleet, | |||
count, | |||
target, | |||
} => { | |||
processor.move_to_meeting_point(*fleet, *count, *target); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
impl Processor<'_> { | |||
fn move_to_position(&mut self, fleet: Entity, count: ShipCount, target: Vector2f) { | |||
let target = self.create_meeting_point(target); | |||
self.move_to_meeting_point(fleet, count, target); | |||
} | |||
fn move_to_meeting_point(&mut self, fleet: Entity, mut count: ShipCount, target: Entity) { | |||
let player_owned = self.player_owned.get(fleet).unwrap(); | |||
let player_id = player_owned.owner(); | |||
let target_fleet = self.create_fleet(player_id, target); | |||
let source_fleet = self.fleets.get(fleet).unwrap(); | |||
let data = (&self.entities, source_fleet.owned(), &self.ships); | |||
for (id, _, ship) in data.join() { | |||
match ship.type_() { | |||
ShipType::Fighter if count.fighter > 0 => { | |||
count.fighter -= 1; | |||
} | |||
ShipType::Bomber if count.bomber > 0 => { | |||
count.bomber -= 1; | |||
} | |||
FleetControlEvent::Move(args) => { | |||
let target_orbit = continue_if_none!(meeting_points.get_mut(args.target)); | |||
let player = continue_if_none!(players.get(args.player)); | |||
let mut count = args.count; | |||
let target_fleet = match target_orbit.fleet_mut(player.index()) { | |||
Some(fleet) => *fleet, | |||
f @ None => { | |||
let fleet = lazy | |||
.create_entity(&entities) | |||
.marked::<<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 crate::{ | |||
components::{Fleet, FleetOwned, Ship}, | |||
misc::{ComponentEvent, StorageHelper, StorageHelperMut}, | |||
components::{Fleet, FleetMoving, FleetOwned, Ship}, | |||
misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, | |||
}; | |||
pub struct FleetOwnedUpdate { | |||
@@ -40,6 +40,7 @@ pub struct FleetOwnedUpdateData<'a> { | |||
entities: Entities<'a>, | |||
ships: ReadStorage<'a, Ship>, | |||
fleets: WriteStorage<'a, Fleet>, | |||
fleets_moving: ReadStorage<'a, FleetMoving>, | |||
fleet_owned: ReadStorage<'a, FleetOwned>, | |||
} | |||
@@ -51,6 +52,7 @@ impl<'a> System<'a> for FleetOwnedUpdate { | |||
entities, | |||
ships, | |||
mut fleets, | |||
fleets_moving, | |||
fleet_owned, | |||
} = data; | |||
@@ -103,6 +105,11 @@ impl<'a> System<'a> for FleetOwnedUpdate { | |||
if old_match && !new_match { | |||
fleet.remove_ship(ship_id, ship); | |||
if fleet.is_empty() && fleets_moving.contains(fleet_id) { | |||
entities | |||
.delete(fleet_id) | |||
.panic("Unable to delete entity for moving fleet!"); | |||
} | |||
} else if !old_match && new_match { | |||
fleet.add_ship(ship_id, ship); | |||
} | |||
@@ -2,12 +2,16 @@ mod fleet_control; | |||
mod fleet_orbiting_update; | |||
mod fleet_owned_update; | |||
mod process; | |||
mod ship_movement; | |||
mod ships; | |||
mod ship_control; | |||
mod ships_movement; | |||
mod ships_moving; | |||
mod ships_orbiting; | |||
pub use fleet_control::{FleetControl, FleetControlEvent}; | |||
pub use fleet_orbiting_update::FleetOrbitingUpdate; | |||
pub use fleet_owned_update::FleetOwnedUpdate; | |||
pub use process::Process; | |||
pub use ship_movement::ShipMovement; | |||
pub use ships::Ships; | |||
pub use ship_control::{ShipControl, ShipControlEvent}; | |||
pub use ships_movement::ShipsMovement; | |||
pub use ships_moving::ShipsMoving; | |||
pub use ships_orbiting::ShipsOrbiting; |
@@ -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); | |||
} | |||
} | |||
} |