diff --git a/CHANGELOG.md b/CHANGELOG.md index 086125408c..055492a259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Brighter / higher contrast main-map - Removed highlighting of non-collectible sprites - Fixed /give_exp ignoring player argument +- Extend run sfx to small animals to prevent sneak attacks by geese. ### Removed diff --git a/common/src/event.rs b/common/src/event.rs index d06f912a7b..6ac5e49531 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -9,12 +9,21 @@ use vek::*; pub struct SfxEventItem { pub sfx: SfxEvent, pub pos: Option>, + pub vol: Option, } impl SfxEventItem { - pub fn new(sfx: SfxEvent, pos: Option>) -> Self { Self { sfx, pos } } + pub fn new(sfx: SfxEvent, pos: Option>, vol: Option) -> Self { + Self { sfx, pos, vol } + } - pub fn at_player_position(sfx: SfxEvent) -> Self { Self { sfx, pos: None } } + pub fn at_player_position(sfx: SfxEvent) -> Self { + Self { + sfx, + pos: None, + vol: None, + } + } } #[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)] diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index 53ef2965df..1709649c89 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -10,7 +10,7 @@ use soundcache::SoundCache; use common::assets; use cpal::traits::DeviceTrait; -use rodio::{Decoder, Device}; +use rodio::{source::Source, Decoder, Device}; use vek::*; const FALLOFF: f32 = 0.13; @@ -135,11 +135,14 @@ impl AudioFrontend { self.music_channels.last_mut() } - pub fn play_sfx(&mut self, sound: &str, pos: Vec3) { + pub fn play_sfx(&mut self, sound: &str, pos: Vec3, vol: Option) { if self.audio_device.is_some() { let calc_pos = ((pos - self.listener_pos) * FALLOFF).into_array(); - let sound = self.sound_cache.load_sound(sound); + let sound = self + .sound_cache + .load_sound(sound) + .amplify(vol.unwrap_or(1.0)); let left_ear = self.listener_ear_left.into_array(); let right_ear = self.listener_ear_right.into_array(); diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index 8c4581c00c..0b0294ecec 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -32,7 +32,7 @@ impl MovementEventMapper { } pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) { - const SFX_DIST_LIMIT_SQR: f32 = 22500.0; + const SFX_DIST_LIMIT_SQR: f32 = 20000.0; let ecs = client.state().ecs(); let player_position = ecs @@ -65,18 +65,25 @@ impl MovementEventMapper { let mapped_event = match body { Body::Humanoid(_) => Self::map_movement_event(character, state, vel.0, stats), - Body::QuadrupedMedium(_) => { - // TODO: Quadriped running sfx - SfxEvent::Idle + Body::QuadrupedMedium(_) + | Body::QuadrupedSmall(_) + | Body::BirdMedium(_) + | Body::BirdSmall(_) + | Body::BipedLarge(_) => { + Self::map_non_humanoid_movement_event(character, vel.0) }, - _ => SfxEvent::Idle, + _ => SfxEvent::Idle, // Ignore fish, critters, etc... }; // Check for SFX config entry for this movement if Self::should_emit(state, triggers.get_key_value(&mapped_event)) { ecs.read_resource::>() .emitter() - .emit(SfxEventItem::new(mapped_event, Some(pos.0))); + .emit(SfxEventItem::new( + mapped_event, + Some(pos.0), + Some(Self::get_volume_for_body_type(body)), + )); // Update the last play time state.event = mapped_event; @@ -99,7 +106,7 @@ impl MovementEventMapper { /// they have not triggered an event for > n seconds. This prevents /// stale records from bloating the Map size. fn cleanup(&mut self, player: EcsEntity) { - const TRACKING_TIMEOUT: u64 = 15; + const TRACKING_TIMEOUT: u64 = 10; let now = Instant::now(); self.event_history.retain(|entity, event| { @@ -197,11 +204,34 @@ impl MovementEventMapper { } } + /// Maps a limited set of movements for other non-humanoid entities + fn map_non_humanoid_movement_event(current_event: &CharacterState, vel: Vec3) -> SfxEvent { + if current_event.movement == MovementState::Run && vel.magnitude() > 0.1 { + SfxEvent::Run + } else { + SfxEvent::Idle + } + } + /// Returns true for any state where the player has their weapon drawn. This /// helps us manage the wield/unwield sfx events fn has_weapon_drawn(state: ActionState) -> bool { state.is_wield() | state.is_attack() | state.is_block() | state.is_charge() } + + /// Returns a relative volume value for a body type. This helps us emit sfx + /// at a volume appropriate fot the entity we are emitting the event for + fn get_volume_for_body_type(body: &Body) -> f32 { + match body { + Body::Humanoid(_) => 0.9, + Body::QuadrupedSmall(_) => 0.3, + Body::QuadrupedMedium(_) => 0.7, + Body::BirdMedium(_) => 0.3, + Body::BirdSmall(_) => 0.2, + Body::BipedLarge(_) => 1.0, + _ => 0.9, + } + } } #[cfg(test)] mod tests; diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index 210f7fe2c1..251efba8a5 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -1,7 +1,10 @@ use super::*; use common::{ assets, - comp::{humanoid, item::Tool, ActionState, Body, MovementState, Stats}, + comp::{ + bird_small, humanoid, item::Tool, quadruped_medium, quadruped_small, ActionState, Body, + MovementState, Stats, + }, event::SfxEvent, }; use std::time::{Duration, Instant}; @@ -417,3 +420,37 @@ fn does_not_map_wield_when_no_main_weapon() { assert_eq!(result, SfxEvent::Run); } + +#[test] +fn maps_quadrupeds_running() { + let result = MovementEventMapper::map_non_humanoid_movement_event( + &CharacterState { + movement: MovementState::Run, + action: ActionState::Idle, + }, + Vec3::new(0.5, 0.8, 0.0), + ); + + assert_eq!(result, SfxEvent::Run); +} + +#[test] +fn determines_relative_volumes() { + let human = + MovementEventMapper::get_volume_for_body_type(&Body::Humanoid(humanoid::Body::random())); + + let quadruped_medium = MovementEventMapper::get_volume_for_body_type(&Body::QuadrupedMedium( + quadruped_medium::Body::random(), + )); + + let quadruped_small = MovementEventMapper::get_volume_for_body_type(&Body::QuadrupedSmall( + quadruped_small::Body::random(), + )); + + let bird_small = + MovementEventMapper::get_volume_for_body_type(&Body::BirdSmall(bird_small::Body::random())); + + assert!(quadruped_medium < human); + assert!(quadruped_small < quadruped_medium); + assert!(bird_small < quadruped_small); +} diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 65231f5758..2af778a0ef 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,7 +91,7 @@ impl SfxMgr { }, }; - audio.play_sfx(sfx_file, position); + audio.play_sfx(sfx_file, position, event.vol); } } }