| @@ -58,7 +58,7 @@ where | |||||
| fn _add(self, other: &Self) -> Self { | fn _add(self, other: &Self) -> Self { | ||||
| match self { | match self { | ||||
| Self::Deg(value) => Self::Deg(value + other.into_deg().into_inner()), | 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::{ | use space_crush_common::{ | ||||
| components::{Fleet, Orbit, Player, Position}, | |||||
| constants::SHIP_ORBIT_DISTANCE_MAX, | |||||
| components::{Fleet, Orbit, Position}, | |||||
| continue_if_none, | continue_if_none, | ||||
| misc::LogResult, | misc::LogResult, | ||||
| return_if_none, | |||||
| }; | }; | ||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World, WriteExpect}; | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||||
| use crate::{ | use crate::{ | ||||
| misc::{HorizontalAlign, Text, TextManager, VerticalAlign}, | misc::{HorizontalAlign, Text, TextManager, VerticalAlign}, | ||||
| resources::{Camera, GameState, Geometry}, | |||||
| resources::{Camera, GameState}, | |||||
| Error, | Error, | ||||
| }; | }; | ||||
| @@ -21,10 +18,8 @@ pub struct Fleets { | |||||
| #[derive(SystemData)] | #[derive(SystemData)] | ||||
| pub struct FleetData<'a> { | pub struct FleetData<'a> { | ||||
| game_state: ReadExpect<'a, GameState>, | game_state: ReadExpect<'a, GameState>, | ||||
| geometry: WriteExpect<'a, Geometry>, | |||||
| camera: ReadExpect<'a, Camera>, | camera: ReadExpect<'a, Camera>, | ||||
| positions: ReadStorage<'a, Position>, | positions: ReadStorage<'a, Position>, | ||||
| players: ReadStorage<'a, Player>, | |||||
| orbits: ReadStorage<'a, Orbit>, | orbits: ReadStorage<'a, Orbit>, | ||||
| fleets: ReadStorage<'a, Fleet>, | fleets: ReadStorage<'a, Fleet>, | ||||
| } | } | ||||
| @@ -52,37 +47,19 @@ impl<'a> System<'a> for Fleets { | |||||
| fn run(&mut self, data: Self::SystemData) { | fn run(&mut self, data: Self::SystemData) { | ||||
| let FleetData { | let FleetData { | ||||
| game_state, | game_state, | ||||
| mut geometry, | |||||
| camera, | camera, | ||||
| positions, | positions, | ||||
| players, | |||||
| orbits, | orbits, | ||||
| fleets, | fleets, | ||||
| } = data; | } = data; | ||||
| let player = return_if_none!(players.get(game_state.player_id)); | |||||
| let player_index = game_state.player_index(); | |||||
| gl::enable(gl::BLEND); | gl::enable(gl::BLEND); | ||||
| for (position, orbit) in (&positions, &orbits).join() { | for (position, orbit) in (&positions, &orbits).join() { | ||||
| let fleet_id = continue_if_none!(orbit.fleets().get(player.index())); | |||||
| let fleet_id = continue_if_none!(fleet_id); | |||||
| let fleet = continue_if_none!(fleets.get(*fleet_id)); | |||||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||||
| gl::blend_equation(gl::FUNC_ADD); | |||||
| geometry.render_lines( | |||||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||||
| &create_circle(position.pos(), 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_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); | ||||
| gl::blend_equation(gl::FUNC_SUBTRACT); | gl::blend_equation(gl::FUNC_SUBTRACT); | ||||
| @@ -104,16 +81,3 @@ impl<'a> System<'a> for Fleets { | |||||
| gl::disable(gl::BLEND); | 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 fleets; | ||||
| mod orbits; | |||||
| mod ships; | mod ships; | ||||
| mod summary; | mod summary; | ||||
| pub use fleets::Fleets; | pub use fleets::Fleets; | ||||
| pub use orbits::Orbits; | |||||
| pub use ships::Ships; | pub use ships::Ships; | ||||
| pub use summary::Summary; | 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; | 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 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 resources::{Camera, Config, GameState, Geometry, InputState, Uniform}; | ||||
| use systems::InputStateUpdate; | use systems::InputStateUpdate; | ||||
| @@ -34,7 +36,7 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| let uniform = Uniform::new()?; | let uniform = Uniform::new()?; | ||||
| let geometry = Geometry::new(world)?; | let geometry = Geometry::new(world)?; | ||||
| let input_state = InputState::default(); | 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))?; | 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(Asteroids::new(world)?) | ||||
| .with_thread_local(Ships::new(world)?) | .with_thread_local(Ships::new(world)?) | ||||
| .with_thread_local(FleetSelect::new(world, &text_manager)?) | .with_thread_local(FleetSelect::new(world, &text_manager)?) | ||||
| .with_thread_local(FleetMove::new(world)?) | |||||
| .with_thread_local(DebugShips::default()) | .with_thread_local(DebugShips::default()) | ||||
| .with_thread_local(DebugOrbits::default()) | |||||
| .with_thread_local(DebugFleets::new(&text_manager)?) | .with_thread_local(DebugFleets::new(&text_manager)?) | ||||
| .with_thread_local(DebugSummary::new(&text_manager)?) | .with_thread_local(DebugSummary::new(&text_manager)?) | ||||
| .build(); | .build(); | ||||
| @@ -1,7 +1,9 @@ | |||||
| use glc::vector::Vector4f; | |||||
| use log::{error, info}; | use log::{error, info}; | ||||
| use rand::random; | use rand::random; | ||||
| use space_crush_app::{App, Error}; | use space_crush_app::{App, Error}; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::Player, | |||||
| misc::{init_logger, Vfs}, | misc::{init_logger, Vfs}, | ||||
| Dispatcher, | Dispatcher, | ||||
| }; | }; | ||||
| @@ -33,9 +35,11 @@ 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 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)?; | let mut app = App::new(&mut world, player1)?; | ||||
| create_world(&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) { | 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::{ | use space_crush_common::{ | ||||
| components::{ | components::{ | ||||
| Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Orbit, OrbitOwned, Planet, Player, | |||||
| Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Orbit, OrbitOwned, Planet, | |||||
| PlayerOwned, Position, Ship, ShipType, Velocity, | PlayerOwned, Position, Ship, ShipType, Velocity, | ||||
| }, | }, | ||||
| misc::{PersistWorld, Persistence}, | misc::{PersistWorld, Persistence}, | ||||
| }; | }; | ||||
| use specs::{saveload::MarkedBuilder, WriteStorage}; | |||||
| use specs::saveload::MarkedBuilder; | |||||
| PersistWorld::setup(world); | 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) | let planets = (0..3) | ||||
| .map(|i| { | .map(|i| { | ||||
| let x = 2000.0 * (i as f32 - 1.0); | 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 shrev::{EventChannel, ReaderId}; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{Fleet, Orbit, OrbitOwned, Player, Position, ShipCount}, | |||||
| components::{Fleet, Orbit, OrbitOwned, Position, ShipCount}, | |||||
| constants::VECTOR_2F_POS_X, | constants::VECTOR_2F_POS_X, | ||||
| continue_if_none, | continue_if_none, | ||||
| misc::{LogResult, WorldHelper as _}, | misc::{LogResult, WorldHelper as _}, | ||||
| @@ -82,23 +82,16 @@ pub struct FleetSelectData<'a> { | |||||
| orbit_owned: ReadStorage<'a, OrbitOwned>, | orbit_owned: ReadStorage<'a, OrbitOwned>, | ||||
| positions: ReadStorage<'a, Position>, | positions: ReadStorage<'a, Position>, | ||||
| players: ReadStorage<'a, Player>, | |||||
| orbits: ReadStorage<'a, Orbit>, | orbits: ReadStorage<'a, Orbit>, | ||||
| fleets: ReadStorage<'a, Fleet>, | fleets: ReadStorage<'a, Fleet>, | ||||
| } | } | ||||
| macro_rules! selection { | macro_rules! selection { | ||||
| (&$data:expr) => { | (&$data:expr) => { | ||||
| return_if_none!(&$data.game_state.selection) | |||||
| return_if_none!($data.game_state.selection()) | |||||
| }; | }; | ||||
| (&mut $data:expr) => { | (&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 { | match event { | ||||
| MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => { | MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => { | ||||
| let pos = d.camera.view_to_world(d.input_state.mouse_pos); | let pos = d.camera.view_to_world(d.input_state.mouse_pos); | ||||
| let selection = d.game_state.selection.take(); | |||||
| let selection = d.game_state.selection_mut().take(); | |||||
| for (position, orbit) in (&d.positions, &d.orbits).join() { | for (position, orbit) in (&d.positions, &d.orbits).join() { | ||||
| let r = orbit.max() * orbit.max(); | let r = orbit.max() * orbit.max(); | ||||
| if (position.pos() - pos).length_sqr() <= r { | 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!(orbit.fleets().get(player_index)); | ||||
| let fleet_id = *continue_if_none!(fleet_id); | 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 => { | Some(s) if s.fleet == fleet_id => { | ||||
| self.is_new_selection = false; | self.is_new_selection = false; | ||||
| @@ -1,10 +1,12 @@ | |||||
| mod asteroids; | mod asteroids; | ||||
| mod fleet_move; | |||||
| mod fleet_select; | mod fleet_select; | ||||
| mod init; | mod init; | ||||
| mod planets; | mod planets; | ||||
| mod ships; | mod ships; | ||||
| pub use asteroids::Asteroids; | pub use asteroids::Asteroids; | ||||
| pub use fleet_move::FleetMove; | |||||
| pub use fleet_select::FleetSelect; | pub use fleet_select::FleetSelect; | ||||
| pub use init::Init; | pub use init::Init; | ||||
| pub use planets::Planets; | pub use planets::Planets; | ||||
| @@ -1,11 +1,12 @@ | |||||
| #![allow(dead_code)] | #![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 struct GameState { | ||||
| pub player_id: Entity, | |||||
| pub selection: Option<Selection>, | |||||
| player_id: Entity, | |||||
| player_index: usize, | |||||
| selection: Option<Selection>, | |||||
| } | } | ||||
| pub struct Selection { | pub struct Selection { | ||||
| @@ -14,10 +15,33 @@ pub struct Selection { | |||||
| } | } | ||||
| impl GameState { | 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 { | Self { | ||||
| player_id, | player_id, | ||||
| player_index, | |||||
| selection: None, | 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; | mod input_state_update; | ||||
| pub use fleet_control::FleetControl; | |||||
| pub use input_state_update::InputStateUpdate; | pub use input_state_update::InputStateUpdate; | ||||
| @@ -64,6 +64,18 @@ impl Orbit { | |||||
| pub(crate) fn fleets_mut(&mut self) -> &mut Fleets { | pub(crate) fn fleets_mut(&mut self) -> &mut Fleets { | ||||
| &mut self.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 { | impl Component for Orbit { | ||||
| @@ -113,6 +125,10 @@ impl Owned { | |||||
| pub fn owner(&self) -> Entity { | pub fn owner(&self) -> Entity { | ||||
| self.owner | self.owner | ||||
| } | } | ||||
| pub(crate) fn set_owner(&mut self, value: Entity) { | |||||
| self.owner = value; | |||||
| } | |||||
| } | } | ||||
| impl Component for Owned { | impl Component for Owned { | ||||
| @@ -123,6 +123,10 @@ impl Count { | |||||
| transporter: min(self.transporter, other.transporter), | 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 { | impl Index<usize> for Count { | ||||
| @@ -3,7 +3,8 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||||
| use crate::{ | use crate::{ | ||||
| components::Player, | components::Player, | ||||
| resources::Global, | resources::Global, | ||||
| systems::{FleetOwnedUpdate, Movement, OrbitOwnedUpdate, Process, Ships}, | |||||
| systems::{FleetControl, FleetOwnedUpdate, Movement, OrbitOwnedUpdate, Process, Ships}, | |||||
| Error, | |||||
| }; | }; | ||||
| pub struct Dispatcher<'a, 'b> { | pub struct Dispatcher<'a, 'b> { | ||||
| @@ -11,7 +12,7 @@ pub struct Dispatcher<'a, 'b> { | |||||
| } | } | ||||
| impl<'a, 'b> 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.insert(Global::default()); | ||||
| world.register::<Player>(); | world.register::<Player>(); | ||||
| @@ -20,12 +21,13 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||||
| .with(Process::default(), "process", &[]) | .with(Process::default(), "process", &[]) | ||||
| .with(Movement::default(), "movement", &[]) | .with(Movement::default(), "movement", &[]) | ||||
| .with(Ships::new(world), "ships", &[]) | .with(Ships::new(world), "ships", &[]) | ||||
| .with(FleetControl::new(world)?, "fleet_control", &[]) | |||||
| .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | .with(FleetOwnedUpdate::new(world), "fleet_owned_update", &[]) | ||||
| .with(OrbitOwnedUpdate::new(world), "orbit_owned_update", &[]) | .with(OrbitOwnedUpdate::new(world), "orbit_owned_update", &[]) | ||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| Self { dispatcher } | |||||
| Ok(Self { dispatcher }) | |||||
| } | } | ||||
| pub fn process(&mut self, world: &World) { | 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 */ | /* 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); | let data = (&entities, &ships, &fleet_owned, &self.fleet_owned_ids); | ||||
| for (ship_id, ship, fleet_owned, _) in data.join() { | for (ship_id, ship, fleet_owned, _) in data.join() { | ||||
| @@ -105,17 +110,17 @@ impl<'a> System<'a> for FleetOwnedUpdate { | |||||
| }; | }; | ||||
| if old_match && !new_match { | if old_match && !new_match { | ||||
| let count = fleet_info.count_mut(); | |||||
| let count = fleet.count_mut(); | |||||
| let count = &mut count[ship.type_()]; | let count = &mut count[ship.type_()]; | ||||
| *count = count.saturating_sub(1); | *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 { | } else if !old_match && new_match { | ||||
| let count = fleet_info.count_mut(); | |||||
| let count = fleet.count_mut(); | |||||
| let count = &mut count[ship.type_()]; | let count = &mut count[ship.type_()]; | ||||
| *count += 1; | *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 fleet_owned_update; | ||||
| mod movement; | mod movement; | ||||
| mod orbit_owned_update; | mod orbit_owned_update; | ||||
| mod process; | mod process; | ||||
| mod ships; | mod ships; | ||||
| pub use fleet_control::{FleetControl, FleetControlEvent}; | |||||
| pub use fleet_owned_update::FleetOwnedUpdate; | pub use fleet_owned_update::FleetOwnedUpdate; | ||||
| pub use movement::Movement; | pub use movement::Movement; | ||||
| pub use orbit_owned_update::OrbitOwnedUpdate; | 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 */ | /* update orbits */ | ||||
| for (orbit_id, orbit, _) in (&entities, &mut orbits, &self.orbit_ids).join() { | for (orbit_id, orbit, _) in (&entities, &mut orbits, &self.orbit_ids).join() { | ||||
| let data = ( | let data = ( | ||||
| @@ -121,6 +126,7 @@ impl<'a> System<'a> for OrbitOwnedUpdate { | |||||
| orbit | orbit | ||||
| .fleets_mut() | .fleets_mut() | ||||
| .resize_with(player_id + 1, Default::default); | .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); | orbit.fleets_mut()[player_id] = Some(fleet_id); | ||||
| } | } | ||||
| } | } | ||||
| @@ -181,7 +181,7 @@ impl Processor<'_> { | |||||
| let orbit_max = orbit.max(); | let orbit_max = orbit.max(); | ||||
| let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random::<f32>(); | 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>(); | let radius = orbit_min + (orbit_max - orbit_min) * random::<f32>(); | ||||
| Vector2f::new( | Vector2f::new( | ||||