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            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);