Recently unequipped items are now tracked and durability loss on death is applied to them

This commit is contained in:
Sam 2023-04-16 13:54:38 -04:00
parent 77785faeec
commit c1a64e1d69
7 changed files with 156 additions and 59 deletions

View File

@ -1,18 +1,26 @@
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
pub(super) recently_unequipped_items: HashMap<ItemDefinitionIdOwned, Time>,
} }
/// 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 +91,36 @@ 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> {
self.recently_unequipped_items.retain(|def, unequip_time| {
let item_reequipped = item
.as_ref()
.map_or(false, |i| def.as_ref() == i.item_definition_id());
let old_unequip = time.0 - unequip_time.0 > UNEQUIP_TRACKING_DURATION;
// Stop tracking item if it is re-equipped or if was unequipped a while ago
!(item_reequipped || old_unequip)
});
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() {
self.recently_unequipped_items
.insert(unequipped_item.item_definition_id().to_owned(), time);
}
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 +182,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 +196,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 +209,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);
} }
} }
@ -367,7 +400,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 +418,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()
); );
} }

View File

@ -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
} }

View File

@ -17,6 +17,7 @@ use crate::{
slot::{InvSlotId, SlotId}, slot::{InvSlotId, SlotId},
Item, Item,
}, },
resources::Time,
uid::Uid, uid::Uid,
LoadoutBuilder, LoadoutBuilder,
}; };
@ -615,18 +616,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 +682,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 +694,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 +727,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 +735,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 +774,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 +784,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 +810,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 +822,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 +874,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 +890,48 @@ 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
.recently_unequipped_items
.retain(|_item, unequip_time| {
time.0 - unequip_time.0 <= loadout::UNEQUIP_TRACKING_DURATION
});
let inv_slots = self
.loadout
.recently_unequipped_items
.keys()
.filter_map(|item_def_id| {
self.slots_with_id()
.find(|&(_, item)| {
if let Some(item) = item {
// Find an item with the matching item definition id and that is not yet
// at maximum durability lost
item.item_definition_id() == *item_def_id
&& item
.durability()
.map_or(true, |dur| dur < Item::MAX_DURABILITY)
} else {
false
}
})
.map(|(slot, _)| slot)
})
.collect::<Vec<_>>();
for inv_slot in inv_slots.iter() {
if let Some(Some(item)) = self.slot_mut(*inv_slot) {
if item.has_durability() {
item.increment_damage(ability_map, msm);
}
}
}
} }
/// Resets durability of item in specified slot /// Resets durability of item in specified slot

View File

@ -508,9 +508,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) {

View File

@ -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);
}, },
} }

View File

@ -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);

View File

@ -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),
); );
} }
}, },