diff --git a/common/src/event.rs b/common/src/event.rs index 51b7c8d451..bb78f6b15c 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -1,17 +1,18 @@ use crate::{ character::CharacterId, - comp, + comp::{ + self, + invite::{InviteKind, InviteResponse}, + item::Item, + Ori, Pos, + }, + outcome::Outcome, rtsim::RtSimEntity, trade::{TradeAction, TradeId}, uid::Uid, util::Dir, Explosion, }; -use comp::{ - invite::{InviteKind, InviteResponse}, - item::Item, - Ori, Pos, -}; use specs::Entity as EcsEntity; use std::{collections::VecDeque, ops::DerefMut, sync::Mutex}; use vek::*; @@ -30,6 +31,8 @@ pub enum LocalEvent { Boost { entity: EcsEntity, vel: Vec3 }, /// Updates the position of the entity PositionUpdate { entity: EcsEntity, pos: Pos }, + /// Creates an outcome + CreateOutcome(Outcome), } #[allow(clippy::large_enum_variant)] // TODO: Pending review in #587 diff --git a/common/src/outcome.rs b/common/src/outcome.rs index 56cc92979c..0fbe660863 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -45,6 +45,10 @@ pub enum Outcome { pos: Vec3, color: Option>, }, + SummonedCreature { + pos: Vec3, + body: comp::Body, + }, } impl Outcome { @@ -53,7 +57,8 @@ impl Outcome { Outcome::Explosion { pos, .. } | Outcome::ProjectileShot { pos, .. } | Outcome::Beam { pos, .. } - | Outcome::SkillPointGain { pos, .. } => Some(*pos), + | Outcome::SkillPointGain { pos, .. } + | Outcome::SummonedCreature { pos, .. } => Some(*pos), Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)), Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None, } diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 0df53d2e37..17dd549fcf 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -4,7 +4,8 @@ use crate::{ inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig}, CharacterState, StateUpdate, }, - event::ServerEvent, + event::{LocalEvent, ServerEvent}, + outcome::Outcome, skillset_builder::{SkillSetBuilder, SkillSetConfig}, states::{ behavior::{CharacterBehavior, JoinData}, @@ -92,6 +93,7 @@ impl CharacterBehavior for Data { ) .build(); + // Send server event to create npc update.server_events.push_front(ServerEvent::CreateNpc { pos: *data.pos, stats, @@ -114,6 +116,14 @@ impl CharacterBehavior for Data { rtsim_entity: None, }); + // Send local event used for frontend shenanigans + update.local_events.push_front(LocalEvent::CreateOutcome( + Outcome::SummonedCreature { + pos: data.pos.0, + body, + }, + )); + update.character = CharacterState::BasicSummon(Data { timer: self .timer diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index e7aee77b24..b598b578de 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -8,6 +8,7 @@ use common::{ comp, depot::{Depot, Id}, event::{EventBus, LocalEvent, ServerEvent}, + outcome::Outcome, region::RegionMap, resources::{DeltaTime, GameMode, PlayerEntity, Time, TimeOfDay}, slowjob::SlowJobPool, @@ -515,6 +516,9 @@ impl State { *position = pos; } }, + LocalEvent::CreateOutcome(outcome) => { + self.ecs.write_resource::>().push(outcome); + }, } } drop(guard); diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 2900ef4559..cf5f816cc2 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -364,11 +364,13 @@ impl SfxMgr { audio.play_sfx(file_ref, *pos, None); }, }, - Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => {}, Outcome::BreakBlock { pos, .. } => { let file_ref = "voxygen.audio.sfx.footsteps.stone_step_1"; audio.play_sfx(file_ref, pos.map(|e| e as f32 + 0.5), Some(3.0)); }, + Outcome::ExpChange { .. } + | Outcome::ComboChange { .. } + | Outcome::SummonedCreature { .. } => {}, } } diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 2706b4c878..b52930c65e 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -9,8 +9,8 @@ use crate::{ use common::{ assets::{AssetExt, DotVoxAsset}, comp::{ - self, aura, beam, buff, item::Reagent, object, BeamSegment, Body, CharacterState, Ori, Pos, - Shockwave, Vel, + self, aura, beam, body, buff, item::Reagent, object, BeamSegment, Body, CharacterState, + Ori, Pos, Shockwave, Vel, }, figure::Segment, outcome::Outcome, @@ -156,7 +156,6 @@ impl ParticleMgr { ); } }, - Outcome::ProjectileShot { .. } => {}, Outcome::BreakBlock { pos, .. } => { // TODO: Use color field when particle colors are a thing self.particles.resize_with(self.particles.len() + 30, || { @@ -168,7 +167,40 @@ impl ParticleMgr { ) }); }, - _ => {}, + Outcome::SummonedCreature { pos, body } => match body { + Body::BipedSmall(b) if matches!(b.species, body::biped_small::Species::Husk) => { + self.particles.resize_with( + self.particles.len() + + 2 * usize::from(self.scheduler.heartbeats(Duration::from_millis(1))), + || { + let start_pos = pos + Vec3::unit_z() * body.height() / 2.0; + let end_pos = pos + + Vec3::new( + 2.0 * rng.gen::() - 1.0, + 2.0 * rng.gen::() - 1.0, + 0.0, + ) + .normalized() + * (body.radius() + 4.0) + + Vec3::unit_z() * (body.height() + 2.0) * rng.gen::(); + + Particle::new_directed( + Duration::from_secs_f32(0.5), + time, + ParticleMode::CultistFlame, + start_pos, + end_pos, + ) + }, + ); + }, + _ => {}, + }, + Outcome::ProjectileShot { .. } + | Outcome::Beam { .. } + | Outcome::ExpChange { .. } + | Outcome::SkillPointGain { .. } + | Outcome::ComboChange { .. } => {}, } }