From 66fca342021ef8cfa241fa418ec50211748a9a87 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Sun, 6 Oct 2019 15:01:18 +0900 Subject: [PATCH] Add saving and loading --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 145 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75e86f1..5bf48b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,6 +999,7 @@ version = "0.1.0" dependencies = [ "quicksilver 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4bfe1cc..008fc0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2018" [dependencies] quicksilver = "0.3.19" rand = "0.7.2" +serde = { version = "1.0.101", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index d4696e1..edb74b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,17 @@ use quicksilver::{ geom::{Circle, Rectangle, Transform, Vector}, graphics::{ - Background::{Col, Img}, + Background::{Blended, Col, Img}, Color, Font, FontStyle, Image, View, }, input::{ButtonState, Key}, lifecycle::{run, Asset, Event, Settings, State, Window}, + saving::{load, save}, sound::Sound, Result, }; use rand::prelude::*; +use serde::{Deserialize, Serialize}; const WIDTH_F: f32 = 800.0; const HEIGHT_F: f32 = 600.0; @@ -23,6 +25,7 @@ const JOINING_OPACITY_RATE: f32 = 0.00013; const JOINING_FAR_DIST: f32 = 700.0; const JOINING_NEAR_DIST: f32 = 150.0; const DOUBLE_CLICK_TIME: f64 = 350.0; +const SL_NOTIF_TIME: f64 = 5000.0; fn interp_sq_inv(x: f32) -> f32 { if x < 0.0 { @@ -515,17 +518,27 @@ impl Menu { fn s_10() -> Menu { Menu { - items: vec![Menu::instant_text( - 20.0, - HEIGHT_F - 20.0, - 20.0, - true, - "Single click to move, Double-click to create something", - )], + items: vec![ + Menu::instant_text( + 20.0, + HEIGHT_F - 40.0, + 20.0, + true, + "Single click to move, Double-click to create something", + ), + Menu::instant_text( + 20.0, + HEIGHT_F - 20.0, + 20.0, + true, + "S - save; L - load (can load from the start)", + ), + ], } } } +#[derive(Serialize, Deserialize, Clone)] struct Particle { rect: Rectangle, circle: Circle, @@ -538,6 +551,7 @@ struct Particle { life_timer: f64, } +#[derive(Serialize, Deserialize, Clone)] struct ParticleSystem { particles: Vec, spawn_timer: f64, @@ -647,6 +661,7 @@ impl ParticleSystem { } } +#[derive(Serialize, Deserialize, Clone)] struct RotatingParticleSystem { particle_system: ParticleSystem, r: f32, @@ -813,6 +828,7 @@ impl ExplConvParticleSystem { } } +#[derive(Serialize, Deserialize, Clone)] struct Planet { circle: Circle, color: Color, @@ -841,7 +857,7 @@ impl Planet { moons: Vec::new(), }; - let mut r: f32 = rand::thread_rng().gen_range(0.0, 360.0); + let r: f32 = rand::thread_rng().gen_range(0.0, 360.0); for i in 0..rand::thread_rng().gen_range(0, 5) { planet.moons.push(RotatingParticleSystem::new( rand::thread_rng().gen_range(1000.0, 2600.0), @@ -880,6 +896,18 @@ impl Planet { } } +#[derive(Serialize, Deserialize, Clone)] +struct SaveData { + planets: Vec, + player: Rectangle, + joining_particles: RotatingParticleSystem, +} + +enum SaveLoadNotification { + Save { text: Option, timer: f64 }, + Load { text: Option, timer: f64 }, +} + struct GameState { s_boom: Asset, s_get: Asset, @@ -911,6 +939,7 @@ struct GameState { planets: Vec, camera: Rectangle, move_to: Vector, + save_load_notification: Option, } impl State for GameState { @@ -969,6 +998,7 @@ impl State for GameState { planets: Vec::new(), camera: Rectangle::new((0.0, 0.0), (WIDTH_F, HEIGHT_F)), move_to: Vector::new(400.0, 300.0), + save_load_notification: None, }) } @@ -1141,6 +1171,48 @@ impl State for GameState { } } } + Event::Key(key, state) => { + if let ButtonState::Pressed = state { + match key { + Key::S => { + if self.state == 10 { + let save_data = SaveData { + planets: self.planets.clone(), + player: self.player.clone(), + joining_particles: self.joining_particles.clone(), + }; + save("OneAndAll_LD45", "slot0", &save_data)?; + self.save_load_notification = Some(SaveLoadNotification::Save { + text: None, + timer: SL_NOTIF_TIME, + }); + } + } + Key::L => { + let load_result = load::("OneAndAll_LD45", "slot0"); + if let Ok(save_data) = load_result { + self.planets = save_data.planets.clone(); + self.player = save_data.player.clone(); + self.joining_particles = save_data.joining_particles.clone(); + self.move_to = self.player.pos; + self.camera.pos = + self.player.pos - Vector::new(WIDTH_F / 2.0, HEIGHT_F / 2.0); + self.dbl_click_timeout = None; + self.click_time = None; + self.click_release_time = DOUBLE_CLICK_TIME; + + self.state = 10; + self.state_dirty = true; + self.save_load_notification = Some(SaveLoadNotification::Load { + text: None, + timer: SL_NOTIF_TIME, + }); + } + } + _ => (), + } + } + } _ => (), } Ok(()) @@ -1165,7 +1237,9 @@ impl State for GameState { } self.player.pos += (self.move_to - self.player.pos) / 20.0; - self.player_particles.host_rect = self.player; + self.player_particles.host_rect.pos = self.player.pos; + self.joining_particles.particle_system.host_rect.pos += + (self.player.pos - self.joining_particles.particle_system.host_rect.pos) / 30.0; self.camera.pos += (self.player.pos - Vector::new(WIDTH_F / 2.0, HEIGHT_F / 2.0) - self.camera.pos) / 40.0; window.set_view(View::new(self.camera)); @@ -1277,7 +1351,19 @@ impl State for GameState { self.music_timer = 0.0; self.music2.execute(|m2| m2.play())?; } + } else if self.state == 10 { + let mut music_on = false; + self.music2.execute(|m2| { + music_on = true; + m2.set_volume(0.6); + m2.play() + })?; + if music_on { + self.music_on = true; + self.music_timer = 0.0; + } } + for i in 0..self.menu.items.len() { let mi: &mut MenuItem = &mut self.menu.items[i]; if !mi.is_loaded { @@ -1387,6 +1473,37 @@ impl State for GameState { planet.update(dt); } + if let Some(sl) = &mut self.save_load_notification { + match sl { + SaveLoadNotification::Save { text, timer } => { + *timer -= dt; + if *timer <= 0.0 { + self.save_load_notification = None; + } else if text.is_none() { + self.font.execute(|f| { + *text = Some( + f.render("Saved the Game", &FontStyle::new(45.0, Color::WHITE))?, + ); + Ok(()) + })?; + } + } + SaveLoadNotification::Load { text, timer } => { + *timer -= dt; + if *timer <= 0.0 { + self.save_load_notification = None; + } else if text.is_none() { + self.font.execute(|f| { + *text = Some( + f.render("Loaded the Game", &FontStyle::new(45.0, Color::WHITE))?, + ); + Ok(()) + })?; + } + } + } + } + Ok(()) } @@ -1469,6 +1586,22 @@ impl State for GameState { for planet in &mut self.planets { planet.draw(window, Transform::IDENTITY); } + + if let Some(sl) = &mut self.save_load_notification { + match sl { + SaveLoadNotification::Save { text, timer } + | SaveLoadNotification::Load { text, timer } => { + if let Some(i) = text { + let mut c = Color::WHITE; + c.a = (*timer / SL_NOTIF_TIME) as f32; + let mut image_rect = i.area(); + image_rect.pos = self.camera.pos + Vector::new(20.0, 20.0); + window.draw(&image_rect, Blended(i, c)); + } + } + } + } + Ok(()) } }