Skip to main content

nil_core/resources/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4pub 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  /// Minimum possible amount of resources.
41  pub const MIN: Self = Self {
42    food: Food::MIN,
43    iron: Iron::MIN,
44    stone: Stone::MIN,
45    wood: Wood::MIN,
46  };
47
48  /// Maximum possible amount of resources.
49  pub const MAX: Self = Self {
50    food: Food::MAX,
51    iron: Iron::MAX,
52    stone: Stone::MAX,
53    wood: Wood::MAX,
54  };
55
56  /// Default amount of resources for a player.
57  pub const PLAYER: Self = Self::splat(800);
58
59  /// Default amount of resources for a bot.
60  pub const BOT: Self = Self::splat(2500);
61
62  /// Default amount of resources for a precursor.
63  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  /// Checked resource subtraction.
140  /// Returns `None` if there are not enough resources available.
141  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);