Added ship collapse sequence
parent
46313b4880
commit
27c8bf6093
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 38fd6766762ce90bb699f98e30e46b17c4eda50c
|
Subproject commit 74ddbde9e1cec1418c17844bc7336324ece88d15
|
|
@ -2,13 +2,19 @@
|
||||||
sprite = "particle::explosion::small"
|
sprite = "particle::explosion::small"
|
||||||
lifetime = "inherit"
|
lifetime = "inherit"
|
||||||
inherit_velocity = "target"
|
inherit_velocity = "target"
|
||||||
size = 3.0
|
size = 8.0
|
||||||
|
|
||||||
|
[effect."large explosion"]
|
||||||
|
sprite = "particle::explosion::large"
|
||||||
|
lifetime = "inherit"
|
||||||
|
inherit_velocity = "target"
|
||||||
|
size = 25.0
|
||||||
|
|
||||||
[effect."huge explosion"]
|
[effect."huge explosion"]
|
||||||
sprite = "particle::explosion::huge"
|
sprite = "particle::explosion::huge"
|
||||||
lifetime = "inherit"
|
lifetime = "inherit"
|
||||||
inherit_velocity = "target"
|
inherit_velocity = "target"
|
||||||
size = 3.0
|
size = 50.0
|
||||||
|
|
||||||
[effect."blaster expire"]
|
[effect."blaster expire"]
|
||||||
sprite = "particle::blaster"
|
sprite = "particle::blaster"
|
||||||
|
@ -25,5 +31,4 @@ size = 3.0
|
||||||
# effect probabilities & variants
|
# effect probabilities & variants
|
||||||
# multiple particles in one effect
|
# multiple particles in one effect
|
||||||
# fade
|
# fade
|
||||||
# better physics
|
# document: effect vs particle
|
||||||
# document:effect vs particle
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ hull = 200
|
||||||
linear_drag = 0.2
|
linear_drag = 0.2
|
||||||
angular_drag = 0.2
|
angular_drag = 0.2
|
||||||
|
|
||||||
|
# TODO: disable
|
||||||
|
# TODO: damage effects
|
||||||
|
|
||||||
space.outfit = 200
|
space.outfit = 200
|
||||||
space.engine = 50
|
space.engine = 50
|
||||||
space.weapon = 50
|
space.weapon = 50
|
||||||
|
@ -13,6 +16,19 @@ space.weapon = 50
|
||||||
engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
|
engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
|
||||||
guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]
|
guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]
|
||||||
|
|
||||||
|
|
||||||
|
# Length of death sequence, in seconds
|
||||||
|
collapse.length = 5.0
|
||||||
|
|
||||||
|
# Effects to create during the collapse sequence.
|
||||||
|
# On average, `count` will be spawned over the sequence,
|
||||||
|
# with a distribution of (x^2 + 0.1)
|
||||||
|
collapse.effects = [
|
||||||
|
{ effect = "small explosion", count = 30 },
|
||||||
|
{ effect = "large explosion", count = 5 },
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
collision = [
|
collision = [
|
||||||
#[rustfmt:skip],
|
#[rustfmt:skip],
|
||||||
[0.53921, 1.0000],
|
[0.53921, 1.0000],
|
||||||
|
@ -37,3 +53,44 @@ collision = [
|
||||||
[-0.53921, 0.29343],
|
[-0.53921, 0.29343],
|
||||||
[-0.53921, 1.0000],
|
[-0.53921, 1.0000],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Scripted explosion
|
||||||
|
[[ship."Gypsum".collapse.event]]
|
||||||
|
time = 5.0
|
||||||
|
effects = [
|
||||||
|
#[rustfmt:skip],
|
||||||
|
{ effect = "small explosion", count = 8 },
|
||||||
|
{ effect = "large explosion", count = 5 },
|
||||||
|
{ effect = "huge explosion", count = 1, pos = [0, 0] },
|
||||||
|
{ effect = "huge explosion", count = 4 },
|
||||||
|
]
|
||||||
|
|
||||||
|
# Scripted explosion
|
||||||
|
[[ship."Gypsum".collapse.event]]
|
||||||
|
time = 0.0
|
||||||
|
effects = [
|
||||||
|
#[rustfmt:skip],
|
||||||
|
{ effect = "small explosion", count = 3 },
|
||||||
|
{ effect = "large explosion", count = 1 },
|
||||||
|
]
|
||||||
|
|
||||||
|
# Play a sprite reel (or change sprite)
|
||||||
|
#[[ship."Gypsum".death.collapse.event]]
|
||||||
|
#time = 10.0
|
||||||
|
#reel = "gypsum post-death"
|
||||||
|
|
||||||
|
# Create debris
|
||||||
|
#[[ship."Gypsum".death.collapse.event]]
|
||||||
|
#time = "end"
|
||||||
|
#physics = "inherit"
|
||||||
|
# OR (relative to original rigidbody)
|
||||||
|
#physics.position = [0, 0]
|
||||||
|
#physics.velocity = [0, 0]
|
||||||
|
#physics.angle = 0
|
||||||
|
#physics.angvel = 0
|
||||||
|
#debris = "debris"
|
||||||
|
|
||||||
|
# Burning, interactable, destructible debris
|
||||||
|
#[debris]
|
||||||
|
#effects = [{ type = "small explosion", count = 10 }]
|
||||||
|
|
|
@ -21,10 +21,7 @@ use walkdir::WalkDir;
|
||||||
pub use handle::{
|
pub use handle::{
|
||||||
EffectHandle, FactionHandle, GunHandle, OutfitHandle, ShipHandle, SpriteHandle, SystemHandle,
|
EffectHandle, FactionHandle, GunHandle, OutfitHandle, ShipHandle, SpriteHandle, SystemHandle,
|
||||||
};
|
};
|
||||||
pub use part::{
|
pub use part::*;
|
||||||
Effect, EnginePoint, Faction, Gun, GunPoint, ImpactInheritVelocity, Outfit, OutfitSpace,
|
|
||||||
Projectile, ProjectileCollider, Relationship, RepeatMode, Ship, Sprite, System,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod syntax {
|
mod syntax {
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
|
|
@ -48,8 +48,10 @@ pub enum ProjectileCollider {
|
||||||
Ball(BallCollider),
|
Ball(BallCollider),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A simple ball-shaped collider, centered at the object's position
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct BallCollider {
|
pub struct BallCollider {
|
||||||
|
/// The radius of this ball
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
//! Content parts
|
//! Content parts
|
||||||
|
|
||||||
pub mod effect;
|
pub(crate) mod effect;
|
||||||
pub mod faction;
|
pub(crate) mod faction;
|
||||||
pub mod gun;
|
pub(crate) mod gun;
|
||||||
pub mod outfit;
|
pub(crate) mod outfit;
|
||||||
pub mod outfitspace;
|
pub(crate) mod outfitspace;
|
||||||
pub mod ship;
|
pub(crate) mod ship;
|
||||||
pub mod sprite;
|
pub(crate) mod sprite;
|
||||||
pub mod system;
|
pub(crate) mod system;
|
||||||
|
|
||||||
pub use effect::{Effect, ImpactInheritVelocity};
|
pub use effect::{Effect, ImpactInheritVelocity};
|
||||||
pub use faction::{Faction, Relationship};
|
pub use faction::{Faction, Relationship};
|
||||||
pub use gun::{Gun, Projectile, ProjectileCollider};
|
pub use gun::{Gun, Projectile, ProjectileCollider};
|
||||||
pub use outfit::Outfit;
|
pub use outfit::Outfit;
|
||||||
pub use outfitspace::OutfitSpace;
|
pub use outfitspace::OutfitSpace;
|
||||||
pub use ship::{EnginePoint, GunPoint, Ship};
|
pub use ship::{
|
||||||
|
CollapseEffectSpawner, CollapseEvent, EffectCollapseEvent, EnginePoint, GunPoint, Ship,
|
||||||
|
ShipCollapse,
|
||||||
|
};
|
||||||
pub use sprite::{RepeatMode, Sprite};
|
pub use sprite::{RepeatMode, Sprite};
|
||||||
pub use system::{Object, System};
|
pub use system::{Object, System};
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use nalgebra::{point, Point};
|
use nalgebra::{point, Point};
|
||||||
|
|
||||||
use crate::{handle::SpriteHandle, Content, ContentBuildContext, OutfitSpace};
|
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitSpace};
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use crate::part::outfitspace;
|
use crate::part::{effect::syntax::EffectReference, outfitspace};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
|
@ -25,6 +25,7 @@ pub(crate) mod syntax {
|
||||||
pub angular_drag: f32,
|
pub angular_drag: f32,
|
||||||
pub linear_drag: f32,
|
pub linear_drag: f32,
|
||||||
pub space: outfitspace::syntax::OutfitSpace,
|
pub space: outfitspace::syntax::OutfitSpace,
|
||||||
|
pub collapse: Option<Collapse>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -39,6 +40,34 @@ pub(crate) mod syntax {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// plural or not? document!
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Collapse {
|
||||||
|
pub length: f32,
|
||||||
|
pub effects: Vec<CollapseEffectSpawner>,
|
||||||
|
pub event: Vec<CollapseEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct CollapseEffectSpawner {
|
||||||
|
pub effect: EffectReference,
|
||||||
|
pub count: f32,
|
||||||
|
pub pos: Option<[f32; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum CollapseEvent {
|
||||||
|
Effect(EffectCollapseEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct EffectCollapseEvent {
|
||||||
|
pub time: f32,
|
||||||
|
pub effects: Vec<CollapseEffectSpawner>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processed data structs.
|
// Processed data structs.
|
||||||
|
@ -86,6 +115,9 @@ pub struct Ship {
|
||||||
|
|
||||||
/// Outfit space in this ship
|
/// Outfit space in this ship
|
||||||
pub space: OutfitSpace,
|
pub space: OutfitSpace,
|
||||||
|
|
||||||
|
/// Ship collapse sequence
|
||||||
|
pub collapse: ShipCollapse,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collision shape for this ship
|
/// Collision shape for this ship
|
||||||
|
@ -116,16 +148,60 @@ pub struct GunPoint {
|
||||||
pub pos: Point2<f32>,
|
pub pos: Point2<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parameters for a ship's collapse sequence
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ShipCollapse {
|
||||||
|
/// Collapse sequence length, in seconds
|
||||||
|
pub length: f32,
|
||||||
|
|
||||||
|
/// Effects to create during collapse
|
||||||
|
pub effects: Vec<CollapseEffectSpawner>,
|
||||||
|
|
||||||
|
/// Scripted events during ship collapse
|
||||||
|
pub events: Vec<CollapseEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A scripted event during a ship collapse sequence
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CollapseEffectSpawner {
|
||||||
|
/// The effect to create
|
||||||
|
pub effect: EffectHandle,
|
||||||
|
|
||||||
|
/// How many effects to create
|
||||||
|
pub count: f32,
|
||||||
|
|
||||||
|
/// Where to create these effects.
|
||||||
|
/// Position is random if None.
|
||||||
|
pub pos: Option<Point2<f32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A scripted event during a ship collapse sequence
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum CollapseEvent {
|
||||||
|
/// A scripted effect during a ship collapse sequence
|
||||||
|
Effect(EffectCollapseEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A scripted effect during a ship collapse sequence
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EffectCollapseEvent {
|
||||||
|
/// When to trigger this event
|
||||||
|
pub time: f32,
|
||||||
|
|
||||||
|
/// The effect to create
|
||||||
|
pub effects: Vec<CollapseEffectSpawner>,
|
||||||
|
}
|
||||||
|
|
||||||
impl crate::Build for Ship {
|
impl crate::Build for Ship {
|
||||||
type InputSyntaxType = HashMap<String, syntax::Ship>;
|
type InputSyntaxType = HashMap<String, syntax::Ship>;
|
||||||
|
|
||||||
fn build(
|
fn build(
|
||||||
ship: Self::InputSyntaxType,
|
ship: Self::InputSyntaxType,
|
||||||
_build_context: &mut ContentBuildContext,
|
build_context: &mut ContentBuildContext,
|
||||||
content: &mut Content,
|
ct: &mut Content,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for (ship_name, ship) in ship {
|
for (ship_name, ship) in ship {
|
||||||
let handle = match content.sprite_index.get(&ship.sprite) {
|
let handle = match ct.sprite_index.get(&ship.sprite) {
|
||||||
None => bail!(
|
None => bail!(
|
||||||
"In ship `{}`: sprite `{}` doesn't exist",
|
"In ship `{}`: sprite `{}` doesn't exist",
|
||||||
ship_name,
|
ship_name,
|
||||||
|
@ -135,10 +211,67 @@ impl crate::Build for Ship {
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = ship.size;
|
let size = ship.size;
|
||||||
let aspect = content.get_sprite(handle).aspect;
|
let aspect = ct.get_sprite(handle).aspect;
|
||||||
|
|
||||||
content.ships.push(Self {
|
let collapse = if let Some(c) = ship.collapse {
|
||||||
|
let mut effects = Vec::new();
|
||||||
|
for e in c.effects {
|
||||||
|
effects.push(CollapseEffectSpawner {
|
||||||
|
effect: e
|
||||||
|
.effect
|
||||||
|
.to_handle(build_context, ct)
|
||||||
|
.with_context(|| format!("while loading ship `{}`", ship_name))?,
|
||||||
|
count: e.count,
|
||||||
|
pos: e.pos.map(|p| Point2 {
|
||||||
|
x: p[0] * (size / 2.0) * aspect,
|
||||||
|
y: p[1] * size / 2.0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut events = Vec::new();
|
||||||
|
for e in c.event {
|
||||||
|
match e {
|
||||||
|
syntax::CollapseEvent::Effect(e) => {
|
||||||
|
let mut effects = Vec::new();
|
||||||
|
for g in e.effects {
|
||||||
|
effects.push(CollapseEffectSpawner {
|
||||||
|
effect: g.effect.to_handle(build_context, ct).with_context(
|
||||||
|
|| format!("while loading ship `{}`", ship_name),
|
||||||
|
)?,
|
||||||
|
count: g.count,
|
||||||
|
pos: g.pos.map(|p| Point2 {
|
||||||
|
x: p[0] * (size / 2.0) * aspect,
|
||||||
|
y: p[1] * size / 2.0,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
events.push(CollapseEvent::Effect(EffectCollapseEvent {
|
||||||
|
time: e.time,
|
||||||
|
effects,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShipCollapse {
|
||||||
|
length: c.length,
|
||||||
|
effects,
|
||||||
|
events,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default collapse sequence
|
||||||
|
ShipCollapse {
|
||||||
|
length: 0.0,
|
||||||
|
effects: vec![],
|
||||||
|
events: vec![],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ct.ships.push(Self {
|
||||||
aspect,
|
aspect,
|
||||||
|
collapse,
|
||||||
name: ship_name,
|
name: ship_name,
|
||||||
sprite: handle,
|
sprite: handle,
|
||||||
mass: ship.mass,
|
mass: ship.mass,
|
||||||
|
|
|
@ -41,6 +41,7 @@ impl Game {
|
||||||
o1.add(&ct, content::OutfitHandle { index: 0 });
|
o1.add(&ct, content::OutfitHandle { index: 0 });
|
||||||
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||||
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||||
|
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||||
|
|
||||||
let s = object::Ship::new(
|
let s = object::Ship::new(
|
||||||
&ct,
|
&ct,
|
||||||
|
@ -57,14 +58,14 @@ impl Game {
|
||||||
// This method of specifying factions is non-deterministic,
|
// This method of specifying factions is non-deterministic,
|
||||||
// but that's ok since this is for debug.
|
// but that's ok since this is for debug.
|
||||||
// TODO: fix
|
// TODO: fix
|
||||||
content::FactionHandle { index: 0 },
|
content::FactionHandle { index: 1 },
|
||||||
object::OutfitSet::new(ss),
|
object::OutfitSet::new(ss),
|
||||||
);
|
);
|
||||||
let h2 = physics.add_ship(&ct, s, Point2 { x: 300.0, y: 300.0 });
|
let h2 = physics.add_ship(&ct, s, Point2 { x: 300.0, y: 300.0 });
|
||||||
|
|
||||||
let mut o1 = object::OutfitSet::new(ss);
|
let mut o1 = object::OutfitSet::new(ss);
|
||||||
o1.add(&ct, content::OutfitHandle { index: 0 });
|
o1.add(&ct, content::OutfitHandle { index: 0 });
|
||||||
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
//o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||||
|
|
||||||
let s = object::Ship::new(
|
let s = object::Ship::new(
|
||||||
&ct,
|
&ct,
|
||||||
|
|
|
@ -30,8 +30,7 @@ impl Ship {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Has this ship been destroyed?
|
pub fn is_dead(&self) -> bool {
|
||||||
pub fn is_destroyed(&self) -> bool {
|
|
||||||
self.hull <= 0.0
|
self.hull <= 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,8 +41,10 @@ impl Ship {
|
||||||
let r = f.relationships.get(&p.faction).unwrap();
|
let r = f.relationships.get(&p.faction).unwrap();
|
||||||
match r {
|
match r {
|
||||||
content::Relationship::Hostile => {
|
content::Relationship::Hostile => {
|
||||||
// TODO: implement death and spawning, and enable damage
|
if self.is_dead() {
|
||||||
//s.hull -= p.damage;
|
return true;
|
||||||
|
}
|
||||||
|
self.hull -= p.content.damage;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
|
@ -51,6 +52,10 @@ impl Ship {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fire_guns(&mut self) -> Vec<(Projectile, content::GunPoint)> {
|
pub fn fire_guns(&mut self) -> Vec<(Projectile, content::GunPoint)> {
|
||||||
|
if self.is_dead() {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
self.outfits
|
self.outfits
|
||||||
.iter_guns_points()
|
.iter_guns_points()
|
||||||
.filter(|(g, _)| g.cooldown <= 0.0)
|
.filter(|(g, _)| g.cooldown <= 0.0)
|
||||||
|
|
|
@ -2,6 +2,7 @@ use anyhow::Result;
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{Deg, EuclideanSpace, Matrix2, Matrix4, Point2, Vector3};
|
use cgmath::{Deg, EuclideanSpace, Matrix2, Matrix4, Point2, Vector3};
|
||||||
use galactica_constants;
|
use galactica_constants;
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
use std::{iter, rc::Rc};
|
use std::{iter, rc::Rc};
|
||||||
use wgpu;
|
use wgpu;
|
||||||
use winit::{self, dpi::LogicalSize, window::Window};
|
use winit::{self, dpi::LogicalSize, window::Window};
|
||||||
|
@ -624,6 +625,7 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write all new particles to GPU buffer
|
// Write all new particles to GPU buffer
|
||||||
|
state.new_particles.shuffle(&mut rand::thread_rng());
|
||||||
for i in state.new_particles.iter() {
|
for i in state.new_particles.iter() {
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.vertex_buffers.particle.instances,
|
&self.vertex_buffers.particle.instances,
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use cgmath::{Deg, InnerSpace, Vector2};
|
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
||||||
use nalgebra::vector;
|
use nalgebra::{point, vector};
|
||||||
|
|
||||||
use rapier2d::dynamics::RigidBody;
|
use rand::{rngs::ThreadRng, Rng};
|
||||||
|
use rapier2d::{dynamics::RigidBody, geometry::Collider};
|
||||||
|
|
||||||
use crate::{util, ShipPhysicsHandle};
|
use crate::{util, ShipPhysicsHandle};
|
||||||
use galactica_content as content;
|
use galactica_content as content;
|
||||||
use galactica_gameobject as object;
|
use galactica_gameobject as object;
|
||||||
use galactica_render::ObjectSprite;
|
use galactica_render::{ObjectSprite, ParticleBuilder};
|
||||||
|
|
||||||
pub struct ShipControls {
|
pub struct ShipControls {
|
||||||
pub left: bool,
|
pub left: bool,
|
||||||
|
@ -26,6 +27,141 @@ impl ShipControls {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ShipCollapseSequence {
|
||||||
|
total_length: f32,
|
||||||
|
time_elapsed: f32,
|
||||||
|
rng: ThreadRng,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShipCollapseSequence {
|
||||||
|
fn new(total_length: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
total_length: total_length,
|
||||||
|
time_elapsed: 0.0,
|
||||||
|
rng: rand::thread_rng(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_done(&self) -> bool {
|
||||||
|
self.time_elapsed >= self.total_length
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_in_ship(
|
||||||
|
&mut self,
|
||||||
|
ship_content: &content::Ship,
|
||||||
|
collider: &Collider,
|
||||||
|
) -> Vector2<f32> {
|
||||||
|
// Pick a random point inside this ship's collider
|
||||||
|
let mut y = 0.0;
|
||||||
|
let mut x = 0.0;
|
||||||
|
let mut a = false;
|
||||||
|
while !a {
|
||||||
|
y = self.rng.gen_range(-1.0..=1.0) * ship_content.size / 2.0;
|
||||||
|
x = self.rng.gen_range(-1.0..=1.0) * ship_content.size * ship_content.sprite.aspect
|
||||||
|
/ 2.0;
|
||||||
|
a = collider.shape().contains_local_point(&point![x, y]);
|
||||||
|
}
|
||||||
|
Vector2 { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(
|
||||||
|
&mut self,
|
||||||
|
ship: &object::Ship,
|
||||||
|
ct: &content::Content,
|
||||||
|
particles: &mut Vec<ParticleBuilder>,
|
||||||
|
rigid_body: &mut RigidBody,
|
||||||
|
collider: &mut Collider,
|
||||||
|
t: f32,
|
||||||
|
) {
|
||||||
|
let h = ship.handle;
|
||||||
|
let ship_content = ct.get_ship(h);
|
||||||
|
let ship_pos = util::rigidbody_position(rigid_body);
|
||||||
|
let ship_rot = util::rigidbody_rotation(rigid_body);
|
||||||
|
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 });
|
||||||
|
|
||||||
|
// The fraction of this collapse sequence that has been played
|
||||||
|
let frac_done = self.time_elapsed / self.total_length;
|
||||||
|
|
||||||
|
// Trigger collapse events
|
||||||
|
for event in &ship_content.collapse.events {
|
||||||
|
match event {
|
||||||
|
content::CollapseEvent::Effect(event) => {
|
||||||
|
if (event.time > self.time_elapsed && event.time <= self.time_elapsed + t)
|
||||||
|
|| (event.time == 0.0 && self.time_elapsed == 0.0)
|
||||||
|
{
|
||||||
|
for spawner in &event.effects {
|
||||||
|
let effect = ct.get_effect(spawner.effect);
|
||||||
|
|
||||||
|
for _ in 0..spawner.count as usize {
|
||||||
|
let pos = if let Some(pos) = spawner.pos {
|
||||||
|
pos.to_vec()
|
||||||
|
} else {
|
||||||
|
self.random_in_ship(ship_content, collider)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Position, adjusted for ship rotation
|
||||||
|
let pos =
|
||||||
|
Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
|
||||||
|
|
||||||
|
particles.push(ParticleBuilder {
|
||||||
|
sprite: effect.sprite,
|
||||||
|
pos: ship_pos + pos,
|
||||||
|
velocity: Vector2::zero(),
|
||||||
|
angle: Deg::zero(),
|
||||||
|
lifetime: effect.lifetime,
|
||||||
|
size: effect.size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create collapse effects
|
||||||
|
for spawner in &ship_content.collapse.effects {
|
||||||
|
let effect = ct.get_effect(spawner.effect);
|
||||||
|
|
||||||
|
// Probability of adding a particle this frame.
|
||||||
|
// The area of this function over [0, 1] should be 1.
|
||||||
|
let pdf = |x: f32| {
|
||||||
|
let f = 0.2;
|
||||||
|
let y = if x < (1.0 - f) {
|
||||||
|
let x = x / (1.0 - f);
|
||||||
|
(x * x + 0.2) * 1.8 - f
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
return y;
|
||||||
|
};
|
||||||
|
|
||||||
|
let p_add = (t / self.total_length) * pdf(frac_done) * spawner.count;
|
||||||
|
|
||||||
|
if self.rng.gen_range(0.0..=1.0) <= p_add {
|
||||||
|
let pos = if let Some(pos) = spawner.pos {
|
||||||
|
pos.to_vec()
|
||||||
|
} else {
|
||||||
|
self.random_in_ship(ship_content, collider)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Position, adjusted for ship rotation
|
||||||
|
let pos = Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
|
||||||
|
|
||||||
|
particles.push(ParticleBuilder {
|
||||||
|
sprite: effect.sprite,
|
||||||
|
pos: ship_pos + pos,
|
||||||
|
velocity: Vector2::zero(),
|
||||||
|
angle: Deg::zero(),
|
||||||
|
lifetime: effect.lifetime,
|
||||||
|
size: effect.size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.time_elapsed += t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A ship instance in the physics system
|
/// A ship instance in the physics system
|
||||||
pub struct ShipWorldObject {
|
pub struct ShipWorldObject {
|
||||||
/// This ship's physics handle
|
/// This ship's physics handle
|
||||||
|
@ -36,20 +172,46 @@ pub struct ShipWorldObject {
|
||||||
|
|
||||||
/// This ship's controls
|
/// This ship's controls
|
||||||
pub controls: ShipControls,
|
pub controls: ShipControls,
|
||||||
|
|
||||||
|
collapse_sequence: ShipCollapseSequence,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShipWorldObject {
|
impl ShipWorldObject {
|
||||||
/// Make a new ship
|
/// Make a new ship
|
||||||
pub fn new(ship: object::Ship, physics_handle: ShipPhysicsHandle) -> Self {
|
pub fn new(
|
||||||
|
ct: &content::Content,
|
||||||
|
ship: object::Ship,
|
||||||
|
physics_handle: ShipPhysicsHandle,
|
||||||
|
) -> Self {
|
||||||
|
let ship_content = ct.get_ship(ship.handle);
|
||||||
ShipWorldObject {
|
ShipWorldObject {
|
||||||
physics_handle,
|
physics_handle,
|
||||||
ship,
|
ship,
|
||||||
controls: ShipControls::new(),
|
controls: ShipControls::new(),
|
||||||
|
collapse_sequence: ShipCollapseSequence::new(ship_content.collapse.length),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Should this ship should be removed from the world?
|
||||||
|
pub fn remove_from_world(&self) -> bool {
|
||||||
|
return self.ship.is_dead() && self.collapse_sequence.is_done();
|
||||||
|
}
|
||||||
|
|
||||||
/// Step this ship's state by t seconds
|
/// Step this ship's state by t seconds
|
||||||
pub fn step(&mut self, r: &mut RigidBody, t: f32) {
|
pub fn step(
|
||||||
|
&mut self,
|
||||||
|
ct: &content::Content,
|
||||||
|
particles: &mut Vec<ParticleBuilder>,
|
||||||
|
r: &mut RigidBody,
|
||||||
|
c: &mut Collider,
|
||||||
|
t: f32,
|
||||||
|
) {
|
||||||
|
if self.ship.is_dead() {
|
||||||
|
return self
|
||||||
|
.collapse_sequence
|
||||||
|
.step(&self.ship, ct, particles, r, c, t);
|
||||||
|
}
|
||||||
|
|
||||||
let ship_rot = util::rigidbody_rotation(r);
|
let ship_rot = util::rigidbody_rotation(r);
|
||||||
let engine_force = ship_rot * t;
|
let engine_force = ship_rot * t;
|
||||||
|
|
||||||
|
|
|
@ -253,7 +253,8 @@ impl<'a> World {
|
||||||
);
|
);
|
||||||
|
|
||||||
let h = ShipPhysicsHandle(r, c);
|
let h = ShipPhysicsHandle(r, c);
|
||||||
self.ships.insert(c, objects::ShipWorldObject::new(ship, h));
|
self.ships
|
||||||
|
.insert(c, objects::ShipWorldObject::new(ct, ship, h));
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,16 +265,19 @@ impl<'a> World {
|
||||||
let mut projectiles = Vec::new();
|
let mut projectiles = Vec::new();
|
||||||
let mut to_remove = Vec::new();
|
let mut to_remove = Vec::new();
|
||||||
for (_, s) in &mut self.ships {
|
for (_, s) in &mut self.ships {
|
||||||
if s.ship.is_destroyed() {
|
|
||||||
to_remove.push(s.physics_handle);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = &mut self.wrapper.rigid_body_set[s.physics_handle.0];
|
let r = &mut self.wrapper.rigid_body_set[s.physics_handle.0];
|
||||||
s.step(r, t);
|
let c = &mut self.wrapper.collider_set[s.physics_handle.1];
|
||||||
|
|
||||||
|
// TODO: unified step info struct
|
||||||
|
s.step(ct, particles, r, c, t);
|
||||||
if s.controls.guns {
|
if s.controls.guns {
|
||||||
projectiles.push((s.physics_handle, s.ship.fire_guns()));
|
projectiles.push((s.physics_handle, s.ship.fire_guns()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.remove_from_world() {
|
||||||
|
to_remove.push(s.physics_handle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (s, p) in projectiles {
|
for (s, p) in projectiles {
|
||||||
self.add_projectiles(s, p);
|
self.add_projectiles(s, p);
|
||||||
|
|
Loading…
Reference in New Issue