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