Compare commits

...

8 commits

7 changed files with 90 additions and 40 deletions

View file

@ -1,13 +1,14 @@
# v0.12.0-dev
- Add power-hungry optional thruster boost
- Implement space suit thruster particle effects
- Implement space suit modding
- Add power-hungry thruster booster
- Add different flashlight power settings
- Add different light amplification settings
# v0.11.1
- Added space suit thruster particle effects
- Fixed radio stations
- Add reactor overload and shutdown
- Add battery damage when overusing thruster booster
- Add radiation damage
- Fix radio stations
# v0.11.0

View file

@ -115,7 +115,7 @@ A variety of relatively simple game systems should interact with each other to c
- [X] Augmented Reality
- [X] Flashlight intensity
- [ ] AI assistance systems
- [ ] Thruster boost
- [X] Thruster boost
- [ ] G-force dampeners
- [ ] Life support, material recyclers (air, water, etc)
- [ ] High energy particle shield?

View file

@ -21,10 +21,13 @@ use bevy_xpbd_3d::prelude::*;
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
const MAX_INTERACT_DISTANCE: f32 = 50.0;
const POWER_DRAIN_THRUSTER: [f32; 3] = [3e6, 3e6, 0.0];
const THRUSTER_BOOST_FACTOR: [f64; 3] = [3.0, 3.0, 0.0];
const POWER_DRAIN_FLASHLIGHT: [f32; 3] = [200e3, 1500e3, 2500e3];
pub const FLASHLIGHT_INTENSITY: [f32; 3] = [10e6, 400e6, 2e9]; // in lumens
pub const POWER_DRAIN_THRUSTER: [f32; 3] = [3e6, 3e6, 0.0];
pub const THRUSTER_BOOST_FACTOR: [f64; 3] = [3.0, 3.0, 0.0];
pub const POWER_DRAIN_FLASHLIGHT: [f32; 4] = [200e3, 1500e3, 2500e3, 10000e3];
pub const FLASHLIGHT_INTENSITY: [f32; 4] = [10e6, 400e6, 2e9, 100e9]; // in lumens
pub const POWER_DRAIN_LIGHTAMP: [f32; 4] = [0.0, 200e3, 400e3, 1400e3];
pub const POWER_DRAIN_AR: f32 = 300e3;
pub const POWER_GAIN_REACTOR: [f32; 3] = [0.0, 2000e3, 10000e3];
pub struct ActorPlugin;
impl Plugin for ActorPlugin {
@ -68,7 +71,7 @@ pub enum DamageType {
Asphyxiation,
Depressurization,
//Poison,
//Radiation,
Radiation,
//Freeze,
//Burn,
}
@ -172,6 +175,7 @@ pub struct OrbitsJupiter;
#[derive(Component)]
pub struct LifeForm {
pub is_alive: bool,
pub is_radioactively_damaged: bool,
pub adrenaline: f32,
pub adrenaline_baseline: f32,
pub adrenaline_jolt: f32,
@ -180,6 +184,7 @@ impl Default for LifeForm {
fn default() -> Self {
Self {
is_alive: true,
is_radioactively_damaged: false,
adrenaline: 0.3,
adrenaline_baseline: 0.3,
adrenaline_jolt: 0.0,
@ -257,7 +262,6 @@ const SUIT_SIMPLE: Suit = Suit {
pub struct Battery {
pub power: f32, // Watt-seconds
pub capacity: f32, // Watt-seconds
pub reactor: f32, // Watt (production)
pub overloaded_recovering: bool,
}
@ -266,7 +270,6 @@ impl Default for Battery {
Self {
power: 10e3 * 3600.0,
capacity: 10e3 * 3600.0, // 10kWh
reactor: 2000e3, // 2MW
overloaded_recovering: false,
}
}
@ -295,8 +298,8 @@ pub fn update_power(
}
}
if settings.hud_active {
let mut hud_drain = 300e3; // 300kW
hud_drain += prefs.light_amp as f32 * 200e3; // 200kW per level
let mut hud_drain = POWER_DRAIN_AR;
hud_drain += POWER_DRAIN_LIGHTAMP[prefs.light_amp];
battery.power -= hud_drain * d;
if battery.power <= 0.0 {
power_down = true;
@ -327,7 +330,8 @@ pub fn update_power(
if power_down {
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::PowerDown));
}
battery.power = (battery.power + battery.reactor * d).clamp(0.0, battery.capacity);
let reactor = POWER_GAIN_REACTOR[settings.reactor_state];
battery.power = (battery.power + reactor * d).clamp(0.0, battery.capacity);
if battery.overloaded_recovering && battery.power > battery.capacity * 0.5 {
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::PowerUp));
battery.overloaded_recovering = false;
@ -337,10 +341,12 @@ pub fn update_power(
pub fn update_physics_lifeforms(
time: Res<Time>,
mut query: Query<(&mut LifeForm, &mut HitPoints, &mut Suit, &LinearVelocity)>,
settings: Res<Settings>,
id2pos: Res<game::Id2Pos>,
mut query: Query<(&mut LifeForm, &mut HitPoints, &mut Suit, &LinearVelocity, &Position, Option<&Player>)>,
) {
let d = time.delta_seconds();
for (mut lifeform, mut hp, mut suit, velocity) in query.iter_mut() {
for (mut lifeform, mut hp, mut suit, velocity, pos, player) in query.iter_mut() {
if lifeform.adrenaline_jolt.abs() > 1e-3 {
lifeform.adrenaline_jolt *= 0.99;
} else {
@ -353,6 +359,20 @@ pub fn update_physics_lifeforms(
lifeform.adrenaline =
(lifeform.adrenaline - 0.0001 + lifeform.adrenaline_jolt * 0.01).clamp(0.0, 1.0);
if player.is_some() {
lifeform.is_radioactively_damaged = if settings.reactor_state == 2 {
true
} else if let Some(pos_jupiter) = id2pos.0.get(cmd::ID_JUPITER) {
pos_jupiter.distance(pos.0) < 140_000_000.0
} else {
false
};
if lifeform.is_radioactively_damaged {
hp.damage += 0.3 * d;
hp.damagetype = DamageType::Radiation;
}
}
let mut oxygen_drain = nature::OXY_S;
let integr_threshold = 0.5;
if suit.integrity < integr_threshold {

View file

@ -331,7 +331,14 @@ fn handle_player_death(
duration: 1.0,
});
}
_ => {
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 => {
settings.death_cause = "Unknown".to_string();
ew_effect.send(visual::SpawnEffectEvent {
class: visual::Effects::FadeIn(css::MAROON.into()),

View file

@ -682,8 +682,7 @@ pub fn setup(
fn update_dashboard(
timer: ResMut<FPSUpdateTimer>,
mut q_dashboard: Query<(&mut Visibility, &Dashboard)>,
id2pos: Res<game::Id2Pos>,
q_player: Query<(&actor::Suit, &actor::Battery, &Position), With<actor::Player>>,
q_player: Query<(&actor::Suit, &actor::Battery, &actor::LifeForm), With<actor::Player>>,
settings: Res<Settings>,
) {
if !settings.hud_active || !timer.0.just_finished() {
@ -693,7 +692,7 @@ fn update_dashboard(
if player.is_err() {
return;
}
let (suit, battery, pos) = player.unwrap();
let (suit, battery, lifeform) = player.unwrap();
for (mut vis, icon) in &mut q_dashboard {
*vis = bool2vis(match icon {
@ -702,13 +701,7 @@ fn update_dashboard(
Dashboard::Battery => battery.overloaded_recovering,
Dashboard::RotationStabiliser => !settings.rotation_stabilizer_active,
Dashboard::CruiseControl => settings.cruise_control_active,
Dashboard::Radioactivity => {
if let Some(pos_jupiter) = id2pos.0.get(cmd::ID_JUPITER) {
pos_jupiter.distance(pos.0) < 140_000_000.0
} else {
false
}
}
Dashboard::Radioactivity => lifeform.is_radioactively_damaged,
});
}
}

View file

@ -69,6 +69,7 @@ pub const MENUDEF: &[(&str, MenuAction)] = &[
("", MenuAction::ModLightAmp),
("", MenuAction::ModFlashlightPower),
("", MenuAction::ModThrusterBoost),
("", MenuAction::ModReactor),
("", MenuAction::ToggleSound),
("", MenuAction::ToggleMusic),
("", MenuAction::ToggleCamera),
@ -86,6 +87,7 @@ pub enum MenuAction {
ModLightAmp,
ModFlashlightPower,
ModThrusterBoost,
ModReactor,
ToggleSound,
ToggleMusic,
ToggleCamera,
@ -486,24 +488,39 @@ pub fn update_menu(
}
MenuAction::ToggleAR => {
let onoff = bool2string(settings.hud_active);
text.sections[i].value = format!("Augmented Reality: {onoff} [TAB]\n");
let p = if settings.hud_active { actor::POWER_DRAIN_AR / 1e3 } else { 0.0 };
let kw = if p > 0.0 { format!(" ({p}kW)") } else { String::from("") };
text.sections[i].value = format!("Augmented Reality: {onoff}{kw} [TAB]\n");
}
MenuAction::ModLightAmp => {
let n = prefs.light_amp;
text.sections[i].value = format!("Light Amplification: {n}/3\n");
let p = actor::POWER_DRAIN_LIGHTAMP[prefs.light_amp] / 1e3;
text.sections[i].value = format!("Light Amplification: {p}kW\n");
}
MenuAction::ModFlashlightPower => {
let n = prefs.flashlight_power + 1;
text.sections[i].value = format!("Flashlight Power: {n}/3\n");
let p = actor::POWER_DRAIN_FLASHLIGHT[prefs.flashlight_power] / 1e3;
text.sections[i].value = format!("Flashlight Power: {p}kW\n");
}
MenuAction::ModThrusterBoost => {
let state = match prefs.thruster_boost {
0 => "Only when slowing down",
1 => "MAX POWER",
0 => "For braking",
1 => "Always",
2 => "Off",
_ => "ERROR",
};
text.sections[i].value = format!("Thruster Boost: {state}\n");
let p = actor::POWER_DRAIN_THRUSTER[prefs.thruster_boost] / 1e3;
let kw = if p > 0.0 { format!(" ({p}kW)") } else { String::from("") };
text.sections[i].value = format!("Thruster Boost: {state}{kw}\n");
}
MenuAction::ModReactor => {
let state = match settings.reactor_state {
0 => "Off",
1 => "On",
2 => "OVERLOAD",
_ => "ERROR",
};
let p = actor::POWER_GAIN_REACTOR[settings.reactor_state] / 1e3;
let kw = if p > 0.0 { format!(" (+{p}kW)") } else { String::from("") };
text.sections[i].value = format!("Reactor: {state}{kw}\n");
}
MenuAction::ChangeARAvatar => {
if let Some(ava) = hud::PLAYER_AR_AVATARS.get(settings.ar_avatar) {
@ -608,14 +625,16 @@ pub fn handle_input(
if prefs.light_amp > 3 {
prefs.light_amp = 0;
}
prefs.save();
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
ew_updatemenu.send(UpdateMenuEvent);
}
MenuAction::ModFlashlightPower => {
prefs.flashlight_power += 1;
if prefs.flashlight_power > 2 {
if prefs.flashlight_power > 3 {
prefs.flashlight_power = 0;
}
prefs.save();
ew_game.send(GameEvent::UpdateFlashlight);
ew_updatemenu.send(UpdateMenuEvent);
}
@ -624,6 +643,14 @@ pub fn handle_input(
if prefs.thruster_boost > 2 {
prefs.thruster_boost = 0;
}
prefs.save();
ew_updatemenu.send(UpdateMenuEvent);
}
MenuAction::ModReactor => {
settings.reactor_state += 1;
if settings.reactor_state > 2 {
settings.reactor_state = 0;
}
ew_updatemenu.send(UpdateMenuEvent);
}
MenuAction::ToggleMusic => {

View file

@ -86,6 +86,7 @@ pub struct Settings {
pub chat_speed: f32,
pub ar_avatar: usize,
pub flashlight_active: bool,
pub reactor_state: usize,
pub hud_active: bool,
pub map_active: bool,
pub deathscreen_active: bool,
@ -222,6 +223,7 @@ impl Default for Settings {
chat_speed: DEFAULT_CHAT_SPEED,
ar_avatar: 0,
flashlight_active: false,
reactor_state: 1,
hud_active: true,
map_active: false,
deathscreen_active: false,
@ -575,7 +577,7 @@ pub fn load_prefs() -> Preferences {
println!("Loaded preferences from internal defaults");
}
prefs.source_file = path;
prefs.flashlight_power = prefs.flashlight_power.clamp(0, 2);
prefs.flashlight_power = prefs.flashlight_power.clamp(0, 3);
prefs.light_amp = prefs.light_amp.clamp(0, 3);
prefs.thruster_boost = prefs.thruster_boost.clamp(0, 2);
prefs