| @@ -1,8 +1,9 @@ | |||||
| use space_crush_common::components::ShipCount; | use space_crush_common::components::ShipCount; | ||||
| use specs::{Component, HashMapStorage}; | |||||
| use specs::{hibitset::BitSet, Component, HashMapStorage}; | |||||
| #[derive(Default, Debug)] | #[derive(Default, Debug)] | ||||
| pub struct FleetInfo { | pub struct FleetInfo { | ||||
| pub owned: BitSet, | |||||
| pub count: ShipCount, | pub count: ShipCount, | ||||
| } | } | ||||
| @@ -15,7 +15,7 @@ use debug::{Fleets as DebugFleets, 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, FleetSelect, Init, Planets, Ships}; | ||||
| use resources::{Camera, Config, Geometry, InputState, PlayerState, Uniform}; | use resources::{Camera, Config, Geometry, InputState, PlayerState, Uniform}; | ||||
| use systems::{FleetInfoUpdate, StateUpdate}; | |||||
| use systems::{FleetControl, FleetInfoUpdate, StateUpdate}; | |||||
| pub struct App<'a, 'b> { | pub struct App<'a, 'b> { | ||||
| is_running: bool, | is_running: bool, | ||||
| @@ -48,6 +48,7 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(StateUpdate::new(world)?, "state_update", &[]) | .with(StateUpdate::new(world)?, "state_update", &[]) | ||||
| .with(FleetInfoUpdate::new(world), "fleet_info_update", &[]) | .with(FleetInfoUpdate::new(world), "fleet_info_update", &[]) | ||||
| .with(FleetControl::new(world)?, "fleet_control", &[]) | |||||
| .with_thread_local(Init::new(world)?) | .with_thread_local(Init::new(world)?) | ||||
| .with_thread_local(Planets::new(world)?) | .with_thread_local(Planets::new(world)?) | ||||
| .with_thread_local(Asteroids::new(world)?) | .with_thread_local(Asteroids::new(world)?) | ||||
| @@ -79,69 +79,69 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| ) | ) | ||||
| .unwrap(); | .unwrap(); | ||||
| world | |||||
| let planet = world | |||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(PlayerOwned { owner: player_id }) | |||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::new(1000.0, -1000.0), | |||||
| shape: Shape::Circle(100.0), | |||||
| pos: Vector2f::default(), | |||||
| shape: Shape::Circle(250.0), | |||||
| }) | }) | ||||
| .with(Fleet { | .with(Fleet { | ||||
| orbit_min: 125.0, | |||||
| orbit_max: 175.0, | |||||
| orbit_min: 325.0, | |||||
| orbit_max: 425.0, | |||||
| }) | }) | ||||
| .with(FleetInfo::default()) | .with(FleetInfo::default()) | ||||
| .with(Asteroid { | |||||
| type_: AsteroidType::Metal, | |||||
| }) | |||||
| .with(Planet {}) | |||||
| .build(); | .build(); | ||||
| world | world | ||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::new(1000.0, 1000.0), | |||||
| shape: Shape::Circle(100.0), | |||||
| pos: Vector2f::new(2000.0, 0.0), | |||||
| shape: Shape::Circle(250.0), | |||||
| }) | }) | ||||
| .with(Fleet { | .with(Fleet { | ||||
| orbit_min: 125.0, | |||||
| orbit_max: 175.0, | |||||
| orbit_min: 325.0, | |||||
| orbit_max: 425.0, | |||||
| }) | }) | ||||
| .with(FleetInfo::default()) | .with(FleetInfo::default()) | ||||
| .with(Asteroid { | |||||
| type_: AsteroidType::Crystal, | |||||
| }) | |||||
| .with(Planet {}) | |||||
| .build(); | .build(); | ||||
| let planet = world | |||||
| world | |||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(PlayerOwned { owner: player_id }) | |||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::default(), | |||||
| shape: Shape::Circle(250.0), | |||||
| pos: Vector2f::new(1000.0, -1000.0), | |||||
| shape: Shape::Circle(100.0), | |||||
| }) | }) | ||||
| .with(Fleet { | .with(Fleet { | ||||
| orbit_min: 325.0, | |||||
| orbit_max: 425.0, | |||||
| orbit_min: 125.0, | |||||
| orbit_max: 175.0, | |||||
| }) | }) | ||||
| .with(FleetInfo::default()) | .with(FleetInfo::default()) | ||||
| .with(Planet {}) | |||||
| .with(Asteroid { | |||||
| type_: AsteroidType::Metal, | |||||
| }) | |||||
| .build(); | .build(); | ||||
| world | world | ||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::new(2000.0, 0.0), | |||||
| shape: Shape::Circle(250.0), | |||||
| pos: Vector2f::new(1000.0, 1000.0), | |||||
| shape: Shape::Circle(100.0), | |||||
| }) | }) | ||||
| .with(Fleet { | .with(Fleet { | ||||
| orbit_min: 325.0, | |||||
| orbit_max: 425.0, | |||||
| orbit_min: 125.0, | |||||
| orbit_max: 175.0, | |||||
| }) | }) | ||||
| .with(FleetInfo::default()) | .with(FleetInfo::default()) | ||||
| .with(Planet {}) | |||||
| .with(Asteroid { | |||||
| type_: AsteroidType::Crystal, | |||||
| }) | |||||
| .build(); | .build(); | ||||
| for i in 0..30 { | for i in 0..30 { | ||||
| @@ -46,6 +46,8 @@ pub struct Input { | |||||
| pub camera_move_button: MouseButton, | pub camera_move_button: MouseButton, | ||||
| #[serde(default = "defaults::fleet_select_button")] | #[serde(default = "defaults::fleet_select_button")] | ||||
| pub fleet_select_button: MouseButton, | pub fleet_select_button: MouseButton, | ||||
| #[serde(default = "defaults::fleet_move_button")] | |||||
| pub fleet_move_button: MouseButton, | |||||
| #[serde(default = "defaults::camera_move_speed")] | #[serde(default = "defaults::camera_move_speed")] | ||||
| pub camera_move_speed: f32, | pub camera_move_speed: f32, | ||||
| @@ -95,6 +97,7 @@ impl Default for Input { | |||||
| camera_move_button: defaults::camera_move_button(), | camera_move_button: defaults::camera_move_button(), | ||||
| fleet_select_button: defaults::fleet_select_button(), | fleet_select_button: defaults::fleet_select_button(), | ||||
| fleet_move_button: defaults::fleet_move_button(), | |||||
| camera_move_speed: defaults::camera_move_speed(), | camera_move_speed: defaults::camera_move_speed(), | ||||
| camera_zoom_speed: defaults::camera_zoom_speed(), | camera_zoom_speed: defaults::camera_zoom_speed(), | ||||
| @@ -145,6 +148,10 @@ mod defaults { | |||||
| MouseButton::Left | MouseButton::Left | ||||
| } | } | ||||
| pub fn fleet_move_button() -> MouseButton { | |||||
| MouseButton::Right | |||||
| } | |||||
| pub fn camera_move_speed() -> f32 { | pub fn camera_move_speed() -> f32 { | ||||
| 500.0 | 500.0 | ||||
| } | } | ||||
| @@ -0,0 +1,113 @@ | |||||
| use shrev::{EventChannel, ReaderId}; | |||||
| use space_crush_common::{ | |||||
| components::{Fleet, FleetOwned, Position, Ship, ShipType}, | |||||
| misc::WorldHelper, | |||||
| return_if_none, | |||||
| }; | |||||
| use specs::{prelude::*, Entities, Entity, ReadStorage, System, World, WriteStorage}; | |||||
| use crate::{ | |||||
| components::FleetInfo, | |||||
| misc::MouseEvent, | |||||
| resources::{Camera, Config, InputState, PlayerState}, | |||||
| Error, | |||||
| }; | |||||
| pub struct FleetControl { | |||||
| mouse_event_id: ReaderId<MouseEvent>, | |||||
| target_fleet: Option<Entity>, | |||||
| } | |||||
| #[derive(SystemData)] | |||||
| pub struct FleetControlData<'a> { | |||||
| player_state: WriteExpect<'a, PlayerState>, | |||||
| mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | |||||
| input_state: ReadExpect<'a, InputState>, | |||||
| camera: ReadExpect<'a, Camera>, | |||||
| config: ReadExpect<'a, Config>, | |||||
| entities: Entities<'a>, | |||||
| ships: ReadStorage<'a, Ship>, | |||||
| fleet_owned: WriteStorage<'a, FleetOwned>, | |||||
| positions: ReadStorage<'a, Position>, | |||||
| fleet_infos: ReadStorage<'a, FleetInfo>, | |||||
| 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 player_state, | |||||
| mouse_events, | |||||
| input_state, | |||||
| camera, | |||||
| config, | |||||
| entities, | |||||
| ships, | |||||
| mut fleet_owned, | |||||
| positions, | |||||
| fleet_infos, | |||||
| 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 (id, position, fleet) in (&entities, &positions, &fleets).join() { | |||||
| let r = fleet.orbit_max * fleet.orbit_max; | |||||
| if (position.pos - pos).length_sqr() <= r { | |||||
| self.target_fleet = Some(id); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| MouseEvent::ButtonUp(button) if button == &config.input.fleet_move_button => { | |||||
| let selection = player_state.selection.take(); | |||||
| let target_fleet = return_if_none!(self.target_fleet.take()); | |||||
| let mut selection = return_if_none!(selection); | |||||
| let fleet_info = return_if_none!(fleet_infos.get(selection.fleet)); | |||||
| for (ship, fleet_owned, _) in | |||||
| (&ships, &mut fleet_owned, &fleet_info.owned).join() | |||||
| { | |||||
| match &ship.type_ { | |||||
| ShipType::Fighter if selection.count.fighter > 0 => { | |||||
| fleet_owned.owner = target_fleet; | |||||
| selection.count.fighter -= 1; | |||||
| } | |||||
| ShipType::Bomber if selection.count.bomber > 0 => { | |||||
| fleet_owned.owner = target_fleet; | |||||
| selection.count.bomber -= 1; | |||||
| } | |||||
| ShipType::Transporter if selection.count.transporter > 0 => { | |||||
| fleet_owned.owner = target_fleet; | |||||
| selection.count.transporter -= 1; | |||||
| } | |||||
| _ => (), | |||||
| } | |||||
| } | |||||
| } | |||||
| MouseEvent::Move(_, _) => { | |||||
| self.target_fleet = None; | |||||
| } | |||||
| _ => (), | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -155,8 +155,10 @@ impl<'a> System<'a> for FleetInfoUpdate { | |||||
| }; | }; | ||||
| if old_match && !new_match { | if old_match && !new_match { | ||||
| fleet_info.owned.remove(ship_id.id()); | |||||
| fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); | fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); | ||||
| } else if !old_match && new_match { | } else if !old_match && new_match { | ||||
| fleet_info.owned.add(ship_id.id()); | |||||
| fleet_info.count[ship.type_] += 1; | fleet_info.count[ship.type_] += 1; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,5 +1,7 @@ | |||||
| mod fleet_control; | |||||
| mod fleet_info_update; | mod fleet_info_update; | ||||
| mod state_update; | mod state_update; | ||||
| pub use fleet_control::FleetControl; | |||||
| pub use fleet_info_update::FleetInfoUpdate; | pub use fleet_info_update::FleetInfoUpdate; | ||||
| pub use state_update::StateUpdate; | pub use state_update::StateUpdate; | ||||