use crate::prelude::Engine;
use bevy::{audio::AudioSink, prelude::*};
use std::{array::IntoIter, fmt::Debug};
#[derive(Default)]
#[doc(hidden)]
pub struct AudioManagerPlugin;
impl Plugin for AudioManagerPlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_system(queue_managed_audio_system);
}
}
#[derive(Default)]
pub struct AudioManager {
sfx_queue: Vec<(String, f32)>,
music_queue: Vec<Option<(String, f32)>>,
playing: Option<Handle<AudioSink>>,
music_playing: bool,
}
impl Debug for AudioManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AudioManager")
.field("sfx_queue", &self.sfx_queue)
.field("music_queue", &self.music_queue)
.field("music_playing", &self.music_playing)
.finish()
}
}
impl AudioManager {
pub fn play_sfx<S: Into<String>>(&mut self, sfx: S, volume: f32) {
self.sfx_queue.push((sfx.into(), volume.clamp(0.0, 1.0)));
}
pub fn play_music<S: Into<String>>(&mut self, music: S, volume: f32) {
self.music_playing = true;
self.music_queue
.push(Some((music.into(), volume.clamp(0.0, 1.0))));
}
pub fn stop_music(&mut self) {
if self.music_playing {
self.music_playing = false;
self.music_queue.push(None);
}
}
pub fn music_playing(&self) -> bool {
self.music_playing
}
}
#[derive(Copy, Clone, Debug)]
pub enum SfxPreset {
Click,
Confirmation1,
Confirmation2,
Congratulations,
Forcefield1,
Forcefield2,
Impact1,
Impact2,
Impact3,
Jingle1,
Jingle2,
Jingle3,
Minimize1,
Minimize2,
Switch1,
Switch2,
Tones1,
Tones2,
}
impl SfxPreset {
pub fn variant_iter() -> IntoIter<SfxPreset, 18> {
static SFX_PRESETS: [SfxPreset; 18] = [
SfxPreset::Click,
SfxPreset::Confirmation1,
SfxPreset::Confirmation2,
SfxPreset::Congratulations,
SfxPreset::Forcefield1,
SfxPreset::Forcefield2,
SfxPreset::Impact1,
SfxPreset::Impact2,
SfxPreset::Impact3,
SfxPreset::Jingle1,
SfxPreset::Jingle2,
SfxPreset::Jingle3,
SfxPreset::Minimize1,
SfxPreset::Minimize2,
SfxPreset::Switch1,
SfxPreset::Switch2,
SfxPreset::Tones1,
SfxPreset::Tones2,
];
SFX_PRESETS.into_iter()
}
}
impl From<SfxPreset> for String {
fn from(sfx_preset: SfxPreset) -> Self {
match sfx_preset {
SfxPreset::Click => "sfx/click.ogg".into(),
SfxPreset::Confirmation1 => "sfx/confirmation1.ogg".into(),
SfxPreset::Confirmation2 => "sfx/confirmation2.ogg".into(),
SfxPreset::Congratulations => "sfx/congratulations.ogg".into(),
SfxPreset::Forcefield1 => "sfx/forcefield1.ogg".into(),
SfxPreset::Forcefield2 => "sfx/forcefield2.ogg".into(),
SfxPreset::Impact1 => "sfx/impact1.ogg".into(),
SfxPreset::Impact2 => "sfx/impact2.ogg".into(),
SfxPreset::Impact3 => "sfx/impact3.ogg".into(),
SfxPreset::Jingle1 => "sfx/jingle1.ogg".into(),
SfxPreset::Jingle2 => "sfx/jingle2.ogg".into(),
SfxPreset::Jingle3 => "sfx/jingle3.ogg".into(),
SfxPreset::Minimize1 => "sfx/minimize1.ogg".into(),
SfxPreset::Minimize2 => "sfx/minimize2.ogg".into(),
SfxPreset::Switch1 => "sfx/switch1.ogg".into(),
SfxPreset::Switch2 => "sfx/switch2.ogg".into(),
SfxPreset::Tones1 => "sfx/tones1.ogg".into(),
SfxPreset::Tones2 => "sfx/tones2.ogg".into(),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum MusicPreset {
Classy8Bit,
MysteriousMagic,
WhimsicalPopsicle,
}
impl MusicPreset {
pub fn variant_iter() -> IntoIter<MusicPreset, 3> {
static MUSIC_PRESETS: [MusicPreset; 3] = [
MusicPreset::Classy8Bit,
MusicPreset::MysteriousMagic,
MusicPreset::WhimsicalPopsicle,
];
MUSIC_PRESETS.into_iter()
}
}
impl From<MusicPreset> for String {
fn from(music_preset: MusicPreset) -> String {
match music_preset {
MusicPreset::Classy8Bit => "music/Classy 8-Bit.ogg".into(),
MusicPreset::MysteriousMagic => "music/Mysterious Magic.ogg".into(),
MusicPreset::WhimsicalPopsicle => "music/Whimsical Popsicle.ogg".into(),
}
}
}
#[doc(hidden)]
pub fn queue_managed_audio_system(
asset_server: Res<AssetServer>,
audio: Res<Audio>,
audio_sinks: Res<Assets<AudioSink>>,
mut game_state: ResMut<Engine>,
) {
for (sfx, volume) in game_state.audio_manager.sfx_queue.drain(..) {
let sfx_path = format!("audio/{}", sfx);
let sfx_handle = asset_server.load(sfx_path.as_str());
audio.play_with_settings(
sfx_handle,
PlaybackSettings {
volume,
..Default::default()
},
);
}
#[allow(clippy::for_loops_over_fallibles)]
for item in game_state.audio_manager.music_queue.drain(..).last() {
if let Some(sink_handle) = &game_state.audio_manager.playing {
if let Some(sink) = audio_sinks.get(sink_handle) {
sink.stop();
}
}
if let Some((music, volume)) = item {
let music_path = format!("audio/{}", music);
let music_handle = asset_server.load(music_path.as_str());
let sink_handle = audio_sinks.get_handle(audio.play_with_settings(
music_handle,
PlaybackSettings {
repeat: true,
volume,
..Default::default()
},
));
game_state.audio_manager.playing = Some(sink_handle);
}
}
}