Compare commits

...

2 Commits

Author SHA1 Message Date
Mark 7854245a4b
Updated TODO 2024-01-09 18:02:11 -08:00
Mark 749cac6442
Repaired collapse sequences 2024-01-09 18:00:02 -08:00
6 changed files with 119 additions and 24 deletions

View File

@ -1,4 +1,12 @@
## Specific Jobs
- Check for handle leaks
- Rename and crtl-f comments
- gameobject and world
- behavior and personality
- ship (content) / ship (data) / ship (world)
- Fix ship controllers
- Don't allocate each frame
- UI: text arranger
- Sound system
- Ship death debris
@ -90,7 +98,6 @@
- Clear all `// TODO:` comments littered in the source
- CLI options (debug, save location, content location, check content)
- Config file and compile options, remove all those consts.
- Sprite optimization: do we need to allocate a new `Vec` every frame? Probably not.
- Better error when run outside of directory
- Documentation site & front page
- Random animation delay/fps?

View File

@ -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);
}
}
}

View File

@ -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(),
}]),
);

View File

@ -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,

View File

@ -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<dyn ShipBehavior>,
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
}
}

View File

@ -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 => {
// 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;
}