| @@ -1,8 +1,9 @@ | |||
| use space_crush_common::components::ShipCount; | |||
| use specs::{Component, HashMapStorage}; | |||
| use specs::{hibitset::BitSet, Component, HashMapStorage}; | |||
| #[derive(Default, Debug)] | |||
| pub struct FleetInfo { | |||
| pub owned: BitSet, | |||
| pub count: ShipCount, | |||
| } | |||
| @@ -15,7 +15,7 @@ use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary} | |||
| use misc::{Events, TextManager, Window}; | |||
| use render::{Asteroids, FleetSelect, Init, Planets, Ships}; | |||
| use resources::{Camera, Config, Geometry, InputState, PlayerState, Uniform}; | |||
| use systems::{FleetInfoUpdate, StateUpdate}; | |||
| use systems::{FleetControl, FleetInfoUpdate, StateUpdate}; | |||
| pub struct App<'a, 'b> { | |||
| is_running: bool, | |||
| @@ -48,6 +48,7 @@ impl<'a, 'b> App<'a, 'b> { | |||
| let mut dispatcher = DispatcherBuilder::new() | |||
| .with(StateUpdate::new(world)?, "state_update", &[]) | |||
| .with(FleetInfoUpdate::new(world), "fleet_info_update", &[]) | |||
| .with(FleetControl::new(world)?, "fleet_control", &[]) | |||
| .with_thread_local(Init::new(world)?) | |||
| .with_thread_local(Planets::new(world)?) | |||
| .with_thread_local(Asteroids::new(world)?) | |||
| @@ -79,69 +79,69 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
| ) | |||
| .unwrap(); | |||
| world | |||
| let planet = world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(PlayerOwned { owner: player_id }) | |||
| .with(Position { | |||
| pos: Vector2f::new(1000.0, -1000.0), | |||
| shape: Shape::Circle(100.0), | |||
| pos: Vector2f::default(), | |||
| shape: Shape::Circle(250.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Metal, | |||
| }) | |||
| .with(Planet {}) | |||
| .build(); | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .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 { | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Crystal, | |||
| }) | |||
| .with(Planet {}) | |||
| .build(); | |||
| let planet = world | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(PlayerOwned { owner: player_id }) | |||
| .with(Position { | |||
| pos: Vector2f::default(), | |||
| shape: Shape::Circle(250.0), | |||
| pos: Vector2f::new(1000.0, -1000.0), | |||
| shape: Shape::Circle(100.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Planet {}) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Metal, | |||
| }) | |||
| .build(); | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .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 { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Planet {}) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Crystal, | |||
| }) | |||
| .build(); | |||
| for i in 0..30 { | |||
| @@ -46,6 +46,8 @@ pub struct Input { | |||
| pub camera_move_button: MouseButton, | |||
| #[serde(default = "defaults::fleet_select_button")] | |||
| pub fleet_select_button: MouseButton, | |||
| #[serde(default = "defaults::fleet_move_button")] | |||
| pub fleet_move_button: MouseButton, | |||
| #[serde(default = "defaults::camera_move_speed")] | |||
| pub camera_move_speed: f32, | |||
| @@ -95,6 +97,7 @@ impl Default for Input { | |||
| camera_move_button: defaults::camera_move_button(), | |||
| fleet_select_button: defaults::fleet_select_button(), | |||
| fleet_move_button: defaults::fleet_move_button(), | |||
| camera_move_speed: defaults::camera_move_speed(), | |||
| camera_zoom_speed: defaults::camera_zoom_speed(), | |||
| @@ -145,6 +148,10 @@ mod defaults { | |||
| MouseButton::Left | |||
| } | |||
| pub fn fleet_move_button() -> MouseButton { | |||
| MouseButton::Right | |||
| } | |||
| pub fn camera_move_speed() -> f32 { | |||
| 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 { | |||
| fleet_info.owned.remove(ship_id.id()); | |||
| fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); | |||
| } else if !old_match && new_match { | |||
| fleet_info.owned.add(ship_id.id()); | |||
| fleet_info.count[ship.type_] += 1; | |||
| } | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| mod fleet_control; | |||
| mod fleet_info_update; | |||
| mod state_update; | |||
| pub use fleet_control::FleetControl; | |||
| pub use fleet_info_update::FleetInfoUpdate; | |||
| pub use state_update::StateUpdate; | |||