use std::cmp::min; use std::ops::{Index, IndexMut, Mul}; use glc::vector::Vector2f; use serde::{Deserialize, Serialize}; use specs::{Component, Entity, VecStorage}; use crate::{builder::ShipBuilder, misc::FlaggedStorage}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Ship { type_: ShipType, dir: Vector2f, #[serde(skip)] obstacle: ShipObstacle, #[serde(skip)] target_pos: Vector2f, #[serde(skip)] target_dir: Vector2f, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ShipObstacle { Known(Entity), Search, Done, } #[derive(Copy, Clone, Debug, Default)] pub struct ShipCount { pub fighter: usize, pub bomber: usize, pub transporter: usize, } #[derive(Clone, Default, Debug)] pub struct ShipsData { pub fighter: ShipData, pub bomber: ShipData, pub transporter: ShipData, } #[derive(Clone, Default, Debug)] pub struct ShipData { pub speed: f32, } #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub enum ShipType { Fighter, Bomber, Transporter, } /* Ship */ impl Ship { #[inline] pub fn builder() -> ShipBuilder { ShipBuilder::default() } #[inline] pub fn type_(&self) -> ShipType { self.type_ } #[inline] pub fn dir(&self) -> &Vector2f { &self.dir } #[inline] pub fn target_pos(&self) -> &Vector2f { &self.target_pos } #[inline] pub fn target_dir(&self) -> &Vector2f { &self.target_dir } #[inline] pub fn obstacle(&self) -> ShipObstacle { self.obstacle } } impl Ship { #[inline] pub(crate) fn new(type_: ShipType, dir: Vector2f) -> Self { Self { type_, dir, obstacle: Default::default(), target_pos: Default::default(), target_dir: Default::default(), } } #[inline] pub(crate) fn set_target(&mut self, pos: Vector2f, dir: Vector2f) { self.target_pos = pos; self.target_dir = dir; } #[inline] pub(crate) fn dir_mut(&mut self) -> &mut Vector2f { &mut self.dir } #[inline] pub(crate) fn obstacle_mut(&mut self) -> &mut ShipObstacle { &mut self.obstacle } } impl Component for Ship { type Storage = FlaggedStorage>; } /* Obstacle */ impl Default for ShipObstacle { fn default() -> Self { Self::Search } } /* Count */ impl ShipCount { 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 ShipCount { 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 ShipCount { 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 ShipCount { type Output = usize; fn index(&self, index: ShipType) -> &Self::Output { match index { ShipType::Fighter => &self.fighter, ShipType::Bomber => &self.bomber, ShipType::Transporter => &self.transporter, } } } impl IndexMut for ShipCount { fn index_mut(&mut self, index: ShipType) -> &mut Self::Output { match index { ShipType::Fighter => &mut self.fighter, ShipType::Bomber => &mut self.bomber, ShipType::Transporter => &mut self.transporter, } } } impl Mul for ShipCount { type Output = ShipCount; #[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; } ShipCount { fighter, bomber, transporter, } } } /* ShipsData */ impl Index for ShipsData { type Output = ShipData; fn index(&self, index: usize) -> &Self::Output { match index { 0 => &self.fighter, 1 => &self.bomber, 2 => &self.transporter, x => panic!("Invalid ships data index: {}", x), } } } impl Index for ShipsData { type Output = ShipData; fn index(&self, index: ShipType) -> &Self::Output { match index { ShipType::Fighter => &self.fighter, ShipType::Bomber => &self.bomber, ShipType::Transporter => &self.transporter, } } }