use std::cmp::min; use std::ops::{Index, IndexMut, Mul}; use glc::vector::Vector2f; use serde::{Deserialize, Serialize}; use specs::{Component, Entity, VecStorage}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Ship { type_: Type, #[serde(skip)] obstacle: Obstacle, #[serde(skip)] target_pos: Vector2f, #[serde(skip)] target_dir: Vector2f, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Obstacle { Known(Entity), Search, Done, } #[derive(Copy, Clone, Debug, Default)] pub struct Count { pub fighter: usize, pub bomber: usize, pub transporter: usize, } #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub enum Type { Fighter, Bomber, Transporter, } impl Ship { #[inline] pub fn new(type_: Type) -> Self { Self { type_, obstacle: Default::default(), target_pos: Default::default(), target_dir: Default::default(), } } #[inline] pub fn type_(&self) -> Type { self.type_ } #[inline] pub fn target_pos(&self) -> &Vector2f { &self.target_pos } #[inline] pub fn target_dir(&self) -> &Vector2f { &self.target_dir } #[inline] pub(crate) fn set_target(&mut self, pos: Vector2f, dir: Vector2f) { self.target_pos = pos; self.target_dir = dir; } #[inline] pub fn obstacle(&self) -> Obstacle { self.obstacle } #[inline] pub(crate) fn set_obstacle(&mut self, value: Obstacle) { self.obstacle = value; } } impl Component for Ship { type Storage = VecStorage; } impl Default for Obstacle { fn default() -> Self { Self::Search } } impl Count { pub fn all() -> Self { Self { fighter: usize::MAX, bomber: usize::MAX, transporter: usize::MAX, } } pub fn none() -> Self { Self { fighter: 0, bomber: 0, transporter: 0, } } pub fn total(&self) -> usize { self.fighter .saturating_add(self.bomber) .saturating_add(self.transporter) } pub fn merge(&self, other: &Self) -> Self { Self { fighter: min(self.fighter, other.fighter), bomber: min(self.bomber, other.bomber), transporter: min(self.transporter, other.transporter), } } pub fn is_all(&self) -> bool { self.fighter == usize::MAX && self.bomber == usize::MAX && self.transporter == usize::MAX } } impl Index for Count { type Output = usize; fn index(&self, index: usize) -> &Self::Output { match index { 0 => &self.fighter, 1 => &self.bomber, 2 => &self.transporter, x => panic!("Invalid ship count index: {}", x), } } } impl IndexMut for Count { fn index_mut(&mut self, index: usize) -> &mut Self::Output { match index { 0 => &mut self.fighter, 1 => &mut self.bomber, 2 => &mut self.transporter, x => panic!("Invalid ship count index: {}", x), } } } impl Index for Count { type Output = usize; fn index(&self, index: Type) -> &Self::Output { match index { Type::Fighter => &self.fighter, Type::Bomber => &self.bomber, Type::Transporter => &self.transporter, } } } impl IndexMut for Count { fn index_mut(&mut self, index: Type) -> &mut Self::Output { match index { Type::Fighter => &mut self.fighter, Type::Bomber => &mut self.bomber, Type::Transporter => &mut self.transporter, } } } impl Mul for Count { type Output = Count; #[allow(unused_assignments)] fn mul(self, rhs: f32) -> Self::Output { let expected = self.total() as f32; let expected = (rhs * expected).ceil() as usize; let mut fighter = (rhs * self.fighter as f32) as usize; let mut bomber = (rhs * self.bomber as f32) as usize; let mut transporter = (rhs * self.transporter as f32) as usize; let mut actual = fighter.saturating_add(bomber).saturating_add(transporter); if actual < expected && fighter < self.fighter { fighter += 1; actual += 1; } if actual < expected && bomber < self.bomber { bomber += 1; actual += 1; } if actual < expected && transporter < self.transporter { transporter += 1; actual += 1; } Count { fighter, bomber, transporter, } } }