Storing objects with slowdown in a backpack or belt (or any such storage) now properly updates your speed (#93967)

## About The Pull Request
Another one of those things that I've noticed when playing around with
fish tanks; The slowdown lingered even when the fish tank (which depends
on the total weight of fish inside it) was no longer held and was only
updated another item is equipped or held.

This is because `attempt_insert` doesn't end up calling `DoUnEquip`,
which along with `equip_to_slot`, is one of the cornerstones of the
whole inventory system that we have had for over a decade. Luckily, this
doesn't break things entirely because `item/doMove` seems to have a
fallback, but it only covers held items and only does half of what
`DoUnEquip` does, because it's its own copypaste code, disconnected from
the standard unequip call stack.

I've done some changes to make sure `DoUnEquip` is always called on
`doMove` if we find that the item still has the IN_INVENTORY flag. I've
also updated the code comment for it as well, to emphasize that the
measure is a fallback and not an excuse to call forceMove or Move if we
know that the object is held or equipped on a mob.
If something doesn't work, it'll be likely caught by the CI (it's a core
feature of the game after all) or stack traces.

Also, despite equipment slowdown supporting all mob types, when
equipping/unequipping items it's only applied to carbon mobs. This is
not _strictly_ a contributing factor to the titled issue but it still
limits a balance feature that ought to affect all mobs with hands and/or
equipment slots.

## Why It's Good For The Game
Fixing issues with inventory and storages. Hopefully improving and
modernizing years old code a little.
This commit is contained in:
Ghom
2025-12-04 17:40:16 -06:00
committed by GitHub
parent bcebd92b4d
commit 043677c6f6
8 changed files with 39 additions and 48 deletions
+12 -3
View File
@@ -181,7 +181,7 @@
I.do_pickup_animation(src)
if(get_item_for_held_index(hand_index))
dropItemToGround(get_item_for_held_index(hand_index), force = TRUE)
I.forceMove(src)
I.forceMove(src) //this has to come before has_equipped() is called
held_items[hand_index] = I
SET_PLANE_EXPLICIT(I, ABOVE_HUD_PLANE, src)
if(I.pulledby)
@@ -440,6 +440,7 @@
item_dropping.layer = initial(item_dropping.layer)
SET_PLANE_EXPLICIT(item_dropping, initial(item_dropping.plane), newloc)
item_dropping.appearance_flags &= ~NO_CLIENT_COLOR
item_dropping.item_flags &= ~IN_INVENTORY //This has to come before MoveToNullspace/forceMove is called
if(!no_move && !(item_dropping.item_flags & DROPDEL)) //item may be moved/qdel'd immedietely, don't bother moving it
if (isnull(newloc))
item_dropping.moveToNullspace()
@@ -458,7 +459,7 @@
* * Optional - include_flags, (see obj.flags.dm) describes which optional things to include or not (pockets, accessories, held items)
*/
/mob/living/proc/get_equipped_items(include_flags = NONE)
/mob/proc/get_equipped_items(include_flags = NONE)
var/list/items = list()
for(var/obj/item/item_contents in contents)
if(item_contents.item_flags & IN_INVENTORY)
@@ -472,6 +473,10 @@
return items
///Get all items in our possession that should affect our movespeed
/mob/proc/get_equipped_speed_mod_items()
return get_equipped_items(INCLUDE_HELD|INCLUDE_ABSTRACT|INCLUDE_PROSTHETICS)
/**
* Returns the items that were successfully unequipped.
*/
@@ -531,12 +536,16 @@
/// This proc is called after an item has been successfully handled and equipped to a slot.
/mob/proc/has_equipped(obj/item/item, slot, initial = FALSE)
SHOULD_CALL_PARENT(TRUE)
return item.on_equipped(src, slot, initial)
item.item_flags |= IN_INVENTORY
. = item.on_equipped(src, slot, initial)
if(.)
update_equipment_speed_mods()
/// This proc is called after an item has been removed from a mob but before it has been officially deslotted.
/mob/proc/has_unequipped(obj/item/item, silent = FALSE)
SHOULD_CALL_PARENT(TRUE)
item.dropped(src, silent)
update_equipment_speed_mods()
return TRUE
/**
@@ -59,7 +59,7 @@
equipping.pulledby.stop_pulling()
equipping.screen_loc = null // will get moved if inventory is visible
equipping.forceMove(src)
equipping.forceMove(src) //This has to come before has_equipped is called.
SET_PLANE_EXPLICIT(equipping, ABOVE_HUD_PLANE, src)
switch(slot)
@@ -207,9 +207,6 @@
return not_handled //For future deeper overrides
/mob/living/carbon/human/get_equipped_speed_mod_items()
return ..() - list(l_store, r_store, s_store)
/mob/living/carbon/human/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
. = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should.
if(!. || !item_dropping)
+2 -7
View File
@@ -118,7 +118,7 @@
for(var/mob/dead/observe as anything in observers)
observe.client?.screen -= equipping
equipping.forceMove(src)
equipping.forceMove(src) //This has to come before has_equipped is called.
SET_PLANE_EXPLICIT(equipping, ABOVE_HUD_PLANE, src)
equipping.appearance_flags |= NO_CLIENT_COLOR
var/not_handled = FALSE
@@ -163,15 +163,11 @@
return not_handled
/mob/living/carbon/get_equipped_speed_mod_items()
return ..() + get_equipped_items(INCLUDE_ABSTRACT)
/mob/living/carbon/has_equipped(obj/item/item, slot, initial = FALSE)
. = ..()
if(!.)
return
update_equipment_speed_mods()
hud_used?.update_locked_slots()
if(!(slot & item.slot_flags)) // Things below only update if slotted in (ie: not held)
return
@@ -180,11 +176,10 @@
add_item_coverage(item)
/mob/living/carbon/has_unequipped(obj/item/item)
. = ..() // NB: ATP the item is still in the slot, but no longer has the IN_INVENTORY flag (so is not returned by get_equipped_items)
. = ..()
if(!.)
return
update_equipment_speed_mods()
hud_used?.update_locked_slots()
if(item.hair_mask)
update_body()
-7
View File
@@ -1562,13 +1562,6 @@
else
remove_movespeed_modifier(/datum/movespeed_modifier/equipment_speedmod)
///Get all items in our possession that should affect our movespeed
/mob/proc/get_equipped_speed_mod_items()
. = list()
for(var/obj/item/thing in held_items)
if(thing.item_flags & SLOWS_WHILE_IN_HAND)
. += thing
/mob/proc/set_stat(new_stat)
if(new_stat == stat)
return