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}