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