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 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  /// Minimum possible amount of resources.
45  pub const MIN: Self = Self {
46    food: Food::MIN,
47    iron: Iron::MIN,
48    stone: Stone::MIN,
49    wood: Wood::MIN,
50  };
51
52  /// Maximum possible amount of resources.
53  pub const MAX: Self = Self {
54    food: Food::MAX,
55    iron: Iron::MAX,
56    stone: Stone::MAX,
57    wood: Wood::MAX,
58  };
59
60  /// Default amount of resources for a player.
61  pub const PLAYER: Self = Self::splat(800);
62
63  /// Default amount of resources for a bot.
64  pub const BOT: Self = Self::splat(2500);
65
66  /// Default amount of resources for a precursor.
67  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  /// Checked resource subtraction.
148  /// Returns `None` if there are not enough resources available.
149  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            debug_assert!(value >= 0.0);
358            Self(value.trunc() as u32)
359          }
360        }
361
362        impl const From<$resource> for f64 {
363          fn from(value: $resource) -> Self {
364            f64::from(value.0)
365          }
366        }
367
368        impl const From<MineProduction> for $resource {
369          fn from(value: MineProduction) -> Self {
370            Self(*value)
371          }
372        }
373
374        impl const From<StorageCapacity> for $resource {
375          fn from(value: StorageCapacity) -> Self {
376            Self(*value)
377          }
378        }
379
380        impl const PartialEq<u32> for $resource {
381          fn eq(&self, other: &u32) -> bool {
382            self.0.eq(other)
383          }
384        }
385
386        impl const PartialOrd<u32> for $resource {
387          fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
388            self.0.partial_cmp(other)
389          }
390        }
391
392        impl const Add for $resource {
393          type Output = Self;
394
395          fn add(self, rhs: Self) -> Self {
396            Self(self.0.saturating_add(rhs.0))
397          }
398        }
399
400        impl const Add<u32> for $resource {
401          type Output = Self;
402
403          fn add(self, rhs: u32) -> Self {
404            Self(self.0.saturating_add(rhs))
405          }
406        }
407
408        impl const AddAssign for $resource {
409          fn add_assign(&mut self, rhs: Self) {
410            *self = *self + rhs;
411          }
412        }
413
414        impl const Sub for $resource {
415          type Output = Self;
416
417          fn sub(self, rhs: Self) -> Self {
418            Self(self.0.saturating_sub(rhs.0))
419          }
420        }
421
422        impl const Sub<u32> for $resource {
423          type Output = Self;
424
425          fn sub(self, rhs: u32) -> Self {
426            Self(self.0.saturating_sub(rhs))
427          }
428        }
429
430        impl const SubAssign for $resource {
431          fn sub_assign(&mut self, rhs: Self) {
432            *self = *self - rhs;
433          }
434        }
435
436        impl const Mul<u32> for $resource {
437          type Output = Self;
438
439          fn mul(self, rhs: u32) -> Self::Output {
440            Self(self.0.saturating_mul(rhs))
441          }
442        }
443
444        impl const Mul<NonZeroU32> for $resource {
445          type Output = Self;
446
447          fn mul(self, rhs: NonZeroU32) -> Self::Output {
448            self * rhs.get()
449          }
450        }
451
452        impl const Mul<Stability> for $resource {
453          type Output = $resource;
454
455          fn mul(self, rhs: Stability) -> Self::Output {
456            Self::from(self.mul_ceil(*rhs))
457          }
458        }
459
460        impl const MulAssign<u32> for $resource {
461          fn mul_assign(&mut self, rhs: u32) {
462            *self = *self * rhs;
463          }
464        }
465
466        impl const MulAssign<Stability> for $resource {
467          fn mul_assign(&mut self, rhs: Stability) {
468            *self = *self * rhs;
469          }
470        }
471
472        impl_mul_ceil!($resource);
473      )+
474    }
475  };
476}
477
478decl_resource!(Food, Iron, Stone, Wood);