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