mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/track-unequipped-items' into 'master'
Recently unequipped items are now tracked and durability loss on death is applied to them See merge request veloren/veloren!3883
This commit is contained in:
commit
4b6c5f57e3
33
Cargo.lock
generated
33
Cargo.lock
generated
@ -2685,6 +2685,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.7.6",
|
"ahash 0.7.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||||
|
dependencies = [
|
||||||
|
"ahash 0.8.0",
|
||||||
"rayon",
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -6651,7 +6660,7 @@ dependencies = [
|
|||||||
"authc",
|
"authc",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap 3.2.22",
|
"clap 3.2.22",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"image",
|
"image",
|
||||||
"num 0.4.0",
|
"num 0.4.0",
|
||||||
"quinn",
|
"quinn",
|
||||||
@ -6686,7 +6695,7 @@ dependencies = [
|
|||||||
"fluent",
|
"fluent",
|
||||||
"fluent-bundle",
|
"fluent-bundle",
|
||||||
"fluent-syntax",
|
"fluent-syntax",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"intl-memoizer",
|
"intl-memoizer",
|
||||||
"ron 0.8.0",
|
"ron 0.8.0",
|
||||||
"serde",
|
"serde",
|
||||||
@ -6713,7 +6722,7 @@ dependencies = [
|
|||||||
"dot_vox",
|
"dot_vox",
|
||||||
"enum-map",
|
"enum-map",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itertools",
|
"itertools",
|
||||||
"kiddo 0.1.7",
|
"kiddo 0.1.7",
|
||||||
@ -6808,7 +6817,7 @@ version = "0.10.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"flate2",
|
"flate2",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"image",
|
"image",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
@ -6824,7 +6833,7 @@ name = "veloren-common-state"
|
|||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"rayon",
|
"rayon",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
@ -6847,7 +6856,7 @@ dependencies = [
|
|||||||
name = "veloren-common-systems"
|
name = "veloren-common-systems"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itertools",
|
"itertools",
|
||||||
"ordered-float 3.1.0",
|
"ordered-float 3.1.0",
|
||||||
@ -6878,7 +6887,7 @@ dependencies = [
|
|||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lz-fear",
|
"lz-fear",
|
||||||
"prometheus",
|
"prometheus",
|
||||||
@ -6906,7 +6915,7 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"bytes",
|
"bytes",
|
||||||
"criterion",
|
"criterion",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"prometheus",
|
"prometheus",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"tokio",
|
"tokio",
|
||||||
@ -6949,7 +6958,7 @@ dependencies = [
|
|||||||
"atomic_refcell",
|
"atomic_refcell",
|
||||||
"enum-map",
|
"enum-map",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"itertools",
|
"itertools",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rand_chacha 0.3.1",
|
"rand_chacha 0.3.1",
|
||||||
@ -6979,7 +6988,7 @@ dependencies = [
|
|||||||
"enum-map",
|
"enum-map",
|
||||||
"enumset",
|
"enumset",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"humantime",
|
"humantime",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -7095,7 +7104,7 @@ dependencies = [
|
|||||||
"gilrs",
|
"gilrs",
|
||||||
"glyph_brush",
|
"glyph_brush",
|
||||||
"guillotiere",
|
"guillotiere",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"iced_native",
|
"iced_native",
|
||||||
"iced_winit",
|
"iced_winit",
|
||||||
"image",
|
"image",
|
||||||
@ -7195,7 +7204,7 @@ dependencies = [
|
|||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"flate2",
|
"flate2",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
"image",
|
"image",
|
||||||
"inline_tweak",
|
"inline_tweak",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
@ -30,7 +30,7 @@ tracing = { version = "0.1", default-features = false }
|
|||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
specs = { version = "0.18", features = ["serde", "storage-event-control", "derive"] }
|
specs = { version = "0.18", features = ["serde", "storage-event-control", "derive"] }
|
||||||
vek = { version = "0.15.8", features = ["serde"] }
|
vek = { version = "0.15.8", features = ["serde"] }
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253f8f2f92760ef44d2679c9a" }
|
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253f8f2f92760ef44d2679c9a" }
|
||||||
|
|
||||||
#TODO: put bot in a different crate
|
#TODO: put bot in a different crate
|
||||||
|
@ -17,7 +17,7 @@ intl-memoizer = { git = "https://github.com/juliancoffee/fluent-rs.git", branch
|
|||||||
fluent = { git = "https://github.com/juliancoffee/fluent-rs.git", branch = "patched"}
|
fluent = { git = "https://github.com/juliancoffee/fluent-rs.git", branch = "patched"}
|
||||||
fluent-bundle = { git = "https://github.com/juliancoffee/fluent-rs.git", branch = "patched"}
|
fluent-bundle = { git = "https://github.com/juliancoffee/fluent-rs.git", branch = "patched"}
|
||||||
# Utility
|
# Utility
|
||||||
hashbrown = { version = "0.12", features = ["serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["serde", "nightly"] }
|
||||||
deunicode = "1.0"
|
deunicode = "1.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
# Bin
|
# Bin
|
||||||
|
@ -74,7 +74,7 @@ petgraph = { version = "0.6", optional = true }
|
|||||||
kiddo = { version = "0.1", optional = true }
|
kiddo = { version = "0.1", optional = true }
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
slotmap = { version = "1.0", features = ["serde"] }
|
slotmap = { version = "1.0", features = ["serde"] }
|
||||||
indexmap = { version = "1.3.0", features = ["rayon"] }
|
indexmap = { version = "1.3.0", features = ["rayon"] }
|
||||||
slab = "0.4.2"
|
slab = "0.4.2"
|
||||||
|
@ -22,7 +22,7 @@ vek = { version = "0.15.8", features = ["serde"] }
|
|||||||
tracing = { version = "0.1", default-features = false }
|
tracing = { version = "0.1", default-features = false }
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
|
|
||||||
# ECS
|
# ECS
|
||||||
specs = { version = "0.18", features = ["serde", "storage-event-control"] }
|
specs = { version = "0.18", features = ["serde", "storage-event-control"] }
|
||||||
|
@ -20,6 +20,7 @@ use core::{
|
|||||||
num::{NonZeroU32, NonZeroU64},
|
num::{NonZeroU32, NonZeroU64},
|
||||||
};
|
};
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
use crossbeam_utils::atomic::AtomicCell;
|
||||||
|
use hashbrown::Equivalent;
|
||||||
use serde::{de, Deserialize, Serialize, Serializer};
|
use serde::{de, Deserialize, Serialize, Serializer};
|
||||||
use specs::{Component, DenseVecStorage, DerefFlaggedStorage};
|
use specs::{Component, DenseVecStorage, DerefFlaggedStorage};
|
||||||
use std::{borrow::Cow, collections::hash_map::DefaultHasher, fmt, sync::Arc};
|
use std::{borrow::Cow, collections::hash_map::DefaultHasher, fmt, sync::Arc};
|
||||||
@ -1213,7 +1214,7 @@ impl Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn durability(&self) -> Option<u32> {
|
pub fn durability_lost(&self) -> Option<u32> {
|
||||||
self.durability_lost.map(|x| x.min(Self::MAX_DURABILITY))
|
self.durability_lost.map(|x| x.min(Self::MAX_DURABILITY))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1294,7 +1295,7 @@ pub trait ItemDesc {
|
|||||||
fn is_modular(&self) -> bool;
|
fn is_modular(&self) -> bool;
|
||||||
fn components(&self) -> &[Item];
|
fn components(&self) -> &[Item];
|
||||||
fn has_durability(&self) -> bool;
|
fn has_durability(&self) -> bool;
|
||||||
fn durability(&self) -> Option<u32>;
|
fn durability_lost(&self) -> Option<u32>;
|
||||||
fn stats_durability_multiplier(&self) -> DurabilityMultiplier;
|
fn stats_durability_multiplier(&self) -> DurabilityMultiplier;
|
||||||
|
|
||||||
fn tool_info(&self) -> Option<ToolKind> {
|
fn tool_info(&self) -> Option<ToolKind> {
|
||||||
@ -1327,7 +1328,7 @@ impl ItemDesc for Item {
|
|||||||
|
|
||||||
fn has_durability(&self) -> bool { self.has_durability() }
|
fn has_durability(&self) -> bool { self.has_durability() }
|
||||||
|
|
||||||
fn durability(&self) -> Option<u32> { self.durability() }
|
fn durability_lost(&self) -> Option<u32> { self.durability_lost() }
|
||||||
|
|
||||||
fn stats_durability_multiplier(&self) -> DurabilityMultiplier {
|
fn stats_durability_multiplier(&self) -> DurabilityMultiplier {
|
||||||
self.stats_durability_multiplier()
|
self.stats_durability_multiplier()
|
||||||
@ -1359,7 +1360,7 @@ impl ItemDesc for ItemDef {
|
|||||||
self.kind().has_durability() && self.quality != Quality::Debug
|
self.kind().has_durability() && self.quality != Quality::Debug
|
||||||
}
|
}
|
||||||
|
|
||||||
fn durability(&self) -> Option<u32> { None }
|
fn durability_lost(&self) -> Option<u32> { None }
|
||||||
|
|
||||||
fn stats_durability_multiplier(&self) -> DurabilityMultiplier { DurabilityMultiplier(1.0) }
|
fn stats_durability_multiplier(&self) -> DurabilityMultiplier { DurabilityMultiplier(1.0) }
|
||||||
}
|
}
|
||||||
@ -1399,7 +1400,7 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T {
|
|||||||
|
|
||||||
fn has_durability(&self) -> bool { (*self).has_durability() }
|
fn has_durability(&self) -> bool { (*self).has_durability() }
|
||||||
|
|
||||||
fn durability(&self) -> Option<u32> { (*self).durability() }
|
fn durability_lost(&self) -> Option<u32> { (*self).durability_lost() }
|
||||||
|
|
||||||
fn stats_durability_multiplier(&self) -> DurabilityMultiplier {
|
fn stats_durability_multiplier(&self) -> DurabilityMultiplier {
|
||||||
(*self).stats_durability_multiplier()
|
(*self).stats_durability_multiplier()
|
||||||
@ -1451,6 +1452,10 @@ impl PartialEq<ItemDefinitionIdOwned> for ItemDefinitionId<'_> {
|
|||||||
fn eq(&self, other: &ItemDefinitionIdOwned) -> bool { other == self }
|
fn eq(&self, other: &ItemDefinitionIdOwned) -> bool { other == self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Equivalent<ItemDefinitionIdOwned> for ItemDefinitionId<'_> {
|
||||||
|
fn equivalent(&self, key: &ItemDefinitionIdOwned) -> bool { self == key }
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
use crate::comp::{
|
use crate::{
|
||||||
|
comp::{
|
||||||
inventory::{
|
inventory::{
|
||||||
item::{self, tool::Tool, Hands, ItemKind},
|
item::{self, tool::Tool, Hands, ItemDefinitionIdOwned, ItemKind},
|
||||||
slot::{ArmorSlot, EquipSlot},
|
slot::{ArmorSlot, EquipSlot},
|
||||||
InvSlot,
|
InvSlot,
|
||||||
},
|
},
|
||||||
Item,
|
Item,
|
||||||
|
},
|
||||||
|
resources::Time,
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
pub(super) const UNEQUIP_TRACKING_DURATION: f64 = 60.0;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Loadout {
|
pub struct Loadout {
|
||||||
slots: Vec<LoadoutSlot>,
|
slots: Vec<LoadoutSlot>,
|
||||||
|
// Includes time that item was unequipped at
|
||||||
|
#[serde(skip)]
|
||||||
|
// Tracks time unequipped at and number that have been unequipped (for things like dual
|
||||||
|
// wielding, rings, or other future cases)
|
||||||
|
pub(super) recently_unequipped_items: HashMap<ItemDefinitionIdOwned, (Time, u8)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE: Please don't derive a PartialEq Instance for this; that's broken!
|
/// NOTE: Please don't derive a PartialEq Instance for this; that's broken!
|
||||||
@ -83,16 +94,40 @@ impl Loadout {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(equip_slot, persistence_key)| LoadoutSlot::new(equip_slot, persistence_key))
|
.map(|(equip_slot, persistence_key)| LoadoutSlot::new(equip_slot, persistence_key))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
recently_unequipped_items: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces the item in the Loadout slot that corresponds to the given
|
/// Replaces the item in the Loadout slot that corresponds to the given
|
||||||
/// EquipSlot and returns the previous item if any
|
/// EquipSlot and returns the previous item if any
|
||||||
pub(super) fn swap(&mut self, equip_slot: EquipSlot, item: Option<Item>) -> Option<Item> {
|
pub(super) fn swap(
|
||||||
self.slots
|
&mut self,
|
||||||
|
equip_slot: EquipSlot,
|
||||||
|
item: Option<Item>,
|
||||||
|
time: Time,
|
||||||
|
) -> Option<Item> {
|
||||||
|
if let Some(item_def_id) = item.as_ref().map(|item| item.item_definition_id()) {
|
||||||
|
if let Some((_unequip_time, count)) =
|
||||||
|
self.recently_unequipped_items.get_mut(&item_def_id)
|
||||||
|
{
|
||||||
|
*count = count.saturating_sub(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.cull_recently_unequipped_items(time);
|
||||||
|
let unequipped_item = self
|
||||||
|
.slots
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|x| x.equip_slot == equip_slot)
|
.find(|x| x.equip_slot == equip_slot)
|
||||||
.and_then(|x| core::mem::replace(&mut x.slot, item))
|
.and_then(|x| core::mem::replace(&mut x.slot, item));
|
||||||
|
if let Some(unequipped_item) = unequipped_item.as_ref() {
|
||||||
|
// TODO: Avoid this allocation when there isn't an insert
|
||||||
|
let entry = self
|
||||||
|
.recently_unequipped_items
|
||||||
|
.entry(unequipped_item.item_definition_id().to_owned())
|
||||||
|
.or_insert((time, 0));
|
||||||
|
*entry = (time, entry.1.saturating_add(1));
|
||||||
|
}
|
||||||
|
unequipped_item
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the item (if any) equipped in the given EquipSlot
|
/// Returns a reference to the item (if any) equipped in the given EquipSlot
|
||||||
@ -154,7 +189,12 @@ impl Loadout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Swaps the contents of two loadout slots
|
/// Swaps the contents of two loadout slots
|
||||||
pub(super) fn swap_slots(&mut self, equip_slot_a: EquipSlot, equip_slot_b: EquipSlot) {
|
pub(super) fn swap_slots(
|
||||||
|
&mut self,
|
||||||
|
equip_slot_a: EquipSlot,
|
||||||
|
equip_slot_b: EquipSlot,
|
||||||
|
time: Time,
|
||||||
|
) {
|
||||||
if self.slot(equip_slot_b).is_none() || self.slot(equip_slot_b).is_none() {
|
if self.slot(equip_slot_b).is_none() || self.slot(equip_slot_b).is_none() {
|
||||||
// Currently all loadouts contain slots for all EquipSlots so this can never
|
// Currently all loadouts contain slots for all EquipSlots so this can never
|
||||||
// happen, but if loadouts with alternate slot combinations are
|
// happen, but if loadouts with alternate slot combinations are
|
||||||
@ -163,9 +203,9 @@ impl Loadout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_a = self.swap(equip_slot_a, None);
|
let item_a = self.swap(equip_slot_a, None, time);
|
||||||
let item_b = self.swap(equip_slot_b, item_a);
|
let item_b = self.swap(equip_slot_b, item_a, time);
|
||||||
assert_eq!(self.swap(equip_slot_a, item_b), None);
|
assert_eq!(self.swap(equip_slot_a, item_b, time), None);
|
||||||
|
|
||||||
// Check if items are valid in their new positions
|
// Check if items are valid in their new positions
|
||||||
if !self.slot_can_hold(
|
if !self.slot_can_hold(
|
||||||
@ -176,9 +216,9 @@ impl Loadout {
|
|||||||
self.equipped(equip_slot_b).map(|x| x.kind()).as_deref(),
|
self.equipped(equip_slot_b).map(|x| x.kind()).as_deref(),
|
||||||
) {
|
) {
|
||||||
// If not, revert the swap
|
// If not, revert the swap
|
||||||
let item_a = self.swap(equip_slot_a, None);
|
let item_a = self.swap(equip_slot_a, None, time);
|
||||||
let item_b = self.swap(equip_slot_b, item_a);
|
let item_b = self.swap(equip_slot_b, item_a, time);
|
||||||
assert_eq!(self.swap(equip_slot_a, item_b), None);
|
assert_eq!(self.swap(equip_slot_a, item_b, time), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +263,22 @@ impl Loadout {
|
|||||||
.and_then(|item| item.slot(loadout_slot_id.slot_idx))
|
.and_then(|item| item.slot(loadout_slot_id.slot_idx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn inv_slot_with_mutable_recently_unequipped_items(
|
||||||
|
&mut self,
|
||||||
|
loadout_slot_id: LoadoutSlotId,
|
||||||
|
) -> (
|
||||||
|
Option<&InvSlot>,
|
||||||
|
&mut HashMap<ItemDefinitionIdOwned, (Time, u8)>,
|
||||||
|
) {
|
||||||
|
(
|
||||||
|
self.slots
|
||||||
|
.get(loadout_slot_id.loadout_idx)
|
||||||
|
.and_then(|loadout_slot| loadout_slot.slot.as_ref())
|
||||||
|
.and_then(|item| item.slot(loadout_slot_id.slot_idx)),
|
||||||
|
&mut self.recently_unequipped_items,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the `InvSlot` for a given `LoadoutSlotId`
|
/// Returns the `InvSlot` for a given `LoadoutSlotId`
|
||||||
pub(super) fn inv_slot_mut(&mut self, loadout_slot_id: LoadoutSlotId) -> Option<&mut InvSlot> {
|
pub(super) fn inv_slot_mut(&mut self, loadout_slot_id: LoadoutSlotId) -> Option<&mut InvSlot> {
|
||||||
self.slots
|
self.slots
|
||||||
@ -367,7 +423,7 @@ impl Loadout {
|
|||||||
(None, None))
|
(None, None))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn swap_equipped_weapons(&mut self) {
|
pub(super) fn swap_equipped_weapons(&mut self, time: Time) {
|
||||||
// Checks if a given slot can hold an item right now, defaults to true if
|
// Checks if a given slot can hold an item right now, defaults to true if
|
||||||
// nothing is equipped in slot
|
// nothing is equipped in slot
|
||||||
let valid_slot = |equip_slot| {
|
let valid_slot = |equip_slot| {
|
||||||
@ -385,25 +441,25 @@ impl Loadout {
|
|||||||
&& valid_slot(EquipSlot::InactiveOffhand)
|
&& valid_slot(EquipSlot::InactiveOffhand)
|
||||||
{
|
{
|
||||||
// Get weapons from each slot
|
// Get weapons from each slot
|
||||||
let active_mainhand = self.swap(EquipSlot::ActiveMainhand, None);
|
let active_mainhand = self.swap(EquipSlot::ActiveMainhand, None, time);
|
||||||
let active_offhand = self.swap(EquipSlot::ActiveOffhand, None);
|
let active_offhand = self.swap(EquipSlot::ActiveOffhand, None, time);
|
||||||
let inactive_mainhand = self.swap(EquipSlot::InactiveMainhand, None);
|
let inactive_mainhand = self.swap(EquipSlot::InactiveMainhand, None, time);
|
||||||
let inactive_offhand = self.swap(EquipSlot::InactiveOffhand, None);
|
let inactive_offhand = self.swap(EquipSlot::InactiveOffhand, None, time);
|
||||||
// Equip weapons into new slots
|
// Equip weapons into new slots
|
||||||
assert!(
|
assert!(
|
||||||
self.swap(EquipSlot::ActiveMainhand, inactive_mainhand)
|
self.swap(EquipSlot::ActiveMainhand, inactive_mainhand, time)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
self.swap(EquipSlot::ActiveOffhand, inactive_offhand)
|
self.swap(EquipSlot::ActiveOffhand, inactive_offhand, time)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
self.swap(EquipSlot::InactiveMainhand, active_mainhand)
|
self.swap(EquipSlot::InactiveMainhand, active_mainhand, time)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
self.swap(EquipSlot::InactiveOffhand, active_offhand)
|
self.swap(EquipSlot::InactiveOffhand, active_offhand, time)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -452,11 +508,26 @@ impl Loadout {
|
|||||||
item.reset_durability(ability_map, msm);
|
item.reset_durability(ability_map, msm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn cull_recently_unequipped_items(&mut self, time: Time) {
|
||||||
|
for (unequip_time, _count) in self.recently_unequipped_items.values_mut() {
|
||||||
|
// If somehow time went backwards or faulty unequip time supplied, set unequip
|
||||||
|
// time to minimum of current time and unequip time
|
||||||
|
if time.0 < unequip_time.0 {
|
||||||
|
*unequip_time = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.recently_unequipped_items
|
||||||
|
.retain(|_def, (unequip_time, count)| {
|
||||||
|
(time.0 - unequip_time.0 < UNEQUIP_TRACKING_DURATION) && *count > 0
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::comp::{
|
use crate::{
|
||||||
|
comp::{
|
||||||
inventory::{
|
inventory::{
|
||||||
item::{
|
item::{
|
||||||
armor::{Armor, ArmorKind, Protection},
|
armor::{Armor, ArmorKind, Protection},
|
||||||
@ -467,6 +538,8 @@ mod tests {
|
|||||||
test_helpers::get_test_bag,
|
test_helpers::get_test_bag,
|
||||||
},
|
},
|
||||||
Item,
|
Item,
|
||||||
|
},
|
||||||
|
resources::Time,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -475,7 +548,7 @@ mod tests {
|
|||||||
|
|
||||||
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
||||||
let bag = get_test_bag(18);
|
let bag = get_test_bag(18);
|
||||||
loadout.swap(bag1_slot, Some(bag));
|
loadout.swap(bag1_slot, Some(bag), Time(0.0));
|
||||||
|
|
||||||
let result = loadout.slot_range_for_equip_slot(bag1_slot).unwrap();
|
let result = loadout.slot_range_for_equip_slot(bag1_slot).unwrap();
|
||||||
|
|
||||||
@ -496,7 +569,7 @@ mod tests {
|
|||||||
|
|
||||||
let feet_slot = EquipSlot::Armor(ArmorSlot::Feet);
|
let feet_slot = EquipSlot::Armor(ArmorSlot::Feet);
|
||||||
let boots = Item::new_from_asset_expect("common.items.testing.test_boots");
|
let boots = Item::new_from_asset_expect("common.items.testing.test_boots");
|
||||||
loadout.swap(feet_slot, Some(boots));
|
loadout.swap(feet_slot, Some(boots), Time(0.0));
|
||||||
let result = loadout.slot_range_for_equip_slot(feet_slot);
|
let result = loadout.slot_range_for_equip_slot(feet_slot);
|
||||||
|
|
||||||
assert_eq!(None, result);
|
assert_eq!(None, result);
|
||||||
@ -506,7 +579,11 @@ mod tests {
|
|||||||
fn test_get_slot_to_equip_into_second_bag_slot_free() {
|
fn test_get_slot_to_equip_into_second_bag_slot_free() {
|
||||||
let mut loadout = Loadout::new_empty();
|
let mut loadout = Loadout::new_empty();
|
||||||
|
|
||||||
loadout.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(1)));
|
loadout.swap(
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag1),
|
||||||
|
Some(get_test_bag(1)),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
|
|
||||||
let result = loadout
|
let result = loadout
|
||||||
.get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor(
|
.get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor(
|
||||||
@ -523,10 +600,26 @@ mod tests {
|
|||||||
fn test_get_slot_to_equip_into_no_bag_slots_free() {
|
fn test_get_slot_to_equip_into_no_bag_slots_free() {
|
||||||
let mut loadout = Loadout::new_empty();
|
let mut loadout = Loadout::new_empty();
|
||||||
|
|
||||||
loadout.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(1)));
|
loadout.swap(
|
||||||
loadout.swap(EquipSlot::Armor(ArmorSlot::Bag2), Some(get_test_bag(1)));
|
EquipSlot::Armor(ArmorSlot::Bag1),
|
||||||
loadout.swap(EquipSlot::Armor(ArmorSlot::Bag3), Some(get_test_bag(1)));
|
Some(get_test_bag(1)),
|
||||||
loadout.swap(EquipSlot::Armor(ArmorSlot::Bag4), Some(get_test_bag(1)));
|
Time(0.0),
|
||||||
|
);
|
||||||
|
loadout.swap(
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag2),
|
||||||
|
Some(get_test_bag(1)),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
|
loadout.swap(
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag3),
|
||||||
|
Some(get_test_bag(1)),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
|
loadout.swap(
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag4),
|
||||||
|
Some(get_test_bag(1)),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
|
|
||||||
let result = loadout
|
let result = loadout
|
||||||
.get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor(
|
.get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor(
|
||||||
|
@ -9,6 +9,7 @@ use crate::{
|
|||||||
item::{self, Item},
|
item::{self, Item},
|
||||||
object, quadruped_low, quadruped_medium, theropod, Body,
|
object, quadruped_low, quadruped_medium, theropod, Body,
|
||||||
},
|
},
|
||||||
|
resources::Time,
|
||||||
trade::SiteInformation,
|
trade::SiteInformation,
|
||||||
};
|
};
|
||||||
use rand::{self, distributions::WeightedError, seq::SliceRandom, Rng};
|
use rand::{self, distributions::WeightedError, seq::SliceRandom, Rng};
|
||||||
@ -1148,7 +1149,11 @@ impl LoadoutBuilder {
|
|||||||
.map_or(true, |item| equip_slot.can_hold(&item.kind()))
|
.map_or(true, |item| equip_slot.can_hold(&item.kind()))
|
||||||
);
|
);
|
||||||
|
|
||||||
self.0.swap(equip_slot, item);
|
// Used when creating a loadout, so time not needed as it is used to check when
|
||||||
|
// stuff gets unequipped. A new loadout has never unequipped an item.
|
||||||
|
let time = Time(0.0);
|
||||||
|
|
||||||
|
self.0.swap(equip_slot, item, time);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use core::ops::Not;
|
use core::ops::Not;
|
||||||
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DerefFlaggedStorage};
|
use specs::{Component, DerefFlaggedStorage};
|
||||||
use std::{cmp::Ordering, convert::TryFrom, mem, ops::Range};
|
use std::{cmp::Ordering, convert::TryFrom, mem, ops::Range};
|
||||||
@ -9,7 +10,10 @@ use crate::{
|
|||||||
comp::{
|
comp::{
|
||||||
body::Body,
|
body::Body,
|
||||||
inventory::{
|
inventory::{
|
||||||
item::{tool::AbilityMap, ItemDef, ItemKind, MaterialStatManifest, TagExampleInfo},
|
item::{
|
||||||
|
tool::AbilityMap, ItemDef, ItemDefinitionIdOwned, ItemKind, MaterialStatManifest,
|
||||||
|
TagExampleInfo,
|
||||||
|
},
|
||||||
loadout::Loadout,
|
loadout::Loadout,
|
||||||
slot::{EquipSlot, Slot, SlotError},
|
slot::{EquipSlot, Slot, SlotError},
|
||||||
},
|
},
|
||||||
@ -17,6 +21,7 @@ use crate::{
|
|||||||
slot::{InvSlotId, SlotId},
|
slot::{InvSlotId, SlotId},
|
||||||
Item,
|
Item,
|
||||||
},
|
},
|
||||||
|
resources::Time,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
LoadoutBuilder,
|
LoadoutBuilder,
|
||||||
};
|
};
|
||||||
@ -574,6 +579,24 @@ impl Inventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn slot_with_mutable_recently_unequipped_items(
|
||||||
|
&mut self,
|
||||||
|
inv_slot_id: InvSlotId,
|
||||||
|
) -> (
|
||||||
|
Option<&InvSlot>,
|
||||||
|
&mut HashMap<ItemDefinitionIdOwned, (Time, u8)>,
|
||||||
|
) {
|
||||||
|
match SlotId::from(inv_slot_id) {
|
||||||
|
SlotId::Inventory(slot_idx) => (
|
||||||
|
self.slots.get(slot_idx),
|
||||||
|
&mut self.loadout.recently_unequipped_items,
|
||||||
|
),
|
||||||
|
SlotId::Loadout(loadout_slot_id) => self
|
||||||
|
.loadout
|
||||||
|
.inv_slot_with_mutable_recently_unequipped_items(loadout_slot_id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn slot_mut(&mut self, inv_slot_id: InvSlotId) -> Option<&mut InvSlot> {
|
pub fn slot_mut(&mut self, inv_slot_id: InvSlotId) -> Option<&mut InvSlot> {
|
||||||
match SlotId::from(inv_slot_id) {
|
match SlotId::from(inv_slot_id) {
|
||||||
SlotId::Inventory(slot_idx) => self.slots.get_mut(slot_idx),
|
SlotId::Inventory(slot_idx) => self.slots.get_mut(slot_idx),
|
||||||
@ -615,18 +638,19 @@ impl Inventory {
|
|||||||
&mut self,
|
&mut self,
|
||||||
equip_slot: EquipSlot,
|
equip_slot: EquipSlot,
|
||||||
replacement_item: Option<Item>,
|
replacement_item: Option<Item>,
|
||||||
|
time: Time,
|
||||||
) -> Option<Item> {
|
) -> Option<Item> {
|
||||||
self.loadout.swap(equip_slot, replacement_item)
|
self.loadout.swap(equip_slot, replacement_item, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equip an item from a slot in inventory. The currently equipped item will
|
/// Equip an item from a slot in inventory. The currently equipped item will
|
||||||
/// go into inventory. If the item is going to mainhand, put mainhand in
|
/// go into inventory. If the item is going to mainhand, put mainhand in
|
||||||
/// offhand and place offhand into inventory.
|
/// offhand and place offhand into inventory.
|
||||||
#[must_use = "Returned items will be lost if not used"]
|
#[must_use = "Returned items will be lost if not used"]
|
||||||
pub fn equip(&mut self, inv_slot: InvSlotId) -> Vec<Item> {
|
pub fn equip(&mut self, inv_slot: InvSlotId, time: Time) -> Vec<Item> {
|
||||||
self.get(inv_slot)
|
self.get(inv_slot)
|
||||||
.and_then(|item| self.loadout.get_slot_to_equip_into(&item.kind()))
|
.and_then(|item| self.loadout.get_slot_to_equip_into(&item.kind()))
|
||||||
.map(|equip_slot| self.swap_inventory_loadout(inv_slot, equip_slot))
|
.map(|equip_slot| self.swap_inventory_loadout(inv_slot, equip_slot, time))
|
||||||
.unwrap_or_else(Vec::new)
|
.unwrap_or_else(Vec::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,7 +704,11 @@ impl Inventory {
|
|||||||
/// equipped if inventory has no slots available.
|
/// equipped if inventory has no slots available.
|
||||||
#[must_use = "Returned items will be lost if not used"]
|
#[must_use = "Returned items will be lost if not used"]
|
||||||
#[allow(clippy::needless_collect)] // This is a false positive, the collect is needed
|
#[allow(clippy::needless_collect)] // This is a false positive, the collect is needed
|
||||||
pub fn unequip(&mut self, equip_slot: EquipSlot) -> Result<Option<Vec<Item>>, SlotError> {
|
pub fn unequip(
|
||||||
|
&mut self,
|
||||||
|
equip_slot: EquipSlot,
|
||||||
|
time: Time,
|
||||||
|
) -> Result<Option<Vec<Item>>, SlotError> {
|
||||||
// Ensure there is enough space in the inventory to place the unequipped item
|
// Ensure there is enough space in the inventory to place the unequipped item
|
||||||
if self.free_slots_minus_equipped_item(equip_slot) == 0 {
|
if self.free_slots_minus_equipped_item(equip_slot) == 0 {
|
||||||
return Err(SlotError::InventoryFull);
|
return Err(SlotError::InventoryFull);
|
||||||
@ -688,7 +716,7 @@ impl Inventory {
|
|||||||
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.loadout
|
.loadout
|
||||||
.swap(equip_slot, None)
|
.swap(equip_slot, None, time)
|
||||||
.and_then(|mut unequipped_item| {
|
.and_then(|mut unequipped_item| {
|
||||||
let unloaded_items: Vec<Item> = unequipped_item.drain().collect();
|
let unloaded_items: Vec<Item> = unequipped_item.drain().collect();
|
||||||
self.push(unequipped_item)
|
self.push(unequipped_item)
|
||||||
@ -721,7 +749,7 @@ impl Inventory {
|
|||||||
/// Swaps items from two slots, regardless of if either is inventory or
|
/// Swaps items from two slots, regardless of if either is inventory or
|
||||||
/// loadout.
|
/// loadout.
|
||||||
#[must_use = "Returned items will be lost if not used"]
|
#[must_use = "Returned items will be lost if not used"]
|
||||||
pub fn swap(&mut self, slot_a: Slot, slot_b: Slot) -> Vec<Item> {
|
pub fn swap(&mut self, slot_a: Slot, slot_b: Slot, time: Time) -> Vec<Item> {
|
||||||
match (slot_a, slot_b) {
|
match (slot_a, slot_b) {
|
||||||
(Slot::Inventory(slot_a), Slot::Inventory(slot_b)) => {
|
(Slot::Inventory(slot_a), Slot::Inventory(slot_b)) => {
|
||||||
self.swap_slots(slot_a, slot_b);
|
self.swap_slots(slot_a, slot_b);
|
||||||
@ -729,10 +757,10 @@ impl Inventory {
|
|||||||
},
|
},
|
||||||
(Slot::Inventory(inv_slot), Slot::Equip(equip_slot))
|
(Slot::Inventory(inv_slot), Slot::Equip(equip_slot))
|
||||||
| (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => {
|
| (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => {
|
||||||
self.swap_inventory_loadout(inv_slot, equip_slot)
|
self.swap_inventory_loadout(inv_slot, equip_slot, time)
|
||||||
},
|
},
|
||||||
(Slot::Equip(slot_a), Slot::Equip(slot_b)) => {
|
(Slot::Equip(slot_a), Slot::Equip(slot_b)) => {
|
||||||
self.loadout.swap_slots(slot_a, slot_b);
|
self.loadout.swap_slots(slot_a, slot_b, time);
|
||||||
Vec::new()
|
Vec::new()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -768,6 +796,7 @@ impl Inventory {
|
|||||||
&mut self,
|
&mut self,
|
||||||
inv_slot_id: InvSlotId,
|
inv_slot_id: InvSlotId,
|
||||||
equip_slot: EquipSlot,
|
equip_slot: EquipSlot,
|
||||||
|
time: Time,
|
||||||
) -> Vec<Item> {
|
) -> Vec<Item> {
|
||||||
if !self.can_swap(inv_slot_id, equip_slot) {
|
if !self.can_swap(inv_slot_id, equip_slot) {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
@ -777,7 +806,7 @@ impl Inventory {
|
|||||||
let from_inv = self.remove(inv_slot_id);
|
let from_inv = self.remove(inv_slot_id);
|
||||||
|
|
||||||
// Swap the equipped item for the item from the inventory
|
// Swap the equipped item for the item from the inventory
|
||||||
let from_equip = self.loadout.swap(equip_slot, from_inv);
|
let from_equip = self.loadout.swap(equip_slot, from_inv, time);
|
||||||
|
|
||||||
let unloaded_items = from_equip
|
let unloaded_items = from_equip
|
||||||
.map(|mut from_equip| {
|
.map(|mut from_equip| {
|
||||||
@ -803,10 +832,10 @@ impl Inventory {
|
|||||||
if self.loadout.equipped(EquipSlot::ActiveMainhand).is_none()
|
if self.loadout.equipped(EquipSlot::ActiveMainhand).is_none()
|
||||||
&& self.loadout.equipped(EquipSlot::ActiveOffhand).is_some()
|
&& self.loadout.equipped(EquipSlot::ActiveOffhand).is_some()
|
||||||
{
|
{
|
||||||
let offhand = self.loadout.swap(EquipSlot::ActiveOffhand, None);
|
let offhand = self.loadout.swap(EquipSlot::ActiveOffhand, None, time);
|
||||||
assert!(
|
assert!(
|
||||||
self.loadout
|
self.loadout
|
||||||
.swap(EquipSlot::ActiveMainhand, offhand)
|
.swap(EquipSlot::ActiveMainhand, offhand, time)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -815,10 +844,10 @@ impl Inventory {
|
|||||||
if self.loadout.equipped(EquipSlot::InactiveMainhand).is_none()
|
if self.loadout.equipped(EquipSlot::InactiveMainhand).is_none()
|
||||||
&& self.loadout.equipped(EquipSlot::InactiveOffhand).is_some()
|
&& self.loadout.equipped(EquipSlot::InactiveOffhand).is_some()
|
||||||
{
|
{
|
||||||
let offhand = self.loadout.swap(EquipSlot::InactiveOffhand, None);
|
let offhand = self.loadout.swap(EquipSlot::InactiveOffhand, None, time);
|
||||||
assert!(
|
assert!(
|
||||||
self.loadout
|
self.loadout
|
||||||
.swap(EquipSlot::InactiveMainhand, offhand)
|
.swap(EquipSlot::InactiveMainhand, offhand, time)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -867,7 +896,7 @@ impl Inventory {
|
|||||||
self.loadout.equipped_items_replaceable_by(item_kind)
|
self.loadout.equipped_items_replaceable_by(item_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn swap_equipped_weapons(&mut self) { self.loadout.swap_equipped_weapons() }
|
pub fn swap_equipped_weapons(&mut self, time: Time) { self.loadout.swap_equipped_weapons(time) }
|
||||||
|
|
||||||
/// Update internal computed state of all top level items in this loadout.
|
/// Update internal computed state of all top level items in this loadout.
|
||||||
/// Used only when loading in persistence code.
|
/// Used only when loading in persistence code.
|
||||||
@ -883,13 +912,56 @@ impl Inventory {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Increments durability of all valid items equipped in loaodut by 1
|
/// Increments durability of all valid items equipped in loaodut and
|
||||||
|
/// recently unequipped from loadout by 1
|
||||||
pub fn damage_items(
|
pub fn damage_items(
|
||||||
&mut self,
|
&mut self,
|
||||||
ability_map: &item::tool::AbilityMap,
|
ability_map: &item::tool::AbilityMap,
|
||||||
msm: &item::MaterialStatManifest,
|
msm: &item::MaterialStatManifest,
|
||||||
|
time: Time,
|
||||||
) {
|
) {
|
||||||
self.loadout.damage_items(ability_map, msm)
|
self.loadout.damage_items(ability_map, msm);
|
||||||
|
self.loadout.cull_recently_unequipped_items(time);
|
||||||
|
|
||||||
|
let slots = self
|
||||||
|
.slots_with_id()
|
||||||
|
.filter(|(_slot, item)| {
|
||||||
|
item.as_ref().map_or(false, |item| {
|
||||||
|
item.durability_lost()
|
||||||
|
.map_or(false, |dur| dur < Item::MAX_DURABILITY)
|
||||||
|
&& self
|
||||||
|
.loadout
|
||||||
|
.recently_unequipped_items
|
||||||
|
.contains_key(&item.item_definition_id())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(|(slot, _item)| slot)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
slots.into_iter().for_each(|slot| {
|
||||||
|
let slot = if let (Some(Some(item)), recently_unequipped_items) =
|
||||||
|
self.slot_with_mutable_recently_unequipped_items(slot)
|
||||||
|
{
|
||||||
|
if let Some((_unequip_time, count)) =
|
||||||
|
recently_unequipped_items.get_mut(&item.item_definition_id())
|
||||||
|
{
|
||||||
|
if *count > 0 {
|
||||||
|
*count -= 1;
|
||||||
|
Some(slot)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some(slot) = slot {
|
||||||
|
if let Some(Some(item)) = self.slot_mut(slot) {
|
||||||
|
item.increment_damage(ability_map, msm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets durability of item in specified slot
|
/// Resets durability of item in specified slot
|
||||||
|
@ -129,7 +129,7 @@ fn free_slots_minus_equipped_item_items_only_present_in_equipped_bag_slots() {
|
|||||||
let bag = get_test_bag(18);
|
let bag = get_test_bag(18);
|
||||||
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(bag1_slot, Some(bag.duplicate(ability_map, msm)));
|
.swap(bag1_slot, Some(bag.duplicate(ability_map, msm)), Time(0.0));
|
||||||
|
|
||||||
assert!(inv.insert_at(InvSlotId::new(15, 0), bag).unwrap().is_none());
|
assert!(inv.insert_at(InvSlotId::new(15, 0), bag).unwrap().is_none());
|
||||||
|
|
||||||
@ -149,10 +149,11 @@ fn free_slots_minus_equipped_item() {
|
|||||||
let bag = get_test_bag(18);
|
let bag = get_test_bag(18);
|
||||||
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(bag1_slot, Some(bag.duplicate(ability_map, msm)));
|
.swap(bag1_slot, Some(bag.duplicate(ability_map, msm)), Time(0.0));
|
||||||
inv.loadout.swap(
|
inv.loadout.swap(
|
||||||
EquipSlot::Armor(ArmorSlot::Bag2),
|
EquipSlot::Armor(ArmorSlot::Bag2),
|
||||||
Some(bag.duplicate(ability_map, msm)),
|
Some(bag.duplicate(ability_map, msm)),
|
||||||
|
Time(0.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(inv.insert_at(InvSlotId::new(16, 0), bag).unwrap().is_none());
|
assert!(inv.insert_at(InvSlotId::new(16, 0), bag).unwrap().is_none());
|
||||||
@ -169,7 +170,7 @@ fn get_slot_range_for_equip_slot() {
|
|||||||
let mut inv = Inventory::with_empty();
|
let mut inv = Inventory::with_empty();
|
||||||
let bag = get_test_bag(18);
|
let bag = get_test_bag(18);
|
||||||
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1);
|
||||||
inv.loadout.swap(bag1_slot, Some(bag));
|
inv.loadout.swap(bag1_slot, Some(bag), Time(0.0));
|
||||||
|
|
||||||
let result = inv.get_slot_range_for_equip_slot(bag1_slot).unwrap();
|
let result = inv.get_slot_range_for_equip_slot(bag1_slot).unwrap();
|
||||||
|
|
||||||
@ -198,7 +199,11 @@ fn can_swap_equipped_bag_into_empty_inv_slot(
|
|||||||
) {
|
) {
|
||||||
let mut inv = Inventory::with_empty();
|
let mut inv = Inventory::with_empty();
|
||||||
|
|
||||||
inv.replace_loadout_item(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(18)));
|
inv.replace_loadout_item(
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag1),
|
||||||
|
Some(get_test_bag(18)),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
|
|
||||||
fill_inv_slots(&mut inv, 18 - free_slots);
|
fill_inv_slots(&mut inv, 18 - free_slots);
|
||||||
|
|
||||||
@ -211,7 +216,11 @@ fn can_swap_equipped_bag_into_empty_inv_slot(
|
|||||||
fn can_swap_equipped_bag_into_only_empty_slot_provided_by_itself_should_return_true() {
|
fn can_swap_equipped_bag_into_only_empty_slot_provided_by_itself_should_return_true() {
|
||||||
let mut inv = Inventory::with_empty();
|
let mut inv = Inventory::with_empty();
|
||||||
|
|
||||||
inv.replace_loadout_item(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(18)));
|
inv.replace_loadout_item(
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag1),
|
||||||
|
Some(get_test_bag(18)),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
|
|
||||||
fill_inv_slots(&mut inv, 35);
|
fill_inv_slots(&mut inv, 35);
|
||||||
|
|
||||||
@ -231,22 +240,26 @@ fn unequip_items_both_hands() {
|
|||||||
inv.replace_loadout_item(
|
inv.replace_loadout_item(
|
||||||
EquipSlot::ActiveMainhand,
|
EquipSlot::ActiveMainhand,
|
||||||
Some(sword.duplicate(ability_map, msm)),
|
Some(sword.duplicate(ability_map, msm)),
|
||||||
|
Time(0.0),
|
||||||
);
|
);
|
||||||
inv.replace_loadout_item(
|
inv.replace_loadout_item(
|
||||||
EquipSlot::InactiveMainhand,
|
EquipSlot::InactiveMainhand,
|
||||||
Some(sword.duplicate(ability_map, msm)),
|
Some(sword.duplicate(ability_map, msm)),
|
||||||
|
Time(0.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fill all inventory slots except one
|
// Fill all inventory slots except one
|
||||||
fill_inv_slots(&mut inv, 17);
|
fill_inv_slots(&mut inv, 17);
|
||||||
|
|
||||||
let result = inv.unequip(EquipSlot::ActiveMainhand);
|
let result = inv.unequip(EquipSlot::ActiveMainhand, Time(0.0));
|
||||||
// We have space in the inventory, so this should have unequipped
|
// We have space in the inventory, so this should have unequipped
|
||||||
assert_eq!(None, inv.equipped(EquipSlot::ActiveMainhand));
|
assert_eq!(None, inv.equipped(EquipSlot::ActiveMainhand));
|
||||||
assert_eq!(18, inv.populated_slots());
|
assert_eq!(18, inv.populated_slots());
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
let result = inv.unequip(EquipSlot::InactiveMainhand).unwrap_err();
|
let result = inv
|
||||||
|
.unequip(EquipSlot::InactiveMainhand, Time(0.0))
|
||||||
|
.unwrap_err();
|
||||||
assert_eq!(SlotError::InventoryFull, result);
|
assert_eq!(SlotError::InventoryFull, result);
|
||||||
|
|
||||||
// There is no more space in the inventory, so this should still be equipped
|
// There is no more space in the inventory, so this should still be equipped
|
||||||
@ -274,9 +287,10 @@ fn equip_replace_already_equipped_item() {
|
|||||||
starting_sandles
|
starting_sandles
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|item| item.duplicate(ability_map, msm)),
|
.map(|item| item.duplicate(ability_map, msm)),
|
||||||
|
Time(0.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = inv.equip(InvSlotId::new(0, 0));
|
let _ = inv.equip(InvSlotId::new(0, 0), Time(0.0));
|
||||||
|
|
||||||
// We should now have the testing boots equipped
|
// We should now have the testing boots equipped
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -302,7 +316,11 @@ fn equip_equipping_smaller_bag_from_last_slot_of_big_bag() {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(large_bag))
|
.swap(
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag1),
|
||||||
|
Some(large_bag),
|
||||||
|
Time(0.0)
|
||||||
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -311,6 +329,7 @@ fn equip_equipping_smaller_bag_from_last_slot_of_big_bag() {
|
|||||||
let result = inv.swap(
|
let result = inv.swap(
|
||||||
Slot::Equip(EquipSlot::Armor(ArmorSlot::Bag1)),
|
Slot::Equip(EquipSlot::Armor(ArmorSlot::Bag1)),
|
||||||
Slot::Inventory(InvSlotId::new(15, 15)),
|
Slot::Inventory(InvSlotId::new(15, 15)),
|
||||||
|
Time(0.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -327,15 +346,18 @@ fn unequip_unequipping_bag_into_its_own_slot_with_no_other_free_slots_returns_on
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag))
|
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag), Time(0.0))
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fill all inventory built-in slots
|
// Fill all inventory built-in slots
|
||||||
fill_inv_slots(&mut inv, 18);
|
fill_inv_slots(&mut inv, 18);
|
||||||
|
|
||||||
let result =
|
let result = inv.swap_inventory_loadout(
|
||||||
inv.swap_inventory_loadout(InvSlotId::new(15, 0), EquipSlot::Armor(ArmorSlot::Bag1));
|
InvSlotId::new(15, 0),
|
||||||
|
EquipSlot::Armor(ArmorSlot::Bag1),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(result.len(), 1);
|
assert_eq!(result.len(), 1);
|
||||||
// Because the slot the bag was swapped with no longer exists as it was provided
|
// Because the slot the bag was swapped with no longer exists as it was provided
|
||||||
@ -358,13 +380,14 @@ fn equip_one_bag_equipped_equip_second_bag() {
|
|||||||
.swap(
|
.swap(
|
||||||
EquipSlot::Armor(ArmorSlot::Bag1),
|
EquipSlot::Armor(ArmorSlot::Bag1),
|
||||||
Some(bag.duplicate(ability_map, msm)),
|
Some(bag.duplicate(ability_map, msm)),
|
||||||
|
Time(0.0)
|
||||||
)
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
|
|
||||||
inv.push(bag).unwrap();
|
inv.push(bag).unwrap();
|
||||||
|
|
||||||
let _ = inv.equip(InvSlotId::new(0, 0));
|
let _ = inv.equip(InvSlotId::new(0, 0), Time(0.0));
|
||||||
|
|
||||||
assert!(inv.equipped(EquipSlot::Armor(ArmorSlot::Bag2)).is_some());
|
assert!(inv.equipped(EquipSlot::Armor(ArmorSlot::Bag2)).is_some());
|
||||||
}
|
}
|
||||||
@ -376,7 +399,7 @@ fn free_after_swap_equipped_item_has_more_slots() {
|
|||||||
let bag = get_test_bag(18);
|
let bag = get_test_bag(18);
|
||||||
assert!(
|
assert!(
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag))
|
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag), Time(0.0))
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -399,7 +422,7 @@ fn free_after_swap_equipped_item_has_less_slots() {
|
|||||||
let bag = get_test_bag(9);
|
let bag = get_test_bag(9);
|
||||||
assert!(
|
assert!(
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag))
|
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag), Time(0.0))
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -422,7 +445,7 @@ fn free_after_swap_equipped_item_with_slots_swapped_with_empty_inv_slot() {
|
|||||||
let bag = get_test_bag(9);
|
let bag = get_test_bag(9);
|
||||||
assert!(
|
assert!(
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag))
|
.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag), Time(0.0))
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -475,15 +498,18 @@ fn backpack_crash() {
|
|||||||
|
|
||||||
let backpack = Item::new_from_asset_expect("common.items.armor.misc.back.backpack");
|
let backpack = Item::new_from_asset_expect("common.items.armor.misc.back.backpack");
|
||||||
inv.loadout
|
inv.loadout
|
||||||
.swap(EquipSlot::Armor(ArmorSlot::Back), Some(backpack));
|
.swap(EquipSlot::Armor(ArmorSlot::Back), Some(backpack), Time(0.0));
|
||||||
|
|
||||||
fill_inv_slots(&mut inv, 35);
|
fill_inv_slots(&mut inv, 35);
|
||||||
|
|
||||||
let cape = Item::new_from_asset_expect("common.items.armor.misc.back.admin");
|
let cape = Item::new_from_asset_expect("common.items.armor.misc.back.admin");
|
||||||
assert!(inv.push(cape).is_ok());
|
assert!(inv.push(cape).is_ok());
|
||||||
|
|
||||||
let returned_items =
|
let returned_items = inv.swap_inventory_loadout(
|
||||||
inv.swap_inventory_loadout(InvSlotId::new(9, 17), EquipSlot::Armor(ArmorSlot::Back));
|
InvSlotId::new(9, 17),
|
||||||
|
EquipSlot::Armor(ArmorSlot::Back),
|
||||||
|
Time(0.0),
|
||||||
|
);
|
||||||
assert_eq!(18, returned_items.len());
|
assert_eq!(18, returned_items.len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ItemDefinitionId::Simple("common.items.armor.misc.back.backpack"),
|
ItemDefinitionId::Simple("common.items.armor.misc.back.backpack"),
|
||||||
|
@ -976,7 +976,7 @@ impl RepairRecipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn inputs(&self, item: &Item) -> impl Iterator<Item = (&RecipeInput, u32)> {
|
pub fn inputs(&self, item: &Item) -> impl Iterator<Item = (&RecipeInput, u32)> {
|
||||||
let item_durability = item.durability().unwrap_or(0);
|
let item_durability = item.durability_lost().unwrap_or(0);
|
||||||
self.inputs
|
self.inputs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |(input, original_amount)| {
|
.filter_map(move |(input, original_amount)| {
|
||||||
|
@ -22,7 +22,7 @@ tracing = { version = "0.1", default-features = false }
|
|||||||
vek = { version = "0.15.8", features = ["serde"] }
|
vek = { version = "0.15.8", features = ["serde"] }
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
|
|
||||||
# ECS
|
# ECS
|
||||||
specs = { version = "0.18", features = ["serde", "storage-event-control", "derive"] }
|
specs = { version = "0.18", features = ["serde", "storage-event-control", "derive"] }
|
||||||
|
@ -23,7 +23,7 @@ ordered-float = { version = "3", default-features = false }
|
|||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
indexmap = "1.3.0"
|
indexmap = "1.3.0"
|
||||||
slab = "0.4.2"
|
slab = "0.4.2"
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ lz-fear = { version = "0.1.1", optional = true }
|
|||||||
async-trait = "0.1.42"
|
async-trait = "0.1.42"
|
||||||
bytes = "^1"
|
bytes = "^1"
|
||||||
# faster HashMaps
|
# faster HashMaps
|
||||||
hashbrown = { version = ">=0.9, <0.13" }
|
hashbrown = { version = "0.13" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tracing-subscriber = { version = "0.3.7", default-features = false, features = ["env-filter", "fmt", "time", "ansi", "smallvec"] }
|
tracing-subscriber = { version = "0.3.7", default-features = false, features = ["env-filter", "fmt", "time", "ansi", "smallvec"] }
|
||||||
|
@ -24,7 +24,7 @@ rand = { version = "0.8" }
|
|||||||
# async traits
|
# async traits
|
||||||
async-trait = "0.1.42"
|
async-trait = "0.1.42"
|
||||||
bytes = "^1"
|
bytes = "^1"
|
||||||
hashbrown = { version = ">=0.12, <0.13" }
|
hashbrown = { version = "0.13" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
async-channel = "1.5.1"
|
async-channel = "1.5.1"
|
||||||
|
@ -8,7 +8,7 @@ common = { package = "veloren-common", path = "../common" }
|
|||||||
world = { package = "veloren-world", path = "../world" }
|
world = { package = "veloren-world", path = "../world" }
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
serde = { version = "1.0.110", features = ["derive"] }
|
serde = { version = "1.0.110", features = ["derive"] }
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
enum-map = { version = "2.4", features = ["serde"] }
|
enum-map = { version = "2.4", features = ["serde"] }
|
||||||
vek = { version = "0.15.8", features = ["serde"] }
|
vek = { version = "0.15.8", features = ["serde"] }
|
||||||
rmp-serde = "1.1.0"
|
rmp-serde = "1.1.0"
|
||||||
|
@ -54,7 +54,7 @@ ron = { version = "0.8", default-features = false }
|
|||||||
serde = { version = "1.0.110", features = ["derive"] }
|
serde = { version = "1.0.110", features = ["derive"] }
|
||||||
serde_json = "1.0.50"
|
serde_json = "1.0.50"
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
parking_lot = { version = "0.12" }
|
parking_lot = { version = "0.12" }
|
||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
|
@ -28,6 +28,7 @@ use common::{
|
|||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
states::utils::StageSection,
|
states::utils::StageSection,
|
||||||
terrain::{Block, BlockKind, TerrainGrid},
|
terrain::{Block, BlockKind, TerrainGrid},
|
||||||
|
trade::{TradeResult, Trades},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
@ -508,9 +509,11 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
|
|||||||
|
|
||||||
// Modify durability on all equipped items
|
// Modify durability on all equipped items
|
||||||
if let Some(mut inventory) = state.ecs().write_storage::<Inventory>().get_mut(entity) {
|
if let Some(mut inventory) = state.ecs().write_storage::<Inventory>().get_mut(entity) {
|
||||||
let ability_map = state.ecs().read_resource::<AbilityMap>();
|
let ecs = state.ecs();
|
||||||
let msm = state.ecs().read_resource::<MaterialStatManifest>();
|
let ability_map = ecs.read_resource::<AbilityMap>();
|
||||||
inventory.damage_items(&ability_map, &msm);
|
let msm = ecs.read_resource::<MaterialStatManifest>();
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
|
inventory.damage_items(&ability_map, &msm, *time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(actor) = state.entity_as_actor(entity) {
|
if let Some(actor) = state.entity_as_actor(entity) {
|
||||||
@ -1407,6 +1410,35 @@ pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) {
|
|||||||
entity,
|
entity,
|
||||||
buff_change: buff::BuffChange::RemoveByKind(BuffKind::Saturation),
|
buff_change: buff::BuffChange::RemoveByKind(BuffKind::Saturation),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If entity was in an active trade, cancel it
|
||||||
|
let mut trades = ecs.write_resource::<Trades>();
|
||||||
|
let uids = ecs.read_storage::<Uid>();
|
||||||
|
if let Some(uid) = uids.get(entity) {
|
||||||
|
if let Some(trade) = trades.entity_trades.get(uid).copied() {
|
||||||
|
trades
|
||||||
|
.decline_trade(trade, *uid)
|
||||||
|
.and_then(|uid| ecs.entity_from_uid(uid.0))
|
||||||
|
.map(|entity_b| {
|
||||||
|
// Notify both parties that the trade ended
|
||||||
|
let clients = ecs.read_storage::<Client>();
|
||||||
|
let mut agents = ecs.write_storage::<Agent>();
|
||||||
|
let mut notify_trade_party = |entity| {
|
||||||
|
if let Some(client) = clients.get(entity) {
|
||||||
|
client
|
||||||
|
.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
|
||||||
|
}
|
||||||
|
if let Some(agent) = agents.get_mut(entity) {
|
||||||
|
agent
|
||||||
|
.inbox
|
||||||
|
.push_back(AgentEvent::FinishedTrade(TradeResult::Declined));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
notify_trade_party(entity);
|
||||||
|
notify_trade_party(entity_b);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_change_ability(
|
pub fn handle_change_ability(
|
||||||
|
@ -15,6 +15,7 @@ use common::{
|
|||||||
recipe::{
|
recipe::{
|
||||||
self, default_component_recipe_book, default_recipe_book, default_repair_recipe_book,
|
self, default_component_recipe_book, default_recipe_book, default_repair_recipe_book,
|
||||||
},
|
},
|
||||||
|
resources::Time,
|
||||||
terrain::{Block, SpriteKind},
|
terrain::{Block, SpriteKind},
|
||||||
trade::Trades,
|
trade::Trades,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -113,6 +114,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let time = *state.ecs().read_resource::<Time>();
|
||||||
|
|
||||||
match manip {
|
match manip {
|
||||||
comp::InventoryManip::Pickup(pickup_uid) => {
|
comp::InventoryManip::Pickup(pickup_uid) => {
|
||||||
let item_entity =
|
let item_entity =
|
||||||
@ -405,7 +408,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
swap_lantern(&mut state.ecs().write_storage(), entity, lantern_info);
|
swap_lantern(&mut state.ecs().write_storage(), entity, lantern_info);
|
||||||
}
|
}
|
||||||
if let Some(pos) = state.ecs().read_storage::<comp::Pos>().get(entity) {
|
if let Some(pos) = state.ecs().read_storage::<comp::Pos>().get(entity) {
|
||||||
dropped_items.extend(inventory.equip(slot).into_iter().map(|x| {
|
dropped_items.extend(inventory.equip(slot, time).into_iter().map(
|
||||||
|
|x| {
|
||||||
(
|
(
|
||||||
*pos,
|
*pos,
|
||||||
state
|
state
|
||||||
@ -413,7 +417,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
x,
|
x,
|
||||||
)
|
)
|
||||||
}));
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Some(InventoryUpdateEvent::Used)
|
Some(InventoryUpdateEvent::Used)
|
||||||
} else if let Some(item) = inventory.take(
|
} else if let Some(item) = inventory.take(
|
||||||
@ -525,7 +530,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
if let Some(pos) = state.ecs().read_storage::<comp::Pos>().get(entity) {
|
if let Some(pos) = state.ecs().read_storage::<comp::Pos>().get(entity) {
|
||||||
// Unequip the item, any items that no longer fit within the inventory (due
|
// Unequip the item, any items that no longer fit within the inventory (due
|
||||||
// to unequipping a bag for example) will be dropped on the floor
|
// to unequipping a bag for example) will be dropped on the floor
|
||||||
if let Ok(Some(leftover_items)) = inventory.unequip(slot) {
|
if let Ok(Some(leftover_items)) = inventory.unequip(slot, time) {
|
||||||
dropped_items.extend(leftover_items.into_iter().map(|x| {
|
dropped_items.extend(leftover_items.into_iter().map(|x| {
|
||||||
(
|
(
|
||||||
*pos,
|
*pos,
|
||||||
@ -598,7 +603,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
|
|
||||||
// If the stacks weren't mergable carry out a swap.
|
// If the stacks weren't mergable carry out a swap.
|
||||||
if !merged_stacks {
|
if !merged_stacks {
|
||||||
dropped_items.extend(inventory.swap(a, b).into_iter().map(|x| {
|
dropped_items.extend(inventory.swap(a, b, time).into_iter().map(|x| {
|
||||||
(
|
(
|
||||||
*pos,
|
*pos,
|
||||||
state
|
state
|
||||||
@ -665,7 +670,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
comp::InventoryManip::Drop(slot) => {
|
comp::InventoryManip::Drop(slot) => {
|
||||||
let item = match slot {
|
let item = match slot {
|
||||||
Slot::Inventory(slot) => inventory.remove(slot),
|
Slot::Inventory(slot) => inventory.remove(slot),
|
||||||
Slot::Equip(slot) => inventory.replace_loadout_item(slot, None),
|
Slot::Equip(slot) => inventory.replace_loadout_item(slot, None, time),
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: We should really require the drop and write to be atomic!
|
// FIXME: We should really require the drop and write to be atomic!
|
||||||
@ -910,7 +915,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
drop(inventories);
|
drop(inventories);
|
||||||
},
|
},
|
||||||
comp::InventoryManip::SwapEquippedWeapons => {
|
comp::InventoryManip::SwapEquippedWeapons => {
|
||||||
inventory.swap_equipped_weapons();
|
inventory.swap_equipped_weapons(time);
|
||||||
drop(inventories);
|
drop(inventories);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ use common::{
|
|||||||
character::CharacterId,
|
character::CharacterId,
|
||||||
comp,
|
comp,
|
||||||
comp::{group, pet::is_tameable, Presence, PresenceKind},
|
comp::{group, pet::is_tameable, Presence, PresenceKind},
|
||||||
|
resources::Time,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
use common_base::span;
|
use common_base::span;
|
||||||
@ -482,6 +483,7 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possessee_uid: Ui
|
|||||||
drop(admins);
|
drop(admins);
|
||||||
|
|
||||||
// Put possess item into loadout
|
// Put possess item into loadout
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
let mut inventories = ecs.write_storage::<Inventory>();
|
let mut inventories = ecs.write_storage::<Inventory>();
|
||||||
let mut inventory = inventories
|
let mut inventory = inventories
|
||||||
.entry(possessee)
|
.entry(possessee)
|
||||||
@ -493,12 +495,13 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possessee_uid: Ui
|
|||||||
let leftover_items = inventory.swap(
|
let leftover_items = inventory.swap(
|
||||||
Slot::Equip(EquipSlot::ActiveMainhand),
|
Slot::Equip(EquipSlot::ActiveMainhand),
|
||||||
Slot::Equip(EquipSlot::InactiveMainhand),
|
Slot::Equip(EquipSlot::InactiveMainhand),
|
||||||
|
*time,
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
leftover_items.is_empty(),
|
leftover_items.is_empty(),
|
||||||
"Swapping active and inactive mainhands never results in leftover items"
|
"Swapping active and inactive mainhands never results in leftover items"
|
||||||
);
|
);
|
||||||
inventory.replace_loadout_item(EquipSlot::ActiveMainhand, Some(debug_item));
|
inventory.replace_loadout_item(EquipSlot::ActiveMainhand, Some(debug_item), *time);
|
||||||
}
|
}
|
||||||
drop(inventories);
|
drop(inventories);
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ dot_vox = "5.1"
|
|||||||
enum-iterator = "1.1.0"
|
enum-iterator = "1.1.0"
|
||||||
guillotiere = "0.6.2"
|
guillotiere = "0.6.2"
|
||||||
etagere = "0.2.7"
|
etagere = "0.2.7"
|
||||||
hashbrown = {version = "0.12", features = ["rayon", "serde", "nightly"]}
|
hashbrown = {version = "0.13", features = ["rayon", "serde", "nightly"]}
|
||||||
image = {version = "0.24", default-features = false, features = ["ico", "png"]}
|
image = {version = "0.24", default-features = false, features = ["ico", "png"]}
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
native-dialog = { version = "0.6.3", optional = true }
|
native-dialog = { version = "0.6.3", optional = true }
|
||||||
|
@ -1376,7 +1376,7 @@ impl<'a> Widget for Crafting<'a> {
|
|||||||
let repair_slot = CraftSlot {
|
let repair_slot = CraftSlot {
|
||||||
index: 0,
|
index: 0,
|
||||||
slot: self.show.crafting_fields.recipe_inputs.get(&0).copied(),
|
slot: self.show.crafting_fields.recipe_inputs.get(&0).copied(),
|
||||||
requirement: |item, _, _| item.durability().map_or(false, |d| d > 0),
|
requirement: |item, _, _| item.durability_lost().map_or(false, |d| d > 0),
|
||||||
info: None,
|
info: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1413,7 +1413,7 @@ impl<'a> Widget for Crafting<'a> {
|
|||||||
let can_repair = |item: &Item| {
|
let can_repair = |item: &Item| {
|
||||||
// Check that item needs to be repaired, and that inventory has sufficient
|
// Check that item needs to be repaired, and that inventory has sufficient
|
||||||
// materials to repair
|
// materials to repair
|
||||||
item.durability().map_or(false, |d| d > 0)
|
item.durability_lost().map_or(false, |d| d > 0)
|
||||||
&& self.client.repair_recipe_book().repair_recipe(item).map_or(
|
&& self.client.repair_recipe_book().repair_recipe(item).map_or(
|
||||||
false,
|
false,
|
||||||
|recipe| {
|
|recipe| {
|
||||||
|
@ -194,7 +194,7 @@ impl<'a> Widget for Group<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
let uid_to_name_text = |uid, client: &Client| match client.player_list().get(&uid) {
|
let uid_to_name_text = |uid: Uid, client: &Client| match client.player_list().get(&uid) {
|
||||||
Some(player_info) => player_info
|
Some(player_info) => player_info
|
||||||
.character
|
.character
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -359,7 +359,7 @@ pub fn protec2string(stat: Protection) -> String {
|
|||||||
/// Gets the durability of an item in a format more intuitive for UI
|
/// Gets the durability of an item in a format more intuitive for UI
|
||||||
pub fn item_durability(item: &dyn ItemDesc) -> Option<u32> {
|
pub fn item_durability(item: &dyn ItemDesc) -> Option<u32> {
|
||||||
let durability = item
|
let durability = item
|
||||||
.durability()
|
.durability_lost()
|
||||||
.or_else(|| item.has_durability().then_some(0));
|
.or_else(|| item.has_durability().then_some(0));
|
||||||
durability.map(|d| Item::MAX_DURABILITY - d)
|
durability.map(|d| Item::MAX_DURABILITY - d)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ use client::{Client, ServerInfo};
|
|||||||
use common::{
|
use common::{
|
||||||
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER, MAX_NAME_LENGTH},
|
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER, MAX_NAME_LENGTH},
|
||||||
comp::{self, humanoid, inventory::slot::EquipSlot, Inventory, Item},
|
comp::{self, humanoid, inventory::slot::EquipSlot, Inventory, Item},
|
||||||
|
resources::Time,
|
||||||
terrain::TerrainChunkSize,
|
terrain::TerrainChunkSize,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
LoadoutBuilder,
|
LoadoutBuilder,
|
||||||
@ -1812,10 +1813,16 @@ impl Controls {
|
|||||||
inventory.replace_loadout_item(
|
inventory.replace_loadout_item(
|
||||||
EquipSlot::ActiveMainhand,
|
EquipSlot::ActiveMainhand,
|
||||||
mainhand.map(Item::new_from_asset_expect),
|
mainhand.map(Item::new_from_asset_expect),
|
||||||
|
// Voxygen is not authoritative on inventory so we don't care if fake time
|
||||||
|
// is supplied
|
||||||
|
Time(0.0),
|
||||||
);
|
);
|
||||||
inventory.replace_loadout_item(
|
inventory.replace_loadout_item(
|
||||||
EquipSlot::ActiveOffhand,
|
EquipSlot::ActiveOffhand,
|
||||||
offhand.map(Item::new_from_asset_expect),
|
offhand.map(Item::new_from_asset_expect),
|
||||||
|
// Voxygen is not authoritative on inventory so we don't care if fake time
|
||||||
|
// is supplied
|
||||||
|
Time(0.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -670,7 +670,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if item.has_durability() {
|
if item.has_durability() {
|
||||||
let durability = Item::MAX_DURABILITY - item.durability().unwrap_or(0);
|
let durability = Item::MAX_DURABILITY - item.durability_lost().unwrap_or(0);
|
||||||
stat_text(
|
stat_text(
|
||||||
format!(
|
format!(
|
||||||
"{} : {}/{}",
|
"{} : {}/{}",
|
||||||
|
@ -29,7 +29,7 @@ vek = { version = "0.15.8", features = ["serde"] }
|
|||||||
noise = { version = "0.7", default-features = false }
|
noise = { version = "0.7", default-features = false }
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
ordered-float = "3"
|
ordered-float = "3"
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.13", features = ["rayon", "serde", "nightly"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
tracing = { version = "0.1", default-features = false }
|
tracing = { version = "0.1", default-features = false }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
Loading…
Reference in New Issue
Block a user