diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm
index fc5f07bee1c..599f9151c40 100644
--- a/code/datums/mood_events/generic_negative_events.dm
+++ b/code/datums/mood_events/generic_negative_events.dm
@@ -315,6 +315,25 @@
mood_change = -3
timeout = 90 SECONDS
+/datum/mood_event/tail_lost
+ description = "My tail!! Why?!\n"
+ mood_change = -8
+ timeout = 10 MINUTES
+
+/datum/mood_event/tail_balance_lost
+ description = "I feel off-balance without my tail.\n"
+ mood_change = -2
+
+/datum/mood_event/tail_regained_right
+ description = "My tail is back, but that was traumatic...\n"
+ mood_change = -2
+ timeout = 5 MINUTES
+
+/datum/mood_event/tail_regained_wrong
+ description = "Is this some kind of sick joke?! This is NOT the right tail.\n"
+ mood_change = -12 // -8 for tail still missing + -4 bonus for being frakenstein's monster
+ timeout = 5 MINUTES
+
/datum/mood_event/burnt_wings
description = "MY PRECIOUS WINGS!!\n"
mood_change = -10
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index b19dd2d0cf4..85c73b9fd79 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -458,6 +458,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
C.dna.species.mutant_bodyparts -= "wings"
C.dna.features["wings"] = "None"
C.update_body()
+ clear_tail_moodlets(C)
C.remove_movespeed_modifier(/datum/movespeed_modifier/species)
@@ -2026,6 +2027,63 @@ GLOBAL_LIST_EMPTY(roundstart_races)
/datum/species/proc/is_wagging_tail(mob/living/carbon/human/H)
return FALSE
+/*
+ * This proc is called when a mob loses their tail.
+ *
+ * tail_owner - the owner of the tail (who holds our species datum)
+ * lost_tail - the tail that was removed
+ * on_species_init - whether or not this was called when the species was initialized, or if it was called due to an ingame means (like surgery)
+ */
+/datum/species/proc/on_tail_lost(mob/living/carbon/human/tail_owner, obj/item/organ/tail/lost_tail, on_species_init = FALSE)
+ SEND_SIGNAL(tail_owner, COMSIG_CLEAR_MOOD_EVENT, "right_tail_regained")
+ SEND_SIGNAL(tail_owner, COMSIG_CLEAR_MOOD_EVENT, "wrong_tail_regained")
+ stop_wagging_tail(tail_owner)
+
+ // If it's initializing the species, don't add moodlets
+ if(on_species_init)
+ return
+ // If we don't have a set tail, don't bother adding moodlets
+ if(!mutant_organs.len)
+ return
+
+ SEND_SIGNAL(tail_owner, COMSIG_ADD_MOOD_EVENT, "tail_lost", /datum/mood_event/tail_lost)
+ SEND_SIGNAL(tail_owner, COMSIG_ADD_MOOD_EVENT, "tail_balance_lost", /datum/mood_event/tail_balance_lost)
+
+/*
+ * This proc is called when a mob gains a tail.
+ *
+ * tail_owner - the owner of the tail (who holds our species datum)
+ * lost_tail - the tail that was added
+ * on_species_init - whether or not this was called when the species was initialized, or if it was called due to an ingame means (like surgery)
+ */
+/datum/species/proc/on_tail_regain(mob/living/carbon/human/tail_owner, obj/item/organ/tail/found_tail, on_species_init = FALSE)
+ SEND_SIGNAL(tail_owner, COMSIG_CLEAR_MOOD_EVENT, "tail_lost")
+ SEND_SIGNAL(tail_owner, COMSIG_CLEAR_MOOD_EVENT, "tail_balance_lost")
+
+ // If it's initializing the species, don't add moodlets
+ if(on_species_init)
+ return
+ // If we don't have a set tail, don't add moodlets
+ if(!mutant_organs.len)
+ return
+
+ if(found_tail.type in mutant_organs)
+ SEND_SIGNAL(tail_owner, COMSIG_ADD_MOOD_EVENT, "right_tail_regained", /datum/mood_event/tail_regained_right)
+ else
+ SEND_SIGNAL(tail_owner, COMSIG_ADD_MOOD_EVENT, "wrong_tail_regained", /datum/mood_event/tail_regained_wrong)
+
+/*
+ * Clears all tail related moodlets when they lose their species.
+ *
+ * former_tail_owner - the mob that was once a species with a tail and now is a different species
+ */
+/datum/species/proc/clear_tail_moodlets(mob/living/carbon/human/former_tail_owner)
+ SEND_SIGNAL(former_tail_owner, COMSIG_CLEAR_MOOD_EVENT, "tail_lost")
+ SEND_SIGNAL(former_tail_owner, COMSIG_CLEAR_MOOD_EVENT, "tail_balance_lost")
+ SEND_SIGNAL(former_tail_owner, COMSIG_CLEAR_MOOD_EVENT, "right_tail_regained")
+ SEND_SIGNAL(former_tail_owner, COMSIG_CLEAR_MOOD_EVENT, "wrong_tail_regained")
+ stop_wagging_tail(former_tail_owner)
+
/datum/species/proc/start_wagging_tail(mob/living/carbon/human/H)
/datum/species/proc/stop_wagging_tail(mob/living/carbon/human/H)
diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm
index db26556e36d..68aabf120ee 100644
--- a/code/modules/mob/living/carbon/human/species_types/felinid.dm
+++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm
@@ -58,7 +58,7 @@
mutantears = /obj/item/organ/ears
if(H.dna.features["tail_human"] == "Cat")
var/obj/item/organ/tail/cat/tail = new
- tail.Insert(H, drop_if_replaced = FALSE)
+ tail.Insert(H, special = TRUE, drop_if_replaced = FALSE)
else
mutant_organs = list()
return ..()
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index 11df1d36932..a64f53d2e09 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -40,7 +40,13 @@
foodtypes = RAW | MEAT | GROSS,\
volume = reagent_vol,\
after_eat = CALLBACK(src, .proc/OnEatFrom))
-
+/*
+ * Insert the organ into the select mob.
+ *
+ * M - the mob who will get our organ
+ * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ * drop_if_replaced - if there's an organ in the slot already, whether we drop it afterwards
+ */
/obj/item/organ/proc/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
if(!iscarbon(M) || owner == M)
return
@@ -64,7 +70,12 @@
A.Grant(M)
STOP_PROCESSING(SSobj, src)
-//Special is for instant replacement like autosurgeons
+/*
+ * Remove the organ from the select mob.
+ *
+ * M - the mob who owns our organ, that we're removing the organ from.
+ * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ */
/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE)
owner = null
if(M)
diff --git a/code/modules/surgery/organs/tails.dm b/code/modules/surgery/organs/tails.dm
index d8cf00be4bd..2ca70316d7a 100644
--- a/code/modules/surgery/organs/tails.dm
+++ b/code/modules/surgery/organs/tails.dm
@@ -8,17 +8,20 @@
slot = ORGAN_SLOT_TAIL
var/tail_type = "None"
-/obj/item/organ/tail/Remove(mob/living/carbon/human/H, special = 0)
- ..()
- if(H && H.dna && H.dna.species)
- H.dna.species.stop_wagging_tail(H)
+/obj/item/organ/tail/Insert(mob/living/carbon/human/tail_owner, special = FALSE, drop_if_replaced = TRUE)
+ . = ..()
+ tail_owner?.dna?.species?.on_tail_regain(tail_owner, src, special)
+
+/obj/item/organ/tail/Remove(mob/living/carbon/human/tail_owner, special = FALSE)
+ . = ..()
+ tail_owner?.dna?.species?.on_tail_lost(tail_owner, src, special)
/obj/item/organ/tail/cat
name = "cat tail"
desc = "A severed cat tail. Who's wagging now?"
tail_type = "Cat"
-/obj/item/organ/tail/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
+/obj/item/organ/tail/cat/Insert(mob/living/carbon/human/H, special = FALSE, drop_if_replaced = TRUE)
..()
if(istype(H))
var/default_part = H.dna.species.mutant_bodyparts["tail_human"]
@@ -26,7 +29,7 @@
H.dna.features["tail_human"] = H.dna.species.mutant_bodyparts["tail_human"] = tail_type
H.update_body()
-/obj/item/organ/tail/cat/Remove(mob/living/carbon/human/H, special = 0)
+/obj/item/organ/tail/cat/Remove(mob/living/carbon/human/H, special = FALSE)
..()
if(istype(H))
H.dna.features["tail_human"] = "None"
@@ -45,7 +48,7 @@
. = ..()
color = "#"+ random_color()
-/obj/item/organ/tail/lizard/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
+/obj/item/organ/tail/lizard/Insert(mob/living/carbon/human/H, special = FALSE, drop_if_replaced = TRUE)
..()
if(istype(H))
// Checks here are necessary so it wouldn't overwrite the tail of a lizard it spawned in
@@ -58,7 +61,7 @@
H.dna.features["spines"] = H.dna.species.mutant_bodyparts["spines"] = spines
H.update_body()
-/obj/item/organ/tail/lizard/Remove(mob/living/carbon/human/H, special = 0)
+/obj/item/organ/tail/lizard/Remove(mob/living/carbon/human/H, special = FALSE)
..()
if(istype(H))
H.dna.species.mutant_bodyparts -= "tail_lizard"
@@ -84,7 +87,7 @@
tail_type = "Monkey"
icon_state = "severedmonkeytail"
-/obj/item/organ/tail/monkey/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
+/obj/item/organ/tail/monkey/Insert(mob/living/carbon/human/H, special = FALSE, drop_if_replaced = TRUE)
..()
if(istype(H))
if(!("tail_monkey" in H.dna.species.mutant_bodyparts))
@@ -92,7 +95,7 @@
H.dna.features["tail_monkey"] = tail_type
H.update_body()
-/obj/item/organ/tail/monkey/Remove(mob/living/carbon/human/H, special = 0)
+/obj/item/organ/tail/monkey/Remove(mob/living/carbon/human/H, special = FALSE)
..()
if(istype(H))
H.dna.features["tail_monkey"] = "None"