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