saveload system
localstorage isn't supported by wasm, so playing online will probably just not have save games for a while
This commit is contained in:
parent
dd91a8cca7
commit
51060f1a85
11 changed files with 290 additions and 63 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
/target
|
||||
.rustfmt.toml
|
||||
.vscode/*
|
||||
savegame.json
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -142,6 +142,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"lazy_static",
|
||||
"parking_lot 0.11.2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -169,6 +170,7 @@ version = "0.8.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f31b525fcd65027885f3a1e3a250a5dd397d70de4a6a5a125f03e0bef951499"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"ultraviolet 0.9.1",
|
||||
]
|
||||
|
||||
|
|
@ -262,6 +264,7 @@ dependencies = [
|
|||
"rand",
|
||||
"rand_xorshift",
|
||||
"regex",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
|
|
@ -2275,6 +2278,8 @@ dependencies = [
|
|||
"bracket-lib 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)",
|
||||
"criterion",
|
||||
"rltk",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"specs",
|
||||
"specs-derive",
|
||||
]
|
||||
|
|
@ -2545,6 +2550,7 @@ dependencies = [
|
|||
"hibitset",
|
||||
"log",
|
||||
"rayon",
|
||||
"serde",
|
||||
"shred",
|
||||
"shrev",
|
||||
"tuple_utils",
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rltk = { version = "^0.8.7" }
|
||||
rltk = { version = "^0.8.7", features = ["serde"] }
|
||||
bracket-lib = { git = "https://github.com/amethyst/bracket-lib.git", rev = "851f6f08675444fb6fa088b9e67bee9fd75554c6", features = ["serde"] }
|
||||
specs = "0.16.1"
|
||||
specs = { version = "0.16.1", features = ["serde"] }
|
||||
specs-derive = "0.4.1"
|
||||
serde = { version = "1.0.93", features = ["derive"]}
|
||||
serde_json = "1.0.39"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "^0.5" }
|
||||
|
|
|
|||
|
|
@ -1,14 +1,26 @@
|
|||
use rltk::RGB;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::error::NoError;
|
||||
use specs::prelude::*;
|
||||
use specs::saveload::{ConvertSaveload, Marker};
|
||||
use specs_derive::*;
|
||||
|
||||
#[derive(Component)]
|
||||
// Serialization helper code. We need to implement ConvertSaveload for each type that contains an
|
||||
// Entity.
|
||||
pub struct SerializeMe;
|
||||
// Special component that exists to help serialize the game data
|
||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||
pub struct SerializationHelper {
|
||||
pub map: super::map::Map,
|
||||
}
|
||||
|
||||
#[derive(Component, ConvertSaveload, Clone)]
|
||||
pub struct Position {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(Component, ConvertSaveload, Clone)]
|
||||
pub struct Renderable {
|
||||
pub glyph: rltk::FontCharType,
|
||||
pub fg: RGB,
|
||||
|
|
@ -16,28 +28,28 @@ pub struct Renderable {
|
|||
pub render_order: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Player {}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Monster {}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(Component, ConvertSaveload, Clone)]
|
||||
pub struct Viewshed {
|
||||
pub visible_tiles: Vec<rltk::Point>,
|
||||
pub range: i32,
|
||||
pub dirty: bool,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload, 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, ConvertSaveload, Clone)]
|
||||
pub struct CombatStats {
|
||||
pub max_hp: i32,
|
||||
pub hp: i32,
|
||||
|
|
@ -45,12 +57,12 @@ pub struct CombatStats {
|
|||
pub power: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Clone)]
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct WantsToMelee {
|
||||
pub target: Entity,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct SufferDamage {
|
||||
pub amount: Vec<i32>,
|
||||
}
|
||||
|
|
@ -66,63 +78,63 @@ impl SufferDamage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Item {}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct ProvidesHealing {
|
||||
pub amount: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct InflictsDamage {
|
||||
pub amount: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct Ranged {
|
||||
pub range: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct AOE {
|
||||
pub radius: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct Confusion {
|
||||
pub turns: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Clone)]
|
||||
#[derive(Component, Debug, ConvertSaveload)]
|
||||
pub struct InBackpack {
|
||||
pub owner: Entity,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Clone)]
|
||||
#[derive(Component, Debug, ConvertSaveload)]
|
||||
pub struct WantsToPickupItem {
|
||||
pub collected_by: Entity,
|
||||
pub item: Entity,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Clone)]
|
||||
#[derive(Component, Debug, ConvertSaveload)]
|
||||
pub struct WantsToDropItem {
|
||||
pub item: Entity,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, ConvertSaveload)]
|
||||
pub struct WantsToUseItem {
|
||||
pub item: Entity,
|
||||
pub target: Option<rltk::Point>,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Consumable {}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Destructible {}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
#[derive(Component, Clone, ConvertSaveload)]
|
||||
pub struct ParticleLifetime {
|
||||
pub lifetime_ms: f32,
|
||||
}
|
||||
|
|
|
|||
46
src/gui.rs
46
src/gui.rs
|
|
@ -273,34 +273,40 @@ pub enum MainMenuResult {
|
|||
}
|
||||
|
||||
pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult {
|
||||
let save_exists = super::saveload_system::does_save_exist();
|
||||
let runstate = gs.ecs.fetch::<RunState>();
|
||||
let assets = gs.ecs.fetch::<RexAssets>();
|
||||
|
||||
ctx.render_xp_sprite(&assets.menu, 0, 0);
|
||||
|
||||
ctx.print_color(38, 21, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "RUST-RL");
|
||||
ctx.print_color(40, 21, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "RUST-RL");
|
||||
|
||||
if let RunState::MainMenu { menu_selection: selection } = *runstate {
|
||||
let mut y = 24;
|
||||
if selection == MainMenuSelection::NewGame {
|
||||
ctx.print_color(34, 24, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(36, 24, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "new game");
|
||||
ctx.print_color(45, 24, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
ctx.print_color(37, 24, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(39, 24, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "new game");
|
||||
ctx.print_color(48, 24, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
} else {
|
||||
ctx.print_color(36, 24, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "new game");
|
||||
ctx.print_color(39, 24, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "new game");
|
||||
}
|
||||
if selection == MainMenuSelection::LoadGame {
|
||||
ctx.print_color(38, 26, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(40, 26, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "load game");
|
||||
ctx.print_color(50, 26, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
} else {
|
||||
ctx.print_color(40, 26, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "load game");
|
||||
y += 2;
|
||||
if save_exists {
|
||||
if selection == MainMenuSelection::LoadGame {
|
||||
ctx.print_color(36, y, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(38, y, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "load game");
|
||||
ctx.print_color(48, y, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
} else {
|
||||
ctx.print_color(38, y, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "load game");
|
||||
}
|
||||
y += 2;
|
||||
}
|
||||
if selection == MainMenuSelection::Quit {
|
||||
ctx.print_color(34, 28, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(36, 28, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "goodbye!");
|
||||
ctx.print_color(45, 28, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
ctx.print_color(37, y, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(39, y, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "goodbye!");
|
||||
ctx.print_color(48, y, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
} else {
|
||||
ctx.print_color(36, 28, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "quit");
|
||||
ctx.print_color(43, y, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "quit");
|
||||
}
|
||||
|
||||
match ctx.key {
|
||||
|
|
@ -312,21 +318,27 @@ pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult {
|
|||
VirtualKeyCode::N => return MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame },
|
||||
VirtualKeyCode::L => return MainMenuResult::NoSelection { selected: MainMenuSelection::LoadGame },
|
||||
VirtualKeyCode::Up => {
|
||||
let new_selection;
|
||||
let mut new_selection;
|
||||
match selection {
|
||||
MainMenuSelection::NewGame => new_selection = MainMenuSelection::Quit,
|
||||
MainMenuSelection::LoadGame => new_selection = MainMenuSelection::NewGame,
|
||||
MainMenuSelection::Quit => new_selection = MainMenuSelection::LoadGame,
|
||||
}
|
||||
if new_selection == MainMenuSelection::LoadGame && !save_exists {
|
||||
new_selection = MainMenuSelection::NewGame;
|
||||
}
|
||||
return MainMenuResult::NoSelection { selected: new_selection };
|
||||
}
|
||||
VirtualKeyCode::Down => {
|
||||
let new_selection;
|
||||
let mut new_selection;
|
||||
match selection {
|
||||
MainMenuSelection::NewGame => new_selection = MainMenuSelection::LoadGame,
|
||||
MainMenuSelection::LoadGame => new_selection = MainMenuSelection::Quit,
|
||||
MainMenuSelection::Quit => new_selection = MainMenuSelection::NewGame,
|
||||
}
|
||||
if new_selection == MainMenuSelection::LoadGame && !save_exists {
|
||||
new_selection = MainMenuSelection::Quit;
|
||||
}
|
||||
return MainMenuResult::NoSelection { selected: new_selection };
|
||||
}
|
||||
VirtualKeyCode::Return => return MainMenuResult::Selected { selected: selection },
|
||||
|
|
|
|||
34
src/main.rs
34
src/main.rs
|
|
@ -1,6 +1,8 @@
|
|||
use rltk::{GameState, Point, Rltk, RGB};
|
||||
use specs::prelude::*;
|
||||
use specs::saveload::{SimpleMarker, SimpleMarkerAllocator};
|
||||
use std::ops::Add;
|
||||
extern crate serde;
|
||||
|
||||
mod components;
|
||||
pub use components::*;
|
||||
|
|
@ -12,6 +14,7 @@ mod rect;
|
|||
pub use rect::Rect;
|
||||
mod gamelog;
|
||||
mod gui;
|
||||
mod saveload_system;
|
||||
mod spawner;
|
||||
mod visibility_system;
|
||||
use visibility_system::VisibilitySystem;
|
||||
|
|
@ -44,6 +47,7 @@ pub enum RunState {
|
|||
ShowDropItem,
|
||||
ShowTargeting { range: i32, item: Entity, aoe: i32 },
|
||||
MainMenu { menu_selection: gui::MainMenuSelection },
|
||||
SaveGame,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
|
|
@ -200,22 +204,23 @@ impl GameState for State {
|
|||
gui::MainMenuResult::NoSelection { selected } => {
|
||||
new_runstate = RunState::MainMenu { menu_selection: selected }
|
||||
}
|
||||
gui::MainMenuResult::Selected { selected } => {
|
||||
match selected {
|
||||
gui::MainMenuSelection::NewGame => new_runstate = RunState::PreRun,
|
||||
gui::MainMenuSelection::LoadGame => {
|
||||
new_runstate = RunState::PreRun;
|
||||
//saveload_system::load_game(&mut self.ecs);
|
||||
//rew_runstate = RunState::AwaitingInput;
|
||||
//saveload_system::delete_save();
|
||||
}
|
||||
gui::MainMenuSelection::Quit => {
|
||||
::std::process::exit(0);
|
||||
}
|
||||
gui::MainMenuResult::Selected { selected } => match selected {
|
||||
gui::MainMenuSelection::NewGame => new_runstate = RunState::PreRun,
|
||||
gui::MainMenuSelection::LoadGame => {
|
||||
saveload_system::load_game(&mut self.ecs);
|
||||
new_runstate = RunState::AwaitingInput;
|
||||
saveload_system::delete_save();
|
||||
}
|
||||
}
|
||||
gui::MainMenuSelection::Quit => {
|
||||
::std::process::exit(0);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
RunState::SaveGame => {
|
||||
saveload_system::save_game(&mut self.ecs);
|
||||
new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::LoadGame };
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -268,6 +273,9 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.register::<Consumable>();
|
||||
gs.ecs.register::<Destructible>();
|
||||
gs.ecs.register::<ParticleLifetime>();
|
||||
gs.ecs.register::<SimpleMarker<SerializeMe>>();
|
||||
gs.ecs.register::<SerializationHelper>();
|
||||
gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
|
||||
|
||||
let map = Map::new_map_rooms_and_corridors();
|
||||
let (player_x, player_y) = map.rooms[0].centre();
|
||||
|
|
|
|||
14
src/map.rs
14
src/map.rs
|
|
@ -1,11 +1,12 @@
|
|||
use super::Rect;
|
||||
use rltk::{Algorithm2D, BaseMap, Point, RandomNumberGenerator, Rltk, RGB};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::prelude::*;
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::HashSet;
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum TileType {
|
||||
Wall,
|
||||
Floor,
|
||||
|
|
@ -14,9 +15,9 @@ pub enum TileType {
|
|||
pub const MAPWIDTH: usize = 80;
|
||||
pub const MAPHEIGHT: usize = 43;
|
||||
const MAX_OFFSET: u8 = 32;
|
||||
const MAPCOUNT: usize = MAPHEIGHT * MAPWIDTH;
|
||||
pub const MAPCOUNT: usize = MAPHEIGHT * MAPWIDTH;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Serialize, Deserialize, Clone)]
|
||||
pub struct Map {
|
||||
pub tiles: Vec<TileType>,
|
||||
pub rooms: Vec<Rect>,
|
||||
|
|
@ -28,8 +29,11 @@ pub struct Map {
|
|||
pub green_offset: Vec<u8>,
|
||||
pub blue_offset: Vec<u8>,
|
||||
pub blocked: Vec<bool>,
|
||||
pub tile_content: Vec<Vec<Entity>>,
|
||||
pub bloodstains: HashSet<usize>,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
#[serde(skip_deserializing)]
|
||||
pub tile_content: Vec<Vec<Entity>>,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
|
|
@ -98,8 +102,8 @@ impl Map {
|
|||
green_offset: vec![0; MAPCOUNT],
|
||||
blue_offset: vec![0; MAPCOUNT],
|
||||
blocked: vec![false; MAPCOUNT],
|
||||
tile_content: vec![Vec::new(); MAPCOUNT],
|
||||
bloodstains: HashSet::new(),
|
||||
tile_content: vec![Vec::new(); MAPCOUNT],
|
||||
};
|
||||
|
||||
const MAX_ROOMS: i32 = 30;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|||
VirtualKeyCode::G => get_item(&mut gs.ecs),
|
||||
VirtualKeyCode::I => return RunState::ShowInventory,
|
||||
VirtualKeyCode::D => return RunState::ShowDropItem,
|
||||
VirtualKeyCode::Escape => return RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame },
|
||||
VirtualKeyCode::Escape => return RunState::SaveGame,
|
||||
_ => {
|
||||
return RunState::AwaitingInput;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct Rect {
|
||||
pub x1: i32,
|
||||
pub x2: i32,
|
||||
|
|
|
|||
170
src/saveload_system.rs
Normal file
170
src/saveload_system.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use super::components::*;
|
||||
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::<NoError, SimpleMarker<SerializeMe>>::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::<super::map::Map>().unwrap().clone();
|
||||
let savehelper =
|
||||
ecs.create_entity().with(SerializationHelper { map: mapcopy }).marked::<SimpleMarker<SerializeMe>>().build();
|
||||
|
||||
// Actually serialize
|
||||
{
|
||||
let data = (ecs.entities(), ecs.read_storage::<SimpleMarker<SerializeMe>>());
|
||||
|
||||
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,
|
||||
Destructible,
|
||||
Ranged,
|
||||
InflictsDamage,
|
||||
AOE,
|
||||
Confusion,
|
||||
ProvidesHealing,
|
||||
InBackpack,
|
||||
WantsToPickupItem,
|
||||
WantsToUseItem,
|
||||
WantsToDropItem,
|
||||
SerializationHelper
|
||||
);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
ecs.delete_entity(savehelper).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::<NoError, _>::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::<SimpleMarker<SerializeMe>>(),
|
||||
&mut ecs.write_resource::<SimpleMarkerAllocator<SerializeMe>>(),
|
||||
);
|
||||
|
||||
deserialize_individually!(
|
||||
ecs,
|
||||
de,
|
||||
d,
|
||||
Position,
|
||||
Renderable,
|
||||
Player,
|
||||
Viewshed,
|
||||
Monster,
|
||||
Name,
|
||||
BlocksTile,
|
||||
CombatStats,
|
||||
SufferDamage,
|
||||
WantsToMelee,
|
||||
Item,
|
||||
Consumable,
|
||||
Destructible,
|
||||
Ranged,
|
||||
InflictsDamage,
|
||||
AOE,
|
||||
Confusion,
|
||||
ProvidesHealing,
|
||||
InBackpack,
|
||||
WantsToPickupItem,
|
||||
WantsToUseItem,
|
||||
WantsToDropItem,
|
||||
SerializationHelper
|
||||
);
|
||||
}
|
||||
|
||||
let mut deleteme: Option<Entity> = None;
|
||||
{
|
||||
let entities = ecs.entities();
|
||||
let helper = ecs.read_storage::<SerializationHelper>();
|
||||
let player = ecs.read_storage::<Player>();
|
||||
let position = ecs.read_storage::<Position>();
|
||||
for (e, h) in (&entities, &helper).join() {
|
||||
let mut worldmap = ecs.write_resource::<super::map::Map>();
|
||||
*worldmap = h.map.clone();
|
||||
worldmap.tile_content = vec![Vec::new(); super::map::MAPCOUNT];
|
||||
deleteme = Some(e);
|
||||
}
|
||||
for (e, _p, pos) in (&entities, &player, &position).join() {
|
||||
let mut ppos = ecs.write_resource::<rltk::Point>();
|
||||
*ppos = rltk::Point::new(pos.x, pos.y);
|
||||
let mut player_resource = ecs.write_resource::<Entity>();
|
||||
*player_resource = e;
|
||||
}
|
||||
}
|
||||
ecs.delete_entity(deleteme.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");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
use super::{
|
||||
BlocksTile, CombatStats, Confusion, Consumable, Destructible, InflictsDamage, Item, Monster, Name, Player,
|
||||
Position, ProvidesHealing, Ranged, Rect, Renderable, Viewshed, AOE, MAPWIDTH,
|
||||
Position, ProvidesHealing, Ranged, Rect, Renderable, SerializeMe, Viewshed, AOE, MAPWIDTH,
|
||||
};
|
||||
use rltk::{RandomNumberGenerator, RGB};
|
||||
use specs::prelude::*;
|
||||
use 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 {
|
||||
|
|
@ -19,6 +20,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
|||
.with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true })
|
||||
.with(Name { name: "hero (you)".to_string() })
|
||||
.with(CombatStats { max_hp: 30, hp: 30, defence: 2, power: 5 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build()
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +66,7 @@ fn monster<S: ToString>(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharTy
|
|||
.with(Name { name: name.to_string() })
|
||||
.with(BlocksTile {})
|
||||
.with(CombatStats { max_hp: 16, hp: 16, defence: 1, power: 4 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +146,7 @@ fn health_potion(ecs: &mut World, x: i32, y: i32) {
|
|||
.with(Consumable {})
|
||||
.with(Destructible {})
|
||||
.with(ProvidesHealing { amount: 12 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +164,7 @@ fn weak_health_potion(ecs: &mut World, x: i32, y: i32) {
|
|||
.with(Consumable {})
|
||||
.with(Destructible {})
|
||||
.with(ProvidesHealing { amount: 6 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +182,7 @@ fn poison_potion(ecs: &mut World, x: i32, y: i32) {
|
|||
.with(Consumable {})
|
||||
.with(Destructible {})
|
||||
.with(ProvidesHealing { amount: -12 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +203,7 @@ fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
|
|||
.with(Destructible {})
|
||||
.with(Ranged { range: 12 }) // Long range - as far as default vision range
|
||||
.with(InflictsDamage { amount: 10 }) // Low~ damage
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -216,6 +223,7 @@ fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
|
|||
.with(Ranged { range: 10 })
|
||||
.with(InflictsDamage { amount: 20 })
|
||||
.with(AOE { radius: 3 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -234,5 +242,6 @@ fn confusion_scroll(ecs: &mut World, x: i32, y: i32) {
|
|||
.with(Destructible {})
|
||||
.with(Ranged { range: 10 })
|
||||
.with(Confusion { turns: 4 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue