You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
5.3 KiB
Rust
178 lines
5.3 KiB
Rust
///
|
|
/// TODO: Custom Asset: FmodEventMapper
|
|
///
|
|
use crate::prelude::*;
|
|
use bevy_fmod::prelude::AudioSource;
|
|
use bevy_fmod::prelude::*;
|
|
use serde::Deserialize;
|
|
use bevy::{
|
|
asset::{AssetLoader, LoadContext, LoadedAsset},
|
|
prelude::*,
|
|
reflect::{TypePath, TypeUuid},
|
|
utils::BoxedFuture,
|
|
};
|
|
|
|
pub(crate) struct AudioPlugin;
|
|
|
|
impl Plugin for AudioPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.add_event::<AudioEvent>();
|
|
|
|
app.add_systems(OnEnter(GameState::Loading), load_tweakfile);
|
|
|
|
app.add_plugins(FmodPlugin {
|
|
audio_banks_paths: &[
|
|
"./assets/audio/Martian Chess Audio/Build/Desktop/Master.bank",
|
|
"./assets/audio/Martian Chess Audio/Build/Desktop/Master.strings.bank",
|
|
"./assets/audio/Martian Chess Audio/Build/Desktop/Music.bank",
|
|
"./assets/audio/Martian Chess Audio/Build/Desktop/SFX.bank",
|
|
],
|
|
});
|
|
app.add_asset::<AudioSettings>().init_asset_loader::<AudioSettingsLoader>();
|
|
|
|
app.add_systems(OnEnter(GameState::Menu), play_background);
|
|
app.add_systems(
|
|
Update,
|
|
(
|
|
play_audio.run_if(any_component_added::<AudioSource>),
|
|
audio_trigger.run_if(on_event::<AudioEvent>()),
|
|
// play_background.run_if(on_event::<AssetEvent<AudioSettings>>()), TODO: Handle music hot-reload
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[derive(Event, Debug)]
|
|
pub enum AudioEvent {
|
|
MainMusic,
|
|
MenuSelect,
|
|
PickUp,
|
|
PutDown,
|
|
Idle,
|
|
Invalid,
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
struct AudioTweakfile(Handle<AudioSettings>);
|
|
|
|
fn load_tweakfile(
|
|
server: Res<AssetServer>,
|
|
mut commands: Commands,
|
|
) {
|
|
let handle: Handle<AudioSettings> = server.load("audio/martian.audio.tweak");
|
|
commands.insert_resource(AudioTweakfile(handle));
|
|
}
|
|
|
|
fn play_background(mut events: EventWriter<AudioEvent>) {
|
|
events.send(AudioEvent::MainMusic);
|
|
}
|
|
|
|
fn play_audio(mut events: Query<&AudioSource, Added<AudioSource>>) {
|
|
events.iter_mut().for_each(|aud| aud.play());
|
|
}
|
|
|
|
fn audio_trigger(
|
|
mut events: EventReader<AudioEvent>,
|
|
studio: Res<FmodStudio>,
|
|
mut commands: Commands,
|
|
server: Res<AssetServer>,
|
|
audio_settings: Res<Assets<AudioSettings>>,
|
|
display_state: Res<State<DisplayState>>,
|
|
) {
|
|
let tweak = audio_settings.get(&server.load("audio/martian.audio.tweak")).expect("Load audio tweakfile");
|
|
debug!("Audio tweaks: {:?}", tweak);
|
|
let state = display_state.get();
|
|
events.iter().for_each(|event| {
|
|
let aud = match event {
|
|
AudioEvent::MainMusic => tweak.music.main.clone(),
|
|
AudioEvent::MenuSelect => tweak.menu.select.clone(),
|
|
AudioEvent::PickUp => match state {
|
|
DisplayState::Display2d => tweak.display2d.pick_up.clone(),
|
|
DisplayState::Display3d => tweak.display3d.pick_up.clone(),
|
|
},
|
|
AudioEvent::PutDown => match state {
|
|
DisplayState::Display2d => tweak.display2d.put_down.clone(),
|
|
DisplayState::Display3d => tweak.display3d.put_down.clone(),
|
|
},
|
|
AudioEvent::Idle => match state {
|
|
DisplayState::Display2d => tweak.display2d.idle.clone(),
|
|
DisplayState::Display3d => tweak.display3d.idle.clone(),
|
|
},
|
|
AudioEvent::Invalid => match state {
|
|
DisplayState::Display2d => tweak.display2d.invalid.clone(),
|
|
DisplayState::Display3d => tweak.display3d.invalid.clone(),
|
|
},
|
|
};
|
|
if !aud.is_empty() {
|
|
let event_str = format!("event:{}", aud);
|
|
if let Ok(event_description) = studio.0.get_event(event_str.as_str()) {
|
|
info!("Playing FMOD {:?}", event_str);
|
|
commands.spawn(AudioSource::new(event_description));
|
|
} else {
|
|
warn!("Music not found for {:?}", event_str);
|
|
}
|
|
} else {
|
|
warn!("No music set for {:?} in {:?}", event, state);
|
|
}
|
|
});
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, TypeUuid, TypePath)]
|
|
#[uuid = "5c7a67ee-56a9-4fe9-9e91-1a709fbc3cf0"]
|
|
pub(crate) struct AudioSettings {
|
|
#[serde(default)]
|
|
display2d: PlaySettings,
|
|
#[serde(default)]
|
|
display3d: PlaySettings,
|
|
#[serde(default)]
|
|
music: MusicSettings,
|
|
#[serde(default)]
|
|
menu: MenuSettings,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Default)]
|
|
struct MusicSettings {
|
|
#[serde(default)]
|
|
main: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Default)]
|
|
struct MenuSettings {
|
|
#[serde(default)]
|
|
select: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Default)]
|
|
struct PlaySettings {
|
|
#[serde(default)]
|
|
pick_up: String,
|
|
#[serde(default)]
|
|
put_down: String,
|
|
#[serde(default)]
|
|
idle: String,
|
|
#[serde(default)]
|
|
invalid: String,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct AudioSettingsLoader;
|
|
|
|
impl AssetLoader for AudioSettingsLoader {
|
|
fn load<'a>(
|
|
&'a self,
|
|
bytes: &'a [u8],
|
|
load_context: &'a mut LoadContext,
|
|
) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> {
|
|
Box::pin(async move {
|
|
let s = std::str::from_utf8(bytes)?;
|
|
let custom_asset = toml::from_str::<AudioSettings>(s)?;
|
|
load_context.set_default_asset(LoadedAsset::new(custom_asset));
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
fn extensions(&self) -> &[&str] {
|
|
&["audio.tweak"]
|
|
}
|
|
}
|