1use serde::{Deserialize, Serialize};
2use thedes_geometry::rect;
3use thiserror::Error;
4
5use crate::{
6 block::{Block, PlaceableBlock},
7 geometry::{CoordPair, Rect},
8 matter::{Biome, Ground},
9};
10
11#[derive(Debug, Error)]
12pub enum InitError {
13 #[error("Map size {given_size} is below the minimum of {}", Map::MIN_SIZE)]
14 TooSmall { given_size: CoordPair },
15 #[error("Map rectangle {given_rect} has overflowing bottom right point")]
16 BottomRightOverflow { given_rect: Rect },
17}
18
19#[derive(Debug, Error)]
20#[error("Point is outside of map")]
21pub struct InvalidPoint {
22 #[from]
23 source: rect::HorzAreaError<usize>,
24}
25
26#[derive(Debug, Error)]
27pub enum AccessError {
28 #[error(transparent)]
29 InvalidPoint(#[from] InvalidPoint),
30 #[error("Bits {1} in point {0} are not valid to decode biome value")]
31 GetBiome(CoordPair, u8),
32 #[error("Bits {1} in point {0} are not valid to decode ground value")]
33 GetGround(CoordPair, u8),
34 #[error("Bits {1} in point {0} are not valid to decode block value")]
35 GetBlock(CoordPair, u8),
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39pub struct Map {
40 rect: Rect,
41 biome_layer: Vec<Biome>,
42 ground_layer: Vec<Ground>,
43 block_layer: Vec<Block>,
44}
45
46impl Map {
47 pub const MIN_SIZE: CoordPair = CoordPair { x: 100, y: 100 };
48
49 pub fn new(rect: Rect) -> Result<Self, InitError> {
50 if rect
51 .size
52 .zip2(Self::MIN_SIZE)
53 .any(|(given, required)| given < required)
54 {
55 Err(InitError::TooSmall { given_size: rect.size })?
56 }
57 if rect.checked_bottom_right().is_none() {
58 Err(InitError::BottomRightOverflow { given_rect: rect })?
59 }
60
61 let total_area = usize::from(rect.map(usize::from).total_area());
62
63 Ok(Self {
64 rect,
65 biome_layer: vec![Biome::default(); total_area],
66 ground_layer: vec![Ground::default(); total_area],
67 block_layer: vec![Block::default(); total_area],
68 })
69 }
70
71 pub fn rect(&self) -> Rect {
72 self.rect
73 }
74
75 pub fn get_biome(&self, point: CoordPair) -> Result<Biome, AccessError> {
76 let index = self.to_flat_index(point)?;
77 Ok(self.biome_layer[index])
78 }
79
80 pub fn set_biome(
81 &mut self,
82 point: CoordPair,
83 biome: Biome,
84 ) -> Result<(), AccessError> {
85 let index = self.to_flat_index(point)?;
86 self.biome_layer[index] = biome;
87 Ok(())
88 }
89
90 pub fn get_ground(&self, point: CoordPair) -> Result<Ground, AccessError> {
91 let index = self.to_flat_index(point)?;
92 Ok(self.ground_layer[index])
93 }
94
95 pub fn set_ground(
96 &mut self,
97 point: CoordPair,
98 ground: Ground,
99 ) -> Result<(), AccessError> {
100 let index = self.to_flat_index(point)?;
101 self.ground_layer[index] = ground;
102 Ok(())
103 }
104
105 pub fn get_block(&self, point: CoordPair) -> Result<Block, AccessError> {
106 let index = self.to_flat_index(point)?;
107 Ok(self.block_layer[index])
108 }
109
110 pub(crate) fn set_block<T>(
111 &mut self,
112 point: CoordPair,
113 block: T,
114 ) -> Result<(), AccessError>
115 where
116 T: Into<Block>,
117 {
118 let index = self.to_flat_index(point)?;
119 self.block_layer[index] = block.into();
120 Ok(())
121 }
122
123 pub fn set_placeable_block(
124 &mut self,
125 point: CoordPair,
126 block: PlaceableBlock,
127 ) -> Result<(), AccessError> {
128 self.set_block(point, block)
129 }
130
131 fn to_flat_index(&self, point: CoordPair) -> Result<usize, InvalidPoint> {
132 let index = self
133 .rect
134 .map(usize::from)
135 .checked_horz_area_down_to(point.map(usize::from))?;
136 Ok(index)
137 }
138}