diff --git a/code/datums/elements/wuv.dm b/code/datums/elements/wuv.dm
new file mode 100644
index 0000000000..84f327500f
--- /dev/null
+++ b/code/datums/elements/wuv.dm
@@ -0,0 +1,60 @@
+
+/datum/element/wuv //D'awwwww
+ element_flags = ELEMENT_BESPOKE
+ id_arg_index = 2
+ //the for the me emote proc call when petted.
+ var/pet_emote
+ //whether the emote is visible or audible
+ var/pet_type
+ //same as above, except when harmed. "You are going into orbit, you stupid mutt!"
+ var/punt_emote
+ //same as pet_type
+ var/punt_type
+ //mood typepath for the moodlet signal when petted.
+ var/pet_moodlet
+ //same as above but for harm
+ var/punt_moodlet
+
+/datum/element/wuv/Attach(datum/target, pet, pet_t, pet_mood, punt, punt_t, punt_mood)
+ . = ..()
+
+ if(!isliving(target))
+ return ELEMENT_INCOMPATIBLE
+
+ pet_emote = pet
+ pet_type = pet_t
+ punt_emote = punt
+ punt_type = punt_t
+ pet_moodlet = pet_mood
+ punt_moodlet = punt_mood
+
+ RegisterSignal(target, COMSIG_MOB_ATTACK_HAND, .proc/on_attack_hand)
+
+/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
+ var/mob/living/L = source
+
+ if(L.stat == DEAD)
+ return
+ //we want to delay the effect to be displayed after the mob is petted, not before.
+ switch(user.a_intent)
+ if(INTENT_HARM, INTENT_DISARM)
+ addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
+ if(INTENT_HELP)
+ addtimer(CALLBACK(src, .proc/pet_the_dog, source, user), 1)
+
+/datum/element/wuv/proc/pet_the_dog(mob/target, mob/user)
+ if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
+ return
+ new /obj/effect/temp_visual/heart(target.loc)
+ if(pet_emote)
+ target.emote("me", pet_type, pet_emote)
+ if(pet_moodlet && !CHECK_BITFIELD(target.flags_1, HOLOGRAM_1)) //prevents unlimited happiness petting park exploit.
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, pet_moodlet, target)
+
+/datum/element/wuv/proc/kick_the_dog(mob/target, mob/user)
+ if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
+ return
+ if(punt_emote)
+ target.emote("me", punt_type, punt_emote)
+ if(punt_moodlet && !CHECK_BITFIELD(target.flags_1, HOLOGRAM_1))
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, punt_moodlet, target)
diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm
index 94fd08535f..98a8eade59 100644
--- a/code/datums/mood_events/generic_positive_events.dm
+++ b/code/datums/mood_events/generic_positive_events.dm
@@ -23,10 +23,13 @@
mood_change = 3
timeout = 3000
-/datum/mood_event/pet_corgi
- description = "Corgis are adorable! I can't stop petting them!\n"
- mood_change = 3
- timeout = 3000
+/datum/mood_event/pet_animal
+ description = "Animals are adorable! I can't stop petting them!\n"
+ mood_change = 2
+ timeout = 5 MINUTES
+
+/datum/mood_event/pet_animal/add_effects(mob/animal)
+ description = "\The [animal.name] is adorable! I can't stop petting [animal.p_them()]!\n"
/datum/mood_event/honk
description = "Maybe clowns aren't so bad after all. Honk!\n"
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index bda309f7c7..00f7b8cab7 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -38,6 +38,10 @@
. = ..()
verbs += /mob/living/proc/lay_down
+/mob/living/simple_animal/pet/cat/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/wuv, "purrs!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "hisses!", EMOTE_AUDIBLE)
+
/mob/living/simple_animal/pet/cat/update_canmove()
..()
if(client && stat != DEAD)
@@ -229,24 +233,6 @@
stop_automated_movement = 1
walk_to(src,movement_target,0,3)
-/mob/living/simple_animal/pet/cat/attack_hand(mob/living/carbon/human/M)
- . = ..()
- switch(M.a_intent)
- if(INTENT_HELP)
- wuv(1, M)
- if(INTENT_HARM)
- wuv(-1, M)
-
-/mob/living/simple_animal/pet/cat/proc/wuv(change, mob/M)
- if(change)
- if(change > 0)
- if(M && stat != DEAD)
- new /obj/effect/temp_visual/heart(loc)
- emote("me", EMOTE_VISIBLE, "purrs!")
- else
- if(M && stat != DEAD)
- emote("me", EMOTE_VISIBLE, "hisses!")
-
/mob/living/simple_animal/pet/cat/cak //I told you I'd do it, Remie
name = "Keeki"
desc = "It's a cat made out of cake."
diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm
index 986e5c9b4d..2371cfd7f1 100644
--- a/code/modules/mob/living/simple_animal/friendly/dog.dm
+++ b/code/modules/mob/living/simple_animal/friendly/dog.dm
@@ -16,6 +16,11 @@
do_footstep = TRUE
can_be_held = TRUE
+
+/mob/living/simple_animal/pet/dog/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/wuv, "yaps_happily!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "growls!", EMOTE_AUDIBLE)
+
//Corgis and pugs are now under one dog subtype
/mob/living/simple_animal/pet/dog/corgi
@@ -268,7 +273,7 @@
return
if(!item_to_add)
user.visible_message("[user] pets [src].","You rest your hand on [src]'s head for a moment.")
- SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "pet_corgi", /datum/mood_event/pet_corgi)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, src, /datum/mood_event/pet_animal, src)
return
if(user && !user.temporarilyRemoveItemFromInventory(item_to_add))
@@ -639,22 +644,3 @@
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
setDir(i)
sleep(1)
-
-/mob/living/simple_animal/pet/dog/attack_hand(mob/living/carbon/human/M)
- . = ..()
- switch(M.a_intent)
- if(INTENT_HELP)
- wuv(1,M)
- if(INTENT_HARM)
- wuv(-1,M)
-
-/mob/living/simple_animal/pet/dog/proc/wuv(change, mob/M)
- if(change)
- if(change > 0)
- if(M && stat != DEAD) // Added check to see if this mob (the dog) is dead to fix issue 2454
- new /obj/effect/temp_visual/heart(loc)
- emote("me", EMOTE_VISIBLE, "yaps happily!")
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "pet_corgi", /datum/mood_event/pet_corgi)
- else
- if(M && stat != DEAD) // Same check here, even though emote checks it as well (poor form to check it only in the help case)
- emote("me", EMOTE_VISIBLE, "growls!")
diff --git a/tgstation.dme b/tgstation.dme
index deffee6617..1a79c32dcb 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -470,6 +470,7 @@
#include "code\datums\elements\_element.dm"
#include "code\datums\elements\cleaning.dm"
#include "code\datums\elements\earhealing.dm"
+#include "code\datums\elements\wuv.dm"
#include "code\datums\elements\ghost_role_eligibility.dm"
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"