diff --git a/game/Cargo.toml b/game/Cargo.toml index 4538ee0..e1da054 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -14,7 +14,7 @@ tracing = { version = "0.1", features = ["log"] } serde = { version = "1.0.126", features = ["derive"] } serde_json = "1.0.64" thiserror = "1.0" -chrono = "0.4" +chrono = { version = "0.4", features = ["serde"] } rust-embed = "6.2.0" raylib = { version = "3.5", git = "https://github.com/ewpratten/raylib-rs", rev = "2ae949cb3488dd1bb052ece71d61021c8dd6e910", features = [ "serde" @@ -31,7 +31,7 @@ pkg-version = "1.0" cfg-if = "1.0" num-derive = "0.3" num = "0.4" -tiled = { version ="0.9.5", default-features = false } +tiled = { version = "0.9.5", default-features = false } async-trait = "0.1.51" webbrowser = "0.5" diff --git a/game/src/context.rs b/game/src/context.rs index 10957df..a27eb26 100644 --- a/game/src/context.rs +++ b/game/src/context.rs @@ -3,19 +3,21 @@ use std::{cell::RefCell, sync::mpsc::Sender}; use chrono::{DateTime, Utc}; use discord_sdk::activity::ActivityBuilder; -use crate::{utilities::non_ref_raylib::HackedRaylibHandle, GameConfig}; +use crate::{progress::ProgressData, utilities::non_ref_raylib::HackedRaylibHandle, GameConfig}; #[derive(Debug)] pub enum ControlFlag { Quit, SwitchLevel(usize), - UpdateLevelStart(DateTime) + UpdateLevelStart(DateTime), + SaveProgress } #[derive(Debug)] pub struct GameContext { pub renderer: RefCell, pub config: GameConfig, + pub player_progress: ProgressData, pub current_level: usize, pub level_start_time: DateTime, pub discord_rpc_send: Sender>, diff --git a/game/src/lib.rs b/game/src/lib.rs index 3b4c965..f1cb87f 100644 --- a/game/src/lib.rs +++ b/game/src/lib.rs @@ -81,6 +81,7 @@ use utilities::discord::DiscordConfig; use crate::{ context::GameContext, discord_rpc::{maybe_set_discord_presence, try_connect_to_local_discord}, + progress::ProgressData, scenes::{build_screen_state_machine, Scenes}, utilities::{ game_config::FinalShaderConfig, @@ -108,6 +109,7 @@ mod scenes; mod utilities; pub use utilities::{datastore::StaticGameData, game_config::GameConfig}; mod character; +mod progress; /// The game entrypoint pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box> { @@ -148,6 +150,9 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box Result<(), Box Result<(), Box break, context::ControlFlag::SwitchLevel(level) => { context.as_mut().current_level = level; + context.as_mut().player_progress.save(); } context::ControlFlag::UpdateLevelStart(time) => { context.as_mut().level_start_time = time; + context.as_mut().player_progress.save(); + } + context::ControlFlag::SaveProgress => { + context.as_mut().player_progress.save(); } } } @@ -333,5 +344,6 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box, +} + +impl ProgressData { + pub fn get_level_best_time(&self, level: usize) -> Option { + let level_best_time = self.level_best_times.get(&level); + match level_best_time { + Some(time) => Some(Duration::seconds(*time)), + None => None, + } + } + + pub fn maybe_write_new_time(&mut self, level: usize, time: &Duration) { + let time_in_seconds = time.num_seconds(); + if let Some(best_time) = self.get_level_best_time(level) { + if best_time.num_seconds() > time_in_seconds { + self.level_best_times.insert(level, time_in_seconds); + } + } else { + self.level_best_times.insert(level, time_in_seconds); + } + } + + pub fn load_from_file() -> Self { + info!("Loading progress data from file"); + serde_json::from_str( + &std::fs::read_to_string("./savegame.json") + .unwrap_or("{\"level_best_times\":{}}".to_string()), + ) + .unwrap_or(Self::default()) + } + + pub fn save(&self) { + info!("Saving progress data to file"); + std::fs::write("./savegame.json", serde_json::to_string(self).unwrap()).unwrap() + } +} diff --git a/game/src/scenes/ingame_scene/mod.rs b/game/src/scenes/ingame_scene/mod.rs index 798a098..3645217 100644 --- a/game/src/scenes/ingame_scene/mod.rs +++ b/game/src/scenes/ingame_scene/mod.rs @@ -104,7 +104,12 @@ impl Action for InGameScreen { if self.current_level_idx != context.current_level { self.current_level_idx = context.current_level; self.level_switch_timestamp = Utc::now(); - context.flag_send.send(Some(ControlFlag::UpdateLevelStart(self.level_switch_timestamp))).unwrap(); + context + .flag_send + .send(Some(ControlFlag::UpdateLevelStart( + self.level_switch_timestamp, + ))) + .unwrap(); } // Grab exclusive access to the renderer @@ -131,6 +136,12 @@ impl Action for InGameScreen { // Check if the player won let cur_level = self.levels.get(context.current_level).unwrap(); if self.player.position.x > cur_level.zones.win.x { + // Save the progress + context + .flag_send + .send(Some(ControlFlag::SaveProgress)) + .unwrap(); + // If this is the last level, win the game if self.current_level_idx >= self.levels.len() - 1 { return Ok(ActionFlag::SwitchState(Scenes::WinScreen)); diff --git a/savegame.json b/savegame.json new file mode 100644 index 0000000..cdcc103 --- /dev/null +++ b/savegame.json @@ -0,0 +1 @@ +{"level_best_times":{}} \ No newline at end of file