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