191 lines
4.4 KiB
Rust
191 lines
4.4 KiB
Rust
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
|
use galactica_content::{Content, FactionHandle, ShipHandle};
|
|
use nalgebra::{point, vector};
|
|
use rand::Rng;
|
|
use rapier2d::{
|
|
dynamics::{RigidBody, RigidBodyHandle},
|
|
geometry::{Collider, ColliderHandle},
|
|
};
|
|
|
|
use crate::data::{ShipData, ShipPersonality, ShipState};
|
|
|
|
use super::{
|
|
super::{util, 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<ShipCollapseSequence>,
|
|
}
|
|
|
|
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 => {
|
|
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 PhysStepResources,
|
|
rigid_body: &mut RigidBody,
|
|
collider: &mut Collider,
|
|
) {
|
|
let ship_content = res.ct.get_ship(self.data.get_content());
|
|
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 });
|
|
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 {
|
|
pos.to_vec()
|
|
} 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 { x, y }
|
|
};
|
|
|
|
let pos =
|
|
ship_pos + (Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos);
|
|
|
|
let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
|
|
|
|
res.particles.push(ParticleBuilder::from_content(
|
|
effect,
|
|
pos,
|
|
Rad::zero(),
|
|
Vector2 {
|
|
x: velocity.x,
|
|
y: velocity.y,
|
|
},
|
|
Vector2::zero(),
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
let engine_force = ship_rot * 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,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PhysSimShip {
|
|
/// Get this ship's control state
|
|
pub fn get_controls(&self) -> &ShipControls {
|
|
&self.controls
|
|
}
|
|
}
|