diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 5267e29a608..b2fb75348f9 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -140,6 +140,8 @@
/////////////
#define STATUS_EFFECT_HIGHFIVE /datum/status_effect/high_five
+#define STATUS_EFFECT_DAP /datum/status_effect/high_five/dap
+#define STATUS_EFFECT_HANDSHAKE /datum/status_effect/high_five/handshake
#define STATUS_EFFECT_CHARGING /datum/status_effect/charging
diff --git a/code/datums/keybindings/emote.dm b/code/datums/keybindings/emote.dm
index 74d85c16d3a..f698b56383d 100644
--- a/code/datums/keybindings/emote.dm
+++ b/code/datums/keybindings/emote.dm
@@ -338,7 +338,7 @@
name = "Cry"
/datum/keybinding/emote/carbon/human/dap
- linked_emote = /datum/emote/living/carbon/human/dap
+ linked_emote = /datum/emote/living/carbon/human/highfive/dap
name = "Dap"
/datum/keybinding/emote/carbon/human/eyebrow
@@ -418,7 +418,7 @@
name = "High Five"
/datum/keybinding/emote/carbon/human/handshake
- linked_emote = /datum/emote/living/carbon/human/handshake
+ linked_emote = /datum/emote/living/carbon/human/highfive/handshake
name = "Handshake"
/datum/keybinding/emote/carbon/human/snap
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index eb6bc4dfaef..915358e0d6f 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -47,25 +47,98 @@
/datum/status_effect/high_five
id = "high_five"
- duration = 5 SECONDS
+ duration = 10 SECONDS
alert_type = null
+ status_type = STATUS_EFFECT_REFRESH
+ /// Message displayed when wizards perform this together
+ var/critical_success = "high-five EPICALLY!"
+ /// Message displayed when normal people perform this together
+ var/success = "high-five!"
+ /// Message displayed when this status effect is applied.
+ var/request = "requests a high-five."
+ /// Item to be shown in the pop-up balloon.
+ var/obj/item/item_path = /obj/item/latexballon
+ /// Sound effect played when this emote is completed.
+ var/sound_effect = 'sound/weapons/slap.ogg'
+
+/// So we don't leave folks with god-mode
+/datum/status_effect/high_five/proc/wiz_cleanup(mob/user, mob/highfived)
+ user.status_flags &= ~GODMODE
+ highfived.status_flags &= ~GODMODE
+
+/datum/status_effect/high_five/on_apply()
+ if(!iscarbon(owner))
+ return FALSE
+ . = ..()
+
+ var/mob/living/carbon/user = owner
+ var/is_wiz = iswizard(user)
+ for(var/mob/living/carbon/C in orange(1, user))
+ if(!C.has_status_effect(type) || C == user)
+ continue
+ if(is_wiz && iswizard(C))
+ user.visible_message("[user.name] and [C.name] [critical_success]")
+ user.status_flags |= GODMODE
+ C.status_flags |= GODMODE
+ explosion(get_turf(user), 5, 2, 1, 3, cause = id)
+ // explosions have a spawn so this makes sure that we don't get gibbed
+ addtimer(CALLBACK(src, PROC_REF(wiz_cleanup), user, C), 1)
+ add_attack_logs(user, C, "caused a wizard [id] explosion")
+ user.remove_status_effect(type)
+ C.remove_status_effect(type)
+
+ user.do_attack_animation(C, no_effect = TRUE)
+ C.do_attack_animation(user, no_effect = TRUE)
+ user.visible_message("[user.name] and [C.name] [success]")
+ playsound(user, sound_effect, 80)
+ user.remove_status_effect(type)
+ C.remove_status_effect(type)
+ return FALSE
+
+ owner.custom_emote(EMOTE_VISIBLE, request)
+ owner.create_point_bubble_from_path(item_path, FALSE)
+
+/datum/status_effect/high_five/on_timeout()
+ owner.visible_message("[owner] [get_missed_message()]")
/datum/status_effect/high_five/proc/get_missed_message()
var/list/missed_highfive_messages = list(
- "it looks like [owner.p_they()] [owner.p_were()] left hanging...",
- "seeming to wave at nobody in particular.",
- "moving [owner.p_their()] hand directly to [owner.p_their()] forehead in shame.",
- "fully committing and high-fiving empty space.",
- "high-fiving [owner.p_their()] other hand shamefully before wiping away a tear.",
- "going for a handshake, then a fistbump, before pulling [owner.p_their()] hand back...? What [owner.p_are()] [owner.p_they()] doing?"
+ "lowers [owner.p_their()] hand, it looks like [owner.p_they()] [owner.p_were()] left hanging...",
+ "seems to awkwardly wave at nobody in particular.",
+ "moves [owner.p_their()] hand directly to [owner.p_their()] forehead in shame.",
+ "fully commits and high-fives empty space.",
+ "high-fives [owner.p_their()] other hand shamefully before wiping away a tear.",
+ "goes for a handshake, then a fistbump, before pulling [owner.p_their()] hand back...? What [owner.p_are()] [owner.p_they()] doing?"
)
return pick(missed_highfive_messages)
-/datum/status_effect/high_five/on_timeout()
- // show some emotionally damaging failure messages
- // high risk, high reward
- owner.visible_message("[owner] awkwardly lowers [owner.p_their()] hand, [get_missed_message()]")
+/datum/status_effect/high_five/dap
+ id = "dap"
+ critical_success = "dap each other up EPICALLY!"
+ success = "dap each other up!"
+ request = "requests someone to dap them up!"
+ sound_effect = 'sound/effects/snap.ogg'
+ item_path = /obj/item/melee/touch_attack/fake_disintegrate // EI-NATH!
+
+/datum/status_effect/high_five/dap/get_missed_message()
+ return "sadly can't find anybody to give daps to, and daps [owner.p_themselves()]. Shameful."
+
+/datum/status_effect/high_five/handshake
+ id = "handshake"
+ critical_success = "give each other an EPIC handshake!"
+ success = "give each other a handshake!"
+ request = "requests a handshake!"
+ sound_effect = "sound/weapons/thudswoosh.ogg"
+
+/datum/status_effect/high_five/handshake/get_missed_message()
+ var/list/missed_messages = list(
+ "drops [owner.p_their()] hand, shamefully.",
+ "grabs [owner.p_their()] outstretched hand with [owner.p_their()] other hand and gives [owner.p_themselves()] a handshake.",
+ "balls [owner.p_their()] hand into a fist, slowly bringing it back in."
+ )
+
+ return pick(missed_messages)
/datum/status_effect/charging
id = "charging"
diff --git a/code/game/objects/items/hand_item.dm b/code/game/objects/items/hand_item.dm
index 3d61ebacbb2..228f6863c02 100644
--- a/code/game/objects/items/hand_item.dm
+++ b/code/game/objects/items/hand_item.dm
@@ -18,6 +18,18 @@
if(force)
return ..()
+/obj/item/slapper/attack_self(mob/user)
+ . = ..()
+ if(!isliving(user))
+ return
+ var/mob/living/L = user
+ if(!L.has_status_effect(STATUS_EFFECT_HIGHFIVE))
+ L.apply_status_effect(STATUS_EFFECT_HIGHFIVE)
+ L.visible_message(
+ "",
+ "You raise your hand for a high-five!"
+ )
+
/obj/item/slapper/attack_obj(obj/O, mob/living/user, params)
if(!istype(O, /obj/structure/table))
return ..()
diff --git a/code/modules/mob/living/carbon/human/human_emote.dm b/code/modules/mob/living/carbon/human/human_emote.dm
index bfafb660ef5..a5ad06e4e3e 100644
--- a/code/modules/mob/living/carbon/human/human_emote.dm
+++ b/code/modules/mob/living/carbon/human/human_emote.dm
@@ -55,14 +55,6 @@
muzzled_noises = list("weak", "pathetic", "sad")
emote_type = EMOTE_AUDIBLE
-/datum/emote/living/carbon/human/dap
- key = "dap"
- key_third_person = "daps"
- message = "sadly can't find anybody to give daps to, and daps themself. Shameful."
- message_param = "give daps to %t."
- emote_target_type = EMOTE_TARGET_MOB
- hands_use_check = TRUE
-
/datum/emote/living/carbon/human/eyebrow
key = "eyebrow"
message = "raises an eyebrow."
@@ -283,9 +275,10 @@
/datum/emote/living/carbon/human/highfive
key = "highfive"
key_third_person = "highfives"
- message = "requests a highfive."
hands_use_check = TRUE
- cooldown = 3 SECONDS
+ cooldown = 5 SECONDS
+ /// Status effect to apply when this emote is used. Should be a subtype
+ var/status = STATUS_EFFECT_HIGHFIVE
/datum/emote/living/carbon/human/highfive/can_run_emote(mob/user, status_check, intentional)
. = ..()
@@ -293,81 +286,24 @@
if(user_carbon.restrained())
return FALSE
-/datum/emote/living/carbon/human/highfive/proc/wiz_cleanup(mob/user, mob/highfived)
- user.status_flags &= ~GODMODE
- highfived.status_flags &= ~GODMODE
-
/datum/emote/living/carbon/human/highfive/run_emote(mob/user, params, type_override, intentional)
var/mob/living/carbon/user_carbon = user
- if(user_carbon.has_status_effect(STATUS_EFFECT_HIGHFIVE))
- user.visible_message("[user.name] shakes [user.p_their()] hand around slightly, impatiently waiting for someone to high-five them.")
+ if(user_carbon.has_status_effect(status))
+ user.visible_message("[user.name] shakes [user.p_their()] hand around slightly, impatiently waiting for someone to [key].")
return TRUE
- user_carbon.apply_status_effect(STATUS_EFFECT_HIGHFIVE)
- for(var/mob/living/L in orange(1))
- if(L.has_status_effect(STATUS_EFFECT_HIGHFIVE) && L != user)
- if(iswizard(user) && iswizard(L))
- user.visible_message("[user.name] and [L.name] high-five EPICALLY!")
- user_carbon.status_flags |= GODMODE
- L.status_flags |= GODMODE
- explosion(get_turf(user), 5, 2, 1, 3)
- // explosions have a spawn so this makes sure that we don't get gibbed
- addtimer(CALLBACK(src, PROC_REF(wiz_cleanup), user_carbon, L), 1)
- user_carbon.remove_status_effect(STATUS_EFFECT_HIGHFIVE)
- L.remove_status_effect(STATUS_EFFECT_HIGHFIVE)
- return TRUE
- user.visible_message("[user.name] and [L.name] high-five!")
- playsound(user, 'sound/effects/snap.ogg', 50)
- user_carbon.remove_status_effect(STATUS_EFFECT_HIGHFIVE)
- L.remove_status_effect(STATUS_EFFECT_HIGHFIVE)
- return TRUE
+ user_carbon.apply_status_effect(status)
+
return ..()
-/datum/emote/living/carbon/human/handshake
+/datum/emote/living/carbon/human/highfive/dap
+ key = "dap"
+ status = STATUS_EFFECT_DAP
+ key_third_person = "daps"
+
+/datum/emote/living/carbon/human/highfive/handshake
key = "handshake"
- message = "holds out their hand."
- hands_use_check = TRUE
- emote_target_type = EMOTE_TARGET_MOB
- target_behavior = EMOTE_TARGET_BHVR_DEFAULT_TO_BASE
-
-/datum/emote/living/carbon/human/handshake/act_on_target(mob/user, target)
- . = ..()
- if(!target)
- user.visible_message(
- "[user] seems to shake hands with empty space.",
- "You shake the air's hand."
- )
- return EMOTE_ACT_STOP_EXECUTION
-
- if(!user.Adjacent(target) || !ishuman(target))
- message_param = "extends a hand towards %t."
- return TRUE
-
- var/mob/living/carbon/human/human_target = target
-
- if(!HAS_TRAIT(human_target, TRAIT_HANDS_BLOCKED) && !human_target.r_hand && !human_target.restrained())
- message_param = "shakes hands with %t."
- else
- message_param = "holds out [user.p_their()] hand to %t."
-
-/datum/emote/living/carbon/human/handshake/run_emote(mob/user, params, type_override, intentional)
- var/mob/living/target
- for(var/mob/living/A in oview(5, user))
- if(params == A.name)
- target = A
-
- if(!target)
- user.visible_message(
- "[user] seems to shake hands with empty space.",
- "You shake the air's hand."
- )
- return TRUE
-
- if(!HAS_TRAIT(target, TRAIT_HANDS_BLOCKED) && !target.r_hand && !target.restrained())
- message_param = "shakes hands with %t."
- else
- message_param = "holds out [user.p_their()] hand to %t."
-
- return ..()
+ key_third_person = "handshakes"
+ status = STATUS_EFFECT_HANDSHAKE
/datum/emote/living/carbon/human/snap
key = "snap"
diff --git a/code/modules/point/point.dm b/code/modules/point/point.dm
index a271cd77b93..d62393cad07 100644
--- a/code/modules/point/point.dm
+++ b/code/modules/point/point.dm
@@ -12,7 +12,7 @@
return
if((pointed_atom in src) || (pointed_atom.loc in src))
- create_point_bubble(pointed_atom)
+ create_point_bubble_from_atom(pointed_atom)
return
var/turf/tile = get_turf(pointed_atom)
@@ -24,9 +24,16 @@
animate(visual, pixel_x = (tile.x - our_tile.x) * world.icon_size + pointed_atom.pixel_x, pixel_y = (tile.y - our_tile.y) * world.icon_size + pointed_atom.pixel_y, time = 1.7, easing = EASE_OUT)
-/atom/movable/proc/create_point_bubble(atom/pointed_atom)
+/// Create a bubble pointing at a particular icon and icon state.
+/// See args for create_point_bubble_from_atom.
+/atom/movable/proc/create_point_bubble(mutable_appearance/pointed_atom_appearance, include_arrow = TRUE)
var/obj/effect/thought_bubble_effect = new
+ pointed_atom_appearance.layer = POINT_LAYER
+ pointed_atom_appearance.blend_mode = BLEND_INSET_OVERLAY
+ pointed_atom_appearance.pixel_x = 0
+ pointed_atom_appearance.pixel_y = 0
+
var/mutable_appearance/thought_bubble = mutable_appearance(
'icons/effects/effects.dmi',
thought_bubble_image,
@@ -34,17 +41,8 @@
appearance_flags = KEEP_APART,
)
- var/mutable_appearance/pointed_atom_appearance = new(pointed_atom.appearance)
- pointed_atom_appearance.blend_mode = BLEND_INSET_OVERLAY
- pointed_atom_appearance.layer = POINT_LAYER
- pointed_atom_appearance.pixel_x = 0
- pointed_atom_appearance.pixel_y = 0
thought_bubble.overlays += pointed_atom_appearance
- var/hover_outline_index = pointed_atom.get_filter("hover_outline")
- if (!isnull(hover_outline_index))
- pointed_atom_appearance.filters.Cut(hover_outline_index, hover_outline_index + 1)
-
thought_bubble.pixel_x = 16
thought_bubble.pixel_y = 32
thought_bubble.alpha = 200
@@ -66,6 +64,41 @@
QDEL_IN(thought_bubble_effect, 2 SECONDS)
+/**
+ * Create a point bubble towards a given item.
+ *
+ * Arguments:
+ * * pointed_atom - Atom to show in the bubble.
+ * * include_arrow - If true, show an arrow pointing downwards.
+ */
+/atom/movable/proc/create_point_bubble_from_atom(atom/pointed_atom, include_arrow = TRUE)
+ var/mutable_appearance/pointed_atom_appearance = new(pointed_atom.appearance)
+
+ var/hover_outline_index = pointed_atom.get_filter("hover_outline")
+ if (!isnull(hover_outline_index))
+ pointed_atom_appearance.filters.Cut(hover_outline_index, hover_outline_index + 1)
+
+ create_point_bubble(pointed_atom_appearance, include_arrow)
+
+/**
+ * Create a point bubble towards a given item, from an icon/icon state.
+ *
+ * Arguments:
+ * * icon - Icon source for the bubble's icon.
+ * * icon_state - Icon state for the bubble's icon.
+ * * include_arrow - If true, show an arrow pointing downwards.
+ */
+/atom/movable/proc/create_point_bubble_from_icons(icon, icon_state, include_arrow = TRUE)
+ var/mutable_appearance/pointed_atom_appearance = mutable_appearance(
+ icon,
+ icon_state,
+ )
+ create_point_bubble(pointed_atom_appearance, include_arrow)
+
+/// See above, this uses an uninstantiated path.
+/atom/movable/proc/create_point_bubble_from_path(atom/pointed_atom_path, include_arrow = TRUE)
+ create_point_bubble_from_icons(initial(pointed_atom_path.icon), initial(pointed_atom_path.icon_state), include_arrow)
+
/obj/effect/temp_visual/point
name = "arrow"
desc = "It's an arrow hanging in mid-air. There may be a wizard about."