use crate::{
errors::RPGXError,
prelude::{Coordinates, Direction, Map, Pawn},
traits::Grid,
};
#[doc = include_str!("../../docs/scene.md")]
#[derive(Clone)]
pub struct Scene {
pub name: String,
pub map: Map,
pub pawn: Option<Pawn>,
}
impl Scene {
pub fn new(name: String, map: Map, pawn: Option<Pawn>) -> Self {
Self { map, pawn, name }
}
pub fn load_pawn(&mut self, texture_id: u32) {
self.pawn = Some(Pawn {
texture_id,
pointer: self.map.spawn,
})
}
pub fn load_pawn_at(&mut self, pawn: Pawn) {
self.pawn = Some(pawn);
}
pub async fn walk_to(
&mut self,
target_position: Coordinates,
) -> Result<Coordinates, RPGXError> {
let start = self
.pawn
.as_ref()
.map(|p| p.pointer)
.ok_or_else(|| RPGXError::TileNotFound(target_position))?;
let path =
self.map
.find_path(&start, &target_position)
.ok_or(RPGXError::PathNotFround {
from: start,
to: target_position,
})?;
let mut tile = None;
for step_coords in path {
tile = Some(self.move_to(step_coords)?);
}
tile.ok_or(RPGXError::WalkFailed {
from: start,
to: target_position,
})
}
pub fn step_to(&mut self, direction: Direction) -> Result<Coordinates, RPGXError> {
let delta = direction.to_delta();
let current = self
.pawn
.as_ref()
.map(|p| p.pointer)
.ok_or(RPGXError::PawnNotFound)?;
if let Some(target_position) = current + delta {
self.move_to(target_position)
} else {
Err(RPGXError::StepFailed(direction))
}
}
pub fn move_to(&mut self, target_position: Coordinates) -> Result<Coordinates, RPGXError> {
if self.map.contains(&target_position) && !self.map.is_blocking_at(&target_position) {
if let Some(pawn) = self.pawn.as_mut() {
pawn.pointer = target_position;
Ok(target_position)
} else {
Err(RPGXError::PawnNotFound)
}
} else {
Err(RPGXError::TileNotWalkable(target_position))
}
}
pub fn steps_to(&self, target_position: Coordinates) -> Result<Vec<Coordinates>, RPGXError> {
let start = self
.pawn
.as_ref()
.map(|p| p.pointer)
.ok_or(RPGXError::PawnNotFound)?;
let path =
self.map
.find_path(&start, &target_position)
.ok_or(RPGXError::PathNotFround {
from: start,
to: target_position,
})?;
Ok(path)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::{Coordinates, Layer, Map};
fn create_test_map() -> Map {
let layer = Layer::new("base".to_string(), vec![], 1);
Map::new("test_map".to_string(), vec![layer], Coordinates::new(0, 0))
}
#[test]
fn test_move_to_blocked_tile_returns_error() {
let mut map = create_test_map();
map.layers.clear(); let mut scene = Scene::new("test".into(), map, None);
scene.load_pawn(1);
let result = scene.move_to(Coordinates::new(0, 0));
assert_eq!(
result,
Err(RPGXError::TileNotWalkable(Coordinates::new(0, 0)))
);
}
}