use crate::error::{Error, Result};
use crate::infrastructure::building::level::BuildingLevel;
use crate::infrastructure::building::{Building, MineId};
use crate::world::config::WorldConfig;
use derive_more::Into;
use nil_num::growth::growth;
use nil_util::{ConstDeref, F64Math};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub trait Mine: Building {
fn mine_id(&self) -> MineId;
fn production(&self, stats: &MineStatsTable) -> Result<MineProduction>;
fn min_production(&self) -> MineProduction;
fn max_production(&self) -> MineProduction;
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct MineStats {
pub level: BuildingLevel,
pub production: MineProduction,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct MineStatsTable {
id: MineId,
table: HashMap<BuildingLevel, MineStats>,
}
impl MineStatsTable {
pub(crate) fn new(config: &WorldConfig, mine: &dyn Mine) -> Self {
let max_level = *mine.max_level();
let mut table = HashMap::with_capacity((max_level).into());
let speed = f64::from(config.speed());
let mut production = mine.min_production() * speed;
let production_growth = growth()
.floor(production)
.ceil(mine.max_production() * speed)
.max_level(max_level)
.call();
for level in 1..=max_level {
let level = BuildingLevel::new(level);
table.insert(
level,
MineStats {
level,
production: MineProduction::from(production.round()),
},
);
debug_assert!(production.is_normal());
production += production * production_growth;
}
table.shrink_to_fit();
Self { id: mine.mine_id(), table }
}
#[inline]
pub fn id(&self) -> MineId {
self.id
}
#[inline]
pub fn get(&self, level: BuildingLevel) -> Result<&MineStats> {
self
.table
.get(&level)
.ok_or(Error::MineStatsNotFoundForLevel(self.id, level))
}
}
#[derive(Copy, Debug, Into, Deserialize, Serialize, ConstDeref, F64Math)]
#[derive_const(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct MineProduction(u32);
impl MineProduction {
#[inline]
pub const fn new(value: u32) -> Self {
Self(value)
}
}
impl const From<f64> for MineProduction {
fn from(value: f64) -> Self {
Self::new(value as u32)
}
}
impl const From<MineProduction> for f64 {
fn from(value: MineProduction) -> Self {
f64::from(value.0)
}
}