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
4mod 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")]
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().cloned()
87  }
88
89  /// Checks whether the city belongs to a player.
90  #[inline]
91  pub fn is_owned_by_player(&self) -> bool {
92    self.owner.player().is_some()
93  }
94
95  pub fn is_owned_by_player_and<F>(&self, f: F) -> bool
96  where
97    F: FnOnce(&PlayerId) -> bool,
98  {
99    self.owner.player().is_some_and(f)
100  }
101
102  /// Checks whether the city belongs to a bot.
103  #[inline]
104  pub fn is_owned_by_bot(&self) -> bool {
105    self.owner.bot().is_some()
106  }
107
108  pub fn is_owned_by_bot_and<F>(&self, f: F) -> bool
109  where
110    F: FnOnce(&BotId) -> bool,
111  {
112    self.owner.bot().is_some_and(f)
113  }
114
115  /// Checks whether the city belongs to a precursor.
116  #[inline]
117  pub fn is_owned_by_precursor(&self) -> bool {
118    self.owner.precursor().is_some()
119  }
120
121  pub fn is_owned_by_precursor_and<F>(&self, f: F) -> bool
122  where
123    F: FnOnce(PrecursorId) -> bool,
124  {
125    self.owner.precursor().is_some_and(f)
126  }
127
128  #[inline]
129  pub fn score(&self, stats: &InfrastructureStats) -> Result<Score> {
130    self.infrastructure.score(stats)
131  }
132
133  /// Determines the amount of resources generated by the city's mines
134  /// while applying all relevant modifiers, such as city stability.
135  pub fn round_production(&self, stats: &InfrastructureStats) -> Result<Resources> {
136    let mut resources = self
137      .infrastructure
138      .round_base_production(stats)?;
139
140    resources.food *= self.stability;
141    resources.iron *= self.stability;
142    resources.stone *= self.stability;
143    resources.wood *= self.stability;
144
145    Ok(resources)
146  }
147
148  /// Determines the maintenance tax required for the city buildings.
149  pub fn maintenance(&self, stats: &InfrastructureStats) -> Result<Maintenance> {
150    self.infrastructure.base_maintenance(stats)
151  }
152
153  pub fn storage_capacity(&self, stats: &InfrastructureStats) -> Result<OverallStorageCapacity> {
154    let silo_stats = stats.storage(StorageId::Silo)?;
155    let warehouse_stats = stats.storage(StorageId::Warehouse)?;
156
157    Ok(OverallStorageCapacity {
158      silo: self
159        .infrastructure
160        .storage(StorageId::Silo)
161        .capacity(silo_stats)?,
162      warehouse: self
163        .infrastructure
164        .storage(StorageId::Warehouse)
165        .capacity(warehouse_stats)?,
166    })
167  }
168}
169
170impl From<&City> for Ruler {
171  fn from(city: &City) -> Self {
172    city.owner.clone()
173  }
174}
175
176#[derive(Clone, Debug, Deref, DerefMut, From, Deserialize, Serialize)]
177#[from(String, &str)]
178pub struct CityName(String);
179
180impl CityName {
181  pub fn new(name: impl AsRef<str>) -> Self {
182    Self(name.as_ref().to_owned())
183  }
184}
185
186/// Public data about a city, to which any player can have access.
187#[derive(Clone, Debug, Deserialize, Serialize)]
188#[serde(rename_all = "camelCase")]
189pub struct PublicCity {
190  coord: Coord,
191  name: Arc<str>,
192  owner: Ruler,
193}
194
195impl From<&City> for PublicCity {
196  fn from(city: &City) -> Self {
197    Self {
198      coord: city.coord,
199      name: Arc::from(city.name.as_str()),
200      owner: city.owner.clone(),
201    }
202  }
203}
204
205#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
206#[serde(default, rename_all = "camelCase")]
207pub struct CitySearch {
208  #[builder(default, with = FromIterator::from_iter)]
209  pub coord: Vec<Coord>,
210  #[builder(default, with = FromIterator::from_iter)]
211  pub name: Vec<CityName>,
212}
213
214impl From<Coord> for CitySearch {
215  fn from(coord: Coord) -> Self {
216    Self::from_iter([coord])
217  }
218}
219
220impl From<CityName> for CitySearch {
221  fn from(name: CityName) -> Self {
222    Self::from_iter([name])
223  }
224}
225
226impl FromIterator<Coord> for CitySearch {
227  fn from_iter<T>(iter: T) -> Self
228  where
229    T: IntoIterator<Item = Coord>,
230  {
231    Self::builder().coord(iter).build()
232  }
233}
234
235impl FromIterator<CityName> for CitySearch {
236  fn from_iter<T>(iter: T) -> Self
237  where
238    T: IntoIterator<Item = CityName>,
239  {
240    Self::builder().name(iter).build()
241  }
242}