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 nil_num::F64Ops,
316 )]
317 #[into(u32, f64)]
318 pub struct $resource(u32);
319
320 impl $resource {
321 pub const MIN: Self = Self::new(0);
322 pub const MAX: Self = Self::new(u32::MAX);
323
324 #[inline]
325 pub const fn new(value: u32) -> Self {
326 Self(value)
327 }
328
329 #[inline]
330 pub fn checked_sub(self, rhs: Self) -> Option<Self> {
331 self.0.checked_sub(rhs.0).map(Self::new)
332 }
333
334 pub fn add_within_capacity(&mut self, diff: [<$resource Diff>], capacity: StorageCapacity) {
335 if diff < 0i32 {
336 *self += diff;
337 } else if self.0 < *capacity {
338 let capacity = $resource::from(capacity);
339 *self = (*self + diff).min(capacity);
340 }
341 }
342 }
343
344 impl From<u32> for $resource {
345 fn from(value: u32) -> Self {
346 Self(value)
347 }
348 }
349
350 impl From<f64> for $resource {
351 fn from(value: f64) -> Self {
352 debug_assert!(value.is_finite());
353 Self(value as u32)
354 }
355 }
356
357 impl From<MineProduction> for $resource {
358 fn from(value: MineProduction) -> Self {
359 Self(*value)
360 }
361 }
362
363 impl From<StorageCapacity> for $resource {
364 fn from(value: StorageCapacity) -> Self {
365 Self(*value)
366 }
367 }
368
369 impl PartialEq<u32> for $resource {
370 fn eq(&self, other: &u32) -> bool {
371 self.0.eq(other)
372 }
373 }
374
375 impl PartialOrd<u32> for $resource {
376 fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
377 self.0.partial_cmp(other)
378 }
379 }
380
381 impl Add for $resource {
382 type Output = Self;
383
384 fn add(self, rhs: Self) -> Self {
385 Self(self.0.saturating_add(rhs.0))
386 }
387 }
388
389 impl Add<u32> for $resource {
390 type Output = Self;
391
392 fn add(self, rhs: u32) -> Self {
393 Self(self.0.saturating_add(rhs))
394 }
395 }
396
397 impl AddAssign for $resource {
398 fn add_assign(&mut self, rhs: Self) {
399 *self = *self + rhs;
400 }
401 }
402
403 impl Sub for $resource {
404 type Output = Self;
405
406 fn sub(self, rhs: Self) -> Self {
407 Self(self.0.saturating_sub(rhs.0))
408 }
409 }
410
411 impl Sub<u32> for $resource {
412 type Output = Self;
413
414 fn sub(self, rhs: u32) -> Self {
415 Self(self.0.saturating_sub(rhs))
416 }
417 }
418
419 impl SubAssign for $resource {
420 fn sub_assign(&mut self, rhs: Self) {
421 *self = *self - rhs;
422 }
423 }
424
425 impl Mul<u32> for $resource {
426 type Output = Self;
427
428 fn mul(self, rhs: u32) -> Self::Output {
429 Self(self.0.saturating_mul(rhs))
430 }
431 }
432
433 impl Mul<NonZeroU32> for $resource {
434 type Output = Self;
435
436 fn mul(self, rhs: NonZeroU32) -> Self::Output {
437 self * rhs.get()
438 }
439 }
440
441 impl Mul<Stability> for $resource {
442 type Output = $resource;
443
444 fn mul(self, rhs: Stability) -> Self::Output {
445 Self::from(self.mul_ceil(*rhs))
446 }
447 }
448
449 impl MulAssign<u32> for $resource {
450 fn mul_assign(&mut self, rhs: u32) {
451 *self = *self * rhs;
452 }
453 }
454
455 impl MulAssign<Stability> for $resource {
456 fn mul_assign(&mut self, rhs: Stability) {
457 *self = *self * rhs;
458 }
459 }
460
461 impl_mul_ceil!($resource);
462 )+
463 }
464 };
465}
466
467decl_resource!(Food, Iron, Stone, Wood);