Skip to main content

nil_core/military/squad/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4pub mod size;
5
6use crate::error::{Error, Result};
7use crate::military::unit::stats::haul::Haul;
8use crate::military::unit::stats::speed::Speed;
9use crate::military::unit::{Unit, UnitBox, UnitId, UnitKind};
10use crate::ranking::score::Score;
11use crate::resources::maintenance::Maintenance;
12use derive_more::{Deref, Into};
13use serde::{Deserialize, Serialize};
14use size::SquadSize;
15use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
16
17/// A group of units of the same type.
18#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
19#[serde(rename_all = "camelCase")]
20pub struct Squad {
21  unit: UnitBox,
22  size: SquadSize,
23}
24
25impl Squad {
26  pub fn new(id: UnitId, size: SquadSize) -> Self {
27    Self { unit: UnitBox::from(id), size }
28  }
29
30  #[inline]
31  pub fn unit(&self) -> &dyn Unit {
32    self.unit.as_dyn()
33  }
34
35  #[inline]
36  pub fn id(&self) -> UnitId {
37    self.unit.id()
38  }
39
40  #[inline]
41  pub fn kind(&self) -> UnitKind {
42    self.unit.kind()
43  }
44
45  #[inline]
46  pub fn size(&self) -> SquadSize {
47    self.size
48  }
49
50  #[inline]
51  pub fn score(&self) -> Score {
52    self.size * self.unit.score()
53  }
54
55  #[inline]
56  pub fn is_empty(&self) -> bool {
57    self.size == 0u32
58  }
59
60  pub fn maintenance(&self) -> Maintenance {
61    let chunk = self.unit.chunk();
62    let c_size = chunk.size();
63    let c_maintenance = chunk.maintenance();
64    (self.size() * c_maintenance) / c_size
65  }
66
67  pub fn attack(&self) -> SquadAttack {
68    let attack = self.unit.stats().attack();
69    let total = f64::from(attack * self.size);
70    SquadAttack::new(total)
71  }
72
73  pub fn defense(&self) -> SquadDefense {
74    let stats = self.unit.stats();
75    let infantry = f64::from(stats.infantry_defense() * self.size);
76    let cavalry = f64::from(stats.cavalry_defense() * self.size);
77    let ranged = f64::from(stats.ranged_defense() * self.size);
78    SquadDefense { infantry, cavalry, ranged }
79  }
80
81  #[inline]
82  pub fn speed(&self) -> Speed {
83    self.unit.speed()
84  }
85
86  #[inline]
87  pub fn haul(&self) -> Haul {
88    self.unit.haul() * self.size
89  }
90
91  pub fn checked_sub(&self, rhs: &Self) -> Result<Option<Self>> {
92    if self.unit == rhs.unit {
93      Ok(try { Self::new(self.id(), self.size.checked_sub(rhs.size)?) })
94    } else {
95      Err(Error::UnexpectedUnit(self.id(), rhs.id()))
96    }
97  }
98}
99
100impl Add for Squad {
101  type Output = Squad;
102
103  fn add(mut self, rhs: Self) -> Self::Output {
104    self += rhs;
105    self
106  }
107}
108
109impl Add<SquadSize> for Squad {
110  type Output = Squad;
111
112  fn add(mut self, rhs: SquadSize) -> Self::Output {
113    self += rhs;
114    self
115  }
116}
117
118impl AddAssign for Squad {
119  fn add_assign(&mut self, rhs: Self) {
120    if self.id() == rhs.id() {
121      self.size += rhs.size;
122    }
123  }
124}
125
126impl AddAssign<SquadSize> for Squad {
127  fn add_assign(&mut self, rhs: SquadSize) {
128    self.size += rhs;
129  }
130}
131
132impl Sub for Squad {
133  type Output = Squad;
134
135  fn sub(mut self, rhs: Self) -> Self::Output {
136    self -= rhs;
137    self
138  }
139}
140
141impl Sub<SquadSize> for Squad {
142  type Output = Squad;
143
144  fn sub(mut self, rhs: SquadSize) -> Self::Output {
145    self -= rhs;
146    self
147  }
148}
149
150impl SubAssign for Squad {
151  fn sub_assign(&mut self, rhs: Self) {
152    if self.id() == rhs.id() {
153      self.size -= rhs.size;
154    }
155  }
156}
157
158impl SubAssign<SquadSize> for Squad {
159  fn sub_assign(&mut self, rhs: SquadSize) {
160    self.size -= rhs;
161  }
162}
163
164impl Mul<f64> for Squad {
165  type Output = Squad;
166
167  fn mul(mut self, rhs: f64) -> Self::Output {
168    self *= rhs;
169    self
170  }
171}
172
173impl MulAssign<f64> for Squad {
174  fn mul_assign(&mut self, rhs: f64) {
175    self.size *= rhs;
176  }
177}
178
179impl From<UnitId> for Squad {
180  fn from(id: UnitId) -> Self {
181    Squad::new(id, SquadSize::new(0))
182  }
183}
184
185#[derive(Clone, Copy, Debug, Deref, Into)]
186pub struct SquadAttack(f64);
187
188impl SquadAttack {
189  #[inline]
190  pub const fn new(value: f64) -> Self {
191    Self(value.max(0.0))
192  }
193}
194
195impl From<f64> for SquadAttack {
196  fn from(value: f64) -> Self {
197    Self::new(value)
198  }
199}
200
201#[derive(Clone, Debug)]
202pub struct SquadDefense {
203  pub(crate) infantry: f64,
204  pub(crate) cavalry: f64,
205  pub(crate) ranged: f64,
206}