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
35const impl From<u32> for Maintenance {
36  fn from(value: u32) -> Self {
37    Self::new(value)
38  }
39}
40
41const impl From<Maintenance> for u32 {
42  fn from(value: Maintenance) -> Self {
43    u32::from(value.0)
44  }
45}
46
47const impl 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
54const impl 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(), |acc, squad| {
66      acc + squad.maintenance()
67    })
68  }
69}
70
71impl<'a> Sum<&'a ArmyPersonnel> for Maintenance {
72  fn sum<I>(iter: I) -> Self
73  where
74    I: Iterator<Item = &'a ArmyPersonnel>,
75  {
76    iter.flat_map(ArmyPersonnel::iter).sum()
77  }
78}
79
80impl<'a> Sum<&'a Army> for Maintenance {
81  fn sum<I>(iter: I) -> Self
82  where
83    I: Iterator<Item = &'a Army>,
84  {
85    iter.flat_map(Army::iter).sum()
86  }
87}
88
89const impl Add for Maintenance {
90  type Output = Self;
91
92  fn add(self, rhs: Self) -> Self {
93    Self(self.0 + rhs.0)
94  }
95}
96
97const impl Add<Food> for Maintenance {
98  type Output = Self;
99
100  fn add(self, rhs: Food) -> Self {
101    Self(self.0 + rhs)
102  }
103}
104
105const impl AddAssign for Maintenance {
106  fn add_assign(&mut self, rhs: Self) {
107    *self = Self(self.0 + rhs.0);
108  }
109}
110
111const impl AddAssign<Food> for Maintenance {
112  fn add_assign(&mut self, rhs: Food) {
113    *self = Self(self.0 + rhs);
114  }
115}
116
117const impl Sub for Maintenance {
118  type Output = Self;
119
120  fn sub(self, rhs: Self) -> Self {
121    Self(self.0 - rhs.0)
122  }
123}
124
125const impl Sub<Food> for Maintenance {
126  type Output = Self;
127
128  fn sub(self, rhs: Food) -> Self {
129    Self(self.0 - rhs)
130  }
131}
132
133const impl Sub<Maintenance> for Food {
134  type Output = Self;
135
136  fn sub(self, rhs: Maintenance) -> Self {
137    self - rhs.0
138  }
139}
140
141const impl Sub<Maintenance> for FoodDiff {
142  type Output = Self;
143
144  fn sub(self, rhs: Maintenance) -> Self {
145    self - rhs.0
146  }
147}
148
149const impl SubAssign for Maintenance {
150  fn sub_assign(&mut self, rhs: Self) {
151    *self = Self(self.0 - rhs.0);
152  }
153}
154
155const impl SubAssign<Food> for Maintenance {
156  fn sub_assign(&mut self, rhs: Food) {
157    *self = Self(self.0 - rhs);
158  }
159}
160
161const impl SubAssign<Maintenance> for Food {
162  fn sub_assign(&mut self, rhs: Maintenance) {
163    *self = *self - rhs.0;
164  }
165}
166
167const impl SubAssign<Maintenance> for FoodDiff {
168  fn sub_assign(&mut self, rhs: Maintenance) {
169    *self = *self - rhs.0;
170  }
171}
172
173const impl Mul<u32> for Maintenance {
174  type Output = Maintenance;
175
176  fn mul(self, rhs: u32) -> Self::Output {
177    Self(self.0 * rhs)
178  }
179}
180
181const impl Mul<NonZeroU32> for Maintenance {
182  type Output = Maintenance;
183
184  fn mul(self, rhs: NonZeroU32) -> Self::Output {
185    Self(self.0 * rhs.get())
186  }
187}
188
189const impl Div<UnitChunkSize> for Maintenance {
190  type Output = Maintenance;
191
192  fn div(self, rhs: UnitChunkSize) -> Self::Output {
193    let maintenance = f64::from(self);
194    let chunk_size = f64::from(rhs);
195    Self::from(maintenance / chunk_size)
196  }
197}
198
199const impl PartialEq<Food> for Maintenance {
200  fn eq(&self, other: &Food) -> bool {
201    self.0.eq(other)
202  }
203}
204
205const impl PartialEq<Maintenance> for Food {
206  fn eq(&self, other: &Maintenance) -> bool {
207    self.eq(&other.0)
208  }
209}
210
211const impl PartialOrd<Food> for Maintenance {
212  fn partial_cmp(&self, other: &Food) -> Option<Ordering> {
213    self.0.partial_cmp(other)
214  }
215}
216
217const impl PartialOrd<Maintenance> for Food {
218  fn partial_cmp(&self, other: &Maintenance) -> Option<Ordering> {
219    self.partial_cmp(&other.0)
220  }
221}
222
223/// Proportion of the base cost that should be used as a maintenance tax.
224#[derive(Copy, Debug, Display, Deserialize, Serialize, ConstDeref)]
225#[derive_const(Clone, PartialEq, PartialOrd)]
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
238const impl 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
261const impl 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}