use crate::prelude::Engine;
use bevy::{
audio::{AudioSink, PlaybackMode, Volume},
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_systems(Update, queue_managed_audio_system);
}
}
#[derive(Default)]
pub struct AudioManager {
sfx_queue: Vec<(String, f32)>,
music_queue: Vec<Option<(String, f32)>>,
playing: Option<Entity>,
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(),
}
}
}
#[derive(Component)]
pub struct Music;
#[doc(hidden)]
pub fn queue_managed_audio_system(
mut commands: Commands,
music_query: Query<(Entity, &AudioSink), With<Music>>,
asset_server: Res<AssetServer>,
mut game_state: ResMut<Engine>,
) {
for (sfx, volume) in game_state.audio_manager.sfx_queue.drain(..) {
commands.spawn((
AudioPlayer::<AudioSource>(asset_server.load(format!("audio/{}", sfx))),
PlaybackSettings {
mode: PlaybackMode::Despawn,
volume: Volume::Linear(volume),
..Default::default()
},
));
}
let last_music_item = game_state.audio_manager.music_queue.pop();
game_state.audio_manager.music_queue.clear();
if let Some(item) = last_music_item {
if let Ok((entity, music)) = music_query.single() {
music.stop();
commands.entity(entity).despawn();
}
if let Some((music, volume)) = item {
let entity = commands
.spawn((
AudioPlayer::<AudioSource>(asset_server.load(format!("audio/{}", music))),
PlaybackSettings {
mode: PlaybackMode::Loop,
volume: Volume::Linear(volume),
..Default::default()
},
Music,
))
.id();
game_state.audio_manager.playing = Some(entity);
}
}
}