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