LD45/src/original_impl.rs

3509 lines
114 KiB
Rust

#[cfg(not(target_family = "wasm"))]
use std::fs::File;
use std::{io::Result as IOResult, path::PathBuf, str::FromStr};
use crate::agnostic_interface::CameraInterface;
use crate::faux_quicksilver::{Circle, Color, Rectangle, Transform, Vector, Window};
use rand::prelude::*;
#[cfg(target_family = "wasm")]
use std::sync::mpsc::{Receiver, TryRecvError};
const WIDTH_F: f32 = 800.0;
const HEIGHT_F: f32 = 600.0;
//const MUSIC2_LENGTH: f32 = 2.0 * 60.0 * 1000.0;
const TEXT_RATE: f32 = 0.1;
const PP_GEN_RATE: f32 = 0.075;
const PARTICLE_RAND_VEL_RANGE: f32 = 80.0;
const PARTICLE_RAND_VEL_DIST: f32 = 0.2828427; // dist where x and y = 0.2
const PARTICLE_RAND_ROT_RANGE: f32 = 5.0;
const JOINING_OPACITY_RATE: f32 = 0.13;
const JOINING_FAR_DIST: f32 = 700.0;
const JOINING_NEAR_DIST: f32 = 150.0;
const DOUBLE_CLICK_TIME: f32 = 0.350;
const SL_NOTIF_TIME: f32 = 7.0;
const MAX_MOONS: usize = 5;
#[cfg(not(target_family = "wasm"))]
const SAVE_FILENAME: &str = "LudumDare45_OneAndAll_SaveFile.bin";
fn interp_sq_inv(x: f32) -> f32 {
if x < 0.0 {
return 0.0;
} else if x > 1.0 {
return 1.0;
}
let y = x - 1.0;
-y * y + 1.0
}
fn interp_sq(x: f32) -> f32 {
if x < 0.0 {
return 0.0;
} else if x > 1.0 {
return 1.0;
}
x * x
}
#[allow(unused_variables)]
#[allow(dead_code)]
enum MenuItemType {
Button {
text: &'static str,
text_c: Color,
h_c: Color,
c: Color,
},
AppearingText {
text: &'static str,
text_idx: usize,
text_size: f32,
text_c: Color,
timer: f32,
},
InstantText {
text: &'static str,
text_size: f32,
text_color: Color,
},
Pause {
timer: f32,
length: f32,
},
}
#[allow(dead_code)]
struct MenuItem {
x: f32,
y: f32,
w: f32,
h: f32,
item_type: MenuItemType,
is_hover: bool,
is_focus: bool,
is_loaded: bool,
}
impl MenuItem {
fn is_inside(&self, x: f32, y: f32) -> bool {
x >= self.x && x < self.x + self.w && y >= self.y && y < self.y + self.h
}
}
struct Menu {
items: Vec<MenuItem>,
}
#[allow(clippy::too_many_arguments)]
impl Menu {
fn button(
x: f32,
y: f32,
w: f32,
h: f32,
s: &'static str,
t_color: Color,
box_color: Color,
boxh_color: Color,
first: bool,
) -> MenuItem {
MenuItem {
x,
y,
w,
h,
item_type: MenuItemType::Button {
text: s,
text_c: t_color,
h_c: boxh_color,
c: box_color,
},
is_hover: false,
is_focus: false,
is_loaded: !first,
}
}
fn start() -> Menu {
let item = MenuItem {
x: WIDTH_F / 2.0 - 120.0,
y: 150.0,
w: 240.0,
h: 150.0,
item_type: MenuItemType::Button {
text: "Start the Game",
text_c: Color::WHITE,
h_c: Color::from_rgba(0x66, 0xFF, 0xFF, 255),
c: Color::from_rgba(0x33, 0xDD, 0xDD, 255),
},
is_hover: false,
is_focus: false,
is_loaded: false,
};
Menu {
items: vec![
item,
Menu::instant_text(
70.0,
50.0,
45.0,
true,
"One And All - A Ludum Dare 45 Entry",
),
Menu::instant_text(
25.0,
HEIGHT_F - 100.0,
27.0,
true,
"Made with Raylib which is licensed with the zlib license",
),
Menu::instant_text(
25.0,
HEIGHT_F - 50.0,
27.0,
true,
"Uses Clear-Sans which is licensed with Apache License Version 2.0",
),
],
}
}
fn instant_text(x: f32, y: f32, text_size: f32, first: bool, s: &'static str) -> MenuItem {
MenuItem {
x,
y,
w: 0.0,
h: 0.0,
item_type: MenuItemType::InstantText {
text: s,
text_size,
text_color: Color::WHITE,
},
is_hover: false,
is_focus: false,
is_loaded: !first,
}
}
fn text(x: f32, y: f32, text_size: f32, first: bool, s: &'static str) -> MenuItem {
MenuItem {
x,
y,
w: 0.0,
h: 0.0,
item_type: MenuItemType::AppearingText {
text: s,
text_size,
text_c: Color::WHITE,
timer: 0.0,
text_idx: 0,
},
is_hover: false,
is_focus: false,
is_loaded: !first,
}
}
fn pause(length: f32, first: bool) -> MenuItem {
MenuItem {
x: 0.0,
y: 0.0,
w: 0.0,
h: 0.0,
item_type: MenuItemType::Pause { timer: 0.0, length },
is_hover: false,
is_focus: false,
is_loaded: !first,
}
}
fn s_01() -> Menu {
Menu {
items: vec![
Menu::pause(0.500, true),
Menu::text(50.0, HEIGHT_F - 140.0, 40.0, false, "This is how it is."),
Menu::pause(0.500, false),
Menu::text(
50.0,
HEIGHT_F - 100.0,
40.0,
false,
"Nothing is, and everything is nothing.",
),
Menu::pause(0.500, false),
Menu::text(50.0, HEIGHT_F - 60.0, 40.0, false, "...until you appeared."),
Menu::pause(0.100, false),
Menu::text(
570.0,
HEIGHT_F - 50.0,
30.0,
false,
"(Click to continue...)",
),
],
}
}
fn s_02() -> Menu {
Menu {
items: vec![
Menu::text(
50.0,
HEIGHT_F - 150.0,
40.0,
true,
"Just by being, you brought light into existence.",
),
Menu::pause(0.500, false),
Menu::text(
50.0,
HEIGHT_F - 110.0,
40.0,
false,
"What brings you here? What drives you?",
),
Menu::pause(0.500, false),
Menu::text(
50.0,
HEIGHT_F - 70.0,
40.0,
false,
"Please tell me, what fuels you?",
),
Menu::button(
100.0,
30.0,
200.0,
85.0,
"Hope",
Color::WHITE,
Color::BLACK,
Color::from_rgba(0x33, 0x33, 0x33, 255),
false,
),
Menu::button(
500.0,
30.0,
200.0,
85.0,
"Miracles",
Color::WHITE,
Color::BLACK,
Color::from_rgba(0x33, 0x33, 0x33, 255),
false,
),
Menu::button(
100.0,
150.0,
200.0,
85.0,
"Kindness",
Color::WHITE,
Color::BLACK,
Color::from_rgba(0x33, 0x33, 0x33, 255),
false,
),
Menu::button(
500.0,
150.0,
200.0,
85.0,
"Determination",
Color::WHITE,
Color::BLACK,
Color::from_rgba(0x33, 0x33, 0x33, 255),
false,
),
],
}
}
// choose hope
fn s_03() -> Menu {
Menu {
items: vec![
Menu::text(
50.0,
HEIGHT_F - 170.0,
40.0,
true,
"Hope... hope that your actions will inspire others..",
),
Menu::pause(0.500, false),
Menu::text(
50.0,
HEIGHT_F - 130.0,
40.0,
false,
"Hope that a brighter future will come tomorrow...",
),
Menu::pause(0.500, false),
Menu::text(
50.0,
HEIGHT_F - 90.0,
40.0,
false,
".. With your appearance, perhaps it shall...",
),
],
}
}
// choose miracles
fn s_04() -> Menu {
Menu {
items: vec![
Menu::text(
30.0,
HEIGHT_F - 170.0,
40.0,
true,
"Miracles huh?.. I see, then your appearance is special.",
),
Menu::pause(0.500, false),
Menu::text(
30.0,
HEIGHT_F - 130.0,
40.0,
false,
"With your appearance, things may change for the better..",
),
Menu::pause(0.500, false),
Menu::text(
30.0,
HEIGHT_F - 90.0,
40.0,
false,
"Now I am certain that this meeting was not by chance.",
),
],
}
}
// choose kindness
fn s_05() -> Menu {
Menu {
items: vec![
Menu::text(
50.0,
HEIGHT_F - 170.0,
40.0,
true,
"Kindness?.. I am in your debt.",
),
Menu::pause(0.250, false),
Menu::text(
50.0,
HEIGHT_F - 130.0,
40.0,
false,
"It has been a long time since I have encountered",
),
Menu::text(50.0, HEIGHT_F - 90.0, 40.0, false, "another being..."),
Menu::pause(0.500, false),
Menu::text(270.0, HEIGHT_F - 90.0, 40.0, false, "... Thank you..."),
],
}
}
// choose determination
fn s_06() -> Menu {
Menu {
items: vec![
Menu::text(
50.0,
HEIGHT_F - 170.0,
40.0,
true,
"Determination.. I see...",
),
Menu::pause(0.500, false),
Menu::text(
400.0,
HEIGHT_F - 170.0,
40.0,
false,
"I do not doubt it, for it",
),
Menu::text(
50.0,
HEIGHT_F - 130.0,
40.0,
false,
"must have been difficult to come here..",
),
Menu::pause(0.500, false),
Menu::text(
50.0,
HEIGHT_F - 90.0,
40.0,
false,
"Your resolve is evident by your mere presence..",
),
],
}
}
fn s_07() -> Menu {
Menu {
items: vec![
Menu::text(
50.0,
HEIGHT_F - 130.0,
40.0,
true,
"Now that you are here, it must mean a new era of",
),
Menu::text(
50.0,
HEIGHT_F - 90.0,
40.0,
false,
"creation for all that will be.",
),
Menu::pause(0.200, false),
Menu::text(
50.0,
HEIGHT_F - 50.0,
40.0,
false,
"Try double-clicking the void to create something...",
),
],
}
}
fn s_08() -> Menu {
Menu {
items: vec![Menu::instant_text(
50.0,
HEIGHT_F - 90.0,
35.0,
true,
"(Try double-clicking now...)",
)],
}
}
fn s_09() -> Menu {
Menu {
items: vec![
Menu::pause(0.400, true),
Menu::text(
50.0,
HEIGHT_F - 140.0,
40.0,
false,
"A new planet... It has most certainly been a while.",
),
Menu::pause(0.500, false),
Menu::text(
50.0,
HEIGHT_F - 100.0,
40.0,
false,
"Please, go out and create the new universe, and again..",
),
Menu::pause(0.300, false),
Menu::text(50.0, HEIGHT_F - 60.0, 40.0, false, "Thank you."),
],
}
}
fn s_10() -> Menu {
Menu {
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); R - reset",
),
],
}
}
}
#[derive(Clone, Debug, PartialEq)]
struct Particle {
rect: Rectangle,
circle: Circle,
is_rect: bool,
velx: f32,
vely: f32,
velr: f32,
r: f32,
lifetime: f32,
life_timer: f32,
}
impl Default for Particle {
fn default() -> Self {
Self {
rect: Rectangle::new(0.0, 0.0, 1.0, 1.0),
circle: Circle::new(0.0, 0.0, 1.0),
is_rect: true,
velx: 0.0,
vely: 0.0,
velr: 0.0,
r: 1.0,
lifetime: 1.0,
life_timer: 0.0,
}
}
}
impl Particle {
pub fn deserialize(data: &[u8], offset: usize) -> Result<(Particle, usize), ()> {
let mut idx: usize = 0;
let mut particle = Particle::default();
let (rect, rect_size) = Rectangle::deserialize(data, offset + idx)?;
particle.rect = rect;
idx += rect_size;
let (circle, circle_size) = Circle::deserialize(data, offset + idx)?;
particle.circle = circle;
idx += circle_size;
if data.len() < offset + idx + 1 {
return Err(());
}
particle.is_rect = data[offset + idx] != 0;
idx += 1;
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
particle.velx = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
particle.vely = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
particle.velr = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
particle.r = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
particle.lifetime = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
particle.life_timer = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
Ok((particle, idx))
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.append(&mut self.rect.serialize());
bytes.append(&mut self.circle.serialize());
bytes.push(if self.is_rect { 1 } else { 0 });
bytes.append(&mut self.velx.to_be_bytes().into());
bytes.append(&mut self.vely.to_be_bytes().into());
bytes.append(&mut self.velr.to_be_bytes().into());
bytes.append(&mut self.r.to_be_bytes().into());
bytes.append(&mut self.lifetime.to_be_bytes().into());
bytes.append(&mut self.life_timer.to_be_bytes().into());
bytes
}
}
#[derive(Clone, Debug, PartialEq)]
struct ParticleSystem {
particles: Vec<Particle>,
spawn_timer: f32,
spawn_time: f32,
lifetime: f32,
host_rect: Rectangle,
host_circle: Circle,
is_rect: bool,
direction: Vector,
color: Color,
opacity: f32,
vel_multiplier: f32,
}
impl Default for ParticleSystem {
fn default() -> Self {
Self {
particles: Vec::new(),
spawn_timer: 0.0,
spawn_time: 1.0,
lifetime: 1.0,
host_rect: Rectangle::default(),
host_circle: Circle::default(),
is_rect: true,
direction: Vector::default(),
color: Color::default(),
opacity: 1.0,
vel_multiplier: 1.0,
}
}
}
impl ParticleSystem {
#[allow(clippy::too_many_arguments)]
fn new(
spawn_time: f32,
lifetime: f32,
host_rect: Rectangle,
host_circle: Circle,
is_rect: bool,
direction: Vector,
color: Color,
opacity: f32,
vel_multiplier: f32,
) -> Self {
Self {
particles: Vec::new(),
spawn_timer: 0.0,
spawn_time,
lifetime,
host_rect,
host_circle,
is_rect,
direction,
color,
opacity,
vel_multiplier,
}
}
fn update(&mut self, dt: f32) {
for i in (0..self.particles.len()).rev() {
self.particles[i].life_timer += dt;
if self.particles[i].life_timer > self.particles[i].lifetime {
self.particles.swap_remove(i);
} else if self.is_rect {
self.particles[i].rect.x += self.particles[i].velx * dt;
self.particles[i].rect.y += self.particles[i].vely * dt;
self.particles[i].r += self.particles[i].velr * dt;
} else {
self.particles[i].circle.x += self.particles[i].velx * dt;
self.particles[i].circle.y += self.particles[i].vely * dt;
}
}
self.spawn_timer += dt;
if self.spawn_timer > self.spawn_time {
self.spawn_timer -= self.spawn_time;
self.particles.push(Particle {
rect: self.host_rect,
circle: self.host_circle,
is_rect: self.is_rect,
velx: (rand::thread_rng()
.gen_range(-PARTICLE_RAND_VEL_RANGE..PARTICLE_RAND_VEL_RANGE)
+ self.direction.x)
* self.vel_multiplier,
vely: (rand::thread_rng()
.gen_range(-PARTICLE_RAND_VEL_RANGE..PARTICLE_RAND_VEL_RANGE)
+ self.direction.y)
* self.vel_multiplier,
// velx: self.direction.x,
// vely: self.direction.y,
velr: rand::thread_rng()
.gen_range(-PARTICLE_RAND_ROT_RANGE..PARTICLE_RAND_ROT_RANGE)
* self.vel_multiplier,
r: rand::thread_rng().gen_range(0.0..90.0),
lifetime: self.lifetime,
life_timer: 0.0,
});
}
}
fn draw(&mut self, window: &mut Window, transform: Transform) {
if self.opacity == 0.0 {
return;
}
for particle in &mut self.particles {
self.color.a =
((1.0 - particle.life_timer / particle.lifetime) * self.opacity * 255.0) as u8;
if particle.is_rect {
let pre_transform =
Transform::translate(particle.rect.w / 2.0, particle.rect.h / 2.0)
* Transform::rotate(particle.r);
window
.get_gi_mut()
.draw_rect_transform(
particle.rect,
self.color,
transform * pre_transform,
Vector {
x: particle.rect.x + particle.rect.w / 2.0,
y: particle.rect.y + particle.rect.h / 2.0,
},
)
.ok();
} else {
window
.get_gi_mut()
.draw_circle_transform(
particle.circle,
self.color,
transform,
Vector {
x: particle.circle.x,
y: particle.circle.y,
},
)
.ok();
}
}
}
fn force_spawn(&mut self, count: usize) {
for _ in 0..count {
self.particles.push(Particle {
rect: self.host_rect,
circle: self.host_circle,
is_rect: self.is_rect,
velx: (rand::thread_rng()
.gen_range(-PARTICLE_RAND_VEL_RANGE..PARTICLE_RAND_VEL_RANGE)
+ self.direction.x)
* self.vel_multiplier,
vely: (rand::thread_rng()
.gen_range(-PARTICLE_RAND_VEL_RANGE..PARTICLE_RAND_VEL_RANGE)
+ self.direction.y)
* self.vel_multiplier,
// velx: self.direction.x,
// vely: self.direction.y,
velr: rand::thread_rng()
.gen_range(-PARTICLE_RAND_ROT_RANGE..PARTICLE_RAND_ROT_RANGE)
* self.vel_multiplier,
r: rand::thread_rng().gen_range(0.0..90.0),
lifetime: self.lifetime,
life_timer: 0.0,
});
}
}
pub fn deserialize(data: &[u8], offset: usize) -> Result<(Self, usize), ()> {
let mut idx: usize = 0;
let mut psystem = ParticleSystem::default();
if data.len() < offset + idx + std::mem::size_of::<usize>() {
return Err(());
}
let particles_len: usize = usize::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<usize>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<usize>();
let mut particles: Vec<Particle> = Vec::new();
for _ in 0..particles_len {
let (particle, particle_size) = Particle::deserialize(data, offset + idx)?;
particles.push(particle);
idx += particle_size;
}
psystem.particles = particles;
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
psystem.spawn_timer = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
psystem.spawn_time = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
psystem.lifetime = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
let (rect, rect_size) = Rectangle::deserialize(data, offset + idx)?;
psystem.host_rect = rect;
idx += rect_size;
let (circle, circle_size) = Circle::deserialize(data, offset + idx)?;
psystem.host_circle = circle;
idx += circle_size;
if data.len() < offset + idx + 1 {
return Err(());
}
psystem.is_rect = data[offset + idx] != 0;
idx += 1;
let (vector, vector_size) = Vector::deserialize(data, offset + idx)?;
psystem.direction = vector;
idx += vector_size;
let (color, color_size) = Color::deserialize(data, offset + idx)?;
psystem.color = color;
idx += color_size;
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
psystem.opacity = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
psystem.vel_multiplier = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
Ok((psystem, idx))
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
let particle_count: usize = self.particles.len();
bytes.append(&mut particle_count.to_be_bytes().into());
for particle in self.particles.iter() {
bytes.append(&mut particle.serialize());
}
bytes.extend(self.spawn_timer.to_be_bytes());
bytes.extend(self.spawn_time.to_be_bytes());
bytes.extend(self.lifetime.to_be_bytes());
bytes.append(&mut self.host_rect.serialize());
bytes.append(&mut self.host_circle.serialize());
bytes.push(if self.is_rect { 1 } else { 0 });
bytes.append(&mut self.direction.serialize());
bytes.append(&mut self.color.serialize());
bytes.extend(self.opacity.to_be_bytes());
bytes.extend(self.vel_multiplier.to_be_bytes());
bytes
}
}
#[derive(Clone, Debug, PartialEq)]
struct RotatingParticleSystem {
particle_system: ParticleSystem,
r: f32,
velr: f32,
offset: f32,
}
impl Default for RotatingParticleSystem {
fn default() -> Self {
Self {
particle_system: ParticleSystem::default(),
r: 0.0,
velr: 0.2,
offset: 0.0,
}
}
}
impl RotatingParticleSystem {
#[allow(clippy::too_many_arguments)]
fn new(
spawn_time: f32,
lifetime: f32,
host_rect: Rectangle,
host_circle: Circle,
is_rect: bool,
direction: Vector,
color: Color,
opacity: f32,
rotation: f32,
velr: f32,
offset: f32,
vel_multiplier: f32,
) -> Self {
RotatingParticleSystem {
particle_system: ParticleSystem::new(
spawn_time,
lifetime,
host_rect,
host_circle,
is_rect,
direction,
color,
opacity,
vel_multiplier,
),
r: rotation,
velr,
offset,
}
}
fn update(&mut self, dt: f32) {
if self.particle_system.is_rect {
let saved_rect = self.particle_system.host_rect;
self.particle_system
.host_rect
.pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
self.particle_system.update(dt);
self.particle_system.host_rect = saved_rect;
} else {
let saved_cir = self.particle_system.host_circle;
self.particle_system
.host_circle
.pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
self.particle_system.update(dt);
self.particle_system.host_circle = saved_cir;
}
self.r += self.velr * dt * 10.0;
}
fn draw(&mut self, window: &mut Window, transform: Transform) {
if self.particle_system.opacity == 0.0 {
return;
}
self.particle_system.direction =
Transform::rotate(self.r) * Vector::new(0.0, -PARTICLE_RAND_VEL_DIST);
self.particle_system.draw(window, transform);
if self.particle_system.is_rect {
let mut moved_rect = self.particle_system.host_rect;
moved_rect.pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
let mut solid_color = self.particle_system.color;
solid_color.a = (self.particle_system.opacity * 255.0) as u8;
window
.get_gi_mut()
.draw_rect_transform(
moved_rect,
solid_color,
transform * Transform::rotate(self.r * 1.3),
Vector {
x: moved_rect.x,
y: moved_rect.y,
},
)
.ok();
} else {
let mut moved_cir = self.particle_system.host_circle;
moved_cir.pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
let mut solid_color = self.particle_system.color;
solid_color.a = (self.particle_system.opacity * 255.0) as u8;
window
.get_gi_mut()
.draw_circle_transform(
moved_cir,
solid_color,
transform,
Vector {
x: moved_cir.x,
y: moved_cir.y,
},
)
.ok();
}
}
pub fn deserialize(data: &[u8], offset: usize) -> Result<(Self, usize), ()> {
let mut idx: usize = 0;
let mut rpsystem = RotatingParticleSystem::default();
let (psystem, psystem_size) = ParticleSystem::deserialize(data, offset + idx)?;
rpsystem.particle_system = psystem;
idx += psystem_size;
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
rpsystem.r = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
rpsystem.velr = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
rpsystem.offset = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
Ok((rpsystem, idx))
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.append(&mut self.particle_system.serialize());
bytes.extend(self.r.to_be_bytes());
bytes.extend(self.velr.to_be_bytes());
bytes.extend(self.offset.to_be_bytes());
bytes
}
}
struct ExplConvCircleParticle {
circle: Circle,
offset: f32,
r: f32,
}
struct ExplConvParticleSystem {
particles: Vec<ExplConvCircleParticle>,
lifetime: f32,
host_circle: Circle,
color: Color,
opacity: f32,
life_timer: f32,
}
impl ExplConvParticleSystem {
fn new(lifetime: f32, host_circle: Circle, color: Color, opacity: f32) -> Self {
ExplConvParticleSystem {
particles: Vec::new(),
lifetime,
host_circle,
color,
opacity,
life_timer: 0.0,
}
}
fn activate(&mut self, count: usize, offset: f32) {
self.life_timer = 0.0;
for _ in 0..count {
self.particles.push(ExplConvCircleParticle {
circle: self.host_circle,
offset,
r: rand::thread_rng().gen_range(0.0..360.0),
});
}
}
// returns true if finished
fn update(&mut self, dt: f32, planets: &mut Vec<Planet>) -> bool {
self.life_timer += dt;
if self.life_timer >= self.lifetime {
if !self.particles.is_empty() {
self.particles.clear();
planets.push(Planet::new(self.host_circle, self.color));
return true;
}
return false;
}
if self.life_timer < self.lifetime / 2.0 {
let amount = interp_sq_inv(self.life_timer / self.lifetime * 2.0);
for particle in &mut self.particles {
let dir =
Transform::rotate(particle.r) * Vector::new(particle.offset * amount, 0.0);
particle.circle.x = dir.x + self.host_circle.x;
particle.circle.y = dir.y + self.host_circle.y;
}
} else {
let amount = 1.0 - interp_sq((self.life_timer / self.lifetime - 0.5) * 2.0);
for particle in &mut self.particles {
let dir =
Transform::rotate(particle.r) * Vector::new(particle.offset * amount, 0.0);
particle.circle.x = dir.x + self.host_circle.x;
particle.circle.y = dir.y + self.host_circle.y;
}
}
false
}
fn draw(&mut self, window: &mut Window, transform: Transform) {
if self.opacity == 0.0 {
return;
}
for particle in &mut self.particles {
self.color.a =
((self.life_timer / self.lifetime / 2.0 + 0.5) * self.opacity * 255.0) as u8;
window
.get_gi_mut()
.draw_circle_transform(
particle.circle,
self.color,
transform,
Vector {
x: particle.circle.x,
y: particle.circle.y,
},
)
.ok();
}
}
}
#[derive(Clone, Debug, PartialEq, Default)]
struct Planet {
circle: Circle,
color: Color,
particle_system: ParticleSystem,
moons: Vec<RotatingParticleSystem>,
}
impl Planet {
fn new(circle: Circle, color: Color) -> Self {
let mut smaller_circle = circle;
smaller_circle.r /= 4.0;
let mut planet = Planet {
circle,
color,
particle_system: ParticleSystem::new(
rand::thread_rng().gen_range(2.0..3.8),
0.9,
Rectangle::new(0.0, 0.0, 1.0, 1.0),
circle,
false,
Vector::new(0.0, 0.0),
color,
1.0,
0.3,
),
moons: Vec::with_capacity(MAX_MOONS),
};
let r: f32 = rand::thread_rng().gen_range(0.0..360.0);
let clockwise = rand::thread_rng().gen_bool(0.5);
for _ in 0..rand::thread_rng().gen_range(0..MAX_MOONS) {
planet.moons.push(RotatingParticleSystem::new(
rand::thread_rng().gen_range(1.0..2.6),
0.6,
Rectangle::new(0.0, 0.0, 1.0, 1.0),
smaller_circle,
false,
Vector::new(0.0, 0.0),
color,
1.0,
r,
if clockwise {
rand::thread_rng().gen_range(0.05..0.15)
} else {
rand::thread_rng().gen_range(-0.15..-0.05)
},
rand::thread_rng().gen_range(35.0..200.0),
0.2,
));
}
planet
}
fn update(&mut self, dt: f32) {
self.particle_system.host_circle.x = self.circle.x;
self.particle_system.host_circle.y = self.circle.y;
self.particle_system.update(dt);
for moon in &mut self.moons {
moon.particle_system.host_circle.x = self.circle.x;
moon.particle_system.host_circle.y = self.circle.y;
moon.update(dt);
}
}
fn draw(&mut self, window: &mut Window, transform: Transform) {
self.particle_system.draw(window, transform);
window
.get_gi_mut()
.draw_circle_transform(
self.circle,
self.color,
transform,
Vector {
x: self.circle.x,
y: self.circle.y,
},
)
.ok();
for moon in &mut self.moons {
moon.draw(window, transform);
}
}
pub fn deserialize(data: &[u8], offset: usize) -> Result<(Planet, usize), ()> {
let mut idx: usize = 0;
let mut planet = Planet::default();
let (circle, circle_size) = Circle::deserialize(data, offset + idx)?;
planet.circle = circle;
idx += circle_size;
let (color, color_size) = Color::deserialize(data, offset + idx)?;
planet.color = color;
idx += color_size;
// No need to deserialize ParticleSystem
//let (psystem, psystem_size) = ParticleSystem::deserialize(data, offset + idx)?;
//planet.particle_system = psystem;
//idx += psystem_size;
if data.len() < offset + idx + std::mem::size_of::<usize>() {
return Err(());
}
let moons_size = usize::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<usize>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<usize>();
for _ in 0..moons_size {
let (rpsystem, rps_size) = RotatingParticleSystem::deserialize(data, offset + idx)?;
planet.moons.push(rpsystem);
idx += rps_size;
}
Ok((planet, idx))
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.append(&mut self.circle.serialize());
bytes.append(&mut self.color.serialize());
// No need to serialize ParticleSystem.
//bytes.append(&mut self.particle_system.serialize());
let moons_size = self.moons.len();
bytes.extend(moons_size.to_be_bytes());
for i in 0..moons_size {
bytes.append(&mut self.moons[i].serialize());
}
bytes
}
}
#[derive(Clone, Debug, PartialEq)]
struct Star {
color: Color,
particle_system: ParticleSystem,
velr: f32,
r: f32,
}
impl Default for Star {
fn default() -> Self {
Self {
color: Color::default(),
particle_system: ParticleSystem::default(),
velr: 0.0,
r: 0.0,
}
}
}
impl Star {
fn new(circle: Circle, color: Color, velr: f32, r: f32) -> Self {
let mut star = Star {
color,
particle_system: ParticleSystem::new(
rand::thread_rng().gen_range(0.08..0.2),
0.85,
Rectangle::new(0.0, 0.0, 1.0, 1.0),
circle,
false,
Vector::new(0.0, 0.0),
color,
1.0,
1.0,
),
velr,
r,
};
if star.color.r < (0.75 * 255.0) as u8 {
star.color.r = (0.75 * 255.0) as u8;
}
if star.color.g < (0.75 * 255.0) as u8 {
star.color.g = (0.75 * 255.0) as u8;
}
if star.color.b < (0.75 * 255.0) as u8 {
star.color.b = (0.75 * 255.0) as u8;
}
star.particle_system
.force_spawn(rand::thread_rng().gen_range(20..45));
star
}
fn update(&mut self, dt: f32) {
self.particle_system.update(dt);
self.r += self.velr * dt;
}
fn draw(&mut self, image: &str, window: &mut Window, transform: Transform) {
self.particle_system.draw(window, transform);
let image = window.get_image_mut(image).expect("Should be loaded image");
let mut image_rect = image.get_wh_rect();
image_rect.x = self.particle_system.host_circle.x - image_rect.w / 2.0;
image_rect.y = self.particle_system.host_circle.y - image_rect.h / 2.0;
image
.draw_transform(
image_rect.x,
image_rect.y,
self.color,
transform * Transform::rotate(self.r),
Vector {
x: image_rect.x + image_rect.w / 2.0,
y: image_rect.y + image_rect.h / 2.0,
},
)
.ok();
}
pub fn deserialize(data: &[u8], offset: usize) -> Result<(Star, usize), ()> {
let mut idx: usize = 0;
let mut star = Star::default();
let (color, color_size) = Color::deserialize(data, offset + idx)?;
star.color = color;
idx += color_size;
// No need to deserialize ParticleSystem.
//let (psystem, psystem_size) = ParticleSystem::deserialize(data, offset + idx)?;
//star.particle_system = psystem;
//idx += psystem_size;
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
star.velr = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
star.r = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
Ok((star, idx))
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.append(&mut self.color.serialize());
// No need to serialize ParticleSystem.
//bytes.append(&mut self.particle_system.serialize());
bytes.extend(self.velr.to_be_bytes());
bytes.extend(self.r.to_be_bytes());
bytes
}
}
#[derive(Clone, Debug, PartialEq)]
struct Fish {
pos: Vector,
r: f32,
swim_time: f32,
swim_timer: f32,
swim_v: f32,
anim_timer: f32,
anim_time: f32,
color: Color,
body_rect: Rectangle,
tail_rect: Rectangle,
}
enum FishState {
Idle,
Swim,
}
impl Default for Fish {
fn default() -> Self {
Self {
pos: Vector::default(),
r: 0.0,
swim_time: 1.0,
swim_timer: 0.0,
swim_v: 0.0,
anim_timer: 0.0,
anim_time: 1.0,
color: Color::default(),
body_rect: Rectangle::default(),
tail_rect: Rectangle::default(),
}
}
}
impl Fish {
fn new(pos: Vector, r: f32, color: Color) -> Self {
let anim_timer = rand::thread_rng().gen_range(0.8..1.0);
Self {
pos,
r,
swim_time: 0.8,
swim_timer: 0.8,
swim_v: 0.2,
anim_timer,
anim_time: anim_timer,
color,
body_rect: Rectangle {
x: 0.0,
y: 0.0,
w: 32.0,
h: 16.0,
},
tail_rect: Rectangle {
x: 32.0,
y: 0.0,
w: 16.0,
h: 16.0,
},
}
}
fn set_next(&mut self, state: FishState) {
match state {
FishState::Idle => {
self.swim_time = rand::thread_rng().gen_range(1.1..2.4);
self.swim_timer = self.swim_time;
self.anim_timer = 2.8;
self.anim_time = 1.6;
self.swim_v = 0.0;
}
FishState::Swim => {
self.swim_time = rand::thread_rng().gen_range(1.4..2.3);
self.swim_timer = self.swim_time;
self.r = rand::thread_rng().gen_range(0.0..std::f32::consts::PI * 2.0);
self.anim_timer = rand::thread_rng().gen_range(1.6..2.0);
self.anim_time = self.anim_timer;
self.swim_v = self.anim_timer / 8.0;
}
}
}
fn update(&mut self, dt: f32) {
self.swim_time -= dt;
if self.swim_time < 0.22 {
self.swim_v /= 1.1;
}
if self.swim_time <= 0.0 {
if rand::thread_rng().gen_bool(0.4) {
self.set_next(FishState::Idle);
} else {
self.set_next(FishState::Swim);
}
}
self.anim_timer -= dt;
if self.anim_timer <= 0.0 {
self.anim_timer = self.anim_time;
}
self.pos -= Transform::rotate(self.r) * Vector::new(self.swim_v, 0.0) * dt * 200.0;
}
fn draw(&mut self, i_fish: &str, window: &mut Window, transform: Transform) {
let fish_img = window
.get_image_mut(i_fish)
.expect("\"fish\" Image should be loaded");
let anim_angle = ((self.anim_timer / self.anim_time) * std::f32::consts::PI * 2.0).sin();
let mut body_rect = self.body_rect;
body_rect.x = self.pos.x - self.body_rect.w / 2.0;
body_rect.y = self.pos.y - self.body_rect.h / 2.0;
let body_tr = Transform::rotate(anim_angle + self.r);
fish_img
.draw_sub_transform(
self.body_rect,
body_rect,
self.color,
transform * body_tr,
Vector {
x: self.pos.x,
y: self.pos.y,
},
)
.ok();
let mut tail_rect = self.tail_rect;
tail_rect.x = self.pos.x + body_rect.w / 2.0;
tail_rect.y = self.pos.y - body_rect.h / 2.0;
let anim_angle = ((self.anim_timer / self.anim_time) * std::f32::consts::PI * 2.0
- std::f32::consts::PI / 3.0)
.sin();
let tail_tr = body_tr
* Transform::translate(-body_rect.w / 2.0, 0.0)
* Transform::rotate(-anim_angle)
* Transform::translate(body_rect.w / 2.0, 0.0);
fish_img
.draw_sub_transform(
self.tail_rect,
tail_rect,
self.color,
transform * tail_tr,
Vector {
x: self.pos.x,
y: self.pos.y,
},
)
.ok();
}
pub fn deserialize(data: &[u8], offset: usize) -> Result<(Fish, usize), ()> {
let mut idx: usize = 0;
let mut fish = Fish::default();
let (pos, pos_size) = Vector::deserialize(data, offset + idx)?;
fish.pos = pos;
idx += pos_size;
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
fish.r = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
fish.swim_time = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
fish.swim_timer = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
fish.swim_v = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
fish.anim_timer = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
if data.len() < offset + idx + std::mem::size_of::<f32>() {
return Err(());
}
fish.anim_time = f32::from_be_bytes(
data[(offset + idx)..(offset + idx + std::mem::size_of::<f32>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<f32>();
let (color, color_size) = Color::deserialize(data, offset + idx)?;
fish.color = color;
idx += color_size;
let (body_rect, body_rect_size) = Rectangle::deserialize(data, offset + idx)?;
fish.body_rect = body_rect;
idx += body_rect_size;
let (tail_rect, tail_rect_size) = Rectangle::deserialize(data, offset + idx)?;
fish.tail_rect = tail_rect;
idx += tail_rect_size;
Ok((fish, idx))
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.append(&mut self.pos.serialize());
bytes.extend(self.r.to_be_bytes());
bytes.extend(self.swim_time.to_be_bytes());
bytes.extend(self.swim_timer.to_be_bytes());
bytes.extend(self.swim_v.to_be_bytes());
bytes.extend(self.anim_timer.to_be_bytes());
bytes.extend(self.anim_time.to_be_bytes());
bytes.append(&mut self.color.serialize());
bytes.append(&mut self.body_rect.serialize());
bytes.append(&mut self.tail_rect.serialize());
bytes
}
}
#[derive(Clone, Debug, PartialEq, Default)]
struct SaveData {
planets: Vec<Planet>,
stars: Vec<Star>,
fishes: Vec<Fish>,
player: Rectangle,
joining_particles: RotatingParticleSystem,
}
const SAVE_DATA_IDENTIFIER: [u8; 8] = [0x53, 0x41, 0x56, 0x45, 'V' as u8, 'e' as u8, 'r' as u8, 1];
impl SaveData {
pub fn deserialize(data: &[u8]) -> Result<(SaveData, usize), ()> {
let mut idx: usize = 0;
let mut save_data = SaveData::default();
if data.len() < idx + SAVE_DATA_IDENTIFIER.len() {
return Err(());
}
for i in 0..SAVE_DATA_IDENTIFIER.len() {
if data[idx + i] != SAVE_DATA_IDENTIFIER[i] {
return Err(());
}
}
idx += SAVE_DATA_IDENTIFIER.len();
if data.len() < idx + std::mem::size_of::<usize>() {
return Err(());
}
let planets_size = usize::from_be_bytes(
data[idx..(idx + std::mem::size_of::<usize>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<usize>();
for _ in 0..planets_size {
let (planet, planet_size) = Planet::deserialize(data, idx)?;
save_data.planets.push(planet);
idx += planet_size;
}
if data.len() < idx + std::mem::size_of::<usize>() {
return Err(());
}
let stars_size = usize::from_be_bytes(
data[idx..(idx + std::mem::size_of::<usize>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<usize>();
for _ in 0..stars_size {
let (star, star_size) = Star::deserialize(data, idx)?;
save_data.stars.push(star);
idx += star_size;
}
if data.len() < idx + std::mem::size_of::<usize>() {
return Err(());
}
let fishes_size = usize::from_be_bytes(
data[idx..(idx + std::mem::size_of::<usize>())]
.try_into()
.map_err(|_| ())?,
);
idx += std::mem::size_of::<usize>();
for _ in 0..fishes_size {
let (fish, fish_size) = Fish::deserialize(data, idx)?;
save_data.fishes.push(fish);
idx += fish_size;
}
let (p_rect, p_rect_size) = Rectangle::deserialize(data, idx)?;
save_data.player = p_rect;
idx += p_rect_size;
let (jp, jp_size) = RotatingParticleSystem::deserialize(data, idx)?;
save_data.joining_particles = jp;
idx += jp_size;
Ok((save_data, idx))
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(SAVE_DATA_IDENTIFIER.iter());
bytes.extend(self.planets.len().to_be_bytes());
for planet in &self.planets {
bytes.append(&mut planet.serialize());
}
bytes.extend(self.stars.len().to_be_bytes());
for star in &self.stars {
bytes.append(&mut star.serialize());
}
bytes.extend(self.fishes.len().to_be_bytes());
for fish in &self.fishes {
bytes.append(&mut fish.serialize());
}
bytes.append(&mut self.player.serialize());
bytes.append(&mut self.joining_particles.serialize());
bytes
}
}
enum SaveLoadNotification {
Save { text: Option<String>, timer: f32 },
Load { text: Option<String>, timer: f32 },
}
#[allow(dead_code)]
pub struct GameState {
s_boom: String,
s_get: String,
s_power_up: String,
s_tap: String,
s_speak_m: String,
s_speak_f: String,
font: String,
music2: String,
i_star: String,
i_fish: String,
music_on: bool,
menu: Menu,
state: u32,
state_dirty: bool,
selection_mode: bool,
current_item: Option<usize>,
current_finished: bool,
player: Rectangle,
player_r: f32,
player_particles: ParticleSystem,
joining_particles: RotatingParticleSystem,
is_create_mode: bool,
click_release_time: f32,
dbl_click_timeout: Option<f32>,
click_time: Option<f32>,
click_pos: Vector,
mouse_pos: Vector,
expl_conv_p_systems: Vec<ExplConvParticleSystem>,
planets: Vec<Planet>,
stars: Vec<Star>,
fishes: Vec<Fish>,
camera: Box<dyn CameraInterface>,
move_to: Vector,
save_load_notification: Option<SaveLoadNotification>,
#[cfg(target_family = "wasm")]
load_recv: Option<Receiver<Vec<u8>>>,
}
impl GameState {
pub fn new(window: &mut Window) -> Result<Self, String> {
let s_boom = String::from("boom.mp3");
window.load_sound(
&PathBuf::from_str("static/boom.mp3")
.map_err(|_| String::from("Failed to load \"static/boom.mp3\""))?,
s_boom.clone(),
)?;
let s_get = String::from("get.mp3");
window.load_sound(
&PathBuf::from_str("static/get.mp3")
.map_err(|_| String::from("Failed to load \"static/get.mp3\""))?,
s_get.clone(),
)?;
let s_power_up = String::from("power_up.mp3");
//window.load_sound(
// &PathBuf::from_str("static/power_up.mp3")
// .map_err(|_| String::from("Failed to load \"static/power_up.mp3\""))?,
// s_power_up.clone(),
//)?;
let s_tap = String::from("tap.mp3");
window.load_sound(
&PathBuf::from_str("static/tap.mp3")
.map_err(|_| String::from("Failed to load \"static/tap.mp3\""))?,
s_tap.clone(),
)?;
let s_speak_m = String::from("speak_m.mp3");
//window.load_sound(
// &PathBuf::from_str("static/speak_m.mp3")
// .map_err(|_| String::from("Failed to load \"static/speak_m.mp3\""))?,
// s_speak_m.clone(),
//)?;
let s_speak_f = String::from("speak_f.mp3");
//window.load_sound(
// &PathBuf::from_str("static/speak_f.mp3")
// .map_err(|_| String::from("Failed to load \"static/speak_f.mp3\""))?,
// s_speak_f.clone(),
//)?;
let font = String::from("ClearSans-Regular.ttf");
window.load_font(
&PathBuf::from_str("static/ClearSans-Regular.ttf")
.map_err(|_| String::from("Failed to load \"static/ClearSans-Regular.ttf\""))?,
font.clone(),
)?;
let music2 = String::from("music2.mp3");
window.load_music(
&PathBuf::from_str("static/music2.mp3")
.map_err(|_| String::from("Failed to load \"static/music2.mp3\""))?,
music2.clone(),
)?;
let i_star = String::from("star.png");
window.load_image(
&PathBuf::from_str("static/star.png")
.map_err(|_| String::from("Failed to load \"static/star.png\""))?,
i_star.clone(),
)?;
let i_fish = String::from("fish.png");
window.load_image(
&PathBuf::from_str("static/fish.png")
.map_err(|_| String::from("Failed to load \"static/fish.png\""))?,
i_fish.clone(),
)?;
let mut camera = window.get_gi_mut().get_default_camera()?;
camera.set_view_xy(0.0, 0.0)?;
Ok(Self {
s_boom,
s_get,
s_power_up,
s_tap,
s_speak_m,
s_speak_f,
font,
music2,
i_star,
i_fish,
music_on: false,
menu: Menu::start(),
state: 0,
state_dirty: false,
selection_mode: true,
current_item: None,
current_finished: true,
player: Rectangle::new(400.0, 300.0, 32.0, 32.0),
player_r: 0.0,
player_particles: ParticleSystem::new(
PP_GEN_RATE,
1.0,
Rectangle::new(400.0, 300.0, 32.0, 32.0),
Circle::new(100.0, 100.0, 32.0),
true,
Vector::new(0.0, 0.0),
Color::WHITE,
0.0,
1.0,
),
joining_particles: RotatingParticleSystem::new(
PP_GEN_RATE,
1.0,
Rectangle::new(400.0, 300.0, 16.0, 16.0),
Circle::new(100.0, 100.0, 32.0),
true,
Vector::new(0.0, 0.0),
Color::GREEN,
0.0,
0.0,
0.1,
JOINING_FAR_DIST,
1.0,
),
is_create_mode: false,
click_release_time: 0.0,
dbl_click_timeout: None,
click_time: None,
click_pos: Vector::new(0.0, 0.0),
mouse_pos: Vector::new(0.0, 0.0),
expl_conv_p_systems: Vec::new(),
planets: Vec::new(),
stars: Vec::new(),
fishes: Vec::new(),
camera,
move_to: Vector::new(400.0, 300.0),
save_load_notification: None,
#[cfg(target_family = "wasm")]
load_recv: None,
})
}
pub fn update(&mut self, window: &mut Window) -> Result<(), String> {
let dt = window.get_gi().get_delta_time();
// check mouse pos
{
self.mouse_pos = window.get_gi().get_mouse_xy_vec()?;
//self.mouse_pos = window.get_gi().vec_to_world(self.mouse_pos)?;
let mut hovered = false;
for i in 0..self.menu.items.len() {
if self.menu.items[i].is_inside(self.mouse_pos.x, self.mouse_pos.y) {
self.menu.items[i].is_hover = true;
self.current_item = Some(i);
hovered = true;
} else {
self.menu.items[i].is_hover = false;
}
}
if !hovered {
self.current_item = None;
}
}
// check mouse down
if window.get_gi_mut().get_mouse_released()? {
if self.dbl_click_timeout.is_none() {
self.click_release_time = 0.0;
}
} else if window.get_gi_mut().get_mouse_pressed()?.is_some() {
if self.current_finished {
if self.is_create_mode {
let click_pos = window.get_gi().vec_to_world(self.mouse_pos)?;
if self.click_release_time < DOUBLE_CLICK_TIME {
self.click_release_time = DOUBLE_CLICK_TIME;
self.dbl_click_timeout = Some(0.0);
self.click_time = None;
if self.state == 8 {
let mut expl_conv_system = ExplConvParticleSystem::new(
1.5,
Circle::new(click_pos.x, click_pos.y, 20.0),
Color::from_rgba(0x99, 0xFF, 0x99, 255),
1.0,
);
expl_conv_system.activate(30, 200.0);
self.expl_conv_p_systems.push(expl_conv_system);
self.state = 9;
self.state_dirty = true;
window.get_sound_mut(&self.s_boom)?.play(0.8)?;
} else if self.state == 10 {
let mut rng = rand::thread_rng();
let rand_out = rng.gen_range(0.0..1.0);
if rand_out < 0.6 {
// spawn planet
let mut expl_conv_system = ExplConvParticleSystem::new(
rng.gen_range(1.2..1.6),
Circle::new(
click_pos.x,
click_pos.y,
rng.gen_range(15.0..25.0),
),
Color::from_rgba(
rng.gen_range(0x44..0xFF),
rng.gen_range(0x44..0xFF),
rng.gen_range(0x44..0xFF),
255,
),
1.0,
);
expl_conv_system
.activate(rng.gen_range(13..40), rng.gen_range(150.0..300.0));
self.expl_conv_p_systems.push(expl_conv_system);
} else if rand_out < 0.85 {
// spawn star
let rot_clockwise = rng.gen_bool(0.5);
self.stars.push(Star::new(
Circle::new(click_pos.x, click_pos.y, rng.gen_range(3.0..7.0)),
Color::from_rgba(
rng.gen_range(0x58..0xFF),
rng.gen_range(0x58..0xFF),
rng.gen_range(0x58..0xFF),
255,
),
if rot_clockwise {
rng.gen_range(0.1..0.3)
} else {
rng.gen_range(-0.3..-0.1)
},
rng.gen_range(0.0..90.0),
));
} else {
// spawn fish
for _ in 0..rng.gen_range(1..4) {
self.fishes.push(Fish::new(
click_pos,
rng.gen_range(0.0..360.0),
Color::from_rgba(
rng.gen_range(0x44..0xFF),
rng.gen_range(0x44..0xFF),
rng.gen_range(0x44..0xFF),
255,
),
));
}
}
window.get_sound_mut(&self.s_boom)?.play(0.8)?;
}
} else if self.state == 10 {
self.click_time = Some(0.0);
self.click_pos = click_pos;
}
} else if self.selection_mode {
if let Some(idx) = self.current_item {
match self.state {
0 => {
self.state += 1;
self.state_dirty = true;
}
2 => {
if idx == 5 {
// hope
self.state = 3;
self.state_dirty = true;
self.joining_particles.particle_system.color =
Color::from_rgba(0xAA, 0xCC, 0xFF, 255);
} else if idx == 6 {
// miracles
self.state = 4;
self.state_dirty = true;
self.joining_particles.particle_system.color =
Color::from_rgba(0xFF, 0xFF, 0xAA, 255);
} else if idx == 7 {
// kindness
self.state = 5;
self.state_dirty = true;
self.joining_particles.particle_system.color =
Color::from_rgba(0xBB, 0xFF, 0xBB, 255);
} else {
// determination
self.state = 6;
self.state_dirty = true;
self.joining_particles.particle_system.color =
Color::from_rgba(0xFF, 0xAA, 0xAA, 255);
}
window.get_sound_mut(&self.s_get)?.play(0.7)?;
}
_ => {
self.state = 0;
self.state_dirty = true;
}
}
}
} else {
match self.state {
0 | 1 => self.state += 1,
3..=6 => self.state = 7,
7 => self.state = 8,
9 => self.state = 10,
_ => self.state = 0,
}
self.state_dirty = true;
}
} else {
for mi in &mut self.menu.items {
match &mut mi.item_type {
MenuItemType::AppearingText {
text,
text_idx,
text_size: _,
text_c: _,
timer: _,
} => {
*text_idx = text.len();
}
MenuItemType::Button {
text: _,
text_c: _,
h_c: _,
c: _,
} => {
//let style = FontStyle::new(42.0, *text_c);
}
MenuItemType::Pause {
timer: _,
length: _,
} => (),
MenuItemType::InstantText {
text: _,
text_size: _,
text_color: _,
} => {}
}
mi.is_loaded = true;
}
self.current_finished = true;
}
}
// check pressed keys
if window.get_gi_mut().get_key_pressed('s')? {
if self.state == 10 {
let save_result = self.save().map_err(|e| e.to_string());
if let Err(s) = save_result {
self.save_load_notification = Some(SaveLoadNotification::Save {
text: Some(format!("Failed to save! {}", s)),
timer: SL_NOTIF_TIME,
});
}
}
} else if window.get_gi_mut().get_key_pressed('l')? {
let load_result = self.load().map_err(|e| e.to_string());
if let Err(s) = load_result {
self.save_load_notification = Some(SaveLoadNotification::Load {
text: Some(format!("Failed to load! {}", s)),
timer: SL_NOTIF_TIME,
});
}
} else if window.get_gi_mut().get_key_pressed('r')? && self.state == 10 {
self.state = 0;
self.state_dirty = true;
window.get_music_mut(&self.music2)?.stop()?;
self.music_on = false;
}
self.click_release_time += dt;
if let Some(t) = &mut self.click_time {
*t += dt;
if *t > DOUBLE_CLICK_TIME {
self.move_to = self.click_pos; // - Vector::new(WIDTH_F / 2.0, HEIGHT_F / 2.0);
}
}
if let Some(t) = &mut self.dbl_click_timeout {
*t += dt;
if *t > 0.3 {
self.dbl_click_timeout = None;
}
}
self.player.x += (self.move_to.x - self.player.x) / 20.0;
self.player.y += (self.move_to.y - self.player.y) / 20.0;
self.player_particles.host_rect.x = self.player.x;
self.player_particles.host_rect.y = self.player.y;
self.joining_particles.particle_system.host_rect.x +=
(self.player.x - self.joining_particles.particle_system.host_rect.x) / 30.0;
self.joining_particles.particle_system.host_rect.y +=
(self.player.y - self.joining_particles.particle_system.host_rect.y) / 30.0;
let (cx, cy) = self.camera.get_view_xy()?;
self.camera.set_view_xy(
cx + (self.player.x - WIDTH_F / 2.0 - cx) / 40.0,
cy + (self.player.y - HEIGHT_F / 2.0 - cy) / 40.0,
)?;
window.get_gi_mut().set_camera(self.camera.as_ref())?;
self.player_r += dt / 10.0;
if self.state_dirty {
self.state_dirty = false;
if self.state > 1 && !self.music_on {
let music = window.get_music_mut(&self.music2)?;
music.set_loop(true)?;
music.play(0.5)?;
self.music_on = true;
}
match self.state {
1 => {
self.menu = Menu::s_01();
self.current_finished = false;
self.selection_mode = false;
}
2 => {
self.menu = Menu::s_02();
self.current_finished = false;
self.selection_mode = true;
}
3 => {
self.menu = Menu::s_03();
self.current_finished = false;
self.selection_mode = false;
}
4 => {
self.menu = Menu::s_04();
self.current_finished = false;
self.selection_mode = false;
}
5 => {
self.menu = Menu::s_05();
self.current_finished = false;
self.selection_mode = false;
}
6 => {
self.menu = Menu::s_06();
self.current_finished = false;
self.selection_mode = false;
}
7 => {
self.menu = Menu::s_07();
self.current_finished = false;
self.selection_mode = false;
}
8 => {
self.menu = Menu::s_08();
self.current_finished = true;
self.selection_mode = false;
self.is_create_mode = true;
}
9 => {
self.menu = Menu::s_09();
self.current_finished = false;
self.selection_mode = false;
self.is_create_mode = false;
}
10 => {
self.menu = Menu::s_10();
self.current_finished = false;
self.selection_mode = false;
self.is_create_mode = true;
}
_ => {
self.menu = Menu::start();
self.current_item = None;
self.selection_mode = true;
self.is_create_mode = false;
self.state = 0;
self.player_particles.opacity = 0.0;
self.joining_particles.particle_system.opacity = 0.0;
self.expl_conv_p_systems.clear();
self.planets.clear();
self.stars.clear();
self.fishes.clear();
self.player.x = WIDTH_F / 2.0;
self.player.y = HEIGHT_F / 2.0;
self.move_to = Vector::new(WIDTH_F / 2.0, HEIGHT_F / 2.0);
self.camera.set_view_xy(0.0, 0.0)?;
self.click_time = None;
}
}
}
if self.joining_particles.particle_system.opacity < 1.0 && self.state > 2 {
self.joining_particles.particle_system.opacity += JOINING_OPACITY_RATE * dt;
if self.joining_particles.particle_system.opacity > 1.0 {
self.joining_particles.particle_system.opacity = 1.0;
}
self.joining_particles.offset =
(1.0 - self.joining_particles.particle_system.opacity / 1.0) * JOINING_FAR_DIST
+ self.joining_particles.particle_system.opacity / 1.0 * JOINING_NEAR_DIST;
}
if self.player_particles.opacity < 1.0 && self.state > 1 {
self.player_particles.opacity += dt / 7.0;
if self.player_particles.opacity > 1.0 {
self.player_particles.opacity = 1.0;
}
}
if self.music_on {
} else if self.state == 10 {
let music = window.get_music_mut(&self.music2)?;
music.set_loop(true)?;
music.play(0.5)?;
self.music_on = true;
}
for i in 0..self.menu.items.len() {
let mi: &mut MenuItem = &mut self.menu.items[i];
if !mi.is_loaded {
match &mut mi.item_type {
MenuItemType::Button {
text: _,
text_c: _,
h_c: _,
c: _,
} => {
//self.font.execute(|font| {
// let style = FontStyle::new(42.0, *text_c);
// *text_image = Some(font.render(text, &style)?);
// Ok(())
//})?;
//if text_image.is_some() {
mi.is_loaded = true;
if i + 1 < self.menu.items.len() {
self.menu.items[i + 1].is_loaded = false;
} else {
self.current_finished = true;
}
//}
}
MenuItemType::AppearingText {
text,
text_idx,
text_size: _,
text_c: _,
timer,
} => {
*timer += dt;
if *timer > TEXT_RATE {
*timer -= TEXT_RATE;
*text_idx += 1;
window.get_sound_mut(&self.s_tap).unwrap().play(0.2)?;
if *text_idx >= text.len() {
mi.is_loaded = true;
if i + 1 < self.menu.items.len() {
self.menu.items[i + 1].is_loaded = false;
} else {
self.current_finished = true;
}
continue;
}
//self.font.execute(|font| {
// let style = FontStyle::new(*text_size, *text_c);
// *text_image = Some(font.render(current_text, &style)?);
// Ok(())
//})?;
}
}
MenuItemType::Pause { timer, length } => {
*timer += dt;
if timer > length {
mi.is_loaded = true;
if i + 1 < self.menu.items.len() {
self.menu.items[i + 1].is_loaded = false;
} else {
self.current_finished = true;
}
}
}
MenuItemType::InstantText {
text: _,
text_size: _,
text_color: _,
} => {
//if text_image.is_none() {
// self.font.execute(|f| {
// let style = FontStyle::new(*text_size, *text_color);
// *text_image = Some(f.render(text, &style)?);
// Ok(())
// })?;
//}
//if text_image.is_some() {
mi.is_loaded = true;
if i + 1 < self.menu.items.len() {
self.menu.items[i + 1].is_loaded = false;
} else {
self.current_finished = true;
}
//}
}
}
}
}
self.player_particles.host_rect = self.player;
self.player_particles.update(dt);
self.joining_particles.update(dt);
for i in (0..self.expl_conv_p_systems.len()).rev() {
if self.expl_conv_p_systems[i].update(dt, &mut self.planets) {
self.expl_conv_p_systems.swap_remove(i);
}
}
for planet in &mut self.planets {
planet.update(dt);
}
for star in &mut self.stars {
star.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() {
*text = Some(String::from("Saved the Game!"));
}
}
SaveLoadNotification::Load { text, timer } => {
*timer -= dt;
if *timer <= 0.0 {
self.save_load_notification = None;
} else if text.is_none() {
*text = Some(String::from("Loaded the Game!"));
}
}
}
}
for fish in &mut self.fishes {
fish.update(dt);
}
#[cfg(target_family = "wasm")]
if let Some(rx) = &mut self.load_recv {
let recv_result = rx.try_recv();
if let Ok(v) = recv_result {
if v.is_empty() {
self.save_load_notification = Some(SaveLoadNotification::Load {
text: Some(String::from("Failed to load! (callback failure)")),
timer: SL_NOTIF_TIME,
});
} else {
let des_result = SaveData::deserialize(&v);
if let Ok((save_data, _)) = des_result {
self.planets = save_data.planets;
self.stars = save_data.stars;
self.fishes = save_data.fishes;
self.player = save_data.player;
self.joining_particles = save_data.joining_particles;
self.expl_conv_p_systems.clear();
self.move_to = Vector::new(self.player.x, self.player.y);
self.camera
.set_view_xy(
self.player.x - WIDTH_F / 2.0,
self.player.y - HEIGHT_F / 2.0,
)
.ok();
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,
});
} else {
self.save_load_notification = Some(SaveLoadNotification::Load {
text: Some(String::from("Failed to load! (parse issue)")),
timer: SL_NOTIF_TIME,
});
}
}
self.load_recv = None;
} else if recv_result == Err(TryRecvError::Disconnected) {
self.save_load_notification = Some(SaveLoadNotification::Load {
text: Some(String::from("Failed to load! (sender disconnected)")),
timer: SL_NOTIF_TIME,
});
self.load_recv = None;
}
}
Ok(())
}
pub fn draw(&mut self, window: &mut Window) -> Result<(), String> {
window.get_gi_mut().begin_drawing()?;
window.get_gi_mut().clear_window(Color::BLACK)?;
let mut rect = Rectangle::default();
for mi in &mut self.menu.items {
rect.x = mi.x;
rect.y = mi.y;
rect.w = mi.w;
rect.h = mi.h;
match &mut mi.item_type {
MenuItemType::Button {
text,
text_c,
h_c,
c,
} => {
if mi.is_hover {
window.get_gi_mut().draw_rect(rect, *h_c)?;
} else {
window.get_gi_mut().draw_rect(rect, *c)?;
}
window
.get_font_mut(&self.font)?
.draw(text, 20, rect.x, rect.y, *text_c)?;
}
MenuItemType::AppearingText {
text,
text_idx,
text_size: _,
text_c,
timer: _,
} => {
window.get_font_mut(&self.font)?.draw(
if *text_idx < text.len() {
&text[0..*text_idx]
} else {
text
},
20,
rect.x,
rect.y,
*text_c,
)?;
}
MenuItemType::InstantText {
text,
text_size,
text_color,
} => {
window.get_font_mut(&self.font)?.draw(
text,
text_size.round() as u32,
rect.x,
rect.y,
*text_color,
)?;
}
MenuItemType::Pause {
timer: _,
length: _,
} => (),
}
}
self.player_particles.draw(window, Transform::IDENTITY);
window.get_gi_mut().draw_rect_transform(
self.player,
Color::from_rgba(255, 255, 255, (self.player_particles.opacity * 255.0) as u8),
Transform::translate(self.player.w / 2.0, self.player.h / 2.0)
* Transform::rotate(self.player_r),
Vector {
x: self.player.x + self.player.w / 2.0,
y: self.player.y + self.player.h / 2.0,
},
)?;
self.joining_particles.draw(window, Transform::IDENTITY);
for expl_conv_ps in &mut self.expl_conv_p_systems {
expl_conv_ps.draw(window, Transform::IDENTITY);
}
for planet in &mut self.planets {
planet.draw(window, Transform::IDENTITY);
}
for star in &mut self.stars {
star.draw(&self.i_star, window, Transform::IDENTITY);
}
for fish in &mut self.fishes {
fish.draw(&self.i_fish, 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(s) = text {
window.get_font_mut(&self.font)?.draw(
s,
20,
20.0,
20.0,
Color::from_rgba(
255,
255,
255,
((*timer / SL_NOTIF_TIME) * 255.0) as u8,
),
)?;
}
}
}
}
window.get_gi_mut().end_drawing()?;
Ok(())
}
#[cfg(not(target_family = "wasm"))]
pub fn save(&mut self) -> IOResult<()> {
use std::io::Write;
let save_bytes = SaveData {
planets: self.planets.clone(),
stars: self.stars.clone(),
fishes: self.fishes.clone(),
player: self.player,
joining_particles: self.joining_particles.clone(),
}
.serialize();
let mut file = File::create(SAVE_FILENAME)?;
file.write_all(&save_bytes)?;
self.save_load_notification = Some(SaveLoadNotification::Save {
text: None,
timer: SL_NOTIF_TIME,
});
Ok(())
}
#[cfg(not(target_family = "wasm"))]
pub fn load(&mut self) -> IOResult<()> {
use std::io::Read;
let mut bytes = Vec::new();
{
let mut file = File::open(SAVE_FILENAME)?;
file.read_to_end(&mut bytes)?;
}
let (save_data, _) = SaveData::deserialize(&bytes).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Failed to deserialize into SaveData!",
)
})?;
self.planets = save_data.planets;
self.stars = save_data.stars;
self.fishes = save_data.fishes;
self.player = save_data.player;
self.joining_particles = save_data.joining_particles;
self.expl_conv_p_systems.clear();
self.move_to = Vector::new(self.player.x, self.player.y);
self.camera
.set_view_xy(
self.player.x - WIDTH_F / 2.0,
self.player.y - HEIGHT_F / 2.0,
)
.ok();
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(())
}
#[cfg(target_family = "wasm")]
pub fn save(&mut self) -> IOResult<()> {
let save_bytes = SaveData {
planets: self.planets.clone(),
stars: self.stars.clone(),
fishes: self.fishes.clone(),
player: self.player,
joining_particles: self.joining_particles.clone(),
}
.serialize();
crate::wasm_helpers::save_data(&save_bytes)?;
self.save_load_notification = Some(SaveLoadNotification::Save {
text: None,
timer: SL_NOTIF_TIME,
});
Ok(())
}
#[cfg(target_family = "wasm")]
pub fn load(&mut self) -> IOResult<()> {
let receiver = crate::wasm_helpers::load_data()?;
self.load_recv = Some(receiver);
self.save_load_notification = Some(SaveLoadNotification::Save {
text: Some(String::from("Loading...")),
timer: SL_NOTIF_TIME,
});
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_de_serialize_particle() {
let mut particle = Particle::default();
particle.rect.x = 1.0;
particle.rect.y = 2.0;
particle.rect.w = 3.0;
particle.rect.h = 4.0;
particle.circle.x = 5.0;
particle.circle.y = 6.0;
particle.circle.r = 7.0;
particle.is_rect = true;
particle.velx = 8.0;
particle.vely = 9.0;
particle.velr = 10.0;
particle.r = 11.0;
particle.lifetime = 12.0;
particle.life_timer = 13.0;
let bytes = particle.serialize();
let (des_particle, size) =
Particle::deserialize(&bytes, 0).expect("Should be able to deserialize Particle!");
assert_eq!(particle, des_particle);
assert_eq!(bytes.len(), size);
let mut particle = Particle::default();
particle.rect.x = 14.0;
particle.rect.y = 15.0;
particle.rect.w = 16.0;
particle.rect.h = 17.0;
particle.circle.x = 18.0;
particle.circle.y = 19.0;
particle.circle.r = 20.0;
particle.is_rect = false;
particle.velx = 21.0;
particle.vely = 22.0;
particle.velr = 23.0;
particle.r = 24.0;
particle.lifetime = 25.0;
particle.life_timer = 26.0;
let bytes = particle.serialize();
let (des_particle, size) =
Particle::deserialize(&bytes, 0).expect("Should be able to deserialize Particle!");
assert_eq!(particle, des_particle);
assert_eq!(bytes.len(), size);
}
#[test]
fn test_de_serialize_particle_system() {
let mut ps = ParticleSystem::default();
let mut particle = Particle::default();
particle.rect.x = 1.0;
particle.rect.y = 2.0;
particle.rect.w = 3.0;
particle.rect.h = 4.0;
particle.circle.x = 5.0;
particle.circle.y = 6.0;
particle.circle.r = 7.0;
particle.is_rect = true;
particle.velx = 8.0;
particle.vely = 9.0;
particle.velr = 10.0;
particle.r = 11.0;
particle.lifetime = 12.0;
particle.life_timer = 13.0;
ps.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 14.0;
particle.rect.y = 15.0;
particle.rect.w = 16.0;
particle.rect.h = 17.0;
particle.circle.x = 18.0;
particle.circle.y = 19.0;
particle.circle.r = 20.0;
particle.is_rect = false;
particle.velx = 21.0;
particle.vely = 22.0;
particle.velr = 23.0;
particle.r = 24.0;
particle.lifetime = 25.0;
particle.life_timer = 26.0;
ps.particles.push(particle);
ps.spawn_timer = 27.0;
ps.spawn_time = 28.0;
ps.lifetime = 29.0;
ps.host_rect.x = 30.0;
ps.host_rect.y = 31.0;
ps.host_rect.w = 32.0;
ps.host_rect.h = 33.0;
ps.host_circle.x = 34.0;
ps.host_circle.y = 35.0;
ps.host_circle.r = 36.0;
ps.is_rect = true;
ps.direction.x = 37.0;
ps.direction.y = 38.0;
ps.color.r = 39;
ps.color.g = 40;
ps.color.b = 41;
ps.color.a = 42;
ps.opacity = 43.0;
ps.vel_multiplier = 44.0;
let bytes = ps.serialize();
let (des_ps, size) = ParticleSystem::deserialize(&bytes, 0)
.expect("Should be able to deserialize ParticleSystem!");
assert_eq!(ps, des_ps);
assert_eq!(bytes.len(), size);
let mut ps = ParticleSystem::default();
let mut particle = Particle::default();
particle.rect.x = 45.0;
particle.rect.y = 46.0;
particle.rect.w = 47.0;
particle.rect.h = 48.0;
particle.circle.x = 49.0;
particle.circle.y = 50.0;
particle.circle.r = 51.0;
particle.is_rect = true;
particle.velx = 52.0;
particle.vely = 53.0;
particle.velr = 54.0;
particle.r = 55.0;
particle.lifetime = 56.0;
particle.life_timer = 57.0;
ps.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 58.0;
particle.rect.y = 59.0;
particle.rect.w = 60.0;
particle.rect.h = 61.0;
particle.circle.x = 62.0;
particle.circle.y = 63.0;
particle.circle.r = 64.0;
particle.is_rect = false;
particle.velx = 65.0;
particle.vely = 66.0;
particle.velr = 67.0;
particle.r = 68.0;
particle.lifetime = 69.0;
particle.life_timer = 70.0;
ps.particles.push(particle);
ps.spawn_timer = 71.0;
ps.spawn_time = 72.0;
ps.lifetime = 73.0;
ps.host_rect.x = 74.0;
ps.host_rect.y = 75.0;
ps.host_rect.w = 76.0;
ps.host_rect.h = 77.0;
ps.host_circle.x = 78.0;
ps.host_circle.y = 79.0;
ps.host_circle.r = 80.0;
ps.is_rect = false;
ps.direction.x = 81.0;
ps.direction.y = 82.0;
ps.color.r = 83;
ps.color.g = 84;
ps.color.b = 85;
ps.color.a = 86;
ps.opacity = 87.0;
ps.vel_multiplier = 88.0;
let bytes = ps.serialize();
let (des_ps, size) = ParticleSystem::deserialize(&bytes, 0)
.expect("Should be able to deserialize ParticleSystem!");
assert_eq!(ps, des_ps);
assert_eq!(bytes.len(), size);
}
#[test]
fn test_de_serialize_rotating_particle_system() {
let mut ps = ParticleSystem::default();
let mut particle = Particle::default();
particle.rect.x = 1.0;
particle.rect.y = 2.0;
particle.rect.w = 3.0;
particle.rect.h = 4.0;
particle.circle.x = 5.0;
particle.circle.y = 6.0;
particle.circle.r = 7.0;
particle.is_rect = true;
particle.velx = 8.0;
particle.vely = 9.0;
particle.velr = 10.0;
particle.r = 11.0;
particle.lifetime = 12.0;
particle.life_timer = 13.0;
ps.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 14.0;
particle.rect.y = 15.0;
particle.rect.w = 16.0;
particle.rect.h = 17.0;
particle.circle.x = 18.0;
particle.circle.y = 19.0;
particle.circle.r = 20.0;
particle.is_rect = false;
particle.velx = 21.0;
particle.vely = 22.0;
particle.velr = 23.0;
particle.r = 24.0;
particle.lifetime = 25.0;
particle.life_timer = 26.0;
ps.particles.push(particle);
ps.spawn_timer = 27.0;
ps.spawn_time = 28.0;
ps.lifetime = 29.0;
ps.host_rect.x = 30.0;
ps.host_rect.y = 31.0;
ps.host_rect.w = 32.0;
ps.host_rect.h = 33.0;
ps.host_circle.x = 34.0;
ps.host_circle.y = 35.0;
ps.host_circle.r = 36.0;
ps.is_rect = true;
ps.direction.x = 37.0;
ps.direction.y = 38.0;
ps.color.r = 39;
ps.color.g = 40;
ps.color.b = 41;
ps.color.a = 42;
ps.opacity = 43.0;
ps.vel_multiplier = 44.0;
let mut rps = RotatingParticleSystem::default();
rps.particle_system = ps;
rps.r = 45.0;
rps.velr = 46.0;
rps.offset = 47.0;
let bytes = rps.serialize();
let (des_rps, size) = RotatingParticleSystem::deserialize(&bytes, 0)
.expect("Should be able to deserialize RotatingParticleSystem!");
assert_eq!(rps, des_rps);
assert_eq!(bytes.len(), size);
}
#[test]
fn test_de_serialize_planet() {
let mut planet = Planet::default();
planet.circle.x = 1.0;
planet.circle.y = 2.0;
planet.circle.r = 3.0;
planet.color.r = 4;
planet.color.g = 5;
planet.color.b = 6;
planet.color.a = 7;
let mut particle = Particle::default();
particle.rect.x = 8.0;
particle.rect.y = 9.0;
particle.rect.w = 10.0;
particle.rect.h = 11.0;
particle.circle.x = 12.0;
particle.circle.y = 13.0;
particle.circle.r = 14.0;
particle.is_rect = true;
particle.velx = 15.0;
particle.vely = 16.0;
particle.velr = 17.0;
particle.r = 18.0;
particle.lifetime = 19.0;
particle.life_timer = 20.0;
planet.particle_system.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 21.0;
particle.rect.y = 22.0;
particle.rect.w = 23.0;
particle.rect.h = 24.0;
particle.circle.x = 25.0;
particle.circle.y = 26.0;
particle.circle.r = 27.0;
particle.is_rect = false;
particle.velx = 28.0;
particle.vely = 29.0;
particle.velr = 30.0;
particle.r = 31.0;
particle.lifetime = 32.0;
particle.life_timer = 33.0;
planet.particle_system.particles.push(particle);
planet.particle_system.spawn_timer = 34.0;
planet.particle_system.spawn_time = 35.0;
planet.particle_system.lifetime = 36.0;
planet.particle_system.host_rect.x = 37.0;
planet.particle_system.host_rect.y = 38.0;
planet.particle_system.host_rect.w = 39.0;
planet.particle_system.host_rect.h = 40.0;
planet.particle_system.host_circle.x = 41.0;
planet.particle_system.host_circle.y = 42.0;
planet.particle_system.host_circle.r = 43.0;
planet.particle_system.is_rect = true;
planet.particle_system.direction.x = 44.0;
planet.particle_system.direction.y = 45.0;
planet.particle_system.color.r = 46;
planet.particle_system.color.g = 47;
planet.particle_system.color.b = 48;
planet.particle_system.color.a = 49;
planet.particle_system.opacity = 50.0;
planet.particle_system.vel_multiplier = 51.0;
let mut rps = RotatingParticleSystem::default();
rps.particle_system = planet.particle_system.clone();
rps.r = 52.0;
rps.velr = 53.0;
rps.offset = 54.0;
planet.moons.push(rps);
let mut rps = RotatingParticleSystem::default();
rps.particle_system = ParticleSystem::default();
rps.r = 55.0;
rps.velr = 56.0;
rps.offset = 57.0;
planet.moons.push(rps);
let bytes = planet.serialize();
let (des_planet, size) =
Planet::deserialize(&bytes, 0).expect("Should be able to deserialize Planet!");
assert_eq!(planet, des_planet);
assert_eq!(bytes.len(), size);
}
#[test]
fn test_de_serialize_star() {
let mut star = Star::default();
star.color.r = 1;
star.color.g = 2;
star.color.b = 3;
star.color.a = 4;
let mut particle = Particle::default();
particle.rect.x = 5.0;
particle.rect.y = 6.0;
particle.rect.w = 7.0;
particle.rect.h = 8.0;
particle.circle.x = 9.0;
particle.circle.y = 10.0;
particle.circle.r = 11.0;
particle.is_rect = true;
particle.velx = 12.0;
particle.vely = 13.0;
particle.velr = 14.0;
particle.r = 15.0;
particle.lifetime = 16.0;
particle.life_timer = 17.0;
star.particle_system.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 18.0;
particle.rect.y = 19.0;
particle.rect.w = 20.0;
particle.rect.h = 21.0;
particle.circle.x = 22.0;
particle.circle.y = 23.0;
particle.circle.r = 24.0;
particle.is_rect = false;
particle.velx = 25.0;
particle.vely = 26.0;
particle.velr = 27.0;
particle.r = 28.0;
particle.lifetime = 29.0;
particle.life_timer = 30.0;
star.particle_system.particles.push(particle);
star.particle_system.spawn_timer = 31.0;
star.particle_system.spawn_time = 32.0;
star.particle_system.lifetime = 33.0;
star.particle_system.host_rect.x = 34.0;
star.particle_system.host_rect.y = 35.0;
star.particle_system.host_rect.w = 36.0;
star.particle_system.host_rect.h = 37.0;
star.particle_system.host_circle.x = 38.0;
star.particle_system.host_circle.y = 39.0;
star.particle_system.host_circle.r = 40.0;
star.particle_system.is_rect = true;
star.particle_system.direction.x = 41.0;
star.particle_system.direction.y = 42.0;
star.particle_system.color.r = 43;
star.particle_system.color.g = 44;
star.particle_system.color.b = 45;
star.particle_system.color.a = 46;
star.particle_system.opacity = 47.0;
star.particle_system.vel_multiplier = 48.0;
star.velr = 49.0;
star.r = 50.0;
let bytes = star.serialize();
let (des_star, size) =
Star::deserialize(&bytes, 0).expect("Should be able to deserialize Star!");
assert_eq!(star, des_star);
assert_eq!(bytes.len(), size);
}
#[test]
fn test_de_serialize_fish() {
let mut fish = Fish::default();
fish.pos.x = 1.0;
fish.pos.y = 2.0;
fish.r = 3.0;
fish.swim_time = 4.0;
fish.swim_timer = 5.0;
fish.swim_v = 6.0;
fish.anim_timer = 7.0;
fish.anim_time = 8.0;
fish.color.r = 9;
fish.color.g = 10;
fish.color.b = 11;
fish.color.a = 12;
fish.body_rect.x = 13.0;
fish.body_rect.y = 14.0;
fish.body_rect.w = 15.0;
fish.body_rect.h = 16.0;
fish.tail_rect.x = 17.0;
fish.tail_rect.y = 18.0;
fish.tail_rect.w = 19.0;
fish.tail_rect.h = 20.0;
let bytes = fish.serialize();
let (des_fish, size) =
Fish::deserialize(&bytes, 0).expect("Should be able to deserialize Fish!");
assert_eq!(fish, des_fish);
assert_eq!(bytes.len(), size);
}
#[test]
fn test_de_serialize_save_data() {
let mut save_data = SaveData::default();
let mut planet = Planet::default();
planet.circle.x = 1.0;
planet.circle.y = 2.0;
planet.circle.r = 3.0;
planet.color.r = 4;
planet.color.g = 5;
planet.color.b = 6;
planet.color.a = 7;
let mut particle = Particle::default();
particle.rect.x = 8.0;
particle.rect.y = 9.0;
particle.rect.w = 10.0;
particle.rect.h = 11.0;
particle.circle.x = 12.0;
particle.circle.y = 13.0;
particle.circle.r = 14.0;
particle.is_rect = true;
particle.velx = 15.0;
particle.vely = 16.0;
particle.velr = 17.0;
particle.r = 18.0;
particle.lifetime = 19.0;
particle.life_timer = 20.0;
planet.particle_system.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 21.0;
particle.rect.y = 22.0;
particle.rect.w = 23.0;
particle.rect.h = 24.0;
particle.circle.x = 25.0;
particle.circle.y = 26.0;
particle.circle.r = 27.0;
particle.is_rect = false;
particle.velx = 28.0;
particle.vely = 29.0;
particle.velr = 30.0;
particle.r = 31.0;
particle.lifetime = 32.0;
particle.life_timer = 33.0;
planet.particle_system.particles.push(particle);
planet.particle_system.spawn_timer = 34.0;
planet.particle_system.spawn_time = 35.0;
planet.particle_system.lifetime = 36.0;
planet.particle_system.host_rect.x = 37.0;
planet.particle_system.host_rect.y = 38.0;
planet.particle_system.host_rect.w = 39.0;
planet.particle_system.host_rect.h = 40.0;
planet.particle_system.host_circle.x = 41.0;
planet.particle_system.host_circle.y = 42.0;
planet.particle_system.host_circle.r = 43.0;
planet.particle_system.is_rect = true;
planet.particle_system.direction.x = 44.0;
planet.particle_system.direction.y = 45.0;
planet.particle_system.color.r = 46;
planet.particle_system.color.g = 47;
planet.particle_system.color.b = 48;
planet.particle_system.color.a = 49;
planet.particle_system.opacity = 50.0;
planet.particle_system.vel_multiplier = 51.0;
let mut rps = RotatingParticleSystem::default();
rps.particle_system = planet.particle_system.clone();
rps.r = 52.0;
rps.velr = 53.0;
rps.offset = 54.0;
planet.moons.push(rps);
let mut rps = RotatingParticleSystem::default();
rps.particle_system = ParticleSystem::default();
rps.r = 55.0;
rps.velr = 56.0;
rps.offset = 57.0;
planet.moons.push(rps);
save_data.planets.push(planet.clone());
planet.color.r = 58;
planet.color.g = 59;
planet.color.b = 60;
planet.color.a = 61;
save_data.planets.push(planet.clone());
planet.color.r = 62;
planet.color.g = 63;
planet.color.b = 64;
planet.color.a = 65;
save_data.planets.push(planet);
let mut star = Star::default();
star.color.r = 1;
star.color.g = 2;
star.color.b = 3;
star.color.a = 4;
let mut particle = Particle::default();
particle.rect.x = 5.0;
particle.rect.y = 6.0;
particle.rect.w = 7.0;
particle.rect.h = 8.0;
particle.circle.x = 9.0;
particle.circle.y = 10.0;
particle.circle.r = 11.0;
particle.is_rect = true;
particle.velx = 12.0;
particle.vely = 13.0;
particle.velr = 14.0;
particle.r = 15.0;
particle.lifetime = 16.0;
particle.life_timer = 17.0;
star.particle_system.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 18.0;
particle.rect.y = 19.0;
particle.rect.w = 20.0;
particle.rect.h = 21.0;
particle.circle.x = 22.0;
particle.circle.y = 23.0;
particle.circle.r = 24.0;
particle.is_rect = false;
particle.velx = 25.0;
particle.vely = 26.0;
particle.velr = 27.0;
particle.r = 28.0;
particle.lifetime = 29.0;
particle.life_timer = 30.0;
star.particle_system.particles.push(particle);
star.particle_system.spawn_timer = 31.0;
star.particle_system.spawn_time = 32.0;
star.particle_system.lifetime = 33.0;
star.particle_system.host_rect.x = 34.0;
star.particle_system.host_rect.y = 35.0;
star.particle_system.host_rect.w = 36.0;
star.particle_system.host_rect.h = 37.0;
star.particle_system.host_circle.x = 38.0;
star.particle_system.host_circle.y = 39.0;
star.particle_system.host_circle.r = 40.0;
star.particle_system.is_rect = true;
star.particle_system.direction.x = 41.0;
star.particle_system.direction.y = 42.0;
star.particle_system.color.r = 43;
star.particle_system.color.g = 44;
star.particle_system.color.b = 45;
star.particle_system.color.a = 46;
star.particle_system.opacity = 47.0;
star.particle_system.vel_multiplier = 48.0;
star.velr = 49.0;
star.r = 50.0;
save_data.stars.push(star.clone());
star.color.r = 51;
star.color.g = 52;
star.color.b = 53;
star.color.a = 54;
save_data.stars.push(star.clone());
star.color.r = 55;
star.color.g = 56;
star.color.b = 57;
star.color.a = 58;
save_data.stars.push(star);
let mut fish = Fish::default();
fish.pos.x = 1.0;
fish.pos.y = 2.0;
fish.r = 3.0;
fish.swim_time = 4.0;
fish.swim_timer = 5.0;
fish.swim_v = 6.0;
fish.anim_timer = 7.0;
fish.anim_time = 8.0;
fish.color.r = 9;
fish.color.g = 10;
fish.color.b = 11;
fish.color.a = 12;
fish.body_rect.x = 13.0;
fish.body_rect.y = 14.0;
fish.body_rect.w = 15.0;
fish.body_rect.h = 16.0;
fish.tail_rect.x = 17.0;
fish.tail_rect.y = 18.0;
fish.tail_rect.w = 19.0;
fish.tail_rect.h = 20.0;
save_data.fishes.push(fish.clone());
fish.pos.x = 21.0;
fish.pos.y = 22.0;
fish.r = 23.0;
save_data.fishes.push(fish.clone());
fish.pos.x = 24.0;
fish.pos.y = 25.0;
fish.r = 26.0;
save_data.fishes.push(fish);
save_data.player.x = 100.0;
save_data.player.y = 101.0;
save_data.player.w = 102.0;
save_data.player.h = 103.0;
let mut ps = ParticleSystem::default();
let mut particle = Particle::default();
particle.rect.x = 1.0;
particle.rect.y = 2.0;
particle.rect.w = 3.0;
particle.rect.h = 4.0;
particle.circle.x = 5.0;
particle.circle.y = 6.0;
particle.circle.r = 7.0;
particle.is_rect = true;
particle.velx = 8.0;
particle.vely = 9.0;
particle.velr = 10.0;
particle.r = 11.0;
particle.lifetime = 12.0;
particle.life_timer = 13.0;
ps.particles.push(particle);
let mut particle = Particle::default();
particle.rect.x = 14.0;
particle.rect.y = 15.0;
particle.rect.w = 16.0;
particle.rect.h = 17.0;
particle.circle.x = 18.0;
particle.circle.y = 19.0;
particle.circle.r = 20.0;
particle.is_rect = false;
particle.velx = 21.0;
particle.vely = 22.0;
particle.velr = 23.0;
particle.r = 24.0;
particle.lifetime = 25.0;
particle.life_timer = 26.0;
ps.particles.push(particle);
ps.spawn_timer = 27.0;
ps.spawn_time = 28.0;
ps.lifetime = 29.0;
ps.host_rect.x = 30.0;
ps.host_rect.y = 31.0;
ps.host_rect.w = 32.0;
ps.host_rect.h = 33.0;
ps.host_circle.x = 34.0;
ps.host_circle.y = 35.0;
ps.host_circle.r = 36.0;
ps.is_rect = true;
ps.direction.x = 37.0;
ps.direction.y = 38.0;
ps.color.r = 39;
ps.color.g = 40;
ps.color.b = 41;
ps.color.a = 42;
ps.opacity = 43.0;
ps.vel_multiplier = 44.0;
save_data.joining_particles.particle_system = ps;
save_data.joining_particles.r = 45.0;
save_data.joining_particles.velr = 46.0;
save_data.joining_particles.offset = 47.0;
let bytes = save_data.serialize();
let (des_save_data, size) =
SaveData::deserialize(&bytes).expect("Should be able to deserialize SaveData!");
assert_eq!(save_data, des_save_data);
assert_eq!(bytes.len(), size);
}
}