diff --git a/.gitignore b/.gitignore index eb5a316..c6262ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +*.swp diff --git a/activate b/activate new file mode 100644 index 0000000..3039d07 --- /dev/null +++ b/activate @@ -0,0 +1,2 @@ +export PATH=$PATH:/Users/paul/Source/cdungeon/target/debug +alias cdg='cd $(cdungeon)' \ No newline at end of file diff --git a/src/game.rs b/src/game.rs index b3a8d00..fc87bcc 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,5 +1,6 @@ -use std::{fs, path::PathBuf}; +use std::{error::Error, fmt::{self}, fs, path::PathBuf}; +use color_eyre::eyre::Result; use crossterm::event::{Event, KeyCode}; use ratatui::{ Frame, @@ -95,21 +96,36 @@ impl Tile { } } +pub enum UpdateResult { + ShouldExit(PathBuf), + NewModel(GameModel), +} + +#[derive(Debug)] +struct GameError {} + +impl fmt::Display for GameError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ouch game broken") + } +} + +impl Error for GameError {} + impl GameModel { - pub fn new(path: PathBuf) -> Self { - let path = path.canonicalize().unwrap(); - let mut entries: Vec = fs::read_dir(&path) - .unwrap() - .map(|entry| entry.unwrap().path()) + pub fn new(path: PathBuf) -> Result { + let path = path.canonicalize()?; + let mut entries: Vec = fs::read_dir(&path)? + .filter_map(|entry| entry.ok().map(|entry| entry.path())) .collect(); entries.sort(); let max_entry_length = entries .iter() - .map(|entry| entry.file_name().unwrap().to_string_lossy().chars().count()) + .filter_map(|entry| entry.file_name().map(|entry| entry.to_string_lossy().chars().count())) .max() .unwrap_or(0); - Self { + Ok(Self { player_pos: P2::new(10, 2), room: Room { tiles: { @@ -152,8 +168,7 @@ impl GameModel { } for (j, c) in entry - .file_name() - .unwrap() + .file_name().ok_or(GameError {})? .to_string_lossy() .chars() .enumerate() @@ -169,25 +184,26 @@ impl GameModel { }, path, - } + }) } - pub fn update(self, event: Event) -> Result { + pub fn update(self, event: Event) -> Result { self.update_game(GameEvent::from_crossterm(event)) } - fn update_game(self, event: GameEvent) -> Result { - match event { - GameEvent::Quit => Err(self.path), - GameEvent::Nop => Ok(self), + fn update_game(self, event: GameEvent) -> Result { + use UpdateResult::*; + Ok(match event { + GameEvent::Quit => ShouldExit(self.path), + GameEvent::Nop => NewModel(self), GameEvent::PlayerMove(direction) => { let player_pos = self.player_pos + direction; if let Some(target_tile) = self.room.tiles.get(player_pos) && target_tile.may_enter() { - Ok(Self { player_pos, ..self }) + NewModel(Self { player_pos, ..self }) } else { - Ok(self) + NewModel(self) } } GameEvent::PlayerDash(direction) => { @@ -206,7 +222,7 @@ impl GameModel { break; } } - Ok(Self { player_pos, ..self }) + NewModel(Self { player_pos, ..self }) } GameEvent::Interact => { let opt_action = self @@ -216,19 +232,20 @@ impl GameModel { .and_then(|tile| tile.action.clone()); if let Some(action) = opt_action { - self.update_game(action) + self.update_game(action)? } else { - Ok(self) + NewModel(self) } } GameEvent::Navigate(target) => { let path = match target { NavigationTarget::Path(path) => path, - NavigationTarget::Parent => self.path.parent().unwrap().to_path_buf(), + NavigationTarget::Parent => self.path.parent().ok_or(GameError {})?.to_path_buf(), }; - Ok(Self::new(path)) + + NewModel(Self::new(path)?) } - } + }) } pub fn render(&self, frame: &mut Frame) { @@ -260,7 +277,7 @@ impl GameModel { self.room .tiles .get(P2::new(x as isize, y as isize)) - .unwrap() + .expect("programmer error: copying a grid shouldnt yield OOB errors") .style .clone() }); diff --git a/src/main.rs b/src/main.rs index 82fc8d5..5a5f933 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use ratatui::{ }; use game::GameModel; +use game::UpdateResult; fn main() -> Result<()> { enable_raw_mode()?; @@ -33,14 +34,14 @@ fn main() -> Result<()> { } fn main_loop(terminal: &mut Terminal) -> Result { - let mut model = GameModel::new(PathBuf::from(".")); + let mut model = GameModel::new(PathBuf::from("."))?; loop { terminal.draw(|frame| model.render(frame))?; let term_event = event::read()?; - match model.update(term_event) { - Ok(new_model) => model = new_model, - Err(path) => break Ok(path), + match model.update(term_event)? { + UpdateResult::NewModel(new_model) => model = new_model, + UpdateResult::ShouldExit(path) => break Ok(path), } } }