Skip to main content

nil_core/world/npc/
precursor.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::city::City;
5use crate::error::Result;
6use crate::infrastructure::Infrastructure;
7use crate::npc::precursor::{self, Precursor, PrecursorId, PrecursorManager};
8use crate::world::World;
9use nil_num::roman::ToRoman;
10use rand::seq::IndexedRandom;
11
12impl World {
13  #[inline]
14  pub fn precursor_manager(&self) -> &PrecursorManager {
15    &self.precursor_manager
16  }
17
18  #[inline]
19  pub fn precursor(&self, id: PrecursorId) -> &dyn Precursor {
20    self.precursor_manager.precursor(id)
21  }
22
23  #[inline]
24  pub(crate) fn precursor_mut(&mut self, id: PrecursorId) -> &mut dyn Precursor {
25    self.precursor_manager.precursor_mut(id)
26  }
27
28  pub fn precursors(&self) -> impl Iterator<Item = &dyn Precursor> {
29    self.precursor_manager.precursors()
30  }
31
32  pub(crate) fn spawn_precursors(&mut self) -> Result<()> {
33    self.spawn_precursor_cities(PrecursorId::A)?;
34    self.spawn_precursor_cities(PrecursorId::B)?;
35    Ok(())
36  }
37
38  fn spawn_precursor_cities(&mut self, id: PrecursorId) -> Result<()> {
39    let size = self.continent.size();
40    let radius = precursor::initial_territory_radius(size);
41    let amount = precursor::initial_city_amount(size);
42    let mut coords = self
43      .precursor_manager
44      .precursor(id)
45      .origin()
46      .within_distance_inclusive(radius);
47
48    coords.retain(|coord| coord.is_within_continent(size));
49
50    for (idx, coord) in coords
51      .sample(&mut rand::rng(), amount.into())
52      .copied()
53      .enumerate()
54    {
55      let field = self.continent.field_mut(coord)?;
56      debug_assert!(field.is_empty());
57
58      // TODO: Our roman numerals implementation only supports numbers between 1 and 3999.
59      // Currently, this is not a problem, but it could become one in the future.
60      let Some(ridx) = idx.saturating_add(1).to_roman() else { continue };
61
62      *field = City::builder(coord)
63        .name(format!("Precursor {id} {ridx}"))
64        .owner(id)
65        .infrastructure(Infrastructure::with_max_level())
66        .build()
67        .into();
68
69      let personnel = if idx.is_multiple_of(3) {
70        precursor::initial_offensive_personnel()
71      } else {
72        precursor::initial_defensive_personnel()
73      };
74
75      self.military.spawn(coord, id, personnel);
76    }
77
78    Ok(())
79  }
80}