1mod stability;
5
6use crate::continent::Coord;
7use crate::error::Result;
8use crate::infrastructure::Infrastructure;
9use crate::infrastructure::building::StorageId;
10use crate::infrastructure::stats::InfrastructureStats;
11use crate::infrastructure::storage::OverallStorageCapacity;
12use crate::npc::bot::BotId;
13use crate::npc::precursor::PrecursorId;
14use crate::player::PlayerId;
15use crate::ranking::score::Score;
16use crate::resources::Resources;
17use crate::resources::maintenance::Maintenance;
18use crate::ruler::Ruler;
19use bon::Builder;
20use derive_more::{Deref, DerefMut, From};
21use serde::{Deserialize, Serialize};
22use std::sync::Arc;
23
24pub use stability::Stability;
25
26#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
27#[serde(rename_all = "camelCase")]
28#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
29pub struct City {
30 #[builder(start_fn, into)]
31 coord: Coord,
32
33 #[builder(into)]
34 name: CityName,
35
36 #[builder(into)]
37 owner: Ruler,
38
39 #[builder(default)]
40 infrastructure: Infrastructure,
41
42 #[builder(default)]
43 stability: Stability,
44}
45
46impl City {
47 #[inline]
48 pub fn coord(&self) -> Coord {
49 self.coord
50 }
51
52 #[inline]
53 pub fn name(&self) -> &CityName {
54 &self.name
55 }
56
57 pub(crate) fn name_mut(&mut self) -> &mut CityName {
58 &mut self.name
59 }
60
61 #[inline]
62 pub fn owner(&self) -> &Ruler {
63 &self.owner
64 }
65
66 #[inline]
67 pub fn infrastructure(&self) -> &Infrastructure {
68 &self.infrastructure
69 }
70
71 #[inline]
72 pub fn infrastructure_mut(&mut self) -> &mut Infrastructure {
73 &mut self.infrastructure
74 }
75
76 #[inline]
77 pub fn stability(&self) -> Stability {
78 self.stability
79 }
80
81 pub(crate) fn stability_mut(&mut self) -> &mut Stability {
82 &mut self.stability
83 }
84
85 #[inline]
86 pub fn player(&self) -> Option<PlayerId> {
87 self.owner().player().cloned()
88 }
89
90 #[inline]
92 pub fn is_owned_by_player(&self) -> bool {
93 self.owner.player().is_some()
94 }
95
96 pub fn is_owned_by_player_and<F>(&self, f: F) -> bool
97 where
98 F: FnOnce(&PlayerId) -> bool,
99 {
100 self.owner.player().is_some_and(f)
101 }
102
103 #[inline]
105 pub fn is_owned_by_bot(&self) -> bool {
106 self.owner.bot().is_some()
107 }
108
109 pub fn is_owned_by_bot_and<F>(&self, f: F) -> bool
110 where
111 F: FnOnce(&BotId) -> bool,
112 {
113 self.owner.bot().is_some_and(f)
114 }
115
116 #[inline]
118 pub fn is_owned_by_precursor(&self) -> bool {
119 self.owner.precursor().is_some()
120 }
121
122 pub fn is_owned_by_precursor_and<F>(&self, f: F) -> bool
123 where
124 F: FnOnce(PrecursorId) -> bool,
125 {
126 self.owner.precursor().is_some_and(f)
127 }
128
129 #[inline]
130 pub fn score(&self, stats: &InfrastructureStats) -> Result<Score> {
131 self.infrastructure.score(stats)
132 }
133
134 pub fn round_production(&self, stats: &InfrastructureStats) -> Result<Resources> {
137 let mut resources = self
138 .infrastructure
139 .round_base_production(stats)?;
140
141 resources.food *= self.stability;
142 resources.iron *= self.stability;
143 resources.stone *= self.stability;
144 resources.wood *= self.stability;
145
146 Ok(resources)
147 }
148
149 pub fn maintenance(&self, stats: &InfrastructureStats) -> Result<Maintenance> {
151 self.infrastructure.base_maintenance(stats)
152 }
153
154 pub fn storage_capacity(&self, stats: &InfrastructureStats) -> Result<OverallStorageCapacity> {
155 let silo_stats = stats.storage(StorageId::Silo)?;
156 let warehouse_stats = stats.storage(StorageId::Warehouse)?;
157
158 Ok(OverallStorageCapacity {
159 silo: self
160 .infrastructure
161 .storage(StorageId::Silo)
162 .capacity(silo_stats)?,
163 warehouse: self
164 .infrastructure
165 .storage(StorageId::Warehouse)
166 .capacity(warehouse_stats)?,
167 })
168 }
169}
170
171impl From<&City> for Ruler {
172 fn from(city: &City) -> Self {
173 city.owner.clone()
174 }
175}
176
177#[derive(Clone, Debug, Deref, DerefMut, From, Deserialize, Serialize)]
178#[from(String, &str)]
179#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
180pub struct CityName(String);
181
182impl CityName {
183 pub fn new(name: impl AsRef<str>) -> Self {
184 Self(name.as_ref().to_owned())
185 }
186}
187
188#[derive(Clone, Debug, Deserialize, Serialize)]
190#[serde(rename_all = "camelCase")]
191#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
192pub struct PublicCity {
193 coord: Coord,
194 name: Arc<str>,
195 owner: Ruler,
196}
197
198impl From<&City> for PublicCity {
199 fn from(city: &City) -> Self {
200 Self {
201 coord: city.coord,
202 name: Arc::from(city.name.as_str()),
203 owner: city.owner.clone(),
204 }
205 }
206}
207
208#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
209#[serde(default, rename_all = "camelCase")]
210#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
211#[cfg_attr(feature = "typescript", ts(optional_fields))]
212pub struct CitySearch {
213 #[builder(default, with = FromIterator::from_iter)]
214 #[cfg_attr(feature = "typescript", ts(as = "Option<Vec<Coord>>"))]
215 pub coord: Vec<Coord>,
216
217 #[builder(default, with = FromIterator::from_iter)]
218 #[cfg_attr(feature = "typescript", ts(as = "Option<Vec<CityName>>"))]
219 pub name: Vec<CityName>,
220}
221
222impl From<Coord> for CitySearch {
223 fn from(coord: Coord) -> Self {
224 Self::from_iter([coord])
225 }
226}
227
228impl From<CityName> for CitySearch {
229 fn from(name: CityName) -> Self {
230 Self::from_iter([name])
231 }
232}
233
234impl FromIterator<Coord> for CitySearch {
235 fn from_iter<T>(iter: T) -> Self
236 where
237 T: IntoIterator<Item = Coord>,
238 {
239 Self::builder().coord(iter).build()
240 }
241}
242
243impl FromIterator<CityName> for CitySearch {
244 fn from_iter<T>(iter: T) -> Self
245 where
246 T: IntoIterator<Item = CityName>,
247 {
248 Self::builder().name(iter).build()
249 }
250}