From da10322dc1022e5dfe5c631db8a11fae60eee0cb Mon Sep 17 00:00:00 2001
From: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Date: Sat, 11 Oct 2025 17:17:23 -0500
Subject: [PATCH] Fix modsuits and defibs (#93373)
## About The Pull Request
Fixes #93359
Caused by #93165
Inventory screen elements were no longer considered reachable, which
broke mousedrop handing on objects that check "is dragging into
inventory slot"
I don't know the best way to fix this yet but I figured the next best
thing would be to make all of these use the `drag_pickup` element, which
skips this reach-ability check
Thus I refactored it slightly to accommodate for items which should
contextually not be drag-pick-up-abble and bam, works like a charm
## Changelog
:cl: Melbert
fix: Dragging defibs and modsuits off your back works again
/:cl:
---------
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: Xander3359 <66163761+Xander3359@users.noreply.github.com>
---
code/datums/elements/drag_pickup.dm | 29 ++++++++++---------
code/datums/storage/storage.dm | 18 +++++++-----
code/game/objects/items.dm | 6 ++--
code/game/objects/items/defib.dm | 9 +-----
code/game/objects/items/tanks/watertank.dm | 7 +----
code/modules/clothing/clothing.dm | 11 -------
code/modules/clothing/shoes/sneakers.dm | 10 +++----
.../living/carbon/alien/special/facehugger.dm | 11 ++++---
code/modules/mod/mod_control.dm | 22 ++------------
.../computers/item/laptop.dm | 11 +------
code/modules/paperwork/paper_cutter.dm | 10 +------
.../projectiles/guns/energy/laser_gatling.dm | 10 +------
.../machinery/portable_chem_mixer.dm | 9 +-----
.../xenobiology/crossbreeding/_clothing.dm | 20 +++----------
code/modules/unit_tests/_unit_tests.dm | 1 +
code/modules/unit_tests/unequip_defib.dm | 19 ++++++++++++
16 files changed, 73 insertions(+), 130 deletions(-)
create mode 100644 code/modules/unit_tests/unequip_defib.dm
diff --git a/code/datums/elements/drag_pickup.dm b/code/datums/elements/drag_pickup.dm
index 8eb73bbb438..f93288303bd 100644
--- a/code/datums/elements/drag_pickup.dm
+++ b/code/datums/elements/drag_pickup.dm
@@ -1,7 +1,8 @@
/**
- * drag_pickup element; for allowing things to be picked up by dragging.
+ * drag_pickup element
*
- * Used for paper bins.
+ * Allowing things to be picked up or unequipped by mouse-dragging.
+ * Useful for objects which have an interaction on click
*/
/datum/element/drag_pickup
@@ -15,20 +16,22 @@
UnregisterSignal(source, COMSIG_MOUSEDROP_ONTO)
return ..()
-/datum/element/drag_pickup/proc/pick_up(atom/source, atom/over, mob/user)
+/datum/element/drag_pickup/proc/pick_up(atom/movable/source, atom/over, mob/user)
SIGNAL_HANDLER
- var/mob/living/picker = user
- if(!istype(picker) || !user.can_perform_action(source, FORBID_TELEKINESIS_REACH))
- return
- var/obj/pickup_object = source
- if (pickup_object)
- if (pickup_object.anchored)
+ if(!user.can_perform_action(source, FORBID_TELEKINESIS_REACH))
+ return NONE
+ if(source.anchored)
+ return NONE
+ if(source.loc == user && isitem(source))
+ var/obj/item/item_source = source
+ if(!item_source.can_mob_unequip(user))
return COMPONENT_CANCEL_MOUSEDROP_ONTO
- if(over == picker)
- INVOKE_ASYNC(picker, TYPE_PROC_REF(/mob/, put_in_hands), source)
+ if(over == user)
+ INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, put_in_hands), source)
else if(istype(over, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/Selected_hand = over
- picker.putItemFromInventoryInHandIfPossible(source, Selected_hand.held_index)
+ var/atom/movable/screen/inventory/hand/selected_hand = over
+ user.putItemFromInventoryInHandIfPossible(source, selected_hand.held_index)
+ source.add_fingerprint(user)
return COMPONENT_CANCEL_MOUSEDROP_ONTO
diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm
index 4c5d6b8efbe..17338ba53b6 100644
--- a/code/datums/storage/storage.dm
+++ b/code/datums/storage/storage.dm
@@ -775,11 +775,15 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
SIGNAL_HANDLER
if(ismecha(user.loc) || user.incapacitated || !user.canUseStorage())
- return
+ return NONE
if(istype(over_object, /atom/movable/screen/inventory/hand))
if(real_location.loc != user || !user.can_perform_action(parent, FORBID_TELEKINESIS_REACH | ALLOW_RESTING))
- return
+ return NONE
+ if(isitem(parent))
+ var/obj/item/item_parent = parent
+ if(!item_parent.can_mob_unequip(user))
+ return COMPONENT_CANCEL_MOUSEDROP_ONTO
var/atom/movable/screen/inventory/hand/hand = over_object
user.putItemFromInventoryInHandIfPossible(parent, hand.held_index)
@@ -788,27 +792,27 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
if(ismob(over_object))
if(over_object != user || !user.can_perform_action(parent, FORBID_TELEKINESIS_REACH | ALLOW_RESTING))
- return
+ return NONE
parent.add_fingerprint(user)
INVOKE_ASYNC(src, PROC_REF(open_storage), user)
return COMPONENT_CANCEL_MOUSEDROP_ONTO
if(istype(over_object, /atom/movable/screen))
- return
+ return NONE
if(!user.can_perform_action(over_object, FORBID_TELEKINESIS_REACH))
- return
+ return NONE
parent.add_fingerprint(user)
var/atom/dump_loc = over_object.get_dumping_location()
if(isnull(dump_loc))
- return
+ return NONE
/// Don't dump *onto* objects in the same storage as ourselves
if (over_object.loc == parent.loc && !isnull(parent.loc.atom_storage) && isnull(over_object.atom_storage))
- return
+ return NONE
INVOKE_ASYNC(src, PROC_REF(dump_content_at), over_object, dump_loc, user)
return COMPONENT_CANCEL_MOUSEDROP_ONTO
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 46a13eafafa..431da812e0f 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -611,7 +611,7 @@
if(throwing)
throwing.finalize(FALSE)
if(loc == user && outside_storage)
- if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
+ if(!can_mob_unequip(user) || !user.temporarilyRemoveItemFromInventory(src))
return
. = FALSE
@@ -621,7 +621,9 @@
user.dropItemToGround(src)
return TRUE
-/obj/item/proc/allow_attack_hand_drop(mob/user)
+/// Called when a mob is manually attempting to unequip the item
+/// Returning FALSE will prevent the unequip from happening
+/obj/item/proc/can_mob_unequip(mob/user)
return TRUE
/obj/item/attack_paw(mob/user, list/modifiers)
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 205a47e9cb6..73684e56eab 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -56,6 +56,7 @@
paddles = new paddle_type(src)
update_power()
RegisterSignal(paddles, COMSIG_DEFIBRILLATOR_SUCCESS, PROC_REF(on_defib_success))
+ AddElement(/datum/element/drag_pickup)
/obj/item/defibrillator/loaded/Initialize(mapload) //starts with hicap
. = ..()
@@ -136,14 +137,6 @@
ui_action_click() //checks for this are handled in defibrillator.mount.dm
return ..()
-/obj/item/defibrillator/mouse_drop_dragged(atom/over_object, mob/user, src_location, over_location, params)
- if(!ismob(loc))
- return
- var/mob/living_mob = loc
- if(!living_mob.incapacitated && istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/hand = over_object
- living_mob.putItemFromInventoryInHandIfPossible(src, hand.held_index)
-
/obj/item/defibrillator/screwdriver_act(mob/living/user, obj/item/tool)
if(!cell || !cell_removable)
return FALSE
diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm
index a704d49a7f3..3891f34bd09 100644
--- a/code/game/objects/items/tanks/watertank.dm
+++ b/code/game/objects/items/tanks/watertank.dm
@@ -28,6 +28,7 @@
create_reagents(volume, OPENCONTAINER)
noz = make_noz()
RegisterSignal(noz, COMSIG_MOVABLE_MOVED, PROC_REF(noz_move))
+ AddElement(/datum/element/drag_pickup)
/obj/item/watertank/Destroy()
QDEL_NULL(noz)
@@ -90,12 +91,6 @@
else
return ..()
-/obj/item/watertank/mouse_drop_dragged(atom/over_object)
- var/mob/M = loc
- if(istype(M) && istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/H = over_object
- M.putItemFromInventoryInHandIfPossible(src, H.held_index)
-
/obj/item/watertank/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
if(attacking_item == noz)
remove_noz()
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 198cb6b27a9..7cccab27ea2 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -72,17 +72,6 @@
if(!icon_state)
item_flags |= ABSTRACT
-/obj/item/clothing/mouse_drop_dragged(atom/over_object, mob/user, src_location, over_location, params)
- var/mob/M = user
-
- if(ismecha(M.loc)) // stops inventory actions in a mech
- return
-
- if(loc == M && istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/H = over_object
- if(M.putItemFromInventoryInHandIfPossible(src, H.held_index))
- add_fingerprint(user)
-
/obj/item/food/clothing
name = "temporary moth clothing snack item"
desc = "If you're reading this it means I messed up. This is related to moths eating clothes and I didn't know a better way to do it than making a new food object. <--- stinky idiot wrote this"
diff --git a/code/modules/clothing/shoes/sneakers.dm b/code/modules/clothing/shoes/sneakers.dm
index 5a1826d55e7..f073336f429 100644
--- a/code/modules/clothing/shoes/sneakers.dm
+++ b/code/modules/clothing/shoes/sneakers.dm
@@ -166,12 +166,10 @@
return ..()
attacking_item.forceMove(src)
-/obj/item/clothing/shoes/sneakers/orange/allow_attack_hand_drop(mob/user)
- if(ishuman(user))
- var/mob/living/carbon/human/C = user
- if(C.shoes == src && attached_cuffs)
- to_chat(user, span_warning("You need help taking these off!"))
- return FALSE
+/obj/item/clothing/shoes/sneakers/orange/can_mob_unequip(mob/user)
+ if(user.get_item_by_slot(slot_flags) == src && attached_cuffs)
+ to_chat(user, span_warning("You need help taking these off!"))
+ return FALSE
return ..()
/obj/item/clothing/shoes/sneakers/orange/mouse_drop_dragged(atom/over_object, mob/user)
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index a028679c996..246dc481aa0 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -257,13 +257,12 @@
// chest maybe because getting slammed in the chest would knock it off your face while dead
AddComponent(/datum/component/knockoff, knockoff_chance = 40, target_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST), slots_knockoffable = slot_flags)
-/obj/item/clothing/mask/facehugger/allow_attack_hand_drop(mob/living/carbon/human/user)
- if(!real || sterile || user.get_organ_by_type(/obj/item/organ/body_egg/alien_embryo))
+/obj/item/clothing/mask/facehugger/can_mob_unequip(mob/user)
+ if(!real || sterile || stat == DEAD || user.get_organ_by_type(/obj/item/organ/body_egg/alien_embryo))
return ..()
- if(istype(user) && ishuman(loc) && stat != DEAD)
- if(user == loc && user.get_item_by_slot(slot_flags) == src)
- to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!"))
- return FALSE
+ if(user.get_item_by_slot(slot_flags) == src)
+ to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!"))
+ return FALSE
return ..()
/obj/item/clothing/mask/facehugger/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index cb4689e81d0..fcc27c3e2e6 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -106,6 +106,7 @@
module = new module(src)
install(module)
START_PROCESSING(SSobj, src)
+ AddElement(/datum/element/drag_pickup)
/obj/item/mod/control/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -212,14 +213,14 @@
return
clean_up()
-/obj/item/mod/control/allow_attack_hand_drop(mob/user)
+/obj/item/mod/control/can_mob_unequip(mob/user)
if(user != wearer)
return ..()
if(active)
balloon_alert(wearer, "unit active!")
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
- return
+ return FALSE
for(var/obj/item/part as anything in get_parts())
if(part.loc != src)
@@ -229,23 +230,6 @@
return ..()
-/obj/item/mod/control/mouse_drop_dragged(atom/over_object, mob/user)
- if(user != wearer || !istype(over_object, /atom/movable/screen/inventory/hand))
- return
- if(active)
- balloon_alert(wearer, "unit active!")
- playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
- return
- for(var/obj/item/part as anything in get_parts())
- if(part.loc != src)
- balloon_alert(wearer, "parts extended!")
- playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
- return
- if(!wearer.incapacitated)
- var/atom/movable/screen/inventory/hand/ui_hand = over_object
- if(wearer.putItemFromInventoryInHandIfPossible(src, ui_hand.held_index))
- add_fingerprint(user)
-
/obj/item/mod/control/wrench_act(mob/living/user, obj/item/wrench)
if(seconds_electrified && get_charge() && shock(user))
return ITEM_INTERACT_BLOCKING
diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm
index a33ae21f9fd..2ae9e794b57 100644
--- a/code/modules/modular_computers/computers/item/laptop.dm
+++ b/code/modules/modular_computers/computers/item/laptop.dm
@@ -29,6 +29,7 @@
if(start_open && !screen_on)
toggle_open()
RegisterSignal(src, COMSIG_SPEED_POTION_APPLIED, PROC_REF(on_speed_potioned))
+ AddElement(/datum/element/drag_pickup)
/obj/item/modular_computer/laptop/examine(mob/user)
. = ..()
@@ -76,16 +77,6 @@
try_toggle_open(usr)
-/obj/item/modular_computer/laptop/mouse_drop_dragged(atom/over_object, mob/user, src_location, over_location, params)
- if(over_object == user || over_object == src)
- try_toggle_open(user)
- return
- if(istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/H = over_object
- if(!isturf(loc))
- return
- user.put_in_hand(src, H.held_index)
-
/obj/item/modular_computer/laptop/proc/try_toggle_open(mob/living/user)
if(issilicon(user))
return
diff --git a/code/modules/paperwork/paper_cutter.dm b/code/modules/paperwork/paper_cutter.dm
index 71471f4bf1f..27301f09448 100644
--- a/code/modules/paperwork/paper_cutter.dm
+++ b/code/modules/paperwork/paper_cutter.dm
@@ -26,6 +26,7 @@
stored_blade = new /obj/item/hatchet/cutterblade(src)
register_context()
update_appearance()
+ AddElement(/datum/element/drag_pickup)
/obj/item/papercutter/Destroy(force)
if(!isnull(stored_paper))
@@ -180,15 +181,6 @@
new /obj/item/paper/paperslip(get_turf(src))
update_appearance()
-/obj/item/papercutter/mouse_drop_dragged(atom/over_object, mob/user)
- if(over_object == user)
- user.put_in_hands(src)
-
- else if(istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/target_hand = over_object
- user.putItemFromInventoryInHandIfPossible(src, target_hand.held_index)
- add_fingerprint(user)
-
/obj/item/paper/paperslip
name = "paper slip"
desc = "A little slip of paper left over after a larger piece was cut. Whoa."
diff --git a/code/modules/projectiles/guns/energy/laser_gatling.dm b/code/modules/projectiles/guns/energy/laser_gatling.dm
index 670f0b0c370..c9b4455919a 100644
--- a/code/modules/projectiles/guns/energy/laser_gatling.dm
+++ b/code/modules/projectiles/guns/energy/laser_gatling.dm
@@ -24,6 +24,7 @@
gun = new(src)
battery = new(src)
START_PROCESSING(SSobj, src)
+ AddElement(/datum/element/drag_pickup)
/obj/item/minigunpack/Destroy()
if(!QDELETED(gun))
@@ -64,15 +65,6 @@
if(armed)
user.dropItemToGround(gun, TRUE)
-/obj/item/minigunpack/mouse_drop_dragged(atom/over_object, mob/user)
- if(armed)
- return
-
- if(iscarbon(user))
- if(istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/H = over_object
- user.putItemFromInventoryInHandIfPossible(src, H.held_index)
-
/obj/item/minigunpack/update_icon_state()
icon_state = armed ? "notholstered" : "holstered"
return ..()
diff --git a/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm b/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm
index 9ea2603cdf6..e360c24f270 100644
--- a/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm
+++ b/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm
@@ -22,8 +22,8 @@
/obj/item/storage/portable_chem_mixer/Initialize(mapload)
. = ..()
-
register_context()
+ AddElement(/datum/element/drag_pickup)
/obj/item/storage/portable_chem_mixer/Destroy()
dispensable_reagents.Cut()
@@ -239,13 +239,6 @@
update_appearance()
return TRUE
-/obj/item/storage/portable_chem_mixer/mouse_drop_dragged(atom/over_object)
- if(ismob(loc))
- var/mob/M = loc
- if(istype(over_object, /atom/movable/screen/inventory/hand))
- var/atom/movable/screen/inventory/hand/H = over_object
- M.putItemFromInventoryInHandIfPossible(src, H.held_index)
-
/obj/item/storage/portable_chem_mixer/click_alt(mob/living/user)
if(!atom_storage.locked)
balloon_alert(user, "lock first to use alt eject!")
diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm
index 91f13560f2f..bd91fa7f9e2 100644
--- a/code/modules/research/xenobiology/crossbreeding/_clothing.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm
@@ -113,22 +113,10 @@ Slimecrossing Armor
throw_speed = 1
throw_range = 3
-/obj/item/clothing/head/peaceflower/proc/at_peace_check(mob/user)
- if(iscarbon(user))
- var/mob/living/carbon/carbon_user = user
- if(src == carbon_user.head)
- to_chat(user, span_warning("You feel at peace. Why would you want anything else?"))
- return TRUE
- return FALSE
-
-/obj/item/clothing/head/peaceflower/attack_hand(mob/user, list/modifiers)
- if(at_peace_check(user))
- return
- return ..()
-
-/obj/item/clothing/head/peaceflower/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
- if(at_peace_check(user))
- return
+/obj/item/clothing/head/peaceflower/can_mob_unequip(mob/user)
+ if(user.get_item_by_slot(slot_flags) == src)
+ to_chat(user, span_warning("You feel at peace. Why would you want anything else?"))
+ return FALSE
return ..()
/obj/item/clothing/suit/armor/heavy/adamantine
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index ba1a26b6883..1d387ffa057 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -322,6 +322,7 @@
#include "trauma_granting.dm"
#include "turf_icons.dm"
#include "tutorial_sanity.dm"
+#include "unequip_defib.dm"
#include "unit_test.dm"
#include "verify_config_tags.dm"
#include "verify_emoji_names.dm"
diff --git a/code/modules/unit_tests/unequip_defib.dm b/code/modules/unit_tests/unequip_defib.dm
new file mode 100644
index 00000000000..e2db399f2de
--- /dev/null
+++ b/code/modules/unit_tests/unequip_defib.dm
@@ -0,0 +1,19 @@
+/// Test you can mouse-drop a defib off your back to unequip it
+/datum/unit_test/unequip_defib
+
+/datum/unit_test/unequip_defib/Run()
+ var/mob/living/carbon/human/consistent/dummy = EASY_ALLOCATE()
+ dummy.mock_client = new()
+ dummy.set_hud_used(new dummy.hud_type(dummy))
+ var/obj/item/defibrillator/defib = EASY_ALLOCATE()
+ dummy.equip_to_slot(defib, ITEM_SLOT_BACK)
+
+ var/old_usr = usr
+
+ usr = dummy // mouse drop still uses usr
+
+ defib.MouseDrop(dummy.hud_used.hand_slots["1"])
+ if(!dummy.is_holding(defib))
+ TEST_FAIL("The dummy failed to remove the defib from their back via mouse drop.")
+
+ usr = old_usr