Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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