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.
192 lines
6.6 KiB
Rust
192 lines
6.6 KiB
Rust
///
|
|
/// TODO: Custom Asset: FmodEventMapper
|
|
///
|
|
use crate::prelude::*;
|
|
|
|
pub(crate) struct AudioPlugin;
|
|
|
|
impl Plugin for AudioPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.add_event::<AudioEvent>();
|
|
|
|
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.init_resource::<AudioVolume>();
|
|
app.add_systems(OnEnter(GameState::Intro), play_background);
|
|
app.add_systems(
|
|
Update,
|
|
(
|
|
play_audio.run_if(any_component_added::<AudioSource>()),
|
|
audio_trigger.run_if(on_event::<AudioEvent>()),
|
|
control_volume.run_if(resource_changed::<AudioVolume>),
|
|
toggle_volume.run_if(just_pressed(KeyCode::KeyM)),
|
|
set_intensity.run_if(in_state(GameState::Play).and_then(
|
|
resource_exists::<tweak::GameTweaks>.and_then(resource_changed::<game::Board>),
|
|
)),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[derive(Event, Debug, PartialEq, Component, Clone)]
|
|
pub enum AudioEvent {
|
|
MainMusic,
|
|
_StopMainMusic,
|
|
MenuHover,
|
|
MenuSelect,
|
|
PickUp,
|
|
PutDown,
|
|
Idle,
|
|
StopIdle,
|
|
Invalid,
|
|
Captured,
|
|
}
|
|
|
|
impl AudioEvent {
|
|
fn as_str(&self) -> &str {
|
|
match self {
|
|
AudioEvent::MainMusic | AudioEvent::_StopMainMusic => "audio_music_main",
|
|
AudioEvent::MenuHover => "audio_menu_hover",
|
|
AudioEvent::MenuSelect => "audio_menu_select",
|
|
AudioEvent::PickUp => "audio_display3d_pick_up",
|
|
AudioEvent::PutDown => "audio_display3d_put_down",
|
|
AudioEvent::Idle | AudioEvent::StopIdle => "audio_display3d_idle",
|
|
AudioEvent::Captured => "audio_display3d_captured",
|
|
AudioEvent::Invalid => "audio_display3d_invalid",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Resource, Debug)]
|
|
pub struct AudioVolume(f32);
|
|
|
|
impl Default for AudioVolume {
|
|
fn default() -> Self {
|
|
AudioVolume(1.0)
|
|
}
|
|
}
|
|
|
|
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>,
|
|
sources: Query<(Entity, &AudioSource, &AudioEvent)>,
|
|
studio: Res<FmodStudio>,
|
|
mut commands: Commands,
|
|
server: Res<AssetServer>,
|
|
tweaks: Res<Assets<tweak::Tweaks>>,
|
|
display_state: Res<State<DisplayState>>,
|
|
vol: Res<AudioVolume>,
|
|
) {
|
|
let tweak = tweaks
|
|
.get(&server.load("martian.tweak.toml"))
|
|
.expect("Load tweaks");
|
|
let state = display_state.get();
|
|
events.read().for_each(|event| {
|
|
let aud = tweak.get::<String>(event.as_str()).unwrap_or("".into());
|
|
|
|
// There is an event, play an audio
|
|
if !aud.is_empty() {
|
|
let event_str = format!("event:{}", aud);
|
|
if let Ok(event_description) = studio.0.get_event(event_str.as_str()) {
|
|
// We are stopping a playing event
|
|
match event {
|
|
// Find and stop playing instances of idle audio
|
|
AudioEvent::StopIdle => {
|
|
sources
|
|
.iter()
|
|
.filter(|(_, _, source_event)| **source_event == AudioEvent::Idle)
|
|
.for_each(|(entity, source, _)| {
|
|
debug!("Stopping audio {}", event_str);
|
|
source.stop();
|
|
commands.entity(entity).despawn_recursive();
|
|
});
|
|
}
|
|
// Find and stop playing instances of main music
|
|
AudioEvent::_StopMainMusic => {
|
|
sources
|
|
.iter()
|
|
.filter(|(_, _, source_event)| **source_event == AudioEvent::MainMusic)
|
|
.for_each(|(entity, source, _)| {
|
|
debug!("Stopping audio {}", event_str);
|
|
source.stop();
|
|
commands.entity(entity).despawn_recursive();
|
|
});
|
|
}
|
|
// we are playing a sound
|
|
_ => {
|
|
debug!("Playing audio {}", event_str);
|
|
let audio_source = AudioSource::new(event_description);
|
|
audio_source.set_volume(vol.0);
|
|
// TODO: Can we batch spawn all sounds at startup? Then Start/Stop/Reset at runtime?
|
|
commands.spawn((audio_source, event.clone()));
|
|
}
|
|
}
|
|
} else {
|
|
debug!("No music set for {:?} in {:?}", event, state);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn toggle_volume(mut vol: ResMut<AudioVolume>) {
|
|
if vol.0 > 0.0 {
|
|
vol.0 = 0.0;
|
|
} else {
|
|
vol.0 = 1.0;
|
|
}
|
|
}
|
|
|
|
fn control_volume(vol: Res<AudioVolume>, query: Query<&AudioSource>) {
|
|
query.iter().for_each(|aud_src| aud_src.set_volume(vol.0));
|
|
}
|
|
|
|
fn set_intensity(
|
|
query: Query<(&AudioSource, &AudioEvent)>,
|
|
board: Res<game::Board>,
|
|
tweaks_file: Res<tweak::GameTweaks>,
|
|
tweaks: Res<Assets<Tweaks>>,
|
|
) {
|
|
let tweak = tweaks
|
|
.get(tweaks_file.handle.clone())
|
|
.expect("Load tweakfile");
|
|
let value = {
|
|
let intensity_min = tweak.get::<f32>("audio_music_intensity_min").unwrap();
|
|
let intensity_max = tweak.get::<f32>("audio_music_intensity_max").unwrap();
|
|
let intensity_range = intensity_max - intensity_min;
|
|
|
|
let count_side_a = board.on(Side::A).len();
|
|
let count_side_b = board.on(Side::B).len();
|
|
let min_side = count_side_a.min(count_side_b);
|
|
let max_side_size = 16;
|
|
|
|
let intensity_ratio = ((max_side_size as f32) - (min_side as f32)) / (max_side_size as f32);
|
|
|
|
(intensity_ratio * intensity_range) + intensity_min
|
|
};
|
|
|
|
query
|
|
.iter()
|
|
.filter(|(_, event)| **event == AudioEvent::MainMusic)
|
|
.for_each(|(source, _)| {
|
|
debug!("Setting music intensity to {:?}", value);
|
|
source
|
|
.event_instance
|
|
.set_parameter_by_name("Intensity", value, true)
|
|
.expect("Set intensity parameter");
|
|
});
|
|
}
|