1pub mod cost;
5pub mod diff;
6pub mod influence;
7pub mod maintenance;
8pub mod prelude;
9pub mod workforce;
10
11use crate::city::stability::Stability;
12use crate::infrastructure::mine::MineProduction;
13use crate::infrastructure::storage::{OverallStorageCapacity, StorageCapacity};
14use bon::Builder;
15use derive_more::Display;
16use diff::{FoodDiff, IronDiff, ResourcesDiff, StoneDiff, WoodDiff};
17use nil_num::impl_mul_ceil;
18use nil_num::mul_ceil::MulCeil;
19use nil_util::{ConstDeref, F64Math};
20use serde::{Deserialize, Serialize};
21use std::cmp::Ordering;
22use std::num::NonZeroU32;
23use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
24
25#[derive(Builder, Debug, Deserialize, Serialize)]
26#[derive_const(Clone, PartialEq, Eq)]
27#[serde(default, rename_all = "camelCase")]
28#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
29pub struct Resources {
30 #[builder(default)]
31 pub food: Food,
32
33 #[builder(default)]
34 pub iron: Iron,
35
36 #[builder(default)]
37 pub stone: Stone,
38
39 #[builder(default)]
40 pub wood: Wood,
41}
42
43impl Resources {
44 pub const MIN: Self = Self {
46 food: Food::MIN,
47 iron: Iron::MIN,
48 stone: Stone::MIN,
49 wood: Wood::MIN,
50 };
51
52 pub const MAX: Self = Self {
54 food: Food::MAX,
55 iron: Iron::MAX,
56 stone: Stone::MAX,
57 wood: Wood::MAX,
58 };
59
60 pub const PLAYER: Self = Self::splat(800);
62
63 pub const BOT: Self = Self::splat(2500);
65
66 pub const PRECURSOR: Self = Self::splat(5_000_000);
68
69 #[inline]
70 #[must_use]
71 pub const fn new() -> Self {
72 Self::MIN.clone()
73 }
74
75 #[must_use]
76 pub const fn splat(value: u32) -> Self {
77 Self {
78 food: Food::new(value),
79 iron: Iron::new(value),
80 stone: Stone::new(value),
81 wood: Wood::new(value),
82 }
83 }
84
85 #[inline]
86 #[must_use]
87 pub const fn with_food(&self, food: Food) -> Self {
88 Self { food, ..self.clone() }
89 }
90
91 #[inline]
92 #[must_use]
93 pub const fn with_iron(&self, iron: Iron) -> Self {
94 Self { iron, ..self.clone() }
95 }
96
97 #[inline]
98 #[must_use]
99 pub const fn with_stone(&self, stone: Stone) -> Self {
100 Self { stone, ..self.clone() }
101 }
102
103 #[inline]
104 #[must_use]
105 pub const fn with_wood(&self, wood: Wood) -> Self {
106 Self { wood, ..self.clone() }
107 }
108
109 #[must_use]
110 pub const fn silo(&self) -> Self {
111 Self {
112 food: self.food,
113 iron: Iron::MIN,
114 stone: Stone::MIN,
115 wood: Wood::MIN,
116 }
117 }
118
119 #[must_use]
120 pub const fn warehouse(&self) -> Self {
121 Self {
122 food: Food::MIN,
123 iron: self.iron,
124 stone: self.stone,
125 wood: self.wood,
126 }
127 }
128
129 pub const fn add_within_capacity(
130 &mut self,
131 diff: &ResourcesDiff,
132 capacity: &OverallStorageCapacity,
133 ) {
134 macro_rules! add {
135 ($($resource:ident => $storage:ident),+ $(,)?) => {
136 $(
137 let resource = diff.$resource;
138 let storage = capacity.$storage;
139 self.$resource.add_within_capacity(resource, storage);
140 )+
141 };
142 }
143
144 add!(food => silo, iron => warehouse, stone => warehouse, wood => warehouse);
145 }
146
147 pub const fn checked_sub(&self, rhs: &Self) -> Option<Self> {
150 Some(Self {
151 food: self.food.checked_sub(rhs.food)?,
152 iron: self.iron.checked_sub(rhs.iron)?,
153 stone: self.stone.checked_sub(rhs.stone)?,
154 wood: self.wood.checked_sub(rhs.wood)?,
155 })
156 }
157
158 pub const fn sum(&self) -> u32 {
159 0u32
160 .saturating_add(self.food.0)
161 .saturating_add(self.iron.0)
162 .saturating_add(self.stone.0)
163 .saturating_add(self.wood.0)
164 }
165
166 #[inline]
167 pub const fn sum_silo(&self) -> u32 {
168 self.silo().sum()
169 }
170
171 #[inline]
172 pub const fn sum_warehouse(&self) -> u32 {
173 self.warehouse().sum()
174 }
175
176 #[inline]
177 pub const fn is_empty(&self) -> bool {
178 self.sum() == 0
179 }
180}
181
182impl const Default for Resources {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188impl const Add for Resources {
189 type Output = Self;
190
191 fn add(self, rhs: Self) -> Self {
192 Self {
193 food: self.food + rhs.food,
194 iron: self.iron + rhs.iron,
195 stone: self.stone + rhs.stone,
196 wood: self.wood + rhs.wood,
197 }
198 }
199}
200
201impl const Add<&Resources> for Resources {
202 type Output = Self;
203
204 fn add(self, rhs: &Resources) -> Self {
205 Self {
206 food: self.food + rhs.food,
207 iron: self.iron + rhs.iron,
208 stone: self.stone + rhs.stone,
209 wood: self.wood + rhs.wood,
210 }
211 }
212}
213
214impl const AddAssign for Resources {
215 fn add_assign(&mut self, rhs: Self) {
216 *self = Self {
217 food: self.food + rhs.food,
218 iron: self.iron + rhs.iron,
219 stone: self.stone + rhs.stone,
220 wood: self.wood + rhs.wood,
221 };
222 }
223}
224
225impl const AddAssign<&Resources> for Resources {
226 fn add_assign(&mut self, rhs: &Resources) {
227 *self = Self {
228 food: self.food + rhs.food,
229 iron: self.iron + rhs.iron,
230 stone: self.stone + rhs.stone,
231 wood: self.wood + rhs.wood,
232 };
233 }
234}
235
236impl const Sub for Resources {
237 type Output = Self;
238
239 fn sub(self, rhs: Self) -> Self {
240 Self {
241 food: self.food - rhs.food,
242 iron: self.iron - rhs.iron,
243 stone: self.stone - rhs.stone,
244 wood: self.wood - rhs.wood,
245 }
246 }
247}
248
249impl const Sub<&Resources> for Resources {
250 type Output = Self;
251
252 fn sub(self, rhs: &Resources) -> Self {
253 Self {
254 food: self.food - rhs.food,
255 iron: self.iron - rhs.iron,
256 stone: self.stone - rhs.stone,
257 wood: self.wood - rhs.wood,
258 }
259 }
260}
261
262impl const SubAssign for Resources {
263 fn sub_assign(&mut self, rhs: Self) {
264 *self = Self {
265 food: self.food - rhs.food,
266 iron: self.iron - rhs.iron,
267 stone: self.stone - rhs.stone,
268 wood: self.wood - rhs.wood,
269 };
270 }
271}
272
273impl const SubAssign<&Resources> for Resources {
274 fn sub_assign(&mut self, rhs: &Resources) {
275 *self = Self {
276 food: self.food - rhs.food,
277 iron: self.iron - rhs.iron,
278 stone: self.stone - rhs.stone,
279 wood: self.wood - rhs.wood,
280 };
281 }
282}
283
284impl const Mul<u32> for &Resources {
285 type Output = Resources;
286
287 fn mul(self, rhs: u32) -> Self::Output {
288 Resources {
289 food: self.food * rhs,
290 iron: self.iron * rhs,
291 stone: self.stone * rhs,
292 wood: self.wood * rhs,
293 }
294 }
295}
296
297impl const Mul<NonZeroU32> for &Resources {
298 type Output = Resources;
299
300 fn mul(self, rhs: NonZeroU32) -> Self::Output {
301 self * rhs.get()
302 }
303}
304
305macro_rules! decl_resource {
306 ($($resource:ident),+ $(,)?) => {
307 paste::paste! {
308 $(
309 #[derive(Copy, Debug, Display, Deserialize, Serialize, ConstDeref, F64Math)]
310 #[derive_const(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
311 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
312 pub struct $resource(u32);
313
314 impl $resource {
315 pub const MIN: Self = Self::new(0);
316 pub const MAX: Self = Self::new(u32::MAX);
317
318 #[inline]
319 pub const fn new(value: u32) -> Self {
320 Self(value)
321 }
322
323 #[inline]
324 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
325 self.0.checked_sub(rhs.0).map(Self::new)
326 }
327
328 pub const fn add_within_capacity(
329 &mut self,
330 diff: [<$resource Diff>],
331 capacity: StorageCapacity
332 ) {
333 if diff < 0i32 {
334 *self += diff;
335 } else if self.0 < *capacity {
336 let capacity = $resource::from(capacity);
337 *self = (*self + diff).min(capacity);
338 }
339 }
340 }
341
342 impl const From<u32> for $resource {
343 fn from(value: u32) -> Self {
344 Self::new(value)
345 }
346 }
347
348 impl const From<$resource> for u32 {
349 fn from(value: $resource) -> Self {
350 value.0
351 }
352 }
353
354 impl const From<f64> for $resource {
355 fn from(value: f64) -> Self {
356 debug_assert!(value.is_finite());
357 Self(value as u32)
358 }
359 }
360
361 impl const From<$resource> for f64 {
362 fn from(value: $resource) -> Self {
363 f64::from(value.0)
364 }
365 }
366
367 impl const From<MineProduction> for $resource {
368 fn from(value: MineProduction) -> Self {
369 Self(*value)
370 }
371 }
372
373 impl const From<StorageCapacity> for $resource {
374 fn from(value: StorageCapacity) -> Self {
375 Self(*value)
376 }
377 }
378
379 impl const PartialEq<u32> for $resource {
380 fn eq(&self, other: &u32) -> bool {
381 self.0.eq(other)
382 }
383 }
384
385 impl const PartialOrd<u32> for $resource {
386 fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
387 self.0.partial_cmp(other)
388 }
389 }
390
391 impl const Add for $resource {
392 type Output = Self;
393
394 fn add(self, rhs: Self) -> Self {
395 Self(self.0.saturating_add(rhs.0))
396 }
397 }
398
399 impl const Add<u32> for $resource {
400 type Output = Self;
401
402 fn add(self, rhs: u32) -> Self {
403 Self(self.0.saturating_add(rhs))
404 }
405 }
406
407 impl const AddAssign for $resource {
408 fn add_assign(&mut self, rhs: Self) {
409 *self = *self + rhs;
410 }
411 }
412
413 impl const Sub for $resource {
414 type Output = Self;
415
416 fn sub(self, rhs: Self) -> Self {
417 Self(self.0.saturating_sub(rhs.0))
418 }
419 }
420
421 impl const Sub<u32> for $resource {
422 type Output = Self;
423
424 fn sub(self, rhs: u32) -> Self {
425 Self(self.0.saturating_sub(rhs))
426 }
427 }
428
429 impl const SubAssign for $resource {
430 fn sub_assign(&mut self, rhs: Self) {
431 *self = *self - rhs;
432 }
433 }
434
435 impl const Mul<u32> for $resource {
436 type Output = Self;
437
438 fn mul(self, rhs: u32) -> Self::Output {
439 Self(self.0.saturating_mul(rhs))
440 }
441 }
442
443 impl const Mul<NonZeroU32> for $resource {
444 type Output = Self;
445
446 fn mul(self, rhs: NonZeroU32) -> Self::Output {
447 self * rhs.get()
448 }
449 }
450
451 impl const Mul<Stability> for $resource {
452 type Output = $resource;
453
454 fn mul(self, rhs: Stability) -> Self::Output {
455 Self::from(self.mul_ceil(*rhs))
456 }
457 }
458
459 impl const MulAssign<u32> for $resource {
460 fn mul_assign(&mut self, rhs: u32) {
461 *self = *self * rhs;
462 }
463 }
464
465 impl const MulAssign<Stability> for $resource {
466 fn mul_assign(&mut self, rhs: Stability) {
467 *self = *self * rhs;
468 }
469 }
470
471 impl_mul_ceil!($resource);
472 )+
473 }
474 };
475}
476
477decl_resource!(Food, Iron, Stone, Wood);