use std::fs; use std::fs::File; use std::path::Path; use ::rltk::Point; use ::specs::error::NoError; use ::specs::prelude::*; use ::specs::saveload::{ DeserializeComponents, MarkedBuilder, SerializeComponents, SimpleMarker, SimpleMarkerAllocator, }; use crate::components::*; use crate::map::Map; macro_rules! serialize_individually { ($ecs:expr, $ser:expr, $data:expr, $( $type:ty),*,) => { $( SerializeComponents::>::serialize( &( $ecs.read_storage::<$type>(), ), &$data.0, &$data.1, &mut $ser, ) .unwrap(); )* }; } #[cfg(target_arch = "wasm32")] pub fn save_game(_ecs: &mut World) {} #[cfg(not(target_arch = "wasm32"))] pub fn save_game(ecs: &mut World) { // Create helper let mapcopy = ecs.get_mut::().unwrap().clone(); let savehelper = ecs .create_entity() .with(SerializationHelper { map: mapcopy }) .marked::>() .build(); // Actually serialize { let data = ( ecs.entities(), ecs.read_storage::>(), ); let writer = File::create("./savegame.json").unwrap(); let mut serializer = ::serde_json::Serializer::new(writer); serialize_individually!( ecs, serializer, data, Position, Renderable, Player, Viewshed, Monster, Name, BlocksTile, CombatStats, SufferDamage, WantsToMelee, Item, Consumable, Ranged, InflictsDamage, AreaOfEffect, Confusion, ProvidesHealing, InBackpack, WantsToPickupItem, WantsToUseItem, WantsToDropItem, SerializationHelper, Equippable, Equipped, MeleePowerBonus, DefenseBonus, WantsToRemoveItem, ParticleLifetime, HungerClock, ProvidesFood, MagicMapper, Hidden, EntryTrigger, EntityMoved, SingleActivation, BlocksVisibility, Door, Bystander, ); } // Clean up ecs.delete_entity(savehelper) .expect("Failed to clean up savehelper component"); } pub fn does_save_exist() -> bool { Path::new("./savegame.json").exists() } macro_rules! deserialize_individually { ($ecs:expr, $de:expr, $data:expr, $( $type:ty),*,) => { $( DeserializeComponents::::deserialize( &mut ( &mut $ecs.write_storage::<$type>(), ), &$data.0, // entities &mut $data.1, // marker &mut $data.2, // allocater &mut $de, ) .unwrap(); )* }; } pub fn load_game(ecs: &mut World) { { // Delete everything let mut to_delete = Vec::new(); for e in ecs.entities().join() { to_delete.push(e); } for del in to_delete.iter() { ecs.delete_entity(*del).expect("Failed to delete entity"); } } let data = fs::read_to_string("./savegame.json").unwrap(); let mut de = ::serde_json::Deserializer::from_str(&data); { let mut d = ( &mut ecs.entities(), &mut ecs.write_storage::>(), &mut ecs.write_resource::>(), ); deserialize_individually!( ecs, de, d, Position, Renderable, Player, Viewshed, Monster, Name, BlocksTile, CombatStats, SufferDamage, WantsToMelee, Item, Consumable, Ranged, InflictsDamage, AreaOfEffect, Confusion, ProvidesHealing, InBackpack, WantsToPickupItem, WantsToUseItem, WantsToDropItem, SerializationHelper, Equippable, Equipped, MeleePowerBonus, DefenseBonus, WantsToRemoveItem, ParticleLifetime, HungerClock, ProvidesFood, MagicMapper, Hidden, EntryTrigger, EntityMoved, SingleActivation, BlocksVisibility, Door, Bystander, ); } let mut deleteme: Option = None; { let entities = ecs.entities(); let helper = ecs.read_storage::(); let player = ecs.read_storage::(); let position = ecs.read_storage::(); for (e, h) in (&entities, &helper).join() { let mut worldmap = ecs.write_resource::(); *worldmap = h.map.clone(); worldmap.tile_content = vec![Vec::new(); (worldmap.height * worldmap.width) as usize]; deleteme = Some(e); } for (e, _p, pos) in (&entities, &player, &position).join() { let mut ppos = ecs.write_resource::(); *ppos = Point::new(pos.x, pos.y); let mut player_resource = ecs.write_resource::(); *player_resource = e; } } ecs.delete_entity(deleteme.unwrap()) .expect("Unable to delete helper entity"); } pub fn delete_save() { if Path::new("./savegame.json").exists() { std::fs::remove_file("./savegame.json").expect("Failed to delete save file."); } }