diff --git a/Cargo.lock b/Cargo.lock index 20e9b76..9e87709 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1537,6 +1537,7 @@ dependencies = [ "serde_yaml", "shred", "shrev", + "smallvec", "specs", "thiserror", "vfs", diff --git a/ToDo b/ToDo deleted file mode 100644 index 26e6e8b..0000000 --- a/ToDo +++ /dev/null @@ -1 +0,0 @@ -- De-/Serialize World diff --git a/space-crush-app/src/components/fleet_info.rs b/space-crush-app/src/components/fleet_info.rs deleted file mode 100644 index af67637..0000000 --- a/space-crush-app/src/components/fleet_info.rs +++ /dev/null @@ -1,12 +0,0 @@ -use space_crush_common::components::ShipCount; -use specs::{hibitset::BitSet, Component, HashMapStorage}; - -#[derive(Default, Debug)] -pub struct FleetInfo { - pub owned: BitSet, - pub count: ShipCount, -} - -impl Component for FleetInfo { - type Storage = HashMapStorage; -} diff --git a/space-crush-app/src/components/mod.rs b/space-crush-app/src/components/mod.rs deleted file mode 100644 index 6fc4956..0000000 --- a/space-crush-app/src/components/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod fleet_info; - -pub use fleet_info::FleetInfo; diff --git a/space-crush-app/src/debug/fleets.rs b/space-crush-app/src/debug/fleets.rs index 2cd1587..cdb1cdc 100644 --- a/space-crush-app/src/debug/fleets.rs +++ b/space-crush-app/src/debug/fleets.rs @@ -1,15 +1,16 @@ use glc::vector::{Angle, Vector2f, Vector4f}; use space_crush_common::{ - components::{Fleet, Position}, + components::{Fleet, Orbit, Player, Position}, constants::SHIP_ORBIT_DISTANCE_MAX, + continue_if_none, misc::LogResult, + return_if_none, }; -use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; +use specs::{prelude::*, ReadExpect, ReadStorage, System, World, WriteExpect}; use crate::{ - components::FleetInfo, misc::{HorizontalAlign, Text, TextManager, VerticalAlign}, - resources::{Camera, Geometry}, + resources::{Camera, Geometry, PlayerState}, Error, }; @@ -19,11 +20,13 @@ pub struct Fleets { #[derive(SystemData)] pub struct FleetData<'a> { + player_state: ReadExpect<'a, PlayerState>, geometry: WriteExpect<'a, Geometry>, camera: ReadExpect<'a, Camera>, positions: ReadStorage<'a, Position>, + players: ReadStorage<'a, Player>, + orbits: ReadStorage<'a, Orbit>, fleets: ReadStorage<'a, Fleet>, - fleet_infos: ReadStorage<'a, FleetInfo>, } impl Fleets { @@ -48,29 +51,37 @@ impl<'a> System<'a> for Fleets { fn run(&mut self, data: Self::SystemData) { let FleetData { + player_state, mut geometry, camera, positions, + players, + orbits, fleets, - fleet_infos, } = data; + let player = return_if_none!(players.get(player_state.player_id)); + gl::enable(gl::BLEND); - for (position, fleet, fleet_info) in (&positions, &fleets, &fleet_infos).join() { + for (position, orbit) in (&positions, &orbits).join() { + let fleet_id = continue_if_none!(orbit.fleets.get(player.index)); + let fleet_id = continue_if_none!(fleet_id); + let fleet = continue_if_none!(fleets.get(*fleet_id)); + gl::blend_func(gl::SRC_ALPHA, gl::ONE); gl::blend_equation(gl::FUNC_ADD); geometry.render_lines( Vector4f::new(0.5, 0.5, 0.5, 0.05), - &create_circle(position.pos, fleet.orbit_min), + &create_circle(position.pos, orbit.min), ); geometry.render_lines( Vector4f::new(0.5, 0.5, 0.5, 0.05), - &create_circle(position.pos, fleet.orbit_max), + &create_circle(position.pos, orbit.max), ); geometry.render_lines( Vector4f::new(0.5, 0.5, 0.5, 0.05), - &create_circle(position.pos, SHIP_ORBIT_DISTANCE_MAX * fleet.orbit_max), + &create_circle(position.pos, SHIP_ORBIT_DISTANCE_MAX * orbit.max), ); gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); @@ -80,9 +91,7 @@ impl<'a> System<'a> for Fleets { 0, format!( "F:{}\nB:{}\nT:{}", - fleet_info.count.fighter, - fleet_info.count.bomber, - fleet_info.count.transporter + fleet.count.fighter, fleet.count.bomber, fleet.count.transporter ), ) .panic("Unable to update text") diff --git a/space-crush-app/src/lib.rs b/space-crush-app/src/lib.rs index 11cbb24..330f6d4 100644 --- a/space-crush-app/src/lib.rs +++ b/space-crush-app/src/lib.rs @@ -1,4 +1,3 @@ -pub mod components; pub mod constants; pub mod debug; pub mod error; @@ -16,7 +15,7 @@ use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary} use misc::{Events, TextManager, Window}; use render::{Asteroids, FleetSelect, Init, Planets, Ships}; use resources::{Camera, Config, Geometry, InputState, PlayerState, Uniform}; -use systems::{FleetControl, FleetInfoUpdate, StateUpdate}; +use systems::{FleetControl, StateUpdate}; pub struct App<'a, 'b> { is_running: bool, @@ -50,7 +49,6 @@ impl<'a, 'b> App<'a, 'b> { let mut dispatcher = DispatcherBuilder::new() .with(StateUpdate::new(world)?, "state_update", &[]) - .with(FleetInfoUpdate::new(world), "fleet_info_update", &[]) .with(FleetControl::new(world)?, "fleet_control", &[]) .with_thread_local(Init::new(world)?) .with_thread_local(Planets::new(world)?) diff --git a/space-crush-app/src/main.rs b/space-crush-app/src/main.rs index bf474ae..12e793b 100644 --- a/space-crush-app/src/main.rs +++ b/space-crush-app/src/main.rs @@ -57,11 +57,11 @@ fn create_world(world: &mut World, player_id: Entity) { matrix::Angle, vector::{Vector2f, Vector4f}, }; - use space_crush_app::components::FleetInfo; + use smallvec::smallvec; use space_crush_common::{ components::{ - Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Planet, Player, PlayerOwned, - Position, Shape, Ship, ShipType, Velocity, + Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Orbit, OrbitOwned, Planet, Player, + PlayerOwned, Position, Shape, Ship, ShipType, Velocity, }, misc::{PersistWorld, Persistence}, }; @@ -74,6 +74,7 @@ fn create_world(world: &mut World, player_id: Entity) { .insert( player_id, Player { + index: 0, color: Vector4f::new(0.0, 0.5, 1.0, 0.1), }, ) @@ -92,11 +93,11 @@ fn create_world(world: &mut World, player_id: Entity) { pos: Vector2f::new(x, y), shape: Shape::Circle(250.0), }) - .with(Fleet { - orbit_min: 325.0, - orbit_max: 425.0, + .with(Orbit { + min: 325.0, + max: 425.0, + fleets: smallvec![], }) - .with(FleetInfo::default()) .with(Obstacle {}) .with(Planet {}) .build() @@ -117,11 +118,11 @@ fn create_world(world: &mut World, player_id: Entity) { pos: Vector2f::new(x, y), shape: Shape::Circle(100.0), }) - .with(Fleet { - orbit_min: 125.0, - orbit_max: 175.0, + .with(Orbit { + min: 125.0, + max: 175.0, + fleets: smallvec![], }) - .with(FleetInfo::default()) .with(Obstacle {}) .with(Asteroid { type_: match i_x * i_y { @@ -133,7 +134,15 @@ fn create_world(world: &mut World, player_id: Entity) { .build(); } - for i in 0..999 { + let fleet_id = world + .create_entity() + .marked::<::Marker>() + .with(PlayerOwned { owner: player_id }) + .with(OrbitOwned { owner: planets[1] }) + .with(Fleet::default()) + .build(); + + for i in 0..9 { let r = 325.0 + 100.0 * random::(); let a = Angle::Deg(360.0 * random::()); @@ -144,6 +153,7 @@ fn create_world(world: &mut World, player_id: Entity) { .create_entity() .marked::<::Marker>() .with(PlayerOwned { owner: player_id }) + .with(FleetOwned { owner: fleet_id }) .with(Position { pos: Vector2f::new(x, y), shape: Shape::Dot, @@ -152,7 +162,6 @@ fn create_world(world: &mut World, player_id: Entity) { dir: Vector2f::new(random::() - 0.5, random::() - 0.5).normalize(), speed: 100.0, }) - .with(FleetOwned { owner: planets[1] }) .with(Ship { type_: match i % 3 { 0 => ShipType::Fighter, diff --git a/space-crush-app/src/render/fleet_select.rs b/space-crush-app/src/render/fleet_select.rs index 5db1f9b..c89f5db 100644 --- a/space-crush-app/src/render/fleet_select.rs +++ b/space-crush-app/src/render/fleet_select.rs @@ -10,16 +10,16 @@ use glc::{ }; use shrev::{EventChannel, ReaderId}; use space_crush_common::{ - components::{Fleet, Position, ShipCount}, + components::{Fleet, Orbit, OrbitOwned, Player, Position, ShipCount}, constants::VECTOR_2F_POS_X, + continue_if_none, misc::{LogResult, WorldHelper as _}, resources::Global, return_if_none, }; -use specs::{prelude::*, Entities, ReadExpect, ReadStorage, System, World}; +use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; use crate::{ - components::FleetInfo, constants::{ FLEET_SELECT_ANIMATION_TIME, FLEET_SELECT_DETAIL_TIMEOUT, FLEET_SELECT_OFFSET, FLEET_SELECT_TEXT_OFFSET, FLEET_SELECT_TEXT_SIZE, FLEET_SELECT_WIDTH, @@ -74,15 +74,16 @@ pub struct FleetSelectData<'a> { player_state: WriteExpect<'a, PlayerState>, camera: ReadExpect<'a, Camera>, - entities: Entities<'a>, mouse_events: ReadExpect<'a, EventChannel>, input_state: ReadExpect<'a, InputState>, geometry: ReadExpect<'a, Geometry>, global: ReadExpect<'a, Global>, config: ReadExpect<'a, Config>, - fleet_infos: ReadStorage<'a, FleetInfo>, + orbit_owned: ReadStorage<'a, OrbitOwned>, positions: ReadStorage<'a, Position>, + players: ReadStorage<'a, Player>, + orbits: ReadStorage<'a, Orbit>, fleets: ReadStorage<'a, Fleet>, } @@ -95,9 +96,15 @@ macro_rules! selection { }; } -macro_rules! fleet_info { +macro_rules! player { (&$data:expr, $id:expr) => { - return_if_none!($data.fleet_infos.get($id)) + return_if_none!($data.players.get($id)) + }; +} + +macro_rules! fleet { + (&$data:expr, $id:expr) => { + return_if_none!($data.fleets.get($id)) }; } @@ -107,6 +114,12 @@ macro_rules! position { }; } +macro_rules! orbit_owned { + (&$data:expr, $id:expr) => { + return_if_none!($data.orbit_owned.get($id)) + }; +} + impl FleetSelect { pub fn new(world: &World, text_manager: &TextManager) -> Result { let program_simple = world.load_program(vec![ @@ -180,11 +193,17 @@ impl FleetSelect { MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => { let pos = d.camera.view_to_world(d.input_state.mouse_pos); let selection = d.player_state.selection.take(); - for (id, position, fleet) in (&d.entities, &d.positions, &d.fleets).join() { - let r = fleet.orbit_max * fleet.orbit_max; + for (position, orbit) in (&d.positions, &d.orbits).join() { + let r = orbit.max * orbit.max; if (position.pos - pos).length_sqr() <= r { + let player_id = d.player_state.player_id; + let player = player!(&d, player_id); + let player_index = player.index; + let fleet_id = continue_if_none!(orbit.fleets.get(player_index)); + let fleet_id = *continue_if_none!(fleet_id); + d.player_state.selection = match selection { - Some(s) if s.fleet == id => { + Some(s) if s.fleet == fleet_id => { self.is_new_selection = false; Some(s) @@ -193,20 +212,20 @@ impl FleetSelect { self.is_new_selection = true; Some(Selection { - fleet: id, + fleet: fleet_id, count: ShipCount::none(), }) } }; let selection = selection!(&d); - let fleet_info = fleet_info!(&d, selection.fleet); + let fleet = fleet!(&d, selection.fleet); let timeout = Instant::now() + FLEET_SELECT_DETAIL_TIMEOUT; self.mouse_pos = d.input_state.mouse_pos; self.select_mode = SelectMode::Init(timeout); self.values_changed_once = false; - self.set_count(selection.count, &fleet_info.count); + self.set_count(selection.count, &fleet.count); break; } @@ -262,8 +281,9 @@ impl FleetSelect { /* calculate values */ let selection = selection!(&d); - let position = position!(&d, selection.fleet); - let fleet_info = fleet_info!(&d, selection.fleet); + let orbit_owned = orbit_owned!(&d, selection.fleet); + let position = position!(&d, orbit_owned.owner); + let fleet = fleet!(&d, selection.fleet); self.marker = d.camera.view_to_world(self.mouse_pos) - position.pos; self.zoom = d.camera.view().axis_x.as_vec3().length(); @@ -294,10 +314,10 @@ impl FleetSelect { x @ 0..=2 => { let mut count = selection.count; count[x] = usize::MAX; - self.set_count(count, &fleet_info.count); + self.set_count(count, &fleet.count); self.values[x] = 1.0; - self.update_values(&fleet_info.count); + self.update_values(&fleet.count); } _ => { self.count = ShipCount::all(); @@ -312,14 +332,14 @@ impl FleetSelect { match sector { x @ 0..=2 => { let mut count = selection.count; - count[x] = (fleet_info.count[x] as f32 * value).round() as usize; - self.set_count(count, &fleet_info.count); + count[x] = (fleet.count[x] as f32 * value).round() as usize; + self.set_count(count, &fleet.count); self.values[x] = value; - self.update_values(&fleet_info.count); + self.update_values(&fleet.count); } _ => { - self.count = fleet_info.count * value; + self.count = fleet.count * value; self.values = value.into(); } } @@ -328,10 +348,10 @@ impl FleetSelect { x @ 0..=2 => { let mut count = selection.count; count[x] = 0; - self.set_count(count, &fleet_info.count); + self.set_count(count, &fleet.count); self.values[x] = 0.0; - self.update_values(&fleet_info.count); + self.update_values(&fleet.count); } _ => { self.count = Default::default(); @@ -341,7 +361,7 @@ impl FleetSelect { } /* update texts */ - let c = self.count.merge(&fleet_info.count); + let c = self.count.merge(&fleet.count); let _guard = self.cache.begin_update(); self.text_total .update(0, c.total().to_string()) @@ -415,7 +435,8 @@ impl FleetSelect { /* extract system data */ let selection = selection!(&d); - let position = position!(&d, selection.fleet); + let orbit_owned = orbit_owned!(&d, selection.fleet); + let position = position!(&d, orbit_owned.owner); /* calculate shared values */ let size = self.ring1 + 50.0; diff --git a/space-crush-app/src/systems/fleet_control.rs b/space-crush-app/src/systems/fleet_control.rs index e6673ab..50ff61e 100644 --- a/space-crush-app/src/systems/fleet_control.rs +++ b/space-crush-app/src/systems/fleet_control.rs @@ -1,13 +1,12 @@ use shrev::{EventChannel, ReaderId}; use space_crush_common::{ - components::{Fleet, FleetOwned, Position, Ship, ShipType}, + components::{Fleet, FleetOwned, Orbit, Player, Position, Ship, ShipType}, + continue_if_none, misc::WorldHelper, - return_if_none, }; -use specs::{prelude::*, Entities, Entity, ReadStorage, System, World, WriteStorage}; +use specs::{prelude::*, Entity, ReadStorage, System, World, WriteStorage}; use crate::{ - components::FleetInfo, misc::MouseEvent, resources::{Camera, Config, InputState, PlayerState}, Error, @@ -26,11 +25,11 @@ pub struct FleetControlData<'a> { camera: ReadExpect<'a, Camera>, config: ReadExpect<'a, Config>, - entities: Entities<'a>, ships: ReadStorage<'a, Ship>, fleet_owned: WriteStorage<'a, FleetOwned>, positions: ReadStorage<'a, Position>, - fleet_infos: ReadStorage<'a, FleetInfo>, + players: ReadStorage<'a, Player>, + orbits: ReadStorage<'a, Orbit>, fleets: ReadStorage<'a, Fleet>, } @@ -56,11 +55,11 @@ impl<'a> System<'a> for FleetControl { input_state, camera, config, - entities, ships, mut fleet_owned, positions, - fleet_infos, + players, + orbits, fleets, } = data; @@ -69,10 +68,15 @@ impl<'a> System<'a> for FleetControl { match event { MouseEvent::ButtonDown(button) if button == &config.input.fleet_move_button => { let pos = camera.view_to_world(input_state.mouse_pos); - for (id, position, fleet) in (&entities, &positions, &fleets).join() { - let r = fleet.orbit_max * fleet.orbit_max; + for (position, orbit) in (&positions, &orbits).join() { + let r = orbit.max * orbit.max; if (position.pos - pos).length_sqr() <= r { - self.target_fleet = Some(id); + let player_id = player_state.player_id; + let player = continue_if_none!(players.get(player_id)); + let fleet_id = continue_if_none!(orbit.fleets.get(player.index)); + let fleet_id = *continue_if_none!(fleet_id); + + self.target_fleet = Some(fleet_id); break; } @@ -80,12 +84,10 @@ impl<'a> System<'a> for FleetControl { } MouseEvent::ButtonUp(button) if button == &config.input.fleet_move_button => { let selection = player_state.selection.take(); - let target_fleet = return_if_none!(self.target_fleet.take()); - let mut selection = return_if_none!(selection); - let fleet_info = return_if_none!(fleet_infos.get(selection.fleet)); - for (ship, fleet_owned, _) in - (&ships, &mut fleet_owned, &fleet_info.owned).join() - { + let target_fleet = continue_if_none!(self.target_fleet.take()); + let mut selection = continue_if_none!(selection); + let fleet = continue_if_none!(fleets.get(selection.fleet)); + for (ship, fleet_owned, _) in (&ships, &mut fleet_owned, &fleet.owned).join() { match &ship.type_ { ShipType::Fighter if selection.count.fighter > 0 => { fleet_owned.owner = target_fleet; diff --git a/space-crush-app/src/systems/fleet_info_update.rs b/space-crush-app/src/systems/fleet_info_update.rs deleted file mode 100644 index c8dae35..0000000 --- a/space-crush-app/src/systems/fleet_info_update.rs +++ /dev/null @@ -1,167 +0,0 @@ -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}; - -pub struct FleetInfoUpdate { - modified: HashMap, - need_update: BitSet, - fleet_owned_id: ReaderId>, - player_owned_id: ReaderId>, -} - -#[derive(Default)] -struct Modified { - old_fleet_id: Option, - old_player_id: Option, -} - -impl FleetInfoUpdate { - pub fn new(world: &mut World) -> Self { - let modified = HashMap::new(); - let need_update = BitSet::new(); - - let fleet_owned_id = unsafe { - WriteStorage::::setup(world); - - let mut fleet_owned = world.system_data::>(); - fleet_owned - .unprotected_storage_mut() - .channel_mut() - .register_reader() - }; - - let player_owned_id = unsafe { - WriteStorage::::setup(world); - - let mut player_owned = world.system_data::>(); - player_owned - .unprotected_storage_mut() - .channel_mut() - .register_reader() - }; - - 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) => unreachable!(), - }, - }; - - if old_match && !new_match { - fleet_info.owned.remove(ship_id.id()); - fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); - } else if !old_match && new_match { - fleet_info.owned.add(ship_id.id()); - fleet_info.count[ship.type_] += 1; - } - } - } - } -} diff --git a/space-crush-app/src/systems/mod.rs b/space-crush-app/src/systems/mod.rs index 5b989d2..590c3e3 100644 --- a/space-crush-app/src/systems/mod.rs +++ b/space-crush-app/src/systems/mod.rs @@ -1,7 +1,5 @@ mod fleet_control; -mod fleet_info_update; mod state_update; pub use fleet_control::FleetControl; -pub use fleet_info_update::FleetInfoUpdate; pub use state_update::StateUpdate; diff --git a/space-crush-common/Cargo.toml b/space-crush-common/Cargo.toml index ad9f23d..ab3f30a 100644 --- a/space-crush-common/Cargo.toml +++ b/space-crush-common/Cargo.toml @@ -14,6 +14,7 @@ serde = "1.0" serde_yaml = "0.8" shred = { version = "0.10", features = [ "shred-derive" ] } shrev = "1.1" +smallvec = { version = "1.5", features = [ "serde" ] } specs = { version = "*", features = [ "serde" ] } thiserror = "1.0" vfs = "0.4" diff --git a/space-crush-common/src/components/fleet.rs b/space-crush-common/src/components/fleet.rs index cf79514..4b11b3d 100644 --- a/space-crush-common/src/components/fleet.rs +++ b/space-crush-common/src/components/fleet.rs @@ -1,16 +1,17 @@ use serde::{Deserialize, Serialize}; use specs::{ error::NoError, + hibitset::BitSet, saveload::{ConvertSaveload, Marker}, Component, Entity, HashMapStorage, VecStorage, }; -use crate::misc::FlaggedStorage; +use crate::{components::ShipCount, misc::FlaggedStorage}; -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Default, Debug, Clone)] pub struct Fleet { - pub orbit_min: f32, - pub orbit_max: f32, + pub owned: BitSet, + pub count: ShipCount, } #[derive(Copy, Clone, Debug)] diff --git a/space-crush-common/src/components/mod.rs b/space-crush-common/src/components/mod.rs index 436886b..0bc9ce3 100644 --- a/space-crush-common/src/components/mod.rs +++ b/space-crush-common/src/components/mod.rs @@ -1,6 +1,7 @@ mod asteroid; mod fleet; mod obstacle; +mod orbit; mod planet; mod player; mod position; @@ -10,6 +11,7 @@ mod velocity; pub use asteroid::{Asteroid, Type as AsteroidType}; pub use fleet::{Fleet, Owned as FleetOwned}; pub use obstacle::Obstacle; +pub use orbit::{Orbit, Owned as OrbitOwned}; pub use planet::Planet; pub use player::{Owned as PlayerOwned, Player}; pub use position::{Position, Shape}; diff --git a/space-crush-common/src/components/orbit.rs b/space-crush-common/src/components/orbit.rs new file mode 100644 index 0000000..f909c78 --- /dev/null +++ b/space-crush-common/src/components/orbit.rs @@ -0,0 +1,102 @@ +use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; +use specs::{ + error::NoError, + saveload::{ConvertSaveload, Marker}, + Component, Entity, HashMapStorage, +}; + +use crate::misc::FlaggedStorage; + +#[derive(Clone, Debug, Default)] +pub struct Orbit { + pub min: f32, + pub max: f32, + pub fleets: SmallVec<[Option; 8]>, +} + +#[derive(Serialize, Deserialize)] +pub struct OrbitData { + pub min: f32, + pub max: f32, + pub fleets: Vec>, +} + +#[derive(Copy, Clone, Debug)] +pub struct Owned { + pub owner: Entity, +} + +#[derive(Serialize, Deserialize)] +pub struct OwnedData { + pub owner: M, +} + +impl Component for Orbit { + type Storage = HashMapStorage; +} + +impl ConvertSaveload for Orbit +where + for<'de> M: Marker + Serialize + Deserialize<'de>, +{ + type Data = OrbitData; + type Error = NoError; + + fn convert_into(&self, mut ids: F) -> Result + where + F: FnMut(Entity) -> Option, + { + let Orbit { min, max, fleets } = self; + + let min = *min; + let max = *max; + let fleets = fleets + .iter() + .cloned() + .map(|e| e.and_then(|e| ids(e))) + .collect(); + + Ok(OrbitData { min, max, fleets }) + } + + fn convert_from(data: Self::Data, mut ids: F) -> Result + where + F: FnMut(M) -> Option, + { + let OrbitData { min, max, fleets } = data; + let fleets = fleets.into_iter().map(|e| e.and_then(|e| ids(e))).collect(); + + Ok(Orbit { min, max, fleets }) + } +} + +impl Component for Owned { + type Storage = FlaggedStorage>; +} + +impl ConvertSaveload for Owned +where + for<'de> M: Marker + Serialize + Deserialize<'de>, +{ + type Data = OwnedData; + type Error = NoError; + + fn convert_into(&self, mut ids: F) -> Result + where + F: FnMut(Entity) -> Option, + { + let owner = ids(self.owner).unwrap(); + + Ok(OwnedData { owner }) + } + + fn convert_from(data: Self::Data, mut ids: F) -> Result + where + F: FnMut(M) -> Option, + { + let owner = ids(data.owner).unwrap(); + + Ok(Owned { owner }) + } +} diff --git a/space-crush-common/src/components/player.rs b/space-crush-common/src/components/player.rs index 949d449..a9a6d19 100644 --- a/space-crush-common/src/components/player.rs +++ b/space-crush-common/src/components/player.rs @@ -3,13 +3,12 @@ use serde::{Deserialize, Serialize}; use specs::{ error::NoError, saveload::{ConvertSaveload, Marker}, - Component, Entity, HashMapStorage, VecStorage, + Component, Entity, HashMapStorage, }; -use crate::misc::FlaggedStorage; - #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct Player { + pub index: usize, pub color: Vector4f, } @@ -28,7 +27,7 @@ impl Component for Player { } impl Component for Owned { - type Storage = FlaggedStorage>; + type Storage = HashMapStorage; } impl ConvertSaveload for Owned diff --git a/space-crush-common/src/dispatcher.rs b/space-crush-common/src/dispatcher.rs index d504d04..f1b5f7f 100644 --- a/space-crush-common/src/dispatcher.rs +++ b/space-crush-common/src/dispatcher.rs @@ -3,7 +3,7 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; use crate::{ components::Player, resources::Global, - systems::{Movement, Process, Ships}, + systems::{FleetOwnedUpdate, Movement, OrbitOwnedUpdate, Process, Ships}, }; pub struct Dispatcher<'a, 'b> { @@ -20,6 +20,8 @@ impl<'a, 'b> Dispatcher<'a, 'b> { .with(Process::default(), "process", &[]) .with(Movement::default(), "movement", &[]) .with(Ships::new(world), "ships", &[]) + .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) + .with(OrbitOwnedUpdate::new(world), "orbit_owned_update", &[]) .build(); dispatcher.setup(world); diff --git a/space-crush-common/src/misc/flagged_storage.rs b/space-crush-common/src/misc/flagged_storage.rs index 05e70c4..8593f86 100644 --- a/space-crush-common/src/misc/flagged_storage.rs +++ b/space-crush-common/src/misc/flagged_storage.rs @@ -20,7 +20,7 @@ pub enum ComponentEvent where C: Send + Sync + 'static, { - Inserted(Index), + Inserted(Index, C), Modified(Index, C), Removed(Index, C), } @@ -67,26 +67,27 @@ where } unsafe fn get_mut(&mut self, id: Index) -> &mut C { - let ret = self.storage.get_mut(id); + let component = self.storage.get_mut(id); self.channel - .single_write(ComponentEvent::Modified(id, ret.clone())); + .single_write(ComponentEvent::Modified(id, component.clone())); - ret + component } - unsafe fn insert(&mut self, id: Index, comp: C) { - self.storage.insert(id, comp); + unsafe fn insert(&mut self, id: Index, component: C) { + self.storage.insert(id, component.clone()); - self.channel.single_write(ComponentEvent::Inserted(id)); + self.channel + .single_write(ComponentEvent::Inserted(id, component)); } unsafe fn remove(&mut self, id: Index) -> C { - let c = self.storage.remove(id); + let component = self.storage.remove(id); self.channel - .single_write(ComponentEvent::Removed(id, c.clone())); + .single_write(ComponentEvent::Removed(id, component.clone())); - c + component } } diff --git a/space-crush-common/src/misc/persistence.rs b/space-crush-common/src/misc/persistence.rs index d88e013..345f431 100644 --- a/space-crush-common/src/misc/persistence.rs +++ b/space-crush-common/src/misc/persistence.rs @@ -5,7 +5,10 @@ use specs::{ Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, }; -use crate::components::{Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, Ship, Velocity}; +use crate::components::{ + Asteroid, FleetOwned, Obstacle, Orbit, OrbitOwned, Planet, Player, PlayerOwned, Position, Ship, + Velocity, +}; /* PersistWorld */ @@ -17,12 +20,15 @@ impl Persistence for PersistWorld { type Components = ( Position, Velocity, - Planet, - Ship, Player, PlayerOwned, - Fleet, + Orbit, + OrbitOwned, FleetOwned, + Ship, + Obstacle, + Planet, + Asteroid, ); } diff --git a/space-crush-common/src/systems/fleet_owned_update.rs b/space-crush-common/src/systems/fleet_owned_update.rs new file mode 100644 index 0000000..4cf24bf --- /dev/null +++ b/space-crush-common/src/systems/fleet_owned_update.rs @@ -0,0 +1,111 @@ +use std::collections::HashMap; + +use shrev::ReaderId; +use specs::{prelude::*, world::Index, Entities, ReadStorage, System, World, WriteStorage}; + +use crate::{ + components::{Fleet, FleetOwned, Ship}, + misc::ComponentEvent, +}; + +pub struct FleetOwnedUpdate { + fleet_ids: BitSet, + fleet_owned_ids: BitSet, + old_fleet_ids: HashMap, + fleet_owned_event_id: ReaderId>, +} + +impl FleetOwnedUpdate { + pub fn new(world: &mut World) -> Self { + let fleet_ids = BitSet::new(); + let fleet_owned_ids = BitSet::new(); + let old_fleet_ids = HashMap::new(); + + let fleet_owned_event_id = unsafe { + WriteStorage::::setup(world); + + let mut fleet_owned = world.system_data::>(); + fleet_owned + .unprotected_storage_mut() + .channel_mut() + .register_reader() + }; + + Self { + fleet_ids, + fleet_owned_ids, + old_fleet_ids, + fleet_owned_event_id, + } + } +} + +#[derive(SystemData)] +pub struct FleetOwnedUpdateData<'a> { + entities: Entities<'a>, + ships: ReadStorage<'a, Ship>, + fleets: WriteStorage<'a, Fleet>, + fleet_owned: ReadStorage<'a, FleetOwned>, +} + +impl<'a> System<'a> for FleetOwnedUpdate { + type SystemData = FleetOwnedUpdateData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let FleetOwnedUpdateData { + entities, + ships, + mut fleets, + fleet_owned, + } = data; + + self.fleet_ids.clear(); + self.fleet_owned_ids.clear(); + self.old_fleet_ids.clear(); + + /* handle events */ + let events = fleet_owned + .unprotected_storage() + .channel() + .read(&mut self.fleet_owned_event_id); + for event in events { + match event { + ComponentEvent::Inserted(id, fleet_owned) => { + self.fleet_ids.add(fleet_owned.owner.id()); + self.fleet_owned_ids.add(*id); + } + ComponentEvent::Modified(id, fleet_owned) => { + self.fleet_ids.add(fleet_owned.owner.id()); + self.fleet_owned_ids.add(*id); + *self.old_fleet_ids.entry(*id).or_insert(fleet_owned.owner) = fleet_owned.owner; + } + ComponentEvent::Removed(id, fleet_owned) => { + self.fleet_ids.add(fleet_owned.owner.id()); + self.fleet_owned_ids.add(*id); + *self.old_fleet_ids.entry(*id).or_insert(fleet_owned.owner) = fleet_owned.owner; + } + } + } + + /* update fleets */ + for (fleet_id, fleet_info, _) in (&entities, &mut fleets, &self.fleet_ids).join() { + let data = (&entities, &ships, &fleet_owned, &self.fleet_owned_ids); + + for (ship_id, ship, fleet_owned, _) in data.join() { + let new_match = fleet_id == fleet_owned.owner; + let old_match = match self.old_fleet_ids.get(&ship_id.id()) { + Some(old_fleet_id) => fleet_id == *old_fleet_id, + None => false, + }; + + if old_match && !new_match { + fleet_info.owned.remove(ship_id.id()); + fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); + } else if !old_match && new_match { + fleet_info.owned.add(ship_id.id()); + fleet_info.count[ship.type_] += 1; + } + } + } + } +} diff --git a/space-crush-common/src/systems/mod.rs b/space-crush-common/src/systems/mod.rs index b17a21a..c6d3073 100644 --- a/space-crush-common/src/systems/mod.rs +++ b/space-crush-common/src/systems/mod.rs @@ -1,7 +1,11 @@ +mod fleet_owned_update; mod movement; +mod orbit_owned_update; mod process; mod ships; +pub use fleet_owned_update::FleetOwnedUpdate; pub use movement::Movement; +pub use orbit_owned_update::OrbitOwnedUpdate; pub use process::Process; pub use ships::Ships; diff --git a/space-crush-common/src/systems/orbit_owned_update.rs b/space-crush-common/src/systems/orbit_owned_update.rs new file mode 100644 index 0000000..ed8ce6a --- /dev/null +++ b/space-crush-common/src/systems/orbit_owned_update.rs @@ -0,0 +1,121 @@ +use std::collections::HashMap; + +use shrev::ReaderId; +use specs::{ + hibitset::BitSet, prelude::*, world::Index, Entities, Entity, ReadStorage, System, World, + WriteStorage, +}; + +use crate::{ + components::{Orbit, OrbitOwned, PlayerOwned}, + misc::ComponentEvent, +}; + +pub struct OrbitOwnedUpdate { + orbit_ids: BitSet, + orbit_owned_ids: BitSet, + old_orbit_ids: HashMap, + orbit_owned_event_id: ReaderId>, +} + +impl OrbitOwnedUpdate { + pub fn new(world: &mut World) -> Self { + let orbit_ids = BitSet::new(); + let orbit_owned_ids = BitSet::new(); + let old_orbit_ids = HashMap::new(); + + let orbit_owned_event_id = unsafe { + WriteStorage::::setup(world); + + let mut orbit_owned = world.system_data::>(); + orbit_owned + .unprotected_storage_mut() + .channel_mut() + .register_reader() + }; + + Self { + orbit_ids, + orbit_owned_ids, + old_orbit_ids, + orbit_owned_event_id, + } + } +} + +#[derive(SystemData)] +pub struct OrbitOwnedUpdateData<'a> { + entities: Entities<'a>, + orbits: WriteStorage<'a, Orbit>, + player_owned: ReadStorage<'a, PlayerOwned>, + orbit_owned: ReadStorage<'a, OrbitOwned>, +} + +impl<'a> System<'a> for OrbitOwnedUpdate { + type SystemData = OrbitOwnedUpdateData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let OrbitOwnedUpdateData { + entities, + mut orbits, + player_owned, + orbit_owned, + } = data; + + self.orbit_ids.clear(); + self.orbit_owned_ids.clear(); + self.old_orbit_ids.clear(); + + /* handle events */ + let events = orbit_owned + .unprotected_storage() + .channel() + .read(&mut self.orbit_owned_event_id); + for event in events { + match event { + ComponentEvent::Inserted(id, orbit_owned) => { + self.orbit_ids.add(orbit_owned.owner.id()); + self.orbit_owned_ids.add(*id); + } + ComponentEvent::Modified(id, orbit_owned) => { + self.orbit_ids.add(orbit_owned.owner.id()); + self.orbit_owned_ids.add(*id); + *self.old_orbit_ids.entry(*id).or_insert(orbit_owned.owner) = orbit_owned.owner; + } + ComponentEvent::Removed(id, orbit_owned) => { + self.orbit_ids.add(orbit_owned.owner.id()); + self.orbit_owned_ids.add(*id); + *self.old_orbit_ids.entry(*id).or_insert(orbit_owned.owner) = orbit_owned.owner; + } + } + } + + /* update orbits */ + for (orbit_id, orbit, _) in (&entities, &mut orbits, &self.orbit_ids).join() { + let data = ( + &entities, + &orbit_owned, + &player_owned, + &self.orbit_owned_ids, + ); + + for (fleet_id, orbit_owned, player_owned, _) in data.join() { + let new_match = orbit_id == orbit_owned.owner; + let old_match = match self.old_orbit_ids.get(&fleet_id.id()) { + Some(old_orbit_id) => orbit_id == *old_orbit_id, + None => false, + }; + + let player_id = player_owned.owner.id() as usize; + if old_match && !new_match { + if let Some(fleet) = orbit.fleets.get_mut(player_id) { + *fleet = None; + } + } else if !old_match && new_match { + orbit.fleets.resize_with(player_id + 1, Default::default); + orbit.fleets[player_id] = Some(fleet_id); + } + } + } + } +} diff --git a/space-crush-common/src/systems/process.rs b/space-crush-common/src/systems/process.rs index f813f9a..62f2e83 100644 --- a/space-crush-common/src/systems/process.rs +++ b/space-crush-common/src/systems/process.rs @@ -34,6 +34,10 @@ impl<'a> System<'a> for Process { self.time += global.delta; self.count += 1; + if global.delta > 1.0 { + global.delta = 1.0; + } + if self.time >= 2.0 { self.time = 0.0; self.count = 0; diff --git a/space-crush-common/src/systems/ships.rs b/space-crush-common/src/systems/ships.rs index b3b7f86..555f240 100644 --- a/space-crush-common/src/systems/ships.rs +++ b/space-crush-common/src/systems/ships.rs @@ -11,7 +11,7 @@ use specs::{ }; use crate::{ - components::{Fleet, FleetOwned, Obstacle, Position, Ship, ShipObstacle, Velocity}, + components::{FleetOwned, Obstacle, Orbit, OrbitOwned, Position, Ship, ShipObstacle, Velocity}, constants::{ SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, @@ -33,9 +33,10 @@ pub struct ShipsData<'a> { ships: WriteStorage<'a, Ship>, velocities: WriteStorage<'a, Velocity>, fleet_owned: ReadStorage<'a, FleetOwned>, + orbit_owned: ReadStorage<'a, OrbitOwned>, positions: ReadStorage<'a, Position>, obstacles: ReadStorage<'a, Obstacle>, - fleets: ReadStorage<'a, Fleet>, + orbits: ReadStorage<'a, Orbit>, } struct Processor<'a> { @@ -43,7 +44,8 @@ struct Processor<'a> { entities: &'a Entities<'a>, positions: &'a ReadStorage<'a, Position>, obstacles: &'a ReadStorage<'a, Obstacle>, - fleets: &'a ReadStorage<'a, Fleet>, + orbits: &'a ReadStorage<'a, Orbit>, + orbit_owned: &'a ReadStorage<'a, OrbitOwned>, delta: f32, } @@ -74,7 +76,7 @@ impl Ships { .read(&mut self.fleet_owned_id); for event in events { let id = match event { - ComponentEvent::Inserted(id) => id, + ComponentEvent::Inserted(id, _) => id, ComponentEvent::Modified(id, _) => id, ComponentEvent::Removed(id, _) => id, }; @@ -94,9 +96,10 @@ impl<'a> System<'a> for Ships { mut ships, mut velocities, fleet_owned, + orbit_owned, positions, obstacles, - fleets, + orbits, } = data; self.progress_events(&fleet_owned); @@ -107,7 +110,8 @@ impl<'a> System<'a> for Ships { entities: &entities, positions: &positions, obstacles: &obstacles, - fleets: &fleets, + orbit_owned: &orbit_owned, + orbits: &orbits, delta: global.delta * global.world_speed, }; @@ -136,8 +140,10 @@ impl Processor<'_> { fleet_owned: &FleetOwned, ) { let fleet_id = fleet_owned.owner; - let fleet = return_if_none!(self.fleets.get(fleet_id)); - let orbit_pos = return_if_none!(self.positions.get(fleet_id)).pos; + let orbit_owned = return_if_none!(self.orbit_owned.get(fleet_id)); + let orbit_id = orbit_owned.owner; + let orbit = return_if_none!(self.orbits.get(orbit_id)); + let orbit_pos = return_if_none!(self.positions.get(orbit_id)).pos; let ship_pos = position.pos; let target_pos = ship.target_pos; let target_dir = ship.target_dir; @@ -149,11 +155,8 @@ impl Processor<'_> { let r_ship = orbit_to_ship.length_sqr(); let r_target = orbit_to_target.length_sqr(); - let orbit_min = fleet.orbit_min; - let orbit_max = fleet.orbit_max; - - let target_in_orbit = (r_target <= sqr(fleet.orbit_max)) && (r_target >= sqr(orbit_min)); - let ship_in_orbit = r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * orbit_max); + let target_in_orbit = (r_target <= sqr(orbit.max)) && (r_target >= sqr(orbit.min)); + let ship_in_orbit = r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * orbit.max); let need_update = self.need_update.contains(id); let has_target = target_dir.length_sqr() != 0.0; @@ -161,7 +164,7 @@ impl Processor<'_> { /* check and update target posistion */ if need_update || !has_target || passed_target || ship_in_orbit != target_in_orbit { - let target_pos = if ship_in_orbit && fleet.orbit_max > 0.0 { + let target_pos = if ship_in_orbit && orbit.max > 0.0 { let orbit_to_ship_vec3 = Vector3f::new(orbit_to_ship.x, orbit_to_ship.y, 0.0); let ship_dir_vec3 = Vector3f::new(velocity.dir.x, velocity.dir.y, 0.0); @@ -173,8 +176,8 @@ impl Processor<'_> { let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random(); let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X); - let angle = angle + add * dir / orbit_max; - let radius = orbit_min + (orbit_max - orbit_min) * random::(); + let angle = angle + add * dir / orbit.max; + let radius = orbit.min + (orbit.max - orbit.min) * random::(); Vector2f::new( orbit_pos.x + radius * angle.cos(), @@ -197,7 +200,7 @@ impl Processor<'_> { ship.obstacle = ShipObstacle::Done; } else if let ShipObstacle::Known(obstacle) = ship.obstacle { if let Some(position) = self.positions.get(obstacle) { - let obstacle_fleet = self.fleets.get(obstacle).unwrap(); + let obstacle_orbit = self.orbits.get(obstacle).unwrap(); let obstacle_pos = position.pos; let ship_to_obstacle = obstacle_pos - ship_pos; @@ -207,7 +210,7 @@ impl Processor<'_> { .into_inner() .abs(); - let orbit_sqr = obstacle_fleet.orbit_max * obstacle_fleet.orbit_max; + let orbit_sqr = obstacle_orbit.max * obstacle_orbit.max; if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > orbit_sqr) || obstacle_angle > 170.0 { @@ -247,13 +250,13 @@ 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_fleet = self.fleets.get(obstacle).unwrap(); + let obstacle_orbit = self.orbits.get(obstacle).unwrap(); let ship_to_obstacle = obstacle_pos.pos - ship_pos; let orbit = ship_to_obstacle.length(); - if orbit < obstacle_fleet.orbit_max { - let orbit_min = obstacle_fleet.orbit_min; - let orbit_max = obstacle_fleet.orbit_max; + if orbit < obstacle_orbit.max { + let orbit_min = obstacle_orbit.min; + let orbit_max = obstacle_orbit.max; let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x); let radius = obstacle_pos.shape.radius();