mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added outcome system, sound effects
This commit is contained in:
@ -30,6 +30,7 @@ use common::{
|
|||||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||||
terrain::{block::Block, TerrainChunk, TerrainChunkSize},
|
terrain::{block::Block, TerrainChunk, TerrainChunkSize},
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
|
outcome::Outcome,
|
||||||
};
|
};
|
||||||
use futures_executor::block_on;
|
use futures_executor::block_on;
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
@ -66,6 +67,7 @@ pub enum Event {
|
|||||||
InventoryUpdated(InventoryUpdateEvent),
|
InventoryUpdated(InventoryUpdateEvent),
|
||||||
Notification(Notification),
|
Notification(Notification),
|
||||||
SetViewDistance(u32),
|
SetViewDistance(u32),
|
||||||
|
Outcome(Outcome),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
@ -1229,6 +1231,9 @@ impl Client {
|
|||||||
self.view_distance = Some(vd);
|
self.view_distance = Some(vd);
|
||||||
frontend_events.push(Event::SetViewDistance(vd));
|
frontend_events.push(Event::SetViewDistance(vd));
|
||||||
},
|
},
|
||||||
|
ServerMsg::Outcomes(outcomes) => frontend_events.extend(outcomes
|
||||||
|
.into_iter()
|
||||||
|
.map(Event::Outcome)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ pub mod generation;
|
|||||||
pub mod loadout_builder;
|
pub mod loadout_builder;
|
||||||
pub mod msg;
|
pub mod msg;
|
||||||
pub mod npc;
|
pub mod npc;
|
||||||
|
pub mod outcome;
|
||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod ray;
|
pub mod ray;
|
||||||
pub mod recipe;
|
pub mod recipe;
|
||||||
|
@ -2,6 +2,7 @@ use super::{ClientState, EcsCompPacket};
|
|||||||
use crate::{
|
use crate::{
|
||||||
character::CharacterItem,
|
character::CharacterItem,
|
||||||
comp,
|
comp,
|
||||||
|
outcome::Outcome,
|
||||||
recipe::RecipeBook,
|
recipe::RecipeBook,
|
||||||
state, sync,
|
state, sync,
|
||||||
sync::Uid,
|
sync::Uid,
|
||||||
@ -120,6 +121,7 @@ pub enum ServerMsg {
|
|||||||
/// Send a popup notification such as "Waypoint Saved"
|
/// Send a popup notification such as "Waypoint Saved"
|
||||||
Notification(Notification),
|
Notification(Notification),
|
||||||
SetViewDistance(u32),
|
SetViewDistance(u32),
|
||||||
|
Outcomes(Vec<Outcome>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
30
common/src/outcome.rs
Normal file
30
common/src/outcome.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ use common::{
|
|||||||
Projectile, Scale, Stats, Vel, WaypointArea,
|
Projectile, Scale, Stats, Vel, WaypointArea,
|
||||||
},
|
},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
|
outcome::Outcome,
|
||||||
};
|
};
|
||||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||||
use vek::{Rgb, Vec3};
|
use vek::{Rgb, Vec3};
|
||||||
@ -89,10 +90,15 @@ pub fn handle_shoot(
|
|||||||
.expect("Failed to fetch entity")
|
.expect("Failed to fetch entity")
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
|
let vel = *dir * 100.0;
|
||||||
|
|
||||||
|
// Add an outcome
|
||||||
|
state.ecs().write_resource::<Vec<Outcome>>().push(Outcome::ProjectileShot { pos, body, vel });
|
||||||
|
|
||||||
// TODO: Player height
|
// TODO: Player height
|
||||||
pos.z += 1.2;
|
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 {
|
if let Some(light) = light {
|
||||||
builder = builder.with(light)
|
builder = builder.with(light)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use common::{
|
|||||||
HealthChange, HealthSource, Player, Pos, Stats,
|
HealthChange, HealthSource, Player, Pos, Stats,
|
||||||
},
|
},
|
||||||
msg::{PlayerListUpdate, ServerMsg},
|
msg::{PlayerListUpdate, ServerMsg},
|
||||||
|
outcome::Outcome,
|
||||||
state::BlockChange,
|
state::BlockChange,
|
||||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||||
sys::combat::BLOCK_ANGLE,
|
sys::combat::BLOCK_ANGLE,
|
||||||
@ -277,25 +278,16 @@ pub fn handle_respawn(server: &Server, entity: EcsEntity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_explosion(
|
pub fn handle_explosion(server: &Server, pos: Vec3<f32>, power: f32, owner: Option<Uid>) {
|
||||||
server: &Server,
|
|
||||||
pos: Vec3<f32>,
|
|
||||||
power: f32,
|
|
||||||
owner: Option<Uid>,
|
|
||||||
friendly_damage: bool,
|
|
||||||
) {
|
|
||||||
// Go through all other entities
|
|
||||||
let hit_range = 3.0 * power;
|
|
||||||
let ecs = &server.state.ecs();
|
let ecs = &server.state.ecs();
|
||||||
|
|
||||||
let owner_entity = owner.and_then(|uid| {
|
// Add an outcome
|
||||||
ecs.read_resource::<UidAllocator>()
|
ecs.write_resource::<Vec<Outcome>>()
|
||||||
.retrieve_entity_internal(uid.into())
|
.push(Outcome::Explosion { pos, power });
|
||||||
});
|
|
||||||
let groups = ecs.read_storage::<comp::Group>();
|
|
||||||
|
|
||||||
for (entity_b, pos_b, ori_b, character_b, stats_b, loadout_b) in (
|
// Go through all other entities
|
||||||
&ecs.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::Pos>(),
|
||||||
&ecs.read_storage::<comp::Ori>(),
|
&ecs.read_storage::<comp::Ori>(),
|
||||||
ecs.read_storage::<comp::CharacterState>().maybe(),
|
ecs.read_storage::<comp::CharacterState>().maybe(),
|
||||||
|
@ -34,6 +34,7 @@ use common::{
|
|||||||
comp::{self, ChatType},
|
comp::{self, ChatType},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
msg::{ClientState, ServerInfo, ServerMsg},
|
msg::{ClientState, ServerInfo, ServerMsg},
|
||||||
|
outcome::Outcome,
|
||||||
recipe::default_recipe_book,
|
recipe::default_recipe_book,
|
||||||
state::{State, TimeOfDay},
|
state::{State, TimeOfDay},
|
||||||
sync::WorldSyncExt,
|
sync::WorldSyncExt,
|
||||||
@ -118,6 +119,7 @@ impl Server {
|
|||||||
state
|
state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.insert(comp::AdminList(settings.admins.clone()));
|
.insert(comp::AdminList(settings.admins.clone()));
|
||||||
|
state.ecs_mut().insert(Vec::<Outcome>::new());
|
||||||
|
|
||||||
// System timers for performance monitoring
|
// System timers for performance monitoring
|
||||||
state.ecs_mut().insert(sys::EntitySyncTimer::default());
|
state.ecs_mut().insert(sys::EntitySyncTimer::default());
|
||||||
|
@ -7,15 +7,19 @@ use crate::{
|
|||||||
Tick,
|
Tick,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Pos, Vel},
|
comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Pos, Vel, Player},
|
||||||
msg::ServerMsg,
|
msg::ServerMsg,
|
||||||
|
outcome::Outcome,
|
||||||
region::{Event as RegionEvent, RegionMap},
|
region::{Event as RegionEvent, RegionMap},
|
||||||
state::TimeOfDay,
|
state::TimeOfDay,
|
||||||
sync::{CompSyncPackage, Uid},
|
sync::{CompSyncPackage, Uid},
|
||||||
|
vol::RectVolSize,
|
||||||
|
terrain::TerrainChunkSize,
|
||||||
};
|
};
|
||||||
use specs::{
|
use specs::{
|
||||||
Entities, Entity as EcsEntity, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage,
|
Entities, Entity as EcsEntity, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage,
|
||||||
};
|
};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
/// This system will send physics updates to the client
|
/// This system will send physics updates to the client
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
@ -33,6 +37,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Ori>,
|
ReadStorage<'a, Ori>,
|
||||||
ReadStorage<'a, Inventory>,
|
ReadStorage<'a, Inventory>,
|
||||||
ReadStorage<'a, RegionSubscription>,
|
ReadStorage<'a, RegionSubscription>,
|
||||||
|
ReadStorage<'a, Player>,
|
||||||
WriteStorage<'a, Last<Pos>>,
|
WriteStorage<'a, Last<Pos>>,
|
||||||
WriteStorage<'a, Last<Vel>>,
|
WriteStorage<'a, Last<Vel>>,
|
||||||
WriteStorage<'a, Last<Ori>>,
|
WriteStorage<'a, Last<Ori>>,
|
||||||
@ -40,6 +45,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteStorage<'a, ForceUpdate>,
|
WriteStorage<'a, ForceUpdate>,
|
||||||
WriteStorage<'a, InventoryUpdate>,
|
WriteStorage<'a, InventoryUpdate>,
|
||||||
Write<'a, DeletedEntities>,
|
Write<'a, DeletedEntities>,
|
||||||
|
Write<'a, Vec<Outcome>>,
|
||||||
TrackedComps<'a>,
|
TrackedComps<'a>,
|
||||||
ReadTrackers<'a>,
|
ReadTrackers<'a>,
|
||||||
);
|
);
|
||||||
@ -58,6 +64,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
orientations,
|
orientations,
|
||||||
inventories,
|
inventories,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
|
players,
|
||||||
mut last_pos,
|
mut last_pos,
|
||||||
mut last_vel,
|
mut last_vel,
|
||||||
mut last_ori,
|
mut last_ori,
|
||||||
@ -65,6 +72,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut force_updates,
|
mut force_updates,
|
||||||
mut inventory_updates,
|
mut inventory_updates,
|
||||||
mut deleted_entities,
|
mut deleted_entities,
|
||||||
|
mut outcomes,
|
||||||
tracked_comps,
|
tracked_comps,
|
||||||
trackers,
|
trackers,
|
||||||
): Self::SystemData,
|
): 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.
|
// Remove all force flags.
|
||||||
force_updates.clear();
|
force_updates.clear();
|
||||||
inventory_updates.clear();
|
inventory_updates.clear();
|
||||||
|
@ -93,6 +93,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
state::State,
|
state::State,
|
||||||
|
outcome::Outcome,
|
||||||
};
|
};
|
||||||
use event_mapper::SfxEventMapper;
|
use event_mapper::SfxEventMapper;
|
||||||
use hashbrown::HashMap;
|
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)]
|
#[derive(Deserialize)]
|
||||||
pub struct SfxTriggerItem {
|
pub struct SfxTriggerItem {
|
||||||
pub files: Vec<String>,
|
pub files: Vec<String>,
|
||||||
|
@ -26,7 +26,7 @@ use common::{
|
|||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use specs::{Join, WorldExt};
|
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 tracing::{error, info};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -158,6 +158,15 @@ impl SessionState {
|
|||||||
global_state.settings.graphics.view_distance = vd;
|
global_state.settings.graphics.view_distance = vd;
|
||||||
global_state.settings.save_to_file_warn();
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user