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