1use std::{
2 collections::{HashMap, VecDeque},
3 fs::File,
4 io::{self, BufReader, BufWriter},
5 path::{Path, PathBuf},
6};
7
8use serde::{Deserialize, Serialize};
9use thedes_geometry::orientation::Direction;
10use thiserror::Error;
11use tokio::task;
12
13use crate::{
14 block::{Block, PlaceableBlock, SpecialBlock},
15 event::{self, Event, MetaEvent},
16 geometry::{Coord, CoordPair, Rect},
17 map::{AccessError, Map},
18 monster::{self, IdShortageError, Monster, MonsterPosition},
19 player::{Player, PlayerPosition},
20 stat::StatValue,
21};
22
23#[derive(Debug, Error)]
24pub enum LoadErrorSource {
25 #[error("I/O error happened")]
26 Io(#[from] io::Error),
27 #[error("Failed to deserialize")]
28 Deserialize(#[from] serde_json::Error),
29}
30
31#[derive(Debug, Error)]
32#[error("Failed to load game from {path}")]
33pub struct LoadError {
34 pub path: PathBuf,
35 #[source]
36 pub source: LoadErrorSource,
37}
38
39#[derive(Debug, Error)]
40pub enum SaveErrorSource {
41 #[error("I/O error happened")]
42 Io(#[from] io::Error),
43 #[error("Failed to serialize")]
44 Serialize(#[from] serde_json::Error),
45}
46
47#[derive(Debug, Error)]
48#[error("Failed to save game to {path}")]
49pub struct SaveError {
50 pub path: PathBuf,
51 #[source]
52 pub source: SaveErrorSource,
53}
54
55#[derive(Debug, Error)]
56pub enum InitError {
57 #[error(
58 "Player with head {} and pointer {} is outside of map {map_rect}",
59 .player_position.head(),
60 .player_position.pointer(),
61 )]
62 PlayerOutsideMap {
63 map_rect: Rect,
64 player_position: PlayerPosition,
65 source: AccessError,
66 },
67}
68
69#[derive(Debug, Error)]
70pub enum MovePlayerError {
71 #[error("Failed to access player positions")]
72 Access(
73 #[from]
74 #[source]
75 AccessError,
76 ),
77}
78
79#[derive(Debug, Error)]
80pub enum SpawnMonsterError {
81 #[error("Failed to access map location")]
82 MapAccess(
83 #[from]
84 #[source]
85 AccessError,
86 ),
87 #[error("Run out of identifiers")]
88 IdShortage(
89 #[from]
90 #[source]
91 IdShortageError,
92 ),
93}
94
95#[derive(Debug, Error)]
96pub enum VanishMonsterError {
97 #[error("Invalid monster ID")]
98 InvalidId(
99 #[from]
100 #[source]
101 monster::InvalidId,
102 ),
103 #[error("Failed to access map location")]
104 MapAccess(
105 #[from]
106 #[source]
107 AccessError,
108 ),
109}
110
111#[derive(Debug, Error)]
112pub enum MoveMonsterError {
113 #[error("Invalid monster ID")]
114 InvalidId(
115 #[from]
116 #[source]
117 monster::InvalidId,
118 ),
119 #[error("Failed to access map location")]
120 MapAccess(
121 #[from]
122 #[source]
123 AccessError,
124 ),
125}
126
127#[derive(Debug, Error)]
128pub enum MonsterAttackError {
129 #[error("Invalid monster ID")]
130 InvalidId(
131 #[from]
132 #[source]
133 monster::InvalidId,
134 ),
135 #[error("Failed to access map location")]
136 MapAccess(
137 #[from]
138 #[source]
139 AccessError,
140 ),
141}
142
143#[derive(Debug, Error)]
144pub enum MonsterGrowlError {
145 #[error("Invalid monster ID")]
146 InvalidId(
147 #[from]
148 #[source]
149 monster::InvalidId,
150 ),
151}
152
153#[derive(Debug, Error)]
154pub enum MonsterFollowError {
155 #[error("Invalid monster ID")]
156 InvalidId(
157 #[from]
158 #[source]
159 monster::InvalidId,
160 ),
161 #[error("Failed to move monster")]
162 MoveMonster(#[from] MoveMonsterError),
163}
164
165fn blocks_movement(block: Block, this: SpecialBlock) -> bool {
166 match block {
167 Block::Placeable(block) => block != PlaceableBlock::Air,
168 Block::Special(block) => {
169 block != this
170 && matches!(
171 block,
172 SpecialBlock::Player | SpecialBlock::Monster(_)
173 )
174 },
175 }
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct Game {
180 map: Map,
181 player: Player,
182 monster_registry: monster::Registry,
183 event_schedule: HashMap<u64, Vec<Event>>,
184 event_epoch: u64,
185 meta_events: VecDeque<MetaEvent>,
186}
187
188impl Game {
189 pub fn new(mut map: Map, player: Player) -> Result<Self, InitError> {
190 if let Err(source) = map
191 .set_block(player.position().head(), SpecialBlock::Player)
192 .and_then(|_| {
193 map.set_block(player.position().pointer(), SpecialBlock::Player)
194 })
195 {
196 return Err(InitError::PlayerOutsideMap {
197 map_rect: map.rect(),
198 player_position: player.position().clone(),
199 source,
200 });
201 }
202 Ok(Self {
203 map,
204 player,
205 monster_registry: monster::Registry::new(),
206 event_schedule: HashMap::new(),
207 event_epoch: 0,
208 meta_events: VecDeque::new(),
209 })
210 }
211
212 pub async fn load(path: &Path) -> Result<Self, LoadError> {
213 task::block_in_place(|| {
214 let file =
215 File::open(&path).map_err(LoadErrorSource::from).map_err(
216 |source| LoadError { path: path.to_owned(), source },
217 )?;
218 let mut file = BufReader::new(file);
219 serde_json::from_reader(&mut file)
220 .map_err(LoadErrorSource::from)
221 .map_err(|source| LoadError { path: path.to_owned(), source })
222 })
223 }
224
225 pub async fn save(&self, path: &Path) -> Result<(), SaveError> {
226 task::block_in_place(|| {
227 let file =
228 File::create(&path).map_err(SaveErrorSource::from).map_err(
229 |source| SaveError { path: path.to_owned(), source },
230 )?;
231 let mut file = BufWriter::new(file);
232 serde_json::to_writer(&mut file, self)
233 .map_err(SaveErrorSource::from)
234 .map_err(|source| SaveError {
235 path: path.to_owned(),
236 source,
237 })?;
238 Ok(())
239 })
240 }
241
242 pub fn schedule_event(&mut self, event: Event, event_ticks: u32) {
243 let event_ticks = u64::from(event_ticks) + self.event_epoch;
244 self.event_schedule.entry(event_ticks).or_default().push(event);
245 }
246
247 pub fn execute_events(&mut self) -> Result<(), event::ApplyError> {
248 let old_epoch = self.event_epoch;
249 self.event_epoch += 1;
250 for event in
251 self.event_schedule.remove(&old_epoch).into_iter().flatten()
252 {
253 event.apply(self)?;
254 }
255 Ok(())
256 }
257
258 pub fn emit_meta_event(&mut self, meta_event: MetaEvent) {
259 self.meta_events.push_back(meta_event);
260 }
261
262 pub fn read_one_meta_event(&mut self) -> Option<MetaEvent> {
263 self.meta_events.pop_front()
264 }
265
266 pub fn map(&self) -> &Map {
267 &self.map
268 }
269
270 pub fn place_block(
271 &mut self,
272 point: CoordPair,
273 block: PlaceableBlock,
274 ) -> Result<(), AccessError> {
275 self.map.set_placeable_block(point, block)
276 }
277
278 pub fn player(&self) -> &Player {
279 &self.player
280 }
281
282 pub fn move_player_pointer(
283 &mut self,
284 direction: Direction,
285 ) -> Result<(), MovePlayerError> {
286 if self.player.position().facing() == direction {
287 self.move_player_head(direction)?;
288 } else {
289 self.make_player_face(direction)?;
290 }
291 Ok(())
292 }
293
294 pub fn move_player_head(
295 &mut self,
296 direction: Direction,
297 ) -> Result<(), MovePlayerError> {
298 let Ok(new_head) = self
299 .map
300 .rect()
301 .checked_move_point_unit(self.player.position().head(), direction)
302 else {
303 return Ok(());
304 };
305 let Ok(new_pointer) = self
306 .map
307 .rect()
308 .checked_move_point_unit(new_head, self.player.position().facing())
309 else {
310 return Ok(());
311 };
312 if blocks_movement(self.map.get_block(new_head)?, SpecialBlock::Player)
313 {
314 return Ok(());
315 }
316 if blocks_movement(
317 self.map.get_block(new_pointer)?,
318 SpecialBlock::Player,
319 ) {
320 return Ok(());
321 }
322 self.map
323 .set_block(self.player.position().head(), PlaceableBlock::Air)?;
324 self.map
325 .set_block(self.player.position().pointer(), PlaceableBlock::Air)?;
326 self.player.position_mut().set_head(new_head);
327 self.map
328 .set_block(self.player.position().head(), SpecialBlock::Player)?;
329 self.map.set_block(
330 self.player.position().pointer(),
331 SpecialBlock::Player,
332 )?;
333 Ok(())
334 }
335
336 pub fn make_player_face(
337 &mut self,
338 direction: Direction,
339 ) -> Result<(), MovePlayerError> {
340 let Ok(new_head) = self
341 .map()
342 .rect()
343 .checked_move_point_unit(self.player.position().head(), direction)
344 else {
345 return Ok(());
346 };
347 if blocks_movement(self.map.get_block(new_head)?, SpecialBlock::Player)
348 {
349 return Ok(());
350 }
351 self.map
352 .set_block(self.player.position().pointer(), PlaceableBlock::Air)?;
353 self.player.position_mut().face(direction);
354 self.map.set_block(
355 self.player.position().pointer(),
356 SpecialBlock::Player,
357 )?;
358 Ok(())
359 }
360
361 pub fn monster_registry(&self) -> &monster::Registry {
362 &self.monster_registry
363 }
364
365 pub fn try_spawn_moster(
366 &mut self,
367 pos: MonsterPosition,
368 ) -> Result<(), SpawnMonsterError> {
369 let block_value = self.map().get_block(pos.body())?;
370 if block_value == Block::Placeable(PlaceableBlock::Air) {
371 let monster = Monster::new(pos);
372 let monster_id = self.monster_registry.create_as(monster)?;
373 self.map
374 .set_block(pos.body(), SpecialBlock::Monster(monster_id))?;
375 }
376 Ok(())
377 }
378
379 pub fn vanish_monster(
380 &mut self,
381 id: monster::Id,
382 ) -> Result<(), VanishMonsterError> {
383 let monster = self.monster_registry.remove(id)?;
384 self.map.set_block(monster.position().body(), PlaceableBlock::Air)?;
385 Ok(())
386 }
387
388 pub fn try_move_monster(
389 &mut self,
390 id: monster::Id,
391 direction: Direction,
392 ) -> Result<(), MoveMonsterError> {
393 let pos = self.monster_registry.get_by_id(id)?.position();
394 if pos.facing() == direction {
395 self.move_monster_head(id, direction)?;
396 } else {
397 self.make_monster_face(id, direction)?;
398 }
399 Ok(())
400 }
401
402 pub fn move_monster_head(
403 &mut self,
404 id: monster::Id,
405 direction: Direction,
406 ) -> Result<(), MoveMonsterError> {
407 let pos = self.monster_registry.get_by_id(id)?.position();
408 let Ok(new_body) =
409 self.map.rect().checked_move_point_unit(pos.body(), direction)
410 else {
411 return Ok(());
412 };
413 if blocks_movement(
414 self.map.get_block(new_body)?,
415 SpecialBlock::Monster(id),
416 ) {
417 return Ok(());
418 }
419 self.map.set_block(pos.body(), PlaceableBlock::Air)?;
420 self.monster_registry
421 .get_by_id_mut(id)?
422 .position_mut()
423 .set_body(new_body);
424 self.map.set_block(new_body, SpecialBlock::Monster(id))?;
425 Ok(())
426 }
427
428 pub fn make_monster_face(
429 &mut self,
430 id: monster::Id,
431 direction: Direction,
432 ) -> Result<(), MoveMonsterError> {
433 self.monster_registry.get_by_id_mut(id)?.position_mut().face(direction);
434 Ok(())
435 }
436
437 pub fn monster_attack(
438 &mut self,
439 id: monster::Id,
440 ) -> Result<(), MonsterAttackError> {
441 let monster = self.monster_registry.get_by_id(id)?;
442 let Some(next_block) = monster
443 .position()
444 .body()
445 .checked_move_unit(monster.position().facing())
446 else {
447 return Ok(());
448 };
449 self.emit_meta_event(MetaEvent::MonsterHit(next_block));
450 let Ok(block) = self.map.get_block(next_block) else {
451 return Ok(());
452 };
453 match block {
454 Block::Special(SpecialBlock::Player) => {
455 self.player.damage(1);
456 },
457 _ => (),
458 }
459 Ok(())
460 }
461
462 pub fn monster_growl(
463 &mut self,
464 id: monster::Id,
465 ) -> Result<(), MonsterGrowlError> {
466 let monster = self.monster_registry.get_by_id(id)?;
467 self.emit_meta_event(MetaEvent::MonsterGrowl(
468 monster.position().body(),
469 ));
470 Ok(())
471 }
472
473 pub fn monster_follow_player(
474 &mut self,
475 id: monster::Id,
476 speed: Coord,
477 limit: u32,
478 ) -> Result<(), MonsterFollowError> {
479 let monster = self.monster_registry.get_by_id(id)?;
480
481 let (_, Some(vec)) = self
482 .player()
483 .position()
484 .head()
485 .diagonal_direction_from(monster.position().body())
486 .max_with_axis_by_key(|opt| opt.map(|vec| vec.magnitude))
487 else {
488 return Ok(());
489 };
490
491 self.try_move_monster(id, vec.direction)?;
492
493 if let Some(new_limit) = limit.checked_sub(1) {
494 self.schedule_event(
495 Event::FollowPlayer { id, period: speed, limit: new_limit },
496 u32::from(speed),
497 );
498 }
499 Ok(())
500 }
501
502 pub fn damage_player(&mut self, amount: StatValue) {
503 self.player.damage(amount);
504 }
505
506 pub fn heal_player(&mut self, amount: StatValue) {
507 self.player.heal(amount);
508 }
509}