Skip to main content

nil_core/city/
mod.rs

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