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