mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implemented loot pickup chat messages and the option for playing different sounds for picked up items
* Added chat message when an item is picked up * Changed InventoryUpdateEvent::Collected to InventoryUpdateEvent::Collected(Item) to facilitate the client being aware of what was picked up * Added SfxInventoryEvent enum to allow different sounds to be used based on the item type. The RON mapping/de-serialization doesn't support matching on structs so we have to give it fixed enum values which are determined in TryFrom<&InventoryUpdateEvent> for SfxEvent * Refactored InventoryManip::Pickup arm of match in inventory_manip::handle_inventory for clarity/better warning messages * Fixed a bug that prevented the CollectFailed event from being raised when a player's inventory is full * Added a panic for the situation where an item is pushed into the players inventory and then the deletion of the entity fails as this would indicate an item dupe bug - this could potentially be reworked to pull the item back from the player's inventory but this seems like there's be a more correct transactional way to do this. * Added two temporary sounds to prove the per-item sound functionality (pickup sounds for Swords and Staffs)
This commit is contained in:
parent
8e67782a49
commit
a9d3f984f0
@ -36,8 +36,26 @@
|
||||
threshold: 0.5,
|
||||
),
|
||||
Inventory(Collected): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_item",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(CollectedTool(Sword)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.pickup_sword",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(CollectedTool(Staff)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.pickup_staff",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(CollectFailed): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_item",
|
||||
"voxygen.audio.sfx.inventory.add_failed",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
@ -88,12 +106,6 @@
|
||||
"voxygen.audio.sfx.inventory.consumable.food",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(CollectFailed): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_failed",
|
||||
],
|
||||
threshold: 0.3,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
BIN
assets/voxygen/audio/sfx/inventory/pickup_staff.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/inventory/pickup_staff.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/inventory/pickup_sword.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/inventory/pickup_sword.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -867,6 +867,13 @@ impl Client {
|
||||
self.clean_state();
|
||||
},
|
||||
ServerMsg::InventoryUpdate(inventory, event) => {
|
||||
// TODO: Move this SFX code to Voxygen
|
||||
let sfx_event = SfxEvent::from(&event);
|
||||
self.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<SfxEventItem>>()
|
||||
.emit_now(SfxEventItem::at_player_position(sfx_event));
|
||||
|
||||
match event {
|
||||
InventoryUpdateEvent::CollectFailed => {
|
||||
frontend_events.push(Event::Chat {
|
||||
@ -877,14 +884,16 @@ impl Client {
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
if let InventoryUpdateEvent::Collected(item) = event {
|
||||
frontend_events.push(Event::Chat {
|
||||
message: format!("Picked up {}", item.name()),
|
||||
chat_type: ChatType::Meta,
|
||||
});
|
||||
}
|
||||
|
||||
self.state.write_component(self.entity, inventory);
|
||||
},
|
||||
}
|
||||
|
||||
self.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<SfxEventItem>>()
|
||||
.emit_now(SfxEventItem::at_player_position(SfxEvent::Inventory(event)));
|
||||
},
|
||||
ServerMsg::TerrainChunkUpdate { key, chunk } => {
|
||||
if let Ok(chunk) = chunk {
|
||||
|
@ -313,7 +313,7 @@ impl Component for Inventory {
|
||||
type Storage = HashMapStorage<Self>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum InventoryUpdateEvent {
|
||||
Init,
|
||||
Used,
|
||||
@ -322,7 +322,7 @@ pub enum InventoryUpdateEvent {
|
||||
Given,
|
||||
Swapped,
|
||||
Dropped,
|
||||
Collected,
|
||||
Collected(Item),
|
||||
CollectFailed,
|
||||
Possession,
|
||||
Debug,
|
||||
@ -332,7 +332,7 @@ impl Default for InventoryUpdateEvent {
|
||||
fn default() -> Self { Self::Init }
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct InventoryUpdate {
|
||||
event: InventoryUpdateEvent,
|
||||
}
|
||||
@ -340,7 +340,7 @@ pub struct InventoryUpdate {
|
||||
impl InventoryUpdate {
|
||||
pub fn new(event: InventoryUpdateEvent) -> Self { Self { event } }
|
||||
|
||||
pub fn event(&self) -> InventoryUpdateEvent { self.event }
|
||||
pub fn event(&self) -> InventoryUpdateEvent { self.event.clone() }
|
||||
}
|
||||
|
||||
impl Component for InventoryUpdate {
|
||||
|
@ -1,9 +1,14 @@
|
||||
use crate::{comp, sync::Uid, util::Dir};
|
||||
use crate::{
|
||||
comp,
|
||||
comp::item::{Consumable, ItemKind},
|
||||
sync::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
use comp::{item::ToolCategory, CharacterAbilityType, InventoryUpdateEvent, Item};
|
||||
use parking_lot::Mutex;
|
||||
use serde::Deserialize;
|
||||
use specs::Entity as EcsEntity;
|
||||
use std::{collections::VecDeque, ops::DerefMut};
|
||||
use std::{collections::VecDeque, convert::TryFrom, ops::DerefMut};
|
||||
use vek::*;
|
||||
|
||||
pub struct SfxEventItem {
|
||||
@ -26,7 +31,7 @@ impl SfxEventItem {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
pub enum SfxEvent {
|
||||
Idle,
|
||||
Run,
|
||||
@ -42,7 +47,47 @@ pub enum SfxEvent {
|
||||
Attack(CharacterAbilityType, ToolCategory),
|
||||
Wield(ToolCategory),
|
||||
Unwield(ToolCategory),
|
||||
Inventory(InventoryUpdateEvent),
|
||||
Inventory(SfxInventoryEvent),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
pub enum SfxInventoryEvent {
|
||||
Collected,
|
||||
CollectedTool(ToolCategory),
|
||||
CollectFailed,
|
||||
Consumed(Consumable),
|
||||
Debug,
|
||||
Dropped,
|
||||
Given,
|
||||
Swapped,
|
||||
}
|
||||
|
||||
impl From<&InventoryUpdateEvent> for SfxEvent {
|
||||
fn from(value: &InventoryUpdateEvent) -> Self {
|
||||
match value {
|
||||
InventoryUpdateEvent::Collected(item) => {
|
||||
// Handle sound effects for types of collected items, falling back to the
|
||||
// default Collected event
|
||||
match item.kind {
|
||||
ItemKind::Tool(tool) => SfxEvent::Inventory(SfxInventoryEvent::CollectedTool(
|
||||
ToolCategory::try_from(tool.kind).unwrap(),
|
||||
)),
|
||||
_ => SfxEvent::Inventory(SfxInventoryEvent::Collected),
|
||||
}
|
||||
},
|
||||
InventoryUpdateEvent::CollectFailed => {
|
||||
SfxEvent::Inventory(SfxInventoryEvent::CollectFailed)
|
||||
},
|
||||
InventoryUpdateEvent::Consumed(consumable) => {
|
||||
SfxEvent::Inventory(SfxInventoryEvent::Consumed(*consumable))
|
||||
},
|
||||
InventoryUpdateEvent::Debug => SfxEvent::Inventory(SfxInventoryEvent::Debug),
|
||||
InventoryUpdateEvent::Dropped => SfxEvent::Inventory(SfxInventoryEvent::Dropped),
|
||||
InventoryUpdateEvent::Given => SfxEvent::Inventory(SfxInventoryEvent::Given),
|
||||
InventoryUpdateEvent::Swapped => SfxEvent::Inventory(SfxInventoryEvent::Swapped),
|
||||
_ => SfxEvent::Inventory(SfxInventoryEvent::Swapped),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LocalEvent {
|
||||
|
@ -11,7 +11,7 @@ use common::{
|
||||
};
|
||||
use rand::Rng;
|
||||
use specs::{join::Join, world::WorldExt, Builder, Entity as EcsEntity, WriteStorage};
|
||||
use tracing::error;
|
||||
use tracing::{debug, error, warn};
|
||||
use vek::Vec3;
|
||||
|
||||
pub fn swap_lantern(
|
||||
@ -29,14 +29,14 @@ pub fn snuff_lantern(storage: &mut WriteStorage<comp::LightEmitter>, entity: Ecs
|
||||
storage.remove(entity);
|
||||
}
|
||||
|
||||
#[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587
|
||||
#[allow(clippy::let_and_return)] // TODO: Pending review in #587
|
||||
#[allow(clippy::blocks_in_if_conditions)]
|
||||
pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::InventoryManip) {
|
||||
let state = server.state_mut();
|
||||
let mut dropped_items = Vec::new();
|
||||
|
||||
match manip {
|
||||
comp::InventoryManip::Pickup(uid) => {
|
||||
let mut picked_up_item: Option<comp::Item> = None;
|
||||
let item_entity = if let (Some((item, item_entity)), Some(inv)) = (
|
||||
state
|
||||
.ecs()
|
||||
@ -53,29 +53,39 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity),
|
||||
) {
|
||||
if within_pickup_range(
|
||||
picked_up_item = Some(item.clone());
|
||||
if !within_pickup_range(
|
||||
state.ecs().read_storage::<comp::Pos>().get(entity),
|
||||
state.ecs().read_storage::<comp::Pos>().get(item_entity),
|
||||
) && inv.push(item).is_none()
|
||||
{
|
||||
Some(item_entity)
|
||||
} else {
|
||||
None
|
||||
) {
|
||||
debug!("Failed to pick up item as not within range, Uid: {}", uid);
|
||||
return;
|
||||
};
|
||||
|
||||
// Attempt to add the item to the player's inventory
|
||||
match inv.push(item) {
|
||||
None => Some(item_entity),
|
||||
Some(_) => None, // Inventory was full
|
||||
}
|
||||
} else {
|
||||
warn!("Failed to get entity/component for item Uid: {}", uid);
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(item_entity) = item_entity {
|
||||
let event = if let Some(item_entity) = item_entity {
|
||||
if let Err(err) = state.delete_entity_recorded(item_entity) {
|
||||
error!("Failed to delete picked up item entity: {:?}", err);
|
||||
// If this occurs it means the item was duped as it's been pushed to the
|
||||
// player's inventory but also left on the ground
|
||||
panic!("Failed to delete picked up item entity: {:?}", err);
|
||||
}
|
||||
}
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected(
|
||||
picked_up_item.unwrap(),
|
||||
))
|
||||
} else {
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::CollectFailed)
|
||||
};
|
||||
|
||||
state.write_component(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected),
|
||||
);
|
||||
state.write_component(entity, event);
|
||||
},
|
||||
|
||||
comp::InventoryManip::Collect(pos) => {
|
||||
|
@ -57,12 +57,12 @@ impl StateExt for State {
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.push(item).is_none())
|
||||
.map(|inv| inv.push(item.clone()).is_none())
|
||||
.unwrap_or(false);
|
||||
if success {
|
||||
self.write_component(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected),
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected(item)),
|
||||
);
|
||||
}
|
||||
success
|
||||
|
@ -68,7 +68,7 @@ impl EventMapper for CombatEventMapper {
|
||||
|
||||
// Check for SFX config entry for this movement
|
||||
if Self::should_emit(state, triggers.get_key_value(&mapped_event)) {
|
||||
sfx_emitter.emit(SfxEventItem::new(mapped_event, Some(pos.0), None));
|
||||
sfx_emitter.emit(SfxEventItem::new(mapped_event.clone(), Some(pos.0), None));
|
||||
|
||||
state.time = Instant::now();
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ impl EventMapper for MovementEventMapper {
|
||||
// Check for SFX config entry for this movement
|
||||
if Self::should_emit(state, triggers.get_key_value(&mapped_event)) {
|
||||
sfx_emitter.emit(SfxEventItem::new(
|
||||
mapped_event,
|
||||
mapped_event.clone(),
|
||||
Some(pos.0),
|
||||
Some(Self::get_volume_for_body_type(body)),
|
||||
));
|
||||
@ -161,7 +161,7 @@ impl MovementEventMapper {
|
||||
}
|
||||
|
||||
// Match all other Movemement and Action states
|
||||
match (previous_state.event, character_state) {
|
||||
match (previous_state.event.clone(), character_state) {
|
||||
(_, CharacterState::Climb { .. }) => SfxEvent::Climb,
|
||||
(SfxEvent::Glide, CharacterState::Idle { .. }) => SfxEvent::GliderClose,
|
||||
(previous_event, CharacterState::Glide { .. }) => {
|
||||
|
Loading…
Reference in New Issue
Block a user