use super::components::*; use bracket_lib::prelude::*; use specs::error::NoError; use specs::prelude::*; use specs::saveload::{ DeserializeComponents, MarkedBuilder, SerializeComponents, SimpleMarker, SimpleMarkerAllocator, }; use std::fs; use std::fs::File; use std::path::Path; 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) { console::log( "Unfortunately, saving isn't supported in any easy way on the web. Sorry! You can, at least, save your morgue file after dying - it'll be written to the log, and just needs saving into a text file." ) } #[cfg(not(target_arch = "wasm32"))] pub fn save_game(ecs: &mut World) { // Create helper let mapcopy = ecs.get_mut::().unwrap().clone(); let dungeon_master = ecs.get_mut::().unwrap().clone(); let savehelper = ecs .create_entity() .with(SerializationHelper { map: mapcopy }) .marked::>() .build(); let savehelper2 = ecs .create_entity() .with(DMSerializationHelper { map: dungeon_master, log: crate::gamelog::clone_log(), event_counts: crate::gamelog::clone_event_counts(), events: crate::gamelog::clone_events(), }) .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, AOE, ArmourClassBonus, Attributes, Beatitude, Bleeds, Blind, BlocksTile, BlocksVisibility, Burden, Chasing, Clock, Confusion, Consumable, Destructible, Digger, Door, Energy, EntityMoved, EntryTrigger, EquipmentChanged, Equippable, Equipped, Faction, GrantsSpell, GrantsXP, HasAncestry, HasClass, HasDamageModifiers, Hidden, HungerClock, IdentifiedBeatitude, IdentifiedItem, InBackpack, InflictsDamage, IntrinsicChanged, Intrinsics, Item, Key, KnownSpells, LootTable, MagicItem, MagicMapper, MeleeWeapon, Mind, MoveMode, MultiAttack, NaturalAttacks, Name, ObfuscatedName, OtherLevelPosition, ParticleLifetime, Player, Pools, Position, Prop, ProvidesHealing, ProvidesIdentify, ProvidesNutrition, ProvidesRemoveCurse, Quips, Ranged, Renderable, SingleActivation, Skills, SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple, Stackable, TakingTurn, Telepath, ToHitBonus, Viewshed, Charges, WantsToApproach, WantsToAssignKey, WantsToDelete, WantsToDropItem, WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper ); } // Clean up ecs.delete_entity(savehelper).expect(" Crash on cleanup"); ecs.delete_entity(savehelper2).expect(" Crash on cleanup"); } 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("Deletion failed"); } } 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, AOE, ArmourClassBonus, Attributes, Beatitude, Bleeds, Blind, BlocksTile, BlocksVisibility, Burden, Chasing, Clock, Confusion, Consumable, Destructible, Digger, Door, Energy, EntityMoved, EntryTrigger, EquipmentChanged, Equippable, Equipped, Faction, GrantsSpell, GrantsXP, HasAncestry, HasClass, HasDamageModifiers, Hidden, HungerClock, IdentifiedBeatitude, IdentifiedItem, InBackpack, InflictsDamage, IntrinsicChanged, Intrinsics, Item, Key, KnownSpells, LootTable, MagicItem, MagicMapper, MeleeWeapon, Mind, MoveMode, MultiAttack, NaturalAttacks, Name, ObfuscatedName, OtherLevelPosition, ParticleLifetime, Player, Pools, Position, Prop, ProvidesHealing, ProvidesIdentify, ProvidesNutrition, ProvidesRemoveCurse, Quips, Ranged, Renderable, SingleActivation, Skills, SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple, Stackable, TakingTurn, Telepath, ToHitBonus, Viewshed, Charges, WantsToApproach, WantsToAssignKey, WantsToDelete, WantsToDropItem, WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper ); } let mut deleteme: Option = None; let mut deleteme2: Option = None; { let entities = ecs.entities(); let helper = ecs.read_storage::(); let helper2 = 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(); crate::spatial::set_size((worldmap.width * worldmap.height) as usize); deleteme = Some(e); } for (e, h) in (&entities, &helper2).join() { let mut dungeonmaster = ecs.write_resource::(); *dungeonmaster = h.map.clone(); deleteme2 = Some(e); crate::gamelog::restore_log(&mut h.log.clone()); crate::gamelog::restore_event_counter(h.event_counts.clone()); crate::gamelog::restore_events(h.events.clone()); } 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"); ecs.delete_entity(deleteme2.unwrap()).expect(" Unable to delete helper"); } pub fn delete_save() { if Path::new("./savegame.json").exists() { std::fs::remove_file("./savegame.json").expect("Unable to delete file"); } }