Skip to main content

nil_core/infrastructure/
mine.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::error::{Error, Result};
5use crate::infrastructure::building::level::BuildingLevel;
6use crate::infrastructure::building::{Building, MineId};
7use crate::world::config::WorldConfig;
8use derive_more::Into;
9use nil_num::growth::growth;
10use nil_util::{ConstDeref, F64Math};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14pub trait Mine: Building {
15  fn mine_id(&self) -> MineId;
16
17  /// Amount of resources generated by the mine at its **current** level.
18  fn production(&self, stats: &MineStatsTable) -> Result<MineProduction>;
19  /// Amount of resources generated by the mine at its **minimum** level.
20  fn min_production(&self) -> MineProduction;
21  /// Amount of resources generated by the mine at its **maximum** level.
22  fn max_production(&self) -> MineProduction;
23}
24
25#[derive(Clone, Debug, Deserialize, Serialize)]
26#[serde(rename_all = "camelCase")]
27#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
28pub struct MineStats {
29  pub level: BuildingLevel,
30  pub production: MineProduction,
31}
32
33#[derive(Clone, Debug, Deserialize, Serialize)]
34#[serde(rename_all = "camelCase")]
35#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
36pub struct MineStatsTable {
37  id: MineId,
38  table: HashMap<BuildingLevel, MineStats>,
39}
40
41impl MineStatsTable {
42  pub(crate) fn new(config: &WorldConfig, mine: &dyn Mine) -> Self {
43    let max_level = *mine.max_level();
44    let mut table = HashMap::with_capacity((max_level).into());
45
46    let speed = f64::from(config.speed());
47    let mut production = mine.min_production() * speed;
48
49    let production_growth = growth()
50      .floor(production)
51      .ceil(mine.max_production() * speed)
52      .max_level(max_level)
53      .call();
54
55    for level in 1..=max_level {
56      let level = BuildingLevel::new(level);
57      table.insert(
58        level,
59        MineStats {
60          level,
61          production: MineProduction::from(production.round()),
62        },
63      );
64
65      debug_assert!(production.is_normal());
66
67      production += production * production_growth;
68    }
69
70    table.shrink_to_fit();
71
72    Self { id: mine.mine_id(), table }
73  }
74
75  #[inline]
76  pub fn id(&self) -> MineId {
77    self.id
78  }
79
80  #[inline]
81  pub fn get(&self, level: BuildingLevel) -> Result<&MineStats> {
82    self
83      .table
84      .get(&level)
85      .ok_or(Error::MineStatsNotFoundForLevel(self.id, level))
86  }
87}
88
89/// Amount of resources generated by a mine in a single round.
90#[derive(Copy, Debug, Into, Deserialize, Serialize, ConstDeref, F64Math)]
91#[derive_const(Clone, PartialEq, Eq, PartialOrd, Ord)]
92#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
93pub struct MineProduction(u32);
94
95impl MineProduction {
96  #[inline]
97  pub const fn new(value: u32) -> Self {
98    Self(value)
99  }
100}
101
102impl const From<f64> for MineProduction {
103  fn from(value: f64) -> Self {
104    Self::new(value as u32)
105  }
106}
107
108impl const From<MineProduction> for f64 {
109  fn from(value: MineProduction) -> Self {
110    f64::from(value.0)
111  }
112}