| @@ -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()), | |||
| } | |||
| } | |||
| @@ -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<Vector2f> { | |||
| 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 | |||
| } | |||
| @@ -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; | |||
| @@ -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<Vector2f> { | |||
| 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 | |||
| } | |||
| @@ -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(); | |||
| @@ -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::<WriteStorage<Player>>() | |||
| .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); | |||
| @@ -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<MouseEvent>, | |||
| target_orbit: Option<Entity>, | |||
| } | |||
| #[derive(SystemData)] | |||
| pub struct FleetMoveData<'a> { | |||
| game_state: WriteExpect<'a, GameState>, | |||
| fleet_control: WriteExpect<'a, EventChannel<FleetControlEvent>>, | |||
| mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | |||
| 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<Self, Error> { | |||
| let mouse_event_id = world.register_event_reader::<MouseEvent>()?; | |||
| 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; | |||
| } | |||
| _ => (), | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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<Selection>, | |||
| player_id: Entity, | |||
| player_index: usize, | |||
| selection: Option<Selection>, | |||
| } | |||
| 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::<ReadStorage<Player>>() | |||
| .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<Selection> { | |||
| &self.selection | |||
| } | |||
| pub(crate) fn selection_mut(&mut self) -> &mut Option<Selection> { | |||
| &mut self.selection | |||
| } | |||
| } | |||
| @@ -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<MouseEvent>, | |||
| target_fleet: Option<Entity>, | |||
| } | |||
| #[derive(SystemData)] | |||
| pub struct FleetControlData<'a> { | |||
| game_state: WriteExpect<'a, GameState>, | |||
| mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | |||
| 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<Self, Error> { | |||
| let mouse_event_id = world.register_event_reader::<MouseEvent>()?; | |||
| 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; | |||
| } | |||
| _ => (), | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,5 +1,3 @@ | |||
| mod fleet_control; | |||
| mod input_state_update; | |||
| pub use fleet_control::FleetControl; | |||
| pub use input_state_update::InputStateUpdate; | |||
| @@ -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<Entity> { | |||
| self.fleets.get(index).cloned().flatten() | |||
| } | |||
| #[inline] | |||
| pub(crate) fn fleet_mut(&mut self, index: usize) -> &mut Option<Entity> { | |||
| 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 { | |||
| @@ -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<usize> for Count { | |||
| @@ -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<Self, Error> { | |||
| world.insert(Global::default()); | |||
| world.register::<Player>(); | |||
| @@ -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) { | |||
| @@ -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<FleetControlEvent>, | |||
| } | |||
| #[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<FleetControlEvent>>, | |||
| 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<Self, Error> { | |||
| world.insert(EventChannel::<FleetControlEvent>::default()); | |||
| let fleet_control_event_id = world.register_event_reader::<FleetControlEvent>()?; | |||
| 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::<<PersistWorld as Persistence>::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, | |||
| }) | |||
| } | |||
| } | |||
| @@ -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()); | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -181,7 +181,7 @@ impl Processor<'_> { | |||
| let orbit_max = orbit.max(); | |||
| let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random::<f32>(); | |||
| 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::<f32>(); | |||
| Vector2f::new( | |||