diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index bd9434f765..3eb3a6d828 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,5 +831,11 @@ ], threshold: 0.2, ), + Utterance(Angry, BipedLarge((species: Wendigo, body_type: Female))): ( + files: [ + "voxygen.audio.sfx.utterance.wendigo_angry", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg new file mode 100644 index 0000000000..f82a249e02 Binary files /dev/null and b/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg differ diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg new file mode 100644 index 0000000000..05d9f248d8 Binary files /dev/null and b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg differ diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg new file mode 100644 index 0000000000..830f70d6d2 Binary files /dev/null and b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg differ diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 b/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 new file mode 100644 index 0000000000..87d5aec70f Binary files /dev/null and b/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 differ diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg b/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg new file mode 100644 index 0000000000..2c3fcc125c Binary files /dev/null and b/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg differ diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 8b792f361f..7c64f62fca 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, ship, Body}, + comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, ship, Body, UtteranceKind}, path::Chaser, rtsim::RtSimController, trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult}, @@ -96,7 +96,7 @@ bitflags::bitflags! { /// # Behavior Component /// This component allow an Entity to register one or more behavior tags. -/// These tags act as flags of what an Entity can do, or what it is doing. +/// These tags act as flags of what an Entity can do, or what it is doing. /// Behaviors Tags can be added and removed as the Entity lives, to update its /// state when needed #[derive(Default, Copy, Clone, Debug)] @@ -117,7 +117,7 @@ impl From for Behavior { } impl Behavior { - /// Builder function + /// Builder function /// Set capabilities if Option is Some pub fn maybe_with_capabilities( mut self, @@ -297,6 +297,7 @@ pub enum SoundKind { Explosion, Beam, Shockwave, + Utterance(UtteranceKind, Body), } #[derive(Clone, Debug)] diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 9e13b4e327..d157b87853 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -96,6 +96,13 @@ pub enum GroupManip { AssignLeader(Uid), } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum UtteranceKind { + Calm, + Angry, + Surprised, +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ControlEvent { //ToggleLantern, @@ -111,6 +118,7 @@ pub enum ControlEvent { GroupManip(GroupManip), RemoveBuff(BuffKind), Respawn, + Utterance(UtteranceKind), } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 8b6b775349..c8998b4a7d 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -67,7 +67,7 @@ pub use self::{ combo::Combo, controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr, - InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, + InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, UtteranceKind, }, energy::{Energy, EnergyChange, EnergySource}, fluid_dynamics::Fluid, diff --git a/common/src/outcome.rs b/common/src/outcome.rs index d1ec35e15c..feeb1e8fec 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -1,5 +1,5 @@ use crate::{comp, uid::Uid}; -use comp::{beam, item::Reagent, poise::PoiseState, skills::SkillGroupKind}; +use comp::{beam, item::Reagent, poise::PoiseState, skills::SkillGroupKind, UtteranceKind}; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; use vek::*; @@ -73,6 +73,11 @@ pub enum Outcome { GroundSlam { pos: Vec3, }, + Utterance { + pos: Vec3, + body: comp::Body, + kind: UtteranceKind, + }, } impl Outcome { @@ -87,7 +92,8 @@ impl Outcome { | Outcome::Damage { pos, .. } | Outcome::Block { pos, .. } | Outcome::PoiseChange { pos, .. } - | Outcome::GroundSlam { pos } => Some(*pos), + | Outcome::GroundSlam { pos } + | Outcome::Utterance { pos, .. } => Some(*pos), Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)), Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None, } diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index 42b448ad80..daeb8f36e3 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -1,5 +1,5 @@ use common::{ - comp::{BuffChange, ControlEvent, Controller}, + comp::{BuffChange, ControlEvent, Controller, Pos, Body, agent::{Sound, SoundKind}}, event::{EventBus, ServerEvent}, uid::UidAllocator, }; @@ -7,7 +7,7 @@ use common_ecs::{Job, Origin, Phase, System}; use specs::{ saveload::{Marker, MarkerAllocator}, shred::ResourceId, - Entities, Join, Read, SystemData, World, WriteStorage, + Entities, Join, Read, SystemData, World, WriteStorage, ReadStorage, }; use vek::*; @@ -16,6 +16,8 @@ pub struct ReadData<'a> { entities: Entities<'a>, uid_allocator: Read<'a, UidAllocator>, server_bus: Read<'a, EventBus>, + positions: ReadStorage<'a, Pos>, + bodies: ReadStorage<'a, Body>, } #[derive(Default)] @@ -92,6 +94,20 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::GroupManip(entity, manip)) }, ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)), + ControlEvent::Utterance(kind) => { + if let (Some(pos), Some(body)) = ( + read_data.positions.get(entity), + read_data.bodies.get(entity), + ) { + let sound = Sound::new( + SoundKind::Utterance(kind, *body), + pos.0, + 8.0, // TODO: Come up with a better way of determining this + 1.0, + ); + server_emitter.emit(ServerEvent::Sound { sound }); + } + }, } } } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 03b5fe992a..f3b380f231 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -6,7 +6,7 @@ use common::{ assets, comp::{ self, - agent::{AgentEvent, Sound, MAX_LISTEN_DIST}, + agent::{AgentEvent, Sound, MAX_LISTEN_DIST, SoundKind}, dialogue::Subject, inventory::slot::EquipSlot, item, @@ -386,6 +386,7 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { let positions = &ecs.read_storage::(); let agents = &mut ecs.write_storage::(); + // TODO: Reduce the complexity of this problem by using spatial partitioning system for (agent, agent_pos) in (agents, positions).join() { // TODO: Use pathfinding for more dropoff around obstacles let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos); @@ -402,5 +403,19 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { .inbox .push_back(AgentEvent::ServerSound(propagated_sound)); } + + // Attempt to turn this sound into an outcome to be received by frontends. + if let Some(outcome) = match sound.kind { + SoundKind::Utterance(kind, body) => Some(Outcome::Utterance { + kind, + pos: sound.pos, + body, + }), + _ => None, + } { + ecs + .write_resource::>() + .push(outcome); + } } } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index addd351b2f..67dc39634d 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -21,7 +21,7 @@ use common::{ Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility, CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos, - Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, + Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, UtteranceKind, }, consts::GRAVITY, effect::{BuffEffect, Effect}, @@ -1548,6 +1548,10 @@ impl<'a> AgentData<'a> { .min_by_key(|(_, e_pos, _, _, _, _, _)| (e_pos.0.distance_squared(self.pos.0) * 100.0) as i32) // TODO choose target by more than just distance .map(|(e, _, _, _, _, _, _)| e); + if agent.target.is_none() && target.is_some() { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry)); + } + agent.target = target.map(|target| Target { target, hostile: true, diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index f0ab9e4a55..57198031ed 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -96,6 +96,7 @@ use common::{ object, poise::PoiseState, Body, CharacterAbilityType, InventoryUpdateEvent, + UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -182,6 +183,7 @@ pub enum SfxEvent { FlameThrower, PoiseChange(PoiseState), GroundSlam, + Utterance(UtteranceKind, Body), } #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -452,6 +454,10 @@ impl SfxMgr { audio.emit_sfx(sfx_trigger_item, *pos, None, false); }, }, + Outcome::Utterance { pos, kind, body } => { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Utterance(*kind, *body)); + audio.emit_sfx(sfx_trigger_item, *pos, None, false); + }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } | Outcome::SummonedCreature { .. } => {}, diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 76bd714891..1b70bd660f 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -248,7 +248,8 @@ impl ParticleMgr { | Outcome::SkillPointGain { .. } | Outcome::ComboChange { .. } | Outcome::Damage { .. } - | Outcome::PoiseChange { .. } => {}, + | Outcome::PoiseChange { .. } + | Outcome::Utterance { .. } => {}, } }