From 2cd8f8c08347db121f10caa28a9f49453bfd1dfa Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Thu, 27 Aug 2020 15:38:21 +0200
Subject: [PATCH] [MIRROR] Fixes quickswap letting you keep clothing traits
(#542)
* Fixes quickswap letting you keep clothing traits (#52790)
Unequipping clothing via quickswap now properly calls dropped(), so you lose any traits you get for wearing them. This fixes being able to use the quickswap hotkey to keep traits and other properties like krav maga, sec/med/diagnostic hud's, and whatever else without actually wearing the clothing.
Fixes: #52788
Fixes: #50798
Why It's Good For The Game
Honestly for my money, a quickswap hotkey is lame and overly gamey, which is funny because the kind of player who would be into that is exactly the kind of player I'd suspect of quietly abusing a bug like this bug for the last several months, but I digress.
Changelog
cl Ryll/Shaps
fix: Quickswapping clothing now properly unequips the swapped-out clothing, removing any traits they granted while worn
/cl
* Fixes quickswap letting you keep clothing traits
Co-authored-by: Ryll Ryll <3589655+Ryll-Ryll@users.noreply.github.com>
---
.../mob/living/carbon/human/inventory.dm | 11 ++-
.../mob/living/carbon/human/species.dm | 88 +++++--------------
code/modules/mob/living/carbon/inventory.dm | 6 +-
code/modules/mob/mob.dm | 5 +-
4 files changed, 40 insertions(+), 70 deletions(-)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 1c8f252ed41..e717b93623a 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -83,27 +83,31 @@
if(!..()) //a check failed or the item has already found its slot
return
- var/current_equip
+ var/obj/item/current_equip
var/not_handled = FALSE //Added in case we make this type path deeper one day
switch(slot)
if(ITEM_SLOT_BELT)
if (belt && swap)
+ belt.dropped(src, TRUE)
current_equip = belt
belt = I
update_inv_belt()
if(ITEM_SLOT_ID)
if (wear_id && swap)
+ wear_id.dropped(src, TRUE)
current_equip = wear_id
wear_id = I
sec_hud_set_ID()
update_inv_wear_id()
if(ITEM_SLOT_EARS)
if (ears && swap)
+ ears.dropped(src, TRUE)
current_equip = ears
ears = I
update_inv_ears()
if(ITEM_SLOT_EYES)
if (glasses && swap)
+ glasses.dropped(src, TRUE)
current_equip = glasses
glasses = I
var/obj/item/clothing/glasses/G = I
@@ -118,16 +122,19 @@
update_inv_glasses()
if(ITEM_SLOT_GLOVES)
if (gloves && swap)
+ gloves.dropped(src, TRUE)
current_equip = gloves
gloves = I
update_inv_gloves()
if(ITEM_SLOT_FEET)
if (shoes && swap)
+ shoes.dropped(src, TRUE)
current_equip = shoes
shoes = I
update_inv_shoes()
if(ITEM_SLOT_OCLOTHING)
if (wear_suit && swap)
+ wear_suit.dropped(src, TRUE)
current_equip = wear_suit
wear_suit = I
if(I.flags_inv & HIDEJUMPSUIT)
@@ -138,6 +145,7 @@
update_inv_wear_suit()
if(ITEM_SLOT_ICLOTHING)
if (w_uniform && swap)
+ w_uniform.dropped(src, TRUE)
current_equip = w_uniform
w_uniform = I
update_suit_sensors()
@@ -150,6 +158,7 @@
update_inv_pockets()
if(ITEM_SLOT_SUITSTORE)
if (s_store && swap)
+ s_store.dropped(src, TRUE)
current_equip = s_store
s_store = I
update_inv_s_store()
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index df83aea6a9b..1a235723b76 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -920,50 +920,44 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(!I.species_exception || !is_type_in_list(src, I.species_exception))
return FALSE
+ var/obj/item/replaced_item = H.get_item_by_slot(slot)
+
+ // if there's an item in the slot we want, only allow past this if we're trying to swap and the item being replaced isn't NODROP or ABSTRACT
+ if(replaced_item && (!swap || (HAS_TRAIT(replaced_item, TRAIT_NODROP) || (replaced_item.item_flags & ABSTRACT))))
+ return FALSE
+
+ // this check prevents us from equipping something to a slot it doesn't support, WITH the exceptions of storage slots (pockets, suit storage, and backpacks)
+ // we don't require having those slots defined in the item's slot_flags, so we'll rely on their own checks further down
+ if(!(I.slot_flags & slot))
+ var/excused = FALSE
+ // Anything that's small or smaller can fit into a pocket by default
+ if((slot == ITEM_SLOT_RPOCKET || slot == ITEM_SLOT_LPOCKET) && I.w_class <= WEIGHT_CLASS_SMALL)
+ excused = TRUE
+ else if(slot == ITEM_SLOT_SUITSTORE || slot == ITEM_SLOT_BACKPACK)
+ excused = TRUE
+ if(!excused)
+ return FALSE
+
switch(slot)
if(ITEM_SLOT_HANDS)
if(H.get_empty_held_indexes())
return TRUE
return FALSE
if(ITEM_SLOT_MASK)
- if(H.wear_mask && !swap)
- return FALSE
- if(!(I.slot_flags & ITEM_SLOT_MASK))
- return FALSE
if(!H.get_bodypart(BODY_ZONE_HEAD))
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_NECK)
- if(H.wear_neck && !swap)
- return FALSE
- if( !(I.slot_flags & ITEM_SLOT_NECK) )
- return FALSE
return TRUE
if(ITEM_SLOT_BACK)
- if(H.back && !swap)
- return FALSE
- if( !(I.slot_flags & ITEM_SLOT_BACK) )
- return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_OCLOTHING)
- if(H.wear_suit && !swap)
- return FALSE
- if( !(I.slot_flags & ITEM_SLOT_OCLOTHING) )
- return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_GLOVES)
- if(H.gloves && !swap)
- return FALSE
- if( !(I.slot_flags & ITEM_SLOT_GLOVES) )
- return FALSE
if(H.num_hands < 2)
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_FEET)
- if(H.shoes && !swap)
- return FALSE
- if( !(I.slot_flags & ITEM_SLOT_FEET) )
- return FALSE
if(H.num_legs < 2)
return FALSE
if(DIGITIGRADE in species_traits)
@@ -972,23 +966,14 @@ GLOBAL_LIST_EMPTY(roundstart_races)
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_BELT)
- if(H.belt && !swap)
- return FALSE
-
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(!disable_warning)
to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
return FALSE
- if(!(I.slot_flags & ITEM_SLOT_BELT))
- return
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_EYES)
- if(H.glasses && !swap)
- return FALSE
- if(!(I.slot_flags & ITEM_SLOT_EYES))
- return FALSE
if(!H.get_bodypart(BODY_ZONE_HEAD))
return FALSE
var/obj/item/organ/eyes/E = H.getorganslot(ORGAN_SLOT_EYES)
@@ -996,43 +981,26 @@ GLOBAL_LIST_EMPTY(roundstart_races)
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_HEAD)
- if(H.head && !swap)
- return FALSE
- if(!(I.slot_flags & ITEM_SLOT_HEAD))
- return FALSE
if(!H.get_bodypart(BODY_ZONE_HEAD))
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_EARS)
- if(H.ears && !swap)
- return FALSE
- if(!(I.slot_flags & ITEM_SLOT_EARS))
- return FALSE
if(!H.get_bodypart(BODY_ZONE_HEAD))
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_ICLOTHING)
- if(H.w_uniform && !swap)
- return FALSE
- if( !(I.slot_flags & ITEM_SLOT_ICLOTHING) )
- return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_ID)
- if(H.wear_id && !swap)
- return FALSE
-
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(!disable_warning)
to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
return FALSE
- if( !(I.slot_flags & ITEM_SLOT_ID) )
- return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_LPOCKET)
if(HAS_TRAIT(I, TRAIT_NODROP)) //Pockets aren't visible, so you can't move TRAIT_NODROP items into them.
return FALSE
- if(H.l_store)
+ if(H.l_store) // no pocket swaps at all
return FALSE
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_L_LEG)
@@ -1041,8 +1009,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(!disable_warning)
to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
return FALSE
- if( I.w_class <= WEIGHT_CLASS_SMALL || (I.slot_flags & ITEM_SLOT_LPOCKET) )
- return TRUE
+ return TRUE
if(ITEM_SLOT_RPOCKET)
if(HAS_TRAIT(I, TRAIT_NODROP))
return FALSE
@@ -1055,14 +1022,10 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(!disable_warning)
to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
return FALSE
- if( I.w_class <= WEIGHT_CLASS_SMALL || (I.slot_flags & ITEM_SLOT_RPOCKET) )
- return TRUE
- return FALSE
+ return TRUE
if(ITEM_SLOT_SUITSTORE)
if(HAS_TRAIT(I, TRAIT_NODROP))
return FALSE
- if(H.s_store && !swap)
- return FALSE
if(!H.wear_suit)
if(!disable_warning)
to_chat(H, "You need a suit before you can attach this [I.name]!")
@@ -1079,25 +1042,20 @@ GLOBAL_LIST_EMPTY(roundstart_races)
return TRUE
return FALSE
if(ITEM_SLOT_HANDCUFFED)
- if(H.handcuffed)
- return FALSE
if(!istype(I, /obj/item/restraints/handcuffs))
return FALSE
if(H.num_hands < 2)
return FALSE
return TRUE
if(ITEM_SLOT_LEGCUFFED)
- if(H.legcuffed)
- return FALSE
if(!istype(I, /obj/item/restraints/legcuffs))
return FALSE
if(H.num_legs < 2)
return FALSE
return TRUE
if(ITEM_SLOT_BACKPACK)
- if(H.back)
- if(SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_CAN_INSERT, I, H, TRUE))
- return TRUE
+ if(H.back && SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_CAN_INSERT, I, H, TRUE))
+ return TRUE
return FALSE
return FALSE //Unsupported slot
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index dfcd3a826ee..2248c225816 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -49,26 +49,30 @@
I.plane = ABOVE_HUD_PLANE
I.appearance_flags |= NO_CLIENT_COLOR
var/not_handled = FALSE
- var/current_equip
+ var/obj/item/current_equip
switch(slot)
if(ITEM_SLOT_BACK)
if (back && swap)
+ back.dropped(src, TRUE)
current_equip = back
back = I
update_inv_back()
if(ITEM_SLOT_MASK)
if (wear_mask && swap)
+ wear_mask.dropped(src, TRUE)
current_equip = wear_mask
wear_mask = I
wear_mask_update(I, toggle_off = 0)
if(ITEM_SLOT_HEAD)
if (head && swap)
+ head.dropped(src, TRUE)
current_equip = head
head = I
SEND_SIGNAL(src, COMSIG_CARBON_EQUIP_HAT, I)
head_update(I)
if(ITEM_SLOT_NECK)
if (wear_neck && swap)
+ wear_neck.dropped(src, TRUE)
current_equip = wear_neck
wear_neck = I
update_inv_neck(I)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d5df002705c..bd5e4ffe28c 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -336,9 +336,8 @@
if(!W.mob_can_equip(src, null, slot, disable_warning, bypass_equip_delay_self, swap))
if(qdel_on_fail)
qdel(W)
- else
- if(!disable_warning)
- to_chat(src, "You are unable to equip that!")
+ else if(!disable_warning)
+ to_chat(src, "You are unable to equip that!")
return FALSE
equip_to_slot(W, slot, initial, redraw_mob, swap) //This proc should not ever fail.
return TRUE