diff --git a/glc/src/angle.rs b/glc/src/angle.rs index 42b81a9..124d586 100644 --- a/glc/src/angle.rs +++ b/glc/src/angle.rs @@ -58,7 +58,7 @@ where fn _add(self, other: &Self) -> Self { match self { Self::Deg(value) => Self::Deg(value + other.into_deg().into_inner()), - Self::Rad(value) => Self::Rad(value * other.into_rad().into_inner()), + Self::Rad(value) => Self::Rad(value + other.into_rad().into_inner()), } } diff --git a/space-crush-app/src/debug/fleets.rs b/space-crush-app/src/debug/fleets.rs index 3d8a6fc..c584511 100644 --- a/space-crush-app/src/debug/fleets.rs +++ b/space-crush-app/src/debug/fleets.rs @@ -1,16 +1,13 @@ -use glc::vector::{Angle, Vector2f, Vector4f}; use space_crush_common::{ - components::{Fleet, Orbit, Player, Position}, - constants::SHIP_ORBIT_DISTANCE_MAX, + components::{Fleet, Orbit, Position}, continue_if_none, misc::LogResult, - return_if_none, }; -use specs::{prelude::*, ReadExpect, ReadStorage, System, World, WriteExpect}; +use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; use crate::{ misc::{HorizontalAlign, Text, TextManager, VerticalAlign}, - resources::{Camera, GameState, Geometry}, + resources::{Camera, GameState}, Error, }; @@ -21,10 +18,8 @@ pub struct Fleets { #[derive(SystemData)] pub struct FleetData<'a> { game_state: ReadExpect<'a, GameState>, - geometry: WriteExpect<'a, Geometry>, camera: ReadExpect<'a, Camera>, positions: ReadStorage<'a, Position>, - players: ReadStorage<'a, Player>, orbits: ReadStorage<'a, Orbit>, fleets: ReadStorage<'a, Fleet>, } @@ -52,37 +47,19 @@ impl<'a> System<'a> for Fleets { fn run(&mut self, data: Self::SystemData) { let FleetData { game_state, - mut geometry, camera, positions, - players, orbits, fleets, } = data; - let player = return_if_none!(players.get(game_state.player_id)); + let player_index = game_state.player_index(); gl::enable(gl::BLEND); for (position, orbit) in (&positions, &orbits).join() { - let fleet_id = continue_if_none!(orbit.fleets().get(player.index())); - let fleet_id = continue_if_none!(fleet_id); - let fleet = continue_if_none!(fleets.get(*fleet_id)); - - gl::blend_func(gl::SRC_ALPHA, gl::ONE); - gl::blend_equation(gl::FUNC_ADD); - geometry.render_lines( - Vector4f::new(0.5, 0.5, 0.5, 0.05), - &create_circle(position.pos(), orbit.min()), - ); - geometry.render_lines( - Vector4f::new(0.5, 0.5, 0.5, 0.05), - &create_circle(position.pos(), orbit.max()), - ); - geometry.render_lines( - Vector4f::new(0.5, 0.5, 0.5, 0.05), - &create_circle(position.pos(), SHIP_ORBIT_DISTANCE_MAX * orbit.max()), - ); + let fleet_id = continue_if_none!(orbit.fleet(player_index)); + let fleet = continue_if_none!(fleets.get(fleet_id)); gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); gl::blend_equation(gl::FUNC_SUBTRACT); @@ -104,16 +81,3 @@ impl<'a> System<'a> for Fleets { gl::disable(gl::BLEND); } } - -fn create_circle(p: &Vector2f, r: f32) -> Vec { - let mut points = Vec::new(); - - for i in 0..=180 { - points.push(Vector2f::new( - p.x + r * Angle::Deg(i as f32 * 2.0).cos(), - p.y + r * Angle::Deg(i as f32 * 2.0).sin(), - )); - } - - points -} diff --git a/space-crush-app/src/debug/mod.rs b/space-crush-app/src/debug/mod.rs index 3b244c0..79832ac 100644 --- a/space-crush-app/src/debug/mod.rs +++ b/space-crush-app/src/debug/mod.rs @@ -1,7 +1,9 @@ mod fleets; +mod orbits; mod ships; mod summary; pub use fleets::Fleets; +pub use orbits::Orbits; pub use ships::Ships; pub use summary::Summary; diff --git a/space-crush-app/src/debug/orbits.rs b/space-crush-app/src/debug/orbits.rs new file mode 100644 index 0000000..73d7bf0 --- /dev/null +++ b/space-crush-app/src/debug/orbits.rs @@ -0,0 +1,65 @@ +use glc::vector::{Angle, Vector2f, Vector4f}; +use space_crush_common::{ + components::{Orbit, Position}, + constants::SHIP_ORBIT_DISTANCE_MAX, +}; +use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; + +use crate::resources::Geometry; + +#[derive(Default)] +pub struct Orbits {} + +#[derive(SystemData)] +pub struct OrbitsData<'a> { + geometry: WriteExpect<'a, Geometry>, + positions: ReadStorage<'a, Position>, + orbits: ReadStorage<'a, Orbit>, +} + +impl<'a> System<'a> for Orbits { + type SystemData = OrbitsData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let OrbitsData { + mut geometry, + positions, + orbits, + } = data; + + gl::enable(gl::BLEND); + + for (position, orbit) in (&positions, &orbits).join() { + gl::blend_func(gl::SRC_ALPHA, gl::ONE); + gl::blend_equation(gl::FUNC_ADD); + geometry.render_lines( + Vector4f::new(0.5, 0.5, 0.5, 0.05), + &create_circle(position.pos(), orbit.min()), + ); + geometry.render_lines( + Vector4f::new(0.5, 0.5, 0.5, 0.05), + &create_circle(position.pos(), orbit.max()), + ); + geometry.render_lines( + Vector4f::new(0.5, 0.5, 0.5, 0.05), + &create_circle(position.pos(), SHIP_ORBIT_DISTANCE_MAX * orbit.max()), + ); + } + + gl::blend_equation(gl::FUNC_ADD); + gl::disable(gl::BLEND); + } +} + +fn create_circle(p: &Vector2f, r: f32) -> Vec { + let mut points = Vec::new(); + + for i in 0..=180 { + points.push(Vector2f::new( + p.x + r * Angle::Deg(i as f32 * 2.0).cos(), + p.y + r * Angle::Deg(i as f32 * 2.0).sin(), + )); + } + + points +} diff --git a/space-crush-app/src/lib.rs b/space-crush-app/src/lib.rs index eba0502..44d3354 100644 --- a/space-crush-app/src/lib.rs +++ b/space-crush-app/src/lib.rs @@ -11,9 +11,11 @@ use specs::{Dispatcher, DispatcherBuilder, Entity, World}; pub use error::Error; -use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary}; +use debug::{ + Fleets as DebugFleets, Orbits as DebugOrbits, Ships as DebugShips, Summary as DebugSummary, +}; use misc::{Events, TextManager, Window}; -use render::{Asteroids, FleetSelect, Init, Planets, Ships}; +use render::{Asteroids, FleetMove, FleetSelect, Init, Planets, Ships}; use resources::{Camera, Config, GameState, Geometry, InputState, Uniform}; use systems::InputStateUpdate; @@ -34,7 +36,7 @@ impl<'a, 'b> App<'a, 'b> { let uniform = Uniform::new()?; let geometry = Geometry::new(world)?; let input_state = InputState::default(); - let game_state = GameState::new(player_id); + let game_state = GameState::new(world, player_id); camera.update(Matrix4f::scale(0.25))?; @@ -54,7 +56,9 @@ impl<'a, 'b> App<'a, 'b> { .with_thread_local(Asteroids::new(world)?) .with_thread_local(Ships::new(world)?) .with_thread_local(FleetSelect::new(world, &text_manager)?) + .with_thread_local(FleetMove::new(world)?) .with_thread_local(DebugShips::default()) + .with_thread_local(DebugOrbits::default()) .with_thread_local(DebugFleets::new(&text_manager)?) .with_thread_local(DebugSummary::new(&text_manager)?) .build(); diff --git a/space-crush-app/src/main.rs b/space-crush-app/src/main.rs index 7b93d10..23e164d 100644 --- a/space-crush-app/src/main.rs +++ b/space-crush-app/src/main.rs @@ -1,7 +1,9 @@ +use glc::vector::Vector4f; use log::{error, info}; use rand::random; use space_crush_app::{App, Error}; use space_crush_common::{ + components::Player, misc::{init_logger, Vfs}, Dispatcher, }; @@ -33,9 +35,11 @@ fn run(vfs: Vfs) -> Result<(), Error> { let mut world = World::new(); world.insert(vfs); - let player1 = world.create_entity().build(); - - let mut common = Dispatcher::new(&mut world); + let mut common = Dispatcher::new(&mut world)?; + let player1 = world + .create_entity() + .with(Player::new(Vector4f::new(0.0, 0.5, 1.0, 0.1))) + .build(); let mut app = App::new(&mut world, player1)?; create_world(&mut world, player1); @@ -53,26 +57,18 @@ fn run(vfs: Vfs) -> Result<(), Error> { } fn create_world(world: &mut World, player_id: Entity) { - use glc::{ - matrix::Angle, - vector::{Vector2f, Vector4f}, - }; + use glc::{matrix::Angle, vector::Vector2f}; use space_crush_common::{ components::{ - Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Orbit, OrbitOwned, Planet, Player, + Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Orbit, OrbitOwned, Planet, PlayerOwned, Position, Ship, ShipType, Velocity, }, misc::{PersistWorld, Persistence}, }; - use specs::{saveload::MarkedBuilder, WriteStorage}; + use specs::saveload::MarkedBuilder; PersistWorld::setup(world); - world - .system_data::>() - .insert(player_id, Player::new(Vector4f::new(0.0, 0.5, 1.0, 0.1))) - .unwrap(); - let planets = (0..3) .map(|i| { let x = 2000.0 * (i as f32 - 1.0); diff --git a/space-crush-app/src/render/fleet_move.rs b/space-crush-app/src/render/fleet_move.rs new file mode 100644 index 0000000..7a87b1f --- /dev/null +++ b/space-crush-app/src/render/fleet_move.rs @@ -0,0 +1,94 @@ +use shrev::{EventChannel, ReaderId}; +use space_crush_common::{ + components::{Orbit, Position}, + continue_if_none, + misc::WorldHelper, + systems::FleetControlEvent, +}; +use specs::{prelude::*, Entities, Entity, ReadStorage, System, World}; + +use crate::{ + misc::MouseEvent, + resources::{Camera, Config, GameState, InputState, Selection}, + Error, +}; + +pub struct FleetMove { + mouse_event_id: ReaderId, + target_orbit: Option, +} + +#[derive(SystemData)] +pub struct FleetMoveData<'a> { + game_state: WriteExpect<'a, GameState>, + fleet_control: WriteExpect<'a, EventChannel>, + mouse_events: ReadExpect<'a, EventChannel>, + input_state: ReadExpect<'a, InputState>, + camera: ReadExpect<'a, Camera>, + config: ReadExpect<'a, Config>, + + entities: Entities<'a>, + positions: ReadStorage<'a, Position>, + orbits: ReadStorage<'a, Orbit>, +} + +impl FleetMove { + pub fn new(world: &mut World) -> Result { + let mouse_event_id = world.register_event_reader::()?; + let target_orbit = None; + + Ok(Self { + mouse_event_id, + target_orbit, + }) + } +} + +impl<'a> System<'a> for FleetMove { + type SystemData = FleetMoveData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let FleetMoveData { + mut game_state, + mut fleet_control, + mouse_events, + input_state, + camera, + config, + entities, + positions, + orbits, + } = data; + + let events = mouse_events.read(&mut self.mouse_event_id); + for event in events { + match event { + MouseEvent::ButtonDown(button) if button == &config.input.fleet_move_button => { + let pos = camera.view_to_world(input_state.mouse_pos); + for (id, position, orbit) in (&entities, &positions, &orbits).join() { + let r = orbit.max() * orbit.max(); + if (position.pos() - pos).length_sqr() <= r { + self.target_orbit = Some(id); + + break; + } + } + } + MouseEvent::ButtonUp(button) if button == &config.input.fleet_move_button => { + let selection = game_state.selection_mut().take(); + let selection = continue_if_none!(selection); + let Selection { fleet, count } = selection; + let target = continue_if_none!(self.target_orbit); + let player = game_state.player_id(); + let event = FleetControlEvent::move_(player, target, fleet, count); + + fleet_control.single_write(event); + } + MouseEvent::Move(_, _) => { + self.target_orbit = None; + } + _ => (), + } + } + } +} diff --git a/space-crush-app/src/render/fleet_select.rs b/space-crush-app/src/render/fleet_select.rs index a27d3d1..2ebbb2f 100644 --- a/space-crush-app/src/render/fleet_select.rs +++ b/space-crush-app/src/render/fleet_select.rs @@ -10,7 +10,7 @@ use glc::{ }; use shrev::{EventChannel, ReaderId}; use space_crush_common::{ - components::{Fleet, Orbit, OrbitOwned, Player, Position, ShipCount}, + components::{Fleet, Orbit, OrbitOwned, Position, ShipCount}, constants::VECTOR_2F_POS_X, continue_if_none, misc::{LogResult, WorldHelper as _}, @@ -82,23 +82,16 @@ pub struct FleetSelectData<'a> { orbit_owned: ReadStorage<'a, OrbitOwned>, positions: ReadStorage<'a, Position>, - players: ReadStorage<'a, Player>, orbits: ReadStorage<'a, Orbit>, fleets: ReadStorage<'a, Fleet>, } macro_rules! selection { (&$data:expr) => { - return_if_none!(&$data.game_state.selection) + return_if_none!($data.game_state.selection()) }; (&mut $data:expr) => { - return_if_none!(&mut $data.game_state.selection) - }; -} - -macro_rules! player { - (&$data:expr, $id:expr) => { - return_if_none!($data.players.get($id)) + return_if_none!($data.game_state.selection_mut()) }; } @@ -192,17 +185,15 @@ impl FleetSelect { match event { MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => { let pos = d.camera.view_to_world(d.input_state.mouse_pos); - let selection = d.game_state.selection.take(); + let selection = d.game_state.selection_mut().take(); for (position, orbit) in (&d.positions, &d.orbits).join() { let r = orbit.max() * orbit.max(); if (position.pos() - pos).length_sqr() <= r { - let player_id = d.game_state.player_id; - let player = player!(&d, player_id); - let player_index = player.index(); + let player_index = d.game_state.player_index(); let fleet_id = continue_if_none!(orbit.fleets().get(player_index)); let fleet_id = *continue_if_none!(fleet_id); - d.game_state.selection = match selection { + *d.game_state.selection_mut() = match selection { Some(s) if s.fleet == fleet_id => { self.is_new_selection = false; diff --git a/space-crush-app/src/render/mod.rs b/space-crush-app/src/render/mod.rs index 4e95fbc..a3ed164 100644 --- a/space-crush-app/src/render/mod.rs +++ b/space-crush-app/src/render/mod.rs @@ -1,10 +1,12 @@ mod asteroids; +mod fleet_move; mod fleet_select; mod init; mod planets; mod ships; pub use asteroids::Asteroids; +pub use fleet_move::FleetMove; pub use fleet_select::FleetSelect; pub use init::Init; pub use planets::Planets; diff --git a/space-crush-app/src/resources/game_state.rs b/space-crush-app/src/resources/game_state.rs index 86073b9..9511aef 100644 --- a/space-crush-app/src/resources/game_state.rs +++ b/space-crush-app/src/resources/game_state.rs @@ -1,11 +1,12 @@ #![allow(dead_code)] -use space_crush_common::components::ShipCount; -use specs::Entity; +use space_crush_common::components::{Player, ShipCount}; +use specs::{Entity, ReadStorage, World}; pub struct GameState { - pub player_id: Entity, - pub selection: Option, + player_id: Entity, + player_index: usize, + selection: Option, } pub struct Selection { @@ -14,10 +15,33 @@ pub struct Selection { } impl GameState { - pub fn new(player_id: Entity) -> Self { + pub fn new(world: &mut World, player_id: Entity) -> Self { + let player_index = world + .system_data::>() + .get(player_id) + .unwrap() + .index(); + Self { player_id, + player_index, selection: None, } } + + pub fn player_id(&self) -> Entity { + self.player_id + } + + pub fn player_index(&self) -> usize { + self.player_index + } + + pub fn selection(&self) -> &Option { + &self.selection + } + + pub(crate) fn selection_mut(&mut self) -> &mut Option { + &mut self.selection + } } diff --git a/space-crush-app/src/systems/fleet_control.rs b/space-crush-app/src/systems/fleet_control.rs deleted file mode 100644 index f9254f3..0000000 --- a/space-crush-app/src/systems/fleet_control.rs +++ /dev/null @@ -1,116 +0,0 @@ -use shrev::{EventChannel, ReaderId}; -use space_crush_common::{ - components::{Fleet, FleetOwned, Orbit, Player, Position, Ship, ShipType}, - continue_if_none, - misc::WorldHelper, -}; -use specs::{prelude::*, Entity, ReadStorage, System, World, WriteStorage}; - -use crate::{ - misc::MouseEvent, - resources::{Camera, Config, GameState, InputState}, - Error, -}; - -pub struct FleetControl { - mouse_event_id: ReaderId, - target_fleet: Option, -} - -#[derive(SystemData)] -pub struct FleetControlData<'a> { - game_state: WriteExpect<'a, GameState>, - mouse_events: ReadExpect<'a, EventChannel>, - input_state: ReadExpect<'a, InputState>, - camera: ReadExpect<'a, Camera>, - config: ReadExpect<'a, Config>, - - ships: ReadStorage<'a, Ship>, - fleet_owned: WriteStorage<'a, FleetOwned>, - positions: ReadStorage<'a, Position>, - players: ReadStorage<'a, Player>, - orbits: ReadStorage<'a, Orbit>, - fleets: ReadStorage<'a, Fleet>, -} - -impl FleetControl { - pub fn new(world: &mut World) -> Result { - let mouse_event_id = world.register_event_reader::()?; - let target_fleet = None; - - Ok(Self { - mouse_event_id, - target_fleet, - }) - } -} - -impl<'a> System<'a> for FleetControl { - type SystemData = FleetControlData<'a>; - - fn run(&mut self, data: Self::SystemData) { - let FleetControlData { - mut game_state, - mouse_events, - input_state, - camera, - config, - ships, - mut fleet_owned, - positions, - players, - orbits, - fleets, - } = data; - - let events = mouse_events.read(&mut self.mouse_event_id); - for event in events { - match event { - MouseEvent::ButtonDown(button) if button == &config.input.fleet_move_button => { - let pos = camera.view_to_world(input_state.mouse_pos); - for (position, orbit) in (&positions, &orbits).join() { - let r = orbit.max() * orbit.max(); - if (position.pos() - pos).length_sqr() <= r { - let player_id = game_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; - } - } - } - #[allow(unused_variables)] // TODO - MouseEvent::ButtonUp(button) if button == &config.input.fleet_move_button => { - let selection = game_state.selection.take(); - let target_fleet = continue_if_none!(self.target_fleet.take()); - let mut selection = continue_if_none!(selection); - let fleet = continue_if_none!(fleets.get(selection.fleet)); - for (ship, fleet_owned, _) in (&ships, &mut fleet_owned, fleet.owned()).join() { - match ship.type_() { - ShipType::Fighter if selection.count.fighter > 0 => { - // TODO fleet_owned.set_owner(target_fleet); - selection.count.fighter -= 1; - } - ShipType::Bomber if selection.count.bomber > 0 => { - // TODO fleet_owned.set_owner(target_fleet); - selection.count.bomber -= 1; - } - ShipType::Transporter if selection.count.transporter > 0 => { - // TODO fleet_owned.set_owner(target_fleet); - selection.count.transporter -= 1; - } - _ => (), - } - } - } - MouseEvent::Move(_, _) => { - self.target_fleet = None; - } - _ => (), - } - } - } -} diff --git a/space-crush-app/src/systems/mod.rs b/space-crush-app/src/systems/mod.rs index 4e959f6..1335ceb 100644 --- a/space-crush-app/src/systems/mod.rs +++ b/space-crush-app/src/systems/mod.rs @@ -1,5 +1,3 @@ -mod fleet_control; mod input_state_update; -pub use fleet_control::FleetControl; pub use input_state_update::InputStateUpdate; diff --git a/space-crush-common/src/components/orbit.rs b/space-crush-common/src/components/orbit.rs index bd23338..5c91936 100644 --- a/space-crush-common/src/components/orbit.rs +++ b/space-crush-common/src/components/orbit.rs @@ -64,6 +64,18 @@ impl Orbit { pub(crate) fn fleets_mut(&mut self) -> &mut Fleets { &mut self.fleets } + + #[inline] + pub fn fleet(&self, index: usize) -> Option { + self.fleets.get(index).cloned().flatten() + } + + #[inline] + pub(crate) fn fleet_mut(&mut self, index: usize) -> &mut Option { + self.fleets.resize_with(index + 1, Default::default); + + &mut self.fleets[index] + } } impl Component for Orbit { @@ -113,6 +125,10 @@ impl Owned { pub fn owner(&self) -> Entity { self.owner } + + pub(crate) fn set_owner(&mut self, value: Entity) { + self.owner = value; + } } impl Component for Owned { diff --git a/space-crush-common/src/components/ship.rs b/space-crush-common/src/components/ship.rs index 94af0d7..285303f 100644 --- a/space-crush-common/src/components/ship.rs +++ b/space-crush-common/src/components/ship.rs @@ -123,6 +123,10 @@ impl Count { transporter: min(self.transporter, other.transporter), } } + + pub fn is_all(&self) -> bool { + self.fighter == usize::MAX && self.bomber == usize::MAX && self.transporter == usize::MAX + } } impl Index for Count { diff --git a/space-crush-common/src/dispatcher.rs b/space-crush-common/src/dispatcher.rs index f1b5f7f..ea9f439 100644 --- a/space-crush-common/src/dispatcher.rs +++ b/space-crush-common/src/dispatcher.rs @@ -3,7 +3,8 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; use crate::{ components::Player, resources::Global, - systems::{FleetOwnedUpdate, Movement, OrbitOwnedUpdate, Process, Ships}, + systems::{FleetControl, FleetOwnedUpdate, Movement, OrbitOwnedUpdate, Process, Ships}, + Error, }; pub struct Dispatcher<'a, 'b> { @@ -11,7 +12,7 @@ pub struct Dispatcher<'a, 'b> { } impl<'a, 'b> Dispatcher<'a, 'b> { - pub fn new(world: &mut World) -> Self { + pub fn new(world: &mut World) -> Result { world.insert(Global::default()); world.register::(); @@ -20,12 +21,13 @@ impl<'a, 'b> Dispatcher<'a, 'b> { .with(Process::default(), "process", &[]) .with(Movement::default(), "movement", &[]) .with(Ships::new(world), "ships", &[]) + .with(FleetControl::new(world)?, "fleet_control", &[]) .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) .with(OrbitOwnedUpdate::new(world), "orbit_owned_update", &[]) .build(); dispatcher.setup(world); - Self { dispatcher } + Ok(Self { dispatcher }) } pub fn process(&mut self, world: &World) { diff --git a/space-crush-common/src/systems/fleet_control.rs b/space-crush-common/src/systems/fleet_control.rs new file mode 100644 index 0000000..ea8c923 --- /dev/null +++ b/space-crush-common/src/systems/fleet_control.rs @@ -0,0 +1,146 @@ +use shrev::{EventChannel, ReaderId}; +use specs::{ + prelude::*, saveload::MarkedBuilder, Entities, Entity, LazyUpdate, ReadExpect, ReadStorage, + System, World, WriteStorage, +}; + +use crate::{ + components::{ + Fleet, FleetOwned, Orbit, OrbitOwned, Player, PlayerOwned, Ship, ShipCount, ShipType, + }, + continue_if_none, + misc::{LogResult, PersistWorld, Persistence, WorldHelper}, + Error, +}; + +pub struct FleetControl { + fleet_control_event_id: ReaderId, +} + +#[derive(Debug)] +pub enum FleetControlEvent { + Move(MoveArgs), +} + +#[derive(Debug)] +pub struct MoveArgs { + player: Entity, + target: Entity, + fleet: Entity, + count: ShipCount, +} + +#[derive(SystemData)] +pub struct FleetControlData<'a> { + lazy: Read<'a, LazyUpdate>, + entities: Entities<'a>, + fleet_control_events: ReadExpect<'a, EventChannel>, + player_owned: WriteStorage<'a, PlayerOwned>, + orbit_owned: WriteStorage<'a, OrbitOwned>, + fleet_owned: WriteStorage<'a, FleetOwned>, + orbits: WriteStorage<'a, Orbit>, + fleets: WriteStorage<'a, Fleet>, + players: ReadStorage<'a, Player>, + ships: ReadStorage<'a, Ship>, +} + +impl FleetControl { + pub fn new(world: &mut World) -> Result { + world.insert(EventChannel::::default()); + + let fleet_control_event_id = world.register_event_reader::()?; + + Ok(Self { + fleet_control_event_id, + }) + } +} + +impl<'a> System<'a> for FleetControl { + type SystemData = FleetControlData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let FleetControlData { + lazy, + entities, + fleet_control_events, + mut player_owned, + mut orbit_owned, + mut fleet_owned, + mut orbits, + mut fleets, + players, + ships, + } = data; + + let events = fleet_control_events.read(&mut self.fleet_control_event_id); + for event in events { + match event { + FleetControlEvent::Move(args) if args.count.is_all() => { + let orbit_owned = continue_if_none!(orbit_owned.get_mut(args.fleet)); + orbit_owned.set_owner(args.target); + } + FleetControlEvent::Move(args) => { + let target_orbit = continue_if_none!(orbits.get_mut(args.target)); + let player = continue_if_none!(players.get(args.player)); + let mut count = args.count; + + let target_fleet = match target_orbit.fleet_mut(player.index()) { + Some(fleet) => *fleet, + f @ None => { + let fleet = lazy + .create_entity(&entities) + .marked::<::Marker>() + .build(); + + player_owned + .insert(fleet, PlayerOwned::new(args.player)) + .error("Unable to insert component: PlayerOwned"); + orbit_owned + .insert(fleet, OrbitOwned::new(args.target)) + .error("Unable to insert component: OrbitOwned"); + fleets + .insert(fleet, Fleet::default()) + .error("Unable to insert component: Fleet"); + + *f = Some(fleet); + + fleet + } + }; + + let source_fleet = continue_if_none!(fleets.get(args.fleet)); + let data = (&mut fleet_owned, &ships, source_fleet.owned()); + for (fleet_owned, ship, _) in data.join() { + match ship.type_() { + ShipType::Fighter if count.fighter > 0 => { + count.fighter -= 1; + fleet_owned.set_owner(target_fleet); + } + ShipType::Bomber if count.bomber > 0 => { + count.bomber -= 1; + fleet_owned.set_owner(target_fleet); + } + ShipType::Transporter if count.transporter > 0 => { + count.transporter -= 1; + fleet_owned.set_owner(target_fleet); + } + _ => (), + } + } + } + } + } + } +} + +impl FleetControlEvent { + pub fn move_(player: Entity, target: Entity, fleet: Entity, count: ShipCount) -> Self { + Self::Move(MoveArgs { + player, + target, + fleet, + count, + }) + } +} diff --git a/space-crush-common/src/systems/fleet_owned_update.rs b/space-crush-common/src/systems/fleet_owned_update.rs index db64491..065ec7c 100644 --- a/space-crush-common/src/systems/fleet_owned_update.rs +++ b/space-crush-common/src/systems/fleet_owned_update.rs @@ -93,8 +93,13 @@ impl<'a> System<'a> for FleetOwnedUpdate { } } + /* find new fleet ids */ + for (fleet_owned, _) in (&fleet_owned, &self.fleet_owned_ids).join() { + self.fleet_ids.add(fleet_owned.owner().id()); + } + /* update fleets */ - for (fleet_id, fleet_info, _) in (&entities, &mut fleets, &self.fleet_ids).join() { + for (fleet_id, fleet, _) 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() { @@ -105,17 +110,17 @@ impl<'a> System<'a> for FleetOwnedUpdate { }; if old_match && !new_match { - let count = fleet_info.count_mut(); + let count = fleet.count_mut(); let count = &mut count[ship.type_()]; *count = count.saturating_sub(1); - fleet_info.owned_mut().remove(ship_id.id()); + fleet.owned_mut().remove(ship_id.id()); } else if !old_match && new_match { - let count = fleet_info.count_mut(); + let count = fleet.count_mut(); let count = &mut count[ship.type_()]; *count += 1; - fleet_info.owned_mut().add(ship_id.id()); + fleet.owned_mut().add(ship_id.id()); } } } diff --git a/space-crush-common/src/systems/mod.rs b/space-crush-common/src/systems/mod.rs index c6d3073..9bb51b5 100644 --- a/space-crush-common/src/systems/mod.rs +++ b/space-crush-common/src/systems/mod.rs @@ -1,9 +1,11 @@ +mod fleet_control; mod fleet_owned_update; mod movement; mod orbit_owned_update; mod process; mod ships; +pub use fleet_control::{FleetControl, FleetControlEvent}; pub use fleet_owned_update::FleetOwnedUpdate; pub use movement::Movement; pub use orbit_owned_update::OrbitOwnedUpdate; diff --git a/space-crush-common/src/systems/orbit_owned_update.rs b/space-crush-common/src/systems/orbit_owned_update.rs index 8bd075b..b14610f 100644 --- a/space-crush-common/src/systems/orbit_owned_update.rs +++ b/space-crush-common/src/systems/orbit_owned_update.rs @@ -96,6 +96,11 @@ impl<'a> System<'a> for OrbitOwnedUpdate { } } + /* find new orbit ids */ + for (orbit_owned, _) in (&orbit_owned, &self.orbit_owned_ids).join() { + self.orbit_ids.add(orbit_owned.owner().id()); + } + /* update orbits */ for (orbit_id, orbit, _) in (&entities, &mut orbits, &self.orbit_ids).join() { let data = ( @@ -121,6 +126,7 @@ impl<'a> System<'a> for OrbitOwnedUpdate { orbit .fleets_mut() .resize_with(player_id + 1, Default::default); + // TODO: merge fleets if player already have a fleet on this orbit orbit.fleets_mut()[player_id] = Some(fleet_id); } } diff --git a/space-crush-common/src/systems/ships.rs b/space-crush-common/src/systems/ships.rs index 3f36d62..f64103c 100644 --- a/space-crush-common/src/systems/ships.rs +++ b/space-crush-common/src/systems/ships.rs @@ -181,7 +181,7 @@ impl Processor<'_> { let orbit_max = orbit.max(); let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random::(); - let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X) + add * dir / orbit_max; + let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X) + (add * dir / orbit_max); let radius = orbit_min + (orbit_max - orbit_min) * random::(); Vector2f::new(