From f8cae70510489d74901531189d2a874febbb59fa Mon Sep 17 00:00:00 2001 From: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:28:22 +0200 Subject: [PATCH] Fixes ore bags being able to reject any ores despite having ample space, stack merging sanity/QOL (#93451) ## About The Pull Request Ore bags try to insert the stack without actually checking if it can merge with anything, meaning that ores can take up limited slots which may end up with 7 single-ore stacks preventing any more ore from entering the bag, despite there being ample space for 49 more ore of each type. Now bags will actually try to merge picked up ore into themselves first, ensuring that this doesn't occur, and that the bag will always be topped up (This needed additional logic to ensure that stacks don't merge beyond the size that the storage they're in can handle) Additionally, I've changed how stacks merge when dragged (instead of merging themselves into the target, target is merged into themselves) and prevent dragging from changing focus to stack that is being merged into if the merging stack isn't empty, fixing the "pull churn" issue that locks out player inputs if you try to pry and drag floor tiles due to constantly swapping the pull target after every tile pried, plus I've removed that weird ore box `insertion` animation (which I recon is an oversight) and ore bags sucking up the ores you drop from your hand because you're probably doing so intentionally ## Changelog :cl: fix: Fixed ore bags being able to reject any ores despite having ample space fix: Pulling a stack behind you and getting it topped up by lying items will no longer spam pulls and lock out your inputs fix: Picking up ores into an ore box via an ore bag will no longer play a jank animation qol: Ore bags and MODule ore bags no longer pick up ores you've dropped from your hand until you walk over them again /:cl: --- code/game/objects/items/stacks/stack.dm | 60 +++++++++++++++++----- code/game/objects/items/storage/bags.dm | 21 ++++++-- code/modules/mod/modules/modules_supply.dm | 2 +- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index c28998c7f6a..4c41a55aff1 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -123,14 +123,14 @@ /obj/item/stack/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) . = ..() if((!throwing || throwing.target_turf == loc) && old_loc != loc && (flags_1 & INITIALIZED_1)) - merge_with_loc() + merge_with_loc(merge_into_ourselves = !isnull(pulledby)) ///Called to lazily update the materials of the item whenever the used or if more is added /obj/item/stack/proc/update_custom_materials() if(length(mats_per_unit)) set_custom_materials(mats_per_unit, amount) -/obj/item/stack/proc/find_other_stack(list/already_found) +/obj/item/stack/proc/find_other_stack(list/already_found, merge_into_ourselves = FALSE) if(QDELETED(src) || isnull(loc)) return for(var/obj/item/stack/item_stack in loc) @@ -141,20 +141,23 @@ var/stack_ref = REF(item_stack) if(already_found[stack_ref]) continue - if(can_merge(item_stack)) + if(merge_into_ourselves ? item_stack.can_merge(src) : can_merge(item_stack)) already_found[stack_ref] = TRUE return item_stack /// Tries to merge the stack with everything on the same tile. -/obj/item/stack/proc/merge_with_loc() +/obj/item/stack/proc/merge_with_loc(merge_into_ourselves = FALSE) var/list/already_found = list() // change to alist whenever dreamchecker and such finally supports that - var/obj/item/other_stack = find_other_stack(already_found) + var/obj/item/stack/other_stack = find_other_stack(already_found, merge_into_ourselves) var/sanity = max_amount // just in case while(other_stack && sanity > 0) sanity-- - if(merge(other_stack)) + if(!merge_into_ourselves) + if(merge(other_stack)) + return FALSE + else if (other_stack.merge(src) && !QDELETED(other_stack)) return FALSE - other_stack = find_other_stack(already_found) + other_stack = find_other_stack(already_found, TRUE) return TRUE /obj/item/stack/apply_material_effects(list/materials) @@ -208,12 +211,14 @@ return list() //empty list /obj/item/stack/proc/update_weight() - if(amount <= (max_amount * (1/3))) - update_weight_class(clamp(full_w_class-2, WEIGHT_CLASS_TINY, full_w_class)) - else if (amount <= (max_amount * (2/3))) - update_weight_class(clamp(full_w_class-1, WEIGHT_CLASS_TINY, full_w_class)) - else - update_weight_class(full_w_class) + update_weight_class(get_weight_from_size(amount)) + +/obj/item/stack/proc/get_weight_from_size(stack_amount) + if(stack_amount <= (max_amount * (1/3))) + return clamp(full_w_class - 2, WEIGHT_CLASS_TINY, full_w_class) + if(stack_amount <= (max_amount * (2/3))) + return clamp(full_w_class - 1, WEIGHT_CLASS_TINY, full_w_class) + return full_w_class /obj/item/stack/update_icon_state() if(novariants) @@ -671,8 +676,35 @@ transfer = min(transfer, round((target_stack.source.max_energy - target_stack.source.energy) / target_stack.cost)) else transfer = min(transfer, (limit ? limit : target_stack.max_amount) - target_stack.amount) - if(pulledby) + // Ensure that we're not bloating the target stack to the point where it falls out of storage + if(target_stack.loc?.atom_storage) + var/datum/storage/target_storage = target_stack.loc?.atom_storage + var/cur_size = target_stack.w_class + var/new_size = target_stack.get_weight_from_size(target_stack.amount + transfer) + var/real_new_size = new_size + var/real_cur_size = cur_size + + // Ensure that we don't end up with two mergeable stacks if our own size gets reduced enough from the merge and we share the space + if (!is_cyborg && loc == target_stack.loc) + real_cur_size += w_class + if (amount > transfer) + real_new_size += get_weight_from_size(amount - transfer) + + // If total size changed, check for overflows + if(new_size > cur_size) + var/size_limit = max(new_size - target_storage.max_specific_storage, target_storage.get_total_weight() + real_new_size - real_cur_size - target_storage.max_total_storage) + // If we're over the stack limit the storage container can support, reduce the transferred amount + // to the nearest size threshold, then by a third of the target stack per excess size + if(size_limit > 0) + var/to_threshold = FLOOR(target_stack.amount + transfer, floor(target_stack.max_amount / 3)) + transfer = clamp(to_threshold - floor(target_stack.max_amount / 3) * (size_limit - 1) - target_stack.amount, 0, transfer) + + if(!transfer) + return + + if(pulledby && is_zero_amount(delete_if_zero = FALSE)) pulledby.start_pulling(target_stack) + target_stack.copy_evidences(src) use(transfer, transfer = TRUE, check = FALSE) target_stack.add(transfer) diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index 0d26a27bbd8..5916524f9dd 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -158,6 +158,10 @@ RegisterSignal(tile, COMSIG_ATOM_ENTERED, PROC_REF(on_obj_entered)) RegisterSignal(tile, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_atom_initialized_on)) + INVOKE_ASYNC(src, PROC_REF(handle_move), user) + +/obj/item/storage/bag/ore/proc/handle_move(mob/living/user) + var/turf/tile = get_turf(user) var/obj/structure/ore_box/box = null if(istype(user.pulling, /obj/structure/ore_box)) box = user.pulling @@ -199,9 +203,18 @@ box = user.pulling if (box) - user.transferItemToLoc(ore, box) + user.transferItemToLoc(ore, box, animated = FALSE) return TRUE + if (istype(ore, /obj/item/stack/ore)) + var/obj/item/stack/ore/real_ore = ore + for(var/obj/item/stack/ore/stored_ore as anything in src) + if(!real_ore.can_merge(stored_ore)) + continue + real_ore.merge(stored_ore) + if(QDELETED(real_ore)) + return TRUE + if (atom_storage.attempt_insert(ore, user)) return TRUE @@ -212,13 +225,13 @@ /obj/item/storage/bag/ore/proc/on_obj_entered(atom/new_loc, atom/movable/arrived, atom/old_loc) SIGNAL_HANDLER - if(is_type_in_list(arrived, atom_storage.can_hold) && !dropping_ores) - pickup_ore(arrived, listening_to) + if(is_type_in_list(arrived, atom_storage.can_hold) && !dropping_ores && old_loc != loc) + INVOKE_ASYNC(src, PROC_REF(pickup_ore), arrived, listening_to) /obj/item/storage/bag/ore/proc/on_atom_initialized_on(atom/loc, atom/new_atom) SIGNAL_HANDLER if(is_type_in_list(new_atom, atom_storage.can_hold)) - pickup_ore(new_atom, listening_to) + INVOKE_ASYNC(src, PROC_REF(pickup_ore), new_atom, listening_to) /obj/item/storage/bag/ore/cyborg name = "cyborg mining satchel" diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index 5a183827233..0ebd99635ae 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -290,7 +290,7 @@ /obj/item/mod/module/orebag/proc/on_obj_entered(atom/new_loc, atom/movable/arrived, atom/old_loc) SIGNAL_HANDLER - if(istype(arrived, /obj/item/stack/ore) && !dropping_ores) + if(istype(arrived, /obj/item/stack/ore) && !dropping_ores && old_loc != mod.wearer) INVOKE_ASYNC(src, PROC_REF(move_ore), arrived) playsound(mod.wearer, SFX_RUSTLE, 50, TRUE)