From 749cac644235af5e222d3160384fe06d75865734 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 9 Jan 2024 18:00:02 -0800 Subject: [PATCH] Repaired collapse sequences --- crates/gameobject/src/gamedata.rs | 19 ++++++++++++ crates/render/src/gpustate/hud.rs | 17 ++++++++--- crates/render/src/gpustate/world.rs | 11 +++++-- crates/world/src/objects/ship.rs | 47 +++++++++++++++++++++++++++-- crates/world/src/world.rs | 40 +++++++++++++++--------- 5 files changed, 111 insertions(+), 23 deletions(-) diff --git a/crates/gameobject/src/gamedata.rs b/crates/gameobject/src/gamedata.rs index acc595b..fa7503f 100644 --- a/crates/gameobject/src/gamedata.rs +++ b/crates/gameobject/src/gamedata.rs @@ -57,8 +57,27 @@ impl GameData { } pub fn step(&mut self, t: f32) { + // TODO: don't allocate on step, need a better + // way to satisfy the borrow checker. + // Same needs to be done in the `world` crate. + let mut to_remove = Vec::new(); for (_, s) in &mut self.ships { s.step(t); + + if s.is_dead() { + to_remove.push(s.get_handle()); + } + } + + // Remove dead ships + // No fancy animation here, that's handled by the physics system. + for i in to_remove { + let system = self.ship_system_table.remove(&i).unwrap(); + self.system_ship_table + .get_mut(&system) + .unwrap() + .retain(|x| *x != i); + self.ships.remove(&i); } } } diff --git a/crates/render/src/gpustate/hud.rs b/crates/render/src/gpustate/hud.rs index c101039..69c9077 100644 --- a/crates/render/src/gpustate/hud.rs +++ b/crates/render/src/gpustate/hud.rs @@ -93,7 +93,18 @@ impl GPUState { // Draw ships for (s, r) in state.world.iter_ship_body() { - let data = state.data.get_ship(s.data_handle).unwrap(); + // This will be None if this ship is dead. + // Stays around while the physics system runs a collapse sequence + let color = match state.data.get_ship(s.data_handle) { + None => { + // TODO: configurable + [0.2, 0.2, 0.2, 1.0] + } + Some(data) => { + let c = state.content.get_faction(data.get_faction()).color; + [c[0], c[1], c[2], 1.0] + } + }; let ship = state.content.get_ship(s.data_handle.content_handle()); let size = (ship.size * ship.sprite.aspect) * ship_scale; let p = util::rigidbody_position(r); @@ -105,8 +116,6 @@ impl GPUState { continue; } let angle = util::rigidbody_rotation(r).angle(Vector2 { x: 0.0, y: 1.0 }); - let f = state.content.get_faction(data.get_faction()).color; - let f = [f[0], f[1], f[2], 1.0]; let position = Point2 { x: radar_size / 2.0 + 10.0, @@ -130,7 +139,7 @@ impl GPUState { position: position.into(), angle: -angle.0, // TODO: consistent angles size, - color: f.into(), + color, sprite_index: ship_sprite.get_index(), }]), ); diff --git a/crates/render/src/gpustate/world.rs b/crates/render/src/gpustate/world.rs index 70e84f7..de99ea2 100644 --- a/crates/render/src/gpustate/world.rs +++ b/crates/render/src/gpustate/world.rs @@ -22,7 +22,6 @@ impl GPUState { s: &ShipWorldObject, ) { let r = state.world.get_rigid_body(s.rigid_body).unwrap(); - let ship = state.data.get_ship(s.data_handle).unwrap(); let ship_pos = util::rigidbody_position(&r); let ship_rot = util::rigidbody_rotation(r); let ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix! @@ -85,8 +84,16 @@ impl GPUState { ); self.vertex_buffers.object_counter += 1; + // This will be None if this ship is dead. + // (physics object stays around to complete the death animation) + // If that is the case, we're done, no flares to draw anyway! + let ship = match state.data.get_ship(s.data_handle) { + None => return, + Some(s) => s, + }; + let flare = ship.get_outfits().get_flare_sprite(state.content); - if s.get_controls().thrust && flare.is_some() && !ship.is_dead() { + if s.get_controls().thrust && flare.is_some() { for engine_point in &ship_cnt.engines { self.queue.write_buffer( &self.global_uniform.object_buffer, diff --git a/crates/world/src/objects/ship.rs b/crates/world/src/objects/ship.rs index c7a5438..044bbd2 100644 --- a/crates/world/src/objects/ship.rs +++ b/crates/world/src/objects/ship.rs @@ -1,4 +1,5 @@ use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero}; +use content::{FactionHandle, ShipHandle}; use nalgebra::{point, vector}; use object::GameShipHandle; @@ -29,6 +30,7 @@ pub struct ShipControls { } impl ShipControls { + /// Create a new, empty ShipControls pub fn new() -> Self { ShipControls { left: false, @@ -81,12 +83,11 @@ impl ShipCollapseSequence { fn step( &mut self, res: &mut StepResources, - ship: GameShipHandle, + ship_handle: ShipHandle, rigid_body: &mut RigidBody, collider: &mut Collider, ) { - let h = ship.content_handle(); - let ship_content = res.ct.get_ship(h); + let ship_content = res.ct.get_ship(ship_handle); 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 }); @@ -198,6 +199,12 @@ pub struct ShipWorldObject { /// This ship's collapse sequence collapse_sequence: ShipCollapseSequence, + + /// This ship's faction. + /// This is technically redundant, faction is also stored in + /// game data, but that's destroyed once the ship dies. + /// We need the faction for the collapse sequence! + faction: FactionHandle, } impl ShipWorldObject { @@ -206,6 +213,7 @@ impl ShipWorldObject { ct: &content::Content, data_handle: GameShipHandle, behavior: Box, + faction: FactionHandle, rigid_body: RigidBodyHandle, collider: ColliderHandle, ) -> Self { @@ -216,6 +224,7 @@ impl ShipWorldObject { data_handle, behavior, controls: ShipControls::new(), + faction, collapse_sequence: ShipCollapseSequence::new(ship_content.collapse.length), } } @@ -225,12 +234,39 @@ impl ShipWorldObject { self.controls = self.behavior.update_controls(res); } + /// If this is true, remove this ship from the physics system. + pub fn should_be_removed(&self) -> bool { + self.collapse_sequence.is_done() + } + /// Step this ship's state by t seconds pub fn step( &mut self, res: &mut StepResources, rigid_body: &mut RigidBody, collider: &mut Collider, + ) { + let ship_data = res.dt.get_ship(self.data_handle); + if ship_data.is_none() { + // If ship data is none, it has been removed because the ship has been destroyed. + // play collapse sequence. + self.collapse_sequence.step( + res, + self.data_handle.content_handle(), + rigid_body, + collider, + ); + } else { + return self.step_live(res, rigid_body, collider); + } + } + + /// Step this ship's state by t seconds (called when alive) + fn step_live( + &mut self, + res: &mut StepResources, + rigid_body: &mut RigidBody, + collider: &mut Collider, ) { let ship = res.dt.get_ship(self.data_handle).unwrap(); let ship_content = res.ct.get_ship(self.data_handle.content_handle()); @@ -306,4 +342,9 @@ impl ShipWorldObject { pub fn get_controls(&self) -> &ShipControls { &self.controls } + + /// Get this ship's faction + pub fn get_faction(&self) -> FactionHandle { + self.faction + } } diff --git a/crates/world/src/world.rs b/crates/world/src/world.rs index c58a467..797d6d7 100644 --- a/crates/world/src/world.rs +++ b/crates/world/src/world.rs @@ -61,7 +61,15 @@ impl<'a> World { return Some((r, p)); } - fn remove_ship(&mut self, s: &ShipWorldObject) { + fn remove_ship(&mut self, s: ColliderHandle) { + let s = match self.collider_ship_table.get(&s) { + None => return, + Some(s) => match self.ships.get(s) { + None => return, + Some(s) => s, + }, + }; + self.wrapper.rigid_body_set.remove( s.rigid_body, &mut self.wrapper.im, @@ -90,13 +98,14 @@ impl<'a> World { let projectile = projectile.unwrap(); let ship = ship.unwrap(); - let ship_d = res.dt.get_ship_mut(ship.data_handle).unwrap(); - let f = res.ct.get_faction(projectile.faction); - let r = f.relationships.get(&ship_d.get_faction()).unwrap(); + let r = f.relationships.get(&ship.get_faction()).unwrap(); let destory_projectile = match r { content::Relationship::Hostile => { - ship_d.apply_damage(projectile.content.damage); + // We only apply damage if the target ship is alive + if let Some(ship_d) = res.dt.get_ship_mut(ship.data_handle) { + ship_d.apply_damage(projectile.content.damage); + } true } _ => false, @@ -224,6 +233,7 @@ impl World { ct, ship.get_handle(), Box::new(behavior::Null::new()), + ship.get_faction(), r, c, ), @@ -238,10 +248,10 @@ impl World { let mut projectiles = Vec::new(); let mut to_remove = Vec::new(); for (_, ship_object) in &mut self.ships { - //if s.remove_from_world() { - // to_remove.push(s.physics_handle); - // continue; - //} + if ship_object.should_be_removed() { + to_remove.push(ship_object.collider); + continue; + } let rigid_body = &mut self.wrapper.rigid_body_set[ship_object.rigid_body]; let collider = &mut self.wrapper.collider_set[ship_object.collider]; @@ -279,8 +289,8 @@ impl World { } // Remove ships that don't exist - for s in to_remove { - self.remove_ship(s); + for c in to_remove { + self.remove_ship(c); } // Create projectiles @@ -367,9 +377,11 @@ impl World { }; let p = self.projectiles.get(&a); - let s = self - .ships - .get_mut(self.collider_ship_table.get(&b).unwrap()); + let s = self.ships.get_mut(match self.collider_ship_table.get(&b) { + // If none, this ship is dead. + Some(x) => x, + None => continue, + }); if p.is_none() || s.is_none() { continue; }