implement damage on low oxygen and collisions

This commit is contained in:
yuni 2024-04-05 03:31:52 +02:00
parent ae2fcf2525
commit 61c7cffcef
5 changed files with 78 additions and 17 deletions

View file

@ -22,6 +22,7 @@ impl Plugin for ActorPlugin {
app.add_systems(Update, ( app.add_systems(Update, (
handle_input, handle_input,
handle_collisions, handle_collisions,
handle_damage,
)); ));
app.add_systems(PostUpdate, ( app.add_systems(PostUpdate, (
handle_vehicle_enter_exit, handle_vehicle_enter_exit,
@ -43,17 +44,14 @@ pub struct VehicleEnterExitEvent {
#[derive(Component)] #[derive(Component)]
pub struct Actor { pub struct Actor {
pub id: String, pub id: String,
pub hp: f32,
pub m: f32, // mass pub m: f32, // mass
pub inside_entity: u32, pub inside_entity: u32,
pub camdistance: f32, pub camdistance: f32,
} }
impl Default for Actor { impl Default for Actor {
fn default() -> Self { fn default() -> Self {
Self { Self {
id: "".to_string(), id: "".to_string(),
hp: 100.0,
m: 100.0, m: 100.0,
inside_entity: NO_RIDE, inside_entity: NO_RIDE,
camdistance: 15.0, camdistance: 15.0,
@ -61,6 +59,22 @@ impl Default for Actor {
} }
} }
#[derive(Component)]
pub struct HitPoints {
pub current: f32,
pub max: f32,
pub damage: f32,
}
impl Default for HitPoints {
fn default() -> Self {
Self {
current: 100.0,
max: 100.0,
damage: 0.0,
}
}
}
#[derive(Component)] pub struct Player; // Attached to the suit of the player #[derive(Component)] pub struct Player; // Attached to the suit of the player
#[derive(Component)] pub struct PlayerDrivesThis; // Attached to the entered vehicle #[derive(Component)] pub struct PlayerDrivesThis; // Attached to the entered vehicle
#[derive(Component)] pub struct PlayerCamera; // Attached to the actor to use as point of view #[derive(Component)] pub struct PlayerCamera; // Attached to the actor to use as point of view
@ -144,10 +158,10 @@ const SUIT_SIMPLE: Suit = Suit {
pub fn update_physics_lifeforms( pub fn update_physics_lifeforms(
time: Res<Time>, time: Res<Time>,
mut query: Query<(&mut LifeForm, &mut Suit, &LinearVelocity)>, mut query: Query<(&mut LifeForm, &mut HitPoints, &mut Suit, &LinearVelocity)>,
) { ) {
let d = time.delta_seconds(); let d = time.delta_seconds();
for (mut lifeform, mut suit, velocity) in query.iter_mut() { for (mut lifeform, mut hp, mut suit, velocity) 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;
} }
@ -180,6 +194,9 @@ pub fn update_physics_lifeforms(
oxygen_drain += suit.oxygen * 0.01 * drain_scaling; oxygen_drain += suit.oxygen * 0.01 * drain_scaling;
} }
suit.oxygen = (suit.oxygen - oxygen_drain*d).clamp(0.0, suit.oxygen_max); suit.oxygen = (suit.oxygen - oxygen_drain*d).clamp(0.0, suit.oxygen_max);
if suit.oxygen <= 0.0 {
hp.damage += 1.0 * d;
}
} }
} }
@ -192,13 +209,9 @@ pub fn handle_input(
mut q_vehicles: Query<(Entity, &mut Visibility, &Transform), (With<actor::Vehicle>, Without<actor::Player>)>, mut q_vehicles: Query<(Entity, &mut Visibility, &Transform), (With<actor::Vehicle>, Without<actor::Player>)>,
mut ew_conv: EventWriter<chat::StartConversationEvent>, mut ew_conv: EventWriter<chat::StartConversationEvent>,
mut ew_vehicle: EventWriter<VehicleEnterExitEvent>, mut ew_vehicle: EventWriter<VehicleEnterExitEvent>,
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
q_player_drives: Query<Entity, With<PlayerDrivesThis>>, q_player_drives: Query<Entity, With<PlayerDrivesThis>>,
) { ) {
let mindist = MIN_INTERACT_DISTANCE * MIN_INTERACT_DISTANCE; let mindist = MIN_INTERACT_DISTANCE * MIN_INTERACT_DISTANCE;
if keyboard_input.just_pressed(settings.key_cheat_die) {
ew_playerdies.send(PlayerDiesEvent);
}
if keyboard_input.just_pressed(settings.key_interact) { if keyboard_input.just_pressed(settings.key_interact) {
// Talking to people // Talking to people
if let Ok((_player_entity, _player_actor, player)) = player.get_single() { if let Ok((_player_entity, _player_actor, player)) = player.get_single() {
@ -298,13 +311,15 @@ fn handle_collisions(
mut collision_event_reader: EventReader<CollisionStarted>, mut collision_event_reader: EventReader<CollisionStarted>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
q_player: Query<Entity, With<PlayerCamera>>, q_player: Query<Entity, With<PlayerCamera>>,
mut q_player_lifeform: Query<&mut LifeForm, With<Player>>, mut q_player_lifeform: Query<(&mut LifeForm, &mut Suit, &mut HitPoints), With<Player>>,
) { ) {
if let (Ok(player), Ok(mut lifeform)) = (q_player.get_single(), q_player_lifeform.get_single_mut()) { if let (Ok(player), Ok((mut lifeform, mut suit, mut hp))) = (q_player.get_single(), q_player_lifeform.get_single_mut()) {
for CollisionStarted(entity1, entity2) in collision_event_reader.read() { for CollisionStarted(entity1, entity2) in collision_event_reader.read() {
if *entity1 == player || *entity2 == player { if *entity1 == player || *entity2 == player {
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Crash)); ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Crash));
lifeform.adrenaline_jolt += 0.1; lifeform.adrenaline_jolt += 0.1;
suit.integrity -= 0.03;
hp.damage += 10.0;
} }
} }
} }
@ -374,7 +389,23 @@ fn handle_player_death(
scene_spawner.despawn_instance(**sceneinstance); scene_spawner.despawn_instance(**sceneinstance);
} }
//cmd.run_system(commands::load_defs); // why is it so complicated to get SystemId? //cmd.run_system(commands::load_defs); // why is it so complicated to get SystemId?
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
commands::load_defs(ew_spawn); commands::load_defs(ew_spawn);
return; return;
} }
} }
fn handle_damage(
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
mut q_hp: Query<(&mut HitPoints, Option<&Player>), Changed<HitPoints>>,
) {
for (mut hp, player_maybe) in &mut q_hp {
hp.current -= hp.damage;
hp.damage = 0.0;
if player_maybe.is_some() {
if hp.current <= 0.0 {
ew_playerdies.send(PlayerDiesEvent);
}
}
}
}

View file

@ -11,7 +11,7 @@ const ASSET_BGM: &str = "music/Aleksey Chistilin - Cinematic Cello.ogg";
const ASSET_THRUSTER: &str = "sounds/thruster.ogg"; const ASSET_THRUSTER: &str = "sounds/thruster.ogg";
const ASSET_ROCKET: &str = "sounds/rocket.ogg"; const ASSET_ROCKET: &str = "sounds/rocket.ogg";
const ASSET_ION: &str = "sounds/ion.ogg"; const ASSET_ION: &str = "sounds/ion.ogg";
//const ASSET_WAKEUP: &str = "sounds/wakeup.ogg"; const ASSET_WAKEUP: &str = "sounds/wakeup.ogg";
const ASSET_BIKESTART: &str = "sounds/bikestart.ogg"; const ASSET_BIKESTART: &str = "sounds/bikestart.ogg";
const ASSET_CRASH: &str = "sounds/crash.ogg"; const ASSET_CRASH: &str = "sounds/crash.ogg";
const ASSET_ELECTRICMOTOR: &str = "sounds/electricmotor.ogg"; const ASSET_ELECTRICMOTOR: &str = "sounds/electricmotor.ogg";
@ -36,6 +36,7 @@ pub enum Sfx {
Connect, Connect,
EnterVehicle, EnterVehicle,
Crash, Crash,
WakeUp,
None, None,
} }
@ -54,6 +55,7 @@ pub enum Sfx {
#[derive(Resource)] pub struct SoundConnect(Handle<AudioSource>); #[derive(Resource)] pub struct SoundConnect(Handle<AudioSource>);
#[derive(Resource)] pub struct SoundBikeStart(Handle<AudioSource>); #[derive(Resource)] pub struct SoundBikeStart(Handle<AudioSource>);
#[derive(Resource)] pub struct SoundCrash(Handle<AudioSource>); #[derive(Resource)] pub struct SoundCrash(Handle<AudioSource>);
#[derive(Resource)] pub struct SoundWakeUp(Handle<AudioSource>);
pub fn setup( pub fn setup(
mut commands: Commands, mut commands: Commands,
@ -131,6 +133,7 @@ pub fn setup(
commands.insert_resource(SoundConnect(asset_server.load(ASSET_CONNECT))); commands.insert_resource(SoundConnect(asset_server.load(ASSET_CONNECT)));
commands.insert_resource(SoundBikeStart(asset_server.load(ASSET_BIKESTART))); commands.insert_resource(SoundBikeStart(asset_server.load(ASSET_BIKESTART)));
commands.insert_resource(SoundCrash(asset_server.load(ASSET_CRASH))); commands.insert_resource(SoundCrash(asset_server.load(ASSET_CRASH)));
commands.insert_resource(SoundWakeUp(asset_server.load(ASSET_WAKEUP)));
} }
pub fn toggle_bgm( pub fn toggle_bgm(
@ -162,6 +165,7 @@ pub fn play_sfx(
sound_connect: Res<SoundConnect>, sound_connect: Res<SoundConnect>,
sound_bikestart: Res<SoundBikeStart>, sound_bikestart: Res<SoundBikeStart>,
sound_crash: Res<SoundCrash>, sound_crash: Res<SoundCrash>,
sound_wakeup: Res<SoundWakeUp>,
) { ) {
if settings.mute_sfx && !events_sfx.is_empty() { if settings.mute_sfx && !events_sfx.is_empty() {
events_sfx.clear(); events_sfx.clear();
@ -180,6 +184,7 @@ pub fn play_sfx(
Sfx::Connect => sound_connect.0.clone(), Sfx::Connect => sound_connect.0.clone(),
Sfx::EnterVehicle => sound_bikestart.0.clone(), Sfx::EnterVehicle => sound_bikestart.0.clone(),
Sfx::Crash => sound_crash.0.clone(), Sfx::Crash => sound_crash.0.clone(),
Sfx::WakeUp => sound_wakeup.0.clone(),
Sfx::None => sound_ping.0.clone(), Sfx::None => sound_ping.0.clone(),
}, },
settings: PlaybackSettings::DESPAWN, settings: PlaybackSettings::DESPAWN,

View file

@ -583,6 +583,7 @@ fn spawn_entities(
..default() ..default()
}); });
actor.insert(world::DespawnOnPlayerDeath); actor.insert(world::DespawnOnPlayerDeath);
actor.insert(actor::HitPoints::default());
actor.insert(Position::from(state.pos)); actor.insert(Position::from(state.pos));
actor.insert(Rotation::from(state.rotation)); actor.insert(Rotation::from(state.rotation));
if state.is_sphere { if state.is_sphere {

View file

@ -209,6 +209,24 @@ fn setup(
..default() ..default()
} }
), ),
TextSection::new(
"\nVitals ",
TextStyle {
font: asset_server.load(FONT),
font_size: settings.font_size_hud,
color: Color::GRAY,
..default()
},
),
TextSection::new(
"",
TextStyle {
font: asset_server.load(FONT),
font_size: settings.font_size_hud,
color: Color::GRAY,
..default()
}
),
TextSection::new( TextSection::new(
"\nProximity 警告 ", "\nProximity 警告 ",
TextStyle { TextStyle {
@ -376,7 +394,7 @@ fn update(
diagnostics: Res<DiagnosticsStore>, diagnostics: Res<DiagnosticsStore>,
time: Res<Time>, time: Res<Time>,
mut log: ResMut<Log>, mut log: ResMut<Log>,
player: Query<(&actor::Suit, &actor::LifeForm), With<actor::Player>>, player: Query<(&actor::HitPoints, &actor::Suit, &actor::LifeForm), With<actor::Player>>,
q_camera: Query<(&Position, &LinearVelocity), With<actor::PlayerCamera>>, q_camera: Query<(&Position, &LinearVelocity), With<actor::PlayerCamera>>,
mut timer: ResMut<FPSUpdateTimer>, mut timer: ResMut<FPSUpdateTimer>,
mut query: Query<&mut Text, With<GaugesText>>, mut query: Query<&mut Text, With<GaugesText>>,
@ -390,7 +408,7 @@ fn update(
let q_camera_result = q_camera.get_single(); let q_camera_result = q_camera.get_single();
let player = player.get_single(); let player = player.get_single();
if player.is_ok() && q_camera_result.is_ok() { if player.is_ok() && q_camera_result.is_ok() {
let (suit, lifeform) = player.unwrap(); let (hp, suit, lifeform) = player.unwrap();
let (pos, cam_v) = q_camera_result.unwrap(); let (pos, cam_v) = q_camera_result.unwrap();
for mut text in &mut query { for mut text in &mut query {
if let Some(fps) = diagnostics.get(&FrameTimeDiagnosticsPlugin::FPS) { if let Some(fps) = diagnostics.get(&FrameTimeDiagnosticsPlugin::FPS) {
@ -414,15 +432,17 @@ fn update(
} }
let adrenaline = lifeform.adrenaline * 990.0 + 10.0; let adrenaline = lifeform.adrenaline * 990.0 + 10.0;
text.sections[9].value = format!("{adrenaline:.0}pg/mL"); text.sections[9].value = format!("{adrenaline:.0}pg/mL");
let vitals = 100.0 * hp.current / hp.max;
text.sections[11].value = format!("{vitals:.0}%");
let all_actors = query_all_actors.iter().len(); let all_actors = query_all_actors.iter().len();
text.sections[11].value = format!("{all_actors:.0}"); text.sections[13].value = format!("{all_actors:.0}");
let (x, y, z) = (pos.x / 1.0e3, pos.y / 1.0e3, pos.z / 1.0e3); let (x, y, z) = (pos.x / 1.0e3, pos.y / 1.0e3, pos.z / 1.0e3);
text.sections[5].value = format!("{x:.0}km / {z:.0}km / {y:.0}km"); text.sections[5].value = format!("{x:.0}km / {z:.0}km / {y:.0}km");
let integrity = suit.integrity * 100.0; let integrity = suit.integrity * 100.0;
text.sections[13].value = format!("{integrity:.0}%"); text.sections[15].value = format!("{integrity:.0}%");
let speed = cam_v.length(); let speed = cam_v.length();
let kmh = speed * 60.0 * 60.0 / 1000.0; let kmh = speed * 60.0 * 60.0 / 1000.0;
text.sections[15].value = format!("{speed:.0}m/s | {kmh:.0}km/h"); text.sections[17].value = format!("{speed:.0}m/s | {kmh:.0}km/h");
} }
} }

View file

@ -353,6 +353,7 @@ fn handle_cheats(
key_input: Res<ButtonInput<KeyCode>>, key_input: Res<ButtonInput<KeyCode>>,
mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>, mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
mut q_life: Query<(&mut actor::LifeForm, ), With<actor::Player>>, mut q_life: Query<(&mut actor::LifeForm, ), With<actor::Player>>,
mut ew_playerdies: EventWriter<actor::PlayerDiesEvent>,
settings: ResMut<settings::Settings>, settings: ResMut<settings::Settings>,
) { ) {
if q_player.is_empty() || q_life.is_empty() { if q_player.is_empty() || q_life.is_empty() {
@ -396,6 +397,9 @@ fn handle_cheats(
if key_input.pressed(settings.key_cheat_adrenaline_max) { if key_input.pressed(settings.key_cheat_adrenaline_max) {
lifeform.adrenaline = 1.0; lifeform.adrenaline = 1.0;
} }
if key_input.just_pressed(settings.key_cheat_die) {
ew_playerdies.send(actor::PlayerDiesEvent);
}
} }
// A variant of bevy_xpbd_3d::plugins::position_to_transform that adjusts // A variant of bevy_xpbd_3d::plugins::position_to_transform that adjusts