Refactor split_stack to two separate procs, fixes a hard delete in the process (#91306)

## About The Pull Request

I was trying to fix a hard delete and it became a whole thing
- Reworks `/obj/item/stack/proc/split_stack` to no longer also try to
put it in the user's hands, or take a user at all. The proc now purely
splits the stack and returns the new one
- Creates `/obj/item/stack/proc/split_n_take` which uses `split_stack`
and does the other behaviors like fingerprint adding and putting in the
user's hands
- Update usages of `split_stack` to either properly use it and remove
the code added to get around the put in hands behavior, or change them
to use `split_n_take` instead
- Fix a random bug in pipe bomb building I noticed while testing

## Why It's Good For The Game

Ultimately this fixes a hard delete with goldgrubs eating a piece of ore
while sitting on top of another piece of ore of the same type (the put
in hands behavior of split stack would cause the ore being consumed to
drop to the ground briefly, merge with the ore there, and then qdel
while still being moved to the goldgrub contents), but also added bonus
that it makes `split_stack` just split the stack and not some other
stuff too. Also a pipe bomb bug fix

## Changelog

🆑
fix: fixed being able to add seemingly infinite refined bluespace
crystals to pipe bombs
refactor: /obj/item/stack/split_stack no longer tries to move the stack
into the user's hands, use /obj/item/stack/split_n_take for that
/🆑
This commit is contained in:
Roxy
2025-05-25 03:14:53 +03:00
committed by GitHub
parent 245f453a43
commit 7d08da9fd6
9 changed files with 39 additions and 32 deletions
+1 -1
View File
@@ -119,7 +119,7 @@
var/atom/movable/final_food = food
if(isstack(food)) //if stack, only consume 1
var/obj/item/stack/food_stack = food
final_food = food_stack.split_stack(udder_mob, 1)
final_food = food_stack.split_stack(1)
final_food.forceMove(src)
/obj/item/udder/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+1 -1
View File
@@ -102,7 +102,7 @@
return
if(isstack(target)) //if stack, only consume 1
var/obj/item/stack/food_stack = target
final_target = food_stack.split_stack(eater, 1)
final_target = food_stack.split_stack(1)
eater.log_message("has eaten [target], [add_to_contents ? "swallowing it" : "destroying it"]!", LOG_ATTACK)
@@ -142,11 +142,10 @@
crystal_stack.use_tool(src, user, amount = 1) //use only one if we were installing from a stack of crystals
return
//the single crystal that we actually install
var/obj/item/stack/ore/bluespace_crystal/single_crystal = crystal_stack.split_stack(null, 1)
var/obj/item/stack/ore/bluespace_crystal/single_crystal = crystal_stack.split_stack(1)
if(isnull(single_crystal))
return
if(!user.transferItemToLoc(single_crystal, src))
return
single_crystal.forceMove(src)
crystal_lens = single_crystal
playsound(src, 'sound/items/tools/screwdriver2.ogg', 30)
balloon_alert(user, "installed \the [crystal_lens.name]")
@@ -225,8 +225,9 @@
var/atom/movable/to_put = item
if(isstack(item))
var/obj/item/stack/as_stack = item
to_put = as_stack.split_stack(user = null, amount = 1)
as_stack.merge_type = null //prevent them from merging inside for contents.len
var/obj/item/stack/new_stack = as_stack.split_stack(1)
new_stack.merge_type = null //prevent them from merging inside for contents.len
to_put = new_stack
to_put.forceMove(src)
return
+25 -14
View File
@@ -695,7 +695,7 @@
if(user.get_inactive_held_item() == src)
if(is_zero_amount(delete_if_zero = TRUE))
return
return split_stack(user, 1)
return split_n_take(user, 1)
else
. = ..()
@@ -712,30 +712,41 @@
var/stackmaterial = tgui_input_number(user, "How many sheets do you wish to take out of this stack?", "Stack Split", max_value = max)
if(!stackmaterial || QDELETED(user) || QDELETED(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
split_stack(user, stackmaterial)
split_n_take(user, stackmaterial)
to_chat(user, span_notice("You take [stackmaterial] sheets out of the stack."))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/** Splits the stack into two stacks.
/** Splits the stack into two stacks, returns the new stack.
*
* Arguments:
* - [user][/mob]: The mob splitting the stack.
* - amount: The number of units to split from this stack.
*/
/obj/item/stack/proc/split_stack(mob/user, amount)
/obj/item/stack/proc/split_stack(amount)
if(!use(amount, TRUE, FALSE))
return null
var/obj/item/stack/F = new type(user? user : drop_location(), amount, FALSE, mats_per_unit)
. = F
F.copy_evidences(src)
var/obj/item/stack/new_stack = new type(null, amount, FALSE, mats_per_unit)
new_stack.copy_evidences(src)
loc.atom_storage?.refresh_views()
if(user)
if(!user.put_in_hands(F, merge_stacks = FALSE))
F.forceMove(user.drop_location())
add_fingerprint(user)
F.add_fingerprint(user)
is_zero_amount(delete_if_zero = TRUE)
return new_stack
/**
* Splits amount items from stack, attempts to place new stack in user's hands.
* Returns the new stack.
* Arguments:
* * [user][/mob] - Mob performing the split, non-nullable
* * amount - Number of units to split from this stack
*/
/obj/item/stack/proc/split_n_take(mob/user, amount)
if(!user)
return null
add_fingerprint(user)
var/obj/item/stack/new_stack = split_stack(amount)
if(isnull(new_stack))
return null
new_stack.add_fingerprint(user)
user.put_in_hands(new_stack, merge_stacks = FALSE)
return new_stack
/obj/item/stack/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
if(can_merge(W, inhand = TRUE))
+1 -1
View File
@@ -57,4 +57,4 @@
/obj/machinery/power/manufacturing/router/proc/handle_stack(obj/item/stack/stack, direction)
if(stack.amount <= 1) // last implementation was just not good so lets cheap out
return stack
return stack.split_stack(amount = 1)
return stack.split_stack(1)
@@ -113,7 +113,7 @@
if(!user.transferItemToLoc(potential_stack, src))
user.balloon_alert(user, "stuck to your hand!")
return
balloon_alert(src, "inserted")
balloon_alert(user, "inserted")
return
if(our_sheet.amount >= our_sheet.max_amount)
user?.balloon_alert(user, "full!")
@@ -121,11 +121,9 @@
if(!our_sheet.can_merge(potential_stack))
user?.balloon_alert(user, "not suitable!")
return
var/atom/movable/to_move = potential_stack.split_stack(user, min(our_sheet.max_amount - our_sheet.amount, potential_stack.amount))
if(!user.transferItemToLoc(to_move, src))
user.balloon_alert(user, "stuck to your hand!")
return
balloon_alert(src, "inserted")
var/atom/movable/to_move = potential_stack.split_stack(min(our_sheet.max_amount - our_sheet.amount, potential_stack.amount))
to_move.forceMove(src)
balloon_alert(user, "inserted")
return
/mob/living/basic/bot/repairbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
@@ -36,8 +36,7 @@
playsound(turf_target, 'sound/machines/click.ogg', 50, TRUE)
new /obj/structure/girder(turf_target)
var/atom/stack_to_delete = our_rods.split_stack(owner, 2)
qdel(stack_to_delete)
our_rods.use(2)
StartCooldown()
qdel(effect)
return TRUE
@@ -248,8 +248,7 @@
repair.set_color(toolbox_color)
to_chat(user, span_notice("You add [item] to [src]. Boop beep!"))
var/obj/item/stack/crafting_stack = item
var/atom/used_belt = crafting_stack.split_stack(user, 1)
qdel(used_belt)
crafting_stack.use(1)
qdel(src)