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."