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