2024-01-14 11:50:19 -08:00

213 lines
5.9 KiB
Rust

use galactica_content::{Content, Relationship, SystemObjectHandle};
use nalgebra::{Isometry2, Point2, Rotation2, Vector2};
use rand::Rng;
use rapier2d::{
dynamics::RigidBody,
geometry::{ColliderHandle, Group, InteractionGroups},
};
use crate::{
data::ShipState,
phys::{objects::PhysProjectile, ParticleBuilder, PhysStepResources},
};
use super::PhysSim;
// Private methods
impl PhysSim {
pub(super) fn remove_projectile(
&mut self,
res: &mut PhysStepResources,
c: ColliderHandle,
) -> Option<(RigidBody, PhysProjectile)> {
let p = match self.projectiles.remove(&c) {
Some(p) => p,
None => return None,
};
let r = self
.rigid_body_set
.remove(
p.rigid_body,
&mut res.wrapper.im,
&mut self.collider_set,
&mut res.wrapper.ij,
&mut res.wrapper.mj,
true,
)
.unwrap();
return Some((r, p));
}
/// Try to land the given ship on the given planet.
/// State changes on success, and nothing happens on failure.
/// returns `true` if landing was successful.
pub(super) fn try_land_ship(
&mut self,
ct: &Content,
collider: ColliderHandle,
target_handle: SystemObjectHandle,
) -> bool {
let ship = self.ships.get_mut(&collider).unwrap();
let r = self.rigid_body_set.get(ship.rigid_body).unwrap();
let target = ct.get_system_object(target_handle);
let t_pos = Vector2::new(target.pos.x, target.pos.y);
let s_pos = Vector2::new(r.position().translation.x, r.position().translation.y);
// TODO: deactivate collider when landing.
// Can't just set_active(false), since we still need that collider's mass.
// We're in land range...
if (t_pos - s_pos).magnitude() > target.size / 2.0 {
return false;
}
// And we'll stay in land range long enough.
if (t_pos - (s_pos + r.velocity_at_point(r.center_of_mass()) * 2.0)).magnitude()
> target.size / 2.0
{
return false;
}
let collider = self.collider_set.get_mut(collider).unwrap();
collider.set_collision_groups(InteractionGroups::new(Group::GROUP_1, Group::empty()));
ship.data.start_land_on(target_handle);
return true;
}
/// Finish landing this ship on a planet.
/// Called after the landing animation finishes.
pub(super) fn finish_land_ship(&mut self, collider: ColliderHandle) {
let ship = self.ships.get_mut(&collider).unwrap();
ship.data.finish_land_on();
let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap();
r.set_enabled(false);
r.set_angvel(0.0, false);
r.set_linvel(nalgebra::Vector2::new(0.0, 0.0), false);
}
pub(super) fn start_unland_ship(&mut self, ct: &Content, collider: ColliderHandle) {
let ship = self.ships.get_mut(&collider).unwrap();
let obj = ship.data.get_state().landed_on().unwrap();
let obj = ct.get_system_object(obj);
let mut rng = rand::thread_rng();
let radius = rng.gen_range(500.0..=1500.0);
let angle = rng.gen_range(0.0..std::f32::consts::TAU);
let target_offset = Rotation2::new(angle) * Vector2::new(radius, 0.0);
let target_trans = Vector2::new(obj.pos.x, obj.pos.y) + target_offset;
let target_pos = Isometry2::new(target_trans, angle);
ship.data.start_unland_to(ct, target_pos);
let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap();
r.set_enabled(true);
r.set_position(
Isometry2::new(Vector2::new(obj.pos.x, obj.pos.y), angle),
true,
);
}
pub(super) fn finish_unland_ship(&mut self, collider: ColliderHandle) {
let ship = self.ships.get_mut(&collider).unwrap();
ship.data.finish_unland_to();
self.collider_set
.get_mut(ship.collider)
.unwrap()
.set_collision_groups(InteractionGroups::new(
Group::GROUP_1,
Group::GROUP_1 | Group::GROUP_2,
));
}
pub(super) fn remove_ship(
&mut self,
res: &mut PhysStepResources,
colliderhandle: ColliderHandle,
) {
let ship = match self.ships.get(&colliderhandle) {
None => return,
Some(s) => s,
};
self.rigid_body_set.remove(
ship.rigid_body,
&mut res.wrapper.im,
&mut self.collider_set,
&mut res.wrapper.ij,
&mut res.wrapper.mj,
true,
);
self.ships.remove(&colliderhandle).unwrap();
self.ship_behaviors.remove(&colliderhandle);
}
pub(super) fn collide_projectile_ship(
&mut self,
res: &mut PhysStepResources,
projectile_h: ColliderHandle,
ship_h: ColliderHandle,
) {
let projectile = self.projectiles.get(&projectile_h);
let ship = self.ships.get_mut(&ship_h);
if projectile.is_none() || ship.is_none() {
return;
}
let projectile = projectile.unwrap();
let ship = ship.unwrap();
let f = res.ct.get_faction(projectile.faction);
let r = f.relationships.get(&ship.data.get_faction()).unwrap();
let destory_projectile = match r {
Relationship::Hostile => match ship.data.get_state() {
ShipState::Flying { .. } => {
ship.data.apply_damage(projectile.content.damage);
true
}
ShipState::Collapsing { .. } => true,
_ => false,
},
_ => false,
};
if destory_projectile {
let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap();
let v =
pr.velocity_at_point(pr.center_of_mass()).normalize() * projectile.content.force;
let pos = *pr.translation();
let _ = pr;
let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap();
r.apply_impulse_at_point(Vector2::new(v.x, v.y), Point2::new(pos.x, pos.y), true);
// Borrow again, we can only have one at a time
let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap();
let pos = *pr.translation();
let angle = pr.rotation().angle();
match &projectile.content.impact_effect {
None => {}
Some(x) => {
let effect = res.ct.get_effect(*x);
let r = ship.rigid_body;
let sr = self.get_rigid_body(r).unwrap();
let parent_velocity = pr.velocity_at_point(pr.center_of_mass());
let target_velocity =
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
res.particles.push(ParticleBuilder::from_content(
effect,
pos.into(),
-angle,
parent_velocity,
target_velocity,
));
}
};
self.remove_projectile(res, projectile.collider);
}
}
}