Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

299 rader
10 KiB

  1. use glc::{
  2. math::{linear_step, sqr},
  3. matrix::Matrix3f,
  4. vector::{Angle, Vector2f, Vector3f},
  5. };
  6. use rand::random;
  7. use shrev::ReaderId;
  8. use specs::{
  9. hibitset::BitSet, prelude::*, world::Index, Entities, ParJoin, Read, ReadStorage, System,
  10. WriteStorage,
  11. };
  12. use crate::{
  13. components::{
  14. FleetOwned, MeetingPoint, MeetingPointOwned, Obstacle, Position, Shape, Ship, ShipObstacle,
  15. },
  16. constants::{
  17. SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND,
  18. SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X,
  19. },
  20. misc::{ComponentEvent, StorageHelper, StorageHelperMut},
  21. resources::Global,
  22. return_if_none,
  23. };
  24. pub struct Ships {
  25. need_update: BitSet,
  26. fleet_owned_id: ReaderId<ComponentEvent<FleetOwned>>,
  27. }
  28. #[derive(SystemData)]
  29. pub struct ShipsData<'a> {
  30. global: Read<'a, Global>,
  31. entities: Entities<'a>,
  32. ships: WriteStorage<'a, Ship>,
  33. fleet_owned: ReadStorage<'a, FleetOwned>,
  34. meeting_point_owned: ReadStorage<'a, MeetingPointOwned>,
  35. positions: ReadStorage<'a, Position>,
  36. shapes: ReadStorage<'a, Shape>,
  37. obstacles: ReadStorage<'a, Obstacle>,
  38. meeting_points: ReadStorage<'a, MeetingPoint>,
  39. }
  40. struct Processor<'a> {
  41. need_update: &'a BitSet,
  42. entities: &'a Entities<'a>,
  43. positions: &'a ReadStorage<'a, Position>,
  44. shapes: &'a ReadStorage<'a, Shape>,
  45. obstacles: &'a ReadStorage<'a, Obstacle>,
  46. meeting_points: &'a ReadStorage<'a, MeetingPoint>,
  47. meeting_point_owned: &'a ReadStorage<'a, MeetingPointOwned>,
  48. delta: f32,
  49. }
  50. impl Ships {
  51. pub fn new(world: &mut World) -> Self {
  52. WriteStorage::<FleetOwned>::setup(world);
  53. let need_update = BitSet::new();
  54. let fleet_owned_id = world
  55. .system_data::<WriteStorage<FleetOwned>>()
  56. .register_event_reader();
  57. Self {
  58. need_update,
  59. fleet_owned_id,
  60. }
  61. }
  62. fn progress_events(&mut self, fleet_owned: &ReadStorage<'_, FleetOwned>) {
  63. self.need_update.clear();
  64. let events = fleet_owned.channel().read(&mut self.fleet_owned_id);
  65. for event in events {
  66. let id = match event {
  67. ComponentEvent::Inserted(id, _) => id,
  68. ComponentEvent::Modified(id, _) => id,
  69. ComponentEvent::Removed(id, _) => id,
  70. };
  71. self.need_update.add(*id);
  72. }
  73. }
  74. }
  75. impl<'a> System<'a> for Ships {
  76. type SystemData = ShipsData<'a>;
  77. fn run(&mut self, data: Self::SystemData) {
  78. let ShipsData {
  79. global,
  80. entities,
  81. mut ships,
  82. fleet_owned,
  83. meeting_point_owned,
  84. positions,
  85. shapes,
  86. obstacles,
  87. meeting_points,
  88. } = data;
  89. self.progress_events(&fleet_owned);
  90. /* update ships */
  91. let processor = Processor {
  92. need_update: &self.need_update,
  93. entities: &entities,
  94. positions: &positions,
  95. shapes: &shapes,
  96. obstacles: &obstacles,
  97. meeting_point_owned: &meeting_point_owned,
  98. meeting_points: &meeting_points,
  99. delta: global.delta * global.world_speed,
  100. };
  101. ships.set_event_emission(false);
  102. let data = (positions.mask(), &mut ships, &positions, &fleet_owned);
  103. data.par_join()
  104. .for_each(|(id, ship, position, fleet_owned)| {
  105. processor.progress_ship(id, ship, position, fleet_owned);
  106. });
  107. ships.set_event_emission(true);
  108. }
  109. }
  110. impl Processor<'_> {
  111. fn progress_ship(
  112. &self,
  113. id: Index,
  114. ship: &mut Ship,
  115. position: &Position,
  116. fleet_owned: &FleetOwned,
  117. ) {
  118. let fleet_id = fleet_owned.owner();
  119. let meeting_point_owned = return_if_none!(self.meeting_point_owned.get(fleet_id));
  120. let meeting_point_id = meeting_point_owned.owner();
  121. let meeting_point = return_if_none!(self.meeting_points.get(meeting_point_id));
  122. let meeting_point_pos = return_if_none!(self.positions.get(meeting_point_id)).get();
  123. let ship_pos = position.get();
  124. let target_pos = ship.target_pos();
  125. let target_dir = ship.target_dir();
  126. let meeting_point_to_target = target_pos - meeting_point_pos;
  127. let meeting_point_to_ship = ship_pos - meeting_point_pos;
  128. let mut ship_to_target = target_pos - ship_pos;
  129. let r_ship = meeting_point_to_ship.length_sqr();
  130. let r_target = meeting_point_to_target.length_sqr();
  131. let meeting_point_min = meeting_point.min();
  132. let meeting_point_max = meeting_point.max();
  133. let target_in_meeting_point =
  134. (r_target <= sqr(meeting_point_max)) && (r_target >= sqr(meeting_point_min));
  135. let ship_in_meeting_point = r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * meeting_point_max);
  136. let need_update = self.need_update.contains(id);
  137. let has_target = target_dir.length_sqr() != 0.0;
  138. let passed_target = target_dir * ship_to_target <= 0.0;
  139. /* check and update target posistion */
  140. if need_update
  141. || !has_target
  142. || passed_target
  143. || ship_in_meeting_point != target_in_meeting_point
  144. {
  145. let target_pos = if ship_in_meeting_point && meeting_point_max > 0.0 {
  146. let meeting_point_to_ship_vec3 =
  147. Vector3f::new(meeting_point_to_ship.x, meeting_point_to_ship.y, 0.0);
  148. let ship_dir_vec3 = ship.dir().into_vec3();
  149. let dir = if meeting_point_to_ship_vec3.cross(&ship_dir_vec3).z > 0.0 {
  150. 1.0
  151. } else {
  152. -1.0
  153. };
  154. let meeting_point_min = meeting_point.min();
  155. let meeting_point_max = meeting_point.max();
  156. let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random::<f32>();
  157. let angle = meeting_point_to_ship.angle2(&VECTOR_2F_POS_X)
  158. + (add * dir / meeting_point_max);
  159. let radius =
  160. meeting_point_min + (meeting_point_max - meeting_point_min) * random::<f32>();
  161. Vector2f::new(
  162. meeting_point_pos.x + radius * angle.cos(),
  163. meeting_point_pos.y + radius * angle.sin(),
  164. )
  165. } else {
  166. *ship.obstacle_mut() = ShipObstacle::Search;
  167. *meeting_point_pos
  168. };
  169. ship.set_target(target_pos, (target_pos - ship_pos).normalize());
  170. ship_to_target = target_pos - ship_pos;
  171. }
  172. /* check if obstacle is still valid */
  173. if ship_in_meeting_point {
  174. *ship.obstacle_mut() = ShipObstacle::Done;
  175. } else if let ShipObstacle::Known(obstacle) = ship.obstacle() {
  176. if let Some(position) = self.positions.get(obstacle) {
  177. let obstacle_meeting_point = self.meeting_points.get(obstacle).unwrap();
  178. let obstacle_pos = position.get();
  179. let ship_to_obstacle = obstacle_pos - ship_pos;
  180. let obstacle_angle = ship_to_target
  181. .angle2(&ship_to_obstacle)
  182. .into_deg()
  183. .into_inner()
  184. .abs();
  185. let meeting_point_sqr = obstacle_meeting_point.max() * obstacle_meeting_point.max();
  186. if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > meeting_point_sqr)
  187. || obstacle_angle > 170.0
  188. {
  189. *ship.obstacle_mut() = ShipObstacle::Search;
  190. }
  191. } else {
  192. *ship.obstacle_mut() = ShipObstacle::Search;
  193. }
  194. }
  195. /* find obstacle */
  196. if !ship_in_meeting_point && ship.obstacle() == ShipObstacle::Search {
  197. let mut dist_sqr = f32::MAX;
  198. for (e, position, _) in (self.entities, self.positions, self.obstacles).join() {
  199. let obstacle_pos = position.get();
  200. let ship_to_obstacle = obstacle_pos - ship_pos;
  201. if ship_to_target * ship_to_obstacle < 0.0 {
  202. continue; // obstacle is behind the ship
  203. }
  204. let len_sqr = ship_to_obstacle.length_sqr();
  205. if len_sqr < dist_sqr {
  206. dist_sqr = len_sqr;
  207. *ship.obstacle_mut() = ShipObstacle::Known(e);
  208. }
  209. }
  210. if let ShipObstacle::Known(e) = ship.obstacle() {
  211. if e == fleet_owned.owner() {
  212. *ship.obstacle_mut() = ShipObstacle::Done;
  213. }
  214. }
  215. }
  216. /* check the obstacle */
  217. let mut expected_dir = ship_to_target;
  218. if let ShipObstacle::Known(obstacle) = ship.obstacle() {
  219. let obstacle_pos = self.positions.get(obstacle).unwrap();
  220. let obstacle_shape = self.shapes.get(obstacle).unwrap();
  221. let obstacle_meeting_point = self.meeting_points.get(obstacle).unwrap();
  222. let ship_to_obstacle = obstacle_pos.get() - ship_pos;
  223. let meeting_point_min = obstacle_meeting_point.min();
  224. let meeting_point_max = obstacle_meeting_point.max();
  225. let meeting_point = ship_to_obstacle.length();
  226. if meeting_point < meeting_point_max {
  227. let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x);
  228. let radius = obstacle_shape.radius();
  229. let mut adjust_low = linear_step(meeting_point_min, radius, meeting_point);
  230. let adjust_high =
  231. 1.0 - linear_step(meeting_point_max, meeting_point_min, meeting_point);
  232. if ship_to_target * tangent < 0.0 {
  233. tangent = -tangent;
  234. } else {
  235. adjust_low = -adjust_low;
  236. }
  237. let a_low = Angle::Deg(45.0);
  238. let a_high = tangent.angle2(&ship_to_target);
  239. let mat = Matrix3f::rotate(a_low * adjust_low + a_high * adjust_high);
  240. expected_dir = mat.transform(tangent);
  241. }
  242. }
  243. /* update ship direction */
  244. let angle = expected_dir.angle2(ship.dir());
  245. if angle.into_inner().abs() > 0.0001 {
  246. let dir = angle.into_inner() / angle.abs().into_inner();
  247. let agility = SHIP_ORBIT_AGILITY;
  248. let rot_speed = agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner());
  249. *ship.dir_mut() *= Matrix3f::rotate(rot_speed * -dir * self.delta);
  250. }
  251. }
  252. }