diff --git a/code/__DEFINES/memory_defines.dm b/code/__DEFINES/memory_defines.dm index 30de201dc58..77c6f151ef7 100644 --- a/code/__DEFINES/memory_defines.dm +++ b/code/__DEFINES/memory_defines.dm @@ -60,6 +60,8 @@ #define MEMORY_BOMB_PRIMED "bomb" /// A memory of pulling off either a high five or a high ten #define MEMORY_HIGH_FIVE "highfive" +/// A memory of being elegantly helped up! +#define MEMORY_HELPED_UP "helped_up" /// A memory of getting borged #define MEMORY_BORGED "borged" /// A memory of dying! includes time of death diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index c441690962f..9d0cbc60b08 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -302,6 +302,8 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." icon_state = "default" var/mob/living/carbon/offerer var/obj/item/receiving + /// Additional text displayed in the description of the alert. + var/additional_desc_text = "Click this alert to take it." /atom/movable/screen/alert/give/Destroy() offerer = null @@ -312,21 +314,42 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." * Handles assigning most of the variables for the alert that pops up when an item is offered * * Handles setting the name, description and icon of the alert and tracking the person giving - * and the item being offered, also registers a signal that removes the alert from anyone who moves away from the offerer + * and the item being offered. * Arguments: * * taker - The person receiving the alert * * offerer - The person giving the alert and item * * receiving - The item being given by the offerer */ /atom/movable/screen/alert/give/proc/setup(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) - name = "[offerer] is offering [receiving]" - desc = "[offerer] is offering [receiving]. Click this alert to take it." + var/receiving_name = get_receiving_name(taker, offerer, receiving) + name = "[offerer] is offering [receiving_name]" + desc = "[offerer] is offering [receiving_name]. [additional_desc_text]" icon_state = "template" cut_overlays() add_overlay(receiving) src.receiving = receiving src.offerer = offerer + +/** + * Called right before `setup()`, to do any sort of logic to change the name of + * what's displayed as the name of what's being offered in the alert. Use this to + * add pronouns and the like, or to totally override the displayed name! + * Also the best place to make changes to `additional_desc_text` before `setup()` + * without having to override `setup()` entirely. + * + * Arguments: + * * taker - The person receiving the alert + * * offerer - The person giving the alert and item + * * receiving - The item being given by the offerer + * + * Returns a string that will be displayed in the alert, which is `receiving.name` + * by default. + */ +/atom/movable/screen/alert/give/proc/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) + return receiving.name + + /atom/movable/screen/alert/give/Click(location, control, params) . = ..() if(!.) @@ -343,12 +366,20 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." taker.take(offerer, receiving) SEND_SIGNAL(offerer, COMSIG_CARBON_ITEM_GIVEN, taker, receiving) + +/atom/movable/screen/alert/give/highfive + additional_desc_text = "Click this alert to slap it." + + +/atom/movable/screen/alert/give/highfive/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) + return "a high-five" + + /atom/movable/screen/alert/give/highfive/setup(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) . = ..() - name = "[offerer] is offering a high-five!" - desc = "[offerer] is offering a high-five! Click this alert to slap it." RegisterSignal(offerer, COMSIG_PARENT_EXAMINE_MORE, PROC_REF(check_fake_out)) + /atom/movable/screen/alert/give/highfive/handle_transfer() var/mob/living/carbon/taker = owner if(receiving && (receiving in offerer.held_items)) @@ -391,6 +422,17 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." if(!receiving) examine_list += "[span_warning("[offerer]'s arm appears tensed up, as if [offerer.p_they()] plan on pulling it back suddenly...")]\n" + +/atom/movable/screen/alert/give/hand/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) + additional_desc_text = "Click this alert to take it and let [offerer.p_them()] pull you around!" + return "[offerer.p_their()] [receiving]" + + +/atom/movable/screen/alert/give/hand/helping/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) + . = ..() + additional_desc_text = "Click this alert to let them help you up!" + + /atom/movable/screen/alert/give/secret_handshake icon_state = "default" diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index 06bd8209f94..7cd06ea3855 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -211,6 +211,20 @@ mood_change = 2 timeout = 45 SECONDS +/datum/mood_event/helped_up + description = "Helping them up felt good!" + mood_change = 2 + timeout = 45 SECONDS + +/datum/mood_event/helped_up/add_effects(mob/other_person, helper) + if(!other_person) + return + + if(helper) + description = "Helping [other_person] up felt good!" + else + description = "[other_person] helped me up, how nice of [other_person.p_them()]!" + /datum/mood_event/high_ten description = "AMAZING! A HIGH-TEN!" mood_change = 3 diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 312346ebcab..3fe5d94e548 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -171,12 +171,13 @@ if(give_alert_override) give_alert_type = give_alert_override - if(offered && owner.CanReach(offered) && !IS_DEAD_OR_INCAP(offered) && offered.can_hold_items()) + if(offered && is_taker_elligible(offered)) register_candidate(offered) else for(var/mob/living/carbon/possible_taker in orange(1, owner)) - if(!owner.CanReach(possible_taker) || IS_DEAD_OR_INCAP(possible_taker) || !possible_taker.can_hold_items()) + if(!is_taker_elligible(possible_taker)) continue + register_candidate(possible_taker) if(!possible_takers) // no one around @@ -234,6 +235,87 @@ SIGNAL_HANDLER qdel(src) +/** + * Is our taker valid as a target for the offering? Meant to be used when registering + * takers in `on_creation()`. You should override `additional_taker_check()` instead of this. + * + * Returns `TRUE` if the taker is valid as a target for the offering. + */ +/datum/status_effect/offering/proc/is_taker_elligible(mob/living/carbon/taker) + return owner.CanReach(taker) && !IS_DEAD_OR_INCAP(taker) && additional_taker_check(taker) + + +/** + * Additional checks added to `CanReach()` and `IS_DEAD_OR_INCAP()` in `is_taker_elligible()`. + * Should be what you override instead of `is_taker_elligible()`. By default, checks if the + * taker can hold items. + * + * Returns `TRUE` if the taker is valid as a target for the offering based on these + * additional checks. + */ +/datum/status_effect/offering/proc/additional_taker_check(mob/living/carbon/taker) + return taker.can_hold_items() + + +/** + * This status effect is meant only for items that you don't actually receive + * when offered, mostly useful for `/obj/item/hand_item` subtypes. + */ +/datum/status_effect/offering/no_item_received + + +/datum/status_effect/offering/no_item_received/additional_taker_check(mob/living/carbon/taker) + return TRUE + + +/** + * This status effect is meant only to be used for offerings that require the target to + * be resting (like when you're trying to give them a hand to help them up). + * Also doesn't require them to have their hands free (since you're not giving them + * anything). + */ +/datum/status_effect/offering/no_item_received/needs_resting + + +/datum/status_effect/offering/no_item_received/needs_resting/additional_taker_check(mob/living/carbon/taker) + return taker.body_position == LYING_DOWN + + +/datum/status_effect/offering/no_item_received/needs_resting/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override, mob/living/carbon/offered) + . = ..() + RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(check_owner_standing)) + + +/datum/status_effect/offering/no_item_received/needs_resting/register_candidate(mob/living/carbon/possible_candidate) + . = ..() + RegisterSignal(possible_candidate, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(check_candidate_resting)) + + +/datum/status_effect/offering/no_item_received/needs_resting/remove_candidate(mob/living/carbon/removed_candidate) + UnregisterSignal(removed_candidate, COMSIG_LIVING_SET_BODY_POSITION) + return ..() + + +/// Simple signal handler that ensures that, if the owner stops standing, the offer no longer stands either! +/datum/status_effect/offering/no_item_received/needs_resting/proc/check_owner_standing(mob/living/carbon/owner) + if(src.owner.body_position == STANDING_UP) + return + + // This doesn't work anymore if the owner is no longer standing up, sorry! + qdel(src) + + +/// Simple signal handler that ensures that, should a candidate now be standing up, the offer won't be standing for them anymore! +/datum/status_effect/offering/no_item_received/needs_resting/proc/check_candidate_resting(mob/living/carbon/candidate) + SIGNAL_HANDLER + + if(candidate.body_position == LYING_DOWN) + return + + // No longer lying down? You're no longer eligible to take the offer, sorry! + remove_candidate(candidate) + + /datum/status_effect/offering/secret_handshake id = "secret_handshake" give_alert_type = /atom/movable/screen/alert/give/secret_handshake diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index bfb2b93a3e8..824e2eac4f7 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1330,9 +1330,10 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e * * Return TRUE if you want to interrupt the offer. * * * Arguments: - * * offerer - the person offering the item + * * offerer - The person offering the item. + * * offered - The person being offered the item. */ -/obj/item/proc/on_offered(mob/living/carbon/offerer) +/obj/item/proc/on_offered(mob/living/carbon/offerer, mob/living/carbon/offered) if(SEND_SIGNAL(src, COMSIG_ITEM_OFFERING, offerer) & COMPONENT_OFFER_INTERRUPT) return TRUE diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm index 37939e70b1f..55576bfc609 100644 --- a/code/game/objects/items/hand_items.dm +++ b/code/game/objects/items/hand_items.dm @@ -313,7 +313,7 @@ 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) +/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))) @@ -363,6 +363,109 @@ taker.add_mood_event("high_five", /datum/mood_event/high_five) qdel(src) + +/obj/item/hand_item/hand + name = "hand" + desc = "Sometimes, you just want to act gentlemanly." + icon_state = "latexballon" + inhand_icon_state = "nothing" + + +/obj/item/hand_item/hand/pre_attack(mob/living/carbon/help_target, mob/living/carbon/helper, params) + if(!loc.Adjacent(help_target) || !istype(helper) || !istype(help_target)) + return ..() + + if(helper.resting) + to_chat(helper, span_warning("You can't act gentlemanly when you're lying down!")) + return TRUE + + +/obj/item/hand_item/hand/pre_attack_secondary(mob/living/carbon/help_target, mob/living/carbon/helper, params) + if(!loc.Adjacent(help_target) || !istype(helper) || !istype(help_target)) + return ..() + + if(helper.resting) + to_chat(helper, span_warning("You can't act gentlemanly when you're lying down!")) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + + return SECONDARY_ATTACK_CALL_NORMAL + + +/obj/item/hand_item/hand/attack(mob/living/carbon/target_mob, mob/living/carbon/user, params) + if(!loc.Adjacent(target_mob) || !istype(user) || !istype(target_mob)) + return TRUE + + user.give(target_mob) + return TRUE + + +/obj/item/hand_item/hand/on_offered(mob/living/carbon/offerer, mob/living/carbon/offered) + . = TRUE + + if(!istype(offerer)) + return + + if(offerer.body_position == LYING_DOWN) + to_chat(offerer, span_warning("You can't act gentlemanly when you're lying down!")) + return + + if(!offered) + offered = locate(/mob/living/carbon) in orange(1, offerer) + + if(offered && istype(offered) && offered.body_position == LYING_DOWN) + offerer.visible_message(span_notice("[offerer] offers [offerer.p_their()] hand to [offered], looking to help them up!"), + span_notice("You offer [offered] your hand, to try to help them up!"), null, 2) + + offerer.apply_status_effect(/datum/status_effect/offering/no_item_received/needs_resting, src, /atom/movable/screen/alert/give/hand/helping, offered) + return + + offerer.visible_message(span_notice("[offerer] extends out [offerer.p_their()] hand."), + span_notice("You extend out your hand."), null, 2) + + offerer.apply_status_effect(/datum/status_effect/offering/no_item_received, src, /atom/movable/screen/alert/give/hand) + return + + +/obj/item/hand_item/hand/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) + . = TRUE + + if(taker.body_position == LYING_DOWN) + taker.help_shake_act(offerer) + + if(taker.body_position == LYING_DOWN) + return // That didn't help them. Awkwaaaaard. + + offerer.visible_message(span_notice("[offerer] helps [taker] up!"), span_nicegreen("You help [taker] up!"), span_hear("You hear someone helping someone else up!"), ignored_mobs = taker) + to_chat(taker, span_nicegreen("You take [offerer]'s hand, letting [offerer.p_them()] help your up! How nice of them!")) + + offerer.mind.add_memory(MEMORY_HELPED_UP, list(DETAIL_DEUTERAGONIST = taker), story_value = STORY_VALUE_OKAY) + taker.mind.add_memory(MEMORY_HELPED_UP, list(DETAIL_DEUTERAGONIST = offerer), story_value = STORY_VALUE_OKAY) + + offerer.add_mood_event("helping_up", /datum/mood_event/helped_up, taker, TRUE) // Different IDs because you could be helped up and then help someone else up. + taker.add_mood_event("helped_up", /datum/mood_event/helped_up, offerer, FALSE) + + qdel(src) + return + + if(taker.buckled?.buckle_prevents_pull) + return // Can't start pulling them if they're buckled and that prevents pulls. + + // We do a little switcheroo to ensure that it displays the pulling message that mentions + // taking taker by their hands. + var/offerer_zone_selected = offerer.zone_selected + offerer.zone_selected = "r_arm" + var/did_we_pull = offerer.start_pulling(taker) // Will return either null or FALSE. We only want to silence FALSE. + offerer.zone_selected = offerer_zone_selected + + if(did_we_pull == FALSE) + return // That didn't work for one reason or the other. No need to display anything. + + to_chat(offerer, span_notice("[taker] takes your hand, allowing you to pull [taker.p_them()] along.")) + to_chat(taker, span_notice("You take [offerer]'s hand, which allows [offerer.p_them()] to pull you along. How polite!")) + + qdel(src) + + /obj/item/hand_item/stealer name = "steal" desc = "Your filthy little fingers are ready to commit crimes." @@ -427,14 +530,14 @@ blown_kiss.fire() qdel(src) -/obj/item/hand_item/kisser/on_offered(mob/living/carbon/offerer) +/obj/item/hand_item/kisser/on_offered(mob/living/carbon/offerer, mob/living/carbon/offered) if(!(locate(/mob/living/carbon) in orange(1, offerer))) return TRUE cheek_kiss = (offerer.zone_selected != BODY_ZONE_PRECISE_MOUTH) offerer.visible_message(span_notice("[offerer] leans in slightly, offering a kiss[cheek_kiss ? " on the cheek" : ""]!"), span_notice("You lean in slightly, indicating you'd like to offer a kiss[cheek_kiss ? " on the cheek" : ""]!"), null, 2) - offerer.apply_status_effect(/datum/status_effect/offering, src) + offerer.apply_status_effect(/datum/status_effect/offering/no_item_received, src) return TRUE /obj/item/hand_item/kisser/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index 3b6e962115a..bc9384de23b 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -134,6 +134,26 @@ qdel(N) to_chat(user, span_warning("You're incapable of slapping in your current state.")) + +/datum/emote/living/carbon/hand + key = "hand" + key_third_person = "hands" + hands_use_check = TRUE + + +/datum/emote/living/carbon/hand/run_emote(mob/user, params, type_override, intentional) + . = ..() + if(!.) + return + + var/obj/item/hand_item/hand/hand = new(user) + if(user.put_in_hands(hand)) + to_chat(user, span_notice("You ready your hand.")) + else + qdel(hand) + to_chat(user, span_warning("You're incapable of using your hand in your current state.")) + + /datum/emote/living/carbon/snap key = "snap" key_third_person = "snaps" diff --git a/strings/memories.json b/strings/memories.json index 7ebbe0b4d87..c09ff61cbc7 100644 --- a/strings/memories.json +++ b/strings/memories.json @@ -191,6 +191,13 @@ "What a %HIGHFIVE_TYPE! %PROTAGONIST %MOOD.", "Wow! %PROTAGONIST %MOOD!" ], + "helped_up_names":[ + "%PROTAGONIST gentlemanly helping up %DEUTERAGONIST." + ], + "helped_up_starts":[ + "%PROTAGONIST helping up %DEUTERAGONIST", + "%DEUTERAGONIST taking the hand offered graciously by %PROTAGONIST to get up" + ], "borged_names":[ "The borging of %PROTAGONIST." ],