You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 lines
6.1 KiB

  1. use std::cmp::min;
  2. use std::ops::{Index, IndexMut, Mul};
  3. use glc::vector::Vector2f;
  4. use serde::{Deserialize, Serialize};
  5. use specs::{Component, Entity, VecStorage};
  6. use crate::{builder::ShipBuilder, misc::FlaggedStorage};
  7. #[derive(Clone, Debug, Serialize, Deserialize)]
  8. pub struct Ship {
  9. type_: ShipType,
  10. dir: Vector2f,
  11. #[serde(skip)]
  12. obstacle: ShipObstacle,
  13. #[serde(skip)]
  14. target_pos: Vector2f,
  15. #[serde(skip)]
  16. target_dir: Vector2f,
  17. }
  18. #[derive(Copy, Clone, Debug, PartialEq, Eq)]
  19. pub enum ShipObstacle {
  20. Known(Entity),
  21. Search,
  22. Done,
  23. }
  24. #[derive(Copy, Clone, Debug, Default)]
  25. pub struct ShipCount {
  26. pub fighter: usize,
  27. pub bomber: usize,
  28. pub transporter: usize,
  29. }
  30. #[derive(Clone, Default, Debug)]
  31. pub struct ShipsData {
  32. pub fighter: ShipData,
  33. pub bomber: ShipData,
  34. pub transporter: ShipData,
  35. }
  36. #[derive(Clone, Default, Debug)]
  37. pub struct ShipData {
  38. pub speed: f32,
  39. }
  40. #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
  41. pub enum ShipType {
  42. Fighter,
  43. Bomber,
  44. Transporter,
  45. }
  46. /* Ship */
  47. impl Ship {
  48. #[inline]
  49. pub fn builder() -> ShipBuilder {
  50. ShipBuilder::default()
  51. }
  52. #[inline]
  53. pub fn type_(&self) -> ShipType {
  54. self.type_
  55. }
  56. #[inline]
  57. pub fn dir(&self) -> &Vector2f {
  58. &self.dir
  59. }
  60. #[inline]
  61. pub fn target_pos(&self) -> &Vector2f {
  62. &self.target_pos
  63. }
  64. #[inline]
  65. pub fn target_dir(&self) -> &Vector2f {
  66. &self.target_dir
  67. }
  68. #[inline]
  69. pub fn obstacle(&self) -> ShipObstacle {
  70. self.obstacle
  71. }
  72. }
  73. impl Ship {
  74. #[inline]
  75. pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self {
  76. Self {
  77. type_,
  78. dir,
  79. obstacle: Default::default(),
  80. target_pos: Default::default(),
  81. target_dir: Default::default(),
  82. }
  83. }
  84. #[inline]
  85. pub(crate) fn set_target(&mut self, pos: Vector2f, dir: Vector2f) {
  86. self.target_pos = pos;
  87. self.target_dir = dir;
  88. }
  89. #[inline]
  90. pub(crate) fn dir_mut(&mut self) -> &mut Vector2f {
  91. &mut self.dir
  92. }
  93. #[inline]
  94. pub(crate) fn obstacle_mut(&mut self) -> &mut ShipObstacle {
  95. &mut self.obstacle
  96. }
  97. }
  98. impl Component for Ship {
  99. type Storage = FlaggedStorage<Self, VecStorage<Self>>;
  100. }
  101. /* Obstacle */
  102. impl Default for ShipObstacle {
  103. fn default() -> Self {
  104. Self::Search
  105. }
  106. }
  107. /* Count */
  108. impl ShipCount {
  109. pub fn all() -> Self {
  110. Self {
  111. fighter: usize::MAX,
  112. bomber: usize::MAX,
  113. transporter: usize::MAX,
  114. }
  115. }
  116. pub fn none() -> Self {
  117. Self {
  118. fighter: 0,
  119. bomber: 0,
  120. transporter: 0,
  121. }
  122. }
  123. pub fn total(&self) -> usize {
  124. self.fighter
  125. .saturating_add(self.bomber)
  126. .saturating_add(self.transporter)
  127. }
  128. pub fn merge(&self, other: &Self) -> Self {
  129. Self {
  130. fighter: min(self.fighter, other.fighter),
  131. bomber: min(self.bomber, other.bomber),
  132. transporter: min(self.transporter, other.transporter),
  133. }
  134. }
  135. pub fn is_all(&self) -> bool {
  136. self.fighter == usize::MAX && self.bomber == usize::MAX && self.transporter == usize::MAX
  137. }
  138. }
  139. impl Index<usize> for ShipCount {
  140. type Output = usize;
  141. fn index(&self, index: usize) -> &Self::Output {
  142. match index {
  143. 0 => &self.fighter,
  144. 1 => &self.bomber,
  145. 2 => &self.transporter,
  146. x => panic!("Invalid ship count index: {}", x),
  147. }
  148. }
  149. }
  150. impl IndexMut<usize> for ShipCount {
  151. fn index_mut(&mut self, index: usize) -> &mut Self::Output {
  152. match index {
  153. 0 => &mut self.fighter,
  154. 1 => &mut self.bomber,
  155. 2 => &mut self.transporter,
  156. x => panic!("Invalid ship count index: {}", x),
  157. }
  158. }
  159. }
  160. impl Index<ShipType> for ShipCount {
  161. type Output = usize;
  162. fn index(&self, index: ShipType) -> &Self::Output {
  163. match index {
  164. ShipType::Fighter => &self.fighter,
  165. ShipType::Bomber => &self.bomber,
  166. ShipType::Transporter => &self.transporter,
  167. }
  168. }
  169. }
  170. impl IndexMut<ShipType> for ShipCount {
  171. fn index_mut(&mut self, index: ShipType) -> &mut Self::Output {
  172. match index {
  173. ShipType::Fighter => &mut self.fighter,
  174. ShipType::Bomber => &mut self.bomber,
  175. ShipType::Transporter => &mut self.transporter,
  176. }
  177. }
  178. }
  179. impl Mul<f32> for ShipCount {
  180. type Output = ShipCount;
  181. #[allow(unused_assignments)]
  182. fn mul(self, rhs: f32) -> Self::Output {
  183. let expected = self.total() as f32;
  184. let expected = (rhs * expected).ceil() as usize;
  185. let mut fighter = (rhs * self.fighter as f32) as usize;
  186. let mut bomber = (rhs * self.bomber as f32) as usize;
  187. let mut transporter = (rhs * self.transporter as f32) as usize;
  188. let mut actual = fighter.saturating_add(bomber).saturating_add(transporter);
  189. if actual < expected && fighter < self.fighter {
  190. fighter += 1;
  191. actual += 1;
  192. }
  193. if actual < expected && bomber < self.bomber {
  194. bomber += 1;
  195. actual += 1;
  196. }
  197. if actual < expected && transporter < self.transporter {
  198. transporter += 1;
  199. actual += 1;
  200. }
  201. ShipCount {
  202. fighter,
  203. bomber,
  204. transporter,
  205. }
  206. }
  207. }
  208. /* ShipsData */
  209. impl Index<usize> for ShipsData {
  210. type Output = ShipData;
  211. fn index(&self, index: usize) -> &Self::Output {
  212. match index {
  213. 0 => &self.fighter,
  214. 1 => &self.bomber,
  215. 2 => &self.transporter,
  216. x => panic!("Invalid ships data index: {}", x),
  217. }
  218. }
  219. }
  220. impl Index<ShipType> for ShipsData {
  221. type Output = ShipData;
  222. fn index(&self, index: ShipType) -> &Self::Output {
  223. match index {
  224. ShipType::Fighter => &self.fighter,
  225. ShipType::Bomber => &self.bomber,
  226. ShipType::Transporter => &self.transporter,
  227. }
  228. }
  229. }