Add a volume option to SfxEvents, and use this to dispatch movement sfx for quadripeds at a volume proportionate to their size.

This commit is contained in:
S Handley 2020-02-23 01:26:51 +00:00 committed by Imbris
parent 13cbef7865
commit d87061fe14
6 changed files with 94 additions and 14 deletions

View File

@ -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

View File

@ -9,12 +9,21 @@ use vek::*;
pub struct SfxEventItem {
pub sfx: SfxEvent,
pub pos: Option<Vec3<f32>>,
pub vol: Option<f32>,
}
impl SfxEventItem {
pub fn new(sfx: SfxEvent, pos: Option<Vec3<f32>>) -> Self { Self { sfx, pos } }
pub fn new(sfx: SfxEvent, pos: Option<Vec3<f32>>, vol: Option<f32>) -> 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)]

View File

@ -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<f32>) {
pub fn play_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
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();

View File

@ -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::<EventBus<SfxEventItem>>()
.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<f32>) -> 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;

View File

@ -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);
}

View File

@ -91,7 +91,7 @@ impl SfxMgr {
},
};
audio.play_sfx(sfx_file, position);
audio.play_sfx(sfx_file, position, event.vol);
}
}
}