Hint: The 'FleetControl' system is broken now, but will be refactored in the future anyway.master
| @@ -1537,6 +1537,7 @@ dependencies = [ | |||||
| "serde_yaml", | "serde_yaml", | ||||
| "shred", | "shred", | ||||
| "shrev", | "shrev", | ||||
| "smallvec", | |||||
| "specs", | "specs", | ||||
| "thiserror", | "thiserror", | ||||
| "vfs", | "vfs", | ||||
| @@ -1 +0,0 @@ | |||||
| - De-/Serialize World | |||||
| @@ -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<Self>; | |||||
| } | |||||
| @@ -1,3 +0,0 @@ | |||||
| mod fleet_info; | |||||
| pub use fleet_info::FleetInfo; | |||||
| @@ -1,15 +1,16 @@ | |||||
| use glc::vector::{Angle, Vector2f, Vector4f}; | use glc::vector::{Angle, Vector2f, Vector4f}; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{Fleet, Position}, | |||||
| components::{Fleet, Orbit, Player, Position}, | |||||
| constants::SHIP_ORBIT_DISTANCE_MAX, | constants::SHIP_ORBIT_DISTANCE_MAX, | ||||
| continue_if_none, | |||||
| misc::LogResult, | misc::LogResult, | ||||
| return_if_none, | |||||
| }; | }; | ||||
| use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World, WriteExpect}; | |||||
| use crate::{ | use crate::{ | ||||
| components::FleetInfo, | |||||
| misc::{HorizontalAlign, Text, TextManager, VerticalAlign}, | misc::{HorizontalAlign, Text, TextManager, VerticalAlign}, | ||||
| resources::{Camera, Geometry}, | |||||
| resources::{Camera, Geometry, PlayerState}, | |||||
| Error, | Error, | ||||
| }; | }; | ||||
| @@ -19,11 +20,13 @@ pub struct Fleets { | |||||
| #[derive(SystemData)] | #[derive(SystemData)] | ||||
| pub struct FleetData<'a> { | pub struct FleetData<'a> { | ||||
| player_state: ReadExpect<'a, PlayerState>, | |||||
| geometry: WriteExpect<'a, Geometry>, | geometry: WriteExpect<'a, Geometry>, | ||||
| camera: ReadExpect<'a, Camera>, | camera: ReadExpect<'a, Camera>, | ||||
| positions: ReadStorage<'a, Position>, | positions: ReadStorage<'a, Position>, | ||||
| players: ReadStorage<'a, Player>, | |||||
| orbits: ReadStorage<'a, Orbit>, | |||||
| fleets: ReadStorage<'a, Fleet>, | fleets: ReadStorage<'a, Fleet>, | ||||
| fleet_infos: ReadStorage<'a, FleetInfo>, | |||||
| } | } | ||||
| impl Fleets { | impl Fleets { | ||||
| @@ -48,29 +51,37 @@ impl<'a> System<'a> for Fleets { | |||||
| fn run(&mut self, data: Self::SystemData) { | fn run(&mut self, data: Self::SystemData) { | ||||
| let FleetData { | let FleetData { | ||||
| player_state, | |||||
| mut geometry, | mut geometry, | ||||
| camera, | camera, | ||||
| positions, | positions, | ||||
| players, | |||||
| orbits, | |||||
| fleets, | fleets, | ||||
| fleet_infos, | |||||
| } = data; | } = data; | ||||
| let player = return_if_none!(players.get(player_state.player_id)); | |||||
| gl::enable(gl::BLEND); | 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_func(gl::SRC_ALPHA, gl::ONE); | ||||
| gl::blend_equation(gl::FUNC_ADD); | gl::blend_equation(gl::FUNC_ADD); | ||||
| geometry.render_lines( | geometry.render_lines( | ||||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | 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( | geometry.render_lines( | ||||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | 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( | geometry.render_lines( | ||||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | 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); | gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); | ||||
| @@ -80,9 +91,7 @@ impl<'a> System<'a> for Fleets { | |||||
| 0, | 0, | ||||
| format!( | format!( | ||||
| "F:{}\nB:{}\nT:{}", | "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") | .panic("Unable to update text") | ||||
| @@ -1,4 +1,3 @@ | |||||
| pub mod components; | |||||
| pub mod constants; | pub mod constants; | ||||
| pub mod debug; | pub mod debug; | ||||
| pub mod error; | pub mod error; | ||||
| @@ -16,7 +15,7 @@ use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary} | |||||
| use misc::{Events, TextManager, Window}; | use misc::{Events, TextManager, Window}; | ||||
| use render::{Asteroids, FleetSelect, Init, Planets, Ships}; | use render::{Asteroids, FleetSelect, Init, Planets, Ships}; | ||||
| use resources::{Camera, Config, Geometry, InputState, PlayerState, Uniform}; | use resources::{Camera, Config, Geometry, InputState, PlayerState, Uniform}; | ||||
| use systems::{FleetControl, FleetInfoUpdate, StateUpdate}; | |||||
| use systems::{FleetControl, StateUpdate}; | |||||
| pub struct App<'a, 'b> { | pub struct App<'a, 'b> { | ||||
| is_running: bool, | is_running: bool, | ||||
| @@ -50,7 +49,6 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(StateUpdate::new(world)?, "state_update", &[]) | .with(StateUpdate::new(world)?, "state_update", &[]) | ||||
| .with(FleetInfoUpdate::new(world), "fleet_info_update", &[]) | |||||
| .with(FleetControl::new(world)?, "fleet_control", &[]) | .with(FleetControl::new(world)?, "fleet_control", &[]) | ||||
| .with_thread_local(Init::new(world)?) | .with_thread_local(Init::new(world)?) | ||||
| .with_thread_local(Planets::new(world)?) | .with_thread_local(Planets::new(world)?) | ||||
| @@ -57,11 +57,11 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| matrix::Angle, | matrix::Angle, | ||||
| vector::{Vector2f, Vector4f}, | vector::{Vector2f, Vector4f}, | ||||
| }; | }; | ||||
| use space_crush_app::components::FleetInfo; | |||||
| use smallvec::smallvec; | |||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{ | 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}, | misc::{PersistWorld, Persistence}, | ||||
| }; | }; | ||||
| @@ -74,6 +74,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| .insert( | .insert( | ||||
| player_id, | player_id, | ||||
| Player { | Player { | ||||
| index: 0, | |||||
| color: Vector4f::new(0.0, 0.5, 1.0, 0.1), | 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), | pos: Vector2f::new(x, y), | ||||
| shape: Shape::Circle(250.0), | 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(Obstacle {}) | ||||
| .with(Planet {}) | .with(Planet {}) | ||||
| .build() | .build() | ||||
| @@ -117,11 +118,11 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| pos: Vector2f::new(x, y), | pos: Vector2f::new(x, y), | ||||
| shape: Shape::Circle(100.0), | 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(Obstacle {}) | ||||
| .with(Asteroid { | .with(Asteroid { | ||||
| type_: match i_x * i_y { | type_: match i_x * i_y { | ||||
| @@ -133,7 +134,15 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| .build(); | .build(); | ||||
| } | } | ||||
| for i in 0..999 { | |||||
| let fleet_id = world | |||||
| .create_entity() | |||||
| .marked::<<PersistWorld as Persistence>::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::<f32>(); | let r = 325.0 + 100.0 * random::<f32>(); | ||||
| let a = Angle::Deg(360.0 * random::<f32>()); | let a = Angle::Deg(360.0 * random::<f32>()); | ||||
| @@ -144,6 +153,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(PlayerOwned { owner: player_id }) | .with(PlayerOwned { owner: player_id }) | ||||
| .with(FleetOwned { owner: fleet_id }) | |||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::new(x, y), | pos: Vector2f::new(x, y), | ||||
| shape: Shape::Dot, | shape: Shape::Dot, | ||||
| @@ -152,7 +162,6 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | ||||
| speed: 100.0, | speed: 100.0, | ||||
| }) | }) | ||||
| .with(FleetOwned { owner: planets[1] }) | |||||
| .with(Ship { | .with(Ship { | ||||
| type_: match i % 3 { | type_: match i % 3 { | ||||
| 0 => ShipType::Fighter, | 0 => ShipType::Fighter, | ||||
| @@ -10,16 +10,16 @@ use glc::{ | |||||
| }; | }; | ||||
| use shrev::{EventChannel, ReaderId}; | use shrev::{EventChannel, ReaderId}; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{Fleet, Position, ShipCount}, | |||||
| components::{Fleet, Orbit, OrbitOwned, Player, Position, ShipCount}, | |||||
| constants::VECTOR_2F_POS_X, | constants::VECTOR_2F_POS_X, | ||||
| continue_if_none, | |||||
| misc::{LogResult, WorldHelper as _}, | misc::{LogResult, WorldHelper as _}, | ||||
| resources::Global, | resources::Global, | ||||
| return_if_none, | return_if_none, | ||||
| }; | }; | ||||
| use specs::{prelude::*, Entities, ReadExpect, ReadStorage, System, World}; | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||||
| use crate::{ | use crate::{ | ||||
| components::FleetInfo, | |||||
| constants::{ | constants::{ | ||||
| FLEET_SELECT_ANIMATION_TIME, FLEET_SELECT_DETAIL_TIMEOUT, FLEET_SELECT_OFFSET, | FLEET_SELECT_ANIMATION_TIME, FLEET_SELECT_DETAIL_TIMEOUT, FLEET_SELECT_OFFSET, | ||||
| FLEET_SELECT_TEXT_OFFSET, FLEET_SELECT_TEXT_SIZE, FLEET_SELECT_WIDTH, | FLEET_SELECT_TEXT_OFFSET, FLEET_SELECT_TEXT_SIZE, FLEET_SELECT_WIDTH, | ||||
| @@ -74,15 +74,16 @@ pub struct FleetSelectData<'a> { | |||||
| player_state: WriteExpect<'a, PlayerState>, | player_state: WriteExpect<'a, PlayerState>, | ||||
| camera: ReadExpect<'a, Camera>, | camera: ReadExpect<'a, Camera>, | ||||
| entities: Entities<'a>, | |||||
| mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | ||||
| input_state: ReadExpect<'a, InputState>, | input_state: ReadExpect<'a, InputState>, | ||||
| geometry: ReadExpect<'a, Geometry>, | geometry: ReadExpect<'a, Geometry>, | ||||
| global: ReadExpect<'a, Global>, | global: ReadExpect<'a, Global>, | ||||
| config: ReadExpect<'a, Config>, | config: ReadExpect<'a, Config>, | ||||
| fleet_infos: ReadStorage<'a, FleetInfo>, | |||||
| orbit_owned: ReadStorage<'a, OrbitOwned>, | |||||
| positions: ReadStorage<'a, Position>, | positions: ReadStorage<'a, Position>, | ||||
| players: ReadStorage<'a, Player>, | |||||
| orbits: ReadStorage<'a, Orbit>, | |||||
| fleets: ReadStorage<'a, Fleet>, | fleets: ReadStorage<'a, Fleet>, | ||||
| } | } | ||||
| @@ -95,9 +96,15 @@ macro_rules! selection { | |||||
| }; | }; | ||||
| } | } | ||||
| macro_rules! fleet_info { | |||||
| macro_rules! player { | |||||
| (&$data:expr, $id:expr) => { | (&$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 { | impl FleetSelect { | ||||
| pub fn new(world: &World, text_manager: &TextManager) -> Result<Self, Error> { | pub fn new(world: &World, text_manager: &TextManager) -> Result<Self, Error> { | ||||
| let program_simple = world.load_program(vec![ | let program_simple = world.load_program(vec![ | ||||
| @@ -180,11 +193,17 @@ impl FleetSelect { | |||||
| MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => { | MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => { | ||||
| let pos = d.camera.view_to_world(d.input_state.mouse_pos); | let pos = d.camera.view_to_world(d.input_state.mouse_pos); | ||||
| let selection = d.player_state.selection.take(); | 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 { | 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 { | d.player_state.selection = match selection { | ||||
| Some(s) if s.fleet == id => { | |||||
| Some(s) if s.fleet == fleet_id => { | |||||
| self.is_new_selection = false; | self.is_new_selection = false; | ||||
| Some(s) | Some(s) | ||||
| @@ -193,20 +212,20 @@ impl FleetSelect { | |||||
| self.is_new_selection = true; | self.is_new_selection = true; | ||||
| Some(Selection { | Some(Selection { | ||||
| fleet: id, | |||||
| fleet: fleet_id, | |||||
| count: ShipCount::none(), | count: ShipCount::none(), | ||||
| }) | }) | ||||
| } | } | ||||
| }; | }; | ||||
| let selection = selection!(&d); | 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; | let timeout = Instant::now() + FLEET_SELECT_DETAIL_TIMEOUT; | ||||
| self.mouse_pos = d.input_state.mouse_pos; | self.mouse_pos = d.input_state.mouse_pos; | ||||
| self.select_mode = SelectMode::Init(timeout); | self.select_mode = SelectMode::Init(timeout); | ||||
| self.values_changed_once = false; | self.values_changed_once = false; | ||||
| self.set_count(selection.count, &fleet_info.count); | |||||
| self.set_count(selection.count, &fleet.count); | |||||
| break; | break; | ||||
| } | } | ||||
| @@ -262,8 +281,9 @@ impl FleetSelect { | |||||
| /* calculate values */ | /* calculate values */ | ||||
| let selection = selection!(&d); | 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.marker = d.camera.view_to_world(self.mouse_pos) - position.pos; | ||||
| self.zoom = d.camera.view().axis_x.as_vec3().length(); | self.zoom = d.camera.view().axis_x.as_vec3().length(); | ||||
| @@ -294,10 +314,10 @@ impl FleetSelect { | |||||
| x @ 0..=2 => { | x @ 0..=2 => { | ||||
| let mut count = selection.count; | let mut count = selection.count; | ||||
| count[x] = usize::MAX; | count[x] = usize::MAX; | ||||
| self.set_count(count, &fleet_info.count); | |||||
| self.set_count(count, &fleet.count); | |||||
| self.values[x] = 1.0; | self.values[x] = 1.0; | ||||
| self.update_values(&fleet_info.count); | |||||
| self.update_values(&fleet.count); | |||||
| } | } | ||||
| _ => { | _ => { | ||||
| self.count = ShipCount::all(); | self.count = ShipCount::all(); | ||||
| @@ -312,14 +332,14 @@ impl FleetSelect { | |||||
| match sector { | match sector { | ||||
| x @ 0..=2 => { | x @ 0..=2 => { | ||||
| let mut count = selection.count; | 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.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(); | self.values = value.into(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -328,10 +348,10 @@ impl FleetSelect { | |||||
| x @ 0..=2 => { | x @ 0..=2 => { | ||||
| let mut count = selection.count; | let mut count = selection.count; | ||||
| count[x] = 0; | count[x] = 0; | ||||
| self.set_count(count, &fleet_info.count); | |||||
| self.set_count(count, &fleet.count); | |||||
| self.values[x] = 0.0; | self.values[x] = 0.0; | ||||
| self.update_values(&fleet_info.count); | |||||
| self.update_values(&fleet.count); | |||||
| } | } | ||||
| _ => { | _ => { | ||||
| self.count = Default::default(); | self.count = Default::default(); | ||||
| @@ -341,7 +361,7 @@ impl FleetSelect { | |||||
| } | } | ||||
| /* update texts */ | /* update texts */ | ||||
| let c = self.count.merge(&fleet_info.count); | |||||
| let c = self.count.merge(&fleet.count); | |||||
| let _guard = self.cache.begin_update(); | let _guard = self.cache.begin_update(); | ||||
| self.text_total | self.text_total | ||||
| .update(0, c.total().to_string()) | .update(0, c.total().to_string()) | ||||
| @@ -415,7 +435,8 @@ impl FleetSelect { | |||||
| /* extract system data */ | /* extract system data */ | ||||
| let selection = selection!(&d); | 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 */ | /* calculate shared values */ | ||||
| let size = self.ring1 + 50.0; | let size = self.ring1 + 50.0; | ||||
| @@ -1,13 +1,12 @@ | |||||
| use shrev::{EventChannel, ReaderId}; | use shrev::{EventChannel, ReaderId}; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{Fleet, FleetOwned, Position, Ship, ShipType}, | |||||
| components::{Fleet, FleetOwned, Orbit, Player, Position, Ship, ShipType}, | |||||
| continue_if_none, | |||||
| misc::WorldHelper, | misc::WorldHelper, | ||||
| return_if_none, | |||||
| }; | }; | ||||
| use specs::{prelude::*, Entities, Entity, ReadStorage, System, World, WriteStorage}; | |||||
| use specs::{prelude::*, Entity, ReadStorage, System, World, WriteStorage}; | |||||
| use crate::{ | use crate::{ | ||||
| components::FleetInfo, | |||||
| misc::MouseEvent, | misc::MouseEvent, | ||||
| resources::{Camera, Config, InputState, PlayerState}, | resources::{Camera, Config, InputState, PlayerState}, | ||||
| Error, | Error, | ||||
| @@ -26,11 +25,11 @@ pub struct FleetControlData<'a> { | |||||
| camera: ReadExpect<'a, Camera>, | camera: ReadExpect<'a, Camera>, | ||||
| config: ReadExpect<'a, Config>, | config: ReadExpect<'a, Config>, | ||||
| entities: Entities<'a>, | |||||
| ships: ReadStorage<'a, Ship>, | ships: ReadStorage<'a, Ship>, | ||||
| fleet_owned: WriteStorage<'a, FleetOwned>, | fleet_owned: WriteStorage<'a, FleetOwned>, | ||||
| positions: ReadStorage<'a, Position>, | positions: ReadStorage<'a, Position>, | ||||
| fleet_infos: ReadStorage<'a, FleetInfo>, | |||||
| players: ReadStorage<'a, Player>, | |||||
| orbits: ReadStorage<'a, Orbit>, | |||||
| fleets: ReadStorage<'a, Fleet>, | fleets: ReadStorage<'a, Fleet>, | ||||
| } | } | ||||
| @@ -56,11 +55,11 @@ impl<'a> System<'a> for FleetControl { | |||||
| input_state, | input_state, | ||||
| camera, | camera, | ||||
| config, | config, | ||||
| entities, | |||||
| ships, | ships, | ||||
| mut fleet_owned, | mut fleet_owned, | ||||
| positions, | positions, | ||||
| fleet_infos, | |||||
| players, | |||||
| orbits, | |||||
| fleets, | fleets, | ||||
| } = data; | } = data; | ||||
| @@ -69,10 +68,15 @@ impl<'a> System<'a> for FleetControl { | |||||
| match event { | match event { | ||||
| MouseEvent::ButtonDown(button) if button == &config.input.fleet_move_button => { | MouseEvent::ButtonDown(button) if button == &config.input.fleet_move_button => { | ||||
| let pos = camera.view_to_world(input_state.mouse_pos); | 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 { | 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; | break; | ||||
| } | } | ||||
| @@ -80,12 +84,10 @@ impl<'a> System<'a> for FleetControl { | |||||
| } | } | ||||
| MouseEvent::ButtonUp(button) if button == &config.input.fleet_move_button => { | MouseEvent::ButtonUp(button) if button == &config.input.fleet_move_button => { | ||||
| let selection = player_state.selection.take(); | 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_ { | match &ship.type_ { | ||||
| ShipType::Fighter if selection.count.fighter > 0 => { | ShipType::Fighter if selection.count.fighter > 0 => { | ||||
| fleet_owned.owner = target_fleet; | fleet_owned.owner = target_fleet; | ||||
| @@ -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<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) -> Self { | |||||
| 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() | |||||
| }; | |||||
| 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,5 @@ | |||||
| mod fleet_control; | mod fleet_control; | ||||
| mod fleet_info_update; | |||||
| mod state_update; | mod state_update; | ||||
| pub use fleet_control::FleetControl; | pub use fleet_control::FleetControl; | ||||
| pub use fleet_info_update::FleetInfoUpdate; | |||||
| pub use state_update::StateUpdate; | pub use state_update::StateUpdate; | ||||
| @@ -14,6 +14,7 @@ serde = "1.0" | |||||
| serde_yaml = "0.8" | serde_yaml = "0.8" | ||||
| shred = { version = "0.10", features = [ "shred-derive" ] } | shred = { version = "0.10", features = [ "shred-derive" ] } | ||||
| shrev = "1.1" | shrev = "1.1" | ||||
| smallvec = { version = "1.5", features = [ "serde" ] } | |||||
| specs = { version = "*", features = [ "serde" ] } | specs = { version = "*", features = [ "serde" ] } | ||||
| thiserror = "1.0" | thiserror = "1.0" | ||||
| vfs = "0.4" | vfs = "0.4" | ||||
| @@ -1,16 +1,17 @@ | |||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||
| use specs::{ | use specs::{ | ||||
| error::NoError, | error::NoError, | ||||
| hibitset::BitSet, | |||||
| saveload::{ConvertSaveload, Marker}, | saveload::{ConvertSaveload, Marker}, | ||||
| Component, Entity, HashMapStorage, VecStorage, | 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 struct Fleet { | ||||
| pub orbit_min: f32, | |||||
| pub orbit_max: f32, | |||||
| pub owned: BitSet, | |||||
| pub count: ShipCount, | |||||
| } | } | ||||
| #[derive(Copy, Clone, Debug)] | #[derive(Copy, Clone, Debug)] | ||||
| @@ -1,6 +1,7 @@ | |||||
| mod asteroid; | mod asteroid; | ||||
| mod fleet; | mod fleet; | ||||
| mod obstacle; | mod obstacle; | ||||
| mod orbit; | |||||
| mod planet; | mod planet; | ||||
| mod player; | mod player; | ||||
| mod position; | mod position; | ||||
| @@ -10,6 +11,7 @@ mod velocity; | |||||
| pub use asteroid::{Asteroid, Type as AsteroidType}; | pub use asteroid::{Asteroid, Type as AsteroidType}; | ||||
| pub use fleet::{Fleet, Owned as FleetOwned}; | pub use fleet::{Fleet, Owned as FleetOwned}; | ||||
| pub use obstacle::Obstacle; | pub use obstacle::Obstacle; | ||||
| pub use orbit::{Orbit, Owned as OrbitOwned}; | |||||
| pub use planet::Planet; | pub use planet::Planet; | ||||
| pub use player::{Owned as PlayerOwned, Player}; | pub use player::{Owned as PlayerOwned, Player}; | ||||
| pub use position::{Position, Shape}; | pub use position::{Position, Shape}; | ||||
| @@ -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<Entity>; 8]>, | |||||
| } | |||||
| #[derive(Serialize, Deserialize)] | |||||
| pub struct OrbitData<M> { | |||||
| pub min: f32, | |||||
| pub max: f32, | |||||
| pub fleets: Vec<Option<M>>, | |||||
| } | |||||
| #[derive(Copy, Clone, Debug)] | |||||
| pub struct Owned { | |||||
| pub owner: Entity, | |||||
| } | |||||
| #[derive(Serialize, Deserialize)] | |||||
| pub struct OwnedData<M> { | |||||
| pub owner: M, | |||||
| } | |||||
| impl Component for Orbit { | |||||
| type Storage = HashMapStorage<Self>; | |||||
| } | |||||
| impl<M> ConvertSaveload<M> for Orbit | |||||
| where | |||||
| for<'de> M: Marker + Serialize + Deserialize<'de>, | |||||
| { | |||||
| type Data = OrbitData<M>; | |||||
| type Error = NoError; | |||||
| fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||||
| where | |||||
| F: FnMut(Entity) -> Option<M>, | |||||
| { | |||||
| 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<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||||
| where | |||||
| F: FnMut(M) -> Option<Entity>, | |||||
| { | |||||
| 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<Self, HashMapStorage<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 }) | |||||
| } | |||||
| } | |||||
| @@ -3,13 +3,12 @@ use serde::{Deserialize, Serialize}; | |||||
| use specs::{ | use specs::{ | ||||
| error::NoError, | error::NoError, | ||||
| saveload::{ConvertSaveload, Marker}, | saveload::{ConvertSaveload, Marker}, | ||||
| Component, Entity, HashMapStorage, VecStorage, | |||||
| Component, Entity, HashMapStorage, | |||||
| }; | }; | ||||
| use crate::misc::FlaggedStorage; | |||||
| #[derive(Clone, Debug, Default, Serialize, Deserialize)] | #[derive(Clone, Debug, Default, Serialize, Deserialize)] | ||||
| pub struct Player { | pub struct Player { | ||||
| pub index: usize, | |||||
| pub color: Vector4f, | pub color: Vector4f, | ||||
| } | } | ||||
| @@ -28,7 +27,7 @@ impl Component for Player { | |||||
| } | } | ||||
| impl Component for Owned { | impl Component for Owned { | ||||
| type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||||
| type Storage = HashMapStorage<Self>; | |||||
| } | } | ||||
| impl<M> ConvertSaveload<M> for Owned | impl<M> ConvertSaveload<M> for Owned | ||||
| @@ -3,7 +3,7 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||||
| use crate::{ | use crate::{ | ||||
| components::Player, | components::Player, | ||||
| resources::Global, | resources::Global, | ||||
| systems::{Movement, Process, Ships}, | |||||
| systems::{FleetOwnedUpdate, Movement, OrbitOwnedUpdate, Process, Ships}, | |||||
| }; | }; | ||||
| pub struct Dispatcher<'a, 'b> { | pub struct Dispatcher<'a, 'b> { | ||||
| @@ -20,6 +20,8 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||||
| .with(Process::default(), "process", &[]) | .with(Process::default(), "process", &[]) | ||||
| .with(Movement::default(), "movement", &[]) | .with(Movement::default(), "movement", &[]) | ||||
| .with(Ships::new(world), "ships", &[]) | .with(Ships::new(world), "ships", &[]) | ||||
| .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | |||||
| .with(OrbitOwnedUpdate::new(world), "orbit_owned_update", &[]) | |||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -20,7 +20,7 @@ pub enum ComponentEvent<C> | |||||
| where | where | ||||
| C: Send + Sync + 'static, | C: Send + Sync + 'static, | ||||
| { | { | ||||
| Inserted(Index), | |||||
| Inserted(Index, C), | |||||
| Modified(Index, C), | Modified(Index, C), | ||||
| Removed(Index, C), | Removed(Index, C), | ||||
| } | } | ||||
| @@ -67,26 +67,27 @@ where | |||||
| } | } | ||||
| unsafe fn get_mut(&mut self, id: Index) -> &mut C { | 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 | 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 { | unsafe fn remove(&mut self, id: Index) -> C { | ||||
| let c = self.storage.remove(id); | |||||
| let component = self.storage.remove(id); | |||||
| self.channel | self.channel | ||||
| .single_write(ComponentEvent::Removed(id, c.clone())); | |||||
| .single_write(ComponentEvent::Removed(id, component.clone())); | |||||
| c | |||||
| component | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,7 +5,10 @@ use specs::{ | |||||
| Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | 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 */ | /* PersistWorld */ | ||||
| @@ -17,12 +20,15 @@ impl Persistence for PersistWorld { | |||||
| type Components = ( | type Components = ( | ||||
| Position, | Position, | ||||
| Velocity, | Velocity, | ||||
| Planet, | |||||
| Ship, | |||||
| Player, | Player, | ||||
| PlayerOwned, | PlayerOwned, | ||||
| Fleet, | |||||
| Orbit, | |||||
| OrbitOwned, | |||||
| FleetOwned, | FleetOwned, | ||||
| Ship, | |||||
| Obstacle, | |||||
| Planet, | |||||
| Asteroid, | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -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<Index, Entity>, | |||||
| fleet_owned_event_id: ReaderId<ComponentEvent<FleetOwned>>, | |||||
| } | |||||
| 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::<FleetOwned>::setup(world); | |||||
| let mut fleet_owned = world.system_data::<WriteStorage<FleetOwned>>(); | |||||
| 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,11 @@ | |||||
| mod fleet_owned_update; | |||||
| mod movement; | mod movement; | ||||
| mod orbit_owned_update; | |||||
| mod process; | mod process; | ||||
| mod ships; | mod ships; | ||||
| pub use fleet_owned_update::FleetOwnedUpdate; | |||||
| pub use movement::Movement; | pub use movement::Movement; | ||||
| pub use orbit_owned_update::OrbitOwnedUpdate; | |||||
| pub use process::Process; | pub use process::Process; | ||||
| pub use ships::Ships; | pub use ships::Ships; | ||||
| @@ -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<Index, Entity>, | |||||
| orbit_owned_event_id: ReaderId<crate::misc::ComponentEvent<OrbitOwned>>, | |||||
| } | |||||
| 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::<OrbitOwned>::setup(world); | |||||
| let mut orbit_owned = world.system_data::<WriteStorage<OrbitOwned>>(); | |||||
| 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); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -34,6 +34,10 @@ impl<'a> System<'a> for Process { | |||||
| self.time += global.delta; | self.time += global.delta; | ||||
| self.count += 1; | self.count += 1; | ||||
| if global.delta > 1.0 { | |||||
| global.delta = 1.0; | |||||
| } | |||||
| if self.time >= 2.0 { | if self.time >= 2.0 { | ||||
| self.time = 0.0; | self.time = 0.0; | ||||
| self.count = 0; | self.count = 0; | ||||
| @@ -11,7 +11,7 @@ use specs::{ | |||||
| }; | }; | ||||
| use crate::{ | use crate::{ | ||||
| components::{Fleet, FleetOwned, Obstacle, Position, Ship, ShipObstacle, Velocity}, | |||||
| components::{FleetOwned, Obstacle, Orbit, OrbitOwned, Position, Ship, ShipObstacle, Velocity}, | |||||
| constants::{ | constants::{ | ||||
| SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, | SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, | ||||
| SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, | SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, | ||||
| @@ -33,9 +33,10 @@ pub struct ShipsData<'a> { | |||||
| ships: WriteStorage<'a, Ship>, | ships: WriteStorage<'a, Ship>, | ||||
| velocities: WriteStorage<'a, Velocity>, | velocities: WriteStorage<'a, Velocity>, | ||||
| fleet_owned: ReadStorage<'a, FleetOwned>, | fleet_owned: ReadStorage<'a, FleetOwned>, | ||||
| orbit_owned: ReadStorage<'a, OrbitOwned>, | |||||
| positions: ReadStorage<'a, Position>, | positions: ReadStorage<'a, Position>, | ||||
| obstacles: ReadStorage<'a, Obstacle>, | obstacles: ReadStorage<'a, Obstacle>, | ||||
| fleets: ReadStorage<'a, Fleet>, | |||||
| orbits: ReadStorage<'a, Orbit>, | |||||
| } | } | ||||
| struct Processor<'a> { | struct Processor<'a> { | ||||
| @@ -43,7 +44,8 @@ struct Processor<'a> { | |||||
| entities: &'a Entities<'a>, | entities: &'a Entities<'a>, | ||||
| positions: &'a ReadStorage<'a, Position>, | positions: &'a ReadStorage<'a, Position>, | ||||
| obstacles: &'a ReadStorage<'a, Obstacle>, | obstacles: &'a ReadStorage<'a, Obstacle>, | ||||
| fleets: &'a ReadStorage<'a, Fleet>, | |||||
| orbits: &'a ReadStorage<'a, Orbit>, | |||||
| orbit_owned: &'a ReadStorage<'a, OrbitOwned>, | |||||
| delta: f32, | delta: f32, | ||||
| } | } | ||||
| @@ -74,7 +76,7 @@ impl Ships { | |||||
| .read(&mut self.fleet_owned_id); | .read(&mut self.fleet_owned_id); | ||||
| for event in events { | for event in events { | ||||
| let id = match event { | let id = match event { | ||||
| ComponentEvent::Inserted(id) => id, | |||||
| ComponentEvent::Inserted(id, _) => id, | |||||
| ComponentEvent::Modified(id, _) => id, | ComponentEvent::Modified(id, _) => id, | ||||
| ComponentEvent::Removed(id, _) => id, | ComponentEvent::Removed(id, _) => id, | ||||
| }; | }; | ||||
| @@ -94,9 +96,10 @@ impl<'a> System<'a> for Ships { | |||||
| mut ships, | mut ships, | ||||
| mut velocities, | mut velocities, | ||||
| fleet_owned, | fleet_owned, | ||||
| orbit_owned, | |||||
| positions, | positions, | ||||
| obstacles, | obstacles, | ||||
| fleets, | |||||
| orbits, | |||||
| } = data; | } = data; | ||||
| self.progress_events(&fleet_owned); | self.progress_events(&fleet_owned); | ||||
| @@ -107,7 +110,8 @@ impl<'a> System<'a> for Ships { | |||||
| entities: &entities, | entities: &entities, | ||||
| positions: &positions, | positions: &positions, | ||||
| obstacles: &obstacles, | obstacles: &obstacles, | ||||
| fleets: &fleets, | |||||
| orbit_owned: &orbit_owned, | |||||
| orbits: &orbits, | |||||
| delta: global.delta * global.world_speed, | delta: global.delta * global.world_speed, | ||||
| }; | }; | ||||
| @@ -136,8 +140,10 @@ impl Processor<'_> { | |||||
| fleet_owned: &FleetOwned, | fleet_owned: &FleetOwned, | ||||
| ) { | ) { | ||||
| let fleet_id = fleet_owned.owner; | 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 ship_pos = position.pos; | ||||
| let target_pos = ship.target_pos; | let target_pos = ship.target_pos; | ||||
| let target_dir = ship.target_dir; | let target_dir = ship.target_dir; | ||||
| @@ -149,11 +155,8 @@ impl Processor<'_> { | |||||
| let r_ship = orbit_to_ship.length_sqr(); | let r_ship = orbit_to_ship.length_sqr(); | ||||
| let r_target = orbit_to_target.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 need_update = self.need_update.contains(id); | ||||
| let has_target = target_dir.length_sqr() != 0.0; | let has_target = target_dir.length_sqr() != 0.0; | ||||
| @@ -161,7 +164,7 @@ impl Processor<'_> { | |||||
| /* check and update target posistion */ | /* check and update target posistion */ | ||||
| if need_update || !has_target || passed_target || ship_in_orbit != target_in_orbit { | 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 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); | 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 add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random(); | ||||
| let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X); | 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::<f32>(); | |||||
| let angle = angle + add * dir / orbit.max; | |||||
| let radius = orbit.min + (orbit.max - orbit.min) * random::<f32>(); | |||||
| Vector2f::new( | Vector2f::new( | ||||
| orbit_pos.x + radius * angle.cos(), | orbit_pos.x + radius * angle.cos(), | ||||
| @@ -197,7 +200,7 @@ impl Processor<'_> { | |||||
| ship.obstacle = ShipObstacle::Done; | ship.obstacle = ShipObstacle::Done; | ||||
| } else if let ShipObstacle::Known(obstacle) = ship.obstacle { | } else if let ShipObstacle::Known(obstacle) = ship.obstacle { | ||||
| if let Some(position) = self.positions.get(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 obstacle_pos = position.pos; | ||||
| let ship_to_obstacle = obstacle_pos - ship_pos; | let ship_to_obstacle = obstacle_pos - ship_pos; | ||||
| @@ -207,7 +210,7 @@ impl Processor<'_> { | |||||
| .into_inner() | .into_inner() | ||||
| .abs(); | .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) | if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > orbit_sqr) | ||||
| || obstacle_angle > 170.0 | || obstacle_angle > 170.0 | ||||
| { | { | ||||
| @@ -247,13 +250,13 @@ impl Processor<'_> { | |||||
| let mut expected_dir = ship_to_target; | let mut expected_dir = ship_to_target; | ||||
| if let ShipObstacle::Known(obstacle) = ship.obstacle { | if let ShipObstacle::Known(obstacle) = ship.obstacle { | ||||
| let obstacle_pos = self.positions.get(obstacle).unwrap(); | 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 ship_to_obstacle = obstacle_pos.pos - ship_pos; | ||||
| let orbit = ship_to_obstacle.length(); | 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 mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x); | ||||
| let radius = obstacle_pos.shape.radius(); | let radius = obstacle_pos.shape.radius(); | ||||