mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-21 13:46:55 +01:00
323 lines
12 KiB
Plaintext
323 lines
12 KiB
Plaintext
//* This file is explicitly licensed under the MIT license. *//
|
|
//* Copyright (c) 2024 Citadel Station Developers *//
|
|
|
|
//* ----- Click Triggers ----- */
|
|
|
|
//* Attack Hand *//
|
|
|
|
/obj/item/on_attack_hand(datum/event_args/actor/clickchain/clickchain, clickchain_flags)
|
|
. = ..()
|
|
if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT)
|
|
return
|
|
|
|
if(clickchain.performer.is_in_inventory(src))
|
|
if(clickchain.performer.is_holding(src))
|
|
if(obj_storage?.allow_open_via_offhand_click && obj_storage.auto_handle_interacted_open(clickchain))
|
|
return . | CLICKCHAIN_DID_SOMETHING
|
|
else
|
|
if(obj_storage?.allow_open_via_equipped_click && obj_storage.auto_handle_interacted_open(clickchain))
|
|
return . | CLICKCHAIN_DID_SOMETHING
|
|
if(!clickchain.performer.is_holding(src))
|
|
if(should_attempt_pickup(clickchain) && attempt_pickup(clickchain.performer))
|
|
return . | CLICKCHAIN_DID_SOMETHING
|
|
|
|
/obj/item/proc/should_attempt_pickup(datum/event_args/actor/actor)
|
|
return TRUE
|
|
|
|
/**
|
|
* @params
|
|
* * actor - (optional) person doing it
|
|
* * silent - suppress feedback
|
|
*/
|
|
/obj/item/proc/should_allow_pickup(datum/event_args/actor/actor, silent)
|
|
if(anchored)
|
|
if(!silent)
|
|
actor?.chat_feedback(
|
|
SPAN_NOTICE("\The [src] won't budge, you can't pick it up!"),
|
|
target = src,
|
|
)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* @return TRUE if an operation happened. if we pick it up and immediately drop it from failing
|
|
* to keep it in us, **this is a TRUE return.**
|
|
*/
|
|
/obj/item/proc/attempt_pickup(mob/user)
|
|
var/datum/event_args/actor/actor = new(user)
|
|
// make sure they have inventory
|
|
if (!user?.inventory)
|
|
return FALSE
|
|
// check if pickup is allowed
|
|
if(!should_allow_pickup(actor))
|
|
return FALSE
|
|
if(!user.should_allow_pickup(src, actor))
|
|
return FALSE
|
|
|
|
if(!CHECK_MOBILITY(user, MOBILITY_CAN_PICKUP))
|
|
user.action_feedback(SPAN_WARNING("You can't do that right now."), src)
|
|
return FALSE
|
|
|
|
if (hasorgans(user))
|
|
var/mob/living/carbon/human/H = user
|
|
var/obj/item/organ/external/temp = H.organs_by_name["r_hand"]
|
|
if (H.active_hand % 2)
|
|
temp = H.organs_by_name["l_hand"]
|
|
if(temp && !temp.is_usable())
|
|
to_chat(user, "<span class='notice'>You try to move your [temp.name], but cannot!</span>")
|
|
return FALSE
|
|
if(!temp)
|
|
to_chat(user, "<span class='notice'>You try to use your hand, but realize it is no longer attached!</span>")
|
|
return FALSE
|
|
|
|
var/old_loc = src.loc
|
|
var/obj/item/actually_picked_up = src
|
|
var/has_to_drop_to_ground_on_fail = FALSE
|
|
|
|
if(isturf(old_loc))
|
|
// if picking up from floor
|
|
throwing?.terminate()
|
|
else if(item_flags & ITEM_IN_STORAGE)
|
|
// trying to take out of backpack
|
|
var/datum/object_system/storage/resolved
|
|
if(istype(loc, /atom/movable/storage_indirection))
|
|
var/atom/movable/storage_indirection/holder = loc
|
|
resolved = holder.parent
|
|
else if(isobj(loc))
|
|
var/obj/obj_loc = loc
|
|
resolved = obj_loc.obj_storage
|
|
if(isnull(resolved))
|
|
item_flags &= ~ITEM_IN_STORAGE
|
|
CRASH("in storage at [loc] ([REF(loc)]) ([loc?.type || "NULL"]) but cannot resolve storage system")
|
|
actually_picked_up = resolved.try_remove(src, user, new /datum/event_args/actor(user))
|
|
// they're in user, but not equipped now. this is so it doesn't touch the ground first.
|
|
has_to_drop_to_ground_on_fail = TRUE
|
|
|
|
if(isnull(actually_picked_up))
|
|
to_chat(user, SPAN_WARNING("[src] somehow slips through your grasp. What just happened?"))
|
|
return FALSE
|
|
if(!user.put_in_hands(actually_picked_up, INV_OP_NO_MERGE_STACKS, user.active_hand))
|
|
if(has_to_drop_to_ground_on_fail)
|
|
actually_picked_up.forceMove(user.drop_location())
|
|
return TRUE
|
|
return FALSE
|
|
// success
|
|
if(isturf(old_loc))
|
|
new /obj/effect/temporary_effect/item_pickup_ghost(old_loc, actually_picked_up, user)
|
|
user.trigger_aiming(TARGET_CAN_CLICK)
|
|
return TRUE
|
|
|
|
//* Drag / Drop *//
|
|
|
|
/obj/item/OnMouseDrop(atom/over, mob/user, proximity, params)
|
|
. = ..()
|
|
if(. & CLICKCHAIN_DO_NOT_PROPAGATE)
|
|
return
|
|
if(anchored) // Don't.
|
|
return
|
|
if(user.restrained())
|
|
return // don't.
|
|
// todo: restraint levels, e.g. handcuffs vs straightjacket
|
|
// todo: this needs to check for user / actor indirection bullshit (e.g. someone does the clickdragging while controlling another mob)
|
|
if(!user.is_in_inventory(src))
|
|
// not being held
|
|
if(!isturf(loc)) // yea nah
|
|
return ..()
|
|
if(user.Adjacent(src))
|
|
// check for equip
|
|
if(istype(over, /atom/movable/screen/actor_hud/inventory/plate/hand))
|
|
var/atom/movable/screen/actor_hud/inventory/plate/hand/H = over
|
|
user.put_in_hand(src, H.hand_index)
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
else if(istype(over, /atom/movable/screen/actor_hud/inventory/plate/slot))
|
|
var/atom/movable/screen/actor_hud/inventory/plate/slot/S = over
|
|
user.equip_to_slot_if_possible(src, S.inventory_slot_id)
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
// check for slide
|
|
if(Adjacent(over) && user.CanSlideItem(src, over) && (istype(over, /obj/structure/table/rack) || istype(over, /obj/structure/table) || istype(over, /turf)))
|
|
var/turf/old = get_turf(src)
|
|
if(over == old) // same tile don't bother
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
if(!Move(get_turf(over)))
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
//! todo: i want to strangle the mofo who did planes instead of invisibility, which makes it computationally infeasible to check ghost invisibility in get hearers in view
|
|
//! :) FUCK YOU.
|
|
//! this if check is all for you. FUCK YOU.
|
|
if(!isobserver(user))
|
|
user.visible_message(SPAN_NOTICE("[user] slides [src] over."), SPAN_NOTICE("You slide [src] over."), range = MESSAGE_RANGE_COMBAT_SUBTLE)
|
|
log_inventory("[user] slid [src] from [COORD(old)] to [COORD(over)]")
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
else
|
|
// being held, check for attempt unequip
|
|
if(istype(over, /atom/movable/screen/actor_hud/inventory/plate/hand))
|
|
var/atom/movable/screen/actor_hud/inventory/plate/hand/H = over
|
|
user.put_in_hand(src, H.hand_index)
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
else if(istype(over, /atom/movable/screen/actor_hud/inventory/plate/slot))
|
|
var/atom/movable/screen/actor_hud/inventory/plate/slot/S = over
|
|
user.equip_to_slot_if_possible(src, S.inventory_slot_id)
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
else if(istype(over, /turf))
|
|
user.drop_item_to_ground(src)
|
|
return CLICKCHAIN_DO_NOT_PROPAGATE
|
|
|
|
// funny!
|
|
// todo: move to mob files
|
|
/mob/proc/CanSlideItem(obj/item/I, turf/over)
|
|
return FALSE
|
|
|
|
// todo: move to mob files
|
|
/mob/living/CanSlideItem(obj/item/I, turf/over)
|
|
return Adjacent(I) && !incapacitated() && !stat && !restrained()
|
|
|
|
// todo: move to mob files
|
|
/mob/observer/dead/CanSlideItem(obj/item/I, turf/over)
|
|
return is_spooky
|
|
|
|
//* ------ Inhand Triggers ------ *//
|
|
|
|
//* Attack Self *//
|
|
|
|
/**
|
|
* Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
|
|
*
|
|
* You should do . = ..() and check ., if it's TRUE, it means a parent proc requested the call chain to stop.
|
|
*
|
|
* todo: please stop overriding this, use on_attack_self.
|
|
* todo: nuke mob/user.
|
|
* todo: rename this to like /activate_inhand, /on_activate_inhand
|
|
* todo: actor is not currently always given.
|
|
*
|
|
* @params
|
|
* * user - The person using us in hand; stop using this, this is deprecated
|
|
* * actor - the event_args that spawned this call
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/attack_self(mob/user, datum/event_args/actor/actor)
|
|
// todo: this should realistically be SHOULD_NOT_OVERRIDE but there's a massive number of overrides (some unnecessary), so this is for a later date
|
|
// SHOULD_NOT_OVERRIDE(TRUE) // may be re-evaluated later
|
|
if(isnull(actor))
|
|
actor = new /datum/event_args/actor(user)
|
|
var/signal_return = SEND_SIGNAL(src, COMSIG_ITEM_ACTIVATE_INHAND, actor)
|
|
if(signal_return & RAISE_ITEM_ACTIVATE_INHAND_HANDLED)
|
|
user.trigger_aiming(TARGET_CAN_CLICK)
|
|
return TRUE
|
|
if(on_attack_self(actor))
|
|
user.trigger_aiming(TARGET_CAN_CLICK)
|
|
return TRUE
|
|
if(interaction_flags_item & INTERACT_ITEM_ATTACK_SELF)
|
|
user.trigger_aiming(TARGET_CAN_CLICK)
|
|
interact(user)
|
|
|
|
/**
|
|
* Called after we attack self
|
|
* Used to allow for attack_self to be interrupted by signals in nearly all cases.
|
|
* You should usually override this instead of attack_self.
|
|
*
|
|
* You should do . = ..() and check ., if it's TRUE, it means a parent proc requested the call chain to stop.
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/on_attack_self(datum/event_args/actor/actor)
|
|
if(!isnull(obj_cell_slot?.cell) && obj_cell_slot.remove_yank_inhand && obj_cell_slot.interaction_active(src))
|
|
actor.visible_feedback(
|
|
target = src,
|
|
range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = SPAN_NOTICE("[actor.performer] removes the cell from [src]."),
|
|
audible = SPAN_NOTICE("You hear fasteners falling out and something being removed."),
|
|
otherwise_self = SPAN_NOTICE("You remove the cell from [src]."),
|
|
)
|
|
log_construction(actor, src, "removed cell [obj_cell_slot.cell] ([obj_cell_slot.cell.type])")
|
|
actor.performer.put_in_hands_or_drop(obj_cell_slot.remove_cell(actor.performer))
|
|
return TRUE
|
|
if(!isnull(obj_storage) && obj_storage.allow_quick_empty && obj_storage.allow_quick_empty_via_attack_self)
|
|
var/turf/turf = get_turf(actor.performer)
|
|
obj_storage.auto_handle_interacted_mass_dumping(actor, turf)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
//* Unique Action *//
|
|
|
|
/**
|
|
* Called when a mob uses our unique aciton.
|
|
*
|
|
* @params
|
|
* * actor - the event_args that spawned this call
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/unique_action(datum/event_args/actor/actor)
|
|
SHOULD_NOT_OVERRIDE(TRUE) // may be re-evaluated later
|
|
if(ismob(actor))
|
|
actor = new /datum/event_args/actor(actor)
|
|
var/signal_return = SEND_SIGNAL(src, COMSIG_ITEM_UNIQUE_ACTION, actor)
|
|
if(signal_return & RAISE_ITEM_UNIQUE_ACTION_HANDLED)
|
|
return TRUE
|
|
if(on_unique_action(actor))
|
|
return TRUE
|
|
|
|
/**
|
|
* Called when an unique action is triggered.
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/on_unique_action(datum/event_args/actor/actor)
|
|
return FALSE
|
|
|
|
//* Defensive Toggle *//
|
|
|
|
/**
|
|
* Called when a mob uses our defensive toggle action.
|
|
*
|
|
* @params
|
|
* * actor - the event_args that spawned this call
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/defensive_toggle(datum/event_args/actor/actor)
|
|
SHOULD_NOT_OVERRIDE(TRUE) // may be re-evaluated later
|
|
if(ismob(actor))
|
|
actor = new /datum/event_args/actor(actor)
|
|
var/signal_return = SEND_SIGNAL(src, COMSIG_ITEM_DEFENSIVE_TOGGLE, actor)
|
|
if(signal_return & RAISE_ITEM_DEFENSIVE_TOGGLE_HANDLED)
|
|
return TRUE
|
|
if(on_defensive_toggle(actor))
|
|
return TRUE
|
|
|
|
/**
|
|
* Called on defensive toggle
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/on_defensive_toggle(datum/event_args/actor/e_args)
|
|
return FALSE
|
|
|
|
//* Defensive Trigger *//
|
|
|
|
/**
|
|
* Called when a mob uses our defensive trigger action.
|
|
*
|
|
* @params
|
|
* * actor - the event_args that spawned this call
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/defensive_trigger(datum/event_args/actor/actor)
|
|
SHOULD_NOT_OVERRIDE(TRUE) // may be re-evaluated later
|
|
if(ismob(actor))
|
|
actor = new /datum/event_args/actor(actor)
|
|
var/signal_return = SEND_SIGNAL(src, COMSIG_ITEM_DEFENSIVE_TRIGGER, actor)
|
|
if(signal_return & RAISE_ITEM_DEFENSIVE_TRIGGER_HANDLED)
|
|
return TRUE
|
|
if(on_defensive_trigger(actor))
|
|
return TRUE
|
|
|
|
/**
|
|
* Called on defensive trigger.
|
|
*
|
|
* @return TRUE to signal to overrides to stop the chain and do nothing.
|
|
*/
|
|
/obj/item/proc/on_defensive_trigger(datum/event_args/actor/e_args)
|
|
return FALSE
|