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