diff --git a/code/__DEFINES/dcs/signals/signals.dm b/code/__DEFINES/dcs/signals/signals.dm
index 638b5220bc3c..9e034edeeb2c 100644
--- a/code/__DEFINES/dcs/signals/signals.dm
+++ b/code/__DEFINES/dcs/signals/signals.dm
@@ -527,7 +527,6 @@
#define COMSIG_CARBON_HUGGED "carbon_hugged"
///When a carbon mob is headpatted, this is called on the carbon that is headpatted. (mob/living/headpatter)
#define COMSIG_CARBON_HEADPAT "carbon_headpatted"
-
///When a carbon slips. Called on /turf/open/handle_slip()
#define COMSIG_ON_CARBON_SLIP "carbon_slip"
///When a carbon gets a vending machine tilted on them
diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm
index f5e51d1d59f4..a3b44b5c29e1 100644
--- a/code/datums/mood_events/generic_negative_events.dm
+++ b/code/datums/mood_events/generic_negative_events.dm
@@ -302,3 +302,18 @@
description = span_boldwarning("It isn't ending... it isn't ending, come on...\n")
mood_change = -18
timeout = 3 MINUTES
+
+/datum/mood_event/bad_touch_bear_hug
+ description = "I just got squeezed way too hard."
+ mood_change = -3
+ timeout = 2 MINUTES
+
+/datum/mood_event/rippedtail
+ description = "I ripped their tail right off, what have I done!\n"
+ mood_change = -5
+ timeout = 30 SECONDS
+
+/datum/mood_event/bad_boop
+ description = "Someone booped my nose... ACK!\n"
+ mood_change = -3
+ timeout = 4 MINUTES
diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm
index 1ab201bc0186..e35d798386c8 100644
--- a/code/datums/mood_events/generic_positive_events.dm
+++ b/code/datums/mood_events/generic_positive_events.dm
@@ -1,6 +1,11 @@
/datum/mood_event/hug
description = "Hugs are nice.\n"
mood_change = 1
+ timeout = 2
+
+/datum/mood_event/bear_hug
+ description = "I got squeezed very tightly, but it was quite nice."
+ mood_change = 2
timeout = 2 MINUTES
/datum/mood_event/betterhug
@@ -19,6 +24,14 @@
/datum/mood_event/besthug/add_effects(mob/friend)
description = "[friend.name] is great to be around, [friend.p_they()] makes me feel so happy!\n"
+/datum/mood_event/best_boop
+ description = "Someone booped my nose, they are silly!\n"
+ mood_change = 5
+ timeout = 4 MINUTES
+
+/datum/mood_event/best_boop/add_effects(mob/friend)
+ description = "[friend.name] booped my nose, [friend.p_they()] [friend.p_are()] silly!\n"
+
/datum/mood_event/warmhug
description = "Warm cozy hugs are the best!\n"
mood_change = 1
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index e5cc9709559c..8a90ac9fe614 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -582,8 +582,8 @@ GLOBAL_LIST_EMPTY(created_baseturf_lists)
/turf/proc/acid_melt()
return
-/turf/handle_fall(mob/faller)
- if(has_gravity(src))
+/turf/handle_fall(mob/faller, fall_sound_played)
+ if(has_gravity(src) && !fall_sound_played)
playsound(src, "bodyfall", 50, TRUE)
faller.drop_all_held_items()
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 62174120a60f..56b8fe2792d4 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -399,6 +399,8 @@
Paralyze(60)
/mob/living/carbon/proc/help_shake_act(mob/living/carbon/M)
+ var/datum/component/mood/hugger_mood = M.GetComponent(/datum/component/mood)
+ var/nosound = FALSE
if(on_fire)
to_chat(M, "You can't put [p_them()] out with just your bare hands!")
return
@@ -426,6 +428,22 @@
mothdust += 10;
if(istype(dna.species, /datum/species/moth))
M.mothdust += 10; // End WS edit
+
+ if(M.zone_selected == BODY_ZONE_PRECISE_MOUTH) // Nose boops!
+ nosound = TRUE
+ playsound(src, 'sound/effects/boop.ogg', 50, 0)
+ if (HAS_TRAIT(M, TRAIT_FRIENDLY))
+ M.visible_message(span_notice("[M] playfully boops your nose."), span_notice("You playfully boop [src]'s nose."))
+ if (hugger_mood.sanity >= SANITY_GREAT)
+ new /obj/effect/temp_visual/heart(loc)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "best_boop", /datum/mood_event/best_boop, M)
+ else
+ M.visible_message(span_notice("[M] boops [src]'s nose."), span_notice("You boop [src] on the nose."))
+ if(HAS_TRAIT(src, TRAIT_BADTOUCH))
+ to_chat(M, span_warning("A scowl forms on [src]'s face as you daringly press your finger against [p_their()] nose."))
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "bad_boop", /datum/mood_event/bad_boop, M)
+
+
else if(check_zone(M.zone_selected) == BODY_ZONE_HEAD) //Headpats!
SEND_SIGNAL(src, COMSIG_CARBON_HEADPAT, M)
M.visible_message("[M] gives [src] a pat on the head to make [p_them()] feel better!", \
@@ -440,10 +458,35 @@
if(HAS_TRAIT(src, TRAIT_BADTOUCH))
to_chat(M, "[src] looks visibly upset as you pat [p_them()] on the head.")
+// Tail pulls!
+ else if((M.zone_selected == BODY_ZONE_PRECISE_GROIN) && !isnull(src.getorgan(/obj/item/organ/tail)))
+ M.visible_message(span_notice("[M] pulls on [src]'s tail!"), \
+ null, span_hear("You hear a soft patter."), DEFAULT_MESSAGE_RANGE, list(M, src))
+ to_chat(M, span_notice("You pull on [src]'s tail!"))
+ to_chat(src, span_notice("[M] pulls on your tail!"))
+
+// Rips off fake tails
+ else if((M.zone_selected == BODY_ZONE_PRECISE_GROIN) && (istype(head, /obj/item/clothing/head/kitty) || istype(head, /obj/item/clothing/head/collectable/kitty)))
+ var/obj/item/clothing/head/faketail = head
+ M.visible_message(span_danger("[M] pulls on [src]'s tail... and it rips off!"), \
+ null, span_hear("You hear a ripping sound."), DEFAULT_MESSAGE_RANGE, list(M, src))
+ to_chat(M, span_danger("You pull on [src]'s tail... and it rips off!"))
+ to_chat(src, span_userdanger("[M] pulls on your tail... and it rips off!"))
+ playsound(loc, 'sound/effects/rip1.ogg', 75, TRUE)
+ dropItemToGround(faketail)
+ M.put_in_hands(faketail)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "rippedtail", /datum/mood_event/rippedtail)
+
else if(M.zone_selected == BODY_ZONE_CHEST || M.zone_selected == BODY_ZONE_PRECISE_GROIN) //WS Edit - Adds more help emotes
SEND_SIGNAL(src, COMSIG_CARBON_HUGGED, M)
SEND_SIGNAL(M, COMSIG_CARBON_HUG, M, src)
- M.visible_message("[M] hugs [src] to make [p_them()] feel better!", \
+ if (M.grab_state >= GRAB_AGGRESSIVE)
+ M.visible_message(span_notice("[M] embraces [src] in a tight bear hug!"), \
+ null, span_hear("You hear the rustling of clothes."), DEFAULT_MESSAGE_RANGE, list(M, src))
+ to_chat(M, span_notice("You wrap [src] into a tight bear hug!"))
+ to_chat(src, span_notice("[M] squeezes you super tightly in a firm bear hug!"))
+ else
+ M.visible_message("[M] hugs [src] to make [p_them()] feel better!", \
"You hug [src] to make [p_them()] feel better!")
if(istype(M.dna.species, /datum/species/moth)) //WS edit - moth dust from hugging
mothdust += 15;
@@ -455,12 +498,17 @@
// No moodlets for people who hate touches
if(!HAS_TRAIT(src, TRAIT_BADTOUCH))
- if(bodytemperature > M.bodytemperature)
- if(!HAS_TRAIT(M, TRAIT_BADTOUCH))
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/warmhug, src) // Hugger got a warm hug (Unless they hate hugs)
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/hug) // Reciver always gets a mood for being hugged
- else
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/warmhug, M) // You got a warm hug
+ if (M.grab_state >= GRAB_AGGRESSIVE)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/bear_hug)
+ if(bodytemperature > M.bodytemperature)
+ if(!HAS_TRAIT(M, TRAIT_BADTOUCH))
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/warmhug) // Hugger got a warm hug (Unless they hate hugs)
+ SEND_SIGNAL(M, "hug", /datum/mood_event/hug) // Receiver always gets a mood for being hugged
+ else
+ SEND_SIGNAL(M, "hug", /datum/mood_event/warmhug,) // You got a warm hug
+ else
+ if (M.grab_state >= GRAB_AGGRESSIVE)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/bad_touch_bear_hug)
// Let people know if they hugged someone really warm or really cold
if(M.bodytemperature > M.dna.species.bodytemp_heat_damage_limit)
@@ -474,7 +522,6 @@
to_chat(M, "It feels like [src] is freezing as you hug them.")
if(HAS_TRAIT(M, TRAIT_FRIENDLY))
- var/datum/component/mood/hugger_mood = M.GetComponent(/datum/component/mood)
if (hugger_mood.sanity >= SANITY_GREAT)
new /obj/effect/temp_visual/heart(loc)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "friendly_hug", /datum/mood_event/besthug, M)
@@ -503,8 +550,17 @@
AdjustParalyzed(-60)
AdjustImmobilized(-60)
set_resting(FALSE)
-
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ if(!nosound)
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+
+// Shake animation
+#define SHAKE_ANIMATION_OFFSET (4)
+ if (incapacitated())
+ var/direction = prob(50) ? -1 : 1
+ animate(src, pixel_x = pixel_x + SHAKE_ANIMATION_OFFSET * direction, time = 1, easing = QUAD_EASING | EASE_OUT, flags = ANIMATION_PARALLEL)
+ animate(pixel_x = pixel_x - (SHAKE_ANIMATION_OFFSET * 2 * direction), time = 1)
+ animate(pixel_x = pixel_x + SHAKE_ANIMATION_OFFSET * direction, time = 1, easing = QUAD_EASING | EASE_IN)
+#undef SHAKE_ANIMATION_OFFSET
/// Check ourselves to see if we've got any shrapnel, return true if we do. This is a much simpler version of what humans do, we only indicate we're checking ourselves if there's actually shrapnel
/mob/living/carbon/proc/check_self_for_injuries()
diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm
index 5e5e8fca6d24..358fa0626092 100644
--- a/code/modules/mob/living/carbon/emote.dm
+++ b/code/modules/mob/living/carbon/emote.dm
@@ -39,25 +39,6 @@
key = "blink_r"
message = "blinks rapidly."
-/datum/emote/living/carbon/clap
- key = "clap"
- key_third_person = "claps"
- message = "claps."
- muzzle_ignore = TRUE
- hands_use_check = TRUE
- emote_type = EMOTE_AUDIBLE
- vary = TRUE
-
-/datum/emote/living/carbon/clap/get_sound(mob/living/user)
- if(ishuman(user))
- if(!user.get_bodypart(BODY_ZONE_L_ARM) || !user.get_bodypart(BODY_ZONE_R_ARM))
- return
- else
- return pick('sound/misc/clap1.ogg',
- 'sound/misc/clap2.ogg',
- 'sound/misc/clap3.ogg',
- 'sound/misc/clap4.ogg')
-
/datum/emote/living/carbon/crack
key = "crack"
key_third_person = "cracks"
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 56ae0db795e5..f4042464f981 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -232,6 +232,16 @@
message = "jumps!"
hands_use_check = TRUE
+/datum/emote/living/jump/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_y = user.pixel_y + 4, time = 0.1 SECONDS)
+ animate(pixel_y = user.pixel_y - 4, time = 0.1 SECONDS)
+
+/datum/emote/living/jump/get_sound(mob/living/user)
+ return 'sound/weapons/thudswoosh.ogg'
+
/datum/emote/living/kiss
key = "kiss"
key_third_person = "kisses"
@@ -361,6 +371,18 @@
message = "shivers."
emote_type = EMOTE_AUDIBLE
+#define SHIVER_LOOP_DURATION (1 SECONDS)
+/datum/emote/living/shiver/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+ for(var/i in 1 to SHIVER_LOOP_DURATION / (0.2 SECONDS)) //desired total duration divided by the iteration duration to give the necessary iteration count
+ animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+#undef SHIVER_LOOP_DURATION
+
/datum/emote/living/sigh
key = "sigh"
key_third_person = "sighs"
@@ -460,20 +482,62 @@
key_third_person = "sways"
message = "sways around dizzily."
+/datum/emote/living/sway/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x + 2, time = 0.5 SECONDS)
+ for(var/i in 1 to 2)
+ animate(pixel_x = user.pixel_x - 4, time = 1.0 SECONDS)
+ animate(pixel_x = user.pixel_x + 4, time = 1.0 SECONDS)
+ animate(pixel_x = user.pixel_x - 2, time = 0.5 SECONDS)
+
/datum/emote/living/tremble
key = "tremble"
key_third_person = "trembles"
message = "trembles in fear!"
+#define TREMBLE_LOOP_DURATION (4.4 SECONDS)
+/datum/emote/living/tremble/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x + 2, time = 0.2 SECONDS)
+ for(var/i in 1 to TREMBLE_LOOP_DURATION / (0.4 SECONDS)) //desired total duration divided by the iteration duration to give the necessary iteration count
+ animate(pixel_x = user.pixel_x - 2, time = 0.2 SECONDS)
+ animate(pixel_x = user.pixel_x + 2, time = 0.2 SECONDS)
+ animate(pixel_x = user.pixel_x - 2, time = 0.2 SECONDS)
+#undef TREMBLE_LOOP_DURATION
+
/datum/emote/living/twitch
key = "twitch"
key_third_person = "twitches"
message = "twitches violently."
+/datum/emote/living/twitch/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+ animate(time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+
/datum/emote/living/twitch_s
key = "twitch_s"
message = "twitches."
+/datum/emote/living/twitch_s/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ animate(user, pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+ animate(time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS)
+ animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS)
+
/datum/emote/living/wave
key = "wave"
key_third_person = "waves"
@@ -603,3 +667,85 @@
key_third_person = "clacks"
message = "clacks their beak."
emote_type = EMOTE_VISIBLE
+
+/datum/emote/living/tilt
+ key = "tilt"
+ key_third_person = "tilts"
+ message = "tilts their head to the side."
+
+/datum/emote/living/carbon/snap
+ key = "snap"
+ key_third_person = "snaps"
+ message = "snaps their fingers."
+ message_param = "snaps their fingers at %t."
+ emote_type = EMOTE_AUDIBLE
+ hands_use_check = TRUE
+ muzzle_ignore = TRUE
+
+/datum/emote/living/carbon/snap/get_sound(mob/living/user)
+ if(ishuman(user))
+ if(!user.get_bodypart(BODY_ZONE_L_ARM) || !user.get_bodypart(BODY_ZONE_R_ARM))
+ return
+ else
+ return pick('sound/misc/fingersnap1.ogg',
+ 'sound/misc/fingersnap2.ogg')
+
+/datum/emote/living/snap2
+ key = "snap2"
+ key_third_person = "snaps twice"
+ message = "snaps twice."
+ message_param = "snaps twice at %t."
+ emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = TRUE
+ hands_use_check = TRUE
+ vary = TRUE
+ sound = 'sound/misc/snap2.ogg'
+
+/datum/emote/living/snap3
+ key = "snap3"
+ key_third_person = "snaps thrice"
+ message = "snaps thrice."
+ message_param = "snaps thrice at %t."
+ emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = TRUE
+ hands_use_check = TRUE
+ vary = TRUE
+ sound = 'sound/misc/snap3.ogg'
+
+/datum/emote/living/carbon/clap
+ key = "clap"
+ key_third_person = "claps"
+ message = "claps."
+ muzzle_ignore = TRUE
+ hands_use_check = TRUE
+ emote_type = EMOTE_AUDIBLE
+ vary = TRUE
+
+/datum/emote/living/carbon/clap/get_sound(mob/living/user)
+ if(ishuman(user))
+ if(!user.get_bodypart(BODY_ZONE_L_ARM) || !user.get_bodypart(BODY_ZONE_R_ARM))
+ return
+ else
+ return pick('sound/misc/clap1.ogg',
+ 'sound/misc/clap2.ogg',
+ 'sound/misc/clap3.ogg',
+ 'sound/misc/clap4.ogg')
+
+/datum/emote/living/clap1
+ key = "clap1"
+ key_third_person = "claps once"
+ message = "claps once."
+ emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = TRUE
+ hands_use_check = TRUE
+ vary = TRUE
+ mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai)
+
+/datum/emote/living/clap1/get_sound(mob/living/user)
+ return pick('sound/misc/claponce1.ogg',
+ 'sound/misc/claponce2.ogg')
+
+/datum/emote/living/clap1/can_run_emote(mob/living/carbon/user, status_check = TRUE , intentional)
+ if(user.usable_hands < 2)
+ return FALSE
+ return ..()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 741dfcc16015..5e3442ba039a 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -484,7 +484,9 @@
if(!silent)
to_chat(src, "You will now lay down as soon as you are able to.")
else
- if(!silent)
+ if(!silent && m_intent == MOVE_INTENT_WALK)
+ to_chat(src, "You gently lay down.")
+ else if(!silent)
to_chat(src, "You lay down.")
set_lying_down()
else
@@ -1787,12 +1789,15 @@ GLOBAL_VAR_INIT(ssd_indicator_overlay, mutable_appearance('icons/mob/ssd_indicat
/// Changes the value of the [living/body_position] variable.
-/mob/living/proc/set_body_position(new_value)
+/mob/living/proc/set_body_position(new_value, fall_sound_played)
if(body_position == new_value)
return
. = body_position
body_position = new_value
if(new_value == LYING_DOWN) // From standing to lying down.
+ if(has_gravity() && m_intent != MOVE_INTENT_WALK)
+ playsound(src, "bodyfall", 50, TRUE) // Will play the falling sound if not walking
+ fall_sound_played = TRUE
on_lying_down()
else // From lying down to standing up.
on_standing_up()
diff --git a/sound/effects/Nose_boop.ogg b/sound/effects/Nose_boop.ogg
new file mode 100644
index 000000000000..6a742e95eac6
Binary files /dev/null and b/sound/effects/Nose_boop.ogg differ
diff --git a/sound/effects/boop.ogg b/sound/effects/boop.ogg
new file mode 100644
index 000000000000..482a2a8ecdab
Binary files /dev/null and b/sound/effects/boop.ogg differ
diff --git a/sound/misc/claponce1.ogg b/sound/misc/claponce1.ogg
new file mode 100644
index 000000000000..1e12d0daa3b5
Binary files /dev/null and b/sound/misc/claponce1.ogg differ
diff --git a/sound/misc/claponce2.ogg b/sound/misc/claponce2.ogg
new file mode 100644
index 000000000000..10dfdba121b4
Binary files /dev/null and b/sound/misc/claponce2.ogg differ
diff --git a/sound/misc/fingersnap1.ogg b/sound/misc/fingersnap1.ogg
new file mode 100644
index 000000000000..2d5d255be1ce
Binary files /dev/null and b/sound/misc/fingersnap1.ogg differ
diff --git a/sound/misc/fingersnap2.ogg b/sound/misc/fingersnap2.ogg
new file mode 100644
index 000000000000..d11f2f7a7415
Binary files /dev/null and b/sound/misc/fingersnap2.ogg differ
diff --git a/sound/misc/snap2.ogg b/sound/misc/snap2.ogg
new file mode 100644
index 000000000000..1537084be43c
Binary files /dev/null and b/sound/misc/snap2.ogg differ
diff --git a/sound/misc/snap3.ogg b/sound/misc/snap3.ogg
new file mode 100644
index 000000000000..ca7506dc2c36
Binary files /dev/null and b/sound/misc/snap3.ogg differ