diff --git a/crates/save/src/level.rs b/crates/save/src/level.rs new file mode 100644 index 0000000..1bf6553 --- /dev/null +++ b/crates/save/src/level.rs @@ -0,0 +1,81 @@ +use core::fmt; + +use binrw::{BinRead, BinWrite}; + +use crate::{Error, Result}; + + +pub const MAX_LEVEL_ID: u64 = 11; + +pub const NAMES: [&str; MAX_LEVEL_ID as usize + 1] = [ + "Chapter Select", + "Broken Bridge", + "Pink Desert", + "Sunken City", + "Underground", + "Tower", + "Snow", + "Paradise", + "Credits", + "Level Bryan", + "Level Matt", + "Level Chris", +]; + + +#[derive(Debug, Clone, Copy, BinRead, BinWrite)] +pub struct Level { + #[br(assert(id <= MAX_LEVEL_ID))] + id: u64, +} + +impl Level { + pub fn set_by_id(&mut self, id: u64) -> Result<()> { + if id > MAX_LEVEL_ID { + return Err(Error::LevelIdOutOfRange); + } + + self.id = id; + + Ok(()) + } + + pub fn set_by_name(&mut self, name: &str) -> Result<()> { + let id = NAMES + .iter() + .position(|&v| v == name) + .ok_or(Error::LevelNameNotFound)?; + + self.id = id as u64; + + Ok(()) + } + + pub fn wrapping_next(&self) -> Self { + let id = self.id + 1; + if id > MAX_LEVEL_ID { + Self { id: 0 } + } else { + Self { id } + } + } + + pub fn wrapping_previous(&self) -> Self { + match self.id.checked_sub(1) { + Some(id) => Self { id }, + None => Self { id: MAX_LEVEL_ID }, + } + } +} + +impl fmt::Display for Level { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", NAMES[self.id as usize]) + } +} + +impl AsRef for Level { + fn as_ref(&self) -> &u64 { + &self.id + } +} diff --git a/crates/save/src/lib.rs b/crates/save/src/lib.rs index e9f0737..2cacd42 100644 --- a/crates/save/src/lib.rs +++ b/crates/save/src/lib.rs @@ -1,5 +1,6 @@ mod companion; mod glyphs; +mod level; mod murals; mod symbol; mod test; @@ -12,29 +13,15 @@ use std::path::{Path, PathBuf}; use binrw::{until_eof, BinRead, BinReaderExt, BinWriterExt}; use chrono::{DateTime, NaiveDateTime, Utc}; +use level::Level; use symbol::Symbol; use crate::companion::{CompanionSymbols, CompanionWithId, Companions}; use crate::glyphs::Glyphs; +pub use crate::level::NAMES as LEVEL_NAMES; use crate::murals::Murals; -pub const LEVEL_NAMES: [&str; 12] = [ - "Chapter Select", - "Broken Bridge", - "Pink Desert", - "Sunken City", - "Underground", - "Tower", - "Snow", - "Paradise", - "Credits", - "Level Bryan", - "Level Matt", - "Level Chris", -]; - - pub type Result = std::result::Result; @@ -46,6 +33,12 @@ pub enum Error { #[error("Failed to serialize savefile")] SerializationFailed(binrw::Error), + #[error("Level id is out of range")] + LevelIdOutOfRange, + + #[error("Level name was not found")] + LevelNameNotFound, + #[error("Symbol id is out of range")] SymbolIdOutOfRange, @@ -89,8 +82,7 @@ pub struct Savefile { #[br(count = 4)] _unknown1: Vec, - #[br(assert(current_level <= 12))] - pub current_level: u64, + pub current_level: Level, pub total_collected_symbols: u32, @@ -190,10 +182,6 @@ impl Savefile { }) } - pub fn current_level_name(&self) -> &'static str { - LEVEL_NAMES[self.current_level as usize] - } - pub fn robe_color(&self) -> RobeColor { if self.robe > 3 { RobeColor::White diff --git a/crates/save/src/test.rs b/crates/save/src/test.rs index 068995a..4341740 100644 --- a/crates/save/src/test.rs +++ b/crates/save/src/test.rs @@ -18,7 +18,7 @@ fn general_info() { assert_eq!(savefile.symbol.as_ref(), &7); assert_eq!(savefile.scarf_length, 27); - assert_eq!(savefile.current_level, 1); + assert_eq!(savefile.current_level.as_ref(), &1); assert_eq!(savefile.total_collected_symbols, 107); assert_eq!(savefile.collected_symbols, 21); assert_eq!(savefile.journey_count, 21); diff --git a/crates/wayfarer/src/edit.rs b/crates/wayfarer/src/edit.rs index de6a3c7..2ecc1d5 100644 --- a/crates/wayfarer/src/edit.rs +++ b/crates/wayfarer/src/edit.rs @@ -59,7 +59,7 @@ fn edit_file(cur_savefile: &Savefile, args: &Args) -> Result { } if let Some(val) = &args.current_level { - savefile.current_level = LEVEL_NAMES.iter().position(|&v| v == val).unwrap() as u64; + savefile.current_level.set_by_name(&val)?; } if let Some(val) = args.symbol { diff --git a/crates/wayfarer/src/tui/state.rs b/crates/wayfarer/src/tui/state.rs index 2522a2c..3c11ae3 100644 --- a/crates/wayfarer/src/tui/state.rs +++ b/crates/wayfarer/src/tui/state.rs @@ -6,7 +6,7 @@ use std::path::Path; use std::time::{Duration, Instant}; use anyhow::{bail, Context, Result}; -use jrny_save::{RobeColor, Savefile, LEVEL_NAMES}; +use jrny_save::{RobeColor, Savefile}; use ratatui::widgets::TableState; use tracing::{debug, error}; use tui_input::Input; @@ -171,13 +171,7 @@ impl State { 0 => savefile.journey_count = value.parse()?, 1 => savefile.total_companions_met = value.parse()?, 2 => savefile.total_collected_symbols = value.parse()?, - 3 => { - let level_id = LEVEL_NAMES - .iter() - .position(|&v| v == value.trim_end()) - .context("invalid level name")?; - savefile.current_level = level_id as u64; - } + 3 => savefile.current_level.set_by_name(value)?, 4 => savefile.companions_met = value.parse()?, 5 => { let new_length = value.parse()?; @@ -222,14 +216,7 @@ impl State { 0 => savefile.journey_count += 1, 1 => savefile.total_companions_met += 1, 2 => savefile.total_collected_symbols += 1, - 3 => { - let next_level = savefile.current_level + 1; - savefile.current_level = if next_level >= LEVEL_NAMES.len() as u64 { - 0 - } else { - savefile.current_level + 1 - }; - } + 3 => savefile.current_level = savefile.current_level.wrapping_next(), 4 => savefile.companions_met += 1, 5 => { if savefile.scarf_length < 30 { @@ -275,14 +262,7 @@ impl State { savefile.total_collected_symbols = savefile.total_collected_symbols.saturating_sub(1) } - 3 => { - let next_level: i64 = savefile.current_level as i64 - 1; - savefile.current_level = if next_level < 0 { - LEVEL_NAMES.len() as u64 - 1 - } else { - next_level as u64 - }; - } + 3 => savefile.current_level = savefile.current_level.wrapping_previous(), 4 => savefile.companions_met = savefile.companions_met.saturating_sub(1), 5 => { if savefile.scarf_length > 0 { diff --git a/crates/wayfarer/src/tui/view/info/stats.rs b/crates/wayfarer/src/tui/view/info/stats.rs index ef8c6b0..aef85ee 100644 --- a/crates/wayfarer/src/tui/view/info/stats.rs +++ b/crates/wayfarer/src/tui/view/info/stats.rs @@ -52,7 +52,7 @@ pub(super) fn render<'a>(state: &mut State, frame: &mut Frame, area: Rect) { "Total Symbols Collected", savefile.total_collected_symbols.to_string(), ), - ("Current Level", savefile.current_level_name().to_string()), + ("Current Level", savefile.current_level.to_string()), ("Companions Met", savefile.companions_met.to_string()), ("Scarf Length", savefile.scarf_length.to_string()), ("Symbol Number", savefile.symbol.as_ref().to_string()),