nil_core/infrastructure/
mod.rs1pub mod building;
5pub mod catalog;
6pub mod mine;
7pub mod prelude;
8pub mod queue;
9pub mod requirements;
10pub mod stats;
11pub mod storage;
12
13use crate::error::Result;
14use crate::military::army::personnel::ArmyPersonnel;
15use crate::military::squad::Squad;
16use crate::ranking::score::Score;
17use crate::resources::Resources;
18use crate::resources::maintenance::Maintenance;
19use bon::Builder;
20use building::academy::recruit_queue::{
21 AcademyRecruitOrder,
22 AcademyRecruitOrderId,
23 AcademyRecruitOrderRequest,
24};
25use building::prefecture::build_queue::{
26 PrefectureBuildOrder,
27 PrefectureBuildOrderKind,
28 PrefectureBuildOrderRequest,
29};
30use building::stable::recruit_queue::{
31 StableRecruitOrder,
32 StableRecruitOrderId,
33 StableRecruitOrderRequest,
34};
35use building::workshop::recruit_queue::{
36 WorkshopRecruitOrder,
37 WorkshopRecruitOrderId,
38 WorkshopRecruitOrderRequest,
39};
40use building::{Building, BuildingId, BuildingStatsTable, MineId, StorageId};
41use mine::Mine;
42use prelude::*;
43use requirements::InfrastructureRequirements;
44use serde::{Deserialize, Serialize};
45use stats::InfrastructureStats;
46use storage::Storage;
47use strum::IntoEnumIterator;
48use tap::Pipe;
49
50#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
51#[serde(default, rename_all = "camelCase")]
52pub struct Infrastructure {
53 #[builder(default)]
54 academy: Academy,
55
56 #[builder(default)]
57 farm: Farm,
58
59 #[builder(default)]
60 iron_mine: IronMine,
61
62 #[builder(default)]
63 prefecture: Prefecture,
64
65 #[builder(default)]
66 quarry: Quarry,
67
68 #[builder(default)]
69 sawmill: Sawmill,
70
71 #[builder(default)]
72 silo: Silo,
73
74 #[builder(default)]
75 stable: Stable,
76
77 #[builder(default)]
78 wall: Wall,
79
80 #[builder(default)]
81 warehouse: Warehouse,
82
83 #[builder(default)]
84 workshop: Workshop,
85}
86
87impl Infrastructure {
88 #[inline]
89 pub fn new() -> Self {
90 Self::default()
91 }
92
93 pub const fn storage(&self, id: StorageId) -> &dyn Storage {
94 match id {
95 StorageId::Silo => &self.silo,
96 StorageId::Warehouse => &self.warehouse,
97 }
98 }
99
100 pub const fn mine(&self, id: MineId) -> &dyn Mine {
101 match id {
102 MineId::Farm => &self.farm,
103 MineId::IronMine => &self.iron_mine,
104 MineId::Quarry => &self.quarry,
105 MineId::Sawmill => &self.sawmill,
106 }
107 }
108
109 pub fn score(&self, stats: &InfrastructureStats) -> Result<Score> {
110 let mut score = Score::default();
111 for id in BuildingId::iter() {
112 let level = self.building(id).level();
113 if level > 0 {
114 let stats = stats.building(id)?;
115 score += stats.get(level)?.score;
116 }
117 }
118
119 Ok(score)
120 }
121
122 pub fn round_base_production(&self, stats: &InfrastructureStats) -> Result<Resources> {
125 let mut resources = Resources::default();
126
127 macro_rules! set {
128 ($building:ident, $resource:ident) => {
129 paste::paste! {
130 let mine = &self.[<$building:snake>];
131 if mine.level() > 0u8 && mine.is_enabled() {
132 let mine_stats = stats.mine(MineId::$building)?;
133 resources.$resource = mine.production(mine_stats)?.into();
134 }
135 }
136 };
137 }
138
139 set!(Farm, food);
140 set!(IronMine, iron);
141 set!(Quarry, stone);
142 set!(Sawmill, wood);
143
144 Ok(resources)
145 }
146
147 pub(crate) fn add_prefecture_build_order(
148 &mut self,
149 request: &PrefectureBuildOrderRequest,
150 table: &BuildingStatsTable,
151 current_resources: Option<&Resources>,
152 ) -> Result<&PrefectureBuildOrder> {
153 let level = self.building(request.building).level();
154 self
155 .prefecture
156 .build_queue_mut()
157 .build(request, table, level, current_resources)
158 }
159
160 #[must_use]
161 pub(crate) fn cancel_prefecture_build_order(&mut self) -> Option<PrefectureBuildOrder> {
162 self.prefecture.build_queue_mut().cancel()
163 }
164
165 pub(crate) fn process_prefecture_build_queue(&mut self) {
166 if let Some(orders) = self.prefecture.process_queue() {
167 for order in orders {
168 let building = self.building_mut(order.building());
169 match order.kind() {
170 PrefectureBuildOrderKind::Construction => building.increase_level(),
171 PrefectureBuildOrderKind::Demolition => building.decrease_level(),
172 }
173 }
174 }
175 }
176}
177
178macro_rules! impl_infrastructure {
179 ($($building:ident),+) => {
180 paste::paste! {
181 impl Infrastructure {
182 $(
183 #[inline]
184 pub const fn [<$building:snake>](&self) -> &$building {
185 &self.[<$building:snake>]
186 }
187 )+
188
189 pub fn with_max_level() -> Self {
191 Self {
192 $([<$building:snake>]: $building::with_max_level(),)+
193 }
194 }
195
196 pub const fn building(&self, id: BuildingId) -> &dyn Building {
197 match id {
198 $(BuildingId::$building => &self.[<$building:snake>],)+
199 }
200 }
201
202 pub(crate) const fn building_mut(&mut self, id: BuildingId) -> &mut dyn Building {
203 match id {
204 $(BuildingId::$building => &mut self.[<$building:snake>],)+
205 }
206 }
207
208 pub fn base_maintenance(&self, stats: &InfrastructureStats) -> Result<Maintenance> {
210 let mut maintenance = Maintenance::default();
211 $(
212 let building = &self.[<$building:snake>];
213 if building.level() > 0u8 && building.is_enabled() {
214 let building_stats = stats.building(BuildingId::$building)?;
215 maintenance += building.maintenance(&building_stats)?;
216 }
217 )+
218
219 Ok(maintenance)
220 }
221
222 pub fn has_required_levels(&self, requirements: &InfrastructureRequirements) -> bool {
224 $(self.[<$building:snake>].level() >= requirements.[<$building:snake>] &&)+ true
225 }
226 }
227 }
228 };
229}
230
231impl_infrastructure!(
232 Academy, Farm, IronMine, Prefecture, Quarry, Sawmill, Silo, Stable, Wall, Warehouse, Workshop
233);
234
235macro_rules! impl_recruitment {
236 ($building:ident) => {
237 paste::paste! {
238 impl Infrastructure {
239 pub(crate) fn [<add_ $building:snake _recruit_order>](
240 &mut self,
241 request: &[<$building RecruitOrderRequest>],
242 current_resources: Option<&Resources>,
243 ) -> Result<&[<$building RecruitOrder>]> {
244 self
245 .[<$building:snake>]
246 .recruit_queue_mut()
247 .recruit(request, current_resources)
248 }
249
250 #[must_use]
251 pub(crate) fn [<cancel_ $building:snake _recruit_order>](
252 &mut self,
253 id: [<$building RecruitOrderId>]
254 ) -> Option<[<$building RecruitOrder>]> {
255 self.[<$building:snake>].recruit_queue_mut().cancel(id)
256 }
257
258 #[must_use]
259 pub(crate) fn [<process_ $building:snake _recruit_queue>](&mut self) -> Option<ArmyPersonnel> {
260 self
261 .[<$building:snake>]
262 .process_queue()?
263 .into_iter()
264 .map(Squad::from)
265 .collect::<ArmyPersonnel>()
266 .pipe(Some)
267 }
268 }
269 }
270 };
271}
272
273impl_recruitment!(Academy);
274impl_recruitment!(Stable);
275impl_recruitment!(Workshop);