Skip to content
Snippets Groups Projects
save_file.rs 3.15 KiB
Newer Older
Louis's avatar
Louis committed
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);
				}
			}
		}
	}
}