Browse Source

I think saving is working, needs more work. This is quite wretched.

Herbert Wolverson 5 years ago
parent
commit
5c8c25c11e

File diff suppressed because it is too large
+ 65 - 0
book/src/chapter_11.md


+ 174 - 16
chapter-11-loadsave/src/components.rs

@@ -3,14 +3,17 @@ use specs::prelude::*;
 extern crate specs_derive;
 extern crate rltk;
 use rltk::{RGB};
+use serde::{Serialize, Deserialize};
+use specs::saveload::{Marker, ConvertSaveload};
+use specs::error::NoError;
 
-#[derive(Component)]
+#[derive(Component, Serialize, Deserialize, Clone)]
 pub struct Position {
     pub x: i32,
     pub y: i32,
 }
 
-#[derive(Component)]
+#[derive(Component, Serialize, Deserialize, Clone)]
 pub struct Renderable {
     pub glyph: u8,
     pub fg: RGB,
@@ -18,28 +21,28 @@ pub struct Renderable {
     pub render_order : i32
 }
  
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct Player {}
 
-#[derive(Component)]
+#[derive(Component, Serialize, Deserialize, Clone)]
 pub struct Viewshed {
     pub visible_tiles : Vec<rltk::Point>,
     pub range : i32,
     pub dirty : bool
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct Monster {}
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct Name {
     pub name : String
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct BlocksTile {}
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct CombatStats {
     pub max_hp : i32,
     pub hp : i32,
@@ -47,65 +50,220 @@ pub struct CombatStats {
     pub power : i32
 }
 
+// See wrapper below for serialization
 #[derive(Component, Debug)]
 pub struct WantsToMelee {
     pub target : Entity
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct SufferDamage {
     pub amount : i32
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct Item {}
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct Consumable {}
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct Ranged {
     pub range : i32
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct InflictsDamage {
     pub damage : i32
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct AreaOfEffect {
     pub radius : i32
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct Confusion {
     pub turns : i32
 }
 
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Serialize, Deserialize, Clone)]
 pub struct ProvidesHealing {
     pub heal_amount : i32
 }
 
+// See wrapper below for serialization
 #[derive(Component, Debug)]
 pub struct InBackpack {
     pub owner : Entity
 }
 
+// See wrapper below for serialization
 #[derive(Component, Debug)]
 pub struct WantsToPickupItem {
     pub collected_by : Entity,
     pub item : Entity
 }
 
+// See wrapper below for serialization
 #[derive(Component, Debug)]
 pub struct WantsToUseItem {
     pub item : Entity,
     pub target : Option<rltk::Point>
 }
 
+// See wrapper below for serialization
 #[derive(Component, Debug)]
 pub struct WantsToDropItem {
     pub item : Entity
 }
+
+// Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an
+// Entity.
+
+pub struct SerializeMe;
+
+
+
+// WantsToMelee wrapper
+#[derive(Serialize, Deserialize, Clone)]
+pub struct WantsToMeleeData<M>(M);
+
+impl<M: Marker + Serialize> ConvertSaveload<M> for WantsToMelee
+where
+    for<'de> M: Deserialize<'de>,
+{
+    type Data = WantsToMeleeData<M>;
+    type Error = NoError;
+
+    fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
+    where
+        F: FnMut(Entity) -> Option<M>,
+    {
+        let marker = ids(self.target).unwrap();
+        Ok(WantsToMeleeData(marker))
+    }
+
+    fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
+    where
+        F: FnMut(M) -> Option<Entity>,
+    {
+        let entity = ids(data.0).unwrap();
+        Ok(WantsToMelee{target: entity})
+    }
+}
+
+// InBackpack wrapper
+#[derive(Serialize, Deserialize, Clone)]
+pub struct InBackpackData<M>(M);
+
+impl<M: Marker + Serialize> ConvertSaveload<M> for InBackpack
+where
+    for<'de> M: Deserialize<'de>,
+{
+    type Data = InBackpackData<M>;
+    type Error = NoError;
+
+    fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
+    where
+        F: FnMut(Entity) -> Option<M>,
+    {
+        let marker = ids(self.owner).unwrap();
+        Ok(InBackpackData(marker))
+    }
+
+    fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
+    where
+        F: FnMut(M) -> Option<Entity>,
+    {
+        let entity = ids(data.0).unwrap();
+        Ok(InBackpack{owner: entity})
+    }
+}
+
+// WantsToPickupItem wrapper
+#[derive(Serialize, Deserialize, Clone)]
+pub struct WantsToPickupItemData<M>(M, M);
+
+impl<M: Marker + Serialize> ConvertSaveload<M> for WantsToPickupItem
+where
+    for<'de> M: Deserialize<'de>,
+{
+    type Data = WantsToPickupItemData<M>;
+    type Error = NoError;
+
+    fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
+    where
+        F: FnMut(Entity) -> Option<M>,
+    {
+        let marker = ids(self.collected_by).unwrap();
+        let marker2 = ids(self.item).unwrap();
+        Ok(WantsToPickupItemData(marker, marker2))
+    }
+
+    fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
+    where
+        F: FnMut(M) -> Option<Entity>,
+    {
+        let collected_by = ids(data.0).unwrap();
+        let item = ids(data.1).unwrap();
+        Ok(WantsToPickupItem{collected_by, item})
+    }
+}
+
+// WantsToUseItem wrapper
+#[derive(Serialize, Deserialize, Clone)]
+pub struct WantsToUseItemData<M>(M, Option<rltk::Point>);
+
+impl<M: Marker + Serialize> ConvertSaveload<M> for WantsToUseItem
+where
+    for<'de> M: Deserialize<'de>,
+{
+    type Data = WantsToUseItemData<M>;
+    type Error = NoError;
+
+    fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
+    where
+        F: FnMut(Entity) -> Option<M>,
+    {
+        let marker = ids(self.item).unwrap();
+        Ok(WantsToUseItemData(marker, self.target))
+    }
+
+    fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
+    where
+        F: FnMut(M) -> Option<Entity>,
+    {
+        let item = ids(data.0).unwrap();
+        let target = data.1;
+        Ok(WantsToUseItem{item, target})
+    }
+}
+
+// WantsToDropItem wrapper
+#[derive(Serialize, Deserialize, Clone)]
+pub struct WantsToDropItemData<M>(M);
+
+impl<M: Marker + Serialize> ConvertSaveload<M> for WantsToDropItem
+where
+    for<'de> M: Deserialize<'de>,
+{
+    type Data = WantsToDropItemData<M>;
+    type Error = NoError;
+
+    fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
+    where
+        F: FnMut(Entity) -> Option<M>,
+    {
+        let marker = ids(self.item).unwrap();
+        Ok(WantsToDropItemData(marker))
+    }
+
+    fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
+    where
+        F: FnMut(M) -> Option<Entity>,
+    {
+        let entity = ids(data.0).unwrap();
+        Ok(WantsToDropItem{item: entity})
+    }
+}

+ 9 - 8
chapter-11-loadsave/src/main.rs

@@ -1,9 +1,9 @@
 extern crate serde;
-use serde::{Serialize, Deserialize};
 extern crate rltk;
 use rltk::{Console, GameState, Rltk, Point};
 extern crate specs;
 use specs::prelude::*;
+use specs::saveload::{SimpleMarker, SimpleMarkerAllocator};
 #[macro_use]
 extern crate specs_derive;
 mod components;
@@ -29,10 +29,7 @@ mod gamelog;
 mod spawner;
 mod inventory_system;
 use inventory_system::{ ItemCollectionSystem, ItemUseSystem, ItemDropSystem };
-use std::fs;
-use std::fs::File;
-use std::io::Write;
-use std::path::Path;
+mod saveload_system;
 
 #[derive(PartialEq, Copy, Clone)]
 pub enum RunState { AwaitingInput, 
@@ -160,9 +157,10 @@ impl GameState for State {
                 }
             }
             RunState::SaveGame => {
-                let data = serde_json::to_string(&*self.ecs.fetch::<Map>()).unwrap();
-                println!("{}", data);
-                //let mut f = File::create("./savegame.json").expect("Unable to create file");
+                saveload_system::save_game(&mut self.ecs);
+
+                //let data = serde_json::to_string(&*self.ecs.fetch::<Map>()).unwrap();
+                //let mut f = File::create("./savemap.json").expect("Unable to create file");
                 //f.write_all(data.as_bytes()).expect("Unable to write data");
 
                 newrunstate = RunState::MainMenu{ menu_selection : gui::MainMenuSelection::LoadGame };
@@ -214,6 +212,9 @@ fn main() {
     gs.ecs.register::<WantsToUseItem>();
     gs.ecs.register::<WantsToDropItem>();
     gs.ecs.register::<Confusion>();
+    gs.ecs.register::<SimpleMarker<SerializeMe>>();
+
+    gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
 
     let map : Map = Map::new_map_rooms_and_corridors();
     let (player_x, player_y) = map.rooms[0].center();

+ 8 - 1
chapter-11-loadsave/src/spawner.rs

@@ -3,7 +3,8 @@ use rltk::{ RGB, RandomNumberGenerator };
 extern crate specs;
 use specs::prelude::*;
 use super::{CombatStats, Player, Renderable, Name, Position, Viewshed, Monster, BlocksTile, Rect, Item, 
-    Consumable, Ranged, ProvidesHealing, map::MAPWIDTH, InflictsDamage, AreaOfEffect, Confusion};
+    Consumable, Ranged, ProvidesHealing, map::MAPWIDTH, InflictsDamage, AreaOfEffect, Confusion, SerializeMe};
+use crate::specs::saveload::{MarkedBuilder, SimpleMarker};
 
 /// Spawns the player and returns his/her entity object.
 pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
@@ -20,6 +21,7 @@ pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
         .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
         .with(Name{name: "Player".to_string() })
         .with(CombatStats{ max_hp: 30, hp: 30, defense: 2, power: 5 })
+        .marked::<SimpleMarker<SerializeMe>>()
         .build()
 }
 
@@ -122,6 +124,7 @@ fn monster<S : ToString>(ecs: &mut World, x: i32, y: i32, glyph : u8, name : S)
         .with(Name{ name : name.to_string() })
         .with(BlocksTile{})
         .with(CombatStats{ max_hp: 16, hp: 16, defense: 1, power: 4 })
+        .marked::<SimpleMarker<SerializeMe>>()
         .build();
 }
 
@@ -138,6 +141,7 @@ fn health_potion(ecs: &mut World, x: i32, y: i32) {
         .with(Item{})
         .with(Consumable{})
         .with(ProvidesHealing{ heal_amount: 8 })
+        .marked::<SimpleMarker<SerializeMe>>()
         .build();
 }
 
@@ -155,6 +159,7 @@ fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
         .with(Consumable{})
         .with(Ranged{ range: 6 })
         .with(InflictsDamage{ damage: 20 })
+        .marked::<SimpleMarker<SerializeMe>>()
         .build();
 }
 
@@ -173,6 +178,7 @@ fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
         .with(Ranged{ range: 6 })
         .with(InflictsDamage{ damage: 20 })
         .with(AreaOfEffect{ radius: 3 })
+        .marked::<SimpleMarker<SerializeMe>>()
         .build();
 }
 
@@ -190,5 +196,6 @@ fn confusion_scroll(ecs: &mut World, x: i32, y: i32) {
         .with(Consumable{})
         .with(Ranged{ range: 6 })
         .with(Confusion{ turns: 4 })
+        .marked::<SimpleMarker<SerializeMe>>()
         .build();
 }