mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/extra-equip-slots' into 'master'
Added Extra Loadout Slots See merge request veloren/veloren!2295
This commit is contained in:
commit
a0599ac046
BIN
assets/voxygen/element/ui/bag/buttons/swap_equipped_weapons.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/bag/buttons/swap_equipped_weapons.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/ui/bag/buttons/swap_equipped_weapons_hover.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/bag/buttons/swap_equipped_weapons_hover.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/ui/bag/buttons/swap_equipped_weapons_press.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/bag/buttons/swap_equipped_weapons_press.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -24,6 +24,10 @@
|
||||
"hud.bag.feet": "Feet",
|
||||
"hud.bag.mainhand": "Mainhand",
|
||||
"hud.bag.offhand": "Offhand",
|
||||
"hud.bag.inactive_mainhand": "Inactive Mainhand",
|
||||
"hud.bag.inactive_offhand": "Inactive Offhand",
|
||||
"hud.bag.swap_equipped_weapons_title": "Swap equipped weapons",
|
||||
"hud.bag.swap_equipped_weapons_desc": "Press {key}",
|
||||
"hud.bag.bag": "Bag",
|
||||
"hud.bag.health": "Health",
|
||||
"hud.bag.stamina": "Stamina",
|
||||
|
@ -805,8 +805,8 @@ fn equipped_item_and_tool(inv: &Inventory, slot: EquipSlot) -> Option<(&Item, &T
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn get_weapons(inv: &Inventory) -> (Option<ToolKind>, Option<ToolKind>) {
|
||||
(
|
||||
equipped_item_and_tool(inv, EquipSlot::Mainhand).map(|(_, tool)| tool.kind),
|
||||
equipped_item_and_tool(inv, EquipSlot::Offhand).map(|(_, tool)| tool.kind),
|
||||
equipped_item_and_tool(inv, EquipSlot::ActiveMainhand).map(|(_, tool)| tool.kind),
|
||||
equipped_item_and_tool(inv, EquipSlot::ActiveOffhand).map(|(_, tool)| tool.kind),
|
||||
)
|
||||
}
|
||||
|
||||
@ -845,14 +845,14 @@ fn weapon_skills(inventory: &Inventory, skill_set: &SkillSet) -> f32 {
|
||||
|
||||
fn get_weapon_rating(inventory: &Inventory, msm: &MaterialStatManifest) -> f32 {
|
||||
let mainhand_rating =
|
||||
if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::Mainhand) {
|
||||
if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::ActiveMainhand) {
|
||||
weapon_rating(item, msm)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let offhand_rating =
|
||||
if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::Offhand) {
|
||||
if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::ActiveOffhand) {
|
||||
weapon_rating(item, msm)
|
||||
} else {
|
||||
0.0
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::comp::{
|
||||
inventory::{
|
||||
item::ItemKind,
|
||||
item::{Hands, ItemKind, Tool},
|
||||
slot::{ArmorSlot, EquipSlot},
|
||||
InvSlot,
|
||||
},
|
||||
@ -75,8 +75,10 @@ impl Loadout {
|
||||
(EquipSlot::Armor(ArmorSlot::Bag2), "bag2".to_string()),
|
||||
(EquipSlot::Armor(ArmorSlot::Bag3), "bag3".to_string()),
|
||||
(EquipSlot::Armor(ArmorSlot::Bag4), "bag4".to_string()),
|
||||
(EquipSlot::Mainhand, "active_item".to_string()),
|
||||
(EquipSlot::Offhand, "second_item".to_string()),
|
||||
(EquipSlot::ActiveMainhand, "active_mainhand".to_string()),
|
||||
(EquipSlot::ActiveOffhand, "active_offhand".to_string()),
|
||||
(EquipSlot::InactiveMainhand, "inactive_mainhand".to_string()),
|
||||
(EquipSlot::InactiveOffhand, "inactive_offhand".to_string()),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(equip_slot, persistence_key)| LoadoutSlot::new(equip_slot, persistence_key))
|
||||
@ -166,23 +168,17 @@ impl Loadout {
|
||||
}
|
||||
|
||||
let item_a = self.swap(equip_slot_a, None);
|
||||
let item_b = self.swap(equip_slot_b, None);
|
||||
let item_b = self.swap(equip_slot_b, item_a);
|
||||
assert_eq!(self.swap(equip_slot_a, item_b), None);
|
||||
|
||||
// Check if items can go in the other slots
|
||||
if item_a
|
||||
.as_ref()
|
||||
.map_or(true, |i| equip_slot_b.can_hold(&i.kind()))
|
||||
&& item_b
|
||||
.as_ref()
|
||||
.map_or(true, |i| equip_slot_a.can_hold(&i.kind()))
|
||||
// Check if items are valid in their new positions
|
||||
if !self.slot_can_hold(equip_slot_a, self.equipped(equip_slot_b).map(|x| x.kind()))
|
||||
|| !self.slot_can_hold(equip_slot_b, self.equipped(equip_slot_a).map(|x| x.kind()))
|
||||
{
|
||||
// Swap
|
||||
self.swap(equip_slot_b, item_a).unwrap_none();
|
||||
self.swap(equip_slot_a, item_b).unwrap_none();
|
||||
} else {
|
||||
// Otherwise put the items back
|
||||
self.swap(equip_slot_a, item_a).unwrap_none();
|
||||
self.swap(equip_slot_b, item_b).unwrap_none();
|
||||
// If not, revert the swap
|
||||
let item_a = self.swap(equip_slot_a, None);
|
||||
let item_b = self.swap(equip_slot_b, item_a);
|
||||
assert_eq!(self.swap(equip_slot_a, item_b), None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,7 +191,7 @@ impl Loadout {
|
||||
let mut suitable_slots = self
|
||||
.slots
|
||||
.iter()
|
||||
.filter(|s| s.equip_slot.can_hold(item_kind));
|
||||
.filter(|s| self.slot_can_hold(s.equip_slot, Some(item_kind)));
|
||||
|
||||
let first = suitable_slots.next();
|
||||
|
||||
@ -215,7 +211,7 @@ impl Loadout {
|
||||
) -> impl Iterator<Item = &Item> {
|
||||
self.slots
|
||||
.iter()
|
||||
.filter(move |s| s.equip_slot.can_hold(&item_kind))
|
||||
.filter(move |s| self.slot_can_hold(s.equip_slot, Some(&item_kind)))
|
||||
.filter_map(|s| s.slot.as_ref())
|
||||
}
|
||||
|
||||
@ -264,7 +260,7 @@ impl Loadout {
|
||||
pub(super) fn inv_slots_mut(&mut self) -> impl Iterator<Item = &mut InvSlot> {
|
||||
self.slots.iter_mut()
|
||||
.filter_map(|x| x.slot.as_mut().map(|item| item.slots_mut())) // Discard loadout items that have no slots of their own
|
||||
.flat_map(|loadout_slots| loadout_slots.iter_mut()) //Collapse iter of Vec<InvSlot> to iter of InvSlot
|
||||
.flat_map(|loadout_slots| loadout_slots.iter_mut()) //Collapse iter of Vec<InvSlot> to iter of InvSlot
|
||||
}
|
||||
|
||||
/// Gets the range of loadout-provided inventory slot indexes that are
|
||||
@ -294,12 +290,17 @@ impl Loadout {
|
||||
/// If no slot is available the item is returned.
|
||||
#[must_use = "Returned item will be lost if not used"]
|
||||
pub(super) fn try_equip(&mut self, item: Item) -> Result<(), Item> {
|
||||
if let Some(loadout_slot) = self
|
||||
let loadout_slot = self
|
||||
.slots
|
||||
.iter()
|
||||
.find(|s| s.slot.is_none() && self.slot_can_hold(s.equip_slot, Some(item.kind())))
|
||||
.map(|s| s.equip_slot);
|
||||
if let Some(slot) = self
|
||||
.slots
|
||||
.iter_mut()
|
||||
.find(|s| s.slot.is_none() && s.equip_slot.can_hold(item.kind()))
|
||||
.find(|s| Some(s.equip_slot) == loadout_slot)
|
||||
{
|
||||
loadout_slot.slot = Some(item);
|
||||
slot.slot = Some(item);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(item)
|
||||
@ -309,6 +310,90 @@ impl Loadout {
|
||||
pub(super) fn items(&self) -> impl Iterator<Item = &Item> {
|
||||
self.slots.iter().filter_map(|x| x.slot.as_ref())
|
||||
}
|
||||
|
||||
/// Checks that a slot can hold a given item
|
||||
pub(super) fn slot_can_hold(
|
||||
&self,
|
||||
equip_slot: EquipSlot,
|
||||
item_kind: Option<&ItemKind>,
|
||||
) -> bool {
|
||||
// Disallow equipping incompatible weapon pairs (i.e a two-handed weapon and a
|
||||
// one-handed weapon)
|
||||
if !(match equip_slot {
|
||||
EquipSlot::ActiveMainhand => Loadout::is_valid_weapon_pair(
|
||||
item_kind,
|
||||
self.equipped(EquipSlot::ActiveOffhand).map(|x| &x.kind),
|
||||
),
|
||||
EquipSlot::ActiveOffhand => Loadout::is_valid_weapon_pair(
|
||||
self.equipped(EquipSlot::ActiveMainhand).map(|x| &x.kind),
|
||||
item_kind,
|
||||
),
|
||||
EquipSlot::InactiveMainhand => Loadout::is_valid_weapon_pair(
|
||||
item_kind,
|
||||
self.equipped(EquipSlot::InactiveOffhand).map(|x| &x.kind),
|
||||
),
|
||||
EquipSlot::InactiveOffhand => Loadout::is_valid_weapon_pair(
|
||||
self.equipped(EquipSlot::InactiveMainhand).map(|x| &x.kind),
|
||||
item_kind,
|
||||
),
|
||||
_ => true,
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
item_kind.map_or(true, |item_kind| equip_slot.can_hold(item_kind))
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn is_valid_weapon_pair(main_hand: Option<&ItemKind>, off_hand: Option<&ItemKind>) -> bool {
|
||||
matches!((main_hand, off_hand),
|
||||
(Some(ItemKind::Tool(Tool { hands: Hands::One, .. })), None) |
|
||||
(Some(ItemKind::Tool(Tool { hands: Hands::Two, .. })), None) |
|
||||
(Some(ItemKind::Tool(Tool { hands: Hands::One, .. })), Some(ItemKind::Tool(Tool { hands: Hands::One, .. }))) |
|
||||
(None, None))
|
||||
}
|
||||
|
||||
pub(super) fn swap_equipped_weapons(&mut self) {
|
||||
// Checks if a given slot can hold an item right now, defaults to true if
|
||||
// nothing is equipped in slot
|
||||
let valid_slot = |equip_slot| {
|
||||
self.equipped(equip_slot)
|
||||
.map_or(true, |i| self.slot_can_hold(equip_slot, Some(i.kind())))
|
||||
};
|
||||
|
||||
// If every weapon is currently in a valid slot, after this change they will
|
||||
// still be in a valid slot. This is because active mainhand and
|
||||
// inactive mainhand, and active offhand and inactive offhand have the same
|
||||
// requirements on what can be equipped.
|
||||
if valid_slot(EquipSlot::ActiveMainhand)
|
||||
&& valid_slot(EquipSlot::ActiveOffhand)
|
||||
&& valid_slot(EquipSlot::InactiveMainhand)
|
||||
&& valid_slot(EquipSlot::InactiveOffhand)
|
||||
{
|
||||
// Get weapons from each slot
|
||||
let active_mainhand = self.swap(EquipSlot::ActiveMainhand, None);
|
||||
let active_offhand = self.swap(EquipSlot::ActiveOffhand, None);
|
||||
let inactive_mainhand = self.swap(EquipSlot::InactiveMainhand, None);
|
||||
let inactive_offhand = self.swap(EquipSlot::InactiveOffhand, None);
|
||||
// Equip weapons into new slots
|
||||
assert!(
|
||||
self.swap(EquipSlot::ActiveMainhand, inactive_mainhand)
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
self.swap(EquipSlot::ActiveOffhand, inactive_offhand)
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
self.swap(EquipSlot::InactiveMainhand, active_mainhand)
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
self.swap(EquipSlot::InactiveOffhand, active_offhand)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -30,7 +30,7 @@ use tracing::warn;
|
||||
/// // Build a loadout with character starter defaults and a specific sword with default sword abilities
|
||||
/// let loadout = LoadoutBuilder::new()
|
||||
/// .defaults()
|
||||
/// .active_item(Some(Item::new_from_asset_expect("common.items.weapons.sword.steel-8")))
|
||||
/// .active_mainhand(Some(Item::new_from_asset_expect("common.items.weapons.sword.steel-8")))
|
||||
/// .build();
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
@ -376,7 +376,7 @@ impl LoadoutBuilder {
|
||||
match config {
|
||||
Adlet => match active_tool_kind {
|
||||
Some(ToolKind::Bow) => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.adlet.head.adlet_bow",
|
||||
)))
|
||||
@ -397,7 +397,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Some(ToolKind::Spear) | Some(ToolKind::Staff) => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.adlet.head.adlet_spear",
|
||||
)))
|
||||
@ -417,11 +417,11 @@ impl LoadoutBuilder {
|
||||
"common.items.npc_armor.biped_small.adlet.tail.adlet",
|
||||
)))
|
||||
.build(),
|
||||
_ => LoadoutBuilder::new().active_item(active_item).build(),
|
||||
_ => LoadoutBuilder::new().active_mainhand(active_item).build(),
|
||||
},
|
||||
Gnarling => match active_tool_kind {
|
||||
Some(ToolKind::Bow) => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.gnarling.head.gnarling",
|
||||
)))
|
||||
@ -442,7 +442,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Some(ToolKind::Staff) => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.gnarling.head.gnarling",
|
||||
)))
|
||||
@ -463,7 +463,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Some(ToolKind::Spear) => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.gnarling.head.gnarling",
|
||||
)))
|
||||
@ -480,10 +480,10 @@ impl LoadoutBuilder {
|
||||
"common.items.npc_armor.biped_small.gnarling.pants.gnarling",
|
||||
)))
|
||||
.build(),
|
||||
_ => LoadoutBuilder::new().active_item(active_item).build(),
|
||||
_ => LoadoutBuilder::new().active_mainhand(active_item).build(),
|
||||
},
|
||||
Sahagin => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.sahagin.head.sahagin",
|
||||
)))
|
||||
@ -504,7 +504,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Haniwa => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.haniwa.head.haniwa",
|
||||
)))
|
||||
@ -522,7 +522,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Myrmidon => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.myrmidon.head.myrmidon",
|
||||
)))
|
||||
@ -543,7 +543,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Husk => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.husk.head.husk",
|
||||
)))
|
||||
@ -561,7 +561,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Guard => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.leather_plate.shoulder",
|
||||
)))
|
||||
@ -703,7 +703,7 @@ impl LoadoutBuilder {
|
||||
}
|
||||
}
|
||||
LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.twigsflowers.shoulder",
|
||||
)))
|
||||
@ -742,7 +742,7 @@ impl LoadoutBuilder {
|
||||
.build()
|
||||
},
|
||||
Outcast => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.cloth_purple.shoulder",
|
||||
)))
|
||||
@ -767,7 +767,7 @@ impl LoadoutBuilder {
|
||||
})
|
||||
.build(),
|
||||
Highwayman => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.swift.shoulder",
|
||||
)))
|
||||
@ -795,7 +795,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Bandit => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.assassin.shoulder",
|
||||
)))
|
||||
@ -823,7 +823,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
CultistNovice => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.steel.shoulder",
|
||||
)))
|
||||
@ -854,7 +854,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
CultistAcolyte => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.cultist.shoulder",
|
||||
)))
|
||||
@ -885,7 +885,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Warlord => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.warlord.shoulder",
|
||||
)))
|
||||
@ -916,7 +916,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Warlock => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
"common.items.armor.warlock.shoulder",
|
||||
)))
|
||||
@ -947,7 +947,7 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
.build(),
|
||||
Villager => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.chest(Some(Item::new_from_asset_expect(
|
||||
match rand::thread_rng().gen_range(0..10) {
|
||||
0 => "common.items.armor.misc.chest.worker_green_0",
|
||||
@ -981,36 +981,46 @@ impl LoadoutBuilder {
|
||||
match body {
|
||||
Body::BipedLarge(b) => match b.species {
|
||||
biped_large::Species::Mindflayer => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.chest(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_large.mindflayer",
|
||||
)))
|
||||
.build(),
|
||||
_ => LoadoutBuilder::new().active_item(active_item).build(),
|
||||
_ => LoadoutBuilder::new().active_mainhand(active_item).build(),
|
||||
},
|
||||
Body::Golem(g) => match g.species {
|
||||
golem::Species::ClayGolem => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.active_mainhand(active_item)
|
||||
.chest(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.golem.claygolem",
|
||||
)))
|
||||
.build(),
|
||||
_ => LoadoutBuilder::new().active_item(active_item).build(),
|
||||
_ => LoadoutBuilder::new().active_mainhand(active_item).build(),
|
||||
},
|
||||
_ => LoadoutBuilder::new().active_item(active_item).build(),
|
||||
_ => LoadoutBuilder::new().active_mainhand(active_item).build(),
|
||||
}
|
||||
};
|
||||
|
||||
Self(loadout)
|
||||
}
|
||||
|
||||
pub fn active_item(mut self, item: Option<Item>) -> Self {
|
||||
self.0.swap(EquipSlot::Mainhand, item);
|
||||
pub fn active_mainhand(mut self, item: Option<Item>) -> Self {
|
||||
self.0.swap(EquipSlot::ActiveMainhand, item);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn second_item(mut self, item: Option<Item>) -> Self {
|
||||
self.0.swap(EquipSlot::Offhand, item);
|
||||
pub fn active_offhand(mut self, item: Option<Item>) -> Self {
|
||||
self.0.swap(EquipSlot::ActiveOffhand, item);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn inactive_mainhand(mut self, item: Option<Item>) -> Self {
|
||||
self.0.swap(EquipSlot::InactiveMainhand, item);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn inactive_offhand(mut self, item: Option<Item>) -> Self {
|
||||
self.0.swap(EquipSlot::InactiveOffhand, item);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -552,15 +552,7 @@ impl Inventory {
|
||||
pub fn equip(&mut self, inv_slot: InvSlotId) -> Vec<Item> {
|
||||
self.get(inv_slot)
|
||||
.and_then(|item| self.loadout.get_slot_to_equip_into(item.kind()))
|
||||
.map(|equip_slot| {
|
||||
// Special case when equipping into main hand - swap with offhand first
|
||||
if equip_slot == EquipSlot::Mainhand {
|
||||
self.loadout
|
||||
.swap_slots(EquipSlot::Mainhand, EquipSlot::Offhand);
|
||||
}
|
||||
|
||||
self.swap_inventory_loadout(inv_slot, equip_slot)
|
||||
})
|
||||
.map(|equip_slot| self.swap_inventory_loadout(inv_slot, equip_slot))
|
||||
.unwrap_or_else(Vec::new)
|
||||
}
|
||||
|
||||
@ -585,7 +577,7 @@ impl Inventory {
|
||||
+ i32::try_from(slots_from_inv).expect("Inventory item with more than i32::MAX slots")
|
||||
- i32::try_from(self.populated_slots())
|
||||
.expect("Inventory item with more than i32::MAX used slots")
|
||||
+ inv_slot_for_equipped // If there is no item already in the equip slot we gain 1 slot
|
||||
+ inv_slot_for_equipped // If there is no item already in the equip slot we gain 1 slot
|
||||
}
|
||||
|
||||
/// Handles picking up an item, unloading any items inside the item being
|
||||
@ -648,7 +640,7 @@ impl Inventory {
|
||||
.expect("Equipped item with more than i32::MAX slots")
|
||||
- i32::try_from(self.populated_slots())
|
||||
.expect("Inventory item with more than i32::MAX used slots")
|
||||
- inv_slot_for_unequipped // If there is an item being unequipped we lose 1 slot
|
||||
- inv_slot_for_unequipped // If there is an item being unequipped we lose 1 slot
|
||||
}
|
||||
|
||||
/// Swaps items from two slots, regardless of if either is inventory or
|
||||
@ -692,7 +684,7 @@ impl Inventory {
|
||||
- i32::try_from(self.populated_slots())
|
||||
.expect("inventory with more than i32::MAX used slots")
|
||||
- inv_slot_for_equipped // +1 inventory slot required if an item was unequipped
|
||||
+ inv_slot_for_inv_item // -1 inventory slot required if an item was equipped
|
||||
+ inv_slot_for_inv_item // -1 inventory slot required if an item was equipped
|
||||
}
|
||||
|
||||
/// Swap item in an inventory slot with one in a loadout slot.
|
||||
@ -730,6 +722,36 @@ impl Inventory {
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
// If 2 1h weapons are equipped, and mainhand weapon removed, move offhand into
|
||||
// mainhand
|
||||
match equip_slot {
|
||||
EquipSlot::ActiveMainhand => {
|
||||
if self.loadout.equipped(EquipSlot::ActiveMainhand).is_none()
|
||||
&& self.loadout.equipped(EquipSlot::ActiveOffhand).is_some()
|
||||
{
|
||||
let offhand = self.loadout.swap(EquipSlot::ActiveOffhand, None);
|
||||
assert!(
|
||||
self.loadout
|
||||
.swap(EquipSlot::ActiveMainhand, offhand)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
},
|
||||
EquipSlot::InactiveMainhand => {
|
||||
if self.loadout.equipped(EquipSlot::InactiveMainhand).is_none()
|
||||
&& self.loadout.equipped(EquipSlot::InactiveOffhand).is_some()
|
||||
{
|
||||
let offhand = self.loadout.swap(EquipSlot::InactiveOffhand, None);
|
||||
assert!(
|
||||
self.loadout
|
||||
.swap(EquipSlot::InactiveMainhand, offhand)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
// Attempt to put any items unloaded from the unequipped item into empty
|
||||
// inventory slots and return any that don't fit to the caller where they
|
||||
// will be dropped on the ground
|
||||
@ -743,12 +765,12 @@ impl Inventory {
|
||||
/// account whether there will be free space in the inventory for the
|
||||
/// loadout item once any slots that were provided by it have been
|
||||
/// removed.
|
||||
#[allow(clippy::blocks_in_if_conditions)]
|
||||
pub fn can_swap(&self, inv_slot_id: InvSlotId, equip_slot: EquipSlot) -> bool {
|
||||
// Check if loadout slot can hold item
|
||||
if !self
|
||||
.get(inv_slot_id)
|
||||
.map_or(true, |item| equip_slot.can_hold(&item.kind()))
|
||||
{
|
||||
if !self.get(inv_slot_id).map_or(true, |item| {
|
||||
self.loadout.slot_can_hold(equip_slot, Some(&item.kind()))
|
||||
}) {
|
||||
trace!("can_swap = false, equip slot can't hold item");
|
||||
return false;
|
||||
}
|
||||
@ -777,6 +799,8 @@ impl Inventory {
|
||||
pub fn equipped_items_of_kind(&self, item_kind: ItemKind) -> impl Iterator<Item = &Item> {
|
||||
self.loadout.equipped_items_of_kind(item_kind)
|
||||
}
|
||||
|
||||
pub fn swap_equipped_weapons(&mut self) { self.loadout.swap_equipped_weapons() }
|
||||
}
|
||||
|
||||
impl Component for Inventory {
|
||||
|
@ -3,7 +3,7 @@ use std::{cmp::Ordering, convert::TryFrom};
|
||||
|
||||
use crate::comp::{
|
||||
inventory::{
|
||||
item::{armor, armor::ArmorKind, ItemKind},
|
||||
item::{armor, armor::ArmorKind, tool, ItemKind},
|
||||
loadout::LoadoutSlotId,
|
||||
},
|
||||
item,
|
||||
@ -81,8 +81,10 @@ impl From<InvSlotId> for SlotId {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum EquipSlot {
|
||||
Armor(ArmorSlot),
|
||||
Mainhand,
|
||||
Offhand,
|
||||
ActiveMainhand,
|
||||
ActiveOffhand,
|
||||
InactiveMainhand,
|
||||
InactiveOffhand,
|
||||
Lantern,
|
||||
Glider,
|
||||
}
|
||||
@ -120,8 +122,10 @@ impl EquipSlot {
|
||||
pub fn can_hold(self, item_kind: &item::ItemKind) -> bool {
|
||||
match (self, item_kind) {
|
||||
(Self::Armor(slot), ItemKind::Armor(armor::Armor { kind, .. })) => slot.can_hold(kind),
|
||||
(Self::Mainhand, ItemKind::Tool(_)) => true,
|
||||
(Self::Offhand, ItemKind::Tool(_)) => true,
|
||||
(Self::ActiveMainhand, ItemKind::Tool(_)) => true,
|
||||
(Self::ActiveOffhand, ItemKind::Tool(tool)) => matches!(tool.hands, tool::Hands::One),
|
||||
(Self::InactiveMainhand, ItemKind::Tool(_)) => true,
|
||||
(Self::InactiveOffhand, ItemKind::Tool(tool)) => matches!(tool.hands, tool::Hands::One),
|
||||
(Self::Lantern, ItemKind::Lantern(_)) => true,
|
||||
(Self::Glider, ItemKind::Glider(_)) => true,
|
||||
_ => false,
|
||||
|
@ -231,23 +231,29 @@ fn unequip_items_both_hands() {
|
||||
|
||||
let sword = Item::new_from_asset_expect("common.items.weapons.sword.steel-8");
|
||||
|
||||
inv.replace_loadout_item(EquipSlot::Mainhand, Some(sword.duplicate(ability_map, msm)));
|
||||
inv.replace_loadout_item(EquipSlot::Offhand, Some(sword.duplicate(ability_map, msm)));
|
||||
inv.replace_loadout_item(
|
||||
EquipSlot::ActiveMainhand,
|
||||
Some(sword.duplicate(ability_map, msm)),
|
||||
);
|
||||
inv.replace_loadout_item(
|
||||
EquipSlot::InactiveMainhand,
|
||||
Some(sword.duplicate(ability_map, msm)),
|
||||
);
|
||||
|
||||
// Fill all inventory slots except one
|
||||
fill_inv_slots(&mut inv, 17);
|
||||
|
||||
let result = inv.unequip(EquipSlot::Mainhand);
|
||||
let result = inv.unequip(EquipSlot::ActiveMainhand);
|
||||
// We have space in the inventory, so this should have unequipped
|
||||
assert_eq!(None, inv.equipped(EquipSlot::Mainhand));
|
||||
assert_eq!(None, inv.equipped(EquipSlot::ActiveMainhand));
|
||||
assert_eq!(18, inv.populated_slots());
|
||||
assert_eq!(true, result.is_ok());
|
||||
|
||||
let result = inv.unequip(EquipSlot::Offhand).unwrap_err();
|
||||
let result = inv.unequip(EquipSlot::InactiveMainhand).unwrap_err();
|
||||
assert_eq!(SlotError::InventoryFull, result);
|
||||
|
||||
// There is no more space in the inventory, so this should still be equipped
|
||||
assert_eq!(&sword, inv.equipped(EquipSlot::Offhand).unwrap());
|
||||
assert_eq!(&sword, inv.equipped(EquipSlot::InactiveMainhand).unwrap());
|
||||
|
||||
// Verify inventory
|
||||
assert_eq!(inv.slots[17], Some(sword));
|
||||
|
@ -14,6 +14,7 @@
|
||||
option_expect_none,
|
||||
option_unwrap_none,
|
||||
option_zip,
|
||||
or_patterns,
|
||||
trait_alias,
|
||||
type_alias_impl_trait
|
||||
)]
|
||||
|
@ -440,14 +440,34 @@ pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) {
|
||||
|
||||
/// If a tool is equipped, goes into Equipping state, otherwise goes to Idle
|
||||
pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) {
|
||||
if let Some((item, ItemKind::Tool(tool))) = data
|
||||
.inventory
|
||||
.equipped(EquipSlot::Mainhand)
|
||||
.map(|i| (i, i.kind()))
|
||||
{
|
||||
// Closure to get equip time provided an equip slot if a tool is equipped in
|
||||
// equip slot
|
||||
let equip_time = |equip_slot| {
|
||||
data.inventory
|
||||
.equipped(equip_slot)
|
||||
.and_then(|item| match item.kind() {
|
||||
ItemKind::Tool(tool) => Some((item, tool)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|(item, tool)| tool.equip_time(data.msm, item.components()))
|
||||
};
|
||||
|
||||
// Calculates time required to equip weapons, if weapon in mainhand and offhand,
|
||||
// uses maximum duration
|
||||
let mainhand_equip_time = equip_time(EquipSlot::ActiveMainhand);
|
||||
let offhand_equip_time = equip_time(EquipSlot::ActiveOffhand);
|
||||
let equip_time = match (mainhand_equip_time, offhand_equip_time) {
|
||||
(Some(a), Some(b)) => Some(a.max(b)),
|
||||
(Some(a), None) | (None, Some(a)) => Some(a),
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
// Moves entity into equipping state if there is some equip time, else moves
|
||||
// intantly into wield
|
||||
if let Some(equip_time) = equip_time {
|
||||
update.character = CharacterState::Equipping(equipping::Data {
|
||||
static_data: equipping::StaticData {
|
||||
buildup_duration: tool.equip_time(data.msm, item.components()),
|
||||
buildup_duration: equip_time,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
});
|
||||
@ -504,7 +524,15 @@ pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) -> bool {
|
||||
|
||||
/// Checks that player can Swap Weapons and updates `Loadout` if so
|
||||
pub fn attempt_swap_equipped_weapons(data: &JoinData, update: &mut StateUpdate) {
|
||||
if data.inventory.equipped(EquipSlot::Offhand).is_some() {
|
||||
if data
|
||||
.inventory
|
||||
.equipped(EquipSlot::InactiveMainhand)
|
||||
.is_some()
|
||||
|| data
|
||||
.inventory
|
||||
.equipped(EquipSlot::InactiveOffhand)
|
||||
.is_some()
|
||||
{
|
||||
update.swap_equipped_weapons = true;
|
||||
}
|
||||
}
|
||||
@ -556,14 +584,14 @@ fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||
let no_main_hand = hands.0.is_none();
|
||||
// skill_index used to select ability for the AbilityKey::Skill2 input
|
||||
let (equip_slot, skill_index) = if no_main_hand {
|
||||
(Some(EquipSlot::Offhand), 1)
|
||||
(Some(EquipSlot::ActiveOffhand), 1)
|
||||
} else if always_main_hand {
|
||||
(Some(EquipSlot::Mainhand), 0)
|
||||
(Some(EquipSlot::ActiveMainhand), 0)
|
||||
} else {
|
||||
match hands {
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(_, Some(Hands::One)) => (Some(EquipSlot::Offhand), 0),
|
||||
(Some(Hands::One), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::ActiveMainhand), 1),
|
||||
(_, Some(Hands::One)) => (Some(EquipSlot::ActiveOffhand), 0),
|
||||
(Some(Hands::One), _) => (Some(EquipSlot::ActiveMainhand), 1),
|
||||
(_, _) => (None, 0),
|
||||
}
|
||||
};
|
||||
@ -596,7 +624,11 @@ fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||
{
|
||||
update.character = CharacterState::from((
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, matches!(equip_slot, EquipSlot::Offhand), input),
|
||||
AbilityInfo::from_input(
|
||||
data,
|
||||
matches!(equip_slot, EquipSlot::ActiveOffhand),
|
||||
input,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -640,7 +672,8 @@ pub fn handle_block_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
|equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some(tool) if tool.can_block());
|
||||
let hands = get_hands(data);
|
||||
if input_is_pressed(data, InputKind::Block)
|
||||
&& (can_block(EquipSlot::Mainhand) || (hands.0.is_none() && can_block(EquipSlot::Offhand)))
|
||||
&& (can_block(EquipSlot::ActiveMainhand)
|
||||
|| (hands.0.is_none() && can_block(EquipSlot::ActiveOffhand)))
|
||||
{
|
||||
let ability = CharacterAbility::default_block();
|
||||
if ability.requirements_paid(data, update) {
|
||||
@ -696,15 +729,18 @@ pub fn get_hands(data: &JoinData) -> (Option<Hands>, Option<Hands>) {
|
||||
None
|
||||
}
|
||||
};
|
||||
(hand(EquipSlot::Mainhand), hand(EquipSlot::Offhand))
|
||||
(
|
||||
hand(EquipSlot::ActiveMainhand),
|
||||
hand(EquipSlot::ActiveOffhand),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_crit_data(data: &JoinData, ai: AbilityInfo) -> (f32, f32) {
|
||||
const DEFAULT_CRIT_DATA: (f32, f32) = (0.5, 1.3);
|
||||
use HandInfo::*;
|
||||
let slot = match ai.hand {
|
||||
Some(TwoHanded) | Some(MainHand) => EquipSlot::Mainhand,
|
||||
Some(OffHand) => EquipSlot::Offhand,
|
||||
Some(TwoHanded) | Some(MainHand) => EquipSlot::ActiveMainhand,
|
||||
Some(OffHand) => EquipSlot::ActiveOffhand,
|
||||
None => return DEFAULT_CRIT_DATA,
|
||||
};
|
||||
if let Some(item) = data.inventory.equipped(slot) {
|
||||
@ -792,9 +828,9 @@ pub struct AbilityInfo {
|
||||
impl AbilityInfo {
|
||||
pub fn from_input(data: &JoinData, from_offhand: bool, input: InputKind) -> Self {
|
||||
let tool_data = if from_offhand {
|
||||
unwrap_tool_data(data, EquipSlot::Offhand)
|
||||
unwrap_tool_data(data, EquipSlot::ActiveOffhand)
|
||||
} else {
|
||||
unwrap_tool_data(data, EquipSlot::Mainhand)
|
||||
unwrap_tool_data(data, EquipSlot::ActiveMainhand)
|
||||
};
|
||||
let (tool, hand) = (
|
||||
tool_data.map(|t| t.kind),
|
||||
|
@ -60,9 +60,12 @@ impl CharacterBehavior for Data {
|
||||
fn manipulate_loadout(&self, data: &JoinData, inv_action: InventoryAction) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
match inv_action {
|
||||
InventoryAction::Drop(EquipSlot::Mainhand)
|
||||
| InventoryAction::Swap(EquipSlot::Mainhand, _)
|
||||
| InventoryAction::Swap(_, Slot::Equip(EquipSlot::Mainhand)) => {
|
||||
InventoryAction::Drop(EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand)
|
||||
| InventoryAction::Swap(EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand, _)
|
||||
| InventoryAction::Swap(
|
||||
_,
|
||||
Slot::Equip(EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand),
|
||||
) => {
|
||||
update.character = CharacterState::Idle;
|
||||
},
|
||||
_ => (),
|
||||
|
@ -5,14 +5,9 @@ use specs::{
|
||||
|
||||
use common::{
|
||||
comp::{
|
||||
self,
|
||||
inventory::{
|
||||
item::MaterialStatManifest,
|
||||
slot::{EquipSlot, Slot},
|
||||
},
|
||||
Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health, Inventory, Mass,
|
||||
Melee, Mounting, Ori, PhysicsState, Poise, PoiseState, Pos, SkillSet, StateUpdate, Stats,
|
||||
Vel,
|
||||
self, inventory::item::MaterialStatManifest, Beam, Body, CharacterState, Combo, Controller,
|
||||
Density, Energy, Health, Inventory, Mass, Melee, Mounting, Ori, PhysicsState, Poise,
|
||||
PoiseState, Pos, SkillSet, StateUpdate, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
outcome::Outcome,
|
||||
@ -48,16 +43,7 @@ fn incorporate_update(join: &mut JoinStruct, mut state_update: StateUpdate) {
|
||||
if state_update.swap_equipped_weapons {
|
||||
let mut inventory = join.inventory.get_mut_unchecked();
|
||||
let inventory = &mut *inventory;
|
||||
assert!(
|
||||
inventory
|
||||
.swap(
|
||||
Slot::Equip(EquipSlot::Mainhand),
|
||||
Slot::Equip(EquipSlot::Offhand),
|
||||
)
|
||||
.first()
|
||||
.is_none(),
|
||||
"Swapping main and offhand never results in leftover items",
|
||||
);
|
||||
inventory.swap_equipped_weapons();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub fn create_character(
|
||||
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.defaults()
|
||||
.active_item(Some(Item::new_from_asset_expect(&tool_id)))
|
||||
.active_mainhand(Some(Item::new_from_asset_expect(&tool_id)))
|
||||
.build();
|
||||
|
||||
let mut inventory = Inventory::new_with_loadout(loadout);
|
||||
|
@ -254,15 +254,15 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possesse_uid: Uid
|
||||
assert!(
|
||||
inventory
|
||||
.swap(
|
||||
Slot::Equip(EquipSlot::Mainhand),
|
||||
Slot::Equip(EquipSlot::Offhand),
|
||||
Slot::Equip(EquipSlot::ActiveMainhand),
|
||||
Slot::Equip(EquipSlot::InactiveMainhand),
|
||||
)
|
||||
.first()
|
||||
.is_none(),
|
||||
"Swapping main and offhand never results in leftover items",
|
||||
"Swapping active and inactive mainhands never results in leftover items",
|
||||
);
|
||||
|
||||
inventory.replace_loadout_item(EquipSlot::Mainhand, Some(debug_item));
|
||||
inventory.replace_loadout_item(EquipSlot::ActiveMainhand, Some(debug_item));
|
||||
}
|
||||
|
||||
// Remove will of the entity
|
||||
|
8
server/src/migrations/V37__equip_slots.sql
Normal file
8
server/src/migrations/V37__equip_slots.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- Sets active_item to active_mainhand and second_item to inactive_mainhand.
|
||||
--
|
||||
-- second_item becomes inactive_mainhand because active_offhand is enforced to be 1h
|
||||
-- and second_item was not necessarily guaranteed to be 1h.
|
||||
UPDATE item
|
||||
SET position = 'active_mainhand' WHERE position = 'active_item';
|
||||
UPDATE item
|
||||
SET position = 'inactive_mainhand' WHERE position = 'second_item';
|
@ -1553,7 +1553,7 @@ impl<'a> AgentData<'a> {
|
||||
|
||||
let tactic = self
|
||||
.inventory
|
||||
.equipped(EquipSlot::Mainhand)
|
||||
.equipped(EquipSlot::ActiveMainhand)
|
||||
.as_ref()
|
||||
.map(|item| {
|
||||
if let Some(ability_spec) = item.ability_spec() {
|
||||
|
@ -145,7 +145,7 @@ impl CombatEventMapper {
|
||||
previous_state: &PreviousEntityState,
|
||||
inventory: &Inventory,
|
||||
) -> SfxEvent {
|
||||
if let Some(item) = inventory.equipped(EquipSlot::Mainhand) {
|
||||
if let Some(item) = inventory.equipped(EquipSlot::ActiveMainhand) {
|
||||
if let ItemKind::Tool(data) = item.kind() {
|
||||
if character_state.is_attack() {
|
||||
return SfxEvent::Attack(
|
||||
|
@ -13,7 +13,7 @@ use std::time::{Duration, Instant};
|
||||
#[test]
|
||||
fn maps_wield_while_equipping() {
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.active_item(Some(Item::new_from_asset_expect(
|
||||
.active_mainhand(Some(Item::new_from_asset_expect(
|
||||
"common.items.weapons.axe.starter_axe",
|
||||
)))
|
||||
.build();
|
||||
@ -40,7 +40,7 @@ fn maps_wield_while_equipping() {
|
||||
#[test]
|
||||
fn maps_unwield() {
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.active_item(Some(Item::new_from_asset_expect(
|
||||
.active_mainhand(Some(Item::new_from_asset_expect(
|
||||
"common.items.weapons.bow.starter",
|
||||
)))
|
||||
.build();
|
||||
@ -62,7 +62,7 @@ fn maps_unwield() {
|
||||
#[test]
|
||||
fn maps_basic_melee() {
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.active_item(Some(Item::new_from_asset_expect(
|
||||
.active_mainhand(Some(Item::new_from_asset_expect(
|
||||
"common.items.weapons.axe.starter_axe",
|
||||
)))
|
||||
.build();
|
||||
@ -104,7 +104,7 @@ fn maps_basic_melee() {
|
||||
#[test]
|
||||
fn matches_ability_stage() {
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.active_item(Some(Item::new_from_asset_expect(
|
||||
.active_mainhand(Some(Item::new_from_asset_expect(
|
||||
"common.items.weapons.sword.starter",
|
||||
)))
|
||||
.build();
|
||||
@ -163,7 +163,7 @@ fn matches_ability_stage() {
|
||||
#[test]
|
||||
fn ignores_different_ability_stage() {
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.active_item(Some(Item::new_from_asset_expect(
|
||||
.active_mainhand(Some(Item::new_from_asset_expect(
|
||||
"common.items.weapons.axe.starter_axe",
|
||||
)))
|
||||
.build();
|
||||
|
@ -13,6 +13,8 @@ use crate::{
|
||||
ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip, TooltipManager,
|
||||
Tooltipable,
|
||||
},
|
||||
window::GameInput,
|
||||
GlobalState,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
@ -448,8 +450,11 @@ widget_ids! {
|
||||
back_slot,
|
||||
tabard_slot,
|
||||
glider_slot,
|
||||
mainhand_slot,
|
||||
offhand_slot,
|
||||
active_mainhand_slot,
|
||||
active_offhand_slot,
|
||||
inactive_mainhand_slot,
|
||||
inactive_offhand_slot,
|
||||
swap_equipped_weapons_btn,
|
||||
bag1_slot,
|
||||
bag2_slot,
|
||||
bag3_slot,
|
||||
@ -463,6 +468,7 @@ widget_ids! {
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Bag<'a> {
|
||||
client: &'a Client,
|
||||
global_state: &'a GlobalState,
|
||||
imgs: &'a Imgs,
|
||||
item_imgs: &'a ItemImgs,
|
||||
fonts: &'a Fonts,
|
||||
@ -487,6 +493,7 @@ impl<'a> Bag<'a> {
|
||||
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
|
||||
pub fn new(
|
||||
client: &'a Client,
|
||||
global_state: &'a GlobalState,
|
||||
imgs: &'a Imgs,
|
||||
item_imgs: &'a ItemImgs,
|
||||
fonts: &'a Fonts,
|
||||
@ -506,6 +513,7 @@ impl<'a> Bag<'a> {
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
global_state,
|
||||
imgs,
|
||||
item_imgs,
|
||||
fonts,
|
||||
@ -543,6 +551,7 @@ pub enum Event {
|
||||
BagExpand,
|
||||
Close,
|
||||
SortInventory,
|
||||
SwapEquippedWeapons,
|
||||
}
|
||||
|
||||
impl<'a> Widget for Bag<'a> {
|
||||
@ -567,6 +576,7 @@ impl<'a> Widget for Bag<'a> {
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
let i18n = &self.localized_strings;
|
||||
let key_layout = &self.global_state.window.key_layout;
|
||||
|
||||
let mut event = None;
|
||||
let bag_tooltip = Tooltip::new({
|
||||
@ -690,7 +700,7 @@ impl<'a> Widget for Bag<'a> {
|
||||
if inventory.slots().count() > 45 || self.show.bag_inv {
|
||||
let expand_btn_top = if self.show.bag_inv { 53.0 } else { 460.0 };
|
||||
if expand_btn
|
||||
.top_left_with_margins_on(state.bg_ids.bg_frame, expand_btn_top, 211.5)
|
||||
.top_right_with_margins_on(state.bg_ids.bg_frame, expand_btn_top, 37.0)
|
||||
.with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR)
|
||||
.set(state.ids.bag_expand_btn, ui)
|
||||
.was_clicked()
|
||||
@ -1152,19 +1162,19 @@ impl<'a> Widget for Bag<'a> {
|
||||
)
|
||||
.set(state.ids.tabard_slot, ui)
|
||||
}
|
||||
// Mainhand/Left-Slot
|
||||
// Active Mainhand/Left-Slot
|
||||
let mainhand_item = inventory
|
||||
.equipped(EquipSlot::Mainhand)
|
||||
.equipped(EquipSlot::ActiveMainhand)
|
||||
.map(|item| item.to_owned());
|
||||
|
||||
let slot = slot_maker
|
||||
.fabricate(EquipSlot::Mainhand, [85.0; 2])
|
||||
.fabricate(EquipSlot::ActiveMainhand, [85.0; 2])
|
||||
.bottom_right_with_margins_on(state.ids.back_slot, -95.0, 0.0)
|
||||
.with_icon(self.imgs.mainhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
|
||||
.filled_slot(filled_slot);
|
||||
if let Some(item) = mainhand_item {
|
||||
slot.with_item_tooltip(self.item_tooltip_manager, &item, &None, &item_tooltip)
|
||||
.set(state.ids.mainhand_slot, ui)
|
||||
.set(state.ids.active_mainhand_slot, ui)
|
||||
} else {
|
||||
slot.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
@ -1173,20 +1183,21 @@ impl<'a> Widget for Bag<'a> {
|
||||
&tooltip,
|
||||
color::WHITE,
|
||||
)
|
||||
.set(state.ids.mainhand_slot, ui)
|
||||
.set(state.ids.active_mainhand_slot, ui)
|
||||
}
|
||||
// Offhand/Right-Slot
|
||||
|
||||
// Active Offhand/Right-Slot
|
||||
let offhand_item = inventory
|
||||
.equipped(EquipSlot::Offhand)
|
||||
.equipped(EquipSlot::ActiveOffhand)
|
||||
.map(|item| item.to_owned());
|
||||
let slot = slot_maker
|
||||
.fabricate(EquipSlot::Offhand, [85.0; 2])
|
||||
.fabricate(EquipSlot::ActiveOffhand, [85.0; 2])
|
||||
.bottom_left_with_margins_on(state.ids.feet_slot, -95.0, 0.0)
|
||||
.with_icon(self.imgs.offhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
|
||||
.filled_slot(filled_slot);
|
||||
if let Some(item) = offhand_item {
|
||||
slot.with_item_tooltip(self.item_tooltip_manager, &item, &None, &item_tooltip)
|
||||
.set(state.ids.offhand_slot, ui)
|
||||
.set(state.ids.active_offhand_slot, ui)
|
||||
} else {
|
||||
slot.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
@ -1195,7 +1206,83 @@ impl<'a> Widget for Bag<'a> {
|
||||
&tooltip,
|
||||
color::WHITE,
|
||||
)
|
||||
.set(state.ids.offhand_slot, ui)
|
||||
.set(state.ids.active_offhand_slot, ui)
|
||||
}
|
||||
// Inactive Mainhand/Left-Slot
|
||||
let mainhand_item = inventory
|
||||
.equipped(EquipSlot::InactiveMainhand)
|
||||
.map(|item| item.to_owned());
|
||||
|
||||
let slot = slot_maker
|
||||
.fabricate(EquipSlot::InactiveMainhand, [40.0; 2])
|
||||
.bottom_right_with_margins_on(state.ids.active_mainhand_slot, 3.0, -47.0)
|
||||
.with_icon(self.imgs.mainhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
|
||||
.filled_slot(filled_slot);
|
||||
if let Some(item) = mainhand_item {
|
||||
slot.with_item_tooltip(self.item_tooltip_manager, &item, &None, &item_tooltip)
|
||||
.set(state.ids.inactive_mainhand_slot, ui)
|
||||
} else {
|
||||
slot.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
i18n.get("hud.bag.inactive_mainhand"),
|
||||
"",
|
||||
&tooltip,
|
||||
color::WHITE,
|
||||
)
|
||||
.set(state.ids.inactive_mainhand_slot, ui)
|
||||
}
|
||||
|
||||
// Inctive Offhand/Right-Slot
|
||||
let offhand_item = inventory
|
||||
.equipped(EquipSlot::InactiveOffhand)
|
||||
.map(|item| item.to_owned());
|
||||
let slot = slot_maker
|
||||
.fabricate(EquipSlot::InactiveOffhand, [40.0; 2])
|
||||
.bottom_left_with_margins_on(state.ids.active_offhand_slot, 3.0, -47.0)
|
||||
.with_icon(self.imgs.offhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
|
||||
.filled_slot(filled_slot);
|
||||
if let Some(item) = offhand_item {
|
||||
slot.with_item_tooltip(self.item_tooltip_manager, &item, &None, &item_tooltip)
|
||||
.set(state.ids.inactive_offhand_slot, ui)
|
||||
} else {
|
||||
slot.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
i18n.get("hud.bag.inactive_offhand"),
|
||||
"",
|
||||
&tooltip,
|
||||
color::WHITE,
|
||||
)
|
||||
.set(state.ids.inactive_offhand_slot, ui)
|
||||
}
|
||||
|
||||
if Button::image(self.imgs.swap_equipped_weapons_btn)
|
||||
.hover_image(self.imgs.swap_equipped_weapons_btn_hover)
|
||||
.press_image(self.imgs.swap_equipped_weapons_btn_press)
|
||||
.w_h(32.0, 40.0)
|
||||
.bottom_left_with_margins_on(state.bg_ids.bg_frame, 0.0, 23.3)
|
||||
.align_middle_y_of(state.ids.active_mainhand_slot)
|
||||
.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
i18n.get("hud.bag.swap_equipped_weapons_title"),
|
||||
if let Some(key) = self
|
||||
.global_state
|
||||
.settings
|
||||
.controls
|
||||
.get_binding(GameInput::SwapLoadout)
|
||||
{
|
||||
i18n.get("hud.bag.swap_equipped_weapons_desc")
|
||||
.replace("{key}", key.display_string(key_layout).as_str())
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
.as_str(),
|
||||
&tooltip,
|
||||
color::WHITE,
|
||||
)
|
||||
.set(state.ids.swap_equipped_weapons_btn, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
event = Some(Event::SwapEquippedWeapons);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,9 +88,12 @@ impl State {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let equip_slot = match (hands(EquipSlot::Mainhand), hands(EquipSlot::Offhand)) {
|
||||
(Some(_), _) => Some(EquipSlot::Mainhand),
|
||||
(_, Some(_)) => Some(EquipSlot::Offhand),
|
||||
let equip_slot = match (
|
||||
hands(EquipSlot::ActiveMainhand),
|
||||
hands(EquipSlot::ActiveOffhand),
|
||||
) {
|
||||
(Some(_), _) => Some(EquipSlot::ActiveMainhand),
|
||||
(_, Some(_)) => Some(EquipSlot::ActiveOffhand),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@ -140,14 +143,14 @@ impl State {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let active_tool_hands = hands(EquipSlot::Mainhand);
|
||||
let second_tool_hands = hands(EquipSlot::Offhand);
|
||||
let active_tool_hands = hands(EquipSlot::ActiveMainhand);
|
||||
let second_tool_hands = hands(EquipSlot::ActiveOffhand);
|
||||
|
||||
let (equip_slot, skill_index) = match (active_tool_hands, second_tool_hands) {
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(Some(_), Some(Hands::One)) => (Some(EquipSlot::Offhand), 0),
|
||||
(Some(Hands::One), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(None, Some(_)) => (Some(EquipSlot::Offhand), 1),
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::ActiveMainhand), 1),
|
||||
(Some(_), Some(Hands::One)) => (Some(EquipSlot::ActiveOffhand), 0),
|
||||
(Some(Hands::One), _) => (Some(EquipSlot::ActiveMainhand), 1),
|
||||
(None, Some(_)) => (Some(EquipSlot::ActiveOffhand), 1),
|
||||
(_, _) => (None, 0),
|
||||
};
|
||||
|
||||
|
@ -421,6 +421,9 @@ image_ids! {
|
||||
inv_sort_btn: "voxygen.element.ui.bag.buttons.inv_sort",
|
||||
inv_sort_btn_hover: "voxygen.element.ui.bag.buttons.inv_sort_hover",
|
||||
inv_sort_btn_press: "voxygen.element.ui.bag.buttons.inv_sort_press",
|
||||
swap_equipped_weapons_btn: "voxygen.element.ui.bag.buttons.swap_equipped_weapons",
|
||||
swap_equipped_weapons_btn_hover: "voxygen.element.ui.bag.buttons.swap_equipped_weapons_hover",
|
||||
swap_equipped_weapons_btn_press: "voxygen.element.ui.bag.buttons.swap_equipped_weapons_press",
|
||||
coin_ico: "voxygen.element.items.coin",
|
||||
cheese_ico: "voxygen.element.items.item_cheese",
|
||||
inv_bg_armor: "voxygen.element.ui.bag.inv_bg_0",
|
||||
|
@ -373,6 +373,7 @@ pub enum Event {
|
||||
slot: comp::slot::Slot,
|
||||
bypass_dialog: bool,
|
||||
},
|
||||
SwapEquippedWeapons,
|
||||
SwapSlots {
|
||||
slot_a: comp::slot::Slot,
|
||||
slot_b: comp::slot::Slot,
|
||||
@ -2448,6 +2449,7 @@ impl Hud {
|
||||
) {
|
||||
match Bag::new(
|
||||
client,
|
||||
global_state,
|
||||
&self.imgs,
|
||||
&self.item_imgs,
|
||||
&self.fonts,
|
||||
@ -2479,6 +2481,9 @@ impl Hud {
|
||||
};
|
||||
},
|
||||
Some(bag::Event::SortInventory) => self.events.push(Event::SortInventory),
|
||||
Some(bag::Event::SwapEquippedWeapons) => {
|
||||
self.events.push(Event::SwapEquippedWeapons)
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
}
|
||||
|
@ -536,7 +536,7 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
.map(|item| (item.name(), item.description())),
|
||||
hotbar::SlotContents::Ability3 => content_source
|
||||
.1
|
||||
.equipped(EquipSlot::Mainhand)
|
||||
.equipped(EquipSlot::ActiveMainhand)
|
||||
.map(|i| i.kind())
|
||||
.and_then(|kind| match kind {
|
||||
ItemKind::Tool(Tool { kind, .. }) => ability_description(kind),
|
||||
@ -552,14 +552,14 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let active_tool_hands = hands(EquipSlot::Mainhand);
|
||||
let second_tool_hands = hands(EquipSlot::Offhand);
|
||||
let active_tool_hands = hands(EquipSlot::ActiveMainhand);
|
||||
let second_tool_hands = hands(EquipSlot::ActiveOffhand);
|
||||
|
||||
let equip_slot = match (active_tool_hands, second_tool_hands) {
|
||||
(Some(Hands::Two), _) => Some(EquipSlot::Mainhand),
|
||||
(Some(_), Some(Hands::One)) => Some(EquipSlot::Offhand),
|
||||
(Some(Hands::One), _) => Some(EquipSlot::Mainhand),
|
||||
(None, Some(_)) => Some(EquipSlot::Offhand),
|
||||
(Some(Hands::Two), _) => Some(EquipSlot::ActiveMainhand),
|
||||
(Some(_), Some(Hands::One)) => Some(EquipSlot::ActiveOffhand),
|
||||
(Some(Hands::One), _) => Some(EquipSlot::ActiveMainhand),
|
||||
(None, Some(_)) => Some(EquipSlot::ActiveOffhand),
|
||||
(_, _) => None,
|
||||
};
|
||||
|
||||
@ -657,8 +657,8 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
.right_from(state.ids.slot5, slot_offset)
|
||||
.set(state.ids.m1_slot_bg, ui);
|
||||
|
||||
let active_tool = get_item_and_tool(self.inventory, EquipSlot::Mainhand);
|
||||
let second_tool = get_item_and_tool(self.inventory, EquipSlot::Offhand);
|
||||
let active_tool = get_item_and_tool(self.inventory, EquipSlot::ActiveMainhand);
|
||||
let second_tool = get_item_and_tool(self.inventory, EquipSlot::ActiveOffhand);
|
||||
|
||||
let tool = match (
|
||||
active_tool.map(|(_, x)| x.hands),
|
||||
@ -700,8 +700,8 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let active_tool = get_item_and_tool(self.inventory, EquipSlot::Mainhand);
|
||||
let second_tool = get_item_and_tool(self.inventory, EquipSlot::Offhand);
|
||||
let active_tool = get_item_and_tool(self.inventory, EquipSlot::ActiveMainhand);
|
||||
let second_tool = get_item_and_tool(self.inventory, EquipSlot::ActiveOffhand);
|
||||
|
||||
let tool = match (
|
||||
active_tool.map(|(_, x)| x.hands),
|
||||
|
@ -143,12 +143,12 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let active_tool_hands = hands(EquipSlot::Mainhand);
|
||||
let second_tool_hands = hands(EquipSlot::Offhand);
|
||||
let active_tool_hands = hands(EquipSlot::ActiveMainhand);
|
||||
let second_tool_hands = hands(EquipSlot::ActiveOffhand);
|
||||
|
||||
let equip_slot = match (active_tool_hands, second_tool_hands) {
|
||||
(Some(_), _) => Some(EquipSlot::Mainhand),
|
||||
(None, Some(_)) => Some(EquipSlot::Offhand),
|
||||
(Some(_), _) => Some(EquipSlot::ActiveMainhand),
|
||||
(None, Some(_)) => Some(EquipSlot::ActiveOffhand),
|
||||
(_, _) => None,
|
||||
};
|
||||
|
||||
@ -189,14 +189,14 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let active_tool_hands = hands(EquipSlot::Mainhand);
|
||||
let second_tool_hands = hands(EquipSlot::Offhand);
|
||||
let active_tool_hands = hands(EquipSlot::ActiveMainhand);
|
||||
let second_tool_hands = hands(EquipSlot::ActiveOffhand);
|
||||
|
||||
let (equip_slot, skill_index) = match (active_tool_hands, second_tool_hands) {
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(Some(_), Some(Hands::One)) => (Some(EquipSlot::Offhand), 0),
|
||||
(Some(Hands::One), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(None, Some(_)) => (Some(EquipSlot::Offhand), 1),
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::ActiveMainhand), 1),
|
||||
(Some(_), Some(Hands::One)) => (Some(EquipSlot::ActiveOffhand), 0),
|
||||
(Some(Hands::One), _) => (Some(EquipSlot::ActiveMainhand), 1),
|
||||
(None, Some(_)) => (Some(EquipSlot::ActiveOffhand), 1),
|
||||
(_, _) => (None, 0),
|
||||
};
|
||||
|
||||
|
@ -182,7 +182,7 @@ impl Mode {
|
||||
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.defaults()
|
||||
.active_item(Some(Item::new_from_asset_expect(tool)))
|
||||
.active_mainhand(Some(Item::new_from_asset_expect(tool)))
|
||||
.build();
|
||||
|
||||
let inventory = Box::new(Inventory::new_with_loadout(loadout));
|
||||
@ -1328,7 +1328,7 @@ impl Controls {
|
||||
{
|
||||
*tool = value;
|
||||
inventory.replace_loadout_item(
|
||||
EquipSlot::Mainhand,
|
||||
EquipSlot::ActiveMainhand,
|
||||
Some(Item::new_from_asset_expect(*tool)),
|
||||
);
|
||||
}
|
||||
|
@ -221,10 +221,10 @@ impl CharacterCacheKey {
|
||||
};
|
||||
Some(CharacterToolKey {
|
||||
active: inventory
|
||||
.equipped(EquipSlot::Mainhand)
|
||||
.equipped(EquipSlot::ActiveMainhand)
|
||||
.map(tool_key_from_item),
|
||||
second: inventory
|
||||
.equipped(EquipSlot::Offhand)
|
||||
.equipped(EquipSlot::ActiveOffhand)
|
||||
.map(tool_key_from_item),
|
||||
})
|
||||
} else {
|
||||
|
@ -740,9 +740,9 @@ impl FigureMgr {
|
||||
};
|
||||
|
||||
let (active_tool_kind, active_tool_hand, active_tool_spec) =
|
||||
tool_info(EquipSlot::Mainhand);
|
||||
tool_info(EquipSlot::ActiveMainhand);
|
||||
let (second_tool_kind, second_tool_hand, second_tool_spec) =
|
||||
tool_info(EquipSlot::Offhand);
|
||||
tool_info(EquipSlot::ActiveOffhand);
|
||||
|
||||
let hands = (active_tool_hand, second_tool_hand);
|
||||
|
||||
|
@ -307,7 +307,7 @@ impl Scene {
|
||||
.clean(&mut self.col_lights, scene_data.tick);
|
||||
|
||||
let active_item_kind = inventory
|
||||
.and_then(|inv| inv.equipped(EquipSlot::Mainhand))
|
||||
.and_then(|inv| inv.equipped(EquipSlot::ActiveMainhand))
|
||||
.map(|i| i.kind());
|
||||
|
||||
let (active_tool_kind, active_tool_hand) =
|
||||
@ -318,7 +318,7 @@ impl Scene {
|
||||
};
|
||||
|
||||
let second_item_kind = inventory
|
||||
.and_then(|inv| inv.equipped(EquipSlot::Offhand))
|
||||
.and_then(|inv| inv.equipped(EquipSlot::ActiveOffhand))
|
||||
.map(|i| i.kind());
|
||||
|
||||
let (second_tool_kind, second_tool_hand) =
|
||||
|
@ -329,7 +329,7 @@ impl PlayState for SessionState {
|
||||
.borrow()
|
||||
.inventories()
|
||||
.get(player_entity)
|
||||
.and_then(|inv| inv.equipped(EquipSlot::Mainhand))
|
||||
.and_then(|inv| inv.equipped(EquipSlot::ActiveMainhand))
|
||||
.and_then(|item| item.tool())
|
||||
.map_or(false, |tool| tool.kind == ToolKind::Pick)
|
||||
&& self.client.borrow().is_wielding() == Some(true);
|
||||
@ -1086,6 +1086,9 @@ impl PlayState for SessionState {
|
||||
self.client.borrow_mut().use_slot(slot);
|
||||
}
|
||||
},
|
||||
HudEvent::SwapEquippedWeapons => {
|
||||
self.client.borrow_mut().swap_loadout();
|
||||
},
|
||||
HudEvent::SwapSlots {
|
||||
slot_a,
|
||||
slot_b,
|
||||
|
Loading…
Reference in New Issue
Block a user