Using builders ensure that the correct components are added to a new created entity.master
@@ -74,7 +74,7 @@ impl<'a> System<'a> for Fleets { | |||
), | |||
) | |||
.panic("Unable to update text") | |||
.render_offset(&camera.world_to_window(*position.pos())); | |||
.render_offset(&camera.world_to_window(*position.get())); | |||
} | |||
gl::blend_equation(gl::FUNC_ADD); | |||
@@ -34,16 +34,16 @@ impl<'a> System<'a> for MeetingPoints { | |||
gl::blend_equation(gl::FUNC_ADD); | |||
geometry.render_lines( | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle(position.pos(), meeting_point.min()), | |||
&create_circle(position.get(), meeting_point.min()), | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle(position.pos(), meeting_point.max()), | |||
&create_circle(position.get(), meeting_point.max()), | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle( | |||
position.pos(), | |||
position.get(), | |||
SHIP_ORBIT_DISTANCE_MAX * meeting_point.max(), | |||
), | |||
); | |||
@@ -33,7 +33,7 @@ impl<'a> System<'a> for Ships { | |||
gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||
for (position, velocity, ship) in (&positions, &velocities, &ships).join() { | |||
let ship_pos = position.pos(); | |||
let ship_pos = position.get(); | |||
geometry.render_lines( | |||
Vector4f::new(0.0, 0.0, 1.0, 0.2), | |||
@@ -49,7 +49,8 @@ impl<'a> System<'a> for Ships { | |||
); | |||
if let ShipObstacle::Known(obstacle) = ship.obstacle() { | |||
let obstacle_pos = continue_if_none!(positions.get(obstacle)).pos(); | |||
let obstacle_pos = continue_if_none!(positions.get(obstacle)); | |||
let obstacle_pos = obstacle_pos.get(); | |||
geometry.render_lines( | |||
Vector4f::new(0.0, 1.0, 0.0, 0.2), | |||
@@ -7,7 +7,7 @@ use glutin::{ | |||
}; | |||
use glyph_brush::ab_glyph::InvalidFont; | |||
use serde_json::Error as JsonError; | |||
use space_crush_common::{misc::VfsError, Error as CommonError}; | |||
use space_crush_common::{builder::Error as BuilderError, misc::VfsError, Error as CommonError}; | |||
use thiserror::Error; | |||
#[derive(Debug, Error)] | |||
@@ -39,6 +39,9 @@ pub enum Error { | |||
#[error("{0}")] | |||
CommonError(CommonError), | |||
#[error("{0}")] | |||
BuilderError(BuilderError), | |||
#[error("Resource is not registered: {0}!")] | |||
ResourceNotRegistered(&'static str), | |||
@@ -102,3 +105,9 @@ impl From<CommonError> for Error { | |||
Self::CommonError(err) | |||
} | |||
} | |||
impl From<BuilderError> for Error { | |||
fn from(err: BuilderError) -> Self { | |||
Self::BuilderError(err) | |||
} | |||
} |
@@ -1,13 +1,12 @@ | |||
use glc::vector::Vector3f; | |||
use log::{error, info}; | |||
use rand::random; | |||
use space_crush_app::{App, Error}; | |||
use space_crush_common::{ | |||
components::Player, | |||
misc::{init_logger, Vfs}, | |||
misc::{init_logger, Persistence, Vfs, WorldPersistence}, | |||
Dispatcher, | |||
}; | |||
use specs::{Builder, Entity, World, WorldExt}; | |||
use specs::{Entity, World, WorldExt}; | |||
fn main() -> Result<(), Error> { | |||
let vfs = Vfs::new(&["space-crush-app"])?; | |||
@@ -34,12 +33,10 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
let mut world = World::new(); | |||
world.insert(vfs); | |||
WorldPersistence::setup(&mut world); | |||
let mut common = Dispatcher::new(&mut world)?; | |||
let player1 = world | |||
.create_entity() | |||
.with(Player::new(Vector3f::new(0.0, 0.5, 1.0))) | |||
.build(); | |||
let player1 = Player::builder().color((0.0, 0.5, 1.0)).build(&mut world)?; | |||
let mut app = App::new(&mut world, player1)?; | |||
create_world(&mut world, player1); | |||
@@ -57,32 +54,21 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
} | |||
fn create_world(world: &mut World, player_id: Entity) { | |||
use glc::{matrix::Angle, vector::Vector2f}; | |||
use space_crush_common::{ | |||
components::{ | |||
Asteroid, AsteroidType, Fleet, FleetOwned, MeetingPoint, MeetingPointOwned, Obstacle, | |||
Planet, PlayerOwned, Position, Ship, ShipType, Velocity, | |||
}, | |||
misc::{PersistWorld, Persistence}, | |||
}; | |||
use specs::saveload::MarkedBuilder; | |||
PersistWorld::setup(world); | |||
use glc::matrix::Angle; | |||
use space_crush_common::components::{Asteroid, AsteroidType, Fleet, Planet, Ship, ShipType}; | |||
let planets = (0..3) | |||
.map(|i| { | |||
let x = 2000.0 * (i as f32 - 1.0); | |||
let y = 0.0; | |||
world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(PlayerOwned::new(player_id)) | |||
.with(Position::circle(Vector2f::new(x, y), 250.0)) | |||
.with(MeetingPoint::new(325.0, 425.0)) | |||
.with(Obstacle {}) | |||
.with(Planet {}) | |||
.build() | |||
Planet::builder() | |||
.position((x, y)) | |||
.size(250.0) | |||
.orbit(325.0, 425.0) | |||
.owner(player_id) | |||
.build(world) | |||
.unwrap() | |||
}) | |||
.collect::<Vec<_>>(); | |||
@@ -93,27 +79,20 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
let x = 1000.0 * i_x as f32; | |||
let y = 1000.0 * i_y as f32; | |||
world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(Position::circle(Vector2f::new(x, y), 100.0)) | |||
.with(MeetingPoint::new(125.0, 175.0)) | |||
.with(Obstacle {}) | |||
.with(Asteroid::new(match i_x * i_y { | |||
Asteroid::builder() | |||
.position((x, y)) | |||
.size(100.0) | |||
.orbit(125.0, 175.0) | |||
.type_(match i_x * i_y { | |||
-1 => AsteroidType::Metal, | |||
1 => AsteroidType::Crystal, | |||
_ => unreachable!(), | |||
})) | |||
.build(); | |||
}) | |||
.build(world) | |||
.unwrap(); | |||
} | |||
let fleet_id = world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(PlayerOwned::new(player_id)) | |||
.with(MeetingPointOwned::new(planets[1])) | |||
.with(Fleet::default()) | |||
.build(); | |||
let fleet_id = Fleet::builder().owner(planets[1]).build(world).unwrap(); | |||
for i in 0..9 { | |||
let r = 325.0 + 100.0 * random::<f32>(); | |||
@@ -122,22 +101,17 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
let x = r * a.cos(); | |||
let y = r * a.sin(); | |||
world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(PlayerOwned::new(player_id)) | |||
.with(FleetOwned::new(fleet_id)) | |||
.with(Position::dot(Vector2f::new(x, y))) | |||
.with(Velocity::new( | |||
Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | |||
100.0, | |||
)) | |||
.with(Ship::new(match i % 3 { | |||
Ship::builder() | |||
.player(player_id) | |||
.fleet(fleet_id) | |||
.position((x, y)) | |||
.type_(match i % 3 { | |||
0 => ShipType::Fighter, | |||
1 => ShipType::Bomber, | |||
2 => ShipType::Transporter, | |||
_ => unreachable!(), | |||
})) | |||
.build(); | |||
}) | |||
.build(world) | |||
.unwrap(); | |||
} | |||
} |
@@ -9,16 +9,13 @@ use glc::{ | |||
vertex_array::{DataType, VertexArray}, | |||
}; | |||
use space_crush_common::{ | |||
components::{Asteroid, AsteroidType, Player, PlayerOwned, Position}, | |||
components::{Asteroid, AsteroidType, Player, PlayerOwned, Position, Shape}, | |||
misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, | |||
}; | |||
use specs::{prelude::*, ReadStorage, System, World}; | |||
use crate::{ | |||
constants::{ | |||
ASTEROID_SIZE, PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, | |||
UNIFORM_BUFFER_INDEX_UNIFORM, | |||
}, | |||
constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||
misc::WorldHelper, | |||
Error, | |||
}; | |||
@@ -40,6 +37,15 @@ struct VertexData { | |||
texture: gl::GLint, | |||
} | |||
#[derive(SystemData)] | |||
pub struct AsteroidsData<'a> { | |||
positions: ReadStorage<'a, Position>, | |||
shapes: ReadStorage<'a, Shape>, | |||
asteroids: ReadStorage<'a, Asteroid>, | |||
players: ReadStorage<'a, Player>, | |||
owned: ReadStorage<'a, PlayerOwned>, | |||
} | |||
impl Asteroids { | |||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||
WriteStorage::<Asteroid>::setup(world); | |||
@@ -91,20 +97,13 @@ impl Asteroids { | |||
} | |||
} | |||
#[derive(SystemData)] | |||
pub struct AsteroidsData<'a> { | |||
positions: ReadStorage<'a, Position>, | |||
asteroids: ReadStorage<'a, Asteroid>, | |||
players: ReadStorage<'a, Player>, | |||
owned: ReadStorage<'a, PlayerOwned>, | |||
} | |||
impl<'a> System<'a> for Asteroids { | |||
type SystemData = AsteroidsData<'a>; | |||
fn run(&mut self, data: Self::SystemData) { | |||
let AsteroidsData { | |||
positions, | |||
shapes, | |||
asteroids, | |||
players, | |||
owned, | |||
@@ -141,16 +140,16 @@ impl<'a> System<'a> for Asteroids { | |||
) | |||
.panic("Unable to change buffer size for asteroid data"); | |||
let data = (&positions, &asteroids, owned.maybe()); | |||
let data = (&positions, &shapes, &asteroids, owned.maybe()); | |||
let mut buffer = buffer | |||
.map_mut::<VertexData>(true) | |||
.panic("Unable to map buffer for asteroid data"); | |||
for (i, (position, asteroid, owned)) in data.join().enumerate() { | |||
for (i, (position, shape, asteroid, owned)) in data.join().enumerate() { | |||
let mut d = &mut buffer[i]; | |||
d.pos = *position.pos(); | |||
d.size = position.shape().circle().unwrap_or(ASTEROID_SIZE); | |||
d.pos = *position.get(); | |||
d.size = shape.radius(); | |||
d.color = match owned.and_then(|owned| players.get(owned.owner())) { | |||
Some(pv) => *pv.color(), | |||
None => PLAYER_COLOR_DEFAULT, | |||
@@ -69,7 +69,7 @@ impl<'a> System<'a> for FleetMove { | |||
(&entities, &positions, &meeting_points).join() | |||
{ | |||
let r = meeting_point.max() * meeting_point.max(); | |||
if (position.pos() - pos).length_sqr() <= r { | |||
if (position.get() - pos).length_sqr() <= r { | |||
self.target_meeting_point = Some(id); | |||
break; | |||
@@ -82,7 +82,12 @@ impl<'a> System<'a> for FleetMove { | |||
let Selection { fleet, count } = selection; | |||
let target = continue_if_none!(self.target_meeting_point); | |||
let player = game_state.player_id(); | |||
let event = FleetControlEvent::move_(player, target, fleet, count); | |||
let event = FleetControlEvent::MoveToMeetingPoint { | |||
player, | |||
fleet, | |||
count, | |||
target, | |||
}; | |||
fleet_control.single_write(event); | |||
} | |||
@@ -10,7 +10,7 @@ use glc::{ | |||
}; | |||
use shrev::{EventChannel, ReaderId}; | |||
use space_crush_common::{ | |||
components::{Fleet, MeetingPoint, MeetingPointOwned, Position, ShipCount}, | |||
components::{Fleet, MeetingPoint, MeetingPointOwned, Position, Shape, ShipCount}, | |||
constants::VECTOR_2F_POS_X, | |||
continue_if_none, | |||
misc::{LogResult, WorldHelper as _}, | |||
@@ -82,6 +82,7 @@ pub struct FleetSelectData<'a> { | |||
meeting_point_owned: ReadStorage<'a, MeetingPointOwned>, | |||
positions: ReadStorage<'a, Position>, | |||
shapes: ReadStorage<'a, Shape>, | |||
meeting_points: ReadStorage<'a, MeetingPoint>, | |||
fleets: ReadStorage<'a, Fleet>, | |||
} | |||
@@ -107,6 +108,12 @@ macro_rules! position { | |||
}; | |||
} | |||
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)) | |||
@@ -188,7 +195,7 @@ impl FleetSelect { | |||
let selection = d.game_state.selection_mut().take(); | |||
for (position, meeting_point) in (&d.positions, &d.meeting_points).join() { | |||
let r = meeting_point.max() * meeting_point.max(); | |||
if (position.pos() - pos).length_sqr() <= r { | |||
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)); | |||
@@ -273,11 +280,12 @@ impl FleetSelect { | |||
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); | |||
self.marker = d.camera.view_to_world(self.mouse_pos) - position.pos(); | |||
self.marker = d.camera.view_to_world(self.mouse_pos) - position.get(); | |||
self.zoom = d.camera.view().axis_x.as_vec3().length(); | |||
self.shape_size = position.shape().radius(); | |||
self.shape_size = shape.radius(); | |||
self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom; | |||
self.ring1 = self.ring0 + FLEET_SELECT_WIDTH / self.zoom; | |||
@@ -431,13 +439,12 @@ impl FleetSelect { | |||
/* calculate shared values */ | |||
let size = self.ring1 + 50.0; | |||
let rings = Vector2f::new(self.ring0 / size, self.ring1 / size); | |||
let px = position.pos().x; | |||
let py = position.pos().y; | |||
let p = position.get(); | |||
let m = Matrix4f::new( | |||
Vector4f::new(size, 0.0, 0.0, 0.0), | |||
Vector4f::new(0.0, size, 0.0, 0.0), | |||
Vector4f::new(0.0, 0.0, size, 0.0), | |||
Vector4f::new(px, py, 0.0, 1.0), | |||
Vector4f::new(p.x, p.y, 0.0, 1.0), | |||
); | |||
/* update uniforms */ | |||
@@ -475,20 +482,20 @@ impl FleetSelect { | |||
if is_simple { | |||
self.text_total | |||
.color(Vector4f::new(1.0, 1.0, 1.0, alpha)) | |||
.render_offset(&self.text_pos(self.marker, *position.pos(), d)); | |||
.render_offset(&self.text_pos(self.marker, *position.get(), d)); | |||
} else { | |||
self.text_total | |||
.color(Vector4f::new(1.0, 1.0, 1.0, alpha)) | |||
.render_offset(&self.text_pos((0.0, 1.0), *position.pos(), d)); | |||
.render_offset(&self.text_pos((0.0, 1.0), *position.get(), d)); | |||
self.text_fighter | |||
.color(Vector4f::new(1.0, 1.0, 1.0, alpha)) | |||
.render_offset(&self.text_pos((1.0, 0.0), *position.pos(), d)); | |||
.render_offset(&self.text_pos((1.0, 0.0), *position.get(), d)); | |||
self.text_bomber | |||
.color(Vector4f::new(1.0, 1.0, 1.0, alpha)) | |||
.render_offset(&self.text_pos((0.0, -1.0), *position.pos(), d)); | |||
.render_offset(&self.text_pos((0.0, -1.0), *position.get(), d)); | |||
self.text_transporter | |||
.color(Vector4f::new(1.0, 1.0, 1.0, alpha)) | |||
.render_offset(&self.text_pos((-1.0, 0.0), *position.pos(), d)); | |||
.render_offset(&self.text_pos((-1.0, 0.0), *position.get(), d)); | |||
} | |||
} | |||
@@ -9,16 +9,13 @@ use glc::{ | |||
vertex_array::{DataType, VertexArray}, | |||
}; | |||
use space_crush_common::{ | |||
components::{Planet, Player, PlayerOwned, Position}, | |||
components::{Planet, Player, PlayerOwned, Position, Shape}, | |||
misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, | |||
}; | |||
use specs::{prelude::*, ReadStorage, System, World}; | |||
use crate::{ | |||
constants::{ | |||
PLANET_SIZE, PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, | |||
UNIFORM_BUFFER_INDEX_UNIFORM, | |||
}, | |||
constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||
misc::WorldHelper, | |||
Error, | |||
}; | |||
@@ -88,6 +85,7 @@ impl Planets { | |||
#[derive(SystemData)] | |||
pub struct PlanetsData<'a> { | |||
positions: ReadStorage<'a, Position>, | |||
shapes: ReadStorage<'a, Shape>, | |||
planets: ReadStorage<'a, Planet>, | |||
players: ReadStorage<'a, Player>, | |||
owned: ReadStorage<'a, PlayerOwned>, | |||
@@ -99,6 +97,7 @@ impl<'a> System<'a> for Planets { | |||
fn run(&mut self, data: Self::SystemData) { | |||
let PlanetsData { | |||
positions, | |||
shapes, | |||
planets, | |||
players, | |||
owned, | |||
@@ -135,16 +134,16 @@ impl<'a> System<'a> for Planets { | |||
) | |||
.panic("Unable to change buffer size for planet data"); | |||
let data = (&positions, &planets, owned.maybe()); | |||
let data = (&positions, &shapes, &planets, owned.maybe()); | |||
let mut buffer = buffer | |||
.map_mut::<VertexData>(true) | |||
.panic("Unable to map buffer for planet data"); | |||
for (i, (position, _, owned)) in data.join().enumerate() { | |||
for (i, (position, shape, _, owned)) in data.join().enumerate() { | |||
let mut d = &mut buffer[i]; | |||
d.pos = *position.pos(); | |||
d.size = position.shape().circle().unwrap_or(PLANET_SIZE); | |||
d.pos = *position.get(); | |||
d.size = shape.radius(); | |||
d.color = match owned.and_then(|owned| players.get(owned.owner())) { | |||
Some(pv) => *pv.color(), | |||
None => PLAYER_COLOR_DEFAULT, | |||
@@ -225,7 +225,7 @@ impl Ships { | |||
let index = self.id_to_index[id as usize]; | |||
let mut s = &mut buf_ship[index as usize]; | |||
s.pos = *position.pos(); | |||
s.pos = *position.get(); | |||
s.dir = *velocity.dir(); | |||
if self.need_init.contains(id) { | |||
@@ -239,7 +239,7 @@ impl Ships { | |||
ShipType::Transporter => 2, | |||
}; | |||
let mut pos = *position.pos(); | |||
let mut pos = *position.get(); | |||
let mut borrow_tail = self.input.tail_data.borrow_mut(); | |||
let mut buf_tail = borrow_tail | |||
.map_mut::<TailData>(true) | |||
@@ -0,0 +1,82 @@ | |||
use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt, WriteStorage}; | |||
use glc::vector::Vector2f; | |||
use crate::{ | |||
components::{Asteroid, AsteroidType, MeetingPoint, Obstacle, PlayerOwned, Position, Shape}, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
use super::Error; | |||
#[derive(Default, Clone)] | |||
pub struct AsteroidBuilder { | |||
type_: Option<AsteroidType>, | |||
position: Option<Vector2f>, | |||
size: Option<f32>, | |||
orbit: Option<(f32, f32)>, | |||
owner: Option<Entity>, | |||
} | |||
impl AsteroidBuilder { | |||
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"))?; | |||
let (orbit_min, orbit_max) = self.orbit.ok_or(Error::MissingValue("orbit"))?; | |||
let position = Position::new(position); | |||
let shape = Shape::Circle(size); | |||
let meeting_point = MeetingPoint::new(orbit_min, orbit_max); | |||
let obstacle = Obstacle::default(); | |||
let asteroid = Asteroid::new(type_); | |||
let entity = world | |||
.create_entity() | |||
.marked::<<WorldPersistence as Persistence>::Marker>() | |||
.with(position) | |||
.with(shape) | |||
.with(meeting_point) | |||
.with(obstacle) | |||
.with(asteroid) | |||
.build(); | |||
if let Some(owner) = self.owner { | |||
let mut player_owned = world.system_data::<WriteStorage<PlayerOwned>>(); | |||
player_owned.insert(entity, PlayerOwned::new(owner))?; | |||
} | |||
Ok(entity) | |||
} | |||
pub fn type_(mut self, value: AsteroidType) -> Self { | |||
self.type_ = Some(value); | |||
self | |||
} | |||
pub fn position<V: Into<Vector2f>>(mut self, value: V) -> Self { | |||
self.position = Some(value.into()); | |||
self | |||
} | |||
pub fn size(mut self, value: f32) -> Self { | |||
self.size = Some(value); | |||
self | |||
} | |||
pub fn orbit(mut self, min: f32, max: f32) -> Self { | |||
self.orbit = Some((min, max)); | |||
self | |||
} | |||
pub fn owner(mut self, player: Entity) -> Self { | |||
self.owner = Some(player); | |||
self | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | |||
use crate::{ | |||
components::{Fleet, MeetingPointOwned}, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
use super::Error; | |||
#[derive(Default, Clone)] | |||
pub struct FleetBuilder { | |||
owner: Option<Entity>, | |||
} | |||
impl FleetBuilder { | |||
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); | |||
let fleet = Fleet::new(); | |||
let entity = world | |||
.create_entity() | |||
.marked::<<WorldPersistence as Persistence>::Marker>() | |||
.with(meeting_point_owned) | |||
.with(fleet) | |||
.build(); | |||
Ok(entity) | |||
} | |||
pub fn owner(mut self, meeting_point: Entity) -> Self { | |||
self.owner = Some(meeting_point); | |||
self | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
mod asteroid; | |||
mod fleet; | |||
mod planet; | |||
mod player; | |||
mod ship; | |||
pub use asteroid::AsteroidBuilder; | |||
pub use fleet::FleetBuilder; | |||
pub use planet::PlanetBuilder; | |||
pub use player::PlayerBuilder; | |||
pub use ship::ShipBuilder; | |||
use specs::error::Error as SpecsError; | |||
use thiserror::Error; | |||
#[derive(Debug, Error)] | |||
pub enum Error { | |||
#[error("Specs Error: {0}")] | |||
SpecsError(SpecsError), | |||
#[error("Builder is missing the value '{0}'")] | |||
MissingValue(&'static str), | |||
} | |||
impl From<SpecsError> for Error { | |||
fn from(err: SpecsError) -> Self { | |||
Self::SpecsError(err) | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt, WriteStorage}; | |||
use glc::vector::Vector2f; | |||
use crate::{ | |||
components::{MeetingPoint, Obstacle, Planet, PlayerOwned, Position, Shape}, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
use super::Error; | |||
#[derive(Default, Clone)] | |||
pub struct PlanetBuilder { | |||
position: Option<Vector2f>, | |||
size: Option<f32>, | |||
orbit: Option<(f32, f32)>, | |||
owner: Option<Entity>, | |||
} | |||
impl PlanetBuilder { | |||
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"))?; | |||
let position = Position::new(position); | |||
let shape = Shape::Circle(size); | |||
let meeting_point = MeetingPoint::new(orbit_min, orbit_max); | |||
let obstacle = Obstacle::default(); | |||
let planet = Planet::default(); | |||
let entity = world | |||
.create_entity() | |||
.marked::<<WorldPersistence as Persistence>::Marker>() | |||
.with(position) | |||
.with(shape) | |||
.with(meeting_point) | |||
.with(obstacle) | |||
.with(planet) | |||
.build(); | |||
if let Some(owner) = self.owner { | |||
let mut player_owned = world.system_data::<WriteStorage<PlayerOwned>>(); | |||
player_owned.insert(entity, PlayerOwned::new(owner))?; | |||
} | |||
Ok(entity) | |||
} | |||
pub fn position<V: Into<Vector2f>>(mut self, value: V) -> Self { | |||
self.position = Some(value.into()); | |||
self | |||
} | |||
pub fn size(mut self, value: f32) -> Self { | |||
self.size = Some(value); | |||
self | |||
} | |||
pub fn orbit(mut self, min: f32, max: f32) -> Self { | |||
self.orbit = Some((min, max)); | |||
self | |||
} | |||
pub fn owner(mut self, player: Entity) -> Self { | |||
self.owner = Some(player); | |||
self | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | |||
use glc::vector::Vector3f; | |||
use crate::{ | |||
components::Player, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
use super::Error; | |||
#[derive(Default, Clone)] | |||
pub struct PlayerBuilder { | |||
color: Option<Vector3f>, | |||
} | |||
impl PlayerBuilder { | |||
pub fn build(self, world: &mut World) -> Result<Entity, Error> { | |||
let color = self.color.ok_or(Error::MissingValue("color"))?; | |||
let player = Player::new(color); | |||
let entity = world | |||
.create_entity() | |||
.marked::<<WorldPersistence as Persistence>::Marker>() | |||
.with(player) | |||
.build(); | |||
Ok(entity) | |||
} | |||
pub fn color<V: Into<Vector3f>>(mut self, value: V) -> Self { | |||
self.color = Some(value.into()); | |||
self | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
use glc::vector::Vector2f; | |||
use rand::random; | |||
use specs::{saveload::MarkedBuilder, Builder, Entity, World, WorldExt}; | |||
use crate::{ | |||
components::{FleetOwned, PlayerOwned, Position, Ship, ShipType, Velocity}, | |||
misc::{Persistence, WorldPersistence}, | |||
}; | |||
use super::Error; | |||
#[derive(Clone)] | |||
pub struct ShipBuilder { | |||
player: Option<Entity>, | |||
fleet: Option<Entity>, | |||
position: Option<Vector2f>, | |||
type_: Option<ShipType>, | |||
direction: Vector2f, | |||
} | |||
impl ShipBuilder { | |||
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 velocity = Velocity::new(direction, speed); | |||
let ship = Ship::new(type_); | |||
let entity = world | |||
.create_entity() | |||
.marked::<<WorldPersistence as Persistence>::Marker>() | |||
.with(player_owned) | |||
.with(fleet_owned) | |||
.with(position) | |||
.with(velocity) | |||
.with(ship) | |||
.build(); | |||
Ok(entity) | |||
} | |||
pub fn player(mut self, value: Entity) -> Self { | |||
self.player = Some(value); | |||
self | |||
} | |||
pub fn fleet(mut self, value: Entity) -> Self { | |||
self.fleet = Some(value); | |||
self | |||
} | |||
pub fn position<V: Into<Vector2f>>(mut self, value: V) -> Self { | |||
self.position = Some(value.into()); | |||
self | |||
} | |||
pub fn direction<V: Into<Vector2f>>(mut self, value: V) -> Self { | |||
self.position = Some(value.into()); | |||
self | |||
} | |||
pub fn type_(mut self, value: ShipType) -> Self { | |||
self.type_ = Some(value); | |||
self | |||
} | |||
} | |||
impl Default for ShipBuilder { | |||
fn default() -> Self { | |||
Self { | |||
player: None, | |||
fleet: None, | |||
position: None, | |||
type_: None, | |||
direction: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | |||
} | |||
} | |||
} |
@@ -1,7 +1,7 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, HashMapStorage}; | |||
use crate::misc::FlaggedStorage; | |||
use crate::{builder::AsteroidBuilder, misc::FlaggedStorage}; | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct Asteroid { | |||
@@ -14,10 +14,12 @@ pub enum Type { | |||
Crystal, | |||
} | |||
/* Asteroid */ | |||
impl Asteroid { | |||
#[inline] | |||
pub fn new(type_: Type) -> Self { | |||
Self { type_ } | |||
pub fn builder() -> AsteroidBuilder { | |||
AsteroidBuilder::default() | |||
} | |||
#[inline] | |||
@@ -26,6 +28,13 @@ impl Asteroid { | |||
} | |||
} | |||
impl Asteroid { | |||
#[inline] | |||
pub(crate) fn new(type_: Type) -> Self { | |||
Self { type_ } | |||
} | |||
} | |||
impl Component for Asteroid { | |||
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>; | |||
} |
@@ -8,10 +8,14 @@ use specs::{ | |||
Component, Entity, HashMapStorage, VecStorage, | |||
}; | |||
use crate::{components::ShipCount, misc::FlaggedStorage}; | |||
use crate::{ | |||
builder::FleetBuilder, | |||
components::{Ship, ShipCount}, | |||
misc::FlaggedStorage, | |||
}; | |||
/// A fleet is a group of ships that share the same operation | |||
#[derive(Default, Debug, Clone)] | |||
#[derive(Debug, Clone)] | |||
pub struct Fleet { | |||
owned: BitSet, | |||
count: ShipCount, | |||
@@ -28,25 +32,45 @@ pub struct FleetOwnedData<M> { | |||
pub owner: M, | |||
} | |||
/* Fleet */ | |||
impl Fleet { | |||
#[inline] | |||
pub fn owned(&self) -> &BitSet { | |||
&self.owned | |||
pub fn builder() -> FleetBuilder { | |||
FleetBuilder::default() | |||
} | |||
#[inline] | |||
pub(crate) fn owned_mut(&mut self) -> &mut BitSet { | |||
&mut self.owned | |||
pub fn owned(&self) -> &BitSet { | |||
&self.owned | |||
} | |||
#[inline] | |||
pub fn count(&self) -> &ShipCount { | |||
&self.count | |||
} | |||
} | |||
#[inline] | |||
pub(crate) fn count_mut(&mut self) -> &mut ShipCount { | |||
&mut self.count | |||
impl Fleet { | |||
pub(crate) fn new() -> Self { | |||
Self { | |||
owned: BitSet::new(), | |||
count: ShipCount::none(), | |||
} | |||
} | |||
pub(crate) fn add_ship(&mut self, ship_id: Entity, ship: &Ship) { | |||
let type_ = ship.type_(); | |||
self.count[type_] += 1; | |||
self.owned.add(ship_id.id()); | |||
} | |||
pub(crate) fn remove_ship(&mut self, ship_id: Entity, ship: &Ship) { | |||
let type_ = ship.type_(); | |||
self.count[type_] -= 1; | |||
self.owned.remove(ship_id.id()); | |||
} | |||
} | |||
@@ -54,6 +78,8 @@ impl Component for Fleet { | |||
type Storage = HashMapStorage<Self>; | |||
} | |||
/* FleetOwned */ | |||
impl FleetOwned { | |||
pub fn new(owner: Entity) -> Self { | |||
Self { owner } | |||
@@ -37,16 +37,9 @@ pub struct MeetingPointOwnedData<M> { | |||
type Fleets = SmallVec<[Option<Entity>; 8]>; | |||
impl MeetingPoint { | |||
#[inline] | |||
pub fn new(min: f32, max: f32) -> Self { | |||
Self { | |||
min, | |||
max, | |||
fleets: smallvec![], | |||
} | |||
} | |||
/* MeetingPoint */ | |||
impl MeetingPoint { | |||
#[inline] | |||
pub fn min(&self) -> f32 { | |||
self.min | |||
@@ -62,21 +55,20 @@ impl MeetingPoint { | |||
&self.fleets | |||
} | |||
#[inline] | |||
pub(crate) fn fleets_mut(&mut self) -> &mut Fleets { | |||
&mut self.fleets | |||
} | |||
#[inline] | |||
pub fn fleet(&self, index: usize) -> Option<Entity> { | |||
self.fleets.get(index).cloned().flatten() | |||
} | |||
} | |||
impl MeetingPoint { | |||
#[inline] | |||
pub(crate) fn fleet_mut(&mut self, index: usize) -> &mut Option<Entity> { | |||
self.fleets.resize_with(index + 1, Default::default); | |||
&mut self.fleets[index] | |||
pub(crate) fn new(min: f32, max: f32) -> Self { | |||
Self { | |||
min, | |||
max, | |||
fleets: smallvec![], | |||
} | |||
} | |||
} | |||
@@ -119,17 +111,17 @@ where | |||
} | |||
} | |||
impl MeetingPointOwned { | |||
pub fn new(owner: Entity) -> Self { | |||
Self { owner } | |||
} | |||
/* MeetingPointOwned */ | |||
impl MeetingPointOwned { | |||
pub fn owner(&self) -> Entity { | |||
self.owner | |||
} | |||
} | |||
pub(crate) fn set_owner(&mut self, value: Entity) { | |||
self.owner = value; | |||
impl MeetingPointOwned { | |||
pub(crate) fn new(owner: Entity) -> Self { | |||
Self { owner } | |||
} | |||
} | |||
@@ -5,6 +5,7 @@ mod obstacle; | |||
mod planet; | |||
mod player; | |||
mod position; | |||
mod shape; | |||
mod ship; | |||
mod velocity; | |||
@@ -14,6 +15,7 @@ pub use meeting_point::{MeetingPoint, MeetingPointOwned}; | |||
pub use obstacle::Obstacle; | |||
pub use planet::Planet; | |||
pub use player::{Player, PlayerOwned}; | |||
pub use position::{Position, Shape}; | |||
pub use position::Position; | |||
pub use shape::Shape; | |||
pub use ship::{Count as ShipCount, Obstacle as ShipObstacle, Ship, Type as ShipType}; | |||
pub use velocity::Velocity; |
@@ -5,6 +5,8 @@ use specs::{Component, NullStorage}; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
pub struct Obstacle {} | |||
/* Obstacle */ | |||
impl Component for Obstacle { | |||
type Storage = NullStorage<Self>; | |||
} |
@@ -1,11 +1,19 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, NullStorage}; | |||
use crate::misc::FlaggedStorage; | |||
use crate::{builder::PlanetBuilder, misc::FlaggedStorage}; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
pub struct Planet {} | |||
/* Planet */ | |||
impl Planet { | |||
pub fn builder() -> PlanetBuilder { | |||
PlanetBuilder::default() | |||
} | |||
} | |||
impl Component for Planet { | |||
type Storage = FlaggedStorage<Self, NullStorage<Self>>; | |||
} |
@@ -6,6 +6,8 @@ use specs::{ | |||
Component, Entity, HashMapStorage, | |||
}; | |||
use crate::builder::PlayerBuilder; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
pub struct Player { | |||
index: usize, | |||
@@ -22,13 +24,12 @@ pub struct PlayerOwnedData<M> { | |||
pub owner: M, | |||
} | |||
/* Player */ | |||
impl Player { | |||
#[inline] | |||
pub fn new(color: Vector3f) -> Self { | |||
Self { | |||
index: next_index(), | |||
color, | |||
} | |||
pub fn builder() -> PlayerBuilder { | |||
PlayerBuilder::default() | |||
} | |||
#[inline] | |||
@@ -42,20 +43,34 @@ impl Player { | |||
} | |||
} | |||
impl Player { | |||
#[inline] | |||
pub(crate) fn new(color: Vector3f) -> Self { | |||
Self { | |||
index: next_index(), | |||
color, | |||
} | |||
} | |||
} | |||
impl Component for Player { | |||
type Storage = HashMapStorage<Self>; | |||
} | |||
impl PlayerOwned { | |||
pub fn new(owner: Entity) -> Self { | |||
Self { owner } | |||
} | |||
/* PlayerOwned */ | |||
impl PlayerOwned { | |||
pub fn owner(&self) -> Entity { | |||
self.owner | |||
} | |||
} | |||
impl PlayerOwned { | |||
pub(crate) fn new(owner: Entity) -> Self { | |||
Self { owner } | |||
} | |||
} | |||
impl Component for PlayerOwned { | |||
type Storage = HashMapStorage<Self>; | |||
} | |||
@@ -5,39 +5,22 @@ use specs::{Component, VecStorage}; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
pub struct Position { | |||
pos: Vector2f, | |||
shape: Shape, | |||
} | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub enum Shape { | |||
Dot, | |||
Circle(f32), | |||
} | |||
/* Position */ | |||
impl Position { | |||
pub fn dot(pos: Vector2f) -> Self { | |||
Self { | |||
pos, | |||
shape: Shape::Dot, | |||
} | |||
} | |||
pub fn circle(pos: Vector2f, radius: f32) -> Self { | |||
Self { | |||
pos, | |||
shape: Shape::Circle(radius), | |||
} | |||
} | |||
pub fn pos(&self) -> &Vector2f { | |||
pub fn get(&self) -> &Vector2f { | |||
&self.pos | |||
} | |||
} | |||
pub fn shape(&self) -> &Shape { | |||
&self.shape | |||
impl Position { | |||
pub(crate) fn new(pos: Vector2f) -> Self { | |||
Self { pos } | |||
} | |||
pub(crate) fn pos_mut(&mut self) -> &mut Vector2f { | |||
pub(crate) fn get_mut(&mut self) -> &mut Vector2f { | |||
&mut self.pos | |||
} | |||
} | |||
@@ -45,26 +28,3 @@ impl Position { | |||
impl Component for Position { | |||
type Storage = VecStorage<Self>; | |||
} | |||
impl Shape { | |||
pub fn radius(&self) -> f32 { | |||
match self { | |||
Self::Dot => 0.0, | |||
Self::Circle(r) => *r, | |||
} | |||
} | |||
pub fn circle(&self) -> Option<f32> { | |||
if let Self::Circle(r) = &self { | |||
Some(*r) | |||
} else { | |||
None | |||
} | |||
} | |||
} | |||
impl Default for Shape { | |||
fn default() -> Self { | |||
Self::Dot | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, HashMapStorage}; | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub enum Shape { | |||
Circle(f32), | |||
} | |||
/* Shape */ | |||
impl Component for Shape { | |||
type Storage = HashMapStorage<Self>; | |||
} | |||
impl Shape { | |||
pub fn radius(&self) -> f32 { | |||
match self { | |||
Self::Circle(r) => *r, | |||
} | |||
} | |||
} |
@@ -5,7 +5,7 @@ use glc::vector::Vector2f; | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, Entity, VecStorage}; | |||
use crate::misc::FlaggedStorage; | |||
use crate::{builder::ShipBuilder, misc::FlaggedStorage}; | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct Ship { | |||
@@ -42,15 +42,12 @@ pub enum Type { | |||
Transporter, | |||
} | |||
/* Ship */ | |||
impl Ship { | |||
#[inline] | |||
pub fn new(type_: Type) -> Self { | |||
Self { | |||
type_, | |||
obstacle: Default::default(), | |||
target_pos: Default::default(), | |||
target_dir: Default::default(), | |||
} | |||
pub fn builder() -> ShipBuilder { | |||
ShipBuilder::default() | |||
} | |||
#[inline] | |||
@@ -69,14 +66,26 @@ impl Ship { | |||
} | |||
#[inline] | |||
pub(crate) fn set_target(&mut self, pos: Vector2f, dir: Vector2f) { | |||
self.target_pos = pos; | |||
self.target_dir = dir; | |||
pub fn obstacle(&self) -> Obstacle { | |||
self.obstacle | |||
} | |||
} | |||
impl Ship { | |||
#[inline] | |||
pub fn obstacle(&self) -> Obstacle { | |||
self.obstacle | |||
pub(crate) fn new(type_: Type) -> Self { | |||
Self { | |||
type_, | |||
obstacle: Default::default(), | |||
target_pos: Default::default(), | |||
target_dir: Default::default(), | |||
} | |||
} | |||
#[inline] | |||
pub(crate) fn set_target(&mut self, pos: Vector2f, dir: Vector2f) { | |||
self.target_pos = pos; | |||
self.target_dir = dir; | |||
} | |||
#[inline] | |||
@@ -89,12 +98,16 @@ impl Component for Ship { | |||
type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||
} | |||
/* Obstacle */ | |||
impl Default for Obstacle { | |||
fn default() -> Self { | |||
Self::Search | |||
} | |||
} | |||
/* Count */ | |||
impl Count { | |||
pub fn all() -> Self { | |||
Self { | |||
@@ -8,24 +8,28 @@ pub struct Velocity { | |||
speed: f32, | |||
} | |||
impl Velocity { | |||
pub fn new(dir: Vector2f, speed: f32) -> Self { | |||
Self { dir, speed } | |||
} | |||
/* Velocity */ | |||
impl Velocity { | |||
pub fn dir(&self) -> &Vector2f { | |||
&self.dir | |||
} | |||
pub fn dir_mut(&mut self) -> &mut Vector2f { | |||
&mut self.dir | |||
} | |||
pub fn speed(&self) -> f32 { | |||
self.speed | |||
} | |||
} | |||
impl Velocity { | |||
pub(crate) fn new(dir: Vector2f, speed: f32) -> Self { | |||
Self { dir, speed } | |||
} | |||
pub(crate) fn dir_mut(&mut self) -> &mut Vector2f { | |||
&mut self.dir | |||
} | |||
} | |||
impl Component for Velocity { | |||
type Storage = VecStorage<Self>; | |||
} |
@@ -3,7 +3,7 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||
use crate::{ | |||
components::Player, | |||
resources::Global, | |||
systems::{FleetControl, FleetOwnedUpdate, MeetingPointOwnedUpdate, Movement, Process, Ships}, | |||
systems::{FleetControl, FleetOwnedUpdate, Movement, Process, Ships}, | |||
Error, | |||
}; | |||
@@ -23,11 +23,6 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||
.with(Ships::new(world), "ships", &[]) | |||
.with(FleetControl::new(world)?, "fleet_control", &[]) | |||
.with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | |||
.with( | |||
MeetingPointOwnedUpdate::new(world), | |||
"meeting_point_owned_update", | |||
&[], | |||
) | |||
.build(); | |||
dispatcher.setup(world); | |||
@@ -1,3 +1,4 @@ | |||
pub mod builder; | |||
pub mod components; | |||
pub mod constants; | |||
pub mod dispatcher; | |||
@@ -11,5 +11,5 @@ pub use flagged_storage::{ | |||
ComponentEvent, FlaggedStorage, StorageHelper, StorageHelperMut, Tracked, | |||
}; | |||
pub use log_result::LogResult; | |||
pub use persistence::{PersistWorld, Persistence}; | |||
pub use world::WorldHelper; | |||
pub use persistence::Persistence; | |||
pub use world::{WorldHelper, WorldPersistence}; |
@@ -1,37 +1,10 @@ | |||
use serde::{de::Deserializer, ser::Serializer}; | |||
use specs::{ | |||
error::NoError, | |||
saveload::{ConvertSaveload, DeserializeComponents, Marker, SerializeComponents, SimpleMarker}, | |||
saveload::{ConvertSaveload, DeserializeComponents, Marker, SerializeComponents}, | |||
Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | |||
}; | |||
use crate::components::{ | |||
Asteroid, FleetOwned, MeetingPoint, MeetingPointOwned, Obstacle, Planet, Player, PlayerOwned, | |||
Position, Ship, Velocity, | |||
}; | |||
/* PersistWorld */ | |||
pub struct PersistWorld; | |||
pub struct PersistWorldMarker; | |||
impl Persistence for PersistWorld { | |||
type Marker = SimpleMarker<PersistWorldMarker>; | |||
type Components = ( | |||
Position, | |||
Velocity, | |||
Player, | |||
PlayerOwned, | |||
MeetingPoint, | |||
MeetingPointOwned, | |||
FleetOwned, | |||
Ship, | |||
Obstacle, | |||
Planet, | |||
Asteroid, | |||
); | |||
} | |||
/* Persistence */ | |||
pub trait Persistence { | |||
@@ -45,6 +18,8 @@ pub trait Persistence { | |||
{ | |||
world.register::<Self::Marker>(); | |||
world.insert(<Self::Marker as Marker>::Allocator::default()); | |||
Self::Components::setup(world); | |||
} | |||
fn serialize<S>(&self, world: &World, serializer: S) -> Result<(), S::Error> | |||
@@ -68,6 +43,8 @@ pub trait PersistenceComponents<M> | |||
where | |||
M: Marker, | |||
{ | |||
fn setup(world: &mut World); | |||
fn serialize<S>(world: &World, serializer: S) -> Result<(), S::Error> | |||
where | |||
S: Serializer; | |||
@@ -85,7 +62,12 @@ macro_rules! define_persistence_components { | |||
M: Marker, | |||
M::Allocator: Default, | |||
$($T: Component + ConvertSaveload<M, Error = NoError>,)+ | |||
$($T::Storage: Default,)+ | |||
{ | |||
fn setup(world: &mut World) { | |||
$( world.register::<$T>(); )* | |||
} | |||
fn serialize<S>(world: &World, serializer: S) -> Result<(), S::Error> | |||
where | |||
S: Serializer, | |||
@@ -3,9 +3,15 @@ use std::any::type_name; | |||
use serde::{de::Deserializer, ser::Serializer}; | |||
use shred::{Fetch, FetchMut, Resource}; | |||
use shrev::{Event, EventChannel, ReaderId}; | |||
use specs::World; | |||
use specs::{saveload::SimpleMarker, World}; | |||
use crate::Error; | |||
use crate::{ | |||
components::{ | |||
Asteroid, MeetingPoint, MeetingPointOwned, Obstacle, Planet, Player, PlayerOwned, Position, | |||
Ship, Velocity, | |||
}, | |||
Error, | |||
}; | |||
use super::Persistence; | |||
@@ -75,3 +81,24 @@ impl WorldHelper for World { | |||
persistence.deserialize(self, deserializer) | |||
} | |||
} | |||
/* WorldPersistence */ | |||
pub struct WorldPersistence; | |||
pub struct PersistWorldMarker; | |||
impl Persistence for WorldPersistence { | |||
type Marker = SimpleMarker<PersistWorldMarker>; | |||
type Components = ( | |||
Position, | |||
Velocity, | |||
Player, | |||
PlayerOwned, | |||
MeetingPoint, | |||
MeetingPointOwned, | |||
Ship, | |||
Obstacle, | |||
Planet, | |||
Asteroid, | |||
); | |||
} |
@@ -1,16 +1,19 @@ | |||
// TODO | |||
#![allow(dead_code)] | |||
#![allow(unused_mut)] | |||
#![allow(unused_imports)] | |||
#![allow(unused_variables)] | |||
use shrev::{EventChannel, ReaderId}; | |||
use specs::{ | |||
prelude::*, saveload::MarkedBuilder, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, | |||
System, World, WriteStorage, | |||
prelude::*, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, System, World, WriteStorage, | |||
}; | |||
use crate::{ | |||
components::{ | |||
Fleet, FleetOwned, MeetingPoint, MeetingPointOwned, Player, PlayerOwned, Ship, ShipCount, | |||
ShipType, | |||
}, | |||
continue_if_none, | |||
misc::{LogResult, PersistWorld, Persistence, WorldHelper}, | |||
misc::WorldHelper, | |||
Error, | |||
}; | |||
@@ -20,15 +23,12 @@ pub struct FleetControl { | |||
#[derive(Debug)] | |||
pub enum FleetControlEvent { | |||
Move(MoveArgs), | |||
} | |||
#[derive(Debug)] | |||
pub struct MoveArgs { | |||
player: Entity, | |||
target: Entity, | |||
fleet: Entity, | |||
count: ShipCount, | |||
MoveToMeetingPoint { | |||
player: Entity, | |||
fleet: Entity, | |||
count: ShipCount, | |||
target: Entity, | |||
}, | |||
} | |||
#[derive(SystemData)] | |||
@@ -74,6 +74,7 @@ impl<'a> System<'a> for FleetControl { | |||
ships, | |||
} = data; | |||
/* TODO | |||
let events = fleet_control_events.read(&mut self.fleet_control_event_id); | |||
for event in events { | |||
match event { | |||
@@ -91,7 +92,7 @@ impl<'a> System<'a> for FleetControl { | |||
f @ None => { | |||
let fleet = lazy | |||
.create_entity(&entities) | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.marked::<<WorldPersistence as Persistence>::Marker>() | |||
.build(); | |||
player_owned | |||
@@ -132,16 +133,6 @@ impl<'a> System<'a> for FleetControl { | |||
} | |||
} | |||
} | |||
} | |||
} | |||
impl FleetControlEvent { | |||
pub fn move_(player: Entity, target: Entity, fleet: Entity, count: ShipCount) -> Self { | |||
Self::Move(MoveArgs { | |||
player, | |||
target, | |||
fleet, | |||
count, | |||
}) | |||
*/ | |||
} | |||
} |
@@ -102,17 +102,9 @@ impl<'a> System<'a> for FleetOwnedUpdate { | |||
}; | |||
if old_match && !new_match { | |||
let count = fleet.count_mut(); | |||
let count = &mut count[ship.type_()]; | |||
*count = count.saturating_sub(1); | |||
fleet.owned_mut().remove(ship_id.id()); | |||
fleet.remove_ship(ship_id, ship); | |||
} else if !old_match && new_match { | |||
let count = fleet.count_mut(); | |||
let count = &mut count[ship.type_()]; | |||
*count += 1; | |||
fleet.owned_mut().add(ship_id.id()); | |||
fleet.add_ship(ship_id, ship); | |||
} | |||
} | |||
} | |||
@@ -1,130 +0,0 @@ | |||
use std::collections::HashMap; | |||
use shrev::ReaderId; | |||
use specs::{ | |||
hibitset::BitSet, prelude::*, world::Index, Entities, Entity, ReadStorage, System, World, | |||
WriteStorage, | |||
}; | |||
use crate::{ | |||
components::{MeetingPoint, MeetingPointOwned, PlayerOwned}, | |||
misc::{ComponentEvent, StorageHelper, StorageHelperMut}, | |||
}; | |||
pub struct MeetingPointOwnedUpdate { | |||
meeting_point_ids: BitSet, | |||
meeting_point_owned_ids: BitSet, | |||
old_meeting_point_ids: HashMap<Index, Entity>, | |||
meeting_point_owned_event_id: ReaderId<crate::misc::ComponentEvent<MeetingPointOwned>>, | |||
} | |||
impl MeetingPointOwnedUpdate { | |||
pub fn new(world: &mut World) -> Self { | |||
WriteStorage::<MeetingPointOwned>::setup(world); | |||
let meeting_point_ids = BitSet::new(); | |||
let meeting_point_owned_ids = BitSet::new(); | |||
let old_meeting_point_ids = HashMap::new(); | |||
let meeting_point_owned_event_id = world | |||
.system_data::<WriteStorage<MeetingPointOwned>>() | |||
.register_event_reader(); | |||
Self { | |||
meeting_point_ids, | |||
meeting_point_owned_ids, | |||
old_meeting_point_ids, | |||
meeting_point_owned_event_id, | |||
} | |||
} | |||
} | |||
#[derive(SystemData)] | |||
pub struct MeetingPointOwnedUpdateData<'a> { | |||
entities: Entities<'a>, | |||
meeting_points: WriteStorage<'a, MeetingPoint>, | |||
player_owned: ReadStorage<'a, PlayerOwned>, | |||
meeting_point_owned: ReadStorage<'a, MeetingPointOwned>, | |||
} | |||
impl<'a> System<'a> for MeetingPointOwnedUpdate { | |||
type SystemData = MeetingPointOwnedUpdateData<'a>; | |||
fn run(&mut self, data: Self::SystemData) { | |||
let MeetingPointOwnedUpdateData { | |||
entities, | |||
mut meeting_points, | |||
player_owned, | |||
meeting_point_owned, | |||
} = data; | |||
self.meeting_point_ids.clear(); | |||
self.meeting_point_owned_ids.clear(); | |||
self.old_meeting_point_ids.clear(); | |||
/* handle events */ | |||
let events = meeting_point_owned | |||
.channel() | |||
.read(&mut self.meeting_point_owned_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::Modified(id, meeting_point_owned) => { | |||
self.meeting_point_ids.add(meeting_point_owned.owner().id()); | |||
self.meeting_point_owned_ids.add(*id); | |||
*self | |||
.old_meeting_point_ids | |||
.entry(*id) | |||
.or_insert_with(|| meeting_point_owned.owner()) = | |||
meeting_point_owned.owner(); | |||
} | |||
ComponentEvent::Removed(id, meeting_point_owned) => { | |||
self.meeting_point_ids.add(meeting_point_owned.owner().id()); | |||
self.meeting_point_owned_ids.add(*id); | |||
*self | |||
.old_meeting_point_ids | |||
.entry(*id) | |||
.or_insert_with(|| meeting_point_owned.owner()) = | |||
meeting_point_owned.owner(); | |||
} | |||
} | |||
} | |||
/* 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()); | |||
} | |||
/* update meeting_points */ | |||
for (meeting_point_id, meeting_point, _) in | |||
(&entities, &mut meeting_points, &self.meeting_point_ids).join() | |||
{ | |||
let data = ( | |||
&entities, | |||
&meeting_point_owned, | |||
&player_owned, | |||
&self.meeting_point_owned_ids, | |||
); | |||
for (fleet_id, meeting_point_owned, player_owned, _) in data.join() { | |||
let new_match = meeting_point_id == meeting_point_owned.owner(); | |||
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().id() as usize; | |||
if old_match && !new_match { | |||
if let Some(fleet) = meeting_point.fleets_mut().get_mut(player_id) { | |||
*fleet = None; | |||
} | |||
} else if !old_match && new_match { | |||
*meeting_point.fleet_mut(player_id) = Some(fleet_id); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,13 +1,11 @@ | |||
mod fleet_control; | |||
mod fleet_owned_update; | |||
mod meeting_point_owned_update; | |||
mod movement; | |||
mod process; | |||
mod ships; | |||
pub use fleet_control::{FleetControl, FleetControlEvent}; | |||
pub use fleet_owned_update::FleetOwnedUpdate; | |||
pub use meeting_point_owned_update::MeetingPointOwnedUpdate; | |||
pub use movement::Movement; | |||
pub use process::Process; | |||
pub use ships::Ships; |
@@ -31,8 +31,9 @@ impl<'a> System<'a> for Movement { | |||
.par_join() | |||
.for_each(|(position, velocity)| { | |||
let delta = global.delta * global.world_speed; | |||
let position = position.get_mut(); | |||
*position.pos_mut() += velocity.dir() * velocity.speed() * delta; | |||
*position += velocity.dir() * velocity.speed() * delta; | |||
}); | |||
} | |||
} |
@@ -12,7 +12,7 @@ use specs::{ | |||
use crate::{ | |||
components::{ | |||
FleetOwned, MeetingPoint, MeetingPointOwned, Obstacle, Position, Ship, ShipObstacle, | |||
FleetOwned, MeetingPoint, MeetingPointOwned, Obstacle, Position, Shape, Ship, ShipObstacle, | |||
Velocity, | |||
}, | |||
constants::{ | |||
@@ -38,6 +38,7 @@ pub struct ShipsData<'a> { | |||
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>, | |||
} | |||
@@ -46,6 +47,7 @@ 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>, | |||
@@ -94,6 +96,7 @@ impl<'a> System<'a> for Ships { | |||
fleet_owned, | |||
meeting_point_owned, | |||
positions, | |||
shapes, | |||
obstacles, | |||
meeting_points, | |||
} = data; | |||
@@ -105,6 +108,7 @@ impl<'a> System<'a> for Ships { | |||
need_update: &self.need_update, | |||
entities: &entities, | |||
positions: &positions, | |||
shapes: &shapes, | |||
obstacles: &obstacles, | |||
meeting_point_owned: &meeting_point_owned, | |||
meeting_points: &meeting_points, | |||
@@ -143,8 +147,8 @@ impl Processor<'_> { | |||
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)).pos(); | |||
let ship_pos = position.pos(); | |||
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(); | |||
@@ -213,7 +217,7 @@ impl Processor<'_> { | |||
} 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.pos(); | |||
let obstacle_pos = position.get(); | |||
let ship_to_obstacle = obstacle_pos - ship_pos; | |||
let obstacle_angle = ship_to_target | |||
@@ -237,7 +241,7 @@ impl Processor<'_> { | |||
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.pos(); | |||
let obstacle_pos = position.get(); | |||
let ship_to_obstacle = obstacle_pos - ship_pos; | |||
if ship_to_target * ship_to_obstacle < 0.0 { | |||
@@ -262,8 +266,9 @@ impl Processor<'_> { | |||
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.pos() - ship_pos; | |||
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(); | |||
@@ -272,7 +277,7 @@ impl Processor<'_> { | |||
if meeting_point < meeting_point_max { | |||
let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x); | |||
let radius = obstacle_pos.shape().radius(); | |||
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); | |||