mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
parent
13cbef7865
commit
d87061fe14
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Brighter / higher contrast main-map
|
- Brighter / higher contrast main-map
|
||||||
- Removed highlighting of non-collectible sprites
|
- Removed highlighting of non-collectible sprites
|
||||||
- Fixed /give_exp ignoring player argument
|
- Fixed /give_exp ignoring player argument
|
||||||
|
- Extend run sfx to small animals to prevent sneak attacks by geese.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -9,12 +9,21 @@ use vek::*;
|
|||||||
pub struct SfxEventItem {
|
pub struct SfxEventItem {
|
||||||
pub sfx: SfxEvent,
|
pub sfx: SfxEvent,
|
||||||
pub pos: Option<Vec3<f32>>,
|
pub pos: Option<Vec3<f32>>,
|
||||||
|
pub vol: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SfxEventItem {
|
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)]
|
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||||
|
@ -10,7 +10,7 @@ use soundcache::SoundCache;
|
|||||||
|
|
||||||
use common::assets;
|
use common::assets;
|
||||||
use cpal::traits::DeviceTrait;
|
use cpal::traits::DeviceTrait;
|
||||||
use rodio::{Decoder, Device};
|
use rodio::{source::Source, Decoder, Device};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
const FALLOFF: f32 = 0.13;
|
const FALLOFF: f32 = 0.13;
|
||||||
@ -135,11 +135,14 @@ impl AudioFrontend {
|
|||||||
self.music_channels.last_mut()
|
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() {
|
if self.audio_device.is_some() {
|
||||||
let calc_pos = ((pos - self.listener_pos) * FALLOFF).into_array();
|
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 left_ear = self.listener_ear_left.into_array();
|
||||||
let right_ear = self.listener_ear_right.into_array();
|
let right_ear = self.listener_ear_right.into_array();
|
||||||
|
@ -32,7 +32,7 @@ impl MovementEventMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) {
|
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 ecs = client.state().ecs();
|
||||||
|
|
||||||
let player_position = ecs
|
let player_position = ecs
|
||||||
@ -65,18 +65,25 @@ impl MovementEventMapper {
|
|||||||
|
|
||||||
let mapped_event = match body {
|
let mapped_event = match body {
|
||||||
Body::Humanoid(_) => Self::map_movement_event(character, state, vel.0, stats),
|
Body::Humanoid(_) => Self::map_movement_event(character, state, vel.0, stats),
|
||||||
Body::QuadrupedMedium(_) => {
|
Body::QuadrupedMedium(_)
|
||||||
// TODO: Quadriped running sfx
|
| Body::QuadrupedSmall(_)
|
||||||
SfxEvent::Idle
|
| 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
|
// Check for SFX config entry for this movement
|
||||||
if Self::should_emit(state, triggers.get_key_value(&mapped_event)) {
|
if Self::should_emit(state, triggers.get_key_value(&mapped_event)) {
|
||||||
ecs.read_resource::<EventBus<SfxEventItem>>()
|
ecs.read_resource::<EventBus<SfxEventItem>>()
|
||||||
.emitter()
|
.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
|
// Update the last play time
|
||||||
state.event = mapped_event;
|
state.event = mapped_event;
|
||||||
@ -99,7 +106,7 @@ impl MovementEventMapper {
|
|||||||
/// they have not triggered an event for > n seconds. This prevents
|
/// they have not triggered an event for > n seconds. This prevents
|
||||||
/// stale records from bloating the Map size.
|
/// stale records from bloating the Map size.
|
||||||
fn cleanup(&mut self, player: EcsEntity) {
|
fn cleanup(&mut self, player: EcsEntity) {
|
||||||
const TRACKING_TIMEOUT: u64 = 15;
|
const TRACKING_TIMEOUT: u64 = 10;
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
self.event_history.retain(|entity, event| {
|
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
|
/// Returns true for any state where the player has their weapon drawn. This
|
||||||
/// helps us manage the wield/unwield sfx events
|
/// helps us manage the wield/unwield sfx events
|
||||||
fn has_weapon_drawn(state: ActionState) -> bool {
|
fn has_weapon_drawn(state: ActionState) -> bool {
|
||||||
state.is_wield() | state.is_attack() | state.is_block() | state.is_charge()
|
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;
|
#[cfg(test)] mod tests;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
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,
|
event::SfxEvent,
|
||||||
};
|
};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@ -417,3 +420,37 @@ fn does_not_map_wield_when_no_main_weapon() {
|
|||||||
|
|
||||||
assert_eq!(result, SfxEvent::Run);
|
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);
|
||||||
|
}
|
||||||
|
@ -91,7 +91,7 @@ impl SfxMgr {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
audio.play_sfx(sfx_file, position);
|
audio.play_sfx(sfx_file, position, event.vol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user