use galactica_content::{Content, FactionHandle, ShipHandle}; use nalgebra::{point, vector, Rotation2, Vector2}; use rand::Rng; use rapier2d::{ dynamics::{RigidBody, RigidBodyHandle}, geometry::{Collider, ColliderHandle}, }; use crate::data::{ShipData, ShipPersonality, ShipState}; use super::{ super::{ParticleBuilder, PhysStepResources}, collapse::ShipCollapseSequence, }; /// A ship's controls #[derive(Debug, Clone)] pub struct ShipControls { /// True if turning left pub left: bool, /// True if turning right pub right: bool, /// True if foward thrust pub thrust: bool, /// True if firing guns pub guns: bool, } impl ShipControls { /// Create a new, empty ShipControls pub fn new() -> Self { ShipControls { left: false, right: false, thrust: false, guns: false, } } } /// A ship instance in the physics system #[derive(Debug, Clone)] pub struct PhysSimShip { /// This ship's physics handle pub rigid_body: RigidBodyHandle, /// This ship's collider pub collider: ColliderHandle, /// This ship's game data pub data: ShipData, /// This ship's controls pub(crate) controls: ShipControls, /// This ship's collapse sequence collapse_sequence: Option, } impl PhysSimShip { /// Make a new ship pub(crate) fn new( ct: &Content, handle: ShipHandle, personality: ShipPersonality, faction: FactionHandle, rigid_body: RigidBodyHandle, collider: ColliderHandle, ) -> Self { PhysSimShip { rigid_body, collider, data: ShipData::new(ct, handle, faction, personality), controls: ShipControls::new(), collapse_sequence: Some(ShipCollapseSequence::new()), } } /// Step this ship's state by t seconds pub fn step( &mut self, res: &mut PhysStepResources, rigid_body: &mut RigidBody, collider: &mut Collider, ) { self.data.step(res.t); match self.data.get_state() { ShipState::Collapsing { .. } => { // Borrow checker hack, so we may pass self.data // to the collapse sequence let mut seq = self.collapse_sequence.take().unwrap(); seq.step(res, &self.data, rigid_body, collider); self.collapse_sequence = Some(seq); } ShipState::Flying { .. } => { self.step_physics(res, rigid_body, collider); self.step_effects(res, rigid_body, collider); } ShipState::Landing { .. } => { self.step_physics(res, rigid_body, collider); } ShipState::UnLanding { .. } | ShipState::Dead | ShipState::Landed { .. } => {} } } /// Update this frame's physics fn step_physics( &mut self, res: &mut PhysStepResources, rigid_body: &mut RigidBody, _collider: &mut Collider, ) { let ship_rot = rigid_body.rotation(); let engine_force = ship_rot * (Vector2::new(1.0, 0.0) * res.t); if self.controls.thrust { rigid_body.apply_impulse( vector![engine_force.x, engine_force.y] * self.data.get_outfits().get_engine_thrust(), true, ); } if self.controls.right { rigid_body.apply_torque_impulse( self.data.get_outfits().get_steer_power() * -100.0 * res.t, true, ); } if self.controls.left { rigid_body.apply_torque_impulse( self.data.get_outfits().get_steer_power() * 100.0 * res.t, true, ); } } /// Spawn this frame's particles fn step_effects( &mut self, res: &mut PhysStepResources, rigid_body: &mut RigidBody, collider: &mut Collider, ) { let ship_content = res.ct.get_ship(self.data.get_content()); let ship_pos = rigid_body.translation(); let ship_rot = rigid_body.rotation(); let ship_ang = ship_rot.angle(); let mut rng = rand::thread_rng(); if self.data.get_hull() <= ship_content.damage.hull { for e in &ship_content.damage.effects { if rng.gen_range(0.0..=1.0) <= res.t / e.frequency { let effect = res.ct.get_effect(e.effect); let pos = if let Some(pos) = e.pos { Vector2::new(pos.x, pos.y) } else { // 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 = rng.gen_range(-1.0..=1.0) * ship_content.size / 2.0; x = 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::new(x, y) }; let pos = ship_pos + (Rotation2::new(ship_ang) * pos); let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]); res.particles.push(ParticleBuilder::from_content( effect, pos.into(), 0.0, velocity, Vector2::new(0.0, 0.0), )) } } } } } impl PhysSimShip { /// Get this ship's control state pub fn get_controls(&self) -> &ShipControls { &self.controls } }