Merge branch 'gravity'

This commit is contained in:
yuni 2024-06-11 03:40:21 +02:00
commit 1a94c31d62
10 changed files with 228 additions and 43 deletions

View file

@ -35,6 +35,8 @@ The `SPACE` key helps you move more intuitively by slowing you down. Hold it fo
When you're ready, take a look around, explore the starting area. There is a friendly person floating nearby in a red space suit that would love to talk to you. When you're ready, take a look around, explore the starting area. There is a friendly person floating nearby in a red space suit that would love to talk to you.
The game is a bit dark, since it's meant to be played in the night. But maybe you're unlucky and you start with a planet or moon eclipsing the sun, making everything extra dark. (Yes, all celestial objects move in real time and have different positions depending on when you play.) If it's too dark, try the flashlight (`f` key), turning off the shadows in the menu, and make sure that Augmented Reality is on (`TAB` key) which gives you a little extra light amplification.
Press the `ESC` key for the menu to restart the game if you get lost, explore more key bindings, game features, and the **achievements** to get some guidance on what you can do in the game. Press the `ESC` key for the menu to restart the game if you get lost, explore more key bindings, game features, and the **achievements** to get some guidance on what you can do in the game.
But in the end, OutFly is an open world game with no true goals other than the ones you set for yourself. Just lean back, get cozy, and drift towards whatever catches your eye :) But in the end, OutFly is an open world game with no true goals other than the ones you set for yourself. Just lean back, get cozy, and drift towards whatever catches your eye :)

View file

@ -30,8 +30,9 @@ impl Plugin for ActorPlugin {
( (
update_physics_lifeforms, update_physics_lifeforms,
update_power, update_power,
handle_gravity,
handle_wants_maxrotation, handle_wants_maxrotation,
handle_wants_maxvelocity, handle_wants_maxvelocity.run_if(any_with_component::<WantsMaxVelocity>),
handle_wants_lookat.run_if(alive), handle_wants_lookat.run_if(alive),
), ),
); );
@ -118,6 +119,7 @@ pub struct ExperiencesGForce {
pub visual_effect_threshold: f32, pub visual_effect_threshold: f32,
pub visual_effect: f32, pub visual_effect: f32,
pub last_linear_velocity: DVec3, pub last_linear_velocity: DVec3,
pub gravitational_component: DVec3,
pub ignore_gforce_seconds: f32, pub ignore_gforce_seconds: f32,
} }
impl Default for ExperiencesGForce { impl Default for ExperiencesGForce {
@ -127,8 +129,9 @@ impl Default for ExperiencesGForce {
damage_threshold: 100.0, damage_threshold: 100.0,
visual_effect_threshold: 20.0, visual_effect_threshold: 20.0,
visual_effect: 0.0, visual_effect: 0.0,
last_linear_velocity: DVec3::splat(0.0), last_linear_velocity: DVec3::ZERO,
ignore_gforce_seconds: 0.0, gravitational_component: DVec3::ZERO,
ignore_gforce_seconds: 0.01,
} }
} }
} }
@ -156,7 +159,11 @@ pub struct WantsMaxVelocity(pub f64);
#[derive(Component)] #[derive(Component)]
pub struct WantsToLookAt(pub String); pub struct WantsToLookAt(pub String);
#[derive(Component)] #[derive(Component)]
pub struct WantsMatchVelocityWith(pub String);
#[derive(Component)]
pub struct Identifier(pub String); pub struct Identifier(pub String);
#[derive(Component)]
pub struct OrbitsJupiter;
#[derive(Component)] #[derive(Component)]
pub struct LifeForm { pub struct LifeForm {
@ -535,25 +542,53 @@ fn handle_wants_maxrotation(
} }
} }
/// Slows down NPC's movement until they reach their target velocity.
fn handle_wants_maxvelocity( fn handle_wants_maxvelocity(
time: Res<Time>, time: Res<Time>,
mut query: Query<(&mut LinearVelocity, &Engine, &WantsMaxVelocity)>, mut query: Query<(
&Position,
&mut LinearVelocity,
&Engine,
&WantsMaxVelocity,
Option<&OrbitsJupiter>,
Option<&WantsMatchVelocityWith>,
)>,
id2v: Res<game::Id2V>,
jupiter_pos: Res<game::JupiterPos>,
) { ) {
let dt = time.delta_seconds(); let dt = time.delta_seconds();
for (mut v, engine, maxv) in &mut query { for (pos, mut v, engine, maxv, orbits_jupiter, matchwith) in &mut query {
let total = v.0.length(); let target_velocity = if let Some(matchwith) = matchwith {
if total <= maxv.0 + EPSILON { if let Some(target_v) = id2v.0.get(&matchwith.0) {
if total > maxv.0 { *target_v
v.0 = DVec3::splat(0.0); } else {
warn!("Can't match velocity with nonexisting ID {}", matchwith.0);
continue;
}
} else if orbits_jupiter.is_some() {
let relative_pos = pos.0 - jupiter_pos.0;
nature::orbital_velocity(relative_pos, nature::JUPITER_MASS)
} else {
DVec3::ZERO
};
let relative_velocity = v.0 - target_velocity;
let relative_speed = relative_velocity.length();
if relative_speed <= maxv.0 + EPSILON {
// it's already pretty close to the target
if relative_speed > maxv.0 {
// but not quite the target, so let's set it to the target
v.0 = target_velocity;
} }
} else { } else {
// slow it down a little bit
// TODO: respect engine parameters for different thrusts for different directions // TODO: respect engine parameters for different thrusts for different directions
let avg_thrust = let avg_thrust =
(engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0; (engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0;
let acceleration = (avg_thrust * dt) as f64 * -v.0; let acceleration = (avg_thrust * dt) as f64 * -relative_velocity;
v.0 += acceleration; v.0 += acceleration;
if v.0.length() + EPSILON < acceleration.length() { if v.0.length() + EPSILON < acceleration.length() {
v.0 = DVec3::splat(0.0); v.0 = target_velocity;
} }
} }
} }
@ -610,8 +645,10 @@ fn handle_gforce(
let dt = time.delta_seconds(); let dt = time.delta_seconds();
let factor = 1.0 / dt / nature::EARTH_GRAVITY; let factor = 1.0 / dt / nature::EARTH_GRAVITY;
for (v, mut hp, mut gforce) in &mut q_actor { for (v, mut hp, mut gforce) in &mut q_actor {
gforce.gforce = factor * (v.0 - gforce.last_linear_velocity).length() as f32; gforce.gforce = factor
* (v.0 - gforce.last_linear_velocity - gforce.gravitational_component).length() as f32;
gforce.last_linear_velocity = v.0; gforce.last_linear_velocity = v.0;
gforce.gravitational_component = DVec3::ZERO;
if gforce.ignore_gforce_seconds > 0.0 { if gforce.ignore_gforce_seconds > 0.0 {
gforce.ignore_gforce_seconds -= dt; gforce.ignore_gforce_seconds -= dt;
continue; continue;
@ -632,3 +669,28 @@ fn handle_gforce(
} }
} }
} }
fn handle_gravity(
time: Res<Time>,
mut q_pos: Query<
(
&Position,
&mut LinearVelocity,
Option<&mut ExperiencesGForce>,
),
With<OrbitsJupiter>,
>,
jupiter_pos: Res<game::JupiterPos>,
) {
let dt = time.delta_seconds() as f64;
// this assumes prograde orbits for every object
for (pos, mut v, gforce_maybe) in &mut q_pos {
let relative_pos = pos.0 - jupiter_pos.0;
let accel = dt * nature::gravitational_acceleration(relative_pos, nature::JUPITER_MASS);
if let Some(mut gforce) = gforce_maybe {
gforce.gravitational_component += accel;
}
v.0 += accel;
}
}

View file

@ -416,6 +416,7 @@ pub fn apply_input_to_player(
time: Res<Time>, time: Res<Time>,
mut commands: Commands, mut commands: Commands,
settings: Res<var::Settings>, settings: Res<var::Settings>,
jupiter_pos: Res<game::JupiterPos>,
windows: Query<&Window, With<PrimaryWindow>>, windows: Query<&Window, With<PrimaryWindow>>,
mut mouse_events: EventReader<MouseMotion>, mut mouse_events: EventReader<MouseMotion>,
key_input: Res<ButtonInput<KeyCode>>, key_input: Res<ButtonInput<KeyCode>>,
@ -426,6 +427,7 @@ pub fn apply_input_to_player(
Entity, Entity,
&Transform, &Transform,
&mut actor::Engine, &mut actor::Engine,
&Position,
&mut LinearVelocity, &mut LinearVelocity,
&mut ExternalTorque, &mut ExternalTorque,
Option<&actor::PlayerDrivesThis>, Option<&actor::PlayerDrivesThis>,
@ -451,15 +453,15 @@ pub fn apply_input_to_player(
win_res_y = 1050.0; win_res_y = 1050.0;
} }
if let Ok((player_entity, player_transform, mut engine, pos, mut v, mut torque, bike)) =
q_playercam.get_single_mut()
{
let target_v: DVec3 = if let Ok(target) = q_target.get_single() { let target_v: DVec3 = if let Ok(target) = q_target.get_single() {
target.0 target.0
} else { } else {
DVec3::splat(0.0) let relative_pos = pos.0 - jupiter_pos.0;
nature::orbital_velocity(relative_pos, nature::JUPITER_MASS)
}; };
if let Ok((player_entity, player_transform, mut engine, mut v, mut torque, bike)) =
q_playercam.get_single_mut()
{
// Handle key input // Handle key input
if focused { if focused {
if key_input.pressed(settings.key_forward) || settings.cruise_control_active { if key_input.pressed(settings.key_forward) || settings.cruise_control_active {

View file

@ -827,6 +827,7 @@ pub fn handle_chat_scripts(
mut ew_effect: EventWriter<visual::SpawnEffectEvent>, mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
mut ew_achievement: EventWriter<game::AchievementEvent>, mut ew_achievement: EventWriter<game::AchievementEvent>,
id2pos: Res<game::Id2Pos>, id2pos: Res<game::Id2Pos>,
id2v: Res<game::Id2V>,
) { ) {
for script in er_chatscript.read() { for script in er_chatscript.read() {
// Parse the script string // Parse the script string
@ -892,15 +893,22 @@ pub fn handle_chat_scripts(
_ => None, _ => None,
}; };
if let Some(station) = busstop { if let Some(station) = busstop {
if let Some(target) = id2pos.0.get(&station.to_string()) { if let Some(target_pos) = id2pos.0.get(&station.to_string()) {
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0); pos.0 = *target_pos + DVec3::new(0.0, -1000.0, 0.0);
v.0 = DVec3::ZERO;
} else { } else {
error!( error!(
"Could not determine position of actor with ID: '{}'", "Could not determine position of actor with ID: '{}'",
station station
); );
} }
if let Some(target_v) = id2v.0.get(&station.to_string()) {
v.0 = *target_v;
} else {
error!(
"Could not determine velocity of actor with ID: '{}'",
station
);
}
} else { } else {
error!("Invalid destination for cryotrip chat script: '{}'", param1); error!("Invalid destination for cryotrip chat script: '{}'", param1);
} }

View file

@ -86,6 +86,7 @@ struct ParserState {
wants_maxrotation: Option<f64>, wants_maxrotation: Option<f64>,
wants_maxvelocity: Option<f64>, wants_maxvelocity: Option<f64>,
wants_tolookat_id: Option<String>, wants_tolookat_id: Option<String>,
wants_matchvelocity_id: Option<String>,
collider_is_mesh: bool, collider_is_mesh: bool,
collider_is_one_mesh_of_scene: bool, collider_is_one_mesh_of_scene: bool,
thrust_forward: f32, thrust_forward: f32,
@ -143,6 +144,7 @@ impl Default for ParserState {
wants_maxrotation: None, wants_maxrotation: None,
wants_maxvelocity: None, wants_maxvelocity: None,
wants_tolookat_id: None, wants_tolookat_id: None,
wants_matchvelocity_id: None,
collider_is_mesh: false, collider_is_mesh: false,
collider_is_one_mesh_of_scene: false, collider_is_one_mesh_of_scene: false,
thrust_forward: default_engine.thrust_forward, thrust_forward: default_engine.thrust_forward,
@ -582,6 +584,10 @@ pub fn load_defs(mut ew_spawn: EventWriter<SpawnEvent>) {
// NOTE: Will not work if the actor has no engine // NOTE: Will not work if the actor has no engine
state.wants_tolookat_id = Some(id.to_string()); state.wants_tolookat_id = Some(id.to_string());
} }
["wants", "matchvelocitywith", id] => {
// NOTE: Will not work if the actor has no engine
state.wants_matchvelocity_id = Some(id.to_string());
}
["armodel", asset_name] => { ["armodel", asset_name] => {
state.ar_model = Some(asset_name.to_string()); state.ar_model = Some(asset_name.to_string());
} }
@ -617,6 +623,12 @@ fn spawn_entities(
settings: Res<var::Settings>, settings: Res<var::Settings>,
) { ) {
for state_wrapper in er_spawn.read() { for state_wrapper in er_spawn.read() {
let jupiter_pos: DVec3 = if let Some(jupiter_pos) = id2pos.0.get(ID_JUPITER) {
*jupiter_pos
} else {
warn!("Could not determine Jupiter's position");
DVec3::ZERO
};
let state = &state_wrapper.0; let state = &state_wrapper.0;
let mut rotation = state.rotation; let mut rotation = state.rotation;
if state.class == DefClass::Actor { if state.class == DefClass::Actor {
@ -668,6 +680,13 @@ fn spawn_entities(
1.0 1.0
} * state.model_scale, } * state.model_scale,
); );
let orbits_jupiter = state.id != ID_JUPITER;
let velocity = if orbits_jupiter {
let coords = absolute_pos - jupiter_pos;
state.velocity + nature::orbital_velocity(coords, nature::JUPITER_MASS)
} else {
state.velocity
};
// Spawn the actor // Spawn the actor
let actor_entity; let actor_entity;
@ -680,6 +699,9 @@ fn spawn_entities(
..default() ..default()
}); });
actor.insert(SleepingDisabled); actor.insert(SleepingDisabled);
if orbits_jupiter {
actor.insert(actor::OrbitsJupiter);
}
actor.insert(world::DespawnOnPlayerDeath); actor.insert(world::DespawnOnPlayerDeath);
actor.insert(actor::HitPoints::default()); actor.insert(actor::HitPoints::default());
actor.insert(Position::from(absolute_pos)); actor.insert(Position::from(absolute_pos));
@ -715,7 +737,7 @@ fn spawn_entities(
// Physics Parameters // Physics Parameters
if state.has_physics { if state.has_physics {
actor.insert(RigidBody::Dynamic); actor.insert(RigidBody::Dynamic);
actor.insert(LinearVelocity(state.velocity)); actor.insert(LinearVelocity(velocity));
actor.insert(AngularVelocity(state.angular_momentum)); actor.insert(AngularVelocity(state.angular_momentum));
actor.insert(ColliderDensity(state.density)); actor.insert(ColliderDensity(state.density));
if state.collider_is_mesh { if state.collider_is_mesh {
@ -799,6 +821,9 @@ fn spawn_entities(
if let Some(value) = &state.wants_tolookat_id { if let Some(value) = &state.wants_tolookat_id {
actor.insert(actor::WantsToLookAt(value.clone())); actor.insert(actor::WantsToLookAt(value.clone()));
} }
if let Some(value) = &state.wants_matchvelocity_id {
actor.insert(actor::WantsMatchVelocityWith(value.clone()));
}
if let Some(color) = state.light_color { if let Some(color) = state.light_color {
actor.insert(( actor.insert((
PointLight { PointLight {

View file

@ -122,11 +122,11 @@ actor 0 0 0
only_in_map_at_dist 1e7 amalthea only_in_map_at_dist 1e7 amalthea
clickable no clickable no
physics off physics off
actor 0 127093 0 moonlet actor 0 0 0 moonlet
name Thebe name Thebe
relativeto jupiter relativeto jupiter
id thebe id thebe
orbit 221900e3 0.34 orbitaround jupiter 221900e3
scale 50e3 scale 50e3
moon yes moon yes
angularmomentum 0 0.025 0 angularmomentum 0 0.025 0
@ -256,10 +256,11 @@ actor 0 0 0
physics off physics off
actor 0 593051 0 suitv2 actor 0 59305 0 suitv2
template person template person
relativeto jupiter relativeto jupiter
orbit 221900e3 0.338 orbitaround jupiter 221900e3
orbit_phase_offset 0.002
player yes player yes
id player id player
wants maxvelocity none wants maxvelocity none
@ -287,6 +288,7 @@ actor -55e3 44e3 0 suitv2
name "Yuni" name "Yuni"
chatid Yuni chatid Yuni
rotationx 180 rotationx 180
wants matchvelocitywith thebe
actor 5000 0 -3000 moonlet actor 5000 0 -3000 moonlet
name Moonlet name Moonlet
@ -308,12 +310,14 @@ actor 13200 300 -3000 hollow_asteroid
actor 0 0 0 suitv2 actor 0 0 0 suitv2
template person template person
relativeto cultasteroid relativeto cultasteroid
wants matchvelocitywith cultasteroid
name "Ash" name "Ash"
chatid Ash chatid Ash
pronoun they pronoun they
actor -8 8 0 suitv2 actor -8 8 0 suitv2
template person template person
relativeto cultasteroid relativeto cultasteroid
wants matchvelocitywith cultasteroid
name "River" name "River"
chatid River chatid River
rotationy 54 rotationy 54
@ -442,6 +446,7 @@ actor -3300 10 0 pizzeria
relativeto pizzeria relativeto pizzeria
armodel clippy_ar armodel clippy_ar
wants lookat PLAYERCAMERA wants lookat PLAYERCAMERA
wants matchvelocitywith pizzeria
rotationy -126 rotationy -126
chatid SubduedClippy chatid SubduedClippy
@ -452,6 +457,7 @@ actor -3300 10 0 pizzeria
chatid PizzaChef chatid PizzaChef
armodel suit_ar_chefhat armodel suit_ar_chefhat
wants lookat PLAYERCAMERA wants lookat PLAYERCAMERA
wants matchvelocitywith pizzeria
rotationy -90 rotationy -90
pronoun he pronoun he
@ -464,6 +470,7 @@ actor 30 -12 -40 suitv2
armodel suit_ar_wings armodel suit_ar_wings
angularmomentum 0.4 0.2 0.1 angularmomentum 0.4 0.2 0.1
wants maxrotation 0.5 wants maxrotation 0.5
wants matchvelocitywith pizzeria
rotationy 108 rotationy 108
rotationx 180 rotationx 180
pronoun it pronoun it
@ -495,6 +502,7 @@ actor -300 0 40 suitv2
name "梓涵" name "梓涵"
chatid Drifter chatid Drifter
alive no alive no
wants maxvelocity none
oxygen 0.08 oxygen 0.08
pronoun she pronoun she
@ -505,12 +513,14 @@ actor 100 -18000 2000 clippy
name "StarTrans Clippy™ Serenity Station" name "StarTrans Clippy™ Serenity Station"
armodel clippy_ar armodel clippy_ar
wants lookat PLAYERCAMERA wants lookat PLAYERCAMERA
wants matchvelocitywith orbbusstopserenity
rotationy -90 rotationy -90
chatid ClippyTransSerenity chatid ClippyTransSerenity
actor 60 0 0 "orb_busstop" actor 60 0 0 "orb_busstop"
name "StarTrans Bus Stop: Serenity Station" name "StarTrans Bus Stop: Serenity Station"
relativeto busstopclippy relativeto busstopclippy
id orbbusstopserenity
scale 5 scale 5
actor 80 0 0 "orb_busstop" actor 80 0 0 "orb_busstop"
name "StarTrans Bus Stop: Serenity Station" name "StarTrans Bus Stop: Serenity Station"
@ -637,6 +647,7 @@ actor 100 -18000 2000 clippy
actor 8 20 0 suitv2 actor 8 20 0 suitv2
template person template person
relativeto "busstopclippy" relativeto "busstopclippy"
wants matchvelocitywith orbbusstopserenity
name "Rudy" name "Rudy"
chatid NPCinCryoStasis chatid NPCinCryoStasis
pronoun he pronoun he
@ -648,12 +659,14 @@ actor -184971e3 149410e3 -134273e3 clippy
name "StarTrans Clippy™ Farview Station" name "StarTrans Clippy™ Farview Station"
armodel clippy_ar armodel clippy_ar
wants lookat PLAYERCAMERA wants lookat PLAYERCAMERA
wants matchvelocitywith orbbusstopfarview
rotationy -90 rotationy -90
chatid ClippyTransFarview chatid ClippyTransFarview
actor 60 0 0 "orb_busstop" actor 60 0 0 "orb_busstop"
name "StarTrans Bus Stop: Farview Station" name "StarTrans Bus Stop: Farview Station"
relativeto busstopclippy2 relativeto busstopclippy2
id orbbusstopfarview
scale 5 scale 5
actor 80 0 0 "orb_busstop" actor 80 0 0 "orb_busstop"
name "StarTrans Bus Stop: Farview Station" name "StarTrans Bus Stop: Farview Station"
@ -785,6 +798,7 @@ actor 0 -44e3 0 clippy
name "StarTrans Clippy™ Metis Prime Station" name "StarTrans Clippy™ Metis Prime Station"
armodel clippy_ar armodel clippy_ar
wants lookat PLAYERCAMERA wants lookat PLAYERCAMERA
wants matchvelocitywith orbbusstopmetis
orbitaround jupiter 128000e3 orbitaround jupiter 128000e3
orbit_phase_offset -0.002 orbit_phase_offset -0.002
rotationy -90 rotationy -90
@ -793,6 +807,7 @@ actor 0 -44e3 0 clippy
actor 60 0 0 "orb_busstop" actor 60 0 0 "orb_busstop"
name "StarTrans Bus Stop: Metis Prime Station" name "StarTrans Bus Stop: Metis Prime Station"
relativeto busstopclippy3 relativeto busstopclippy3
id orbbusstopmetis
scale 5 scale 5
actor 80 0 0 "orb_busstop" actor 80 0 0 "orb_busstop"
name "StarTrans Bus Stop: Metis Prime Station" name "StarTrans Bus Stop: Metis Prime Station"

View file

@ -18,6 +18,10 @@ use bevy::window::{PrimaryWindow, Window, WindowMode};
use bevy_xpbd_3d::prelude::*; use bevy_xpbd_3d::prelude::*;
use std::collections::HashMap; use std::collections::HashMap;
pub const CHEAT_WARP_1: &str = "pizzeria";
pub const CHEAT_WARP_2: &str = "busstopclippy2";
pub const CHEAT_WARP_3: &str = "busstopclippy3";
pub struct GamePlugin; pub struct GamePlugin;
impl Plugin for GamePlugin { impl Plugin for GamePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@ -26,12 +30,15 @@ impl Plugin for GamePlugin {
app.add_systems(PostUpdate, handle_game_event); app.add_systems(PostUpdate, handle_game_event);
app.add_systems(PreUpdate, handle_player_death); app.add_systems(PreUpdate, handle_player_death);
app.add_systems(PostUpdate, update_id2pos); app.add_systems(PostUpdate, update_id2pos);
app.add_systems(PostUpdate, update_id2v);
app.add_systems( app.add_systems(
Update, Update,
handle_achievement_event.run_if(on_event::<AchievementEvent>()), handle_achievement_event.run_if(on_event::<AchievementEvent>()),
); );
app.add_systems(Update, check_achievements); app.add_systems(Update, check_achievements);
app.insert_resource(Id2Pos(HashMap::new())); app.insert_resource(Id2Pos(HashMap::new()));
app.insert_resource(Id2V(HashMap::new()));
app.insert_resource(JupiterPos(DVec3::ZERO));
app.insert_resource(var::AchievementTracker::default()); app.insert_resource(var::AchievementTracker::default());
app.insert_resource(var::Settings::default()); app.insert_resource(var::Settings::default());
app.insert_resource(var::GameVars::default()); app.insert_resource(var::GameVars::default());
@ -50,6 +57,10 @@ pub struct PlayerDiesEvent(pub actor::DamageType);
#[derive(Resource)] #[derive(Resource)]
pub struct Id2Pos(pub HashMap<String, DVec3>); pub struct Id2Pos(pub HashMap<String, DVec3>);
#[derive(Resource)] #[derive(Resource)]
pub struct Id2V(pub HashMap<String, DVec3>);
#[derive(Resource)]
pub struct JupiterPos(pub DVec3);
#[derive(Resource)]
pub struct AchievementCheckTimer(pub Timer); pub struct AchievementCheckTimer(pub Timer);
#[derive(Event)] #[derive(Event)]
@ -260,7 +271,9 @@ fn handle_cheats(
>, >,
mut ew_playerdies: EventWriter<PlayerDiesEvent>, mut ew_playerdies: EventWriter<PlayerDiesEvent>,
mut settings: ResMut<Settings>, mut settings: ResMut<Settings>,
jupiter_pos: Res<JupiterPos>,
id2pos: Res<Id2Pos>, id2pos: Res<Id2Pos>,
id2v: Res<Id2V>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
) { ) {
if q_player.is_empty() || q_life.is_empty() { if q_player.is_empty() || q_life.is_empty() {
@ -285,7 +298,7 @@ fn handle_cheats(
if key_input.just_pressed(settings.key_cheat_stop) { if key_input.just_pressed(settings.key_cheat_stop) {
gforce.ignore_gforce_seconds = 1.0; gforce.ignore_gforce_seconds = 1.0;
v.0 = DVec3::ZERO; v.0 = nature::orbital_velocity(pos.0 - jupiter_pos.0, nature::JUPITER_MASS);
} }
if key_input.pressed(settings.key_cheat_speed) if key_input.pressed(settings.key_cheat_speed)
|| key_input.pressed(settings.key_cheat_speed_backward) || key_input.pressed(settings.key_cheat_speed_backward)
@ -319,22 +332,31 @@ fn handle_cheats(
} }
if key_input.just_pressed(settings.key_cheat_pizza) { if key_input.just_pressed(settings.key_cheat_pizza) {
if let Some(target) = id2pos.0.get(&"pizzeria".to_string()) { if let Some(target) = id2pos.0.get(&CHEAT_WARP_1.to_string()) {
pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0); pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0);
gforce.ignore_gforce_seconds = 1.0; gforce.ignore_gforce_seconds = 1.0;
} }
if let Some(target) = id2v.0.get(&CHEAT_WARP_1.to_string()) {
v.0 = *target;
}
} }
if key_input.just_pressed(settings.key_cheat_farview1) { if key_input.just_pressed(settings.key_cheat_farview1) {
if let Some(target) = id2pos.0.get(&"busstopclippy2".to_string()) { if let Some(target) = id2pos.0.get(&CHEAT_WARP_2.to_string()) {
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0); pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
gforce.ignore_gforce_seconds = 1.0; gforce.ignore_gforce_seconds = 1.0;
} }
if let Some(target) = id2v.0.get(&CHEAT_WARP_2.to_string()) {
v.0 = *target;
}
} }
if key_input.just_pressed(settings.key_cheat_farview2) { if key_input.just_pressed(settings.key_cheat_farview2) {
if let Some(target) = id2pos.0.get(&"busstopclippy3".to_string()) { if let Some(target) = id2pos.0.get(&CHEAT_WARP_3.to_string()) {
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0); pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
gforce.ignore_gforce_seconds = 1.0; gforce.ignore_gforce_seconds = 1.0;
} }
if let Some(target) = id2v.0.get(&CHEAT_WARP_3.to_string()) {
v.0 = *target;
}
} }
if key_input.pressed(settings.key_cheat_adrenaline_zero) { if key_input.pressed(settings.key_cheat_adrenaline_zero) {
lifeform.adrenaline = 0.0; lifeform.adrenaline = 0.0;
@ -351,10 +373,24 @@ fn handle_cheats(
} }
} }
fn update_id2pos(mut id2pos: ResMut<Id2Pos>, q_id: Query<(&Position, &actor::Identifier)>) { fn update_id2pos(
mut id2pos: ResMut<Id2Pos>,
mut jupiterpos: ResMut<JupiterPos>,
q_id: Query<(&Position, &actor::Identifier)>,
) {
id2pos.0.clear(); id2pos.0.clear();
for (pos, id) in &q_id { for (pos, id) in &q_id {
id2pos.0.insert(id.0.clone(), pos.0); id2pos.0.insert(id.0.clone(), pos.0);
if id.0 == "jupiter" {
jupiterpos.0 = pos.0;
}
}
}
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);
} }
} }

View file

@ -707,7 +707,8 @@ fn update_dashboard(
fn update_speedometer( fn update_speedometer(
timer: ResMut<FPSUpdateTimer>, timer: ResMut<FPSUpdateTimer>,
settings: Res<Settings>, settings: Res<Settings>,
q_camera: Query<&LinearVelocity, With<actor::PlayerCamera>>, jupiter_pos: Res<game::JupiterPos>,
q_camera: Query<(&LinearVelocity, &Position), With<actor::PlayerCamera>>,
q_player: Query<&actor::ExperiencesGForce, With<actor::Player>>, q_player: Query<&actor::ExperiencesGForce, With<actor::Player>>,
q_target: Query<&LinearVelocity, With<IsTargeted>>, q_target: Query<&LinearVelocity, With<IsTargeted>>,
mut q_speedometer: Query<&mut Style, (With<Speedometer>, Without<Speedometer2>)>, mut q_speedometer: Query<&mut Style, (With<Speedometer>, Without<Speedometer2>)>,
@ -717,8 +718,9 @@ fn update_speedometer(
if !settings.hud_active || !timer.0.just_finished() { if !settings.hud_active || !timer.0.just_finished() {
return; return;
} }
if let Ok(cam_v) = q_camera.get_single() { if let Ok((cam_v, pos)) = q_camera.get_single() {
let speed = cam_v.length(); let orbital_v = nature::orbital_velocity(pos.0 - jupiter_pos.0, nature::JUPITER_MASS);
let speed = (cam_v.0 - orbital_v).length();
let speedometer_split = 5_000.0; let speedometer_split = 5_000.0;
if let Ok(mut speedometer) = q_speedometer.get_single_mut() { if let Ok(mut speedometer) = q_speedometer.get_single_mut() {

View file

@ -27,9 +27,11 @@ pub const G: f64 = 6.6743015e-11; // Gravitational constant in Nm²/kg²
pub const SOL_RADIUS: f64 = 696_300_000.0; pub const SOL_RADIUS: f64 = 696_300_000.0;
pub const JUPITER_RADIUS: f64 = 71_492_000.0; pub const JUPITER_RADIUS: f64 = 71_492_000.0;
pub const JUPITER_RING_RADIUS: f64 = 229_000_000.0; pub const JUPITER_RING_RADIUS: f64 = 229_000_000.0;
pub const EARTH_RADIUS: f64 = 6_371_000.0;
pub const SOL_MASS: f64 = 1.9885e30; pub const SOL_MASS: f64 = 1.9885e30;
pub const JUPITER_MASS: f64 = 1.8982e27; pub const JUPITER_MASS: f64 = 1.8982e27;
pub const EARTH_MASS: f64 = 5.972168e24;
// Each star's values: (x, y, z, magnitude, color index, distance, name) // Each star's values: (x, y, z, magnitude, color index, distance, name)
pub const STARS: &[(f32, f32, f32, f32, f32, f32, &str)] = &include!("data/stars.in"); pub const STARS: &[(f32, f32, f32, f32, f32, f32, &str)] = &include!("data/stars.in");
@ -163,10 +165,38 @@ pub fn inverse_lorentz_factor_custom_c(speed: f64, c: f64) -> f64 {
(1.0 - (speed.powf(2.0) / c.powf(2.0))).sqrt() (1.0 - (speed.powf(2.0) / c.powf(2.0))).sqrt()
} }
/// Calculates orbit duration in seconds, with given parameters, assuming circular orbit.
pub fn simple_orbital_period(mass: f64, distance: f64) -> f64 { pub fn simple_orbital_period(mass: f64, distance: f64) -> f64 {
return 2.0 * PI * (distance.powf(3.0) / (G * mass)).sqrt(); return 2.0 * PI * (distance.powf(3.0) / (G * mass)).sqrt();
} }
/// Calculates the orbital velocity with given parameters, assuming prograde circular orbit.
pub fn orbital_velocity(coords: DVec3, mass: f64) -> DVec3 {
let r = coords.length();
let speed = (G * mass / r).sqrt();
// This generates a perpendicular orbital vector in the prograde direction
let perpendicular = DVec3::new(coords.z, 0.0, -coords.x).normalize();
return perpendicular * speed;
}
/// Calculates the acceleration towards a mass in m/s
pub fn gravitational_acceleration(coords: DVec3, mass: f64) -> DVec3 {
let r_squared = coords.length_squared();
let acceleration_magnitude = G * mass / r_squared;
return -acceleration_magnitude * (coords / r_squared.sqrt());
}
#[test]
fn test_gravitational_acceleration() {
let coords = DVec3::new(EARTH_RADIUS, 0.0, 0.0);
let mass = EARTH_MASS;
let g = gravitational_acceleration(coords, mass);
let g_rounded = (g * 10.0).round() / 10.0;
assert_eq!(g_rounded, DVec3::new(-9.8, 0.0, 0.0));
}
pub fn phase_dist_to_coords(phase_radians: f64, distance: f64) -> DVec3 { pub fn phase_dist_to_coords(phase_radians: f64, distance: f64) -> DVec3 {
return DVec3::new( return DVec3::new(
distance * phase_radians.cos(), distance * phase_radians.cos(),

View file

@ -19,6 +19,7 @@ use bevy_xpbd_3d::prelude::*;
use fastrand; use fastrand;
use std::collections::HashMap; use std::collections::HashMap;
const ENABLE_ASTEROIDS: bool = false;
const ASTEROID_UPDATE_INTERVAL: f32 = 0.1; // seconds const ASTEROID_UPDATE_INTERVAL: f32 = 0.1; // seconds
const ASTEROID_SIZE_FACTOR: f32 = 10.0; const ASTEROID_SIZE_FACTOR: f32 = 10.0;
const RING_THICKNESS: f64 = 8.0e6; const RING_THICKNESS: f64 = 8.0e6;
@ -36,19 +37,21 @@ pub struct WorldPlugin;
impl Plugin for WorldPlugin { impl Plugin for WorldPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, setup); app.add_systems(Startup, setup);
app.add_systems(PostUpdate, handle_despawn);
app.add_systems(Update, spawn_despawn_asteroids);
app.add_systems(Update, handle_respawn.run_if(on_event::<RespawnEvent>())); app.add_systems(Update, handle_respawn.run_if(on_event::<RespawnEvent>()));
app.add_plugins(PhysicsPlugins::default()); app.add_plugins(PhysicsPlugins::default());
//app.add_plugins(PhysicsDebugPlugin::default()); //app.add_plugins(PhysicsDebugPlugin::default());
app.insert_resource(Gravity(DVec3::splat(0.0))); app.insert_resource(Gravity(DVec3::splat(0.0)));
app.insert_resource(ActiveAsteroids(HashMap::new()));
app.add_event::<RespawnEvent>();
if ENABLE_ASTEROIDS {
app.insert_resource(AsteroidUpdateTimer(Timer::from_seconds( app.insert_resource(AsteroidUpdateTimer(Timer::from_seconds(
ASTEROID_UPDATE_INTERVAL, ASTEROID_UPDATE_INTERVAL,
TimerMode::Repeating, TimerMode::Repeating,
))); )));
app.insert_resource(ActiveAsteroids(HashMap::new())); app.add_systems(Update, spawn_despawn_asteroids);
app.add_systems(PostUpdate, handle_despawn_asteroids);
app.add_event::<DespawnAsteroidEvent>(); app.add_event::<DespawnAsteroidEvent>();
app.add_event::<RespawnEvent>(); }
} }
} }
@ -344,7 +347,7 @@ fn spawn_despawn_asteroids(
} }
} }
fn handle_despawn( fn handle_despawn_asteroids(
mut commands: Commands, mut commands: Commands,
mut er_despawn: EventReader<DespawnAsteroidEvent>, mut er_despawn: EventReader<DespawnAsteroidEvent>,
mut db: ResMut<ActiveAsteroids>, mut db: ResMut<ActiveAsteroids>,