diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index c895b91efe3..455acb7bf09 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -607,6 +607,24 @@
category = "Summons"
limit = 3
+/datum/spellbook_entry/item/shadowbottle
+ name = "Bottle of Shadows"
+ desc = "A bottle of pure darkness, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned from the shadows are indiscriminate in their killing, and you yourself may become a victim."
+ item_path = /obj/item/antag_spawner/slaughter_demon/shadow
+ log_name = "BOS"
+ category = "Summons"
+ limit = 3
+ cost = 1 //Unless you blackout the station this ain't going to do much, wizard doesn't get NV, still dies easily to a group of 2 and it doesn't eat bodies.
+
+/datum/spellbook_entry/item/revenantbottle
+ name = "Bottle of Ectoplasm"
+ desc = "A magically infused bottle of ectoplasm, effectivly pure salt from the spectral realm. Be careful though, these salty spirits are indiscriminate in their harvesting, and you yourself may become a victim."
+ item_path = /obj/item/antag_spawner/revenant
+ log_name = "RB"
+ category = "Summons"
+ limit = 3
+ cost = 1 //Needs essence to live. Needs crew to die for essence, doubt xenobio will be making many monkeys. As such, weaker. Also can hardstun the wizard.
+
/datum/spellbook_entry/item/contract
name = "Contract of Apprenticeship"
desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side."
@@ -725,6 +743,11 @@
for(var/datum/spellbook_entry/item/hugbottle/HB in entries)
if(!isnull(HB.limit))
HB.limit++
+ else if(istype(O, /obj/item/antag_spawner/slaughter_demon/shadow))
+ uses += 1
+ for(var/datum/spellbook_entry/item/shadowbottle/SB in entries)
+ if(!isnull(SB.limit))
+ SB.limit++
else
uses += 2
for(var/datum/spellbook_entry/item/bloodbottle/BB in entries)
@@ -741,6 +764,15 @@
OB.limit++
qdel(O)
return
+
+ if(istype(O, /obj/item/antag_spawner/revenant))
+ to_chat(user, "On second thought, maybe the ghosts have been salty enough today. You refund your points.")
+ uses += 1
+ for(var/datum/spellbook_entry/item/revenantbottle/RB in entries)
+ if(!isnull(RB.limit))
+ RB.limit++
+ qdel(O)
+ return
return ..()
/obj/item/spellbook/proc/GetCategoryHeader(category)
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index efd2d781047..343b8e4c5e8 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -196,24 +196,27 @@
/obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "", mob/user)
var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T)
- var/mob/living/simple_animal/demon/slaughter/S = new demon_type(holder)
- S.vialspawned = TRUE
- S.key = C.key
- S.mind.assigned_role = S.name
- S.mind.special_role = S.name
- SSticker.mode.traitors += S.mind
+ var/mob/living/simple_animal/demon/D = new demon_type(holder)
+ if(istype(D, /mob/living/simple_animal/demon/slaughter))
+ var/mob/living/simple_animal/demon/slaughter/S = D
+ S.vialspawned = TRUE
+
+ D.key = C.key
+ D.mind.assigned_role = D.name
+ D.mind.special_role = D.name
+ SSticker.mode.traitors += D.mind
var/datum/objective/assassinate/KillDaWiz = new /datum/objective/assassinate
- KillDaWiz.owner = S.mind
+ KillDaWiz.owner = D.mind
KillDaWiz.target = user.mind
KillDaWiz.explanation_text = "[objective_verb] [user.real_name], the one who was foolish enough to summon you."
- S.mind.objectives += KillDaWiz
+ D.mind.objectives += KillDaWiz
var/datum/objective/KillDaCrew = new /datum/objective
- KillDaCrew.owner = S.mind
+ KillDaCrew.owner = D.mind
KillDaCrew.explanation_text = "[objective_verb] everyone else while you're at it."
KillDaCrew.completed = TRUE
- S.mind.objectives += KillDaCrew
- to_chat(S, "Objective #[1]: [KillDaWiz.explanation_text]")
- to_chat(S, "Objective #[2]: [KillDaCrew.explanation_text]")
+ D.mind.objectives += KillDaCrew
+ to_chat(D, "Objective #[1]: [KillDaWiz.explanation_text]")
+ to_chat(D, "Objective #[2]: [KillDaCrew.explanation_text]")
/obj/item/antag_spawner/slaughter_demon/laughter
name = "vial of tickles"
@@ -227,6 +230,18 @@
objective_verb = "Hug and tickle"
demon_type = /mob/living/simple_animal/demon/slaughter/laughter
+/obj/item/antag_spawner/slaughter_demon/shadow
+ name = "vial of shadow"
+ desc = "A magically infused bottle of pure darkness, distilled from \
+ ground up shadowling bones. Used in dark rituals to attract \
+ dark creatures."
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "vialshadows"
+ veil_msg = "You sense a dark presence \
+ lurking in the shadows..."
+ objective_verb = "Kill"
+ demon_type = /mob/living/simple_animal/demon/shadow
+
///////////MORPH
/obj/item/antag_spawner/morph
@@ -281,3 +296,58 @@
M.mind.objectives += KillDaCrew
to_chat(M, "Objective #[1]: [KillDaWiz.explanation_text]")
to_chat(M, "Objective #[2]: [KillDaCrew.explanation_text]")
+
+///////////Revenant
+
+/obj/item/antag_spawner/revenant
+ name = "vial of ectoplasm"
+ desc = "A magically infused bottle of ectoplasm, effectivly pure salt from the spectral realm."
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "vialectoplasm"
+ var/shatter_msg = "You shatter the bottle, no \
+ turning back now!"
+ var/veil_msg = "The ectoplasm is awake and seeps \
+ away..."
+ var/objective_verb = "Harvest"
+ var/mob/living/revenant = /mob/living/simple_animal/revenant
+
+/obj/item/antag_spawner/revenant/attack_self(mob/user)
+ if(level_blocks_magic(user.z)) //this is to make sure the wizard does NOT summon a revenant from the Den..
+ to_chat(user, "You should probably wait until you reach the station.")
+ return
+
+ if(used)
+ to_chat(user, "This bottle already has a broken seal.")
+ return
+ used = TRUE
+ to_chat(user, "You break the seal on the bottle, calling upon the salty specter to awaken...")
+
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant awakened by [user.real_name]?", ROLE_REVENANT, 1, 10 SECONDS, source = revenant)
+
+ if(!length(candidates))
+ used = FALSE
+ to_chat(user, "The ectoplasm does not respond to your attempt to awake it. Perhaps you should try again later.")
+ return
+
+ var/mob/C = pick(candidates)
+ spawn_antag(C, get_turf(src), initial(revenant.name), user)
+ to_chat(user, "[shatter_msg]")
+ to_chat(user, "[veil_msg]")
+ playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, TRUE)
+ qdel(src)
+
+/obj/item/antag_spawner/revenant/spawn_antag(client/C, turf/T, type = "", mob/user)
+ var/mob/living/simple_animal/revenant/M = new /mob/living/simple_animal/revenant(pick(GLOB.xeno_spawn))
+ M.key = C.key
+ var/datum/objective/assassinate/KillDaWiz = new /datum/objective/assassinate
+ KillDaWiz.owner = M.mind
+ KillDaWiz.target = user.mind
+ KillDaWiz.explanation_text = "[objective_verb] [user.real_name], the one who was foolish enough to awake you."
+ M.mind.objectives += KillDaWiz
+ var/datum/objective/KillDaCrew = new /datum/objective
+ KillDaCrew.owner = M.mind
+ KillDaCrew.explanation_text = "[objective_verb] everyone and everything else while you're at it."
+ KillDaCrew.completed = TRUE
+ M.mind.objectives += KillDaCrew
+ to_chat(M, "Objective #[1]: [KillDaWiz.explanation_text]")
+ to_chat(M, "Objective #[2]: [KillDaCrew.explanation_text]")
diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi
index 01fe0b5e82c..9d378000f5c 100644
Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ