From dc284adbd85a7c64b0d5283899e720c7f5eed662 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 3 Oct 2021 14:07:23 -0400 Subject: [PATCH] Add button sounds --- game/assets/audio/button-press.mp3 | Bin 0 -> 1616 bytes game/src/context.rs | 10 +++-- game/src/lib.rs | 54 ++++++++++++++++++++------ game/src/scenes/death_screen.rs | 12 +++--- game/src/scenes/how_to_play_screen.rs | 12 +++--- game/src/scenes/main_menu_screen.rs | 16 ++++++++ game/src/scenes/mod.rs | 8 +--- game/src/scenes/next_level_screen.rs | 12 +++--- game/src/scenes/options_screen.rs | 12 +++--- game/src/scenes/pause_screen.rs | 16 +++++--- game/src/scenes/win_screen.rs | 12 +++--- game/src/utilities/audio_player.rs | 1 + game/src/utilities/datastore.rs | 40 ++++++++++++++++++- 13 files changed, 145 insertions(+), 60 deletions(-) create mode 100644 game/assets/audio/button-press.mp3 diff --git a/game/assets/audio/button-press.mp3 b/game/assets/audio/button-press.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..82cbcf32231b10cc5cb4dc6343e6ba3ae87c6a4d GIT binary patch literal 1616 zcmezWd%_V0bP$o5mkt!;2VyoL3Se+T16Al?FB-Uv4ql>z|7ZYcm!*%RudA`1rDYM* zQjnAMycrlo+*DXTI59A|f!v`Rm01Fk7)jv&?*d>1vHfAr`=Z#OywpkivB?JphNoh) z*D=m#VPtTS=U*egLg3){&VxT49FH>pQ(%#0Klq_W{s$1-|2qF)PyUbLzhWTf$oZkr zrNGd$hk>!-#D*op(;uoGe8QO6ni>&k`1G01iW2TM`$9AuGy;~%X%wN^e2{bc#_T%XY9?>4)pee%{_%lKyfJip`KFAqh0 za&I@Tn=Mmvy5quTwoOSqUsHN+dKk)VF8j}ycII?vwp!B3jIZ}f^;9;;C0zVc`BF~% z_{Kz+(?1Q_Hr;|Jq93yj?O~kHT$5J`c>VvKaDCL!$W zz_8;|TY`(o%PAYDOiReWG~4w3rCWY&ZF{~33tj8osxiSfWQ$9dQru}N?U4Gf%bv?P z9Z%@Z{q1;fW54jP*FwKu%auxq&Q{Yn@JZ#`$Dm|i$EYS^-KTih3n=w;8VychP+u}1XDOleUM!I>K0IA=_* zyKi=DR{q@VyzN<;UGw;A=HLJM|K6Vr7N3=((p~|jK1nm4>^*6l^dx?t=uuOklh- zwLX45FM)w|GW!OmhVrTygRO6v6m~z*2e73A(yj zt1>J$^}MxWo%#dMzhTSPW=?a7T-z{`azvb$Tw^-|3Ft>YEn2a@M4XXUlYb;%EQdllIea&awTG*}AVk{;97w zX1R25J}jz%^skju?*E_1*T8X(yEsyEkKlueeCj5zmhN~~m=x%Cx8P??=K+OPi&h3N z3f?2iD`ul~B=Y@hR>$By@4qs~{|k8;e_HO5{zv^EU9iGO&vX?K{QoU*gq`^S1LG24 c04p#sm^3gjuz)0}3_$i6G}FhPC_4BD0B}*i761SM literal 0 HcmV?d00001 diff --git a/game/src/context.rs b/game/src/context.rs index b08f8e8..6612f52 100644 --- a/game/src/context.rs +++ b/game/src/context.rs @@ -1,9 +1,10 @@ -use std::{cell::RefCell, sync::mpsc::Sender}; +use std::{cell::RefCell, collections::HashMap, sync::mpsc::Sender}; use chrono::{DateTime, Duration, Utc}; use discord_sdk::activity::ActivityBuilder; +use raylib::audio::Sound; -use crate::{progress::ProgressData, utilities::non_ref_raylib::HackedRaylibHandle, GameConfig}; +use crate::{GameConfig, progress::ProgressData, utilities::{audio_player::AudioPlayer, non_ref_raylib::HackedRaylibHandle}}; #[derive(Debug)] pub enum ControlFlag { @@ -11,12 +12,15 @@ pub enum ControlFlag { SwitchLevel(usize), UpdateLevelStart(DateTime), SaveProgress, - MaybeUpdateHighScore(usize, Duration) + MaybeUpdateHighScore(usize, Duration), + SoundTrigger(String) } #[derive(Debug)] pub struct GameContext { pub renderer: RefCell, + pub audio: AudioPlayer, + pub sounds: HashMap, pub config: GameConfig, pub player_progress: ProgressData, pub current_level: usize, diff --git a/game/src/lib.rs b/game/src/lib.rs index eba4048..b768fee 100644 --- a/game/src/lib.rs +++ b/game/src/lib.rs @@ -70,7 +70,7 @@ )] #![clippy::msrv = "1.57.0"] -use std::{borrow::BorrowMut, cell::RefCell, sync::mpsc::TryRecvError}; +use std::{borrow::BorrowMut, cell::RefCell, collections::HashMap, sync::mpsc::TryRecvError}; use chrono::Utc; use discord_sdk::activity::ActivityBuilder; @@ -78,10 +78,21 @@ use raylib::prelude::*; use tracing::{error, info, warn}; use utilities::discord::DiscordConfig; -use crate::{context::GameContext, discord_rpc::{maybe_set_discord_presence, try_connect_to_local_discord}, progress::ProgressData, scenes::{build_screen_state_machine, Scenes}, utilities::{audio_player::AudioPlayer, datastore::load_music_from_internal_data, game_config::FinalShaderConfig, shaders::{ +use crate::{ + context::GameContext, + discord_rpc::{maybe_set_discord_presence, try_connect_to_local_discord}, + progress::ProgressData, + scenes::{build_screen_state_machine, Scenes}, + utilities::{ + audio_player::AudioPlayer, + datastore::{load_music_from_internal_data, load_sound_from_internal_data}, + game_config::FinalShaderConfig, + shaders::{ shader::ShaderWrapper, util::{dynamic_screen_texture::DynScreenTexture, render_texture::render_to_texture}, - }}}; + }, + }, +}; #[macro_use] extern crate thiserror; @@ -163,10 +174,23 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box { + context.audio.play_sound( + context.sounds.get(&name).unwrap(), + ); + } } } } diff --git a/game/src/scenes/death_screen.rs b/game/src/scenes/death_screen.rs index d3ae105..ee2d4d3 100644 --- a/game/src/scenes/death_screen.rs +++ b/game/src/scenes/death_screen.rs @@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{ - context::GameContext, - utilities::{ +use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }, - GameConfig, -}; + }}; use super::{Scenes, ScreenError}; use tracing::{debug, error, info, trace}; @@ -69,6 +65,10 @@ impl Action for DeathScreen { self.timer_value = format!("{:02}:{:02}", elapsed.num_minutes(), elapsed.num_seconds() % 60); if self.is_retry_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::InGameScene)) } else { Ok(ActionFlag::Continue) diff --git a/game/src/scenes/how_to_play_screen.rs b/game/src/scenes/how_to_play_screen.rs index bcfb308..1592b81 100644 --- a/game/src/scenes/how_to_play_screen.rs +++ b/game/src/scenes/how_to_play_screen.rs @@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{ - context::GameContext, - utilities::{ +use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }, - GameConfig, -}; + }}; use super::{Scenes, ScreenError}; use tracing::{debug, error, info, trace}; @@ -66,6 +62,10 @@ impl Action for HowToPlayScreen { self.render_screen_space(&mut context.renderer.borrow_mut(), &context.config); if self.is_btm_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen)) } else { Ok(ActionFlag::Continue) diff --git a/game/src/scenes/main_menu_screen.rs b/game/src/scenes/main_menu_screen.rs index 6fc6633..d8fa2c4 100644 --- a/game/src/scenes/main_menu_screen.rs +++ b/game/src/scenes/main_menu_screen.rs @@ -71,12 +71,28 @@ impl Action for MainMenuScreen { self.render_screen_space(&mut context.renderer.borrow_mut(), &context.config); if self.is_start_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::InGameScene)) } else if self.is_htp_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::HowToPlayScreen)) } else if self.is_options_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::OptionsScreen)) } else if self.is_quit_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); context.flag_send.send(Some(ControlFlag::Quit)).unwrap(); Ok(ActionFlag::Continue) } else { diff --git a/game/src/scenes/mod.rs b/game/src/scenes/mod.rs index 1a1c055..1143503 100644 --- a/game/src/scenes/mod.rs +++ b/game/src/scenes/mod.rs @@ -6,13 +6,7 @@ use self::{ death_screen::DeathScreen, win_screen::WinScreen, next_level_screen::NextLevelScreen }; -use crate::{ - context::GameContext, - utilities::{ - datastore::{load_texture_from_internal_data, ResourceLoadError}, - non_ref_raylib::HackedRaylibHandle, - }, -}; +use crate::{context::GameContext, utilities::{datastore::{ResourceLoadError, load_music_from_internal_data, load_sound_from_internal_data, load_texture_from_internal_data}, non_ref_raylib::HackedRaylibHandle}}; use dirty_fsm::StateMachine; use raylib::{texture::Texture2D, RaylibThread}; diff --git a/game/src/scenes/next_level_screen.rs b/game/src/scenes/next_level_screen.rs index b677855..464ff8c 100644 --- a/game/src/scenes/next_level_screen.rs +++ b/game/src/scenes/next_level_screen.rs @@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{ - context::GameContext, - utilities::{ +use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }, - GameConfig, -}; + }}; use super::{Scenes, ScreenError}; use tracing::{debug, error, info, trace}; @@ -87,6 +83,10 @@ impl Action for NextLevelScreen { ); if self.is_next_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::InGameScene)) } else { Ok(ActionFlag::Continue) diff --git a/game/src/scenes/options_screen.rs b/game/src/scenes/options_screen.rs index b0b3832..cff3716 100644 --- a/game/src/scenes/options_screen.rs +++ b/game/src/scenes/options_screen.rs @@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{ - context::GameContext, - utilities::{ +use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }, - GameConfig, -}; + }}; use super::{Scenes, ScreenError}; use tracing::{debug, error, info, trace}; @@ -69,6 +65,10 @@ impl Action for OptionsScreen { self.render_screen_space(&mut context.renderer.borrow_mut(), &context.config); if self.is_btm_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen)) } else { Ok(ActionFlag::Continue) diff --git a/game/src/scenes/pause_screen.rs b/game/src/scenes/pause_screen.rs index 1dde2e6..c2e59ae 100644 --- a/game/src/scenes/pause_screen.rs +++ b/game/src/scenes/pause_screen.rs @@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{ - context::GameContext, - utilities::{ +use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }, - GameConfig, -}; + }}; use super::{Scenes, ScreenError}; use tracing::{debug, error, info, trace}; @@ -82,6 +78,10 @@ impl Action for PauseScreen { && Rectangle::new(centered_x_paused, centered_y_paused, 435.0, 80.0) .check_collision_point_rec(mouse_position) { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); return Ok(ActionFlag::SwitchState(Scenes::InGameScene)); } //For Menu @@ -89,6 +89,10 @@ impl Action for PauseScreen { && Rectangle::new(centered_x_menu, centered_y_menu, 200.0, 50.0) .check_collision_point_rec(mouse_position) { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); return Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen)); } diff --git a/game/src/scenes/win_screen.rs b/game/src/scenes/win_screen.rs index dc2851f..2f2dca2 100644 --- a/game/src/scenes/win_screen.rs +++ b/game/src/scenes/win_screen.rs @@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{ - context::GameContext, - utilities::{ +use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }, - GameConfig, -}; + }}; use super::{Scenes, ScreenError}; use tracing::{debug, error, info, trace}; @@ -69,6 +65,10 @@ impl Action for WinScreen { self.counter += 1; if self.is_menu_pressed { + context + .flag_send + .send(Some(ControlFlag::SoundTrigger("button-press".to_string()))) + .unwrap(); Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen)) } else { Ok(ActionFlag::Continue) diff --git a/game/src/utilities/audio_player.rs b/game/src/utilities/audio_player.rs index 182a217..e33c61b 100644 --- a/game/src/utilities/audio_player.rs +++ b/game/src/utilities/audio_player.rs @@ -1,6 +1,7 @@ use raylib::audio::RaylibAudio; /// A thin wrapper around `raylib::core::audio::RaylibAudio` that keeps track of the volume of its audio channels. +#[derive(Debug)] pub struct AudioPlayer { backend: RaylibAudio, diff --git a/game/src/utilities/datastore.rs b/game/src/utilities/datastore.rs index 724e4ba..a8dfd0e 100644 --- a/game/src/utilities/datastore.rs +++ b/game/src/utilities/datastore.rs @@ -1,6 +1,10 @@ use std::{io::Write, path::Path}; -use raylib::{RaylibHandle, RaylibThread, audio::Music, texture::Texture2D}; +use raylib::{ + audio::{Music, Sound}, + texture::Texture2D, + RaylibHandle, RaylibThread, +}; use tempfile::{tempdir, NamedTempFile}; use tracing::debug; @@ -68,7 +72,6 @@ pub fn load_texture_from_internal_data( Ok(texture) } - pub fn load_music_from_internal_data( raylib_handle: &mut RaylibHandle, thread: &RaylibThread, @@ -103,3 +106,36 @@ pub fn load_music_from_internal_data( Ok(texture) } + +pub fn load_sound_from_internal_data( + path: &str, +) -> Result { + // Create a temp file path to work with + let temp_dir = tempdir()?; + debug!( + "Created temporary directory for passing embedded data to Raylib: {}", + temp_dir.path().display() + ); + let tmp_path = temp_dir.path().join(Path::new(path).file_name().unwrap()); + + // Unpack the raw sound data to a real file on the local filesystem so raylib will read it correctly + std::fs::write( + &tmp_path, + &StaticGameData::get(path) + .ok_or(ResourceLoadError::AssetNotFound(path.to_string()))? + .data, + )?; + + // Call through via FFI to re-load the file + let texture = + Sound::load_sound(tmp_path.to_str().unwrap()).map_err(ResourceLoadError::Generic)?; + + // Close the file + debug!( + "Dropping temporary directory: {}", + temp_dir.path().display() + ); + temp_dir.close()?; + + Ok(texture) +}