Merge branch 'gravity'
This commit is contained in:
commit
1a94c31d62
|
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
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 :)
|
||||
|
|
86
src/actor.rs
86
src/actor.rs
|
@ -30,8 +30,9 @@ impl Plugin for ActorPlugin {
|
|||
(
|
||||
update_physics_lifeforms,
|
||||
update_power,
|
||||
handle_gravity,
|
||||
handle_wants_maxrotation,
|
||||
handle_wants_maxvelocity,
|
||||
handle_wants_maxvelocity.run_if(any_with_component::<WantsMaxVelocity>),
|
||||
handle_wants_lookat.run_if(alive),
|
||||
),
|
||||
);
|
||||
|
@ -118,6 +119,7 @@ pub struct ExperiencesGForce {
|
|||
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 {
|
||||
|
@ -127,8 +129,9 @@ impl Default for ExperiencesGForce {
|
|||
damage_threshold: 100.0,
|
||||
visual_effect_threshold: 20.0,
|
||||
visual_effect: 0.0,
|
||||
last_linear_velocity: DVec3::splat(0.0),
|
||||
ignore_gforce_seconds: 0.0,
|
||||
last_linear_velocity: DVec3::ZERO,
|
||||
gravitational_component: DVec3::ZERO,
|
||||
ignore_gforce_seconds: 0.01,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +159,11 @@ pub struct WantsMaxVelocity(pub f64);
|
|||
#[derive(Component)]
|
||||
pub struct WantsToLookAt(pub String);
|
||||
#[derive(Component)]
|
||||
pub struct WantsMatchVelocityWith(pub String);
|
||||
#[derive(Component)]
|
||||
pub struct Identifier(pub String);
|
||||
#[derive(Component)]
|
||||
pub struct OrbitsJupiter;
|
||||
|
||||
#[derive(Component)]
|
||||
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(
|
||||
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();
|
||||
for (mut v, engine, maxv) in &mut query {
|
||||
let total = v.0.length();
|
||||
if total <= maxv.0 + EPSILON {
|
||||
if total > maxv.0 {
|
||||
v.0 = DVec3::splat(0.0);
|
||||
for (pos, mut v, engine, maxv, orbits_jupiter, matchwith) in &mut query {
|
||||
let target_velocity = if let Some(matchwith) = matchwith {
|
||||
if let Some(target_v) = id2v.0.get(&matchwith.0) {
|
||||
*target_v
|
||||
} 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 {
|
||||
// slow it down a little bit
|
||||
// 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;
|
||||
let acceleration = (avg_thrust * dt) as f64 * -relative_velocity;
|
||||
v.0 += acceleration;
|
||||
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 factor = 1.0 / dt / nature::EARTH_GRAVITY;
|
||||
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.gravitational_component = DVec3::ZERO;
|
||||
if gforce.ignore_gforce_seconds > 0.0 {
|
||||
gforce.ignore_gforce_seconds -= dt;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -416,6 +416,7 @@ pub fn apply_input_to_player(
|
|||
time: Res<Time>,
|
||||
mut commands: Commands,
|
||||
settings: Res<var::Settings>,
|
||||
jupiter_pos: Res<game::JupiterPos>,
|
||||
windows: Query<&Window, With<PrimaryWindow>>,
|
||||
mut mouse_events: EventReader<MouseMotion>,
|
||||
key_input: Res<ButtonInput<KeyCode>>,
|
||||
|
@ -426,6 +427,7 @@ pub fn apply_input_to_player(
|
|||
Entity,
|
||||
&Transform,
|
||||
&mut actor::Engine,
|
||||
&Position,
|
||||
&mut LinearVelocity,
|
||||
&mut ExternalTorque,
|
||||
Option<&actor::PlayerDrivesThis>,
|
||||
|
@ -451,15 +453,15 @@ pub fn apply_input_to_player(
|
|||
win_res_y = 1050.0;
|
||||
}
|
||||
|
||||
let target_v: DVec3 = if let Ok(target) = q_target.get_single() {
|
||||
target.0
|
||||
} else {
|
||||
DVec3::splat(0.0)
|
||||
};
|
||||
|
||||
if let Ok((player_entity, player_transform, mut engine, mut v, mut torque, bike)) =
|
||||
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() {
|
||||
target.0
|
||||
} else {
|
||||
let relative_pos = pos.0 - jupiter_pos.0;
|
||||
nature::orbital_velocity(relative_pos, nature::JUPITER_MASS)
|
||||
};
|
||||
// Handle key input
|
||||
if focused {
|
||||
if key_input.pressed(settings.key_forward) || settings.cruise_control_active {
|
||||
|
|
14
src/chat.rs
14
src/chat.rs
|
@ -827,6 +827,7 @@ pub fn handle_chat_scripts(
|
|||
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
||||
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
||||
id2pos: Res<game::Id2Pos>,
|
||||
id2v: Res<game::Id2V>,
|
||||
) {
|
||||
for script in er_chatscript.read() {
|
||||
// Parse the script string
|
||||
|
@ -892,15 +893,22 @@ pub fn handle_chat_scripts(
|
|||
_ => None,
|
||||
};
|
||||
if let Some(station) = busstop {
|
||||
if let Some(target) = id2pos.0.get(&station.to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
v.0 = DVec3::ZERO;
|
||||
if let Some(target_pos) = id2pos.0.get(&station.to_string()) {
|
||||
pos.0 = *target_pos + DVec3::new(0.0, -1000.0, 0.0);
|
||||
} else {
|
||||
error!(
|
||||
"Could not determine position of actor with ID: '{}'",
|
||||
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 {
|
||||
error!("Invalid destination for cryotrip chat script: '{}'", param1);
|
||||
}
|
||||
|
|
27
src/cmd.rs
27
src/cmd.rs
|
@ -86,6 +86,7 @@ struct ParserState {
|
|||
wants_maxrotation: Option<f64>,
|
||||
wants_maxvelocity: Option<f64>,
|
||||
wants_tolookat_id: Option<String>,
|
||||
wants_matchvelocity_id: Option<String>,
|
||||
collider_is_mesh: bool,
|
||||
collider_is_one_mesh_of_scene: bool,
|
||||
thrust_forward: f32,
|
||||
|
@ -143,6 +144,7 @@ impl Default for ParserState {
|
|||
wants_maxrotation: None,
|
||||
wants_maxvelocity: None,
|
||||
wants_tolookat_id: None,
|
||||
wants_matchvelocity_id: None,
|
||||
collider_is_mesh: false,
|
||||
collider_is_one_mesh_of_scene: false,
|
||||
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
|
||||
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] => {
|
||||
state.ar_model = Some(asset_name.to_string());
|
||||
}
|
||||
|
@ -617,6 +623,12 @@ fn spawn_entities(
|
|||
settings: Res<var::Settings>,
|
||||
) {
|
||||
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 mut rotation = state.rotation;
|
||||
if state.class == DefClass::Actor {
|
||||
|
@ -668,6 +680,13 @@ fn spawn_entities(
|
|||
1.0
|
||||
} * 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
|
||||
let actor_entity;
|
||||
|
@ -680,6 +699,9 @@ fn spawn_entities(
|
|||
..default()
|
||||
});
|
||||
actor.insert(SleepingDisabled);
|
||||
if orbits_jupiter {
|
||||
actor.insert(actor::OrbitsJupiter);
|
||||
}
|
||||
actor.insert(world::DespawnOnPlayerDeath);
|
||||
actor.insert(actor::HitPoints::default());
|
||||
actor.insert(Position::from(absolute_pos));
|
||||
|
@ -715,7 +737,7 @@ fn spawn_entities(
|
|||
// Physics Parameters
|
||||
if state.has_physics {
|
||||
actor.insert(RigidBody::Dynamic);
|
||||
actor.insert(LinearVelocity(state.velocity));
|
||||
actor.insert(LinearVelocity(velocity));
|
||||
actor.insert(AngularVelocity(state.angular_momentum));
|
||||
actor.insert(ColliderDensity(state.density));
|
||||
if state.collider_is_mesh {
|
||||
|
@ -799,6 +821,9 @@ fn spawn_entities(
|
|||
if let Some(value) = &state.wants_tolookat_id {
|
||||
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 {
|
||||
actor.insert((
|
||||
PointLight {
|
||||
|
|
|
@ -122,11 +122,11 @@ actor 0 0 0
|
|||
only_in_map_at_dist 1e7 amalthea
|
||||
clickable no
|
||||
physics off
|
||||
actor 0 127093 0 moonlet
|
||||
actor 0 0 0 moonlet
|
||||
name Thebe
|
||||
relativeto jupiter
|
||||
id thebe
|
||||
orbit 221900e3 0.34
|
||||
orbitaround jupiter 221900e3
|
||||
scale 50e3
|
||||
moon yes
|
||||
angularmomentum 0 0.025 0
|
||||
|
@ -256,10 +256,11 @@ actor 0 0 0
|
|||
physics off
|
||||
|
||||
|
||||
actor 0 593051 0 suitv2
|
||||
actor 0 59305 0 suitv2
|
||||
template person
|
||||
relativeto jupiter
|
||||
orbit 221900e3 0.338
|
||||
orbitaround jupiter 221900e3
|
||||
orbit_phase_offset 0.002
|
||||
player yes
|
||||
id player
|
||||
wants maxvelocity none
|
||||
|
@ -287,6 +288,7 @@ actor -55e3 44e3 0 suitv2
|
|||
name "Yuni"
|
||||
chatid Yuni
|
||||
rotationx 180
|
||||
wants matchvelocitywith thebe
|
||||
|
||||
actor 5000 0 -3000 moonlet
|
||||
name Moonlet
|
||||
|
@ -308,12 +310,14 @@ actor 13200 300 -3000 hollow_asteroid
|
|||
actor 0 0 0 suitv2
|
||||
template person
|
||||
relativeto cultasteroid
|
||||
wants matchvelocitywith cultasteroid
|
||||
name "Ash"
|
||||
chatid Ash
|
||||
pronoun they
|
||||
actor -8 8 0 suitv2
|
||||
template person
|
||||
relativeto cultasteroid
|
||||
wants matchvelocitywith cultasteroid
|
||||
name "River"
|
||||
chatid River
|
||||
rotationy 54
|
||||
|
@ -442,6 +446,7 @@ actor -3300 10 0 pizzeria
|
|||
relativeto pizzeria
|
||||
armodel clippy_ar
|
||||
wants lookat PLAYERCAMERA
|
||||
wants matchvelocitywith pizzeria
|
||||
rotationy -126
|
||||
chatid SubduedClippy
|
||||
|
||||
|
@ -452,6 +457,7 @@ actor -3300 10 0 pizzeria
|
|||
chatid PizzaChef
|
||||
armodel suit_ar_chefhat
|
||||
wants lookat PLAYERCAMERA
|
||||
wants matchvelocitywith pizzeria
|
||||
rotationy -90
|
||||
pronoun he
|
||||
|
||||
|
@ -464,6 +470,7 @@ actor 30 -12 -40 suitv2
|
|||
armodel suit_ar_wings
|
||||
angularmomentum 0.4 0.2 0.1
|
||||
wants maxrotation 0.5
|
||||
wants matchvelocitywith pizzeria
|
||||
rotationy 108
|
||||
rotationx 180
|
||||
pronoun it
|
||||
|
@ -495,6 +502,7 @@ actor -300 0 40 suitv2
|
|||
name "梓涵"
|
||||
chatid Drifter
|
||||
alive no
|
||||
wants maxvelocity none
|
||||
oxygen 0.08
|
||||
pronoun she
|
||||
|
||||
|
@ -505,12 +513,14 @@ actor 100 -18000 2000 clippy
|
|||
name "StarTrans Clippy™ Serenity Station"
|
||||
armodel clippy_ar
|
||||
wants lookat PLAYERCAMERA
|
||||
wants matchvelocitywith orbbusstopserenity
|
||||
rotationy -90
|
||||
chatid ClippyTransSerenity
|
||||
|
||||
actor 60 0 0 "orb_busstop"
|
||||
name "StarTrans Bus Stop: Serenity Station"
|
||||
relativeto busstopclippy
|
||||
id orbbusstopserenity
|
||||
scale 5
|
||||
actor 80 0 0 "orb_busstop"
|
||||
name "StarTrans Bus Stop: Serenity Station"
|
||||
|
@ -637,6 +647,7 @@ actor 100 -18000 2000 clippy
|
|||
actor 8 20 0 suitv2
|
||||
template person
|
||||
relativeto "busstopclippy"
|
||||
wants matchvelocitywith orbbusstopserenity
|
||||
name "Rudy"
|
||||
chatid NPCinCryoStasis
|
||||
pronoun he
|
||||
|
@ -648,12 +659,14 @@ actor -184971e3 149410e3 -134273e3 clippy
|
|||
name "StarTrans Clippy™ Farview Station"
|
||||
armodel clippy_ar
|
||||
wants lookat PLAYERCAMERA
|
||||
wants matchvelocitywith orbbusstopfarview
|
||||
rotationy -90
|
||||
chatid ClippyTransFarview
|
||||
|
||||
actor 60 0 0 "orb_busstop"
|
||||
name "StarTrans Bus Stop: Farview Station"
|
||||
relativeto busstopclippy2
|
||||
id orbbusstopfarview
|
||||
scale 5
|
||||
actor 80 0 0 "orb_busstop"
|
||||
name "StarTrans Bus Stop: Farview Station"
|
||||
|
@ -785,6 +798,7 @@ actor 0 -44e3 0 clippy
|
|||
name "StarTrans Clippy™ Metis Prime Station"
|
||||
armodel clippy_ar
|
||||
wants lookat PLAYERCAMERA
|
||||
wants matchvelocitywith orbbusstopmetis
|
||||
orbitaround jupiter 128000e3
|
||||
orbit_phase_offset -0.002
|
||||
rotationy -90
|
||||
|
@ -793,6 +807,7 @@ actor 0 -44e3 0 clippy
|
|||
actor 60 0 0 "orb_busstop"
|
||||
name "StarTrans Bus Stop: Metis Prime Station"
|
||||
relativeto busstopclippy3
|
||||
id orbbusstopmetis
|
||||
scale 5
|
||||
actor 80 0 0 "orb_busstop"
|
||||
name "StarTrans Bus Stop: Metis Prime Station"
|
||||
|
|
46
src/game.rs
46
src/game.rs
|
@ -18,6 +18,10 @@ use bevy::window::{PrimaryWindow, Window, WindowMode};
|
|||
use bevy_xpbd_3d::prelude::*;
|
||||
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;
|
||||
impl Plugin for GamePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
|
@ -26,12 +30,15 @@ impl Plugin for GamePlugin {
|
|||
app.add_systems(PostUpdate, handle_game_event);
|
||||
app.add_systems(PreUpdate, handle_player_death);
|
||||
app.add_systems(PostUpdate, update_id2pos);
|
||||
app.add_systems(PostUpdate, update_id2v);
|
||||
app.add_systems(
|
||||
Update,
|
||||
handle_achievement_event.run_if(on_event::<AchievementEvent>()),
|
||||
);
|
||||
app.add_systems(Update, check_achievements);
|
||||
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::Settings::default());
|
||||
app.insert_resource(var::GameVars::default());
|
||||
|
@ -50,6 +57,10 @@ pub struct PlayerDiesEvent(pub actor::DamageType);
|
|||
#[derive(Resource)]
|
||||
pub struct Id2Pos(pub HashMap<String, DVec3>);
|
||||
#[derive(Resource)]
|
||||
pub struct Id2V(pub HashMap<String, DVec3>);
|
||||
#[derive(Resource)]
|
||||
pub struct JupiterPos(pub DVec3);
|
||||
#[derive(Resource)]
|
||||
pub struct AchievementCheckTimer(pub Timer);
|
||||
|
||||
#[derive(Event)]
|
||||
|
@ -260,7 +271,9 @@ fn handle_cheats(
|
|||
>,
|
||||
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
||||
mut settings: ResMut<Settings>,
|
||||
jupiter_pos: Res<JupiterPos>,
|
||||
id2pos: Res<Id2Pos>,
|
||||
id2v: Res<Id2V>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
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) {
|
||||
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)
|
||||
|| 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 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);
|
||||
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 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);
|
||||
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 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);
|
||||
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) {
|
||||
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();
|
||||
for (pos, id) in &q_id {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -707,7 +707,8 @@ fn update_dashboard(
|
|||
fn update_speedometer(
|
||||
timer: ResMut<FPSUpdateTimer>,
|
||||
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_target: Query<&LinearVelocity, With<IsTargeted>>,
|
||||
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() {
|
||||
return;
|
||||
}
|
||||
if let Ok(cam_v) = q_camera.get_single() {
|
||||
let speed = cam_v.length();
|
||||
if let Ok((cam_v, pos)) = q_camera.get_single() {
|
||||
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;
|
||||
if let Ok(mut speedometer) = q_speedometer.get_single_mut() {
|
||||
|
|
|
@ -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 JUPITER_RADIUS: f64 = 71_492_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 JUPITER_MASS: f64 = 1.8982e27;
|
||||
pub const EARTH_MASS: f64 = 5.972168e24;
|
||||
|
||||
// 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");
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
/// Calculates orbit duration in seconds, with given parameters, assuming circular orbit.
|
||||
pub fn simple_orbital_period(mass: f64, distance: f64) -> f64 {
|
||||
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 {
|
||||
return DVec3::new(
|
||||
distance * phase_radians.cos(),
|
||||
|
|
19
src/world.rs
19
src/world.rs
|
@ -19,6 +19,7 @@ use bevy_xpbd_3d::prelude::*;
|
|||
use fastrand;
|
||||
use std::collections::HashMap;
|
||||
|
||||
const ENABLE_ASTEROIDS: bool = false;
|
||||
const ASTEROID_UPDATE_INTERVAL: f32 = 0.1; // seconds
|
||||
const ASTEROID_SIZE_FACTOR: f32 = 10.0;
|
||||
const RING_THICKNESS: f64 = 8.0e6;
|
||||
|
@ -36,19 +37,21 @@ pub struct WorldPlugin;
|
|||
impl Plugin for WorldPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
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_plugins(PhysicsPlugins::default());
|
||||
//app.add_plugins(PhysicsDebugPlugin::default());
|
||||
app.insert_resource(Gravity(DVec3::splat(0.0)));
|
||||
app.insert_resource(AsteroidUpdateTimer(Timer::from_seconds(
|
||||
ASTEROID_UPDATE_INTERVAL,
|
||||
TimerMode::Repeating,
|
||||
)));
|
||||
app.insert_resource(ActiveAsteroids(HashMap::new()));
|
||||
app.add_event::<DespawnAsteroidEvent>();
|
||||
app.add_event::<RespawnEvent>();
|
||||
if ENABLE_ASTEROIDS {
|
||||
app.insert_resource(AsteroidUpdateTimer(Timer::from_seconds(
|
||||
ASTEROID_UPDATE_INTERVAL,
|
||||
TimerMode::Repeating,
|
||||
)));
|
||||
app.add_systems(Update, spawn_despawn_asteroids);
|
||||
app.add_systems(PostUpdate, handle_despawn_asteroids);
|
||||
app.add_event::<DespawnAsteroidEvent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,7 +347,7 @@ fn spawn_despawn_asteroids(
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_despawn(
|
||||
fn handle_despawn_asteroids(
|
||||
mut commands: Commands,
|
||||
mut er_despawn: EventReader<DespawnAsteroidEvent>,
|
||||
mut db: ResMut<ActiveAsteroids>,
|
||||
|
|
Loading…
Reference in a new issue