Spraying items with lube can make them slip out of people's hands (#95224)

## About The Pull Request

If you coat an item with lube, it becomes slippery. Not "clown pda"
slippery but "harder to handle" slippery.

Trying to pick up or use a slippery item has a chance of it falling out
of your hands
(Though there is another chance that you catch it, if you have an empty
hand)

"Use" includes arming grenades or firing guns. So if you try to arm a
lubed grenade it might slip right to your feet!

## Why It's Good For The Game

The clown can coat security's batons in lube. That's basically all I
need to say

## Changelog

🆑 Melbert
add: Spraying items with lube makes them slippery, causing them to fall
out of people's hands who try to pick them up or use them.
/🆑
This commit is contained in:
MrMelbert
2026-02-25 14:38:21 +13:00
committed by GitHub
parent 5f423d78bc
commit 9ceeb187b7
5 changed files with 146 additions and 4 deletions
@@ -7,6 +7,9 @@
/// From /datum/surgery_operation/failure(): (datum/surgery_operation/operation, atom/movable/operating_on, tool)
#define COMSIG_ATOM_SURGERY_FAILED "atom_surgery_step_failed"
/// From /datum/surgery_operation/try_perform(), sent to the tool: (datum/surgery_operation/operation, atom/movable/operating_on, mob/living/surgeon)
#define COMSIG_ITEM_USED_IN_SURGERY "item_used_in_surgery"
/// From /obj/item/shockpaddles/do_help, after the defib do_after is complete, but before any effects are applied: (mob/living/defibber, obj/item/shockpaddles/source)
#define COMSIG_DEFIBRILLATOR_PRE_HELP_ZAP "carbon_being_defibbed"
/// Return to stop default defib handling
+122
View File
@@ -0,0 +1,122 @@
/// Item is slippery - picking it up or using it may cause it to immediately fall out of the user's hands
/datum/component/slippery_item
/// Chance the item will fall on pick up or use
var/fall_chance = 50
/// Chance the mob will catch the item if it falls (if they have an empty hand)
var/fall_catch_chance = 0
/// Message appended to examine when examining the item
var/examine_msg
/// Optional wash flags that removes the effect if washed
var/wash_flags = NONE
/// World.time of the last fall to avoid same tick falls
VAR_PRIVATE/last_fall
/datum/component/slippery_item/Initialize(fall_chance = 50, fall_catch_chance = 0, examine_msg, duration = INFINITY, wash_flags = NONE)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
src.fall_chance = fall_chance
src.fall_catch_chance = fall_catch_chance
src.examine_msg = examine_msg || "It looks very slippery, and may fall out of your hands when you try to use it."
src.wash_flags = wash_flags
if(duration != INFINITY)
QDEL_IN(src, duration)
/datum/component/slippery_item/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_cleaned))
// bunch of generic "item used" triggers, feel free to expand
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(on_preattack))
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(on_afterattack))
RegisterSignal(parent, COMSIG_GUN_TRY_FIRE, PROC_REF(on_tryfire))
RegisterSignal(parent, COMSIG_GRENADE_ARMED, PROC_REF(on_grenade_arm))
RegisterSignal(parent, COMSIG_ITEM_USED_IN_SURGERY, PROC_REF(on_surgery_started))
/datum/component/slippery_item/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_EXAMINE,
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_ITEM_EQUIPPED,
COMSIG_ITEM_PRE_ATTACK,
COMSIG_ITEM_AFTERATTACK,
COMSIG_GUN_TRY_FIRE,
COMSIG_GRENADE_ARMED,
COMSIG_ITEM_USED_IN_SURGERY,
))
/datum/component/slippery_item/proc/on_examine(obj/item/source, mob/living/user, list/examine_list)
SIGNAL_HANDLER
examine_list += examine_msg
/datum/component/slippery_item/proc/on_cleaned(obj/item/source, clean_flags)
SIGNAL_HANDLER
if(wash_flags && (wash_flags & clean_flags))
qdel(src)
return COMPONENT_CLEANED
/datum/component/slippery_item/proc/on_equip(obj/item/source, mob/living/user, slot)
SIGNAL_HANDLER
if(slot & ITEM_SLOT_HANDS)
try_fall(source, user)
/datum/component/slippery_item/proc/on_preattack(obj/item/source, atom/target, mob/living/user, ...)
SIGNAL_HANDLER
if(try_fall(source, user))
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/slippery_item/proc/on_afterattack(obj/item/source, atom/target, mob/living/user, ...)
SIGNAL_HANDLER
try_fall(source, user)
/datum/component/slippery_item/proc/on_tryfire(obj/item/source, mob/living/user, ...)
SIGNAL_HANDLER
if(try_fall(source, user))
return COMPONENT_CANCEL_GUN_FIRE
/datum/component/slippery_item/proc/on_grenade_arm(obj/item/source, ...)
SIGNAL_HANDLER
if(isliving(source.loc))
try_fall(source, source.loc)
/datum/component/slippery_item/proc/on_surgery_started(obj/item/source, datum/surgery_operation/surgery, atom/movable/operating_on, mob/living/surgeon)
SIGNAL_HANDLER
if(try_fall(source, surgeon))
return ITEM_INTERACT_BLOCKING
/// Check for falling and handle it if it happens.
/// Returns TRUE if the item fell, FALSE otherwise
/datum/component/slippery_item/proc/try_fall(obj/item/source, mob/living/user)
set waitfor = FALSE
// prevents fall -> catch -> fall -> catch. even though that'd be funny, you wouldn't even be able to see it happening.
if(last_fall == world.time)
return FALSE
if(!prob(fall_chance))
return FALSE
if(source.loc != user || !user.dropItemToGround(source))
return FALSE
if(QDELETED(source))
return FALSE // dropdel
playsound(source, 'sound/misc/slip.ogg', 20, TRUE, SILENCED_SOUND_EXTRARANGE)
source.SpinAnimation(4, 1)
last_fall = world.time
if(prob(fall_catch_chance) && !HAS_TRAIT(user, TRAIT_CLUMSY))
for(var/empty_hand in user.get_empty_held_indexes())
if(empty_hand == user.active_hand_index)
continue
if(user.putItemFromInventoryInHandIfPossible(source, empty_hand))
to_chat(user, span_notice("[source] slips out of your hands - but you manage to catch it, just in time."))
return TRUE
to_chat(user, span_warning("[source] slips out of your hands!"))
return TRUE
@@ -524,10 +524,20 @@
/datum/reagent/lube/expose_turf(turf/open/exposed_turf, reac_volume)
. = ..()
if(!istype(exposed_turf))
return
if(reac_volume >= 1)
exposed_turf.MakeSlippery(lube_kind, 15 SECONDS, min(reac_volume * 2 SECONDS, 120))
if(isopenturf(exposed_turf) && reac_volume >= 1)
exposed_turf.MakeSlippery(lube_kind, 15 SECONDS, min(reac_volume * 2 SECONDS, 12 SECONDS))
/datum/reagent/lube/expose_obj(obj/exposed_obj, reac_volume, methods, show_message)
. = ..()
if(isitem(exposed_obj) && reac_volume >= 1)
exposed_obj.AddComponent( \
/datum/component/slippery_item, \
fall_chance = (lube_kind == TURF_WET_SUPERLUBE ? 80 : 50), \
fall_catch_chance = (lube_kind == TURF_WET_SUPERLUBE ? 10 : 30), \
examine_msg = span_info("It's coated in a slippery substance!"), \
wash_flags = CLEAN_TYPE_HARD_DECAL, \
duration = min(reac_volume * 3 SECONDS, 30 SECONDS), \
)
/datum/reagent/lube/used_on_fish(obj/item/fish/fish)
ADD_TRAIT(fish, TRAIT_FISH_FED_LUBE, type) //required for the lubefish mutation
@@ -837,6 +837,12 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(!check_availability(patient, operating_on, surgeon, tool, operation_args[OPERATION_TARGET_ZONE]))
return ITEM_INTERACT_BLOCKING
if(isitem(tool))
var/obj/item/realtool = tool
var/tool_return = SEND_SIGNAL(realtool, COMSIG_ITEM_USED_IN_SURGERY, src, operating_on, surgeon)
if(tool_return & ITEM_INTERACT_ANY_BLOCKER)
return tool_return
if(!start_operation(operating_on, surgeon, tool, operation_args))
return ITEM_INTERACT_BLOCKING
+1
View File
@@ -1308,6 +1308,7 @@
#include "code\datums\components\slide_under_doors.dm"
#include "code\datums\components\slime_friends.dm"
#include "code\datums\components\slippery.dm"
#include "code\datums\components\slippery_item.dm"
#include "code\datums\components\smooth_tunes.dm"
#include "code\datums\components\soapbox.dm"
#include "code\datums\components\soul_stealer.dm"