3 Commits

Author SHA1 Message Date
  Bergmann89 a1a30ecf96 Refactored ship movement 3 years ago
  Bergmann89 b3d139a4e7 Striker error handling 3 years ago
  Bergmann89 7f82b5430b Renamed 'MeetingPointOwned' to 'FleetOrbiting' 3 years ago
32 changed files with 1315 additions and 737 deletions
Split View
  1. +9
    -0
      glc/src/angle.rs
  2. +28
    -25
      overview
  3. +5
    -3
      space-crush-app/src/debug/fleets.rs
  4. +1
    -11
      space-crush-app/src/debug/meeting_points.rs
  5. +34
    -19
      space-crush-app/src/debug/ships.rs
  6. +1
    -1
      space-crush-app/src/main.rs
  7. +8
    -7
      space-crush-app/src/render/fleet_move.rs
  8. +30
    -53
      space-crush-app/src/render/fleet_select.rs
  9. +64
    -12
      space-crush-common/src/builder/fleet.rs
  10. +65
    -0
      space-crush-common/src/builder/meeting_point.rs
  11. +2
    -0
      space-crush-common/src/builder/mod.rs
  12. +3
    -1
      space-crush-common/src/builder/ship.rs
  13. +113
    -2
      space-crush-common/src/components/fleet.rs
  14. +7
    -46
      space-crush-common/src/components/meeting_point.rs
  15. +5
    -3
      space-crush-common/src/components/mod.rs
  16. +5
    -0
      space-crush-common/src/components/player.rs
  17. +56
    -25
      space-crush-common/src/components/ship.rs
  18. +44
    -18
      space-crush-common/src/constants.rs
  19. +7
    -8
      space-crush-common/src/dispatcher.rs
  20. +0
    -1
      space-crush-common/src/lib.rs
  21. +0
    -29
      space-crush-common/src/macros.rs
  22. +7
    -4
      space-crush-common/src/misc/world.rs
  23. +133
    -73
      space-crush-common/src/systems/fleet_control.rs
  24. +44
    -42
      space-crush-common/src/systems/fleet_orbiting_update.rs
  25. +9
    -2
      space-crush-common/src/systems/fleet_owned_update.rs
  26. +10
    -6
      space-crush-common/src/systems/mod.rs
  27. +166
    -0
      space-crush-common/src/systems/ship_control.rs
  28. +0
    -48
      space-crush-common/src/systems/ship_movement.rs
  29. +0
    -298
      space-crush-common/src/systems/ships.rs
  30. +82
    -0
      space-crush-common/src/systems/ships_movement.rs
  31. +239
    -0
      space-crush-common/src/systems/ships_moving.rs
  32. +138
    -0
      space-crush-common/src/systems/ships_orbiting.rs

+ 9
- 0
glc/src/angle.rs View File

@@ -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>,


+ 28
- 25
overview View File

@@ -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 |
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

+ 5
- 3
space-crush-app/src/debug/fleets.rs View File

@@ -1,6 +1,5 @@
use space_crush_common::{
components::{Fleet, MeetingPoint, Position},
continue_if_none,
misc::LogResult,
};
use specs::{prelude::*, ReadExpect, ReadStorage, System, World};
@@ -58,8 +57,11 @@ impl<'a> System<'a> for Fleets {
gl::enable(gl::BLEND);

for (position, meeting_point) in (&positions, &meeting_points).join() {
let fleet_id = continue_if_none!(meeting_point.fleet(player_index));
let fleet = continue_if_none!(fleets.get(fleet_id));
let fleet_id = match meeting_point.fleet(player_index) {
Some(fleet_id) => fleet_id,
None => continue,
};
let fleet = fleets.get(fleet_id).unwrap();

gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::blend_equation(gl::FUNC_SUBTRACT);


+ 1
- 11
space-crush-app/src/debug/meeting_points.rs View File

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


+ 34
- 19
space-crush-app/src/debug/ships.rs View File

@@ -1,7 +1,6 @@
use glc::vector::Vector4f;
use space_crush_common::{
components::{Player, PlayerOwned, Position, Ship, ShipObstacle},
continue_if_none,
use space_crush_common::components::{
Player, PlayerOwned, Position, Ship, ShipMoving, ShipObstacle, ShipOrbiting,
};
use specs::{prelude::*, ReadStorage, System, World, WriteExpect};

@@ -17,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 {
@@ -29,39 +30,53 @@ 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();
let player = continue_if_none!(players.get(player_id));
let player = players.get(player_id).unwrap();
let ship_data = player.ship_data(type_);

geometry.render_lines(
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 = continue_if_none!(positions.get(obstacle));
let obstacle_pos = obstacle_pos.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],
);
}
}
}



+ 1
- 1
space-crush-app/src/main.rs View File

@@ -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();



+ 8
- 7
space-crush-app/src/render/fleet_move.rs View File

@@ -1,7 +1,6 @@
use shrev::{EventChannel, ReaderId};
use space_crush_common::{
components::{MeetingPoint, Position},
continue_if_none,
misc::WorldHelper,
systems::FleetControlEvent,
};
@@ -77,13 +76,15 @@ impl<'a> System<'a> for FleetMove {
}
}
MouseEvent::ButtonUp(button) if button == &config.input.fleet_move_button => {
let selection = game_state.selection_mut().take();
let selection = continue_if_none!(selection);
let Selection { fleet, count } = selection;
let target = continue_if_none!(self.target_meeting_point);
let player = game_state.player_id();
let Selection { fleet, count } = match game_state.selection_mut().take() {
Some(selection) => selection,
None => continue,
};
let target = match self.target_meeting_point {
Some(target) => target,
None => continue,
};
let event = FleetControlEvent::MoveToMeetingPoint {
player,
fleet,
count,
target,


+ 30
- 53
space-crush-app/src/render/fleet_select.rs View File

@@ -10,12 +10,9 @@ use glc::{
};
use shrev::{EventChannel, ReaderId};
use space_crush_common::{
components::{Fleet, MeetingPoint, MeetingPointOwned, Position, Shape, ShipCount},
constants::VECTOR_2F_POS_X,
continue_if_none,
components::{Fleet, FleetOrbiting, MeetingPoint, Position, Shape, ShipCount},
misc::{LogResult, WorldHelper as _},
resources::Global,
return_if_none,
};
use specs::{prelude::*, ReadExpect, ReadStorage, System, World};

@@ -80,44 +77,12 @@ pub struct FleetSelectData<'a> {
global: ReadExpect<'a, Global>,
config: ReadExpect<'a, Config>,

meeting_point_owned: ReadStorage<'a, MeetingPointOwned>,
positions: ReadStorage<'a, Position>,
shapes: ReadStorage<'a, Shape>,
meeting_points: ReadStorage<'a, MeetingPoint>,
fleets: ReadStorage<'a, Fleet>,
}

macro_rules! selection {
(&$data:expr) => {
return_if_none!($data.game_state.selection())
};
(&mut $data:expr) => {
return_if_none!($data.game_state.selection_mut())
};
}

macro_rules! fleet {
(&$data:expr, $id:expr) => {
return_if_none!($data.fleets.get($id))
};
}

macro_rules! position {
(&$data:expr, $id:expr) => {
return_if_none!($data.positions.get($id))
};
}

macro_rules! shape {
(&$data:expr, $id:expr) => {
return_if_none!($data.shapes.get($id))
};
}

macro_rules! meeting_point_owned {
(&$data:expr, $id:expr) => {
return_if_none!($data.meeting_point_owned.get($id))
};
fleets: ReadStorage<'a, Fleet>,
fleets_orbiting: ReadStorage<'a, FleetOrbiting>,
}

impl FleetSelect {
@@ -197,7 +162,10 @@ impl FleetSelect {
let r = meeting_point.max() * meeting_point.max();
if (position.get() - pos).length_sqr() <= r {
let player_index = d.game_state.player_index();
let fleet_id = continue_if_none!(meeting_point.fleet(player_index));
let fleet_id = match meeting_point.fleet(player_index) {
Some(fleet_id) => fleet_id,
None => continue,
};

*d.game_state.selection_mut() = match selection {
Some(s) if s.fleet == fleet_id => {
@@ -215,8 +183,8 @@ impl FleetSelect {
}
};

let selection = selection!(&d);
let fleet = fleet!(&d, selection.fleet);
let selection = d.game_state.selection().as_ref().unwrap();
let fleet = d.fleets.get(selection.fleet).unwrap();
let timeout = Instant::now() + FLEET_SELECT_DETAIL_TIMEOUT;

self.mouse_pos = d.input_state.mouse_pos;
@@ -231,21 +199,21 @@ impl FleetSelect {
MouseEvent::ButtonUp(button) if button == &d.config.input.fleet_select_button => {
self.select_mode = match self.select_mode {
SelectMode::Simple(progress) => {
selection!(&mut d).count = self.count;
d.game_state.selection_mut().as_mut().unwrap().count = self.count;

self.mouse_pos = d.input_state.mouse_pos;

SelectMode::SimpleClose(progress)
}
SelectMode::Detail(progress) => {
selection!(&mut d).count = self.count;
d.game_state.selection_mut().as_mut().unwrap().count = self.count;

self.mouse_pos = d.input_state.mouse_pos;

SelectMode::DetailClose(progress)
}
SelectMode::Init(_) if self.is_new_selection => {
selection!(&mut d).count = ShipCount::all();
d.game_state.selection_mut().as_mut().unwrap().count = ShipCount::all();

SelectMode::None
}
@@ -277,11 +245,13 @@ impl FleetSelect {
}

/* calculate values */
let selection = selection!(&d);
let meeting_point_owned = meeting_point_owned!(&d, selection.fleet);
let position = position!(&d, meeting_point_owned.owner());
let shape = shape!(&d, meeting_point_owned.owner());
let fleet = fleet!(&d, selection.fleet);
let selection = d.game_state.selection().as_ref().unwrap();
let fleet_id = selection.fleet;
let fleet = d.fleets.get(fleet_id).unwrap();
let fleet_orbiting = d.fleets_orbiting.get(fleet_id).unwrap();
let meeting_point_id = fleet_orbiting.meeting_point();
let position = d.positions.get(meeting_point_id).unwrap();
let shape = d.shapes.get(meeting_point_id).unwrap();

self.marker = d.camera.view_to_world(self.mouse_pos) - position.get();
self.zoom = d.camera.view().axis_x.as_vec3().length();
@@ -289,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();
@@ -432,9 +404,14 @@ impl FleetSelect {
};

/* extract system data */
let selection = selection!(&d);
let meeting_point_owned = meeting_point_owned!(&d, selection.fleet);
let position = position!(&d, meeting_point_owned.owner());
let selection = d.game_state.selection().as_ref().unwrap();
let fleet_id = selection.fleet;
let fleet_orbiting = d
.fleets_orbiting
.get(fleet_id)
.expect("Selection contains invalid fleet!");
let meeting_point_id = fleet_orbiting.meeting_point();
let position = d.positions.get(meeting_point_id).unwrap();

/* calculate shared values */
let size = self.ring1 + 50.0;


+ 64
- 12
space-crush-common/src/builder/fleet.rs View File

@@ -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, MeetingPointOwned, 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 meeting_point_owned = MeetingPointOwned::new(meeting_point);
let fleet = Fleet::new();

let entity = world
let builder = world
.create_entity()
.marked::<<WorldPersistence as Persistence>::Marker>()
.with(meeting_point_owned)
.with(player_owned)
.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
}


+ 65
- 0
space-crush-common/src/builder/meeting_point.rs View File

@@ -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
}
}

+ 2
- 0
space-crush-common/src/builder/mod.rs View File

@@ -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
- 1
space-crush-common/src/builder/ship.rs View File

@@ -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)


+ 113
- 2
space-crush-common/src/components/fleet.rs View File

@@ -14,24 +14,42 @@ use crate::{
misc::FlaggedStorage,
};

/// A fleet is a group of ships that share the same operation
#[derive(Debug, Clone)]
pub struct Fleet {
owned: BitSet,
count: ShipCount,
}

/// Entities with this component are owned by a certain fleet entity
#[derive(Copy, Clone, Debug)]
pub struct FleetOwned {
owner: Entity,
}

#[derive(Debug, Clone)]
pub struct FleetOrbiting {
meeting_point: Entity,
}

#[derive(Debug, Clone)]
pub struct FleetMoving {
target: Entity,
}

#[derive(Serialize, Deserialize)]
pub struct FleetOwnedData<M> {
pub owner: M,
}

#[derive(Serialize, Deserialize)]
pub struct FleetOrbitingData<M> {
pub meeting_point: M,
}

#[derive(Serialize, Deserialize)]
pub struct FleetMovingData<M> {
pub target: M,
}

/* Fleet */

impl Fleet {
@@ -49,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 {
@@ -125,3 +148,91 @@ where
Ok(FleetOwned { owner })
}
}

/* FleetOrbiting */

impl FleetOrbiting {
pub fn meeting_point(&self) -> Entity {
self.meeting_point
}
}

impl FleetOrbiting {
pub(crate) fn new(meeting_point: Entity) -> Self {
Self { meeting_point }
}
}

impl Component for FleetOrbiting {
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
}

impl<M> ConvertSaveload<M> for FleetOrbiting
where
for<'de> M: Marker + Serialize + Deserialize<'de>,
{
type Data = FleetOrbitingData<M>;
type Error = NoError;

fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
where
F: FnMut(Entity) -> Option<M>,
{
let meeting_point = ids(self.meeting_point).unwrap();

Ok(FleetOrbitingData { meeting_point })
}

fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
where
F: FnMut(M) -> Option<Entity>,
{
let meeting_point = ids(data.meeting_point).unwrap();

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
- 46
space-crush-common/src/components/meeting_point.rs View File

@@ -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
@@ -84,7 +89,7 @@ impl MeetingPoint {
}

impl Component for MeetingPoint {
type Storage = HashMapStorage<Self>;
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
}

impl<M> ConvertSaveload<M> for MeetingPoint
@@ -121,47 +126,3 @@ where
Ok(MeetingPoint { min, max, fleets })
}
}

/* MeetingPointOwned */

impl MeetingPointOwned {
pub fn owner(&self) -> Entity {
self.owner
}
}

impl MeetingPointOwned {
pub(crate) fn new(owner: Entity) -> Self {
Self { owner }
}
}

impl Component for MeetingPointOwned {
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
}

impl<M> ConvertSaveload<M> for MeetingPointOwned
where
for<'de> M: Marker + Serialize + Deserialize<'de>,
{
type Data = MeetingPointOwnedData<M>;
type Error = NoError;

fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
where
F: FnMut(Entity) -> Option<M>,
{
let owner = ids(self.owner).unwrap();

Ok(MeetingPointOwnedData { owner })
}

fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
where
F: FnMut(M) -> Option<Entity>,
{
let owner = ids(data.owner).unwrap();

Ok(MeetingPointOwned { owner })
}
}

+ 5
- 3
space-crush-common/src/components/mod.rs View File

@@ -9,11 +9,13 @@ mod shape;
mod ship;

pub use asteroid::{Asteroid, Type as AsteroidType};
pub use fleet::{Fleet, FleetOwned};
pub use meeting_point::{MeetingPoint, MeetingPointOwned};
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,
};

+ 5
- 0
space-crush-common/src/components/player.rs View File

@@ -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 {


+ 56
- 25
space-crush-common/src/components/ship.rs View File

@@ -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 */


+ 44
- 18
space-crush-common/src/constants.rs View File

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

+ 7
- 8
space-crush-common/src/dispatcher.rs View File

@@ -4,7 +4,8 @@ use crate::{
components::Player,
resources::Global,
systems::{
FleetControl, FleetOwnedUpdate, MeetingPointOwnedUpdate, Process, ShipMovement, Ships,
FleetControl, FleetOrbitingUpdate, FleetOwnedUpdate, Process, ShipControl, ShipsMovement,
ShipsMoving, ShipsOrbiting,
},
Error,
};
@@ -21,15 +22,13 @@ 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(
MeetingPointOwnedUpdate::new(world),
"meeting_point_owned_update",
&[],
)
.with(FleetOrbitingUpdate::new(world), "fleet_orbiting", &[])
.build();
dispatcher.setup(world);



+ 0
- 1
space-crush-common/src/lib.rs View File

@@ -3,7 +3,6 @@ pub mod components;
pub mod constants;
pub mod dispatcher;
pub mod error;
pub mod macros;
pub mod misc;
pub mod resources;
pub mod systems;


+ 0
- 29
space-crush-common/src/macros.rs View File

@@ -1,29 +0,0 @@
#[macro_export]
macro_rules! return_if_none {
($value:expr) => {
match $value {
Some(value) => value,
None => return,
}
};
}

#[macro_export]
macro_rules! break_if_none {
($value:expr) => {
match $value {
Some(value) => value,
None => break,
}
};
}

#[macro_export]
macro_rules! continue_if_none {
($value:expr) => {
match $value {
Some(value) => value,
None => continue,
}
};
}

+ 7
- 4
space-crush-common/src/misc/world.rs View File

@@ -7,8 +7,8 @@ use specs::{saveload::SimpleMarker, World};

use crate::{
components::{
Asteroid, MeetingPoint, MeetingPointOwned, Obstacle, Planet, Player, PlayerOwned, Position,
Ship,
Asteroid, FleetOrbiting, FleetOwned, MeetingPoint, Obstacle, Planet, Player, PlayerOwned,
Position, Ship, ShipMoving, ShipOrbiting,
},
Error,
};
@@ -93,10 +93,13 @@ impl Persistence for WorldPersistence {
Position,
Player,
PlayerOwned,
MeetingPoint,
MeetingPointOwned,
FleetOwned,
FleetOrbiting,
Ship,
ShipMoving,
ShipOrbiting,
Obstacle,
MeetingPoint,
Planet,
Asteroid,
);


+ 133
- 73
space-crush-common/src/systems/fleet_control.rs View File

@@ -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, FleetOwned, MeetingPoint, MeetingPointOwned, 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>,
meeting_point_owned: WriteStorage<'a, MeetingPointOwned>,
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 meeting_point_owned,
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!(meeting_point_owned.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");
meeting_point_owned
.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")
}
}

space-crush-common/src/systems/meeting_point_owned_update.rs → space-crush-common/src/systems/fleet_orbiting_update.rs View File

@@ -7,98 +7,100 @@ use specs::{
};

use crate::{
components::{MeetingPoint, MeetingPointOwned, Player, PlayerOwned},
continue_if_none,
components::{FleetOrbiting, MeetingPoint, Player, PlayerOwned},
misc::{ComponentEvent, StorageHelper, StorageHelperMut},
};

pub struct MeetingPointOwnedUpdate {
pub struct FleetOrbitingUpdate {
meeting_point_ids: BitSet,
meeting_point_owned_ids: BitSet,
fleet_orbiting_ids: BitSet,
old_meeting_point_ids: HashMap<Index, Entity>,
meeting_point_owned_event_id: ReaderId<crate::misc::ComponentEvent<MeetingPointOwned>>,
fleet_orbiting_event_id: ReaderId<crate::misc::ComponentEvent<FleetOrbiting>>,
}

#[derive(SystemData)]
pub struct MeetingPointOwnedUpdateData<'a> {
pub struct FleetOrbitingUpdateData<'a> {
entities: Entities<'a>,
players: ReadStorage<'a, Player>,
player_owned: ReadStorage<'a, PlayerOwned>,
meeting_point_owned: ReadStorage<'a, MeetingPointOwned>,
fleet_orbiting: ReadStorage<'a, FleetOrbiting>,
meeting_points: WriteStorage<'a, MeetingPoint>,
}

impl MeetingPointOwnedUpdate {
impl FleetOrbitingUpdate {
pub fn new(world: &mut World) -> Self {
WriteStorage::<MeetingPointOwned>::setup(world);
WriteStorage::<FleetOrbiting>::setup(world);

let meeting_point_ids = BitSet::new();
let meeting_point_owned_ids = BitSet::new();
let fleet_orbiting_ids = BitSet::new();
let old_meeting_point_ids = HashMap::new();
let meeting_point_owned_event_id = world
.system_data::<WriteStorage<MeetingPointOwned>>()
let fleet_orbiting_event_id = world
.system_data::<WriteStorage<FleetOrbiting>>()
.register_event_reader();

Self {
meeting_point_ids,
meeting_point_owned_ids,
fleet_orbiting_ids,
old_meeting_point_ids,
meeting_point_owned_event_id,
fleet_orbiting_event_id,
}
}
}

impl<'a> System<'a> for MeetingPointOwnedUpdate {
type SystemData = MeetingPointOwnedUpdateData<'a>;
impl<'a> System<'a> for FleetOrbitingUpdate {
type SystemData = FleetOrbitingUpdateData<'a>;

fn run(&mut self, data: Self::SystemData) {
let MeetingPointOwnedUpdateData {
let FleetOrbitingUpdateData {
entities,
players,
player_owned,
meeting_point_owned,
fleet_orbiting,
mut meeting_points,
} = data;

self.meeting_point_ids.clear();
self.meeting_point_owned_ids.clear();
self.fleet_orbiting_ids.clear();
self.old_meeting_point_ids.clear();

/* handle events */
let events = meeting_point_owned
let events = fleet_orbiting
.channel()
.read(&mut self.meeting_point_owned_event_id);
.read(&mut self.fleet_orbiting_event_id);
for event in events {
match event {
ComponentEvent::Inserted(id, meeting_point_owned) => {
self.meeting_point_ids.add(meeting_point_owned.owner().id());
self.meeting_point_owned_ids.add(*id);
ComponentEvent::Inserted(id, fleet_orbiting) => {
self.meeting_point_ids
.add(fleet_orbiting.meeting_point().id());
self.fleet_orbiting_ids.add(*id);
}
ComponentEvent::Modified(id, meeting_point_owned) => {
self.meeting_point_ids.add(meeting_point_owned.owner().id());
self.meeting_point_owned_ids.add(*id);
ComponentEvent::Modified(id, fleet_orbiting) => {
self.meeting_point_ids
.add(fleet_orbiting.meeting_point().id());
self.fleet_orbiting_ids.add(*id);
*self
.old_meeting_point_ids
.entry(*id)
.or_insert_with(|| meeting_point_owned.owner()) =
meeting_point_owned.owner();
.or_insert_with(|| fleet_orbiting.meeting_point()) =
fleet_orbiting.meeting_point();
}
ComponentEvent::Removed(id, meeting_point_owned) => {
self.meeting_point_ids.add(meeting_point_owned.owner().id());
self.meeting_point_owned_ids.add(*id);
ComponentEvent::Removed(id, fleet_orbiting) => {
self.meeting_point_ids
.add(fleet_orbiting.meeting_point().id());
self.fleet_orbiting_ids.add(*id);
*self
.old_meeting_point_ids
.entry(*id)
.or_insert_with(|| meeting_point_owned.owner()) =
meeting_point_owned.owner();
.or_insert_with(|| fleet_orbiting.meeting_point()) =
fleet_orbiting.meeting_point();
}
}
}

/* find new meeting_point ids */
for (meeting_point_owned, _) in (&meeting_point_owned, &self.meeting_point_owned_ids).join()
{
self.meeting_point_ids.add(meeting_point_owned.owner().id());
for (fleet_orbiting, _) in (&fleet_orbiting, &self.fleet_orbiting_ids).join() {
self.meeting_point_ids
.add(fleet_orbiting.meeting_point().id());
}

/* update meeting_points */
@@ -107,20 +109,20 @@ impl<'a> System<'a> for MeetingPointOwnedUpdate {
{
let data = (
&entities,
&meeting_point_owned,
&fleet_orbiting,
&player_owned,
&self.meeting_point_owned_ids,
&self.fleet_orbiting_ids,
);

for (fleet_id, meeting_point_owned, player_owned, _) in data.join() {
let new_match = meeting_point_id == meeting_point_owned.owner();
for (fleet_id, fleet_orbiting, player_owned, _) in data.join() {
let new_match = meeting_point_id == fleet_orbiting.meeting_point();
let old_match = match self.old_meeting_point_ids.get(&fleet_id.id()) {
Some(old_meeting_point_id) => meeting_point_id == *old_meeting_point_id,
None => false,
};

let player_id = player_owned.owner();
let player = continue_if_none!(players.get(player_id));
let player = players.get(player_id).unwrap();
let player_index = player.index();

if old_match && !new_match {

+ 9
- 2
space-crush-common/src/systems/fleet_owned_update.rs View File

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


+ 10
- 6
space-crush-common/src/systems/mod.rs View File

@@ -1,13 +1,17 @@
mod fleet_control;
mod fleet_orbiting_update;
mod fleet_owned_update;
mod meeting_point_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 meeting_point_owned_update::MeetingPointOwnedUpdate;
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;

+ 166
- 0
space-crush-common/src/systems/ship_control.rs View File

@@ -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'");
}
}

+ 0
- 48
space-crush-common/src/systems/ship_movement.rs View File

@@ -1,48 +0,0 @@
#![allow(dead_code)]

use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage};

use crate::{
components::{Player, PlayerOwned, Position, Ship},
resources::Global,
return_if_none,
};

#[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 = return_if_none!(players.get(player_id));
let ship_data = player.ship_data(type_);

*position += ship.dir() * ship_data.speed * delta;
},
);
}
}

+ 0
- 298
space-crush-common/src/systems/ships.rs View File

@@ -1,298 +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::{
FleetOwned, MeetingPoint, MeetingPointOwned, 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,
return_if_none,
};

pub struct Ships {
need_update: BitSet,
fleet_owned_id: ReaderId<ComponentEvent<FleetOwned>>,
}

#[derive(SystemData)]
pub struct ShipsData<'a> {
global: Read<'a, Global>,
entities: Entities<'a>,
ships: WriteStorage<'a, Ship>,
fleet_owned: ReadStorage<'a, FleetOwned>,
meeting_point_owned: ReadStorage<'a, MeetingPointOwned>,
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>,
meeting_point_owned: &'a ReadStorage<'a, MeetingPointOwned>,
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,
meeting_point_owned,
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,
meeting_point_owned: &meeting_point_owned,
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 meeting_point_owned = return_if_none!(self.meeting_point_owned.get(fleet_id));
let meeting_point_id = meeting_point_owned.owner();
let meeting_point = return_if_none!(self.meeting_points.get(meeting_point_id));
let meeting_point_pos = return_if_none!(self.positions.get(meeting_point_id)).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);
}
}
}

+ 82
- 0
space-crush-common/src/systems/ships_movement.rs View File

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

+ 239
- 0
space-crush-common/src/systems/ships_moving.rs View File

@@ -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
}
}

+ 138
- 0
space-crush-common/src/systems/ships_orbiting.rs View File

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

Loading…
Cancel
Save