From 6a1cec886097215dbe4d9caa5b9b17e12bef48f1 Mon Sep 17 00:00:00 2001 From: Shane Handley Date: Mon, 8 Jun 2020 19:50:19 +1000 Subject: [PATCH] Docs, make adding event mappers easier for sfx, remove placeholder sounds. --- CHANGELOG.md | 1 + assets/voxygen/audio/sfx.ron | 58 --------- .../audio/sfx/weapon/bow_attack_01.wav | 3 - .../audio/sfx/weapon/bow_attack_02.wav | 3 - assets/voxygen/audio/sfx/weapon/sword_in.wav | 3 - assets/voxygen/audio/sfx/weapon/sword_out.wav | 3 - .../sfx/weapon/sword_triple_strike_01.wav | 3 - .../sfx/weapon/sword_triple_strike_02.wav | 3 - .../sfx/weapon/sword_triple_strike_03.wav | 3 - .../audio/sfx/weapon/whoosh_low_01.wav | 3 - .../audio/sfx/weapon/whoosh_low_02.wav | 3 - .../audio/sfx/weapon/whoosh_low_03.wav | 3 - .../audio/sfx/weapon/whoosh_normal_01.wav | 3 - .../audio/sfx/weapon/whoosh_normal_02.wav | 3 - .../audio/sfx/weapon/whoosh_normal_03.wav | 3 - .../audio/sfx/weapon/whoosh_normal_04.wav | 3 - .../src/audio/sfx/event_mapper/combat/mod.rs | 33 ++--- .../audio/sfx/event_mapper/combat/tests.rs | 48 ++++++- voxygen/src/audio/sfx/event_mapper/mod.rs | 22 ++-- .../audio/sfx/event_mapper/movement/mod.rs | 27 ++-- .../src/audio/sfx/event_mapper/progression.rs | 122 ------------------ .../audio/sfx/event_mapper/progression/mod.rs | 75 +++++++++++ .../sfx/event_mapper/progression/tests.rs | 43 ++++++ voxygen/src/audio/sfx/mod.rs | 85 +++++++++++- 24 files changed, 293 insertions(+), 263 deletions(-) delete mode 100644 assets/voxygen/audio/sfx/weapon/bow_attack_01.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/bow_attack_02.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/sword_in.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/sword_out.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/sword_triple_strike_01.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/sword_triple_strike_02.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/sword_triple_strike_03.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/whoosh_low_01.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/whoosh_low_02.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/whoosh_low_03.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/whoosh_normal_01.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/whoosh_normal_02.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/whoosh_normal_03.wav delete mode 100644 assets/voxygen/audio/sfx/weapon/whoosh_normal_04.wav delete mode 100644 voxygen/src/audio/sfx/event_mapper/progression.rs create mode 100644 voxygen/src/audio/sfx/event_mapper/progression/mod.rs create mode 100644 voxygen/src/audio/sfx/event_mapper/progression/tests.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 626d356555..6576a27406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added inventory, armour and weapon saving - Show where screenshots are saved to in the chat - Added basic auto walk +- Added weapon/attack sound effects ### Changed diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index a7512238f3..d890560890 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -23,64 +23,6 @@ ], threshold: 0.5, ), - Attack(DashMelee, Sword): ( - files: [ - "voxygen.audio.sfx.weapon.whoosh_normal_01", - "voxygen.audio.sfx.weapon.whoosh_normal_02", - "voxygen.audio.sfx.weapon.whoosh_normal_03", - "voxygen.audio.sfx.weapon.whoosh_normal_04", - ], - threshold: 1.2, - ), - Attack(TripleStrike(First), Sword): ( - files: [ - "voxygen.audio.sfx.weapon.whoosh_normal_01", - "voxygen.audio.sfx.weapon.whoosh_normal_02", - "voxygen.audio.sfx.weapon.whoosh_normal_03", - "voxygen.audio.sfx.weapon.whoosh_normal_04", - ], - threshold: 0.5, - ), - Attack(TripleStrike(Second), Sword): ( - files: [ - "voxygen.audio.sfx.weapon.whoosh_low_01", - "voxygen.audio.sfx.weapon.whoosh_low_02", - "voxygen.audio.sfx.weapon.whoosh_low_03", - ], - threshold: 0.5, - ), - Attack(TripleStrike(Third), Sword): ( - files: [ - "voxygen.audio.sfx.weapon.whoosh_low_01", - "voxygen.audio.sfx.weapon.whoosh_low_02", - "voxygen.audio.sfx.weapon.whoosh_low_03", - ], - threshold: 0.5, - ), - Attack(BasicRanged, Bow): ( - files: [ - "voxygen.audio.sfx.weapon.bow_attack_01", - "voxygen.audio.sfx.weapon.bow_attack_02", - ], - threshold: 0.5, - ), - Attack(BasicMelee, Hammer): ( - files: [ - "voxygen.audio.sfx.weapon.whoosh_low_01", - "voxygen.audio.sfx.weapon.whoosh_low_02", - "voxygen.audio.sfx.weapon.whoosh_low_03", - ], - threshold: 0.5, - ), - Attack(BasicMelee, Staff): ( - files: [ - "voxygen.audio.sfx.weapon.whoosh_normal_01", - "voxygen.audio.sfx.weapon.whoosh_normal_02", - "voxygen.audio.sfx.weapon.whoosh_normal_03", - "voxygen.audio.sfx.weapon.whoosh_normal_04", - ], - threshold: 0.5, - ), Wield(Sword): ( files: [ "voxygen.audio.sfx.weapon.sword_out", diff --git a/assets/voxygen/audio/sfx/weapon/bow_attack_01.wav b/assets/voxygen/audio/sfx/weapon/bow_attack_01.wav deleted file mode 100644 index e303bbd863..0000000000 --- a/assets/voxygen/audio/sfx/weapon/bow_attack_01.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:609b2283b1f30939e59a6aceba59d9cae371b290e0d02a4986da9b5bcd843b25 -size 38562 diff --git a/assets/voxygen/audio/sfx/weapon/bow_attack_02.wav b/assets/voxygen/audio/sfx/weapon/bow_attack_02.wav deleted file mode 100644 index 90a44bed8e..0000000000 --- a/assets/voxygen/audio/sfx/weapon/bow_attack_02.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d035b886fe6a3357c030624dd1c2501e6052c9aa3186c4a971df2caa4a45a55e -size 29878 diff --git a/assets/voxygen/audio/sfx/weapon/sword_in.wav b/assets/voxygen/audio/sfx/weapon/sword_in.wav deleted file mode 100644 index fd4d2361f2..0000000000 --- a/assets/voxygen/audio/sfx/weapon/sword_in.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14de6bf4651f2cdd2fc515b7b90d0cb150b33fe023cc4216b13711cc748b7f55 -size 47504 diff --git a/assets/voxygen/audio/sfx/weapon/sword_out.wav b/assets/voxygen/audio/sfx/weapon/sword_out.wav deleted file mode 100644 index 896a8340db..0000000000 --- a/assets/voxygen/audio/sfx/weapon/sword_out.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:452022ad876c911ed2cd927b6d21bde668c1e0d416603a77e453179efe5d8dea -size 37444 diff --git a/assets/voxygen/audio/sfx/weapon/sword_triple_strike_01.wav b/assets/voxygen/audio/sfx/weapon/sword_triple_strike_01.wav deleted file mode 100644 index a4cb50683a..0000000000 --- a/assets/voxygen/audio/sfx/weapon/sword_triple_strike_01.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ffaf3658efef24c496ad46586e478357c02766a911d797b1bca8de60b0e31d19 -size 57188 diff --git a/assets/voxygen/audio/sfx/weapon/sword_triple_strike_02.wav b/assets/voxygen/audio/sfx/weapon/sword_triple_strike_02.wav deleted file mode 100644 index 9fd15054e8..0000000000 --- a/assets/voxygen/audio/sfx/weapon/sword_triple_strike_02.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7cc36ec20064a7a37508ecf6428c6b3e2e8a75226667f6799002b2cb0a433e5a -size 53336 diff --git a/assets/voxygen/audio/sfx/weapon/sword_triple_strike_03.wav b/assets/voxygen/audio/sfx/weapon/sword_triple_strike_03.wav deleted file mode 100644 index c0a092eaf6..0000000000 --- a/assets/voxygen/audio/sfx/weapon/sword_triple_strike_03.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e6a85b277160c90534c1300fb31c86669c746fa37f6c616aa388cd5e0fbeafac -size 56104 diff --git a/assets/voxygen/audio/sfx/weapon/whoosh_low_01.wav b/assets/voxygen/audio/sfx/weapon/whoosh_low_01.wav deleted file mode 100644 index a66aed52ba..0000000000 --- a/assets/voxygen/audio/sfx/weapon/whoosh_low_01.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:727522d7d246ff1213a3aa09086805c6d6d42e3bbeb9d4267a8b79d058566832 -size 14808 diff --git a/assets/voxygen/audio/sfx/weapon/whoosh_low_02.wav b/assets/voxygen/audio/sfx/weapon/whoosh_low_02.wav deleted file mode 100644 index 2b06630951..0000000000 --- a/assets/voxygen/audio/sfx/weapon/whoosh_low_02.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ee6431f2ce33762dc64d91822921e93ade2ea80671abf6f682144f0ea643a0a -size 17576 diff --git a/assets/voxygen/audio/sfx/weapon/whoosh_low_03.wav b/assets/voxygen/audio/sfx/weapon/whoosh_low_03.wav deleted file mode 100644 index 09bd5b8cce..0000000000 --- a/assets/voxygen/audio/sfx/weapon/whoosh_low_03.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:90a44f9a827d30cbdfb2fb0707a17df705b73ba4d80fcb1bf91b8d2141538891 -size 13272 diff --git a/assets/voxygen/audio/sfx/weapon/whoosh_normal_01.wav b/assets/voxygen/audio/sfx/weapon/whoosh_normal_01.wav deleted file mode 100644 index 863d8ab79a..0000000000 --- a/assets/voxygen/audio/sfx/weapon/whoosh_normal_01.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f53a2bcd4f17b14fe3c0b2fa5cde63fc0b607503bd5961faa3ac52f7210a7ec -size 17688 diff --git a/assets/voxygen/audio/sfx/weapon/whoosh_normal_02.wav b/assets/voxygen/audio/sfx/weapon/whoosh_normal_02.wav deleted file mode 100644 index ef29dde874..0000000000 --- a/assets/voxygen/audio/sfx/weapon/whoosh_normal_02.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ced9097c2189bc39353578bb723ababad018b473443d793b2581e39ccc9c7526 -size 18174 diff --git a/assets/voxygen/audio/sfx/weapon/whoosh_normal_03.wav b/assets/voxygen/audio/sfx/weapon/whoosh_normal_03.wav deleted file mode 100644 index 551e1e71a8..0000000000 --- a/assets/voxygen/audio/sfx/weapon/whoosh_normal_03.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98e707c0617d68ec45ab43ce33596d811fe82d659d6ad72ebfe436ffabac8bac -size 23066 diff --git a/assets/voxygen/audio/sfx/weapon/whoosh_normal_04.wav b/assets/voxygen/audio/sfx/weapon/whoosh_normal_04.wav deleted file mode 100644 index f0f8096830..0000000000 --- a/assets/voxygen/audio/sfx/weapon/whoosh_normal_04.wav +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4e8bbff7e7c008f94dbef91b39d1aab9de491035aed40bde0785d95237b542e -size 20396 diff --git a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs index 41ab25072a..75baa6d180 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs @@ -1,7 +1,9 @@ -/// event_mapper::combat watches the combat state of entities and emits -/// associated sfx events +/// EventMapper::Combat watches the combat states of surrounding entities' and +/// emits sfx related to weapons and attacks/abilities use crate::audio::sfx::{SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR}; +use super::EventMapper; + use common::{ comp::{ item::{Item, ItemKind, ToolCategory}, @@ -36,14 +38,8 @@ pub struct CombatEventMapper { event_history: HashMap, } -impl CombatEventMapper { - pub fn new() -> Self { - Self { - event_history: HashMap::new(), - } - } - - pub fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) { +impl EventMapper for CombatEventMapper { + fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) { let ecs = state.ecs(); let sfx_event_bus = ecs.read_resource::>(); @@ -89,6 +85,14 @@ impl CombatEventMapper { self.cleanup(player_entity); } +} + +impl CombatEventMapper { + pub fn new() -> Self { + Self { + event_history: HashMap::new(), + } + } /// As the player explores the world, we track the last event of the nearby /// entities to determine the correct SFX item to play next based on @@ -105,11 +109,10 @@ impl CombatEventMapper { }); } - /// When specific entity movements are detected, the associated sound (if - /// 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 + /// Ensures that: + /// 1. An sfx.ron entry exists for an SFX event + /// 2. The sfx has not been played since it's timeout threshold has elapsed, + /// which prevents firing every tick fn should_emit( previous_state: &PreviousEntityState, sfx_trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index 67b3184fd2..baaa66d8ad 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -98,7 +98,7 @@ fn maps_basic_melee() { } #[test] -fn maps_triple_strike() { +fn matches_ability_stage() { let mut loadout = Loadout::default(); loadout.active_item = Some(ItemConfig { @@ -131,6 +131,50 @@ fn maps_triple_strike() { assert_eq!( result, - SfxEvent::Attack(CharacterAbilityType::TripleStrike, ToolCategory::Sword) + SfxEvent::Attack( + CharacterAbilityType::TripleStrike(states::triple_strike::Stage::First), + ToolCategory::Sword + ) + ); +} + +#[test] +fn ignores_different_ability_stage() { + let mut loadout = Loadout::default(); + + loadout.active_item = Some(ItemConfig { + item: assets::load_expect_cloned("common.items.weapons.sword.starter_sword"), + ability1: None, + ability2: None, + ability3: None, + block_ability: None, + dodge_ability: None, + }); + + let result = CombatEventMapper::map_event( + &CharacterState::TripleStrike(states::triple_strike::Data { + base_damage: 10, + stage: states::triple_strike::Stage::Second, + stage_time_active: Duration::default(), + stage_exhausted: false, + initialized: true, + transition_style: states::triple_strike::TransitionStyle::Hold( + states::triple_strike::HoldingState::Released, + ), + }), + &PreviousEntityState { + event: SfxEvent::Idle, + time: Instant::now(), + weapon_drawn: true, + }, + Some(&loadout), + ); + + assert_ne!( + result, + SfxEvent::Attack( + CharacterAbilityType::TripleStrike(states::triple_strike::Stage::First), + ToolCategory::Sword + ) ); } diff --git a/voxygen/src/audio/sfx/event_mapper/mod.rs b/voxygen/src/audio/sfx/event_mapper/mod.rs index 7b6eea95c0..1628185479 100644 --- a/voxygen/src/audio/sfx/event_mapper/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/mod.rs @@ -10,18 +10,22 @@ use progression::ProgressionEventMapper; use super::SfxTriggers; +trait EventMapper { + fn maintain(&mut self, state: &State, player_entity: specs::Entity, triggers: &SfxTriggers); +} + pub struct SfxEventMapper { - progression: ProgressionEventMapper, - movement: MovementEventMapper, - combat: CombatEventMapper, + mappers: Vec>, } impl SfxEventMapper { pub fn new() -> Self { Self { - progression: ProgressionEventMapper::new(), - combat: CombatEventMapper::new(), - movement: MovementEventMapper::new(), + mappers: vec![ + Box::new(CombatEventMapper::new()), + Box::new(MovementEventMapper::new()), + Box::new(ProgressionEventMapper::new()), + ], } } @@ -31,8 +35,8 @@ impl SfxEventMapper { player_entity: specs::Entity, triggers: &SfxTriggers, ) { - self.progression.maintain(state, player_entity, triggers); - self.movement.maintain(state, player_entity, triggers); - self.combat.maintain(state, player_entity, triggers); + for mapper in &mut self.mappers { + mapper.maintain(state, player_entity, triggers); + } } } diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index 6ad6bfb401..6a0b184286 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -1,8 +1,9 @@ -/// event_mapper::movement watches all local entities movements and determines -/// which sfx to emit, and the position at which the sound should be emitted -/// from -use crate::audio::sfx::{SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR}; +/// EventMapper::Movement watches the movement states of surrounding entities, +/// and triggers sfx related to running, climbing and gliding, at a volume +/// proportionate to the extity's size +use super::EventMapper; +use crate::audio::sfx::{SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR}; use common::{ comp::{Body, CharacterState, PhysicsState, Pos, Vel}, event::{EventBus, SfxEvent, SfxEventItem}, @@ -34,14 +35,8 @@ pub struct MovementEventMapper { event_history: HashMap, } -impl MovementEventMapper { - pub fn new() -> Self { - Self { - event_history: HashMap::new(), - } - } - - pub fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) { +impl EventMapper for MovementEventMapper { + fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) { let ecs = state.ecs(); let sfx_event_bus = ecs.read_resource::>(); @@ -101,6 +96,14 @@ impl MovementEventMapper { self.cleanup(player_entity); } +} + +impl MovementEventMapper { + pub fn new() -> Self { + Self { + event_history: HashMap::new(), + } + } /// As the player explores the world, we track the last event of the nearby /// entities to determine the correct SFX item to play next based on diff --git a/voxygen/src/audio/sfx/event_mapper/progression.rs b/voxygen/src/audio/sfx/event_mapper/progression.rs deleted file mode 100644 index 01ab1cc82c..0000000000 --- a/voxygen/src/audio/sfx/event_mapper/progression.rs +++ /dev/null @@ -1,122 +0,0 @@ -/// event_mapper::progression watches the the current player's level -/// and experience and emits associated SFX -use crate::audio::sfx::SfxTriggers; - -use common::{ - comp::Stats, - event::{EventBus, SfxEvent, SfxEventItem}, - state::State, -}; -use specs::WorldExt; - -#[derive(Clone, PartialEq)] -struct ProgressionState { - level: u32, - exp: u32, -} - -impl Default for ProgressionState { - fn default() -> Self { Self { level: 1, exp: 0 } } -} - -pub struct ProgressionEventMapper { - state: ProgressionState, -} - -impl ProgressionEventMapper { - pub fn new() -> Self { - Self { - state: ProgressionState::default(), - } - } - - pub fn maintain( - &mut self, - state: &State, - player_entity: specs::Entity, - triggers: &SfxTriggers, - ) { - let ecs = state.ecs(); - - // level and exp changes - let next_state = - ecs.read_storage::() - .get(player_entity) - .map_or(self.state.clone(), |stats| ProgressionState { - level: stats.level.level(), - exp: stats.exp.current(), - }); - - if &self.state != &next_state { - if let Some(mapped_event) = self.map_event(&next_state) { - let sfx_trigger_item = triggers.get_trigger(&mapped_event); - - if sfx_trigger_item.is_some() { - ecs.read_resource::>() - .emit_now(SfxEventItem::at_player_position(mapped_event)); - } - } - - self.state = next_state; - } - } - - fn map_event(&mut self, next_state: &ProgressionState) -> Option { - let sfx_event = if next_state.level > self.state.level { - Some(SfxEvent::LevelUp) - } else if next_state.exp > self.state.exp { - Some(SfxEvent::ExperienceGained) - } else { - None - }; - - sfx_event - } -} - -#[cfg(test)] -mod tests { - use super::*; - use common::event::SfxEvent; - - #[test] - fn no_change_returns_none() { - let mut mapper = ProgressionEventMapper::new(); - let next_client_state = ProgressionState::default(); - - assert_eq!(mapper.map_event(&next_client_state), None); - } - - #[test] - fn change_level_returns_levelup() { - let mut mapper = ProgressionEventMapper::new(); - let next_client_state = ProgressionState { level: 2, exp: 0 }; - - assert_eq!( - mapper.map_event(&next_client_state), - Some(SfxEvent::LevelUp) - ); - } - - #[test] - fn change_exp_returns_expup() { - let mut mapper = ProgressionEventMapper::new(); - let next_client_state = ProgressionState { level: 1, exp: 100 }; - - assert_eq!( - mapper.map_event(&next_client_state), - Some(SfxEvent::ExperienceGained) - ); - } - - #[test] - fn level_up_and_gained_exp_prioritises_levelup() { - let mut mapper = ProgressionEventMapper::new(); - let next_client_state = ProgressionState { level: 2, exp: 100 }; - - assert_eq!( - mapper.map_event(&next_client_state), - Some(SfxEvent::LevelUp) - ); - } -} diff --git a/voxygen/src/audio/sfx/event_mapper/progression/mod.rs b/voxygen/src/audio/sfx/event_mapper/progression/mod.rs new file mode 100644 index 0000000000..e46b49bceb --- /dev/null +++ b/voxygen/src/audio/sfx/event_mapper/progression/mod.rs @@ -0,0 +1,75 @@ +/// EventMapper::Progress watches the player entity's stats +/// and triggers sfx for gaining experience and levelling up +use super::EventMapper; + +use crate::audio::sfx::SfxTriggers; + +use common::{ + comp::Stats, + event::{EventBus, SfxEvent, SfxEventItem}, + state::State, +}; +use specs::WorldExt; + +#[derive(Clone, PartialEq)] +struct ProgressionState { + level: u32, + exp: u32, +} + +impl Default for ProgressionState { + fn default() -> Self { Self { level: 1, exp: 0 } } +} + +pub struct ProgressionEventMapper { + state: ProgressionState, +} + +impl EventMapper for ProgressionEventMapper { + fn maintain(&mut self, state: &State, player_entity: specs::Entity, triggers: &SfxTriggers) { + let ecs = state.ecs(); + + let next_state = ecs.read_storage::().get(player_entity).map_or( + ProgressionState::default(), + |stats| ProgressionState { + level: stats.level.level(), + exp: stats.exp.current(), + }, + ); + + if &self.state != &next_state { + if let Some(mapped_event) = self.map_event(&next_state) { + let sfx_trigger_item = triggers.get_trigger(&mapped_event); + + if sfx_trigger_item.is_some() { + ecs.read_resource::>() + .emit_now(SfxEventItem::at_player_position(mapped_event)); + } + } + + self.state = next_state; + } + } +} + +impl ProgressionEventMapper { + pub fn new() -> Self { + Self { + state: ProgressionState::default(), + } + } + + fn map_event(&mut self, next_state: &ProgressionState) -> Option { + let sfx_event = if next_state.level > self.state.level { + Some(SfxEvent::LevelUp) + } else if next_state.exp > self.state.exp { + Some(SfxEvent::ExperienceGained) + } else { + None + }; + + sfx_event + } +} + +#[cfg(test)] mod tests; diff --git a/voxygen/src/audio/sfx/event_mapper/progression/tests.rs b/voxygen/src/audio/sfx/event_mapper/progression/tests.rs new file mode 100644 index 0000000000..b56dcf2bc2 --- /dev/null +++ b/voxygen/src/audio/sfx/event_mapper/progression/tests.rs @@ -0,0 +1,43 @@ +use super::*; +use common::event::SfxEvent; + +#[test] +fn no_change_returns_none() { + let mut mapper = ProgressionEventMapper::new(); + let next_client_state = ProgressionState::default(); + + assert_eq!(mapper.map_event(&next_client_state), None); +} + +#[test] +fn change_level_returns_levelup() { + let mut mapper = ProgressionEventMapper::new(); + let next_client_state = ProgressionState { level: 2, exp: 0 }; + + assert_eq!( + mapper.map_event(&next_client_state), + Some(SfxEvent::LevelUp) + ); +} + +#[test] +fn change_exp_returns_expup() { + let mut mapper = ProgressionEventMapper::new(); + let next_client_state = ProgressionState { level: 1, exp: 100 }; + + assert_eq!( + mapper.map_event(&next_client_state), + Some(SfxEvent::ExperienceGained) + ); +} + +#[test] +fn level_up_and_gained_exp_prioritises_levelup() { + let mut mapper = ProgressionEventMapper::new(); + let next_client_state = ProgressionState { level: 2, exp: 100 }; + + assert_eq!( + mapper.map_event(&next_client_state), + Some(SfxEvent::LevelUp) + ); +} diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 951fb72a81..1e6b4f6a1b 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -1,6 +1,85 @@ -/// The Sfx Manager manages individual sfx event system, listens for -/// SFX events and plays the sound at the requested position, or the current -/// player position +//! Manages individual sfx event system, listens for sfx events, and requests +//! playback at the requested position and volume +//! +//! Veloren's sfx are managed through a configuration which lives in the +//! codebase under `/assets/voxygen/audio/sfx.ron`. +//! +//! Each entry in the configuration consists of an +//! [SfxEvent](../../../veloren_common/event/enum.SfxEvent.html) item, with some +//! additional information to allow playback: +//! - `files` - the paths to the `.wav` files to be played for the sfx. minus +//! the file extension. This can be a single item if the same sound can be +//! played each time, or a list of files from which one is chosen at random to +//! be played. +//! - `threshold` - the time that the system should wait between successive +//! plays. +//! +//! The following snippet details some entries in the configuration and how they +//! map to the sound files: +//! ```ignore +//! Run: ( +//! files: [ +//! "voxygen.audio.sfx.footsteps.stepgrass_1", +//! "voxygen.audio.sfx.footsteps.stepgrass_2", +//! "voxygen.audio.sfx.footsteps.stepgrass_3", +//! "voxygen.audio.sfx.footsteps.stepgrass_4", +//! "voxygen.audio.sfx.footsteps.stepgrass_5", +//! "voxygen.audio.sfx.footsteps.stepgrass_6", +//! ], +//! threshold: 0.25, // wait 0.25s between plays +//! ), +//! Wield(Sword): ( // depends on the player's weapon +//! files: [ +//! "voxygen.audio.sfx.weapon.sword_out", +//! ], +//! threshold: 0.5, +//! ), +//! ... +//! ``` +//! +//! These items (for example, the `Wield(Sword)` occasionally depend on some +//! property which varies in game. The +//! [SfxEvent](../../../veloren_common/event/enum.SfxEvent.html) documentation +//! provides links to those variables, some examples are provided her for longer +//! items: +//! +//! ```ignore +//! // An inventory action +//! Inventory(Dropped): ( +//! files: [ +//! "voxygen.audio.sfx.footsteps.stepgrass_4", +//! ], +//! threshold: 0.5, +//! ), +//! // An inventory action which depends upon the item +//! Inventory(Consumed(Apple)): ( +//! files: [ +//! "voxygen.audio.sfx.inventory.consumable.apple", +//! ], +//! threshold: 0.5 +//! ), +//! // An attack ability which depends on the weapon +//! Attack(DashMelee, Sword): ( +//! files: [ +//! "voxygen.audio.sfx.weapon.whoosh_normal_01", +//! "voxygen.audio.sfx.weapon.whoosh_normal_02", +//! "voxygen.audio.sfx.weapon.whoosh_normal_03", +//! "voxygen.audio.sfx.weapon.whoosh_normal_04", +//! ], +//! threshold: 1.2, +//! ), +//! // A multi-stage attack ability which depends on the weapon +//! Attack(TripleStrike(First), Sword): ( +//! files: [ +//! "voxygen.audio.sfx.weapon.whoosh_normal_01", +//! "voxygen.audio.sfx.weapon.whoosh_normal_02", +//! "voxygen.audio.sfx.weapon.whoosh_normal_03", +//! "voxygen.audio.sfx.weapon.whoosh_normal_04", +//! ], +//! threshold: 0.5, +//! ), +//! ``` + mod event_mapper; use crate::audio::AudioFrontend;