thedes_domain/
game.rs

1use thedes_geometry::orientation::Direction;
2use thiserror::Error;
3
4use crate::{
5    block::{Block, PlaceableBlock, SpecialBlock},
6    geometry::{CoordPair, Rect},
7    map::{AccessError, Map},
8    player::{Player, PlayerPosition},
9};
10
11#[derive(Debug, Error)]
12pub enum InitError {
13    #[error(
14        "Player with head {} and pointer {} is outside of map {map_rect}",
15        .player_position.head(),
16        .player_position.pointer(),
17    )]
18    PlayerOutsideMap {
19        map_rect: Rect,
20        player_position: PlayerPosition,
21        source: AccessError,
22    },
23}
24
25#[derive(Debug, Error)]
26pub enum MovePlayerError {
27    #[error("Failed to access player positions")]
28    Access(
29        #[from]
30        #[source]
31        AccessError,
32    ),
33}
34
35fn blocks_player_move(block: Block) -> bool {
36    match block {
37        Block::Placeable(block) => block != PlaceableBlock::Air,
38        Block::Special(block) => block != SpecialBlock::Player,
39    }
40}
41
42#[derive(Debug, Clone)]
43pub struct Game {
44    map: Map,
45    player: Player,
46}
47
48impl Game {
49    pub fn new(
50        mut map: Map,
51        player_position: PlayerPosition,
52    ) -> Result<Self, InitError> {
53        if let Err(source) = map
54            .set_block(player_position.head(), SpecialBlock::Player)
55            .and_then(|_| {
56                map.set_block(player_position.pointer(), SpecialBlock::Player)
57            })
58        {
59            return Err(InitError::PlayerOutsideMap {
60                map_rect: map.rect(),
61                player_position,
62                source,
63            });
64        }
65        Ok(Self { map, player: Player::new(player_position) })
66    }
67
68    pub fn map(&self) -> &Map {
69        &self.map
70    }
71
72    pub fn place_block(
73        &mut self,
74        point: CoordPair,
75        block: PlaceableBlock,
76    ) -> Result<(), AccessError> {
77        self.map.set_placeable_block(point, block)
78    }
79
80    pub fn player(&self) -> &Player {
81        &self.player
82    }
83
84    pub fn move_player_pointer(
85        &mut self,
86        direction: Direction,
87    ) -> Result<(), MovePlayerError> {
88        if self.player.position().facing() == direction {
89            self.move_player_head(direction)?;
90        } else {
91            self.make_player_face(direction)?;
92        }
93        Ok(())
94    }
95
96    pub fn move_player_head(
97        &mut self,
98        direction: Direction,
99    ) -> Result<(), MovePlayerError> {
100        let Ok(new_head) = self
101            .map
102            .rect()
103            .checked_move_point_unit(self.player.position().head(), direction)
104        else {
105            return Ok(());
106        };
107        let Ok(new_pointer) = self
108            .map
109            .rect()
110            .checked_move_point_unit(new_head, self.player.position().facing())
111        else {
112            return Ok(());
113        };
114        if blocks_player_move(self.map.get_block(new_head)?) {
115            return Ok(());
116        }
117        if blocks_player_move(self.map.get_block(new_pointer)?) {
118            return Ok(());
119        }
120        self.map
121            .set_block(self.player.position().head(), PlaceableBlock::Air)?;
122        self.map
123            .set_block(self.player.position().pointer(), PlaceableBlock::Air)?;
124        self.player.position_mut().set_head(new_head);
125        self.map
126            .set_block(self.player.position().head(), SpecialBlock::Player)?;
127        self.map.set_block(
128            self.player.position().pointer(),
129            SpecialBlock::Player,
130        )?;
131        Ok(())
132    }
133
134    pub fn make_player_face(
135        &mut self,
136        direction: Direction,
137    ) -> Result<(), MovePlayerError> {
138        let Ok(new_head) = self
139            .map()
140            .rect()
141            .checked_move_point_unit(self.player.position().head(), direction)
142        else {
143            return Ok(());
144        };
145        if blocks_player_move(self.map.get_block(new_head)?) {
146            return Ok(());
147        }
148        self.map
149            .set_block(self.player.position().pointer(), PlaceableBlock::Air)?;
150        self.player.position_mut().face(direction);
151        self.map.set_block(
152            self.player.position().pointer(),
153            SpecialBlock::Player,
154        )?;
155        Ok(())
156    }
157}