diff --git a/code/datums/elements/high_fiver.dm b/code/datums/elements/high_fiver.dm
new file mode 100644
index 00000000000..d4dc594e64f
--- /dev/null
+++ b/code/datums/elements/high_fiver.dm
@@ -0,0 +1,92 @@
+/**
+ * # High Fiver Element
+ *
+ * Attach to an item to make it offer a "high five" when offered to people
+ */
+/datum/element/high_fiver
+
+/datum/element/high_fiver/Attach(datum/target)
+ . = ..()
+ if(!isitem(target))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(target, COMSIG_ITEM_OFFERING, PROC_REF(on_offer))
+ RegisterSignal(target, COMSIG_ITEM_OFFER_TAKEN, PROC_REF(on_offer_taken))
+
+/datum/element/high_fiver/Detach(datum/source, ...)
+ . = ..()
+ UnregisterSignal(source, list(COMSIG_ITEM_OFFERING, COMSIG_ITEM_OFFER_TAKEN))
+
+/// Signal proc for [COMSIG_ITEM_OFFERING] to set up the high-five on offer
+/datum/element/high_fiver/proc/on_offer(obj/item/source, mob/living/carbon/offerer)
+ SIGNAL_HANDLER
+
+ if(locate(/mob/living/carbon) in orange(1, offerer))
+ offerer.visible_message(
+ span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"),
+ span_notice("You post up, looking for a high-five!"),
+ vision_distance = 2,
+ )
+ offerer.apply_status_effect(/datum/status_effect/offering, source, /atom/movable/screen/alert/give/highfive)
+
+ else
+ offerer.visible_message(
+ span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"),
+ span_warning("You post up, looking for a high-five, but find no one to accept it..."),
+ vision_distance = 4,
+ )
+
+ return COMPONENT_OFFER_INTERRUPT
+
+/// Signal proc for [COMSIG_ITEM_OFFER_TAKEN] to continue through with the high-five on take
+/datum/element/high_fiver/proc/on_offer_taken(obj/item/source, mob/living/carbon/offerer, mob/living/carbon/taker)
+ SIGNAL_HANDLER
+
+ var/open_hands_taker = 0
+ var/slappers_giver = 0
+ // see how many hands the taker has open for high'ing
+ for(var/hand in taker.held_items)
+ if(isnull(hand))
+ open_hands_taker++
+
+ // see how many hands the offerer is using for high'ing
+ for(var/obj/item/slap_check in offerer.held_items)
+ if(slap_check.item_flags & HAND_ITEM)
+ slappers_giver++
+
+ var/high_ten = (slappers_giver >= 2)
+ var/descriptor = "high-[high_ten ? "ten" : "five"]"
+
+ if(open_hands_taker <= 0)
+ to_chat(taker, span_warning("You can't [descriptor] [offerer] with no open hands!"))
+ taker.add_mood_event(descriptor, /datum/mood_event/high_five_full_hand) // not so successful now!
+ return COMPONENT_OFFER_INTERRUPT
+
+ playsound(offerer, 'sound/weapons/slap.ogg', min(50 * slappers_giver, 300), TRUE, 1)
+ offerer.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_DEUTERAGONIST = taker, DETAIL_HIGHFIVE_TYPE = descriptor), story_value = STORY_VALUE_OKAY)
+ taker.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_DEUTERAGONIST = offerer, DETAIL_HIGHFIVE_TYPE = descriptor), story_value = STORY_VALUE_OKAY)
+
+ if(high_ten)
+ to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
+ offerer.visible_message(
+ span_notice("[taker] enthusiastically high-tens [offerer]!"),
+ span_nicegreen("Wow! You're high-tenned [taker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ ignored_mobs = taker,
+ )
+
+ offerer.add_mood_event(descriptor, /datum/mood_event/high_ten)
+ taker.add_mood_event(descriptor, /datum/mood_event/high_ten)
+ else
+ to_chat(taker, span_nicegreen("You high-five [offerer]!"))
+ offerer.visible_message(
+ span_notice("[taker] high-fives [offerer]!"),
+ span_nicegreen("All right! You're high-fived by [taker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ ignored_mobs = taker,
+ )
+
+ offerer.add_mood_event(descriptor, /datum/mood_event/high_five)
+ taker.add_mood_event(descriptor, /datum/mood_event/high_five)
+
+ return COMPONENT_OFFER_INTERRUPT
diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm
index 55576bfc609..358b5c208fb 100644
--- a/code/game/objects/items/hand_items.dm
+++ b/code/game/objects/items/hand_items.dm
@@ -212,6 +212,10 @@
/// How many smaller table smacks we can do before we're out
var/table_smacks_left = 3
+/obj/item/hand_item/slapper/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/high_fiver)
+
/obj/item/hand_item/slapper/attack(mob/living/slapped, mob/living/carbon/human/user)
SEND_SIGNAL(user, COMSIG_LIVING_SLAP_MOB, slapped)
@@ -313,54 +317,12 @@
user.visible_message("[span_danger("[user] slams [user.p_their()] fist down on [table]!")]", "[span_danger("You slam your fist down on [table]!")]")
qdel(src)
-/obj/item/hand_item/slapper/on_offered(mob/living/carbon/offerer, mob/living/carbon/offered)
- . = TRUE
-
- if(!(locate(/mob/living/carbon) in orange(1, offerer)))
- visible_message(span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"), \
- span_warning("You post up, looking for a high-five, but finding no one within range!"), null, 2)
- return
-
- offerer.visible_message(span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"), \
- span_notice("You post up, looking for a high-five!"), null, 2)
- offerer.apply_status_effect(/datum/status_effect/offering, src, /atom/movable/screen/alert/give/highfive)
-
-/// Yeah broh! This is where we do the high-fiving (or high-tenning :o)
+// Successful takes will qdel our hand after
/obj/item/hand_item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
- . = TRUE
-
- var/open_hands_taker
- var/slappers_giver
- for(var/i in taker.held_items) // see how many hands the taker has open for high'ing
- if(isnull(i))
- open_hands_taker++
-
- if(!open_hands_taker)
- to_chat(taker, span_warning("You can't high-five [offerer] with no open hands!"))
- taker.add_mood_event("high_five", /datum/mood_event/high_five_full_hand) // not so successful now!
+ . = ..()
+ if(!.)
return
- for(var/i in offerer.held_items)
- var/obj/item/hand_item/slapper/slap_check = i
- if(istype(slap_check))
- slappers_giver++
-
- if(slappers_giver >= 2) // we only check this if it's already established the taker has 2+ hands free
- offerer.visible_message(span_notice("[taker] enthusiastically high-tens [offerer]!"), span_nicegreen("Wow! You're high-tenned [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
- to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
- playsound(offerer, 'sound/weapons/slap.ogg', 100, TRUE, 1)
- offerer.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_DEUTERAGONIST = taker, DETAIL_HIGHFIVE_TYPE = "high ten"), story_value = STORY_VALUE_OKAY)
- taker.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_DEUTERAGONIST = offerer, DETAIL_HIGHFIVE_TYPE = "high ten"), story_value = STORY_VALUE_OKAY)
- offerer.add_mood_event("high_five", /datum/mood_event/high_ten)
- taker.add_mood_event("high_five", /datum/mood_event/high_ten)
- else
- offerer.visible_message(span_notice("[taker] high-fives [offerer]!"), span_nicegreen("All right! You're high-fived by [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
- to_chat(taker, span_nicegreen("You high-five [offerer]!"))
- playsound(offerer, 'sound/weapons/slap.ogg', 50, TRUE, -1)
- offerer.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_DEUTERAGONIST = taker, DETAIL_HIGHFIVE_TYPE = "high five"), story_value = STORY_VALUE_OKAY)
- taker.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_DEUTERAGONIST = offerer, DETAIL_HIGHFIVE_TYPE = "high five"), story_value = STORY_VALUE_OKAY)
- offerer.add_mood_event("high_five", /datum/mood_event/high_five)
- taker.add_mood_event("high_five", /datum/mood_event/high_five)
qdel(src)
diff --git a/code/modules/spells/spell_types/touch/_touch.dm b/code/modules/spells/spell_types/touch/_touch.dm
index ce7fced2ce4..6fab59c54ef 100644
--- a/code/modules/spells/spell_types/touch/_touch.dm
+++ b/code/modules/spells/spell_types/touch/_touch.dm
@@ -132,6 +132,10 @@
RegisterSignal(attached_hand, COMSIG_ITEM_DROPPED, PROC_REF(on_hand_dropped))
RegisterSignal(attached_hand, COMSIG_PARENT_QDELETING, PROC_REF(on_hand_deleted))
+ // We can high five with our touch hand. It casts the spell on people. Radical
+ attached_hand.AddElement(/datum/element/high_fiver)
+ RegisterSignal(attached_hand, COMSIG_ITEM_OFFER_TAKEN, PROC_REF(on_hand_taken))
+
/// Unregisters all signal procs for the hand.
/datum/action/cooldown/spell/touch/proc/unregister_hand_signals()
SHOULD_CALL_PARENT(TRUE)
@@ -141,6 +145,7 @@
COMSIG_ITEM_AFTERATTACK_SECONDARY,
COMSIG_ITEM_DROPPED,
COMSIG_PARENT_QDELETING,
+ COMSIG_ITEM_OFFER_TAKEN,
))
// Touch spells don't go on cooldown OR give off an invocation until the hand is used itself.
@@ -289,6 +294,20 @@
remove_hand(dropper, reset_cooldown_after = TRUE)
+/**
+ * Signal proc for [COMSIG_ITEM_OFFER_TAKEN] from our attached hand.
+ *
+ * Giving a high five with our hand makes it cast
+ */
+/datum/action/cooldown/spell/touch/proc/on_hand_taken(obj/item/source, mob/living/carbon/offerer, mob/living/carbon/taker)
+ SIGNAL_HANDLER
+
+ if(!can_hit_with_hand(taker, offerer))
+ return
+
+ INVOKE_ASYNC(src, PROC_REF(do_hand_hit), source, taker, offerer)
+ return COMPONENT_OFFER_INTERRUPT
+
/**
* Called whenever our spell is cast, but blocked by antimagic.
*/
diff --git a/tgstation.dme b/tgstation.dme
index 1833abd9988..d863683a64c 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1155,6 +1155,7 @@
#include "code\datums\elements\forced_gravity.dm"
#include "code\datums\elements\frozen.dm"
#include "code\datums\elements\haunted.dm"
+#include "code\datums\elements\high_fiver.dm"
#include "code\datums\elements\honkspam.dm"
#include "code\datums\elements\item_fov.dm"
#include "code\datums\elements\item_scaling.dm"