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