From dacd19063fae6af40ad826ce4905326f93f46284 Mon Sep 17 00:00:00 2001 From: Zicklag Date: Sun, 15 Oct 2023 11:02:48 -0500 Subject: [PATCH] feat: re-implement music player. --- Cargo.lock | 18 ++--- Cargo.toml | 5 ++ assets/game.yaml | 28 ++++---- src/core/audio.rs | 5 +- src/main.rs | 13 ++++ src/music.rs | 170 ++++++++++++++++++++++++++++++++++++++++++++++ src/sessions.rs | 1 + 7 files changed, 214 insertions(+), 26 deletions(-) create mode 100644 src/music.rs diff --git a/Cargo.lock b/Cargo.lock index aba0cae359..20bf0fa057 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1046,7 +1046,7 @@ dependencies = [ [[package]] name = "bones_asset" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "anyhow", "append-only-vec", @@ -1075,7 +1075,7 @@ dependencies = [ [[package]] name = "bones_bevy_renderer" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "anyhow", "bevy", @@ -1092,7 +1092,7 @@ dependencies = [ [[package]] name = "bones_ecs" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "anyhow", "atomicell", @@ -1107,7 +1107,7 @@ dependencies = [ [[package]] name = "bones_framework" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "bones_asset", "bones_lib", @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "bones_lib" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "bones_ecs", "instant", @@ -1144,7 +1144,7 @@ dependencies = [ [[package]] name = "bones_schema" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "append-only-vec", "bones_schema_macros", @@ -1161,7 +1161,7 @@ dependencies = [ [[package]] name = "bones_schema_macros" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "proc-macro2", "quote", @@ -1171,7 +1171,7 @@ dependencies = [ [[package]] name = "bones_utils" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "bevy_ptr", "bones_utils_macros", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "bones_utils_macros" version = "0.3.0" -source = "git+https://github.com/fishfolk/bones#2335ab66e4f98534dc01ea60ccb5479bb79817b2" +source = "git+https://github.com/fishfolk/bones#92a4c1216225f01e2242bf76e918c41e35193a8f" dependencies = [ "quote", "venial", diff --git a/Cargo.toml b/Cargo.toml index 311519f4d8..e9352bd6b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,3 +140,8 @@ ignored = [ "getrandom", # Needed to add `js` feature ] +# # Uncomment for testing during bones development, if you clone bones adjacent to the jumpy +# # repo. +# [patch.'https://github.com/fishfolk/bones'] +# bones_framework = { path = "../bones/framework_crates/bones_framework" } +# bones_bevy_renderer = { path = "../bones/framework_crates/bones_bevy_renderer" } diff --git a/assets/game.yaml b/assets/game.yaml index 245bd2471d..a4808c1bcb 100644 --- a/assets/game.yaml +++ b/assets/game.yaml @@ -1,17 +1,17 @@ -# music: -# title_screen: music/01 fishycuffs.ogg -# fight: -# - music/02 whalecome.ogg -# - music/03 ahoy!.ogg -# - music/04 bait the hook.ogg -# - music/05 fire in the hole.ogg -# - music/06 fishsticks.ogg -# - music/07 jolly roger.ogg -# - music/08 krill or be krilled.ogg -# - music/09 landlubber.ogg -# character_screen: music/10 fish bucket.ogg -# results_screen: music/11 thar she blows!.ogg -# credits: music/12 all hands hoay!.ogg +music: + title_screen: music/01 fishycuffs.ogg + fight: + - music/02 whalecome.ogg + - music/03 ahoy!.ogg + - music/04 bait the hook.ogg + - music/05 fire in the hole.ogg + - music/06 fishsticks.ogg + - music/07 jolly roger.ogg + - music/08 krill or be krilled.ogg + - music/09 landlubber.ogg + character_screen: music/10 fish bucket.ogg + results_screen: music/11 thar she blows!.ogg + credits: music/12 all hands hoay!.ogg main_menu: diff --git a/src/core/audio.rs b/src/core/audio.rs index c79bfd4d00..ca24a16841 100644 --- a/src/core/audio.rs +++ b/src/core/audio.rs @@ -45,7 +45,7 @@ pub enum AudioEvent { } fn play_sounds( - audio: Res, + mut audio: ResMut, mut audio_events: ResMut, assets: Res, ) { @@ -56,10 +56,9 @@ fn play_sounds( sound_source, volume, } => { - if let Err(e) = audio.borrow_mut().play( + if let Err(e) = audio.play( assets .get(sound_source) - .0 .with_settings(StaticSoundSettings::default().volume(volume)), ) { warn!("Error playing sound: {e}"); diff --git a/src/main.rs b/src/main.rs index 9698ee015e..e6dcef6f8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ use bones_framework::prelude::*; pub mod core; pub mod input; +pub mod music; pub mod sessions; pub mod settings; pub mod ui; @@ -46,6 +47,17 @@ pub struct GameMeta { pub localization: Handle, pub theme: ui::UiTheme, pub main_menu: ui::main_menu::MainMenuMeta, + pub music: GameMusic, +} + +#[derive(HasSchema, Clone, Debug, Default)] +#[repr(C)] +pub struct GameMusic { + pub title_screen: Handle, + pub fight: SVec>, + pub character_screen: Handle, + pub results_screen: Handle, + pub credits: Handle, } fn main() { @@ -64,6 +76,7 @@ fn main() { game // Install game plugins .install_plugin(DefaultGamePlugin) + .install_plugin(music::game_plugin) .install_plugin(settings::game_plugin) .install_plugin(input::game_plugin) .install_plugin(core::game_plugin) diff --git a/src/music.rs b/src/music.rs new file mode 100644 index 0000000000..969f6e3918 --- /dev/null +++ b/src/music.rs @@ -0,0 +1,170 @@ +use bones_framework::prelude::kira::{ + sound::{ + static_sound::{StaticSoundHandle, StaticSoundSettings}, + PlaybackState, Region, + }, + tween::Tween, +}; + +use crate::{prelude::*, ui::main_menu::MenuPage}; + +pub fn game_plugin(game: &mut Game) { + let session = game.sessions.create(SessionNames::MUSIC_PLAYER); + + // Music player doesn't do any rendering + session.visible = false; + session.stages.add_system_to_stage(First, music_system); +} + +/// The music playback state. +#[derive(HasSchema, Default)] +#[schema(no_clone)] +pub enum MusicState { + /// Music is not playing. + #[default] + None, + /// Playing the main menu music. + MainMenu(StaticSoundHandle), + /// Playing the character select music. + CharacterSelect(StaticSoundHandle), + /// Playing the credits music. + Credits(StaticSoundHandle), + /// Playing the fight music. + Fight { + /// The handle to the audio instance. + instance: StaticSoundHandle, + /// The index of the song in the shuffled playlist. + idx: usize, + }, +} + +impl MusicState { + /// Get the current audio instance, if one is contained. + fn current_instance(&mut self) -> Option<&mut StaticSoundHandle> { + match self { + MusicState::None => None, + MusicState::MainMenu(i) => Some(i), + MusicState::CharacterSelect(i) => Some(i), + MusicState::Credits(i) => Some(i), + MusicState::Fight { instance, .. } => Some(instance), + } + } +} + +/// Bevy resource containing the in-game music playlist shuffled. +#[derive(HasSchema, Deref, DerefMut, Clone, Default)] +#[repr(C)] +pub struct ShuffledPlaylist(pub SVec>); + +/// The amount of time to spend fading the music in and out. +const MUSIC_FADE_DURATION: Duration = Duration::from_millis(500); + +const MUSIC_VOLUME: f64 = 0.1; + +/// System that plays music according to the game mode. +fn music_system( + meta: Root, + mut audio: ResMut, + mut shuffled_fight_music: ResMutInit, + mut music_state: ResMutInit, + ctx: Res, + sessions: Res, + assets: Res, +) { + if shuffled_fight_music.is_empty() { + let mut songs = meta.music.fight.clone(); + THREAD_RNG.with(|rng| rng.shuffle(&mut songs)); + **shuffled_fight_music = songs; + } + + let tween = Tween { + start_time: kira::StartTime::Immediate, + duration: MUSIC_FADE_DURATION, + easing: kira::tween::Easing::Linear, + }; + let play_settings = StaticSoundSettings::default() + .volume(MUSIC_VOLUME) + .fade_in_tween(tween); + + // If we are in a game + if sessions.get(SessionNames::GAME).is_some() { + if let MusicState::Fight { instance, idx } = &mut *music_state { + if let PlaybackState::Stopped = instance.state() { + *idx += 1; + *idx %= shuffled_fight_music.len(); + + *instance = audio + .play( + assets + .get(shuffled_fight_music[*idx]) + .0 + .with_settings(play_settings), + ) + .unwrap(); + } + } else { + if let Some(instance) = music_state.current_instance() { + instance.stop(tween).unwrap(); + } + + if let Some(song) = shuffled_fight_music.get(0) { + *music_state = MusicState::Fight { + instance: audio + .play(assets.get(*song).with_settings(play_settings)) + .unwrap(), + idx: 0, + }; + } + } + + // If we are on a menu page + } else if sessions.get(SessionNames::MAIN_MENU).is_some() { + let menu_page = ctx.get_state::(); + match menu_page { + MenuPage::PlayerSelect | MenuPage::MapSelect { .. } | MenuPage::NetworkGame => { + if !matches!(*music_state, MusicState::CharacterSelect(..)) { + if let Some(instance) = music_state.current_instance() { + instance.stop(tween).unwrap(); + } + *music_state = MusicState::CharacterSelect( + audio + .play( + assets + .get(meta.music.character_screen) + .with_settings(play_settings.loop_region(Region::default())), + ) + .unwrap(), + ); + } + } + MenuPage::Home | MenuPage::Settings => { + if !matches!(*music_state, MusicState::MainMenu(..)) { + if let Some(instance) = music_state.current_instance() { + instance.stop(tween).unwrap(); + } + *music_state = MusicState::MainMenu( + audio + .play( + assets + .get(meta.music.title_screen) + .with_settings(play_settings), + ) + .unwrap(), + ); + } + } + MenuPage::Credits => { + if !matches!(*music_state, MusicState::Credits(..)) { + if let Some(instance) = music_state.current_instance() { + instance.stop(tween).unwrap(); + } + *music_state = MusicState::Credits( + audio + .play(assets.get(meta.music.credits).with_settings(play_settings)) + .unwrap(), + ); + } + } + } + } +} diff --git a/src/sessions.rs b/src/sessions.rs index 9d059a20ab..1286c1381b 100644 --- a/src/sessions.rs +++ b/src/sessions.rs @@ -5,6 +5,7 @@ impl SessionNames { pub const GAME: &str = "game"; pub const MAIN_MENU: &str = "main_menu"; pub const PAUSE_MENU: &str = "pause_menu"; + pub const MUSIC_PLAYER: &str = "music_player"; } pub trait SessionExt {