Skip to main content

nil_core/continent/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4pub mod coord;
5pub mod distance;
6pub mod field;
7pub mod index;
8pub mod size;
9
10#[cfg(test)]
11mod tests;
12
13use crate::city::{City, CitySearch};
14use crate::continent::coord::Coord;
15use crate::continent::distance::Distance;
16use crate::continent::field::Field;
17use crate::continent::index::{ContinentIndex, ContinentKey};
18use crate::continent::size::ContinentSize;
19use crate::error::{Error, Result};
20use crate::ruler::Ruler;
21use serde::{Deserialize, Serialize};
22
23#[derive(Clone, Debug, Deserialize, Serialize)]
24#[serde(rename_all = "camelCase")]
25#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
26pub struct Continent {
27  fields: Box<[Field]>,
28  size: ContinentSize,
29}
30
31impl Continent {
32  pub(crate) fn new(size: u8) -> Self {
33    let size = ContinentSize::new(size);
34    let capacity = usize::from(size.get()).pow(2);
35    let mut fields = Vec::with_capacity(capacity);
36    fields.resize_with(capacity, Field::default);
37
38    Self {
39      fields: fields.into_boxed_slice(),
40      size,
41    }
42  }
43
44  #[inline]
45  pub fn size(&self) -> ContinentSize {
46    self.size
47  }
48
49  #[inline]
50  pub fn radius(&self) -> u8 {
51    self.size.get().div_ceil(2)
52  }
53
54  #[inline]
55  pub fn center(&self) -> Coord {
56    Coord::splat(self.radius())
57  }
58
59  pub fn field(&self, key: impl ContinentKey) -> Result<&Field> {
60    let index = key.into_index(self.size);
61    self
62      .fields
63      .get(usize::from(index))
64      .ok_or(Error::IndexOutOfBounds(index))
65  }
66
67  pub(crate) fn field_mut(&mut self, key: impl ContinentKey) -> Result<&mut Field> {
68    let index = key.into_index(self.size);
69    self
70      .fields
71      .get_mut(usize::from(index))
72      .ok_or(Error::IndexOutOfBounds(index))
73  }
74
75  pub fn fields(&self) -> impl Iterator<Item = &Field> {
76    self.fields.iter()
77  }
78
79  fn fields_mut(&mut self) -> impl Iterator<Item = &mut Field> {
80    self.fields.iter_mut()
81  }
82
83  pub fn enumerate_fields(&self) -> impl Iterator<Item = (ContinentIndex, &Field)> {
84    self
85      .fields()
86      .enumerate()
87      .map(|(idx, field)| (ContinentIndex::new(idx), field))
88  }
89
90  pub fn city(&self, key: impl ContinentKey) -> Result<&City> {
91    let index = key.into_index(self.size);
92    if let Some(city) = self.field(index)?.city() {
93      Ok(city)
94    } else {
95      let coord = index.to_coord(self.size)?;
96      Err(Error::CityNotFound(coord))
97    }
98  }
99
100  pub fn city_mut(&mut self, key: impl ContinentKey) -> Result<&mut City> {
101    let size = self.size;
102    let index = key.into_index(size);
103    if let Some(city) = self.field_mut(index)?.city_mut() {
104      Ok(city)
105    } else {
106      let coord = index.to_coord(size)?;
107      Err(Error::CityNotFound(coord))
108    }
109  }
110
111  pub fn cities(&self) -> impl Iterator<Item = &City> {
112    self.fields().filter_map(Field::city)
113  }
114
115  pub fn cities_mut(&mut self) -> impl Iterator<Item = &mut City> {
116    self.fields_mut().filter_map(Field::city_mut)
117  }
118
119  pub fn cities_by<F>(&self, f: F) -> impl Iterator<Item = &City>
120  where
121    F: Fn(&City) -> bool,
122  {
123    self.cities().filter(move |city| f(city))
124  }
125
126  pub fn cities_of<R>(&self, owner: R) -> impl Iterator<Item = &City>
127  where
128    R: Into<Ruler>,
129  {
130    let owner: Ruler = owner.into();
131    self.cities_by(move |city| city.owner() == &owner)
132  }
133
134  pub fn cities_within(&self, origin: Coord, distance: Distance) -> impl Iterator<Item = &City> {
135    self.cities_by(move |city| origin.is_within_distance(city.coord(), distance))
136  }
137
138  pub fn coords_by<F>(&self, f: F) -> impl Iterator<Item = Coord>
139  where
140    F: Fn(&City) -> bool,
141  {
142    self.cities_by(f).map(City::coord)
143  }
144
145  pub fn coords_of<R>(&self, owner: R) -> impl Iterator<Item = Coord>
146  where
147    R: Into<Ruler>,
148  {
149    let owner: Ruler = owner.into();
150    self.coords_by(move |city| city.owner() == &owner)
151  }
152
153  pub fn city_coords(&self) -> impl Iterator<Item = Coord> {
154    self.cities().map(City::coord)
155  }
156
157  pub fn owner_of(&self, key: impl ContinentKey) -> Result<&Ruler> {
158    self.city(key).map(City::owner)
159  }
160
161  pub fn search<S>(&self, search: S) -> Result<Vec<&City>>
162  where
163    S: Into<CitySearch>,
164  {
165    let mut found = Vec::new();
166    let mut search: CitySearch = search.into();
167    search.name = search
168      .name
169      .into_iter()
170      .map(|name| name.trim().to_lowercase().into())
171      .collect();
172
173    'outer: for city in self.cities() {
174      if search.coord.contains(&city.coord()) {
175        found.push(city);
176        continue;
177      }
178
179      if !search.name.is_empty() {
180        let city_name = city.name().to_lowercase();
181        for name in &search.name {
182          if city_name.contains(name.as_str()) {
183            found.push(city);
184            continue 'outer;
185          }
186        }
187      }
188    }
189
190    Ok(found)
191  }
192}
193
194impl Default for Continent {
195  fn default() -> Self {
196    Self::new(ContinentSize::default().get())
197  }
198}