Skip to main content

nil_core/infrastructure/
storage.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::continent::coord::Coord;
5use crate::error::{Error, Result};
6use crate::infrastructure::building::level::BuildingLevel;
7use crate::infrastructure::building::{Building, StorageId};
8use derive_more::{From, Into};
9use nil_num::growth::growth;
10use nil_util::ConstDeref;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::iter::Sum;
14use std::ops::{Add, AddAssign, Sub, SubAssign};
15
16/// A building that stores resources.
17pub trait Storage: Building {
18  fn storage_id(&self) -> StorageId;
19  /// Storage capacity at the **current** level.
20  fn capacity(&self, stats: &StorageStatsTable) -> Result<StorageCapacity>;
21  /// Storage capacity at its **minimum** level.
22  fn min_capacity(&self) -> StorageCapacity;
23  /// Storage capacity at its **maximum** level.
24  fn max_capacity(&self) -> StorageCapacity;
25}
26
27#[derive(Clone, Debug, Deserialize, Serialize)]
28#[serde(rename_all = "camelCase")]
29#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
30pub struct StorageStats {
31  pub level: BuildingLevel,
32  pub capacity: StorageCapacity,
33}
34
35#[derive(Clone, Debug, Deserialize, Serialize)]
36#[serde(rename_all = "camelCase")]
37#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
38pub struct StorageStatsTable {
39  id: StorageId,
40  table: HashMap<BuildingLevel, StorageStats>,
41}
42
43impl StorageStatsTable {
44  pub(crate) fn new(storage: &dyn Storage) -> Self {
45    let max_level = storage.max_level();
46    let mut table = HashMap::with_capacity(max_level.into());
47
48    let mut capacity = f64::from(storage.min_capacity());
49    let capacity_growth = growth()
50      .floor(capacity)
51      .ceil(storage.max_capacity())
52      .max_level(max_level)
53      .call();
54
55    for level in 1..=u8::from(max_level) {
56      let level = BuildingLevel::new(level);
57      table.insert(
58        level,
59        StorageStats {
60          level,
61          capacity: StorageCapacity::from(capacity.ceil()),
62        },
63      );
64
65      debug_assert!(capacity.is_normal());
66
67      capacity += capacity * capacity_growth;
68    }
69
70    table.shrink_to_fit();
71
72    Self { id: storage.storage_id(), table }
73  }
74
75  #[inline]
76  pub fn id(&self) -> StorageId {
77    self.id
78  }
79
80  #[inline]
81  pub fn get(&self, level: BuildingLevel) -> Result<&StorageStats> {
82    self
83      .table
84      .get(&level)
85      .ok_or(Error::StorageStatsNotFoundForLevel(self.id, level))
86  }
87}
88
89/// Storage capacity of a building.
90#[derive(Clone, Copy, Debug, From, Into, Deserialize, Serialize, ConstDeref)]
91#[derive_const(Default, PartialEq, Eq, PartialOrd, Ord)]
92#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
93pub struct StorageCapacity(u32);
94
95impl StorageCapacity {
96  #[inline]
97  pub const fn new(value: u32) -> Self {
98    Self(value)
99  }
100}
101
102const impl From<f64> for StorageCapacity {
103  fn from(value: f64) -> Self {
104    Self::new(value as u32)
105  }
106}
107
108const impl From<StorageCapacity> for f64 {
109  fn from(value: StorageCapacity) -> Self {
110    f64::from(value.0)
111  }
112}
113
114const impl PartialEq<u32> for StorageCapacity {
115  fn eq(&self, other: &u32) -> bool {
116    self.0.eq(other)
117  }
118}
119
120const impl Add for StorageCapacity {
121  type Output = StorageCapacity;
122
123  fn add(self, rhs: Self) -> Self::Output {
124    Self(self.0.saturating_add(rhs.0))
125  }
126}
127
128const impl Add<u32> for StorageCapacity {
129  type Output = StorageCapacity;
130
131  fn add(self, rhs: u32) -> Self::Output {
132    Self(self.0.saturating_add(rhs))
133  }
134}
135
136const impl AddAssign for StorageCapacity {
137  fn add_assign(&mut self, rhs: Self) {
138    *self = *self + rhs;
139  }
140}
141
142const impl AddAssign<u32> for StorageCapacity {
143  fn add_assign(&mut self, rhs: u32) {
144    *self = *self + rhs;
145  }
146}
147
148const impl Sub for StorageCapacity {
149  type Output = StorageCapacity;
150
151  fn sub(self, rhs: Self) -> Self::Output {
152    Self(self.0.saturating_sub(rhs.0))
153  }
154}
155
156const impl Sub<u32> for StorageCapacity {
157  type Output = StorageCapacity;
158
159  fn sub(self, rhs: u32) -> Self::Output {
160    Self(self.0.saturating_sub(rhs))
161  }
162}
163
164const impl SubAssign for StorageCapacity {
165  fn sub_assign(&mut self, rhs: Self) {
166    *self = *self - rhs;
167  }
168}
169
170const impl SubAssign<u32> for StorageCapacity {
171  fn sub_assign(&mut self, rhs: u32) {
172    *self = *self - rhs;
173  }
174}
175
176impl Sum for StorageCapacity {
177  fn sum<I>(iter: I) -> Self
178  where
179    I: Iterator<Item = Self>,
180  {
181    iter.fold(Self::default(), |acc, capacity| acc + capacity)
182  }
183}
184
185#[derive(Copy, Debug, Deserialize, Serialize)]
186#[derive_const(Clone, Default, PartialEq, Eq)]
187#[serde(rename_all = "camelCase")]
188#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
189pub struct OverallStorageCapacity {
190  pub silo: StorageCapacity,
191  pub warehouse: StorageCapacity,
192}
193
194impl OverallStorageCapacity {
195  pub const fn mean(&self) -> f64 {
196    let Self { silo, warehouse } = *self;
197    let total = f64::from(silo) + f64::from(warehouse);
198    total / 2.0
199  }
200}
201
202const impl Add for OverallStorageCapacity {
203  type Output = OverallStorageCapacity;
204
205  fn add(mut self, rhs: Self) -> Self::Output {
206    self += rhs;
207    self
208  }
209}
210
211const impl AddAssign for OverallStorageCapacity {
212  fn add_assign(&mut self, rhs: Self) {
213    self.silo += rhs.silo;
214    self.warehouse += rhs.warehouse;
215  }
216}
217
218#[derive(Copy, Debug, From, Into, ConstDeref)]
219#[derive_const(Clone, Default, PartialEq, PartialOrd)]
220pub struct StorageCapacityWeight(f64);
221
222#[derive(Clone, Debug)]
223pub struct OverallStorageCapacityWeight {
224  pub coord: Coord,
225  pub silo: StorageCapacityWeight,
226  pub warehouse: StorageCapacityWeight,
227}
228
229impl OverallStorageCapacityWeight {
230  pub const fn new(coord: Coord) -> Self {
231    Self {
232      coord,
233      silo: StorageCapacityWeight::default(),
234      warehouse: StorageCapacityWeight::default(),
235    }
236  }
237}