diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ccdc5867..1dd760e5e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Protection rating was moved to the top left of the loadout view - Changed camera smoothing to be off by default. - Fixed AI behavior so only humanoids will attempt to roll +- Footstep SFX is now dependant on distance moved, not time since last play ### Removed diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index a01aa1e925..2a8478a276 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -64,7 +64,27 @@ ], threshold: 0.25, ), - Run: ( + Run(Earth): ( + files: [ + "voxygen.audio.sfx.footsteps.stepdirt_1", + "voxygen.audio.sfx.footsteps.stepdirt_2", + "voxygen.audio.sfx.footsteps.stepdirt_3", + "voxygen.audio.sfx.footsteps.stepdirt_4", + "voxygen.audio.sfx.footsteps.stepdirt_5", + ], + threshold: 1.6, + ), + QuadRun(Earth): ( + files: [ + "voxygen.audio.sfx.footsteps.stepdirt_1", + "voxygen.audio.sfx.footsteps.stepdirt_2", + "voxygen.audio.sfx.footsteps.stepdirt_3", + "voxygen.audio.sfx.footsteps.stepdirt_4", + "voxygen.audio.sfx.footsteps.stepdirt_5", + ], + threshold: 0.8, + ), + Run(Grass): ( files: [ "voxygen.audio.sfx.footsteps.stepgrass_1", "voxygen.audio.sfx.footsteps.stepgrass_2", @@ -73,9 +93,9 @@ "voxygen.audio.sfx.footsteps.stepgrass_5", "voxygen.audio.sfx.footsteps.stepgrass_6", ], - threshold: 0.25, + threshold: 1.6, ), - QuadRun: ( + QuadRun(Grass): ( files: [ "voxygen.audio.sfx.footsteps.stepgrass_1", "voxygen.audio.sfx.footsteps.stepgrass_2", @@ -84,23 +104,41 @@ "voxygen.audio.sfx.footsteps.stepgrass_5", "voxygen.audio.sfx.footsteps.stepgrass_6", ], - threshold: 0.12, + threshold: 0.8, ), - SnowRun: ( + Run(Snow): ( files: [ "voxygen.audio.sfx.footsteps.snow_step_1", "voxygen.audio.sfx.footsteps.snow_step_2", "voxygen.audio.sfx.footsteps.snow_step_3", ], - threshold: 0.25, + threshold: 1.6, ), - QuadSnowRun: ( + QuadRun(Snow): ( files: [ "voxygen.audio.sfx.footsteps.snow_step_1", "voxygen.audio.sfx.footsteps.snow_step_2", "voxygen.audio.sfx.footsteps.snow_step_3", ], - threshold: 0.12, + threshold: 0.8, + ), + Run(Rock): ( + files: [ + "voxygen.audio.sfx.footsteps.stone_step_1", + "voxygen.audio.sfx.footsteps.stone_step_2", + "voxygen.audio.sfx.footsteps.stone_step_3", + "voxygen.audio.sfx.footsteps.stone_step_4", + ], + threshold: 1.6, + ), + QuadRun(Rock): ( + files: [ + "voxygen.audio.sfx.footsteps.stone_step_1", + "voxygen.audio.sfx.footsteps.stone_step_2", + "voxygen.audio.sfx.footsteps.stone_step_3", + "voxygen.audio.sfx.footsteps.stone_step_4", + ], + threshold: 0.8, ), //ExperienceGained: ( // files: [ diff --git a/assets/voxygen/audio/sfx/footsteps/stepdirt_1.wav b/assets/voxygen/audio/sfx/footsteps/stepdirt_1.wav new file mode 100644 index 0000000000..698bf5334d --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stepdirt_1.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21d9fdd101f074a7b3181781e42d433ae6e2c4d2cf9ed9f622e74c5e8842a0c9 +size 93390 diff --git a/assets/voxygen/audio/sfx/footsteps/stepdirt_2.wav b/assets/voxygen/audio/sfx/footsteps/stepdirt_2.wav new file mode 100644 index 0000000000..6ea1e5d2bc --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stepdirt_2.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4af32c6a13baf00c81a940a2781b90eaf0c5427c8a844eb925bd661a95c5249f +size 93390 diff --git a/assets/voxygen/audio/sfx/footsteps/stepdirt_3.wav b/assets/voxygen/audio/sfx/footsteps/stepdirt_3.wav new file mode 100644 index 0000000000..475950159b --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stepdirt_3.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9943f0ba9faad3e956bd8fafdfde1d18dc8dae54fb85eb9d8f69d0b972447ac8 +size 77340 diff --git a/assets/voxygen/audio/sfx/footsteps/stepdirt_4.wav b/assets/voxygen/audio/sfx/footsteps/stepdirt_4.wav new file mode 100644 index 0000000000..d6a49dd4c8 --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stepdirt_4.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4d9f13579ae9c5a472aa800b65994e57d7b518a211e55b3dd1d4181bc8d67cf +size 88364 diff --git a/assets/voxygen/audio/sfx/footsteps/stepdirt_5.wav b/assets/voxygen/audio/sfx/footsteps/stepdirt_5.wav new file mode 100644 index 0000000000..1f7e26fb71 --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stepdirt_5.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acab39b1ec950f4ddd4253b873c515a31d56214cd0e7145006128889713183a9 +size 88364 diff --git a/assets/voxygen/audio/sfx/footsteps/stone_step_1.wav b/assets/voxygen/audio/sfx/footsteps/stone_step_1.wav new file mode 100644 index 0000000000..a2b0c7e47e --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stone_step_1.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb83d6421e0100a777a6a081f777663129418554e4db0c323a5c8e3895beee63 +size 35640 diff --git a/assets/voxygen/audio/sfx/footsteps/stone_step_2.wav b/assets/voxygen/audio/sfx/footsteps/stone_step_2.wav new file mode 100644 index 0000000000..a7e02c21cb --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stone_step_2.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff7ab14fa6cdce34ba4cd3161af704f5020438ab73ecb1371a66789378b84b5c +size 26464 diff --git a/assets/voxygen/audio/sfx/footsteps/stone_step_3.wav b/assets/voxygen/audio/sfx/footsteps/stone_step_3.wav new file mode 100644 index 0000000000..8d982f1c42 --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stone_step_3.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e1779e8ba831cf58d61b7fc7809a91860fbb83a3f57c08a6cbffe347f6f0502 +size 36600 diff --git a/assets/voxygen/audio/sfx/footsteps/stone_step_4.wav b/assets/voxygen/audio/sfx/footsteps/stone_step_4.wav new file mode 100644 index 0000000000..f27621a500 --- /dev/null +++ b/assets/voxygen/audio/sfx/footsteps/stone_step_4.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cc3d086bced41f99fc77981a6b066a06bb8ebc4cd82cdbe180a03bdc7d65b38 +size 35576 diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index 95974c1ea5..3665b8d048 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -10,6 +10,7 @@ use crate::{ use client::Client; use common::{ comp::{Body, CharacterState, PhysicsState, Pos, Vel}, + resources::DeltaTime, terrain::{BlockKind, TerrainChunk}, vol::ReadVol, }; @@ -25,6 +26,7 @@ struct PreviousEntityState { time: Instant, on_ground: bool, in_water: bool, + distance_travelled: f32, } impl Default for PreviousEntityState { @@ -34,6 +36,7 @@ impl Default for PreviousEntityState { time: Instant::now(), on_ground: true, in_water: false, + distance_travelled: 0.0, } } } @@ -112,6 +115,7 @@ impl EventMapper for MovementEventMapper { underwater, ); internal_state.time = Instant::now(); + internal_state.distance_travelled = 0.0; } // update state to determine the next event. We only record the time (above) if @@ -123,6 +127,8 @@ impl EventMapper for MovementEventMapper { } else { internal_state.in_water = false; } + let dt = ecs.fetch::().0; + internal_state.distance_travelled += vel.0.magnitude() * dt; } } @@ -156,14 +162,19 @@ impl MovementEventMapper { /// any) needs to satisfy two conditions to be allowed to play: /// 1. An sfx.ron entry exists for the movement (we need to know which sound /// file(s) to play) 2. The sfx has not been played since it's timeout - /// threshold has elapsed, which prevents firing every tick + /// threshold has elapsed, which prevents firing every tick. For movement, + /// threshold is not a time, but a distance. fn should_emit( previous_state: &PreviousEntityState, sfx_trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, ) -> bool { if let Some((event, item)) = sfx_trigger_item { if &previous_state.event == event { - previous_state.time.elapsed().as_secs_f32() >= item.threshold + match event { + SfxEvent::Run(_) => previous_state.distance_travelled >= item.threshold, + SfxEvent::QuadRun(_) => previous_state.distance_travelled >= item.threshold, + _ => previous_state.time.elapsed().as_secs_f32() >= item.threshold, + } } else { true } @@ -199,8 +210,11 @@ impl MovementEventMapper { SfxEvent::Sneak } else { match underfoot_block_kind { - BlockKind::Snow => SfxEvent::SnowRun, - _ => SfxEvent::Run, + BlockKind::Snow => SfxEvent::Run(BlockKind::Snow), + BlockKind::Rock | BlockKind::WeakRock => SfxEvent::Run(BlockKind::Rock), + BlockKind::Sand => SfxEvent::Run(BlockKind::Sand), + BlockKind::Air => SfxEvent::Idle, + _ => SfxEvent::Run(BlockKind::Grass), } }; } @@ -230,8 +244,11 @@ impl MovementEventMapper { SfxEvent::Swim } else if physics_state.on_ground && vel.magnitude() > 0.1 { match underfoot_block_kind { - BlockKind::Snow => SfxEvent::SnowRun, - _ => SfxEvent::Run, + BlockKind::Snow => SfxEvent::Run(BlockKind::Snow), + BlockKind::Rock | BlockKind::WeakRock => SfxEvent::Run(BlockKind::Rock), + BlockKind::Sand => SfxEvent::Run(BlockKind::Sand), + BlockKind::Air => SfxEvent::Idle, + _ => SfxEvent::Run(BlockKind::Grass), } } else { SfxEvent::Idle @@ -248,8 +265,11 @@ impl MovementEventMapper { SfxEvent::Swim } else if physics_state.on_ground && vel.magnitude() > 0.1 { match underfoot_block_kind { - BlockKind::Snow => SfxEvent::QuadSnowRun, - _ => SfxEvent::QuadRun, + BlockKind::Snow => SfxEvent::QuadRun(BlockKind::Snow), + BlockKind::Rock | BlockKind::WeakRock => SfxEvent::QuadRun(BlockKind::Rock), + BlockKind::Sand => SfxEvent::QuadRun(BlockKind::Sand), + BlockKind::Air => SfxEvent::Idle, + _ => SfxEvent::QuadRun(BlockKind::Grass), } } else { SfxEvent::Idle diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index cbf49f2e4e..018deaaa0c 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -26,14 +26,17 @@ fn config_but_played_since_threshold_no_emit() { // Triggered a 'Run' 0 seconds ago let previous_state = PreviousEntityState { - event: SfxEvent::Run, + event: SfxEvent::Run(BlockKind::Grass), time: Instant::now(), on_ground: true, in_water: false, + distance_travelled: 0.0, }; - let result = - MovementEventMapper::should_emit(&previous_state, Some((&SfxEvent::Run, &trigger_item))); + let result = MovementEventMapper::should_emit( + &previous_state, + Some((&SfxEvent::Run(BlockKind::Grass), &trigger_item)), + ); assert_eq!(result, false); } @@ -50,10 +53,13 @@ fn config_and_not_played_since_threshold_emits() { time: Instant::now().checked_add(Duration::from_secs(1)).unwrap(), on_ground: true, in_water: false, + distance_travelled: 0.0, }; - let result = - MovementEventMapper::should_emit(&previous_state, Some((&SfxEvent::Run, &trigger_item))); + let result = MovementEventMapper::should_emit( + &previous_state, + Some((&SfxEvent::Run(BlockKind::Grass), &trigger_item)), + ); assert_eq!(result, true); } @@ -66,16 +72,19 @@ fn same_previous_event_elapsed_emits() { }; let previous_state = PreviousEntityState { - event: SfxEvent::Run, + event: SfxEvent::Run(BlockKind::Grass), time: Instant::now() - .checked_sub(Duration::from_millis(500)) + .checked_sub(Duration::from_millis(1800)) .unwrap(), on_ground: true, in_water: false, + distance_travelled: 2.0, }; - let result = - MovementEventMapper::should_emit(&previous_state, Some((&SfxEvent::Run, &trigger_item))); + let result = MovementEventMapper::should_emit( + &previous_state, + Some((&SfxEvent::Run(BlockKind::Grass), &trigger_item)), + ); assert_eq!(result, true); } @@ -93,6 +102,7 @@ fn maps_idle() { time: Instant::now(), on_ground: true, in_water: false, + distance_travelled: 0.0, }, Vec3::zero(), BlockKind::Grass, @@ -114,12 +124,13 @@ fn maps_run_with_sufficient_velocity() { time: Instant::now(), on_ground: true, in_water: false, + distance_travelled: 0.0, }, Vec3::new(0.5, 0.8, 0.0), BlockKind::Grass, ); - assert_eq!(result, SfxEvent::Run); + assert_eq!(result, SfxEvent::Run(BlockKind::Grass)); } #[test] @@ -135,6 +146,7 @@ fn does_not_map_run_with_insufficient_velocity() { time: Instant::now(), on_ground: true, in_water: false, + distance_travelled: 0.0, }, Vec3::new(0.02, 0.0001, 0.0), BlockKind::Grass, @@ -153,6 +165,7 @@ fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() { time: Instant::now(), on_ground: false, in_water: false, + distance_travelled: 0.0, }, Vec3::new(0.5, 0.8, 0.0), BlockKind::Grass, @@ -183,10 +196,11 @@ fn maps_roll() { ..Default::default() }, &PreviousEntityState { - event: SfxEvent::Run, + event: SfxEvent::Run(BlockKind::Grass), time: Instant::now(), on_ground: true, in_water: false, + distance_travelled: 0.0, }, Vec3::new(0.5, 0.5, 0.0), BlockKind::Grass, @@ -208,12 +222,13 @@ fn maps_land_on_ground_to_run() { time: Instant::now(), on_ground: false, in_water: false, + distance_travelled: 0.0, }, Vec3::zero(), BlockKind::Grass, ); - assert_eq!(result, SfxEvent::Run); + assert_eq!(result, SfxEvent::Run(BlockKind::Grass)); } #[test] @@ -226,6 +241,7 @@ fn maps_glider_open() { time: Instant::now(), on_ground: false, in_water: false, + distance_travelled: 0.0, }, Vec3::zero(), BlockKind::Grass, @@ -244,6 +260,7 @@ fn maps_glide() { time: Instant::now(), on_ground: false, in_water: false, + distance_travelled: 0.0, }, Vec3::zero(), BlockKind::Grass, @@ -262,6 +279,7 @@ fn maps_glider_close_when_closing_mid_flight() { time: Instant::now(), on_ground: false, in_water: false, + distance_travelled: 0.0, }, Vec3::zero(), BlockKind::Grass, @@ -284,6 +302,7 @@ fn maps_glider_close_when_landing() { time: Instant::now(), on_ground: false, in_water: false, + distance_travelled: 0.0, }, Vec3::zero(), BlockKind::Grass, @@ -303,7 +322,7 @@ fn maps_quadrupeds_running() { BlockKind::Grass, ); - assert_eq!(result, SfxEvent::Run); + assert_eq!(result, SfxEvent::Run(BlockKind::Grass)); } #[test] diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index d6e5ceb130..1c817b698b 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -22,7 +22,7 @@ //! The following snippet details some entries in the configuration and how they //! map to the sound files: //! ```ignore -//! Run: ( +//! Run(Grass): ( // depends on underfoot block //! files: [ //! "voxygen.audio.sfx.footsteps.stepgrass_1", //! "voxygen.audio.sfx.footsteps.stepgrass_2", @@ -31,13 +31,13 @@ //! "voxygen.audio.sfx.footsteps.stepgrass_5", //! "voxygen.audio.sfx.footsteps.stepgrass_6", //! ], -//! threshold: 0.25, // wait 0.25s between plays +//! threshold: 1.6, // travelled distance before next play //! ), //! Wield(Sword): ( // depends on the player's weapon //! files: [ //! "voxygen.audio.sfx.weapon.sword_out", //! ], -//! threshold: 0.5, +//! threshold: 0.5, // wait 0.5s between plays //! ), //! ... //! ``` @@ -95,7 +95,7 @@ use common::{ object, Body, CharacterAbilityType, InventoryUpdateEvent, }, outcome::Outcome, - terrain::TerrainChunk, + terrain::{BlockKind, TerrainChunk}, }; use common_sys::state::State; use event_mapper::SfxEventMapper; @@ -147,10 +147,8 @@ pub enum SfxEvent { RunningWater, Idle, Swim, - Run, - QuadRun, - SnowRun, - QuadSnowRun, + Run(BlockKind), + QuadRun(BlockKind), Roll, Sneak, Climb,