Savegames hold only the game state: no settings, no camera (settings stay in local storage)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
68e878265c
commit
7709226a58
4 files changed
+30
-30
No files matched your search
+1
-1
@@ -228,7 +228,7 @@ mod tests {
|
|||||||
assert!(g.map.shown(out), "own ground beyond the lifted start is known");
|
assert!(g.map.shown(out), "own ground beyond the lifted start is known");
|
||||||
assert!(g.map.explored.iter().filter(|&&b| b).count() > explored);
|
assert!(g.map.explored.iter().filter(|&&b| b).count() > explored);
|
||||||
// savegame round trip
|
// savegame round trip
|
||||||
let json = crate::save::to_json(&g, &crate::render3d::Cam::new(0.0, 0.0), &crate::settings::Settings::default());
|
let json = crate::save::to_json(&g);
|
||||||
let l = crate::save::from_bytes(json.as_bytes()).unwrap();
|
let l = crate::save::from_bytes(json.as_bytes()).unwrap();
|
||||||
assert_eq!(l.game.map.explored, g.map.explored);
|
assert_eq!(l.game.map.explored, g.map.explored);
|
||||||
// the seer's eye opens a far area for a while
|
// the seer's eye opens a far area for a while
|
||||||
|
|||||||
+10
-10
@@ -609,20 +609,20 @@ impl App {
|
|||||||
self.status = Some((text, now));
|
self.status = Some((text, now));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a savegame (settings in the savegame are always applied)
|
// Load a savegame (only the game state; the settings stay as they are). The camera looks at
|
||||||
|
// the heart (screenshot scenes bring their own camera).
|
||||||
fn load_bytes(&mut self, bytes: &[u8], ui: &mut Ui, menu: &mut Menu, r3d: &mut R3d, now: f64) {
|
fn load_bytes(&mut self, bytes: &[u8], ui: &mut Ui, menu: &mut Menu, r3d: &mut R3d, now: f64) {
|
||||||
match save::from_bytes(bytes) {
|
match save::from_bytes(bytes) {
|
||||||
Ok(l) => {
|
Ok(l) => {
|
||||||
self.game = l.game;
|
self.game = l.game;
|
||||||
if let Some(c) = l.camera {
|
match l.camera {
|
||||||
self.cam = c;
|
Some(c) => self.cam = c,
|
||||||
|
None => {
|
||||||
|
let (cx, cy) = self.game.map.core;
|
||||||
|
self.cam = Cam::new(cx as f32 + 1.5, cy as f32 + 1.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
self.cam.snap();
|
self.cam.snap();
|
||||||
}
|
|
||||||
if let Some(s) = l.settings {
|
|
||||||
self.settings = s;
|
|
||||||
apply_settings(&self.settings, r3d);
|
|
||||||
store_settings(&self.settings);
|
|
||||||
}
|
|
||||||
apply_game_settings(&mut self.game, &self.settings);
|
apply_game_settings(&mut self.game, &self.settings);
|
||||||
self.game.coop = false;
|
self.game.coop = false;
|
||||||
self.coop = false;
|
self.coop = false;
|
||||||
@@ -638,7 +638,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn autosave_json(&self) -> String {
|
fn autosave_json(&self) -> String {
|
||||||
save::to_json(&self.game, &self.cam, &self.settings)
|
save::to_json(&self.game)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_match(&mut self, cloud: &Cloud) -> bool {
|
fn connect_match(&mut self, cloud: &Cloud) -> bool {
|
||||||
|
|||||||
+18
-18
@@ -1,9 +1,9 @@
|
|||||||
// Savegames: JSON with format tag and version, containing the game state, the camera
|
// Savegames: JSON with format tag and version, containing the game state - nothing else. The
|
||||||
// and the player's settings (settings are always applied when a savegame is loaded).
|
// player's settings live in the browser's local storage; settings in older savegames are
|
||||||
|
// ignored. Only the screenshot scenes (scenes.rs) also carry a camera.
|
||||||
|
|
||||||
use crate::game::Game;
|
use crate::game::Game;
|
||||||
use crate::render3d::Cam;
|
use crate::render3d::Cam;
|
||||||
use crate::settings::Settings;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub const FORMAT: &str = "dungeoncraft-save";
|
pub const FORMAT: &str = "dungeoncraft-save";
|
||||||
@@ -15,8 +15,8 @@ struct SaveOut<'a> {
|
|||||||
format: &'a str,
|
format: &'a str,
|
||||||
version: u32,
|
version: u32,
|
||||||
game: &'a Game,
|
game: &'a Game,
|
||||||
camera: &'a Cam,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
settings: &'a Settings,
|
camera: Option<&'a Cam>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -25,17 +25,21 @@ struct SaveIn {
|
|||||||
version: u32,
|
version: u32,
|
||||||
game: Game,
|
game: Game,
|
||||||
camera: Option<Cam>,
|
camera: Option<Cam>,
|
||||||
settings: Option<Settings>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Loaded {
|
pub struct Loaded {
|
||||||
pub game: Game,
|
pub game: Game,
|
||||||
pub camera: Option<Cam>,
|
pub camera: Option<Cam>,
|
||||||
pub settings: Option<Settings>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_json(game: &Game, camera: &Cam, settings: &Settings) -> String {
|
pub fn to_json(game: &Game) -> String {
|
||||||
serde_json::to_string(&SaveOut { format: FORMAT, version: VERSION, game, camera, settings }).unwrap_or_default()
|
serde_json::to_string(&SaveOut { format: FORMAT, version: VERSION, game, camera: None }).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
// a screenshot scene: the game and where the camera looks
|
||||||
|
#[allow(dead_code)] // scenes.rs (tests) only
|
||||||
|
pub fn to_json_with_camera(game: &Game, camera: &Cam) -> String {
|
||||||
|
serde_json::to_string(&SaveOut { format: FORMAT, version: VERSION, game, camera: Some(camera) }).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Err contains a language key describing the problem
|
// Err contains a language key describing the problem
|
||||||
@@ -50,7 +54,7 @@ pub fn from_bytes(data: &[u8]) -> Result<Loaded, &'static str> {
|
|||||||
let mut game = s.game;
|
let mut game = s.game;
|
||||||
game.map.activate();
|
game.map.activate();
|
||||||
game.recount();
|
game.recount();
|
||||||
Ok(Loaded { game, camera: s.camera, settings: s.settings })
|
Ok(Loaded { game, camera: s.camera })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -65,18 +69,14 @@ mod tests {
|
|||||||
for _ in 0..3000 {
|
for _ in 0..3000 {
|
||||||
g.update(0.05);
|
g.update(0.05);
|
||||||
}
|
}
|
||||||
let cam = Cam::new(10.0, 20.0);
|
let json = to_json(&g);
|
||||||
let mut settings = Settings::default();
|
|
||||||
settings.language = "de".into();
|
|
||||||
let json = to_json(&g, &cam, &settings);
|
|
||||||
println!("savegame size: {} KB", json.len() / 1024);
|
println!("savegame size: {} KB", json.len() / 1024);
|
||||||
let l = from_bytes(json.as_bytes()).expect("load");
|
let l = from_bytes(json.as_bytes()).expect("load");
|
||||||
assert_eq!(l.game.time, g.time);
|
assert_eq!(l.game.time, g.time);
|
||||||
assert_eq!(l.game.ents.len(), g.ents.len());
|
assert_eq!(l.game.ents.len(), g.ents.len());
|
||||||
assert_eq!(l.game.gold, g.gold);
|
assert_eq!(l.game.gold, g.gold);
|
||||||
assert!(l.game.map.t.iter().zip(g.map.t.iter()).all(|(a, b)| a.kind == b.kind && a.owner == b.owner && a.marked == b.marked));
|
assert!(l.game.map.t.iter().zip(g.map.t.iter()).all(|(a, b)| a.kind == b.kind && a.owner == b.owner && a.marked == b.marked));
|
||||||
assert_eq!(l.settings.unwrap().language, "de");
|
assert!(l.camera.is_none() && !json.contains("\"settings\"") && !json.contains("\"camera\""), "only the game state");
|
||||||
assert_eq!(l.camera.unwrap().tx, 10.0);
|
|
||||||
assert_eq!((l.game.map.w, l.game.map.h), (96, 128));
|
assert_eq!((l.game.map.w, l.game.map.h), (96, 128));
|
||||||
assert_eq!(l.game.map.portals.len(), g.map.portals.len());
|
assert_eq!(l.game.map.portals.len(), g.map.portals.len());
|
||||||
assert!(l.game.map.t.iter().zip(g.map.t.iter()).all(|(a, b)| a.var == b.var && a.hp == b.hp && a.food == b.food && a.gold == b.gold));
|
assert!(l.game.map.t.iter().zip(g.map.t.iter()).all(|(a, b)| a.var == b.var && a.hp == b.hp && a.food == b.food && a.gold == b.gold));
|
||||||
@@ -107,7 +107,7 @@ mod tests {
|
|||||||
g.update(0.05);
|
g.update(0.05);
|
||||||
}
|
}
|
||||||
let sim = t1.elapsed();
|
let sim = t1.elapsed();
|
||||||
let json = to_json(&g, &Cam::new(0.0, 0.0), &Settings::default());
|
let json = to_json(&g);
|
||||||
println!("256x256: generate {:?}, 2 min simulated in {:?}, savegame {} KB", gen, sim, json.len() / 1024);
|
println!("256x256: generate {:?}, 2 min simulated in {:?}, savegame {} KB", gen, sim, json.len() / 1024);
|
||||||
let l = from_bytes(json.as_bytes()).expect("load");
|
let l = from_bytes(json.as_bytes()).expect("load");
|
||||||
assert_eq!(l.game.map.t.len(), 256 * 256);
|
assert_eq!(l.game.map.t.len(), 256 * 256);
|
||||||
@@ -132,7 +132,7 @@ mod tests {
|
|||||||
let (ex, ey) = (cx + 1, cy + 1);
|
let (ex, ey) = (cx + 1, cy + 1);
|
||||||
let mut cam = Cam::new(ex as f32 + 0.5, ey as f32 + 0.5);
|
let mut cam = Cam::new(ex as f32 + 0.5, ey as f32 + 0.5);
|
||||||
cam.zoom = 12.0;
|
cam.zoom = 12.0;
|
||||||
std::fs::write(path, to_json(&g, &cam, &Settings { language: "en".into(), ..Settings::default() })).unwrap();
|
std::fs::write(path, to_json_with_camera(&g, &cam)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+1
-1
@@ -55,7 +55,7 @@ fn save(name: &str, g: &mut Game, x: f32, z: f32, zoom: f32, yaw: f32, fog: bool
|
|||||||
let mut s = Settings::default();
|
let mut s = Settings::default();
|
||||||
s.language = "en".into();
|
s.language = "en".into();
|
||||||
s.hints = false;
|
s.hints = false;
|
||||||
std::fs::write(format!("/tmp/scenes/{}.json", name), crate::save::to_json(g, &cam, &s)).unwrap();
|
std::fs::write(format!("/tmp/scenes/{}.json", name), crate::save::to_json_with_camera(g, &cam)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(g: &mut Game, secs: f32) {
|
fn run(g: &mut Game, secs: f32) {
|
||||||
|
|||||||
Reference in new issue
Block a user