Added projectile parameters
parent
67f19e91b6
commit
e344c344ad
1
TODO.md
1
TODO.md
|
@ -1,5 +1,4 @@
|
||||||
## Specific Jobs
|
## Specific Jobs
|
||||||
- Define projectile colliders & particles
|
|
||||||
- Particle variation
|
- Particle variation
|
||||||
- Animated sprites
|
- Animated sprites
|
||||||
- UI: health, shield, fuel, heat, energy bars
|
- UI: health, shield, fuel, heat, energy bars
|
||||||
|
|
|
@ -4,10 +4,10 @@ space.weapon = 10
|
||||||
|
|
||||||
# Average delay between shots
|
# Average delay between shots
|
||||||
rate = 0.2
|
rate = 0.2
|
||||||
# Random rate variation (+- this in both directions)
|
# Random rate variation (each cooldown is +- this)
|
||||||
rate_rng = 0.1
|
rate_rng = 0.1
|
||||||
|
|
||||||
|
# TODO: apply force on fire
|
||||||
projectile.sprite_texture = "projectile::blaster"
|
projectile.sprite_texture = "projectile::blaster"
|
||||||
# Height of projectile in game units
|
# Height of projectile in game units
|
||||||
projectile.size = 6
|
projectile.size = 6
|
||||||
|
@ -24,3 +24,18 @@ projectile.damage = 10.0
|
||||||
# If this is 10, projectiles will form a cone of 10 degrees
|
# If this is 10, projectiles will form a cone of 10 degrees
|
||||||
# ( 5 degrees on each side )
|
# ( 5 degrees on each side )
|
||||||
projectile.angle_rng = 2
|
projectile.angle_rng = 2
|
||||||
|
# How much force this projectile will apply to an object it hits
|
||||||
|
projectile.force = 0.0
|
||||||
|
|
||||||
|
projectile.collider.ball.radius = 2.0
|
||||||
|
|
||||||
|
projectile.impact.texture = "particle::blaster"
|
||||||
|
projectile.impact.lifetime = "inherit"
|
||||||
|
projectile.impact.inherit_velocity = "target"
|
||||||
|
projectile.impact.size = 3.0
|
||||||
|
|
||||||
|
|
||||||
|
projectile.expire.texture = "particle::blaster"
|
||||||
|
projectile.expire.lifetime = "inherit"
|
||||||
|
projectile.expire.inherit_velocity = "projectile"
|
||||||
|
projectile.expire.size = 3.0
|
||||||
|
|
|
@ -17,7 +17,7 @@ file = "planet/luna.png"
|
||||||
file = "projectile/blaster.png"
|
file = "projectile/blaster.png"
|
||||||
|
|
||||||
[texture."ship::gypsum"]
|
[texture."ship::gypsum"]
|
||||||
file = "ship/gypsum2.png"
|
file = "ship/gypsum.png"
|
||||||
|
|
||||||
[texture."ui::radar"]
|
[texture."ui::radar"]
|
||||||
file = "ui/radar.png"
|
file = "ui/radar.png"
|
||||||
|
|
|
@ -19,8 +19,8 @@ use walkdir::WalkDir;
|
||||||
|
|
||||||
pub use handle::{FactionHandle, GunHandle, OutfitHandle, ShipHandle, SystemHandle, TextureHandle};
|
pub use handle::{FactionHandle, GunHandle, OutfitHandle, ShipHandle, SystemHandle, TextureHandle};
|
||||||
pub use part::{
|
pub use part::{
|
||||||
EnginePoint, Faction, Gun, GunPoint, Outfit, OutfitSpace, Projectile, Relationship, RepeatMode,
|
EnginePoint, Faction, Gun, GunPoint, ImpactInheritVelocity, Outfit, OutfitSpace, Projectile,
|
||||||
Ship, System, Texture,
|
ProjectileCollider, ProjectileParticle, Relationship, RepeatMode, Ship, System, Texture,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod syntax {
|
mod syntax {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
|
||||||
use cgmath::Deg;
|
use cgmath::Deg;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{handle::TextureHandle, Content};
|
use crate::{handle::TextureHandle, Content};
|
||||||
|
|
||||||
|
@ -32,7 +32,58 @@ pub(crate) mod syntax {
|
||||||
pub lifetime_rng: f32,
|
pub lifetime_rng: f32,
|
||||||
pub damage: f32,
|
pub damage: f32,
|
||||||
pub angle_rng: f32,
|
pub angle_rng: f32,
|
||||||
|
pub impact: Option<ProjectileParticle>,
|
||||||
|
pub expire: Option<ProjectileParticle>,
|
||||||
|
pub collider: super::ProjectileCollider,
|
||||||
|
pub force: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ProjectileParticle {
|
||||||
|
pub texture: String,
|
||||||
|
pub lifetime: ParticleLifetime,
|
||||||
|
pub inherit_velocity: super::ImpactInheritVelocity,
|
||||||
|
pub size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ParticleLifetime {
|
||||||
|
Inherit(String),
|
||||||
|
Seconds(f32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines a projectile's collider
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub enum ProjectileCollider {
|
||||||
|
/// A ball collider
|
||||||
|
#[serde(rename = "ball")]
|
||||||
|
Ball(BallCollider),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct BallCollider {
|
||||||
|
pub radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How we should set an impact particle's velocity
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub enum ImpactInheritVelocity {
|
||||||
|
/// Don't inherit any velocity.
|
||||||
|
/// This impact particle will be still.
|
||||||
|
#[serde(rename = "don't")]
|
||||||
|
Dont,
|
||||||
|
|
||||||
|
/// Inherit target velocity.
|
||||||
|
/// This impact particle will stick to the object it hits.
|
||||||
|
#[serde(rename = "target")]
|
||||||
|
Target,
|
||||||
|
|
||||||
|
/// Inherit projectile velocity.
|
||||||
|
/// This impact particle will continue on its projectile's path.
|
||||||
|
#[serde(rename = "projectile")]
|
||||||
|
Projectile,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a gun outfit.
|
/// Represents a gun outfit.
|
||||||
|
@ -81,12 +132,74 @@ pub struct Projectile {
|
||||||
/// The damage this projectile does
|
/// The damage this projectile does
|
||||||
pub damage: f32,
|
pub damage: f32,
|
||||||
|
|
||||||
|
/// The force this projectile applies
|
||||||
|
pub force: f32,
|
||||||
|
|
||||||
/// The angle variation of this projectile.
|
/// The angle variation of this projectile.
|
||||||
/// Projectiles can be off center up to
|
/// Projectiles can be off center up to
|
||||||
/// `spread / 2` degrees in both directions.
|
/// `spread / 2` degrees in both directions.
|
||||||
///
|
///
|
||||||
/// (Forming a "fire cone" of `spread` degrees)
|
/// (Forming a "fire cone" of `spread` degrees)
|
||||||
pub angle_rng: Deg<f32>,
|
pub angle_rng: Deg<f32>,
|
||||||
|
|
||||||
|
/// The particle this projectile will spawn when it hits something
|
||||||
|
pub impact_particle: Option<ProjectileParticle>,
|
||||||
|
|
||||||
|
/// The particle this projectile will spawn when it expires
|
||||||
|
pub expire_particle: Option<ProjectileParticle>,
|
||||||
|
|
||||||
|
/// Collider parameters for this projectile
|
||||||
|
pub collider: ProjectileCollider,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The particle a projectile will spawn when it hits something
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ProjectileParticle {
|
||||||
|
/// The texture to use for this particle.
|
||||||
|
/// This is most likely animated.
|
||||||
|
pub texture: TextureHandle,
|
||||||
|
|
||||||
|
/// How many seconds this particle should live
|
||||||
|
pub lifetime: f32,
|
||||||
|
|
||||||
|
/// How we should set this particle's velocity
|
||||||
|
pub inherit_velocity: ImpactInheritVelocity,
|
||||||
|
|
||||||
|
/// The height of this particle, in game units.
|
||||||
|
pub size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_projectile_particle(
|
||||||
|
ct: &Content,
|
||||||
|
p: Option<syntax::ProjectileParticle>,
|
||||||
|
) -> Result<Option<ProjectileParticle>> {
|
||||||
|
if let Some(impact) = p {
|
||||||
|
let impact_texture = match ct.texture_index.get(&impact.texture) {
|
||||||
|
None => bail!("impact texture `{}` doesn't exist", impact.texture),
|
||||||
|
Some(t) => *t,
|
||||||
|
};
|
||||||
|
|
||||||
|
let impact_lifetime = match impact.lifetime {
|
||||||
|
syntax::ParticleLifetime::Seconds(s) => s,
|
||||||
|
syntax::ParticleLifetime::Inherit(s) => {
|
||||||
|
if s == "inherit" {
|
||||||
|
let t = ct.get_texture(impact_texture);
|
||||||
|
t.fps * t.frames.len() as f32
|
||||||
|
} else {
|
||||||
|
bail!("bad impact lifetime, must be float or \"inherit\"",)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(ProjectileParticle {
|
||||||
|
texture: impact_texture,
|
||||||
|
lifetime: impact_lifetime,
|
||||||
|
inherit_velocity: impact.inherit_velocity,
|
||||||
|
size: impact.size,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Build for Gun {
|
impl crate::Build for Gun {
|
||||||
|
@ -94,22 +207,29 @@ impl crate::Build for Gun {
|
||||||
|
|
||||||
fn build(gun: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
fn build(gun: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
||||||
for (gun_name, gun) in gun {
|
for (gun_name, gun) in gun {
|
||||||
let th = match ct.texture_index.get(&gun.projectile.sprite_texture) {
|
let projectile_texture = match ct.texture_index.get(&gun.projectile.sprite_texture) {
|
||||||
None => bail!(
|
None => bail!(
|
||||||
"In gun `{}`: texture `{}` doesn't exist",
|
"In gun `{}`: projectile texture `{}` doesn't exist",
|
||||||
gun_name,
|
gun_name,
|
||||||
gun.projectile.sprite_texture
|
gun.projectile.sprite_texture
|
||||||
),
|
),
|
||||||
Some(t) => *t,
|
Some(t) => *t,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let impact_particle = parse_projectile_particle(ct, gun.projectile.impact)
|
||||||
|
.with_context(|| format!("In gun `{}`", gun_name))?;
|
||||||
|
|
||||||
|
let expire_particle = parse_projectile_particle(ct, gun.projectile.expire)
|
||||||
|
.with_context(|| format!("In gun `{}`", gun_name))?;
|
||||||
|
|
||||||
ct.guns.push(Self {
|
ct.guns.push(Self {
|
||||||
name: gun_name,
|
name: gun_name,
|
||||||
space: gun.space.into(),
|
space: gun.space.into(),
|
||||||
rate: gun.rate,
|
rate: gun.rate,
|
||||||
rate_rng: gun.rate_rng,
|
rate_rng: gun.rate_rng,
|
||||||
projectile: Projectile {
|
projectile: Projectile {
|
||||||
sprite_texture: th,
|
force: gun.projectile.force,
|
||||||
|
sprite_texture: projectile_texture,
|
||||||
size: gun.projectile.size,
|
size: gun.projectile.size,
|
||||||
size_rng: gun.projectile.size_rng,
|
size_rng: gun.projectile.size_rng,
|
||||||
speed: gun.projectile.speed,
|
speed: gun.projectile.speed,
|
||||||
|
@ -118,6 +238,9 @@ impl crate::Build for Gun {
|
||||||
lifetime_rng: gun.projectile.lifetime_rng,
|
lifetime_rng: gun.projectile.lifetime_rng,
|
||||||
damage: gun.projectile.damage,
|
damage: gun.projectile.damage,
|
||||||
angle_rng: Deg(gun.projectile.angle_rng),
|
angle_rng: Deg(gun.projectile.angle_rng),
|
||||||
|
impact_particle,
|
||||||
|
expire_particle,
|
||||||
|
collider: gun.projectile.collider,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub mod system;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
|
|
||||||
pub use faction::{Faction, Relationship};
|
pub use faction::{Faction, Relationship};
|
||||||
pub use gun::{Gun, Projectile};
|
pub use gun::{Gun, ImpactInheritVelocity, Projectile, ProjectileCollider, ProjectileParticle};
|
||||||
pub use outfit::Outfit;
|
pub use outfit::Outfit;
|
||||||
pub use shared::OutfitSpace;
|
pub use shared::OutfitSpace;
|
||||||
pub use ship::{EnginePoint, GunPoint, Ship};
|
pub use ship::{EnginePoint, GunPoint, Ship};
|
||||||
|
|
|
@ -168,7 +168,7 @@ impl Game {
|
||||||
.get_ship_mut(&self.player)
|
.get_ship_mut(&self.player)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.physics_handle;
|
.physics_handle;
|
||||||
let r = self.world.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
|
let r = self.world.get_rigid_body(r.0).unwrap(); // TODO: r.0 shouldn't be public
|
||||||
let ship_pos = util::rigidbody_position(r);
|
let ship_pos = util::rigidbody_position(r);
|
||||||
self.camera.pos = ship_pos;
|
self.camera.pos = ship_pos;
|
||||||
self.last_update = Instant::now();
|
self.last_update = Instant::now();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2};
|
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2};
|
||||||
use crossbeam::channel::Receiver;
|
use crossbeam::channel::Receiver;
|
||||||
use nalgebra::vector;
|
use nalgebra::{point, vector};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
||||||
|
@ -27,19 +27,28 @@ pub struct World {
|
||||||
|
|
||||||
// Private methods
|
// Private methods
|
||||||
impl<'a> World {
|
impl<'a> World {
|
||||||
fn remove_projectile(&mut self, c: ColliderHandle) {
|
fn remove_projectile(
|
||||||
|
&mut self,
|
||||||
|
c: ColliderHandle,
|
||||||
|
) -> Option<(RigidBody, ProjectileWorldObject)> {
|
||||||
let p = match self.projectiles.remove(&c) {
|
let p = match self.projectiles.remove(&c) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return,
|
None => return None,
|
||||||
};
|
};
|
||||||
self.wrapper.rigid_body_set.remove(
|
let r = self
|
||||||
p.rigid_body,
|
.wrapper
|
||||||
&mut self.wrapper.im,
|
.rigid_body_set
|
||||||
&mut self.wrapper.collider_set,
|
.remove(
|
||||||
&mut self.wrapper.ij,
|
p.rigid_body,
|
||||||
&mut self.wrapper.mj,
|
&mut self.wrapper.im,
|
||||||
true,
|
&mut self.wrapper.collider_set,
|
||||||
);
|
&mut self.wrapper.ij,
|
||||||
|
&mut self.wrapper.mj,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
return Some((r, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_ship(&mut self, h: ShipPhysicsHandle) {
|
fn remove_ship(&mut self, h: ShipPhysicsHandle) {
|
||||||
|
@ -92,10 +101,12 @@ impl<'a> World {
|
||||||
.linvel(vector![vel.x, vel.y])
|
.linvel(vector![vel.x, vel.y])
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let collider = ColliderBuilder::ball(1.0)
|
let collider = match &projectile.content.collider {
|
||||||
.sensor(true)
|
content::ProjectileCollider::Ball(b) => ColliderBuilder::ball(b.radius)
|
||||||
.active_events(ActiveEvents::COLLISION_EVENTS)
|
.sensor(true)
|
||||||
.build();
|
.active_events(ActiveEvents::COLLISION_EVENTS)
|
||||||
|
.build(),
|
||||||
|
};
|
||||||
|
|
||||||
let rigid_body = self.wrapper.rigid_body_set.insert(rigid_body);
|
let rigid_body = self.wrapper.rigid_body_set.insert(rigid_body);
|
||||||
let collider = self.wrapper.collider_set.insert_with_parent(
|
let collider = self.wrapper.collider_set.insert_with_parent(
|
||||||
|
@ -114,6 +125,83 @@ impl<'a> World {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collide_projectile_ship(
|
||||||
|
&mut self,
|
||||||
|
ct: &content::Content,
|
||||||
|
particles: &mut Vec<ParticleBuilder>,
|
||||||
|
projectile_h: ColliderHandle,
|
||||||
|
ship_h: ColliderHandle,
|
||||||
|
) {
|
||||||
|
let projectile = self.projectiles.get(&projectile_h);
|
||||||
|
let ship = self.ships.get_mut(&ship_h);
|
||||||
|
if projectile.is_none() || ship.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let projectile = projectile.unwrap();
|
||||||
|
let ship = ship.unwrap();
|
||||||
|
|
||||||
|
let hit = ship
|
||||||
|
.ship
|
||||||
|
.handle_projectile_collision(ct, &projectile.projectile);
|
||||||
|
let s = ship.physics_handle;
|
||||||
|
if hit {
|
||||||
|
let pr = self
|
||||||
|
.wrapper
|
||||||
|
.rigid_body_set
|
||||||
|
.get(projectile.rigid_body)
|
||||||
|
.unwrap();
|
||||||
|
let v = util::rigidbody_velocity(pr).normalize() * projectile.projectile.content.force;
|
||||||
|
let pos = util::rigidbody_position(pr);
|
||||||
|
let _ = pr;
|
||||||
|
|
||||||
|
let r = self.wrapper.rigid_body_set.get_mut(s.0).unwrap();
|
||||||
|
r.apply_impulse_at_point(vector![v.x, v.y], point![pos.x, pos.y], true);
|
||||||
|
|
||||||
|
// Borrow again, we can only have one at a time
|
||||||
|
let pr = self
|
||||||
|
.wrapper
|
||||||
|
.rigid_body_set
|
||||||
|
.get(projectile.rigid_body)
|
||||||
|
.unwrap();
|
||||||
|
let pos = util::rigidbody_position(pr);
|
||||||
|
let angle: Deg<f32> = util::rigidbody_rotation(pr)
|
||||||
|
.angle(Vector2 { x: 1.0, y: 0.0 })
|
||||||
|
.into();
|
||||||
|
|
||||||
|
match &projectile.projectile.content.impact_particle {
|
||||||
|
None => {}
|
||||||
|
Some(x) => {
|
||||||
|
let velocity = match x.inherit_velocity {
|
||||||
|
content::ImpactInheritVelocity::Dont => Vector2 { x: 0.0, y: 0.0 },
|
||||||
|
content::ImpactInheritVelocity::Projectile => util::rigidbody_velocity(pr),
|
||||||
|
content::ImpactInheritVelocity::Target => {
|
||||||
|
// Match target ship velocity.
|
||||||
|
// Particles will fly off if the ship is spinning fast, but I
|
||||||
|
// haven't found a good way to fix that.
|
||||||
|
let (_, sr) = self.get_ship_body(s).unwrap();
|
||||||
|
let velocity =
|
||||||
|
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
|
||||||
|
Vector2 {
|
||||||
|
x: velocity.x,
|
||||||
|
y: velocity.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
particles.push(ParticleBuilder {
|
||||||
|
texture: x.texture,
|
||||||
|
pos: Point2 { x: pos.x, y: pos.y },
|
||||||
|
velocity,
|
||||||
|
angle: -angle,
|
||||||
|
lifetime: x.lifetime,
|
||||||
|
size: x.size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.remove_projectile(projectile.collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public methods
|
// Public methods
|
||||||
|
@ -199,76 +287,71 @@ impl<'a> World {
|
||||||
// Handle collision events
|
// Handle collision events
|
||||||
while let Ok(event) = &self.collision_queue.try_recv() {
|
while let Ok(event) = &self.collision_queue.try_recv() {
|
||||||
if event.started() {
|
if event.started() {
|
||||||
let a = &event.collider1();
|
let a = event.collider1();
|
||||||
let b = &event.collider2();
|
let b = event.collider2();
|
||||||
|
|
||||||
// If projectiles are part of this collision, make sure
|
// If projectiles are part of this collision, make sure
|
||||||
// `a` is one of them.
|
// `a` is one of them.
|
||||||
let (a, b) = if self.projectiles.contains_key(b) {
|
let (a, b) = if self.projectiles.contains_key(&b) {
|
||||||
(b, a)
|
(b, a)
|
||||||
} else {
|
} else {
|
||||||
(a, b)
|
(a, b)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(p) = self.projectiles.get(a) {
|
let p = self.projectiles.get(&a);
|
||||||
let hit = if let Some(s) = self.ships.get_mut(b) {
|
let s = self.ships.get_mut(&b);
|
||||||
s.ship.handle_projectile_collision(ct, &p.projectile)
|
if p.is_none() || s.is_none() {
|
||||||
} else {
|
continue;
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stupid re-borrow trick.
|
|
||||||
// We can't have s be mutable inside this block.
|
|
||||||
if let Some(s) = self.ships.get(b) {
|
|
||||||
if hit {
|
|
||||||
let pr = self.get_rigid_body(p.rigid_body);
|
|
||||||
let pos = util::rigidbody_position(pr);
|
|
||||||
let angle: Deg<f32> = util::rigidbody_rotation(pr)
|
|
||||||
.angle(Vector2 { x: 1.0, y: 0.0 })
|
|
||||||
.into();
|
|
||||||
|
|
||||||
// Match target ship velocity, so impact particles are "sticky".
|
|
||||||
// Particles will fly off if the ship is spinning fast, but I
|
|
||||||
// haven't found a good way to fix that.
|
|
||||||
let (_, sr) = self.get_ship_body(s.physics_handle).unwrap();
|
|
||||||
let velocity =
|
|
||||||
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
|
|
||||||
let velocity = Vector2 {
|
|
||||||
x: velocity.x,
|
|
||||||
y: velocity.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
particles.push(ParticleBuilder {
|
|
||||||
texture: ct.get_texture_handle("particle::blaster"),
|
|
||||||
pos: Point2 { x: pos.x, y: pos.y },
|
|
||||||
velocity,
|
|
||||||
angle: -angle,
|
|
||||||
lifetime: 0.15,
|
|
||||||
size: 5.0,
|
|
||||||
});
|
|
||||||
self.remove_projectile(*a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.collide_projectile_ship(ct, particles, a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete projectiles
|
// Delete projectiles
|
||||||
let mut to_remove = Vec::new();
|
let mut to_remove = Vec::new();
|
||||||
for (_, p) in &mut self.projectiles {
|
for (c, p) in &mut self.projectiles {
|
||||||
p.projectile.tick(t);
|
p.projectile.tick(t);
|
||||||
if p.projectile.is_expired() {
|
if p.projectile.is_expired() {
|
||||||
to_remove.push(p.collider);
|
to_remove.push(*c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i in to_remove {
|
for c in to_remove {
|
||||||
self.remove_projectile(i);
|
let (pr, p) = self.remove_projectile(c).unwrap();
|
||||||
|
|
||||||
|
match &p.projectile.content.expire_particle {
|
||||||
|
None => {}
|
||||||
|
Some(x) => {
|
||||||
|
let pos = util::rigidbody_position(&pr);
|
||||||
|
let angle: Deg<f32> = util::rigidbody_rotation(&pr)
|
||||||
|
.angle(Vector2 { x: 1.0, y: 0.0 })
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let velocity = match x.inherit_velocity {
|
||||||
|
content::ImpactInheritVelocity::Target
|
||||||
|
| content::ImpactInheritVelocity::Dont => Vector2 { x: 0.0, y: 0.0 },
|
||||||
|
content::ImpactInheritVelocity::Projectile => util::rigidbody_velocity(&pr),
|
||||||
|
};
|
||||||
|
particles.push(ParticleBuilder {
|
||||||
|
texture: x.texture,
|
||||||
|
pos: Point2 { x: pos.x, y: pos.y },
|
||||||
|
velocity,
|
||||||
|
angle: -angle,
|
||||||
|
lifetime: x.lifetime,
|
||||||
|
size: x.size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a rigid body from a handle
|
/// Get a rigid body from a handle
|
||||||
pub fn get_rigid_body(&self, r: RigidBodyHandle) -> &RigidBody {
|
pub fn get_rigid_body(&self, r: RigidBodyHandle) -> Option<&RigidBody> {
|
||||||
&self.wrapper.rigid_body_set[r]
|
self.wrapper.rigid_body_set.get(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a rigid body from a handle
|
||||||
|
pub fn get_rigid_body_mut(&mut self, r: RigidBodyHandle) -> Option<&mut RigidBody> {
|
||||||
|
self.wrapper.rigid_body_set.get_mut(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a ship from a handle
|
/// Get a ship from a handle
|
||||||
|
|
Loading…
Reference in New Issue