Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

240 řádky
7.7 KiB

  1. use glc::{
  2. math::{linear_step, sqr},
  3. matrix::Matrix3f,
  4. vector::{Angle, Vector2f},
  5. };
  6. use shrev::EventChannel;
  7. use specs::{prelude::*, Entities, Read, ReadStorage, System, WriteStorage};
  8. use crate::{
  9. components::{
  10. FleetMoving, FleetOwned, MeetingPoint, Obstacle, Player, PlayerOwned, Position, Shape,
  11. Ship, ShipMoving, ShipObstacle,
  12. },
  13. constants::SHIP_ORBIT_DISTANCE_MAX,
  14. misc::StorageHelperMut,
  15. resources::Global,
  16. systems::ShipControlEvent,
  17. };
  18. #[derive(Default)]
  19. pub struct ShipsMoving {}
  20. #[derive(SystemData)]
  21. pub struct ShipsData<'a> {
  22. global: Read<'a, Global>,
  23. entities: Entities<'a>,
  24. positions: ReadStorage<'a, Position>,
  25. ships: WriteStorage<'a, Ship>,
  26. ships_moving: WriteStorage<'a, ShipMoving>,
  27. ship_control: WriteExpect<'a, EventChannel<ShipControlEvent>>,
  28. fleet_owned: ReadStorage<'a, FleetOwned>,
  29. fleet_moving: ReadStorage<'a, FleetMoving>,
  30. meeting_points: ReadStorage<'a, MeetingPoint>,
  31. obstacles: ReadStorage<'a, Obstacle>,
  32. shapes: ReadStorage<'a, Shape>,
  33. players: ReadStorage<'a, Player>,
  34. player_owned: ReadStorage<'a, PlayerOwned>,
  35. }
  36. struct Processor<'a> {
  37. delta: f32,
  38. entities: Entities<'a>,
  39. positions: ReadStorage<'a, Position>,
  40. shapes: ReadStorage<'a, Shape>,
  41. obstacles: ReadStorage<'a, Obstacle>,
  42. meeting_points: ReadStorage<'a, MeetingPoint>,
  43. fleet_moving: ReadStorage<'a, FleetMoving>,
  44. players: ReadStorage<'a, Player>,
  45. }
  46. impl<'a> System<'a> for ShipsMoving {
  47. type SystemData = ShipsData<'a>;
  48. fn run(&mut self, data: Self::SystemData) {
  49. let ShipsData {
  50. global,
  51. entities,
  52. positions,
  53. mut ships,
  54. mut ships_moving,
  55. mut ship_control,
  56. fleet_owned,
  57. fleet_moving,
  58. meeting_points,
  59. obstacles,
  60. shapes,
  61. players,
  62. player_owned,
  63. } = data;
  64. /* update ships */
  65. let processor = Processor {
  66. delta: global.delta * global.world_speed,
  67. entities,
  68. positions,
  69. shapes,
  70. obstacles,
  71. meeting_points,
  72. fleet_moving,
  73. players,
  74. };
  75. ships.set_event_emission(false);
  76. let data = (
  77. &processor.entities,
  78. &processor.positions,
  79. &mut ships,
  80. &mut ships_moving,
  81. &fleet_owned,
  82. &player_owned,
  83. );
  84. for (id, position, ship, ship_moving, fleet_owned, player_owned) in data.join() {
  85. let target =
  86. processor.progress_ship(position, ship, ship_moving, fleet_owned, player_owned);
  87. if let Some(target) = target {
  88. let event = ShipControlEvent::SetOrbiting { ship: id, target };
  89. ship_control.single_write(event);
  90. }
  91. }
  92. ships.set_event_emission(true);
  93. }
  94. }
  95. impl Processor<'_> {
  96. fn progress_ship(
  97. &self,
  98. position: &Position,
  99. ship: &mut Ship,
  100. ship_moving: &mut ShipMoving,
  101. fleet_owned: &FleetOwned,
  102. player_owned: &PlayerOwned,
  103. ) -> Option<Entity> {
  104. let player_id = player_owned.owner();
  105. let player = self.players.get(player_id).unwrap();
  106. let fleet_id = fleet_owned.owner();
  107. let fleet_moving = self.fleet_moving.get(fleet_id).unwrap();
  108. let target_id = fleet_moving.target();
  109. let target_orbit = self.meeting_points.get(target_id).unwrap().max();
  110. let target_pos = self.positions.get(target_id).unwrap().get();
  111. let ship_pos = position.get();
  112. let ship_data = player.ship_data(ship.type_());
  113. let meeting_point_to_ship = ship_pos - target_pos;
  114. let ship_to_target = target_pos - ship_pos;
  115. let r_ship = meeting_point_to_ship.length_sqr();
  116. if r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * target_orbit) {
  117. return Some(target_id);
  118. }
  119. /* check if obstacle is still valid */
  120. if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() {
  121. let obstacle_id = *obstacle_id;
  122. let obstacle_pos = self.positions.get(obstacle_id).unwrap().get();
  123. let obstacle_meeting_point = self.meeting_points.get(obstacle_id).unwrap();
  124. let ship_to_obstacle = obstacle_pos - ship_pos;
  125. let obstacle_angle = ship_to_target
  126. .angle2(&ship_to_obstacle)
  127. .into_deg()
  128. .into_inner()
  129. .abs();
  130. let meeting_point_sqr = obstacle_meeting_point.max() * obstacle_meeting_point.max();
  131. if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > meeting_point_sqr)
  132. || obstacle_angle > 170.0
  133. {
  134. *ship_moving.obstacle_mut() = ShipObstacle::Search;
  135. }
  136. }
  137. /* find obstacle */
  138. if ship_moving.obstacle() == &ShipObstacle::Search {
  139. let mut dist_sqr = f32::MAX;
  140. for (obstacle_id, position, _) in
  141. (&self.entities, &self.positions, &self.obstacles).join()
  142. {
  143. let obstacle_pos = position.get();
  144. let ship_to_obstacle = obstacle_pos - ship_pos;
  145. if ship_to_target * ship_to_obstacle < 0.0 {
  146. continue; // obstacle is behind the ship
  147. }
  148. let len_sqr = ship_to_obstacle.length_sqr();
  149. if len_sqr < dist_sqr {
  150. dist_sqr = len_sqr;
  151. *ship_moving.obstacle_mut() = ShipObstacle::Known(obstacle_id);
  152. }
  153. }
  154. if let ShipObstacle::Known(obstacle_id) = ship_moving.obstacle() {
  155. if *obstacle_id == fleet_owned.owner() {
  156. *ship_moving.obstacle_mut() = ShipObstacle::Done;
  157. }
  158. }
  159. }
  160. /* check the obstacle */
  161. let mut expected_dir = ship_to_target;
  162. if let ShipObstacle::Known(obstacle) = ship_moving.obstacle() {
  163. let obstacle_pos = self.positions.get(*obstacle).unwrap();
  164. let obstacle_shape = self.shapes.get(*obstacle).unwrap();
  165. let obstacle_meeting_point = self.meeting_points.get(*obstacle).unwrap();
  166. let ship_to_obstacle = obstacle_pos.get() - ship_pos;
  167. let meeting_point_min = obstacle_meeting_point.min();
  168. let meeting_point_max = obstacle_meeting_point.max();
  169. let meeting_point = ship_to_obstacle.length();
  170. if meeting_point < meeting_point_max {
  171. let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x);
  172. let radius = obstacle_shape.radius();
  173. let mut adjust_low = linear_step(meeting_point_min, radius, meeting_point);
  174. let adjust_high =
  175. 1.0 - linear_step(meeting_point_max, meeting_point_min, meeting_point);
  176. if ship_to_target * tangent < 0.0 {
  177. tangent = -tangent;
  178. } else {
  179. adjust_low = -adjust_low;
  180. }
  181. let a_low = Angle::Deg(45.0);
  182. let a_high = tangent.angle2(&ship_to_target);
  183. let mat = Matrix3f::rotate(a_low * adjust_low + a_high * adjust_high);
  184. expected_dir = mat.transform(tangent);
  185. }
  186. }
  187. /* update ship direction */
  188. let angle = expected_dir.angle2(ship.dir());
  189. if angle.into_inner().abs() > 0.0001 {
  190. let dir = angle.into_inner() / angle.abs().into_inner();
  191. let rot_speed =
  192. ship_data.agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner());
  193. *ship.dir_mut() *= Matrix3f::rotate(rot_speed * -dir * self.delta);
  194. }
  195. None
  196. }
  197. }