/// /// TODO: Custom Asset: FmodEventMapper /// use crate::prelude::*; pub(crate) struct AudioPlugin; impl Plugin for AudioPlugin { fn build(&self, app: &mut App) { app.add_event::(); 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::(); app.add_systems(OnEnter(GameState::Intro), play_background); app.add_systems( Update, ( play_audio.run_if(any_component_added::()), audio_trigger.run_if(on_event::()), control_volume.run_if(resource_changed::), toggle_volume.run_if(just_pressed(KeyCode::KeyM)), set_intensity.run_if(in_state(GameState::Play).and_then( resource_exists::.and_then(resource_changed::), )), ), ); } } #[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) { events.send(AudioEvent::MainMusic); } fn play_audio(mut events: Query<&AudioSource, Added>) { events.iter_mut().for_each(|aud| aud.play()); } fn audio_trigger( mut events: EventReader, sources: Query<(Entity, &AudioSource, &AudioEvent)>, studio: Res, mut commands: Commands, server: Res, tweaks: Res>, display_state: Res>, vol: Res, ) { 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::(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) { if vol.0 > 0.0 { vol.0 = 0.0; } else { vol.0 = 1.0; } } fn control_volume(vol: Res, query: Query<&AudioSource>) { query.iter().for_each(|aud_src| aud_src.set_volume(vol.0)); } fn set_intensity( query: Query<(&AudioSource, &AudioEvent)>, board: Res, tweaks_file: Res, tweaks: Res>, ) { let tweak = tweaks .get(tweaks_file.handle.clone()) .expect("Load tweakfile"); let value = { let intensity_min = tweak.get::("audio_music_intensity_min").unwrap(); let intensity_max = tweak.get::("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"); }); }