diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index e9705d5505..70580be584 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -132,4 +132,4 @@ void main() { all_mat * vec4(f_pos, 1); gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); -} \ No newline at end of file +} diff --git a/client/src/lib.rs b/client/src/lib.rs index 404c2a1686..08109865db 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -30,6 +30,7 @@ use common::{ sync::{Uid, UidAllocator, WorldSyncExt}, terrain::{block::Block, TerrainChunk, TerrainChunkSize}, vol::RectVolSize, + outcome::Outcome, }; use futures_executor::block_on; use futures_timer::Delay; @@ -66,6 +67,7 @@ pub enum Event { InventoryUpdated(InventoryUpdateEvent), Notification(Notification), SetViewDistance(u32), + Outcome(Outcome), } pub struct Client { @@ -1229,6 +1231,9 @@ impl Client { self.view_distance = Some(vd); frontend_events.push(Event::SetViewDistance(vd)); }, + ServerMsg::Outcomes(outcomes) => frontend_events.extend(outcomes + .into_iter() + .map(Event::Outcome)), } } } diff --git a/common/src/lib.rs b/common/src/lib.rs index fb04ff28b1..3bddacde79 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -24,6 +24,7 @@ pub mod generation; pub mod loadout_builder; pub mod msg; pub mod npc; +pub mod outcome; pub mod path; pub mod ray; pub mod recipe; diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 156c18fd61..d6d55e46e8 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -2,6 +2,7 @@ use super::{ClientState, EcsCompPacket}; use crate::{ character::CharacterItem, comp, + outcome::Outcome, recipe::RecipeBook, state, sync, sync::Uid, @@ -120,6 +121,7 @@ pub enum ServerMsg { /// Send a popup notification such as "Waypoint Saved" Notification(Notification), SetViewDistance(u32), + Outcomes(Vec), } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/common/src/outcome.rs b/common/src/outcome.rs new file mode 100644 index 0000000000..9f047bcc26 --- /dev/null +++ b/common/src/outcome.rs @@ -0,0 +1,30 @@ +use crate::comp; +use serde::{Deserialize, Serialize}; +use vek::*; + +/// An outcome represents the final result of an instantaneous event. It implies that said event has +/// already occurred. It is not a request for that event to occur, nor is it something that may be +/// cancelled or otherwise altered. Its primary purpose is to act as something for frontends (both +/// server and client) to listen to in order to receive feedback about events in the world. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum Outcome { + Explosion { + pos: Vec3, + power: f32, + }, + ProjectileShot { + pos: Vec3, + body: comp::Body, + vel: Vec3, + }, +} + + +impl Outcome { + pub fn get_pos(&self) -> Option> { + match self { + Outcome::Explosion { pos, .. } => Some(*pos), + Outcome::ProjectileShot { pos, .. } => Some(*pos), + } + } +} diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index a62c0b342c..0d9a8ff61a 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -5,6 +5,7 @@ use common::{ Projectile, Scale, Stats, Vel, WaypointArea, }, util::Dir, + outcome::Outcome, }; use specs::{Builder, Entity as EcsEntity, WorldExt}; use vek::{Rgb, Vec3}; @@ -89,10 +90,15 @@ pub fn handle_shoot( .expect("Failed to fetch entity") .0; + let vel = *dir * 100.0; + + // Add an outcome + state.ecs().write_resource::>().push(Outcome::ProjectileShot { pos, body, vel }); + // TODO: Player height pos.z += 1.2; - let mut builder = state.create_projectile(Pos(pos), Vel(*dir * 100.0), body, projectile); + let mut builder = state.create_projectile(Pos(pos), Vel(vel), body, projectile); if let Some(light) = light { builder = builder.with(light) } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 0a73123a28..d87e61ea08 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -6,6 +6,7 @@ use common::{ HealthChange, HealthSource, Player, Pos, Stats, }, msg::{PlayerListUpdate, ServerMsg}, + outcome::Outcome, state::BlockChange, sync::{Uid, UidAllocator, WorldSyncExt}, sys::combat::BLOCK_ANGLE, @@ -277,25 +278,16 @@ pub fn handle_respawn(server: &Server, entity: EcsEntity) { } } -pub fn handle_explosion( - server: &Server, - pos: Vec3, - power: f32, - owner: Option, - friendly_damage: bool, -) { - // Go through all other entities - let hit_range = 3.0 * power; +pub fn handle_explosion(server: &Server, pos: Vec3, power: f32, owner: Option) { let ecs = &server.state.ecs(); - let owner_entity = owner.and_then(|uid| { - ecs.read_resource::() - .retrieve_entity_internal(uid.into()) - }); - let groups = ecs.read_storage::(); + // Add an outcome + ecs.write_resource::>() + .push(Outcome::Explosion { pos, power }); - for (entity_b, pos_b, ori_b, character_b, stats_b, loadout_b) in ( - &ecs.entities(), + // Go through all other entities + let hit_range = 3.0 * power; + for (pos_b, ori_b, character_b, stats_b, loadout_b) in ( &ecs.read_storage::(), &ecs.read_storage::(), ecs.read_storage::().maybe(), diff --git a/server/src/lib.rs b/server/src/lib.rs index 7d27b933ab..1447793c98 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -34,6 +34,7 @@ use common::{ comp::{self, ChatType}, event::{EventBus, ServerEvent}, msg::{ClientState, ServerInfo, ServerMsg}, + outcome::Outcome, recipe::default_recipe_book, state::{State, TimeOfDay}, sync::WorldSyncExt, @@ -118,6 +119,7 @@ impl Server { state .ecs_mut() .insert(comp::AdminList(settings.admins.clone())); + state.ecs_mut().insert(Vec::::new()); // System timers for performance monitoring state.ecs_mut().insert(sys::EntitySyncTimer::default()); diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index d4bbf62d20..6e4a5fa897 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -7,15 +7,19 @@ use crate::{ Tick, }; use common::{ - comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Pos, Vel}, + comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Pos, Vel, Player}, msg::ServerMsg, + outcome::Outcome, region::{Event as RegionEvent, RegionMap}, state::TimeOfDay, sync::{CompSyncPackage, Uid}, + vol::RectVolSize, + terrain::TerrainChunkSize, }; use specs::{ Entities, Entity as EcsEntity, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage, }; +use vek::*; /// This system will send physics updates to the client pub struct Sys; @@ -33,6 +37,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Ori>, ReadStorage<'a, Inventory>, ReadStorage<'a, RegionSubscription>, + ReadStorage<'a, Player>, WriteStorage<'a, Last>, WriteStorage<'a, Last>, WriteStorage<'a, Last>, @@ -40,6 +45,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, ForceUpdate>, WriteStorage<'a, InventoryUpdate>, Write<'a, DeletedEntities>, + Write<'a, Vec>, TrackedComps<'a>, ReadTrackers<'a>, ); @@ -58,6 +64,7 @@ impl<'a> System<'a> for Sys { orientations, inventories, subscriptions, + players, mut last_pos, mut last_vel, mut last_ori, @@ -65,6 +72,7 @@ impl<'a> System<'a> for Sys { mut force_updates, mut inventory_updates, mut deleted_entities, + mut outcomes, tracked_comps, trackers, ): Self::SystemData, @@ -316,6 +324,22 @@ impl<'a> System<'a> for Sys { )); } + // Sync outcomes + for (client, player, pos) in (&mut clients, &players, positions.maybe()).join() { + let is_near = |o_pos: Vec3| pos + .zip_with(player.view_distance, |pos, vd| pos.0.xy().distance_squared(o_pos.xy()) < (vd as f32 * TerrainChunkSize::RECT_SIZE.x as f32).powf(2.0)); + + let outcomes = outcomes + .iter() + .filter(|o| o.get_pos().and_then(&is_near).unwrap_or(true)) + .cloned() + .collect::>(); + if outcomes.len() > 0 { + client.notify(ServerMsg::Outcomes(outcomes)); + } + } + outcomes.clear(); + // Remove all force flags. force_updates.clear(); inventory_updates.clear(); diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index b76c287cdb..669065f1cc 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -93,6 +93,7 @@ use common::{ }, event::EventBus, state::State, + outcome::Outcome, }; use event_mapper::SfxEventMapper; use hashbrown::HashMap; @@ -188,6 +189,18 @@ impl From<&InventoryUpdateEvent> for SfxEvent { } } +impl TryFrom for SfxEventItem { + type Error = (); + + fn try_from(outcome: Outcome) -> Result { + 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)), + _ => Err(()), + } + } +} + #[derive(Deserialize)] pub struct SfxTriggerItem { pub files: Vec, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 9c5122cee8..abf55456bc 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -26,7 +26,7 @@ use common::{ vol::ReadVol, }; use specs::{Join, WorldExt}; -use std::{cell::RefCell, rc::Rc, time::Duration}; +use std::{cell::RefCell, rc::Rc, time::Duration, convert::TryFrom}; use tracing::{error, info}; use vek::*; @@ -158,6 +158,15 @@ 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::>() + .emit_now(sfx_event_item); + } + }, } }