@@ -1530,6 +1530,7 @@ name = "space-crush-common" | |||
version = "0.1.0" | |||
dependencies = [ | |||
"glc", | |||
"hibitset", | |||
"lazy_static", | |||
"log", | |||
"log4rs", | |||
@@ -1737,7 +1738,7 @@ version = "0.2.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
checksum = "d68a369614cd12ca384ca6e75ab0a5ac3a5acbfe8003622e20808a322b9bb652" | |||
dependencies = [ | |||
"flate2 0.2.20", | |||
"flate2 1.0.14", | |||
"vfs", | |||
"zip", | |||
] | |||
@@ -20,5 +20,5 @@ shred = { version = "0.10", features = [ "shred-derive" ] } | |||
shrev = "1.1" | |||
smallvec = { version = "1.5", features = [ "serde" ] } | |||
space-crush-common = "0.1" | |||
specs = "0.16" | |||
specs = "*" | |||
thiserror = "1.0" |
@@ -0,0 +1,11 @@ | |||
use space_crush_common::components::ShipCount; | |||
use specs::{Component, HashMapStorage}; | |||
#[derive(Default, Debug)] | |||
pub struct FleetInfo { | |||
pub count: ShipCount, | |||
} | |||
impl Component for FleetInfo { | |||
type Storage = HashMapStorage<Self>; | |||
} |
@@ -0,0 +1,3 @@ | |||
mod fleet_info; | |||
pub use fleet_info::FleetInfo; |
@@ -32,15 +32,15 @@ impl<'a> System<'a> for Fleets { | |||
for (p, f) in (&position, &fleet).join() { | |||
geometry.render_lines( | |||
Vector4f::new(0.5, 0.5, 0.5, 0.2), | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle(p.pos, f.orbit_min), | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(0.5, 0.5, 0.5, 0.2), | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle(p.pos, f.orbit_max), | |||
); | |||
geometry.render_lines( | |||
Vector4f::new(0.5, 0.5, 0.5, 0.1), | |||
Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
&create_circle(p.pos, SHIP_ORBIT_DISTANCE_MAX * f.orbit_max), | |||
); | |||
} | |||
@@ -1,20 +1,21 @@ | |||
mod constants; | |||
mod debug; | |||
mod error; | |||
mod misc; | |||
mod render; | |||
mod resources; | |||
mod systems; | |||
pub mod components; | |||
pub mod constants; | |||
pub mod debug; | |||
pub mod error; | |||
pub mod misc; | |||
pub mod render; | |||
pub mod resources; | |||
pub mod systems; | |||
use specs::{Dispatcher, DispatcherBuilder, World}; | |||
use specs::{Dispatcher, DispatcherBuilder, Entity, World}; | |||
pub use error::Error; | |||
use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary}; | |||
use misc::{Events, TextManager, Window}; | |||
use render::{Asteroids, Init, Planets, Ships}; | |||
use resources::{Camera, Config, Geometry, State, Uniform}; | |||
use systems::StateUpdate; | |||
use resources::{Camera, Config, Geometry, PlayerState, State, Uniform}; | |||
use systems::{FleetInfoUpdate, StateUpdate}; | |||
pub struct App<'a, 'b> { | |||
is_running: bool, | |||
@@ -24,7 +25,7 @@ pub struct App<'a, 'b> { | |||
} | |||
impl<'a, 'b> App<'a, 'b> { | |||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||
pub fn new(world: &mut World, player_id: Entity) -> Result<Self, Error> { | |||
let config = Config::new(world)?; | |||
let events = Events::new(world)?; | |||
let window = Window::new(events.handle(), &config)?; | |||
@@ -33,17 +34,20 @@ impl<'a, 'b> App<'a, 'b> { | |||
let camera = Camera::new()?; | |||
let uniform = Uniform::new()?; | |||
let geometry = Geometry::new(world)?; | |||
let player_state = PlayerState::new(player_id); | |||
world.insert(state); | |||
world.insert(config); | |||
world.insert(camera); | |||
world.insert(uniform); | |||
world.insert(geometry); | |||
world.insert(player_state); | |||
let text_manager = TextManager::new(world)?; | |||
let mut dispatcher = DispatcherBuilder::new() | |||
.with(StateUpdate::new(world)?, "state_update", &[]) | |||
.with(FleetInfoUpdate::new(world)?, "fleet_info_update", &[]) | |||
.with_thread_local(Init::new(world)?) | |||
.with_thread_local(Planets::new(world)?) | |||
.with_thread_local(Asteroids::new(world)?) | |||
@@ -5,7 +5,7 @@ use space_crush_common::{ | |||
misc::{init_logger, Vfs}, | |||
Dispatcher, | |||
}; | |||
use specs::{World, WorldExt}; | |||
use specs::{Builder, Entity, World, WorldExt}; | |||
fn main() -> Result<(), Error> { | |||
let vfs = Vfs::new(&["space-crush-app"])?; | |||
@@ -33,12 +33,12 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
let mut world = World::new(); | |||
world.insert(vfs); | |||
let player1 = world.create_entity().build(); | |||
let mut common = Dispatcher::new(&mut world); | |||
let mut app = App::new(&mut world)?; | |||
let mut app = App::new(&mut world, player1)?; | |||
if !load_world(&mut world, WORLD_FILEPATH)? { | |||
create_world(&mut world); | |||
} | |||
create_world(&mut world, player1); | |||
info!("Application initialized"); | |||
@@ -49,31 +49,35 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
app.process(&world)?; | |||
} | |||
save_world(&mut world, WORLD_FILEPATH)?; | |||
Ok(()) | |||
} | |||
fn create_world(world: &mut World) { | |||
fn create_world(world: &mut World, player_id: Entity) { | |||
use glc::{ | |||
matrix::Angle, | |||
vector::{Vector2f, Vector4f}, | |||
}; | |||
use space_crush_app::components::FleetInfo; | |||
use space_crush_common::{ | |||
components::{ | |||
Asteroid, AsteroidType, Fleet, Owned, Planet, Player, Position, Ship, ShipType, | |||
Velocity, | |||
Asteroid, AsteroidType, Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, Ship, | |||
ShipType, Velocity, | |||
}, | |||
misc::{PersistWorld, Persistence}, | |||
}; | |||
use specs::{saveload::MarkedBuilder, Builder}; | |||
use specs::{saveload::MarkedBuilder, WriteStorage}; | |||
let player1 = world | |||
.create_entity() | |||
.with(Player { | |||
color: Vector4f::new(0.0, 0.5, 1.0, 0.1), | |||
}) | |||
.build(); | |||
PersistWorld::setup(world); | |||
world | |||
.system_data::<WriteStorage<Player>>() | |||
.insert( | |||
player_id, | |||
Player { | |||
color: Vector4f::new(0.0, 0.5, 1.0, 0.1), | |||
}, | |||
) | |||
.unwrap(); | |||
world | |||
.create_entity() | |||
@@ -86,6 +90,7 @@ fn create_world(world: &mut World) { | |||
orbit_min: 125.0, | |||
orbit_max: 175.0, | |||
}) | |||
.with(FleetInfo::default()) | |||
.with(Asteroid { | |||
type_: AsteroidType::Metal, | |||
}) | |||
@@ -102,6 +107,7 @@ fn create_world(world: &mut World) { | |||
orbit_min: 125.0, | |||
orbit_max: 175.0, | |||
}) | |||
.with(FleetInfo::default()) | |||
.with(Asteroid { | |||
type_: AsteroidType::Crystal, | |||
}) | |||
@@ -110,7 +116,7 @@ fn create_world(world: &mut World) { | |||
let planet = world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(Owned { owner: player1 }) | |||
.with(PlayerOwned { owner: player_id }) | |||
.with(Position { | |||
pos: Vector2f::default(), | |||
size: 250.0, | |||
@@ -119,6 +125,7 @@ fn create_world(world: &mut World) { | |||
orbit_min: 325.0, | |||
orbit_max: 425.0, | |||
}) | |||
.with(FleetInfo::default()) | |||
.with(Planet {}) | |||
.build(); | |||
@@ -126,7 +133,7 @@ fn create_world(world: &mut World) { | |||
world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(Owned { owner: player1 }) | |||
.with(PlayerOwned { owner: player_id }) | |||
.with(Position { | |||
pos: Vector2f::new( | |||
500.0 * random::<f32>() - 250.0, | |||
@@ -138,6 +145,7 @@ fn create_world(world: &mut World) { | |||
dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | |||
speed: 100.0, | |||
}) | |||
.with(FleetOwned { owner: planet }) | |||
.with(Ship { | |||
type_: match i % 3 { | |||
0 => ShipType::Fighter, | |||
@@ -145,7 +153,6 @@ fn create_world(world: &mut World) { | |||
2 => ShipType::Transporter, | |||
_ => unreachable!(), | |||
}, | |||
fleet: planet, | |||
agility: Angle::Deg(360.0), | |||
target_pos: Default::default(), | |||
target_dir: Default::default(), | |||
@@ -153,37 +160,3 @@ fn create_world(world: &mut World) { | |||
.build(); | |||
} | |||
} | |||
fn load_world(world: &mut World, path: &str) -> Result<bool, Error> { | |||
use serde_json::de::{Deserializer, IoRead}; | |||
use space_crush_common::misc::{PersistWorld, Persistence, WorldHelper}; | |||
PersistWorld::setup(world); | |||
let vfs = world.resource::<Vfs>()?; | |||
let path = vfs.join(path)?; | |||
if !path.exists() { | |||
return Ok(false); | |||
} | |||
let mut file = path.open_file()?; | |||
let mut read = IoRead::new(&mut file); | |||
let mut deserializer = Deserializer::new(&mut read); | |||
world.deserialize(PersistWorld, &mut deserializer)?; | |||
Ok(true) | |||
} | |||
fn save_world(world: &mut World, path: &str) -> Result<(), Error> { | |||
use serde_json::Serializer; | |||
use space_crush_common::misc::{PersistWorld, WorldHelper}; | |||
let vfs = world.resource::<Vfs>()?; | |||
let mut file = vfs.join(path)?.create_file()?; | |||
let mut serializer = Serializer::new(&mut file); | |||
world.serialize(PersistWorld, &mut serializer)?; | |||
Ok(()) | |||
} | |||
const WORLD_FILEPATH: &str = "resources/world.json"; |
@@ -99,7 +99,6 @@ impl Window { | |||
(monitor_size.width - window_size.width) / 2, | |||
(monitor_size.height - window_size.height) / 2, | |||
)); | |||
window.set_cursor_grab(true).unwrap(); | |||
let context = unsafe { context.make_current().unwrap() }; | |||
gl::load_with(|s| context.get_proc_address(s)); | |||
@@ -6,7 +6,7 @@ use glc::{ | |||
vector::Vector4f, | |||
}; | |||
use space_crush_common::{ | |||
components::{Asteroid, AsteroidType, Owned, Player, Position}, | |||
components::{Asteroid, AsteroidType, Player, PlayerOwned, Position}, | |||
misc::LogResult, | |||
}; | |||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
@@ -61,7 +61,7 @@ pub struct AsteroidsData<'a> { | |||
geometry: ReadExpect<'a, Geometry>, | |||
position: ReadStorage<'a, Position>, | |||
asteroid: ReadStorage<'a, Asteroid>, | |||
owned: ReadStorage<'a, Owned>, | |||
owned: ReadStorage<'a, PlayerOwned>, | |||
player: ReadStorage<'a, Player>, | |||
} | |||
@@ -6,7 +6,7 @@ use glc::{ | |||
vector::Vector4f, | |||
}; | |||
use space_crush_common::{ | |||
components::{Owned, Planet, Player, Position}, | |||
components::{Planet, Player, PlayerOwned, Position}, | |||
misc::LogResult, | |||
}; | |||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
@@ -58,7 +58,7 @@ pub struct PlanetsData<'a> { | |||
geometry: ReadExpect<'a, Geometry>, | |||
position: ReadStorage<'a, Position>, | |||
planet: ReadStorage<'a, Planet>, | |||
owned: ReadStorage<'a, Owned>, | |||
owned: ReadStorage<'a, PlayerOwned>, | |||
player: ReadStorage<'a, Player>, | |||
} | |||
@@ -6,7 +6,7 @@ use glc::{ | |||
vector::Vector4f, | |||
}; | |||
use space_crush_common::{ | |||
components::{Owned, Player, Position, Ship, ShipType, Velocity}, | |||
components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity}, | |||
misc::LogResult, | |||
}; | |||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
@@ -65,7 +65,7 @@ pub struct ShipsData<'a> { | |||
position: ReadStorage<'a, Position>, | |||
velocity: ReadStorage<'a, Velocity>, | |||
ship: ReadStorage<'a, Ship>, | |||
owned: ReadStorage<'a, Owned>, | |||
owned: ReadStorage<'a, PlayerOwned>, | |||
player: ReadStorage<'a, Player>, | |||
} | |||
@@ -1,11 +1,13 @@ | |||
mod camera; | |||
mod config; | |||
mod geometry; | |||
mod player_state; | |||
mod state; | |||
mod uniform; | |||
pub use camera::Camera; | |||
pub use config::Config; | |||
pub use geometry::Geometry; | |||
pub use player_state::PlayerState; | |||
pub use state::State; | |||
pub use uniform::Uniform; |
@@ -0,0 +1,13 @@ | |||
#![allow(dead_code)] | |||
use specs::Entity; | |||
pub struct PlayerState { | |||
pub player_id: Entity, | |||
} | |||
impl PlayerState { | |||
pub fn new(player_id: Entity) -> Self { | |||
Self { player_id } | |||
} | |||
} |
@@ -0,0 +1,165 @@ | |||
use std::collections::HashMap; | |||
use shrev::ReaderId; | |||
use space_crush_common::{ | |||
components::{FleetOwned, PlayerOwned, Ship}, | |||
misc::ComponentEvent, | |||
}; | |||
use specs::{ | |||
hibitset::BitSetLike, prelude::*, world::Index, Entities, ReadExpect, ReadStorage, System, | |||
World, WriteStorage, | |||
}; | |||
use crate::{components::FleetInfo, resources::PlayerState, Error}; | |||
pub struct FleetInfoUpdate { | |||
modified: HashMap<Index, Modified>, | |||
need_update: BitSet, | |||
fleet_owned_id: ReaderId<ComponentEvent<FleetOwned>>, | |||
player_owned_id: ReaderId<ComponentEvent<PlayerOwned>>, | |||
} | |||
#[derive(Default)] | |||
struct Modified { | |||
old_fleet_id: Option<Entity>, | |||
old_player_id: Option<Entity>, | |||
} | |||
impl FleetInfoUpdate { | |||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||
let modified = HashMap::new(); | |||
let need_update = BitSet::new(); | |||
let fleet_owned_id = unsafe { | |||
WriteStorage::<FleetOwned>::setup(world); | |||
let mut fleet_owned = world.system_data::<WriteStorage<FleetOwned>>(); | |||
fleet_owned | |||
.unprotected_storage_mut() | |||
.channel_mut() | |||
.register_reader() | |||
}; | |||
let player_owned_id = unsafe { | |||
WriteStorage::<PlayerOwned>::setup(world); | |||
let mut player_owned = world.system_data::<WriteStorage<PlayerOwned>>(); | |||
player_owned | |||
.unprotected_storage_mut() | |||
.channel_mut() | |||
.register_reader() | |||
}; | |||
Ok(Self { | |||
modified, | |||
need_update, | |||
fleet_owned_id, | |||
player_owned_id, | |||
}) | |||
} | |||
} | |||
#[derive(SystemData)] | |||
pub struct FleetInfoUpdateData<'a> { | |||
entities: Entities<'a>, | |||
player_state: ReadExpect<'a, PlayerState>, | |||
ships: ReadStorage<'a, Ship>, | |||
fleet_infos: WriteStorage<'a, FleetInfo>, | |||
fleet_owned: ReadStorage<'a, FleetOwned>, | |||
player_owned: ReadStorage<'a, PlayerOwned>, | |||
} | |||
impl<'a> System<'a> for FleetInfoUpdate { | |||
type SystemData = FleetInfoUpdateData<'a>; | |||
fn run(&mut self, data: Self::SystemData) { | |||
let FleetInfoUpdateData { | |||
entities, | |||
player_state, | |||
ships, | |||
mut fleet_infos, | |||
fleet_owned, | |||
player_owned, | |||
} = data; | |||
self.modified.clear(); | |||
self.need_update.clear(); | |||
/* player owned events */ | |||
let events = player_owned | |||
.unprotected_storage() | |||
.channel() | |||
.read(&mut self.player_owned_id); | |||
for event in events { | |||
match event { | |||
ComponentEvent::Inserted(id) => { | |||
self.need_update.add(*id); | |||
} | |||
ComponentEvent::Modified(id, player_owned) | |||
| ComponentEvent::Removed(id, player_owned) => { | |||
self.need_update.add(*id); | |||
self.modified.entry(*id).or_default().old_player_id = Some(player_owned.owner); | |||
} | |||
} | |||
} | |||
/* fleet owned events */ | |||
let events = fleet_owned | |||
.unprotected_storage() | |||
.channel() | |||
.read(&mut self.fleet_owned_id); | |||
for event in events { | |||
match event { | |||
ComponentEvent::Inserted(id) => { | |||
self.need_update.add(*id); | |||
} | |||
ComponentEvent::Modified(id, fleet_owned) | |||
| ComponentEvent::Removed(id, fleet_owned) => { | |||
self.need_update.add(*id); | |||
self.modified.entry(*id).or_default().old_fleet_id = Some(fleet_owned.owner); | |||
} | |||
} | |||
} | |||
if self.need_update.is_empty() { | |||
return; | |||
} | |||
/* update fleets */ | |||
let player_id = player_state.player_id; | |||
for (fleet_id, fleet_info) in (&entities, &mut fleet_infos).join() { | |||
for (ship_id, ship, fleet_owned, player_owned, _) in ( | |||
&entities, | |||
&ships, | |||
&fleet_owned, | |||
&player_owned, | |||
&self.need_update, | |||
) | |||
.join() | |||
{ | |||
let new_match = fleet_owned.owner == fleet_id && player_owned.owner == player_id; | |||
let old_match = match self.modified.get(&ship_id.id()) { | |||
None => false, | |||
Some(modified) => match (modified.old_fleet_id, modified.old_player_id) { | |||
(Some(old_fleet_id), Some(old_player_id)) => { | |||
old_fleet_id != fleet_id && old_player_id == player_id | |||
} | |||
(Some(old_fleet_id), None) => { | |||
old_fleet_id != fleet_id && player_owned.owner == player_id | |||
} | |||
(None, Some(old_player_id)) => { | |||
fleet_owned.owner != fleet_id && old_player_id == player_id | |||
} | |||
(None, None) => false, | |||
}, | |||
}; | |||
if old_match && !new_match { | |||
fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); | |||
} else if !old_match && new_match { | |||
fleet_info.count[ship.type_] += 1; | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,3 +1,5 @@ | |||
mod fleet_info_update; | |||
mod state_update; | |||
pub use fleet_info_update::FleetInfoUpdate; | |||
pub use state_update::StateUpdate; |
@@ -6,6 +6,7 @@ edition = "2018" | |||
[dependencies] | |||
glc = { version = "0.1", features = [ "serde" ] } | |||
hibitset = "0.6" | |||
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | |||
lazy_static = "1.4" | |||
log4rs = "0.13" | |||
@@ -14,7 +15,7 @@ serde = "1.0" | |||
serde_yaml = "0.8" | |||
shred = { version = "0.10", features = [ "shred-derive" ] } | |||
shrev = "1.1" | |||
specs = { version = "0.16", features = [ "serde" ] } | |||
specs = { version = "*", features = [ "serde" ] } | |||
thiserror = "1.0" | |||
vfs = "0.4" | |||
vfs-zip = "0.2" |
@@ -1,5 +1,11 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, HashMapStorage}; | |||
use specs::{ | |||
error::NoError, | |||
saveload::{ConvertSaveload, Marker}, | |||
Component, Entity, HashMapStorage, VecStorage, | |||
}; | |||
use crate::misc::FlaggedStorage; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
pub struct Fleet { | |||
@@ -7,6 +13,46 @@ pub struct Fleet { | |||
pub orbit_max: f32, | |||
} | |||
#[derive(Copy, Clone, Debug)] | |||
pub struct Owned { | |||
pub owner: Entity, | |||
} | |||
#[derive(Serialize, Deserialize)] | |||
pub struct OwnedData<M> { | |||
pub owner: M, | |||
} | |||
impl Component for Fleet { | |||
type Storage = HashMapStorage<Self>; | |||
} | |||
impl Component for Owned { | |||
type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||
} | |||
impl<M> ConvertSaveload<M> for Owned | |||
where | |||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||
{ | |||
type Data = OwnedData<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(OwnedData { 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(Owned { owner }) | |||
} | |||
} |
@@ -1,6 +1,5 @@ | |||
mod asteroid; | |||
mod fleet; | |||
mod owned; | |||
mod planet; | |||
mod player; | |||
mod position; | |||
@@ -8,10 +7,9 @@ mod ship; | |||
mod velocity; | |||
pub use asteroid::{Asteroid, Type as AsteroidType}; | |||
pub use fleet::Fleet; | |||
pub use owned::Owned; | |||
pub use fleet::{Fleet, Owned as FleetOwned}; | |||
pub use planet::Planet; | |||
pub use player::Player; | |||
pub use player::{Owned as PlayerOwned, Player}; | |||
pub use position::Position; | |||
pub use ship::{Ship, Type as ShipType}; | |||
pub use ship::{Count as ShipCount, Ship, Type as ShipType}; | |||
pub use velocity::Velocity; |
@@ -1,46 +0,0 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{ | |||
error::NoError, | |||
saveload::{ConvertSaveload, Marker}, | |||
Component, Entity, VecStorage, | |||
}; | |||
#[derive(Clone, Debug)] | |||
pub struct Owned { | |||
pub owner: Entity, | |||
} | |||
#[derive(Serialize, Deserialize)] | |||
pub struct OwnedData<M> { | |||
pub owner: M, | |||
} | |||
impl Component for Owned { | |||
type Storage = VecStorage<Self>; | |||
} | |||
impl<M> ConvertSaveload<M> for Owned | |||
where | |||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||
{ | |||
type Data = OwnedData<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(OwnedData { 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(Owned { owner }) | |||
} | |||
} |
@@ -1,12 +1,58 @@ | |||
use glc::vector::Vector4f; | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, HashMapStorage}; | |||
use specs::{ | |||
error::NoError, | |||
saveload::{ConvertSaveload, Marker}, | |||
Component, Entity, HashMapStorage, VecStorage, | |||
}; | |||
use crate::misc::FlaggedStorage; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
pub struct Player { | |||
pub color: Vector4f, | |||
} | |||
#[derive(Copy, Clone, Debug)] | |||
pub struct Owned { | |||
pub owner: Entity, | |||
} | |||
#[derive(Serialize, Deserialize)] | |||
pub struct OwnedData<M> { | |||
pub owner: M, | |||
} | |||
impl Component for Player { | |||
type Storage = HashMapStorage<Self>; | |||
} | |||
impl Component for Owned { | |||
type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||
} | |||
impl<M> ConvertSaveload<M> for Owned | |||
where | |||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||
{ | |||
type Data = OwnedData<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(OwnedData { 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(Owned { owner }) | |||
} | |||
} |
@@ -1,88 +1,53 @@ | |||
use std::ops::{Index, IndexMut}; | |||
use glc::{matrix::Angle, vector::Vector2f}; | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{ | |||
error::NoError, | |||
saveload::{ConvertSaveload, Marker}, | |||
Component, Entity, VecStorage, | |||
}; | |||
use specs::{Component, VecStorage}; | |||
#[derive(Clone, Debug)] | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct Ship { | |||
pub type_: Type, | |||
pub fleet: Entity, | |||
pub agility: Angle<f32>, | |||
pub target_pos: Vector2f, | |||
pub target_dir: Vector2f, | |||
} | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
#[derive(Clone, Debug, Default)] | |||
pub struct Count { | |||
pub fighter: usize, | |||
pub bomber: usize, | |||
pub transporter: usize, | |||
} | |||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] | |||
pub enum Type { | |||
Fighter, | |||
Bomber, | |||
Transporter, | |||
} | |||
#[derive(Serialize, Deserialize)] | |||
pub struct ShipData<M> { | |||
pub type_: Type, | |||
pub fleet: M, | |||
pub agility: Angle<f32>, | |||
pub target_pos: Vector2f, | |||
pub target_dir: Vector2f, | |||
} | |||
impl Component for Ship { | |||
type Storage = VecStorage<Self>; | |||
} | |||
impl<M> ConvertSaveload<M> for Ship | |||
where | |||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||
{ | |||
type Data = ShipData<M>; | |||
type Error = NoError; | |||
impl Index<Type> for Count { | |||
type Output = usize; | |||
fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||
where | |||
F: FnMut(Entity) -> Option<M>, | |||
{ | |||
let Ship { | |||
type_, | |||
fleet, | |||
agility, | |||
target_pos, | |||
target_dir, | |||
} = self.clone(); | |||
let fleet = ids(fleet).unwrap(); | |||
Ok(ShipData { | |||
type_, | |||
fleet, | |||
agility, | |||
target_pos, | |||
target_dir, | |||
}) | |||
fn index(&self, index: Type) -> &Self::Output { | |||
match index { | |||
Type::Fighter => &self.fighter, | |||
Type::Bomber => &self.bomber, | |||
Type::Transporter => &self.transporter, | |||
} | |||
} | |||
} | |||
fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||
where | |||
F: FnMut(M) -> Option<Entity>, | |||
{ | |||
let ShipData { | |||
type_, | |||
fleet, | |||
agility, | |||
target_pos, | |||
target_dir, | |||
} = data; | |||
let fleet = ids(fleet).unwrap(); | |||
Ok(Ship { | |||
type_, | |||
fleet, | |||
agility, | |||
target_pos, | |||
target_dir, | |||
}) | |||
impl IndexMut<Type> for Count { | |||
fn index_mut(&mut self, index: Type) -> &mut Self::Output { | |||
match index { | |||
Type::Fighter => &mut self.fighter, | |||
Type::Bomber => &mut self.bomber, | |||
Type::Transporter => &mut self.transporter, | |||
} | |||
} | |||
} |
@@ -0,0 +1,92 @@ | |||
#![allow(dead_code)] | |||
use hibitset::BitSetLike; | |||
use shrev::EventChannel; | |||
use specs::{ | |||
storage::{TryDefault, UnprotectedStorage}, | |||
world::Index, | |||
Component, DenseVecStorage, | |||
}; | |||
pub struct FlaggedStorage<C, T = DenseVecStorage<C>> | |||
where | |||
C: Send + Sync + 'static, | |||
{ | |||
channel: EventChannel<ComponentEvent<C>>, | |||
storage: T, | |||
} | |||
pub enum ComponentEvent<C> | |||
where | |||
C: Send + Sync + 'static, | |||
{ | |||
Inserted(Index), | |||
Modified(Index, C), | |||
Removed(Index, C), | |||
} | |||
impl<C, T> FlaggedStorage<C, T> | |||
where | |||
C: Send + Sync + 'static, | |||
{ | |||
pub fn channel(&self) -> &EventChannel<ComponentEvent<C>> { | |||
&self.channel | |||
} | |||
pub fn channel_mut(&mut self) -> &mut EventChannel<ComponentEvent<C>> { | |||
&mut self.channel | |||
} | |||
} | |||
impl<C, T> Default for FlaggedStorage<C, T> | |||
where | |||
C: Send + Sync + 'static, | |||
T: TryDefault, | |||
{ | |||
fn default() -> Self { | |||
FlaggedStorage { | |||
channel: EventChannel::new(), | |||
storage: T::unwrap_default(), | |||
} | |||
} | |||
} | |||
impl<C: Component + Clone, T: UnprotectedStorage<C>> UnprotectedStorage<C> for FlaggedStorage<C, T> | |||
where | |||
C: Send + Sync + 'static, | |||
{ | |||
unsafe fn clean<B>(&mut self, has: B) | |||
where | |||
B: BitSetLike, | |||
{ | |||
self.storage.clean(has); | |||
} | |||
unsafe fn get(&self, id: Index) -> &C { | |||
self.storage.get(id) | |||
} | |||
unsafe fn get_mut(&mut self, id: Index) -> &mut C { | |||
let ret = self.storage.get_mut(id); | |||
self.channel | |||
.single_write(ComponentEvent::Modified(id, ret.clone())); | |||
ret | |||
} | |||
unsafe fn insert(&mut self, id: Index, comp: C) { | |||
self.storage.insert(id, comp); | |||
self.channel.single_write(ComponentEvent::Inserted(id)); | |||
} | |||
unsafe fn remove(&mut self, id: Index) -> C { | |||
let c = self.storage.remove(id); | |||
self.channel | |||
.single_write(ComponentEvent::Removed(id, c.clone())); | |||
c | |||
} | |||
} |
@@ -1,3 +1,4 @@ | |||
mod flagged_storage; | |||
mod log; | |||
mod log_result; | |||
mod persistence; | |||
@@ -6,6 +7,7 @@ mod world; | |||
pub use self::log::init as init_logger; | |||
pub use self::vfs::{Vfs, VfsError}; | |||
pub use flagged_storage::{ComponentEvent, FlaggedStorage}; | |||
pub use log_result::LogResult; | |||
pub use persistence::{PersistWorld, Persistence}; | |||
pub use world::WorldHelper; |
@@ -5,7 +5,7 @@ use specs::{ | |||
Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | |||
}; | |||
use crate::components::{Fleet, Owned, Planet, Player, Position, Ship, Velocity}; | |||
use crate::components::{Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, Ship, Velocity}; | |||
/* PersistWorld */ | |||
@@ -14,7 +14,16 @@ pub struct PersistWorldMarker; | |||
impl Persistence for PersistWorld { | |||
type Marker = SimpleMarker<PersistWorldMarker>; | |||
type Components = (Position, Velocity, Planet, Ship, Owned, Player, Fleet); | |||
type Components = ( | |||
Position, | |||
Velocity, | |||
Planet, | |||
Ship, | |||
Player, | |||
PlayerOwned, | |||
Fleet, | |||
FleetOwned, | |||
); | |||
} | |||
/* Persistence */ | |||
@@ -7,7 +7,7 @@ use rand::random; | |||
use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; | |||
use crate::{ | |||
components::{Fleet, Position, Ship, Velocity}, | |||
components::{Fleet, FleetOwned, Position, Ship, Velocity}, | |||
constants::{ | |||
SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, | |||
SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, | |||
@@ -22,6 +22,7 @@ pub struct Fleets; | |||
pub struct FleetsData<'a> { | |||
ship: WriteStorage<'a, Ship>, | |||
velocity: WriteStorage<'a, Velocity>, | |||
fleet_owned: ReadStorage<'a, FleetOwned>, | |||
position: ReadStorage<'a, Position>, | |||
fleet: ReadStorage<'a, Fleet>, | |||
global: Read<'a, Global>, | |||
@@ -34,15 +35,24 @@ impl<'a> System<'a> for Fleets { | |||
let FleetsData { | |||
mut ship, | |||
mut velocity, | |||
fleet_owned, | |||
position, | |||
fleet, | |||
global, | |||
} = data; | |||
(&mut ship, &mut velocity, &position) | |||
(&mut ship, &mut velocity, &fleet_owned, &position) | |||
.par_join() | |||
.for_each(|(ship, vel, pos)| { | |||
progress_ship(&position, &fleet, ship, vel, pos, global.delta) | |||
.for_each(|(ship, vel, fleet_owned, pos)| { | |||
progress_ship( | |||
&position, | |||
&fleet, | |||
ship, | |||
vel, | |||
fleet_owned.owner, | |||
pos, | |||
global.delta, | |||
) | |||
}); | |||
} | |||
} | |||
@@ -52,11 +62,12 @@ fn progress_ship<'a>( | |||
fleets: &ReadStorage<'a, Fleet>, | |||
ship: &mut Ship, | |||
velocity: &mut Velocity, | |||
fleet_id: Entity, | |||
position: &Position, | |||
delta: f32, | |||
) { | |||
let (orbit_pos, orbit_min, orbit_max): (Vector2f, f32, f32) = | |||
match (positions.get(ship.fleet), fleets.get(ship.fleet)) { | |||
match (positions.get(fleet_id), fleets.get(fleet_id)) { | |||
(Some(position), Some(fleet)) => (position.pos, fleet.orbit_min, fleet.orbit_max), | |||
(_, _) => return, | |||
}; | |||