diff --git a/src/actor.rs b/src/actor.rs index 8a05d5d..36d941f 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -10,16 +10,14 @@ // // This module manages the internal states of individual characters, // such as their resources, the damage they receive, and interactions -// between characters and with vehicles. It also handles cheats. +// between characters and with vehicles. // // This module should never handle any visual aspects directly. use bevy::prelude::*; use bevy_xpbd_3d::prelude::*; -use bevy::scene::SceneInstance; use bevy::math::DVec3; use crate::prelude::*; -use std::collections::HashMap; pub const ENGINE_SPEED_FACTOR: f32 = 30.0; const MAX_TRANSMISSION_DISTANCE: f32 = 100.0; @@ -28,9 +26,6 @@ const MAX_INTERACT_DISTANCE: f32 = 50.0; pub struct ActorPlugin; impl Plugin for ActorPlugin { fn build(&self, app: &mut App) { - app.add_systems(PreUpdate, ( - handle_player_death, - )); app.add_systems(FixedUpdate, ( update_physics_lifeforms, update_power, @@ -44,15 +39,11 @@ impl Plugin for ActorPlugin { handle_input, handle_collisions, handle_damage, - handle_cheats, )); app.add_systems(PostUpdate, ( handle_vehicle_enter_exit, - update_id2pos, )); app.add_event::(); - app.add_event::(); - app.insert_resource(Id2Pos(HashMap::new())); } } @@ -69,8 +60,6 @@ pub enum DamageType { //Burn, } -#[derive(Event)] pub struct PlayerDiesEvent(pub DamageType); - #[derive(Event)] pub struct VehicleEnterExitEvent { vehicle: Entity, @@ -142,7 +131,6 @@ impl Default for ExperiencesGForce { fn default() -> Self { Self { #[derive(Component)] pub struct WantsMaxRotation(pub f64); #[derive(Component)] pub struct WantsMaxVelocity(pub f64); #[derive(Component)] pub struct Identifier(pub String); -#[derive(Resource)] pub struct Id2Pos(pub HashMap); #[derive(Component)] pub struct LifeForm { @@ -308,7 +296,7 @@ pub fn handle_input( q_vehicles: Query<(Entity, &Transform), (With, Without, Without)>, mut ew_conv: EventWriter, mut ew_vehicle: EventWriter, - mut ew_playerdies: EventWriter, + mut ew_playerdies: EventWriter, mut ew_sfx: EventWriter, q_player_drives: Query>, ) { @@ -377,7 +365,7 @@ pub fn handle_input( } else if keyboard_input.just_pressed(settings.key_restart) { settings.god_mode = false; - ew_playerdies.send(PlayerDiesEvent(DamageType::Mental)); + ew_playerdies.send(game::PlayerDiesEvent(DamageType::Mental)); } } @@ -498,78 +486,8 @@ fn handle_wants_maxvelocity( } } -fn handle_player_death( - mut cmd: Commands, - mut er_playerdies: EventReader, - q_scenes: Query<(Entity, &SceneInstance), With>, - q_noscenes: Query, Without)>, - mut scene_spawner: ResMut, - mut active_asteroids: ResMut, - mut ew_effect: EventWriter, - mut ew_deathscreen: EventWriter, - mut log: ResMut, - mut settings: ResMut, -) { - for death in er_playerdies.read() { - if settings.god_mode { - return; - } - settings.reset_player_settings(); - active_asteroids.0.clear(); - for entity in &q_noscenes { - cmd.entity(entity).despawn(); - } - for (entity, sceneinstance) in &q_scenes { - cmd.entity(entity).despawn(); - scene_spawner.despawn_instance(**sceneinstance); - } - log.clear(); - - match death.0 { - DamageType::Mental => { - settings.death_cause = "Brain Damage".to_string(); - ew_effect.send(visual::SpawnEffectEvent { - class: visual::Effects::FadeIn(Color::BLACK), - duration: 4.0, - }); - } - DamageType::Asphyxiation => { - settings.death_cause = "Suffocation".to_string(); - ew_effect.send(visual::SpawnEffectEvent { - class: visual::Effects::FadeIn(Color::BLACK), - duration: 1.0, - }); - } - DamageType::Trauma => { - settings.death_cause = "Trauma".to_string(); - ew_effect.send(visual::SpawnEffectEvent { - class: visual::Effects::FadeIn(Color::MAROON), - duration: 1.0, - }); - } - DamageType::GForce => { - settings.death_cause = "Trauma from excessive g forces".to_string(); - ew_effect.send(visual::SpawnEffectEvent { - class: visual::Effects::FadeIn(Color::MAROON), - duration: 1.0, - }); - } - _ => { - settings.death_cause = "Unknown".to_string(); - ew_effect.send(visual::SpawnEffectEvent { - class: visual::Effects::FadeIn(Color::MAROON), - duration: 1.0, - }); - } - } - - ew_deathscreen.send(menu::DeathScreenEvent::Show); - return; - } -} - fn handle_damage( - mut ew_playerdies: EventWriter, + mut ew_playerdies: EventWriter, mut q_hp: Query<(&mut HitPoints, Option<&Player>), Changed>, settings: Res, ) { @@ -579,7 +497,7 @@ fn handle_damage( hp.current -= hp.damage; } if hp.current <= 0.0 { - ew_playerdies.send(PlayerDiesEvent(hp.damagetype)); + ew_playerdies.send(game::PlayerDiesEvent(hp.damagetype)); } } else { @@ -618,102 +536,3 @@ fn handle_gforce( } } } - -fn update_id2pos( - mut id2pos: ResMut, - q_id: Query<(&Position, &Identifier)>, -) { - id2pos.0.clear(); - for (pos, id) in &q_id { - id2pos.0.insert(id.0.clone(), pos.0); - } -} - -fn handle_cheats( - key_input: Res>, - mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With>, - mut q_life: Query<(&mut actor::LifeForm, &mut actor::ExperiencesGForce), With>, - q_target: Query<(&Transform, &Position, Option<&LinearVelocity>), (With, Without)>, - mut ew_playerdies: EventWriter, - mut settings: ResMut, - id2pos: Res, - mut ew_sfx: EventWriter, -) { - if q_player.is_empty() || q_life.is_empty() { - return; - } - let (trans, mut pos, mut v) = q_player.get_single_mut().unwrap(); - let (mut lifeform, mut gforce) = q_life.get_single_mut().unwrap(); - let boost = if key_input.pressed(KeyCode::ShiftLeft) { - 1e6 - } else { - 1e3 - }; - if key_input.just_pressed(settings.key_cheat_god_mode) { - settings.god_mode ^= true; - if settings.god_mode { - ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle)); - } - } - if !settings.god_mode && !settings.dev_mode { - return; - } - - if key_input.just_pressed(settings.key_cheat_stop) { - gforce.ignore_gforce_seconds = 1.0; - v.0 = DVec3::ZERO; - } - if key_input.pressed(settings.key_cheat_speed) { - gforce.ignore_gforce_seconds = 1.0; - v.0 += DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, boost)); - } - if key_input.pressed(settings.key_cheat_speed_backward) { - gforce.ignore_gforce_seconds = 1.0; - v.0 += DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, -boost)); - } - if key_input.just_pressed(settings.key_cheat_teleport) { - if let Ok((transform, target_pos, target_v)) = q_target.get_single() { - let offset: DVec3 = 4.0 * (**pos - **target_pos).normalize() * transform.scale.as_dvec3(); - pos.0 = **target_pos + offset; - if let Some(target_v) = target_v { - *v = target_v.clone(); - } - } - } - - if !settings.dev_mode { - return; - } - - if key_input.just_pressed(settings.key_cheat_pizza) { - if let Some(target) = id2pos.0.get(&"pizzeria".to_string()) { - pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0); - gforce.ignore_gforce_seconds = 1.0; - } - } - if key_input.just_pressed(settings.key_cheat_farview1) { - if let Some(target) = id2pos.0.get(&"busstopclippy2".to_string()) { - pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0); - gforce.ignore_gforce_seconds = 1.0; - } - } - if key_input.just_pressed(settings.key_cheat_farview2) { - if let Some(target) = id2pos.0.get(&"busstopclippy3".to_string()) { - pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0); - gforce.ignore_gforce_seconds = 1.0; - } - } - if key_input.pressed(settings.key_cheat_adrenaline_zero) { - lifeform.adrenaline = 0.0; - } - if key_input.pressed(settings.key_cheat_adrenaline_mid) { - lifeform.adrenaline = 0.5; - } - if key_input.pressed(settings.key_cheat_adrenaline_max) { - lifeform.adrenaline = 1.0; - } - if key_input.just_pressed(settings.key_cheat_die) { - settings.god_mode = false; - ew_playerdies.send(actor::PlayerDiesEvent(actor::DamageType::Trauma)); - } -} diff --git a/src/camera.rs b/src/camera.rs index ef3109c..ff8f583 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -615,7 +615,7 @@ pub fn update_map_only_object_visibility( q_camera: Query<&Transform, With>, q_player: Query<&Position, With>, mut q_onlyinmap: Query<(&mut Visibility, &ShowOnlyInMap), Without>, - id2pos: Res, + id2pos: Res, ) { if q_camera.is_empty() || q_player.is_empty() { return; diff --git a/src/chat.rs b/src/chat.rs index cb0b478..5c4a79e 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -792,7 +792,7 @@ pub fn handle_chat_scripts( mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With>, mut ew_sfx: EventWriter, mut ew_effect: EventWriter, - id2pos: Res, + id2pos: Res, ) { for script in er_chatscript.read() { // Parse the script string diff --git a/src/cmd.rs b/src/cmd.rs index b3c5210..89637bc 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -531,7 +531,7 @@ fn spawn_entities( mut meshes: ResMut>, mut materials: ResMut>, mut materials_jupiter: ResMut>, - mut id2pos: ResMut, + mut id2pos: ResMut, settings: Res, ) { for state_wrapper in er_spawn.read() { diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..426d119 --- /dev/null +++ b/src/game.rs @@ -0,0 +1,201 @@ +// ▄████████▄ + ███ + ▄█████████ ███ + +// ███▀ ▀███ + + ███ ███▀ + ███ + + +// ███ + ███ ███ ███ █████████ ███ ███ ███ ███ +// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███ +// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███ +// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███ +// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████ +// + + + ███ +// + ▀████████████████████████████████████████████████████▀ +// +// This module handles player input, and coordinates interplay between other modules + +use crate::prelude::*; +use bevy::prelude::*; +use bevy::math::DVec3; +use bevy::scene::SceneInstance; +use bevy_xpbd_3d::prelude::*; +use std::collections::HashMap; + +pub struct GamePlugin; +impl Plugin for GamePlugin { + fn build(&self, app: &mut App) { + app.add_systems(Update, handle_cheats); + app.add_systems(PreUpdate, handle_player_death); + app.add_systems(PostUpdate, update_id2pos); + app.insert_resource(Id2Pos(HashMap::new())); + app.add_event::(); + } +} + +#[derive(Event)] pub struct PlayerDiesEvent(pub actor::DamageType); +#[derive(Resource)] pub struct Id2Pos(pub HashMap); + +fn handle_player_death( + mut cmd: Commands, + mut er_playerdies: EventReader, + q_scenes: Query<(Entity, &SceneInstance), With>, + q_noscenes: Query, Without)>, + mut scene_spawner: ResMut, + mut active_asteroids: ResMut, + mut ew_effect: EventWriter, + mut ew_deathscreen: EventWriter, + mut log: ResMut, + mut settings: ResMut, +) { + for death in er_playerdies.read() { + if settings.god_mode { + return; + } + settings.reset_player_settings(); + active_asteroids.0.clear(); + for entity in &q_noscenes { + cmd.entity(entity).despawn(); + } + for (entity, sceneinstance) in &q_scenes { + cmd.entity(entity).despawn(); + scene_spawner.despawn_instance(**sceneinstance); + } + log.clear(); + + match death.0 { + actor::DamageType::Mental => { + settings.death_cause = "Brain Damage".to_string(); + ew_effect.send(visual::SpawnEffectEvent { + class: visual::Effects::FadeIn(Color::BLACK), + duration: 4.0, + }); + } + actor::DamageType::Asphyxiation => { + settings.death_cause = "Suffocation".to_string(); + ew_effect.send(visual::SpawnEffectEvent { + class: visual::Effects::FadeIn(Color::BLACK), + duration: 1.0, + }); + } + actor::DamageType::Trauma => { + settings.death_cause = "Trauma".to_string(); + ew_effect.send(visual::SpawnEffectEvent { + class: visual::Effects::FadeIn(Color::MAROON), + duration: 1.0, + }); + } + actor::DamageType::GForce => { + settings.death_cause = "Trauma from excessive g forces".to_string(); + ew_effect.send(visual::SpawnEffectEvent { + class: visual::Effects::FadeIn(Color::MAROON), + duration: 1.0, + }); + } + _ => { + settings.death_cause = "Unknown".to_string(); + ew_effect.send(visual::SpawnEffectEvent { + class: visual::Effects::FadeIn(Color::MAROON), + duration: 1.0, + }); + } + } + + ew_deathscreen.send(menu::DeathScreenEvent::Show); + return; + } +} + +fn handle_cheats( + key_input: Res>, + mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With>, + mut q_life: Query<(&mut actor::LifeForm, &mut actor::ExperiencesGForce), With>, + q_target: Query<(&Transform, &Position, Option<&LinearVelocity>), (With, Without)>, + mut ew_playerdies: EventWriter, + mut settings: ResMut, + id2pos: Res, + mut ew_sfx: EventWriter, +) { + if q_player.is_empty() || q_life.is_empty() { + return; + } + let (trans, mut pos, mut v) = q_player.get_single_mut().unwrap(); + let (mut lifeform, mut gforce) = q_life.get_single_mut().unwrap(); + let boost = if key_input.pressed(KeyCode::ShiftLeft) { + 1e6 + } else { + 1e3 + }; + if key_input.just_pressed(settings.key_cheat_god_mode) { + settings.god_mode ^= true; + if settings.god_mode { + ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle)); + } + } + if !settings.god_mode && !settings.dev_mode { + return; + } + + if key_input.just_pressed(settings.key_cheat_stop) { + gforce.ignore_gforce_seconds = 1.0; + v.0 = DVec3::ZERO; + } + if key_input.pressed(settings.key_cheat_speed) { + gforce.ignore_gforce_seconds = 1.0; + v.0 += DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, boost)); + } + if key_input.pressed(settings.key_cheat_speed_backward) { + gforce.ignore_gforce_seconds = 1.0; + v.0 += DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, -boost)); + } + if key_input.just_pressed(settings.key_cheat_teleport) { + if let Ok((transform, target_pos, target_v)) = q_target.get_single() { + let offset: DVec3 = 4.0 * (**pos - **target_pos).normalize() * transform.scale.as_dvec3(); + pos.0 = **target_pos + offset; + if let Some(target_v) = target_v { + *v = target_v.clone(); + } + } + } + + if !settings.dev_mode { + return; + } + + if key_input.just_pressed(settings.key_cheat_pizza) { + if let Some(target) = id2pos.0.get(&"pizzeria".to_string()) { + pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0); + gforce.ignore_gforce_seconds = 1.0; + } + } + if key_input.just_pressed(settings.key_cheat_farview1) { + if let Some(target) = id2pos.0.get(&"busstopclippy2".to_string()) { + pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0); + gforce.ignore_gforce_seconds = 1.0; + } + } + if key_input.just_pressed(settings.key_cheat_farview2) { + if let Some(target) = id2pos.0.get(&"busstopclippy3".to_string()) { + pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0); + gforce.ignore_gforce_seconds = 1.0; + } + } + if key_input.pressed(settings.key_cheat_adrenaline_zero) { + lifeform.adrenaline = 0.0; + } + if key_input.pressed(settings.key_cheat_adrenaline_mid) { + lifeform.adrenaline = 0.5; + } + if key_input.pressed(settings.key_cheat_adrenaline_max) { + lifeform.adrenaline = 1.0; + } + if key_input.just_pressed(settings.key_cheat_die) { + settings.god_mode = false; + ew_playerdies.send(PlayerDiesEvent(actor::DamageType::Trauma)); + } +} + +fn update_id2pos( + mut id2pos: ResMut, + q_id: Query<(&Position, &actor::Identifier)>, +) { + id2pos.0.clear(); + for (pos, id) in &q_id { + id2pos.0.insert(id.0.clone(), pos.0); + } +} diff --git a/src/main.rs b/src/main.rs index 8a9751a..30d75fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ pub mod audio; pub mod camera; pub mod chat; pub mod cmd; +pub mod game; pub mod hud; pub mod load; pub mod menu; @@ -26,7 +27,7 @@ pub mod visual; pub mod world; pub mod prelude { - pub use crate::{actor, audio, camera, chat, cmd, hud, load, menu, nature, var, visual, world}; + pub use crate::{actor, audio, camera, chat, cmd, game, hud, load, menu, nature, var, visual, world}; pub use crate::var::{FONT, Settings}; pub use crate::load::load_asset; pub fn bool2vis(boolean: bool) -> bevy::prelude::Visibility { @@ -141,6 +142,7 @@ impl Plugin for OutFlyPlugin { camera::CameraPlugin, chat::ChatPlugin, cmd::CmdPlugin, + game::GamePlugin, menu::MenuPlugin, visual::VisualPlugin, hud::HudPlugin, @@ -165,7 +167,7 @@ fn setup( window.cursor.grab_mode = CursorGrabMode::Locked; window.cursor.visible = false; window.mode = opt.window_mode_initial; - window.title = "OutFly".to_string(); + window.title = var::WINDOW_TITLE.to_string(); } } diff --git a/src/var.rs b/src/var.rs index 1835c5b..8a8c535 100644 --- a/src/var.rs +++ b/src/var.rs @@ -19,6 +19,7 @@ use toml_edit::DocumentMut; use std::env; use std::fs; +pub const WINDOW_TITLE: &str = "OutFly"; pub const FONT: &str = "fonts/Yupiter-Regular.ttf"; pub const SCOPE_SEPARATOR: &str = "$"; diff --git a/src/world.rs b/src/world.rs index 05a790b..4860a98 100644 --- a/src/world.rs +++ b/src/world.rs @@ -178,7 +178,7 @@ fn spawn_despawn_asteroids( mut db: ResMut, q_asteroid: Query<(&Position, &SceneInstance), With>, mut last_player_cell: Local, - id2pos: Res, + id2pos: Res, asset_server: Res, ) { if !timer.0.tick(time.delta()).just_finished() || q_player.is_empty() {