use std::fs; use std::io::Write; use std::ops::Deref; use std::path::Path; use bevy::math::Vec3; use bevy::prelude::*; use bevy::utils::HashMap; use serde::{Deserialize, Serialize}; use crate::states::Player; use crate::world::{ CurrentResidence, EncounterState, HungerState, TradingState, TravelPath, TravelTarget, }; #[derive(Serialize, Deserialize, Debug, Resource)] pub struct PersistenceState { pub player_location: Vec3, pub player_inventory: TradingState, pub player_hunger: HungerState, pub travel_path: Option<TravelPath>, pub travel_target: Option<TravelTarget>, pub previous_location: CurrentResidence, pub encounter_state: EncounterState, pub town_states: HashMap<String, (TradingState, HungerState)>, } #[derive(Clone, Debug)] pub struct SaveFileEvent { pub filename: Option<String>, } #[derive(Clone, Debug)] pub struct LoadFileEvent { pub filename: Option<String>, } pub fn sync_state_to_persistence( mut commands: Commands, mut state: Option<ResMut<PersistenceState>>, player_query: Query< ( &Transform, Option<&TravelPath>, Option<&TravelTarget>, &CurrentResidence, ), With<Player>, >, encounters: Res<EncounterState>, hunger: Option<Res<HungerState>>, trading: Option<Res<TradingState>>, ) { match state { Some(mut state) => { if let Some((transform, maybe_travel, maybe_target, residence)) = player_query.iter().next() { *state = PersistenceState { player_location: transform.translation, player_inventory: trading.map(|r| r.clone()).unwrap_or_default(), player_hunger: hunger.map(|r| r.clone()).unwrap_or_default(), travel_path: maybe_travel.cloned(), travel_target: maybe_target.cloned(), previous_location: residence.clone(), encounter_state: encounters.clone(), town_states: Default::default(), }; } } None => { if let Some((transform, maybe_travel, maybe_target, residence)) = player_query.iter().next() { commands.insert_resource(PersistenceState { player_location: transform.translation, player_inventory: trading.map(|r| r.clone()).unwrap_or_default(), player_hunger: hunger.map(|r| r.clone()).unwrap_or_default(), travel_path: maybe_travel.cloned(), travel_target: maybe_target.cloned(), previous_location: residence.clone(), encounter_state: encounters.clone(), town_states: Default::default(), }) } } } } pub fn handle_save_event( mut events: ResMut<Events<SaveFileEvent>>, state: Option<Res<PersistenceState>>, ) { let root_data_dir = directories::ProjectDirs::from("com", "microhacks", "TraderTales") .expect("Failed to get project dir"); if let Some(state) = state { for event in events.drain() { std::fs::create_dir_all(root_data_dir.data_dir()).expect("Failed to create data dir"); match fs::File::create(match event.filename { Some(name) => root_data_dir.data_dir().join(name), None => root_data_dir.data_dir().join("autosave.json"), }) { Ok(file) => { serde_json::to_writer_pretty(file, &*state) .inspect_err(|err| { log::error!("{}", err); }) .expect("Failed to create save data"); } Err(e) => { log::error!("{}", e); } } } } }