mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
Handcuffs can now be used to bind certain items (briefcases, toolboxes, etc.) to your hand. (#93305)
## About The Pull Request Technically, this PR introduces the cuffable_item element and the cuffed_item status effect and their relative code. In more player-friendly terms, this allows the ability to use handcuffs to bind certain items to your hands by right-clicking it with a pair of handcuffs in your active hand. This makes the item unable to be dropped, for better or worse, until you or someone else remove said cuffs. And no, this doesn't conflict with the ability to be handcuffed if you're silly enough to think that. There are more than one way to remove the cuffs. For the player with the item cuffed to their hand, to remove the cuffs they can either click the status alert, or examine the item and click the relative hyperlink. The second option is good to have if for some reason the status alert doesn't show up (too many alerts etc.). For other people, they can remove the cuffs by opening the strip inventory menu (the one you open by click-dragging the sprite of person with the item onto yours). It's an alternative action specific to this status effect (therefore only held items). Until the cuffs are removed, trying to remove the item **directly** will bring you nowhere **because the item is stuck to their hands**, duh. Alternatively you can just chop their arm off. You do what you do. For a list of items that can be bound with cuffs (suggestions welcome): - briefcases - toolboxes - lockboxes - first aid kits - shields (they generally have handles and all. gameplay-wise they already take away one hand slot to use. Using cuffs seals the deal: no swapping items on the go, so no two-handed weapons, but you won't drop the shield until it's broken) - jerrycans (Kryson's suggestion) - soup pots (ditto, kinda weird) - coffee mugs, and the mauna mug (ditto) - buckets - plushes (silly stuff, if you ever want to arrest a plush or test the feature) - pet carriers - mining drills - swords with closed guards (ERT chainsaw-sword, cap's sabre, parsnip sabre, cutlass, e-cutlass...) - crutches and the white cane - baskets - flashlights and lamps (not subtypes like flares, glowsticks and torches) - TTVs - chairs ## Why It's Good For The Game This opens up for some emergent use for handcuffs beside people (or prisoner shoes). Inspired by a scene of some 1998 action movie, where one of the bad guys had the mc guffin briefcase latched to his wrist with a pair of handcuffs. Codewise, it was also a reason to refactor bits of code like handcuffs and screen alerts slightly. On a sidenote, actual sprites for cult/heretic shackles. ## Changelog 🆑 add: You can now bind certain items like briefcases, toolboxes, medkits, shields, jerrycans etc. to your hand with a pair of handcuffs, preventing them from being dropped. You can remove said binds at any time unless incapacitated, and so can others through the strip inventory menu. qol: The appearance of a screen alert now updates if the object it represents (like, an item offered by another player) changes appearance. imageadd: The shadow shackles item (from cult magic and heretic sacrifices) now has its own icon. /🆑
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
///Called on user, from base of /datum/strippable_item/perform_alternate_action() (atom/target, action_key)
|
||||
#define COMSIG_TRY_ALT_ACTION "try_alt_action"
|
||||
#define COMPONENT_CANT_ALT_ACTION (1<<0)
|
||||
///Called on /basic when updating its speed, from base of /mob/living/basic/update_basic_mob_varspeed(): ()
|
||||
#define POST_BASIC_MOB_UPDATE_VARSPEED "post_basic_mob_update_varspeed"
|
||||
///from base of /mob/Login(): ()
|
||||
|
||||
@@ -154,6 +154,19 @@
|
||||
///from base of datum/storage/handle_exit(): (datum/storage/storage)
|
||||
#define COMSIG_ITEM_UNSTORED "item_unstored"
|
||||
|
||||
/**
|
||||
* From base of datum/strippable_item/get_alternate_actions(): (atom/owner, mob/user, list/alt_actions)
|
||||
* As a side note, make sure the strippable item datum (the slot) in question doesn't have too many alternate actions already,
|
||||
* as only up to three are supported at a time (as of september 2025), though, so far only the jumpsuit slot uses all three slots.
|
||||
*
|
||||
* Also make sure to code the alt action and add it to the StripMenu.tsx interface
|
||||
*/
|
||||
#define COMSIG_ITEM_GET_STRIPPABLE_ALT_ACTIONS "item_get_strippable_alt_actions"
|
||||
|
||||
/// From base of datum/strippable_item/perform_alternate_action(): (atom/owner, mob/user, action_key)
|
||||
#define COMSIG_ITEM_STRIPPABLE_ALT_ACTION "item_strippable_alt_action"
|
||||
#define COMPONENT_ALT_ACTION_DONE (1<<0)
|
||||
|
||||
///from base of obj/item/apply_fantasy_bonuses(): (bonus)
|
||||
#define COMSIG_ITEM_APPLY_FANTASY_BONUSES "item_apply_fantasy_bonuses"
|
||||
///from base of obj/item/remove_fantasy_bonuses(): (bonus)
|
||||
|
||||
@@ -107,6 +107,9 @@
|
||||
/// Trait given to you by shapeshifting
|
||||
#define SHAPESHIFT_TRAIT "shapeshift_trait"
|
||||
|
||||
///From the cuffed_item status effect
|
||||
#define CUFFED_ITEM_TRAIT "cuffed_item_trait"
|
||||
|
||||
// unique trait sources, still defines
|
||||
#define EMP_TRAIT "emp_trait"
|
||||
#define STATUE_MUTE "statue"
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
*flicks are forwarded to master
|
||||
*override makes it so the alert is not replaced until cleared by a clear_alert with clear_override, and it's used for hallucinations.
|
||||
*/
|
||||
/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim = FALSE)
|
||||
/mob/proc/throw_alert(category, type, severity, atom/new_master, override = FALSE, timeout_override, no_anim = FALSE)
|
||||
|
||||
if(!category || QDELETED(src))
|
||||
return
|
||||
|
||||
var/datum/weakref/master_ref
|
||||
if(isdatum(new_master))
|
||||
if(isatom(new_master))
|
||||
master_ref = WEAKREF(new_master)
|
||||
var/atom/movable/screen/alert/thealert
|
||||
if(alerts[category])
|
||||
@@ -51,17 +51,9 @@
|
||||
thealert.owner = src
|
||||
|
||||
if(new_master)
|
||||
var/mutable_appearance/master_appearance = new(new_master)
|
||||
master_appearance.appearance_flags = KEEP_TOGETHER
|
||||
master_appearance.layer = FLOAT_LAYER
|
||||
master_appearance.plane = FLOAT_PLANE
|
||||
master_appearance.dir = SOUTH
|
||||
master_appearance.pixel_x = new_master.base_pixel_x
|
||||
master_appearance.pixel_y = new_master.base_pixel_y
|
||||
master_appearance.pixel_z = new_master.base_pixel_z
|
||||
thealert.add_overlay(strip_appearance_underlays(master_appearance))
|
||||
thealert.icon_state = "template" // We'll set the icon to the client's ui pref in reorganize_alerts()
|
||||
thealert.master_ref = master_ref
|
||||
thealert.RegisterSignal(new_master, COMSIG_ATOM_UPDATE_APPEARANCE, TYPE_PROC_REF(/atom/movable/screen/alert, on_master_update_appearance))
|
||||
thealert.update_appearance(UPDATE_OVERLAYS)
|
||||
else
|
||||
thealert.icon_state = "[initial(thealert.icon_state)][severity]"
|
||||
thealert.severity = severity
|
||||
@@ -118,6 +110,9 @@
|
||||
/// Boolean. If TRUE, the Click() proc will attempt to Click() on the master first if there is a master.
|
||||
var/click_master = TRUE
|
||||
|
||||
///If set true, instead of using the default icon file for screen alerts, it will use the hud's ui style
|
||||
var/use_user_hud_icon = FALSE
|
||||
|
||||
/atom/movable/screen/alert/Initialize(mapload, datum/hud/hud_owner)
|
||||
. = ..()
|
||||
if(clickable_glow)
|
||||
@@ -129,10 +124,63 @@
|
||||
if(!QDELETED(src))
|
||||
openToolTip(usr,src,params,title = name,content = desc,theme = alerttooltipstyle)
|
||||
|
||||
|
||||
/atom/movable/screen/alert/MouseExited()
|
||||
closeToolTip(usr)
|
||||
|
||||
/atom/movable/screen/alert/proc/on_master_update_appearance(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
update_appearance(UPDATE_OVERLAYS)
|
||||
|
||||
/atom/movable/screen/alert/update_overlays()
|
||||
. = ..()
|
||||
var/atom/our_master = master_ref?.resolve()
|
||||
if(!istype(our_master) || QDELETED(our_master))
|
||||
return
|
||||
. += add_atom_icon(our_master)
|
||||
|
||||
///Returns a copy of the appearance of the atom, with its base pixel coordinates. Useful for overlays
|
||||
/atom/movable/screen/alert/proc/add_atom_icon(atom/atom)
|
||||
var/mutable_appearance/atom_appearance = new(atom)
|
||||
atom_appearance.appearance_flags = KEEP_TOGETHER
|
||||
atom_appearance.layer = FLOAT_LAYER
|
||||
atom_appearance.plane = FLOAT_PLANE
|
||||
atom_appearance.dir = SOUTH
|
||||
atom_appearance.pixel_x = atom.base_pixel_x
|
||||
atom_appearance.pixel_y = atom.base_pixel_y
|
||||
atom_appearance.pixel_w = atom.base_pixel_w
|
||||
atom_appearance.pixel_z = atom.base_pixel_z
|
||||
return atom_appearance
|
||||
|
||||
/atom/movable/screen/alert/Click(location, control, params)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
..()
|
||||
if(!usr || !usr.client)
|
||||
return FALSE
|
||||
if(usr != owner)
|
||||
return FALSE
|
||||
var/list/modifiers = params2list(params)
|
||||
if(LAZYACCESS(modifiers, SHIFT_CLICK)) // screen objects don't do the normal Click() stuff so we'll cheat
|
||||
to_chat(usr, boxed_message(jointext(examine(usr), "\n")))
|
||||
return FALSE
|
||||
if(!click_master)
|
||||
return TRUE
|
||||
var/datum/our_master = master_ref?.resolve()
|
||||
if(our_master)
|
||||
return usr.client.Click(our_master, location, control, params)
|
||||
|
||||
/atom/movable/screen/alert/Destroy()
|
||||
. = ..()
|
||||
severity = 0
|
||||
master_ref = null
|
||||
owner = null
|
||||
screen_loc = ""
|
||||
|
||||
/atom/movable/screen/alert/examine(mob/user)
|
||||
return list(
|
||||
span_boldnotice(name),
|
||||
span_info(desc),
|
||||
)
|
||||
|
||||
//Gas alerts
|
||||
// Gas alerts are continuously thrown/cleared by:
|
||||
@@ -326,7 +374,8 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
return roller.resist_fire()
|
||||
|
||||
/atom/movable/screen/alert/give // information set when the give alert is made
|
||||
icon_state = "default"
|
||||
icon_state = "template"
|
||||
use_user_hud_icon = TRUE
|
||||
clickable_glow = TRUE
|
||||
/// The offer we're linked to, yes this is suspiciously like a status effect alert
|
||||
var/datum/status_effect/offering/offer
|
||||
@@ -848,6 +897,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
name = "Something interesting is happening!"
|
||||
desc = "This can be clicked on to perform an action."
|
||||
icon_state = "template"
|
||||
use_user_hud_icon = TRUE
|
||||
timeout = 30 SECONDS
|
||||
clickable_glow = TRUE
|
||||
/// Weakref to the target atom to use the action on
|
||||
@@ -875,6 +925,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
/atom/movable/screen/alert/poll_alert
|
||||
name = "Looking for candidates"
|
||||
icon_state = "template"
|
||||
use_user_hud_icon = TRUE
|
||||
timeout = 30 SECONDS
|
||||
ghost_screentips = TRUE
|
||||
/// If true you need to call START_PROCESSING manually
|
||||
@@ -1030,6 +1081,8 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
clickable_glow = TRUE
|
||||
|
||||
/atom/movable/screen/alert/restrained
|
||||
icon_state = "template"
|
||||
use_user_hud_icon = TRUE
|
||||
clickable_glow = TRUE
|
||||
|
||||
/atom/movable/screen/alert/restrained/handcuffed
|
||||
@@ -1128,7 +1181,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
return TRUE
|
||||
for(var/i in 1 to length(alerts))
|
||||
var/atom/movable/screen/alert/alert = alerts[alerts[i]]
|
||||
if(alert.icon_state == "template")
|
||||
if(alert.use_user_hud_icon)
|
||||
alert.icon = ui_style
|
||||
alert.screen_loc = get_ui_alert_placement(i)
|
||||
screenmob.client.screen |= alert
|
||||
@@ -1136,34 +1189,3 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
for(var/viewer in mymob.observers)
|
||||
reorganize_alerts(viewer)
|
||||
return TRUE
|
||||
|
||||
/atom/movable/screen/alert/Click(location, control, params)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
..()
|
||||
if(!usr || !usr.client)
|
||||
return FALSE
|
||||
if(usr != owner)
|
||||
return FALSE
|
||||
var/list/modifiers = params2list(params)
|
||||
if(LAZYACCESS(modifiers, SHIFT_CLICK)) // screen objects don't do the normal Click() stuff so we'll cheat
|
||||
to_chat(usr, boxed_message(jointext(examine(usr), "\n")))
|
||||
return FALSE
|
||||
var/datum/our_master = master_ref?.resolve()
|
||||
if(our_master && click_master)
|
||||
return usr.client.Click(our_master, location, control, params)
|
||||
|
||||
return TRUE
|
||||
|
||||
/atom/movable/screen/alert/Destroy()
|
||||
. = ..()
|
||||
severity = 0
|
||||
master_ref = null
|
||||
owner = null
|
||||
screen_loc = ""
|
||||
|
||||
/atom/movable/screen/alert/examine(mob/user)
|
||||
return list(
|
||||
span_boldnotice(name),
|
||||
span_info(desc),
|
||||
)
|
||||
|
||||
@@ -158,5 +158,7 @@
|
||||
/atom/movable/screen/alert/aura_healing
|
||||
name = "Aura Healing"
|
||||
icon_state = "template"
|
||||
use_user_hud_icon = TRUE
|
||||
clickable_glow = TRUE
|
||||
|
||||
#undef HEAL_EFFECT_COOLDOWN
|
||||
|
||||
69
code/datums/elements/cuffable_item.dm
Normal file
69
code/datums/elements/cuffable_item.dm
Normal file
@@ -0,0 +1,69 @@
|
||||
///This element allows the item it's attached to be bound to oneself's arm with a pair of handcuffs (sold separately). Borgs need not to apply
|
||||
/datum/element/cuffable_item
|
||||
|
||||
/datum/element/cuffable_item/Attach(datum/target)
|
||||
. = ..()
|
||||
|
||||
if(!isitem(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
RegisterSignal(target, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examine_more))
|
||||
RegisterSignal(target, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(item_interaction))
|
||||
|
||||
var/atom/atom_target = target
|
||||
atom_target.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
|
||||
RegisterSignal(atom_target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
|
||||
|
||||
///Tell the player about the interaction if they examine the item twice.
|
||||
/datum/element/cuffable_item/proc/on_examine_more(obj/item/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(length(user.held_items) < 0 || iscyborg(user) || source.anchored)
|
||||
return
|
||||
examine_list += span_smallnotice("You could bind [source.p_they()] to your wrist with a pair of handcuffs...")
|
||||
|
||||
///Give context to players holding a pair of handcuffs when hovering the item
|
||||
/datum/element/cuffable_item/proc/on_requesting_context_from_item(datum/source, list/context, obj/item/held_item, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (!istype(held_item, /obj/item/restraints/handcuffs))
|
||||
return NONE
|
||||
var/obj/item/restraints/handcuffs/cuffs = held_item
|
||||
if(!cuffs.used)
|
||||
context[SCREENTIP_CONTEXT_RMB] = "Cuff to your wrist"
|
||||
return CONTEXTUAL_SCREENTIP_SET
|
||||
|
||||
/datum/element/cuffable_item/proc/item_interaction(obj/item/source, mob/living/user, obj/item/tool, modifiers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!istype(tool, /obj/item/restraints/handcuffs) || iscyborg(user) || source.anchored || !user.CanReach(source))
|
||||
return NONE
|
||||
|
||||
INVOKE_ASYNC(src, PROC_REF(apply_cuffs), source, user, tool)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
///The proc responsible for adding the status effect to the player and all...
|
||||
/datum/element/cuffable_item/proc/apply_cuffs(obj/item/source, mob/living/user, obj/item/restraints/handcuffs/cuffs)
|
||||
if(cuffs.used || DOING_INTERACTION_WITH_TARGET(user, source))
|
||||
return
|
||||
|
||||
if(HAS_TRAIT_FROM(source, TRAIT_NODROP, CUFFED_ITEM_TRAIT))
|
||||
to_chat(user, span_warning("[source] is already cuffed to your wrist!"))
|
||||
return
|
||||
|
||||
if(cuffs.handcuffs_clumsiness_check(user))
|
||||
return
|
||||
|
||||
source.balloon_alert(user, "cuffing item...")
|
||||
playsound(source, cuffs.cuffsound, 30, TRUE, -2)
|
||||
if(!do_after(user, cuffs.get_handcuff_time(user), source))
|
||||
return
|
||||
|
||||
playsound(source, cuffs.cuffsuccesssound, 30, TRUE, -2)
|
||||
|
||||
if(user.apply_status_effect(/datum/status_effect/cuffed_item, source, cuffs))
|
||||
source.balloon_alert(user, "item cuffed to wrist")
|
||||
return
|
||||
|
||||
source.balloon_alert(user, "couldn't cuff to wrist!")
|
||||
return
|
||||
@@ -49,12 +49,16 @@
|
||||
UnregisterSignal(target, list(COMSIG_ITEM_ATTACK_SECONDARY, COMSIG_ATOM_EXAMINE, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET))
|
||||
return ..()
|
||||
|
||||
/datum/element/cuffsnapping/proc/add_item_context(obj/item/source, list/context, mob/living/carbon/target, mob/living/user)
|
||||
/datum/element/cuffsnapping/proc/add_item_context(obj/item/source, list/context, mob/living/target, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
if(!iscarbon(target) || !target.handcuffed)
|
||||
return NONE
|
||||
context[SCREENTIP_CONTEXT_RMB] = "Cut Restraints"
|
||||
return CONTEXTUAL_SCREENTIP_SET
|
||||
if(iscarbon(target)) //Removing restraints takes precedence
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
if(carbon_target.handcuffed)
|
||||
context[SCREENTIP_CONTEXT_RMB] = "Cut Restraints"
|
||||
return CONTEXTUAL_SCREENTIP_SET
|
||||
if(target.has_status_effect(/datum/status_effect/cuffed_item))
|
||||
context[SCREENTIP_CONTEXT_RMB] = "Remove Binds From Item"
|
||||
return CONTEXTUAL_SCREENTIP_SET
|
||||
|
||||
///signal called on parent being examined
|
||||
/datum/element/cuffsnapping/proc/on_examine(datum/target, mob/user, list/examine_list)
|
||||
@@ -72,48 +76,74 @@
|
||||
|
||||
examine_list += span_notice(examine_string)
|
||||
|
||||
/datum/element/cuffsnapping/proc/try_cuffsnap_target(obj/item/cutter, mob/living/carbon/target, mob/living/cutter_user, list/modifiers)
|
||||
///Signal called on parent when it right-clicks another mob.
|
||||
/datum/element/cuffsnapping/proc/try_cuffsnap_target(obj/item/cutter, mob/living/target, mob/living/cutter_user, list/modifiers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!istype(target)) //we aren't the kind of mob that can even have cuffs, so we skip.
|
||||
return
|
||||
|
||||
if(!target.handcuffed)
|
||||
return
|
||||
|
||||
var/obj/item/restraints/handcuffs/cuffs = target.handcuffed
|
||||
|
||||
if(!istype(cuffs))
|
||||
return
|
||||
|
||||
if(cuffs.restraint_strength && isnull(src.snap_time_strong))
|
||||
cutter_user.visible_message(span_notice("[cutter_user] tries to cut through [target]'s restraints with [cutter], but fails!"))
|
||||
playsound(source = get_turf(cutter), soundin = cutter.usesound ? cutter.usesound : cutter.hitsound, vol = cutter.get_clamped_volume(), vary = TRUE)
|
||||
return COMPONENT_SKIP_ATTACK
|
||||
|
||||
else if(isnull(src.snap_time_weak))
|
||||
cutter_user.visible_message(span_notice("[cutter_user] tries to cut through [target]'s restraints with [cutter], but fails!"))
|
||||
playsound(source = get_turf(cutter), soundin = cutter.usesound ? cutter.usesound : cutter.hitsound, vol = cutter.get_clamped_volume(), vary = TRUE)
|
||||
return COMPONENT_SKIP_ATTACK
|
||||
|
||||
. = COMPONENT_SKIP_ATTACK
|
||||
|
||||
INVOKE_ASYNC(src, PROC_REF(do_cuffsnap_target), cutter, target, cutter_user, cuffs)
|
||||
|
||||
/datum/element/cuffsnapping/proc/do_cuffsnap_target(obj/item/cutter, mob/living/carbon/target, mob/cutter_user, obj/item/restraints/handcuffs/cuffs)
|
||||
if(LAZYACCESS(cutter_user.do_afters, cutter))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
if(!istype(carbon_target) || !carbon_target.handcuffed)
|
||||
var/datum/status_effect/cuffed_item/cuffed_status = target.has_status_effect(/datum/status_effect/cuffed_item)
|
||||
if(!cuffed_status)
|
||||
return NONE
|
||||
INVOKE_ASYNC(src, PROC_REF(try_cuffsnap_item), cutter, target, cutter_user, cuffed_status.cuffed, cuffed_status.cuffs)
|
||||
return COMPONENT_SKIP_ATTACK
|
||||
|
||||
var/obj/item/restraints/handcuffs/cuffs = carbon_target.handcuffed
|
||||
|
||||
if(!istype(cuffs))
|
||||
return NONE
|
||||
|
||||
if(check_cuffs_strength(carbon_target, target, cutter_user, cuffs, span_notice("[cutter_user] tries to cut through [target]'s restraints with [cutter], but fails!")))
|
||||
INVOKE_ASYNC(src, PROC_REF(do_cuffsnap_target), cutter, target, cutter_user, cuffs)
|
||||
|
||||
return COMPONENT_SKIP_ATTACK
|
||||
|
||||
///Check that the type of restraints can be cut by this element.
|
||||
/datum/element/cuffsnapping/proc/check_cuffs_strength(obj/item/cutter, mob/living/target, mob/living/cutter_user, obj/item/restraints/handcuffs/cuffs, message)
|
||||
if(cuffs.restraint_strength ? snap_time_strong : snap_time_weak)
|
||||
return TRUE
|
||||
cutter_user.visible_message(message)
|
||||
playsound(source = get_turf(cutter), soundin = cutter.usesound || cutter.hitsound, vol = cutter.get_clamped_volume(), vary = TRUE)
|
||||
return FALSE
|
||||
|
||||
///Called when a player tries to remove the cuffs restraining another mob.
|
||||
/datum/element/cuffsnapping/proc/do_cuffsnap_target(obj/item/cutter, mob/living/carbon/target, mob/cutter_user, obj/item/restraints/handcuffs/cuffs)
|
||||
if(LAZYACCESS(cutter_user.do_afters, cutter))
|
||||
return
|
||||
log_combat(cutter_user, target, "cut or tried to cut [target]'s cuffs", cutter)
|
||||
|
||||
var/snap_time = src.snap_time_weak
|
||||
if(cuffs.restraint_strength)
|
||||
snap_time = src.snap_time_strong
|
||||
do_snip_snap(cutter, target, cutter_user, cuffs, span_notice("[cutter_user] cuts [target]'s restraints with [cutter]!"))
|
||||
|
||||
if(snap_time == 0 || do_after(cutter_user, snap_time, target, interaction_key = cutter)) // If 0 just do it. This to bypass the do_after() creating a needless progress bar.
|
||||
cutter_user.do_attack_animation(target, used_item = cutter)
|
||||
cutter_user.visible_message(span_notice("[cutter_user] cuts [target]'s restraints with [cutter]!"))
|
||||
qdel(target.handcuffed)
|
||||
playsound(source = get_turf(cutter), soundin = cutter.usesound ? cutter.usesound : cutter.hitsound, vol = cutter.get_clamped_volume(), vary = TRUE)
|
||||
///Called when a player tries to remove the cuffs binding an item to their owner
|
||||
/datum/element/cuffsnapping/proc/try_cuffsnap_item(obj/item/cutter, mob/living/target, mob/living/cutter_user, obj/item/cuffed, obj/item/restraints/handcuffs/cuffs)
|
||||
if(check_cuffs_strength(cutter, target, cutter_user, cuffs, span_notice("[cutter_user] tries to cut through the restraints binding [cuffed] to [target], but fails!")))
|
||||
return
|
||||
|
||||
return
|
||||
log_combat(cutter_user, target, "cut or tried to cut restraints binding [cuffed] to")
|
||||
|
||||
do_snip_snap(cutter, target, cutter_user, cuffs, span_notice("[cutter_user] cuts the restraints binding [src] to [target] with [cutter]!"))
|
||||
|
||||
///The proc responsible for the very timed action that deletes the cuffs
|
||||
/datum/element/cuffsnapping/proc/do_snip_snap(obj/item/cutter, mob/living/target, mob/cutter_user, obj/item/restraints/handcuffs/cuffs, message)
|
||||
var/snap_time = cuffs.restraint_strength ? snap_time_strong : snap_time_weak
|
||||
|
||||
var/target_was_restrained = FALSE
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
target_was_restrained = carbon_target.handcuffed
|
||||
|
||||
if(snap_time)
|
||||
if(!do_after(cutter_user, snap_time, target, interaction_key = cutter)) // If 0 just do it. This to bypass the do_after() creating a needless progress bar.
|
||||
return
|
||||
if(target_was_restrained) //Removing restraints takes priority over cuffed items. This only applies for carbon mobs, but we need to make sure the restraints are still the same.
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
if(carbon_target.handcuffed != cuffs)
|
||||
return
|
||||
|
||||
cutter_user.do_attack_animation(target, used_item = cutter)
|
||||
cutter_user.visible_message(message)
|
||||
qdel(cuffs)
|
||||
playsound(source = get_turf(cutter), soundin = cutter.usesound || cutter.hitsound, vol = cutter.get_clamped_volume(), vary = TRUE)
|
||||
|
||||
@@ -182,9 +182,14 @@
|
||||
* All string keys in the list must be inside tgui\packages\tgui\interfaces\StripMenu.tsx
|
||||
* You can also return null if there are no alternate actions.
|
||||
*/
|
||||
/datum/strippable_item/proc/get_alternate_actions(atom/source, mob/user)
|
||||
/datum/strippable_item/proc/get_alternate_actions(atom/source, mob/user, obj/item/item)
|
||||
RETURN_TYPE(/list)
|
||||
return null
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
var/list/alt_actions = list()
|
||||
if(item)
|
||||
SEND_SIGNAL(item, COMSIG_ITEM_GET_STRIPPABLE_ALT_ACTIONS, source, user, alt_actions)
|
||||
return alt_actions
|
||||
|
||||
/**
|
||||
* Performs an alternate action on this strippable_item.
|
||||
@@ -193,9 +198,9 @@
|
||||
* - action_key: The key of the alternate action to perform.
|
||||
* Returns FALSE if unable to perform the action; whether it be due to the signal or some other factor.
|
||||
*/
|
||||
/datum/strippable_item/proc/perform_alternate_action(atom/source, mob/user, action_key)
|
||||
/datum/strippable_item/proc/perform_alternate_action(atom/source, mob/user, action_key, obj/item/item)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
if(SEND_SIGNAL(user, COMSIG_TRY_ALT_ACTION, source, action_key) & COMPONENT_CANT_ALT_ACTION)
|
||||
if(item && SEND_SIGNAL(item, COMSIG_ITEM_STRIPPABLE_ALT_ACTION, source, user, action_key) & COMPONENT_ALT_ACTION_DONE)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -375,7 +380,8 @@
|
||||
|
||||
result["icon"] = icon2base64(icon(item.icon, item.icon_state))
|
||||
result["name"] = item.name
|
||||
result["alternate"] = item_data.get_alternate_actions(owner, user)
|
||||
var/list/alt_actions = item_data.get_alternate_actions(owner, user, item)
|
||||
result["alternate"] = length(alt_actions) ? alt_actions : null
|
||||
var/static/list/already_cried = list()
|
||||
if(length(result["alternate"]) > 3 && !(type in already_cried))
|
||||
stack_trace("Too many alternate actions for [type]! Only three are supported at the moment! This will look bad!")
|
||||
@@ -486,16 +492,14 @@
|
||||
return
|
||||
|
||||
var/item = strippable_item.get_item(owner)
|
||||
if (isnull(item))
|
||||
return
|
||||
|
||||
if (!(alt_action in strippable_item.get_alternate_actions(owner, user)))
|
||||
if (!(alt_action in strippable_item.get_alternate_actions(owner, user, item)))
|
||||
return
|
||||
|
||||
LAZYORASSOCLIST(interactions, user, key)
|
||||
|
||||
// Potentially yielding
|
||||
strippable_item.perform_alternate_action(owner, user, alt_action)
|
||||
strippable_item.perform_alternate_action(owner, user, alt_action, item)
|
||||
|
||||
LAZYREMOVEASSOC(interactions, user, key)
|
||||
|
||||
|
||||
165
code/datums/status_effects/cuffed_item.dm
Normal file
165
code/datums/status_effects/cuffed_item.dm
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* The status effect given by the cuffable_item.
|
||||
* It basically binds an item to your arm, basically making it undroppable until the cuffs or item are removed, usually done by one of:
|
||||
* - clicking the status alert
|
||||
* - using the topic hyperlink
|
||||
* - strip menu for others
|
||||
* - alternatively, dismemberment or destroying the item
|
||||
*/
|
||||
/datum/status_effect/cuffed_item
|
||||
id = "cuffed_item"
|
||||
status_type = STATUS_EFFECT_MULTIPLE
|
||||
alert_type = /atom/movable/screen/alert/status_effect/cuffed_item
|
||||
///Reference to the item stuck into the player's hand
|
||||
var/obj/item/cuffed
|
||||
///Reference to the pair of handcuffs used to bind the item
|
||||
var/obj/item/restraints/handcuffs/cuffs
|
||||
|
||||
/datum/status_effect/cuffed_item/on_creation(mob/living/new_owner, obj/item/cuffed, obj/item/restraints/handcuffs/cuffs)
|
||||
src.cuffed = cuffed
|
||||
src.cuffs = cuffs
|
||||
. = ..() //throws the alert and all
|
||||
linked_alert.update_appearance(UPDATE_OVERLAYS)
|
||||
|
||||
/datum/status_effect/cuffed_item/on_apply()
|
||||
if(HAS_TRAIT_FROM(cuffed, TRAIT_NODROP, CUFFED_ITEM_TRAIT))
|
||||
qdel(src)
|
||||
return FALSE
|
||||
owner.temporarilyRemoveItemFromInventory(cuffs, force = TRUE)
|
||||
if(!owner.is_holding(cuffed) && !owner.put_in_hands(cuffed))
|
||||
owner.put_in_hands(cuffs)
|
||||
qdel(src)
|
||||
return FALSE
|
||||
|
||||
ADD_TRAIT(cuffed, TRAIT_NODROP, CUFFED_ITEM_TRAIT)
|
||||
|
||||
RegisterSignals(cuffed, list(COMSIG_ITEM_DROPPED, COMSIG_MOVABLE_MOVED, COMSIG_QDELETING), PROC_REF(on_displaced))
|
||||
RegisterSignal(cuffed, COMSIG_ATOM_UPDATE_APPEARANCE, PROC_REF(on_item_update_appearance))
|
||||
RegisterSignal(cuffed, COMSIG_ATOM_EXAMINE, PROC_REF(cuffed_reminder))
|
||||
RegisterSignal(cuffed, COMSIG_TOPIC, PROC_REF(topic_handler))
|
||||
RegisterSignal(cuffed, COMSIG_ITEM_GET_STRIPPABLE_ALT_ACTIONS, PROC_REF(get_strippable_action))
|
||||
RegisterSignal(cuffed, COMSIG_ITEM_STRIPPABLE_ALT_ACTION, PROC_REF(do_strippable_action))
|
||||
|
||||
RegisterSignals(cuffs, list(COMSIG_ITEM_EQUIPPED, COMSIG_MOVABLE_MOVED, COMSIG_QDELETING), PROC_REF(on_displaced))
|
||||
RegisterSignal(cuffs, COMSIG_ATOM_UPDATE_APPEARANCE, PROC_REF(on_item_update_appearance))
|
||||
|
||||
RegisterSignal(owner, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examine_more))
|
||||
|
||||
owner.log_message("bound [src] to themselves with restraints", LOG_GAME)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/cuffed_item/on_remove()
|
||||
//Prevent possible recursions from these signals
|
||||
UnregisterSignal(cuffed, list(COMSIG_ITEM_DROPPED, COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
|
||||
UnregisterSignal(cuffs, list(COMSIG_ITEM_EQUIPPED, COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
|
||||
|
||||
REMOVE_TRAIT(cuffed, TRAIT_NODROP, CUFFED_ITEM_TRAIT)
|
||||
cuffed = null
|
||||
|
||||
if(!QDELETED(cuffs))
|
||||
cuffs.on_uncuffed(wearer = owner)
|
||||
if(!QDELETED(owner) && cuffs.loc == owner && !(cuffs in owner.get_equipped_items(INCLUDE_POCKETS | INCLUDE_HELD)))
|
||||
cuffs.forceMove(owner.drop_location())
|
||||
cuffs = null
|
||||
|
||||
///Called when someone examines the owner twice, so they can know if someone has a cuffed item
|
||||
/datum/status_effect/cuffed_item/proc/on_examine_more(datum/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
examine_list += span_warning("[cuffed.examine_title(user)] is bound to [owner.p_their()] [owner.get_held_index_name(owner.get_held_index_of_item(cuffed))] by [cuffs.examine_title(user)]")
|
||||
|
||||
///What happens if one of the items is moved away from the mob
|
||||
/datum/status_effect/cuffed_item/proc/on_displaced(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
///Tell the player that the item is stuck to their hands someway. Also another way to trigger the try_remove_cuffs proc.
|
||||
/datum/status_effect/cuffed_item/proc/cuffed_reminder(obj/item/item, mob/user, list/examine_texts)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(user == owner)
|
||||
examine_texts += span_notice("[item.p_Theyre()] cuffed to you by \a [cuffs]. You can <a href='byond://?src=[REF(item)];remove_cuffs_item=1'>remove them</a>.")
|
||||
|
||||
/// This mainly exists as a fallback in the rare case the alert icon is not reachable (too many alerts?). You should be somewhat able to examine items while blind so all good.
|
||||
/datum/status_effect/cuffed_item/proc/topic_handler(atom/source, user, href_list)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(user == owner && href_list["remove_cuffs_item"])
|
||||
INVOKE_ASYNC(src, PROC_REF(try_remove_cuffs), user)
|
||||
|
||||
/datum/status_effect/cuffed_item/proc/get_strippable_action(obj/item/source, atom/owner, mob/user, list/alt_actions)
|
||||
SIGNAL_HANDLER
|
||||
alt_actions += "remove_item_cuffs"
|
||||
|
||||
/datum/status_effect/cuffed_item/proc/do_strippable_action(obj/item/source, atom/owner, mob/user, action_key)
|
||||
SIGNAL_HANDLER
|
||||
if(action_key != "remove_item_cuffs")
|
||||
return NONE
|
||||
if(source != cuffed || !isliving(user))
|
||||
return NONE
|
||||
|
||||
INVOKE_ASYNC(src, PROC_REF(try_remove_cuffs), user)
|
||||
return COMPONENT_ALT_ACTION_DONE
|
||||
|
||||
///The main proc responsible for attempting to remove the hancfuss.
|
||||
/datum/status_effect/cuffed_item/proc/try_remove_cuffs(mob/living/user)
|
||||
|
||||
var/interaction_key = REF(src)
|
||||
if(LAZYACCESS(user.do_afters, interaction_key))
|
||||
return FALSE
|
||||
|
||||
if(!(user.mobility_flags & MOBILITY_USE) || (user != owner && !user.CanReach(owner)))
|
||||
owner.balloon_alert(user, "can't do it right now!")
|
||||
return FALSE
|
||||
|
||||
if(user != owner)
|
||||
owner.visible_message(span_notice("[user] tries to remove [cuffs] binding [cuffed] to [owner]"), span_warning("[user] is trying to remove [cuffs] binding [cuffed] to you."))
|
||||
|
||||
owner.balloon_alert(user, "removing cuffs...")
|
||||
playsound(owner, cuffs.cuffsound, 30, TRUE, -2)
|
||||
if(!do_after(user, cuffs.get_handcuff_time(user) * 1.5, owner, interaction_key = interaction_key) || QDELETED(src))
|
||||
owner.balloon_alert(user, "interrupted!")
|
||||
return FALSE
|
||||
|
||||
if(user != owner)
|
||||
owner.visible_message(span_notice("[user] removes [cuffs] binding [cuffed] to [owner]"), span_warning("[user] removes [cuffs] binding [cuffed] to you."))
|
||||
|
||||
log_combat(user, owner, "removed restraints binding [cuffed] to")
|
||||
|
||||
var/obj/item/restraints/handcuffs/ref_cuffs = cuffs
|
||||
ref_cuffs.forceMove(owner.drop_location()) //This will cause the status effect to delete itself, which unsets the 'cuffs' var
|
||||
user.put_in_hands(ref_cuffs)
|
||||
owner.balloon_alert(user, "cuffs removed from item")
|
||||
|
||||
return TRUE
|
||||
|
||||
///Whenever the appearance of one of either cuffed or cuffs is updated, update the alert appearance
|
||||
/datum/status_effect/cuffed_item/proc/on_item_update_appearance(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
linked_alert.update_appearance(UPDATE_OVERLAYS)
|
||||
|
||||
///The status alert linked to the cuffed_item status effect
|
||||
/atom/movable/screen/alert/status_effect/cuffed_item
|
||||
name = "Cuffed Item"
|
||||
desc = "You've an item firmly cuffed to your arm. You probably won't be accidentally dropping it somewhere anytime soon."
|
||||
icon_state = "template"
|
||||
use_user_hud_icon = TRUE
|
||||
clickable_glow = TRUE
|
||||
click_master = FALSE
|
||||
|
||||
/atom/movable/screen/alert/status_effect/cuffed_item/update_overlays()
|
||||
. = ..()
|
||||
if(!attached_effect)
|
||||
return
|
||||
var/datum/status_effect/cuffed_item/effect = attached_effect
|
||||
. += add_atom_icon(effect.cuffed)
|
||||
var/mutable_appearance/cuffs_appearance = add_atom_icon(effect.cuffs)
|
||||
cuffs_appearance.transform *= 0.8
|
||||
. += cuffs_appearance
|
||||
|
||||
/atom/movable/screen/alert/status_effect/cuffed_item/Click(location, control, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/datum/status_effect/cuffed_item/effect = attached_effect
|
||||
effect?.try_remove_cuffs(owner)
|
||||
@@ -37,9 +37,13 @@
|
||||
var/start_on = FALSE
|
||||
/// When true, painting the flashlight won't change its light color
|
||||
var/ignore_base_color = FALSE
|
||||
/// This simply means if the flashlight can be cuffed to your hand (why?)
|
||||
var/has_closed_handle = TRUE
|
||||
|
||||
/obj/item/flashlight/Initialize(mapload)
|
||||
. = ..()
|
||||
if(has_closed_handle)
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
if(start_on)
|
||||
set_light_on(TRUE)
|
||||
update_brightness()
|
||||
@@ -328,6 +332,7 @@
|
||||
light_range = 2
|
||||
light_power = 0.8
|
||||
light_color = "#CCFFFF"
|
||||
has_closed_handle = FALSE
|
||||
COOLDOWN_DECLARE(holosign_cooldown)
|
||||
|
||||
/obj/item/flashlight/pen/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
@@ -385,6 +390,7 @@
|
||||
light_power = 0.8
|
||||
light_color = "#99ccff"
|
||||
hitsound = 'sound/items/weapons/genhit1.ogg'
|
||||
has_closed_handle = FALSE
|
||||
|
||||
// the desk lamps are a bit special
|
||||
/obj/item/flashlight/lamp
|
||||
@@ -402,6 +408,7 @@
|
||||
obj_flags = CONDUCTS_ELECTRICITY
|
||||
custom_materials = null
|
||||
start_on = TRUE
|
||||
has_closed_handle = FALSE
|
||||
|
||||
// green-shaded desk lamp
|
||||
/obj/item/flashlight/lamp/green
|
||||
@@ -434,6 +441,7 @@
|
||||
grind_results = list(/datum/reagent/sulfur = 15)
|
||||
sound_on = 'sound/items/match_strike.ogg'
|
||||
toggle_context = FALSE
|
||||
has_closed_handle = FALSE
|
||||
/// How many seconds of fuel we have left
|
||||
var/fuel = 0
|
||||
/// Do we randomize the fuel when initialized
|
||||
@@ -770,6 +778,7 @@
|
||||
light_range = 6 //luminosity when on
|
||||
light_color = "#ffff66"
|
||||
light_system = OVERLAY_LIGHT
|
||||
has_closed_handle = FALSE
|
||||
|
||||
/obj/item/flashlight/emp
|
||||
var/emp_max_charges = 4
|
||||
@@ -844,6 +853,7 @@
|
||||
sound_on = 'sound/effects/wounds/crack2.ogg' // the cracking sound isn't just for wounds silly
|
||||
toggle_context = FALSE
|
||||
ignore_base_color = TRUE
|
||||
has_closed_handle = FALSE
|
||||
/// How much max fuel we have
|
||||
var/max_fuel = 0
|
||||
/// How much oxygen gets added upon cracking the stick. Doesn't actually produce a reaction with the fluid but it does allow for bootleg chemical "grenades"
|
||||
@@ -1023,6 +1033,7 @@
|
||||
plane = FLOOR_PLANE
|
||||
anchored = TRUE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
has_closed_handle = FALSE
|
||||
///Boolean that switches when a full color flip ends, so the light can appear in all colors.
|
||||
var/even_cycle = FALSE
|
||||
///Base light_range that can be set on Initialize to use in smooth light range expansions and contractions.
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
/obj/item/transfer_valve/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
RegisterSignal(src, COMSIG_ITEM_FRIED, PROC_REF(on_fried))
|
||||
register_context()
|
||||
register_item_context()
|
||||
|
||||
@@ -55,17 +55,16 @@
|
||||
|
||||
///How long it takes to handcuff someone
|
||||
var/handcuff_time = 4 SECONDS
|
||||
///Multiplier for handcuff time
|
||||
var/handcuff_time_mod = 1
|
||||
///Sound that plays when starting to put handcuffs on someone
|
||||
var/cuffsound = 'sound/items/weapons/handcuffs.ogg'
|
||||
///Sound that plays when restrain is successful
|
||||
var/cuffsuccesssound = 'sound/items/handcuff_finish.ogg'
|
||||
///If set, handcuffs will be destroyed on application and leave behind whatever this is set to.
|
||||
var/trashtype = null
|
||||
/// How strong the cuffs are. Weak cuffs can be broken with wirecutters or boxcutters.
|
||||
var/restraint_strength = HANDCUFFS_TYPE_STRONG
|
||||
|
||||
/// Is this pair of cuff being actually used?
|
||||
var/used = FALSE
|
||||
|
||||
/obj/item/restraints/handcuffs/apply_fantasy_bonuses(bonus)
|
||||
. = ..()
|
||||
handcuff_time = modify_fantasy_variable("handcuff_time", handcuff_time, -bonus * 2, minimum = 0.3 SECONDS)
|
||||
@@ -79,7 +78,7 @@
|
||||
acid = 50
|
||||
|
||||
/obj/item/restraints/handcuffs/attack(mob/living/target_mob, mob/living/user)
|
||||
if(!iscarbon(target_mob))
|
||||
if(!iscarbon(target_mob) || used)
|
||||
return
|
||||
|
||||
attempt_to_cuff(target_mob, user)
|
||||
@@ -90,9 +89,7 @@
|
||||
victim.balloon_alert(user, "can't be handcuffed!")
|
||||
return
|
||||
|
||||
if(iscarbon(user) && (HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))) //Clumsy people have a 50% chance to handcuff themselves instead of their target.
|
||||
to_chat(user, span_warning("Uh... how do those things work?!"))
|
||||
apply_cuffs(user, user)
|
||||
if(handcuffs_clumsiness_check(user))
|
||||
return
|
||||
|
||||
if(!isnull(victim.handcuffed))
|
||||
@@ -114,12 +111,7 @@
|
||||
playsound(loc, cuffsound, 30, TRUE, -2)
|
||||
log_combat(user, victim, "attempted to handcuff")
|
||||
|
||||
if(HAS_TRAIT(user, TRAIT_FAST_CUFFING))
|
||||
handcuff_time_mod = 0.75
|
||||
else
|
||||
handcuff_time_mod = 1
|
||||
|
||||
if(!do_after(user, handcuff_time * handcuff_time_mod, victim, timed_action_flags = IGNORE_SLOWDOWNS) || !victim.canBeHandcuffed())
|
||||
if(!do_after(user, get_handcuff_time(user), victim, timed_action_flags = IGNORE_SLOWDOWNS) || !victim.canBeHandcuffed())
|
||||
victim.balloon_alert(user, "failed to handcuff!")
|
||||
to_chat(user, span_warning("You fail to handcuff [victim]!"))
|
||||
log_combat(user, victim, "failed to handcuff")
|
||||
@@ -136,7 +128,16 @@
|
||||
log_combat(user, victim, "successfully handcuffed")
|
||||
SSblackbox.record_feedback("tally", "handcuffs", 1, type)
|
||||
|
||||
///Return the amount of time the user would spend cuffing someone or something
|
||||
/obj/item/restraints/handcuffs/proc/get_handcuff_time(mob/user)
|
||||
return handcuff_time * (HAS_TRAIT(user, TRAIT_FAST_CUFFING) ? 0.75 : 1)
|
||||
|
||||
/obj/item/restraints/handcuffs/proc/handcuffs_clumsiness_check(mob/user)
|
||||
if(!iscarbon(user) || !HAS_TRAIT(user, TRAIT_CLUMSY) || prob(50)) //Clumsy people have a 50% chance to handcuff themselves instead of their target.
|
||||
return FALSE
|
||||
to_chat(user, span_warning("Uh... how do those things work?!"))
|
||||
apply_cuffs(user, user)
|
||||
return TRUE
|
||||
/**
|
||||
* When called, this instantly puts handcuffs on someone (if actually possible)
|
||||
*
|
||||
@@ -153,16 +154,24 @@
|
||||
return
|
||||
|
||||
var/obj/item/restraints/handcuffs/cuffs = src
|
||||
if(trashtype)
|
||||
cuffs = new trashtype()
|
||||
else if(dispense)
|
||||
if(dispense)
|
||||
cuffs = new type()
|
||||
|
||||
target.equip_to_slot(cuffs, ITEM_SLOT_HANDCUFFED)
|
||||
|
||||
if(trashtype && !dispense)
|
||||
if(dispense)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/restraints/handcuffs/equipped(mob/living/user, slot)
|
||||
. = ..()
|
||||
if(slot == ITEM_SLOT_HANDCUFFED)
|
||||
RegisterSignal(src, COMSIG_ITEM_DROPPED, PROC_REF(on_uncuffed)) //Make sure zipties are no longer usable the next time someone removes them
|
||||
|
||||
/obj/item/restraints/handcuffs/proc/on_uncuffed(datum/source, mob/living/wearer)
|
||||
SIGNAL_HANDLER
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
UnregisterSignal(src, COMSIG_ITEM_DROPPED)
|
||||
|
||||
/**
|
||||
* # Alien handcuffs
|
||||
*
|
||||
@@ -342,10 +351,15 @@
|
||||
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
|
||||
custom_materials = null
|
||||
breakouttime = 45 SECONDS
|
||||
trashtype = /obj/item/restraints/handcuffs/cable/zipties/used
|
||||
color = null
|
||||
cable_color = null
|
||||
|
||||
/obj/item/restraints/handcuffs/cable/zipties/on_uncuffed(datum/source, mob/living/wearer)
|
||||
. = ..()
|
||||
desc = "A pair of broken zipties."
|
||||
icon_state = "cuff_used"
|
||||
used = TRUE
|
||||
|
||||
/**
|
||||
* # Used zipties
|
||||
*
|
||||
@@ -354,9 +368,7 @@
|
||||
/obj/item/restraints/handcuffs/cable/zipties/used
|
||||
desc = "A pair of broken zipties."
|
||||
icon_state = "cuff_used"
|
||||
|
||||
/obj/item/restraints/handcuffs/cable/zipties/used/attack()
|
||||
return
|
||||
used = TRUE
|
||||
|
||||
/**
|
||||
* # Fake Zipties
|
||||
@@ -372,6 +384,21 @@
|
||||
/obj/item/restraints/handcuffs/cable/zipties/fake/used
|
||||
desc = "A pair of broken fake zipties."
|
||||
icon_state = "cuff_used"
|
||||
used = TRUE
|
||||
|
||||
///handcuffs applied by cult magic and heretics sacrifice
|
||||
/obj/item/restraints/handcuffs/cult
|
||||
name = "shadow shackles"
|
||||
desc = "Shackles that bind the wrists with sinister magic."
|
||||
breakouttime = 45 SECONDS
|
||||
icon_state = "cult_shackles"
|
||||
flags_1 = NONE
|
||||
|
||||
/obj/item/restraints/handcuffs/cult/on_uncuffed(datum/source, mob/living/wearer)
|
||||
. = ..()
|
||||
wearer.visible_message(span_danger("[wearer]'s shackles shatter in a discharge of dark magic!"), span_userdanger("Your [src] shatters in a discharge of dark magic!"))
|
||||
qdel(src)
|
||||
|
||||
|
||||
/**
|
||||
* # Generic leg cuffs
|
||||
|
||||
@@ -329,6 +329,10 @@
|
||||
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
|
||||
light_color = COLOR_RED
|
||||
|
||||
/obj/item/melee/energy/sword/pirate/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed sword guard
|
||||
|
||||
/// Energy blades, which are effectively perma-extended energy swords
|
||||
/obj/item/melee/energy/blade
|
||||
name = "energy blade"
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
|
||||
/obj/item/melee/sabre/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed sword guard
|
||||
AddComponent(/datum/component/jousting)
|
||||
//fast and effective, but as a sword, it might damage the results.
|
||||
AddComponent(/datum/component/butchering, \
|
||||
@@ -173,6 +174,7 @@
|
||||
|
||||
/obj/item/melee/parsnip_sabre/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed sword guard
|
||||
AddComponent(/datum/component/jousting)
|
||||
|
||||
/obj/item/melee/parsnip_sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
/obj/item/pet_carrier/Initialize(mapload)
|
||||
. = ..()
|
||||
register_context()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/obj/item/pet_carrier/Destroy()
|
||||
if(occupants.len)
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
AddComponent(/datum/component/squeak, squeak_override)
|
||||
AddElement(/datum/element/bed_tuckable, mapload, 6, -5, 90)
|
||||
AddElement(/datum/element/toy_talk)
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
//have we decided if Pinocchio goes in the blue or pink aisle yet?
|
||||
if(gender == NEUTER)
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
/obj/item/shield/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/disarm_attack)
|
||||
AddElement(/datum/element/cuffable_item) //I mean, it has a closed handle, right?
|
||||
|
||||
/obj/item/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
|
||||
var/effective_block_chance = final_block_chance
|
||||
|
||||
@@ -7,3 +7,6 @@
|
||||
resistance_flags = FLAMMABLE
|
||||
storage_type = /datum/storage/basket
|
||||
|
||||
/obj/item/storage/basket/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
/// The path of the folder that gets spawned in New()
|
||||
var/folder_path = /obj/item/folder
|
||||
|
||||
/obj/item/storage/briefcase/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/obj/item/storage/briefcase/PopulateContents()
|
||||
new /obj/item/pen(src)
|
||||
var/obj/item/folder/folder = new folder_path(src)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
register_context()
|
||||
update_icon_state()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
///screentips for lockboxes
|
||||
/obj/item/storage/lockbox/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
/// Defines damage type of the medkit. General ones stay null. Used for medibot healing bonuses
|
||||
var/damagetype_healed
|
||||
|
||||
/obj/item/storage/medkit/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/obj/item/storage/medkit/regular
|
||||
icon_state = "medkit"
|
||||
desc = "A first aid kit with the ability to heal common types of injuries."
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
latches = "quad_latch" // like winning the lottery, but worse
|
||||
update_appearance()
|
||||
AddElement(/datum/element/falling_hazard, damage = force, wound_bonus = wound_bonus, hardhat_safety = TRUE, crushes = FALSE, impact_sound = hitsound)
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/obj/item/storage/toolbox/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if (user.combat_mode || !user.has_hand_for_held_index(user.get_inactive_hand_index()))
|
||||
|
||||
@@ -173,6 +173,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
throw_range = 5
|
||||
armour_penetration = 35
|
||||
|
||||
/obj/item/claymore/cutlass/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed sword guard
|
||||
|
||||
/obj/item/claymore/cutlass/old
|
||||
name = "old cutlass"
|
||||
desc = parent_type::desc + " This one seems a tad old."
|
||||
@@ -672,6 +676,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
attack_verb_continuous = list("bludgeons", "whacks", "thrashes")
|
||||
attack_verb_simple = list("bludgeon", "whack", "thrash")
|
||||
|
||||
/obj/item/cane/crutch/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/obj/item/cane/crutch/examine(mob/user, thats)
|
||||
. = ..()
|
||||
// tacked on after the cane string
|
||||
@@ -724,6 +732,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
|
||||
/obj/item/cane/white/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
AddComponent( \
|
||||
/datum/component/transforming, \
|
||||
force_on = 7, \
|
||||
|
||||
@@ -428,6 +428,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
|
||||
// What structure type does this chair become when placed?
|
||||
var/obj/structure/chair/origin_type = /obj/structure/chair
|
||||
|
||||
/obj/item/chair/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/obj/item/chair/suicide_act(mob/living/carbon/user)
|
||||
user.visible_message(span_suicide("[user] begins hitting [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
|
||||
playsound(src,hitsound,50,TRUE)
|
||||
@@ -442,25 +446,28 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
|
||||
plant(user)
|
||||
|
||||
/obj/item/chair/proc/plant(mob/user)
|
||||
var/turf/T = get_turf(loc)
|
||||
if(isgroundlessturf(T))
|
||||
var/turf/turf = user.loc
|
||||
if(!istype(turf) || isgroundlessturf(turf))
|
||||
to_chat(user, span_warning("You need ground to plant this on!"))
|
||||
return
|
||||
if(!user.dropItemToGround(src))
|
||||
to_chat(user, span_warning("[src] is stuck to your hand!"))
|
||||
return
|
||||
if(flags_1 & HOLOGRAM_1)
|
||||
to_chat(user, span_notice("You try to place down \the [src], but it fades away!"))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
for(var/obj/A in T)
|
||||
if(istype(A, /obj/structure/chair))
|
||||
for(var/obj/object in turf)
|
||||
if(istype(object, /obj/structure/chair))
|
||||
to_chat(user, span_warning("There is already a chair here!"))
|
||||
return
|
||||
if(A.density && !(A.flags_1 & ON_BORDER_1))
|
||||
if(object.density && !(object.flags_1 & ON_BORDER_1))
|
||||
to_chat(user, span_warning("There is already something here!"))
|
||||
return
|
||||
|
||||
user.visible_message(span_notice("[user] rights \the [src.name]."), span_notice("You right \the [name]."))
|
||||
var/obj/structure/chair/chair = new origin_type(get_turf(loc))
|
||||
var/obj/structure/chair/chair = new origin_type(turf)
|
||||
chair.set_custom_materials(custom_materials)
|
||||
TransferComponents(chair)
|
||||
chair.setDir(user.dir)
|
||||
|
||||
@@ -445,7 +445,7 @@ Return to step 11 of normal process."}
|
||||
span_userdanger("[user] begins shaping an energy field around your hands!"))
|
||||
if(do_after(user, time_to_cuff, carbon_victim) && carbon_victim.canBeHandcuffed())
|
||||
if(!carbon_victim.handcuffed)
|
||||
carbon_victim.set_handcuffed(new /obj/item/restraints/handcuffs/energy/used(carbon_victim))
|
||||
carbon_victim.set_handcuffed(new /obj/item/restraints/handcuffs/energy(carbon_victim))
|
||||
to_chat(user, span_notice("You restrain [carbon_victim]."))
|
||||
log_combat(user, carbon_victim, "handcuffed")
|
||||
else
|
||||
@@ -481,22 +481,16 @@ Return to step 11 of normal process."}
|
||||
name = "hard-light energy field"
|
||||
desc = "A hard-light field restraining the hands."
|
||||
icon_state = "cuff" // Needs sprite
|
||||
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
|
||||
breakouttime = 45 SECONDS
|
||||
trashtype = /obj/item/restraints/handcuffs/energy/used
|
||||
flags_1 = NONE
|
||||
|
||||
/obj/item/restraints/handcuffs/energy/used
|
||||
item_flags = DROPDEL
|
||||
|
||||
/obj/item/restraints/handcuffs/energy/used/dropped(mob/user)
|
||||
user.visible_message(span_danger("[user]'s [name] breaks in a discharge of energy!"), \
|
||||
span_userdanger("[user]'s [name] breaks in a discharge of energy!"))
|
||||
var/datum/effect_system/spark_spread/sparks = new
|
||||
sparks.set_up(4,0,user.loc)
|
||||
sparks.start()
|
||||
/obj/item/restraints/handcuffs/energy/on_uncuffed(datum/source, mob/living/wearer)
|
||||
. = ..()
|
||||
wearer.visible_message(span_danger("[wearer]'s [name] breaks in a discharge of energy!"), span_userdanger("[wearer]'s [name] breaks in a discharge of energy!"))
|
||||
var/datum/effect_system/spark_spread/sparks = new
|
||||
sparks.set_up(4,0,wearer.loc)
|
||||
sparks.start()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/melee/baton/abductor/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -572,7 +572,7 @@
|
||||
span_userdanger("[user] begins shaping dark magic shackles around your wrists!"))
|
||||
if(do_after(user, 3 SECONDS, C))
|
||||
if(!C.handcuffed)
|
||||
C.set_handcuffed(new /obj/item/restraints/handcuffs/energy/cult/used(C))
|
||||
C.equip_to_slot_or_del(new /obj/item/restraints/handcuffs/cult, ITEM_SLOT_HANDCUFFED, indirect_action = TRUE)
|
||||
C.adjust_silence(10 SECONDS)
|
||||
to_chat(user, span_notice("You shackle [C]."))
|
||||
log_combat(user, C, "shackled")
|
||||
@@ -584,19 +584,6 @@
|
||||
else
|
||||
to_chat(user, span_warning("[C] is already bound."))
|
||||
|
||||
|
||||
/obj/item/restraints/handcuffs/energy/cult //For the shackling spell
|
||||
name = "shadow shackles"
|
||||
desc = "Shackles that bind the wrists with sinister magic."
|
||||
trashtype = /obj/item/restraints/handcuffs/energy/used
|
||||
item_flags = DROPDEL
|
||||
|
||||
/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user)
|
||||
user.visible_message(span_danger("[user]'s shackles shatter in a discharge of dark magic!"), \
|
||||
span_userdanger("Your [src] shatters in a discharge of dark magic!"))
|
||||
. = ..()
|
||||
|
||||
|
||||
//Construction: Converts 50 iron to a construct shell, plasteel to runed metal, airlock to brittle runed airlock, a borg to a construct, or borg shell to a construct shell
|
||||
/obj/item/melee/blood_magic/construction
|
||||
name = "Twisting Aura"
|
||||
|
||||
@@ -333,7 +333,7 @@
|
||||
var/turf/destination = get_turf(destination_landmark)
|
||||
|
||||
sac_target.visible_message(span_danger("[sac_target] begins to shudder violenty as dark tendrils begin to drag them into thin air!"))
|
||||
sac_target.set_handcuffed(new /obj/item/restraints/handcuffs/energy/cult(sac_target))
|
||||
sac_target.equip_to_slot_or_del(new /obj/item/restraints/handcuffs/cult, ITEM_SLOT_HANDCUFFED, indirect_action = TRUE)
|
||||
sac_target.dropItemToGround(sac_target.legcuffed, TRUE)
|
||||
|
||||
sac_target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 85, 150)
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
|
||||
/obj/item/reagent_containers/cup/soup_pot/Initialize(mapload, vol)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
RegisterSignal(src, COMSIG_ATOM_REAGENT_EXAMINE, PROC_REF(reagent_special_examine))
|
||||
register_context()
|
||||
|
||||
|
||||
@@ -319,6 +319,10 @@
|
||||
toolspeed = 0.5 //same speed as an active chainsaw
|
||||
chaplain_spawnable = FALSE //prevents being pickable as a chaplain weapon (it has 30 force)
|
||||
|
||||
/obj/item/nullrod/vibro/talking/chainsword/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //Thanks goodness it cannot be selected by chappies
|
||||
|
||||
/// Other Variants
|
||||
/// Not a special category on their own, but usually possess more unique mechanics
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@
|
||||
hitsound = 'sound/items/weapons/drill.ogg'
|
||||
desc = "An electric mining drill for the especially scrawny."
|
||||
|
||||
/obj/item/pickaxe/drill/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed handle
|
||||
|
||||
/obj/item/pickaxe/drill/diamonddrill
|
||||
name = "diamond-tipped mining drill"
|
||||
icon_state = "diamonddrill"
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
key = STRIPPABLE_ITEM_BACK
|
||||
item_slot = ITEM_SLOT_BACK
|
||||
|
||||
/datum/strippable_item/mob_item_slot/back/get_alternate_actions(atom/source, mob/user)
|
||||
return get_strippable_alternate_action_internals(get_item(source), source)
|
||||
/datum/strippable_item/mob_item_slot/back/get_alternate_actions(atom/source, mob/user, obj/item/item)
|
||||
. = ..()
|
||||
. += get_strippable_alternate_action_internals(item, source)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/back/perform_alternate_action(atom/source, mob/user, action_key)
|
||||
/datum/strippable_item/mob_item_slot/back/perform_alternate_action(atom/source, mob/user, action_key, obj/item/item)
|
||||
if(!..())
|
||||
return
|
||||
if(action_key in get_strippable_alternate_action_internals(get_item(source), source))
|
||||
strippable_alternate_action_internals(get_item(source), source, user)
|
||||
if(action_key in get_strippable_alternate_action_internals(item, source))
|
||||
strippable_alternate_action_internals(item, source, user)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/mask
|
||||
key = STRIPPABLE_ITEM_MASK
|
||||
|
||||
@@ -45,25 +45,23 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
|
||||
key = STRIPPABLE_ITEM_JUMPSUIT
|
||||
item_slot = ITEM_SLOT_ICLOTHING
|
||||
|
||||
/datum/strippable_item/mob_item_slot/jumpsuit/get_alternate_actions(atom/source, mob/user)
|
||||
var/obj/item/clothing/under/jumpsuit = get_item(source)
|
||||
/datum/strippable_item/mob_item_slot/jumpsuit/get_alternate_actions(atom/source, mob/user, obj/item/item)
|
||||
. = ..()
|
||||
var/obj/item/clothing/under/jumpsuit = item
|
||||
if (!istype(jumpsuit))
|
||||
return null
|
||||
return
|
||||
|
||||
var/list/actions = list()
|
||||
if(jumpsuit.has_sensor == HAS_SENSORS)
|
||||
actions += "adjust_sensor"
|
||||
. += "adjust_sensor"
|
||||
if(jumpsuit.can_adjust)
|
||||
actions += "adjust_jumpsuit"
|
||||
. += "adjust_jumpsuit"
|
||||
if(length(jumpsuit.attached_accessories))
|
||||
actions += "strip_accessory"
|
||||
. += "strip_accessory"
|
||||
|
||||
return actions
|
||||
|
||||
/datum/strippable_item/mob_item_slot/jumpsuit/perform_alternate_action(atom/source, mob/user, action_key)
|
||||
/datum/strippable_item/mob_item_slot/jumpsuit/perform_alternate_action(atom/source, mob/user, action_key, obj/item/item)
|
||||
if (!..())
|
||||
return
|
||||
var/obj/item/clothing/under/jumpsuit = get_item(source)
|
||||
var/obj/item/clothing/under/jumpsuit = item
|
||||
if (!istype(jumpsuit))
|
||||
return null
|
||||
|
||||
@@ -168,24 +166,25 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
|
||||
key = STRIPPABLE_ITEM_FEET
|
||||
item_slot = ITEM_SLOT_FEET
|
||||
|
||||
/datum/strippable_item/mob_item_slot/feet/get_alternate_actions(atom/source, mob/user)
|
||||
var/obj/item/clothing/shoes/shoes = get_item(source)
|
||||
/datum/strippable_item/mob_item_slot/feet/get_alternate_actions(atom/source, mob/user, obj/item/item)
|
||||
. = ..()
|
||||
var/obj/item/clothing/shoes/shoes = item
|
||||
if (!istype(shoes) || shoes.fastening_type == SHOES_SLIPON)
|
||||
return null
|
||||
return
|
||||
|
||||
switch (shoes.tied)
|
||||
if (SHOES_UNTIED)
|
||||
return list("knot")
|
||||
. += "knot"
|
||||
if (SHOES_TIED)
|
||||
return list("untie")
|
||||
. += "untie"
|
||||
if (SHOES_KNOTTED)
|
||||
return list("unknot")
|
||||
. += "unknot"
|
||||
|
||||
/datum/strippable_item/mob_item_slot/feet/perform_alternate_action(atom/source, mob/user, action_key)
|
||||
/datum/strippable_item/mob_item_slot/feet/perform_alternate_action(atom/source, mob/user, action_key, obj/item/item)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
var/obj/item/clothing/shoes/shoes = get_item(source)
|
||||
var/obj/item/clothing/shoes/shoes = item
|
||||
if (!istype(shoes))
|
||||
return
|
||||
switch(action_key)
|
||||
@@ -198,14 +197,15 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
|
||||
key = STRIPPABLE_ITEM_SUIT_STORAGE
|
||||
item_slot = ITEM_SLOT_SUITSTORE
|
||||
|
||||
/datum/strippable_item/mob_item_slot/suit_storage/get_alternate_actions(atom/source, mob/user)
|
||||
return get_strippable_alternate_action_internals(get_item(source), source)
|
||||
/datum/strippable_item/mob_item_slot/suit_storage/get_alternate_actions(atom/source, mob/user, obj/item/item)
|
||||
. = ..()
|
||||
. += get_strippable_alternate_action_internals(item, source)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/suit_storage/perform_alternate_action(atom/source, mob/user, action_key)
|
||||
/datum/strippable_item/mob_item_slot/suit_storage/perform_alternate_action(atom/source, mob/user, action_key, obj/item/item)
|
||||
if(!..())
|
||||
return
|
||||
if(action_key in get_strippable_alternate_action_internals(get_item(source), source))
|
||||
strippable_alternate_action_internals(get_item(source), source, user)
|
||||
if(action_key in get_strippable_alternate_action_internals(item, source))
|
||||
strippable_alternate_action_internals(item, source, user)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/id
|
||||
key = STRIPPABLE_ITEM_ID
|
||||
@@ -215,14 +215,15 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
|
||||
key = STRIPPABLE_ITEM_BELT
|
||||
item_slot = ITEM_SLOT_BELT
|
||||
|
||||
/datum/strippable_item/mob_item_slot/belt/get_alternate_actions(atom/source, mob/user)
|
||||
return get_strippable_alternate_action_internals(get_item(source), source)
|
||||
/datum/strippable_item/mob_item_slot/belt/get_alternate_actions(atom/source, mob/user, obj/item/item)
|
||||
. = ..()
|
||||
. += get_strippable_alternate_action_internals(item, source)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/belt/perform_alternate_action(atom/source, mob/user, action_key)
|
||||
/datum/strippable_item/mob_item_slot/belt/perform_alternate_action(atom/source, mob/user, action_key, obj/item/item)
|
||||
if (!..())
|
||||
return
|
||||
if(action_key in get_strippable_alternate_action_internals(get_item(source), source))
|
||||
strippable_alternate_action_internals(get_item(source), source, user)
|
||||
if(action_key in get_strippable_alternate_action_internals(item, source))
|
||||
strippable_alternate_action_internals(item, source, user)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/pocket
|
||||
/// Which pocket we're referencing. Used for visible text.
|
||||
|
||||
@@ -331,6 +331,10 @@
|
||||
ITEM_SLOT_DEX_STORAGE
|
||||
)
|
||||
|
||||
/obj/item/reagent_containers/cup/bucket/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/datum/armor/cup_bucket
|
||||
melee = 10
|
||||
fire = 75
|
||||
|
||||
@@ -71,6 +71,10 @@
|
||||
custom_materials = list(/datum/material/gold=HALF_SHEET_MATERIAL_AMOUNT)
|
||||
volume = 150
|
||||
|
||||
/obj/item/reagent_containers/cup/glass/trophy/gold_cup/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed handles
|
||||
|
||||
/obj/item/reagent_containers/cup/glass/trophy/silver_cup
|
||||
name = "silver cup"
|
||||
desc = "Best loser!"
|
||||
@@ -83,6 +87,9 @@
|
||||
custom_materials = list(/datum/material/silver=SMALL_MATERIAL_AMOUNT*8)
|
||||
volume = 100
|
||||
|
||||
/obj/item/reagent_containers/cup/glass/trophy/silver_cup/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item) //closed handle
|
||||
|
||||
/obj/item/reagent_containers/cup/glass/trophy/bronze_cup
|
||||
name = "bronze cup"
|
||||
@@ -167,6 +174,10 @@
|
||||
base_icon_state = "tea"
|
||||
inhand_icon_state = "coffee"
|
||||
|
||||
/obj/item/reagent_containers/cup/glass/mug/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
|
||||
/obj/item/reagent_containers/cup/glass/mug/update_icon_state()
|
||||
icon_state = "[base_icon_state][reagents.total_volume ? null : "_empty"]"
|
||||
return ..()
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
/obj/item/reagent_containers/cup/maunamug/Initialize(mapload, vol)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
cell = new /obj/item/stock_parts/power_store/cell(src)
|
||||
|
||||
/obj/item/reagent_containers/cup/maunamug/get_cell()
|
||||
|
||||
@@ -67,6 +67,11 @@
|
||||
///You can use this var to tone down the strength of the highlight for less shiny types of plastic.
|
||||
var/highlight_strenght = 1.0
|
||||
|
||||
/obj/item/reagent_containers/cup/jerrycan/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/cuffable_item)
|
||||
update_appearance()
|
||||
|
||||
/obj/item/reagent_containers/cup/jerrycan/update_overlays()
|
||||
. = ..()
|
||||
|
||||
@@ -91,10 +96,6 @@
|
||||
if(cap_type)
|
||||
. += mutable_appearance(icon_file, "[base_icon_state]_cap_[cap_type]")
|
||||
|
||||
/obj/item/reagent_containers/cup/jerrycan/Initialize(mapload)
|
||||
. = ..()
|
||||
update_appearance()
|
||||
|
||||
/obj/item/reagent_containers/cup/jerrycan/opaque
|
||||
fill_icon_thresholds = null
|
||||
initial_reagent_flags = parent_type::initial_reagent_flags & ~TRANSPARENT
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -1518,6 +1518,7 @@
|
||||
#include "code\datums\elements\corrupted_organ.dm"
|
||||
#include "code\datums\elements\crackable.dm"
|
||||
#include "code\datums\elements\crusher_loot.dm"
|
||||
#include "code\datums\elements\cuffable_item.dm"
|
||||
#include "code\datums\elements\cuffsnapping.dm"
|
||||
#include "code\datums\elements\cult_eyes.dm"
|
||||
#include "code\datums\elements\cult_halo.dm"
|
||||
@@ -1967,6 +1968,7 @@
|
||||
#include "code\datums\status_effects\_status_effect_helpers.dm"
|
||||
#include "code\datums\status_effects\agent_pinpointer.dm"
|
||||
#include "code\datums\status_effects\buffs.dm"
|
||||
#include "code\datums\status_effects\cuffed_item.dm"
|
||||
#include "code\datums\status_effects\death_sound.dm"
|
||||
#include "code\datums\status_effects\drug_effects.dm"
|
||||
#include "code\datums\status_effects\gas.dm"
|
||||
|
||||
@@ -59,6 +59,11 @@ const ALTERNATE_ACTIONS: Record<string, AlternateAction> = {
|
||||
text: 'Unknot',
|
||||
},
|
||||
|
||||
remove_item_cuffs: {
|
||||
icon: 'handcuffs',
|
||||
text: 'Remove Handcuffs',
|
||||
},
|
||||
|
||||
enable_internals: {
|
||||
icon: 'tg-air-tank',
|
||||
text: 'Enable internals',
|
||||
|
||||
Reference in New Issue
Block a user