Skip to main content

nil_core/military/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4#[cfg(test)]
5mod tests;
6
7pub mod army;
8pub mod maneuver;
9pub mod squad;
10pub mod unit;
11
12use crate::continent::coord::Coord;
13use crate::continent::index::{ContinentIndex, ContinentKey};
14use crate::continent::size::ContinentSize;
15use crate::error::{Error, Result};
16use crate::military::army::personnel::ArmyPersonnel;
17use crate::military::army::{Army, ArmyId, collapse_armies};
18use crate::military::maneuver::{Maneuver, ManeuverId};
19use crate::military::squad::Squad;
20use crate::military::unit::stats::power::{AttackPower, DefensePower, Power};
21use crate::ranking::score::Score;
22use crate::resources::maintenance::Maintenance;
23use crate::ruler::Ruler;
24use itertools::Itertools;
25use serde::{Deserialize, Serialize};
26use std::collections::HashMap;
27
28#[derive(Clone, Debug, Deserialize, Serialize)]
29#[serde(rename_all = "camelCase")]
30#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
31pub struct Military {
32  continent: HashMap<ContinentIndex, Vec<Army>>,
33  continent_size: ContinentSize,
34  maneuvers: HashMap<ManeuverId, Maneuver>,
35}
36
37impl Military {
38  pub(crate) fn new(size: ContinentSize) -> Self {
39    Self {
40      continent: HashMap::new(),
41      continent_size: size,
42      maneuvers: HashMap::new(),
43    }
44  }
45
46  pub(crate) fn spawn<K, R>(&mut self, key: K, owner: R, personnel: ArmyPersonnel)
47  where
48    K: ContinentKey,
49    R: Into<Ruler>,
50  {
51    let ruler: Ruler = owner.into();
52    let army = Army::builder()
53      .owner(ruler)
54      .personnel(personnel)
55      .build();
56
57    let index = key.into_index(self.continent_size);
58    self
59      .continent
60      .entry(index)
61      .or_default()
62      .push(army);
63
64    self.collapse_armies_in(index);
65  }
66
67  pub fn collapse_armies(&mut self) {
68    self
69      .continent
70      .values_mut()
71      .for_each(collapse_armies);
72
73    self
74      .continent
75      .retain(|_, armies| !armies.is_empty());
76  }
77
78  pub fn collapse_armies_in<K>(&mut self, key: K)
79  where
80    K: ContinentKey,
81  {
82    let index = key.into_index(self.continent_size);
83    if let Some(armies) = self.continent.get_mut(&index) {
84      collapse_armies(armies);
85    }
86  }
87
88  /// Creates a new instance containing only entries related to a given set of coords.
89  pub fn intersection<K, I>(&self, keys: I) -> Result<Self>
90  where
91    K: ContinentKey,
92    I: IntoIterator<Item = K>,
93  {
94    let mut military = Self::new(self.continent_size);
95    for key in keys {
96      let coord = key.into_coord(self.continent_size)?;
97      let index = coord.into_index(self.continent_size);
98      if let Some(armies) = self.continent.get(&index).cloned() {
99        military.continent.insert(index, armies);
100      }
101
102      let maneuvers = self
103        .maneuvers_at(coord)
104        .map(|maneuver| (maneuver.id(), maneuver.clone()));
105
106      military.maneuvers.extend(maneuvers);
107    }
108
109    Ok(military)
110  }
111
112  pub(crate) fn remove_army(&mut self, id: ArmyId) -> Result<Army> {
113    let (curr_vec, pos) = self
114      .continent
115      .values_mut()
116      .find_map(|armies| {
117        armies
118          .iter()
119          .position(|army| army.id() == id)
120          .map(|pos| (armies, pos))
121      })
122      .ok_or(Error::ArmyNotFound(id))?;
123
124    Ok(curr_vec.swap_remove(pos))
125  }
126
127  pub(crate) fn remove_armies<I>(&mut self, armies: I) -> Result<Vec<Army>>
128  where
129    I: IntoIterator<Item = ArmyId>,
130  {
131    armies
132      .into_iter()
133      .map(|id| self.remove_army(id))
134      .try_collect()
135  }
136
137  pub(crate) fn relocate_army<K>(&mut self, id: ArmyId, new_key: K) -> Result<()>
138  where
139    K: ContinentKey,
140  {
141    let army = self.remove_army(id)?;
142    let index = new_key.into_index(self.continent_size);
143    self
144      .continent
145      .entry(index)
146      .or_default()
147      .push(army);
148
149    self.collapse_armies_in(index);
150
151    Ok(())
152  }
153
154  pub fn continent(&self) -> impl Iterator<Item = (ContinentIndex, &[Army])> {
155    self
156      .continent
157      .iter()
158      .map(|(index, armies)| (*index, armies.as_slice()))
159  }
160
161  pub fn army(&self, id: ArmyId) -> Result<&Army> {
162    self
163      .armies()
164      .find(|army| army.id() == id)
165      .ok_or(Error::ArmyNotFound(id))
166  }
167
168  pub(crate) fn army_mut(&mut self, id: ArmyId) -> Result<&mut Army> {
169    self
170      .armies_mut()
171      .find(|army| army.id() == id)
172      .ok_or(Error::ArmyNotFound(id))
173  }
174
175  pub fn armies(&self) -> impl Iterator<Item = &Army> {
176    self.continent.values().flatten()
177  }
178
179  pub(crate) fn armies_mut(&mut self) -> impl Iterator<Item = &mut Army> {
180    self.continent.values_mut().flatten()
181  }
182
183  pub fn armies_at<K>(&self, key: K) -> &[Army]
184  where
185    K: ContinentKey,
186  {
187    let index = key.into_index(self.continent_size);
188    self
189      .continent
190      .get(&index)
191      .map(Vec::as_slice)
192      .unwrap_or_default()
193  }
194
195  pub(crate) fn armies_mut_at<K>(&mut self, key: K) -> &mut [Army]
196  where
197    K: ContinentKey,
198  {
199    let index = key.into_index(self.continent_size);
200    self
201      .continent
202      .get_mut(&index)
203      .map(Vec::as_mut_slice)
204      .unwrap_or_default()
205  }
206
207  pub fn armies_of<R>(&self, owner: R) -> impl Iterator<Item = &Army>
208  where
209    R: Into<Ruler>,
210  {
211    let owner: Ruler = owner.into();
212    self
213      .continent
214      .values()
215      .flatten()
216      .filter(move |army| army.is_owned_by(&owner))
217  }
218
219  pub fn indexed_armies_of<R>(&self, owner: R) -> Vec<(ContinentIndex, &Army)>
220  where
221    R: Into<Ruler>,
222  {
223    let owner: Ruler = owner.into();
224    let mut owner_armies = Vec::new();
225
226    for (index, armies) in &self.continent {
227      for army in armies {
228        if army.is_owned_by(&owner) {
229          owner_armies.push((*index, army));
230        }
231      }
232    }
233
234    owner_armies.sort_by_key(|(index, _)| *index);
235    owner_armies
236  }
237
238  #[inline]
239  pub fn count_armies(&self) -> usize {
240    self.armies().count()
241  }
242
243  pub fn count_armies_at<K>(&self, key: K) -> usize
244  where
245    K: ContinentKey,
246  {
247    self.armies_at(key).iter().count()
248  }
249
250  pub fn idle_armies_at<K>(&self, key: K) -> impl Iterator<Item = &Army>
251  where
252    K: ContinentKey,
253  {
254    self
255      .armies_at(key)
256      .iter()
257      .filter(|army| army.is_idle())
258  }
259
260  pub(crate) fn idle_armies_mut_at<K>(&mut self, key: K) -> impl Iterator<Item = &mut Army>
261  where
262    K: ContinentKey,
263  {
264    self
265      .armies_mut_at(key)
266      .iter_mut()
267      .filter(|army| army.is_idle())
268  }
269
270  #[inline]
271  pub fn personnel(&self, id: ArmyId) -> Result<&ArmyPersonnel> {
272    self.army(id).map(Army::personnel)
273  }
274
275  pub fn personnel_at<K>(&self, key: K) -> impl Iterator<Item = &ArmyPersonnel>
276  where
277    K: ContinentKey,
278  {
279    self
280      .armies_at(key)
281      .iter()
282      .map(Army::personnel)
283  }
284
285  pub fn fold_personnel_at<K>(&self, key: K) -> ArmyPersonnel
286  where
287    K: ContinentKey,
288  {
289    self.personnel_at(key).sum()
290  }
291
292  pub fn personnel_of<R>(&self, owner: R) -> impl Iterator<Item = &ArmyPersonnel>
293  where
294    R: Into<Ruler>,
295  {
296    self.armies_of(owner).map(Army::personnel)
297  }
298
299  pub fn fold_personnel_of<R>(&self, owner: R) -> ArmyPersonnel
300  where
301    R: Into<Ruler>,
302  {
303    self.personnel_of(owner).sum()
304  }
305
306  pub fn idle_personnel_at<K>(&self, key: K) -> impl Iterator<Item = &ArmyPersonnel>
307  where
308    K: ContinentKey,
309  {
310    self.idle_armies_at(key).map(Army::personnel)
311  }
312
313  pub fn fold_idle_personnel_at<K>(&self, key: K) -> ArmyPersonnel
314  where
315    K: ContinentKey,
316  {
317    self.idle_personnel_at(key).sum()
318  }
319
320  #[inline]
321  pub fn squads(&self, id: ArmyId) -> Result<Vec<Squad>> {
322    self
323      .personnel(id)
324      .cloned()
325      .map(ArmyPersonnel::to_vec)
326  }
327
328  pub fn idle_squads_at<K>(&self, key: K) -> Vec<Squad>
329  where
330    K: ContinentKey,
331  {
332    self.fold_idle_personnel_at(key).to_vec()
333  }
334
335  #[inline]
336  pub fn maneuver(&self, id: ManeuverId) -> Result<&Maneuver> {
337    self
338      .maneuvers
339      .get(&id)
340      .ok_or(Error::ManeuverNotFound(id))
341  }
342
343  fn maneuver_mut(&mut self, id: ManeuverId) -> Result<&mut Maneuver> {
344    self
345      .maneuvers
346      .get_mut(&id)
347      .ok_or(Error::ManeuverNotFound(id))
348  }
349
350  pub fn maneuvers(&self) -> impl Iterator<Item = &Maneuver> {
351    self.maneuvers.values()
352  }
353
354  /// Find all maneuvers whose origin or destination matches the specified coord.
355  pub fn maneuvers_at(&self, coord: Coord) -> impl Iterator<Item = &Maneuver> {
356    self
357      .maneuvers()
358      .filter(move |maneuver| maneuver.matches_coord(coord))
359  }
360
361  #[inline]
362  pub fn has_maneuver(&self, id: ManeuverId) -> bool {
363    self.maneuvers.contains_key(&id)
364  }
365
366  pub(crate) fn insert_maneuver(&mut self, maneuver: Maneuver) {
367    self
368      .maneuvers
369      .insert(maneuver.id(), maneuver);
370  }
371
372  pub(crate) fn advance_maneuvers(&mut self) -> Result<Vec<Maneuver>> {
373    let mut done = Vec::new();
374    for (id, maneuver) in &mut self.maneuvers {
375      maneuver.advance()?;
376
377      if maneuver.is_done() {
378        done.push(*id);
379      }
380    }
381
382    let done = done
383      .into_iter()
384      .filter_map(|id| self.maneuvers.remove(&id))
385      .collect_vec();
386
387    self.maneuvers.shrink_to_fit();
388
389    Ok(done)
390  }
391
392  pub(crate) fn cancel_maneuver(&mut self, id: ManeuverId) -> Result<()> {
393    self.maneuver_mut(id)?.cancel()
394  }
395
396  /// Retains only the maneuvers specified by the predicate.
397  pub(crate) fn retain_maneuvers<F>(&mut self, f: F)
398  where
399    F: Fn(&Maneuver) -> bool,
400  {
401    self
402      .maneuvers
403      .retain(|_, maneuver| f(maneuver));
404  }
405
406  pub fn score_of<R>(&self, owner: R) -> Score
407  where
408    R: Into<Ruler>,
409  {
410    self.armies_of(owner).sum()
411  }
412
413  pub fn maintenance_of<R>(&self, owner: R) -> Maintenance
414  where
415    R: Into<Ruler>,
416  {
417    self.armies_of(owner).sum()
418  }
419
420  pub fn power_of<R>(&self, owner: R) -> Power
421  where
422    R: Into<Ruler>,
423  {
424    self.armies_of(owner).sum()
425  }
426
427  pub fn attack_of<R>(&self, owner: R) -> AttackPower
428  where
429    R: Into<Ruler>,
430  {
431    self.armies_of(owner).sum()
432  }
433
434  pub fn defense_of<R>(&self, owner: R) -> DefensePower
435  where
436    R: Into<Ruler>,
437  {
438    self.armies_of(owner).sum()
439  }
440}