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