@@ -1,3 +1,6 @@ | |||
x -> needed | |||
o -> optional | |||
___________________________________________________________ | |||
WorldPersistence________________________________________ \ | |||
Planet_______________________________________________ \ \ | |||
@@ -19,7 +22,7 @@ Player______________ \ \ \ \ \ \ \ \ \ \ \ \ \ | |||
| Player | x | | | | | | | | | | | | x | | |||
| Ship | | x | x | x | x | | | | | | | | x | | |||
| Fleet | | | | | | x | x | | | | | | x | | |||
| Asteroid | | x | x | | | | | x | x | x | x | | x | | |||
| Planet | | x | x | | | | | x | x | x | | x | x | | |||
| Asteroid | | o | x | | | | | x | x | x | x | | x | | |||
| Planet | | o | x | | | | | x | x | x | | x | x | | |||
| MeetingPoint | | | x | | | | | | | x | | | x | | |||
+--------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
@@ -1,6 +1,6 @@ | |||
use glc::vector::Vector4f; | |||
use space_crush_common::{ | |||
components::{Position, Ship, ShipObstacle}, | |||
components::{Player, PlayerOwned, Position, Ship, ShipObstacle}, | |||
continue_if_none, | |||
}; | |||
use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | |||
@@ -14,6 +14,8 @@ pub struct Ships; | |||
pub struct ShipsData<'a> { | |||
geometry: WriteExpect<'a, Geometry>, | |||
positions: ReadStorage<'a, Position>, | |||
player_owned: ReadStorage<'a, PlayerOwned>, | |||
players: ReadStorage<'a, Player>, | |||
ships: ReadStorage<'a, Ship>, | |||
} | |||
@@ -24,18 +26,24 @@ impl<'a> System<'a> for Ships { | |||
let ShipsData { | |||
mut geometry, | |||
positions, | |||
player_owned, | |||
players, | |||
ships, | |||
} = data; | |||
gl::enable(gl::BLEND); | |||
gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||
for (position, ship) in (&positions, &ships).join() { | |||
for (position, player_owned, ship) in (&positions, &player_owned, &ships).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 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.speed()], | |||
&[*ship_pos, ship_pos + ship.dir() * ship_data.speed], | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(1.0, 0.0, 0.0, 0.2), | |||
@@ -19,7 +19,7 @@ pub struct AsteroidBuilder { | |||
} | |||
impl AsteroidBuilder { | |||
pub fn build(self, world: &mut World) -> Result<Entity, Error> { | |||
pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | |||
let type_ = self.type_.ok_or(Error::MissingValue("type"))?; | |||
let position = self.position.ok_or(Error::MissingValue("position"))?; | |||
let size = self.size.ok_or(Error::MissingValue("size"))?; | |||
@@ -13,7 +13,7 @@ pub struct FleetBuilder { | |||
} | |||
impl FleetBuilder { | |||
pub fn build(self, world: &mut World) -> Result<Entity, Error> { | |||
pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | |||
let owner = self.owner.ok_or(Error::MissingValue("owner"))?; | |||
let meeting_point_owned = MeetingPointOwned::new(owner); | |||
@@ -18,7 +18,7 @@ pub struct PlanetBuilder { | |||
} | |||
impl PlanetBuilder { | |||
pub fn build(self, world: &mut World) -> Result<Entity, Error> { | |||
pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | |||
let position = self.position.ok_or(Error::MissingValue("position"))?; | |||
let size = self.size.ok_or(Error::MissingValue("size"))?; | |||
let (orbit_min, orbit_max) = self.orbit.ok_or(Error::MissingValue("orbit"))?; | |||
@@ -3,22 +3,26 @@ use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | |||
use glc::vector::Vector3f; | |||
use crate::{ | |||
components::Player, | |||
components::{Player, Race}, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
use super::Error; | |||
#[derive(Default, Clone)] | |||
#[derive(Clone)] | |||
pub struct PlayerBuilder { | |||
index: usize, | |||
race: Race, | |||
color: Option<Vector3f>, | |||
} | |||
impl PlayerBuilder { | |||
pub fn build(self, world: &mut World) -> Result<Entity, Error> { | |||
let color = self.color.ok_or(Error::MissingValue("color"))?; | |||
pub fn build(&mut self, world: &mut World) -> Result<Entity, Error> { | |||
let index = self.index; | |||
let race = self.race; | |||
let color = self.color.take().ok_or(Error::MissingValue("color"))?; | |||
let player = Player::new(color); | |||
let player = Player::new(index, race, color); | |||
let entity = world | |||
.create_entity() | |||
@@ -26,6 +30,8 @@ impl PlayerBuilder { | |||
.with(player) | |||
.build(); | |||
self.index += 1; | |||
Ok(entity) | |||
} | |||
@@ -35,3 +41,13 @@ impl PlayerBuilder { | |||
self | |||
} | |||
} | |||
impl Default for PlayerBuilder { | |||
fn default() -> Self { | |||
Self { | |||
index: 0, | |||
race: Race::Fighter, | |||
color: None, | |||
} | |||
} | |||
} |
@@ -19,18 +19,17 @@ pub struct ShipBuilder { | |||
} | |||
impl ShipBuilder { | |||
pub fn build(self, world: &mut World) -> Result<Entity, Error> { | |||
pub fn build(&self, world: &mut World) -> Result<Entity, Error> { | |||
let player = self.player.ok_or(Error::MissingValue("player"))?; | |||
let fleet = self.fleet.ok_or(Error::MissingValue("fleet"))?; | |||
let position = self.position.ok_or(Error::MissingValue("position"))?; | |||
let type_ = self.type_.ok_or(Error::MissingValue("type"))?; | |||
let direction = self.direction; | |||
let speed = 100.0; | |||
let player_owned = PlayerOwned::new(player); | |||
let fleet_owned = FleetOwned::new(fleet); | |||
let position = Position::new(position); | |||
let ship = Ship::new(type_, direction, speed); | |||
let ship = Ship::new(type_, direction); | |||
let entity = world | |||
.create_entity() | |||
@@ -13,7 +13,7 @@ pub use fleet::{Fleet, FleetOwned}; | |||
pub use meeting_point::{MeetingPoint, MeetingPointOwned}; | |||
pub use obstacle::Obstacle; | |||
pub use planet::Planet; | |||
pub use player::{Player, PlayerOwned}; | |||
pub use player::{Player, PlayerOwned, Race}; | |||
pub use position::Position; | |||
pub use shape::Shape; | |||
pub use ship::{Count as ShipCount, Obstacle as ShipObstacle, Ship, Type as ShipType}; | |||
pub use ship::{Ship, ShipCount, ShipData, ShipObstacle, ShipType, ShipsData}; |
@@ -6,12 +6,27 @@ use specs::{ | |||
Component, Entity, HashMapStorage, | |||
}; | |||
use crate::builder::PlayerBuilder; | |||
use crate::{ | |||
builder::PlayerBuilder, | |||
components::{ShipData, ShipType, ShipsData}, | |||
constants::{SHIPS_DATA_FIGHTER, SHIPS_DATA_RESEARCHER, SHIPS_DATA_TRADER}, | |||
}; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct Player { | |||
index: usize, | |||
race: Race, | |||
color: Vector3f, | |||
#[serde(skip)] | |||
ships_data: ShipsData, | |||
} | |||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] | |||
pub enum Race { | |||
Fighter, | |||
Trader, | |||
Researcher, | |||
} | |||
#[derive(Copy, Clone, Debug)] | |||
@@ -41,14 +56,25 @@ impl Player { | |||
pub fn color(&self) -> &Vector3f { | |||
&self.color | |||
} | |||
#[inline] | |||
pub fn ship_data(&self, type_: ShipType) -> &ShipData { | |||
&self.ships_data[type_] | |||
} | |||
} | |||
impl Player { | |||
#[inline] | |||
pub(crate) fn new(color: Vector3f) -> Self { | |||
pub(crate) fn new(index: usize, race: Race, color: Vector3f) -> Self { | |||
Self { | |||
index: next_index(), | |||
index, | |||
race, | |||
color, | |||
ships_data: match race { | |||
Race::Fighter => SHIPS_DATA_FIGHTER, | |||
Race::Trader => SHIPS_DATA_TRADER, | |||
Race::Researcher => SHIPS_DATA_RESEARCHER, | |||
}, | |||
} | |||
} | |||
} | |||
@@ -100,11 +126,3 @@ where | |||
Ok(PlayerOwned { owner }) | |||
} | |||
} | |||
fn next_index() -> usize { | |||
use std::sync::atomic::{AtomicUsize, Ordering}; | |||
static NEXT_INDEX: AtomicUsize = AtomicUsize::new(0); | |||
NEXT_INDEX.fetch_add(1, Ordering::Relaxed) | |||
} |
@@ -9,12 +9,11 @@ use crate::{builder::ShipBuilder, misc::FlaggedStorage}; | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct Ship { | |||
type_: Type, | |||
type_: ShipType, | |||
dir: Vector2f, | |||
speed: f32, | |||
#[serde(skip)] | |||
obstacle: Obstacle, | |||
obstacle: ShipObstacle, | |||
#[serde(skip)] | |||
target_pos: Vector2f, | |||
@@ -24,21 +23,33 @@ pub struct Ship { | |||
} | |||
#[derive(Copy, Clone, Debug, PartialEq, Eq)] | |||
pub enum Obstacle { | |||
pub enum ShipObstacle { | |||
Known(Entity), | |||
Search, | |||
Done, | |||
} | |||
#[derive(Copy, Clone, Debug, Default)] | |||
pub struct Count { | |||
pub struct ShipCount { | |||
pub fighter: usize, | |||
pub bomber: usize, | |||
pub transporter: usize, | |||
} | |||
#[derive(Clone, Default, Debug)] | |||
pub struct ShipsData { | |||
pub fighter: ShipData, | |||
pub bomber: ShipData, | |||
pub transporter: ShipData, | |||
} | |||
#[derive(Clone, Default, Debug)] | |||
pub struct ShipData { | |||
pub speed: f32, | |||
} | |||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] | |||
pub enum Type { | |||
pub enum ShipType { | |||
Fighter, | |||
Bomber, | |||
Transporter, | |||
@@ -53,7 +64,7 @@ impl Ship { | |||
} | |||
#[inline] | |||
pub fn type_(&self) -> Type { | |||
pub fn type_(&self) -> ShipType { | |||
self.type_ | |||
} | |||
@@ -62,11 +73,6 @@ impl Ship { | |||
&self.dir | |||
} | |||
#[inline] | |||
pub fn speed(&self) -> f32 { | |||
self.speed | |||
} | |||
#[inline] | |||
pub fn target_pos(&self) -> &Vector2f { | |||
&self.target_pos | |||
@@ -78,18 +84,17 @@ impl Ship { | |||
} | |||
#[inline] | |||
pub fn obstacle(&self) -> Obstacle { | |||
pub fn obstacle(&self) -> ShipObstacle { | |||
self.obstacle | |||
} | |||
} | |||
impl Ship { | |||
#[inline] | |||
pub(crate) fn new(type_: Type, dir: Vector2f, speed: f32) -> Self { | |||
pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { | |||
Self { | |||
type_, | |||
dir, | |||
speed, | |||
obstacle: Default::default(), | |||
target_pos: Default::default(), | |||
target_dir: Default::default(), | |||
@@ -108,7 +113,7 @@ impl Ship { | |||
} | |||
#[inline] | |||
pub(crate) fn obstacle_mut(&mut self) -> &mut Obstacle { | |||
pub(crate) fn obstacle_mut(&mut self) -> &mut ShipObstacle { | |||
&mut self.obstacle | |||
} | |||
} | |||
@@ -119,7 +124,7 @@ impl Component for Ship { | |||
/* Obstacle */ | |||
impl Default for Obstacle { | |||
impl Default for ShipObstacle { | |||
fn default() -> Self { | |||
Self::Search | |||
} | |||
@@ -127,7 +132,7 @@ impl Default for Obstacle { | |||
/* Count */ | |||
impl Count { | |||
impl ShipCount { | |||
pub fn all() -> Self { | |||
Self { | |||
fighter: usize::MAX, | |||
@@ -163,7 +168,7 @@ impl Count { | |||
} | |||
} | |||
impl Index<usize> for Count { | |||
impl Index<usize> for ShipCount { | |||
type Output = usize; | |||
fn index(&self, index: usize) -> &Self::Output { | |||
@@ -176,7 +181,7 @@ impl Index<usize> for Count { | |||
} | |||
} | |||
impl IndexMut<usize> for Count { | |||
impl IndexMut<usize> for ShipCount { | |||
fn index_mut(&mut self, index: usize) -> &mut Self::Output { | |||
match index { | |||
0 => &mut self.fighter, | |||
@@ -187,30 +192,30 @@ impl IndexMut<usize> for Count { | |||
} | |||
} | |||
impl Index<Type> for Count { | |||
impl Index<ShipType> for ShipCount { | |||
type Output = usize; | |||
fn index(&self, index: Type) -> &Self::Output { | |||
fn index(&self, index: ShipType) -> &Self::Output { | |||
match index { | |||
Type::Fighter => &self.fighter, | |||
Type::Bomber => &self.bomber, | |||
Type::Transporter => &self.transporter, | |||
ShipType::Fighter => &self.fighter, | |||
ShipType::Bomber => &self.bomber, | |||
ShipType::Transporter => &self.transporter, | |||
} | |||
} | |||
} | |||
impl IndexMut<Type> for Count { | |||
fn index_mut(&mut self, index: Type) -> &mut Self::Output { | |||
impl IndexMut<ShipType> for ShipCount { | |||
fn index_mut(&mut self, index: ShipType) -> &mut Self::Output { | |||
match index { | |||
Type::Fighter => &mut self.fighter, | |||
Type::Bomber => &mut self.bomber, | |||
Type::Transporter => &mut self.transporter, | |||
ShipType::Fighter => &mut self.fighter, | |||
ShipType::Bomber => &mut self.bomber, | |||
ShipType::Transporter => &mut self.transporter, | |||
} | |||
} | |||
} | |||
impl Mul<f32> for Count { | |||
type Output = Count; | |||
impl Mul<f32> for ShipCount { | |||
type Output = ShipCount; | |||
#[allow(unused_assignments)] | |||
fn mul(self, rhs: f32) -> Self::Output { | |||
@@ -238,10 +243,37 @@ impl Mul<f32> for Count { | |||
actual += 1; | |||
} | |||
Count { | |||
ShipCount { | |||
fighter, | |||
bomber, | |||
transporter, | |||
} | |||
} | |||
} | |||
/* ShipsData */ | |||
impl Index<usize> for ShipsData { | |||
type Output = ShipData; | |||
fn index(&self, index: usize) -> &Self::Output { | |||
match index { | |||
0 => &self.fighter, | |||
1 => &self.bomber, | |||
2 => &self.transporter, | |||
x => panic!("Invalid ships data index: {}", x), | |||
} | |||
} | |||
} | |||
impl Index<ShipType> for ShipsData { | |||
type Output = ShipData; | |||
fn index(&self, index: ShipType) -> &Self::Output { | |||
match index { | |||
ShipType::Fighter => &self.fighter, | |||
ShipType::Bomber => &self.bomber, | |||
ShipType::Transporter => &self.transporter, | |||
} | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
use glc::{matrix::Angle, vector::Vector2f}; | |||
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; | |||
@@ -13,3 +15,21 @@ pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(5000.0); | |||
pub const SHIP_ORBIT_AGILITY: Angle<f32> = Angle::Deg(90.0); | |||
pub const VECTOR_2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0); | |||
pub const SHIPS_DATA_FIGHTER: ShipsData = ShipsData { | |||
fighter: ShipData { speed: 120.0 }, | |||
bomber: ShipData { speed: 80.0 }, | |||
transporter: ShipData { speed: 100.0 }, | |||
}; | |||
pub const SHIPS_DATA_TRADER: ShipsData = ShipsData { | |||
fighter: ShipData { speed: 100.0 }, | |||
bomber: ShipData { speed: 70.0 }, | |||
transporter: ShipData { speed: 120.0 }, | |||
}; | |||
pub const SHIPS_DATA_RESEARCHER: ShipsData = ShipsData { | |||
fighter: ShipData { speed: 90.0 }, | |||
bomber: ShipData { speed: 60.0 }, | |||
transporter: ShipData { speed: 100.0 }, | |||
}; |
@@ -3,7 +3,7 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||
use crate::{ | |||
components::Player, | |||
resources::Global, | |||
systems::{FleetControl, FleetOwnedUpdate, Movement, Process, Ships}, | |||
systems::{FleetControl, FleetOwnedUpdate, Process, ShipMovement, Ships}, | |||
Error, | |||
}; | |||
@@ -19,7 +19,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||
let mut dispatcher = DispatcherBuilder::new() | |||
.with(Process::default(), "process", &[]) | |||
.with(Movement::default(), "movement", &[]) | |||
.with(ShipMovement::default(), "ship_movement", &[]) | |||
.with(Ships::new(world), "ships", &[]) | |||
.with(FleetControl::new(world)?, "fleet_control", &[]) | |||
.with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | |||
@@ -1,11 +1,11 @@ | |||
mod fleet_control; | |||
mod fleet_owned_update; | |||
mod movement; | |||
mod process; | |||
mod ship_movement; | |||
mod ships; | |||
pub use fleet_control::{FleetControl, FleetControlEvent}; | |||
pub use fleet_owned_update::FleetOwnedUpdate; | |||
pub use movement::Movement; | |||
pub use process::Process; | |||
pub use ship_movement::ShipMovement; | |||
pub use ships::Ships; |
@@ -1,39 +0,0 @@ | |||
#![allow(dead_code)] | |||
use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; | |||
use crate::{ | |||
components::{Position, Ship}, | |||
resources::Global, | |||
}; | |||
#[derive(Default)] | |||
pub struct Movement; | |||
#[derive(SystemData)] | |||
pub struct MovementData<'a> { | |||
positions: WriteStorage<'a, Position>, | |||
ships: ReadStorage<'a, Ship>, | |||
global: Read<'a, Global>, | |||
} | |||
impl<'a> System<'a> for Movement { | |||
type SystemData = MovementData<'a>; | |||
fn run(&mut self, data: Self::SystemData) { | |||
let MovementData { | |||
mut positions, | |||
ships, | |||
global, | |||
} = data; | |||
(&mut positions, &ships) | |||
.par_join() | |||
.for_each(|(position, ship)| { | |||
let delta = global.delta * global.world_speed; | |||
let position = position.get_mut(); | |||
*position += ship.dir() * ship.speed() * delta; | |||
}); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
#![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; | |||
}, | |||
); | |||
} | |||
} |