|
- use glc::{
- math::{linear_step, sqr},
- matrix::Matrix3f,
- vector::{Angle, Vector2f},
- };
- use shrev::EventChannel;
- use specs::{prelude::*, Entities, Read, ReadStorage, System, WriteStorage};
-
- use crate::{
- components::{
- FleetMoving, FleetOwned, MeetingPoint, Obstacle, Player, PlayerOwned, Position, Shape,
- Ship, ShipMoving, ShipObstacle,
- },
- constants::SHIP_ORBIT_DISTANCE_MAX,
- misc::StorageHelperMut,
- resources::Global,
- systems::ShipControlEvent,
- };
-
- #[derive(Default)]
- pub struct ShipsMoving {}
-
- #[derive(SystemData)]
- pub struct ShipsData<'a> {
- global: Read<'a, Global>,
- entities: Entities<'a>,
-
- positions: ReadStorage<'a, Position>,
- ships: WriteStorage<'a, Ship>,
- ships_moving: WriteStorage<'a, ShipMoving>,
- ship_control: WriteExpect<'a, EventChannel<ShipControlEvent>>,
-
- fleet_owned: ReadStorage<'a, FleetOwned>,
- fleet_moving: ReadStorage<'a, FleetMoving>,
-
- meeting_points: ReadStorage<'a, MeetingPoint>,
- obstacles: ReadStorage<'a, Obstacle>,
- shapes: ReadStorage<'a, Shape>,
-
- players: ReadStorage<'a, Player>,
- player_owned: ReadStorage<'a, PlayerOwned>,
- }
-
- struct Processor<'a> {
- delta: f32,
- entities: Entities<'a>,
- positions: ReadStorage<'a, Position>,
- shapes: ReadStorage<'a, Shape>,
- obstacles: ReadStorage<'a, Obstacle>,
- meeting_points: ReadStorage<'a, MeetingPoint>,
- fleet_moving: ReadStorage<'a, FleetMoving>,
- players: ReadStorage<'a, Player>,
- }
-
- impl<'a> System<'a> for ShipsMoving {
- type SystemData = ShipsData<'a>;
-
- fn run(&mut self, data: Self::SystemData) {
- let ShipsData {
- global,
- entities,
-
- positions,
- mut ships,
- mut ships_moving,
- mut ship_control,
-
- fleet_owned,
- fleet_moving,
-
- meeting_points,
- obstacles,
- shapes,
-
- players,
- player_owned,
- } = data;
-
- /* update ships */
- let processor = Processor {
- delta: global.delta * global.world_speed,
- entities,
- positions,
- shapes,
- obstacles,
- meeting_points,
- fleet_moving,
- players,
- };
-
- ships.set_event_emission(false);
-
- let data = (
- &processor.entities,
- &processor.positions,
- &mut ships,
- &mut ships_moving,
- &fleet_owned,
- &player_owned,
- );
-
- for (id, position, ship, ship_moving, fleet_owned, player_owned) in data.join() {
- let target =
- processor.progress_ship(position, ship, ship_moving, fleet_owned, player_owned);
-
- if let Some(target) = target {
- let event = ShipControlEvent::SetOrbiting { ship: id, target };
- ship_control.single_write(event);
- }
- }
-
- ships.set_event_emission(true);
- }
- }
-
- impl Processor<'_> {
- fn progress_ship(
- &self,
- position: &Position,
- ship: &mut Ship,
- ship_moving: &mut ShipMoving,
- fleet_owned: &FleetOwned,
- player_owned: &PlayerOwned,
- ) -> Option<Entity> {
- let player_id = player_owned.owner();
- let player = self.players.get(player_id).unwrap();
-
- let fleet_id = fleet_owned.owner();
- let fleet_moving = self.fleet_moving.get(fleet_id).unwrap();
-
- let target_id = fleet_moving.target();
- let target_orbit = self.meeting_points.get(target_id).unwrap().max();
- let target_pos = self.positions.get(target_id).unwrap().get();
-
- let ship_pos = position.get();
- let ship_data = player.ship_data(ship.type_());
-
- let meeting_point_to_ship = ship_pos - target_pos;
- let ship_to_target = target_pos - ship_pos;
- let r_ship = meeting_point_to_ship.length_sqr();
-
- if r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * target_orbit) {
- return Some(target_id);
- }
-
- /* check if obstacle is still valid */
- if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() {
- let obstacle_id = *obstacle_id;
- let obstacle_pos = self.positions.get(obstacle_id).unwrap().get();
- let obstacle_meeting_point = self.meeting_points.get(obstacle_id).unwrap();
- let ship_to_obstacle = obstacle_pos - ship_pos;
-
- let obstacle_angle = ship_to_target
- .angle2(&ship_to_obstacle)
- .into_deg()
- .into_inner()
- .abs();
-
- let meeting_point_sqr = obstacle_meeting_point.max() * obstacle_meeting_point.max();
- if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > meeting_point_sqr)
- || obstacle_angle > 170.0
- {
- *ship_moving.obstacle_mut() = ShipObstacle::Search;
- }
- }
-
- /* find obstacle */
- if ship_moving.obstacle() == &ShipObstacle::Search {
- let mut dist_sqr = f32::MAX;
- for (obstacle_id, position, _) in
- (&self.entities, &self.positions, &self.obstacles).join()
- {
- let obstacle_pos = position.get();
- let ship_to_obstacle = obstacle_pos - ship_pos;
-
- if ship_to_target * ship_to_obstacle < 0.0 {
- continue; // obstacle is behind the ship
- }
-
- let len_sqr = ship_to_obstacle.length_sqr();
- if len_sqr < dist_sqr {
- dist_sqr = len_sqr;
- *ship_moving.obstacle_mut() = ShipObstacle::Known(obstacle_id);
- }
- }
-
- if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() {
- if *obstacle_id == fleet_owned.owner() {
- *ship_moving.obstacle_mut() = ShipObstacle::Done;
- }
- }
- }
-
- /* check the obstacle */
- let mut expected_dir = ship_to_target;
- if let ShipObstacle::Known(obstacle) = ship_moving.obstacle() {
- let obstacle_pos = self.positions.get(*obstacle).unwrap();
- let obstacle_shape = self.shapes.get(*obstacle).unwrap();
- let obstacle_meeting_point = self.meeting_points.get(*obstacle).unwrap();
- let ship_to_obstacle = obstacle_pos.get() - ship_pos;
-
- let meeting_point_min = obstacle_meeting_point.min();
- let meeting_point_max = obstacle_meeting_point.max();
-
- let meeting_point = ship_to_obstacle.length();
- if meeting_point < meeting_point_max {
- let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x);
-
- let radius = obstacle_shape.radius();
- let mut adjust_low = linear_step(meeting_point_min, radius, meeting_point);
- let adjust_high =
- 1.0 - linear_step(meeting_point_max, meeting_point_min, meeting_point);
-
- if ship_to_target * tangent < 0.0 {
- tangent = -tangent;
- } else {
- adjust_low = -adjust_low;
- }
-
- let a_low = Angle::Deg(45.0);
- let a_high = tangent.angle2(&ship_to_target);
- let mat = Matrix3f::rotate(a_low * adjust_low + a_high * adjust_high);
- expected_dir = mat.transform(tangent);
- }
- }
-
- /* update ship direction */
- let angle = expected_dir.angle2(ship.dir());
- if angle.into_inner().abs() > 0.0001 {
- let dir = angle.into_inner() / angle.abs().into_inner();
- let rot_speed =
- ship_data.agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner());
-
- *ship.dir_mut() *= Matrix3f::rotate(rot_speed * -dir * self.delta);
- }
-
- None
- }
- }
|