2024-03-17 22:49:50 +00:00
|
|
|
use bevy::prelude::*;
|
2024-03-29 15:58:42 +00:00
|
|
|
use bevy_xpbd_3d::prelude::*;
|
2024-04-05 00:58:02 +00:00
|
|
|
use bevy::scene::SceneInstance;
|
2024-04-04 22:54:58 +00:00
|
|
|
use bevy::math::DVec3;
|
2024-04-05 17:57:55 +00:00
|
|
|
use crate::{actor, audio, camera, chat, commands, effects, hud, nature, settings, world};
|
2024-03-19 00:24:27 +00:00
|
|
|
|
2024-03-29 01:40:55 +00:00
|
|
|
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
|
2024-04-05 17:57:55 +00:00
|
|
|
const MAX_TRANSMISSION_DISTANCE: f32 = 60.0;
|
|
|
|
const MAX_INTERACT_DISTANCE: f32 = 30.0;
|
2024-03-17 22:49:50 +00:00
|
|
|
|
|
|
|
pub struct ActorPlugin;
|
|
|
|
impl Plugin for ActorPlugin {
|
|
|
|
fn build(&self, app: &mut App) {
|
2024-04-05 00:58:02 +00:00
|
|
|
app.add_systems(PreUpdate, (
|
|
|
|
handle_player_death,
|
|
|
|
));
|
2024-03-19 15:14:12 +00:00
|
|
|
app.add_systems(FixedUpdate, (
|
|
|
|
update_physics_lifeforms,
|
2024-04-04 23:42:50 +00:00
|
|
|
handle_wants_maxrotation,
|
|
|
|
handle_wants_maxvelocity,
|
2024-04-05 23:11:11 +00:00
|
|
|
handle_gforce,
|
2024-03-19 15:14:12 +00:00
|
|
|
));
|
2024-03-19 00:24:27 +00:00
|
|
|
app.add_systems(Update, (
|
|
|
|
handle_input,
|
2024-03-29 15:58:42 +00:00
|
|
|
handle_collisions,
|
2024-04-05 01:31:52 +00:00
|
|
|
handle_damage,
|
2024-03-19 00:24:27 +00:00
|
|
|
));
|
2024-04-03 12:42:48 +00:00
|
|
|
app.add_systems(PostUpdate, (
|
|
|
|
handle_vehicle_enter_exit,
|
|
|
|
));
|
2024-03-28 15:02:36 +00:00
|
|
|
app.add_event::<VehicleEnterExitEvent>();
|
2024-04-05 00:58:02 +00:00
|
|
|
app.add_event::<PlayerDiesEvent>();
|
2024-03-17 22:49:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-05 00:58:02 +00:00
|
|
|
#[derive(Event)] pub struct PlayerDiesEvent;
|
2024-03-28 15:02:36 +00:00
|
|
|
#[derive(Event)]
|
|
|
|
pub struct VehicleEnterExitEvent {
|
|
|
|
vehicle: Entity,
|
|
|
|
driver: Entity,
|
|
|
|
is_entering: bool,
|
|
|
|
is_player: bool
|
|
|
|
}
|
|
|
|
|
2024-03-17 22:49:50 +00:00
|
|
|
#[derive(Component)]
|
|
|
|
pub struct Actor {
|
2024-03-23 20:26:56 +00:00
|
|
|
pub id: String,
|
2024-04-05 17:03:50 +00:00
|
|
|
pub name: Option<String>,
|
2024-03-29 18:41:46 +00:00
|
|
|
pub camdistance: f32,
|
2024-03-17 22:49:50 +00:00
|
|
|
}
|
|
|
|
impl Default for Actor {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2024-03-23 20:26:56 +00:00
|
|
|
id: "".to_string(),
|
2024-04-05 17:03:50 +00:00
|
|
|
name: None,
|
2024-03-30 14:37:51 +00:00
|
|
|
camdistance: 15.0,
|
2024-03-17 22:49:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-05 01:31:52 +00:00
|
|
|
#[derive(Component)]
|
|
|
|
pub struct HitPoints {
|
|
|
|
pub current: f32,
|
|
|
|
pub max: f32,
|
|
|
|
pub damage: f32,
|
|
|
|
}
|
|
|
|
impl Default for HitPoints {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
current: 100.0,
|
|
|
|
max: 100.0,
|
|
|
|
damage: 0.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-05 23:11:11 +00:00
|
|
|
#[derive(Component)]
|
|
|
|
pub struct ExperiencesGForce {
|
|
|
|
pub gforce: f32,
|
|
|
|
pub damage_threshold: f32,
|
2024-04-08 00:36:47 +00:00
|
|
|
pub visual_effect_threshold: f32,
|
|
|
|
pub visual_effect: f32,
|
2024-04-05 23:11:11 +00:00
|
|
|
pub last_linear_velocity: DVec3,
|
2024-04-05 23:36:14 +00:00
|
|
|
pub ignore_gforce_seconds: f32,
|
2024-04-05 23:11:11 +00:00
|
|
|
}
|
|
|
|
impl Default for ExperiencesGForce { fn default() -> Self { Self {
|
|
|
|
gforce: 0.0,
|
2024-04-05 23:36:14 +00:00
|
|
|
damage_threshold: 100.0,
|
2024-04-08 00:36:47 +00:00
|
|
|
visual_effect_threshold: 20.0,
|
|
|
|
visual_effect: 0.0,
|
2024-04-05 23:11:11 +00:00
|
|
|
last_linear_velocity: DVec3::splat(0.0),
|
2024-04-05 23:36:14 +00:00
|
|
|
ignore_gforce_seconds: 0.0,
|
2024-04-05 23:11:11 +00:00
|
|
|
}}}
|
|
|
|
|
2024-03-29 18:41:46 +00:00
|
|
|
#[derive(Component)] pub struct Player; // Attached to the suit of the player
|
|
|
|
#[derive(Component)] pub struct PlayerDrivesThis; // Attached to the entered vehicle
|
|
|
|
#[derive(Component)] pub struct PlayerCamera; // Attached to the actor to use as point of view
|
2024-03-28 15:02:36 +00:00
|
|
|
#[derive(Component)] pub struct ActorEnteringVehicle;
|
|
|
|
#[derive(Component)] pub struct ActorVehicleBeingEntered;
|
2024-04-04 23:42:50 +00:00
|
|
|
#[derive(Component)] pub struct WantsMaxRotation(pub f64);
|
|
|
|
#[derive(Component)] pub struct WantsMaxVelocity(pub f64);
|
2024-03-19 00:24:27 +00:00
|
|
|
|
2024-03-17 22:49:50 +00:00
|
|
|
#[derive(Component)]
|
|
|
|
pub struct LifeForm {
|
2024-03-20 17:37:42 +00:00
|
|
|
pub is_alive: bool,
|
2024-03-17 22:49:50 +00:00
|
|
|
pub adrenaline: f32,
|
|
|
|
pub adrenaline_baseline: f32,
|
|
|
|
pub adrenaline_jolt: f32,
|
|
|
|
}
|
|
|
|
impl Default for LifeForm { fn default() -> Self { Self {
|
2024-03-20 17:37:42 +00:00
|
|
|
is_alive: true,
|
2024-03-17 22:49:50 +00:00
|
|
|
adrenaline: 0.3,
|
|
|
|
adrenaline_baseline: 0.3,
|
|
|
|
adrenaline_jolt: 0.0,
|
|
|
|
}}}
|
|
|
|
|
2024-03-28 12:26:14 +00:00
|
|
|
#[derive(Component)]
|
2024-03-30 21:31:07 +00:00
|
|
|
pub struct Vehicle {
|
|
|
|
stored_drivers_collider: Option<Collider>,
|
|
|
|
}
|
|
|
|
impl Default for Vehicle { fn default() -> Self { Self {
|
|
|
|
stored_drivers_collider: None,
|
|
|
|
}}}
|
2024-03-28 12:26:14 +00:00
|
|
|
|
2024-03-28 13:10:10 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
|
|
pub enum EngineType {
|
|
|
|
Monopropellant,
|
|
|
|
Rocket,
|
2024-03-29 03:36:20 +00:00
|
|
|
Ion,
|
2024-03-28 13:10:10 +00:00
|
|
|
}
|
|
|
|
|
2024-03-28 12:26:14 +00:00
|
|
|
#[derive(Component)]
|
|
|
|
pub struct Engine {
|
|
|
|
pub thrust_forward: f32,
|
|
|
|
pub thrust_back: f32,
|
|
|
|
pub thrust_sideways: f32,
|
|
|
|
pub reaction_wheels: f32,
|
2024-03-28 13:10:10 +00:00
|
|
|
pub engine_type: EngineType,
|
2024-03-28 23:01:17 +00:00
|
|
|
pub warmup_seconds: f32,
|
|
|
|
pub current_warmup: f32,
|
2024-03-28 12:26:14 +00:00
|
|
|
}
|
|
|
|
impl Default for Engine {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
thrust_forward: 1.0,
|
|
|
|
thrust_back: 1.0,
|
|
|
|
thrust_sideways: 1.0,
|
|
|
|
reaction_wheels: 1.0,
|
2024-03-28 13:10:10 +00:00
|
|
|
engine_type: EngineType::Monopropellant,
|
2024-03-28 23:01:17 +00:00
|
|
|
warmup_seconds: 1.5,
|
|
|
|
current_warmup: 0.0,
|
2024-03-28 12:26:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-17 22:49:50 +00:00
|
|
|
#[derive(Component)]
|
|
|
|
pub struct Suit {
|
|
|
|
pub oxygen: f32,
|
|
|
|
pub power: f32,
|
|
|
|
pub oxygen_max: f32,
|
|
|
|
pub power_max: f32,
|
2024-03-20 17:37:10 +00:00
|
|
|
pub integrity: f32, // [0.0 - 1.0]
|
2024-03-17 22:49:50 +00:00
|
|
|
}
|
|
|
|
impl Default for Suit { fn default() -> Self { SUIT_SIMPLE } }
|
|
|
|
|
|
|
|
const SUIT_SIMPLE: Suit = Suit {
|
|
|
|
power: 1e5,
|
|
|
|
power_max: 1e5,
|
2024-03-18 22:53:52 +00:00
|
|
|
oxygen: nature::OXY_D,
|
|
|
|
oxygen_max: nature::OXY_D,
|
2024-03-30 18:39:53 +00:00
|
|
|
integrity: 1.0,
|
2024-03-17 22:49:50 +00:00
|
|
|
};
|
|
|
|
|
2024-03-19 15:14:12 +00:00
|
|
|
pub fn update_physics_lifeforms(
|
2024-03-17 22:49:50 +00:00
|
|
|
time: Res<Time>,
|
2024-04-05 01:31:52 +00:00
|
|
|
mut query: Query<(&mut LifeForm, &mut HitPoints, &mut Suit, &LinearVelocity)>,
|
2024-03-17 22:49:50 +00:00
|
|
|
) {
|
|
|
|
let d = time.delta_seconds();
|
2024-04-05 01:31:52 +00:00
|
|
|
for (mut lifeform, mut hp, mut suit, velocity) in query.iter_mut() {
|
2024-03-17 22:49:50 +00:00
|
|
|
if lifeform.adrenaline_jolt.abs() > 1e-3 {
|
|
|
|
lifeform.adrenaline_jolt *= 0.99;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lifeform.adrenaline_jolt = 0.0
|
|
|
|
}
|
2024-03-30 21:31:07 +00:00
|
|
|
let speed = velocity.length();
|
2024-03-28 22:13:59 +00:00
|
|
|
if speed > 1000.0 {
|
|
|
|
lifeform.adrenaline += 0.001;
|
|
|
|
}
|
2024-03-17 22:49:50 +00:00
|
|
|
lifeform.adrenaline = (lifeform.adrenaline - 0.0001 + lifeform.adrenaline_jolt * 0.01).clamp(0.0, 1.0);
|
2024-03-20 17:37:10 +00:00
|
|
|
|
|
|
|
let mut oxygen_drain = nature::OXY_S;
|
|
|
|
let integr_threshold = 0.5;
|
|
|
|
if suit.integrity < integr_threshold {
|
|
|
|
// The oxygen drain from suit integrity scales with (2 - 2x)^4,
|
|
|
|
// which is a function with ~16 at x=0 and 0 at x=1.
|
|
|
|
// Furthermore, x is divided by the integrity threshold (e.g. 0.5)
|
|
|
|
// to squeeze the function horizontally, and change the range for
|
|
|
|
// the x parameter from [0-1] to [0-integritythreshold]
|
|
|
|
//
|
|
|
|
// 16 |.
|
|
|
|
// |.
|
|
|
|
// |'.
|
|
|
|
// | '.
|
|
|
|
// | '..
|
|
|
|
// |______''....
|
|
|
|
// x=0 x=1
|
|
|
|
let drain_scaling = (2.0 - 2.0 * suit.integrity / integr_threshold).powf(4.0);
|
|
|
|
oxygen_drain += suit.oxygen * 0.01 * drain_scaling;
|
|
|
|
}
|
|
|
|
suit.oxygen = (suit.oxygen - oxygen_drain*d).clamp(0.0, suit.oxygen_max);
|
2024-04-05 01:31:52 +00:00
|
|
|
if suit.oxygen <= 0.0 {
|
|
|
|
hp.damage += 1.0 * d;
|
|
|
|
}
|
2024-03-17 22:49:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-19 00:24:27 +00:00
|
|
|
pub fn handle_input(
|
2024-03-28 12:26:14 +00:00
|
|
|
mut commands: Commands,
|
2024-03-19 00:24:27 +00:00
|
|
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
|
|
|
settings: ResMut<settings::Settings>,
|
2024-04-05 17:57:55 +00:00
|
|
|
q_talker: Query<(&chat::Talker, &Transform), (Without<actor::Player>, Without<Camera>)>,
|
2024-04-05 18:05:58 +00:00
|
|
|
player: Query<Entity, With<actor::Player>>,
|
2024-04-05 17:57:55 +00:00
|
|
|
q_camera: Query<&Transform, With<Camera>>,
|
2024-04-05 18:05:58 +00:00
|
|
|
q_vehicles: Query<(Entity, &Transform), (With<actor::Vehicle>, Without<actor::Player>, Without<Camera>)>,
|
2024-04-04 11:33:54 +00:00
|
|
|
mut ew_conv: EventWriter<chat::StartConversationEvent>,
|
2024-03-28 15:02:36 +00:00
|
|
|
mut ew_vehicle: EventWriter<VehicleEnterExitEvent>,
|
|
|
|
q_player_drives: Query<Entity, With<PlayerDrivesThis>>,
|
|
|
|
) {
|
2024-04-05 18:01:44 +00:00
|
|
|
if q_camera.is_empty() || player.is_empty() {
|
2024-04-05 17:57:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let camtrans = q_camera.get_single().unwrap();
|
2024-04-05 18:05:58 +00:00
|
|
|
let player_entity = player.get_single().unwrap();
|
2024-04-05 17:57:55 +00:00
|
|
|
|
2024-03-19 00:24:27 +00:00
|
|
|
if keyboard_input.just_pressed(settings.key_interact) {
|
2024-04-03 12:21:47 +00:00
|
|
|
// Talking to people
|
2024-04-05 18:01:44 +00:00
|
|
|
let objects: Vec<(chat::Talker, &Transform)> = q_talker
|
|
|
|
.iter()
|
|
|
|
.map(|(talker, transform)| (talker.clone(), transform))
|
|
|
|
.collect();
|
|
|
|
// TODO: replace Transform.translation with Position
|
|
|
|
if let (Some(talker), dist) = camera::find_closest_target::<chat::Talker>(objects, camtrans) {
|
|
|
|
if dist <= MAX_TRANSMISSION_DISTANCE {
|
|
|
|
ew_conv.send(chat::StartConversationEvent{talker: talker.clone()});
|
2024-03-19 00:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
2024-03-28 15:02:36 +00:00
|
|
|
// Entering Vehicles
|
|
|
|
if q_player_drives.is_empty() {
|
2024-04-05 18:05:58 +00:00
|
|
|
let objects: Vec<(Entity, &Transform)> = q_vehicles
|
|
|
|
.iter()
|
|
|
|
.collect();
|
|
|
|
if let (Some(entity), dist) = camera::find_closest_target::<Entity>(objects, camtrans) {
|
|
|
|
if dist <= MAX_INTERACT_DISTANCE {
|
|
|
|
commands.entity(entity).insert(ActorVehicleBeingEntered);
|
2024-04-05 18:01:44 +00:00
|
|
|
commands.entity(player_entity).insert(ActorEnteringVehicle);
|
|
|
|
ew_vehicle.send(VehicleEnterExitEvent{
|
2024-04-05 18:05:58 +00:00
|
|
|
vehicle: entity,
|
2024-04-05 18:01:44 +00:00
|
|
|
driver: player_entity,
|
|
|
|
is_entering: q_player_drives.is_empty(),
|
|
|
|
is_player: true,
|
|
|
|
});
|
2024-03-28 15:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-03 12:21:47 +00:00
|
|
|
}
|
|
|
|
else if keyboard_input.just_pressed(settings.key_vehicle) {
|
2024-03-28 15:02:36 +00:00
|
|
|
// Exiting Vehicles
|
2024-04-05 18:01:44 +00:00
|
|
|
for vehicle_entity in &q_player_drives {
|
|
|
|
commands.entity(vehicle_entity).insert(ActorVehicleBeingEntered);
|
|
|
|
commands.entity(player_entity).insert(ActorEnteringVehicle);
|
|
|
|
ew_vehicle.send(VehicleEnterExitEvent{
|
|
|
|
vehicle: vehicle_entity,
|
|
|
|
driver: player_entity,
|
|
|
|
is_entering: false,
|
|
|
|
is_player: true,
|
|
|
|
});
|
|
|
|
break;
|
2024-03-28 12:26:14 +00:00
|
|
|
}
|
|
|
|
}
|
2024-03-19 00:24:27 +00:00
|
|
|
}
|
|
|
|
|
2024-03-28 15:02:36 +00:00
|
|
|
pub fn handle_vehicle_enter_exit(
|
|
|
|
mut commands: Commands,
|
|
|
|
mut er_vehicle: EventReader<VehicleEnterExitEvent>,
|
2024-03-30 21:31:07 +00:00
|
|
|
mut q_drivers: Query<(Entity, &mut Visibility, Option<&Collider>), (Without<ActorVehicleBeingEntered>, With<ActorEnteringVehicle>)>,
|
|
|
|
mut q_vehicles: Query<(Entity, &mut Vehicle, &mut Visibility), (With<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>,
|
2024-03-28 15:02:36 +00:00
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
|
|
|
) {
|
|
|
|
for event in er_vehicle.read() {
|
2024-03-30 21:31:07 +00:00
|
|
|
for (driver, mut driver_vis, driver_collider) in q_drivers.iter_mut() {
|
2024-03-28 15:02:36 +00:00
|
|
|
if driver == event.driver {
|
2024-03-30 21:31:07 +00:00
|
|
|
for (vehicle, mut vehicle_component, mut vehicle_vis) in q_vehicles.iter_mut() {
|
|
|
|
if !event.is_player {
|
|
|
|
continue;
|
|
|
|
}
|
2024-03-28 15:02:36 +00:00
|
|
|
if vehicle == event.vehicle {
|
|
|
|
if event.is_entering {
|
|
|
|
// Entering Vehicle
|
2024-03-30 21:31:07 +00:00
|
|
|
if let Some(collider) = driver_collider {
|
|
|
|
vehicle_component.stored_drivers_collider = Some(collider.clone());
|
|
|
|
}
|
2024-03-30 17:48:19 +00:00
|
|
|
commands.entity(driver).remove::<RigidBody>();
|
2024-03-30 17:56:24 +00:00
|
|
|
*driver_vis = Visibility::Hidden; //seems to have no effect...
|
2024-03-30 21:31:07 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle));
|
|
|
|
commands.entity(driver).remove::<PlayerCamera>();
|
|
|
|
commands.entity(driver).remove::<Collider>();
|
|
|
|
commands.entity(vehicle).insert(PlayerCamera);
|
|
|
|
commands.entity(vehicle).insert(PlayerDrivesThis);
|
2024-03-28 15:02:36 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Exiting Vehicle
|
2024-03-30 21:31:07 +00:00
|
|
|
if let Some(collider) = &vehicle_component.stored_drivers_collider {
|
|
|
|
commands.entity(driver).insert(collider.clone());
|
2024-03-28 15:02:36 +00:00
|
|
|
}
|
2024-03-30 21:31:07 +00:00
|
|
|
commands.entity(driver).insert(RigidBody::Dynamic);
|
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
|
|
|
commands.entity(vehicle).remove::<PlayerCamera>();
|
|
|
|
commands.entity(driver).insert(PlayerCamera);
|
|
|
|
commands.entity(vehicle).remove::<PlayerDrivesThis>();
|
2024-04-03 10:27:43 +00:00
|
|
|
*vehicle_vis = Visibility::Visible;
|
2024-03-28 15:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-29 15:58:42 +00:00
|
|
|
fn handle_collisions(
|
2024-03-31 03:13:21 +00:00
|
|
|
mut collision_event_reader: EventReader<CollisionStarted>,
|
2024-03-29 15:58:42 +00:00
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
2024-04-05 01:52:46 +00:00
|
|
|
q_player: Query<(Entity, Option<&Player>), With<PlayerCamera>>,
|
2024-04-05 23:11:11 +00:00
|
|
|
mut q_player_lifeform: Query<(&mut LifeForm, &mut Suit), With<Player>>,
|
2024-03-29 15:58:42 +00:00
|
|
|
) {
|
2024-04-05 23:11:11 +00:00
|
|
|
if let (Ok((player, player_maybe)), Ok((mut lifeform, mut suit))) = (q_player.get_single(), q_player_lifeform.get_single_mut()) {
|
2024-03-31 03:13:21 +00:00
|
|
|
for CollisionStarted(entity1, entity2) in collision_event_reader.read() {
|
|
|
|
if *entity1 == player || *entity2 == player {
|
2024-03-29 15:58:42 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Crash));
|
2024-04-01 04:24:29 +00:00
|
|
|
lifeform.adrenaline_jolt += 0.1;
|
2024-04-05 01:52:46 +00:00
|
|
|
|
|
|
|
if player_maybe.is_some() {
|
|
|
|
suit.integrity -= 0.03;
|
|
|
|
}
|
2024-03-29 15:58:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-04 22:54:58 +00:00
|
|
|
|
2024-04-04 23:42:50 +00:00
|
|
|
fn handle_wants_maxrotation(
|
2024-04-04 23:19:46 +00:00
|
|
|
//time: Res<Time>,
|
2024-04-04 23:42:50 +00:00
|
|
|
mut query: Query<(&mut AngularVelocity, &Engine, &WantsMaxRotation)>,
|
2024-04-04 22:54:58 +00:00
|
|
|
) {
|
2024-04-04 23:19:46 +00:00
|
|
|
let epsilon = 0.0001;
|
|
|
|
//let d = time.delta_seconds();
|
2024-04-04 23:42:50 +00:00
|
|
|
for (mut v_ang, engine, maxrot) in &mut query {
|
2024-04-04 22:54:58 +00:00
|
|
|
let total = v_ang.0.length();
|
2024-04-04 23:42:50 +00:00
|
|
|
if total <= maxrot.0 + epsilon {
|
|
|
|
if total > maxrot.0 {
|
2024-04-04 22:54:58 +00:00
|
|
|
v_ang.0 = DVec3::splat(0.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let angular_slowdown: f64 = (2.0 - engine.reaction_wheels.powf(0.01).clamp(1.001, 1.1)) as f64;
|
|
|
|
v_ang.0 *= angular_slowdown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-04 23:19:46 +00:00
|
|
|
|
2024-04-04 23:42:50 +00:00
|
|
|
fn handle_wants_maxvelocity(
|
2024-04-04 23:19:46 +00:00
|
|
|
time: Res<Time>,
|
2024-04-04 23:42:50 +00:00
|
|
|
mut query: Query<(&mut LinearVelocity, &Engine, &WantsMaxVelocity)>,
|
2024-04-04 23:19:46 +00:00
|
|
|
) {
|
|
|
|
let dt = time.delta_seconds();
|
|
|
|
let epsilon = 0.0001;
|
2024-04-04 23:42:50 +00:00
|
|
|
for (mut v, engine, maxv) in &mut query {
|
2024-04-04 23:19:46 +00:00
|
|
|
let total = v.0.length();
|
2024-04-04 23:42:50 +00:00
|
|
|
if total <= maxv.0 + epsilon {
|
|
|
|
if total > maxv.0 {
|
2024-04-04 23:19:46 +00:00
|
|
|
v.0 = DVec3::splat(0.0);
|
|
|
|
}
|
|
|
|
// already not moving
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: respect engine parameters for different thrusts for different directions
|
|
|
|
let avg_thrust = (engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0;
|
|
|
|
let acceleration = (avg_thrust * dt) as f64 * -v.0;
|
|
|
|
v.0 += acceleration;
|
|
|
|
if v.0.length() + epsilon < acceleration.length() {
|
|
|
|
v.0 = DVec3::splat(0.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-05 00:58:02 +00:00
|
|
|
|
|
|
|
fn handle_player_death(
|
|
|
|
mut cmd: Commands,
|
|
|
|
mut er_playerdies: EventReader<PlayerDiesEvent>,
|
|
|
|
q_scenes: Query<(Entity, &SceneInstance), With<world::DespawnOnPlayerDeath>>,
|
|
|
|
q_noscenes: Query<Entity, (With<world::DespawnOnPlayerDeath>, Without<SceneInstance>)>,
|
|
|
|
ew_spawn: EventWriter<commands::SpawnEvent>,
|
|
|
|
mut scene_spawner: ResMut<SceneSpawner>,
|
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
2024-04-05 01:49:29 +00:00
|
|
|
mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
|
2024-04-05 03:13:09 +00:00
|
|
|
mut log: ResMut<hud::Log>,
|
2024-04-07 23:44:36 +00:00
|
|
|
settings: Res<settings::Settings>,
|
2024-04-05 00:58:02 +00:00
|
|
|
) {
|
|
|
|
for _ in er_playerdies.read() {
|
2024-04-07 23:44:36 +00:00
|
|
|
if settings.god_mode {
|
|
|
|
return;
|
|
|
|
}
|
2024-04-05 00:58:02 +00:00
|
|
|
for entity in &q_noscenes {
|
|
|
|
cmd.entity(entity).despawn();
|
|
|
|
}
|
|
|
|
for (entity, sceneinstance) in &q_scenes {
|
|
|
|
cmd.entity(entity).despawn();
|
|
|
|
scene_spawner.despawn_instance(**sceneinstance);
|
|
|
|
}
|
2024-04-05 03:13:09 +00:00
|
|
|
log.clear();
|
2024-04-05 00:58:02 +00:00
|
|
|
//cmd.run_system(commands::load_defs); // why is it so complicated to get SystemId?
|
2024-04-05 01:31:52 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
2024-04-05 01:49:29 +00:00
|
|
|
ew_effect.send(effects::SpawnEffectEvent {
|
|
|
|
class: effects::Effects::FadeIn(Color::MAROON),
|
|
|
|
duration: 1.0,
|
|
|
|
});
|
2024-04-05 00:58:02 +00:00
|
|
|
commands::load_defs(ew_spawn);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2024-04-05 01:31:52 +00:00
|
|
|
|
|
|
|
fn handle_damage(
|
|
|
|
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
|
|
|
mut q_hp: Query<(&mut HitPoints, Option<&Player>), Changed<HitPoints>>,
|
2024-04-07 23:44:36 +00:00
|
|
|
settings: Res<settings::Settings>,
|
2024-04-05 01:31:52 +00:00
|
|
|
) {
|
|
|
|
for (mut hp, player_maybe) in &mut q_hp {
|
|
|
|
if player_maybe.is_some() {
|
2024-04-07 23:44:36 +00:00
|
|
|
if !settings.god_mode {
|
|
|
|
hp.current -= hp.damage;
|
|
|
|
}
|
2024-04-05 01:31:52 +00:00
|
|
|
if hp.current <= 0.0 {
|
|
|
|
ew_playerdies.send(PlayerDiesEvent);
|
|
|
|
}
|
|
|
|
}
|
2024-04-07 23:44:36 +00:00
|
|
|
else {
|
|
|
|
hp.current -= hp.damage;
|
|
|
|
}
|
|
|
|
hp.damage = 0.0;
|
2024-04-05 01:31:52 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-05 23:11:11 +00:00
|
|
|
|
|
|
|
fn handle_gforce(
|
|
|
|
time: Res<Time>,
|
|
|
|
mut q_actor: Query<(&LinearVelocity, &mut HitPoints, &mut ExperiencesGForce)>,
|
|
|
|
) {
|
|
|
|
let dt = time.delta_seconds();
|
2024-04-07 23:53:56 +00:00
|
|
|
let factor = 1.0 / dt / nature::EARTH_GRAVITY;
|
2024-04-05 23:11:11 +00:00
|
|
|
for (v, mut hp, mut gforce) in &mut q_actor {
|
2024-04-07 23:53:56 +00:00
|
|
|
gforce.gforce = factor * (v.0 - gforce.last_linear_velocity).length() as f32;
|
|
|
|
gforce.last_linear_velocity = v.0;
|
2024-04-05 23:36:14 +00:00
|
|
|
if gforce.ignore_gforce_seconds > 0.0 {
|
|
|
|
gforce.ignore_gforce_seconds -= dt;
|
|
|
|
continue;
|
|
|
|
}
|
2024-04-05 23:11:11 +00:00
|
|
|
if gforce.gforce > gforce.damage_threshold {
|
|
|
|
hp.damage += (gforce.gforce - gforce.damage_threshold).powf(2.0) / 3000.0;
|
|
|
|
}
|
|
|
|
|
2024-04-08 00:36:47 +00:00
|
|
|
if gforce.visual_effect > 0.0001 {
|
|
|
|
gforce.visual_effect *= 0.984;
|
|
|
|
}
|
|
|
|
else if gforce.visual_effect > 0.0 {
|
|
|
|
gforce.visual_effect = 0.0;
|
|
|
|
}
|
|
|
|
if gforce.gforce > gforce.visual_effect_threshold {
|
|
|
|
gforce.visual_effect += (gforce.gforce - gforce.visual_effect_threshold).powf(2.0) / 300000.0
|
|
|
|
}
|
2024-04-05 23:11:11 +00:00
|
|
|
}
|
|
|
|
}
|