mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added outcome sound effects, fixed directional sound, particle outcomes
This commit is contained in:
parent
7c31baef6f
commit
8547cdd681
@ -106,6 +106,18 @@
|
||||
"voxygen.audio.sfx.inventory.consumable.food",
|
||||
],
|
||||
threshold: 0.3,
|
||||
)
|
||||
),
|
||||
Explosion: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.glider_open",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
ProjectileShot: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.glider_open",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
@ -16,7 +16,10 @@
|
||||
//! the channel capacity has been reached and all channels are occupied, a
|
||||
//! warning is logged, and no sound is played.
|
||||
|
||||
use crate::audio::fader::{FadeDirection, Fader};
|
||||
use crate::audio::{
|
||||
fader::{FadeDirection, Fader},
|
||||
Listener,
|
||||
};
|
||||
use rodio::{Device, Sample, Sink, Source, SpatialSink};
|
||||
use vek::*;
|
||||
|
||||
@ -163,11 +166,13 @@ impl SfxChannel {
|
||||
|
||||
pub fn is_done(&self) -> bool { self.sink.empty() }
|
||||
|
||||
pub fn set_emitter_position(&mut self, pos: [f32; 3]) { self.sink.set_emitter_position(pos); }
|
||||
pub fn set_pos(&mut self, pos: Vec3<f32>) { self.pos = pos; }
|
||||
|
||||
pub fn set_left_ear_position(&mut self, pos: [f32; 3]) { self.sink.set_left_ear_position(pos); }
|
||||
pub fn update(&mut self, listener: &Listener) {
|
||||
const FALLOFF: f32 = 0.13;
|
||||
|
||||
pub fn set_right_ear_position(&mut self, pos: [f32; 3]) {
|
||||
self.sink.set_right_ear_position(pos);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,14 @@ use cpal::traits::DeviceTrait;
|
||||
use rodio::{source::Source, Decoder, Device};
|
||||
use vek::*;
|
||||
|
||||
const FALLOFF: f32 = 0.13;
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Listener {
|
||||
pos: Vec3<f32>,
|
||||
ori: Vec3<f32>,
|
||||
|
||||
ear_left_rpos: Vec3<f32>,
|
||||
ear_right_rpos: Vec3<f32>,
|
||||
}
|
||||
|
||||
/// Holds information about the system audio devices and internal channels used
|
||||
/// for sfx and music playback. An instance of `AudioFrontend` is used by
|
||||
@ -34,11 +41,7 @@ pub struct AudioFrontend {
|
||||
sfx_volume: f32,
|
||||
music_volume: f32,
|
||||
|
||||
listener_pos: Vec3<f32>,
|
||||
listener_ori: Vec3<f32>,
|
||||
|
||||
listener_ear_left: Vec3<f32>,
|
||||
listener_ear_right: Vec3<f32>,
|
||||
listener: Listener,
|
||||
}
|
||||
|
||||
impl AudioFrontend {
|
||||
@ -63,10 +66,8 @@ impl AudioFrontend {
|
||||
sfx_channels,
|
||||
sfx_volume: 1.0,
|
||||
music_volume: 1.0,
|
||||
listener_pos: Vec3::zero(),
|
||||
listener_ori: Vec3::zero(),
|
||||
listener_ear_left: Vec3::zero(),
|
||||
listener_ear_right: Vec3::zero(),
|
||||
|
||||
listener: Listener::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,10 +82,7 @@ impl AudioFrontend {
|
||||
sfx_channels: Vec::new(),
|
||||
sfx_volume: 1.0,
|
||||
music_volume: 1.0,
|
||||
listener_pos: Vec3::zero(),
|
||||
listener_ori: Vec3::zero(),
|
||||
listener_ear_left: Vec3::zero(),
|
||||
listener_ear_right: Vec3::zero(),
|
||||
listener: Listener::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,20 +144,15 @@ impl AudioFrontend {
|
||||
/// 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_device.is_some() {
|
||||
let calc_pos = ((pos - self.listener_pos) * FALLOFF).into_array();
|
||||
|
||||
let sound = self
|
||||
.sound_cache
|
||||
.load_sound(sound)
|
||||
.amplify(vol.unwrap_or(1.0));
|
||||
|
||||
let left_ear = self.listener_ear_left.into_array();
|
||||
let right_ear = self.listener_ear_right.into_array();
|
||||
|
||||
let listener = self.listener.clone();
|
||||
if let Some(channel) = self.get_sfx_channel() {
|
||||
channel.set_emitter_position(calc_pos);
|
||||
channel.set_left_ear_position(left_ear);
|
||||
channel.set_right_ear_position(right_ear);
|
||||
channel.set_pos(pos);
|
||||
channel.update(&listener);
|
||||
channel.play(sound);
|
||||
}
|
||||
}
|
||||
@ -174,27 +167,17 @@ impl AudioFrontend {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_listener_pos(&mut self, pos: &Vec3<f32>, ori: &Vec3<f32>) {
|
||||
self.listener_pos = *pos;
|
||||
self.listener_ori = ori.normalized();
|
||||
pub fn set_listener_pos(&mut self, pos: Vec3<f32>, ori: Vec3<f32>) {
|
||||
self.listener.pos = pos;
|
||||
self.listener.ori = ori.normalized();
|
||||
|
||||
let up = Vec3::new(0.0, 0.0, 1.0);
|
||||
|
||||
let pos_left = up.cross(self.listener_ori).normalized();
|
||||
let pos_right = self.listener_ori.cross(up).normalized();
|
||||
|
||||
self.listener_ear_left = pos_left;
|
||||
self.listener_ear_right = pos_right;
|
||||
self.listener.ear_left_rpos = up.cross(self.listener.ori).normalized();
|
||||
self.listener.ear_right_rpos = -up.cross(self.listener.ori).normalized();
|
||||
|
||||
for channel in self.sfx_channels.iter_mut() {
|
||||
if !channel.is_done() {
|
||||
// TODO: Update this to correctly determine the updated relative position of
|
||||
// the SFX emitter when the player (listener) moves
|
||||
// channel.set_emitter_position(
|
||||
// ((channel.pos - self.listener_pos) * FALLOFF).into_array(),
|
||||
// );
|
||||
channel.set_left_ear_position(pos_left.into_array());
|
||||
channel.set_right_ear_position(pos_right.into_array());
|
||||
channel.update(&self.listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +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};
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
scene::Camera,
|
||||
};
|
||||
|
||||
use super::EventMapper;
|
||||
|
||||
@ -15,7 +18,6 @@ use common::{
|
||||
use hashbrown::HashMap;
|
||||
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||
use std::time::{Duration, Instant};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct PreviousEntityState {
|
||||
@ -39,16 +41,13 @@ pub struct CombatEventMapper {
|
||||
}
|
||||
|
||||
impl EventMapper for CombatEventMapper {
|
||||
fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) {
|
||||
fn maintain(&mut self, state: &State, player_entity: specs::Entity, camera: &Camera, triggers: &SfxTriggers) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
let sfx_event_bus = ecs.read_resource::<EventBus<SfxEventItem>>();
|
||||
let mut sfx_emitter = sfx_event_bus.emitter();
|
||||
|
||||
let player_position = ecs
|
||||
.read_storage::<Pos>()
|
||||
.get(player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
let cam_pos = camera.dependents().cam_pos;
|
||||
|
||||
for (entity, pos, loadout, character) in (
|
||||
&ecs.entities(),
|
||||
@ -58,7 +57,7 @@ impl EventMapper for CombatEventMapper {
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, e_pos, ..)| {
|
||||
(e_pos.0.distance_squared(player_position)) < SFX_DIST_LIMIT_SQR
|
||||
(e_pos.0.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR
|
||||
})
|
||||
{
|
||||
if let Some(character) = character {
|
||||
|
@ -8,10 +8,11 @@ use combat::CombatEventMapper;
|
||||
use movement::MovementEventMapper;
|
||||
use progression::ProgressionEventMapper;
|
||||
|
||||
use crate::scene::Camera;
|
||||
use super::SfxTriggers;
|
||||
|
||||
trait EventMapper {
|
||||
fn maintain(&mut self, state: &State, player_entity: specs::Entity, triggers: &SfxTriggers);
|
||||
fn maintain(&mut self, state: &State, player_entity: specs::Entity, camera: &Camera, triggers: &SfxTriggers);
|
||||
}
|
||||
|
||||
pub struct SfxEventMapper {
|
||||
@ -33,10 +34,11 @@ impl SfxEventMapper {
|
||||
&mut self,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
) {
|
||||
for mapper in &mut self.mappers {
|
||||
mapper.maintain(state, player_entity, triggers);
|
||||
mapper.maintain(state, player_entity, camera, triggers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
/// 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::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR};
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||
scene::Camera,
|
||||
};
|
||||
use common::{
|
||||
comp::{Body, CharacterState, PhysicsState, Pos, Vel},
|
||||
event::EventBus,
|
||||
@ -35,16 +38,13 @@ pub struct MovementEventMapper {
|
||||
}
|
||||
|
||||
impl EventMapper for MovementEventMapper {
|
||||
fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) {
|
||||
fn maintain(&mut self, state: &State, player_entity: specs::Entity, camera: &Camera, triggers: &SfxTriggers) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
let sfx_event_bus = ecs.read_resource::<EventBus<SfxEventItem>>();
|
||||
let mut sfx_emitter = sfx_event_bus.emitter();
|
||||
|
||||
let player_position = ecs
|
||||
.read_storage::<Pos>()
|
||||
.get(player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
let cam_pos = camera.dependents().cam_pos;
|
||||
|
||||
for (entity, pos, vel, body, physics, character) in (
|
||||
&ecs.entities(),
|
||||
@ -56,7 +56,7 @@ impl EventMapper for MovementEventMapper {
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, e_pos, ..)| {
|
||||
(e_pos.0.distance_squared(player_position)) < SFX_DIST_LIMIT_SQR
|
||||
(e_pos.0.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR
|
||||
})
|
||||
{
|
||||
if let Some(character) = character {
|
||||
|
@ -2,7 +2,10 @@
|
||||
/// and triggers sfx for gaining experience and levelling up
|
||||
use super::EventMapper;
|
||||
|
||||
use crate::audio::sfx::{SfxEvent, SfxEventItem, SfxTriggers};
|
||||
use crate::{
|
||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggers},
|
||||
scene::Camera,
|
||||
};
|
||||
|
||||
use common::{comp::Stats, event::EventBus, state::State};
|
||||
use specs::WorldExt;
|
||||
@ -23,7 +26,7 @@ pub struct ProgressionEventMapper {
|
||||
|
||||
impl EventMapper for ProgressionEventMapper {
|
||||
#[allow(clippy::op_ref)] // TODO: Pending review in #587
|
||||
fn maintain(&mut self, state: &State, player_entity: specs::Entity, triggers: &SfxTriggers) {
|
||||
fn maintain(&mut self, state: &State, player_entity: specs::Entity, _camera: &Camera, triggers: &SfxTriggers) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
let next_state = ecs.read_storage::<Stats>().get(player_entity).map_or(
|
||||
|
@ -83,17 +83,17 @@
|
||||
|
||||
mod event_mapper;
|
||||
|
||||
use crate::audio::AudioFrontend;
|
||||
use crate::{audio::AudioFrontend, scene::Camera};
|
||||
|
||||
use common::{
|
||||
assets,
|
||||
comp::{
|
||||
item::{ItemKind, ToolCategory},
|
||||
item::{Consumable, ItemKind, ToolCategory},
|
||||
CharacterAbilityType, InventoryUpdateEvent, Ori, Pos,
|
||||
},
|
||||
event::EventBus,
|
||||
state::State,
|
||||
outcome::Outcome,
|
||||
state::State,
|
||||
};
|
||||
use event_mapper::SfxEventMapper;
|
||||
use hashbrown::HashMap;
|
||||
@ -147,6 +147,8 @@ pub enum SfxEvent {
|
||||
Wield(ToolCategory),
|
||||
Unwield(ToolCategory),
|
||||
Inventory(SfxInventoryEvent),
|
||||
Explosion,
|
||||
ProjectileShot,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
@ -189,13 +191,19 @@ impl From<&InventoryUpdateEvent> for SfxEvent {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Outcome> for SfxEventItem {
|
||||
impl<'a> TryFrom<&'a Outcome> for SfxEventItem {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(outcome: Outcome) -> Result<Self, Self::Error> {
|
||||
fn try_from(outcome: &'a Outcome) -> Result<Self, Self::Error> {
|
||||
match outcome {
|
||||
Outcome::Explosion { pos, power } => Ok(Self::new(SfxEvent::GliderOpen, Some(pos), Some((power / 10.0).min(1.0)))),
|
||||
Outcome::ProjectileShot { pos, .. } => Ok(Self::new(SfxEvent::GliderOpen, Some(pos), None)),
|
||||
Outcome::Explosion { pos, power } => Ok(Self::new(
|
||||
SfxEvent::Explosion,
|
||||
Some(*pos),
|
||||
Some((*power / 10.0).min(1.0)),
|
||||
)),
|
||||
Outcome::ProjectileShot { pos, .. } => {
|
||||
Ok(Self::new(SfxEvent::ProjectileShot, Some(*pos), None))
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
@ -237,36 +245,25 @@ impl SfxMgr {
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
) {
|
||||
if !audio.sfx_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.event_mapper
|
||||
.maintain(state, player_entity, &self.triggers);
|
||||
.maintain(state, player_entity, camera, &self.triggers);
|
||||
|
||||
let ecs = state.ecs();
|
||||
|
||||
let player_position = ecs
|
||||
.read_storage::<Pos>()
|
||||
.get(player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
let player_ori = *ecs
|
||||
.read_storage::<Ori>()
|
||||
.get(player_entity)
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
.0;
|
||||
|
||||
audio.set_listener_pos(&player_position, &player_ori);
|
||||
audio.set_listener_pos(camera.dependents().cam_pos, camera.dependents().cam_dir);
|
||||
|
||||
let events = ecs.read_resource::<EventBus<SfxEventItem>>().recv_all();
|
||||
|
||||
for event in events {
|
||||
let position = match event.pos {
|
||||
Some(pos) => pos,
|
||||
_ => player_position,
|
||||
_ => camera.dependents().cam_pos,
|
||||
};
|
||||
|
||||
if let Some(item) = self.triggers.get_trigger(&event.sfx) {
|
||||
|
@ -29,6 +29,7 @@ pub struct Dependents {
|
||||
pub view_mat: Mat4<f32>,
|
||||
pub proj_mat: Mat4<f32>,
|
||||
pub cam_pos: Vec3<f32>,
|
||||
pub cam_dir: Vec3<f32>,
|
||||
}
|
||||
|
||||
pub struct Camera {
|
||||
@ -67,6 +68,7 @@ impl Camera {
|
||||
view_mat: Mat4::identity(),
|
||||
proj_mat: Mat4::identity(),
|
||||
cam_pos: Vec3::zero(),
|
||||
cam_dir: Vec3::unit_y(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -104,6 +106,8 @@ impl Camera {
|
||||
|
||||
// TODO: Make this more efficient.
|
||||
self.dependents.cam_pos = Vec3::from(self.dependents.view_mat.inverted() * Vec4::unit_w());
|
||||
|
||||
self.dependents.cam_dir = Vec3::from(self.dependents.view_mat.inverted() * -Vec4::unit_z());
|
||||
}
|
||||
|
||||
pub fn frustum(&self) -> Frustum<f32> {
|
||||
|
@ -4,7 +4,7 @@ pub mod particle;
|
||||
pub mod simple;
|
||||
pub mod terrain;
|
||||
|
||||
use self::{
|
||||
pub use self::{
|
||||
camera::{Camera, CameraMode},
|
||||
figure::FigureMgr,
|
||||
particle::ParticleMgr,
|
||||
@ -135,6 +135,9 @@ impl Scene {
|
||||
/// Get a reference to the scene's particle manager.
|
||||
pub fn particle_mgr(&self) -> &ParticleMgr { &self.particle_mgr }
|
||||
|
||||
/// Get a mutable reference to the scene's particle manager.
|
||||
pub fn particle_mgr_mut(&mut self) -> &mut ParticleMgr { &mut self.particle_mgr }
|
||||
|
||||
/// Get a reference to the scene's figure manager.
|
||||
pub fn figure_mgr(&self) -> &FigureMgr { &self.figure_mgr }
|
||||
|
||||
@ -273,6 +276,7 @@ impl Scene {
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
..
|
||||
} = self.camera.dependents();
|
||||
|
||||
// Update chunk loaded distance smoothly for nice shader fog
|
||||
@ -401,7 +405,7 @@ impl Scene {
|
||||
|
||||
// Maintain audio
|
||||
self.sfx_mgr
|
||||
.maintain(audio, scene_data.state, scene_data.player_entity);
|
||||
.maintain(audio, scene_data.state, scene_data.player_entity, &self.camera);
|
||||
self.music_mgr.maintain(audio, scene_data.state);
|
||||
}
|
||||
|
||||
|
@ -10,13 +10,14 @@ use common::{
|
||||
assets,
|
||||
comp::{object, Body, CharacterState, Pos},
|
||||
figure::Segment,
|
||||
outcome::Outcome,
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use hashbrown::HashMap;
|
||||
use rand::Rng;
|
||||
use specs::{Join, WorldExt};
|
||||
use std::time::{Duration, Instant};
|
||||
use vek::Vec3;
|
||||
use vek::*;
|
||||
|
||||
struct Particles {
|
||||
alive_until: Instant, // created_at + lifespan
|
||||
@ -45,6 +46,24 @@ impl ParticleMgr {
|
||||
|
||||
pub fn particle_count_visible(&self) -> usize { self.instances.count() }
|
||||
|
||||
pub fn handle_outcome(&mut self, outcome: &Outcome, scene_data: &SceneData) {
|
||||
let time = scene_data.state.get_time();
|
||||
let now = Instant::now();
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
match outcome {
|
||||
Outcome::Explosion { pos, power } => {
|
||||
for _ in 0..64 {
|
||||
self.particles.push(Particles {
|
||||
alive_until: now + Duration::from_secs(4),
|
||||
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireSmoke, *pos + Vec2::<f32>::zero().map(|_| rng.gen_range(-1.0, 1.0) * power)),
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
|
||||
if scene_data.particles_enabled {
|
||||
let now = Instant::now();
|
||||
|
@ -171,6 +171,7 @@ impl Scene {
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
..
|
||||
} = self.camera.dependents();
|
||||
const VD: f32 = 115.0; // View Distance
|
||||
const TIME: f64 = 43200.0; // 12 hours*3600 seconds
|
||||
|
@ -24,6 +24,7 @@ use common::{
|
||||
terrain::{Block, BlockKind},
|
||||
util::Dir,
|
||||
vol::ReadVol,
|
||||
outcome::Outcome,
|
||||
};
|
||||
use specs::{Join, WorldExt};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration, convert::TryFrom};
|
||||
@ -100,7 +101,7 @@ impl SessionState {
|
||||
}
|
||||
|
||||
/// Tick the session (and the client attached to it).
|
||||
fn tick(&mut self, dt: Duration, global_state: &mut GlobalState) -> Result<TickAction, Error> {
|
||||
fn tick(&mut self, dt: Duration, global_state: &mut GlobalState, outcomes: &mut Vec<Outcome>) -> Result<TickAction, Error> {
|
||||
self.inputs.tick(dt);
|
||||
|
||||
let mut client = self.client.borrow_mut();
|
||||
@ -158,15 +159,7 @@ impl SessionState {
|
||||
global_state.settings.graphics.view_distance = vd;
|
||||
global_state.settings.save_to_file_warn();
|
||||
},
|
||||
client::Event::Outcome(outcome) => {
|
||||
if let Ok(sfx_event_item) = SfxEventItem::try_from(outcome) {
|
||||
client
|
||||
.state()
|
||||
.ecs()
|
||||
.read_resource::<EventBus<SfxEventItem>>()
|
||||
.emit_now(sfx_event_item);
|
||||
}
|
||||
},
|
||||
client::Event::Outcome(outcome) => outcomes.push(outcome),
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +211,7 @@ impl PlayState for SessionState {
|
||||
.camera_mut()
|
||||
.compute_dependents(&*self.client.borrow().state().terrain());
|
||||
let camera::Dependents {
|
||||
view_mat, cam_pos, ..
|
||||
cam_pos, cam_dir, ..
|
||||
} = self.scene.camera().dependents();
|
||||
|
||||
let (is_aiming, aim_dir_offset) = {
|
||||
@ -241,8 +234,6 @@ impl PlayState for SessionState {
|
||||
};
|
||||
self.is_aiming = is_aiming;
|
||||
|
||||
let cam_dir: Vec3<f32> = Vec3::from(view_mat.inverted() * -Vec4::unit_z());
|
||||
|
||||
// Check to see whether we're aiming at anything
|
||||
let (build_pos, select_pos, target_entity) =
|
||||
under_cursor(&self.client.borrow(), cam_pos, cam_dir);
|
||||
@ -642,10 +633,12 @@ impl PlayState for SessionState {
|
||||
|
||||
self.inputs.climb = self.key_state.climb();
|
||||
|
||||
let mut outcomes = Vec::new();
|
||||
|
||||
// Runs if either in a multiplayer server or the singleplayer server is unpaused
|
||||
if !global_state.paused() {
|
||||
// Perform an in-game tick.
|
||||
match self.tick(global_state.clock.get_avg_delta(), global_state) {
|
||||
match self.tick(global_state.clock.get_avg_delta(), global_state, &mut outcomes) {
|
||||
Ok(TickAction::Continue) => {}, // Do nothing
|
||||
Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu
|
||||
Err(err) => {
|
||||
@ -1030,6 +1023,18 @@ impl PlayState for SessionState {
|
||||
&mut global_state.audio,
|
||||
&scene_data,
|
||||
);
|
||||
|
||||
// Process outcomes from client
|
||||
for outcome in outcomes {
|
||||
if let Ok(sfx_event_item) = SfxEventItem::try_from(&outcome) {
|
||||
client
|
||||
.state()
|
||||
.ecs()
|
||||
.read_resource::<EventBus<SfxEventItem>>()
|
||||
.emit_now(sfx_event_item);
|
||||
}
|
||||
self.scene.particle_mgr_mut().handle_outcome(&outcome, &scene_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user