mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/grunts' into 'master'
Utterances and sound optimisation See merge request veloren/veloren!2458
This commit is contained in:
commit
c63cf5f734
@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
- Added a skill tree for mining, which gains xp from mining ores and gems.
|
- Added a skill tree for mining, which gains xp from mining ores and gems.
|
||||||
- Added debug line info to release builds, enhancing the usefulness of panic backtraces
|
- Added debug line info to release builds, enhancing the usefulness of panic backtraces
|
||||||
|
- NPCs and animals can now make sounds in response to certain events
|
||||||
|
- Players can press H to greet others
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Entity-entity pushback is no longer applied in forced movement states like rolling and leaping.
|
- Entity-entity pushback is no longer applied in forced movement states like rolling and leaping.
|
||||||
@ -18,7 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Cases where no audio output could be produced before.
|
- Cases where no audio output could be produced before.
|
||||||
|
- Significantly improved the performance of playing sound effects
|
||||||
|
|
||||||
## [0.10.0] - 2021-06-12
|
## [0.10.0] - 2021-06-12
|
||||||
|
|
||||||
|
@ -831,5 +831,56 @@
|
|||||||
],
|
],
|
||||||
threshold: 0.2,
|
threshold: 0.2,
|
||||||
),
|
),
|
||||||
|
Utterance(Angry, Wendigo): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.wendigo_angry",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
|
Utterance(Angry, BipedLarge): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.ogre_angry",
|
||||||
|
"voxygen.audio.sfx.utterance.ogre_angry2",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
|
Utterance(Angry, Reptile): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.saurok_angry",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
|
Utterance(Angry, Bird): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.bird_angry",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
|
Utterance(Calm, Pig): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.pig_calm",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
|
Utterance(Calm, Cow): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.cow_calm",
|
||||||
|
"voxygen.audio.sfx.utterance.cow_calm2",
|
||||||
|
"voxygen.audio.sfx.utterance.cow_calm3",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
|
Utterance(Calm, Sheep): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.sheep_calm",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
|
Utterance(Greeting, HumanMale): (
|
||||||
|
files: [
|
||||||
|
"voxygen.audio.sfx.utterance.humanmale_greeting",
|
||||||
|
],
|
||||||
|
threshold: 0.2,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
BIN
assets/voxygen/audio/sfx/utterance/bird_angry.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/bird_angry.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/cow_calm.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/cow_calm.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/cow_calm2.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/cow_calm2.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/cow_calm3.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/cow_calm3.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/ogre_angry.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/ogre_angry.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/pig_calm.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/pig_calm.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/saurok_angry.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/saurok_angry.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/sheep_calm.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/sheep_calm.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -28,7 +28,7 @@ use common::{
|
|||||||
skills::Skill,
|
skills::Skill,
|
||||||
slot::Slot,
|
slot::Slot,
|
||||||
ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputKind,
|
ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputKind,
|
||||||
InventoryAction, InventoryEvent, InventoryUpdateEvent,
|
InventoryAction, InventoryEvent, InventoryUpdateEvent, UtteranceKind,
|
||||||
},
|
},
|
||||||
event::{EventBus, LocalEvent},
|
event::{EventBus, LocalEvent},
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
@ -1224,6 +1224,10 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn utter(&mut self, kind: UtteranceKind) {
|
||||||
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Utterance(kind)));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn toggle_sneak(&mut self) {
|
pub fn toggle_sneak(&mut self) {
|
||||||
let is_sneaking = self
|
let is_sneaking = self
|
||||||
.state
|
.state
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
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,
|
path::Chaser,
|
||||||
rtsim::RtSimController,
|
rtsim::RtSimController,
|
||||||
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
|
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
|
||||||
@ -297,6 +297,7 @@ pub enum SoundKind {
|
|||||||
Explosion,
|
Explosion,
|
||||||
Beam,
|
Beam,
|
||||||
Shockwave,
|
Shockwave,
|
||||||
|
Utterance(UtteranceKind, Body),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -96,6 +96,15 @@ pub enum GroupManip {
|
|||||||
AssignLeader(Uid),
|
AssignLeader(Uid),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum UtteranceKind {
|
||||||
|
Calm,
|
||||||
|
Angry,
|
||||||
|
Surprised,
|
||||||
|
Hurt,
|
||||||
|
Greeting,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum ControlEvent {
|
pub enum ControlEvent {
|
||||||
//ToggleLantern,
|
//ToggleLantern,
|
||||||
@ -111,6 +120,7 @@ pub enum ControlEvent {
|
|||||||
GroupManip(GroupManip),
|
GroupManip(GroupManip),
|
||||||
RemoveBuff(BuffKind),
|
RemoveBuff(BuffKind),
|
||||||
Respawn,
|
Respawn,
|
||||||
|
Utterance(UtteranceKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -68,6 +68,7 @@ pub use self::{
|
|||||||
controller::{
|
controller::{
|
||||||
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr,
|
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr,
|
||||||
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
|
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
|
||||||
|
UtteranceKind,
|
||||||
},
|
},
|
||||||
energy::{Energy, EnergyChange, EnergySource},
|
energy::{Energy, EnergyChange, EnergySource},
|
||||||
fluid_dynamics::Fluid,
|
fluid_dynamics::Fluid,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{comp, uid::Uid};
|
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 hashbrown::HashSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -73,6 +73,11 @@ pub enum Outcome {
|
|||||||
GroundSlam {
|
GroundSlam {
|
||||||
pos: Vec3<f32>,
|
pos: Vec3<f32>,
|
||||||
},
|
},
|
||||||
|
Utterance {
|
||||||
|
pos: Vec3<f32>,
|
||||||
|
body: comp::Body,
|
||||||
|
kind: UtteranceKind,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Outcome {
|
impl Outcome {
|
||||||
@ -87,7 +92,8 @@ impl Outcome {
|
|||||||
| Outcome::Damage { pos, .. }
|
| Outcome::Damage { pos, .. }
|
||||||
| Outcome::Block { pos, .. }
|
| Outcome::Block { pos, .. }
|
||||||
| Outcome::PoiseChange { 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::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
|
||||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
|
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{BuffChange, ControlEvent, Controller},
|
comp::{
|
||||||
|
agent::{Sound, SoundKind},
|
||||||
|
Body, BuffChange, ControlEvent, Controller, Pos,
|
||||||
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
uid::UidAllocator,
|
uid::UidAllocator,
|
||||||
};
|
};
|
||||||
@ -7,7 +10,7 @@ use common_ecs::{Job, Origin, Phase, System};
|
|||||||
use specs::{
|
use specs::{
|
||||||
saveload::{Marker, MarkerAllocator},
|
saveload::{Marker, MarkerAllocator},
|
||||||
shred::ResourceId,
|
shred::ResourceId,
|
||||||
Entities, Join, Read, SystemData, World, WriteStorage,
|
Entities, Join, Read, ReadStorage, SystemData, World, WriteStorage,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -16,6 +19,8 @@ pub struct ReadData<'a> {
|
|||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
uid_allocator: Read<'a, UidAllocator>,
|
uid_allocator: Read<'a, UidAllocator>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||||
|
positions: ReadStorage<'a, Pos>,
|
||||||
|
bodies: ReadStorage<'a, Body>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -92,6 +97,20 @@ impl<'a> System<'a> for Sys {
|
|||||||
server_emitter.emit(ServerEvent::GroupManip(entity, manip))
|
server_emitter.emit(ServerEvent::GroupManip(entity, manip))
|
||||||
},
|
},
|
||||||
ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)),
|
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 + Vec3::unit_z() * body.eye_height(),
|
||||||
|
8.0, // TODO: Come up with a better way of determining this
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
server_emitter.emit(ServerEvent::Sound { sound });
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use common::{
|
|||||||
assets,
|
assets,
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
agent::{AgentEvent, Sound, MAX_LISTEN_DIST},
|
agent::{AgentEvent, Sound, SoundKind, MAX_LISTEN_DIST},
|
||||||
dialogue::Subject,
|
dialogue::Subject,
|
||||||
inventory::slot::EquipSlot,
|
inventory::slot::EquipSlot,
|
||||||
item,
|
item,
|
||||||
@ -386,6 +386,8 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) {
|
|||||||
let positions = &ecs.read_storage::<comp::Pos>();
|
let positions = &ecs.read_storage::<comp::Pos>();
|
||||||
let agents = &mut ecs.write_storage::<comp::Agent>();
|
let agents = &mut ecs.write_storage::<comp::Agent>();
|
||||||
|
|
||||||
|
// TODO: Reduce the complexity of this problem by using spatial partitioning
|
||||||
|
// system
|
||||||
for (agent, agent_pos) in (agents, positions).join() {
|
for (agent, agent_pos) in (agents, positions).join() {
|
||||||
// TODO: Use pathfinding for more dropoff around obstacles
|
// TODO: Use pathfinding for more dropoff around obstacles
|
||||||
let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos);
|
let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos);
|
||||||
@ -403,4 +405,16 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) {
|
|||||||
.push_back(AgentEvent::ServerSound(propagated_sound));
|
.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::<Vec<Outcome>>().push(outcome);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ use common::{
|
|||||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility,
|
Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility,
|
||||||
CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange,
|
CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange,
|
||||||
InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos,
|
InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos,
|
||||||
Scale, SkillSet, Stats, UnresolvedChatMsg, Vel,
|
Scale, SkillSet, Stats, UnresolvedChatMsg, UtteranceKind, Vel,
|
||||||
},
|
},
|
||||||
consts::GRAVITY,
|
consts::GRAVITY,
|
||||||
effect::{BuffEffect, Effect},
|
effect::{BuffEffect, Effect},
|
||||||
@ -409,6 +409,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
.uid_allocator
|
.uid_allocator
|
||||||
.retrieve_entity_internal(by.id())
|
.retrieve_entity_internal(by.id())
|
||||||
{
|
{
|
||||||
|
if agent.target.is_none() {
|
||||||
|
controller.push_event(
|
||||||
|
ControlEvent::Utterance(
|
||||||
|
UtteranceKind::Angry,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
agent.target = Some(Target {
|
agent.target = Some(Target {
|
||||||
target: attacker,
|
target: attacker,
|
||||||
hostile: true,
|
hostile: true,
|
||||||
@ -511,6 +519,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
&mut event_emitter,
|
&mut event_emitter,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
if agent.target.is_none() {
|
||||||
|
controller.push_event(ControlEvent::Utterance(
|
||||||
|
UtteranceKind::Angry,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
agent.target = Some(Target {
|
agent.target = Some(Target {
|
||||||
target: attacker,
|
target: attacker,
|
||||||
hostile: true,
|
hostile: true,
|
||||||
@ -945,6 +959,10 @@ impl<'a> AgentData<'a> {
|
|||||||
controller.actions.push(ControlAction::Unwield);
|
controller.actions.push(ControlAction::Unwield);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if thread_rng().gen::<f32>() < 0.0015 {
|
||||||
|
controller.push_event(ControlEvent::Utterance(UtteranceKind::Calm));
|
||||||
|
}
|
||||||
|
|
||||||
// Sit
|
// Sit
|
||||||
if thread_rng().gen::<f32>() < 0.0035 {
|
if thread_rng().gen::<f32>() < 0.0035 {
|
||||||
controller.actions.push(ControlAction::Sit);
|
controller.actions.push(ControlAction::Sit);
|
||||||
@ -990,6 +1008,8 @@ impl<'a> AgentData<'a> {
|
|||||||
if self.look_toward(controller, read_data, &target) {
|
if self.look_toward(controller, read_data, &target) {
|
||||||
controller.actions.push(ControlAction::Stand);
|
controller.actions.push(ControlAction::Stand);
|
||||||
controller.actions.push(ControlAction::Talk);
|
controller.actions.push(ControlAction::Talk);
|
||||||
|
controller.push_event(ControlEvent::Utterance(UtteranceKind::Greeting));
|
||||||
|
|
||||||
match subject {
|
match subject {
|
||||||
Subject::Regular => {
|
Subject::Regular => {
|
||||||
if let (
|
if let (
|
||||||
@ -1548,6 +1568,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
|
.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);
|
.map(|(e, _, _, _, _, _, _)| e);
|
||||||
|
|
||||||
|
if agent.target.is_none() && target.is_some() {
|
||||||
|
controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry));
|
||||||
|
}
|
||||||
|
|
||||||
agent.target = target.map(|target| Target {
|
agent.target = target.map(|target| Target {
|
||||||
target,
|
target,
|
||||||
hostile: true,
|
hostile: true,
|
||||||
|
@ -209,7 +209,7 @@ impl AudioFrontend {
|
|||||||
// TODO: Should this take `underwater` into consideration?
|
// TODO: Should this take `underwater` into consideration?
|
||||||
match self.play_sfx(sfx_file, self.listener.pos, None, false) {
|
match self.play_sfx(sfx_file, self.listener.pos, None, false) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => warn!("Failed to play sfx. {}", e),
|
Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("Missing sfx trigger config for external sfx event.",);
|
debug!("Missing sfx trigger config for external sfx event.",);
|
||||||
@ -244,7 +244,7 @@ impl AudioFrontend {
|
|||||||
|
|
||||||
match self.play_sfx(sfx_file, position, volume, underwater) {
|
match self.play_sfx(sfx_file, position, volume, underwater) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => warn!("Failed to play sfx. {}", e),
|
Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
@ -265,8 +265,8 @@ impl AudioFrontend {
|
|||||||
) -> Result<(), rodio::decoder::DecoderError> {
|
) -> Result<(), rodio::decoder::DecoderError> {
|
||||||
if self.audio_stream.is_some() {
|
if self.audio_stream.is_some() {
|
||||||
let sound = OggSound::load_expect(sound)
|
let sound = OggSound::load_expect(sound)
|
||||||
.cloned()
|
.read()
|
||||||
.decoder()?
|
.to_source()
|
||||||
.amplify(vol.unwrap_or(1.0));
|
.amplify(vol.unwrap_or(1.0));
|
||||||
|
|
||||||
let listener = self.listener.clone();
|
let listener = self.listener.clone();
|
||||||
@ -291,9 +291,7 @@ impl AudioFrontend {
|
|||||||
) {
|
) {
|
||||||
if self.audio_stream.is_some() {
|
if self.audio_stream.is_some() {
|
||||||
if let Some(channel) = self.get_ambient_channel(channel_tag, volume_multiplier) {
|
if let Some(channel) = self.get_ambient_channel(channel_tag, volume_multiplier) {
|
||||||
if let Ok(sound) = OggSound::load_expect(sound).cloned().decoder() {
|
channel.play(OggSound::load_expect(sound).read().to_source());
|
||||||
channel.play(sound);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,9 +347,7 @@ impl AudioFrontend {
|
|||||||
fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) {
|
fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) {
|
||||||
if self.music_enabled() {
|
if self.music_enabled() {
|
||||||
if let Some(channel) = self.get_music_channel(channel_tag) {
|
if let Some(channel) = self.get_music_channel(channel_tag) {
|
||||||
if let Ok(sound) = OggSound::load_expect(sound).cloned().decoder() {
|
channel.play(OggSound::load_expect(sound).read().to_source(), channel_tag);
|
||||||
channel.play(sound, channel_tag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,11 +91,12 @@ use client::Client;
|
|||||||
use common::{
|
use common::{
|
||||||
assets::{self, AssetExt, AssetHandle},
|
assets::{self, AssetExt, AssetHandle},
|
||||||
comp::{
|
comp::{
|
||||||
beam,
|
beam, biped_large, humanoid,
|
||||||
item::{ItemKind, ToolKind},
|
item::{ItemKind, ToolKind},
|
||||||
object,
|
object,
|
||||||
poise::PoiseState,
|
poise::PoiseState,
|
||||||
Body, CharacterAbilityType, InventoryUpdateEvent,
|
quadruped_medium, quadruped_small, Body, CharacterAbilityType, InventoryUpdateEvent,
|
||||||
|
UtteranceKind,
|
||||||
},
|
},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
terrain::{BlockKind, TerrainChunk},
|
terrain::{BlockKind, TerrainChunk},
|
||||||
@ -105,7 +106,7 @@ use event_mapper::SfxEventMapper;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::warn;
|
use tracing::{debug, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// We watch the states of nearby entities in order to emit SFX at their
|
/// We watch the states of nearby entities in order to emit SFX at their
|
||||||
@ -182,6 +183,67 @@ pub enum SfxEvent {
|
|||||||
FlameThrower,
|
FlameThrower,
|
||||||
PoiseChange(PoiseState),
|
PoiseChange(PoiseState),
|
||||||
GroundSlam,
|
GroundSlam,
|
||||||
|
Utterance(UtteranceKind, VoiceKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||||
|
pub enum VoiceKind {
|
||||||
|
HumanFemale,
|
||||||
|
HumanMale,
|
||||||
|
BipedLarge,
|
||||||
|
Wendigo,
|
||||||
|
Reptile,
|
||||||
|
Bird,
|
||||||
|
Critter,
|
||||||
|
Sheep,
|
||||||
|
Pig,
|
||||||
|
Cow,
|
||||||
|
Canine,
|
||||||
|
BigCat,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body_to_voice(body: &Body) -> Option<VoiceKind> {
|
||||||
|
Some(match body {
|
||||||
|
Body::Humanoid(body) => match &body.body_type {
|
||||||
|
humanoid::BodyType::Female => VoiceKind::HumanFemale,
|
||||||
|
humanoid::BodyType::Male => VoiceKind::HumanMale,
|
||||||
|
},
|
||||||
|
Body::QuadrupedSmall(body) => match body.species {
|
||||||
|
quadruped_small::Species::Sheep => VoiceKind::Sheep,
|
||||||
|
quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig,
|
||||||
|
_ => VoiceKind::Critter,
|
||||||
|
},
|
||||||
|
Body::QuadrupedMedium(body) => match body.species {
|
||||||
|
quadruped_medium::Species::Saber
|
||||||
|
| quadruped_medium::Species::Tiger
|
||||||
|
| quadruped_medium::Species::Lion
|
||||||
|
| quadruped_medium::Species::Frostfang
|
||||||
|
| quadruped_medium::Species::Snowleopard => VoiceKind::BigCat,
|
||||||
|
quadruped_medium::Species::Wolf
|
||||||
|
| quadruped_medium::Species::Roshwalr
|
||||||
|
| quadruped_medium::Species::Tarasque
|
||||||
|
| quadruped_medium::Species::Darkhound
|
||||||
|
| quadruped_medium::Species::Bonerattler
|
||||||
|
| quadruped_medium::Species::Grolgar => VoiceKind::Canine,
|
||||||
|
quadruped_medium::Species::Cattle
|
||||||
|
| quadruped_medium::Species::Catoblepas
|
||||||
|
| quadruped_medium::Species::Highland
|
||||||
|
| quadruped_medium::Species::Yak
|
||||||
|
| quadruped_medium::Species::Moose
|
||||||
|
| quadruped_medium::Species::Dreadhorn => VoiceKind::Cow,
|
||||||
|
_ => return None,
|
||||||
|
},
|
||||||
|
Body::BirdMedium(_) | Body::BirdLarge(_) => VoiceKind::Bird,
|
||||||
|
Body::BipedLarge(body) => match body.species {
|
||||||
|
biped_large::Species::Wendigo => VoiceKind::Wendigo,
|
||||||
|
biped_large::Species::Occultsaurok
|
||||||
|
| biped_large::Species::Mightysaurok
|
||||||
|
| biped_large::Species::Slysaurok => VoiceKind::Reptile,
|
||||||
|
_ => VoiceKind::BipedLarge,
|
||||||
|
},
|
||||||
|
Body::Theropod(_) | Body::Dragon(_) => VoiceKind::Reptile,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||||
@ -452,6 +514,20 @@ impl SfxMgr {
|
|||||||
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
|
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Outcome::Utterance { pos, kind, body } => {
|
||||||
|
if let Some(voice) = body_to_voice(body) {
|
||||||
|
let sfx_trigger_item =
|
||||||
|
triggers.get_key_value(&SfxEvent::Utterance(*kind, voice));
|
||||||
|
if let Some(sfx_trigger_item) = sfx_trigger_item {
|
||||||
|
audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(2.5), false);
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"No utterance sound effect exists for ({:?}, {:?})",
|
||||||
|
kind, voice
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Outcome::ExpChange { .. }
|
Outcome::ExpChange { .. }
|
||||||
| Outcome::ComboChange { .. }
|
| Outcome::ComboChange { .. }
|
||||||
| Outcome::SummonedCreature { .. } => {},
|
| Outcome::SummonedCreature { .. } => {},
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
//! Handles caching and retrieval of decoded `.ogg` sfx sound data, eliminating
|
//! Handles caching and retrieval of decoded `.ogg` sfx sound data, eliminating
|
||||||
//! the need to decode files on each playback
|
//! the need to decode files on each playback
|
||||||
use common::assets;
|
use common::assets::{self, Loader};
|
||||||
use std::{borrow::Cow, io, sync::Arc};
|
use rodio::{source::Buffered, Decoder, Source};
|
||||||
|
use std::{borrow::Cow, io};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
// Implementation of sound taken from this github issue:
|
// Implementation of sound taken from this github issue:
|
||||||
@ -10,16 +11,12 @@ use tracing::warn;
|
|||||||
pub struct SoundLoader;
|
pub struct SoundLoader;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct OggSound(Arc<Vec<u8>>);
|
pub struct OggSound(Buffered<Decoder<io::Cursor<Vec<u8>>>>);
|
||||||
|
|
||||||
impl AsRef<[u8]> for OggSound {
|
impl Loader<OggSound> for SoundLoader {
|
||||||
fn as_ref(&self) -> &[u8] { &self.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl assets::Loader<OggSound> for SoundLoader {
|
|
||||||
fn load(content: Cow<[u8]>, _: &str) -> Result<OggSound, assets::BoxedError> {
|
fn load(content: Cow<[u8]>, _: &str) -> Result<OggSound, assets::BoxedError> {
|
||||||
let arc = Arc::new(content.into_owned());
|
let source = Decoder::new(io::Cursor::new(content.into_owned()))?.buffered();
|
||||||
Ok(OggSound(arc))
|
Ok(OggSound(source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,16 +34,13 @@ impl assets::Asset for OggSound {
|
|||||||
|
|
||||||
/// Wrapper for decoded audio data
|
/// Wrapper for decoded audio data
|
||||||
impl OggSound {
|
impl OggSound {
|
||||||
pub fn decoder(
|
pub fn to_source(&self) -> impl Source + Iterator<Item = i16> { self.0.clone() }
|
||||||
self,
|
|
||||||
) -> Result<rodio::Decoder<io::Cursor<OggSound>>, rodio::decoder::DecoderError> {
|
|
||||||
let cursor = io::Cursor::new(self);
|
|
||||||
rodio::Decoder::new(cursor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty() -> OggSound {
|
pub fn empty() -> OggSound {
|
||||||
OggSound(Arc::new(
|
SoundLoader::load(
|
||||||
include_bytes!("../../../assets/voxygen/audio/null.ogg").to_vec(),
|
Cow::Borrowed(include_bytes!("../../../assets/voxygen/audio/null.ogg")),
|
||||||
))
|
"empty",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,8 @@ impl ParticleMgr {
|
|||||||
| Outcome::SkillPointGain { .. }
|
| Outcome::SkillPointGain { .. }
|
||||||
| Outcome::ComboChange { .. }
|
| Outcome::ComboChange { .. }
|
||||||
| Outcome::Damage { .. }
|
| Outcome::Damage { .. }
|
||||||
| Outcome::PoiseChange { .. } => {},
|
| Outcome::PoiseChange { .. }
|
||||||
|
| Outcome::Utterance { .. } => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use common::{
|
|||||||
inventory::slot::{EquipSlot, Slot},
|
inventory::slot::{EquipSlot, Slot},
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
item::{tool::ToolKind, ItemDef, ItemDesc},
|
item::{tool::ToolKind, ItemDef, ItemDesc},
|
||||||
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Vel,
|
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, UtteranceKind, Vel,
|
||||||
},
|
},
|
||||||
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -525,6 +525,11 @@ impl PlayState for SessionState {
|
|||||||
self.client.borrow_mut().toggle_dance();
|
self.client.borrow_mut().toggle_dance();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
GameInput::Greet => {
|
||||||
|
if state {
|
||||||
|
self.client.borrow_mut().utter(UtteranceKind::Greeting);
|
||||||
|
}
|
||||||
|
},
|
||||||
GameInput::Sneak => {
|
GameInput::Sneak => {
|
||||||
if state {
|
if state {
|
||||||
self.stop_auto_walk();
|
self.stop_auto_walk();
|
||||||
|
@ -123,6 +123,7 @@ impl ControlSettings {
|
|||||||
GameInput::Jump => KeyMouse::Key(VirtualKeyCode::Space),
|
GameInput::Jump => KeyMouse::Key(VirtualKeyCode::Space),
|
||||||
GameInput::Sit => KeyMouse::Key(VirtualKeyCode::K),
|
GameInput::Sit => KeyMouse::Key(VirtualKeyCode::K),
|
||||||
GameInput::Dance => KeyMouse::Key(VirtualKeyCode::J),
|
GameInput::Dance => KeyMouse::Key(VirtualKeyCode::J),
|
||||||
|
GameInput::Greet => KeyMouse::Key(VirtualKeyCode::H),
|
||||||
GameInput::Glide => KeyMouse::Key(VirtualKeyCode::LShift),
|
GameInput::Glide => KeyMouse::Key(VirtualKeyCode::LShift),
|
||||||
GameInput::Climb => KeyMouse::Key(VirtualKeyCode::Space),
|
GameInput::Climb => KeyMouse::Key(VirtualKeyCode::Space),
|
||||||
GameInput::ClimbDown => KeyMouse::Key(VirtualKeyCode::LControl),
|
GameInput::ClimbDown => KeyMouse::Key(VirtualKeyCode::LControl),
|
||||||
|
@ -39,6 +39,7 @@ pub enum GameInput {
|
|||||||
Jump,
|
Jump,
|
||||||
Sit,
|
Sit,
|
||||||
Dance,
|
Dance,
|
||||||
|
Greet,
|
||||||
Glide,
|
Glide,
|
||||||
Climb,
|
Climb,
|
||||||
ClimbDown,
|
ClimbDown,
|
||||||
@ -94,6 +95,7 @@ impl GameInput {
|
|||||||
GameInput::Jump => "gameinput.jump",
|
GameInput::Jump => "gameinput.jump",
|
||||||
GameInput::Sit => "gameinput.sit",
|
GameInput::Sit => "gameinput.sit",
|
||||||
GameInput::Dance => "gameinput.dance",
|
GameInput::Dance => "gameinput.dance",
|
||||||
|
GameInput::Greet => "gameinput.greet",
|
||||||
GameInput::Glide => "gameinput.glide",
|
GameInput::Glide => "gameinput.glide",
|
||||||
GameInput::Climb => "gameinput.climb",
|
GameInput::Climb => "gameinput.climb",
|
||||||
GameInput::ClimbDown => "gameinput.climbdown",
|
GameInput::ClimbDown => "gameinput.climbdown",
|
||||||
@ -159,6 +161,7 @@ impl GameInput {
|
|||||||
GameInput::Jump,
|
GameInput::Jump,
|
||||||
GameInput::Sit,
|
GameInput::Sit,
|
||||||
GameInput::Dance,
|
GameInput::Dance,
|
||||||
|
GameInput::Greet,
|
||||||
GameInput::Glide,
|
GameInput::Glide,
|
||||||
GameInput::Climb,
|
GameInput::Climb,
|
||||||
GameInput::ClimbDown,
|
GameInput::ClimbDown,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user