From 132148fee3f9787c1f88491341ee222a678b44ef Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 20 Jan 2024 09:59:21 -0800 Subject: [PATCH] Added animation automata to ships and projectiles --- crates/content/src/animautomaton.rs | 8 +- crates/system/src/phys/objects/projectile.rs | 19 ++++- crates/system/src/phys/objects/ship.rs | 86 +++++++++++++++++++- 3 files changed, 104 insertions(+), 9 deletions(-) diff --git a/crates/content/src/animautomaton.rs b/crates/content/src/animautomaton.rs index 7ff9546..ed57167 100644 --- a/crates/content/src/animautomaton.rs +++ b/crates/content/src/animautomaton.rs @@ -2,7 +2,7 @@ use crate::{AnimSectionHandle, Content, SectionEdge, SpriteHandle}; /// A single frame's state #[derive(Debug, Clone)] -pub struct SpriteAnimationFrame { +pub struct AnimationState { /// The index of the texture we're fading from pub texture_a: u32, @@ -16,7 +16,7 @@ pub struct SpriteAnimationFrame { pub fade: f32, } -impl SpriteAnimationFrame { +impl AnimationState { /// Convenience method. /// Get texture index as an array pub fn texture_index(&self) -> [u32; 2] { @@ -224,8 +224,8 @@ impl AnimAutomaton { } /// Get the current frame of this animation - pub fn get_texture_idx(&self) -> SpriteAnimationFrame { - return SpriteAnimationFrame { + pub fn get_texture_idx(&self) -> AnimationState { + return AnimationState { texture_a: self.last_texture, texture_b: self.next_texture, fade: self.current_fade, diff --git a/crates/system/src/phys/objects/projectile.rs b/crates/system/src/phys/objects/projectile.rs index 68b8c85..2f4810f 100644 --- a/crates/system/src/phys/objects/projectile.rs +++ b/crates/system/src/phys/objects/projectile.rs @@ -1,4 +1,4 @@ -use galactica_content::{FactionHandle, Projectile}; +use galactica_content::{AnimAutomaton, AnimationState, Content, FactionHandle, Projectile}; use rand::Rng; use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; @@ -8,6 +8,9 @@ pub struct PhysProjectile { /// This projectile's game data pub content: Projectile, + /// This projectile's sprite animation state + anim: AnimAutomaton, + /// The remaining lifetime of this projectile, in seconds pub lifetime: f32, @@ -27,7 +30,8 @@ pub struct PhysProjectile { impl PhysProjectile { /// Create a new projectile pub fn new( - content: Projectile, // TODO: use a handle + ct: &Content, + content: Projectile, // TODO: use a handle? rigid_body: RigidBodyHandle, faction: FactionHandle, collider: ColliderHandle, @@ -36,6 +40,7 @@ impl PhysProjectile { let size_rng = content.size_rng; let lifetime = content.lifetime; PhysProjectile { + anim: AnimAutomaton::new(ct, content.sprite), rigid_body, collider, content, @@ -46,8 +51,9 @@ impl PhysProjectile { } /// Process this projectile's state after `t` seconds - pub fn tick(&mut self, t: f32) { + pub fn tick(&mut self, ct: &Content, t: f32) { self.lifetime -= t; + self.anim.step(ct, t) } /// Has this projectile expired? @@ -55,3 +61,10 @@ impl PhysProjectile { return self.lifetime < 0.0; } } + +impl PhysProjectile { + /// Get this projectile's animation state + pub fn get_anim_state(&self) -> AnimationState { + self.anim.get_texture_idx() + } +} diff --git a/crates/system/src/phys/objects/ship.rs b/crates/system/src/phys/objects/ship.rs index 0c83f9e..99418b4 100644 --- a/crates/system/src/phys/objects/ship.rs +++ b/crates/system/src/phys/objects/ship.rs @@ -1,4 +1,6 @@ -use galactica_content::{Content, FactionHandle, ShipHandle}; +use galactica_content::{ + AnimAutomaton, AnimationState, Content, EnginePoint, FactionHandle, OutfitHandle, ShipHandle, +}; use nalgebra::{point, vector, Rotation2, Vector2}; use rand::Rng; use rapier2d::{ @@ -51,7 +53,13 @@ pub struct PhysSimShip { pub collider: ColliderHandle, /// This ship's game data - pub data: ShipData, + pub(crate) data: ShipData, + + /// This ship's sprite animation state + anim: AnimAutomaton, + + /// Animation state for each of this ship's engines + engine_anim: Vec<(EnginePoint, AnimAutomaton)>, /// This ship's controls pub(crate) controls: ShipControls, @@ -72,9 +80,11 @@ impl PhysSimShip { ) -> Self { let ship_ct = ct.get_ship(handle); PhysSimShip { + anim: AnimAutomaton::new(ct, ship_ct.sprite), rigid_body, collider, data: ShipData::new(ct, handle, faction, personality), + engine_anim: Vec::new(), controls: ShipControls::new(), collapse_sequence: Some(ShipCollapseSequence::new(ship_ct.collapse.length)), } @@ -88,6 +98,18 @@ impl PhysSimShip { collider: &mut Collider, ) { self.data.step(res.t); + self.anim.step(res.ct, res.t); + + if self.controls.thrust { + for (_, e) in &mut self.engine_anim { + e.step(res.ct, res.t); + } + } else { + for (_, e) in &mut self.engine_anim { + e.reset(res.ct); + } + } + match self.data.get_state() { ShipState::Collapsing { .. } => { // Borrow checker hack, so we may pass self.data @@ -197,9 +219,69 @@ impl PhysSimShip { } } +/// Public mutable +impl PhysSimShip { + /// Re-create this ship's engine flare animations + /// Should be called whenever we change outfits + fn update_flares(&mut self, ct: &Content) { + // TODO: better way to pick flare sprite + let mut flare = None; + for (h, _) in self.data.get_outfits().iter_outfits() { + let c = ct.get_outfit(*h); + if c.engine_flare_sprite.is_some() { + flare = c.engine_flare_sprite; + break; + } + } + + if flare.is_none() { + self.engine_anim = Vec::new(); + return; + } + + let flare = flare.unwrap(); + self.engine_anim = ct + .get_ship(self.data.get_content()) + .engines + .iter() + .map(|e| (e.clone(), AnimAutomaton::new(ct, flare))) + .collect(); + } + + /// Add one outfit to this ship + pub fn add_outfit(&mut self, ct: &Content, o: OutfitHandle) { + self.data.add_outfit(ct.get_outfit(o)); + self.update_flares(ct); + } + + /// Add many outfits to this ship + pub fn add_outfits(&mut self, ct: &Content, outfits: impl IntoIterator) { + for o in outfits { + self.data.add_outfit(ct.get_outfit(o)); + } + self.update_flares(ct); + } +} + +/// Public immutable impl PhysSimShip { /// Get this ship's control state pub fn get_controls(&self) -> &ShipControls { &self.controls } + + /// Get this ship's engine animations + pub fn iter_engine_anim(&self) -> impl Iterator { + self.engine_anim.iter() + } + + /// Get this ship's animation state + pub fn get_anim_state(&self) -> AnimationState { + self.anim.get_texture_idx() + } + + /// Get this ship's game data struct + pub fn get_data(&self) -> &ShipData { + &self.data + } }