diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm
index cb317e90ab3..fe91339b0c5 100644
--- a/code/__HELPERS/trait_helpers.dm
+++ b/code/__HELPERS/trait_helpers.dm
@@ -239,6 +239,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_CLING_BURSTING "cling_bursting" // This changeling is about to burst into a headslug, block cremation / gibber to prevent nullspace issues
#define TRAIT_I_WANT_BRAINS "mob_zombie" // A general trait for tracking if the mob is a zombie.
#define TRAIT_NON_INFECTIOUS_ZOMBIE "non_infectious_zombie" // A trait for checking if a zombie shouldn't be able to infect other people
+#define TRAIT_PLAGUE_ZOMBIE "plague_zombie" // For checking if this is a wizard plague zombie
#define TRAIT_NPC_ZOMBIE "npc_zombie" // A trait for checking if a zombie should act like an NPC and attack
#define TRAIT_ABSTRACT_HANDS "abstract_hands" // Mobs with this trait can only pick up abstract items.
#define TRAIT_LANGUAGE_LOCKED "language_locked" // cant add/remove languages until removed (excludes babel because fuck everything i guess)
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 54f8727dc89..f66faea1536 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -101,6 +101,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP,
"TRAIT_CLING_BURSTING" = TRAIT_CLING_BURSTING,
"TRAIT_I_WANT_BRAINS" = TRAIT_I_WANT_BRAINS,
+ "TRAIT_PLAGUE_ZOMBIE" = TRAIT_PLAGUE_ZOMBIE,
"TRAIT_ABSTRACT_HANDS" = TRAIT_ABSTRACT_HANDS,
"TRAIT_LANGUAGE_LOCKED" = TRAIT_LANGUAGE_LOCKED,
"TRAIT_NON_INFECTIOUS_ZOMBIE" = TRAIT_NON_INFECTIOUS_ZOMBIE,
diff --git a/code/datums/components/zombie_regen.dm b/code/datums/components/zombie_regen.dm
index 689aa036bd6..6338b862747 100644
--- a/code/datums/components/zombie_regen.dm
+++ b/code/datums/components/zombie_regen.dm
@@ -16,10 +16,17 @@
return
var/turf/current_turf = get_turf(zomboid)
var/healing_factor = max(1, 6 * (1 - current_turf.get_lumcount()))
+ if(HAS_TRAIT(zomboid, TRAIT_PLAGUE_ZOMBIE))
+ healing_factor *= 1.6 // plague zombies have more health, and should heal faster
if(zomboid.reagents.has_reagent("zombiecure3"))
healing_factor /= 3
if(zomboid.stat == DEAD)
healing_factor *= 2
+ for(var/obj/item/organ/external/E in zomboid.bodyparts)
+ if(E.status & ORGAN_BURNT) // lets slowly remove any crit burns
+ if(prob(20))
+ E.status &= ~ORGAN_BURNT
+ break
zomboid.heal_overall_damage(healing_factor, healing_factor)
zomboid.adjustBrainLoss(-healing_factor)
if(zomboid.stat == DEAD && zomboid.getBruteLoss() <= 1 && zomboid.getFireLoss() <= 1 && (zomboid.timeofdeath + 15 SECONDS <= world.time))
@@ -36,6 +43,8 @@
return
if(zomboid.client || isLivingSSD(zomboid))
return
+ if(HAS_TRAIT(zomboid, TRAIT_PLAGUE_ZOMBIE)) // dont want plague zombie NPCs
+ return
if((zomboid.last_known_ckey || HAS_TRAIT(zomboid, TRAIT_NPC_ZOMBIE)) && !zomboid.key) // make sure they were player inhabited and not admin ghosted
mindless_hunger()
diff --git a/code/datums/diseases/wizard_diseases.dm b/code/datums/diseases/wizard_diseases.dm
new file mode 100644
index 00000000000..48e2e2fc864
--- /dev/null
+++ b/code/datums/diseases/wizard_diseases.dm
@@ -0,0 +1,234 @@
+/datum/disease/beesease/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/cold9/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/fluspanish/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/kingstons_advanced/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/dna_retrovirus/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/tuberculosis/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/wizarditis/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/anxiety/wizard_variant
+ spread_flags = NON_CONTAGIOUS
+
+/datum/disease/grut_gut
+ name = "Grut Gut"
+ max_stages = 5
+ stage_prob = 5
+ spread_text = "Non-contagious"
+ spread_flags = NON_CONTAGIOUS
+ cure_text = "Pyrotech stabilizing agents"
+ agent = "Eruca Stomachum"
+ cures = list("stabilizing_agent")
+ viable_mobtypes = list(/mob/living/carbon/human)
+ desc = "A magic-infused disease that builds up dangerously high pressure bile within the stomach."
+ severity = DANGEROUS
+ virus_heal_resistant = TRUE
+
+/datum/disease/grut_gut/stage_act()
+ if(!..())
+ return FALSE
+
+ switch(stage)
+ if(1)
+ if(prob(3))
+ to_chat(affected_mob, "Your stomach feels strange.")
+ if(2)
+ if(prob(3))
+ to_chat(affected_mob, "You feel bloated.")
+ if(prob(2))
+ to_chat(affected_mob, "Your feel an uncomfortable pressure in your abdomen.")
+ if(3)
+ if(prob(3))
+ to_chat(affected_mob, "Your feel an uncomfortable pressure in your abdomen.")
+ if(prob(2))
+ affected_mob.custom_emote(EMOTE_AUDIBLE, "burps")
+ if(prob(1))
+ affected_mob.emote("groan")
+ if(4)
+ if(prob(1))
+ affected_mob.fakevomit(no_text = 1)
+ affected_mob.adjust_nutrition(-rand(3,5))
+ if(prob(2))
+ affected_mob.custom_emote(EMOTE_AUDIBLE, "burps")
+ if(prob(3))
+ to_chat(affected_mob, "Your feel horribly bloated.")
+ if(prob(2))
+ to_chat(affected_mob, "A deep bubbling resounds through your chest.")
+ if(5)
+ if(prob(2))
+ affected_mob.custom_emote(EMOTE_AUDIBLE, "belches loudly!")
+ if(prob(1))
+ affected_mob.custom_emote(EMOTE_AUDIBLE, "burps")
+ if(prob(1))
+ affected_mob.emote("groan")
+ if(prob(3))
+ to_chat(affected_mob, "Your stomach is killing you!")
+ if(prob(3))
+ to_chat(affected_mob, "Your abdomen gurgles and bubbles with a fierce intensity!")
+ if(prob(2) && affected_mob.nutrition >= 100)
+ affected_mob.adjustBruteLoss(5)
+ affected_mob.vomit(6, 0, TRUE, 7, 0)
+ affected_mob.visible_message(
+ "[affected_mob] vomits with such force that [affected_mob.p_theyre(FALSE)] sent flying backwards!",
+ "You vomit a torrent of magic bile so forcefully, that you are sent flying!",
+ "You hear someone vomit profusely."
+ )
+ do_fling(affected_mob)
+
+/datum/disease/grut_gut/proc/do_fling()
+ var/fling_dir = REVERSE_DIR(affected_mob.dir)
+ var/turf/general_direction = get_edge_target_turf(affected_mob, fling_dir)
+ affected_mob.throw_at(general_direction, 200, 3) // YEET them til they hit something!!
+
+
+/datum/disease/wand_rot
+ name = "Wand Rot"
+ max_stages = 5
+ stage_prob = 5
+ spread_text = "Non-contagious"
+ spread_flags = NON_CONTAGIOUS
+ cure_text = "Acetaldehyde"
+ agent = "nasum magicum"
+ cures = list("acetaldehyde")
+ viable_mobtypes = list(/mob/living/carbon/human)
+ desc = "A magic-infused disease that replaces one's nose hairs with tiny wands. Avoid nasal irritants."
+ severity = DANGEROUS
+ virus_heal_resistant = TRUE
+
+/datum/disease/wand_rot/stage_act()
+ if(!..())
+ return FALSE
+
+ switch(stage)
+ if(1)
+ if(prob(2))
+ affected_mob.emote("sniff")
+ if(2)
+ if(prob(3))
+ affected_mob.emote("sniff")
+ if(prob(3))
+ to_chat(affected_mob, "Your nose and throat feel uncomfortably dry.")
+ if(3)
+ if(prob(2))
+ affected_mob.emote("cough")
+ if(prob(3))
+ affected_mob.emote("sniff")
+ if(prob(3))
+ to_chat(affected_mob, "Your nose and throat feel uncomfortably dry.")
+ if(4)
+ if(prob(2))
+ affected_mob.emote("cough")
+ if(prob(3))
+ affected_mob.emote("sneeze")
+ if(prob(3))
+ to_chat(affected_mob, "Your throat burns and itches!")
+
+ if(5)
+ if(prob(2))
+ affected_mob.emote("cough")
+ if(prob(2))
+ to_chat(affected_mob, "Your nose feels like it's going to burn right off your face!")
+ affected_mob.adjustFireLoss(5)
+ if(prob(3))
+ affected_mob.emote("sniff")
+ if(prob(1))
+ affected_mob.emote("sneeze")
+ sleep(2) // to give the audio a short time to sync with the fireball shot
+ sneeze_fireballs()
+
+//sneeze a fireball in a random direction
+/datum/disease/wand_rot/proc/sneeze_fireballs()
+ var/fuck_you_dir = pick(GLOB.alldirs)
+ var/turf/target_tile = get_edge_target_turf(affected_mob, fuck_you_dir)
+ var/obj/item/projectile/magic/fireball/FB = new /obj/item/projectile/magic/fireball(affected_mob.loc)
+ FB.current = get_turf(affected_mob)
+ FB.original = target_tile
+ FB.firer = affected_mob
+ FB.preparePixelProjectile(target_tile, affected_mob)
+ FB.fire()
+ playsound(affected_mob, 'sound/magic/fireball.ogg', 50, 1)
+
+/datum/disease/mystic_malaise
+ name = "Mystic Malaise"
+ max_stages = 5
+ stage_prob = 5
+ spread_text = "Non-contagious"
+ spread_flags = NON_CONTAGIOUS
+ cure_text = "liquid dark matter"
+ agent = "Spatio Ventrem"
+ cures = list("liquid_dark_matter")
+ viable_mobtypes = list(/mob/living/carbon/human)
+ desc = "A magic-infused disease that resides in the gut, converting gastric juices into space-matter."
+ severity = DANGEROUS
+ virus_heal_resistant = TRUE
+
+/datum/disease/mystic_malaise/stage_act()
+ if(!..())
+ return FALSE
+
+ switch(stage)
+ if(1)
+ if(prob(2))
+ to_chat(affected_mob, "You feel nauseous.")
+ if(2)
+ if(prob(3))
+ to_chat(affected_mob, "Your stomach aches.")
+ if(3)
+ if(prob(3))
+ to_chat(affected_mob, "You feel a growing emptiness inside you.")
+ if(prob(2))
+ affected_mob.emote("cough")
+ if(4)
+ if(prob(3))
+ to_chat(affected_mob, "Your chest feels like it's going to cave in!")
+ if(prob(2))
+ affected_mob.emote("cough")
+ if(prob(2))
+ affected_mob.emote("groan")
+ if(5)
+ if(prob(2))
+ affected_mob.AdjustConfused(5 SECONDS)
+ playsound(get_turf(affected_mob), 'sound/effects/splat.ogg', 50, 1)
+ var/turf/T = get_turf(affected_mob)
+ T.add_vomit_floor(FALSE, FALSE, /obj/effect/decal/cleanable/vomit/spacematter)
+
+/obj/effect/decal/cleanable/vomit/spacematter
+ name = "spacematter vomit"
+ desc = "Despite the hazard, its dazzlingly beautiful. You should probably get away from it though."
+ icon_state = "vomitnebula_1"
+ random_icon_states = list("vomitnebula_1", "vomitnebula_2", "vomitnebula_3", "vomitnebula_4")
+ no_scoop = TRUE
+ var/time_to_live
+
+/obj/effect/decal/cleanable/vomit/spacematter/Initialize(mapload)
+ . = ..()
+ START_PROCESSING(SSprocessing, src)
+ time_to_live = world.time + 15 SECONDS
+
+/obj/effect/decal/cleanable/vomit/spacematter/process()
+ var/datum/milla_safe/spacematter_vomit_process/milla = new()
+ milla.invoke_async(src)
+ if(world.time >= time_to_live) // dont want to remove the spot, but stop removing atmos
+ STOP_PROCESSING(SSprocessing, src)
+ src.visible_message("[src] evaporates into nothingness!")
+ qdel(src)
+
+/datum/milla_safe/spacematter_vomit_process
+
+/datum/milla_safe/spacematter_vomit_process/on_run(obj/effect/decal/cleanable/vomit/spacematter/splatter)
+ var/turf/tile = get_turf(splatter)
+ var/datum/gas_mixture/environment = get_turf_air(tile)
+ var/removed_moles = environment.total_moles() * (1500 / environment.volume)
+ environment.remove(removed_moles)
diff --git a/code/datums/diseases/zombie_virus.dm b/code/datums/diseases/zombie_virus.dm
index 4f41eb8483d..ce080ee0e0f 100644
--- a/code/datums/diseases/zombie_virus.dm
+++ b/code/datums/diseases/zombie_virus.dm
@@ -19,6 +19,15 @@
var/cure_stage = 0
/// Cooldown until the virus can advance to the next stage
COOLDOWN_DECLARE(stage_timer)
+ /// What disease are we passing along to datum/antagonist/zombie
+ var/plague_disease
+ /// Activate the immediate zombie rot loop
+ var/instant_zombie
+
+/datum/disease/zombie/New(chosen_plague, plague_zomb)
+ if(plague_zomb)
+ plague_disease = chosen_plague
+ instant_zombie = TRUE
/datum/disease/zombie/stage_act()
if(stage == 8)
@@ -126,7 +135,14 @@
affected_mob.update_hands_hud()
H.update_body()
if(affected_mob.mind && !affected_mob.mind.has_antag_datum(/datum/antagonist/zombie))
- affected_mob.mind.add_antag_datum(/datum/antagonist/zombie)
+ if(HAS_TRAIT(affected_mob, TRAIT_PLAGUE_ZOMBIE))
+ var/datum/antagonist/zombie/plague = new /datum/antagonist/zombie(plague_disease)
+ plague.silent = TRUE //to prevent the second box from appearing
+ plague.wiki_page_name = null
+ affected_mob.mind.add_antag_datum(plague)
+ else
+ affected_mob.mind.add_antag_datum(/datum/antagonist/zombie)
+
return TRUE
@@ -162,3 +178,13 @@
affected_mob.med_hud_set_health()
affected_mob.med_hud_set_status()
return ..()
+
+/datum/disease/zombie/wizard
+ spread_flags = NON_CONTAGIOUS
+ bypasses_immunity = TRUE
+ spread_text = "Non Contagious"
+ cure_text = "Anti-magic"
+
+// this should not be curable. Prevents things like rez wands from un-zombifying your zombs.
+/datum/disease/zombie/wizard/cure()
+ return
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 31df2551846..3bfcffd266c 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1130,11 +1130,13 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
..(clean_hands, clean_mask, clean_feet)
update_icons() //apply the now updated overlays to the mob
-/atom/proc/add_vomit_floor(toxvomit = FALSE, green = FALSE)
+/atom/proc/add_vomit_floor(toxvomit = FALSE, green = FALSE, type_override)
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
if(!isspaceturf(src))
var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit
var/vomit_reagent = green ? "green_vomit" : "vomit"
+ if(type_override)
+ type = type_override
for(var/obj/effect/decal/cleanable/vomit/V in get_turf(src))
if(V.type == type)
V.reagents.add_reagent(vomit_reagent, 5)
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index 85c82e23927..a2cac902462 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -690,7 +690,6 @@ GLOBAL_LIST_EMPTY(multiverse)
if(!istype(victim) || !istype(necromancer))
return ..()
-
if(victim.stat != DEAD)
to_chat(necromancer, "This artifact can only affect the dead!")
return
@@ -699,6 +698,10 @@ GLOBAL_LIST_EMPTY(multiverse)
to_chat(necromancer, "There is no soul connected to this body...")
return
+ if(victim.mind.has_antag_datum(/datum/antagonist/mindslave/necromancy/plague_zombie))
+ to_chat(necromancer, "This one is already under another artifact's influence!")
+ return
+
if(!check_skeletons()) //If above the cap, there is a cooldown on additional skeletons
to_chat(necromancer, "The amount of skeleton thralls risen by [src] strains its power.")
if(!COOLDOWN_FINISHED(src, additional_thralls_cooldown))
@@ -837,6 +840,103 @@ GLOBAL_LIST_EMPTY(multiverse)
heresy = TRUE
unlimited = TRUE
+//////////////////////// plague Talisman //////////////////////////////
+
+/obj/item/plague_talisman
+ name = "\improper Plague Talisman"
+ desc = "A vile rune, capable of raising the dead as plague-bearing creatures of destruction. The edges have sharp hooks."
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "plague_talisman"
+ origin_tech = "bluespace=4;materials=4"
+ w_class = WEIGHT_CLASS_TINY
+ new_attack_chain = TRUE
+ var/chosen_plague
+
+//checks if they're a valid target before trying to raise
+/obj/item/plague_talisman/attack(mob/living/carbon/human/victim, mob/living/carbon/human/necromancer)
+ if(!istype(victim) || !istype(necromancer))
+ return ..()
+
+ if(victim.stat != DEAD)
+ to_chat(necromancer, "This artifact can only affect the dead!")
+ return
+
+ if(ismachineperson(victim))
+ to_chat(necromancer, "This one isn't vulnerable to this form of plague magic.")
+ return
+
+ if((!victim.mind || !victim.client) && !victim.grab_ghost())
+ to_chat(necromancer, "There is no soul connected to this body...")
+ return
+
+ if(victim.mind.has_antag_datum(/datum/antagonist/mindslave/necromancy))
+ to_chat(necromancer, "This one is already under the artifact's influence! Give it time.")
+ return
+
+ raise_victim(victim, necromancer)
+
+//raises the victim into a special zombies and binds them to wiz
+/obj/item/plague_talisman/proc/raise_victim(mob/living/carbon/human/victim, mob/living/carbon/human/necromancer)
+
+ var/datum/disease/chosen_plague = pick_disease() //what disease to give them
+
+ victim.grab_ghost() // to attempt to hold their ghost still while we do our thing
+ victim.visible_message("[necromancer] places a vile rune upon [victim]'s lifeless forehead. The rune adheres to the flesh, and [victim]'s body rots and decays at unnatural speeds, before rising into a horrendous undead creature!")
+
+ var/static/list/plague_traits = list(TRAIT_NON_INFECTIOUS_ZOMBIE, TRAIT_PLAGUE_ZOMBIE)
+ for(var/trait in plague_traits)
+ ADD_TRAIT(victim, trait, ZOMBIE_TRAIT)
+
+ var/datum/disease/zombie/wizard/plague_virus = new /datum/disease/zombie/wizard(chosen_plague, TRUE)
+ victim.ForceContractDisease(plague_virus)
+ for(var/datum/disease/V in victim.viruses)
+ if(istype(V, /datum/disease/zombie))
+ V.stage = 8 // immediate zombie!
+ else
+ V.cure() // lets remove any other annoying viruses
+
+ // Wiz and minions shouldnt be able to contract their own diseases
+ ADD_TRAIT(necromancer, TRAIT_VIRUSIMMUNE, MAGIC_TRAIT)
+ ADD_TRAIT(victim, TRAIT_VIRUSIMMUNE, MAGIC_TRAIT)
+ necromancer.add_language("Zombie", TRUE) // make sure necromancer can speak to the bois
+
+ playsound(victim, 'sound/magic/mutate.ogg', 50)
+ addtimer(CALLBACK(src, PROC_REF(finish_convert), victim, necromancer, chosen_plague), 5 SECONDS)
+
+/obj/item/plague_talisman/proc/finish_convert(mob/living/carbon/human/victim, mob/living/carbon/human/necromancer, datum/disease/chosen_plague)
+ var/greet_text = "You have been raised into undeath by [necromancer.real_name]!
\
+ [necromancer.p_theyre(TRUE)] your master now, assist [necromancer.p_them()] at all costs, for you are now above death!
\
+ You have been bestowed the following plague:
\
+ [chosen_plague.name]!"
+ victim.mind.add_antag_datum(new /datum/antagonist/mindslave/necromancy(necromancer.mind, greet_text, chosen_plague))
+
+ // Cant very well have your new minions dead for so long. Make em stronk!
+ victim.maxHealth = 200
+ victim.health = 200
+ victim.rejuvenate()
+ qdel(src) // talismans are single use
+
+//choose what disease this zombie will get
+/obj/item/plague_talisman/proc/pick_disease()
+ var/picked_disease
+ var/static/list/possible_diseases = list(
+ /datum/disease/beesease/wizard_variant,
+ /datum/disease/cold9/wizard_variant,
+ /datum/disease/fluspanish/wizard_variant,
+ /datum/disease/kingstons_advanced/wizard_variant,
+ /datum/disease/dna_retrovirus/wizard_variant,
+ /datum/disease/tuberculosis/wizard_variant,
+ /datum/disease/anxiety/wizard_variant,
+ /datum/disease/wizarditis/wizard_variant,
+ /datum/disease/berserker,
+ /datum/disease/appendicitis,
+ /datum/disease/grut_gut,
+ /datum/disease/wand_rot,
+ /datum/disease/mystic_malaise
+ )
+ picked_disease = pick(possible_diseases)
+ return picked_disease
+
/obj/item/organ/internal/heart/cursed/wizard
max_shocks_allowed = 3
pump_delay = 60
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index ace93d72b8f..cd3890214a4 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -432,6 +432,18 @@
if(.)
user.mind.AddSpell(new /datum/spell/aoe/conjure/construct(null))
+/datum/spellbook_entry/item/plague_talisman
+ name = "Five Plague Talismans"
+ desc = "Special engraved runes, used to raise the dead into plague-ridden, animated corpses."
+ item_path = /obj/item/storage/belt/plague_talisman
+ category = "Artefacts"
+ cost = 1
+
+/datum/spellbook_entry/item/plague_talisman/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
+ . = ..()
+ if(.)
+ user.add_language("Zombie")
+
/datum/spellbook_entry/item/wands
name = "Wand Assortment"
desc = "A collection of wands that allow for a wide variety of utility. Wands do not recharge, so be conservative in use. Comes in a handy belt."
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index 77ceae34889..58ba4a20c23 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -703,6 +703,22 @@
W.charges = W.max_charges
update_icon()
+/obj/item/storage/belt/plague_talisman
+ name = "\improper Runic Belt"
+ desc = "A belt designed to hold safely contain Plague Talismans. Better hope it doesn't start dripping..."
+ icon_state = "soulstonebelt"
+ item_state = "soulstonebelt"
+ storage_slots = 5
+ use_item_overlays = TRUE
+ can_hold = list(
+ /obj/item/plague_talisman
+ )
+
+/obj/item/storage/belt/plague_talisman/populate_contents()
+ for(var/count in 1 to storage_slots)
+ new /obj/item/plague_talisman(src)
+ update_icon()
+
/obj/item/storage/belt/fannypack
name = "fannypack"
desc = "A dorky fannypack for keeping small items in."
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index b1ae6bc7f43..b65806da0ec 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -310,12 +310,13 @@ GLOBAL_LIST_EMPTY(antagonists)
messages.Add(greet())
messages.Add(owner.prepare_announce_objectives())
apply_innate_effects()
- messages.Add(finalize_antag())
+ var/finalized = finalize_antag()
+ if(length(finalized) || istext(finalized))
+ messages.Add(finalized)
if(wiki_page_name)
messages.Add("For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/[wiki_page_name])")
-
- to_chat(owner.current, chat_box_red(messages.Join("
")))
-
+ if(length(messages))
+ to_chat(owner.current, chat_box_red(messages.Join("
")))
if(is_banned(owner.current) && replace_banned)
INVOKE_ASYNC(src, PROC_REF(replace_banned_player))
owner.current.create_log(MISC_LOG, "[owner.current] was made into \an [special_role]")
diff --git a/code/modules/antagonists/traitor/misc_mindslave.dm b/code/modules/antagonists/traitor/misc_mindslave.dm
index a995cf21d4e..db384b1671b 100644
--- a/code/modules/antagonists/traitor/misc_mindslave.dm
+++ b/code/modules/antagonists/traitor/misc_mindslave.dm
@@ -7,3 +7,7 @@
/datum/antagonist/mindslave/necromancy
name = "Necromancy-risen Thrall"
master_hud_name = "wizard"
+
+/datum/antagonist/mindslave/necromancy/plague_zombie
+ name = "Necromancy-risen Plague Zombie"
+
diff --git a/code/modules/antagonists/zombie/datum_zombie.dm b/code/modules/antagonists/zombie/datum_zombie.dm
index 9e7f181b4dd..f23920429a8 100644
--- a/code/modules/antagonists/zombie/datum_zombie.dm
+++ b/code/modules/antagonists/zombie/datum_zombie.dm
@@ -11,13 +11,23 @@ RESTRICT_TYPE(/datum/antagonist/zombie)
var/list/old_languages = list() // someone make this better to prevent langs changing if species changes while zombie somehow
var/static/list/zombie_traits = list(TRAIT_LANGUAGE_LOCKED, TRAIT_GOTTAGOSLOW, TRAIT_ABSTRACT_HANDS, TRAIT_NOBREATH)
var/datum/unarmed_attack/claws/claw_attack
+ var/chosen_disease
// possibly upgrades for the zombies after eating brains? Better vision (/datum/action/changeling/augmented_eyesight), better weapons (armblade), better infection, more inhereint armor (physiology)
// ability to find nearby brains to eat (like cling/vamp ability to track people around them)
+/datum/antagonist/zombie/New(chosen_plague)
+ chosen_disease = chosen_plague
+ ..()
/datum/antagonist/zombie/on_gain()
. = ..()
- owner.AddSpell(new /datum/spell/zombie_claws)
+ if(HAS_TRAIT(owner.current, TRAIT_PLAGUE_ZOMBIE))
+ var/datum/spell/zombie_claws/plague_claws/plague_claws = new /datum/spell/zombie_claws/plague_claws
+ plague_claws.disease = chosen_disease
+ owner.AddSpell(plague_claws)
+ owner.AddSpell(new /datum/spell/zombie_leap)
+ else
+ owner.AddSpell(new /datum/spell/zombie_claws)
claw_attack = new /datum/unarmed_attack/claws()
/datum/antagonist/zombie/Destroy(force, ...)
@@ -25,7 +35,11 @@ RESTRICT_TYPE(/datum/antagonist/zombie)
return ..()
/datum/antagonist/zombie/detach_from_owner()
- owner.RemoveSpell(/datum/spell/zombie_claws)
+ if(HAS_TRAIT(owner, TRAIT_PLAGUE_ZOMBIE))
+ owner.RemoveSpell(new /datum/spell/zombie_claws/plague_claws)
+ owner.RemoveSpell(new /datum/spell/zombie_leap)
+ else
+ owner.RemoveSpell(new /datum/spell/zombie_claws)
return ..()
/datum/antagonist/zombie/add_owner_to_gamemode()
@@ -35,6 +49,8 @@ RESTRICT_TYPE(/datum/antagonist/zombie)
SSticker.mode.zombies -= owner
/datum/antagonist/zombie/greet()
+ if(HAS_TRAIT(owner.current, TRAIT_PLAGUE_ZOMBIE)) // we shouldnt get a second welcome message for wiz zombies
+ return
var/list/messages = list()
. = messages
if(owner && owner.current)
@@ -42,6 +58,8 @@ RESTRICT_TYPE(/datum/antagonist/zombie)
messages.Add("You can feel your heart stopping, but something isn't right... life has not abandoned your broken form. You can only feel a deep and immutable hunger that not even death can stop.")
/datum/antagonist/zombie/finalize_antag()
+ if(HAS_TRAIT(owner.current, TRAIT_PLAGUE_ZOMBIE)) // we shouldnt get a second welcome message for wiz zombies
+ return
var/list/messages = list()
. = messages
if(owner && owner.current)
@@ -55,10 +73,10 @@ RESTRICT_TYPE(/datum/antagonist/zombie)
L.remove_language(lang.name)
L.add_language("Zombie")
L.default_language = GLOB.all_languages["Zombie"]
- L.extinguish_light() // zombies prefer darkness
+ if(!HAS_TRAIT(owner.current, TRAIT_PLAGUE_ZOMBIE))
+ L.extinguish_light() // zombies prefer darkness
for(var/trait in zombie_traits)
ADD_TRAIT(L, trait, ZOMBIE_TRAIT)
-
if(!L.HasDisease(/datum/disease/zombie))
var/datum/disease/zombie/zomb = new /datum/disease/zombie()
zomb.stage = 7
@@ -91,4 +109,16 @@ RESTRICT_TYPE(/datum/antagonist/zombie)
return ..()
/datum/antagonist/zombie/give_objectives()
+ if(HAS_TRAIT(owner.current, TRAIT_PLAGUE_ZOMBIE))
+ return
add_antag_objective(/datum/objective/zombie)
+
+/datum/antagonist/zombie/get_antag_objectives()
+ if(HAS_TRAIT(owner.current, TRAIT_PLAGUE_ZOMBIE))
+ return
+ return ..()
+
+/datum/antagonist/zombie/add_antag_objective()
+ if(HAS_TRAIT(owner.current, TRAIT_PLAGUE_ZOMBIE))
+ return
+ return ..()
diff --git a/code/modules/antagonists/zombie/zombie_spells.dm b/code/modules/antagonists/zombie/zombie_spells.dm
index 39d02fa9982..25899cdd354 100644
--- a/code/modules/antagonists/zombie/zombie_spells.dm
+++ b/code/modules/antagonists/zombie/zombie_spells.dm
@@ -9,6 +9,7 @@
base_cooldown = 0 SECONDS
var/list/our_claws = list()
var/infection_stage = 1 // mostly for adminbus and testing
+ var/disease
/datum/spell/zombie_claws/Destroy()
QDEL_LIST_CONTENTS(our_claws)
@@ -18,8 +19,13 @@
if(dispel())
return
- var/obj/item/zombie_claw/claws = new /obj/item/zombie_claw(user.loc, src)
- claws.infection_stage = infection_stage
+ var/obj/item/zombie_claw/claws
+ if(HAS_TRAIT(user, TRAIT_PLAGUE_ZOMBIE))
+ claws = new /obj/item/zombie_claw/plague_claw(user.loc, src, disease)
+ else
+ claws = new /obj/item/zombie_claw(user.loc, src)
+ claws.infection_stage = infection_stage
+
if(user.put_in_hands(claws))
our_claws += claws
else
@@ -29,14 +35,14 @@
/datum/spell/zombie_claws/proc/dispel()
var/mob/living/carbon/human/user = action.owner
var/obj/item/zombie_claw/claw = user.get_active_hand()
- if(istype(claw, /obj/item/zombie_claw))
+ if(istype(claw, /obj/item/zombie_claw) || istype(claw, /obj/item/zombie_claw/plague_claw))
qdel(claw)
return TRUE
/datum/spell/zombie_claws/can_cast(mob/user, charge_check, show_message)
var/mob/living/L = user
- if(!L.get_active_hand() || istype(L.get_active_hand(), /obj/item/zombie_claw))
- return ..()
+ if(!L.get_active_hand() && !user.is_holding_item_of_type(/obj/item/zombie_claw/))
+ return TRUE
/datum/spell/zombie_claws/create_new_targeting()
return new /datum/spell_targeting/self
@@ -60,6 +66,10 @@
var/datum/spell/zombie_claws/parent_spell
var/force_weak = 10
var/infection_stage = 1
+ var/claw_disease
+
+ new_attack_chain = TRUE
+
/obj/item/zombie_claw/Initialize(mapload, new_parent_spell)
. = ..()
@@ -84,24 +94,30 @@
/obj/item/zombie_claw/pre_attack(atom/A, mob/living/user, params)
. = ..()
- if(user.reagents.has_reagent("zombiecure2"))
- force = force_weak
- else
- force = initial(force)
+ if(!HAS_TRAIT(user, TRAIT_PLAGUE_ZOMBIE))
+ if(user.reagents.has_reagent("zombiecure2"))
+ force = force_weak
+ else
+ force = initial(force)
-/obj/item/zombie_claw/afterattack__legacy__attackchain(atom/atom_target, mob/user, proximity_flag, click_parameters)
+/obj/item/zombie_claw/after_attack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!proximity_flag)
return
- if(!ishuman(atom_target) || ismachineperson(atom_target))
+ if(!ishuman(target) || ismachineperson(target))
return
- var/mob/living/carbon/human/target = atom_target
- try_infect(target, user)
+ var/mob/living/carbon/human/attack_target = target
- var/obj/item/organ/internal/brain/eat_brain = target.get_organ_slot("brain")
+ if(HAS_TRAIT(user, TRAIT_PLAGUE_ZOMBIE))
+ try_virus_infect(attack_target, user, claw_disease)
+ return
+ else
+ try_infect(attack_target, user)
+
+ var/obj/item/organ/internal/brain/eat_brain = attack_target.get_organ_slot("brain")
if(!eat_brain)
return
- var/obj/item/organ/external/brain_holder = target.get_limb_by_name(eat_brain.parent_organ)
+ var/obj/item/organ/external/brain_holder = attack_target.get_limb_by_name(eat_brain.parent_organ)
if(!brain_holder || brain_holder.open || brain_holder.limb_name != user.zone_selected)
return
@@ -109,7 +125,7 @@
if(brain_holder.brute_dam + brain_holder.burn_dam <= brain_holder.max_damage - 5) // Deal more damage
return
- if(target.getarmor(brain_holder, MELEE) > 0) // dont count negative armor
+ if(attack_target.getarmor(brain_holder, MELEE) > 0) // dont count negative armor
to_chat(user, "[target]'s brains are blocked.")
return // Armor blocks zombies trying to eat your brains!
@@ -151,6 +167,68 @@
for(var/datum/disease/zombie/zomb in target.viruses)
zomb.stage = max(zomb.stage, infection_stage)
-/obj/item/zombie_claw/attack_self__legacy__attackchain(mob/user)
+/obj/item/zombie_claw/activate_self(mob/user)
. = ..()
qdel(src)
+
+
+//wizard plague zombie claws
+/datum/spell/zombie_claws/plague_claws
+ name = "Plague Claws"
+ desc = "Toggle your claws, allowing you to slash and infect people with deadly diseases."
+
+/obj/item/zombie_claw/plague_claw
+ name = "Plague Claws"
+ desc = "Claws extend from your rotting hands, oozing a putrid ichor. Perfect for rending bone and flesh for your master."
+ armour_penetration_flat = 15
+ force = 13
+
+/obj/item/zombie_claw/plague_claw/Initialize(mapload, new_parent_spell, disease)
+ . = ..()
+ claw_disease = disease
+
+/obj/item/zombie_claw/plague_claw/customised_abstract_text(mob/living/carbon/owner)
+ return "[owner.p_they(TRUE)] [owner.p_have(FALSE)] sharp, ichor-laden claws extending from [owner.p_their(FALSE)] [owner.l_hand == src ? "left hand" : "right hand"]."
+
+/obj/item/zombie_claw/plague_claw/activate_self(mob/user)
+ if(..())
+ return
+ qdel(src) // drops if "used" in hand
+
+/obj/item/zombie_claw/proc/try_virus_infect(mob/living/carbon/human/target, mob/living/user)
+ if(!target.HasDisease(claw_disease))
+ var/datum/disease/plague = new claw_disease
+ target.ContractDisease(plague)
+ else if(prob(40)) // 40% chance to advance the disease
+ for(var/datum/disease/D in target.viruses)
+ if(D.type == claw_disease && D.stage < D.max_stages)
+ D.stage += 1
+ target.bleed_rate = max(5, target.bleed_rate + 1) // Very sharp, ouch!
+
+/datum/spell/zombie_leap
+ name = "Frenzied Leap"
+ desc = "Momentarily remove the limiters on your muscles to leap a great distance towards your targe."
+ action_icon_state = "mutate"
+ action_background_icon_state = "bg_vampire"
+ human_req = TRUE
+ clothes_req = FALSE
+ antimagic_flags = NONE
+ base_cooldown = 60 SECONDS
+
+/datum/spell/zombie_leap/create_new_targeting()
+ return new /datum/spell_targeting/clicked_atom
+
+/datum/spell/zombie_leap/can_cast(mob/user, charge_check, show_message)
+ var/mob/living/L = user
+ if(IS_HORIZONTAL(L))
+ return FALSE
+ return ..()
+
+/datum/spell/zombie_leap/cast(list/targets, mob/user)
+ var/target = targets[1]
+ if(isliving(user))
+ playsound(user, 'sound/voice/zombie_leap.ogg', 80, FALSE)
+ var/mob/living/L = user
+ L.apply_status_effect(STATUS_EFFECT_CHARGING)
+ L.throw_at(target, 5, 1, L, FALSE, callback = CALLBACK(L, TYPE_PROC_REF(/mob/living, remove_status_effect), STATUS_EFFECT_CHARGING))
+
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index d039572e45b..9e1274e53d3 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -4,7 +4,10 @@ GLOBAL_LIST_EMPTY(current_pending_diseases)
var/datum/disease/chosen_disease
var/list/disease_blacklist = list(/datum/disease/advance, /datum/disease/appendicitis, /datum/disease/kuru, /datum/disease/critical, /datum/disease/rhumba_beat, /datum/disease/fake_gbs,
- /datum/disease/gbs, /datum/disease/transformation, /datum/disease/food_poisoning, /datum/disease/berserker, /datum/disease/zombie)
+ /datum/disease/gbs, /datum/disease/transformation, /datum/disease/food_poisoning, /datum/disease/berserker, /datum/disease/zombie, /datum/disease/beesease/wizard_variant,
+ /datum/disease/cold9/wizard_variant, /datum/disease/fluspanish/wizard_variant, /datum/disease/kingstons_advanced/wizard_variant,
+ /datum/disease/dna_retrovirus/wizard_variant, /datum/disease/tuberculosis/wizard_variant, /datum/disease/wizarditis/wizard_variant, /datum/disease/anxiety/wizard_variant,
+ /datum/disease/grut_gut, /datum/disease/wand_rot, /datum/disease/mystic_malaise)
var/static/list/transmissable_symptoms = list()
var/static/list/diseases_minor = list()
var/static/list/diseases_moderate_major = list()
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index f0140b97d49..1df864a4e2c 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -777,7 +777,7 @@
ask_verb = "groans"
exclaim_verbs = list("yells")
colour = "zombie"
- key = "zz" //doesn't matter, this is their default and only language
+ key = "w"
flags = RESTRICTED | NOLIBRARIAN
syllables = list("Brains", "Brainssss", "Flesh", "Grrr", "Hnng", "Braaaains", "Braaiiiins")
english_names = TRUE
diff --git a/code/modules/mob/living/carbon/carbon_procs.dm b/code/modules/mob/living/carbon/carbon_procs.dm
index a8f860c952b..b64c85aac29 100644
--- a/code/modules/mob/living/carbon/carbon_procs.dm
+++ b/code/modules/mob/living/carbon/carbon_procs.dm
@@ -639,7 +639,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
var/atom/movable/AM = hit_atom
var/atom/throw_target = get_edge_target_turf(AM, dir)
if(!AM.anchored || ismecha(AM))
- AM.throw_at(throw_target, 5, 12, src)
+ if(!HAS_TRAIT(src, TRAIT_PLAGUE_ZOMBIE))
+ AM.throw_at(throw_target, 5, 12, src)
hit_something = TRUE
if(isobj(hit_atom))
var/obj/O = hit_atom
@@ -647,10 +648,16 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
hit_something = TRUE
if(isliving(hit_atom))
var/mob/living/L = hit_atom
- L.adjustBruteLoss(60)
- L.KnockDown(12 SECONDS)
- L.Confused(10 SECONDS)
shake_camera(L, 4, 3)
+ if(HAS_TRAIT(src, TRAIT_PLAGUE_ZOMBIE))
+ var/obj/item/I = src.get_active_hand()
+ if(I)
+ I.attack(L, src)
+ L.KnockDown(1 SECONDS)
+ else
+ L.adjustBruteLoss(60)
+ L.KnockDown(12 SECONDS)
+ L.Confused(10 SECONDS)
hit_something = TRUE
if(isturf(hit_atom))
var/turf/T = hit_atom
@@ -658,8 +665,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
T.dismantle_wall(TRUE)
hit_something = TRUE
if(hit_something)
- visible_message("[src] slams into [hit_atom]!", "You slam into [hit_atom]!")
playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100, TRUE)
+ visible_message("[src] slams into [hit_atom]!", "You slam into [hit_atom]!")
return
if(has_status_effect(STATUS_EFFECT_IMPACT_IMMUNE))
return
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index c7f0f0a13c8..75919d6485b 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -308,6 +308,9 @@
if(reagents.has_reagent("teslium"))
msg += "[p_they(TRUE)] [p_are()] emitting a gentle blue glow!\n"
+ if(HAS_TRAIT(src, TRAIT_PLAGUE_ZOMBIE)) //to tell plague zombies easier through clothing
+ msg += "[p_they(TRUE)] smell[p_s()] like rot and death!\n"
+
// add in anything else we want at the end of this block
msg += examine_extra_damage_flavor()
diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi
index c82e9b9bec4..b13279714eb 100644
Binary files a/icons/effects/blood.dmi and b/icons/effects/blood.dmi differ
diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi
index a4e21fa49fc..1c2ad8bbd1c 100644
Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 546276467e5..181cce498aa 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -597,6 +597,7 @@
#include "code\datums\diseases\rhumba_beat.dm"
#include "code\datums\diseases\transformation.dm"
#include "code\datums\diseases\tuberculosis.dm"
+#include "code\datums\diseases\wizard_diseases.dm"
#include "code\datums\diseases\wizarditis.dm"
#include "code\datums\diseases\zombie_virus.dm"
#include "code\datums\diseases\advance\advance.dm"
diff --git a/sound/Attributions.txt b/sound/Attributions.txt
index 3927b51415b..d52102ac2f3 100644
--- a/sound/Attributions.txt
+++ b/sound/Attributions.txt
@@ -8,6 +8,8 @@ machines/triple_beep.ogg is taken from /tg/station. Source (https://github.com/t
misc/server_alert.ogg is taken from https://freesound.org/people/rhodesmas/sounds/380264/ and compressed to .ogg, under the CC-BY 3.0 license. Sound modified by Carthusia
+voice/zombie_leap.ogg is taken from https://freesound.org/people/scorpion67890/sounds/396797/ and compressed to .ogg under Creative Commons 0 liscence.
+
Pig Oink by qubodup (cut down version of below for sound/creatures/pig.ogg) -- https://freesound.org/s/442906/ -- License: Creative Commons 0
Angry Pig Oinking by Jofae (full version of above for sound/creatures/pig.ogg)-- https://freesound.org/s/352698/ -- License: Creative Commons 0
diff --git a/sound/voice/zombie_leap.ogg b/sound/voice/zombie_leap.ogg
new file mode 100644
index 00000000000..ebdf0bfd79f
Binary files /dev/null and b/sound/voice/zombie_leap.ogg differ