Skip to content
Snippets Groups Projects
music_box.rs 3.53 KiB
Newer Older
use std::marker::PhantomData;

use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use bevy_kira_audio::{AudioChannel, InstanceHandle};

use crate::utilities::{AudioSettings, CrossFadeTrack, SuppliesAudio, TrackType};
use crate::{
	AmbianceAudioChannelA, AmbianceAudioChannelB, AudioCrossFade, MusicAudioChannelA,
	MusicAudioChannelB, SfxAudioChannel, UiSfxAudioChannel,
};

#[derive(SystemParam)]
pub struct AudioChannels<'w, 's> {
	pub music_channel_a: Res<'w, AudioChannel<MusicAudioChannelA>>,
	pub music_channel_b: Res<'w, AudioChannel<MusicAudioChannelB>>,
	pub ambiance_channel_a: Res<'w, AudioChannel<AmbianceAudioChannelA>>,
	pub ambiance_channel_b: Res<'w, AudioChannel<AmbianceAudioChannelB>>,
	pub sfx_channel: Res<'w, AudioChannel<SfxAudioChannel>>,
	pub ui_sfx_channel: Res<'w, AudioChannel<UiSfxAudioChannel>>,

	#[system_param(ignore)]
	_p: PhantomData<&'s ()>,
}

#[derive(SystemParam)]
pub struct MusicBox<'w, 's, T: SuppliesAudio> {
	pub commands: Commands<'w, 's>,
	pub channels: AudioChannels<'w, 's>,
	pub handles: Res<'w, T>,
	pub settings: Res<'w, AudioSettings>,
	pub fade_state: Res<'w, AudioCrossFade>,
}

pub enum MusicTrackState {
	Pending,
	Playing,
	FadeOut { progress: f32 },
	FadeIn { progress: f32 },
	CrossFade { out_progress: f32, in_progress: f32 },
}

impl Default for MusicTrackState {
	fn default() -> Self {
		Self::Pending
	}
}

impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> {
	pub fn play_looped_music<Name: ToString>(&self, name: Name) -> Option<InstanceHandle> {
		self.channels.music_channel_a.stop();
		self.channels.music_channel_b.stop();

		match self.resolve_track_name(name) {
			TrackType::Single(track) => match self.fade_state.music.active {
				CrossFadeTrack::A => Some(
					self.channels
						.music_channel_a
						.play_looped(track.clone_weak()),
				),
				CrossFadeTrack::B => Some(
					self.channels
						.music_channel_b
						.play_looped(track.clone_weak()),
				),
			},
			TrackType::WithIntro(intro, looped) => match self.fade_state.music.active {
				CrossFadeTrack::A => Some(
					self.channels
						.music_channel_a
						.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
				),
				CrossFadeTrack::B => Some(
					self.channels
						.music_channel_b
						.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
				),
			},
			TrackType::Missing => None,
		}
	}

	pub fn play_effect_once<Name: ToString>(&mut self, name: Name) -> Option<InstanceHandle> {
		let name = name.to_string();

		match self.handles.get_audio_track(&name) {
			Some(track) => Some(self.channels.sfx_channel.play(track)),
			None => None,
		}
	}

	pub fn play_ambiance<Name: ToString>(&self, name: Name) -> Option<InstanceHandle> {
		self.channels.ambiance_channel_a.stop();
		self.channels.ambiance_channel_b.stop();

		match self.resolve_track_name(name) {
			TrackType::Single(track) => match self.fade_state.ambiance.active {
				CrossFadeTrack::A => Some(
					self.channels
						.ambiance_channel_a
						.play_looped(track.clone_weak()),
				),
				CrossFadeTrack::B => Some(
					self.channels
						.ambiance_channel_b
						.play_looped(track.clone_weak()),
				),
			},
			TrackType::WithIntro(intro, looped) => match self.fade_state.ambiance.active {
				CrossFadeTrack::A => Some(
					self.channels
						.ambiance_channel_a
						.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
				),
				CrossFadeTrack::B => Some(
					self.channels
						.ambiance_channel_b
						.play_looped_with_intro(intro.clone_weak(), looped.clone_weak()),
				),
			},
			TrackType::Missing => None,
		}
	}
}