From 8486f2f7e28fecd131c2377abd04ec276980ff95 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:48:19 -0500 Subject: [PATCH] Storage / table interactions at the bottom of the interaction chain (#85512) Because the wings were in fact made of wax ## About The Pull Request Storage goes to the very bottom of the interaction chain, hardcoded in on `/atom`. This is not preferred, obviously, but it ends up being a lot less snowflaking overall. Tables also go at the very bottom by extending `base_item_interaction`. Fixes #83742 Fixes #84434 Fixes #83982 Fixes #85516 Fixes #84990 Fixes #84890 Closes #85036 Closes #84025 (RMB places it on the table.) Closes #86616 Other changes: Refactored pod storage to be less jank. Patches some exploits around it. ## Why It's Good For The Game Should make a lot more interactions a lot more reliable... hopefully ## Changelog :cl: Melbert refactor: Storage and Tables are now a lower priority action, meaning some uses of items on storage should work... better, now. Here's hoping at least, report any oddities. refactor: Note: For an overwhelming majority of items, **combat mode** will attempt to attack/insert into the target, while **non-combat-mode** will attempt to use on a target. This means screwdrivering or emagging a MODsuit must be done on non-combat-mode, as combat mode will simply put the screwdriver or emag into its storage. Same applies to tables, though when in doubt, RMB may help (for things which are also weapons, like mops). refactor: Refactored escape pod storage, now they actually properly show as unlocked on red alert and above. /:cl: --- .../signals_atom/signals_atom_x_act.dm | 5 -- code/__DEFINES/tools.dm | 6 ++ code/__DEFINES/traits/declarations.dm | 13 +++ code/_globalvars/traits/_traits.dm | 11 +-- code/datums/components/cleaner.dm | 3 + code/datums/components/lockable_storage.dm | 1 - code/datums/storage/storage.dm | 33 ++------ code/game/atom/atom_tool_acts.dm | 38 +++------ code/game/objects/items/climbingrope.dm | 2 + code/game/objects/items/clown_items.dm | 3 - code/game/objects/items/control_wand.dm | 8 +- code/game/objects/items/crayons.dm | 12 ++- code/game/objects/items/devices/aicard.dm | 16 ++-- .../objects/items/devices/chameleonproj.dm | 47 ++++++----- .../items/devices/forcefieldprojector.dm | 6 +- .../objects/items/devices/geiger_counter.dm | 10 +-- .../objects/items/devices/laserpointer.dm | 8 +- .../objects/items/devices/traitordevices.dm | 9 --- code/game/objects/items/emags.dm | 8 +- code/game/objects/items/extinguisher.dm | 10 ++- code/game/objects/items/machine_wand.dm | 8 +- code/game/objects/items/rcd/RCD.dm | 23 +++--- code/game/objects/items/religion.dm | 8 +- code/game/objects/items/stacks/wrap.dm | 9 +-- code/game/objects/items/storage/bags.dm | 10 +-- code/game/objects/items/taster.dm | 2 +- .../structures/crates_lockers/closets.dm | 2 +- .../objects/structures/deployable_turret.dm | 6 +- code/game/objects/structures/tables_racks.dm | 38 ++++----- .../game/objects/structures/tank_dispenser.dm | 2 +- .../structures/water_structures/sink.dm | 2 +- .../water_structures/water_source.dm | 2 +- .../abductor/equipment/gear/abductor_items.dm | 24 +++--- .../heretic/items/labyrinth_handbook.dm | 8 +- code/modules/antagonists/spy/spy_bounty.dm | 8 ++ code/modules/antagonists/spy/spy_uplink.dm | 12 +-- .../antagonists/traitor/objectives/steal.dm | 8 +- .../antagonists/wizard/equipment/artefact.dm | 6 +- .../antagonists/wizard/equipment/soulstone.dm | 15 ++-- .../clothing/chameleon/chameleon_scanner.dm | 10 ++- code/modules/detectivework/evidence.dm | 29 ++++--- code/modules/detectivework/scanner.dm | 5 +- code/modules/fishing/fishing_rod.dm | 2 +- .../food_and_drinks/machinery/microwave.dm | 24 +++--- .../food_and_drinks/machinery/smartfridge.dm | 2 +- .../hydroponics/beekeeping/bee_smoker.dm | 15 +++- code/modules/library/bibles.dm | 3 - .../equipment/monster_organs/monster_organ.dm | 3 + .../modules/mining/lavaland/megafauna_loot.dm | 8 +- .../basic/minebots/minebot_remote_control.dm | 2 + code/modules/mod/mod_control.dm | 12 +-- code/modules/mod/mod_core.dm | 21 +---- code/modules/paperwork/filingcabinet.dm | 2 +- code/modules/paperwork/handlabeler.dm | 3 - code/modules/photography/camera/camera.dm | 4 + code/modules/power/singularity/emitter.dm | 2 + .../machinery/portable_chem_mixer.dm | 18 ++--- .../reagents/reagent_containers/cups/_cup.dm | 4 +- .../reagents/reagent_containers/spray.dm | 3 + code/modules/recycling/conveyor.dm | 2 +- code/modules/recycling/disposal/bin.dm | 3 +- .../research/xenobiology/xenobiology.dm | 11 +-- code/modules/shuttle/emergency.dm | 79 +++++++++++++------ code/modules/shuttle/on_move.dm | 13 +-- code/modules/unit_tests/storage.dm | 21 +++++ 65 files changed, 386 insertions(+), 357 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm index a33c4951384..46e179ee567 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm @@ -89,11 +89,6 @@ /// Sent from [atom/proc/item_interaction], when this atom is used as a tool and an event occurs #define COMSIG_ITEM_TOOL_ACTED "tool_item_acted" -/// This is sent via item interaction (IE, item clicking on atom) right before the item's inserted into the atom's storage -/// Args: (obj/item/inserting, mob/living/user) -#define COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT "atom_storage_item_interact_insert" - #define BLOCK_STORAGE_INSERT (1<<0) - /// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target #define COMSIG_ATOM_SABOTEUR_ACT "hit_by_saboteur" #define COMSIG_SABOTEUR_SUCCESS 1 diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm index 2348bd49f19..b0812746cb8 100644 --- a/code/__DEFINES/tools.dm +++ b/code/__DEFINES/tools.dm @@ -39,3 +39,9 @@ /// Combination flag for any item interaction that blocks the rest of the attack chain #define ITEM_INTERACT_ANY_BLOCKER (ITEM_INTERACT_SUCCESS | ITEM_INTERACT_BLOCKING) + +/** + * A helper for checking if an item interaction should be skipped. + * This is only used explicitly because some interactions may not want to ever be skipped. + */ +#define SHOULD_SKIP_INTERACTION(target, item, user) (HAS_TRAIT(target, TRAIT_COMBAT_MODE_SKIP_INTERACTION) && user.combat_mode) diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 39547eac6ac..7cc8085888b 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -1278,6 +1278,19 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Trait which allows mobs to parry mining mob projectiles #define TRAIT_MINING_PARRYING "mining_parrying" +/** + * + * This trait is used in some interactions very high in the interaction chain to allow + * certain atoms to be skipped by said interactions if the user is in combat mode. + * + * Its primarily use case is for stuff like storage and tables, to allow things like emags to be bagged + * (because in some contexts you might want to be emagging a bag, and in others you might want to be storing it.) + * + * This is only checked by certain items explicitly so you can't just add the trait and expect it to work. + * (This may be changed later but I chose to do it this way to avoid messing up interactions which require combat mode) + */ +#define TRAIT_COMBAT_MODE_SKIP_INTERACTION "combat_mode_skip_interaction" + ///A "fake" effect that should not be subject to normal effect removal methods (like the effect remover component) #define TRAIT_ILLUSORY_EFFECT "illusory_effect" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index ba3491dc44b..1a0fc110722 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -37,6 +37,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_BLOCKING_EXPLOSIVES" = TRAIT_BLOCKING_EXPLOSIVES, "TRAIT_BOULDER_BREAKER" = TRAIT_BOULDER_BREAKER, "TRAIT_CASTABLE_LOC" = TRAIT_CASTABLE_LOC, + "TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER, + "TRAIT_COMBAT_MODE_SKIP_INTERACTION" = TRAIT_COMBAT_MODE_SKIP_INTERACTION, "TRAIT_DEL_ON_SPACE_DUMP" = TRAIT_DEL_ON_SPACE_DUMP, "TRAIT_FISH_CASE_COMPATIBILE" = TRAIT_FISH_CASE_COMPATIBILE, "TRAIT_FROZEN" = TRAIT_FROZEN, @@ -50,27 +52,26 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING, "TRAIT_MOVE_GROUND" = TRAIT_MOVE_GROUND, "TRAIT_MOVE_PHASING" = TRAIT_MOVE_PHASING, - "TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING, "TRAIT_MOVE_UPSIDE_DOWN" = TRAIT_MOVE_UPSIDE_DOWN, + "TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING, + "TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE, "TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM, "TRAIT_NO_MANIFEST_CONTENTS_ERROR" = TRAIT_NO_MANIFEST_CONTENTS_ERROR, "TRAIT_NO_MISSING_ITEM_ERROR" = TRAIT_NO_MISSING_ITEM_ERROR, "TRAIT_NO_THROW_HITPUSH" = TRAIT_NO_THROW_HITPUSH, - "TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE, - "TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC, "TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT" = TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT, "TRAIT_ON_HIT_EFFECT" = TRAIT_ON_HIT_EFFECT, "TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN, "TRAIT_SCARY_FISHERMAN" = TRAIT_SCARY_FISHERMAN, "TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION, "TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE, + "TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC, "TRAIT_TELEKINESIS_CONTROLLED" = TRAIT_TELEKINESIS_CONTROLLED, "TRAIT_UNDERFLOOR" = TRAIT_UNDERFLOOR, "TRAIT_UNIQUE_IMMERSE" = TRAIT_UNIQUE_IMMERSE, - "TRAIT_WAS_RENAMED" = TRAIT_WAS_RENAMED, "TRAIT_WADDLING" = TRAIT_WADDLING, + "TRAIT_WAS_RENAMED" = TRAIT_WAS_RENAMED, "TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE, - "TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER, ), /datum/controller/subsystem/economy = list( "TRAIT_MARKET_CRASHING" = TRAIT_MARKET_CRASHING, diff --git a/code/datums/components/cleaner.dm b/code/datums/components/cleaner.dm index 3001fde9837..75319a7133f 100644 --- a/code/datums/components/cleaner.dm +++ b/code/datums/components/cleaner.dm @@ -62,6 +62,9 @@ /datum/component/cleaner/proc/on_interaction(datum/source, mob/living/user, atom/target, list/modifiers) SIGNAL_HANDLER + if(isitem(source) && SHOULD_SKIP_INTERACTION(target, source, user)) + return NONE + // By default, give XP var/give_xp = TRUE if(pre_clean_callback) diff --git a/code/datums/components/lockable_storage.dm b/code/datums/components/lockable_storage.dm index 482cb134159..ca058cb3fbf 100644 --- a/code/datums/components/lockable_storage.dm +++ b/code/datums/components/lockable_storage.dm @@ -62,7 +62,6 @@ UnregisterSignal(parent, list( COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER), COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), - COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT, )) UnregisterSignal(parent, list( COMSIG_ATOM_EXAMINE, diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm index 8bdfa3f3de2..aedb28a74e9 100644 --- a/code/datums/storage/storage.dm +++ b/code/datums/storage/storage.dm @@ -189,20 +189,19 @@ /// Set the passed atom as the parent /datum/storage/proc/set_parent(atom/new_parent) - PRIVATE_PROC(TRUE) + PROTECTED_PROC(TRUE) ASSERT(isnull(parent)) parent = new_parent + ADD_TRAIT(parent, TRAIT_COMBAT_MODE_SKIP_INTERACTION, REF(src)) // a few of theses should probably be on the real_location rather than the parent - RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interact)) RegisterSignals(parent, list(COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_ATTACK_HAND), PROC_REF(on_attack)) RegisterSignal(parent, COMSIG_MOUSEDROP_ONTO, PROC_REF(on_mousedrop_onto)) RegisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(on_mousedropped_onto)) RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(on_preattack)) RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(mass_empty)) RegisterSignals(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_ATOM_ATTACK_HAND_SECONDARY), PROC_REF(open_storage_on_signal)) - RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(on_item_interact_secondary)) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(close_distance)) RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(update_actions)) RegisterSignal(parent, COMSIG_TOPIC, PROC_REF(topic_handle)) @@ -834,19 +833,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) attempt_insert(dropping, user) return COMPONENT_CANCEL_MOUSEDROPPED_ONTO -/// Signal handler for whenever we're attacked by an object. -/datum/storage/proc/on_item_interact(datum/source, mob/user, obj/item/thing, params) - SIGNAL_HANDLER - - if(!insert_on_attack) - return NONE - if(!thing.storage_insert_on_interaction(src, parent, user)) - return NONE - if(!parent.storage_insert_on_interacted_with(src, thing, user)) - return NONE - if(SEND_SIGNAL(parent, COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT, thing, user) & BLOCK_STORAGE_INSERT) - return NONE - +/// Called directly from the attack chain if [insert_on_attack] is TRUE. +/// Handles inserting an item into the storage when clicked. +/datum/storage/proc/item_interact_insert(mob/living/user, obj/item/thing) if(iscyborg(user)) return ITEM_INTERACT_BLOCKING @@ -897,18 +886,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) return toreturn -/// Signal handler for when we get attacked with secondary click by an item. -/datum/storage/proc/on_item_interact_secondary(datum/source, mob/user, atom/weapon) - SIGNAL_HANDLER - - if(istype(weapon, /obj/item/chameleon)) - var/obj/item/chameleon/chameleon_weapon = weapon - chameleon_weapon.make_copy(source, user) - - if(open_storage_on_signal(source, user)) - return ITEM_INTERACT_BLOCKING - return NONE - /// Signal handler to open up the storage when we receive a signal. /datum/storage/proc/open_storage_on_signal(datum/source, mob/to_show) SIGNAL_HANDLER diff --git a/code/game/atom/atom_tool_acts.dm b/code/game/atom/atom_tool_acts.dm index 6fe1ffefc5d..efb43fc16e5 100644 --- a/code/game/atom/atom_tool_acts.dm +++ b/code/game/atom/atom_tool_acts.dm @@ -14,7 +14,7 @@ if(tool_return) return tool_return - var/is_right_clicking = LAZYACCESS(modifiers, RIGHT_CLICK) + var/is_right_clicking = text2num(LAZYACCESS(modifiers, RIGHT_CLICK)) var/is_left_clicking = !is_right_clicking var/early_sig_return = NONE if(is_left_clicking) @@ -22,10 +22,8 @@ * This is intentionally using `||` instead of `|` to short-circuit the signal calls * This is because we want to return early if ANY of these signals return a value * - * This puts priority on the atom's signals, then the tool's signals, then the user's signals - * So stuff like storage can be handled before stuff the item wants to do like cleaner component - * - * Future idea: Being on combat mode could change/reverse the priority of these signals + * This puts priority on the atom's signals, then the tool's signals, then the user's signals, + * so we can avoid doing two interactions at once */ early_sig_return = SEND_SIGNAL(src, COMSIG_ATOM_ITEM_INTERACTION, user, tool, modifiers) \ || SEND_SIGNAL(tool, COMSIG_ITEM_INTERACTING_WITH_ATOM, user, src, modifiers) \ @@ -50,6 +48,16 @@ if(interact_return) return interact_return + // We have to manually handle storage in item_interaction because storage is blocking in 99% of interactions, which stifles a lot + // Yeah it sucks not being able to signalize this, but the other option is to have a second signal here just for storage which is also not great + if(atom_storage) + if(is_left_clicking) + if(atom_storage.insert_on_attack) + return atom_storage.item_interact_insert(user, tool) + else + if(atom_storage.open_storage(user) && atom_storage.display_contents) + return ITEM_INTERACT_SUCCESS + return NONE /** @@ -315,23 +323,3 @@ /// Called on an object when a tool with analyzer capabilities is used to right click an object /atom/proc/analyzer_act_secondary(mob/living/user, obj/item/tool) return - -/** - * Called before this item is placed into a storage container - * via the item clicking on the target atom - * - * Returning FALSE will prevent the item from being stored. - */ -/obj/item/proc/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user) - return TRUE - -/** - * Called before an item is put into this atom's storage datum via the item clicking on this atom - * - * This can be used to add item-atom interactions that you want handled before inserting something into storage - * (But it's also fairly snowflakey) - * - * Returning FALSE will block that item from being put into our storage. - */ -/atom/proc/storage_insert_on_interacted_with(datum/storage, obj/item/inserted, mob/living/user) - return TRUE diff --git a/code/game/objects/items/climbingrope.dm b/code/game/objects/items/climbingrope.dm index e68e5087712..552b5d58850 100644 --- a/code/game/objects/items/climbingrope.dm +++ b/code/game/objects/items/climbingrope.dm @@ -27,6 +27,8 @@ . += span_notice("The rope looks like you could use it [uses] times before it falls apart.") /obj/item/climbing_hook/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE return ranged_interact_with_atom(interacting_with, user, modifiers) /obj/item/climbing_hook/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index 6565bcae44f..58f2cb49a13 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -161,9 +161,6 @@ return CLEAN_BLOCKED return ..() -/obj/item/soap/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/living/user) - return !user.combat_mode // only cleans a storage item if on combat - /* * Bike Horns */ diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index abad07f96d8..9734661e88f 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -35,10 +35,12 @@ update_icon_state() balloon_alert(user, "mode: [desc[mode]]") -/obj/item/door_remote/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/door_remote/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!istype(interacting_with, /obj/machinery/door) && !isturf(interacting_with)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/door_remote/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) var/obj/machinery/door/door if (istype(interacting_with, /obj/machinery/door)) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 3b2d9d196a1..484cb54b0e3 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -865,7 +865,10 @@ /obj/item/toy/crayon/spraycan/can_use_on(atom/target, mob/user, list/modifiers) if(iscarbon(target)) return TRUE - if(ismob(target) && (HAS_TRAIT(target, TRAIT_SPRAY_PAINTABLE))) + if(is_capped && HAS_TRAIT(target, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + // specifically don't try to use a capped spraycan on stuff like bags and tables, just place it + return FALSE + if(ismob(target) && HAS_TRAIT(target, TRAIT_SPRAY_PAINTABLE)) return TRUE if(isobj(target) && !(target.flags_1 & UNPAINTABLE_1)) return TRUE @@ -967,6 +970,10 @@ /obj/item/toy/crayon/spraycan/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) if(is_capped) + if(!interacting_with.color) + // let's be generous and assume if they're trying to match something with no color, while capped, + // we shouldn't be blocking further interactions + return NONE balloon_alert(user, "take the cap off first!") return ITEM_INTERACT_BLOCKING if(check_empty(user)) @@ -1003,9 +1010,6 @@ update_appearance() return CLICK_ACTION_SUCCESS -/obj/item/toy/crayon/spraycan/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user) - return is_capped - /obj/item/toy/crayon/spraycan/update_icon_state() icon_state = is_capped ? icon_capped : icon_uncapped return ..() diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index c619f7d7018..77eefa8a150 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -43,17 +43,15 @@ user.visible_message(span_suicide("[user] is trying to upload [user.p_them()]self into [src]! That's not going to work out well!")) return BRUTELOSS -/obj/item/aicard/pre_attack(atom/target, mob/living/user, params) - . = ..() - if(.) - return - +/obj/item/aicard/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(AI) - if(upload_ai(target, user)) - return TRUE + if(upload_ai(interacting_with, user)) + return ITEM_INTERACT_SUCCESS else - if(capture_ai(target, user)) - return TRUE + if(capture_ai(interacting_with, user)) + return ITEM_INTERACT_SUCCESS + + return NONE /// Tries to get an AI from the atom clicked /obj/item/aicard/proc/capture_ai(atom/from_what, mob/living/user) diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 1920e47f97f..f0a934fc198 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -36,26 +36,37 @@ else to_chat(user, span_warning("You can't use [src] while inside something!")) -/obj/item/chameleon/interact_with_atom(atom/target, mob/living/user, list/modifiers) - if(!check_sprite(target)) - return ITEM_INTERACT_BLOCKING - if(active_dummy)//I now present you the blackli(f)st - return ITEM_INTERACT_BLOCKING - if(isturf(target)) - return ITEM_INTERACT_BLOCKING - if(ismob(target)) - return ITEM_INTERACT_BLOCKING - if(istype(target, /obj/structure/falsewall)) - return ITEM_INTERACT_BLOCKING - if(target.alpha != 255) - return ITEM_INTERACT_BLOCKING - if(target.invisibility != 0) - return ITEM_INTERACT_BLOCKING - if(iseffect(target) && !istype(target, /obj/effect/decal)) //be a footprint - return ITEM_INTERACT_BLOCKING - make_copy(target, user) +/obj/item/chameleon/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!can_copy(interacting_with) || SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE + make_copy(interacting_with, user) return ITEM_INTERACT_SUCCESS +/obj/item/chameleon/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) + if(!can_copy(interacting_with)) // RMB scan works on storage items, LMB scan does not + return NONE + make_copy(interacting_with, user) + return ITEM_INTERACT_SUCCESS + +/obj/item/chameleon/proc/can_copy(atom/target) + if(!check_sprite(target)) + return FALSE + if(active_dummy)//I now present you the blackli(f)st + return FALSE + if(isturf(target)) + return FALSE + if(ismob(target)) + return FALSE + if(istype(target, /obj/structure/falsewall)) + return FALSE + if(target.alpha != 255) + return FALSE + if(target.invisibility != 0) + return FALSE + if(iseffect(target) && !istype(target, /obj/effect/decal)) //be a footprint + return FALSE + return TRUE + /obj/item/chameleon/proc/make_copy(atom/target, mob/user) playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, TRUE, -6) to_chat(user, span_notice("Scanned [target].")) diff --git a/code/game/objects/items/devices/forcefieldprojector.dm b/code/game/objects/items/devices/forcefieldprojector.dm index 5d40d40a4d9..91bd3040832 100644 --- a/code/game/objects/items/devices/forcefieldprojector.dm +++ b/code/game/objects/items/devices/forcefieldprojector.dm @@ -21,10 +21,10 @@ /// Checks to make sure the projector isn't busy with making another forcefield. var/force_proj_busy = FALSE -/obj/item/forcefield_projector/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/forcefield_projector/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/forcefield_projector/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!check_allowed_items(interacting_with, not_inside = TRUE)) return NONE if(istype(interacting_with, /obj/structure/projected_forcefield)) diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm index 1d5ef17a90c..25bae430609 100644 --- a/code/game/objects/items/devices/geiger_counter.dm +++ b/code/game/objects/items/devices/geiger_counter.dm @@ -67,13 +67,13 @@ update_appearance(UPDATE_ICON) balloon_alert(user, "switch [scanning ? "on" : "off"]") -/obj/item/geiger_counter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/geiger_counter/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - if (user.combat_mode) + if(SHOULD_SKIP_INTERACTION(interacting_with, src, user)) return NONE - if (!CAN_IRRADIATE(interacting_with)) + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/geiger_counter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!CAN_IRRADIATE(interacting_with)) return NONE user.visible_message(span_notice("[user] scans [interacting_with] with [src]."), span_notice("You scan [interacting_with]'s radiation levels with [src]...")) diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 3fe16fdb3fd..8dd03736a65 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -183,12 +183,14 @@ and the wide margin between it and the focus lens could probably house a crystal of some sort." /obj/item/laser_pointer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - -/obj/item/laser_pointer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) laser_act(interacting_with, user, modifiers) return ITEM_INTERACT_BLOCKING +/obj/item/laser_pointer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) + ///Handles shining the clicked atom, /obj/item/laser_pointer/proc/laser_act(atom/target, mob/living/user, list/modifiers) if(isnull(diode)) diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index 8327e185ea6..ee4a61b9351 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -361,15 +361,6 @@ effective or pretty fucking useless. new /obj/item/analyzer(src) new /obj/item/wirecutters(src) -/obj/item/storage/toolbox/emergency/turret/storage_insert_on_interacted_with(datum/storage, obj/item/inserted, mob/living/user) - if(!istype(inserted, /obj/item/wrench/combat)) - return TRUE - if(!user.combat_mode) - return TRUE - if(!inserted.toolspeed) - return TRUE - return FALSE - /obj/item/storage/toolbox/emergency/turret/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(!istype(tool, /obj/item/wrench/combat)) return NONE diff --git a/code/game/objects/items/emags.dm b/code/game/objects/items/emags.dm index ccb52b71bc3..f854ba1b90a 100644 --- a/code/game/objects/items/emags.dm +++ b/code/game/objects/items/emags.dm @@ -137,16 +137,16 @@ . = ..() type_blacklist = list(typesof(/obj/machinery/door/airlock) + typesof(/obj/machinery/door/window/) + typesof(/obj/machinery/door/firedoor) - typesof(/obj/machinery/door/airlock/tram)) //list of all typepaths that require a specialized emag to hack. -/obj/item/card/emag/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/living/user) - return !user.combat_mode - /obj/item/card/emag/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE // lets us put things in bags without trying to emag them if(!can_emag(interacting_with, user)) return ITEM_INTERACT_BLOCKING log_combat(user, interacting_with, "attempted to emag") if(interacting_with.emag_act(user, src)) SSblackbox.record_feedback("tally", "atom_emagged", 1, interacting_with.type) - return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_SUCCESS + return NONE // In a perfect world this would be blocking, but this is not a perfect world /obj/item/card/emag/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) return prox_check ? NONE : interact_with_atom(interacting_with, user) diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index 764e2fc6173..c542048b49d 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -207,13 +207,15 @@ else return FALSE -/obj/item/extinguisher/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/extinguisher/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - if (interacting_with.loc == user) + if(interacting_with.loc == user) return NONE + // Always skip interaction if it's a bag or table (that's not on fire) + if(!(interacting_with.resistance_flags & ON_FIRE) && HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) +/obj/item/extinguisher/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(refilling) refilling = FALSE return NONE diff --git a/code/game/objects/items/machine_wand.dm b/code/game/objects/items/machine_wand.dm index 75c765730f7..7d8b4a0697d 100644 --- a/code/game/objects/items/machine_wand.dm +++ b/code/game/objects/items/machine_wand.dm @@ -85,10 +85,12 @@ remove_old_machine() return CLICK_ACTION_SUCCESS -/obj/item/machine_remote/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/machine_remote/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION) || (!ismachinery(interacting_with) && !isbot(interacting_with))) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/machine_remote/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!COOLDOWN_FINISHED(src, timeout_time)) playsound(src, 'sound/machines/synth_no.ogg', 30 , TRUE) say("Remote control disabled temporarily. Please try again soon.") diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm index 0a9ff79d1ac..279bc91d622 100644 --- a/code/game/objects/items/rcd/RCD.dm +++ b/code/game/objects/items/rcd/RCD.dm @@ -207,15 +207,15 @@ * * [mob][user]- the user building this structure */ /obj/item/construction/rcd/proc/rcd_create(atom/target, mob/user) + var/list/rcd_results = target.rcd_vals(user, src) // does this atom allow for rcd actions? + if(!rcd_results) // nope + return NONE + //straight up can't touch this if(mode == RCD_DECONSTRUCT && (target.resistance_flags & INDESTRUCTIBLE)) balloon_alert(user, "too durable!") - return + return ITEM_INTERACT_BLOCKING - //does this atom allow for rcd actions? - var/list/rcd_results = target.rcd_vals(user, src) - if(!rcd_results) - return FALSE rcd_results["[RCD_DESIGN_MODE]"] = mode rcd_results["[RCD_DESIGN_PATH]"] = rcd_design_path @@ -237,6 +237,7 @@ log_tool("[key_name(user)] used [src] to [rcd_results["[RCD_DESIGN_MODE]"] != RCD_DECONSTRUCT ? "construct [initial(design_path.name)]([design_path])" : "deconstruct [target_name]([target_path])"] at [location]") current_active_effects -= 1 + return ITEM_INTERACT_SUCCESS /** * Internal proc which creates the rcd effects & creates the structure @@ -401,29 +402,25 @@ return . mode = construction_mode - rcd_create(interacting_with, user) - return ITEM_INTERACT_SUCCESS + return rcd_create(interacting_with, user) /obj/item/construction/rcd/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!ranged || !range_check(interacting_with, user)) return ITEM_INTERACT_BLOCKING mode = construction_mode - rcd_create(interacting_with, user) - return ITEM_INTERACT_SUCCESS + return rcd_create(interacting_with, user) /obj/item/construction/rcd/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) mode = RCD_DECONSTRUCT - rcd_create(interacting_with, user) - return ITEM_INTERACT_SUCCESS + return rcd_create(interacting_with, user) /obj/item/construction/rcd/ranged_interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) if(!ranged || !range_check(interacting_with, user)) return ITEM_INTERACT_BLOCKING mode = RCD_DECONSTRUCT - rcd_create(interacting_with, user) - return ITEM_INTERACT_SUCCESS + return rcd_create(interacting_with, user) /obj/item/construction/rcd/handle_openspace_click(turf/target, mob/user, list/modifiers) interact_with_atom(target, user, modifiers) diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm index 232de4c4619..77b087b8ac5 100644 --- a/code/game/objects/items/religion.dm +++ b/code/game/objects/items/religion.dm @@ -343,10 +343,12 @@ var/staffcooldown = 0 var/staffwait = 30 -/obj/item/godstaff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/godstaff/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/godstaff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(staffcooldown + staffwait > world.time) return ITEM_INTERACT_BLOCKING diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm index d7fcd17f295..d1cbb7f1ce8 100644 --- a/code/game/objects/items/stacks/wrap.dm +++ b/code/game/objects/items/stacks/wrap.dm @@ -102,13 +102,6 @@ /obj/item/delivery/can_be_package_wrapped() return FALSE -/obj/item/stack/package_wrap/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user) - if(isitem(storage_holder)) - // Don't insert if the target can be wrapped - var/obj/item/item = storage_holder - return !item.can_be_package_wrapped() - return TRUE - /obj/item/stack/package_wrap/interact_with_atom(obj/interacting_with, mob/living/user, list/modifiers) if(!isobj(interacting_with)) return NONE @@ -118,6 +111,8 @@ if(isitem(interacting_with)) var/obj/item/item = interacting_with if(!item.can_be_package_wrapped()) + if(SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE // put it in the bag instead of yelling balloon_alert(user, "can't be wrapped!") return ITEM_INTERACT_BLOCKING if(user.is_holding(item)) diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index 269a6699d3e..2a2affef2cb 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -155,13 +155,13 @@ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED) listeningTo = null -/obj/item/storage/bag/ore/storage_insert_on_interacted_with(datum/storage, obj/item/inserted, mob/living/user) - if(istype(inserted, /obj/item/boulder)) - to_chat(user, span_warning("You can't fit [inserted] into [src]. \ +/obj/item/storage/bag/ore/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/boulder)) + to_chat(user, span_warning("You can't fit [tool] into [src]. \ Perhaps you should break it down first, or find an ore box.")) - return FALSE + return ITEM_INTERACT_BLOCKING - return TRUE + return NONE /obj/item/storage/bag/ore/proc/pickup_ores(mob/living/user) SIGNAL_HANDLER diff --git a/code/game/objects/items/taster.dm b/code/game/objects/items/taster.dm index 599b78971db..cdd67ceae56 100644 --- a/code/game/objects/items/taster.dm +++ b/code/game/objects/items/taster.dm @@ -16,4 +16,4 @@ else var/message = interacting_with.reagents.generate_taste_message(user, taste_sensitivity) to_chat(user, span_notice("[src] tastes [message] in [interacting_with].")) - return ITEM_INTERACT_SUCCESS + return user.combat_mode ? NONE : ITEM_INTERACT_SUCCESS diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index ea2bd7e97e7..3bfac213ba5 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -888,7 +888,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) user.log_message("[welded ? "welded":"unwelded"] closet [src] with [weapon]", LOG_GAME) update_appearance() - else if(!user.combat_mode) + else if(!user.combat_mode || (weapon.item_flags & NOBLUDGEON)) var/item_is_id = weapon.GetID() if(!item_is_id) return FALSE diff --git a/code/game/objects/structures/deployable_turret.dm b/code/game/objects/structures/deployable_turret.dm index 908d2348db4..6abb14294de 100644 --- a/code/game/objects/structures/deployable_turret.dm +++ b/code/game/objects/structures/deployable_turret.dm @@ -259,11 +259,11 @@ M.attacked_by(src, user) add_fingerprint(user) -/obj/item/gun_control/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) +/obj/item/gun_control/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) var/obj/machinery/deployable_turret/E = user.buckled E.calculated_projectile_vars = calculate_projectile_angle_and_pixel_offsets(user, interacting_with, modifiers) E.direction_track(user, interacting_with) E.checkfire(interacting_with, user) -/obj/item/gun_control/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) +/obj/item/gun_control/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + return ranged_interact_with_atom(interacting_with, user, modifiers) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 8dc8d82ff5f..32e76bb2c83 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -55,6 +55,8 @@ AddElement(/datum/element/give_turf_traits, give_turf_traits) register_context() + ADD_TRAIT(src, TRAIT_COMBAT_MODE_SKIP_INTERACTION, INNATE_TRAIT) + ///Adds the element used to make the object climbable, and also the one that shift the mob buckled to it up. /obj/structure/table/proc/make_climbable() AddElement(/datum/element/climbable) @@ -224,36 +226,24 @@ deconstruct(TRUE) return ITEM_INTERACT_SUCCESS -/obj/structure/table/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) - if(istype(tool, /obj/item/construction/rcd)) - return NONE +// This extends base item interaction because tables default to blocking 99% of interactions +/obj/structure/table/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = ..() + if(.) + return . - var/deck_act_value = NONE if(istype(tool, /obj/item/toy/cards/deck)) - deck_act_value = deck_act(user, tool, modifiers, TRUE) - // Continue to placing if we don't do anything else - if(deck_act_value != NONE) - return deck_act_value - - if(!user.combat_mode) - return table_place_act(user, tool, modifiers) - - return NONE - -/obj/structure/table/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - . = NONE + . = deck_act(user, tool, modifiers, !!LAZYACCESS(modifiers, RIGHT_CLICK)) if(istype(tool, /obj/item/storage/bag/tray)) . = tray_act(user, tool) - else if(istype(tool, /obj/item/toy/cards/deck)) - . = deck_act(user, tool, modifiers, FALSE) else if(istype(tool, /obj/item/riding_offhand)) . = riding_offhand_act(user, tool) // Continue to placing if we don't do anything else - if(. != NONE) + if(.) return . - if(!user.combat_mode) + if(!user.combat_mode || (tool.item_flags & NOBLUDGEON)) return table_place_act(user, tool, modifiers) return NONE @@ -865,6 +855,7 @@ AddElement(/datum/element/climbable) AddElement(/datum/element/elevation, pixel_shift = 12) register_context() + ADD_TRAIT(src, TRAIT_COMBAT_MODE_SKIP_INTERACTION, INNATE_TRAIT) /obj/structure/rack/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) if(isnull(held_item)) @@ -892,8 +883,11 @@ deconstruct(TRUE) return ITEM_INTERACT_SUCCESS -/obj/structure/rack/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if((tool.item_flags & ABSTRACT) || user.combat_mode) +/obj/structure/rack/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = ..() + if(.) + return . + if((tool.item_flags & ABSTRACT) || (user.combat_mode && !(tool.item_flags & NOBLUDGEON))) return NONE if(user.transferItemToLoc(tool, drop_location(), silent = FALSE)) return ITEM_INTERACT_SUCCESS diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index 2d16ea30a69..46c18d85b40 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -71,7 +71,7 @@ oxygentanks++ else full = TRUE - else if(!user.combat_mode) + else if(!user.combat_mode || (I.item_flags & NOBLUDGEON)) balloon_alert(user, "can't insert!") return else diff --git a/code/game/objects/structures/water_structures/sink.dm b/code/game/objects/structures/water_structures/sink.dm index 878dab578a5..1cd3f7d7aaa 100644 --- a/code/game/objects/structures/water_structures/sink.dm +++ b/code/game/objects/structures/water_structures/sink.dm @@ -204,7 +204,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14)) if(O.item_flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand. return - if(!user.combat_mode) + if(!user.combat_mode || (O.item_flags & NOBLUDGEON)) to_chat(user, span_notice("You start washing [O]...")) busy = TRUE if(!do_after(user, 4 SECONDS, target = src)) diff --git a/code/game/objects/structures/water_structures/water_source.dm b/code/game/objects/structures/water_structures/water_source.dm index 9420156d918..a59051f92dd 100644 --- a/code/game/objects/structures/water_structures/water_source.dm +++ b/code/game/objects/structures/water_structures/water_source.dm @@ -114,7 +114,7 @@ attacking_item.use(1) return - if(!user.combat_mode) + if(!user.combat_mode || (attacking_item.item_flags & NOBLUDGEON)) to_chat(user, span_notice("You start washing [attacking_item]...")) busy = TRUE if(!do_after(user, 4 SECONDS, target = src)) diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm index 25bbea66577..5a8f699832e 100644 --- a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm @@ -48,7 +48,7 @@ icon_state = "gizmo_scan" to_chat(user, span_notice("You switch the device to [mode == GIZMO_SCAN? "SCAN": "MARK"] MODE")) -/obj/item/abductor/gizmo/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) +/obj/item/abductor/gizmo/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!ScientistCheck(user)) return ITEM_INTERACT_SKIP_TO_ATTACK // So you slap them with it if(!console) @@ -63,8 +63,10 @@ return ITEM_INTERACT_SUCCESS -/obj/item/abductor/gizmo/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) +/obj/item/abductor/gizmo/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!ismob(interacting_with)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) /obj/item/abductor/gizmo/proc/scan(atom/target, mob/living/user) if(ishuman(target)) @@ -104,15 +106,17 @@ icon_state = "silencer" inhand_icon_state = "gizmo" -/obj/item/abductor/silencer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) +/obj/item/abductor/silencer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!AbductorCheck(user)) return ITEM_INTERACT_SKIP_TO_ATTACK // So you slap them with it radio_off(interacting_with, user) return ITEM_INTERACT_SUCCESS -/obj/item/abductor/silencer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) +/obj/item/abductor/silencer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!ismob(interacting_with)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) /obj/item/abductor/silencer/proc/radio_off(atom/target, mob/living/user) if( !(user in (viewers(7,target))) ) @@ -155,10 +159,12 @@ icon_state = "mind_device_message" to_chat(user, span_notice("You switch the device to [mode == MIND_DEVICE_MESSAGE? "TRANSMISSION": "COMMAND"] MODE")) -/obj/item/abductor/mind_device/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/abductor/mind_device/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!ismob(interacting_with)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/abductor/mind_device/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!ScientistCheck(user)) return ITEM_INTERACT_BLOCKING diff --git a/code/modules/antagonists/heretic/items/labyrinth_handbook.dm b/code/modules/antagonists/heretic/items/labyrinth_handbook.dm index 8555b60f0c3..e4f333260a8 100644 --- a/code/modules/antagonists/heretic/items/labyrinth_handbook.dm +++ b/code/modules/antagonists/heretic/items/labyrinth_handbook.dm @@ -41,10 +41,12 @@ . += span_hypnophrase("Materializes a barrier upon any tile in sight, which only you can pass through. Lasts 8 seconds.") . += span_hypnophrase("It has [uses] uses left.") -/obj/item/heretic_labyrinth_handbook/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/heretic_labyrinth_handbook/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/heretic_labyrinth_handbook/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!IS_HERETIC(user)) if(ishuman(user)) var/mob/living/carbon/human/human_user = user diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm index 42ab0203e5c..ccab7952a9c 100644 --- a/code/modules/antagonists/spy/spy_bounty.dm +++ b/code/modules/antagonists/spy/spy_bounty.dm @@ -132,6 +132,14 @@ /datum/spy_bounty/proc/clean_up_stolen_item(atom/movable/stealing, mob/living/spy) do_sparks(3, FALSE, stealing) + if(isitem(stealing) && stealing.loc == spy) + // get it out of our inventory before we mess with it to prevent any weirdness. + // bypasses nodrop - if you want, add a bespoke check for that higher up the chain + spy.temporarilyRemoveItemFromInventory(stealing, force = TRUE) + // also check for DROPDEL + if(QDELETED(stealing)) + return + // Don't mess with it while it's going away var/had_attack_hand_interaction = stealing.interaction_flags_atom & INTERACT_ATOM_ATTACK_HAND stealing.interaction_flags_atom &= ~INTERACT_ATOM_ATTACK_HAND diff --git a/code/modules/antagonists/spy/spy_uplink.dm b/code/modules/antagonists/spy/spy_uplink.dm index 5de66271fe2..920076f21b4 100644 --- a/code/modules/antagonists/spy/spy_uplink.dm +++ b/code/modules/antagonists/spy/spy_uplink.dm @@ -23,7 +23,7 @@ /datum/component/spy_uplink/RegisterWithParent() RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_attack_self)) - RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK_SECONDARY, PROC_REF(on_pre_attack_secondary)) + RegisterSignal(parent, COMSIG_ITEM_INTERACTING_WITH_ATOM, PROC_REF(on_item_atom_interaction)) RegisterSignal(parent, COMSIG_TABLET_CHECK_DETONATE, PROC_REF(block_pda_bombs)) /datum/component/spy_uplink/UnregisterFromParent() @@ -60,13 +60,15 @@ INVOKE_ASYNC(src, TYPE_PROC_REF(/datum, ui_interact), user) return NONE -/datum/component/spy_uplink/proc/on_pre_attack_secondary(obj/item/source, atom/target, mob/living/user, params) +/datum/component/spy_uplink/proc/on_item_atom_interaction(obj/item/source, mob/living/user, atom/target, list/modifiers) SIGNAL_HANDLER if(!ismovable(target)) return NONE if(!IS_SPY(user)) return NONE + if(SHOULD_SKIP_INTERACTION(target, source, user)) + return NONE if(!try_steal(target, user)) return NONE return COMPONENT_CANCEL_ATTACK_CHAIN @@ -94,7 +96,7 @@ /// Wraps the stealing process in a scanning effect. /datum/component/spy_uplink/proc/start_stealing(atom/movable/stealing, mob/living/spy, datum/spy_bounty/bounty) if(!isturf(stealing.loc) && stealing.loc != spy) - to_chat(spy, span_warning("Your uplinks blinks red: [stealing] cannot be extracted from there.")) + to_chat(spy, span_warning("Your uplink blinks red: [stealing] cannot be extracted from there.")) return FALSE log_combat(spy, stealing, "started stealing", parent, "(spy bounty)") @@ -139,10 +141,10 @@ if(!do_after(spy, bounty.theft_time, stealing, interaction_key = REF(src), hidden = TRUE)) return FALSE if(bounty.claimed) - to_chat(spy, span_warning("Your uplinks blinks red: The bounty for [stealing] has been claimed by another spy!")) + to_chat(spy, span_warning("Your uplink blinks red: The bounty for [stealing] has been claimed by another spy!")) return FALSE if(spy.is_holding(stealing) && !spy.dropItemToGround(stealing)) - to_chat(spy, span_warning("Your uplinks blinks red: [stealing] seems stuck to your hand!")) + to_chat(spy, span_warning("Your uplink blinks red: [stealing] seems stuck to your hand!")) return FALSE var/bounty_key = bounty.get_dupe_protection_key(stealing) diff --git a/code/modules/antagonists/traitor/objectives/steal.dm b/code/modules/antagonists/traitor/objectives/steal.dm index fdcd693fed8..4c697d66d57 100644 --- a/code/modules/antagonists/traitor/objectives/steal.dm +++ b/code/modules/antagonists/traitor/objectives/steal.dm @@ -255,7 +255,7 @@ GLOBAL_DATUM_INIT(steal_item_handler, /datum/objective_item_handler, new()) /obj/item/traitor_bug name = "suspicious device" desc = "It looks dangerous." - item_flags = EXAMINE_SKIP + item_flags = EXAMINE_SKIP|NOBLUDGEON icon = 'icons/obj/antags/syndicate_tools.dmi' icon_state = "bug" @@ -280,7 +280,8 @@ GLOBAL_DATUM_INIT(steal_item_handler, /datum/objective_item_handler, new()) /obj/item/traitor_bug/interact_with_atom(atom/movable/target, mob/living/user, list/modifiers) if(!target_object_type || !ismovable(target)) return NONE - + if(SHOULD_SKIP_INTERACTION(target, src, user)) + return NONE var/result = SEND_SIGNAL(src, COMSIG_TRAITOR_BUG_PRE_PLANTED_OBJECT, target) if(!(result & COMPONENT_FORCE_PLACEMENT)) if(result & COMPONENT_FORCE_FAIL_PLACEMENT || !istype(target, target_object_type)) @@ -315,6 +316,3 @@ GLOBAL_DATUM_INIT(steal_item_handler, /datum/objective_item_handler, new()) anchored = FALSE UnregisterSignal(planted_on, COMSIG_QDELETING) planted_on = null - -/obj/item/traitor_bug/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user) - return !istype(storage_holder, target_object_type) diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm index 9176558c7a1..bf04c535a56 100644 --- a/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/code/modules/antagonists/wizard/equipment/artefact.dm @@ -431,10 +431,10 @@ COMSIG_ITEM_MAGICALLY_CHARGED = PROC_REF(on_magic_charge), ) -/obj/item/runic_vendor_scepter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/runic_vendor_scepter/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/runic_vendor_scepter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(scepter_is_busy_recharging) user.balloon_alert(user, "busy!") return ITEM_INTERACT_BLOCKING diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index f8f5cf22350..eb4df6b6239 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -224,19 +224,19 @@ shade_datum.release_time = world.time on_release_spirits() -/obj/item/soulstone/pre_attack(atom/A, mob/living/user, params) - var/mob/living/basic/shade/occupant = (locate() in src) - var/obj/item/storage/toolbox/mechanical/target_toolbox = A - if(!occupant || !istype(target_toolbox) || target_toolbox.has_soul) - return ..() +/obj/item/soulstone/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + var/mob/living/basic/shade/occupant = locate() in src + var/obj/item/storage/toolbox/mechanical/target_toolbox = interacting_with + if(isnull(occupant) || !istype(target_toolbox) || target_toolbox.has_soul) + return NONE if(theme == THEME_HOLY && IS_CULTIST(user)) hot_potato(user) - return + return ITEM_INTERACT_BLOCKING if(!role_check(user)) user.Unconscious(10 SECONDS) to_chat(user, span_userdanger("Your body is wracked with debilitating pain!")) - return + return ITEM_INTERACT_BLOCKING user.visible_message("[user] holds [src] above [user.p_their()] head and forces it into [target_toolbox] with a flash of light!", \ span_notice("You hold [src] above your head briefly, then force it into [target_toolbox], transferring the [occupant]'s soul!"), ignored_mobs = occupant) @@ -253,6 +253,7 @@ target_toolbox.icon_state = "toolbox_blue_old" target_toolbox.has_soul = TRUE target_toolbox.has_latches = FALSE + return ITEM_INTERACT_SUCCESS ///////////////////////////Transferring to constructs///////////////////////////////////////////////////// /obj/structure/constructshell diff --git a/code/modules/clothing/chameleon/chameleon_scanner.dm b/code/modules/clothing/chameleon/chameleon_scanner.dm index 2ea0958a0cc..8b213e3f816 100644 --- a/code/modules/clothing/chameleon/chameleon_scanner.dm +++ b/code/modules/clothing/chameleon/chameleon_scanner.dm @@ -46,10 +46,14 @@ /obj/item/chameleon_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) return scan_target(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING -/obj/item/chameleon_scanner/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) +/obj/item/chameleon_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) -/obj/item/chameleon_scanner/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) +/obj/item/chameleon_scanner/ranged_interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) + if(!isliving(interacting_with) && !isturf(interacting_with)) + return NONE var/list/scanned_outfit = scan_target(interacting_with, user) if(length(scanned_outfit)) var/datum/outfit/empty_outfit = new() diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm index 4f8d8c74cb1..7110e368dce 100644 --- a/code/modules/detectivework/evidence.dm +++ b/code/modules/detectivework/evidence.dm @@ -7,16 +7,19 @@ icon_state = "evidenceobj" inhand_icon_state = "" w_class = WEIGHT_CLASS_TINY + item_flags = NOBLUDGEON /obj/item/evidencebag/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - if(interacting_with == loc) + if(interacting_with == loc || !isitem(interacting_with) || HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) return NONE - evidencebagEquip(interacting_with, user) - return ITEM_INTERACT_SUCCESS + if(evidencebagEquip(interacting_with, user)) + return ITEM_INTERACT_SUCCESS + return NONE -/obj/item/evidencebag/attackby(obj/item/I, mob/user, params) - if(evidencebagEquip(I, user)) - return 1 +/obj/item/evidencebag/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(evidencebagEquip(tool, user)) + return ITEM_INTERACT_SUCCESS + return NONE /obj/item/evidencebag/Exited(atom/movable/gone, direction) . = ..() @@ -27,7 +30,7 @@ /obj/item/evidencebag/proc/evidencebagEquip(obj/item/I, mob/user) if(!istype(I) || I.anchored) - return + return FALSE if(loc.atom_storage && I.atom_storage) to_chat(user, span_warning("No matter what way you try, you can't get [I] to fit inside [src].")) @@ -43,24 +46,24 @@ if(loc in I.get_all_contents()) // fixes tg #39452, evidence bags could store their own location, causing I to be stored in the bag while being present inworld still, and able to be teleported when removed. to_chat(user, span_warning("You find putting [I] in [src] while it's still inside it quite difficult!")) - return + return TRUE if(I.w_class > WEIGHT_CLASS_NORMAL) to_chat(user, span_warning("[I] won't fit in [src]!")) - return + return TRUE if(contents.len) to_chat(user, span_warning("[src] already has something inside it!")) - return + return TRUE if(!isturf(I.loc)) //If it isn't on the floor. Do some checks to see if it's in our hands or a box. Otherwise give up. if(I.loc.atom_storage) //in a container. I.loc.atom_storage.remove_single(user, I, src) if(!user.is_holding(I) || HAS_TRAIT(I, TRAIT_NODROP)) - return + return TRUE if(QDELETED(I)) - return + return TRUE user.visible_message(span_notice("[user] puts [I] into [src]."), span_notice("You put [I] inside [src]."),\ span_hear("You hear a rustle as someone puts something into a plastic bag.")) @@ -78,7 +81,7 @@ desc = "An evidence bag containing [I]. [I.desc]" I.forceMove(src) update_weight_class(I.w_class) - return 1 + return TRUE /obj/item/evidencebag/attack_self(mob/user) if(contents.len) diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index 57987eda621..002647f7a08 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -74,10 +74,9 @@ // Clear the logs log = list() -/obj/item/detective_scanner/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/living/user) - return !user.combat_mode - /obj/item/detective_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE // lets us put our scanner away without trying to scan the bag safe_scan(user, interacting_with) return ITEM_INTERACT_SUCCESS diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 86fe5178a6c..122378e99e6 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -221,7 +221,7 @@ /obj/item/fishing_rod/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) //this prevent trying to use telekinesis to fish (which would be broken anyway) - if(!user.contains(src)) + if(!user.contains(src) || (user.combat_mode && !isturf(interacting_with)) ||HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) return ..() return ranged_interact_with_atom(interacting_with, user, modifiers) diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm index 4fa586401ff..92687aee8d0 100644 --- a/code/modules/food_and_drinks/machinery/microwave.dm +++ b/code/modules/food_and_drinks/machinery/microwave.dm @@ -381,12 +381,20 @@ if(operating) return NONE - if (item.item_flags & ABSTRACT) + if(item.item_flags & ABSTRACT) + return NONE + + if(dirty >= MAX_MICROWAVE_DIRTINESS) // The microwave is all dirty so can't be used! + if(IS_EDIBLE(item)) + balloon_alert(user, "it's too dirty!") + return ITEM_INTERACT_BLOCKING return NONE if(broken > NOT_BROKEN) - balloon_alert(user, "it's broken!") - return ITEM_INTERACT_BLOCKING + if(IS_EDIBLE(item)) + balloon_alert(user, "it's broken!") + return ITEM_INTERACT_BLOCKING + return NONE if(istype(item, /obj/item/stock_parts/power_store/cell) && cell_powered) var/swapped = FALSE @@ -405,12 +413,10 @@ return ITEM_INTERACT_SUCCESS if(!anchored) - balloon_alert(user, "not secured!") - return ITEM_INTERACT_BLOCKING - - if(dirty >= MAX_MICROWAVE_DIRTINESS) // The microwave is all dirty so can't be used! - balloon_alert(user, "it's too dirty!") - return ITEM_INTERACT_BLOCKING + if(IS_EDIBLE(item)) + balloon_alert(user, "not secured!") + return ITEM_INTERACT_BLOCKING + return NONE if(vampire_charging_capable && istype(item, /obj/item/modular_computer) && ingredients.len > 0) balloon_alert(user, "max 1 device!") diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm index 1dca29b9504..b80a37a0ce6 100644 --- a/code/modules/food_and_drinks/machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/machinery/smartfridge.dm @@ -313,7 +313,7 @@ to_chat(user, span_warning("\The [src]'s magnetic door won't open without power!")) return FALSE - if(!user.combat_mode) + if(!user.combat_mode || (weapon.item_flags & NOBLUDGEON)) to_chat(user, span_warning("\The [src] smartly refuses [weapon].")) return FALSE diff --git a/code/modules/hydroponics/beekeeping/bee_smoker.dm b/code/modules/hydroponics/beekeeping/bee_smoker.dm index 91195dacc84..f3eae4dc41c 100644 --- a/code/modules/hydroponics/beekeeping/bee_smoker.dm +++ b/code/modules/hydroponics/beekeeping/bee_smoker.dm @@ -37,13 +37,17 @@ return TRUE /obj/item/bee_smoker/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!istype(interacting_with, /obj/structure/beebox) && !isturf(interacting_with) && !istype(interacting_with, /mob/living/basic/bee)) + return NONE + + . = ITEM_INTERACT_BLOCKING if(!activated) user.balloon_alert(user, "not activated!") - return ITEM_INTERACT_BLOCKING + return . if(current_herb_fuel < single_use_cost) user.balloon_alert(user, "not enough fuel!") - return ITEM_INTERACT_BLOCKING + return . current_herb_fuel -= single_use_cost playsound(src, 'sound/effects/spray2.ogg', 100, TRUE) @@ -53,16 +57,19 @@ if(friend.flags_1 & HOLOGRAM_1) continue friend.befriend(user) + . = ITEM_INTERACT_SUCCESS if(!istype(interacting_with, /obj/structure/beebox)) - return ITEM_INTERACT_BLOCKING + return . var/obj/structure/beebox/hive = interacting_with for(var/mob/living/bee as anything in hive.bees) if(bee.flags_1 & HOLOGRAM_1) continue bee.befriend(user) - return ITEM_INTERACT_SUCCESS + . = ITEM_INTERACT_SUCCESS + + return . /obj/item/bee_smoker/attackby(obj/item/herb, mob/living/carbon/human/user, list/modifiers) . = ..() diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm index 0c6a1aad71d..4b9b4ae61d1 100644 --- a/code/modules/library/bibles.dm +++ b/code/modules/library/bibles.dm @@ -269,9 +269,6 @@ GLOBAL_LIST_INIT(bibleitemstates, list( playsound(target_mob, SFX_PUNCH, 25, TRUE, -1) log_combat(user, target_mob, "attacked", src) -/obj/item/book/bible/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user) - return !istype(storage_holder, /obj/item/book/bible) - /obj/item/book/bible/interact_with_atom(atom/bible_smacked, mob/living/user, list/modifiers) if(SEND_SIGNAL(bible_smacked, COMSIG_BIBLE_SMACKED, user) & COMSIG_END_BIBLE_CHAIN) return ITEM_INTERACT_SUCCESS diff --git a/code/modules/mining/equipment/monster_organs/monster_organ.dm b/code/modules/mining/equipment/monster_organs/monster_organ.dm index 9b6330f3467..cf6131fa922 100644 --- a/code/modules/mining/equipment/monster_organs/monster_organ.dm +++ b/code/modules/mining/equipment/monster_organs/monster_organ.dm @@ -132,6 +132,9 @@ return ..() /obj/item/organ/internal/monster_core/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!isliving(interacting_with)) + return NONE + try_apply(interacting_with, user) return ITEM_INTERACT_SUCCESS diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm index f0270b66d37..94837a4ae48 100644 --- a/code/modules/mining/lavaland/megafauna_loot.dm +++ b/code/modules/mining/lavaland/megafauna_loot.dm @@ -805,10 +805,12 @@ var/timer = 0 var/static/list/banned_turfs = typecacheof(list(/turf/open/space, /turf/closed)) -/obj/item/lava_staff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) - return interact_with_atom(interacting_with, user, modifiers) - /obj/item/lava_staff/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(interacting_with.atom_storage || SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) + +/obj/item/lava_staff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(timer > world.time) return NONE if(is_type_in_typecache(interacting_with, banned_turfs)) diff --git a/code/modules/mob/living/basic/minebots/minebot_remote_control.dm b/code/modules/mob/living/basic/minebots/minebot_remote_control.dm index 36019409123..adb6d79e541 100644 --- a/code/modules/mob/living/basic/minebots/minebot_remote_control.dm +++ b/code/modules/mob/living/basic/minebots/minebot_remote_control.dm @@ -44,6 +44,8 @@ user.update_mouse_pointer() /obj/item/minebot_remote_control/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE return ranged_interact_with_atom(interacting_with, user, modifiers) /obj/item/minebot_remote_control/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index cbd299e8b58..1faadafe0ec 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -303,22 +303,12 @@ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return ITEM_INTERACT_BLOCKING -/obj/item/mod/control/storage_insert_on_interacted_with(datum/storage, obj/item/inserted, mob/living/user) - // Hack. revisit later - if(istype(inserted, /obj/item/aicard)) - var/obj/item/aicard/ai_card = inserted - if(ai_card.AI) - return FALSE // we want to get an AI assistant, try uploading instead of insertion - if(ai_assistant) - return FALSE // we already have an AI assistant, try withdrawing instead of insertion - return TRUE - // Makes use of tool act to prevent shoving stuff into our internal storage /obj/item/mod/control/tool_act(mob/living/user, obj/item/tool, list/modifiers) if(istype(tool, /obj/item/pai_card)) if(!open) balloon_alert(user, "open the cover first!") - return ITEM_INTERACT_BLOCKING + return NONE // shoves the card in the storage anyways insert_pai(user, tool) return ITEM_INTERACT_SUCCESS if(istype(tool, /obj/item/mod/paint)) diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm index 5f93427f532..2c9845afe4c 100644 --- a/code/modules/mod/mod_core.dm +++ b/code/modules/mod/mod_core.dm @@ -97,7 +97,6 @@ install_cell(cell) RegisterSignal(mod, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(mod, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand)) - RegisterSignal(mod, COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT, PROC_REF(on_mod_storage_insert)) RegisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_mod_interaction)) RegisterSignal(mod, COMSIG_MOD_WEARER_SET, PROC_REF(on_wearer_set)) if(mod.wearer) @@ -109,7 +108,6 @@ UnregisterSignal(mod, list( COMSIG_ATOM_EXAMINE, COMSIG_ATOM_ATTACK_HAND, - COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT, COMSIG_ATOM_ITEM_INTERACTION, COMSIG_MOD_WEARER_SET, )) @@ -212,17 +210,9 @@ cell_to_move.forceMove(drop_location()) user.put_in_hands(cell_to_move) -/obj/item/mod/core/standard/proc/on_mod_storage_insert(datum/source, obj/item/thing, mob/living/user) - SIGNAL_HANDLER - - return replace_cell(thing, user) ? BLOCK_STORAGE_INSERT : NONE - /obj/item/mod/core/standard/proc/on_mod_interaction(datum/source, mob/living/user, obj/item/thing) SIGNAL_HANDLER - if(mod.atom_storage) // handled by the storage signal - return NONE - return item_interaction(user, thing) /obj/item/mod/core/standard/item_interaction(mob/living/user, obj/item/tool, list/modifiers) @@ -323,11 +313,10 @@ /obj/item/mod/core/plasma/install(obj/item/mod/control/mod_unit) . = ..() - RegisterSignal(mod, COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT, PROC_REF(on_mod_storage_insert)) RegisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_mod_interaction)) /obj/item/mod/core/plasma/uninstall() - UnregisterSignal(mod, list(COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT, COMSIG_ATOM_ITEM_INTERACTION)) + UnregisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION) return ..() /obj/item/mod/core/plasma/charge_source() @@ -366,17 +355,9 @@ return "empty" -/obj/item/mod/core/plasma/proc/on_mod_storage_insert(datum/source, obj/item/thing, mob/living/user) - SIGNAL_HANDLER - - return charge_plasma(thing, user) ? BLOCK_STORAGE_INSERT : NONE - /obj/item/mod/core/plasma/proc/on_mod_interaction(datum/source, mob/living/user, obj/item/thing) SIGNAL_HANDLER - if(mod.atom_storage) // handled by the storage signal - return NONE - return item_interaction(thing, user) /obj/item/mod/core/plasma/item_interaction(mob/living/user, obj/item/tool, list/modifiers) diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index 41d9aabb54a..f297d76b9e0 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -56,7 +56,7 @@ icon_state = "[initial(icon_state)]-open" sleep(0.5 SECONDS) icon_state = initial(icon_state) - else if(!user.combat_mode) + else if(!user.combat_mode || (P.item_flags & NOBLUDGEON)) to_chat(user, span_warning("You can't put [P] in [src]!")) else return ..() diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 98f6662f8c5..5d6b10c0568 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -116,9 +116,6 @@ labels_left = initial(labels_left) //Yes, it's capped at its initial value return ITEM_INTERACT_SUCCESS -/obj/item/hand_labeler/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user) - return !mode - /obj/item/hand_labeler/borg name = "cyborg-hand labeler" diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index 5814750dab1..eb29ca4b8b5 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -129,6 +129,10 @@ return TRUE /obj/item/camera/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + // Always skip on storage and tables + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE + return ranged_interact_with_atom(interacting_with, user, modifiers) /obj/item/camera/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index b288fd6aa87..90aed008763 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -504,6 +504,8 @@ ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT) /obj/item/turret_control/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE return ranged_interact_with_atom(interacting_with, user, modifiers) /obj/item/turret_control/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) diff --git a/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm b/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm index 21f23a209b6..8071887f852 100644 --- a/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm +++ b/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm @@ -100,18 +100,18 @@ /obj/item/storage/portable_chem_mixer/ex_act(severity, target) return severity > EXPLODE_LIGHT ? ..() : FALSE -/obj/item/storage/portable_chem_mixer/storage_insert_on_interacted_with(datum/storage, obj/item/weapon, mob/living/user) +/obj/item/storage/portable_chem_mixer/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if (!atom_storage.locked || \ - (weapon.item_flags & ABSTRACT) || \ - (weapon.flags_1 & HOLOGRAM_1) || \ - !is_reagent_container(weapon) || \ - !weapon.is_open_container() \ + (tool.item_flags & ABSTRACT) || \ + (tool.flags_1 & HOLOGRAM_1) || \ + !is_reagent_container(tool) || \ + !tool.is_open_container() \ ) - return TRUE //continue with regular insertion + return NONE // continue with regular storage handling - replace_beaker(user, weapon) + replace_beaker(user, tool) update_appearance() - return FALSE //block insertion cause we handled it ourselves + return ITEM_INTERACT_SUCCESS /** * Replaces the beaker of the portable chemical mixer with another beaker, or simply adds the new beaker if none is in currently @@ -128,7 +128,7 @@ user.put_in_hands(beaker) if(!QDELETED(new_beaker)) - if(!user.transferItemToLoc(new_beaker, src)) + if(!user.transferItemToLoc(new_beaker, src, silent = FALSE)) return beaker = new_beaker diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index 5590be84f43..431e1d66c69 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -142,11 +142,11 @@ /obj/item/reagent_containers/cup/interact_with_atom_secondary(atom/target, mob/living/user, list/modifiers) if(user.combat_mode) - return ITEM_INTERACT_SKIP_TO_ATTACK + return NONE if(!check_allowed_items(target, target_self = TRUE)) return NONE if(!spillable) - return ITEM_INTERACT_BLOCKING + return NONE if(target.is_drainable()) //A dispenser. Transfer FROM it TO us. if(!target.reagents.total_volume) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 4b6ee9c8c78..2149b2fd4c4 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -35,6 +35,9 @@ // (because the desired effect will just work out of the box) if(istype(interacting_with, /obj/structure/sink) || istype(interacting_with, /obj/structure/mop_bucket/janitorialcart) || istype(interacting_with, /obj/machinery/hydroponics)) return NONE + // Always skip on storage and tables + if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION)) + return NONE return try_spray(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING diff --git a/code/modules/recycling/conveyor.dm b/code/modules/recycling/conveyor.dm index 0d207ed7e11..82ac4646e05 100644 --- a/code/modules/recycling/conveyor.dm +++ b/code/modules/recycling/conveyor.dm @@ -309,7 +309,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) belt_item.use(1) new /obj/machinery/conveyor(target_turf, forwards, id) - else if(!user.combat_mode) + else if(!user.combat_mode || (attacking_item.item_flags & NOBLUDGEON)) user.transferItemToLoc(attacking_item, drop_location()) else return ..() diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 46d023dbfaf..dbcabfab196 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -66,6 +66,7 @@ COMSIG_TURF_RECEIVE_SWEEPED_ITEMS = PROC_REF(ready_for_trash), ) AddElement(/datum/element/connect_loc, loc_connections) + ADD_TRAIT(src, TRAIT_COMBAT_MODE_SKIP_INTERACTION, INNATE_TRAIT) return INITIALIZE_HINT_LATELOAD //we need turfs to have air /// Checks if there a connecting trunk diposal pipe under the disposal @@ -128,7 +129,7 @@ deconstruct() return - if(!user.combat_mode) + if(!user.combat_mode || (I.item_flags & NOBLUDGEON)) if((I.item_flags & ABSTRACT) || !user.temporarilyRemoveItemFromInventory(I)) return place_item_in_disposal(I, user) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 6ec7fca37e3..9a416372a28 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -910,6 +910,8 @@ if(isitem(interacting_with)) var/obj/item/apply_to = interacting_with if(apply_to.slowdown <= 0 || (apply_to.item_flags & IMMUTABLE_SLOW)) + if(interacting_with.atom_storage) + return NONE // lets us put the potion in the bag to_chat(user, span_warning("The [apply_to] can't be made any faster!")) return ITEM_INTERACT_BLOCKING apply_to.slowdown = 0 @@ -920,15 +922,6 @@ qdel(src) return ITEM_INTERACT_SUCCESS -/obj/item/slimepotion/speed/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user) - if(!isitem(storage_holder)) - return TRUE - if(istype(storage_holder, /obj/item/mod/control)) - var/obj/item/mod/control/mod = storage_holder - return mod.slowdown_inactive <= 0 - var/obj/item/storage_item = storage_holder - return storage_item.slowdown <= 0 - /obj/item/slimepotion/fireproof name = "slime chill potion" desc = "A potent chemical mix that will fireproof any article of clothing. Has three uses." diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 6586539b1d0..5d2242a6144 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -767,6 +767,57 @@ name = "emergency disembarkation tool" desc = "For extracting yourself from rough landings." +/datum/storage/pod + max_slots = 14 + max_total_storage = WEIGHT_CLASS_BULKY * 14 + /// If TRUE, we unlock regardless of security level + var/always_unlocked = FALSE + +/datum/storage/pod/open_storage(mob/to_show) + if(isliving(to_show) && SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_RED) + to_chat(to_show, span_warning("The storage unit will only unlock during a Red or Delta security alert.")) + return FALSE + return ..() + +/datum/storage/pod/New(atom/parent, max_slots, max_specific_storage, max_total_storage) + . = ..() + // all of these are a type below what actually spawn with + // (IE all space suits instead of just the emergency ones) + // because an enterprising traitor might be able to hide things, + // like their syndicate toolbox or softsuit. may be fun? + var/static/list/exception_cache = typecacheof(list( + /obj/item/clothing/suit/space, + /obj/item/pickaxe, + /obj/item/storage/toolbox, + )) + src.exception_hold = exception_cache + RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(update_lock)) + update_lock(new_level = SSsecurity_level.get_current_level_as_number()) + +/datum/storage/pod/set_parent(atom/new_parent) + . = ..() + RegisterSignal(parent, COMSIG_ATOM_AFTER_SHUTTLE_MOVE, PROC_REF(pod_launch)) + +/datum/storage/pod/proc/update_lock(datum/source, new_level) + SIGNAL_HANDLER + if(always_unlocked) + return + + locked = (new_level < SEC_LEVEL_RED) ? STORAGE_FULLY_LOCKED : STORAGE_NOT_LOCKED + parent.update_appearance(UPDATE_ICON_STATE) + if(locked) // future todo : make `locked` a setter so this behavior can be built in (avoids exploits) + close_all() + +/datum/storage/pod/proc/pod_launch(datum/source, turf/old_turf) + SIGNAL_HANDLER + // This check is to ignore the movement of the shuttle from the transit level to the station as it is loaded in. + if(old_turf && is_reserved_level(old_turf.z)) + return + // If the pod was launched, the storage will always open. + always_unlocked = TRUE + locked = STORAGE_NOT_LOCKED + parent.update_appearance(UPDATE_ICON_STATE) + /obj/item/storage/pod name = "emergency space suits" desc = "A wall mounted safe containing space suits. Will only open in emergencies." @@ -774,11 +825,11 @@ density = FALSE icon = 'icons/obj/storage/storage.dmi' icon_state = "wall_safe_locked" - var/unlocked = FALSE + storage_type = /datum/storage/pod /obj/item/storage/pod/update_icon_state() . = ..() - icon_state = "wall_safe[unlocked ? "" : "_locked"]" + icon_state = "wall_safe[atom_storage?.locked ? "_locked" : ""]" MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/pod, 32) @@ -798,30 +849,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/pod, 32) new /obj/item/bodybag/environmental(src) new /obj/item/bodybag/environmental(src) -/obj/item/storage/pod/storage_insert_on_interacted_with(datum/storage, obj/item/inserted, mob/living/user) - return can_interact(user) - -/obj/item/storage/pod/attack_hand(mob/user, list/modifiers) - if (can_interact(user)) - atom_storage?.show_contents(user) - return TRUE - -/obj/item/storage/pod/attack_hand_secondary(mob/user, list/modifiers) - if(!can_interact(user)) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN - return ..() - -/obj/item/storage/pod/click_alt(mob/user) - return CLICK_ACTION_SUCCESS - -/obj/item/storage/pod/can_interact(mob/user) - if(!..()) - return FALSE - if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED || unlocked) - return TRUE - to_chat(user, "The storage unit will only unlock during a Red or Delta security alert.") - return FALSE - /obj/docking_port/mobile/emergency/backup name = "backup shuttle" shuttle_id = "backup" diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 4c73b7bd263..5203d7d20e8 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -109,7 +109,7 @@ All ShuttleMove procs go here // Called on atoms after everything has been moved /atom/movable/proc/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) - SEND_SIGNAL(src, COMSIG_ATOM_AFTER_SHUTTLE_MOVE) + SEND_SIGNAL(src, COMSIG_ATOM_AFTER_SHUTTLE_MOVE, oldT) if(light) update_light() if(rotation) @@ -250,17 +250,6 @@ All ShuttleMove procs go here GLOB.deliverybeacons += src GLOB.deliverybeacontags += location -/************************************Item move procs************************************/ - -/obj/item/storage/pod/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) - . = ..() - // If the pod was launched, the storage will always open. The reserved_level check - // ignores the movement of the shuttle from the transit level to - // the station as it is loaded in. - if (oldT && !is_reserved_level(oldT.z)) - unlocked = TRUE - update_appearance() - /************************************Mob move procs************************************/ /mob/onShuttleMove(turf/newT, turf/oldT, list/movement_force, move_dir, obj/docking_port/stationary/old_dock, obj/docking_port/mobile/moving_dock) diff --git a/code/modules/unit_tests/storage.dm b/code/modules/unit_tests/storage.dm index 82ac035ed7e..cadf2261682 100644 --- a/code/modules/unit_tests/storage.dm +++ b/code/modules/unit_tests/storage.dm @@ -20,3 +20,24 @@ small_thing.update_weight_class(WEIGHT_CLASS_BULKY) TEST_ASSERT_NOTEQUAL(small_thing.loc, storage_item, "A small item changed back into bulky size should have ejected from the backpack") + +/datum/unit_test/common_item_inserting + +/datum/unit_test/common_item_inserting/Run() + var/obj/item/storage/backpack/bag = allocate(__IMPLIED_TYPE__, run_loc_floor_bottom_left) + var/mob/living/carbon/human/consistent/dummy = allocate(__IMPLIED_TYPE__, run_loc_floor_bottom_left) + bag.atom_storage.max_slots = INFINITY + bag.atom_storage.max_total_storage = INFINITY + + var/list/common_noncombat_insertion_items = list( + /obj/item/reagent_containers/cup/rag, + /obj/item/soap, + /obj/item/card/emag, + /obj/item/detective_scanner, + ) + + dummy.set_combat_mode(TRUE) + for(var/item_type in common_noncombat_insertion_items) + var/obj/item/item = allocate(item_type, run_loc_floor_bottom_left) + item.melee_attack_chain(dummy, bag) + TEST_ASSERT_EQUAL(item.loc, bag, "[item_type] was unable to be inserted into a backpack on click while off combat mode")