185 lines
4.2 KiB
Rust
185 lines
4.2 KiB
Rust
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2};
|
|
use content::{FactionHandle, TextureHandle};
|
|
use nalgebra::vector;
|
|
use rand::Rng;
|
|
use rapier2d::{
|
|
dynamics::{RigidBody, RigidBodyBuilder},
|
|
geometry::ColliderBuilder,
|
|
pipeline::ActiveEvents,
|
|
};
|
|
|
|
use super::ProjectileBuilder;
|
|
use crate::{
|
|
content,
|
|
game::outfits,
|
|
physics::{util, ShipHandle},
|
|
render::Sprite,
|
|
};
|
|
|
|
pub struct ShipTickResult {
|
|
pub projectiles: Vec<ProjectileBuilder>,
|
|
}
|
|
|
|
pub struct ShipControls {
|
|
pub left: bool,
|
|
pub right: bool,
|
|
pub thrust: bool,
|
|
pub guns: bool,
|
|
}
|
|
|
|
impl ShipControls {
|
|
pub fn new() -> Self {
|
|
ShipControls {
|
|
left: false,
|
|
right: false,
|
|
thrust: false,
|
|
guns: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Ship {
|
|
pub physics_handle: ShipHandle,
|
|
pub faction: FactionHandle,
|
|
pub hull: f32,
|
|
pub controls: ShipControls,
|
|
|
|
outfits: outfits::ShipOutfits,
|
|
sprite_texture: TextureHandle,
|
|
size: f32,
|
|
}
|
|
|
|
impl Ship {
|
|
pub fn new(
|
|
c: &content::Ship,
|
|
outfits: Vec<outfits::ShipOutfit>,
|
|
physics_handle: ShipHandle,
|
|
faction: FactionHandle,
|
|
) -> Self {
|
|
let mut o = outfits::ShipOutfits::new(c);
|
|
for x in outfits.into_iter() {
|
|
o.add(x)
|
|
}
|
|
|
|
Ship {
|
|
physics_handle,
|
|
outfits: o,
|
|
sprite_texture: c.sprite_texture,
|
|
size: c.size,
|
|
hull: c.hull,
|
|
controls: ShipControls::new(),
|
|
faction,
|
|
}
|
|
}
|
|
|
|
pub fn fire_guns(&mut self, r: &RigidBody) -> Vec<ProjectileBuilder> {
|
|
let mut rng = rand::thread_rng();
|
|
let mut out = Vec::new();
|
|
|
|
for (g, p) in self.outfits.iter_guns_points() {
|
|
if g.cooldown > 0.0 {
|
|
continue;
|
|
}
|
|
|
|
g.cooldown = g.kind.rate + rng.gen_range(-g.kind.rate_rng..=g.kind.rate_rng);
|
|
|
|
let ship_pos = util::rigidbody_position(r);
|
|
let ship_rot = util::rigidbody_rotation(r);
|
|
let ship_vel = util::rigidbody_velocity(r);
|
|
let ship_ang = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 });
|
|
|
|
let pos = ship_pos + (Matrix2::from_angle(-ship_ang) * p.pos.to_vec());
|
|
|
|
let spread: Rad<f32> =
|
|
Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)).into();
|
|
|
|
let vel = ship_vel
|
|
+ (Matrix2::from_angle(-ship_ang + spread)
|
|
* Vector2 {
|
|
x: 0.0,
|
|
y: g.kind.projectile.speed
|
|
+ rng.gen_range(
|
|
-g.kind.projectile.speed_rng..=g.kind.projectile.speed_rng,
|
|
),
|
|
});
|
|
|
|
let p_r = RigidBodyBuilder::kinematic_velocity_based()
|
|
.translation(vector![pos.x, pos.y])
|
|
.rotation(-ship_ang.0)
|
|
.linvel(vector![vel.x, vel.y]);
|
|
|
|
let p_c = ColliderBuilder::ball(5.0)
|
|
.sensor(true)
|
|
.active_events(ActiveEvents::COLLISION_EVENTS);
|
|
|
|
out.push(ProjectileBuilder {
|
|
rigid_body: p_r,
|
|
collider: p_c,
|
|
sprite_texture: g.kind.projectile.sprite_texture,
|
|
lifetime: g.kind.projectile.lifetime
|
|
+ rng.gen_range(
|
|
-g.kind.projectile.lifetime_rng..=g.kind.projectile.lifetime_rng,
|
|
),
|
|
size: g.kind.projectile.size
|
|
+ rng.gen_range(-g.kind.projectile.size_rng..=g.kind.projectile.size_rng),
|
|
damage: g.kind.projectile.damage, // TODO: kind as param to builder
|
|
faction: self.faction,
|
|
})
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/// Apply the effects of all active controls
|
|
pub fn apply_controls(&mut self, r: &mut RigidBody, t: f32) -> ShipTickResult {
|
|
let ship_rot = util::rigidbody_rotation(r);
|
|
let engine_force = ship_rot * t;
|
|
|
|
if self.controls.thrust {
|
|
for e in self.outfits.iter_engines() {
|
|
r.apply_impulse(vector![engine_force.x, engine_force.y] * e.thrust, true);
|
|
}
|
|
}
|
|
|
|
if self.controls.right {
|
|
r.apply_torque_impulse(-500.0 * t, true);
|
|
}
|
|
|
|
if self.controls.left {
|
|
r.apply_torque_impulse(500.0 * t, true);
|
|
}
|
|
|
|
let p = if self.controls.guns {
|
|
self.fire_guns(r)
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
for i in self.outfits.iter_guns() {
|
|
i.cooldown -= t;
|
|
}
|
|
|
|
return ShipTickResult { projectiles: p };
|
|
}
|
|
|
|
pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
|
|
let ship_pos = util::rigidbody_position(r);
|
|
let ship_rot = util::rigidbody_rotation(r);
|
|
|
|
// Sprites point north at 0 degrees
|
|
let ship_ang: Deg<f32> = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }).into();
|
|
|
|
Sprite {
|
|
pos: (ship_pos.x, ship_pos.y, 1.0).into(),
|
|
texture: self.sprite_texture,
|
|
angle: -ship_ang,
|
|
size: self.size,
|
|
|
|
children: if self.controls.thrust {
|
|
Some(self.outfits.get_engine_flares())
|
|
} else {
|
|
None
|
|
},
|
|
}
|
|
}
|
|
}
|