thedes_domain/
map.rs

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}