Skip to main content

nil_core/military/army/
personnel.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::military::army::Army;
5use crate::military::squad::Squad;
6use crate::military::squad::size::SquadSize;
7use crate::military::unit::stats::haul::Haul;
8use crate::military::unit::stats::power::{AttackPower, DefensePower, Power};
9use crate::military::unit::stats::speed::Speed;
10use crate::military::unit::{UnitId, UnitIdIter};
11use crate::ranking::score::Score;
12use crate::resources::maintenance::Maintenance;
13use crate::world::config::WorldConfig;
14use serde::{Deserialize, Serialize};
15use std::iter::Sum;
16use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
17use strum::IntoEnumIterator;
18use tap::Pipe;
19
20#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
21#[serde(rename_all = "camelCase")]
22#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
23pub struct ArmyPersonnel {
24  archer: Squad,
25  axeman: Squad,
26  heavy_cavalry: Squad,
27  light_cavalry: Squad,
28  pikeman: Squad,
29  ram: Squad,
30  swordsman: Squad,
31}
32
33#[bon::bon]
34impl ArmyPersonnel {
35  #[builder]
36  pub fn new(
37    #[builder(default, into)] archer: SquadSize,
38    #[builder(default, into)] axeman: SquadSize,
39    #[builder(default, into)] heavy_cavalry: SquadSize,
40    #[builder(default, into)] light_cavalry: SquadSize,
41    #[builder(default, into)] pikeman: SquadSize,
42    #[builder(default, into)] ram: SquadSize,
43    #[builder(default, into)] swordsman: SquadSize,
44  ) -> Self {
45    use UnitId::*;
46    Self {
47      archer: Squad::new(Archer, archer),
48      axeman: Squad::new(Axeman, axeman),
49      heavy_cavalry: Squad::new(HeavyCavalry, heavy_cavalry),
50      light_cavalry: Squad::new(LightCavalry, light_cavalry),
51      pikeman: Squad::new(Pikeman, pikeman),
52      ram: Squad::new(Ram, ram),
53      swordsman: Squad::new(Swordsman, swordsman),
54    }
55  }
56
57  pub fn to_vec(self) -> Vec<Squad> {
58    Vec::<Squad>::from(self)
59  }
60
61  #[inline]
62  pub fn iter(&self) -> ArmyPersonnelIter<'_> {
63    ArmyPersonnelIter::new(self)
64  }
65
66  pub fn slowest_squad(&self, config: &WorldConfig) -> Option<&Squad> {
67    self
68      .iter()
69      .filter(|squad| squad.size() > 0u32)
70      .min_by(|a, b| a.speed(config).total_cmp(&b.speed(config)))
71  }
72
73  pub fn speed(&self, config: &WorldConfig) -> Speed {
74    self
75      .slowest_squad(config)
76      .map(|squad| squad.speed(config))
77      .unwrap_or_default()
78  }
79
80  #[inline]
81  pub fn haul(&self) -> Haul {
82    self.iter().sum()
83  }
84
85  #[inline]
86  pub fn score(&self) -> Score {
87    self.iter().sum()
88  }
89
90  #[inline]
91  pub fn maintenance(&self) -> Maintenance {
92    self.iter().sum()
93  }
94
95  #[inline]
96  pub fn power(&self) -> Power {
97    self.iter().sum()
98  }
99
100  #[inline]
101  pub fn attack(&self) -> AttackPower {
102    self.iter().sum()
103  }
104
105  #[inline]
106  pub fn defense(&self) -> DefensePower {
107    self.iter().sum()
108  }
109
110  #[inline]
111  pub fn is_empty(&self) -> bool {
112    self.iter().all(Squad::is_empty)
113  }
114}
115
116macro_rules! impl_army_personnel {
117  ($($unit:ident),+) => {
118    paste::paste! {
119      impl ArmyPersonnel {
120        pub fn splat(size: impl Into<SquadSize>) -> Self {
121          let size: SquadSize = size.into();
122          Self::builder()
123            $(.[<$unit:snake>](size))+
124            .build()
125        }
126
127        pub fn random() -> Self {
128          Self::builder()
129            $(.[<$unit:snake>](SquadSize::random()))+
130            .build()
131        }
132
133        pub fn squad(&self, id: UnitId) -> &Squad {
134          match id {
135            $(UnitId::$unit => &self.[<$unit:snake>],)+
136          }
137        }
138
139        pub(super) fn squad_mut(&mut self, id: UnitId) -> &mut Squad {
140          match id {
141            $(UnitId::$unit => &mut self.[<$unit:snake>],)+
142          }
143        }
144
145        $(
146          #[inline]
147          pub fn [<$unit:snake>](&self) -> &Squad {
148            &self.[<$unit:snake>]
149          }
150        )+
151
152        pub fn has_enough_personnel(&self, required: &ArmyPersonnel) -> bool {
153          $(self.[<$unit:snake>].size() >= required.[<$unit:snake>].size() && )+ true
154        }
155
156        pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
157          Self::builder()
158            $(
159              .[<$unit:snake>](
160                self
161                  .[<$unit:snake>]
162                  .size()
163                  .checked_sub(rhs.[<$unit:snake>].size())?
164              )
165            )+
166            .build()
167            .pipe(Some)
168        }
169      }
170
171      impl From<ArmyPersonnel> for Vec<Squad> {
172        fn from(personnel: ArmyPersonnel) -> Self {
173          vec![$(personnel.[<$unit:snake>],)+]
174        }
175      }
176    }
177  };
178}
179
180impl_army_personnel!(
181  Archer,
182  Axeman,
183  HeavyCavalry,
184  LightCavalry,
185  Pikeman,
186  Ram,
187  Swordsman
188);
189
190impl Default for ArmyPersonnel {
191  fn default() -> Self {
192    Self::builder().build()
193  }
194}
195
196impl From<Army> for ArmyPersonnel {
197  fn from(army: Army) -> Self {
198    army.personnel
199  }
200}
201
202impl From<SquadSize> for ArmyPersonnel {
203  fn from(size: SquadSize) -> Self {
204    ArmyPersonnel::splat(size)
205  }
206}
207
208impl FromIterator<Squad> for ArmyPersonnel {
209  fn from_iter<T>(iter: T) -> Self
210  where
211    T: IntoIterator<Item = Squad>,
212  {
213    iter
214      .into_iter()
215      .fold(Self::default(), |mut personnel, squad| {
216        personnel += squad;
217        personnel
218      })
219  }
220}
221
222impl<'a> IntoIterator for &'a ArmyPersonnel {
223  type Item = &'a Squad;
224  type IntoIter = ArmyPersonnelIter<'a>;
225
226  fn into_iter(self) -> Self::IntoIter {
227    self.iter()
228  }
229}
230
231impl<'a> Sum<&'a ArmyPersonnel> for ArmyPersonnel {
232  fn sum<I>(iter: I) -> Self
233  where
234    I: Iterator<Item = &'a ArmyPersonnel>,
235  {
236    iter.fold(ArmyPersonnel::default(), |mut acc, personnel| {
237      acc += personnel;
238      acc
239    })
240  }
241}
242
243impl<'a> Sum<&'a Army> for ArmyPersonnel {
244  fn sum<I>(iter: I) -> Self
245  where
246    I: Iterator<Item = &'a Army>,
247  {
248    iter.map(Army::personnel).sum()
249  }
250}
251
252impl Add for ArmyPersonnel {
253  type Output = ArmyPersonnel;
254
255  fn add(mut self, rhs: Self) -> Self::Output {
256    self += &rhs;
257    self
258  }
259}
260
261impl Add<Squad> for ArmyPersonnel {
262  type Output = ArmyPersonnel;
263
264  fn add(mut self, rhs: Squad) -> Self::Output {
265    self += rhs;
266    self
267  }
268}
269
270impl AddAssign for ArmyPersonnel {
271  fn add_assign(&mut self, rhs: Self) {
272    *self += &rhs;
273  }
274}
275
276impl AddAssign<&ArmyPersonnel> for ArmyPersonnel {
277  fn add_assign(&mut self, rhs: &ArmyPersonnel) {
278    for squad in rhs {
279      *self.squad_mut(squad.id()) += squad.size();
280    }
281  }
282}
283
284impl AddAssign<Squad> for ArmyPersonnel {
285  fn add_assign(&mut self, rhs: Squad) {
286    *self.squad_mut(rhs.id()) += rhs;
287  }
288}
289
290impl Sub for ArmyPersonnel {
291  type Output = ArmyPersonnel;
292
293  fn sub(mut self, rhs: Self) -> Self::Output {
294    self -= rhs;
295    self
296  }
297}
298
299impl SubAssign for ArmyPersonnel {
300  fn sub_assign(&mut self, rhs: Self) {
301    for squad in &rhs {
302      *self.squad_mut(squad.id()) -= squad.size();
303    }
304  }
305}
306
307impl SubAssign<Squad> for ArmyPersonnel {
308  fn sub_assign(&mut self, rhs: Squad) {
309    *self.squad_mut(rhs.id()) -= rhs;
310  }
311}
312
313impl Mul<f64> for ArmyPersonnel {
314  type Output = ArmyPersonnel;
315
316  fn mul(mut self, rhs: f64) -> Self::Output {
317    self *= rhs;
318    self
319  }
320}
321
322impl MulAssign<f64> for ArmyPersonnel {
323  fn mul_assign(&mut self, rhs: f64) {
324    for id in UnitId::iter() {
325      *self.squad_mut(id) *= rhs;
326    }
327  }
328}
329
330pub struct ArmyPersonnelIter<'a> {
331  personnel: &'a ArmyPersonnel,
332  units: UnitIdIter,
333}
334
335impl<'a> ArmyPersonnelIter<'a> {
336  pub fn new(personnel: &'a ArmyPersonnel) -> Self {
337    Self { personnel, units: UnitId::iter() }
338  }
339}
340
341impl<'a> Iterator for ArmyPersonnelIter<'a> {
342  type Item = &'a Squad;
343
344  fn next(&mut self) -> Option<Self::Item> {
345    let id = self.units.next()?;
346    Some(self.personnel.squad(id))
347  }
348}