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