@@ -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( | ||||