use crate::military::army::Army;
use crate::military::squad::Squad;
use crate::military::squad::size::SquadSize;
use crate::military::unit::stats::haul::Haul;
use crate::military::unit::stats::power::{AttackPower, DefensePower, Power};
use crate::military::unit::stats::speed::Speed;
use crate::military::unit::{UnitId, UnitIdIter};
use crate::ranking::score::Score;
use crate::resources::maintenance::Maintenance;
use crate::world::config::WorldConfig;
use serde::{Deserialize, Serialize};
use std::iter::Sum;
use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
use strum::IntoEnumIterator;
use tap::Pipe;
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct ArmyPersonnel {
archer: Squad,
axeman: Squad,
heavy_cavalry: Squad,
light_cavalry: Squad,
pikeman: Squad,
ram: Squad,
swordsman: Squad,
}
#[bon::bon]
impl ArmyPersonnel {
#[builder]
pub fn new(
#[builder(default, into)] archer: SquadSize,
#[builder(default, into)] axeman: SquadSize,
#[builder(default, into)] heavy_cavalry: SquadSize,
#[builder(default, into)] light_cavalry: SquadSize,
#[builder(default, into)] pikeman: SquadSize,
#[builder(default, into)] ram: SquadSize,
#[builder(default, into)] swordsman: SquadSize,
) -> Self {
use UnitId::*;
Self {
archer: Squad::new(Archer, archer),
axeman: Squad::new(Axeman, axeman),
heavy_cavalry: Squad::new(HeavyCavalry, heavy_cavalry),
light_cavalry: Squad::new(LightCavalry, light_cavalry),
pikeman: Squad::new(Pikeman, pikeman),
ram: Squad::new(Ram, ram),
swordsman: Squad::new(Swordsman, swordsman),
}
}
pub fn to_vec(self) -> Vec<Squad> {
Vec::<Squad>::from(self)
}
#[inline]
pub fn iter(&self) -> ArmyPersonnelIter<'_> {
ArmyPersonnelIter::new(self)
}
pub fn slowest_squad(&self, config: &WorldConfig) -> Option<&Squad> {
self
.iter()
.filter(|squad| squad.size() > 0u32)
.min_by(|a, b| a.speed(config).total_cmp(&b.speed(config)))
}
pub fn speed(&self, config: &WorldConfig) -> Speed {
self
.slowest_squad(config)
.map(|squad| squad.speed(config))
.unwrap_or_default()
}
#[inline]
pub fn haul(&self) -> Haul {
self.iter().sum()
}
#[inline]
pub fn score(&self) -> Score {
self.iter().sum()
}
#[inline]
pub fn maintenance(&self) -> Maintenance {
self.iter().sum()
}
#[inline]
pub fn power(&self) -> Power {
self.iter().sum()
}
#[inline]
pub fn attack(&self) -> AttackPower {
self.iter().sum()
}
#[inline]
pub fn defense(&self) -> DefensePower {
self.iter().sum()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.iter().all(Squad::is_empty)
}
}
macro_rules! impl_army_personnel {
($($unit:ident),+) => {
paste::paste! {
impl ArmyPersonnel {
pub fn splat(size: impl Into<SquadSize>) -> Self {
let size: SquadSize = size.into();
Self::builder()
$(.[<$unit:snake>](size))+
.build()
}
pub fn random() -> Self {
Self::builder()
$(.[<$unit:snake>](SquadSize::random()))+
.build()
}
pub fn squad(&self, id: UnitId) -> &Squad {
match id {
$(UnitId::$unit => &self.[<$unit:snake>],)+
}
}
pub(super) fn squad_mut(&mut self, id: UnitId) -> &mut Squad {
match id {
$(UnitId::$unit => &mut self.[<$unit:snake>],)+
}
}
$(
#[inline]
pub fn [<$unit:snake>](&self) -> &Squad {
&self.[<$unit:snake>]
}
)+
pub fn has_enough_personnel(&self, required: &ArmyPersonnel) -> bool {
$(self.[<$unit:snake>].size() >= required.[<$unit:snake>].size() && )+ true
}
pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
Self::builder()
$(
.[<$unit:snake>](
self
.[<$unit:snake>]
.size()
.checked_sub(rhs.[<$unit:snake>].size())?
)
)+
.build()
.pipe(Some)
}
}
impl From<ArmyPersonnel> for Vec<Squad> {
fn from(personnel: ArmyPersonnel) -> Self {
vec![$(personnel.[<$unit:snake>],)+]
}
}
}
};
}
impl_army_personnel!(
Archer,
Axeman,
HeavyCavalry,
LightCavalry,
Pikeman,
Ram,
Swordsman
);
impl Default for ArmyPersonnel {
fn default() -> Self {
Self::builder().build()
}
}
impl From<Army> for ArmyPersonnel {
fn from(army: Army) -> Self {
army.personnel
}
}
impl From<SquadSize> for ArmyPersonnel {
fn from(size: SquadSize) -> Self {
ArmyPersonnel::splat(size)
}
}
impl FromIterator<Squad> for ArmyPersonnel {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = Squad>,
{
iter
.into_iter()
.fold(Self::default(), |mut personnel, squad| {
personnel += squad;
personnel
})
}
}
impl<'a> IntoIterator for &'a ArmyPersonnel {
type Item = &'a Squad;
type IntoIter = ArmyPersonnelIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> Sum<&'a ArmyPersonnel> for ArmyPersonnel {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a ArmyPersonnel>,
{
iter.fold(ArmyPersonnel::default(), |mut acc, personnel| {
acc += personnel;
acc
})
}
}
impl<'a> Sum<&'a Army> for ArmyPersonnel {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Army>,
{
iter.map(Army::personnel).sum()
}
}
impl Add for ArmyPersonnel {
type Output = ArmyPersonnel;
fn add(mut self, rhs: Self) -> Self::Output {
self += &rhs;
self
}
}
impl Add<Squad> for ArmyPersonnel {
type Output = ArmyPersonnel;
fn add(mut self, rhs: Squad) -> Self::Output {
self += rhs;
self
}
}
impl AddAssign for ArmyPersonnel {
fn add_assign(&mut self, rhs: Self) {
*self += &rhs;
}
}
impl AddAssign<&ArmyPersonnel> for ArmyPersonnel {
fn add_assign(&mut self, rhs: &ArmyPersonnel) {
for squad in rhs {
*self.squad_mut(squad.id()) += squad.size();
}
}
}
impl AddAssign<Squad> for ArmyPersonnel {
fn add_assign(&mut self, rhs: Squad) {
*self.squad_mut(rhs.id()) += rhs;
}
}
impl Sub for ArmyPersonnel {
type Output = ArmyPersonnel;
fn sub(mut self, rhs: Self) -> Self::Output {
self -= rhs;
self
}
}
impl SubAssign for ArmyPersonnel {
fn sub_assign(&mut self, rhs: Self) {
for squad in &rhs {
*self.squad_mut(squad.id()) -= squad.size();
}
}
}
impl SubAssign<Squad> for ArmyPersonnel {
fn sub_assign(&mut self, rhs: Squad) {
*self.squad_mut(rhs.id()) -= rhs;
}
}
impl Mul<f64> for ArmyPersonnel {
type Output = ArmyPersonnel;
fn mul(mut self, rhs: f64) -> Self::Output {
self *= rhs;
self
}
}
impl MulAssign<f64> for ArmyPersonnel {
fn mul_assign(&mut self, rhs: f64) {
for id in UnitId::iter() {
*self.squad_mut(id) *= rhs;
}
}
}
pub struct ArmyPersonnelIter<'a> {
personnel: &'a ArmyPersonnel,
units: UnitIdIter,
}
impl<'a> ArmyPersonnelIter<'a> {
pub fn new(personnel: &'a ArmyPersonnel) -> Self {
Self { personnel, units: UnitId::iter() }
}
}
impl<'a> Iterator for ArmyPersonnelIter<'a> {
type Item = &'a Squad;
fn next(&mut self) -> Option<Self::Item> {
let id = self.units.next()?;
Some(self.personnel.squad(id))
}
}