2024-05-12 21:57:21 +00:00
|
|
|
// ▄████████▄ + ███ + ▄█████████ ███ +
|
|
|
|
// ███▀ ▀███ + + ███ ███▀ + ███ + +
|
|
|
|
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
|
|
|
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
|
|
|
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
|
|
|
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
|
|
|
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
|
|
|
// + + + ███
|
|
|
|
// + ▀████████████████████████████████████████████████████▀
|
|
|
|
//
|
|
|
|
// This module handles player input, and coordinates interplay between other modules
|
|
|
|
|
|
|
|
use crate::prelude::*;
|
2024-11-18 02:50:25 +00:00
|
|
|
use actor::PlayerCamera;
|
2024-07-09 00:28:55 +00:00
|
|
|
use bevy::color::palettes::css;
|
2024-05-22 02:57:20 +00:00
|
|
|
use bevy::prelude::*;
|
2024-05-12 21:57:21 +00:00
|
|
|
use bevy::scene::SceneInstance;
|
2024-05-22 02:57:20 +00:00
|
|
|
use bevy::window::{PrimaryWindow, Window, WindowMode};
|
2024-05-12 21:57:21 +00:00
|
|
|
use bevy_xpbd_3d::prelude::*;
|
2024-11-18 03:14:17 +00:00
|
|
|
use fastrand;
|
2024-05-12 21:57:21 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2024-06-11 00:59:10 +00:00
|
|
|
pub const CHEAT_WARP_1: &str = "pizzeria";
|
|
|
|
pub const CHEAT_WARP_2: &str = "busstopclippy2";
|
|
|
|
pub const CHEAT_WARP_3: &str = "busstopclippy3";
|
2024-11-18 02:50:25 +00:00
|
|
|
pub const RACE_TARGET_RADIUS: f64 = 5.0;
|
2024-11-18 03:52:29 +00:00
|
|
|
pub const RACE_SCORE_ACHIEVEMENT: u64 = 25;
|
2024-06-11 00:59:10 +00:00
|
|
|
|
2024-05-12 21:57:21 +00:00
|
|
|
pub struct GamePlugin;
|
|
|
|
impl Plugin for GamePlugin {
|
|
|
|
fn build(&self, app: &mut App) {
|
2024-06-13 00:18:22 +00:00
|
|
|
app.add_systems(Startup, setup);
|
2024-10-09 22:49:01 +00:00
|
|
|
app.add_systems(
|
|
|
|
Update,
|
|
|
|
handle_cheats.run_if(game_running).run_if(in_control),
|
|
|
|
);
|
|
|
|
app.add_systems(Update, debug.run_if(game_running));
|
2024-05-13 21:50:53 +00:00
|
|
|
app.add_systems(PostUpdate, handle_game_event);
|
2024-10-09 22:49:01 +00:00
|
|
|
app.add_systems(Update, handle_window_focus);
|
2024-10-10 04:08:22 +00:00
|
|
|
app.add_systems(PreUpdate, handle_player_death);
|
2024-06-11 23:30:07 +00:00
|
|
|
app.add_systems(
|
|
|
|
PostUpdate,
|
2024-10-09 22:49:01 +00:00
|
|
|
update_id2pos
|
|
|
|
.run_if(game_running)
|
|
|
|
.in_set(bevy_xpbd_3d::plugins::sync::SyncSet::PositionToTransform),
|
2024-06-11 23:30:07 +00:00
|
|
|
);
|
2024-06-11 00:46:31 +00:00
|
|
|
app.add_systems(PostUpdate, update_id2v);
|
2024-05-22 02:57:20 +00:00
|
|
|
app.add_systems(
|
|
|
|
Update,
|
2024-10-09 22:49:01 +00:00
|
|
|
handle_achievement_event
|
|
|
|
.run_if(game_running)
|
|
|
|
.run_if(on_event::<AchievementEvent>()),
|
2024-05-22 02:57:20 +00:00
|
|
|
);
|
2024-10-09 22:49:01 +00:00
|
|
|
app.add_systems(Update, check_achievements.run_if(game_running));
|
2024-11-18 03:52:12 +00:00
|
|
|
app.add_systems(Update, handle_race.run_if(alive).run_if(game_running));
|
2024-05-12 21:57:21 +00:00
|
|
|
app.insert_resource(Id2Pos(HashMap::new()));
|
2024-06-11 00:46:31 +00:00
|
|
|
app.insert_resource(Id2V(HashMap::new()));
|
2024-06-10 22:58:56 +00:00
|
|
|
app.insert_resource(JupiterPos(DVec3::ZERO));
|
2024-05-13 23:24:57 +00:00
|
|
|
app.insert_resource(var::AchievementTracker::default());
|
2024-06-02 19:03:40 +00:00
|
|
|
app.insert_resource(var::Settings::default());
|
|
|
|
app.insert_resource(var::GameVars::default());
|
2024-11-18 02:50:25 +00:00
|
|
|
app.insert_resource(RaceState::default());
|
2024-05-22 02:57:20 +00:00
|
|
|
app.insert_resource(AchievementCheckTimer(Timer::from_seconds(
|
|
|
|
1.0,
|
|
|
|
TimerMode::Repeating,
|
|
|
|
)));
|
2024-05-12 21:57:21 +00:00
|
|
|
app.add_event::<PlayerDiesEvent>();
|
2024-05-13 18:21:56 +00:00
|
|
|
app.add_event::<GameEvent>();
|
2024-05-13 23:24:57 +00:00
|
|
|
app.add_event::<AchievementEvent>();
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
#[derive(Event)]
|
|
|
|
pub struct PlayerDiesEvent(pub actor::DamageType);
|
|
|
|
#[derive(Resource)]
|
|
|
|
pub struct Id2Pos(pub HashMap<String, DVec3>);
|
|
|
|
#[derive(Resource)]
|
2024-06-11 00:46:31 +00:00
|
|
|
pub struct Id2V(pub HashMap<String, DVec3>);
|
|
|
|
#[derive(Resource)]
|
2024-06-10 22:58:56 +00:00
|
|
|
pub struct JupiterPos(pub DVec3);
|
|
|
|
#[derive(Resource)]
|
2024-05-22 02:57:20 +00:00
|
|
|
pub struct AchievementCheckTimer(pub Timer);
|
2024-11-18 02:50:25 +00:00
|
|
|
#[derive(Resource, Default)]
|
|
|
|
pub struct RaceState {
|
|
|
|
pub initialized: bool,
|
|
|
|
pub started: bool,
|
|
|
|
pub start_countdown: f64,
|
|
|
|
pub timeout: f64,
|
2024-11-18 03:52:29 +00:00
|
|
|
pub score: f64,
|
2024-11-18 02:50:25 +00:00
|
|
|
}
|
|
|
|
#[derive(Component)]
|
|
|
|
pub struct RaceTarget;
|
2024-05-12 21:57:21 +00:00
|
|
|
|
2024-05-13 23:24:57 +00:00
|
|
|
#[derive(Event)]
|
|
|
|
pub enum AchievementEvent {
|
|
|
|
RepairSuit,
|
|
|
|
TalkTo(String),
|
|
|
|
RideVehicle(String),
|
|
|
|
DrinkPizza,
|
|
|
|
InJupitersShadow,
|
2024-05-14 03:33:35 +00:00
|
|
|
FindEarth,
|
2024-11-18 03:52:29 +00:00
|
|
|
RaceScore,
|
2024-05-13 23:24:57 +00:00
|
|
|
}
|
|
|
|
|
2024-05-13 18:21:56 +00:00
|
|
|
#[derive(Event)]
|
|
|
|
pub enum GameEvent {
|
|
|
|
SetAR(Turn),
|
2024-06-13 01:26:19 +00:00
|
|
|
SetMusic(Cycle),
|
2024-06-13 02:06:15 +00:00
|
|
|
SetSound(Cycle),
|
2024-05-13 18:21:56 +00:00
|
|
|
SetMap(Turn),
|
|
|
|
SetFullscreen(Turn),
|
2024-05-13 18:53:08 +00:00
|
|
|
SetMenu(Turn),
|
2024-05-13 19:11:27 +00:00
|
|
|
SetThirdPerson(Turn),
|
|
|
|
SetRotationStabilizer(Turn),
|
|
|
|
SetShadows(Turn),
|
2024-09-22 01:40:12 +00:00
|
|
|
UpdateFlashlight,
|
2024-05-13 23:24:57 +00:00
|
|
|
Achievement(String),
|
2024-10-26 17:36:54 +00:00
|
|
|
PhoneCall,
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum Turn {
|
|
|
|
On,
|
|
|
|
Off,
|
|
|
|
Toggle,
|
2024-10-26 22:20:46 +00:00
|
|
|
Unchanged,
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Turn {
|
|
|
|
pub fn to_bool(&self, current_state: bool) -> bool {
|
|
|
|
match self {
|
|
|
|
Turn::On => true,
|
|
|
|
Turn::Off => false,
|
|
|
|
Turn::Toggle => !current_state,
|
2024-10-26 22:20:46 +00:00
|
|
|
Turn::Unchanged => current_state,
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:19 +00:00
|
|
|
pub enum Cycle {
|
|
|
|
First,
|
|
|
|
Last,
|
|
|
|
Next,
|
|
|
|
Previous,
|
2024-10-26 22:20:46 +00:00
|
|
|
Unchanged,
|
2024-06-13 01:26:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Cycle {
|
|
|
|
pub fn to_index<T>(&self, current_index: usize, vector: &Vec<T>) -> Option<usize> {
|
|
|
|
if vector.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
match self {
|
|
|
|
Cycle::First => Some(0),
|
|
|
|
Cycle::Last => Some(vector.len() - 1),
|
|
|
|
Cycle::Next => {
|
|
|
|
let index = current_index.saturating_add(1);
|
|
|
|
if index >= vector.len() {
|
|
|
|
Some(0)
|
|
|
|
} else {
|
|
|
|
Some(index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Cycle::Previous => {
|
|
|
|
if current_index == 0 {
|
|
|
|
Some(vector.len() - 1)
|
|
|
|
} else {
|
|
|
|
Some(current_index - 1)
|
|
|
|
}
|
|
|
|
}
|
2024-10-27 01:01:47 +00:00
|
|
|
Cycle::Unchanged => Some(current_index),
|
2024-06-13 01:26:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-18 02:50:25 +00:00
|
|
|
pub fn setup(
|
|
|
|
mut commands: Commands,
|
|
|
|
mut settings: ResMut<Settings>,
|
|
|
|
prefs: ResMut<var::Preferences>,
|
|
|
|
asset_server: Res<AssetServer>,
|
|
|
|
) {
|
2024-06-13 00:18:22 +00:00
|
|
|
settings.hud_active = prefs.augmented_reality;
|
2024-06-13 01:26:19 +00:00
|
|
|
settings.radio_mode = prefs.radio_station;
|
2024-06-13 02:06:15 +00:00
|
|
|
settings.set_noise_cancellation_mode(prefs.noise_cancellation_mode);
|
2024-06-13 00:18:22 +00:00
|
|
|
settings.third_person = prefs.third_person;
|
|
|
|
settings.shadows_sun = prefs.shadows_sun;
|
2024-06-16 23:43:23 +00:00
|
|
|
settings.ar_avatar = prefs.avatar;
|
2024-11-18 02:50:25 +00:00
|
|
|
|
|
|
|
// Setup Race
|
|
|
|
let mut entitycmd = commands.spawn((
|
|
|
|
RaceTarget,
|
|
|
|
actor::OrbitsJupiter,
|
|
|
|
bevy::pbr::NotShadowCaster,
|
|
|
|
bevy::pbr::NotShadowReceiver,
|
|
|
|
Position::default(),
|
|
|
|
actor::WantsToLookAt(cmd::ID_SPECIAL_PLAYERCAM.to_string()),
|
|
|
|
LinearVelocity::default(),
|
|
|
|
SpatialBundle {
|
|
|
|
transform: Transform::from_scale(Vec3::splat(RACE_TARGET_RADIUS as f32)),
|
|
|
|
visibility: Visibility::Hidden,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
));
|
|
|
|
load_asset("marker_race", &mut entitycmd, &*asset_server);
|
2024-06-13 00:18:22 +00:00
|
|
|
}
|
|
|
|
|
2024-05-13 19:49:30 +00:00
|
|
|
pub fn handle_game_event(
|
2024-05-13 18:21:56 +00:00
|
|
|
mut settings: ResMut<Settings>,
|
|
|
|
mut er_game: EventReader<GameEvent>,
|
2024-10-26 17:36:54 +00:00
|
|
|
mut ew_conv: EventWriter<chat::StartConversationEvent>,
|
2024-05-13 18:21:56 +00:00
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
|
|
|
mut ew_updateoverlays: EventWriter<hud::UpdateOverlayVisibility>,
|
2024-05-13 19:37:57 +00:00
|
|
|
mut ew_updatemenu: EventWriter<menu::UpdateMenuEvent>,
|
2024-05-13 18:21:56 +00:00
|
|
|
mut ew_togglemusic: EventWriter<audio::ToggleMusicEvent>,
|
|
|
|
mut q_window: Query<&mut Window, With<PrimaryWindow>>,
|
2024-05-13 19:11:27 +00:00
|
|
|
mut q_light: Query<&mut DirectionalLight>,
|
2024-10-26 22:20:46 +00:00
|
|
|
mut q_flashlight: Query<(&mut Visibility, &mut SpotLight), With<actor::PlayersFlashLight>>,
|
2024-05-13 18:21:56 +00:00
|
|
|
mut mapcam: ResMut<camera::MapCam>,
|
2024-05-13 23:24:57 +00:00
|
|
|
mut log: ResMut<hud::Log>,
|
2024-05-13 18:21:56 +00:00
|
|
|
opt: Res<var::CommandLineOptions>,
|
2024-06-13 00:18:22 +00:00
|
|
|
mut prefs: ResMut<var::Preferences>,
|
2024-05-13 18:21:56 +00:00
|
|
|
) {
|
|
|
|
for event in er_game.read() {
|
|
|
|
match event {
|
|
|
|
GameEvent::SetAR(turn) => {
|
|
|
|
settings.hud_active = turn.to_bool(settings.hud_active);
|
|
|
|
ew_togglemusic.send(audio::ToggleMusicEvent());
|
|
|
|
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
2024-06-13 00:18:22 +00:00
|
|
|
prefs.augmented_reality = settings.hud_active;
|
|
|
|
prefs.save();
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
2024-06-13 02:06:15 +00:00
|
|
|
GameEvent::SetMusic(cycle) => {
|
|
|
|
match cycle.to_index(settings.radio_mode, &settings.radio_modes) {
|
|
|
|
Some(mode) => {
|
|
|
|
settings.radio_mode = mode;
|
2024-06-13 01:26:19 +00:00
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
ew_togglemusic.send(audio::ToggleMusicEvent());
|
2024-06-13 01:26:19 +00:00
|
|
|
prefs.radio_station = settings.radio_mode;
|
2024-06-13 00:18:22 +00:00
|
|
|
prefs.save();
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
2024-06-13 02:06:15 +00:00
|
|
|
GameEvent::SetSound(cycle) => {
|
2024-06-16 23:16:53 +00:00
|
|
|
match cycle.to_index(
|
|
|
|
settings.noise_cancellation_mode,
|
|
|
|
&settings.noise_cancellation_modes,
|
|
|
|
) {
|
2024-06-13 02:06:15 +00:00
|
|
|
Some(mode) => {
|
|
|
|
settings.set_noise_cancellation_mode(mode);
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
ew_togglemusic.send(audio::ToggleMusicEvent());
|
2024-06-13 02:06:15 +00:00
|
|
|
prefs.noise_cancellation_mode = settings.noise_cancellation_mode;
|
2024-06-13 00:18:22 +00:00
|
|
|
prefs.save();
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
GameEvent::SetMap(turn) => {
|
|
|
|
settings.map_active = turn.to_bool(settings.map_active);
|
|
|
|
if settings.map_active {
|
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Woosh));
|
|
|
|
}
|
|
|
|
*mapcam = camera::MapCam::default();
|
|
|
|
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
|
|
|
}
|
|
|
|
GameEvent::SetFullscreen(turn) => {
|
|
|
|
for mut window in &mut q_window {
|
|
|
|
let current_state = window.mode != WindowMode::Windowed;
|
2024-06-13 00:18:22 +00:00
|
|
|
prefs.fullscreen_on = turn.to_bool(current_state);
|
|
|
|
window.mode = match prefs.fullscreen_on {
|
2024-05-13 18:21:56 +00:00
|
|
|
true => opt.window_mode_fullscreen,
|
2024-05-22 02:57:20 +00:00
|
|
|
false => WindowMode::Windowed,
|
2024-05-13 18:21:56 +00:00
|
|
|
};
|
2024-06-13 00:18:22 +00:00
|
|
|
prefs.save();
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
}
|
2024-05-13 18:53:08 +00:00
|
|
|
GameEvent::SetMenu(turn) => {
|
2024-09-16 23:10:55 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
2024-05-13 18:53:08 +00:00
|
|
|
settings.menu_active = turn.to_bool(settings.menu_active);
|
2024-09-16 00:31:51 +00:00
|
|
|
for mut window in &mut q_window {
|
|
|
|
window.cursor.grab_mode = if settings.menu_active {
|
|
|
|
bevy::window::CursorGrabMode::None
|
|
|
|
} else {
|
|
|
|
bevy::window::CursorGrabMode::Locked
|
|
|
|
};
|
|
|
|
window.cursor.visible = settings.menu_active;
|
|
|
|
}
|
2024-05-13 19:37:57 +00:00
|
|
|
ew_updatemenu.send(menu::UpdateMenuEvent);
|
2024-05-13 18:53:08 +00:00
|
|
|
}
|
2024-05-13 19:11:27 +00:00
|
|
|
GameEvent::SetThirdPerson(turn) => {
|
|
|
|
settings.third_person = turn.to_bool(settings.third_person);
|
2024-06-13 00:18:22 +00:00
|
|
|
prefs.third_person = settings.third_person;
|
|
|
|
prefs.save();
|
2024-05-13 19:11:27 +00:00
|
|
|
}
|
|
|
|
GameEvent::SetRotationStabilizer(turn) => {
|
2024-05-22 02:57:20 +00:00
|
|
|
settings.rotation_stabilizer_active =
|
|
|
|
turn.to_bool(settings.rotation_stabilizer_active);
|
2024-05-13 19:11:27 +00:00
|
|
|
}
|
|
|
|
GameEvent::SetShadows(turn) => {
|
|
|
|
settings.shadows_sun = turn.to_bool(settings.shadows_sun);
|
|
|
|
for mut light in &mut q_light {
|
|
|
|
light.shadows_enabled = settings.shadows_sun;
|
|
|
|
}
|
2024-06-13 00:18:22 +00:00
|
|
|
prefs.shadows_sun = settings.shadows_sun;
|
|
|
|
prefs.save();
|
2024-05-13 19:11:27 +00:00
|
|
|
}
|
2024-05-13 23:24:57 +00:00
|
|
|
GameEvent::Achievement(name) => {
|
2024-05-14 04:37:00 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Achieve));
|
2024-05-22 02:57:20 +00:00
|
|
|
log.add(
|
|
|
|
format!("Achievement accomplished: {name}!"),
|
|
|
|
"".to_string(),
|
|
|
|
hud::LogLevel::Achievement,
|
|
|
|
);
|
2024-05-13 23:24:57 +00:00
|
|
|
}
|
2024-09-22 01:40:12 +00:00
|
|
|
GameEvent::UpdateFlashlight => {
|
2024-10-26 22:20:46 +00:00
|
|
|
for (mut visibility, mut spotlight) in &mut q_flashlight {
|
2024-09-22 01:40:12 +00:00
|
|
|
spotlight.intensity = actor::FLASHLIGHT_INTENSITY[prefs.flashlight_power];
|
2024-10-26 22:20:46 +00:00
|
|
|
*visibility = bool2vis(settings.flashlight_active);
|
2024-09-22 01:40:12 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-26 17:36:54 +00:00
|
|
|
GameEvent::PhoneCall => {
|
|
|
|
let talker = chat::Talker {
|
|
|
|
chat_name: "phone".to_string(),
|
|
|
|
actor_id: "".to_string(),
|
|
|
|
name: Some("Phone".to_string()),
|
|
|
|
counts_towards_achievement: false,
|
|
|
|
pronoun: None,
|
|
|
|
talking_speed: 0.0,
|
|
|
|
};
|
2024-10-27 01:01:47 +00:00
|
|
|
ew_conv.send(chat::StartConversationEvent { talker: talker });
|
2024-10-26 17:36:54 +00:00
|
|
|
}
|
2024-05-13 18:21:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-12 21:57:21 +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>)>,
|
|
|
|
mut scene_spawner: ResMut<SceneSpawner>,
|
|
|
|
mut active_asteroids: ResMut<world::ActiveAsteroids>,
|
|
|
|
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
|
|
|
mut ew_deathscreen: EventWriter<menu::DeathScreenEvent>,
|
|
|
|
mut log: ResMut<hud::Log>,
|
2024-06-02 19:03:40 +00:00
|
|
|
mut gamevars: ResMut<var::GameVars>,
|
2024-05-12 21:57:21 +00:00
|
|
|
mut settings: ResMut<Settings>,
|
|
|
|
) {
|
|
|
|
for death in er_playerdies.read() {
|
|
|
|
if settings.god_mode {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
settings.reset_player_settings();
|
2024-06-02 19:03:40 +00:00
|
|
|
gamevars.reset();
|
2024-05-12 21:57:21 +00:00
|
|
|
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 {
|
2024-05-14 17:12:36 +00:00
|
|
|
actor::DamageType::Depressurization => {
|
|
|
|
settings.death_cause = "Depressurization".to_string();
|
2024-05-13 18:53:08 +00:00
|
|
|
ew_effect.send(visual::SpawnEffectEvent {
|
|
|
|
class: visual::Effects::FadeIn(Color::BLACK),
|
|
|
|
duration: 4.0,
|
|
|
|
});
|
|
|
|
}
|
2024-05-12 21:57:21 +00:00
|
|
|
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 {
|
2024-07-09 00:28:55 +00:00
|
|
|
class: visual::Effects::FadeIn(css::MAROON.into()),
|
2024-05-12 21:57:21 +00:00
|
|
|
duration: 1.0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
actor::DamageType::GForce => {
|
|
|
|
settings.death_cause = "Trauma from excessive g forces".to_string();
|
|
|
|
ew_effect.send(visual::SpawnEffectEvent {
|
2024-07-09 00:28:55 +00:00
|
|
|
class: visual::Effects::FadeIn(css::MAROON.into()),
|
2024-05-12 21:57:21 +00:00
|
|
|
duration: 1.0,
|
|
|
|
});
|
|
|
|
}
|
2024-09-22 05:15:29 +00:00
|
|
|
actor::DamageType::Radiation => {
|
|
|
|
settings.death_cause = "Acute radiation poisoning".to_string();
|
|
|
|
ew_effect.send(visual::SpawnEffectEvent {
|
|
|
|
class: visual::Effects::FadeIn(Color::BLACK),
|
|
|
|
duration: 4.0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
actor::DamageType::Unknown => {
|
2024-05-12 21:57:21 +00:00
|
|
|
settings.death_cause = "Unknown".to_string();
|
|
|
|
ew_effect.send(visual::SpawnEffectEvent {
|
2024-07-09 00:28:55 +00:00
|
|
|
class: visual::Effects::FadeIn(css::MAROON.into()),
|
2024-05-12 21:57:21 +00:00
|
|
|
duration: 1.0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ew_deathscreen.send(menu::DeathScreenEvent::Show);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_cheats(
|
|
|
|
key_input: Res<ButtonInput<KeyCode>>,
|
2024-05-22 02:57:20 +00:00
|
|
|
mut q_player: Query<
|
|
|
|
(&Transform, &mut Position, &mut LinearVelocity),
|
|
|
|
With<actor::PlayerCamera>,
|
|
|
|
>,
|
2024-05-12 21:57:21 +00:00
|
|
|
mut q_life: Query<(&mut actor::LifeForm, &mut actor::ExperiencesGForce), With<actor::Player>>,
|
2024-05-22 02:57:20 +00:00
|
|
|
q_target: Query<
|
|
|
|
(&Transform, &Position, Option<&LinearVelocity>),
|
|
|
|
(With<hud::IsTargeted>, Without<actor::PlayerCamera>),
|
|
|
|
>,
|
2024-05-12 21:57:21 +00:00
|
|
|
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
|
|
|
mut settings: ResMut<Settings>,
|
2024-06-11 01:04:54 +00:00
|
|
|
jupiter_pos: Res<JupiterPos>,
|
2024-05-12 21:57:21 +00:00
|
|
|
id2pos: Res<Id2Pos>,
|
2024-06-11 00:59:10 +00:00
|
|
|
id2v: Res<Id2V>,
|
2024-05-12 21:57:21 +00:00
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
|
|
|
) {
|
|
|
|
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 {
|
2024-09-15 02:30:09 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Honk));
|
2024-09-15 02:53:33 +00:00
|
|
|
} else {
|
2024-09-15 02:30:09 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !settings.god_mode && !settings.dev_mode {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if key_input.just_pressed(settings.key_cheat_stop) {
|
|
|
|
gforce.ignore_gforce_seconds = 1.0;
|
2024-06-11 01:04:54 +00:00
|
|
|
v.0 = nature::orbital_velocity(pos.0 - jupiter_pos.0, nature::JUPITER_MASS);
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
2024-05-22 02:57:20 +00:00
|
|
|
if key_input.pressed(settings.key_cheat_speed)
|
|
|
|
|| key_input.pressed(settings.key_cheat_speed_backward)
|
|
|
|
{
|
2024-05-12 21:57:21 +00:00
|
|
|
gforce.ignore_gforce_seconds = 1.0;
|
2024-05-22 02:57:20 +00:00
|
|
|
let sign = if key_input.pressed(settings.key_cheat_speed) {
|
|
|
|
1.0
|
|
|
|
} else {
|
|
|
|
-1.0
|
|
|
|
};
|
2024-05-13 04:10:34 +00:00
|
|
|
let dv = DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, sign * boost));
|
|
|
|
let current_speed = v.0.length();
|
|
|
|
let next_speed = (v.0 + dv).length();
|
|
|
|
let avg_speed = (current_speed + next_speed) / 2.0;
|
|
|
|
let inv_lorentz = nature::inverse_lorentz_factor(avg_speed.clamp(0.0, nature::C));
|
|
|
|
v.0 = v.0 + inv_lorentz * dv;
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
|
|
|
if key_input.just_pressed(settings.key_cheat_teleport) {
|
|
|
|
if let Ok((transform, target_pos, target_v)) = q_target.get_single() {
|
2024-10-03 04:11:27 +00:00
|
|
|
gforce.ignore_gforce_seconds = 1.0;
|
2024-05-22 02:57:20 +00:00
|
|
|
let offset: DVec3 =
|
|
|
|
4.0 * (**pos - **target_pos).normalize() * transform.scale.as_dvec3();
|
2024-05-12 21:57:21 +00:00
|
|
|
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) {
|
2024-06-11 00:59:10 +00:00
|
|
|
if let Some(target) = id2pos.0.get(&CHEAT_WARP_1.to_string()) {
|
2024-05-12 21:57:21 +00:00
|
|
|
pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0);
|
|
|
|
gforce.ignore_gforce_seconds = 1.0;
|
|
|
|
}
|
2024-06-11 00:59:10 +00:00
|
|
|
if let Some(target) = id2v.0.get(&CHEAT_WARP_1.to_string()) {
|
|
|
|
v.0 = *target;
|
|
|
|
}
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
|
|
|
if key_input.just_pressed(settings.key_cheat_farview1) {
|
2024-06-11 00:59:10 +00:00
|
|
|
if let Some(target) = id2pos.0.get(&CHEAT_WARP_2.to_string()) {
|
2024-05-12 21:57:21 +00:00
|
|
|
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
|
|
|
gforce.ignore_gforce_seconds = 1.0;
|
|
|
|
}
|
2024-06-11 00:59:10 +00:00
|
|
|
if let Some(target) = id2v.0.get(&CHEAT_WARP_2.to_string()) {
|
|
|
|
v.0 = *target;
|
|
|
|
}
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
|
|
|
if key_input.just_pressed(settings.key_cheat_farview2) {
|
2024-06-11 00:59:10 +00:00
|
|
|
if let Some(target) = id2pos.0.get(&CHEAT_WARP_3.to_string()) {
|
2024-05-12 21:57:21 +00:00
|
|
|
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
|
|
|
gforce.ignore_gforce_seconds = 1.0;
|
|
|
|
}
|
2024-06-11 00:59:10 +00:00
|
|
|
if let Some(target) = id2v.0.get(&CHEAT_WARP_3.to_string()) {
|
|
|
|
v.0 = *target;
|
|
|
|
}
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-10 22:58:56 +00:00
|
|
|
fn update_id2pos(
|
|
|
|
mut id2pos: ResMut<Id2Pos>,
|
|
|
|
mut jupiterpos: ResMut<JupiterPos>,
|
|
|
|
q_id: Query<(&Position, &actor::Identifier)>,
|
|
|
|
) {
|
2024-05-12 21:57:21 +00:00
|
|
|
id2pos.0.clear();
|
|
|
|
for (pos, id) in &q_id {
|
|
|
|
id2pos.0.insert(id.0.clone(), pos.0);
|
2024-10-26 19:25:00 +00:00
|
|
|
if id.0 == cmd::ID_JUPITER {
|
2024-06-10 22:58:56 +00:00
|
|
|
jupiterpos.0 = pos.0;
|
|
|
|
}
|
2024-05-12 21:57:21 +00:00
|
|
|
}
|
|
|
|
}
|
2024-05-12 22:10:13 +00:00
|
|
|
|
2024-06-11 00:46:31 +00:00
|
|
|
fn update_id2v(mut id2v: ResMut<Id2V>, q_id: Query<(&LinearVelocity, &actor::Identifier)>) {
|
|
|
|
id2v.0.clear();
|
|
|
|
for (v, id) in &q_id {
|
|
|
|
id2v.0.insert(id.0.clone(), v.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-12 22:10:13 +00:00
|
|
|
fn debug(
|
|
|
|
settings: Res<var::Settings>,
|
|
|
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
2024-09-15 21:56:03 +00:00
|
|
|
// mut commands: Commands,
|
|
|
|
// mut extended_materials: ResMut<
|
2024-09-17 14:41:08 +00:00
|
|
|
// Assets<bevy::pbr::ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>,
|
2024-09-15 21:56:03 +00:00
|
|
|
// >,
|
2024-05-14 04:28:14 +00:00
|
|
|
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
2024-06-11 22:57:29 +00:00
|
|
|
vars: Res<var::GameVars>,
|
2024-09-15 21:56:03 +00:00
|
|
|
// materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
|
2024-05-12 22:10:13 +00:00
|
|
|
) {
|
2024-05-13 23:24:57 +00:00
|
|
|
if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyP) {
|
2024-09-15 21:56:03 +00:00
|
|
|
|
|
|
|
// for (entity, _name, mesh) in &materials {
|
2024-09-17 14:41:08 +00:00
|
|
|
// dbg!(mesh);
|
|
|
|
// let mut entity = commands.entity(entity);
|
|
|
|
// entity.remove::<Handle<StandardMaterial>>();
|
|
|
|
// let material = extended_materials.add(load::AsteroidSurface::material());
|
|
|
|
// entity.insert(material);
|
2024-09-15 21:56:03 +00:00
|
|
|
// }
|
2024-05-12 22:10:13 +00:00
|
|
|
}
|
2024-05-13 23:24:57 +00:00
|
|
|
if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyN) {
|
2024-05-14 04:28:14 +00:00
|
|
|
achievement_tracker.achieve_all();
|
2024-06-11 22:57:29 +00:00
|
|
|
dbg!(&vars);
|
2024-05-13 23:24:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_achievement_event(
|
|
|
|
mut er_achievement: EventReader<AchievementEvent>,
|
|
|
|
mut ew_game: EventWriter<GameEvent>,
|
|
|
|
mut tracker: ResMut<var::AchievementTracker>,
|
|
|
|
) {
|
|
|
|
for event in er_achievement.read() {
|
|
|
|
match event {
|
|
|
|
AchievementEvent::RepairSuit => {
|
|
|
|
if !tracker.repair_suit {
|
|
|
|
ew_game.send(GameEvent::Achievement("Repair Your Suit".into()));
|
|
|
|
}
|
|
|
|
tracker.repair_suit = true;
|
|
|
|
}
|
|
|
|
AchievementEvent::InJupitersShadow => {
|
|
|
|
if !tracker.in_jupiters_shadow {
|
2024-07-14 19:16:19 +00:00
|
|
|
ew_game.send(GameEvent::Achievement("Enter Jupiter's Shadow".into()));
|
2024-05-13 23:24:57 +00:00
|
|
|
}
|
|
|
|
tracker.in_jupiters_shadow = true;
|
|
|
|
}
|
|
|
|
AchievementEvent::DrinkPizza => {
|
|
|
|
if !tracker.drink_a_pizza {
|
2024-05-14 17:17:40 +00:00
|
|
|
ew_game.send(GameEvent::Achievement("Enjoy A Pizza".into()));
|
2024-05-13 23:24:57 +00:00
|
|
|
}
|
|
|
|
tracker.drink_a_pizza = true;
|
|
|
|
}
|
2024-05-14 03:33:35 +00:00
|
|
|
AchievementEvent::FindEarth => {
|
|
|
|
if !tracker.find_earth {
|
|
|
|
ew_game.send(GameEvent::Achievement("Find Earth".into()));
|
|
|
|
}
|
|
|
|
tracker.find_earth = true;
|
|
|
|
}
|
2024-11-18 03:52:29 +00:00
|
|
|
AchievementEvent::RaceScore => {
|
|
|
|
if !tracker.race_score {
|
|
|
|
ew_game.send(GameEvent::Achievement(format!("Score {} Points In Time Trial", RACE_SCORE_ACHIEVEMENT)));
|
|
|
|
}
|
|
|
|
tracker.race_score = true;
|
|
|
|
}
|
2024-05-13 23:24:57 +00:00
|
|
|
AchievementEvent::RideVehicle(name) => {
|
|
|
|
tracker.vehicles_ridden.insert(name.clone());
|
|
|
|
let len = tracker.vehicles_ridden.len();
|
|
|
|
let total = tracker.all_vehicles.len();
|
|
|
|
if !tracker.ride_every_vehicle && len == total {
|
|
|
|
tracker.ride_every_vehicle = true;
|
2024-05-14 17:17:40 +00:00
|
|
|
ew_game.send(GameEvent::Achievement("Ride Every Vehicle".into()));
|
2024-05-13 23:24:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
AchievementEvent::TalkTo(name) => {
|
|
|
|
tracker.people_talked_to.insert(name.clone());
|
|
|
|
let len = tracker.people_talked_to.len();
|
|
|
|
let total = tracker.all_people.len();
|
|
|
|
if !tracker.talk_to_everyone && len == total {
|
|
|
|
tracker.talk_to_everyone = true;
|
|
|
|
ew_game.send(GameEvent::Achievement("Talk To Everyone".into()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-05-12 22:10:13 +00:00
|
|
|
}
|
2024-05-14 02:35:45 +00:00
|
|
|
|
|
|
|
fn check_achievements(
|
|
|
|
time: Res<Time>,
|
|
|
|
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
2024-11-11 22:29:16 +00:00
|
|
|
mut q_mirrorlight: Query<&mut Visibility, With<actor::MirrorLight>>,
|
2024-05-14 02:35:45 +00:00
|
|
|
id2pos: Res<Id2Pos>,
|
|
|
|
mut ew_achievement: EventWriter<AchievementEvent>,
|
|
|
|
mut timer: ResMut<AchievementCheckTimer>,
|
|
|
|
) {
|
2024-05-22 02:57:20 +00:00
|
|
|
if !timer.0.tick(time.delta()).just_finished() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let pos_player = if let Ok(pos) = q_player.get_single() {
|
|
|
|
pos
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2024-10-26 19:25:00 +00:00
|
|
|
let pos_sun = if let Some(pos) = id2pos.0.get(cmd::ID_SOL) {
|
2024-05-22 02:57:20 +00:00
|
|
|
pos
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2024-10-26 19:25:00 +00:00
|
|
|
let pos_jupiter = if let Some(pos) = id2pos.0.get(cmd::ID_JUPITER) {
|
2024-05-22 02:57:20 +00:00
|
|
|
pos
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2024-05-14 02:35:45 +00:00
|
|
|
|
2024-05-22 02:57:20 +00:00
|
|
|
let shadowed = in_shadow(
|
|
|
|
*pos_sun,
|
|
|
|
nature::SOL_RADIUS,
|
|
|
|
*pos_jupiter,
|
|
|
|
nature::JUPITER_RADIUS,
|
|
|
|
**pos_player,
|
|
|
|
);
|
2024-05-14 02:35:45 +00:00
|
|
|
|
|
|
|
if shadowed {
|
|
|
|
ew_achievement.send(AchievementEvent::InJupitersShadow);
|
2024-11-11 22:29:16 +00:00
|
|
|
for mut mirrorlight_vis in &mut q_mirrorlight {
|
|
|
|
*mirrorlight_vis = Visibility::Hidden;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This is checking for whether the player is shadowed, not the mirror.
|
|
|
|
// But in the case where these two are not the same, the player doesn't see
|
|
|
|
// the mirror anyway and the mirror light's visibility state is irrelevant.
|
|
|
|
for mut mirrorlight_vis in &mut q_mirrorlight {
|
|
|
|
*mirrorlight_vis = Visibility::Inherited;
|
|
|
|
}
|
2024-05-14 02:35:45 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-09 22:49:01 +00:00
|
|
|
|
|
|
|
fn handle_window_focus(
|
2024-10-09 23:51:31 +00:00
|
|
|
mut local_paused: Local<bool>,
|
2024-10-09 22:49:01 +00:00
|
|
|
mut settings: ResMut<Settings>,
|
2024-10-09 23:51:31 +00:00
|
|
|
mut q_window: Query<&mut Window, With<PrimaryWindow>>,
|
2024-10-09 22:49:01 +00:00
|
|
|
mut er_focus: EventReader<bevy::window::WindowFocused>,
|
|
|
|
mut physicstime: ResMut<Time<Physics>>,
|
|
|
|
) {
|
|
|
|
for event in er_focus.read() {
|
|
|
|
settings.window_focused = event.focused;
|
|
|
|
}
|
|
|
|
|
2024-10-09 23:51:31 +00:00
|
|
|
let paused = !settings.is_game_running();
|
|
|
|
|
|
|
|
if paused != *local_paused {
|
|
|
|
if paused {
|
|
|
|
physicstime.pause();
|
|
|
|
} else {
|
|
|
|
physicstime.unpause();
|
|
|
|
}
|
|
|
|
|
|
|
|
for mut window in &mut q_window {
|
|
|
|
window.cursor.grab_mode = if paused {
|
|
|
|
bevy::window::CursorGrabMode::None
|
|
|
|
} else {
|
|
|
|
bevy::window::CursorGrabMode::Locked
|
|
|
|
};
|
|
|
|
window.cursor.visible = paused;
|
|
|
|
}
|
|
|
|
*local_paused = paused;
|
2024-10-09 22:49:01 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-18 02:50:25 +00:00
|
|
|
|
|
|
|
fn handle_race(
|
|
|
|
mut commands: Commands,
|
|
|
|
time: Res<Time>,
|
|
|
|
mut log: ResMut<hud::Log>,
|
|
|
|
mut settings: ResMut<Settings>,
|
|
|
|
mut race: ResMut<RaceState>,
|
|
|
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
2024-11-18 03:52:29 +00:00
|
|
|
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
2024-11-18 03:02:12 +00:00
|
|
|
q_player: Query<(&Position, &LinearVelocity, &Transform), With<PlayerCamera>>,
|
2024-11-18 02:50:25 +00:00
|
|
|
mut q_racetarget: Query<
|
|
|
|
(Entity, &mut Position, &mut LinearVelocity, &mut Visibility),
|
|
|
|
(With<RaceTarget>, Without<PlayerCamera>),
|
|
|
|
>,
|
|
|
|
) {
|
2024-11-18 03:02:12 +00:00
|
|
|
let mut deinitialize = false;
|
2024-11-18 02:50:25 +00:00
|
|
|
if !settings.race_active {
|
2024-11-18 03:02:12 +00:00
|
|
|
if race.initialized {
|
|
|
|
log.warning(format!("Stopped race! Final score: {}", race.score));
|
|
|
|
deinitialize = true;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2024-11-18 02:50:25 +00:00
|
|
|
}
|
2024-11-18 03:02:12 +00:00
|
|
|
|
|
|
|
let (player_pos, player_v, player_trans) = if let Ok(val) = q_player.get_single() {
|
2024-11-18 02:50:25 +00:00
|
|
|
val
|
|
|
|
} else {
|
|
|
|
error!("No player found in handle_race!");
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
let (target_entity, mut target_pos, mut target_v, mut target_vis) =
|
|
|
|
if let Ok(val) = q_racetarget.get_single_mut() {
|
|
|
|
val
|
|
|
|
} else {
|
|
|
|
error!("No race target entity found in handle_race!");
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
2024-11-18 03:02:12 +00:00
|
|
|
if !deinitialize {
|
|
|
|
let mut spawn_target = false;
|
2024-11-18 02:50:25 +00:00
|
|
|
|
2024-11-18 03:02:12 +00:00
|
|
|
if !race.initialized {
|
|
|
|
log.warning("Time Trial Race START!".to_string());
|
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Honk));
|
2024-11-18 03:52:29 +00:00
|
|
|
race.score = 0.0;
|
2024-11-18 03:02:12 +00:00
|
|
|
race.initialized = true;
|
|
|
|
spawn_target = true;
|
|
|
|
}
|
2024-11-18 02:50:25 +00:00
|
|
|
|
2024-11-18 03:02:12 +00:00
|
|
|
if player_pos.distance(target_pos.0) < RACE_TARGET_RADIUS {
|
2024-11-18 03:52:29 +00:00
|
|
|
race.score += 1.0;
|
2024-11-18 03:02:12 +00:00
|
|
|
spawn_target = true;
|
|
|
|
}
|
2024-11-18 02:50:25 +00:00
|
|
|
|
2024-11-18 03:02:12 +00:00
|
|
|
if spawn_target {
|
|
|
|
race.timeout = time.elapsed_seconds_f64() + 5.0;
|
2024-11-18 03:52:29 +00:00
|
|
|
if race.score >= RACE_SCORE_ACHIEVEMENT as f64 {
|
|
|
|
ew_achievement.send(game::AchievementEvent::RaceScore);
|
|
|
|
}
|
2024-11-18 03:14:17 +00:00
|
|
|
let difficulty = 2.0 * race.score as f64;
|
2024-11-18 03:02:12 +00:00
|
|
|
let mut delta = DVec3::new(0.0, 0.0, 100.0);
|
|
|
|
delta = (player_trans.rotation * delta.as_vec3()).as_dvec3();
|
2024-11-18 03:14:17 +00:00
|
|
|
delta += DVec3::new(
|
|
|
|
(fastrand::f64() - 0.5) * difficulty,
|
|
|
|
(fastrand::f64() - 0.5) * difficulty,
|
|
|
|
(fastrand::f64() - 0.5) * difficulty,
|
|
|
|
);
|
2024-11-18 03:02:12 +00:00
|
|
|
*target_pos = Position(player_pos.0 + delta);
|
|
|
|
*target_v = *player_v;
|
|
|
|
*target_vis = Visibility::Inherited;
|
|
|
|
commands.entity(target_entity).insert(RigidBody::Kinematic);
|
|
|
|
}
|
|
|
|
|
|
|
|
if race.timeout <= time.elapsed_seconds_f64() {
|
|
|
|
log.warning(format!("GAME OVER! Final score: {}", race.score));
|
|
|
|
deinitialize = true;
|
|
|
|
}
|
2024-11-18 02:50:25 +00:00
|
|
|
}
|
|
|
|
|
2024-11-18 03:02:12 +00:00
|
|
|
if deinitialize {
|
2024-11-18 02:50:25 +00:00
|
|
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::PowerDown));
|
|
|
|
race.initialized = false;
|
|
|
|
settings.race_active = false;
|
|
|
|
*target_vis = Visibility::Hidden;
|
|
|
|
commands.entity(target_entity).remove::<RigidBody>();
|
|
|
|
}
|
|
|
|
}
|