nil_core/infrastructure/building/
wall.rs1use crate::check_total_resource_ratio;
5use crate::error::{Error, Result};
6use crate::infrastructure::building::{BuildingId, BuildingLevel};
7use crate::infrastructure::requirements::InfrastructureRequirements;
8use crate::ranking::score::Score;
9use crate::resources::cost::{Cost, ResourceRatio};
10use crate::resources::maintenance::MaintenanceRatio;
11use crate::resources::workforce::Workforce;
12use derive_more::Deref;
13use nil_core_macros::Building;
14use nil_num::growth::growth;
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17
18#[derive(Building, Clone, Debug, Deserialize, Serialize)]
19#[serde(rename_all = "camelCase")]
20pub struct Wall {
21 level: BuildingLevel,
22 enabled: bool,
23}
24
25impl Wall {
26 pub const ID: BuildingId = BuildingId::Wall;
27
28 pub const MIN_LEVEL: BuildingLevel = BuildingLevel::ZERO;
29 pub const MAX_LEVEL: BuildingLevel = BuildingLevel::new(20);
30
31 pub const MIN_COST: Cost = Cost::new(1_200);
32 pub const MAX_COST: Cost = Cost::new(50_000);
33
34 pub const WOOD_RATIO: ResourceRatio = ResourceRatio::new(0.3);
35 pub const STONE_RATIO: ResourceRatio = ResourceRatio::new(0.5);
36 pub const IRON_RATIO: ResourceRatio = ResourceRatio::new(0.2);
37 pub const MAINTENANCE_RATIO: MaintenanceRatio = MaintenanceRatio::new(0.005);
38
39 pub const MIN_WORKFORCE: Workforce = Workforce::new(2);
40 pub const MAX_WORKFORCE: Workforce = Workforce::new(200);
41
42 pub const MIN_DEFENSE: WallDefense = WallDefense::new(500);
43 pub const MAX_DEFENSE: WallDefense = WallDefense::new(10000);
44
45 pub const MIN_DEFENSE_BONUS: WallDefenseBonus = WallDefenseBonus::new(5.0);
46 pub const MAX_DEFENSE_BONUS: WallDefenseBonus = WallDefenseBonus::new(107.0);
47
48 pub const MIN_SCORE: Score = Score::new(8);
49 pub const MAX_SCORE: Score = Score::new(256);
50
51 pub const INFRASTRUCTURE_REQUIREMENTS: InfrastructureRequirements =
52 InfrastructureRequirements::builder()
53 .prefecture(BuildingLevel::new(3))
54 .academy(BuildingLevel::new(1))
55 .build();
56}
57
58impl Default for Wall {
59 fn default() -> Self {
60 Self {
61 level: BuildingLevel::ZERO,
62 enabled: true,
63 }
64 }
65}
66
67check_total_resource_ratio!(Wall::WOOD_RATIO, Wall::STONE_RATIO, Wall::IRON_RATIO);
68
69#[derive(Clone, Copy, Debug, Deref, Deserialize, Serialize)]
70pub struct WallDefense(u32);
71
72impl WallDefense {
73 #[inline]
74 pub const fn new(value: u32) -> Self {
75 Self(value)
76 }
77}
78
79impl From<WallDefense> for f64 {
80 fn from(value: WallDefense) -> Self {
81 f64::from(value.0)
82 }
83}
84
85impl From<f64> for WallDefense {
86 fn from(value: f64) -> Self {
87 Self::new(value as u32)
88 }
89}
90
91#[derive(Clone, Copy, Debug, Deref, Deserialize, Serialize)]
92pub struct WallDefenseBonus(f64);
93
94impl WallDefenseBonus {
95 pub const MIN: WallDefenseBonus = WallDefenseBonus(0.0);
96 #[inline]
97 pub const fn new(value: f64) -> Self {
98 Self(value.max(Self::MIN.0))
99 }
100}
101
102impl From<WallDefenseBonus> for f64 {
103 fn from(value: WallDefenseBonus) -> Self {
104 value.0
105 }
106}
107
108impl From<f64> for WallDefenseBonus {
109 fn from(value: f64) -> Self {
110 Self::new(value)
111 }
112}
113
114#[derive(Clone, Debug, Deserialize, Serialize)]
115#[serde(rename_all = "camelCase")]
116pub struct WallStats {
117 pub level: BuildingLevel,
118 pub defense: WallDefense,
119 pub defense_percent: WallDefenseBonus,
120}
121
122#[derive(Clone, Debug, Deserialize, Serialize)]
123pub struct WallStatsTable(HashMap<BuildingLevel, WallStats>);
124
125impl WallStatsTable {
126 pub fn new() -> Self {
127 let max_level = Wall::MAX_LEVEL;
128 let mut table = HashMap::with_capacity((*max_level).into());
129
130 let mut defense = f64::from(Wall::MIN_DEFENSE);
131 let defense_growth = growth()
132 .floor(defense)
133 .ceil(f64::from(Wall::MAX_DEFENSE))
134 .max_level(max_level)
135 .call();
136
137 let mut defense_percent = f64::from(Wall::MIN_DEFENSE_BONUS);
138 let defense_percent_growth = growth()
139 .floor(defense_percent)
140 .ceil(f64::from(Wall::MAX_DEFENSE_BONUS))
141 .max_level(max_level)
142 .call();
143
144 for level in 1..=max_level.0 {
145 let level = BuildingLevel::new(level);
146
147 table.insert(
148 level,
149 WallStats {
150 level,
151 defense: WallDefense::from(defense.round()),
152 defense_percent: WallDefenseBonus::from(defense_percent.round()),
153 },
154 );
155
156 defense += defense * defense_growth;
157 defense_percent += defense_percent * defense_percent_growth;
158 }
159
160 table.shrink_to_fit();
161
162 Self(table)
163 }
164
165 #[inline]
166 pub fn get(&self, level: BuildingLevel) -> Result<&WallStats> {
167 self
168 .0
169 .get(&level)
170 .ok_or(Error::WallStatsNotFoundForLevel(level))
171 }
172}
173
174impl Default for WallStatsTable {
175 fn default() -> Self {
176 Self::new()
177 }
178}