Added outcome system, sound effects

This commit is contained in:
Joshua Barretto 2020-07-31 18:16:20 +01:00 committed by scott-c
parent 3f7667352d
commit 7c31baef6f
11 changed files with 104 additions and 20 deletions

View File

@ -132,4 +132,4 @@ void main() {
all_mat *
vec4(f_pos, 1);
gl_Position.z = -1000.0 / (gl_Position.z + 10000.0);
}
}

View File

@ -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)),
}
}
}

View File

@ -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;

View File

@ -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<Outcome>),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]

30
common/src/outcome.rs Normal file
View File

@ -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<f32>,
power: f32,
},
ProjectileShot {
pos: Vec3<f32>,
body: comp::Body,
vel: Vec3<f32>,
},
}
impl Outcome {
pub fn get_pos(&self) -> Option<Vec3<f32>> {
match self {
Outcome::Explosion { pos, .. } => Some(*pos),
Outcome::ProjectileShot { pos, .. } => Some(*pos),
}
}
}

View File

@ -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::<Vec<Outcome>>().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)
}

View File

@ -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<f32>,
power: f32,
owner: Option<Uid>,
friendly_damage: bool,
) {
// Go through all other entities
let hit_range = 3.0 * power;
pub fn handle_explosion(server: &Server, pos: Vec3<f32>, power: f32, owner: Option<Uid>) {
let ecs = &server.state.ecs();
let owner_entity = owner.and_then(|uid| {
ecs.read_resource::<UidAllocator>()
.retrieve_entity_internal(uid.into())
});
let groups = ecs.read_storage::<comp::Group>();
// Add an outcome
ecs.write_resource::<Vec<Outcome>>()
.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::<comp::Pos>(),
&ecs.read_storage::<comp::Ori>(),
ecs.read_storage::<comp::CharacterState>().maybe(),

View File

@ -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::<Outcome>::new());
// System timers for performance monitoring
state.ecs_mut().insert(sys::EntitySyncTimer::default());

View File

@ -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<Pos>>,
WriteStorage<'a, Last<Vel>>,
WriteStorage<'a, Last<Ori>>,
@ -40,6 +45,7 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, ForceUpdate>,
WriteStorage<'a, InventoryUpdate>,
Write<'a, DeletedEntities>,
Write<'a, Vec<Outcome>>,
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<f32>| 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::<Vec<_>>();
if outcomes.len() > 0 {
client.notify(ServerMsg::Outcomes(outcomes));
}
}
outcomes.clear();
// Remove all force flags.
force_updates.clear();
inventory_updates.clear();

View File

@ -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<Outcome> for SfxEventItem {
type Error = ();
fn try_from(outcome: 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)),
_ => Err(()),
}
}
}
#[derive(Deserialize)]
pub struct SfxTriggerItem {
pub files: Vec<String>,

View File

@ -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::<EventBus<SfxEventItem>>()
.emit_now(sfx_event_item);
}
},
}
}