Skip to main content

nil_core/npc/precursor/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4mod a;
5mod b;
6
7use crate::continent::{ContinentSize, Coord, Distance};
8use crate::ethic::Ethics;
9use crate::military::army::personnel::ArmyPersonnel;
10use crate::resources::Resources;
11use derive_more::Deref;
12use serde::{Deserialize, Serialize};
13use strum::{Display, EnumIter, IntoEnumIterator};
14
15pub use crate::npc::precursor::a::A;
16pub use crate::npc::precursor::b::B;
17
18pub trait Precursor: Send + Sync {
19  fn id(&self) -> PrecursorId;
20  fn ethics(&self) -> &Ethics;
21  fn origin(&self) -> Coord;
22  fn resources(&self) -> &Resources;
23  fn resources_mut(&mut self) -> &mut Resources;
24}
25
26#[derive(Clone, Debug, Deserialize, Serialize)]
27#[serde(rename_all = "camelCase")]
28pub struct PrecursorManager {
29  a: A,
30  b: B,
31}
32
33impl PrecursorManager {
34  pub fn new(size: ContinentSize) -> Self {
35    Self { a: A::new(size), b: B::new(size) }
36  }
37
38  pub fn precursor(&self, id: PrecursorId) -> &dyn Precursor {
39    match id {
40      PrecursorId::A => &self.a,
41      PrecursorId::B => &self.b,
42    }
43  }
44
45  pub(crate) fn precursor_mut(&mut self, id: PrecursorId) -> &mut dyn Precursor {
46    match id {
47      PrecursorId::A => &mut self.a,
48      PrecursorId::B => &mut self.b,
49    }
50  }
51
52  pub fn precursors(&self) -> impl Iterator<Item = &dyn Precursor> {
53    PrecursorId::iter().map(|id| self.precursor(id))
54  }
55
56  pub fn is_within_territory(&self, coord: Coord, size: ContinentSize) -> bool {
57    let distance = initial_territory_radius(size);
58    macro_rules! check {
59      ($($id:ident),+ $(,)?) => {
60        $(
61          let precursor = self.precursor(PrecursorId::$id);
62          if precursor.origin().is_within_distance(coord, distance) {
63            return true;
64          }
65        )+
66      };
67    }
68
69    check!(A, B);
70    false
71  }
72}
73
74#[derive(Deref)]
75pub struct PrecursorBox(Box<dyn Precursor>);
76
77impl PrecursorBox {
78  #[inline]
79  pub fn new(precursor: Box<dyn Precursor>) -> Self {
80    Self(precursor)
81  }
82
83  #[inline]
84  pub fn as_dyn(&self) -> &dyn Precursor {
85    &*self.0
86  }
87}
88
89impl<T> From<T> for PrecursorBox
90where
91  T: Precursor + 'static,
92{
93  fn from(value: T) -> Self {
94    Self::new(Box::new(value))
95  }
96}
97
98#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Hash, EnumIter, Deserialize, Serialize)]
99pub enum PrecursorId {
100  #[serde(rename = "A")]
101  #[strum(serialize = "A")]
102  A,
103
104  #[serde(rename = "B")]
105  #[strum(serialize = "B")]
106  B,
107}
108
109#[derive(Clone, Debug, Deserialize, Serialize)]
110#[serde(rename_all = "camelCase")]
111pub struct PublicPrecursor {
112  id: PrecursorId,
113  origin: Coord,
114}
115
116impl PublicPrecursor {
117  pub fn new<T>(precursor: &T) -> Self
118  where
119    T: Precursor + ?Sized,
120  {
121    Self {
122      id: precursor.id(),
123      origin: precursor.origin(),
124    }
125  }
126}
127
128impl From<&dyn Precursor> for PublicPrecursor {
129  fn from(precursor: &dyn Precursor) -> Self {
130    Self::new(precursor)
131  }
132}
133
134impl<T: Precursor> From<&T> for PublicPrecursor {
135  fn from(precursor: &T) -> Self {
136    Self::new(precursor)
137  }
138}
139
140#[inline]
141pub fn initial_territory_radius(size: ContinentSize) -> Distance {
142  Distance::new(size.get().div_ceil(20).next_multiple_of(2))
143}
144
145#[inline]
146pub fn initial_city_amount(size: ContinentSize) -> u8 {
147  size.get().div_ceil(10).saturating_mul(2)
148}
149
150pub fn initial_offensive_personnel() -> ArmyPersonnel {
151  ArmyPersonnel::builder()
152    .axeman(5000)
153    .light_cavalry(2500)
154    .build()
155}
156
157pub fn initial_defensive_personnel() -> ArmyPersonnel {
158  ArmyPersonnel::builder()
159    .archer(3000)
160    .pikeman(5000)
161    .swordsman(5000)
162    .heavy_cavalry(1000)
163    .build()
164}