merging some UI sfx from a now-dead branch

Merge part 2

merge part 3

Merge part 4

merge part 5
This commit is contained in:
DaforLynx 2022-03-15 11:56:18 -07:00 committed by IsseW
parent b578f0231f
commit ca815f25a1
11 changed files with 148 additions and 58 deletions

View File

@ -52,8 +52,6 @@ pub enum Outcome {
uid: Uid,
skill_tree: comp::skillset::SkillGroupKind,
total_points: u16,
// TODO: Access ECS to get position from Uid to conserve bandwidth
pos: Vec3<f32>,
},
ComboChange {
uid: Uid,
@ -95,6 +93,9 @@ pub enum Outcome {
pos: Vec3<f32>,
wielded: bool,
},
Jump {
pos: Vec3<f32>,
}
}
impl Outcome {
@ -104,7 +105,6 @@ impl Outcome {
| Outcome::ProjectileShot { pos, .. }
| Outcome::ProjectileHit { pos, .. }
| Outcome::Beam { pos, .. }
| Outcome::SkillPointGain { pos, .. }
| Outcome::SummonedCreature { pos, .. }
| Outcome::HealthChange { pos, .. }
| Outcome::Death { pos, .. }
@ -112,9 +112,10 @@ impl Outcome {
| Outcome::PoiseChange { pos, .. }
| Outcome::GroundSlam { pos }
| Outcome::Utterance { pos, .. }
| Outcome::Glider { pos, .. } => Some(*pos),
| Outcome::Glider { pos, .. }
| Outcome::Jump { pos, .. } => Some(*pos),
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } | Outcome::SkillPointGain { .. } => None,
}
}
}

View File

@ -379,23 +379,16 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
exp_awards.iter().for_each(|(attacker, exp_reward, _)| {
// Process the calculated EXP rewards
if let (
Some(mut attacker_skill_set),
Some(attacker_uid),
Some(attacker_inventory),
Some(pos),
) = (
if let (Some(mut attacker_skill_set), Some(attacker_uid), Some(attacker_inventory)) = (
skill_sets.get_mut(*attacker),
uids.get(*attacker),
inventories.get(*attacker),
positions.get(*attacker),
) {
handle_exp_gain(
*exp_reward,
attacker_inventory,
&mut attacker_skill_set,
attacker_uid,
pos,
&mut outcomes,
);
}
@ -1178,7 +1171,6 @@ fn handle_exp_gain(
inventory: &Inventory,
skill_set: &mut SkillSet,
uid: &Uid,
pos: &Pos,
outcomes: &mut EventBus<Outcome>,
) {
use comp::inventory::{item::ItemKind, slot::EquipSlot};
@ -1219,7 +1211,6 @@ fn handle_exp_gain(
uid: *uid,
skill_tree: *pool,
total_points: level_outcome,
pos: pos.0,
});
}
}

View File

@ -186,16 +186,11 @@ pub fn handle_mine_block(
) {
let skill_group = SkillGroupKind::Weapon(tool);
let outcome_bus = state.ecs().read_resource::<EventBus<Outcome>>();
let positions = state.ecs().read_component::<comp::Pos>();
if let (Some(level_outcome), Some(pos)) = (
skillset.add_experience(skill_group, exp_reward),
positions.get(entity),
) {
if let Some(level_outcome) = skillset.add_experience(skill_group, exp_reward) {
outcome_bus.emit_now(Outcome::SkillPointGain {
uid,
skill_tree: skill_group,
total_points: level_outcome,
pos: pos.0,
});
}
outcome_bus.emit_now(Outcome::ExpChange {

View File

@ -255,6 +255,8 @@ impl SfxChannel {
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
pub fn stop(&mut self) { self.sink.stop(); }
pub fn is_done(&self) -> bool { self.sink.empty() }
pub fn set_pos(&mut self, pos: Vec3<f32>) { self.pos = pos; }
@ -262,11 +264,40 @@ impl SfxChannel {
pub fn update(&mut self, listener: &Listener) {
const FALLOFF: f32 = 0.13;
self.sink
.set_emitter_position(((self.pos - listener.pos) * FALLOFF).into_array());
self.sink.set_emitter_position(
((self.pos - listener.pos) * FALLOFF).into_array(),
);
self.sink
.set_left_ear_position(listener.ear_left_rpos.into_array());
self.sink
.set_right_ear_position(listener.ear_right_rpos.into_array());
}
}
pub struct UIChannel {
sink: Sink,
}
impl UIChannel {
pub fn new(stream: &OutputStreamHandle) -> Self {
Self {
sink: Sink::try_new(stream).unwrap()
}
}
pub fn play<S>(&mut self, source: S)
where
S: Source + Send + 'static,
S::Item: Sample,
S::Item: Send,
<S as std::iter::Iterator>::Item: std::fmt::Debug,
{
self.sink.append(source);
}
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
pub fn stop(&mut self) { self.sink.stop(); }
pub fn is_done(&self) -> bool { self.sink.empty() }
}

View File

@ -7,7 +7,7 @@ pub mod music;
pub mod sfx;
pub mod soundcache;
use channel::{AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel};
use channel::{AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel, UIChannel};
use fader::Fader;
use music::MusicTransitionManifest;
use sfx::{SfxEvent, SfxTriggerItem};
@ -43,6 +43,7 @@ pub struct AudioFrontend {
music_channels: Vec<MusicChannel>,
ambient_channels: Vec<AmbientChannel>,
sfx_channels: Vec<SfxChannel>,
ui_channels: Vec<UIChannel>,
sfx_volume: f32,
music_volume: f32,
master_volume: f32,
@ -53,7 +54,7 @@ pub struct AudioFrontend {
impl AudioFrontend {
/// Construct with given device
pub fn new(/* dev: String, */ num_sfx_channels: usize) -> Self {
pub fn new(/* dev: String, */ num_sfx_channels: usize, num_ui_channels: usize) -> Self {
// Commented out until audio device switcher works
//let audio_device = get_device_raw(&dev);
@ -81,6 +82,11 @@ impl AudioFrontend {
sfx_channels.resize_with(num_sfx_channels, || SfxChannel::new(audio_stream));
};
let mut ui_channels = Vec::with_capacity(num_ui_channels);
if let Some(audio_stream) = &audio_stream {
ui_channels.resize_with(num_ui_channels, || UIChannel::new(audio_stream))
}
Self {
// The following is for the disabled device switcher
//device,
@ -90,6 +96,7 @@ impl AudioFrontend {
audio_stream,
music_channels: Vec::new(),
sfx_channels,
ui_channels,
ambient_channels: Vec::new(),
sfx_volume: 1.0,
music_volume: 1.0,
@ -119,6 +126,7 @@ impl AudioFrontend {
audio_stream: None,
music_channels: Vec::new(),
sfx_channels: Vec::new(),
ui_channels: Vec::new(),
ambient_channels: Vec::new(),
sfx_volume: 1.0,
music_volume: 1.0,
@ -151,6 +159,19 @@ impl AudioFrontend {
None
}
fn get_ui_channel(&mut self) -> Option<&mut UIChannel> {
if self.audio_stream.is_some() {
let sfx_volume = self.get_sfx_volume();
if let Some(channel) = self.ui_channels.iter_mut().find(|c| c.is_done()) {
channel.set_volume(sfx_volume);
return Some(channel);
}
}
None
}
/// Retrieve a music channel from the channel list. This inspects the
/// MusicChannelTag to determine whether we are transitioning between
/// music types and acts accordingly. For example transitioning between
@ -195,7 +216,7 @@ impl AudioFrontend {
/// 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)>) {
pub fn emit_sfx_item(&mut self, trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, vol: Option<f32>) {
if let Some((event, item)) = trigger_item {
let sfx_file = match item.files.len() {
0 => {
@ -213,8 +234,7 @@ impl AudioFrontend {
},
};
// TODO: Should this take `underwater` into consideration?
match self.play_sfx(sfx_file, self.listener.pos, None, false) {
match self.play_ui_sfx(sfx_file, vol) {
Ok(_) => {},
Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e),
}
@ -282,6 +302,21 @@ impl AudioFrontend {
} else {
channel.play(sound);
}
}
}
Ok(())
}
pub fn play_ui_sfx(
&mut self,
sound: &str,
vol: Option<f32>,
) -> Result<(), rodio::decoder::DecoderError> {
if self.audio_stream.is_some() {
let sound = load_ogg(sound).amplify(vol.unwrap_or(1.0));
if let Some(channel) = self.get_ui_channel() {
channel.play(sound);
}
}
Ok(())
@ -471,6 +506,15 @@ impl AudioFrontend {
}
}
pub fn stop_all_sfx(&mut self) {
for channel in self.sfx_channels.iter_mut() {
channel.stop()
}
for channel in self.ui_channels.iter_mut() {
channel.stop()
}
}
// The following is for the disabled device switcher
//// TODO: figure out how badly this will break things when it is called
//pub fn set_device(&mut self, name: String) {

View File

@ -82,6 +82,8 @@
mod event_mapper;
use specs::{WorldExt};
use crate::{
audio::AudioFrontend,
scene::{Camera, Terrain},
@ -100,6 +102,7 @@ use common::{
},
outcome::Outcome,
terrain::{BlockKind, TerrainChunk},
uid::Uid,
};
use common_state::State;
use event_mapper::SfxEventMapper;
@ -407,11 +410,14 @@ impl SfxMgr {
outcome: &Outcome,
audio: &mut AudioFrontend,
client: &Client,
state: &State,
underwater: bool,
) {
if !audio.sfx_enabled() {
return;
}
let triggers = self.triggers.read();
let uids = state.ecs().read_storage::<Uid>();
// TODO handle underwater
match outcome {
@ -421,12 +427,12 @@ impl SfxMgr {
sfx_trigger_item,
*pos,
Some((power.abs() / 2.5).min(1.5)),
false,
underwater,
);
},
Outcome::GroundSlam { pos, .. } => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::GroundSlam);
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);
},
Outcome::ProjectileShot { pos, body, .. } => {
match body {
@ -437,7 +443,7 @@ impl SfxMgr {
| object::Body::ArrowTurret,
) => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::ArrowShot);
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
audio.emit_sfx(sfx_trigger_item, *pos, None, underwater);
},
Body::Object(
object::Body::BoltFire
@ -445,7 +451,7 @@ impl SfxMgr {
| object::Body::BoltNature,
) => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::FireShot);
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
audio.emit_sfx(sfx_trigger_item, *pos, None, underwater);
},
_ => {
// not mapped to sfx file
@ -467,37 +473,41 @@ impl SfxMgr {
) => {
if target.is_none() {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::ArrowMiss);
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);
} else if *source == client.uid() {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::ArrowHit);
audio.emit_sfx(
sfx_trigger_item,
client.position().unwrap_or(*pos),
Some(2.0),
false,
underwater,
);
} else {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::ArrowHit);
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);
}
},
_ => {},
},
Outcome::SkillPointGain { pos, .. } => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SkillPointGain);
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
Outcome::SkillPointGain { uid, .. } => {
if let Some(client_uid) = uids.get(client.entity()) {
if uid == client_uid {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SkillPointGain);
audio.emit_sfx_item(sfx_trigger_item, Some(0.4));
}
}
},
Outcome::Beam { pos, specifier } => match specifier {
beam::FrontendSpecifier::LifestealBeam => {
if thread_rng().gen_bool(0.5) {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SceptreBeam);
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
audio.emit_sfx(sfx_trigger_item, *pos, None, underwater);
};
},
beam::FrontendSpecifier::Flamethrower | beam::FrontendSpecifier::Cultist => {
if thread_rng().gen_bool(0.5) {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::FlameThrower);
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
audio.emit_sfx(sfx_trigger_item, *pos, None, underwater);
}
},
beam::FrontendSpecifier::ClayGolem
@ -511,27 +521,27 @@ impl SfxMgr {
sfx_trigger_item,
pos.map(|e| e as f32 + 0.5),
Some(3.0),
false,
underwater,
);
},
Outcome::HealthChange { pos, info, .. } => {
// Don't emit sound effects from positive damage (healing)
if info.amount < Health::HEALTH_EPSILON {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Damage);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
}
},
Outcome::Death { pos, .. } => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Death);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
},
Outcome::Block { pos, parry, .. } => {
if *parry {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Parry);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
} else {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Block);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
}
},
Outcome::PoiseChange { pos, state, .. } => match state {
@ -539,22 +549,22 @@ impl SfxMgr {
PoiseState::Interrupted => {
let sfx_trigger_item =
triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Interrupted));
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
},
PoiseState::Stunned => {
let sfx_trigger_item =
triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Stunned));
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
},
PoiseState::Dazed => {
let sfx_trigger_item =
triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Dazed));
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
},
PoiseState::KnockedDown => {
let sfx_trigger_item =
triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::KnockedDown));
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), underwater);
},
},
Outcome::Utterance { pos, kind, body } => {
@ -562,7 +572,7 @@ impl SfxMgr {
let sfx_trigger_item =
triggers.get_key_value(&SfxEvent::Utterance(*kind, voice));
if let Some(sfx_trigger_item) = sfx_trigger_item {
audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(1.5), false);
audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(1.5), underwater);
} else {
debug!(
"No utterance sound effect exists for ({:?}, {:?})",
@ -574,12 +584,17 @@ impl SfxMgr {
Outcome::Glider { pos, wielded } => {
if *wielded {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::GliderOpen);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), underwater);
} else {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::GliderClose);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), false);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), underwater);
}
},
// unused for now
Outcome::Jump { pos } => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Jump);
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), underwater)
},
Outcome::ExpChange { .. }
| Outcome::ComboChange { .. }
| Outcome::SummonedCreature { .. } => {},

View File

@ -223,7 +223,7 @@ fn main() {
// Setup audio
let mut audio = match settings.audio.output {
AudioOutput::Off => AudioFrontend::no_audio(),
AudioOutput::Automatic => AudioFrontend::new(settings.audio.num_sfx_channels),
AudioOutput::Automatic => AudioFrontend::new(settings.audio.num_sfx_channels, settings.audio.num_ui_channels),
// AudioOutput::Device(ref dev) => Some(dev.clone()),
};

View File

@ -409,11 +409,18 @@ impl Scene {
outcome: &Outcome,
scene_data: &SceneData,
audio: &mut AudioFrontend,
state: &State,
cam_pos: Vec3<f32>,
) {
span!(_guard, "handle_outcome", "Scene::handle_outcome");
let underwater = state
.terrain()
.get(cam_pos.map(|e| e.floor() as i32))
.map(|b| b.is_liquid())
.unwrap_or(false);
self.particle_mgr.handle_outcome(outcome, scene_data);
self.sfx_mgr
.handle_outcome(outcome, audio, scene_data.client);
.handle_outcome(outcome, audio, scene_data.client, state, underwater);
match outcome {
Outcome::Explosion {

View File

@ -287,7 +287,8 @@ impl ParticleMgr {
| Outcome::HealthChange { .. }
| Outcome::PoiseChange { .. }
| Outcome::Utterance { .. }
| Outcome::Glider { .. } => {},
| Outcome::Glider { .. }
| Outcome::Jump { .. } => {},
}
}

View File

@ -245,7 +245,7 @@ impl SessionState {
let sfx_triggers = self.scene.sfx_mgr.triggers.read();
let sfx_trigger_item = sfx_triggers.get_key_value(&SfxEvent::from(&inv_event));
global_state.audio.emit_sfx_item(sfx_trigger_item);
global_state.audio.emit_sfx_item(sfx_trigger_item, Some(1.0));
match inv_event {
InventoryUpdateEvent::BlockCollectFailed { pos, reason } => {
@ -358,6 +358,7 @@ impl PlayState for SessionState {
let client = self.client.borrow();
(client.presence(), client.registered())
};
if client_presence.is_some() {
let camera = self.scene.camera_mut();
@ -1174,6 +1175,7 @@ impl PlayState for SessionState {
HudEvent::Logout => {
self.client.borrow_mut().logout();
global_state.audio.stop_ambient_sounds();
global_state.audio.stop_all_sfx();
return PlayStateResult::Pop;
},
HudEvent::Quit => {
@ -1575,11 +1577,12 @@ impl PlayState for SessionState {
&scene_data,
&client,
);
// Process outcomes from client
for outcome in outcomes {
self.scene
.handle_outcome(&outcome, &scene_data, &mut global_state.audio);
.handle_outcome(&outcome, &scene_data, &mut global_state.audio, &client.state(), cam_pos);
self.hud
.handle_outcome(&outcome, scene_data.client, global_state);
}

View File

@ -26,6 +26,7 @@ pub struct AudioSettings {
pub music_volume: f32,
pub sfx_volume: f32,
pub num_sfx_channels: usize,
pub num_ui_channels: usize,
/// Audio Device that Voxygen will use to play audio.
pub output: AudioOutput,
@ -36,9 +37,10 @@ impl Default for AudioSettings {
Self {
master_volume: 1.0,
inactive_master_volume_perc: 0.5,
music_volume: 0.4,
music_volume: 0.3,
sfx_volume: 0.6,
num_sfx_channels: 60,
num_ui_channels: 10,
output: AudioOutput::Automatic,
}
}