mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 21:48:39 +01:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
. = ..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user