Skip to main content

nil_core/resources/
maintenance.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use super::Food;
5use super::diff::FoodDiff;
6use crate::military::army::Army;
7use crate::military::army::personnel::ArmyPersonnel;
8use crate::military::squad::Squad;
9use crate::military::unit::UnitChunkSize;
10use derive_more::Display;
11use nil_util::ConstDeref;
12use serde::{Deserialize, Serialize};
13use std::cmp::Ordering;
14use std::iter::Sum;
15use std::num::NonZeroU32;
16use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
17
18/// Maintenance tax of an entity.
19///
20/// Its value is equivalent to a percentage of the [base cost].
21///
22/// [base cost]: crate::resources::cost::Cost
23#[derive(Copy, Debug, Display, Deserialize, Serialize, ConstDeref)]
24#[derive_const(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
25#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
26pub struct Maintenance(Food);
27
28impl Maintenance {
29  #[inline]
30  pub const fn new(value: u32) -> Self {
31    Self(Food::new(value))
32  }
33}
34
35impl const From<u32> for Maintenance {
36  fn from(value: u32) -> Self {
37    Self::new(value)
38  }
39}
40
41impl const From<Maintenance> for u32 {
42  fn from(value: Maintenance) -> Self {
43    u32::from(value.0)
44  }
45}
46
47impl const From<f64> for Maintenance {
48  fn from(value: f64) -> Self {
49    debug_assert!(value.is_finite());
50    Self::new(value as u32)
51  }
52}
53
54impl const From<Maintenance> for f64 {
55  fn from(value: Maintenance) -> Self {
56    f64::from(value.0)
57  }
58}
59
60impl<'a> Sum<&'a Squad> for Maintenance {
61  fn sum<I>(iter: I) -> Self
62  where
63    I: Iterator<Item = &'a Squad>,
64  {
65    iter.fold(Maintenance::default(), |mut acc, squad| {
66      acc += squad.maintenance();
67      acc
68    })
69  }
70}
71
72impl<'a> Sum<&'a ArmyPersonnel> for Maintenance {
73  fn sum<I>(iter: I) -> Self
74  where
75    I: Iterator<Item = &'a ArmyPersonnel>,
76  {
77    iter.flat_map(ArmyPersonnel::iter).sum()
78  }
79}
80
81impl<'a> Sum<&'a Army> for Maintenance {
82  fn sum<I>(iter: I) -> Self
83  where
84    I: Iterator<Item = &'a Army>,
85  {
86    iter.flat_map(Army::iter).sum()
87  }
88}
89
90impl const Add for Maintenance {
91  type Output = Self;
92
93  fn add(self, rhs: Self) -> Self {
94    Self(self.0 + rhs.0)
95  }
96}
97
98impl const Add<Food> for Maintenance {
99  type Output = Self;
100
101  fn add(self, rhs: Food) -> Self {
102    Self(self.0 + rhs)
103  }
104}
105
106impl const AddAssign for Maintenance {
107  fn add_assign(&mut self, rhs: Self) {
108    *self = Self(self.0 + rhs.0);
109  }
110}
111
112impl const AddAssign<Food> for Maintenance {
113  fn add_assign(&mut self, rhs: Food) {
114    *self = Self(self.0 + rhs);
115  }
116}
117
118impl const Sub for Maintenance {
119  type Output = Self;
120
121  fn sub(self, rhs: Self) -> Self {
122    Self(self.0 - rhs.0)
123  }
124}
125
126impl const Sub<Food> for Maintenance {
127  type Output = Self;
128
129  fn sub(self, rhs: Food) -> Self {
130    Self(self.0 - rhs)
131  }
132}
133
134impl const Sub<Maintenance> for Food {
135  type Output = Self;
136
137  fn sub(self, rhs: Maintenance) -> Self {
138    self - rhs.0
139  }
140}
141
142impl const Sub<Maintenance> for FoodDiff {
143  type Output = Self;
144
145  fn sub(self, rhs: Maintenance) -> Self {
146    self - rhs.0
147  }
148}
149
150impl const SubAssign for Maintenance {
151  fn sub_assign(&mut self, rhs: Self) {
152    *self = Self(self.0 - rhs.0);
153  }
154}
155
156impl const SubAssign<Food> for Maintenance {
157  fn sub_assign(&mut self, rhs: Food) {
158    *self = Self(self.0 - rhs);
159  }
160}
161
162impl const SubAssign<Maintenance> for Food {
163  fn sub_assign(&mut self, rhs: Maintenance) {
164    *self = *self - rhs.0;
165  }
166}
167
168impl const SubAssign<Maintenance> for FoodDiff {
169  fn sub_assign(&mut self, rhs: Maintenance) {
170    *self = *self - rhs.0;
171  }
172}
173
174impl const Mul<u32> for Maintenance {
175  type Output = Maintenance;
176
177  fn mul(self, rhs: u32) -> Self::Output {
178    Self(self.0 * rhs)
179  }
180}
181
182impl const Mul<NonZeroU32> for Maintenance {
183  type Output = Maintenance;
184
185  fn mul(self, rhs: NonZeroU32) -> Self::Output {
186    Self(self.0 * rhs.get())
187  }
188}
189
190impl const Div<UnitChunkSize> for Maintenance {
191  type Output = Maintenance;
192
193  fn div(self, rhs: UnitChunkSize) -> Self::Output {
194    let maintenance = f64::from(self);
195    let chunk_size = f64::from(rhs);
196    Self::from(maintenance / chunk_size)
197  }
198}
199
200impl const PartialEq<Food> for Maintenance {
201  fn eq(&self, other: &Food) -> bool {
202    self.0.eq(other)
203  }
204}
205
206impl const PartialEq<Maintenance> for Food {
207  fn eq(&self, other: &Maintenance) -> bool {
208    self.eq(&other.0)
209  }
210}
211
212impl const PartialOrd<Food> for Maintenance {
213  fn partial_cmp(&self, other: &Food) -> Option<Ordering> {
214    self.0.partial_cmp(other)
215  }
216}
217
218impl const PartialOrd<Maintenance> for Food {
219  fn partial_cmp(&self, other: &Maintenance) -> Option<Ordering> {
220    self.partial_cmp(&other.0)
221  }
222}
223
224/// Proportion of the base cost that should be used as a maintenance tax.
225#[derive(Clone, Copy, Debug, Deserialize, Serialize, ConstDeref)]
226#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
227pub struct MaintenanceRatio(f64);
228
229impl MaintenanceRatio {
230  #[inline]
231  pub const fn new(ratio: f64) -> Self {
232    debug_assert!(ratio.is_finite());
233    debug_assert!(!ratio.is_subnormal());
234    Self(ratio.clamp(0.0, 1.0))
235  }
236}
237
238impl const From<MaintenanceRatio> for f64 {
239  fn from(value: MaintenanceRatio) -> Self {
240    value.0
241  }
242}
243
244#[derive(Debug, Deserialize, Serialize)]
245#[derive_const(Clone)]
246#[serde(rename_all = "camelCase")]
247#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
248pub struct MaintenanceBalance {
249  pub maintenance: Maintenance,
250  pub production: Food,
251}
252
253impl MaintenanceBalance {
254  /// Checks if the food production is enough to cover the maintenance costs.
255  #[inline]
256  pub const fn is_sustainable(&self) -> bool {
257    self.production >= self.maintenance
258  }
259}
260
261impl const Add<Maintenance> for MaintenanceBalance {
262  type Output = Self;
263
264  fn add(self, rhs: Maintenance) -> Self::Output {
265    Self {
266      maintenance: self.maintenance + rhs,
267      production: self.production,
268    }
269  }
270}