Refactors how item actions are handled (#89654)

## About The Pull Request

This PR tackles our piss-poor item action handling. Currently in order
to make an item only have actions when its equipped to a certain slot
you need to override a proc, which I've changed by introducing an
action_slots variable. I've also cleaned up a ton of action code, and
most importantly moved a lot of Trigger effects on items to do_effect,
which allows actions to not call ui_action_click or attack_self on an
item without bypassing IsAvailible and comsigs that parent Trigger has.
This resolves issues like jump boots being usable from your hands, HUDs
being toggleable out of your pockets, etc. Also moved a few actions from
relying on attack_self to individual handling on their side.

This also stops welding masks/hardhats from showing their action while
you hold them, this part of the change is just something I thought
didn't make much sense - you can use their action by using them in-hand,
and flickering on your action bar can be annoying when reshuffling your
backpack.

Closes #89653

## Why It's Good For The Game
Makes action handling significantly less ass, allows us to avoid code
like this
```js
/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, action)
	if(istype(action, /datum/action/item_action/halt))
		halt()
	else
		adjust_visor(user)
```
This commit is contained in:
SmArtKar
2025-03-01 19:02:07 +01:00
committed by Roxy
parent c11b0d5617
commit 589cf0a904
59 changed files with 189 additions and 242 deletions

View File

@@ -27,7 +27,11 @@
. = ..()
if(!.)
return FALSE
if(target)
var/obj/item/item_target = target
item_target.ui_action_click(owner, src)
return do_effect(trigger_flags)
/datum/action/item_action/proc/do_effect(trigger_flags)
if(!target)
return FALSE
var/obj/item/item_target = target
item_target.ui_action_click(owner, src)
return TRUE

View File

@@ -5,3 +5,18 @@
..()
var/obj/item/item_target = target
name = "Adjust [item_target.name]"
/datum/action/item_action/adjust/do_effect(trigger_flags)
if(!isclothing(target))
CRASH("adjust_visor action attempted to trigger on a non-clothing atom [target] ([target?.type]) owned by [owner] ([owner?.type]!")
var/obj/item/clothing/as_clothing = target
as_clothing.adjust_visor(owner)
return TRUE
/datum/action/item_action/adjust_style
name = "Adjust Item Style"
/datum/action/item_action/adjust_style/New(Target)
..()
var/obj/item/item_target = target
name = "Adjust [item_target.name]'s Style"

View File

@@ -6,10 +6,7 @@
background_icon_state = "bg_demon"
overlay_icon_state = "bg_demon_border"
/datum/action/item_action/berserk_mode/Trigger(trigger_flags)
. = ..()
if(!.)
return FALSE
/datum/action/item_action/berserk_mode/do_effect(trigger_flags)
var/obj/item/clothing/head/hooded/berserker/berserk = target
berserk.berserk_mode(owner)
return TRUE

View File

@@ -16,24 +16,25 @@
return ..()
/datum/action/item_action/cult_dagger/Trigger(trigger_flags)
if(target in owner.held_items)
var/obj/item/target_item = target
target_item.attack_self(owner)
return
/datum/action/item_action/cult_dagger/do_effect(trigger_flags)
if(!isliving(owner))
to_chat(owner, span_warning("You lack the necessary living force for this action."))
return FALSE
var/obj/item/target_item = target
var/mob/living/living_owner = owner
if(target in owner.held_items)
target_item.attack_self(owner)
return TRUE
if(owner.can_equip(target_item, ITEM_SLOT_HANDS))
owner.temporarilyRemoveItemFromInventory(target_item)
owner.put_in_hands(target_item)
target_item.attack_self(owner)
return
return TRUE
if(!isliving(owner))
to_chat(owner, span_warning("You lack the necessary living force for this action."))
return
var/mob/living/living_owner = owner
if (living_owner.usable_hands <= 0)
to_chat(living_owner, span_warning("You don't have any usable hands!"))
else
to_chat(living_owner, span_warning("Your hands are full!"))
return FALSE

View File

@@ -12,7 +12,7 @@
COOLDOWN_DECLARE(box_cooldown)
///Handles opening and closing the box.
/datum/action/item_action/agent_box/Trigger(trigger_flags)
/datum/action/item_action/agent_box/do_effect(trigger_flags)
. = ..()
if(!.)
return FALSE
@@ -20,13 +20,13 @@
var/obj/structure/closet/cardboard/agent/box = owner.loc
if(box.open())
owner.playsound_local(box, 'sound/misc/box_deploy.ogg', 50, TRUE)
return
return FALSE
//Box closing from here on out.
if(!isturf(owner.loc)) //Don't let the player use this to escape mechs/welded closets.
to_chat(owner, span_warning("You need more space to activate this implant!"))
return
return FALSE
if(!COOLDOWN_FINISHED(src, box_cooldown))
return
return FALSE
COOLDOWN_START(src, box_cooldown, 10 SECONDS)
var/box = new boxtype(owner.drop_location())
owner.forceMove(box)

View File

@@ -82,16 +82,14 @@
return FALSE
return ..()
/datum/action/item_action/toggle_hud
/datum/action/item_action/organ_action/toggle_hud
name = "Toggle Implant HUD"
desc = "Disables your HUD implant's visuals. You can still access examine information."
/datum/action/item_action/toggle_hud/Trigger(trigger_flags)
. = ..()
if(!.)
return
/datum/action/item_action/organ_action/toggle_hud/do_effect(trigger_flags)
var/obj/item/organ/cyberimp/eyes/hud/hud_implant = target
hud_implant.toggle_hud(owner)
return TRUE
/datum/action/item_action/wheelys
name = "Toggle Wheels"
@@ -122,12 +120,10 @@
name = "Toggle Wearable HUD"
desc = "Toggles your wearable HUD. You can still access examine information while it's off."
/datum/action/item_action/toggle_wearable_hud/Trigger(trigger_flags)
. = ..()
if(!.)
return
/datum/action/item_action/toggle_wearable_hud/do_effect(trigger_flags)
var/obj/item/clothing/glasses/hud/hud_display = target
hud_display.toggle_hud_display(owner)
return TRUE
/datum/action/item_action/toggle_nv
name = "Toggle Night Vision"
@@ -138,7 +134,7 @@
. = ..()
target.AddElement(/datum/element/update_icon_updates_onmob)
/datum/action/item_action/toggle_nv/Trigger(trigger_flags)
/datum/action/item_action/toggle_nv/do_effect(trigger_flags)
if(!istype(target, /obj/item/clothing/glasses))
return ..()
var/obj/item/clothing/glasses/goggles = target
@@ -162,3 +158,4 @@
playsound(goggles, 'sound/machines/click.ogg', 30, TRUE, -3)
holder?.update_sight()
goggles.update_appearance()
return TRUE

View File

@@ -121,6 +121,8 @@
var/list/datum/action/actions
///list of paths of action datums to give to the item on New().
var/list/actions_types
///Slot flags in which this item grants actions. If null, defaults to the item's slot flags (so actions are granted when worn)
var/action_slots = null
//Since any item can now be a piece of clothing, this has to be put here so all items share it.
///This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc.
@@ -805,6 +807,10 @@
/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/action)
if(slot & (ITEM_SLOT_BACKPACK|ITEM_SLOT_LEGCUFFED)) //these aren't true slots, so avoid granting actions there
return FALSE
if(!isnull(action_slots))
return (slot & action_slots)
else if (slot_flags)
return (slot & slot_flags)
return TRUE
/**

View File

@@ -1619,6 +1619,7 @@
trim = /datum/id_trim/chameleon
wildcard_slots = WILDCARD_LIMIT_CHAMELEON_PLUS // SKYRAT EDIT - Original WILDCARD_LIMIT_CHAMELEON
actions_types = list(/datum/action/item_action/chameleon/change/id, /datum/action/item_action/chameleon/change/id_trim)
action_slots = ALL
/// Have we set a custom name and job assignment, or will we use what we're given when we chameleon change?
var/forged = FALSE

View File

@@ -222,10 +222,6 @@
remove_paddles(user)
update_power()
/obj/item/defibrillator/item_action_slot_check(slot, mob/user)
if(slot_flags & slot)
return TRUE
/obj/item/defibrillator/proc/remove_paddles(mob/user) //this fox the bug with the paddles when other player stole you the defib when you have the paddles equiped
if(ismob(paddles.loc))
var/mob/M = paddles.loc
@@ -286,10 +282,6 @@
nocell_state = "defibcompact-nocell"
emagged_state = "defibcompact-emagged"
/obj/item/defibrillator/compact/item_action_slot_check(slot, mob/user)
if(slot & user.getBeltSlot())
return TRUE
/obj/item/defibrillator/compact/loaded/Initialize(mapload)
. = ..()
cell = new(src)

View File

@@ -19,6 +19,7 @@
slot_flags = ITEM_SLOT_BELT
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 0.2)
actions_types = list(/datum/action/item_action/toggle_light)
action_slots = ALL
light_system = OVERLAY_LIGHT_DIRECTIONAL
light_color = COLOR_LIGHT_ORANGE
light_range = 4

View File

@@ -36,10 +36,6 @@
/obj/item/clothing/glasses/sunglasses/spy/ui_action_click(mob/user)
show_to_user(user)
/obj/item/clothing/glasses/sunglasses/spy/item_action_slot_check(slot)
if(slot & ITEM_SLOT_EYES)
return TRUE
/obj/item/clothing/glasses/sunglasses/spy/Destroy()
if(linked_bug)
linked_bug.linked_glasses = null

View File

@@ -234,15 +234,12 @@ effective or pretty fucking useless.
STOP_PROCESSING(SSobj, src)
return ..()
/datum/action/item_action/stealth_mode/Trigger(trigger_flags)
. = ..()
if(!.)
return
/datum/action/item_action/stealth_mode/do_effect(trigger_flags)
if(stealth_engaged)
stealth_off()
else
stealth_on()
return TRUE
/datum/action/item_action/stealth_mode/proc/stealth_on()
animate(owner, alpha = get_alpha(), time = 0.5 SECONDS)
@@ -311,9 +308,6 @@ effective or pretty fucking useless.
attack_verb_simple = list("whip", "lash", "discipline")
actions_types = list(/datum/action/item_action/stealth_mode)
/obj/item/shadowcloak/item_action_slot_check(slot, mob/user)
return slot & slot_flags
/obj/item/shadowcloak/weaker
name = "stealth belt"
desc = "Makes you nigh-invisible to the naked eye for a short period of time. \

View File

@@ -16,6 +16,7 @@
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 // We detonate upon being exploded.
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT
action_slots = ALL
max_integrity = 40
pickup_sound = 'sound/items/handling/grenade/grenade_pick_up.ogg'
drop_sound = 'sound/items/handling/grenade/grenade_drop.ogg'

View File

@@ -14,6 +14,7 @@
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT
action_slots = ALL
force = 10
throwforce = 7
demolition_mod = 0.25

View File

@@ -16,6 +16,7 @@
item_flags = NO_MAT_REDEMPTION | NOBLUDGEON
has_ammobar = TRUE
actions_types = list(/datum/action/item_action/rcd_scan)
action_slots = ALL
drop_sound = 'sound/items/handling/tools/rcd_drop.ogg'
pickup_sound = 'sound/items/handling/tools/rcd_pickup.ogg'
sound_vary = TRUE

View File

@@ -10,6 +10,7 @@
throw_range = 7
resistance_flags = FLAMMABLE
actions_types = list(/datum/action/cooldown/spell/teleport/area_teleport/wizard/scroll)
action_slots = ITEM_SLOT_HANDS
/// Number of uses the scroll gets.
var/uses = 4
@@ -33,9 +34,6 @@
to_chat(cast_on, span_warning("[src] runs out of uses and crumbles to dust!"))
qdel(src)
/obj/item/teleportation_scroll/item_action_slot_check(slot, mob/user)
return (slot & ITEM_SLOT_HANDS)
/obj/item/teleportation_scroll/apprentice
name = "lesser scroll of teleportation"
uses = 1

View File

@@ -62,11 +62,12 @@
/datum/action/item_action/nano_picket_sign
name = "Retext Nano Picket Sign"
/datum/action/item_action/nano_picket_sign/Trigger(trigger_flags)
/datum/action/item_action/nano_picket_sign/do_effect(trigger_flags)
if(!istype(target, /obj/item/picket_sign))
return
return FALSE
var/obj/item/picket_sign/sign = target
sign.retext(owner)
return TRUE
/datum/crafting_recipe/picket_sign
name = "Picket Sign"

View File

@@ -414,6 +414,7 @@
icon_state = "duffel"
inhand_icon_state = "duffel"
actions_types = list(/datum/action/item_action/zipper)
action_slots = ALL
storage_type = /datum/storage/duffel
// How much to slow you down if your bag isn't zipped up
var/zip_slowdown = 1

View File

@@ -668,6 +668,7 @@
w_class = WEIGHT_CLASS_NORMAL
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
actions_types = list(/datum/action/item_action/reload_rebar)
action_slots = ALL
/obj/item/storage/bag/rebar_quiver/syndicate/Initialize(mapload)
. = ..()

View File

@@ -138,6 +138,7 @@
worn_icon_state = "syndicate_holster"
w_class = WEIGHT_CLASS_NORMAL
actions_types = list(/datum/action/item_action/chameleon/change/belt)
action_slots = ALL
/obj/item/storage/belt/holster/chameleon/Initialize(mapload)
. = ..()

View File

@@ -63,10 +63,6 @@
else
REMOVE_TRAIT(user, TRAIT_NOGRAV_ALWAYS_DRIFT, JETPACK_TRAIT)
/obj/item/tank/jetpack/item_action_slot_check(slot)
if(slot & slot_flags)
return TRUE
/obj/item/tank/jetpack/equipped(mob/user, slot, initial)
. = ..()
if(on && !(slot & slot_flags))

View File

@@ -34,6 +34,7 @@
demolition_mod = 1.25
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*5)
actions_types = list(/datum/action/item_action/set_internals)
action_slots = ALL
armor_type = /datum/armor/item_tank
integrity_failure = 0.5
/// If we are in the process of exploding, stops multi explosions

View File

@@ -37,10 +37,6 @@
/obj/item/watertank/ui_action_click(mob/user)
toggle_mister(user)
/obj/item/watertank/item_action_slot_check(slot, mob/user)
if(slot & user.getBackSlot())
return 1
/obj/item/watertank/proc/toggle_mister(mob/living/user)
if(!istype(user))
return
@@ -409,10 +405,6 @@
/obj/item/reagent_containers/chemtank/ui_action_click()
toggle_injection()
/obj/item/reagent_containers/chemtank/item_action_slot_check(slot, mob/user)
if(slot & ITEM_SLOT_BACK)
return 1
/obj/item/reagent_containers/chemtank/proc/toggle_injection()
var/mob/living/carbon/human/user = usr
if(!istype(user))

View File

@@ -80,10 +80,6 @@
human_target.update_worn_oversuit()
update_item_action_buttons()
/obj/item/clothing/suit/armor/abductor/vest/item_action_slot_check(slot, mob/user)
if(slot & ITEM_SLOT_OCLOTHING) //we only give the mob the ability to activate the vest if he's actually wearing it.
return TRUE
/obj/item/clothing/suit/armor/abductor/vest/proc/SetDisguise(datum/icon_snapshot/entry)
disguise = entry

View File

@@ -306,6 +306,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
wound_bonus = FALSE
actions_types = list(/datum/action/item_action/toggle_mode)
action_slots = ALL
cooldown = 0 SECONDS
stamina_damage = 0

View File

@@ -27,10 +27,10 @@
return ..()
/datum/action/item_action/camouflage/Trigger(trigger_flags)
/datum/action/item_action/camouflage/do_effect(trigger_flags)
. = ..()
if(!.)
return FALSE
return
if(cloaking)
remove_cloaking()
@@ -39,8 +39,6 @@
to_chat(owner, span_notice("You activate your camouflage and blend into your surroundings..."))
cloaking = TRUE
return TRUE
/**
* Returns the owner's alpha value to its initial value,
*

View File

@@ -156,11 +156,7 @@
if(!length(target_sword.current_runes))
return FALSE
/datum/action/item_action/rune_shatter/Trigger(trigger_flags)
. = ..()
if(!.)
return
/datum/action/item_action/rune_shatter/do_effect(trigger_flags)
owner.playsound_local(get_turf(owner), 'sound/effects/magic/blind.ogg', 50, TRUE)
var/obj/item/melee/rune_carver/target_sword = target
QDEL_LIST(target_sword.current_runes)

View File

@@ -2,6 +2,7 @@
name = "referee whistle"
desc = "A referee whistle used to call fouls against players."
actions_types = list(/datum/action/innate/timeout)
action_slots = ALL
// should be /datum/action/item_action but it doesn't support InterceptClickOn()
/datum/action/innate/timeout

View File

@@ -31,9 +31,6 @@
if (!active)
. += span_warning("It requires a Bioscrambler Anomaly Core in order to function.")
/obj/item/polymorph_belt/item_action_slot_check(slot, mob/user, datum/action/action)
return slot & ITEM_SLOT_BELT
/obj/item/polymorph_belt/update_icon_state()
icon_state = base_icon_state + (active ? "" : "_inactive")
worn_icon_state = base_icon_state + (active ? "" : "_inactive")

View File

@@ -173,10 +173,7 @@
else
atom_target.icon = initial(picked_item.icon)
/datum/action/item_action/chameleon/change/Trigger(trigger_flags)
if(!IsAvailable(feedback = TRUE))
return FALSE
/datum/action/item_action/chameleon/change/do_effect(trigger_flags)
select_look(owner)
return TRUE

View File

@@ -3,10 +3,7 @@
button_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "random"
/datum/action/item_action/chameleon/drone/randomise/Trigger(trigger_flags)
if(!IsAvailable(feedback = TRUE))
return FALSE
/datum/action/item_action/chameleon/drone/randomise/do_effect(trigger_flags)
for(var/datum/action/item_action/chameleon/change/to_randomize in owner.actions)
to_randomize.random_look()
return TRUE
@@ -28,10 +25,7 @@
/datum/action/item_action/chameleon/drone/togglehatmask/IsAvailable(feedback)
return ..() && isdrone(owner)
/datum/action/item_action/chameleon/drone/togglehatmask/Trigger(trigger_flags)
if(!IsAvailable(feedback = TRUE))
return FALSE
/datum/action/item_action/chameleon/drone/togglehatmask/do_effect(trigger_flags)
var/mob/living/basic/drone/droney = owner
// The drone unEquip() proc sets head to null after dropping

View File

@@ -6,6 +6,7 @@
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_TINY
actions_types = list(/datum/action/item_action/chameleon/change/scanner)
action_slots = ALL
throw_speed = 3
/// Range that we can scan people
var/scan_range = 5

View File

@@ -30,6 +30,7 @@ do { \
can_adjust = FALSE
armor_type = /datum/armor/clothing_under/chameleon
actions_types = list(/datum/action/item_action/chameleon/change/jumpsuit)
action_slots = ALL
/obj/item/clothing/under/chameleon/broken
@@ -56,6 +57,7 @@ do { \
resistance_flags = NONE
armor_type = /datum/armor/suit_chameleon
actions_types = list(/datum/action/item_action/chameleon/change/suit)
action_slots = ALL
/obj/item/clothing/suit/chameleon/Initialize(mapload)
. = ..()
@@ -84,6 +86,7 @@ do { \
resistance_flags = NONE
armor_type = /datum/armor/glasses_chameleon
actions_types = list(/datum/action/item_action/chameleon/change/glasses)
action_slots = ALL
/obj/item/clothing/glasses/chameleon/broken
@@ -110,6 +113,7 @@ do { \
body_parts_covered = HANDS|ARMS
armor_type = /datum/armor/gloves_chameleon
actions_types = list(/datum/action/item_action/chameleon/change/gloves)
action_slots = ALL
clothing_traits = list(TRAIT_FAST_CUFFING)
/obj/item/clothing/gloves/chameleon/broken
@@ -135,6 +139,7 @@ do { \
resistance_flags = NONE
armor_type = /datum/armor/head_chameleon
actions_types = list(/datum/action/item_action/chameleon/change/hat)
action_slots = ALL
/obj/item/clothing/head/chameleon/broken
@@ -144,6 +149,7 @@ do { \
/obj/item/clothing/head/chameleon/drone
actions_types = list(/datum/action/item_action/chameleon/change/hat, /datum/action/item_action/chameleon/drone/togglehatmask, /datum/action/item_action/chameleon/drone/randomise)
action_slots = ALL
item_flags = DROPDEL
// The camohat, I mean, holographic hat projection, is part of the drone itself.
armor_type = /datum/armor/none
@@ -176,6 +182,7 @@ do { \
flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
w_class = WEIGHT_CLASS_SMALL
actions_types = list(/datum/action/item_action/chameleon/change/mask)
action_slots = ALL
/// Is our voice changer enabled or disabled?
var/voice_change = TRUE
@@ -191,6 +198,7 @@ do { \
/obj/item/clothing/mask/chameleon/drone
actions_types = list(/datum/action/item_action/chameleon/change/mask, /datum/action/item_action/chameleon/drone/togglehatmask, /datum/action/item_action/chameleon/drone/randomise)
action_slots = ALL
item_flags = DROPDEL
//Same as the drone chameleon hat, undroppable and no protection
armor_type = /datum/armor/none
@@ -228,6 +236,7 @@ do { \
resistance_flags = NONE
armor_type = /datum/armor/shoes_chameleon
actions_types = list(/datum/action/item_action/chameleon/change/shoes)
action_slots = ALL
/obj/item/clothing/shoes/chameleon/Initialize(mapload)
. = ..()
@@ -253,6 +262,7 @@ do { \
/obj/item/storage/backpack/chameleon
name = "backpack"
actions_types = list(/datum/action/item_action/chameleon/change/backpack)
action_slots = ALL
/obj/item/storage/backpack/chameleon/Initialize(mapload)
. = ..()
@@ -269,6 +279,7 @@ do { \
name = "toolbelt"
desc = "Holds tools."
actions_types = list(/datum/action/item_action/chameleon/change/belt)
action_slots = ALL
/obj/item/storage/belt/chameleon/Initialize(mapload)
. = ..()
@@ -284,6 +295,7 @@ do { \
/obj/item/radio/headset/chameleon
name = "radio headset"
actions_types = list(/datum/action/item_action/chameleon/change/headset)
action_slots = ALL
/obj/item/radio/headset/chameleon/broken
@@ -295,6 +307,7 @@ do { \
/obj/item/modular_computer/pda/chameleon
name = "tablet"
actions_types = list(/datum/action/item_action/chameleon/change/tablet)
action_slots = ALL
/obj/item/modular_computer/pda/chameleon/broken
@@ -305,6 +318,7 @@ do { \
// Cham Stamp
/obj/item/stamp/chameleon
actions_types = list(/datum/action/item_action/chameleon/change/stamp)
action_slots = ALL
/obj/item/stamp/chameleon/broken
@@ -325,6 +339,7 @@ do { \
armor_type = /datum/armor/neck_chameleon
w_class = WEIGHT_CLASS_SMALL
actions_types = list(/datum/action/item_action/chameleon/change/neck)
action_slots = ALL
/obj/item/clothing/neck/chameleon/broken

View File

@@ -130,10 +130,6 @@
fire = 80
acid = 100
/obj/item/clothing/glasses/science/item_action_slot_check(slot)
if(slot & ITEM_SLOT_EYES)
return 1
/obj/item/clothing/glasses/science/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is tightening \the [src]'s straps around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!"))
return OXYLOSS

View File

@@ -16,6 +16,3 @@
. = ..()
AddComponent(/datum/component/scope, range_modifier = 1.2, zoom_method = ZOOM_METHOD_ITEM_ACTION, item_action_type = /datum/action/item_action/hands_free/moth_googles)
AddComponent(/datum/component/adjust_fishing_difficulty, -4)
/obj/item/clothing/head/mothcap/original/item_action_slot_check(slot, mob/user, datum/action/action)
return (slot & ITEM_SLOT_HEAD)

View File

@@ -120,9 +120,6 @@
if (!core_installed)
. += span_warning("It requires a hallucination anomaly core in order to function.")
/obj/item/clothing/head/helmet/perceptomatrix/item_action_slot_check(slot, mob/user, datum/action/action)
return slot & ITEM_SLOT_HEAD
/obj/item/clothing/head/helmet/perceptomatrix/update_icon_state()
icon_state = base_icon_state + (core_installed ? "" : "_inactive")
worn_icon_state = base_icon_state + (core_installed ? "" : "_inactive")

View File

@@ -292,7 +292,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
flags_cover = MASKCOVERSEYES
clothing_traits = list(TRAIT_PERCEIVED_AS_CLOWN)
resistance_flags = FLAMMABLE
actions_types = list(/datum/action/item_action/adjust)
actions_types = list(/datum/action/item_action/adjust_style)
dog_fashion = /datum/dog_fashion/head/clown
var/list/clownmask_designs = list()
voice_filter = null // performer masks expect to be talked through
@@ -359,7 +359,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
w_class = WEIGHT_CLASS_SMALL
flags_cover = MASKCOVERSEYES
resistance_flags = FLAMMABLE
actions_types = list(/datum/action/item_action/adjust)
actions_types = list(/datum/action/item_action/adjust_style)
species_exception = list(/datum/species/golem)
fishing_modifier = 0
var/list/mimemask_designs = list()
@@ -467,7 +467,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
resistance_flags = FLAMMABLE
flags_cover = MASKCOVERSEYES
max_integrity = 100
actions_types = list(/datum/action/item_action/adjust)
actions_types = list(/datum/action/item_action/adjust_style)
dog_fashion = null
fishing_modifier = -4
var/list/tikimask_designs = list()

View File

@@ -123,13 +123,7 @@ GLOBAL_LIST_INIT(hailer_phrases, list(
aggressiveness = AGGR_BROKEN
return
/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/halt))
halt()
else
adjust_visor(user)
/obj/item/clothing/mask/gas/sechailer/attack_self()
/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype)
halt()
/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user, obj/item/card/emag/emag_card)
@@ -203,6 +197,7 @@ GLOBAL_LIST_INIT(hailer_phrases, list(
custom_price = PAYCHECK_COMMAND * 1.5
w_class = WEIGHT_CLASS_SMALL
actions_types = list(/datum/action/item_action/halt)
action_slots = ALL
COOLDOWN_DECLARE(whistle_cooldown)
/obj/item/clothing/mask/whistle/ui_action_click(mob/user, action)

View File

@@ -233,7 +233,6 @@
var/robe_charge = TRUE
actions_types = list(/datum/action/item_action/stickmen)
/obj/item/clothing/suit/wizrobe/durathread
name = "durathread robe"
desc = "A rather dull durathread robe; not quite as protective as a proper piece of armour, but much more stylish."

View File

@@ -199,6 +199,7 @@
force = 5
w_class = WEIGHT_CLASS_SMALL
actions_types = list(/datum/action/item_action/instrument)
action_slots = ALL
/obj/item/instrument/harmonica/equipped(mob/user, slot, initial = FALSE)
. = ..()
@@ -223,12 +224,12 @@
name = "Use Instrument"
desc = "Use the instrument specified"
/datum/action/item_action/instrument/Trigger(trigger_flags)
if(istype(target, /obj/item/instrument))
var/obj/item/instrument/I = target
I.interact(usr)
return
return ..()
/datum/action/item_action/instrument/do_effect(trigger_flags)
if(!istype(target, /obj/item/instrument))
return FALSE
var/obj/item/instrument/instrument = target
instrument.interact(usr)
return TRUE
/obj/item/instrument/bikehorn
name = "gilded bike horn"

View File

@@ -34,9 +34,6 @@
if(gps_enabled)
. += span_notice("The cuff's GPS signal is on.")
/obj/item/kheiral_cuffs/item_action_slot_check(slot)
return (slot & ITEM_SLOT_GLOVES)
/obj/item/kheiral_cuffs/equipped(mob/user, slot, initial)
. = ..()
if(!(slot & ITEM_SLOT_GLOVES))

View File

@@ -29,6 +29,7 @@
attack_verb_simple = list("smash", "crush", "cleave", "chop", "pulp")
sharpness = SHARP_EDGED
actions_types = list(/datum/action/item_action/toggle_light)
action_slots = ALL
obj_flags = UNIQUE_RENAME
light_system = OVERLAY_LIGHT
light_range = 5

View File

@@ -60,6 +60,7 @@
hitsound = 'sound/items/weapons/sonic_jackhammer.ogg'
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
actions_types = list(/datum/action/item_action/vortex_recall)
action_slots = ALL
/// Linked teleport beacon for the group teleport functionality.
var/obj/effect/hierophant/beacon
/// TRUE if currently doing a teleport to the beacon, FALSE otherwise.

View File

@@ -148,9 +148,6 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/mob/living/carbon/human/active_owner
/obj/item/clothing/neck/necklace/memento_mori/item_action_slot_check(slot)
return (slot & ITEM_SLOT_NECK)
/obj/item/clothing/neck/necklace/memento_mori/dropped(mob/user)
..()
if(active_owner)
@@ -216,12 +213,13 @@
name = "Memento Mori"
desc = "Bind your life to the pendant."
/datum/action/item_action/hands_free/memento_mori/Trigger(trigger_flags)
var/obj/item/clothing/neck/necklace/memento_mori/MM = target
if(!MM.active_owner)
if(ishuman(owner))
MM.memento(owner)
Remove(MM.active_owner) //Remove the action button, since there's no real use in having it now.
/datum/action/item_action/hands_free/memento_mori/do_effect(trigger_flags)
var/obj/item/clothing/neck/necklace/memento_mori/memento = target
if(memento.active_owner || !ishuman(owner))
return FALSE
memento.memento(owner)
Remove(memento.active_owner) //Remove the action button, since there's no real use in having it now.
return TRUE
//Wisp Lantern
/obj/item/wisp_lantern

View File

@@ -566,9 +566,6 @@
/mob/proc/getBackSlot()
return ITEM_SLOT_BACK
/mob/proc/getBeltSlot()
return ITEM_SLOT_BELT
//Inventory.dm is -kind of- an ok place for this I guess
//This is NOT for dismemberment, as the user still technically has 2 "hands"

View File

@@ -78,6 +78,3 @@
/mob/living/basic/drone/getBackSlot()
return ITEM_SLOT_DEX_STORAGE
/mob/living/basic/drone/getBeltSlot()
return ITEM_SLOT_DEX_STORAGE

View File

@@ -87,9 +87,6 @@
/mob/living/basic/guardian/dextrous/getBackSlot()
return ITEM_SLOT_DEX_STORAGE
/mob/living/basic/guardian/dextrous/getBeltSlot()
return ITEM_SLOT_DEX_STORAGE
/mob/living/basic/guardian/dextrous/proc/update_inv_internal_storage()
if(isnull(internal_storage) || isnull(client) || !hud_used?.hud_shown)
return

View File

@@ -196,16 +196,15 @@
name = "Toggle Perspective"
desc = "Switch between seeing normally from your head, or blindly from your body."
/datum/action/item_action/organ_action/dullahan/Trigger(trigger_flags)
. = ..()
/datum/action/item_action/organ_action/dullahan/do_effect(trigger_flags)
var/obj/item/organ/eyes/dullahan/dullahan_eyes = target
dullahan_eyes.tint = dullahan_eyes.tint ? NONE : INFINITY
if(ishuman(owner))
var/mob/living/carbon/human/human = owner
if(isdullahan(human))
var/datum/species/dullahan/dullahan_species = human.dna.species
dullahan_species.update_vision_perspective(human)
if(!isdullahan(owner))
return FALSE
var/mob/living/carbon/human/human = owner
var/datum/species/dullahan/dullahan_species = human.dna.species
dullahan_species.update_vision_perspective(human)
return TRUE
/obj/item/dullahan_relay

View File

@@ -153,11 +153,7 @@
background_icon_state = "bg_default_on"
overlay_icon_state = "bg_default_border"
/datum/action/item_action/organ_action/toggle_trip/Trigger(trigger_flags)
. = ..()
if(!.)
return
/datum/action/item_action/organ_action/toggle_trip/do_effect(trigger_flags)
var/obj/item/organ/brain/primate/monkey_brain = target
if(monkey_brain.tripping)
monkey_brain.tripping = FALSE
@@ -168,6 +164,7 @@
background_icon_state = "bg_default_on"
to_chat(monkey_brain.owner, span_notice("You will now stumble while colliding with people who are in combat mode."))
build_all_button_icons()
return TRUE
/obj/item/organ/brain/primate/on_mob_insert(mob/living/carbon/primate)
. = ..()

View File

@@ -154,45 +154,50 @@
name = "Drain Victim"
desc = "Leech blood from any carbon victim you are passively grabbing."
/datum/action/item_action/organ_action/vampire/Trigger(trigger_flags)
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/H = owner
var/obj/item/organ/tongue/vampire/V = target
if(!COOLDOWN_FINISHED(V, drain_cooldown))
to_chat(H, span_warning("You just drained blood, wait a few seconds!"))
return
if(H.pulling && iscarbon(H.pulling))
var/mob/living/carbon/victim = H.pulling
if(H.blood_volume >= BLOOD_VOLUME_MAXIMUM)
to_chat(H, span_warning("You're already full!"))
return
if(victim.stat == DEAD)
to_chat(H, span_warning("You need a living victim!"))
return
if(!victim.blood_volume || (victim.dna && (HAS_TRAIT(victim, TRAIT_NOBLOOD) || victim.dna.species.exotic_blood)))
to_chat(H, span_warning("[victim] doesn't have blood!"))
return
COOLDOWN_START(V, drain_cooldown, 3 SECONDS)
if(victim.can_block_magic(MAGIC_RESISTANCE_HOLY, charge_cost = 0))
victim.show_message(span_warning("[H] tries to bite you, but stops before touching you!"))
to_chat(H, span_warning("[victim] is blessed! You stop just in time to avoid catching fire."))
return
if(victim.has_reagent(/datum/reagent/consumable/garlic))
victim.show_message(span_warning("[H] tries to bite you, but recoils in disgust!"))
to_chat(H, span_warning("[victim] reeks of garlic! you can't bring yourself to drain such tainted blood."))
return
if(!do_after(H, 3 SECONDS, target = victim, hidden = TRUE))
return
var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - H.blood_volume //How much capacity we have left to absorb blood
var/drained_blood = min(victim.blood_volume, VAMP_DRAIN_AMOUNT, blood_volume_difference)
victim.show_message(span_danger("[H] is draining your blood!"))
to_chat(H, span_notice("You drain some blood!"))
playsound(H, 'sound/items/drink.ogg', 30, TRUE, -2)
victim.blood_volume = clamp(victim.blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
H.blood_volume = clamp(H.blood_volume + drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
if(!victim.blood_volume)
to_chat(H, span_notice("You finish off [victim]'s blood supply."))
/datum/action/item_action/organ_action/vampire/do_effect(trigger_flags)
if(!iscarbon(owner))
return FALSE
var/mob/living/carbon/user = owner
var/obj/item/organ/tongue/vampire/licker_drinker = target
if(!COOLDOWN_FINISHED(licker_drinker, drain_cooldown))
to_chat(user, span_warning("You just drained blood, wait a few seconds!"))
return FALSE
if(!iscarbon(user.pulling))
return FALSE
var/mob/living/carbon/victim = user.pulling
if(user.blood_volume >= BLOOD_VOLUME_MAXIMUM)
to_chat(user, span_warning("You're already full!"))
return FALSE
if(victim.stat == DEAD)
to_chat(user, span_warning("You need a living victim!"))
return FALSE
if(!victim.blood_volume || (victim.dna && (HAS_TRAIT(victim, TRAIT_NOBLOOD) || victim.dna.species.exotic_blood)))
to_chat(user, span_warning("[victim] doesn't have blood!"))
return FALSE
COOLDOWN_START(licker_drinker, drain_cooldown, 3 SECONDS)
if(victim.can_block_magic(MAGIC_RESISTANCE_HOLY, charge_cost = 0))
victim.show_message(span_warning("[user] tries to bite you, but stops before touching you!"))
to_chat(user, span_warning("[victim] is blessed! You stop just in time to avoid catching fire."))
return FALSE
if(victim.has_reagent(/datum/reagent/consumable/garlic))
victim.show_message(span_warning("[user] tries to bite you, but recoils in disgust!"))
to_chat(user, span_warning("[victim] reeks of garlic! you can't bring yourself to drain such tainted blood."))
return FALSE
if(!do_after(user, 3 SECONDS, target = victim, hidden = TRUE))
return FALSE
var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - user.blood_volume //How much capacity we have left to absorb blood
var/drained_blood = min(victim.blood_volume, VAMP_DRAIN_AMOUNT, blood_volume_difference)
victim.show_message(span_danger("[user] is draining your blood!"))
to_chat(user, span_notice("You drain some blood!"))
playsound(user, 'sound/items/drink.ogg', 30, TRUE, -2)
victim.blood_volume = clamp(victim.blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
user.blood_volume = clamp(user.blood_volume + drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
if(!victim.blood_volume)
to_chat(user, span_notice("You finish off [victim]'s blood supply."))
return TRUE
/obj/item/organ/heart/vampire
name = "vampire heart"

View File

@@ -30,9 +30,7 @@
return
return ..()
/datum/action/item_action/mod/Trigger(trigger_flags)
if(!IsAvailable(feedback = TRUE))
return FALSE
/datum/action/item_action/mod/do_effect(trigger_flags)
var/obj/item/mod/control/mod = target
if(mod.malfunctioning && prob(75))
mod.balloon_alert(usr, "button malfunctions!")
@@ -44,7 +42,7 @@
desc = "LMB: Deploy/Undeploy part. RMB: Deploy/Undeploy full suit."
button_icon_state = "deploy"
/datum/action/item_action/mod/deploy/Trigger(trigger_flags)
/datum/action/item_action/mod/deploy/do_effect(trigger_flags)
. = ..()
if(!.)
return
@@ -64,7 +62,7 @@
/// First time clicking this will set it to TRUE, second time will activate it.
var/ready = FALSE
/datum/action/item_action/mod/activate/Trigger(trigger_flags)
/datum/action/item_action/mod/activate/do_effect(trigger_flags)
. = ..()
if(!.)
return
@@ -92,7 +90,7 @@
desc = "Toggle a MODsuit module."
button_icon_state = "module"
/datum/action/item_action/mod/module/Trigger(trigger_flags)
/datum/action/item_action/mod/module/do_effect(trigger_flags)
. = ..()
if(!.)
return
@@ -107,7 +105,7 @@
desc = "Open the MODsuit's panel."
button_icon_state = "panel"
/datum/action/item_action/mod/panel/Trigger(trigger_flags)
/datum/action/item_action/mod/panel/do_effect(trigger_flags)
. = ..()
if(!.)
return
@@ -180,7 +178,7 @@
pinner = null
return ..()
/datum/action/item_action/mod/pinnable/module/Trigger(trigger_flags)
/datum/action/item_action/mod/pinnable/module/do_effect(trigger_flags)
. = ..()
if(!.)
return

View File

@@ -202,10 +202,6 @@
return
clean_up()
/obj/item/mod/control/item_action_slot_check(slot)
if(slot & slot_flags)
return TRUE
// Grant pinned actions to pin owners, gives AI pinned actions to the AI and not the wearer
/obj/item/mod/control/grant_action_to_bearer(datum/action/action)
if (!istype(action, /datum/action/item_action/mod/pinnable))

View File

@@ -182,10 +182,7 @@
qdel(src)
return
/datum/action/item_action/mod_recall/Trigger(trigger_flags)
. = ..()
if(!.)
return
/datum/action/item_action/mod_recall/do_effect(trigger_flags)
var/obj/item/implant/mod/implant = target
if(!COOLDOWN_FINISHED(src, recall_cooldown))
implant.balloon_alert(implant.imp_in, "on cooldown!")

View File

@@ -20,6 +20,7 @@
item_flags = NEEDS_PERMIT
attack_verb_continuous = list("strikes", "hits", "bashes")
attack_verb_simple = list("strike", "hit", "bash")
action_slots = ALL
var/gun_flags = NONE
var/fire_sound = 'sound/items/weapons/gun/pistol/shot.ogg'

View File

@@ -38,6 +38,7 @@ Slimecrossing Armor
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "prismglasses"
actions_types = list(/datum/action/item_action/change_prism_colour, /datum/action/item_action/place_light_prism)
forced_glass_color = TRUE
var/glasses_color = COLOR_WHITE
@@ -45,10 +46,6 @@ Slimecrossing Armor
. = ..()
AddElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, glasses_color, forced_glass_color)
/obj/item/clothing/glasses/prism_glasses/item_action_slot_check(slot)
if(slot & ITEM_SLOT_EYES)
return TRUE
/obj/structure/light_prism
name = "light prism"
desc = "A shining crystal of semi-solid light. Looks fragile."
@@ -74,9 +71,7 @@ Slimecrossing Armor
button_icon = 'icons/obj/science/slimecrossing.dmi'
button_icon_state = "prismcolor"
/datum/action/item_action/change_prism_colour/Trigger(trigger_flags)
if(!IsAvailable(feedback = TRUE))
return
/datum/action/item_action/change_prism_colour/do_effect(trigger_flags)
var/obj/item/clothing/glasses/prism_glasses/glasses = target
var/new_color = tgui_color_picker(owner, "Choose the lens color:", "Color change",glasses.glasses_color) // BUBBERSTATION EDIT: TGUI COLOR PICKER
if(!new_color)
@@ -90,9 +85,7 @@ Slimecrossing Armor
button_icon = 'icons/obj/science/slimecrossing.dmi'
button_icon_state = "lightprism"
/datum/action/item_action/place_light_prism/Trigger(trigger_flags)
if(!IsAvailable(feedback = TRUE))
return
/datum/action/item_action/place_light_prism/do_effect(trigger_flags)
var/obj/item/clothing/glasses/prism_glasses/glasses = target
if(locate(/obj/structure/light_prism) in get_turf(owner))
to_chat(owner, span_warning("There isn't enough ambient energy to fabricate another light prism here."))

View File

@@ -75,9 +75,7 @@
return FALSE
return ..()
/datum/action/item_action/activate_pill/Trigger(trigger_flags)
if(!..())
return FALSE
/datum/action/item_action/activate_pill/do_effect(trigger_flags)
owner.balloon_alert_to_viewers("[owner] grinds their teeth!", "You grit your teeth.")
if(!do_after(owner, owner.stat * (2.5 SECONDS), owner, IGNORE_USER_LOC_CHANGE | IGNORE_INCAPACITATED))
return FALSE

View File

@@ -11,12 +11,11 @@
name = "HUD implant"
desc = "These cybernetic eyes will display a HUD over everything you see. Maybe."
slot = ORGAN_SLOT_HUD
actions_types = list(/datum/action/item_action/toggle_hud)
actions_types = list(/datum/action/item_action/organ_action/toggle_hud)
var/HUD_traits = list()
/// Whether the HUD implant is on or off
var/toggled_on = TRUE
/obj/item/organ/cyberimp/eyes/hud/proc/toggle_hud(mob/living/carbon/eye_owner)
if(toggled_on)
toggled_on = FALSE

View File

@@ -56,16 +56,14 @@
return FALSE
return TRUE
/datum/action/item_action/organ_action/colossus/Trigger(trigger_flags)
. = ..()
if(!.)
return
/datum/action/item_action/organ_action/colossus/do_effect(trigger_flags)
var/command = tgui_input_text(owner, "Speak with the Voice of God", "Command", max_length = MAX_MESSAGE_LEN)
if(!command)
return
return FALSE
if(QDELETED(src) || QDELETED(owner))
return
return FALSE
owner.say(".x[command]")
return TRUE
/obj/item/organ/vocal_cords/colossus/can_speak_with()
if(!owner)
@@ -98,15 +96,14 @@
actions_types = list(/datum/action/item_action/organ_action/use/adamantine_vocal_cords)
icon_state = "adamantine_cords"
/datum/action/item_action/organ_action/use/adamantine_vocal_cords/Trigger(trigger_flags)
if(!IsAvailable(feedback = TRUE))
return
/datum/action/item_action/organ_action/use/adamantine_vocal_cords/do_effect(trigger_flags)
var/message = tgui_input_text(owner, "Resonate a message to all nearby golems", "Resonate", max_length = MAX_MESSAGE_LEN)
if(!message)
return
return FALSE
if(QDELETED(src) || QDELETED(owner))
return
return FALSE
owner.say(".x[message]")
return TRUE
/obj/item/organ/vocal_cords/adamantine/handle_speech(message)
var/msg = span_resonate("[span_name("[owner.real_name]")] resonates, \"[message]\"")

View File

@@ -114,17 +114,17 @@
return ..()
/datum/action/item_action/mod/pinnable/circuit/Trigger(trigger_flags)
/datum/action/item_action/mod/pinnable/circuit/do_effect(trigger_flags)
. = ..()
if(!.)
return
var/obj/item/mod/control/mod = module.mod
if(!istype(mod))
return
return FALSE
if(!mod.active || mod.activating)
if(mod.wearer)
module.balloon_alert(mod.wearer, "not active!")
return
return FALSE
circuit_component.user.set_output(owner)
circuit_component.signal.set_output(COMPONENT_SIGNAL)