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)