mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fully move sfx away from ECS event bus
This commit is contained in:
parent
b3aa454f8e
commit
3b47add55a
@ -181,24 +181,24 @@
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
//Attack(ComboMelee(Swing, 1), Sword): (
|
||||
// files: [
|
||||
// "voxygen.audio.sfx.abilities.swing_sword",
|
||||
// ],
|
||||
// threshold: 0.7,
|
||||
//),
|
||||
//Attack(ComboMelee(Swing, 2), Sword): (
|
||||
// files: [
|
||||
// "voxygen.audio.sfx.abilities.separated_second_swing",
|
||||
// ],
|
||||
// threshold: 0.7,
|
||||
//),
|
||||
//Attack(ComboMelee(Swing, 3), Sword): (
|
||||
// files: [
|
||||
// "voxygen.audio.sfx.abilities.separated_third_swing",
|
||||
// ],
|
||||
// threshold: 0.7,
|
||||
//),
|
||||
Attack(ComboMelee(Swing, 1), Sword): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.swing_sword",
|
||||
],
|
||||
threshold: 0.7,
|
||||
),
|
||||
Attack(ComboMelee(Swing, 2), Sword): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.separated_second_swing",
|
||||
],
|
||||
threshold: 0.7,
|
||||
),
|
||||
Attack(ComboMelee(Swing, 3), Sword): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.separated_third_swing",
|
||||
],
|
||||
threshold: 0.7,
|
||||
),
|
||||
Attack(DashMelee(Swing), Sword): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.sword_dash",
|
||||
@ -319,12 +319,12 @@
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
//Attack(BasicBeam, Staff): (
|
||||
// files: [
|
||||
// "voxygen.audio.sfx.abilities.flame_thrower",
|
||||
// ],
|
||||
// threshold: 0.2,
|
||||
//),
|
||||
Attack(BasicBeam, Staff): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.flame_thrower",
|
||||
],
|
||||
threshold: 0.2,
|
||||
),
|
||||
Attack(BasicRanged, Staff): (
|
||||
files: [
|
||||
// "voxygen.audio.sfx.abilities.staff_channeling",
|
||||
@ -353,12 +353,12 @@
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Attack(BasicRanged, Bow): (
|
||||
files: [
|
||||
// channeling sound.
|
||||
],
|
||||
threshold: 0.8,
|
||||
),
|
||||
//Attack(BasicRanged, Bow): (
|
||||
// files: [
|
||||
// // channeling sound.
|
||||
// ],
|
||||
// threshold: 0.8,
|
||||
//),
|
||||
Inventory(CollectedTool(Bow)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_item",
|
||||
@ -381,12 +381,6 @@
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
//Attack(BasicBeam, Sceptre): (
|
||||
// files: [
|
||||
// "voxygen.audio.sfx.abilities.staff_channeling",
|
||||
// ],
|
||||
// threshold: 0.6,
|
||||
//),
|
||||
|
||||
//
|
||||
// Dagger
|
||||
|
@ -858,15 +858,11 @@ impl Client {
|
||||
self.state.terrain().get_key_arc(chunk_pos).cloned()
|
||||
}
|
||||
|
||||
pub fn current<C: Component>(&self) -> Option<C> where
|
||||
C: Clone,
|
||||
{
|
||||
Some(
|
||||
self.state
|
||||
.read_storage::<C>()
|
||||
.get(self.entity)
|
||||
.cloned()?,
|
||||
)
|
||||
pub fn current<C: Component>(&self) -> Option<C>
|
||||
where
|
||||
C: Clone,
|
||||
{
|
||||
Some(self.state.read_storage::<C>().get(self.entity).cloned()?)
|
||||
}
|
||||
|
||||
pub fn current_biome(&self) -> BiomeKind {
|
||||
|
@ -9,8 +9,6 @@ use std::{collections::VecDeque, ops::DerefMut};
|
||||
use vek::*;
|
||||
|
||||
pub enum LocalEvent {
|
||||
/// An attack for use with particles and sfx
|
||||
Attack(EcsEntity),
|
||||
/// Applies upward force to entity's `Vel`
|
||||
Jump(EcsEntity),
|
||||
/// Applies the `impulse` to `entity`'s `Vel`
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::comp;
|
||||
use comp::{item::Reagent, CharacterState, Loadout};
|
||||
use comp::item::Reagent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::*;
|
||||
|
||||
@ -22,11 +22,6 @@ pub enum Outcome {
|
||||
body: comp::Body,
|
||||
vel: Vec3<f32>,
|
||||
},
|
||||
Attack {
|
||||
pos: Vec3<f32>,
|
||||
character_state: CharacterState,
|
||||
loadout: Loadout,
|
||||
},
|
||||
LevelUp {
|
||||
pos: Vec3<f32>,
|
||||
},
|
||||
@ -41,7 +36,6 @@ impl Outcome {
|
||||
match self {
|
||||
Outcome::Explosion { pos, .. } => Some(*pos),
|
||||
Outcome::ProjectileShot { pos, .. } => Some(*pos),
|
||||
Outcome::Attack { pos, .. } => Some(*pos),
|
||||
Outcome::LevelUp { pos } => Some(*pos),
|
||||
Outcome::Beam { pos, .. } => Some(*pos),
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use crate::{
|
||||
comp,
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
metrics::{PhysicsMetrics, SysMetrics},
|
||||
outcome::Outcome,
|
||||
region::RegionMap,
|
||||
sync::WorldSyncExt,
|
||||
sys,
|
||||
@ -188,9 +187,6 @@ impl State {
|
||||
ecs.insert(SysMetrics::default());
|
||||
ecs.insert(PhysicsMetrics::default());
|
||||
|
||||
// Register outcomes
|
||||
ecs.insert(Vec::<Outcome>::new());
|
||||
|
||||
ecs
|
||||
}
|
||||
|
||||
@ -392,28 +388,7 @@ impl State {
|
||||
for event in events {
|
||||
let mut velocities = self.ecs.write_storage::<comp::Vel>();
|
||||
let mut controllers = self.ecs.write_storage::<comp::Controller>();
|
||||
let positions = self.ecs.read_storage::<comp::Pos>();
|
||||
let character_states = self.ecs.read_storage::<comp::CharacterState>();
|
||||
let loadouts = self.ecs.read_storage::<comp::Loadout>();
|
||||
match event {
|
||||
LocalEvent::Attack(entity) => {
|
||||
self.ecs
|
||||
.write_resource::<Vec<Outcome>>()
|
||||
.push(Outcome::Attack {
|
||||
pos: positions
|
||||
.get(entity)
|
||||
.expect("Failed to fetch attacking entity")
|
||||
.0,
|
||||
character_state: character_states
|
||||
.get(entity)
|
||||
.expect("Failed to get the character state of the attacking entity")
|
||||
.clone(),
|
||||
loadout: loadouts
|
||||
.get(entity)
|
||||
.expect("Failed to get attacking entity's loadout")
|
||||
.clone(),
|
||||
});
|
||||
},
|
||||
LocalEvent::Jump(entity) => {
|
||||
if let Some(vel) = velocities.get_mut(entity) {
|
||||
vel.0.z = HUMANOID_JUMP_ACCEL;
|
||||
|
@ -56,7 +56,7 @@ impl<'a> System<'a> for Sys {
|
||||
let start_time = std::time::Instant::now();
|
||||
span!(_guard, "run", "melee::Sys::run");
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let mut local_emitter = local_bus.emitter();
|
||||
let _local_emitter = local_bus.emitter();
|
||||
// Attacks
|
||||
for (entity, uid, pos, ori, scale_maybe, attack) in (
|
||||
&entities,
|
||||
@ -72,7 +72,6 @@ impl<'a> System<'a> for Sys {
|
||||
continue;
|
||||
}
|
||||
attack.applied = true;
|
||||
local_emitter.emit(LocalEvent::Attack(entity));
|
||||
|
||||
// Go through all other entities
|
||||
for (b, pos_b, scale_b_maybe, health_b, body_b, char_state_b_maybe) in (
|
||||
|
@ -9,9 +9,10 @@ pub mod soundcache;
|
||||
|
||||
use channel::{AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel};
|
||||
use fader::Fader;
|
||||
use sfx::{SfxEvent, SfxTriggerItem};
|
||||
use soundcache::SoundCache;
|
||||
use std::time::Duration;
|
||||
//use tracing::warn;
|
||||
use tracing::debug;
|
||||
|
||||
use common::assets;
|
||||
use rodio::{source::Source, Decoder, OutputStream, OutputStreamHandle, StreamError};
|
||||
@ -162,6 +163,71 @@ impl AudioFrontend {
|
||||
self.music_channels.last_mut()
|
||||
}
|
||||
|
||||
/// Function to play sfx from external places. Useful for UI and
|
||||
/// inventory events
|
||||
pub fn emit_sfx_item(&mut self, trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>) {
|
||||
if let Some((event, item)) = trigger_item {
|
||||
let sfx_file = match item.files.len() {
|
||||
0 => {
|
||||
debug!("Sfx event {:?} is missing audio file.", event);
|
||||
"voxygen.audio.sfx.placeholder"
|
||||
},
|
||||
1 => item
|
||||
.files
|
||||
.last()
|
||||
.expect("Failed to determine sound file for this trigger item."),
|
||||
_ => {
|
||||
// If more than one file is listed, choose one at random
|
||||
let rand_step = rand::random::<usize>() % item.files.len();
|
||||
&item.files[rand_step]
|
||||
},
|
||||
};
|
||||
|
||||
self.play_sfx(sfx_file, self.listener.pos, None);
|
||||
} else {
|
||||
debug!("Missing sfx trigger config for external sfx event.",);
|
||||
}
|
||||
}
|
||||
|
||||
/// Play an sfx file given the position, SfxEvent, and whether it is
|
||||
/// underwater or not
|
||||
pub fn emit_sfx(
|
||||
&mut self,
|
||||
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
|
||||
position: Vec3<f32>,
|
||||
volume: Option<f32>,
|
||||
underwater: bool,
|
||||
) {
|
||||
if let Some((event, item)) = trigger_item {
|
||||
let sfx_file = match item.files.len() {
|
||||
0 => {
|
||||
debug!("Sfx event {:?} is missing audio file.", event);
|
||||
"voxygen.audio.sfx.placeholder"
|
||||
},
|
||||
1 => item
|
||||
.files
|
||||
.last()
|
||||
.expect("Failed to determine sound file for this trigger item."),
|
||||
_ => {
|
||||
// If more than one file is listed, choose one at random
|
||||
let rand_step = rand::random::<usize>() % item.files.len();
|
||||
&item.files[rand_step]
|
||||
},
|
||||
};
|
||||
|
||||
if underwater {
|
||||
self.play_underwater_sfx(sfx_file, position, volume);
|
||||
} else {
|
||||
self.play_sfx(sfx_file, position, volume);
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
"Missing sfx trigger config for sfx event at position: {:?}",
|
||||
position
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Play (once) an sfx file by file path at the give position and volume
|
||||
pub fn play_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
||||
if self.audio_stream.is_some() {
|
||||
|
@ -1,19 +1,22 @@
|
||||
/// EventMapper::Block watches the sound emitting blocks within
|
||||
/// chunk range of the player and emits ambient sfx
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
audio::sfx::{SfxEvent, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
scene::{terrain::BlocksOfInterest, Camera, Terrain},
|
||||
AudioFrontend,
|
||||
};
|
||||
|
||||
use super::EventMapper;
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::Pos, event::EventBus, spiral::Spiral2d, state::State, terrain::TerrainChunk,
|
||||
vol::RectRasterableVol,
|
||||
comp::Pos,
|
||||
spiral::Spiral2d,
|
||||
state::State,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
vol::{ReadVol, RectRasterableVol},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use rand::{thread_rng, Rng};
|
||||
use specs::WorldExt;
|
||||
use std::time::Instant;
|
||||
use vek::*;
|
||||
|
||||
@ -39,6 +42,7 @@ pub struct BlockEventMapper {
|
||||
impl EventMapper for BlockEventMapper {
|
||||
fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
@ -46,11 +50,6 @@ impl EventMapper for BlockEventMapper {
|
||||
terrain: &Terrain<TerrainChunk>,
|
||||
client: &Client,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
let sfx_event_bus = ecs.read_resource::<EventBus<SfxEventItem>>();
|
||||
let mut sfx_emitter = sfx_event_bus.emitter();
|
||||
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||
|
||||
@ -175,21 +174,29 @@ impl EventMapper for BlockEventMapper {
|
||||
continue;
|
||||
}
|
||||
let block_pos: Vec3<i32> = absolute_pos + block;
|
||||
let state = self.history.entry(block_pos).or_default();
|
||||
let internal_state = self.history.entry(block_pos).or_default();
|
||||
|
||||
let block_pos = block_pos.map(|x| x as f32);
|
||||
|
||||
if Self::should_emit(state, triggers.get_key_value(&sounds.sfx)) {
|
||||
if Self::should_emit(internal_state, triggers.get_key_value(&sounds.sfx)) {
|
||||
// If the camera is within SFX distance
|
||||
if (block_pos.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR {
|
||||
sfx_emitter.emit(SfxEventItem::new(
|
||||
sounds.sfx.clone(),
|
||||
Some(block_pos),
|
||||
let underwater = state
|
||||
.terrain()
|
||||
.get(cam_pos.map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind() == BlockKind::Water)
|
||||
.unwrap_or(false);
|
||||
|
||||
let sfx_trigger_item = triggers.get_key_value(&sounds.sfx);
|
||||
audio.emit_sfx(
|
||||
sfx_trigger_item,
|
||||
block_pos,
|
||||
Some(sounds.volume),
|
||||
));
|
||||
underwater,
|
||||
);
|
||||
}
|
||||
state.time = Instant::now();
|
||||
state.event = sounds.sfx.clone();
|
||||
internal_state.time = Instant::now();
|
||||
internal_state.event = sounds.sfx.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,8 @@
|
||||
/// EventMapper::Campfire maps sfx to campfires
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
audio::sfx::{SfxEvent, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
scene::{Camera, Terrain},
|
||||
AudioFrontend,
|
||||
};
|
||||
|
||||
use super::EventMapper;
|
||||
@ -9,9 +10,9 @@ use super::EventMapper;
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{object, Body, Pos},
|
||||
event::EventBus,
|
||||
state::State,
|
||||
terrain::TerrainChunk,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||
@ -39,6 +40,7 @@ pub struct CampfireEventMapper {
|
||||
impl EventMapper for CampfireEventMapper {
|
||||
fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
@ -47,10 +49,6 @@ impl EventMapper for CampfireEventMapper {
|
||||
_client: &Client,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
let sfx_event_bus = ecs.read_resource::<EventBus<SfxEventItem>>();
|
||||
let mut sfx_emitter = sfx_event_bus.emitter();
|
||||
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||
for (entity, body, pos) in (
|
||||
@ -62,24 +60,25 @@ impl EventMapper for CampfireEventMapper {
|
||||
.filter(|(_, _, e_pos)| (e_pos.0.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR)
|
||||
{
|
||||
if let Body::Object(object::Body::CampfireLit) = body {
|
||||
let state = self.event_history.entry(entity).or_default();
|
||||
let internal_state = self.event_history.entry(entity).or_default();
|
||||
|
||||
let mapped_event = SfxEvent::Campfire;
|
||||
|
||||
// Check for SFX config entry for this movement
|
||||
if Self::should_emit(state, triggers.get_key_value(&mapped_event)) {
|
||||
sfx_emitter.emit(SfxEventItem::new(
|
||||
mapped_event.clone(),
|
||||
Some(pos.0),
|
||||
Some(0.25),
|
||||
));
|
||||
|
||||
state.time = Instant::now();
|
||||
if Self::should_emit(internal_state, triggers.get_key_value(&mapped_event)) {
|
||||
let underwater = state
|
||||
.terrain()
|
||||
.get(cam_pos.map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind() == BlockKind::Water)
|
||||
.unwrap_or(false);
|
||||
let sfx_trigger_item = triggers.get_key_value(&mapped_event);
|
||||
audio.emit_sfx(sfx_trigger_item, pos.0, None, underwater);
|
||||
internal_state.time = Instant::now();
|
||||
}
|
||||
|
||||
// update state to determine the next event. We only record the time (above) if
|
||||
// it was dispatched
|
||||
state.event = mapped_event;
|
||||
internal_state.event = mapped_event;
|
||||
}
|
||||
}
|
||||
self.cleanup(player_entity);
|
||||
|
@ -1,8 +1,9 @@
|
||||
/// EventMapper::Combat watches the combat states of surrounding entities' and
|
||||
/// emits sfx related to weapons and attacks/abilities
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
audio::sfx::{SfxEvent, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
scene::{Camera, Terrain},
|
||||
AudioFrontend,
|
||||
};
|
||||
|
||||
use super::EventMapper;
|
||||
@ -10,9 +11,9 @@ use super::EventMapper;
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{item::ItemKind, CharacterAbilityType, CharacterState, Loadout, Pos},
|
||||
event::EventBus,
|
||||
state::State,
|
||||
terrain::TerrainChunk,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||
@ -42,6 +43,7 @@ pub struct CombatEventMapper {
|
||||
impl EventMapper for CombatEventMapper {
|
||||
fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
@ -51,9 +53,6 @@ impl EventMapper for CombatEventMapper {
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
let sfx_event_bus = ecs.read_resource::<EventBus<SfxEventItem>>();
|
||||
let mut sfx_emitter = sfx_event_bus.emitter();
|
||||
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||
|
||||
@ -67,21 +66,27 @@ impl EventMapper for CombatEventMapper {
|
||||
.filter(|(_, e_pos, ..)| (e_pos.0.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR)
|
||||
{
|
||||
if let Some(character) = character {
|
||||
let state = self.event_history.entry(entity).or_default();
|
||||
let sfx_state = self.event_history.entry(entity).or_default();
|
||||
|
||||
let mapped_event = Self::map_event(character, state, loadout);
|
||||
let mapped_event = Self::map_event(character, sfx_state, loadout);
|
||||
|
||||
// Check for SFX config entry for this movement
|
||||
if Self::should_emit(state, triggers.get_key_value(&mapped_event)) {
|
||||
sfx_emitter.emit(SfxEventItem::new(mapped_event.clone(), Some(pos.0), None));
|
||||
if Self::should_emit(sfx_state, triggers.get_key_value(&mapped_event)) {
|
||||
let underwater = state
|
||||
.terrain()
|
||||
.get(cam_pos.map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind() == BlockKind::Water)
|
||||
.unwrap_or(false);
|
||||
|
||||
state.time = Instant::now();
|
||||
let sfx_trigger_item = triggers.get_key_value(&mapped_event);
|
||||
audio.emit_sfx(sfx_trigger_item, pos.0, None, underwater);
|
||||
sfx_state.time = Instant::now();
|
||||
}
|
||||
|
||||
// update state to determine the next event. We only record the time (above) if
|
||||
// it was dispatched
|
||||
state.event = mapped_event;
|
||||
state.weapon_drawn = Self::weapon_drawn(character);
|
||||
sfx_state.event = mapped_event;
|
||||
sfx_state.weapon_drawn = Self::weapon_drawn(character);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,15 @@ use combat::CombatEventMapper;
|
||||
use movement::MovementEventMapper;
|
||||
|
||||
use super::SfxTriggers;
|
||||
use crate::scene::{Camera, Terrain};
|
||||
use crate::{
|
||||
scene::{Camera, Terrain},
|
||||
AudioFrontend,
|
||||
};
|
||||
|
||||
trait EventMapper {
|
||||
fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
@ -44,6 +48,7 @@ impl SfxEventMapper {
|
||||
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
@ -52,7 +57,15 @@ impl SfxEventMapper {
|
||||
client: &Client,
|
||||
) {
|
||||
for mapper in &mut self.mappers {
|
||||
mapper.maintain(state, player_entity, camera, triggers, terrain, client);
|
||||
mapper.maintain(
|
||||
audio,
|
||||
state,
|
||||
player_entity,
|
||||
camera,
|
||||
triggers,
|
||||
terrain,
|
||||
client,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,16 @@
|
||||
/// proportionate to the extity's size
|
||||
use super::EventMapper;
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
audio::sfx::{SfxEvent, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
scene::{Camera, Terrain},
|
||||
AudioFrontend,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{Body, CharacterState, PhysicsState, Pos, Vel},
|
||||
event::EventBus,
|
||||
state::State,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||
@ -44,6 +45,7 @@ pub struct MovementEventMapper {
|
||||
impl EventMapper for MovementEventMapper {
|
||||
fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
@ -53,9 +55,6 @@ impl EventMapper for MovementEventMapper {
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
let sfx_event_bus = ecs.read_resource::<EventBus<SfxEventItem>>();
|
||||
let mut sfx_emitter = sfx_event_bus.emitter();
|
||||
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||
|
||||
@ -99,12 +98,19 @@ impl EventMapper for MovementEventMapper {
|
||||
|
||||
// Check for SFX config entry for this movement
|
||||
if Self::should_emit(internal_state, triggers.get_key_value(&mapped_event)) {
|
||||
sfx_emitter.emit(SfxEventItem::new(
|
||||
mapped_event.clone(),
|
||||
Some(pos.0),
|
||||
Some(Self::get_volume_for_body_type(body)),
|
||||
));
|
||||
let underwater = state
|
||||
.terrain()
|
||||
.get(cam_pos.map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind() == BlockKind::Water)
|
||||
.unwrap_or(false);
|
||||
|
||||
let sfx_trigger_item = triggers.get_key_value(&mapped_event);
|
||||
audio.emit_sfx(
|
||||
sfx_trigger_item,
|
||||
pos.0,
|
||||
Some(Self::get_volume_for_body_type(body)),
|
||||
underwater,
|
||||
);
|
||||
internal_state.time = Instant::now();
|
||||
}
|
||||
|
||||
|
@ -94,19 +94,15 @@ use common::{
|
||||
item::{ItemKind, ToolKind},
|
||||
object, Body, CharacterAbilityType, InventoryUpdateEvent,
|
||||
},
|
||||
event::EventBus,
|
||||
outcome::Outcome,
|
||||
state::State,
|
||||
states::utils::StageSection,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
vol::ReadVol,
|
||||
terrain::TerrainChunk,
|
||||
};
|
||||
use event_mapper::SfxEventMapper;
|
||||
use hashbrown::HashMap;
|
||||
use rand::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use specs::WorldExt;
|
||||
use tracing::{debug, warn};
|
||||
use tracing::warn;
|
||||
use vek::*;
|
||||
|
||||
/// We watch the states of nearby entities in order to emit SFX at their
|
||||
@ -241,7 +237,7 @@ impl SfxTriggers {
|
||||
}
|
||||
|
||||
pub struct SfxMgr {
|
||||
triggers: SfxTriggers,
|
||||
pub triggers: SfxTriggers,
|
||||
event_mapper: SfxEventMapper,
|
||||
}
|
||||
|
||||
@ -269,25 +265,16 @@ impl SfxMgr {
|
||||
if !audio.sfx_enabled() {
|
||||
return;
|
||||
}
|
||||
let ecs = state.ecs();
|
||||
|
||||
// This checks to see if the camera is underwater. If it is,
|
||||
// we pass all sfx through a low pass filter
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
let underwater = state
|
||||
.terrain()
|
||||
.get((camera.dependents().cam_pos + focus_off).map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind())
|
||||
.unwrap_or(BlockKind::Air)
|
||||
== BlockKind::Water;
|
||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||
|
||||
// Sets the listener position to the camera position facing the
|
||||
// same direction as the camera
|
||||
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
|
||||
|
||||
// TODO: replace; deprecated in favor of outcomes
|
||||
self.event_mapper.maintain(
|
||||
audio,
|
||||
state,
|
||||
player_entity,
|
||||
camera,
|
||||
@ -295,42 +282,6 @@ impl SfxMgr {
|
||||
terrain,
|
||||
client,
|
||||
);
|
||||
|
||||
// TODO: replace; deprecated in favor of outcomes
|
||||
let events = ecs.read_resource::<EventBus<SfxEventItem>>().recv_all();
|
||||
|
||||
for event in events {
|
||||
let position = match event.pos {
|
||||
Some(pos) => pos,
|
||||
_ => cam_pos,
|
||||
};
|
||||
|
||||
if let Some(item) = self.triggers.get_trigger(&event.sfx) {
|
||||
let sfx_file = match item.files.len() {
|
||||
0 => {
|
||||
debug!("Sfx event {:?} is missing audio file.", event.sfx);
|
||||
"voxygen.audio.sfx.placeholder"
|
||||
},
|
||||
1 => item
|
||||
.files
|
||||
.last()
|
||||
.expect("Failed to determine sound file for this trigger item."),
|
||||
_ => {
|
||||
// If more than one file is listed, choose one at random
|
||||
let rand_step = rand::random::<usize>() % item.files.len();
|
||||
&item.files[rand_step]
|
||||
},
|
||||
};
|
||||
|
||||
if underwater {
|
||||
audio.play_underwater_sfx(sfx_file, position, event.vol);
|
||||
} else {
|
||||
audio.play_sfx(sfx_file, position, event.vol);
|
||||
}
|
||||
} else {
|
||||
debug!("Missing sfx trigger config for sfx event. {:?}", event.sfx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_outcome(&mut self, outcome: &Outcome, audio: &mut AudioFrontend) {
|
||||
@ -392,54 +343,6 @@ impl SfxMgr {
|
||||
audio.play_sfx(file_ref, *pos, None);
|
||||
}
|
||||
},
|
||||
Outcome::Attack {
|
||||
pos,
|
||||
character_state,
|
||||
loadout,
|
||||
} => {
|
||||
if let Some(item_config) = &loadout.active_item {
|
||||
if let ItemKind::Tool(data) = item_config.item.kind() {
|
||||
if character_state.is_attack() {
|
||||
match (
|
||||
CharacterAbilityType::from(character_state),
|
||||
data.kind.clone(),
|
||||
) {
|
||||
(
|
||||
CharacterAbilityType::ComboMelee(StageSection::Swing, 1),
|
||||
ToolKind::Sword,
|
||||
) => {
|
||||
audio.play_sfx(
|
||||
"voxygen.audio.sfx.abilities.swing_sword",
|
||||
*pos,
|
||||
None,
|
||||
);
|
||||
},
|
||||
(
|
||||
CharacterAbilityType::ComboMelee(StageSection::Swing, 2),
|
||||
ToolKind::Sword,
|
||||
) => {
|
||||
audio.play_sfx(
|
||||
"voxygen.audio.sfx.abilities.separated_second_swing",
|
||||
*pos,
|
||||
None,
|
||||
);
|
||||
},
|
||||
(
|
||||
CharacterAbilityType::ComboMelee(StageSection::Swing, 3),
|
||||
ToolKind::Sword,
|
||||
) => {
|
||||
audio.play_sfx(
|
||||
"voxygen.audio.sfx.abilities.separated_third_swing",
|
||||
*pos,
|
||||
None,
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ pub mod comp;
|
||||
pub mod sys;
|
||||
|
||||
use crate::audio::sfx::SfxEventItem;
|
||||
use common::event::EventBus;
|
||||
use common::{event::EventBus, outcome::Outcome};
|
||||
use specs::{Entity, World, WorldExt};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -27,6 +27,7 @@ pub fn init(world: &mut World) {
|
||||
world.register::<comp::HpFloaterList>();
|
||||
world.register::<comp::Interpolated>();
|
||||
world.insert(MyExpFloaterList::default());
|
||||
world.insert(Vec::<Outcome>::new());
|
||||
|
||||
// Voxygen event buses
|
||||
world.insert(EventBus::<SfxEventItem>::default());
|
||||
|
@ -101,7 +101,7 @@ pub struct Scene {
|
||||
|
||||
particle_mgr: ParticleMgr,
|
||||
figure_mgr: FigureMgr,
|
||||
sfx_mgr: SfxMgr,
|
||||
pub sfx_mgr: SfxMgr,
|
||||
music_mgr: MusicMgr,
|
||||
ambient_mgr: AmbientMgr,
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem},
|
||||
audio::sfx::SfxEvent,
|
||||
ecs::MyEntity,
|
||||
hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, PressBehavior},
|
||||
i18n::{i18n_asset_key, Localization},
|
||||
@ -17,7 +17,6 @@ use common::{
|
||||
comp,
|
||||
comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel},
|
||||
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
||||
event::EventBus,
|
||||
msg::PresenceKind,
|
||||
outcome::Outcome,
|
||||
span,
|
||||
@ -121,12 +120,12 @@ impl SessionState {
|
||||
self.hud.new_message(m);
|
||||
},
|
||||
client::Event::InventoryUpdated(inv_event) => {
|
||||
let sfx_event = SfxEvent::from(&inv_event);
|
||||
client
|
||||
.state()
|
||||
.ecs()
|
||||
.read_resource::<EventBus<SfxEventItem>>()
|
||||
.emit_now(SfxEventItem::at_player_position(sfx_event));
|
||||
let sfx_trigger_item = self
|
||||
.scene
|
||||
.sfx_mgr
|
||||
.triggers
|
||||
.get_key_value(&SfxEvent::from(&inv_event));
|
||||
global_state.audio.emit_sfx_item(sfx_trigger_item);
|
||||
|
||||
match inv_event {
|
||||
InventoryUpdateEvent::CollectFailed => {
|
||||
|
Loading…
Reference in New Issue
Block a user