diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index a3d9dbd8442..2e88f141f4a 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -1516,6 +1516,19 @@
spell.action.Grant(new_character)
return
+/datum/mind/proc/get_ghost(even_if_they_cant_reenter)
+ for(var/mob/dead/observer/G in dead_mob_list)
+ if(G.mind == src)
+ if(G.can_reenter_corpse || even_if_they_cant_reenter)
+ return G
+ break
+
+/datum/mind/proc/grab_ghost(force)
+ var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force)
+ . = G
+ if(G)
+ G.reenter_corpse()
+
//Initialisation procs
/mob/proc/mind_initialize()
if(mind)
diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm
index 42d82639129..2e462a49ea5 100644
--- a/code/game/gamemodes/antag_spawner.dm
+++ b/code/game/gamemodes/antag_spawner.dm
@@ -72,6 +72,12 @@
desc = "A magically infused bottle of blood, distilled from countless murder victims. Used in unholy rituals to attract horrifying creatures."
icon = 'icons/obj/wizard.dmi'
icon_state = "vial"
+ var/shatter_msg = "You shatter the bottle, no \
+ turning back now!"
+ var/veil_msg = "You sense a dark presence lurking \
+ just beyond the veil..."
+ var/objective_verb = "Kill"
+ var/mob/living/demon_type = /mob/living/simple_animal/slaughter
/obj/item/weapon/antag_spawner/slaughter_demon/attack_self(mob/user as mob)
if(user.z == ZLEVEL_CENTCOMM)//this is to make sure the wizard does NOT summon a demon from the Den..
@@ -88,9 +94,9 @@
if(candidates.len > 0)
var/mob/C = pick(candidates)
- spawn_antag(C, get_turf(src.loc), "Slaughter Demon", user)
- to_chat(user, "You shatter the bottle, no turning back now!")
- to_chat(user, "You sense a dark presence lurking just beyond the veil...")
+ spawn_antag(C, get_turf(src.loc), initial(demon_type.name), user)
+ to_chat(user, "[shatter_msg]")
+ to_chat(user, "[veil_msg]")
playsound(user.loc, 'sound/effects/Glassbr1.ogg', 100, 1)
qdel(src)
else
@@ -99,22 +105,34 @@
/obj/item/weapon/antag_spawner/slaughter_demon/spawn_antag(var/client/C, var/turf/T, var/type = "", mob/user as mob)
var /obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T)
- var/mob/living/simple_animal/slaughter/S = new /mob/living/simple_animal/slaughter/(holder)
+ var/mob/living/simple_animal/slaughter/S = new demon_type(holder)
S.vialspawned = TRUE
S.holder = holder
S.key = C.key
- S.mind.assigned_role = "Slaughter Demon"
- S.mind.special_role = "Slaughter Demon"
+ S.mind.assigned_role = S.name
+ S.mind.special_role = S.name
ticker.mode.traitors += S.mind
var/datum/objective/assassinate/KillDaWiz = new /datum/objective/assassinate
KillDaWiz.owner = S.mind
KillDaWiz.target = user.mind
- KillDaWiz.explanation_text = "Kill [user.real_name], the one who was foolish enough to summon you."
+ KillDaWiz.explanation_text = "[objective_verb] [user.real_name], the one who was foolish enough to summon you."
S.mind.objectives += KillDaWiz
var/datum/objective/KillDaCrew = new /datum/objective
KillDaCrew.owner = S.mind
- KillDaCrew.explanation_text = "Kill everyone else while you're at it."
+ KillDaCrew.explanation_text = "[objective_verb] everyone else while you're at it."
S.mind.objectives += KillDaCrew
S.mind.objectives += KillDaCrew
to_chat(S, "Objective #[1]: [KillDaWiz.explanation_text]")
to_chat(S, "Objective #[2]: [KillDaCrew.explanation_text]")
+
+
+/obj/item/weapon/antag_spawner/slaughter_demon/laughter
+ name = "vial of tickles"
+ desc = "A magically infused bottle of clown love, distilled from \
+ countless hugging attacks. Used in funny rituals to attract \
+ adorable creatures."
+ color = "#FF69B4" // HOT PINK
+ veil_msg = "You sense an adorable presence \
+ lurking just beyond the veil..."
+ objective_verb = "Hug and Tickle"
+ demon_type = /mob/living/simple_animal/slaughter/laughter
\ No newline at end of file
diff --git a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
index bab7c597a69..a0a1f9f30d1 100644
--- a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
+++ b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
@@ -59,8 +59,15 @@
sleep(6)
if(animation)
qdel(animation)
- for(var/i = 3; i > 0; i--)
- playsound(get_turf(src),'sound/misc/Demon_consume.ogg', 100, 1)
+ var/sound
+ if(istype(src, /mob/living/simple_animal/slaughter))
+ var/mob/living/simple_animal/slaughter/SD = src
+ sound = SD.feast_sound
+ else
+ sound = 'sound/misc/Demon_consume.ogg'
+
+ for(var/i in 1 to 3)
+ playsound(get_turf(src), sound, 100, 1)
sleep(30)
if(kidnapped)
to_chat(src, "You devour [kidnapped]. Your health is fully restored.")
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
index e255394e7af..ac32105229a 100644
--- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm
+++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
@@ -18,6 +18,8 @@
stop_automated_movement = 1
status_flags = CANPUSH
attack_sound = 'sound/misc/demon_attack1.ogg'
+ var/feast_sound = 'sound/misc/Demon_consume.ogg'
+ var/death_sound = 'sound/misc/demon_dies.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = INFINITY
@@ -47,6 +49,7 @@
You may Ctrl+Click on blood pools to travel through them, appearing and dissaapearing from the station at will. \
Pulling a dead or critical mob while you enter a pool will pull them in with you, allowing you to feast. \
You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. "
+ var/deathmessage = "screams in anger as it collapses into a puddle of viscera!"
/mob/living/simple_animal/slaughter/New()
@@ -88,8 +91,8 @@
/mob/living/simple_animal/slaughter/death()
..()
- playsound(get_turf(src),'sound/misc/demon_dies.ogg', 200, 1)
- visible_message("[src] screams in anger as it collapses into a puddle of viscera, its most recent meals spilling out of it.")
+ playsound(get_turf(src), death_sound, 200, 1)
+ visible_message("[src] [deathmessage].")
for(var/mob/living/M in consumed_mobs)
M.forceMove(get_turf(src))
ghostize()
@@ -181,6 +184,36 @@
/obj/item/organ/internal/heart/demon/Stop()
return 0 // Always beating.
+
+/mob/living/simple_animal/slaughter/laughter
+ // The laughter demon! It's everyone's best friend! It just wants to hug
+ // them so much, it wants to hug everyone at once!
+ name = "laughter demon"
+ real_name = "laughter demon"
+ desc = "A large, adorable creature covered in armor with pink bows."
+ speak_emote = list("giggles", "titters", "chuckles")
+ emote_hear = list("gaffaws", "laughs")
+ response_help = "hugs"
+ attacktext = "wildly tickles"
+
+ attack_sound = 'sound/items/bikehorn.ogg'
+ feast_sound = 'sound/spookoween/scary_horn2.ogg'
+ death_sound = 'sound/misc/sadtrombone.ogg'
+
+ icon_state = "bowmon"
+ icon_living = "bowmon"
+ deathmessage = "fades out, as all of its friends are released from its prison of hugs."
+ loot = list(/mob/living/simple_animal/pet/cat/kitten{name = "Laughter"})
+
+/mob/living/simple_animal/slaughter/laughter/death()
+ for(var/mob/living/M in consumed_mobs)
+ if(M.revive())
+ M.grab_ghost(force = TRUE)
+ playsound(get_turf(src), feast_sound, 50, 1, -1)
+ to_chat(M, "You leave the [src]'s warm embrace, and feel ready to take on the world.")
+ ..()
+
+
//Objectives and helpers.
//Objective info, Based on Reverent mini Atang
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index 8d3d7cc4278..ac487a8e703 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -363,6 +363,21 @@
limit = 3
category = "Assistance"
+/datum/spellbook_entry/item/hugbottle
+ name = "Bottle of Tickles"
+ desc = "A bottle of magically infused fun, the smell of which will \
+ attract adorable extradimensional beings when broken. These beings \
+ are similar to slaughter demons, but they do not permamently kill \
+ their victims, instead putting them in an extradimensional hugspace, \
+ to be released on the demon's death. Chaotic, but not ultimately \
+ damaging. The crew's reaction to the other hand could be very \
+ destructive."
+ item_path = /obj/item/weapon/antag_spawner/slaughter_demon/laughter
+ cost = 1 //non-destructive; it's just a jape, sibling!
+ log_name = "HB"
+ limit = 3
+ category = "Assistance"
+
/datum/spellbook_entry/item/tarotdeck
name = "Tarot Deck"
desc = "A deck of tarot cards that can be used to summon a spirit companion for the wizard."
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index f41cef386aa..7bce0e0e0cf 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1328,11 +1328,11 @@ mob/proc/yank_out_object()
/mob/proc/get_ghost(even_if_they_cant_reenter = 0)
if(mind)
- for(var/mob/dead/observer/G in dead_mob_list)
- if(G.mind == mind)
- if(G.can_reenter_corpse || even_if_they_cant_reenter)
- return G
- break
+ return mind.get_ghost(even_if_they_cant_reenter)
+
+/mob/proc/grab_ghost(force)
+ if(mind)
+ return mind.grab_ghost(force = force)
/mob/proc/notify_ghost_cloning(var/message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", var/sound = 'sound/effects/genetics.ogg', var/atom/source = null)
var/mob/dead/observer/ghost = get_ghost()
diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi
index 685e6b2977b..491349985d4 100644
Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ
diff --git a/sound/spookoween/scary_horn2.ogg b/sound/spookoween/scary_horn2.ogg
new file mode 100644
index 00000000000..fb2805bd2cd
Binary files /dev/null and b/sound/spookoween/scary_horn2.ogg differ