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