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

🆑 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.
/🆑
This commit is contained in:
MrMelbert
2024-09-12 23:48:19 +02:00
committed by GitHub
parent 76968949d7
commit 8486f2f7e2
65 changed files with 386 additions and 357 deletions
@@ -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
+6
View File
@@ -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)
+13
View File
@@ -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"
+6 -5
View File
@@ -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,
+3
View File
@@ -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)
@@ -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,
+5 -28
View File
@@ -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
+13 -25
View File
@@ -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
+2
View File
@@ -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)
-3
View File
@@ -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
*/
+5 -3
View File
@@ -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))
+8 -4
View File
@@ -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 ..()
+7 -9
View File
@@ -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)
@@ -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]."))
@@ -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))
@@ -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]..."))
@@ -183,12 +183,14 @@
and the wide margin between it and the focus lens could probably house <b>a crystal</b> of some sort.</i>"
/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))
@@ -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
+4 -4
View File
@@ -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)
+6 -4
View File
@@ -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
+5 -3
View File
@@ -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.")
+10 -13
View File
@@ -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)
+5 -3
View File
@@ -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
+2 -7
View File
@@ -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))
+5 -5
View File
@@ -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
+1 -1
View File
@@ -16,4 +16,4 @@
else
var/message = interacting_with.reagents.generate_taste_message(user, taste_sensitivity)
to_chat(user, span_notice("[src] tastes <i>[message]</i> in [interacting_with]."))
return ITEM_INTERACT_SUCCESS
return user.combat_mode ? NONE : ITEM_INTERACT_SUCCESS
@@ -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
@@ -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)
+16 -22
View File
@@ -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
@@ -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
@@ -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))
@@ -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))
@@ -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
@@ -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 <b>[uses]</b> 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
@@ -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
+7 -5
View File
@@ -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)
@@ -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)
@@ -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
@@ -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("<span class='notice'>[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
@@ -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()
+16 -13
View File
@@ -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)
+2 -3
View File
@@ -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
+1 -1
View File
@@ -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)
@@ -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!")
@@ -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
@@ -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)
. = ..()
-3
View File
@@ -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
@@ -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
@@ -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))
@@ -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)
+1 -11
View File
@@ -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))
+1 -20
View File
@@ -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)
+1 -1
View File
@@ -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 ..()
-3
View File
@@ -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"
@@ -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)
@@ -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)
@@ -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
@@ -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)
@@ -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
+1 -1
View File
@@ -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 ..()
+2 -1
View File
@@ -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)
@@ -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."
+53 -26
View File
@@ -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"
+1 -12
View File
@@ -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)
+21
View File
@@ -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")