// ▄████████▄ + ███ + ▄█████████ ███ + // ███▀ ▀███ + + ███ ███▀ + ███ + + // ███ + ███ ███ ███ █████████ ███ ███ ███ ███ // ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███ // ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███ // ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███ // ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████ // + + + ███ // + ▀████████████████████████████████████████████████████▀ // // This module manages the internal states of individual characters, // such as their resources, the damage they receive, and interactions // between characters and with vehicles. // // This module should never handle any visual aspects directly. use crate::prelude::*; use bevy::prelude::*; use bevy_xpbd_3d::prelude::*; use std::collections::HashMap; pub const ENGINE_SPEED_FACTOR: f32 = 30.0; const MAX_TRANSMISSION_DISTANCE: f32 = 100.0; const MAX_INTERACT_DISTANCE: f32 = 50.0; pub const POWER_DRAIN_THRUSTER: [f32; 3] = [3000.0, 3000.0, 0.0]; pub const THRUSTER_BOOST_FACTOR: [f64; 3] = [3.0, 3.0, 0.0]; pub const POWER_DRAIN_FLASHLIGHT: [f32; 4] = [200.0, 1500.0, 2500.0, 10000.0]; pub const FLASHLIGHT_INTENSITY: [f32; 4] = [10e6, 400e6, 2e9, 100e9]; // in lumens pub const POWER_DRAIN_LIGHTAMP: [f32; 4] = [0.0, 200.0, 400.0, 1400.0]; pub const POWER_DRAIN_AR: f32 = 300.0; pub const POWER_GAIN_REACTOR: [f32; 3] = [0.0, 2000.0, 10000.0]; pub struct ActorPlugin; impl Plugin for ActorPlugin { fn build(&self, app: &mut App) { app.add_systems( FixedUpdate, ( update_physics_lifeforms.run_if(game_running), update_power.run_if(game_running), handle_gravity.run_if(game_running), handle_wants_rotation_change.run_if(game_running), handle_wants_maxrotation.run_if(game_running), handle_wants_maxvelocity .run_if(game_running) .run_if(any_with_component::), handle_wants_lookat.run_if(game_running).run_if(alive), ), ); app.add_systems( PreUpdate, handle_wants_acceleration .run_if(game_running) .run_if(alive) .after(PhysicsSet::Sync) .after(sync::position_to_transform), ); app.add_systems( PostUpdate, handle_gforce .run_if(game_running) .run_if(alive) .after(PhysicsSet::Sync) .after(sync::position_to_transform) .after(handle_wants_acceleration), ); app.add_systems( Update, ( handle_input.run_if(in_control).run_if(game_running), handle_collisions.run_if(game_running), handle_damage.run_if(game_running), ), ); app.add_systems( PostUpdate, (handle_vehicle_enter_exit.run_if(game_running),), ); app.add_event::(); } } #[derive(Copy, Clone)] pub enum DamageType { Unknown, Mental, Trauma, GForce, Asphyxiation, Depressurization, //Poison, Radiation, //Freeze, //Burn, } #[derive(Event)] pub struct VehicleEnterExitEvent { vehicle: Entity, driver: Entity, name: Option, is_entering: bool, is_player: bool, } #[derive(Component)] pub struct Actor { pub id: String, pub name: Option, pub camdistance: f32, } impl Default for Actor { fn default() -> Self { Self { id: "".to_string(), name: None, camdistance: 7.5, } } } #[derive(Component)] pub struct HitPoints { pub current: f32, pub max: f32, pub damage: f32, pub damagetype: DamageType, } impl Default for HitPoints { fn default() -> Self { Self { current: 100.0, max: 100.0, damage: 0.0, damagetype: DamageType::Unknown, } } } #[derive(Component)] pub struct ExperiencesGForce { pub gforce: f32, pub damage_threshold: f32, pub visual_effect_threshold: f32, pub visual_effect: f32, pub last_linear_velocity: DVec3, pub gravitational_component: DVec3, pub ignore_gforce_seconds: f32, } impl Default for ExperiencesGForce { fn default() -> Self { Self { gforce: 0.0, damage_threshold: 100.0, visual_effect_threshold: 20.0, visual_effect: 0.0, last_linear_velocity: DVec3::ZERO, gravitational_component: DVec3::ZERO, ignore_gforce_seconds: 0.01, } } } #[derive(Component, Default)] pub struct WantsAcceleration { pub direction: DVec3, pub brake: bool, } #[derive(Component)] pub struct Player; // Attached to the suit of the player #[derive(Component)] pub struct PlayerCollider; // Attached to the collider of 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 #[derive(Component)] pub struct JustNowEnteredVehicle; #[derive(Component)] pub struct ActorEnteringVehicle; #[derive(Component)] pub struct ActorVehicleBeingEntered; #[derive(Component)] pub struct HiddenInsideVehicle; #[derive(Component)] pub struct MessageOnVehicleEntry(pub String); #[derive(Component)] pub struct PlayersFlashLight; #[derive(Component)] pub struct MirrorLight; #[derive(Component)] pub struct WantsMaxRotation(pub f64); #[derive(Component)] pub struct WantsMaxVelocity(pub f64); #[derive(Component)] pub struct WantsToLookAt(pub String); #[derive(Component)] pub struct WantsRotationChange(pub Vec3); // Vec3 = (pitch, yaw, rot) #[derive(Component)] pub struct WantsMatchVelocityWith(pub String); #[derive(Component)] pub struct Identifier(pub String); #[derive(Component)] pub struct OrbitsJupiter; #[derive(Component)] pub struct LifeForm { pub is_alive: bool, pub is_radioactively_damaged: bool, pub adrenaline: f32, pub adrenaline_baseline: f32, pub adrenaline_jolt: f32, } impl Default for LifeForm { fn default() -> Self { Self { is_alive: true, is_radioactively_damaged: false, adrenaline: 0.3, adrenaline_baseline: 0.3, adrenaline_jolt: 0.0, } } } #[derive(Component)] pub struct Vehicle { stored_drivers_collider: Option, } impl Default for Vehicle { fn default() -> Self { Self { stored_drivers_collider: None, } } } #[derive(Copy, Clone, PartialEq)] pub enum EngineType { Monopropellant, Ion, } #[derive(Component, Copy, Clone)] pub struct Engine { pub thrust_forward: f32, pub thrust_back: f32, pub thrust_sideways: f32, pub reaction_wheels: f32, pub engine_type: EngineType, pub warmup_seconds: f32, pub current_warmup: f32, // between 0.0 and 1.0 pub current_boost_factor: f64, pub currently_firing: bool, pub currently_matching_velocity: bool, } impl Default for Engine { fn default() -> Self { Self { thrust_forward: 1.0, thrust_back: 1.0, thrust_sideways: 1.0, reaction_wheels: 1.0, engine_type: EngineType::Monopropellant, warmup_seconds: 1.5, current_warmup: 0.0, current_boost_factor: 1.0, currently_firing: false, currently_matching_velocity: false, } } } #[derive(Component)] pub struct Suit { pub oxygen: f32, pub oxygen_max: f32, pub integrity: f32, // [0.0 - 1.0] } impl Default for Suit { fn default() -> Self { SUIT_SIMPLE } } const SUIT_SIMPLE: Suit = Suit { oxygen: nature::OXY_D, oxygen_max: nature::OXY_D, integrity: 1.0, }; #[derive(Component)] pub struct Battery { pub power: f32, // Watt-seconds pub capacity: f32, // Watt-seconds pub overloaded_recovering: bool, } impl Default for Battery { fn default() -> Self { Self { power: 10.0 * 3600.0, capacity: 10.0 * 3600.0, // 10Wh overloaded_recovering: false, } } } pub fn update_power( time: Res