1use std::{collections::hash_map, path::Path};
22
23use crate::asset::{Resource, ResourceData};
24use fxhash::FxHashMap;
25use fyrox_core::visitor::BinaryBlob;
26
27use crate::core::{
28 algebra::Vector2, reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*,
29};
30
31use super::*;
32
33const CHUNK_WIDTH: usize = 16;
34const CHUNK_HEIGHT: usize = 16;
35const WIDTH_BITS: i32 = (CHUNK_WIDTH - 1) as i32;
36const HEIGHT_BITS: i32 = (CHUNK_HEIGHT - 1) as i32;
37
38pub type TileMapDataResource = Resource<TileMapData>;
40
41fn tile_position_to_chunk_position(position: Vector2<i32>) -> (Vector2<i32>, Vector2<i32>) {
45 let x = position.x;
46 let y = position.y;
47 let x_chunk = x & !WIDTH_BITS;
48 let y_chunk = y & !HEIGHT_BITS;
49 (
50 Vector2::new(x_chunk, y_chunk),
51 Vector2::new(x - x_chunk, y - y_chunk),
52 )
53}
54
55#[derive(Clone, Debug, Reflect)]
56struct Chunk([TileDefinitionHandle; CHUNK_WIDTH * CHUNK_HEIGHT]);
57
58impl Visit for Chunk {
59 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
60 if visitor.is_reading() {
61 let mut data = Vec::default();
62 BinaryBlob { vec: &mut data }.visit(name, visitor)?;
63 if data.len() != CHUNK_WIDTH * CHUNK_HEIGHT {
64 return Err(VisitError::User(
65 "Wrong number of handles in a chunk".into(),
66 ));
67 }
68 self.0
69 .clone_from_slice(&data[0..CHUNK_WIDTH * CHUNK_HEIGHT]);
70 Ok(())
71 } else {
72 BinaryBlob {
73 vec: &mut self.0.to_vec(),
74 }
75 .visit(name, visitor)
76 }
77 }
78}
79
80impl Default for Chunk {
81 fn default() -> Self {
82 Self([TileDefinitionHandle::EMPTY; CHUNK_WIDTH * CHUNK_HEIGHT])
83 }
84}
85
86impl std::ops::Index<Vector2<i32>> for Chunk {
87 type Output = TileDefinitionHandle;
88
89 fn index(&self, index: Vector2<i32>) -> &Self::Output {
90 let x: usize = index.x.try_into().unwrap();
91 let y: usize = index.y.try_into().unwrap();
92 &self.0[x + y * CHUNK_WIDTH]
93 }
94}
95
96impl std::ops::IndexMut<Vector2<i32>> for Chunk {
97 fn index_mut(&mut self, index: Vector2<i32>) -> &mut Self::Output {
98 let x: usize = index.x.try_into().unwrap();
99 let y: usize = index.y.try_into().unwrap();
100 &mut self.0[x + y * CHUNK_WIDTH]
101 }
102}
103
104impl Chunk {
105 fn iter(&self, offset: Vector2<i32>) -> ChunkIterator {
106 ChunkIterator {
107 position: Vector2::new(0, 0),
108 chunk: self,
109 offset,
110 }
111 }
112 fn is_empty(&self) -> bool {
113 self.0.iter().all(|h| *h == TileDefinitionHandle::EMPTY)
114 }
115}
116
117struct ChunkIterator<'a> {
118 position: Vector2<i32>,
119 offset: Vector2<i32>,
120 chunk: &'a Chunk,
121}
122
123impl Iterator for ChunkIterator<'_> {
124 type Item = (Vector2<i32>, TileDefinitionHandle);
125
126 fn next(&mut self) -> Option<Self::Item> {
127 loop {
128 if self.position.y >= CHUNK_HEIGHT as i32 {
129 return None;
130 }
131 let result_position = self.position;
132 let result = self.chunk[result_position];
133 if self.position.x < (CHUNK_WIDTH - 1) as i32 {
134 self.position.x += 1;
135 } else {
136 self.position.x = 0;
137 self.position.y += 1;
138 }
139 if !result.is_empty() {
140 return Some((result_position + self.offset, result));
141 }
142 }
143 }
144}
145
146pub struct TileMapDataIterator<'a, P: 'a> {
148 predicate: P,
149 map_iter: hash_map::Iter<'a, Vector2<i32>, Chunk>,
150 chunk_iter: Option<ChunkIterator<'a>>,
151}
152
153impl<P: FnMut(Vector2<i32>) -> bool> Iterator for TileMapDataIterator<'_, P> {
154 type Item = (Vector2<i32>, TileDefinitionHandle);
155 fn next(&mut self) -> Option<Self::Item> {
156 let chunk_iter = match &mut self.chunk_iter {
157 Some(iter) => iter,
158 None => self.next_chunk()?,
159 };
160 if let Some(result) = chunk_iter.next() {
161 Some(result)
162 } else {
163 self.next_chunk()?.next()
164 }
165 }
166}
167
168impl<'a, P: FnMut(Vector2<i32>) -> bool> TileMapDataIterator<'a, P> {
169 fn next_chunk(&mut self) -> Option<&mut ChunkIterator<'a>> {
170 loop {
171 let (pos, chunk) = self.map_iter.next()?;
172 if (self.predicate)(*pos) {
173 return Some(self.chunk_iter.insert(chunk.iter(*pos)));
174 }
175 }
176 }
177}
178
179#[derive(Clone, Default, Debug, Reflect, TypeUuidProvider, ComponentProvider)]
181#[type_uuid(id = "a8e4b6b4-c1bd-4ed9-a753-0d5a3dfe1729")]
182pub struct TileMapData {
183 content: FxHashMap<Vector2<i32>, Chunk>,
184}
185
186impl Visit for TileMapData {
187 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
188 if !visitor.is_reading() {
189 self.shrink_to_fit();
190 }
191 self.content.visit(name, visitor)
192 }
193}
194
195impl ResourceData for TileMapData {
196 fn type_uuid(&self) -> Uuid {
197 <Self as TypeUuidProvider>::type_uuid()
198 }
199
200 fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
201 let mut visitor = Visitor::new();
202 self.visit("TileMapData", &mut visitor)?;
203 visitor.save_ascii_to_file(path)?;
204 Ok(())
205 }
206
207 fn can_be_saved(&self) -> bool {
208 false
209 }
210
211 fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
212 Some(Box::new(self.clone()))
213 }
214}
215
216impl TileSource for TileMapData {
217 fn brush(&self) -> Option<&TileMapBrushResource> {
218 None
219 }
220 fn transformation(&self) -> OrthoTransformation {
221 OrthoTransformation::default()
222 }
223
224 fn get_at(&self, position: Vector2<i32>) -> Option<StampElement> {
225 self.get(position).map(|h| h.into())
226 }
227}
228
229impl BoundedTileSource for TileMapData {
230 fn bounding_rect(&self) -> OptionTileRect {
231 let mut rect = OptionTileRect::default();
232 for (pos, _) in self.iter() {
233 rect.push(pos);
234 }
235 rect
236 }
237}
238
239impl TileMapData {
240 pub fn iter(&self) -> impl Iterator<Item = (Vector2<i32>, TileDefinitionHandle)> + '_ {
242 let map_iter = self.content.iter();
243 TileMapDataIterator {
244 predicate: |_| true,
245 map_iter,
246 chunk_iter: None,
247 }
248 }
249 pub fn bounded_iter(
251 &self,
252 bounds: OptionTileRect,
253 ) -> impl Iterator<Item = (Vector2<i32>, TileDefinitionHandle)> + '_ {
254 let map_iter = self.content.iter();
255 TileMapDataIterator {
256 predicate: move |pos: Vector2<i32>| {
257 bounds.intersects(TileRect::new(
258 pos.x,
259 pos.y,
260 CHUNK_WIDTH as i32,
261 CHUNK_HEIGHT as i32,
262 ))
263 },
264 map_iter,
265 chunk_iter: None,
266 }
267 }
268 pub fn swap_tiles(&mut self, tiles: &mut TilesUpdate) {
272 for (p, h) in tiles.iter_mut() {
273 *h = self.replace(*p, *h);
274 }
275 }
276 pub fn get(&self, position: Vector2<i32>) -> Option<TileDefinitionHandle> {
278 let (chunk, pos) = tile_position_to_chunk_position(position);
279 let chunk = self.content.get(&chunk)?;
280 let handle = chunk[pos];
281 if handle.is_empty() {
282 None
283 } else {
284 Some(handle)
285 }
286 }
287 pub fn replace(
290 &mut self,
291 position: Vector2<i32>,
292 value: Option<TileDefinitionHandle>,
293 ) -> Option<TileDefinitionHandle> {
294 let (chunk, pos) = tile_position_to_chunk_position(position);
295 if let Some(chunk) = self.content.get_mut(&chunk) {
296 let handle = &mut chunk[pos];
297 let result = *handle;
298 *handle = value.unwrap_or(TileDefinitionHandle::EMPTY);
299 if result.is_empty() {
300 None
301 } else {
302 Some(result)
303 }
304 } else if let Some(value) = value {
305 let chunk = self.content.entry(chunk).or_default();
306 chunk[pos] = value;
307 None
308 } else {
309 None
310 }
311 }
312 pub fn set(&mut self, position: Vector2<i32>, value: TileDefinitionHandle) {
314 let (chunk, pos) = tile_position_to_chunk_position(position);
315 let chunk = self.content.entry(chunk).or_default();
316 chunk[pos] = value;
317 }
318 pub fn remove(&mut self, position: Vector2<i32>) {
320 let (chunk, pos) = tile_position_to_chunk_position(position);
321 if let Some(chunk) = self.content.get_mut(&chunk) {
322 chunk[pos] = TileDefinitionHandle::EMPTY;
323 }
324 }
325 pub fn shrink_to_fit(&mut self) {
327 self.content.retain(|_, v| !v.is_empty())
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use std::cmp::Ordering;
334
335 use super::*;
336
337 fn v(x: i32, y: i32) -> Vector2<i32> {
338 Vector2::new(x, y)
339 }
340
341 fn h(a: i16, b: i16, c: i16, d: i16) -> TileDefinitionHandle {
342 TileDefinitionHandle::new(a, b, c, d)
343 }
344
345 fn v_ord(a: &Vector2<i32>, b: &Vector2<i32>) -> Ordering {
346 a.y.cmp(&b.y).reverse().then(a.x.cmp(&b.x))
347 }
348
349 #[test]
350 fn position_to_chunk() {
351 assert_eq!(
352 tile_position_to_chunk_position(v(16, 16)),
353 (v(16, 16), v(0, 0))
354 );
355 assert_eq!(tile_position_to_chunk_position(v(0, 0)), (v(0, 0), v(0, 0)));
356 assert_eq!(
357 tile_position_to_chunk_position(v(-5, 5)),
358 (v(-16, 0), v(11, 5))
359 );
360 assert_eq!(
361 tile_position_to_chunk_position(v(-16, 5)),
362 (v(-16, 0), v(0, 5))
363 );
364 assert_eq!(
365 tile_position_to_chunk_position(v(-17, 5)),
366 (v(-32, 0), v(15, 5))
367 );
368 }
369 #[test]
370 fn create_chunks() {
371 let mut data = TileMapData::default();
372 let coords = vec![
373 (v(0, 0), h(1, 2, 3, 4)),
374 (v(-1, -2), h(1, 2, 3, 0)),
375 (v(16, 16), h(1, 2, 3, 5)),
376 (v(-1, -1), h(1, 2, 3, 6)),
377 (v(-17, 0), h(1, 2, 3, 7)),
378 ];
379 for (pos, handle) in coords.iter() {
380 data.set(*pos, *handle);
381 }
382 let mut coords = coords
383 .into_iter()
384 .map(|(p, _)| tile_position_to_chunk_position(p).0)
385 .collect::<Vec<_>>();
386 coords.sort_by(v_ord);
387 coords.dedup();
388 let mut result = data.content.keys().copied().collect::<Vec<_>>();
389 result.sort_by(v_ord);
390 assert_eq!(result, coords);
391 }
392 #[test]
393 fn iter_full_chunk() {
394 let mut data = TileMapData::default();
395 let mut required = FxHashSet::default();
396 let mut extra = Vec::default();
397 for x in 0..CHUNK_WIDTH as i32 {
398 for y in 0..CHUNK_HEIGHT as i32 {
399 data.set(v(x, y), h(0, 0, 0, 0));
400 required.insert(v(x, y));
401 }
402 }
403 for (result, _) in data.iter() {
404 if !required.remove(&result) {
405 extra.push(result);
406 }
407 }
408 let required = required.into_iter().collect::<Vec<_>>();
409 assert_eq!((required, extra), (vec![], vec![]));
410 }
411 #[test]
412 fn iter() {
413 let mut data = TileMapData::default();
414 let mut coords = vec![
415 (v(0, 0), h(1, 2, 3, 4)),
416 (v(-1, -2), h(1, 2, 3, 0)),
417 (v(16, 16), h(1, 2, 3, 5)),
418 (v(-1, -1), h(1, 2, 3, 6)),
419 (v(-17, 0), h(1, 2, 3, 7)),
420 ];
421 for (pos, handle) in coords.iter() {
422 data.set(*pos, *handle);
423 }
424 let mut result = data.iter().collect::<Vec<_>>();
425 result.sort_by(|(a, _), (b, _)| v_ord(a, b));
426 coords.sort_by(|(a, _), (b, _)| v_ord(a, b));
427 assert_eq!(result, coords);
428 }
429}