diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 6e685160572..98f4f744024 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -883,6 +883,14 @@ #define COMSIG_ARMOR_PLATED "armor_plated" ///Called when an item gets recharged by the ammo powerup #define COMSIG_ITEM_RECHARGED "item_recharged" +///Called when an item is being offered, from [/obj/item/proc/on_offered(mob/living/carbon/offerer)] +#define COMSIG_ITEM_OFFERING "item_offering" + ///Interrupts the offer proc + #define COMPONENT_OFFER_INTERRUPT (1<<0) +///Called when an someone tries accepting an offered item, from [/obj/item/proc/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)] +#define COMSIG_ITEM_OFFER_TAKEN "item_offer_taken" + ///Interrupts the offer acceptance + #define COMPONENT_OFFER_TAKE_INTERRUPT (1<<0) ///from base of [/obj/item/proc/tool_check_callback]: (mob/living/user) #define COMSIG_TOOL_IN_USE "tool_in_use" diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index d018dd29e60..faf3e6a3db1 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -136,7 +136,7 @@ #define STATUS_EFFECT_HOLDUP /datum/status_effect/holdup // you are currently pointing a gun at someone -#define STATUS_EFFECT_HIGHFIVE /datum/status_effect/high_fiving // you are angling for a high five +#define STATUS_EFFECT_OFFERING /datum/status_effect/offering // you are offering up an item to people #define STATUS_EFFECT_SURRENDER /datum/status_effect/grouped/surrender // gives an alert to quickly surrender diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 6a5341b8d0b..5bae095928d 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -305,36 +305,29 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /atom/movable/screen/alert/give // information set when the give alert is made icon_state = "default" - var/mob/living/carbon/giver + var/mob/living/carbon/offerer var/obj/item/receiving /** * 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 giver + * and the item being offered, also registers a signal that removes the alert from anyone who moves away from the offerer * Arguments: * * taker - The person receiving the alert - * * giver - The person giving the alert and item - * * receiving - The item being given by the giver + * * 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/giver, obj/item/receiving) - name = "[giver] is offering [receiving]" - desc = "[giver] is offering [receiving]. Click this alert to take it." +/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." icon_state = "template" cut_overlays() add_overlay(receiving) src.receiving = receiving - src.giver = giver + src.offerer = offerer RegisterSignal(taker, COMSIG_MOVABLE_MOVED, .proc/check_in_range, override = TRUE) //Override to prevent runtimes when people offer a item multiple times -/atom/movable/screen/alert/give/proc/check_in_range(atom/taker) - SIGNAL_HANDLER - - if (!giver.CanReach(taker)) - to_chat(owner, span_warning("You moved out of range of [giver]!")) - owner.clear_alert("[giver]") - /atom/movable/screen/alert/give/Click(location, control, params) . = ..() if(!.) @@ -343,31 +336,68 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." if(!iscarbon(usr)) CRASH("User for [src] is of type \[[usr.type]\]. This should never happen.") - var/mob/living/carbon/C = owner - C.take(giver, receiving) + handle_transfer() -/atom/movable/screen/alert/highfive - icon_state = "default" - var/mob/living/carbon/giver - var/obj/item/slapper/slapper_item +/// An overrideable proc used simply to hand over the item when claimed, this is a proc so that high-fives can override them since nothing is actually transferred +/atom/movable/screen/alert/give/proc/handle_transfer() + var/mob/living/carbon/taker = owner + taker.take(offerer, receiving) -/atom/movable/screen/alert/highfive/proc/setup(mob/living/carbon/taker, mob/living/carbon/giver, obj/item/slapper/slap) - name = "[giver] is offering a high-five" - desc = "[giver] wants a high-five! Click this alert to take it." - icon_state = "template" - cut_overlays() - add_overlay(slap) - src.slapper_item = slap - src.giver = giver +/// Simply checks if the other person is still in range +/atom/movable/screen/alert/give/proc/check_in_range(atom/taker) + SIGNAL_HANDLER -/atom/movable/screen/alert/highfive/Click(location, control, params) + if(!offerer.CanReach(taker)) + to_chat(owner, span_warning("You moved out of range of [offerer]!")) + owner.clear_alert("[offerer]") + +/atom/movable/screen/alert/give/highfive/setup(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) . = ..() - if(!.) + 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/check_fake_out) + +/atom/movable/screen/alert/give/highfive/handle_transfer() + var/mob/living/carbon/taker = owner + if(receiving && (receiving in offerer.held_items)) + receiving.on_offer_taken(offerer, taker) return - var/datum/status_effect/high_fiving/high_five_effect = giver.has_status_effect(STATUS_EFFECT_HIGHFIVE) - if(high_five_effect) - high_five_effect.we_did_it(owner) + too_slow_p1() + +/// If the person who offered the high five no longer has it when we try to accept it, we get pranked hard +/atom/movable/screen/alert/give/highfive/proc/too_slow_p1() + var/mob/living/carbon/rube = owner + if(!rube || !offerer) + qdel(src) + return + + offerer.visible_message(span_notice("[rube] rushes in to high-five [offerer], but-"), span_nicegreen("[rube] falls for your trick just as planned, lunging for a high-five that no longer exists! Classic!"), ignored_mobs=rube) + to_chat(rube, span_nicegreen("You go in for [offerer]'s high-five, but-")) + addtimer(CALLBACK(src, .proc/too_slow_p2, offerer, rube), 0.5 SECONDS) + +/// Part two of the ultimate prank +/atom/movable/screen/alert/give/highfive/proc/too_slow_p2() + var/mob/living/carbon/rube = owner + if(!rube || !offerer) + qdel(src) + return + + offerer.visible_message(span_danger("[offerer] pulls away from [rube]'s slap at the last second, dodging the high-five entirely!"), span_nicegreen("[rube] fails to make contact with your hand, making an utter fool of [rube.p_them()]self!"), span_hear("You hear a disappointing sound of flesh not hitting flesh!"), ignored_mobs=rube) + var/all_caps_for_emphasis = uppertext("NO! [offerer] PULLS [offerer.p_their()] HAND AWAY FROM YOURS! YOU'RE TOO SLOW!") + to_chat(rube, span_userdanger("[all_caps_for_emphasis]")) + playsound(offerer, 'sound/weapons/thudswoosh.ogg', 100, TRUE, 1) + rube.Knockdown(1 SECONDS) + SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/down_low) + SEND_SIGNAL(rube, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/too_slow) + qdel(src) + +/// If someone examine_more's the offerer while they're trying to pull a too-slow, it'll tip them off to the offerer's trickster ways +/atom/movable/screen/alert/give/highfive/proc/check_fake_out(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + + if(!receiving) + examine_list += "[span_warning("[offerer]'s arm appears tensed up, as if [offerer.p_they()] plan on pulling it back suddenly...")]\n" /// Gives the player the option to succumb while in critical condition /atom/movable/screen/alert/succumb diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index 01cfea5ea85..c4950c8ba52 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -232,8 +232,12 @@ mood_change = 1.5 timeout = 2 MINUTES -/datum/mood_event/kiss/add_effects(mob/beau) - if(beau) +/datum/mood_event/kiss/add_effects(mob/beau, direct) + if(!beau) + return + if(direct) + description = "[beau.name] gave me a kiss, ahh!!\n" + else description = "[beau.name] blew a kiss at me, I must be a real catch!\n" /datum/mood_event/honorbound diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 76ecd882fb0..1cb77589444 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -151,166 +151,86 @@ icon_state = "aimed" // this status effect is used to negotiate the high-fiving capabilities of all concerned parties -/datum/status_effect/high_fiving - id = "high_fiving" +/datum/status_effect/offering + id = "offering" duration = -1 tick_interval = -1 status_type = STATUS_EFFECT_UNIQUE alert_type = null - /// The carbons who were offered the ability to partake in the high-five + /// The people who were offered this item at the start var/list/possible_takers - /// The actual slapper item - var/obj/item/slapper/slap_item + /// The actual item being offered + var/obj/item/offered_item + /// The type of alert given to people when offered, in case you need to override some behavior (like for high-fives) + var/give_alert_type = /atom/movable/screen/alert/give -/datum/status_effect/high_fiving/on_creation(mob/living/new_owner, obj/item/slap) +/datum/status_effect/offering/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override) . = ..() if(!.) return - slap_item = slap - owner.visible_message(span_notice("[owner] raises [owner.p_their()] arm, looking for a high-five!"), \ - span_notice("You post up, looking for a high-five!"), null, 2) + offered_item = offer + if(give_alert_override) + give_alert_type = give_alert_override for(var/mob/living/carbon/possible_taker in orange(1, owner)) - if(!owner.CanReach(possible_taker) || possible_taker.incapacitated()) + if(!owner.CanReach(possible_taker) || IS_DEAD_OR_INCAP(possible_taker) || !possible_taker.can_hold_items()) continue register_candidate(possible_taker) - if(!possible_takers) // in case we tried high-fiving with only a dead body around or something - owner.visible_message(span_danger("[owner] realizes no one within range is actually capable of high-fiving, lowering [owner.p_their()] arm in shame..."), \ - span_warning("You realize a moment too late that no one within range is actually capable of high-fiving you, oof..."), null, 2) - SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_alone) + if(!possible_takers) // no one around qdel(src) return RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/check_owner_in_range) - RegisterSignal(slap_item, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED), .proc/dropped_slap) - RegisterSignal(owner, COMSIG_PARENT_EXAMINE_MORE, .proc/check_fake_out) + RegisterSignal(offered_item, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED), .proc/dropped_item) + //RegisterSignal(owner, COMSIG_PARENT_EXAMINE_MORE, .proc/check_fake_out) -/datum/status_effect/high_fiving/Destroy() - QDEL_NULL(slap_item) +/datum/status_effect/offering/Destroy() for(var/i in possible_takers) - var/mob/living/carbon/lost_hope = i - remove_candidate(lost_hope) + var/mob/living/carbon/removed_taker = i + remove_candidate(removed_taker) LAZYCLEARLIST(possible_takers) return ..() -/// Hook up the specified carbon mob for possible high-fiving, give them the alert and signals and all -/datum/status_effect/high_fiving/proc/register_candidate(mob/living/carbon/possible_candidate) - var/atom/movable/screen/alert/highfive/G = possible_candidate.throw_alert("[owner]", /atom/movable/screen/alert/highfive) +/// Hook up the specified carbon mob to be offered the item in question, give them the alert and signals and all +/datum/status_effect/offering/proc/register_candidate(mob/living/carbon/possible_candidate) + var/atom/movable/screen/alert/give/G = possible_candidate.throw_alert("[owner]", give_alert_type) if(!G) return LAZYADD(possible_takers, possible_candidate) RegisterSignal(possible_candidate, COMSIG_MOVABLE_MOVED, .proc/check_taker_in_range) - G.setup(possible_candidate, owner, slap_item) + G.setup(possible_candidate, owner, offered_item) -/// Remove the alert and signals for the specified carbon mob -/datum/status_effect/high_fiving/proc/remove_candidate(mob/living/carbon/removed_candidate) +/// Remove the alert and signals for the specified carbon mob. Automatically removes the status effect when we lost the last taker +/datum/status_effect/offering/proc/remove_candidate(mob/living/carbon/removed_candidate) removed_candidate.clear_alert("[owner]") LAZYREMOVE(possible_takers, removed_candidate) UnregisterSignal(removed_candidate, COMSIG_MOVABLE_MOVED) - -/// We failed to high-five broh, either because there's no one viable next to us anymore, or we lost the slapper, or what -/datum/status_effect/high_fiving/proc/fail() - owner.visible_message(span_danger("[owner] slowly lowers [owner.p_their()] arm, realizing no one will high-five [owner.p_them()]! How embarassing..."), \ - span_warning("You realize the futility of continuing to wait for a high-five, and lower your arm..."), null, 2) - SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/left_hanging) - qdel(src) - -/// Yeah broh! This is where we do the high-fiving (or high-tenning :o) -/datum/status_effect/high_fiving/proc/we_did_it(mob/living/carbon/successful_taker) - var/open_hands_taker - var/slappers_owner - for(var/i in successful_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(successful_taker, span_warning("You can't high-five [owner] with no open hands!")) - SEND_SIGNAL(successful_taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now! - return - - for(var/i in owner.held_items) - var/obj/item/slapper/slap_check = i - if(istype(slap_check)) - slappers_owner++ - - if(!slappers_owner) // THE PRANKAGE - too_slow_p1(successful_taker) - return - - if(slappers_owner >= 2) // we only check this if it's already established the taker has 2+ hands free - owner.visible_message(span_notice("[successful_taker] enthusiastically high-tens [owner]!"), span_nicegreen("Wow! You're high-tenned [successful_taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=successful_taker) - to_chat(successful_taker, span_nicegreen("You give high-tenning [owner] your all!")) - playsound(owner, 'sound/weapons/slap.ogg', 100, TRUE, 1) - owner.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_PROTAGONIST = successful_taker, DETAIL_HIGHFIVE_TYPE = "high ten"), story_value = STORY_VALUE_OKAY) - successful_taker.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_PROTAGONIST = owner, DETAIL_HIGHFIVE_TYPE = "high ten"), story_value = STORY_VALUE_OKAY) - SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten) - SEND_SIGNAL(successful_taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten) - else - owner.visible_message(span_notice("[successful_taker] high-fives [owner]!"), span_nicegreen("All right! You're high-fived by [successful_taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=successful_taker) - to_chat(successful_taker, span_nicegreen("You high-five [owner]!")) - playsound(owner, 'sound/weapons/slap.ogg', 50, TRUE, -1) - owner.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_PROTAGONIST = successful_taker, DETAIL_HIGHFIVE_TYPE = "high five"), story_value = STORY_VALUE_OKAY) - successful_taker.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_PROTAGONIST = owner, DETAIL_HIGHFIVE_TYPE = "high five"), story_value = STORY_VALUE_OKAY) - SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five) - SEND_SIGNAL(successful_taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five) - qdel(src) - -/// If we don't have any slappers in hand when someone goes to high-five us, we prank the hell out of them -/datum/status_effect/high_fiving/proc/too_slow_p1(mob/living/carbon/rube) - owner.visible_message(span_notice("[rube] rushes in to high-five [owner], but-"), span_nicegreen("[rube] falls for your trick just as planned, lunging for a high-five that no longer exists! Classic!"), ignored_mobs=rube) - to_chat(rube, span_nicegreen("You go in for [owner]'s high-five, but-")) - addtimer(CALLBACK(src, .proc/too_slow_p2, rube), 0.5 SECONDS) - -/// Part two of the ultimate prank -/datum/status_effect/high_fiving/proc/too_slow_p2(mob/living/carbon/rube) - if(!owner || !rube) + if(!possible_takers && !QDELING(src)) qdel(src) - return - owner.visible_message(span_danger("[owner] pulls away from [rube]'s slap at the last second, dodging the high-five entirely!"), span_nicegreen("[rube] fails to make contact with your hand, making an utter fool of [rube.p_them()]self!"), span_hear("You hear a disappointing sound of flesh not hitting flesh!"), ignored_mobs=rube) - var/all_caps_for_emphasis = uppertext("NO! [owner] PULLS [owner.p_their()] HAND AWAY FROM YOURS! YOU'RE TOO SLOW!") - to_chat(rube, span_userdanger("[all_caps_for_emphasis]")) - playsound(owner, 'sound/weapons/thudswoosh.ogg', 100, TRUE, 1) - rube.Knockdown(1 SECONDS) - SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/down_low) - SEND_SIGNAL(rube, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/too_slow) - qdel(src) - -/// If someone examine_more's us while we don't have a slapper in hand, it'll tip them off to our trickster ways -/datum/status_effect/high_fiving/proc/check_fake_out(datum/source, mob/user, list/examine_list) - SIGNAL_HANDLER - - if(!slap_item) - examine_list += "[span_warning("[owner]'s arm appears tensed up, as if [owner.p_they()] plan on pulling it back suddenly...")]\n" /// One of our possible takers moved, see if they left us hanging -/datum/status_effect/high_fiving/proc/check_taker_in_range(mob/living/carbon/taker) +/datum/status_effect/offering/proc/check_taker_in_range(mob/living/carbon/taker) SIGNAL_HANDLER - if(owner.CanReach(taker) && !taker.incapacitated()) + if(owner.CanReach(taker) && !IS_DEAD_OR_INCAP(taker)) return - to_chat(taker, span_warning("You left [owner] hanging!")) remove_candidate(taker) - if(!possible_takers) - fail() -/// The propositioner moved, see if anyone is out of range now -/datum/status_effect/high_fiving/proc/check_owner_in_range(mob/living/carbon/source) +/// The offerer moved, see if anyone is out of range now +/datum/status_effect/offering/proc/check_owner_in_range(mob/living/carbon/source) SIGNAL_HANDLER for(var/i in possible_takers) var/mob/living/carbon/checking_taker = i - if(!istype(checking_taker) || !owner.CanReach(checking_taker) || checking_taker.incapacitated()) + if(!istype(checking_taker) || !owner.CanReach(checking_taker) || IS_DEAD_OR_INCAP(checking_taker)) remove_candidate(checking_taker) - if(!possible_takers) - fail() - -/// Something fishy is going on here... -/datum/status_effect/high_fiving/proc/dropped_slap(obj/item/source) +/// We lost the item, give it up +/datum/status_effect/offering/proc/dropped_item(obj/item/source) SIGNAL_HANDLER - slap_item = null + qdel(src) //this effect gives the user an alert they can use to surrender quickly /datum/status_effect/grouped/surrender diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index a4ed33294f3..68d781a0eb4 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1202,3 +1202,28 @@ attack_basic_mob /// Called on [/datum/element/openspace_item_click_handler/proc/on_afterattack]. Check the relative file for information. /obj/item/proc/handle_openspace_click(turf/target, mob/user, proximity_flag, click_parameters) stack_trace("Undefined handle_openspace_click() behaviour. Ascertain the openspace_item_click_handler element has been attached to the right item and that its proc override doesn't call parent.") + +/** + * * An interrupt for offering an item to other people, called mainly from [/mob/living/carbon/proc/give], in case you want to run your own offer behavior instead. + * + * * Return TRUE if you want to interrupt the offer. + * + * * Arguments: + * * offerer - the person offering the item + */ +/obj/item/proc/on_offered(mob/living/carbon/offerer) + if(SEND_SIGNAL(src, COMSIG_ITEM_OFFERING, offerer) & COMPONENT_OFFER_INTERRUPT) + return TRUE + +/** + * * An interrupt for someone trying to accept an offered item, called mainly from [/mob/living/carbon/proc/take], in case you want to run your own take behavior instead. + * + * * Return TRUE if you want to interrupt the taking. + * + * * Arguments: + * * offerer - the person offering the item + * * taker - the person trying to accept the offer + */ +/obj/item/proc/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) + if(SEND_SIGNAL(src, COMSIG_ITEM_OFFER_TAKEN, offerer, taker) & COMPONENT_OFFER_INTERRUPT) + return TRUE diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm index f0102b97448..7e2f3f4d565 100644 --- a/code/game/objects/items/hand_items.dm +++ b/code/game/objects/items/hand_items.dm @@ -102,62 +102,6 @@ to_chat(sucker, span_userdanger("[owner] bops you with [owner.p_their()] [src.name]!")) qdel(src) -/obj/item/slapper - name = "slapper" - desc = "This is how real men fight." - icon_state = "latexballon" - inhand_icon_state = "nothing" - force = 0 - throwforce = 0 - item_flags = DROPDEL | ABSTRACT | HAND_ITEM - attack_verb_continuous = list("slaps") - attack_verb_simple = list("slap") - hitsound = 'sound/effects/snap.ogg' - /// How many smaller table smacks we can do before we're out - var/table_smacks_left = 3 - -/obj/item/slapper/attack(mob/M, mob/living/carbon/human/user) - if(ishuman(M)) - var/mob/living/carbon/human/L = M - if(L && L.dna && L.dna.species) - L.dna.species.stop_wagging_tail(M) - user.do_attack_animation(M) - playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1) - if(user.zone_selected == BODY_ZONE_HEAD || user.zone_selected == BODY_ZONE_PRECISE_MOUTH) - user.visible_message(span_danger("[user] slaps [M] in the face!"), - span_notice("You slap [M] in the face!"),\ - span_hear("You hear a slap.")) - else - user.visible_message(span_danger("[user] slaps [M]!"), - span_notice("You slap [M]!"),\ - span_hear("You hear a slap.")) - return - -/obj/item/slapper/attack_atom(obj/O, mob/living/user, params) - if(!istype(O, /obj/structure/table)) - return ..() - - var/obj/structure/table/the_table = O - var/is_right_clicking = LAZYACCESS(params2list(params), RIGHT_CLICK) - - if(is_right_clicking && table_smacks_left == initial(table_smacks_left)) // so you can't do 2 weak slaps followed by a big slam - transform = transform.Scale(5) // BIG slap - if(HAS_TRAIT(user, TRAIT_HULK)) - transform = transform.Scale(2) - color = COLOR_GREEN - user.do_attack_animation(the_table) - SEND_SIGNAL(user, COMSIG_LIVING_SLAM_TABLE, the_table) - SEND_SIGNAL(the_table, COMSIG_TABLE_SLAMMED, user) - playsound(get_turf(the_table), 'sound/effects/tableslam.ogg', 110, TRUE) - user.visible_message("[span_danger("[user] slams [user.p_their()] fist down on [the_table]!")]", "[span_danger("You slam your fist down on [the_table]!")]") - qdel(src) - else - user.do_attack_animation(the_table) - playsound(get_turf(the_table), 'sound/effects/tableslam.ogg', 40, TRUE) - user.visible_message(span_notice("[user] slaps [user.p_their()] hand on [the_table]."), span_notice("You slap your hand on [the_table]."), vision_distance=COMBAT_MESSAGE_RANGE) - table_smacks_left-- - if(table_smacks_left <= 0) - qdel(src) /obj/item/noogie name = "noogie" @@ -255,6 +199,129 @@ noogie_loop(user, target, iteration) +/obj/item/slapper + name = "slapper" + desc = "This is how real men fight." + icon_state = "latexballon" + inhand_icon_state = "nothing" + force = 0 + throwforce = 0 + item_flags = DROPDEL | ABSTRACT | HAND_ITEM + attack_verb_continuous = list("slaps") + attack_verb_simple = list("slap") + hitsound = 'sound/effects/snap.ogg' + /// How many smaller table smacks we can do before we're out + var/table_smacks_left = 3 + +/obj/item/slapper/attack(mob/living/M, mob/living/carbon/human/user) + if(ishuman(M)) + var/mob/living/carbon/human/L = M + if(L && L.dna && L.dna.species) + L.dna.species.stop_wagging_tail(M) + user.do_attack_animation(M) + + var/slap_volume = 50 + var/datum/status_effect/offering/kiss_check = M.has_status_effect(STATUS_EFFECT_OFFERING) + if(kiss_check && istype(kiss_check.offered_item, /obj/item/kisser) && (user in kiss_check.possible_takers)) + user.visible_message(span_danger("[user] scoffs at [M]'s advance, winds up, and smacks [M.p_them()] hard to the ground!"), + span_notice("The nerve! You wind back your hand and smack [M] hard enough to knock [M.p_them()] over!"), + span_hear("You hear someone get the everloving shit smacked out of them!"), ignored_mobs = M) + to_chat(M, span_userdanger("You see [user] scoff and pull back [user.p_their()] arm, then suddenly you're on the ground with an ungodly ringing in your ears!")) + slap_volume = 120 + SEND_SOUND(M, sound('sound/weapons/flash_ring.ogg')) + shake_camera(M, 2, 2) + M.Paralyze(2.5 SECONDS) + M.add_confusion(7) + M.adjustStaminaLoss(40) + else if(user.zone_selected == BODY_ZONE_HEAD || user.zone_selected == BODY_ZONE_PRECISE_MOUTH) + user.visible_message(span_danger("[user] slaps [M] in the face!"), + span_notice("You slap [M] in the face!"), + span_hear("You hear a slap.")) + else + user.visible_message(span_danger("[user] slaps [M]!"), + span_notice("You slap [M]!"), + span_hear("You hear a slap.")) + playsound(M, 'sound/weapons/slap.ogg', slap_volume, TRUE, -1) + return + +/obj/item/slapper/attack_atom(obj/O, mob/living/user, params) + if(!istype(O, /obj/structure/table)) + return ..() + + var/obj/structure/table/the_table = O + var/is_right_clicking = LAZYACCESS(params2list(params), RIGHT_CLICK) + + if(is_right_clicking && table_smacks_left == initial(table_smacks_left)) // so you can't do 2 weak slaps followed by a big slam + transform = transform.Scale(5) // BIG slap + if(HAS_TRAIT(user, TRAIT_HULK)) + transform = transform.Scale(2) + color = COLOR_GREEN + user.do_attack_animation(the_table) + SEND_SIGNAL(user, COMSIG_LIVING_SLAM_TABLE, the_table) + SEND_SIGNAL(the_table, COMSIG_TABLE_SLAMMED, user) + playsound(get_turf(the_table), 'sound/effects/tableslam.ogg', 110, TRUE) + user.visible_message("[span_danger("[user] slams [user.p_their()] fist down on [the_table]!")]", "[span_danger("You slam your fist down on [the_table]!")]") + qdel(src) + else + user.do_attack_animation(the_table) + playsound(get_turf(the_table), 'sound/effects/tableslam.ogg', 40, TRUE) + user.visible_message(span_notice("[user] slaps [user.p_their()] hand on [the_table]."), span_notice("You slap your hand on [the_table]."), vision_distance=COMBAT_MESSAGE_RANGE) + table_smacks_left-- + if(table_smacks_left <= 0) + qdel(src) + +/obj/item/slapper/on_offered(mob/living/carbon/offerer) + . = 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(STATUS_EFFECT_OFFERING, src, /atom/movable/screen/alert/give/highfive) + +/// Yeah broh! This is where we do the high-fiving (or high-tenning :o) +/obj/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!")) + SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now! + return + + for(var/i in offerer.held_items) + var/obj/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_PROTAGONIST = taker, DETAIL_HIGHFIVE_TYPE = "high ten"), story_value = STORY_VALUE_OKAY) + taker.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_PROTAGONIST = offerer, DETAIL_HIGHFIVE_TYPE = "high ten"), story_value = STORY_VALUE_OKAY) + SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten) + SEND_SIGNAL(taker, COMSIG_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_PROTAGONIST = taker, DETAIL_HIGHFIVE_TYPE = "high five"), story_value = STORY_VALUE_OKAY) + taker.mind.add_memory(MEMORY_HIGH_FIVE, list(DETAIL_PROTAGONIST = offerer, DETAIL_HIGHFIVE_TYPE = "high five"), story_value = STORY_VALUE_OKAY) + SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five) + SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five) + qdel(src) + + + /obj/item/kisser name = "kiss" desc = "I want you all to know, everyone and anyone, to seal it with a kiss." @@ -266,7 +333,8 @@ item_flags = DROPDEL | ABSTRACT | HAND_ITEM /// The kind of projectile this version of the kiss blower fires var/kiss_type = /obj/projectile/kiss - + /// TRUE if the user was aiming anywhere but the mouth when they offer the kiss, if it's offered + var/cheek_kiss /obj/item/kisser/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() @@ -276,7 +344,6 @@ user.visible_message("[user] blows \a [blown_kiss] at [target]!", span_notice("You blow \a [blown_kiss] at [target]!")) //Shooting Code: - blown_kiss.spread = 0 blown_kiss.original = target blown_kiss.fired_from = user blown_kiss.firer = user // don't hit ourself that would be really annoying @@ -285,6 +352,34 @@ blown_kiss.fire() qdel(src) +/obj/item/kisser/on_offered(mob/living/carbon/offerer) + 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(STATUS_EFFECT_OFFERING, src) + return TRUE + +/obj/item/kisser/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) + var/obj/projectile/blown_kiss = new kiss_type(get_turf(offerer)) + offerer.visible_message("[offerer] gives [taker] \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!!", span_notice("You give [taker] \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!"), ignored_mobs = taker) + to_chat(taker, span_nicegreen("[offerer] gives you \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!")) + offerer.face_atom(taker) + taker.face_atom(offerer) + offerer.do_item_attack_animation(taker, used_item=src) + //We're still firing a shot here because I don't want to deal with some weird edgecase where direct impacting them with the projectile causes it to freak out because there's no angle or something + blown_kiss.original = taker + blown_kiss.fired_from = offerer + blown_kiss.firer = offerer // don't hit ourself that would be really annoying + blown_kiss.impacted = list(offerer = TRUE) // just to make sure we don't hit the wearer + blown_kiss.preparePixelProjectile(taker, offerer) + blown_kiss.suppressed = SUPPRESSED_VERY // this also means it's a direct offer + blown_kiss.fire() + qdel(src) + return TRUE // so the core offering code knows to halt + /obj/item/kisser/death name = "kiss of death" desc = "If looks could kill, they'd be this." @@ -325,8 +420,11 @@ */ /obj/projectile/kiss/proc/harmless_on_hit(mob/living/living_target) playsound(get_turf(living_target), hitsound, 100, TRUE) - living_target.visible_message(span_danger("[living_target] is hit by \a [src]."), span_userdanger("You're hit by \a [src]!"), vision_distance=COMBAT_MESSAGE_RANGE) - living_target.mind?.add_memory(MEMORY_KISS, list(DETAIL_PROTAGONIST = living_target, DETAIL_KISSER = firer), story_value = STORY_VALUE_OKAY, memory_flags = MEMORY_CHECK_BLINDNESS) + if(!suppressed) // direct + living_target.visible_message(span_danger("[living_target] is hit by \a [src]."), span_userdanger("You're hit by \a [src]!"), vision_distance=COMBAT_MESSAGE_RANGE) + + living_target.mind?.add_memory(MEMORY_KISS, list(DETAIL_PROTAGONIST = living_target, DETAIL_KISSER = firer), story_value = STORY_VALUE_OKAY) + SEND_SIGNAL(living_target, COMSIG_ADD_MOOD_EVENT, "kiss", /datum/mood_event/kiss, firer, suppressed) if(isliving(firer)) var/mob/living/kisser = firer kisser.mind?.add_memory(MEMORY_KISS, list(DETAIL_PROTAGONIST = living_target, DETAIL_KISSER = firer), story_value = STORY_VALUE_OKAY, memory_flags = MEMORY_CHECK_BLINDNESS) @@ -364,6 +462,7 @@ def_zone = BODY_ZONE_HEAD // let's keep it PG, people . = ..() if(isliving(target)) + SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "kiss", /datum/mood_event/kiss, firer, suppressed) try_fluster(target) /obj/projectile/kiss/death diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index 2beaae0c83e..7d3f4cd6898 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -158,13 +158,13 @@ return index && hand_bodyparts[index] /** - * Proc called when giving an item to another player + * Proc called when offering an item to another player * * This handles creating an alert and adding an overlay to it */ /mob/living/carbon/proc/give() - var/obj/item/receiving = get_active_held_item() - if(!receiving) + var/obj/item/offered_item = get_active_held_item() + if(!offered_item) to_chat(src, span_warning("You're not holding anything to give!")) return @@ -172,58 +172,46 @@ to_chat(src, span_warning("You're unable to offer anything in your current state!")) return - if(istype(receiving, /obj/item/slapper)) - offer_high_five(receiving) + if(has_status_effect(STATUS_EFFECT_OFFERING)) + to_chat(src, span_warning("You're already offering up something!")) return - visible_message(span_notice("[src] is offering [receiving]."), \ - span_notice("You offer [receiving]."), null, 2) - for(var/mob/living/carbon/C in orange(1, src)) //Fixed that, now it shouldn't be able to give benos stunbatons and IDs - if(!CanReach(C)) - continue - if(!C.can_hold_items()) - continue + if(offered_item.on_offered(src)) // see if the item interrupts with its own behavior + return - var/atom/movable/screen/alert/give/G = C.throw_alert("[src]", /atom/movable/screen/alert/give) - if(!G) - continue - G.setup(C, src, receiving) + visible_message(span_notice("[src] is offering [offered_item]."), \ + span_notice("You offer [offered_item]."), null, 2) + + apply_status_effect(STATUS_EFFECT_OFFERING, offered_item) /** * Proc called when the player clicks the give alert * - * Handles checking if the player taking the item has open slots and is in range of the giver + * Handles checking if the player taking the item has open slots and is in range of the offerer * Also deals with the actual transferring of the item to the players hands * Arguments: - * * giver - The person giving the original item - * * I - The item being given by the giver + * * offerer - The person giving the original item + * * I - The item being given by the offerer */ -/mob/living/carbon/proc/take(mob/living/carbon/giver, obj/item/I) - clear_alert("[giver]") - if(get_dist(src, giver) > 1) - to_chat(src, span_warning("[giver] is out of range! ")) +/mob/living/carbon/proc/take(mob/living/carbon/offerer, obj/item/I) + clear_alert("[offerer]") + if(get_dist(src, offerer) > 1) + to_chat(src, span_warning("[offerer] is out of range!")) return - if(!I || giver.get_active_held_item() != I) - to_chat(src, span_warning("[giver] is no longer holding the item they were offering! ")) + if(!I || offerer.get_active_held_item() != I) + to_chat(src, span_warning("[offerer] is no longer holding the item they were offering!")) return if(!get_empty_held_indexes()) to_chat(src, span_warning("You have no empty hands!")) return - if(!giver.temporarilyRemoveItemFromInventory(I)) - visible_message(span_notice("[giver] tries to hand over [I] but it's stuck to them....")) + + if(I.on_offer_taken(offerer, src)) // see if the item has special behavior for being accepted return - visible_message(span_notice("[src] takes [I] from [giver]"), \ - span_notice("You take [I] from [giver]")) + + if(!offerer.temporarilyRemoveItemFromInventory(I)) + visible_message(span_notice("[offerer] tries to hand over [I] but it's stuck to them....")) + return + + visible_message(span_notice("[src] takes [I] from [offerer]"), \ + span_notice("You take [I] from [offerer]")) put_in_hands(I) - -/// Spin-off of [/mob/living/carbon/proc/give] exclusively for high-fiving -/mob/living/carbon/proc/offer_high_five(obj/item/slap) - if(has_status_effect(STATUS_EFFECT_HIGHFIVE)) - return - if(!(locate(/mob/living/carbon) in orange(1, src))) - visible_message(span_danger("[src] raises [p_their()] arm, looking around for a high-five, but there's no one around! How embarassing..."), \ - span_warning("You post up, looking for a high-five, but finding no one within range! How embarassing..."), null, 2) - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_alone) - return - - apply_status_effect(STATUS_EFFECT_HIGHFIVE, slap)