use crate::check_total_resource_ratio;
use crate::error::{Error, Result};
use crate::infrastructure::building::BuildingId;
use crate::infrastructure::building::level::BuildingLevel;
use crate::infrastructure::requirements::InfrastructureRequirements;
use crate::ranking::score::Score;
use crate::resources::cost::{Cost, ResourceRatio};
use crate::resources::maintenance::MaintenanceRatio;
use crate::resources::workforce::Workforce;
use derive_more::Deref;
use nil_core_macros::Building;
use nil_num::growth::growth;
use nil_util::F64Math;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Building, Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct Wall {
level: BuildingLevel,
enabled: bool,
}
impl Wall {
pub const ID: BuildingId = BuildingId::Wall;
pub const MIN_LEVEL: BuildingLevel = BuildingLevel::ZERO;
pub const MAX_LEVEL: BuildingLevel = BuildingLevel::new(20);
pub const MIN_COST: Cost = Cost::new(1_200);
pub const MAX_COST: Cost = Cost::new(50_000);
pub const FOOD_RATIO: ResourceRatio = ResourceRatio::new(0.0);
pub const IRON_RATIO: ResourceRatio = ResourceRatio::new(0.2);
pub const STONE_RATIO: ResourceRatio = ResourceRatio::new(0.5);
pub const WOOD_RATIO: ResourceRatio = ResourceRatio::new(0.3);
pub const MAINTENANCE_RATIO: MaintenanceRatio = MaintenanceRatio::new(0.005);
pub const MIN_WORKFORCE: Workforce = Workforce::new(2);
pub const MAX_WORKFORCE: Workforce = Workforce::new(200);
pub const MIN_DEFENSE: WallDefense = WallDefense::new(500);
pub const MAX_DEFENSE: WallDefense = WallDefense::new(10_000);
pub const MIN_DEFENSE_BONUS: WallDefenseBonus = WallDefenseBonus::new(5.0);
pub const MAX_DEFENSE_BONUS: WallDefenseBonus = WallDefenseBonus::new(110.0);
pub const MIN_SCORE: Score = Score::new(8);
pub const MAX_SCORE: Score = Score::new(256);
pub const INFRASTRUCTURE_REQUIREMENTS: InfrastructureRequirements =
InfrastructureRequirements::builder()
.prefecture(BuildingLevel::new(3))
.academy(BuildingLevel::new(1))
.build();
}
impl const Default for Wall {
fn default() -> Self {
Self {
level: BuildingLevel::ZERO,
enabled: true,
}
}
}
check_total_resource_ratio!(
Wall::FOOD_RATIO,
Wall::IRON_RATIO,
Wall::STONE_RATIO,
Wall::WOOD_RATIO
);
#[derive(Clone, Copy, Debug, Deref, Deserialize, Serialize, F64Math)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct WallDefense(u32);
impl WallDefense {
#[inline]
pub const fn new(value: u32) -> Self {
Self(value)
}
}
impl const From<WallDefense> for f64 {
fn from(value: WallDefense) -> Self {
f64::from(value.0)
}
}
impl const From<f64> for WallDefense {
fn from(value: f64) -> Self {
debug_assert!(value >= 0.0);
debug_assert!(value.is_finite());
Self::new(value as u32)
}
}
#[derive(Clone, Copy, Debug, Deref, Deserialize, Serialize, F64Math)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct WallDefenseBonus(f64);
impl WallDefenseBonus {
pub const MIN: WallDefenseBonus = WallDefenseBonus(0.0);
#[inline]
pub const fn new(value: f64) -> Self {
Self(value.max(Self::MIN.0))
}
}
impl const From<WallDefenseBonus> for f64 {
fn from(value: WallDefenseBonus) -> Self {
value.0
}
}
impl const From<f64> for WallDefenseBonus {
fn from(value: f64) -> Self {
Self::new(value)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct WallStats {
pub level: BuildingLevel,
pub defense: WallDefense,
pub defense_percent: WallDefenseBonus,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct WallStatsTable(HashMap<BuildingLevel, WallStats>);
impl WallStatsTable {
pub fn new() -> Self {
let max_level = Wall::MAX_LEVEL;
let mut table = HashMap::with_capacity((*max_level).into());
let mut defense = f64::from(Wall::MIN_DEFENSE);
let defense_growth = growth()
.floor(defense)
.ceil(f64::from(Wall::MAX_DEFENSE))
.max_level(max_level)
.call();
let mut defense_percent = f64::from(Wall::MIN_DEFENSE_BONUS);
let defense_percent_growth = growth()
.floor(defense_percent)
.ceil(f64::from(Wall::MAX_DEFENSE_BONUS))
.max_level(max_level)
.call();
for level in 1..=u8::from(max_level) {
let level = BuildingLevel::new(level);
table.insert(
level,
WallStats {
level,
defense: WallDefense::from(defense.round()),
defense_percent: WallDefenseBonus::from(defense_percent.round()),
},
);
defense += defense * defense_growth;
defense_percent += defense_percent * defense_percent_growth;
}
table.shrink_to_fit();
Self(table)
}
#[inline]
pub fn get(&self, level: BuildingLevel) -> Result<&WallStats> {
self
.0
.get(&level)
.ok_or(Error::WallStatsNotFoundForLevel(level))
}
}
impl Default for WallStatsTable {
fn default() -> Self {
Self::new()
}
}