outfly/src/audio.rs

308 lines
9.2 KiB
Rust
Raw Normal View History

2024-04-21 16:23:40 +00:00
// ▄████████▄ + ███ + ▄█████████ ███ +
// ███▀ ▀███ + + ███ ███▀ + ███ + +
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
2024-04-21 17:34:00 +00:00
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
2024-04-21 16:23:40 +00:00
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
// + + + ███
// + ▀████████████████████████████████████████████████████▀
2024-04-23 15:33:36 +00:00
//
// This module manages sound effects and music.
2024-04-21 16:23:40 +00:00
2024-05-12 21:42:56 +00:00
use crate::prelude::*;
2024-05-22 02:57:20 +00:00
use bevy::audio::{PlaybackMode, Volume};
use bevy::prelude::*;
2024-05-13 02:20:18 +00:00
use std::collections::HashMap;
2024-03-18 01:15:44 +00:00
2024-03-17 23:04:23 +00:00
pub struct AudioPlugin;
impl Plugin for AudioPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup);
2024-05-22 02:57:20 +00:00
app.add_systems(
Update,
(
play_zoom_sfx,
2024-06-07 21:55:04 +00:00
play_gasp_sfx,
respawn_sinks.run_if(on_event::<RespawnSinksEvent>()),
pause_all.run_if(on_event::<PauseAllSfxEvent>()),
),
);
app.add_systems(
PostUpdate,
(
play_sfx,
toggle_music.run_if(on_event::<ToggleMusicEvent>()),
),
2024-05-22 02:57:20 +00:00
);
app.add_event::<PlaySfxEvent>();
2024-05-13 02:33:03 +00:00
app.add_event::<PauseAllSfxEvent>();
app.add_event::<ToggleMusicEvent>();
2024-05-13 02:41:17 +00:00
app.add_event::<RespawnSinksEvent>();
2024-05-22 02:57:20 +00:00
app.insert_resource(ZoomTimer(Timer::from_seconds(0.09, TimerMode::Repeating)));
2024-03-17 23:04:23 +00:00
}
}
2024-05-22 02:57:20 +00:00
#[derive(Resource)]
pub struct ZoomTimer(Timer);
2024-04-25 02:15:57 +00:00
2024-05-13 02:20:18 +00:00
const PATHS: &[(SfxType, Sfx, &str)] = &[
2024-05-22 02:57:20 +00:00
(
SfxType::BGM,
Sfx::BGM,
"music/Aleksey Chistilin - Cinematic Cello.ogg",
),
(
SfxType::BGMNoAR,
Sfx::BGMActualJupiterRecording,
"music/JupiterRecording.ogg",
),
2024-05-22 02:57:20 +00:00
(
SfxType::LoopSfx,
Sfx::ElectricMotor,
"sounds/electricmotor.ogg",
),
2024-05-13 02:20:18 +00:00
(SfxType::LoopSfx, Sfx::Ion, "sounds/ion.ogg"),
(SfxType::LoopSfx, Sfx::Rocket, "sounds/rocket.ogg"),
(SfxType::LoopSfx, Sfx::Thruster, "sounds/thruster.ogg"),
2024-06-07 21:55:04 +00:00
(SfxType::LoopSfx, Sfx::Gasp, "sounds/gasp.ogg"),
(SfxType::OneOff, Sfx::GaspRelief, "sounds/gasprelief.ogg"),
2024-05-14 04:37:00 +00:00
(SfxType::OneOff, Sfx::Achieve, "sounds/achieve.ogg"),
2024-05-22 02:57:20 +00:00
(
SfxType::OneOff,
Sfx::Click,
"sounds/click-button-140881-crop.ogg",
),
2024-05-13 02:20:18 +00:00
(SfxType::OneOff, Sfx::Connect, "sounds/connect.ogg"),
(SfxType::OneOff, Sfx::Crash, "sounds/crash.ogg"),
(SfxType::OneOff, Sfx::EnterVehicle, "sounds/bikestart.ogg"),
2024-05-22 02:57:20 +00:00
(
SfxType::OneOff,
Sfx::IncomingChatMessage,
"sounds/connect.ogg",
),
2024-05-13 02:20:18 +00:00
(SfxType::OneOff, Sfx::Ping, "sounds/connect.ogg"),
2024-05-22 02:57:20 +00:00
(
SfxType::OneOff,
Sfx::Switch,
"sounds/typosonic-typing-192811-crop.ogg",
),
2024-05-13 02:20:18 +00:00
(SfxType::OneOff, Sfx::WakeUp, "sounds/wakeup.ogg"),
(SfxType::OneOff, Sfx::Woosh, "sounds/woosh.ogg"),
(SfxType::OneOff, Sfx::Zoom, "sounds/zoom.ogg"),
];
#[derive(Component, PartialEq, Hash, Eq, Copy, Clone)]
pub enum Sfx {
2024-05-14 04:37:00 +00:00
Achieve,
2024-05-13 02:20:18 +00:00
BGM,
BGMActualJupiterRecording,
Click,
2024-03-19 05:24:27 +00:00
Connect,
2024-03-29 15:58:42 +00:00
Crash,
2024-05-13 02:20:18 +00:00
ElectricMotor,
EnterVehicle,
2024-06-07 21:55:04 +00:00
Gasp,
GaspRelief,
2024-05-13 02:20:18 +00:00
IncomingChatMessage,
Ion,
Ping,
Rocket,
Switch,
Thruster,
WakeUp,
2024-05-13 02:20:18 +00:00
Woosh,
Zoom,
}
pub fn str2sfx(sfx_label: &str) -> Sfx {
return match sfx_label {
2024-05-14 04:37:00 +00:00
"achieve" => Sfx::Achieve,
2024-05-13 02:20:18 +00:00
"switch" => Sfx::Switch,
"click" => Sfx::Click,
"woosh" => Sfx::Woosh,
"zoom" => Sfx::Zoom,
"chat" => Sfx::IncomingChatMessage,
"ping" => Sfx::Ping,
"connect" => Sfx::Connect,
"entervehicle" => Sfx::EnterVehicle,
"crash" => Sfx::Ping,
_ => Sfx::Click,
};
}
pub enum SfxType {
BGM,
BGMNoAR,
2024-05-13 02:20:18 +00:00
LoopSfx,
OneOff,
}
2024-03-18 00:52:41 +00:00
2024-05-22 02:57:20 +00:00
#[derive(Event)]
pub struct PlaySfxEvent(pub Sfx);
#[derive(Event)]
pub struct PauseAllSfxEvent;
#[derive(Event)]
pub struct RespawnSinksEvent;
#[derive(Event)]
pub struct ToggleMusicEvent();
#[derive(Resource)]
pub struct Sounds(HashMap<Sfx, Handle<AudioSource>>);
2024-03-18 01:15:44 +00:00
2024-03-16 19:53:57 +00:00
pub fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
2024-05-13 02:41:17 +00:00
mut ew_respawnsinks: EventWriter<RespawnSinksEvent>,
2024-03-16 19:53:57 +00:00
) {
2024-05-13 02:20:18 +00:00
let mut map = HashMap::new();
2024-05-13 02:41:17 +00:00
for (_, sfx, path) in PATHS {
2024-05-13 02:20:18 +00:00
let source = asset_server.load(*path);
map.insert(*sfx, source.clone());
2024-05-13 02:41:17 +00:00
}
commands.insert_resource(Sounds(map));
ew_respawnsinks.send(RespawnSinksEvent);
}
pub fn respawn_sinks(
mut commands: Commands,
asset_server: Res<AssetServer>,
settings: Res<var::Settings>,
q_audiosinks: Query<Entity, (With<AudioSink>, With<Sfx>)>,
) {
for sink in &q_audiosinks {
commands.entity(sink).despawn();
}
for (sfxtype, sfx, path) in PATHS {
let source = asset_server.load(*path);
2024-05-13 02:20:18 +00:00
match sfxtype {
SfxType::BGM => {
commands.spawn((
*sfx,
AudioBundle {
source,
settings: PlaybackSettings {
mode: PlaybackMode::Loop,
paused: settings.mute_music || !settings.hud_active,
..default()
},
},
));
}
SfxType::BGMNoAR => {
commands.spawn((
*sfx,
AudioBundle {
source,
settings: PlaybackSettings {
mode: PlaybackMode::Loop,
paused: settings.mute_music || settings.hud_active,
2024-05-13 02:20:18 +00:00
..default()
},
},
));
2024-05-22 02:57:20 +00:00
}
2024-05-13 02:20:18 +00:00
SfxType::LoopSfx => {
commands.spawn((
*sfx,
AudioBundle {
source,
settings: PlaybackSettings {
mode: PlaybackMode::Loop,
volume: Volume::new(0.0),
paused: true,
..default()
},
},
));
2024-05-22 02:57:20 +00:00
}
SfxType::OneOff => (),
2024-05-13 02:20:18 +00:00
}
}
2024-03-16 19:53:57 +00:00
}
2024-03-18 01:15:44 +00:00
pub fn play_sfx(
2024-03-18 00:52:41 +00:00
mut commands: Commands,
settings: Res<var::Settings>,
mut events_sfx: EventReader<PlaySfxEvent>,
2024-05-13 02:20:18 +00:00
sounds: Res<Sounds>,
2024-03-18 00:52:41 +00:00
) {
if settings.mute_sfx && !events_sfx.is_empty() {
events_sfx.clear();
2024-03-18 01:15:44 +00:00
}
for sfx in events_sfx.read() {
2024-05-13 02:20:18 +00:00
if let Some(source) = sounds.0.get(&sfx.0) {
commands.spawn(AudioBundle {
source: source.clone(),
settings: PlaybackSettings::DESPAWN,
});
}
2024-03-18 00:52:41 +00:00
}
}
pub fn toggle_music(q_audiosinks: Query<(&AudioSink, &Sfx)>, settings: Res<var::Settings>) {
for (bgm_sink, sfx) in &q_audiosinks {
let play = match *sfx {
Sfx::BGM => settings.hud_active,
Sfx::BGMActualJupiterRecording => !settings.hud_active,
_ => {
2024-05-13 02:20:18 +00:00
continue;
}
};
if settings.mute_music || !play {
bgm_sink.pause();
} else {
bgm_sink.play();
}
}
}
2024-04-25 02:15:57 +00:00
pub fn play_zoom_sfx(
time: Res<Time>,
mut timer: ResMut<ZoomTimer>,
mut last_zoom_level: Local<f64>,
mapcam: Res<camera::MapCam>,
mut ew_sfx: EventWriter<PlaySfxEvent>,
settings: Res<var::Settings>,
) {
if !timer.0.tick(time.delta()).just_finished() {
return;
}
2024-05-22 02:57:20 +00:00
if settings.map_active
&& (*last_zoom_level - mapcam.target_zoom_level).abs() > mapcam.target_zoom_level * 0.2
{
2024-04-25 02:15:57 +00:00
if *last_zoom_level != 0.0 && mapcam.target_zoom_level != camera::INITIAL_ZOOM_LEVEL {
ew_sfx.send(PlaySfxEvent(Sfx::Zoom));
}
*last_zoom_level = mapcam.target_zoom_level;
}
}
2024-05-13 02:33:03 +00:00
2024-06-07 21:55:04 +00:00
pub fn play_gasp_sfx(
player: Query<&actor::Suit, With<actor::Player>>,
mut ew_sfx: EventWriter<PlaySfxEvent>,
q_audiosinks: Query<(&audio::Sfx, &AudioSink)>,
) {
if let Ok(suit) = player.get_single() {
for (sfxtype, sink) in &q_audiosinks {
if *sfxtype != Sfx::Gasp {
continue;
}
if suit.oxygen <= 0.0 {
sink.set_volume(0.6);
sink.play();
} else {
if !sink.is_paused() {
ew_sfx.send(PlaySfxEvent(Sfx::GaspRelief));
sink.pause();
}
}
}
}
}
2024-05-22 02:57:20 +00:00
pub fn pause_all(q_audiosinks: Query<&AudioSink, With<Sfx>>) {
2024-05-13 02:33:03 +00:00
for sink in &q_audiosinks {
sink.pause();
}
}