Compare commits
8 commits
ae3e9c9b48
...
18b1d6feae
Author | SHA1 | Date | |
---|---|---|---|
yuni | 18b1d6feae | ||
yuni | b6b8e6a8d0 | ||
yuni | 56bab3f526 | ||
yuni | b1b2ed5f34 | ||
yuni | 5aa815a803 | ||
yuni | 5104f2bbcf | ||
yuni | bb539c00cc | ||
yuni | 0b5058abf5 |
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,13 +1,14 @@
|
||||||
# v0.12.0-dev
|
# 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 flashlight power settings
|
||||||
- Add different light amplification settings
|
- Add different light amplification settings
|
||||||
|
- Add reactor overload and shutdown
|
||||||
# v0.11.1
|
- Add battery damage when overusing thruster booster
|
||||||
|
- Add radiation damage
|
||||||
- Added space suit thruster particle effects
|
- Fix radio stations
|
||||||
- Fixed radio stations
|
|
||||||
|
|
||||||
# v0.11.0
|
# v0.11.0
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ A variety of relatively simple game systems should interact with each other to c
|
||||||
- [X] Augmented Reality
|
- [X] Augmented Reality
|
||||||
- [X] Flashlight intensity
|
- [X] Flashlight intensity
|
||||||
- [ ] AI assistance systems
|
- [ ] AI assistance systems
|
||||||
- [ ] Thruster boost
|
- [X] Thruster boost
|
||||||
- [ ] G-force dampeners
|
- [ ] G-force dampeners
|
||||||
- [ ] Life support, material recyclers (air, water, etc)
|
- [ ] Life support, material recyclers (air, water, etc)
|
||||||
- [ ] High energy particle shield?
|
- [ ] High energy particle shield?
|
||||||
|
|
44
src/actor.rs
44
src/actor.rs
|
@ -21,10 +21,13 @@ use bevy_xpbd_3d::prelude::*;
|
||||||
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
|
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
|
||||||
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
|
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
|
||||||
const MAX_INTERACT_DISTANCE: f32 = 50.0;
|
const MAX_INTERACT_DISTANCE: f32 = 50.0;
|
||||||
const POWER_DRAIN_THRUSTER: [f32; 3] = [3e6, 3e6, 0.0];
|
pub const POWER_DRAIN_THRUSTER: [f32; 3] = [3e6, 3e6, 0.0];
|
||||||
const THRUSTER_BOOST_FACTOR: [f64; 3] = [3.0, 3.0, 0.0];
|
pub const THRUSTER_BOOST_FACTOR: [f64; 3] = [3.0, 3.0, 0.0];
|
||||||
const POWER_DRAIN_FLASHLIGHT: [f32; 3] = [200e3, 1500e3, 2500e3];
|
pub const POWER_DRAIN_FLASHLIGHT: [f32; 4] = [200e3, 1500e3, 2500e3, 10000e3];
|
||||||
pub const FLASHLIGHT_INTENSITY: [f32; 3] = [10e6, 400e6, 2e9]; // in lumens
|
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;
|
pub struct ActorPlugin;
|
||||||
impl Plugin for ActorPlugin {
|
impl Plugin for ActorPlugin {
|
||||||
|
@ -68,7 +71,7 @@ pub enum DamageType {
|
||||||
Asphyxiation,
|
Asphyxiation,
|
||||||
Depressurization,
|
Depressurization,
|
||||||
//Poison,
|
//Poison,
|
||||||
//Radiation,
|
Radiation,
|
||||||
//Freeze,
|
//Freeze,
|
||||||
//Burn,
|
//Burn,
|
||||||
}
|
}
|
||||||
|
@ -172,6 +175,7 @@ pub struct OrbitsJupiter;
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct LifeForm {
|
pub struct LifeForm {
|
||||||
pub is_alive: bool,
|
pub is_alive: bool,
|
||||||
|
pub is_radioactively_damaged: bool,
|
||||||
pub adrenaline: f32,
|
pub adrenaline: f32,
|
||||||
pub adrenaline_baseline: f32,
|
pub adrenaline_baseline: f32,
|
||||||
pub adrenaline_jolt: f32,
|
pub adrenaline_jolt: f32,
|
||||||
|
@ -180,6 +184,7 @@ impl Default for LifeForm {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
is_alive: true,
|
is_alive: true,
|
||||||
|
is_radioactively_damaged: false,
|
||||||
adrenaline: 0.3,
|
adrenaline: 0.3,
|
||||||
adrenaline_baseline: 0.3,
|
adrenaline_baseline: 0.3,
|
||||||
adrenaline_jolt: 0.0,
|
adrenaline_jolt: 0.0,
|
||||||
|
@ -257,7 +262,6 @@ const SUIT_SIMPLE: Suit = Suit {
|
||||||
pub struct Battery {
|
pub struct Battery {
|
||||||
pub power: f32, // Watt-seconds
|
pub power: f32, // Watt-seconds
|
||||||
pub capacity: f32, // Watt-seconds
|
pub capacity: f32, // Watt-seconds
|
||||||
pub reactor: f32, // Watt (production)
|
|
||||||
pub overloaded_recovering: bool,
|
pub overloaded_recovering: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +270,6 @@ impl Default for Battery {
|
||||||
Self {
|
Self {
|
||||||
power: 10e3 * 3600.0,
|
power: 10e3 * 3600.0,
|
||||||
capacity: 10e3 * 3600.0, // 10kWh
|
capacity: 10e3 * 3600.0, // 10kWh
|
||||||
reactor: 2000e3, // 2MW
|
|
||||||
overloaded_recovering: false,
|
overloaded_recovering: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,8 +298,8 @@ pub fn update_power(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if settings.hud_active {
|
if settings.hud_active {
|
||||||
let mut hud_drain = 300e3; // 300kW
|
let mut hud_drain = POWER_DRAIN_AR;
|
||||||
hud_drain += prefs.light_amp as f32 * 200e3; // 200kW per level
|
hud_drain += POWER_DRAIN_LIGHTAMP[prefs.light_amp];
|
||||||
battery.power -= hud_drain * d;
|
battery.power -= hud_drain * d;
|
||||||
if battery.power <= 0.0 {
|
if battery.power <= 0.0 {
|
||||||
power_down = true;
|
power_down = true;
|
||||||
|
@ -327,7 +330,8 @@ pub fn update_power(
|
||||||
if power_down {
|
if power_down {
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::PowerDown));
|
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 {
|
if battery.overloaded_recovering && battery.power > battery.capacity * 0.5 {
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::PowerUp));
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::PowerUp));
|
||||||
battery.overloaded_recovering = false;
|
battery.overloaded_recovering = false;
|
||||||
|
@ -337,10 +341,12 @@ pub fn update_power(
|
||||||
|
|
||||||
pub fn update_physics_lifeforms(
|
pub fn update_physics_lifeforms(
|
||||||
time: Res<Time>,
|
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();
|
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 {
|
if lifeform.adrenaline_jolt.abs() > 1e-3 {
|
||||||
lifeform.adrenaline_jolt *= 0.99;
|
lifeform.adrenaline_jolt *= 0.99;
|
||||||
} else {
|
} else {
|
||||||
|
@ -353,6 +359,20 @@ pub fn update_physics_lifeforms(
|
||||||
lifeform.adrenaline =
|
lifeform.adrenaline =
|
||||||
(lifeform.adrenaline - 0.0001 + lifeform.adrenaline_jolt * 0.01).clamp(0.0, 1.0);
|
(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 mut oxygen_drain = nature::OXY_S;
|
||||||
let integr_threshold = 0.5;
|
let integr_threshold = 0.5;
|
||||||
if suit.integrity < integr_threshold {
|
if suit.integrity < integr_threshold {
|
||||||
|
|
|
@ -331,7 +331,14 @@ fn handle_player_death(
|
||||||
duration: 1.0,
|
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();
|
settings.death_cause = "Unknown".to_string();
|
||||||
ew_effect.send(visual::SpawnEffectEvent {
|
ew_effect.send(visual::SpawnEffectEvent {
|
||||||
class: visual::Effects::FadeIn(css::MAROON.into()),
|
class: visual::Effects::FadeIn(css::MAROON.into()),
|
||||||
|
|
13
src/hud.rs
13
src/hud.rs
|
@ -682,8 +682,7 @@ pub fn setup(
|
||||||
fn update_dashboard(
|
fn update_dashboard(
|
||||||
timer: ResMut<FPSUpdateTimer>,
|
timer: ResMut<FPSUpdateTimer>,
|
||||||
mut q_dashboard: Query<(&mut Visibility, &Dashboard)>,
|
mut q_dashboard: Query<(&mut Visibility, &Dashboard)>,
|
||||||
id2pos: Res<game::Id2Pos>,
|
q_player: Query<(&actor::Suit, &actor::Battery, &actor::LifeForm), With<actor::Player>>,
|
||||||
q_player: Query<(&actor::Suit, &actor::Battery, &Position), With<actor::Player>>,
|
|
||||||
settings: Res<Settings>,
|
settings: Res<Settings>,
|
||||||
) {
|
) {
|
||||||
if !settings.hud_active || !timer.0.just_finished() {
|
if !settings.hud_active || !timer.0.just_finished() {
|
||||||
|
@ -693,7 +692,7 @@ fn update_dashboard(
|
||||||
if player.is_err() {
|
if player.is_err() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (suit, battery, pos) = player.unwrap();
|
let (suit, battery, lifeform) = player.unwrap();
|
||||||
|
|
||||||
for (mut vis, icon) in &mut q_dashboard {
|
for (mut vis, icon) in &mut q_dashboard {
|
||||||
*vis = bool2vis(match icon {
|
*vis = bool2vis(match icon {
|
||||||
|
@ -702,13 +701,7 @@ fn update_dashboard(
|
||||||
Dashboard::Battery => battery.overloaded_recovering,
|
Dashboard::Battery => battery.overloaded_recovering,
|
||||||
Dashboard::RotationStabiliser => !settings.rotation_stabilizer_active,
|
Dashboard::RotationStabiliser => !settings.rotation_stabilizer_active,
|
||||||
Dashboard::CruiseControl => settings.cruise_control_active,
|
Dashboard::CruiseControl => settings.cruise_control_active,
|
||||||
Dashboard::Radioactivity => {
|
Dashboard::Radioactivity => lifeform.is_radioactively_damaged,
|
||||||
if let Some(pos_jupiter) = id2pos.0.get(cmd::ID_JUPITER) {
|
|
||||||
pos_jupiter.distance(pos.0) < 140_000_000.0
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
src/menu.rs
45
src/menu.rs
|
@ -69,6 +69,7 @@ pub const MENUDEF: &[(&str, MenuAction)] = &[
|
||||||
("", MenuAction::ModLightAmp),
|
("", MenuAction::ModLightAmp),
|
||||||
("", MenuAction::ModFlashlightPower),
|
("", MenuAction::ModFlashlightPower),
|
||||||
("", MenuAction::ModThrusterBoost),
|
("", MenuAction::ModThrusterBoost),
|
||||||
|
("", MenuAction::ModReactor),
|
||||||
("", MenuAction::ToggleSound),
|
("", MenuAction::ToggleSound),
|
||||||
("", MenuAction::ToggleMusic),
|
("", MenuAction::ToggleMusic),
|
||||||
("", MenuAction::ToggleCamera),
|
("", MenuAction::ToggleCamera),
|
||||||
|
@ -86,6 +87,7 @@ pub enum MenuAction {
|
||||||
ModLightAmp,
|
ModLightAmp,
|
||||||
ModFlashlightPower,
|
ModFlashlightPower,
|
||||||
ModThrusterBoost,
|
ModThrusterBoost,
|
||||||
|
ModReactor,
|
||||||
ToggleSound,
|
ToggleSound,
|
||||||
ToggleMusic,
|
ToggleMusic,
|
||||||
ToggleCamera,
|
ToggleCamera,
|
||||||
|
@ -486,24 +488,39 @@ pub fn update_menu(
|
||||||
}
|
}
|
||||||
MenuAction::ToggleAR => {
|
MenuAction::ToggleAR => {
|
||||||
let onoff = bool2string(settings.hud_active);
|
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 => {
|
MenuAction::ModLightAmp => {
|
||||||
let n = prefs.light_amp;
|
let p = actor::POWER_DRAIN_LIGHTAMP[prefs.light_amp] / 1e3;
|
||||||
text.sections[i].value = format!("Light Amplification: {n}/3\n");
|
text.sections[i].value = format!("Light Amplification: {p}kW\n");
|
||||||
}
|
}
|
||||||
MenuAction::ModFlashlightPower => {
|
MenuAction::ModFlashlightPower => {
|
||||||
let n = prefs.flashlight_power + 1;
|
let p = actor::POWER_DRAIN_FLASHLIGHT[prefs.flashlight_power] / 1e3;
|
||||||
text.sections[i].value = format!("Flashlight Power: {n}/3\n");
|
text.sections[i].value = format!("Flashlight Power: {p}kW\n");
|
||||||
}
|
}
|
||||||
MenuAction::ModThrusterBoost => {
|
MenuAction::ModThrusterBoost => {
|
||||||
let state = match prefs.thruster_boost {
|
let state = match prefs.thruster_boost {
|
||||||
0 => "Only when slowing down",
|
0 => "For braking",
|
||||||
1 => "MAX POWER",
|
1 => "Always",
|
||||||
2 => "Off",
|
2 => "Off",
|
||||||
_ => "ERROR",
|
_ => "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 => {
|
MenuAction::ChangeARAvatar => {
|
||||||
if let Some(ava) = hud::PLAYER_AR_AVATARS.get(settings.ar_avatar) {
|
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 {
|
if prefs.light_amp > 3 {
|
||||||
prefs.light_amp = 0;
|
prefs.light_amp = 0;
|
||||||
}
|
}
|
||||||
|
prefs.save();
|
||||||
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
}
|
}
|
||||||
MenuAction::ModFlashlightPower => {
|
MenuAction::ModFlashlightPower => {
|
||||||
prefs.flashlight_power += 1;
|
prefs.flashlight_power += 1;
|
||||||
if prefs.flashlight_power > 2 {
|
if prefs.flashlight_power > 3 {
|
||||||
prefs.flashlight_power = 0;
|
prefs.flashlight_power = 0;
|
||||||
}
|
}
|
||||||
|
prefs.save();
|
||||||
ew_game.send(GameEvent::UpdateFlashlight);
|
ew_game.send(GameEvent::UpdateFlashlight);
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
}
|
}
|
||||||
|
@ -624,6 +643,14 @@ pub fn handle_input(
|
||||||
if prefs.thruster_boost > 2 {
|
if prefs.thruster_boost > 2 {
|
||||||
prefs.thruster_boost = 0;
|
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);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
}
|
}
|
||||||
MenuAction::ToggleMusic => {
|
MenuAction::ToggleMusic => {
|
||||||
|
|
|
@ -86,6 +86,7 @@ pub struct Settings {
|
||||||
pub chat_speed: f32,
|
pub chat_speed: f32,
|
||||||
pub ar_avatar: usize,
|
pub ar_avatar: usize,
|
||||||
pub flashlight_active: bool,
|
pub flashlight_active: bool,
|
||||||
|
pub reactor_state: usize,
|
||||||
pub hud_active: bool,
|
pub hud_active: bool,
|
||||||
pub map_active: bool,
|
pub map_active: bool,
|
||||||
pub deathscreen_active: bool,
|
pub deathscreen_active: bool,
|
||||||
|
@ -222,6 +223,7 @@ impl Default for Settings {
|
||||||
chat_speed: DEFAULT_CHAT_SPEED,
|
chat_speed: DEFAULT_CHAT_SPEED,
|
||||||
ar_avatar: 0,
|
ar_avatar: 0,
|
||||||
flashlight_active: false,
|
flashlight_active: false,
|
||||||
|
reactor_state: 1,
|
||||||
hud_active: true,
|
hud_active: true,
|
||||||
map_active: false,
|
map_active: false,
|
||||||
deathscreen_active: false,
|
deathscreen_active: false,
|
||||||
|
@ -575,7 +577,7 @@ pub fn load_prefs() -> Preferences {
|
||||||
println!("Loaded preferences from internal defaults");
|
println!("Loaded preferences from internal defaults");
|
||||||
}
|
}
|
||||||
prefs.source_file = path;
|
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.light_amp = prefs.light_amp.clamp(0, 3);
|
||||||
prefs.thruster_boost = prefs.thruster_boost.clamp(0, 2);
|
prefs.thruster_boost = prefs.thruster_boost.clamp(0, 2);
|
||||||
prefs
|
prefs
|
||||||
|
|
Loading…
Reference in a new issue