From e0b706c36ccf3789b031be0cbf96ba809e2413bf Mon Sep 17 00:00:00 2001 From: itseasytosee <55666666+itseasytosee@users.noreply.github.com> Date: Sat, 8 Oct 2022 20:51:29 -0500 Subject: [PATCH] Put clothing on people by clicking on them with clothing inhand + targeting correct area. (#69560) Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> --- .../__DEFINES/dcs/signals/signals_clothing.dm | 3 + code/datums/elements/attack_equip.dm | 58 ++++++++++++++++++ code/datums/elements/strippable.dm | 31 +--------- code/game/objects/items.dm | 60 +++++++++++++++++++ code/game/objects/items/storage/backpack.dm | 1 + code/game/objects/items/storage/belt.dm | 1 + code/modules/clothing/clothing.dm | 7 ++- tgstation.dme | 1 + 8 files changed, 129 insertions(+), 33 deletions(-) create mode 100644 code/datums/elements/attack_equip.dm diff --git a/code/__DEFINES/dcs/signals/signals_clothing.dm b/code/__DEFINES/dcs/signals/signals_clothing.dm index 242a076615f..b48acbf97e9 100644 --- a/code/__DEFINES/dcs/signals/signals_clothing.dm +++ b/code/__DEFINES/dcs/signals/signals_clothing.dm @@ -1,3 +1,6 @@ // /obj/item/clothing /// (/obj/item/clothing, visor_state) - When a clothing gets it's visor toggled. #define COMSIG_CLOTHING_VISOR_TOGGLE "clothing_visor_toggle" +// /obj/item/clothing +/// Sent when mobs try to equip clothing on others through attacking +#define COMSIG_CLOTHING_ATTACK_EQUIP "clothing_attack_equip" diff --git a/code/datums/elements/attack_equip.dm b/code/datums/elements/attack_equip.dm new file mode 100644 index 00000000000..b82d5653dba --- /dev/null +++ b/code/datums/elements/attack_equip.dm @@ -0,0 +1,58 @@ +/** + * Attached to an item, when the item is used to attack a human, and the attacker isn't in combat mode, attempts to equip the item to the target after the normal delay. + * + * Uses the compare_zone_to_item_slot() proc to see if the attacker is targeting a valid slot. + */ +/datum/element/attack_equip + +/datum/element/attack_equip/Attach(datum/target) + . = ..() + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_ITEM_ATTACK, .proc/on_item_attack) + +/datum/element/attack_equip/Detach(datum/source, ...) + . = ..() + + UnregisterSignal(source, COMSIG_ITEM_ATTACK) + +/datum/element/attack_equip/proc/on_item_attack(obj/item/attire, mob/living/target, mob/living/user) + SIGNAL_HANDLER + if(user.combat_mode || !ishuman(target) || target == user) + return + + var/mob/living/carbon/human/sharp_dresser = target + var/targeted_zone = user.zone_selected + + if(!attire.compare_zone_to_item_slot(targeted_zone)) + return + + if(attire.mob_can_equip(target, user, attire.slot_flags, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) + INVOKE_ASYNC(src, .proc/equip, attire, sharp_dresser, user) + return COMPONENT_CANCEL_ATTACK_CHAIN + + +/datum/element/attack_equip/proc/equip(obj/item/attire, mob/living/carbon/human/sharp_dresser, mob/living/user) + + if(HAS_TRAIT(attire, TRAIT_NODROP)) + to_chat(user, span_warning("You can't put [attire] on [sharp_dresser], it's stuck to your hand!")) + return + var/equip_time = attire.equip_delay_other + + attire.item_start_equip(sharp_dresser, attire, user) + + if(!do_mob(user, sharp_dresser, equip_time)) + return + + if(QDELETED(src) || QDELETED(sharp_dresser)) + return + + if(!user.Adjacent(sharp_dresser)) // Due to teleporting shenanigans + user.put_in_hands(attire) + return + + user.temporarilyRemoveItemFromInventory(attire) + + sharp_dresser.equip_to_slot_if_possible(attire, attire.slot_flags) + + return finish_equip_mob(attire, sharp_dresser, user) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index c5cf34d75b8..5bcc029e0ce 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -87,37 +87,8 @@ /// Start the equipping process. This is the proc you should yield in. /// Returns TRUE/FALSE depending on if it is allowed. /datum/strippable_item/proc/start_equip(atom/source, obj/item/equipping, mob/user) - if (warn_dangerous_clothing && isclothing(source)) - var/obj/item/clothing/clothing = source - if(clothing.clothing_flags & DANGEROUS_OBJECT) - source.visible_message( - span_danger("[user] tries to put [equipping] on [source]."), - span_userdanger("[user] tries to put [equipping] on you."), - ignored_mobs = user, - ) - - else - source.visible_message( - span_notice("[user] tries to put [equipping] on [source]."), - span_notice("[user] tries to put [equipping] on you."), - ignored_mobs = user, - ) - - if(ishuman(source)) - var/mob/living/carbon/human/victim_human = source - if(victim_human.key && !victim_human.client) // AKA braindead - if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES) - var/list/new_entry = list(list(user.name, "tried equipping you with [equipping]", world.time)) - LAZYADD(victim_human.afk_thefts, new_entry) - - else if(victim_human.is_blind()) - to_chat(source, span_userdanger("You feel someone trying to put something on you.")) - - to_chat(user, span_notice("You try to put [equipping] on [source]...")) - - user.log_message("is putting [equipping] on [key_name(source)]", LOG_ATTACK, color="red") - source.log_message("is having [equipping] put on them by [key_name(user)]", LOG_VICTIM, color="orange", log_globally=FALSE) + equipping.item_start_equip(source, equipping, user, warn_dangerous_clothing) return TRUE /// The proc that places the item on the source. This should not yield. diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index a961604885e..8b82b9ab2f7 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1486,3 +1486,63 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons */ /obj/item/proc/get_writing_implement_details() return null + +/** + * When called on an item, and given a body targeting zone, this will return TRUE if the item slot matches the target zone, and FALSE otherwise. + * Currently supports the jumpsuit, outersuit, backpack, belt, gloves, hat, ears, neck, mask, eyes, and feet slots. All other slots will auto return FALSE. + */ +/obj/item/proc/compare_zone_to_item_slot(zone) + switch(slot_flags) + if(ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING, ITEM_SLOT_BACK) + return (zone == BODY_ZONE_CHEST) + if(ITEM_SLOT_BELT) + return (zone == BODY_ZONE_PRECISE_GROIN) + if(ITEM_SLOT_GLOVES) + return (zone == BODY_ZONE_R_ARM || zone == BODY_ZONE_L_ARM) + if(ITEM_SLOT_HEAD, ITEM_SLOT_EARS, ITEM_SLOT_NECK) + return (zone == BODY_ZONE_HEAD) + if(ITEM_SLOT_MASK) + return (zone == BODY_ZONE_PRECISE_MOUTH) + if(ITEM_SLOT_EYES) + return (zone == BODY_ZONE_PRECISE_EYES) + if(ITEM_SLOT_FEET) + return (zone == BODY_ZONE_L_LEG || zone == BODY_ZONE_R_LEG) + return FALSE + +/** + * This proc calls at the begining of anytime an item is being equiped to a target by another mob. + * It handles initial messages, AFK stripping, and initial logging. + */ +/obj/item/proc/item_start_equip(atom/target, obj/item/equipping, mob/user, warn_dangerous = TRUE) + + if(warn_dangerous && isclothing(equipping)) + var/obj/item/clothing/clothing = equipping + if(clothing.clothing_flags & DANGEROUS_OBJECT) + target.visible_message( + span_danger("[user] tries to put [equipping] on [target]."), + span_userdanger("[user] tries to put [equipping] on you."), + ignored_mobs = user, + ) + + else + target.visible_message( + span_notice("[user] tries to put [equipping] on [target]."), + span_notice("[user] tries to put [equipping] on you."), + ignored_mobs = user, + ) + + if(ishuman(target)) + var/mob/living/carbon/human/victim_human = target + if(victim_human.key && !victim_human.client) // AKA braindead + if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES) + var/list/new_entry = list(list(user.name, "tried equipping you with [equipping]", world.time)) + LAZYADD(victim_human.afk_thefts, new_entry) + + else if(victim_human.is_blind()) + to_chat(target, span_userdanger("You feel someone trying to put something on you.")) + user.do_item_attack_animation(target, used_item = equipping) + + to_chat(user, span_notice("You try to put [equipping] on [target]...")) + + user.log_message("is putting [equipping] on [key_name(target)]", LOG_ATTACK, color="red") + target.log_message("is having [equipping] put on them by [key_name(user)]", LOG_VICTIM, color="orange", log_globally=FALSE) diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index de2120c26be..e3188ef7d1c 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -26,6 +26,7 @@ /obj/item/storage/backpack/Initialize(mapload) . = ..() create_storage(max_slots = 21, max_total_storage = 21) + AddElement(/datum/element/attack_equip) /* * Backpack Types diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index bbfa614151f..dcad1def751 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -28,6 +28,7 @@ /obj/item/storage/belt/Initialize(mapload) . = ..() + AddElement(/datum/element/attack_equip) update_appearance() /obj/item/storage/belt/utility diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index bdf41b37591..71078e6c733 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -53,6 +53,7 @@ AddElement(/datum/element/venue_price, FOOD_PRICE_CHEAP) if(can_be_bloody && ((body_parts_covered & FEET) || (flags_inv & HIDESHOES))) LoadComponent(/datum/component/bloodysoles) + AddElement(/datum/element/attack_equip) if(!icon_state) item_flags |= ABSTRACT @@ -104,8 +105,8 @@ else qdel(src) -/obj/item/clothing/attack(mob/living/M, mob/living/user, params) - if(user.combat_mode || !ismoth(M) || ispickedupmob(src)) +/obj/item/clothing/attack(mob/living/target, mob/living/user, params) + if(user.combat_mode || !ismoth(target) || ispickedupmob(src)) return ..() if(clothing_flags & INEDIBLE_CLOTHING) return ..() @@ -113,7 +114,7 @@ moth_snack = new moth_snack.name = name moth_snack.clothing = WEAKREF(src) - moth_snack.attack(M, user, params) + moth_snack.attack(target, user, params) /obj/item/clothing/attackby(obj/item/W, mob/user, params) if(!istype(W, repairable_by)) diff --git a/tgstation.dme b/tgstation.dme index 6134f3785a2..dc006764237 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -998,6 +998,7 @@ #include "code\datums\elements\art.dm" #include "code\datums\elements\atmos_requirements.dm" #include "code\datums\elements\atmos_sensitive.dm" +#include "code\datums\elements\attack_equip.dm" #include "code\datums\elements\backblast.dm" #include "code\datums\elements\bane.dm" #include "code\datums\elements\basic_body_temp_sensitive.dm"