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")