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