main/offhand weapon swap check refactor

This commit is contained in:
Ben Wallis 2021-05-16 19:38:29 +01:00
parent ad0c247838
commit 811712316b
3 changed files with 53 additions and 116 deletions

View File

@ -1,6 +1,6 @@
use crate::comp::{
inventory::{
item::{tool, ItemKind},
item::{Hands, ItemKind, Tool},
slot::{ArmorSlot, EquipSlot},
InvSlot,
},
@ -167,84 +167,18 @@ impl Loadout {
return;
}
enum MainhandHand {
MainhandA,
MainhandB,
}
let hands_swapping = match (equip_slot_a, equip_slot_b) {
(EquipSlot::ActiveMainhand, EquipSlot::ActiveOffhand) => Some(MainhandHand::MainhandA),
(EquipSlot::ActiveOffhand, EquipSlot::ActiveMainhand) => Some(MainhandHand::MainhandB),
(EquipSlot::InactiveMainhand, EquipSlot::InactiveOffhand) => {
Some(MainhandHand::MainhandA)
},
(EquipSlot::InactiveOffhand, EquipSlot::InactiveMainhand) => {
Some(MainhandHand::MainhandB)
},
_ => None,
};
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);
if let Some(hands_swapping) = hands_swapping {
match hands_swapping {
MainhandHand::MainhandA => {
if item_b
.as_ref()
.map_or(true, |i| self.slot_can_hold(equip_slot_a, &i.kind()))
&& item_a
.as_ref()
.map_or(true, |i| equip_slot_b.can_hold(&i.kind()))
{
// Checks that item b (from offhand) can go into equip slot a (mainhand
// slot) and that item a (from mainhand) is a valid item to insert into
// equip slot b (offhand slot) Swap
self.swap(equip_slot_a, item_b).unwrap_none();
self.swap(equip_slot_b, item_a).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();
}
},
MainhandHand::MainhandB => {
if item_a
.as_ref()
.map_or(true, |i| self.slot_can_hold(equip_slot_b, &i.kind()))
&& item_b
.as_ref()
.map_or(true, |i| equip_slot_b.can_hold(&i.kind()))
{
// Checks that item a (from offhand) can go into equip slot b (mainhand
// slot) and that item b (from mainhand) is a valid item to insert into
// equip slot a (offhand slot) 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();
}
},
}
} else {
// Check if items can go in the other slots
if item_a
.as_ref()
.map_or(true, |i| self.slot_can_hold(equip_slot_b, &i.kind()))
&& item_b
.as_ref()
.map_or(true, |i| self.slot_can_hold(equip_slot_a, &i.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();
}
// 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()))
{
// 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);
}
}
@ -257,7 +191,7 @@ impl Loadout {
let mut suitable_slots = self
.slots
.iter()
.filter(|s| self.slot_can_hold(s.equip_slot, item_kind));
.filter(|s| self.slot_can_hold(s.equip_slot, Some(item_kind)));
let first = suitable_slots.next();
@ -277,7 +211,7 @@ impl Loadout {
) -> impl Iterator<Item = &Item> {
self.slots
.iter()
.filter(move |s| self.slot_can_hold(s.equip_slot, &item_kind))
.filter(move |s| self.slot_can_hold(s.equip_slot, Some(&item_kind)))
.filter_map(|s| s.slot.as_ref())
}
@ -370,7 +304,7 @@ impl Loadout {
let loadout_slot = self
.slots
.iter()
.find(|s| s.slot.is_none() && self.slot_can_hold(s.equip_slot, item.kind()))
.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
@ -389,39 +323,42 @@ impl Loadout {
}
/// Checks that a slot can hold a given item
pub(super) fn slot_can_hold(&self, equip_slot: EquipSlot, item_kind: &ItemKind) -> bool {
// Checks if item can be equipped in a mainhand slot
let mainhand_check = |offhand_slot| {
// Allows item to be equipped if itemkind is a tool and...
matches!(item_kind, ItemKind::Tool(mainhand) if {
if let Some(ItemKind::Tool(offhand)) = self.equipped(offhand_slot).map(|i| i.kind()) {
// if offhand is 1 handed, only if mainhand is also 1 handed
matches!(offhand.hands, tool::Hands::One) && matches!(mainhand.hands, tool::Hands::One)
} else {
// else there is no tool equipped in offhand, so only if slot can normally hold this item
equip_slot.can_hold(item_kind)
}
})
pub(super) fn slot_can_hold(
&self,
equip_slot: EquipSlot,
item_kind: Option<&ItemKind>,
) -> bool {
let weapon_compare_slots = match equip_slot {
EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand => {
Some((EquipSlot::ActiveMainhand, EquipSlot::ActiveOffhand))
},
EquipSlot::InactiveMainhand | EquipSlot::InactiveOffhand => {
Some((EquipSlot::InactiveMainhand, EquipSlot::InactiveOffhand))
},
_ => None,
};
// Checks if item can be equipped in an offhand slot
let offhand_check = |mainhand_slot| {
// Allows item to be equipped if itemkind is a tool and...
matches!(item_kind, ItemKind::Tool(offhand) if {
// if offhand weapon is 1 handed...
matches!(offhand.hands, tool::Hands::One)
// and if mainhand has a 1 handed weapon...
&& matches!(self.equipped(mainhand_slot).map(|i| i.kind()), Some(ItemKind::Tool(mainhand)) if matches!(mainhand.hands, tool::Hands::One))
})
};
match equip_slot {
EquipSlot::ActiveMainhand => mainhand_check(EquipSlot::ActiveOffhand),
EquipSlot::ActiveOffhand => offhand_check(EquipSlot::ActiveMainhand),
EquipSlot::InactiveMainhand => mainhand_check(EquipSlot::InactiveOffhand),
EquipSlot::InactiveOffhand => offhand_check(EquipSlot::InactiveMainhand),
_ => equip_slot.can_hold(item_kind),
// Disallow equipping incompatible weapon pairs (i.e a two-handed weapon and a
// one-handed weapon)
if let Some(weapon_compare_slots) = weapon_compare_slots {
if !Loadout::is_valid_weapon_pair(
self.equipped(weapon_compare_slots.0).map(|x| &x.kind),
self.equipped(weapon_compare_slots.1).map(|x| &x.kind),
) {
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) {
@ -429,7 +366,7 @@ impl Loadout {
// nothing is equipped in slot
let valid_slot = |equip_slot| {
self.equipped(equip_slot)
.map_or(true, |i| self.slot_can_hold(equip_slot, i.kind()))
.map_or(true, |i| self.slot_can_hold(equip_slot, Some(i.kind())))
};
if valid_slot(EquipSlot::ActiveMainhand)

View File

@ -765,7 +765,7 @@ impl Inventory {
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| {
self.loadout.slot_can_hold(equip_slot, &item.kind())
self.loadout.slot_can_hold(equip_slot, Some(&item.kind()))
}) {
trace!("can_swap = false, equip slot can't hold item");
return false;

View File

@ -1000,10 +1000,10 @@ impl<'a> Widget for Bag<'a> {
}
// Ring
let ring1_item = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Ring1))
.equipped(EquipSlot::InactiveOffhand)
.map(|item| item.to_owned());
let slot = slot_maker
.fabricate(EquipSlot::Armor(ArmorSlot::Ring1), [45.0; 2])
.fabricate(EquipSlot::InactiveOffhand, [45.0; 2])
.bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0)
.with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
.filled_slot(filled_slot);
@ -1022,10 +1022,10 @@ impl<'a> Widget for Bag<'a> {
}
// Ring 2
let ring2_item = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Ring2))
.equipped(EquipSlot::InactiveMainhand)
.map(|item| item.to_owned());
let slot = slot_maker
.fabricate(EquipSlot::Armor(ArmorSlot::Ring2), [45.0; 2])
.fabricate(EquipSlot::InactiveMainhand, [45.0; 2])
.bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0)
.with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
.filled_slot(filled_slot);