diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
index c2b3b89de2d64..b55555d052621 100644
--- a/code/__HELPERS/atoms.dm
+++ b/code/__HELPERS/atoms.dm
@@ -350,3 +350,18 @@ B --><-- A
return get_step(ref, base_dir)
*/
+
+/// returns a mob that possesses this atom(usually item),
+/// or returns null if it is possessed by no mob.
+/atom/proc/get_mob_loc(as_first_mob=TRUE)
+ var/atom/upper = src
+ var/final_mob
+ while(upper)
+ upper = upper.loc
+ if(ismob(upper))
+ final_mob = upper
+ // if this is TRUE, it will immediately return a mob that holds the atom
+ // if this is FALSE, it will find a final mob when it's chain-contained (i.e. mob in mob in mob...)
+ if(as_first_mob)
+ return final_mob
+ return final_mob
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index e1123759e9a30..90ad887cbf746 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -309,14 +309,7 @@
/datum/action/item_action/chameleon/change/proc/update_mob_hud(atom/card_holder)
// we're going to find a human, and store human ref to 'card_holder' by checking loc multiple time.
if(!ishuman(card_holder))
- if(!card_holder)
- card_holder = target.loc
- if(istype(card_holder, /obj/item/storage/wallet))
- card_holder = card_holder.loc // this should be human
- if(istype(card_holder, /obj/item/computer_hardware/card_slot))
- card_holder = card_holder.loc
- if(istype(card_holder, /obj/item/modular_computer/tablet))
- card_holder = card_holder.loc // tihs should be human
+ card_holder = target.get_mob_loc()
if(!ishuman(card_holder))
return
var/mob/living/carbon/human/card_holding_human = card_holder
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 65ef7c2305fdf..08c3fa83708d4 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -161,7 +161,7 @@
/mob/proc/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
return FALSE
-/mob/proc/can_put_in_hand(I, hand_index)
+/mob/proc/can_put_in_hand(atom/I, hand_index)
if(hand_index > length(held_items))
return FALSE
if(!put_in_hand_check(I))
@@ -209,7 +209,12 @@
return FALSE
//Puts the item into our active hand if possible. returns TRUE on success.
-/mob/proc/put_in_active_hand(obj/item/I, forced = FALSE, ignore_animation = TRUE)
+/mob/proc/put_in_active_hand(obj/item/I, forced = FALSE, ignore_animation = TRUE, checks_mob_loc=TRUE)
+ if(checks_mob_loc) // checks if it's possessed by a mob to prevent client delay. offer code sends this as FALSE.
+ var/owner = I.get_mob_loc()
+ if(ismob(owner) && owner != src)
+ show_message("You wanted to pick up [src], but it's already on someone's hand. You feel bad about the space latency...")
+ return FALSE // sorry, you won't get the benefit of your bad latency.
return put_in_hand(I, active_hand_index, forced, ignore_animation)
@@ -221,7 +226,7 @@
//Puts the item our active hand if possible. Failing that it tries other hands. Returns TRUE on success.
//If both fail it drops it on the floor and returns FALSE.
//This is probably the main one you need to know :)
-/mob/proc/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, forced = FALSE)
+/mob/proc/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, forced = FALSE, checks_mob_loc = TRUE)
if(QDELETED(I))
return FALSE
@@ -245,7 +250,7 @@
to_chat(usr, "Your [inactive_stack.name] stack now contains [inactive_stack.get_amount()] [inactive_stack.singular_name]\s.")
return TRUE
- if(put_in_active_hand(I, forced))
+ if(put_in_active_hand(I, forced, checks_mob_loc=checks_mob_loc))
return TRUE
var/hand = get_empty_held_index_for_side("l")
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index abe3f9c158ba5..85d86fa5da16c 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -355,4 +355,4 @@
return
visible_message("[src] takes [I] from [offerer]", \
"You take [I] from [offerer]")
- put_in_hands(I)
+ put_in_hands(I, checks_mob_loc=FALSE)