diff --git a/aurorastation.dme b/aurorastation.dme index d5a1a4d4564..293ca85ea81 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -824,6 +824,7 @@ #include "code\game\machinery\ai_slipper.dm" #include "code\game\machinery\alarm.dm" #include "code\game\machinery\anti_bluespace.dm" +#include "code\game\machinery\antibody.dm" #include "code\game\machinery\atmo_control.dm" #include "code\game\machinery\Beacon.dm" #include "code\game\machinery\biogenerator.dm" @@ -1115,6 +1116,7 @@ #include "code\game\objects\items\devices\radio\intercom.dm" #include "code\game\objects\items\devices\radio\microphone.dm" #include "code\game\objects\items\devices\radio\radio.dm" +#include "code\game\objects\items\stacks\barricades.dm" #include "code\game\objects\items\stacks\matter_synth.dm" #include "code\game\objects\items\stacks\medical.dm" #include "code\game\objects\items\stacks\nanopaste.dm" @@ -2856,7 +2858,6 @@ #include "code\modules\organs\subtypes\industrial.dm" #include "code\modules\organs\subtypes\machine.dm" #include "code\modules\organs\subtypes\nymph_limbs.dm" -#include "code\modules\organs\subtypes\parasite.dm" #include "code\modules\organs\subtypes\skrell.dm" #include "code\modules\organs\subtypes\standard.dm" #include "code\modules\organs\subtypes\tajara.dm" @@ -2865,6 +2866,12 @@ #include "code\modules\organs\subtypes\vaurca.dm" #include "code\modules\organs\subtypes\xenos.dm" #include "code\modules\organs\subtypes\zombie.dm" +#include "code\modules\organs\subtypes\parasite\_parasite.dm" +#include "code\modules\organs\subtypes\parasite\black_kois.dm" +#include "code\modules\organs\subtypes\parasite\heart_fluke.dm" +#include "code\modules\organs\subtypes\parasite\kois.dm" +#include "code\modules\organs\subtypes\parasite\worms.dm" +#include "code\modules\organs\subtypes\parasite\zombie.dm" #include "code\modules\overmap\_defines.dm" #include "code\modules\overmap\overmap_object.dm" #include "code\modules\overmap\overmap_shuttle.dm" diff --git a/code/__DEFINES/antagonist.dm b/code/__DEFINES/antagonist.dm index 5365690bfc0..a75431382c2 100644 --- a/code/__DEFINES/antagonist.dm +++ b/code/__DEFINES/antagonist.dm @@ -2,4 +2,4 @@ #define VAMPIRE_MAX_USABLE_BLOOD 950 // Zombie -#define ZOMBIE_MAX_TRIOXIN 30 +#define ZOMBIE_MAX_HYLEMNOMIL 30 diff --git a/code/__DEFINES/chemistry.dm b/code/__DEFINES/chemistry.dm index 592c316ee4c..6ad8dcec322 100644 --- a/code/__DEFINES/chemistry.dm +++ b/code/__DEFINES/chemistry.dm @@ -82,6 +82,7 @@ #define CE_BLOODCLOT "bloodclot" // Coagzolug #define CE_ORGANREPAIR "organrepair" // Rezadone #define CE_ANTIPARASITE "antiparasite" // Helmizole, garlic +#define CE_ANTIBODIES "antibodies" // Hylemnomil-Zeta antibodies // Deal damage #define CE_BREATHLOSS "breathloss" diff --git a/code/__DEFINES/damage_organs.dm b/code/__DEFINES/damage_organs.dm index 79f9143cbeb..ec97a717866 100644 --- a/code/__DEFINES/damage_organs.dm +++ b/code/__DEFINES/damage_organs.dm @@ -48,7 +48,8 @@ #define ORGAN_PLANT (1<<10) #define ORGAN_ARTERY_CUT (1<<11) #define ORGAN_LIFELIKE (1<<12) // Robotic, made to appear organic. -#define ORGAN_NYMPH (1<<13) +#define ORGAN_NYMPH (1<<13) +#define ORGAN_ZOMBIFIED (1<<14) // the largest bitflag, in the WORLD #define ORGAN_DAMAGE_STATES ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_SPLINTED|ORGAN_DEAD|ORGAN_MUTATED|ORGAN_ARTERY_CUT diff --git a/code/game/machinery/antibody.dm b/code/game/machinery/antibody.dm new file mode 100644 index 00000000000..e0873dcaa58 --- /dev/null +++ b/code/game/machinery/antibody.dm @@ -0,0 +1,133 @@ +#define MAX_STAGES 3 + +/obj/machinery/antibody_extractor + name = "antibody extractor" + desc = "A machine made to extract antibodies. There's a holographic info-label on it..." + desc_extended = "This machine is made to extract antibodies from a subject. It will only work with the unmutated, original version of the virus." + icon = 'icons/obj/machinery/extractor.dmi' + icon_state = "extractor" + anchored = TRUE + can_buckle = list(/mob/living/carbon/human) + /// If the machine is currently under use. + var/working = FALSE + /// Current stage. + var/stage = 0 + /// Current progress to the next stage. + var/cure_progress = 0 + /// Threshold to the next stage. + var/stage_threshold = 100 + /// The person from which the cure is being extracted. + var/mob/living/carbon/human/occupant + +/obj/machinery/antibody_extractor/Destroy() + occupant = null + return ..() + +/obj/machinery/antibody_extractor/attack_hand(mob/user) + if(working) + to_chat(user, SPAN_WARNING("\The [src] is totally unresponsive while processing the cure!")) + return + + if(user == occupant) + to_chat(user, SPAN_WARNING("The buckles stop you from doing much at all.")) + ..() + return FALSE + + if(!user.use_check_and_message()) + return FALSE + + if(occupant) + var/is_infected = FALSE + if(occupant.organs_by_name[BP_ZOMBIE_PARASITE]) + is_infected = TRUE + if(!is_infected) + to_chat(user, SPAN_WARNING("\The [src] beeps in disapproval. The error message says something along the lines of needing a patient infected with \ + Hylemnomil-Zeta.")) + return ..() + var/time_to_die = tgui_alert(user, "Are you sure?", "Begin Antibody Creation", list("Yes", "No")) + if(time_to_die == "Yes") + user.visible_message(SPAN_WARNING("[user] starts fiddling with \the [src]'s controls...")) + if(do_after(user, 5 SECONDS, src)) + user.visible_message(SPAN_WARNING(FONT_HUGE("[user] activates \the [src] as it begins rumbling!"))) + working = TRUE + stage = 1 + log_and_message_admins("has begun antibody extraction", usr, get_turf(src)) + to_chat(occupant, SPAN_CULT(FONT_HUGE("You are locked by bindings into \the [src] and your arm is stabbed by a needle!"))) + playsound(src, 'sound/effects/lingextends.ogg') + +/obj/machinery/antibody_extractor/get_examine_text(mob/user, distance, is_adjacent, infix, suffix) + . = ..() + if(!working) + . += "It's inactive, and beeping ominously every now and then." + switch(stage) + if(1) + . += SPAN_WARNING("It's sucking dark, almost black blood, from the arm of [occupant] into a container.") + if(2) + . += SPAN_DANGER("More and more dark, black blood is being collected and centrifuged.") + if(3) + . += SPAN_CULT("The dark, black blood is slowly being treated and filtered into a shiny, white substance...") + +/obj/machinery/antibody_extractor/process() + if(working) + icon_state = "extractor-active" + if(stage < MAX_STAGES+1) + if(cure_progress > stage*stage_threshold) + playsound(src, 'sound/effects/lingextends.ogg') + stage++ + cure_progress += 5 + + switch(stage) + if(1) + if(prob(25)) + to_chat(occupant, SPAN_DANGER(FONT_HUGE("Your blood is being sucked out of you - the pain is unbearable!"))) + occupant.adjustHalLoss(10) + if(prob(10)) + occupant.emote("whimper") + if(2) + if(prob(25)) + to_chat(occupant, SPAN_DANGER(FONT_HUGE("Your arm feels like it's being torn apart as the needle digs further and further inside!"))) + occupant.adjustHalLoss(15) + if(prob(5)) + occupant.emote("scream") + if(3) + if(prob(25)) + to_chat(occupant, SPAN_DANGER(FONT_HUGE("You feel like you're going to die any second now!"))) + occupant.adjustHalLoss(20) + if(prob(10)) + occupant.emote("scream") + else + if(occupant) + visible_message(SPAN_CULT(FONT_HUGE("\The [src] finishes working and, with a click, releases [occupant] from their bindings. It helpfully dispenses \ + a vial of shining white liquid..."))) + unbuckle() + new /obj/item/reagent_containers/glass/beaker/vial/antidote(get_turf(src)) + playsound(src, 'sound/machines/weapons_analyzer_finish.ogg') + working = FALSE + stage = 0 + cure_progress = 0 + icon_state = "extractor" + +/obj/machinery/antibody_extractor/ex_act(severity) + return + +/obj/machinery/antibody_extractor/post_buckle(mob/living/carbon/human/H) + if(H.buckled_to == src) + occupant = H + playsound(src, 'sound/effects/buckle.ogg') + +/obj/machinery/antibody_extractor/user_unbuckle(mob/user) + if(working) + to_chat(user, SPAN_WARNING("The buckles are too tight! There's no way out for [occupant]!")) + return + . = ..() + occupant = null + +/obj/machinery/antibody_extractor/relaymove(mob/user as mob) + if (user.stat) + return + if(working) + to_chat(user, SPAN_DANGER("The bindings are keeping you locked in place!")) + return + . = ..() + +#undef MAX_STAGES diff --git a/code/game/objects/items/stacks/barricades.dm b/code/game/objects/items/stacks/barricades.dm new file mode 100644 index 00000000000..908fcd071c7 --- /dev/null +++ b/code/game/objects/items/stacks/barricades.dm @@ -0,0 +1,87 @@ +/obj/item/stack/barricade + name = "metal barricade kit" + desc = "A kit of metal, pipes, nuts, and bolts. You can make a barricade with this." + singular_name = "barricade kit" + max_amount = 5 + icon = 'icons/obj/barricade_stacks.dmi' + icon_state = "steel-kit" + w_class = ITEMSIZE_SMALL + matter = list(DEFAULT_WALL_MATERIAL = 650, MATERIAL_PHORON = 100, MATERIAL_PLASTEEL = 150) + var/barricade_name = "steel" + var/barricade_type = /obj/structure/barricade/metal + var/build_sound = 'sound/effects/clang.ogg' + +/obj/item/stack/barricade/attack_self(mob/living/user) + ..() + add_fingerprint(user) + + if(!isturf(user.loc)) + return + + if(istype(user.loc, /turf/space)) + to_chat(user, SPAN_WARNING("The barricade must be constructed on a proper surface!")) + return + + user.visible_message(SPAN_NOTICE("[user] starts assembling a [barricade_name] barricade."), + SPAN_NOTICE("You start assembling a barricade.")) + + if(!do_after(user, 3 SECONDS, do_flags = DO_REPAIR_CONSTRUCT)) + return + + for(var/obj/O in user.loc) //Objects, we don't care about mobs. Turfs are checked elsewhere + if(O.density) + if(!(O.atom_flags & ATOM_FLAG_CHECKS_BORDER) || O.dir == user.dir) + return + + var/obj/structure/barricade/SB = new barricade_type(user.loc, user, user.dir) + user.visible_message(SPAN_NOTICE("[user] assembles a [barricade_name] barricade."), + SPAN_NOTICE("You assemble a [barricade_name] barricade.")) + SB.set_dir(user.dir) + SB.add_fingerprint(user) + playsound(SB, build_sound) + use(1) + +/obj/item/stack/barricade/random/Initialize(mapload) + . = ..() + amount = rand(1, 5) + update_icon() + +/obj/item/stack/barricade/full/Initialize(mapload) + . = ..() + amount = 5 + update_icon() + +/obj/item/stack/barricade/plasteel + name = "plasteel barricade kit" + desc = "A kit of plasteel, pipes, nuts, and bolts. You can make a barricade with this." + barricade_name = "plasteel" + barricade_type = /obj/structure/barricade/plasteel + icon_state = "plasteel-kit" + +/obj/item/stack/barricade/plasteel/random/Initialize(mapload) + . = ..() + amount = rand(1, 5) + update_icon() + +/obj/item/stack/barricade/plasteel/full/Initialize(mapload) + . = ..() + amount = 5 + update_icon() + +/obj/item/stack/barricade/wood + name = "wooden barricade kit" + desc = "A kit of wooden planks, nails, and probably something to use as a hammer. You can make a barricade with this." + barricade_name = "wood" + barricade_type = /obj/structure/barricade/wooden + icon_state = "wood-kit" + build_sound = 'sound/effects/woodhit.ogg' + +/obj/item/stack/barricade/wood/random/Initialize(mapload) + . = ..() + amount = rand(1,5) + update_icon() + +/obj/item/stack/barricade/wood/full/Initialize(mapload) + . = ..() + amount = 5 + update_icon() diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 19efb133006..a762cbd3f8e 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -395,22 +395,22 @@ icon_state = initial(icon_state) +"_locked" ///Zombie-producing grenade -/obj/item/grenade/chem_grenade/trioxin - name = "trioxin grenade" - desc = "Concentrated trioxin. The lab really cooked for this one." +/obj/item/grenade/chem_grenade/hylemnomil + name = "hylemnomil grenade" + desc = "Concentrated hylemnomil. The lab really cooked for this one." stage = 2 path = 1 -/obj/item/grenade/chem_grenade/trioxin/Initialize() +/obj/item/grenade/chem_grenade/hylemnomil/Initialize() . = ..() var/obj/item/reagent_containers/glass/beaker/large/B1 = new(src) var/obj/item/reagent_containers/glass/beaker/large/B2 = new(src) B1.reagents.add_reagent(/singleton/reagent/phosphorus, 40) B1.reagents.add_reagent(/singleton/reagent/potassium, 40) - B1.reagents.add_reagent(/singleton/reagent/toxin/trioxin, 40) + B1.reagents.add_reagent(/singleton/reagent/toxin/hylemnomil, 40) B2.reagents.add_reagent(/singleton/reagent/sugar, 40) - B2.reagents.add_reagent(/singleton/reagent/toxin/trioxin, 80) + B2.reagents.add_reagent(/singleton/reagent/toxin/hylemnomil, 80) detonator = new/obj/item/device/assembly_holder/timer_igniter(src) diff --git a/code/game/objects/random/loot.dm b/code/game/objects/random/loot.dm index 3e6b1c596d2..46a846daceb 100644 --- a/code/game/objects/random/loot.dm +++ b/code/game/objects/random/loot.dm @@ -406,3 +406,209 @@ /obj/random/finances/post_spawn(var/obj/item/spawned) spawned.update_icon() + +/obj/random/barricade_materials + name = "random barricade materials" + spawnlist = list( + /obj/item/stack/material/steel = 0.75, + /obj/item/stack/material/plasteel = 0.25, + /obj/item/stack/material/wood = 0.75 + ) + has_postspawn = TRUE + +/obj/random/barricade_materials/post_spawn(obj/thing) + var/obj/item/stack/material/M = thing + M.amount = rand(10, 20) + M.update_icon() + +/obj/random/barbed_wire + name = "barbed wire" + spawnlist = list( + /obj/item/stack/barbed_wire = 1 + ) + has_postspawn = TRUE + +/obj/random/barbed_wire/post_spawn(obj/thing) + var/obj/item/stack/material/M = thing + M.amount = rand(10, 20) + M.update_icon() + +/obj/random/tool_konyang + name = "tool" + spawnlist = list( + /obj/item/screwdriver = 1, + /obj/item/crowbar = 1, + /obj/item/wirecutters = 1, + /obj/item/wrench = 1, + /obj/item/crowbar/rescue_axe = 0.25, + /obj/item/crowbar/red = 0.25, + ) + +/obj/random/survival_weapon + name = "survival weapon" + problist = list( + /obj/item/crowbar = 1, + /obj/item/shovel = 1, + /obj/item/pickaxe = 0.5, + /obj/item/surgery/scalpel = 0.5, + /obj/item/surgery/surgicaldrill = 0.5, + /obj/item/surgery/circular_saw = 0.5, + /obj/item/material/sword/improvised_sword = 0.5, + /obj/item/material/twohanded/baseballbat = 0.5, + /obj/item/material/twohanded/fireaxe = 0.05, + /obj/item/material/harpoon = 0.5, + /obj/item/material/knife/bayonet = 0.5, + /obj/item/material/knife/trench = 0.5, + /obj/item/material/knife/butterfly = 0.25, + /obj/item/material/twohanded/spear/steel = 0.5, + /obj/item/material/twohanded/spear/plasteel = 0.05, + /obj/item/melee/classic_baton = 0.5 + ) + +/obj/random/high_grade_weapon + name = "high grade weapon" + problist = list( + /obj/item/material/sword/axe = 0.5, + /obj/item/material/sword/longsword = 0.25, + /obj/item/material/sword/rapier = 0.5, + /obj/item/material/sword/sabre = 0.25, + /obj/item/material/sword/khopesh = 0.5, + /obj/item/material/sword/katana = 0.05, + /obj/item/pickaxe/hammer = 0.5 + ) + +/obj/random/light + name = "random light" + problist = list( + /obj/item/device/flashlight = 1, + /obj/item/device/flashlight/flare = 2, + /obj/item/device/flashlight/lamp = 0.5, + /obj/item/device/flashlight/maglight = 0.25, + /obj/item/device/flashlight/marshallingwand = 0.25, + /obj/item/device/flashlight/pen = 1, + /obj/item/device/flashlight/heavy = 0.05, + /obj/item/device/flashlight/lamp = 0.25 + ) + +/obj/random/med_stack + name = "medical supplies" + problist = list( + /obj/item/stack/medical/bruise_pack = 2, + /obj/item/stack/medical/ointment = 2, + /obj/item/stack/medical/advanced/bruise_pack = 0.05, + /obj/item/stack/medical/advanced/ointment = 0.05 + ) + has_postspawn = TRUE + +/obj/random/med_stack/post_spawn(obj/thing) + var/obj/item/stack/M = thing + M.amount = rand(1, 10) + M.update_icon() + +/obj/random/splints + name = "splints" + problist = list( + /obj/item/stack/medical/splint = 1 + ) + has_postspawn = TRUE + +/obj/random/splints/post_spawn(obj/thing) + var/obj/item/stack/M = thing + M.amount = rand(1, 5) + M.update_icon() + +/obj/random/gun_with_ammo + has_postspawn = TRUE + +/obj/random/gun_with_ammo/post_spawn(obj/thing) + var/obj/item/gun/projectile/G = thing + if(G.ammo_magazine) + var/mags = rand(0, 2) + for(var/i = 1 to mags) + var/obj/item/ammo_magazine/AG = new G.ammo_magazine.type(get_turf(G)) + if(prob(50)) + var/to_delete = rand(1, length(AG.stored_ammo)) + var/delete_counter = 0 + for(var/obj/item/ammo_casing/AC in AG.stored_ammo) + qdel(AC) + delete_counter++ + if(delete_counter > to_delete) + break + AG.update_icon() + +/obj/random/gun_with_ammo/pistols + name = "pistols" + problist = list( + /obj/item/gun/projectile/pistol = 1, + /obj/item/gun/projectile/pistol/detective = 1, + /obj/item/gun/projectile/colt = 1, + /obj/item/gun/projectile/pistol/sol = 0.5, + /obj/item/gun/projectile/tanto = 0.25, + /obj/item/gun/projectile/sec/lethal = 1, + /obj/item/gun/projectile/leyon = 1, + /obj/item/gun/projectile/revolver = 0.05, + /obj/item/gun/projectile/revolver/detective = 1, + /obj/item/gun/projectile/revolver/lemat = 0.5, + /obj/item/gun/projectile/revolver/konyang/pirate = 0.5, + /obj/item/gun/projectile/revolver/konyang/police = 0.5 + ) + +/obj/random/gun_with_ammo/automatic + name = "automatics" + problist = list( + /obj/item/gun/projectile/automatic/mini_uzi = 1, + /obj/item/gun/projectile/automatic/c20r = 1, + /obj/item/gun/projectile/automatic/xanusmg = 1, + /obj/item/gun/projectile/automatic/rifle/jingya = 0.25, + /obj/item/gun/projectile/automatic/rifle/sol = 0.1, + /obj/item/gun/projectile/automatic/rifle/carbine/civcarbine = 0.75, + /obj/item/gun/projectile/automatic/konyang_pirate = 1, + /obj/item/gun/projectile/automatic/tommygun = 0.5, + /obj/item/gun/projectile/automatic/lebman = 1, + /obj/item/gun/projectile/automatic/x9 = 0.5 + ) + +/obj/random/shotgun + name = "shotgun" + problist = list( + /obj/item/gun/projectile/shotgun/foldable = 1, + /obj/item/gun/projectile/shotgun/doublebarrel = 1, + /obj/item/gun/projectile/shotgun/doublebarrel/sawn = 1, + /obj/item/gun/projectile/shotgun/pump = 1, + /obj/item/gun/projectile/shotgun/pump/combat = 0.5, + /obj/item/gun/projectile/shotgun/pump/lever_action = 0.5 + ) + has_postspawn = TRUE + +/obj/random/shotgun/post_spawn(obj/thing) + var/shells = rand(1, 10) + var/shell_type = pick("shells", "slugs") + for(var/i = 1 to shells) + if(shell_type == "shells") + new /obj/item/ammo_casing/shotgun/pellet(get_turf(thing)) + else + new /obj/item/ammo_casing/shotgun(get_turf(thing)) + +/obj/random/semiautos + name = "semiautos" + problist = list( + /obj/item/gun/projectile/shotgun/pump/rifle/scope = 0.05, + /obj/item/gun/projectile/shotgun/pump/rifle/obrez = 1, + /obj/item/gun/projectile/shotgun/pump/rifle/vintage = 0.5, + /obj/item/gun/projectile/shotgun/pump/lever_action = 0.5 + ) + has_postspawn = TRUE + +/obj/random/semiautos/post_spawn(obj/thing) + var/shells = rand(1, 20) + var/obj/item/gun/projectile/shotgun/pump/rifle/R = thing + for(var/i = 1 to shells) + new R.ammo_type(get_turf(R)) + +/obj/random/barricade_kit + name = "barricade kits" + problist = list( + /obj/item/stack/barricade/random = 1, + /obj/item/stack/barricade/wood/random = 0.5, + /obj/item/stack/barricade/plasteel/random = 0.25 + ) diff --git a/code/modules/cargo/random_stock/t2_uncommon.dm b/code/modules/cargo/random_stock/t2_uncommon.dm index f2cbd8b9a3b..6d58d5e9756 100644 --- a/code/modules/cargo/random_stock/t2_uncommon.dm +++ b/code/modules/cargo/random_stock/t2_uncommon.dm @@ -91,7 +91,7 @@ STOCK_ITEM_UNCOMMON(chempack, 5) var/list/chems = GET_SINGLETON_SUBTYPE_MAP(/singleton/reagent/) var/list/exclusion = list(/singleton/reagent/drink, /singleton/reagent, /singleton/reagent/adminordrazine, /singleton/reagent/polysomnine/beer2, /singleton/reagent/azoth, /singleton/reagent/elixir,\ /singleton/reagent/liquid_fire, /singleton/reagent/philosopher_stone, /singleton/reagent/toxin/undead, /singleton/reagent/love_potion, /singleton/reagent/shapesand, /singleton/reagent/usolve,\ - /singleton/reagent/sglue, /singleton/reagent/black_matter, /singleton/reagent/bottle_lightning, /singleton/reagent/toxin/trioxin, /singleton/reagent/toxin/nanites, /singleton/reagent/nitroglycerin, \ + /singleton/reagent/sglue, /singleton/reagent/black_matter, /singleton/reagent/bottle_lightning, /singleton/reagent/toxin/hylemnomil, /singleton/reagent/toxin/nanites, /singleton/reagent/nitroglycerin, \ /singleton/reagent/aslimetoxin, /singleton/reagent/sanasomnum, /singleton/reagent/rezadone, /singleton/reagent/toxin/carpotoxin) chems -= exclusion for (var/i in 1 to rand(2, 4)) diff --git a/code/modules/mob/living/carbon/human/species/outsider/undead.dm b/code/modules/mob/living/carbon/human/species/outsider/undead.dm index 97f6320d996..05d538163c2 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/undead.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/undead.dm @@ -143,10 +143,6 @@ return TRUE return FALSE - -/mob/living/carbon/human/zombie/Initialize(mapload) - . = ..(mapload, SPECIES_ZOMBIE) - /datum/species/zombie name = SPECIES_ZOMBIE name_plural = "Zombies" @@ -166,8 +162,13 @@ has_fine_manipulation = FALSE - speech_sounds = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg') - speech_chance = 50 + speech_sounds = list( + 'sound/effects/zombies/zombie_1.ogg', + 'sound/effects/zombies/zombie_2.ogg', + 'sound/effects/zombies/zombie_3.ogg', + 'sound/effects/zombies/zombie_4.ogg' + ) + speech_chance = 100 ethanol_resistance = -1 taste_sensitivity = TASTE_NUMB @@ -192,16 +193,16 @@ BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/zombie) ) - total_health = 80 + total_health = 150 - slowdown = 2 + slowdown = 1 vision_flags = DEFAULT_SIGHT reagent_tag = IS_UNDEAD rarity_value = 10 - blurb = "Once a living person, this unholy creature was created either by the power of science or necromancy." + blurb = "Once a living person, this unholy creature was created by the power of science." remains_type = /obj/effect/decal/remains/human dust_remains_type = /obj/effect/decal/remains/human/burned @@ -214,35 +215,37 @@ appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_TONE | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR | HAS_SOCKS spawn_flags = IS_RESTRICTED - stamina = 500 //Tireless automatons + stamina = 500 //Tireless automatons stamina_recovery = 1 - sprint_speed_factor = 0.1 + sprint_speed_factor = 1 exhaust_threshold = 10 - inherent_verbs = list(/mob/living/carbon/human/proc/darkness_eyes) + inherent_verbs = list(/mob/living/carbon/human/proc/darkness_eyes, /mob/living/carbon/proc/consume) allowed_eat_types = TYPE_ORGANIC | TYPE_HUMANOID gluttonous = 1 + show_ssd = null + +/mob/living/carbon/human/zombie/Initialize(mapload) + . = ..(mapload, SPECIES_ZOMBIE) + set_name("zombie") /datum/species/zombie/handle_post_spawn(var/mob/living/carbon/human/H) H.mutations |= CLUMSY var/datum/martial_art/zombie/Z = new /datum/martial_art/zombie() Z.teach(H) to_chat(H, "Use the Check Attacks verb in your IC tab for information on your attacks! They are important! Your bite infects, but is worse at getting through armour than your claws, which have great damage and are armor piercing!") - H.accent = ACCENT_BLUESPACE return ..() //Zombies do not have oxygen, so we have to handle the sprint this way /datum/species/zombie/handle_sprint_cost(var/mob/living/carbon/human/H, var/cost, var/pre_move) . = ..() - if(exhaust_threshold && (H.stamina) < (exhaust_threshold * 0.8)) H.m_intent = M_WALK H.hud_used.move_intent.update_move_icon(H) to_chat(H, SPAN_DANGER("You're too exhausted to run anymore!")) - H.flash_pain(H.get_shock()) - return 0 + return FALSE /datum/species/zombie/tajara name = SPECIES_ZOMBIE_TAJARA @@ -329,7 +332,7 @@ icobase = 'icons/mob/human_races/zombie/r_zombie_bull.dmi' deform = 'icons/mob/human_races/zombie/r_zombie_bull.dmi' - total_health = 300 + total_health = 250 allowed_eat_types = TYPE_ORGANIC | TYPE_HUMANOID | TYPE_SYNTHETIC | TYPE_WEIRD gluttonous = GLUT_ANYTHING @@ -344,15 +347,28 @@ species_height = HEIGHT_CLASS_TALL natural_armor = list( - ballistic = ARMOR_BALLISTIC_RIFLE, - laser = ARMOR_LASER_MEDIUM, + ballistic = ARMOR_BALLISTIC_MEDIUM, melee = ARMOR_MELEE_MAJOR, bomb = null, energy = ARMOR_ENERGY_RESISTANT ) - unarmed_types = list(/datum/unarmed_attack/bite/infectious) + mob_size = 20 + unarmed_types = list(/datum/unarmed_attack/bite/infectious) + inherent_verbs = list( + /mob/living/carbon/human/proc/darkness_eyes, + /mob/living/carbon/proc/consume, + /mob/living/carbon/proc/smash_barricade, + /mob/living/carbon/human/proc/throw_rock + ) + show_ssd = TRUE + +/datum/species/zombie/bull/get_random_name(gender) + return "zombie bull" + +/mob/living/carbon/human/bull/Initialize(mapload, new_species) + . = ..(mapload, SPECIES_ZOMBIE_BULL) ///A zombie tuned to hunt preys /datum/species/zombie/hunter @@ -362,10 +378,10 @@ deform = 'icons/mob/human_races/zombie/r_zombie_hunter.dmi' slowdown = 2 - total_health = 90 + total_health = 80 - stamina = 40 - sprint_speed_factor = 0.9 + stamina = 80 + sprint_speed_factor = 1.2 slowdown = -2 standing_jump_range = 5 natural_climbing = TRUE @@ -373,15 +389,20 @@ vision_flags = SEE_SELF | SEE_MOBS hearing_sensitivity = HEARING_VERY_SENSITIVE - natural_armor = list( - ballistic = ARMOR_BALLISTIC_MEDIUM, - laser = ARMOR_LASER_PISTOL, - melee = ARMOR_MELEE_MAJOR, - bomb = ARMOR_BOMB_PADDED, - energy = ARMOR_ENERGY_SMALL - ) + maneuvers = list(/singleton/maneuver/leap/areagrab) + inherent_verbs = list( + /mob/living/carbon/human/proc/darkness_eyes, + /mob/living/carbon/proc/consume + ) - unarmed_types = list(/datum/unarmed_attack/shocking) //This zombie cannot infect, it's an harrass type of zombie + unarmed_types = list(/datum/unarmed_attack/claws/strong) + show_ssd = TRUE + +/datum/species/zombie/hunter/get_random_name(gender) + return "zombie hunter" + +/mob/living/carbon/human/hunter/Initialize(mapload, new_species) + . = ..(mapload, SPECIES_ZOMBIE_HUNTER) ///A zombie tuned for charge attacks /datum/species/zombie/rhino @@ -391,7 +412,7 @@ deform = 'icons/mob/human_races/zombie/r_zombie_rhino.dmi' slowdown = 2 - total_health = 120 + total_health = 200 stamina = 50 sprint_speed_factor = 0.7 @@ -406,13 +427,184 @@ grab_mod = 5 natural_armor = list( - ballistic = ARMOR_BALLISTIC_MEDIUM, - laser = ARMOR_LASER_PISTOL, - melee = ARMOR_MELEE_MAJOR, + ballistic = ARMOR_BALLISTIC_MINOR, + laser = ARMOR_LASER_MINOR, + melee = ARMOR_MELEE_SMALL, bomb = ARMOR_BOMB_PADDED, energy = ARMOR_ENERGY_SMALL ) - maneuvers = list(/singleton/maneuver/leap/areagrab) + inherent_verbs = list( + /mob/living/carbon/human/proc/trample, + /mob/living/carbon/human/proc/darkness_eyes, + /mob/living/carbon/proc/consume + ) unarmed_types = list(/datum/unarmed_attack/bite/infectious, /datum/unarmed_attack/golem) + show_ssd = TRUE + +/datum/species/zombie/rhino/get_random_name(gender) + return "zombie rhino" + +/mob/living/carbon/human/rhino/Initialize(mapload, new_species) + . = ..(mapload, SPECIES_ZOMBIE_RHINO) + +/mob/living/carbon/proc/consume() + set name = "Consume" + set desc = "Regain life and infect others by feeding upon them." + set category = "Abilities" + + if (last_special > world.time) + to_chat(src, SPAN_WARNING("You aren't ready to do that! Wait [round(last_special - world.time) / 10] seconds.")) + return + + var/mob/living/carbon/human/target + var/list/victims = list() + for (var/mob/living/carbon/human/L in get_turf(src)) + if (L != src && (L.lying || L.stat == DEAD)) + if (iszombie(L)) + to_chat(src, SPAN_WARNING("\The [L] isn't fresh anymore!")) + continue + if (!L.species.zombie_type || L.is_diona() || L.isSynthetic()) + to_chat(src, SPAN_WARNING("You'd break your teeth on \the [L]!")) + continue + victims += L + + if (!length(victims)) + to_chat(src, SPAN_WARNING("No valid targets nearby!")) + return + if (client) + if (length(victims) == 1) //No need to choose + target = victims[1] + else + target = tgui_input_list(src, "Who would you like to consume?", "Zombie", victims) + else //NPCs + if (length(victims) > 0) + target = victims[1] + + if (!target) + return + if (get_turf(src) != get_turf(target) || !(target.lying || target.stat == DEAD)) + to_chat(src, SPAN_WARNING("You're no longer on top of \the [target]!")) + return + + last_special = world.time + 5 SECONDS + + src.visible_message(SPAN_DANGER("\The [src] hunkers down over \the [target], tearing into their flesh.")) + playsound(loc, 'sound/effects/bonebreak3.ogg', 20, 1) + playsound(loc, 'sound/weapons/bloodyslice.ogg', 30, 1) + + target.adjustHalLoss(50) + + if (do_after(src, 5 SECONDS, target, DO_DEFAULT | DO_USER_UNIQUE_ACT, INCAPACITATION_KNOCKOUT)) + admin_attack_log(src, target, "Consumed their victim.", "Was consumed.", "consumed") + + if (!target.lying && target.stat != DEAD) //Check victims are still prone + return + + target.apply_damage(rand(50, 60), DAMAGE_BRUTE, BP_CHEST) + target.adjustBruteLoss(20) + var/infection_chance = 80 + var/hylemnomil_amount = REAGENT_VOLUME(target.reagents, /singleton/reagent/toxin/hylemnomil) + if(prob(infection_chance)) + if(target.reagents) + target.reagents.add_reagent(/singleton/reagent/toxin/hylemnomil, min(10, ZOMBIE_MAX_HYLEMNOMIL - hylemnomil_amount)) + + if (target.getBruteLoss() > target.maxHealth * 3) + if (target.stat != DEAD) + to_chat(src,SPAN_WARNING("You've scraped \the [target] down to the bones already!.")) + else + to_chat(src,SPAN_DANGER("You shred and rip apart \the [target]'s remains!.")) + target.gib() + playsound(loc, 'sound/effects/splat.ogg', 40, 1) + return + + to_chat(target,SPAN_DANGER("\The [src] scrapes your flesh from your bones!")) + to_chat(src,SPAN_DANGER("You feed hungrily off \the [target]'s flesh.")) + + if (iszombie(target)) //Just in case they turn whilst being eaten + return + + var/obj/item/organ/external/E = target.organs_by_name[BP_CHEST] + if(E && E.open < 3 && target.getBruteLoss() > 100) + E.open = min(E.open + 1, 3) + target.update_surgery() //Update broken ribcage sprites etc. + + adjustBruteLoss(-5) + adjustFireLoss(-15) + adjustToxLoss(-5) + adjustBrainLoss(-5) + nutrition += 40 + + playsound(loc, 'sound/effects/splat.ogg', 20, 1) + new /obj/effect/decal/cleanable/blood/splatter(get_turf(src), target.species.blood_color) + if (target.getBruteLoss() > target.maxHealth*0.75) + if (prob(50)) + gibs(get_turf(src), target.dna) + src.visible_message(SPAN_DANGER("\The [src] tears out \the [target]'s insides!")) + else + src.visible_message(SPAN_WARNING("\The [src] leaves their meal for later.")) + +/mob/living/carbon/proc/smash_barricade() + set name = "Destroy Barricade" + set desc = "Destroy a barricade in front of you." + set category = "Abilities" + + if (last_special > world.time) + to_chat(src, SPAN_WARNING("You aren't ready to do that! Wait [round(last_special - world.time) / 10] seconds.")) + return + + var/obj/structure/barricade/B = locate() in get_turf(get_step(src, dir)) + if(!B) + to_chat(src, SPAN_WARNING("You are not facing a barricade!")) + return + + last_special = world.time + 10 SECONDS + if(do_after(src, 2 SECONDS, B)) + visible_message(SPAN_DANGER("\The [src] raises a giant, mutated hand and smashes \the [B]!")) + playsound(src, 'sound/weapons/heavysmash.ogg', 100) + B.attack_generic(src, 200, "destroys", TRUE) + +/mob/living/carbon/human/proc/throw_rock() + set name = "Throw Rock" + set desc = "Pick up a large rock and throw it. Tank-style." + set category = "Abilities" + + if (last_special > world.time) + to_chat(src, SPAN_WARNING("You aren't ready to do that! Wait [round(last_special - world.time) / 10] seconds.")) + return + + last_special = world.time + 10 SECONDS + visible_message(SPAN_DANGER(FONT_LARGE("[src] digs their hands into the ground and starts picking up a portion of it!"))) + if(do_after(src, 3 SECONDS)) + visible_message(SPAN_DANGER(FONT_LARGE("[src] picks up a large boulder out of the ground!"))) + var/obj/item/tank_rock/TR = new() + put_in_any_hand_if_possible(TR) + +/obj/item/tank_rock + name = "boulder" + desc = "A large boulder." + icon = 'icons/obj/flora/rocks_grey.dmi' + icon_state = "basalt2" + throwforce = 30 + armor_penetration = 20 + mob_throw_hit_sound = 'sound/effects/rock_break.ogg' + +/obj/item/tank_rock/do_additional_pickup_checks(mob/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.mob_size < 16) + to_chat(user, SPAN_WARNING("That's way too heavy for you to pick up!")) + return FALSE + else + return TRUE + +/obj/item/tank_rock/throw_impact(atom/hit_atom) + . = ..() + if(isliving(hit_atom)) + var/mob/living/M = hit_atom + M.visible_message(SPAN_DANGER(FONT_LARGE("\The [src] breaks as it makes contact with [M]!"))) + M.Paralyse(5) + M.apply_damage(30, DAMAGE_BRUTE, used_weapon = src, armor_pen = 30) + qdel(src) diff --git a/code/modules/mob/living/carbon/human/species/species_attack.dm b/code/modules/mob/living/carbon/human/species/species_attack.dm index fe4e20d7f45..eb8964f8ac8 100644 --- a/code/modules/mob/living/carbon/human/species/species_attack.dm +++ b/code/modules/mob/living/carbon/human/species/species_attack.dm @@ -209,8 +209,8 @@ infection_chance -= target.get_blocked_ratio(zone, DAMAGE_BRUTE, damage_flags = DAMAGE_FLAG_SHARP|DAMAGE_FLAG_EDGE, damage = damage)*100 if(prob(infection_chance)) if(target.reagents) - var/trioxin_amount = REAGENT_VOLUME(target.reagents, /singleton/reagent/toxin/trioxin) - target.reagents.add_reagent(/singleton/reagent/toxin/trioxin, min(10, ZOMBIE_MAX_TRIOXIN - trioxin_amount)) + var/hylemnomil_amount = REAGENT_VOLUME(target.reagents, /singleton/reagent/toxin/hylemnomil) + target.reagents.add_reagent(/singleton/reagent/toxin/hylemnomil, min(10, ZOMBIE_MAX_HYLEMNOMIL - hylemnomil_amount)) /datum/unarmed_attack/bite/infectious/get_unarmed_damage(var/mob/attacker, var/target) if(istype(target, /mob/living/heavy_vehicle)) diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 6b0aff25f0c..4137e3cc7e3 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -502,8 +502,9 @@ var/edge_eligible = FALSE var/blunt_eligible = FALSE - var/maim_bonus = 0 var/dam_flags = 0 + /// Certain limbs like zombie limbs have an integrated maiming bonus that make them easier to delimb. Add that. + var/maim_bonus_to_add = src.maim_bonus if(isitem(used_weapon)) var/obj/item/W = used_weapon @@ -512,17 +513,17 @@ var/obj/item/projectile/P = W if(dam_flags & DAMAGE_FLAG_BULLET) blunt_eligible = TRUE - maim_bonus += P.maim_rate + maim_bonus_to_add += P.maim_rate if(W.w_class >= w_class && (dam_flags & DAMAGE_FLAG_EDGE)) edge_eligible = TRUE - if(!blunt_eligible && edge_eligible && (brute >= max_damage / (DROPLIMB_THRESHOLD_EDGE + maim_bonus))) + if(!blunt_eligible && edge_eligible && (brute >= max_damage / (DROPLIMB_THRESHOLD_EDGE + maim_bonus_to_add))) droplimb(0, DROPLIMB_EDGE) - else if(burn >= max_damage / ((dam_flags & DAMAGE_FLAG_LASER ? DROPLIMB_THRESHOLD_DESTROY_PROJECTILE : DROPLIMB_THRESHOLD_DESTROY) + maim_bonus)) + else if(burn >= max_damage / ((dam_flags & DAMAGE_FLAG_LASER ? DROPLIMB_THRESHOLD_DESTROY_PROJECTILE : DROPLIMB_THRESHOLD_DESTROY) + maim_bonus_to_add)) droplimb(0, DROPLIMB_BURN) - else if(blunt_eligible && brute >= max_damage / ((dam_flags & DAMAGE_FLAG_BULLET ? DROPLIMB_THRESHOLD_DESTROY_PROJECTILE : DROPLIMB_THRESHOLD_DESTROY) + maim_bonus)) + else if(blunt_eligible && brute >= max_damage / ((dam_flags & DAMAGE_FLAG_BULLET ? DROPLIMB_THRESHOLD_DESTROY_PROJECTILE : DROPLIMB_THRESHOLD_DESTROY) + maim_bonus_to_add)) droplimb(0, DROPLIMB_BLUNT) - else if(brute >= max_damage / (DROPLIMB_THRESHOLD_TEAROFF + maim_bonus)) + else if(brute >= max_damage / (DROPLIMB_THRESHOLD_TEAROFF + maim_bonus_to_add)) droplimb(0, DROPLIMB_EDGE) /obj/item/organ/external/heal_damage(brute, burn, internal = 0, robo_repair = 0) diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index 46964f66ba3..d0203ef225b 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -197,6 +197,10 @@ mob_icon.ColorTone(rgb(10,50,0)) mob_icon.SetIntensity(0.7) + if(status & ORGAN_ZOMBIFIED) + mob_icon.ColorTone(rgb(30, 30, 30)) + mob_icon.SetIntensity(0.7) + if(skin_color) mob_icon.Blend(skin_color, ICON_ADD) diff --git a/code/modules/organs/subtypes/parasite.dm b/code/modules/organs/subtypes/parasite.dm deleted file mode 100644 index 115f4b1d79c..00000000000 --- a/code/modules/organs/subtypes/parasite.dm +++ /dev/null @@ -1,520 +0,0 @@ -/obj/item/organ/internal/parasite - name = "parasite" - - organ_tag = "parasite" - var/stage = 1 - var/max_stage = 4 - var/stage_ticker = 0 - var/recession = 0 - - ///Infection progress speed, will be determined by get_infect_speed() - var/infection_speed = 2 - - ///The fastest the parasite will advance its stage - var/infect_speed_high = 35 - - ///The fastest the parasite will advance its stage - var/infect_speed_low = 15 - - ///Time between stages, in seconds. interval of 300 allows convenient treating with antiparasitics, higher will require spaced dosing of medications. - var/stage_interval = 300 - - ///Boolean, if the body should reject the parasite naturally - var/subtle = FALSE - - ///Does the parasite have a reagent which seeds an infection? - var/egg = null - - //Boolean, if the parasite is resistant to antiparasitic medications - var/drug_resistance = FALSE - -/obj/item/organ/internal/parasite/Initialize() - . = ..() - get_infect_speed() - -/obj/item/organ/internal/parasite/proc/get_infect_speed() //Slightly randomizes how fast each infection progresses. - infection_speed = rand(infect_speed_low, infect_speed_high) / 10 - -/obj/item/organ/internal/parasite/process() - ..() - if(!owner) - return - - if(owner.chem_effects[CE_ANTIPARASITE] && !drug_resistance) - recession = owner.chem_effects[CE_ANTIPARASITE]/10 - - if((stage < max_stage) && !recession) - stage_ticker = Clamp(stage_ticker+=infection_speed, 0, stage_interval*max_stage) - if(stage_ticker >= stage*stage_interval) - stage = min(stage+1,max_stage) - get_infect_speed() //Each stage may progress faster or slower than the previous one - stage_effect() - - if(recession) - stage_ticker = Clamp(stage_ticker-=recession, 0, stage_interval*max_stage) - if(stage_ticker <= stage*stage_interval-stage_interval) - stage = max(stage-1, 1) - stage_effect() - if(!owner.chem_effects[CE_ANTIPARASITE] || drug_resistance) - recession = 0 - if(stage_ticker == 0) - qdel(src) - -/obj/item/organ/internal/parasite/handle_rejection() - if(subtle) - return ..() - else - if(rejecting) - rejecting = 0 - return - -/obj/item/organ/internal/parasite/proc/stage_effect() - return - -/////////////////// -///K'ois Mycosis/// -/////////////////// - -/obj/item/organ/internal/parasite/kois - name = "k'ois mycosis" - icon = 'icons/obj/organs/organs.dmi' - icon_state = "kois-on" - dead_icon = "kois-off" - - organ_tag = "kois" - - parent_organ = BP_CHEST - stage_interval = 150 - drug_resistance = 1 - - origin_tech = list(TECH_BIO = 3) - - egg = /singleton/reagent/kois - -/obj/item/organ/internal/parasite/kois/process() - ..() - - if (!owner) - return - - if(prob(10) && (owner.can_feel_pain())) - to_chat(owner, "You feel a stinging pain in your abdomen!") - owner.visible_message("[owner] winces slightly.") - owner.adjustHalLoss(5) - - else if(prob(10) && !(owner.species.flags & NO_BREATHE)) - owner.emote("cough") - - else if(prob(10) && !(owner.species.flags & NO_BREATHE)) - owner.visible_message("[owner] coughs up blood!") - owner.drip(10) - - if(stage >= 2) - if(prob(10) && !(owner.species.flags & NO_BREATHE)) - owner.visible_message("[owner] gasps for air!") - owner.losebreath += 5 - - if(stage >= 3) - set_light(1, l_color = "#E6E600") - if(prob(10)) - to_chat(owner, "You feel something squirming inside of you!") - owner.reagents.add_reagent(/singleton/reagent/toxin/phoron, 8) - owner.reagents.add_reagent(/singleton/reagent/kois, 5) - - if(stage >= 4) - if(prob(10)) - to_chat(owner, "You feel something alien coming up your throat!") - owner.emote("cough") - - var/turf/T = get_turf(owner) - - var/datum/reagents/R = new/datum/reagents(100) - R.add_reagent(/singleton/reagent/kois,10) - R.add_reagent(/singleton/reagent/toxin/phoron,10) - var/datum/effect/effect/system/smoke_spread/chem/spores/S = new("koisspore") - - S.attach(T) - S.set_up(R, 20, 0, T, 40) - S.start() - - if(owner.can_feel_pain()) - owner.emote("scream") - owner.adjustHalLoss(15) - owner.drip(15) - owner.delayed_vomit() - -/////////////////// -///Black Mycosis/// -/////////////////// - -/obj/item/organ/internal/parasite/blackkois - name = "k'ois mycosis" - icon = 'icons/obj/organs/organs.dmi' - icon_state = "black-on" - dead_icon = "black-off" - subtle = 1 - - organ_tag = "blackkois" - - parent_organ = BP_HEAD - var/removed_langs = FALSE - var/full_zombie = FALSE - stage_interval = 150 - drug_resistance = 1 - max_stage = 5 - - origin_tech = list(TECH_BIO = 7) - - egg = /singleton/reagent/kois/black - -/obj/item/organ/internal/parasite/blackkois/process() - ..() - - if(prob(10) && (owner.can_feel_pain())) - if(stage < 3) - to_chat(owner, "You feel a stinging pain in your head!") - else - to_chat(owner, "A part of you tries to fight back, but the taste of the black k'ois puts you at ease.") - owner.visible_message("[owner] winces slightly.") - owner.adjustHalLoss(5) - - if(stage >= 2) - if(prob(10) && !(owner.species.flags & NO_BREATHE)) - owner.visible_message("[owner] gasps for air!") - owner.losebreath += 5 - - if(stage >= 3) - set_light(-1.5, 6, "#FFFFFF") - if(!(GLOB.all_languages[LANGUAGE_VAURCA] in owner.languages)) - owner.add_language(LANGUAGE_VAURCA) - to_chat(owner, " Your mind expands, and your thoughts join the unity of the Hivenet.") - - if(prob(5)) - if(stage < 4) - to_chat(owner, SPAN_WARNING("You feel something squirming inside of you!")) - else - to_chat(owner, SPAN_GOOD("You feel it, within you. Its presence soothes, a constant companion. There is no need to resist. We are with you. We will not abandon you.")) - owner.reagents.add_reagent(/singleton/reagent/kois/black, 4) - - else if(prob(5)) - if(stage < 4) - to_chat(owner, SPAN_GOOD("In your struggle, a part of you wishes for the spread to continue.")) - else - to_chat(owner, SPAN_GOOD("You cannot ever believe that you struggled against this feeling. You are home. You are happy.")) - - else if(prob(10)) - to_chat(owner, "You feel disorientated!") - switch(rand(1,3)) - if(1) - owner.confused += 10 - owner.apply_effect(10,EYE_BLUR) - if(2) - owner.slurring += 30 - if(3) - owner.make_dizzy(10) - - if(stage >= 4) - - var/obj/item/organ/internal/brain/B = owner.internal_organs_by_name[BP_BRAIN] - - if(B && !B.prepared) - to_chat(owner, SPAN_GOOD("As the K'ois consumes your mind, you feel your past self, your memories, your very being slip away. Do not resist it. Let Us in.")) - to_chat(owner, SPAN_HIGHDANGER("You have been lobotomized by K'ois infection.")) - to_chat(owner, SPAN_NOTICE("All of your previous memories up until this point are gone, and all of your ambitions are nothing. You live for only one purpose; to serve the Lii'dra hive.")) - - B.prepared = 1 - - - if(!removed_langs) - for(var/datum/language/L in owner.languages) - owner.remove_language(L.name) - owner.add_language(LANGUAGE_VAURCA) - owner.add_language(LANGUAGE_LIIDRA) - owner.set_default_language(LANGUAGE_LIIDRA) - removed_langs = TRUE - - owner.set_see_invisible(SEE_INVISIBLE_NOLIGHTING) - owner.add_client_color(/datum/client_color/vaurca) - - if(prob(10)) - if(owner.can_feel_pain()) - to_chat(owner, SPAN_WARNING("You feel an unbearable pain in your mind!")) - owner.emote("scream") - else if(full_zombie) - to_chat(owner, SPAN_GOOD("Your mind hums with a billion voices that are not your own. You will never be alone again. Unbidden, a smile comes to your face.")) - owner.adjustBrainLoss(1, 55) - - else if(prob(0.5)) - if(stage <= 4) - to_chat(owner, SPAN_DANGER("You feel something alien coming up your throat!")) - else - to_chat(owner, SPAN_GOOD("You can feel it, flowering within your lungs. What a beautiful feeling, to share it with the Spur. You exhale, and the companion nestled within your flesh blooms, drowning out the light.")) - var/turf/T = get_turf(owner) - - var/datum/reagents/R = new/datum/reagents(20) - R.add_reagent(/singleton/reagent/kois/black,5) - var/datum/effect/effect/system/smoke_spread/chem/spores/S = new("blackkois") - - S.attach(T) - S.set_up(R, 20, 0, T, 40) - S.start() - - if(owner.can_feel_pain()) - owner.emote("scream") - owner.adjustHalLoss(5) - owner.drip(5) - owner.delayed_vomit() - - if(stage >= 5) - if(!full_zombie) - to_chat(owner, SPAN_GOOD("You are dying. But it does not matter. You will serve Our will, until you can serve no more.")) - to_chat(owner, SPAN_NOTICE("You are in the final stage of black k'ois mycosis. It cannot be removed, and you can no longer handle guns or other complex devices, as \ - your fine motor skills erode. However, you are stronger, and no longer affected by pain.")) - if(owner.can_feel_pain()) - owner.pain_immune = TRUE - if(!owner.lobotomized) - owner.lobotomized = TRUE //no guns for you, beat people to death as high queen lii'dra intended - owner.max_stamina = 500 //you are a discount zombie, you run good - add_verb(owner, /mob/living/carbon/human/proc/kois_cough) - add_verb(owner, /mob/living/carbon/human/proc/kois_infect) - full_zombie = TRUE - -/obj/item/organ/internal/parasite/blackkois/removed(var/mob/living/carbon/human/target) - var/obj/item/organ/internal/brain/B = target.internal_organs_by_name[BP_BRAIN] - switch(stage) - if(1) - target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] briefly, the black k'ois pulsing in [target.get_pronoun("his")] brain. You can see the scars it has left - it was not a clean surgery, but the damage is minor. [target.get_pronoun("He")] will likely fully recover, given time."),\ - SPAN_WARNING("They cut at your mind, removing the growth that infests it. Your head aches - but your mind is still intact.")) - to_chat(target, SPAN_CULT("A shiver passes over your mind, an odd sense of loneliness. But it lasts only a moment, and then it is gone.")) - B.max_damage = B.max_damage*0.95 //you got it early, good job - if(2) - target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, the black k'ois embedded in [target.get_pronoun("his")] brain. You can see the scars it has left - though it was not a clean surgery, the damage is not too great. [target.get_pronoun("He")] may well fully recover."),\ - SPAN_WARNING("They cut at your mind, removing the growth that infests it. Your head hurts, and you find some things hard to recall - but you remember who you are, and you are free from a truly terrible fate. Perhaps that is some comfort.")) - to_chat(target, SPAN_CULT("Something lingers on you. You are free of the mycosis before it could kill you - and why does that fact make you feel almost sad?")) - B.max_damage = B.max_damage*0.75 //bit more scarring but still ok - if(3) - target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, the black k'ois deeply embedded in [target.get_pronoun("his")] brain. You can see the scars it has left - it was not a clean surgery, and [target.get_pronoun("he")] may not fully recover - but [target.get_pronoun("he")] will live, at least."),\ - SPAN_WARNING("They cut at your mind, removing the growth that infests it. Your head hurts, and you find some things hard to recall - but you remember who you are, and you are free from a truly terrible fate. Perhaps that is some comfort.")) - to_chat(target, SPAN_CULT("A part of you misses the feeling of it, pulsing inside your skull like a friend, a constant companion. You have survived - but you cannot help but feel empty.")) - B.max_damage = B.max_damage*0.65 //ok, now you have permanent brain damage - if(4) - target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, the black k'ois deeply embedded in [target.get_pronoun("his")] brain. You can see the scars it has left - it was not a clean surgery, and [target.get_pronoun("he")] will never fully recover - but [target.get_pronoun("he")] will live, at least."),\ - SPAN_WARNING("They cut at your mind, removing the growth that infests it. It's hard to remember what you are, without it. Perhaps, if you think hard enough, you can remember your name.")) - to_chat(target, SPAN_CULT("You have failed Us. We will find another way. Releasing control.")) - B.max_damage = B.max_damage*0.45 //yeah your brain is severely messed up man - if(5) - target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, [target.get_pronoun("his")] brain too damaged to function. A horrible choking sound emerges from [target.get_pronoun("his")] mouth, before it is mercifully silenced as [target.get_pronoun("he")] falls still."),\ - SPAN_DANGER("You feel the cold of the knife cutting and cutting, but it is too late. There is nothing left of you but the Lii'dra's will, now. A final cut - and then you are granted the only escape that still remains to you.")) - to_chat(target, SPAN_GOOD("Your function is fulfilled. We have no further need of you. Releasing control.")) - target.death(FALSE) //too late, there is no brain left to save - if(GLOB.all_languages[LANGUAGE_VAURCA] in target.languages && stage >= 3 && !isvaurca(target)) - target.remove_language(LANGUAGE_VAURCA) - to_chat(target, "Your mind suddenly grows dark as the unity of the Hive is torn from you.") - if(GLOB.all_languages[LANGUAGE_LIIDRA] in target.languages && stage >= 3) - target.remove_language(LANGUAGE_LIIDRA) - removed_langs = 0 - remove_verb(owner, /mob/living/carbon/human/proc/kois_cough) - remove_verb(owner, /mob/living/carbon/human/proc/kois_infect) - ..() - -/obj/item/organ/internal/parasite/zombie - name = "black tumour" - icon = 'icons/obj/organs/organs.dmi' - icon_state = "black_tumour" - dead_icon = "black_tumour" - - organ_tag = BP_ZOMBIE_PARASITE - parent_organ = BP_HEAD - stage_interval = 110 - drug_resistance = TRUE - relative_size = 0 - - egg = /singleton/reagent/toxin/trioxin - - var/last_heal = 0 - var/heal_rate = 5 SECONDS - -/obj/item/organ/internal/parasite/zombie/process() - ..() - - if (!owner) - return - - if(length(owner.bad_external_organs) && last_heal + heal_rate < world.time && iszombie(owner)) - var/list/organs_to_heal = owner.bad_external_organs - shuffle(organs_to_heal) - for(var/thing in organs_to_heal) - var/obj/item/organ/external/O = thing - if(istype(O, /obj/item/organ/external/head)) // the head is the weak point - continue - var/healed = FALSE - if(O.status & ORGAN_ARTERY_CUT) - O.status &= ~ORGAN_ARTERY_CUT - owner.visible_message(SPAN_WARNING("The severed artery in \the [owner]'s [O] stitches itself back together..."), SPAN_NOTICE("The severed artery in your [O] stitches itself back together...")) - healed = TRUE - else if((O.tendon_status() & TENDON_CUT) && O.tendon.can_recover()) - O.tendon.rejuvenate() - owner.visible_message(SPAN_WARNING("The severed tendon in \the [owner]'s [O] stitches itself back together..."), SPAN_NOTICE("The severed tendon in your [O] stitches itself back together...")) - healed = TRUE - else if(O.status & ORGAN_BROKEN) - var/list/brute_wounds = list() - for(var/wound in O.wounds) - var/datum/wound/W = wound - if(W.damage_type in list(CUT, BRUISE, PIERCE)) - brute_wounds += W - for(var/wound in brute_wounds) - var/datum/wound/W = wound - W.damage = max(min(W.damage, (O.min_broken_damage / length(brute_wounds))), 0) - O.status &= ~ORGAN_BROKEN - owner.visible_message(SPAN_WARNING("The shattered bone in \the [owner]'s [O] melds back together..."), SPAN_NOTICE("The shattered bone in your [O] melds back together...")) - healed = TRUE - if(healed) - last_heal = world.time - heal_rate += 2 SECONDS - O.update_damages() - break - - if(prob(10) && (owner.can_feel_pain())) - to_chat(owner, "You feel a burning sensation on your skin!") - owner.make_jittery(10) - - else if(prob(10)) - owner.emote("moan") - - if(stage >= 2) - if(prob(15)) - owner.emote("scream") - if(!isundead(owner)) - owner.adjustBrainLoss(2, 55) - - else if(prob(10)) - if(!isundead(owner)) - to_chat(owner, "You feel sick.") - owner.adjustToxLoss(5) - owner.delayed_vomit() - - if(stage >= 3) - if(prob(10)) - if(isundead(owner)) - owner.adjustBruteLoss(-30) - owner.adjustFireLoss(-30) - else - to_chat(owner, "You feel an insatiable hunger.") - owner.nutrition = -1 - - if(stage >= 4) - if(prob(10)) - if(!isundead(owner)) - if(owner.species.zombie_type) - var/r = owner.r_skin - var/g = owner.g_skin - var/b = owner.b_skin - - for(var/datum/language/L in owner.languages) - owner.remove_language(L.name) - to_chat(owner, "You feel life leaving your husk, but death rejects you...") - playsound(src.loc, 'sound/hallucinations/far_noise.ogg', 50, 1) - to_chat(owner, "All that is left is a cruel hunger for the flesh of the living, and the desire to spread this infection. You must consume all the living!") - owner.set_species(owner.species.zombie_type, 0, 0, 0) - owner.change_skin_color(r, g, b) - owner.update_dna() - else - owner.adjustToxLoss(50) - -//Parasitic Worms// - -/obj/item/organ/internal/parasite/nerveworm - name = "bundle of nerve flukes" - icon = 'icons/obj/organs/organs.dmi' - icon_state = "helminth" - dead_icon = "helminth_dead" - - organ_tag = BP_WORM_NERVE - parent_organ = BP_L_ARM //if desperate, can lop your arm off to remove the infection :) - subtle = 1 - - origin_tech = list(TECH_BIO = 4) - - egg = /singleton/reagent/toxin/nerveworm_eggs - -/obj/item/organ/internal/parasite/nerveworm/process() - ..() - - if (!owner) - return - - if(prob(4)) - owner.adjustNutritionLoss(10) - - if(stage >= 2) //after ~5 minutes - owner.confused = max(owner.confused, 10) - - if(stage >= 3) //after ~10 minutes - owner.slurring = max(owner.slurring, 100) - owner.drowsiness = max(owner.drowsiness, 20) - if(prob(1)) - owner.delayed_vomit() - if(prob(2)) - to_chat(owner, SPAN_WARNING(pick("You feel a tingly sensation in your fingers.", "Is that pins and needles?", "An electric sensation jolts its way up your left arm.", "You smell something funny.", "You taste something funny."))) - - if(stage >= 4) //after ~15 minutes - if(prob(2)) - to_chat(owner, SPAN_WARNING(pick("A deep, tingling sensation paralyses your left arm.", "You feel as if you just struck your funny bone.", "You feel a pulsing sensation within your left arm."))) - owner.emote(pick("twitch", "shiver")) - owner.stuttering = 20 - if(prob(1)) - owner.seizure() - if(prob(5)) - owner.reagents.add_reagent(/singleton/reagent/toxin/nerveworm_eggs, 2) - owner.adjustHalLoss(15) - to_chat(owner, SPAN_WARNING("An extreme, nauseating pain erupts from deep within your left arm!")) - - -/obj/item/organ/internal/parasite/heartworm - name = "bundle of heart flukes" - icon = 'icons/obj/organs/organs.dmi' - icon_state = "helminth" - dead_icon = "helminth_dead" - - organ_tag = BP_WORM_HEART - parent_organ = BP_CHEST - subtle = 1 - - stage_interval = 450 //~7.5 minutes/stage - - origin_tech = list(TECH_BIO = 4) - - egg = /singleton/reagent/toxin/heartworm_eggs - -/obj/item/organ/internal/parasite/heartworm/process() - ..() - - var/obj/item/organ/internal/heart = owner.internal_organs_by_name[BP_HEART] - - if (!owner) - return - - if(BP_IS_ROBOTIC(heart)) - recession = 10 - return - - if(prob(4)) - owner.adjustNutritionLoss(10) - - if(stage >= 2) //after ~7.5 minutes - if(prob(2)) - owner.emote("cough") - - if(stage >= 3) //after ~15 minutes - if(prob(7)) - heart.take_damage(rand(2,5)) - if(prob(5)) - to_chat(owner, SPAN_WARNING(pick("Your chest feels tight.", "Your chest is aching.", "You feel a stabbing pain in your chest!", "You feel a painful, tickly sensation within your chest."))) - owner.adjustHalLoss(15) - - if(stage >= 4) //after ~22.5 minutes - if(prob(5)) - owner.reagents.add_reagent(/singleton/reagent/toxin/heartworm_eggs, 2) - owner.adjustHalLoss(15) - to_chat(owner, SPAN_WARNING("An extreme, nauseating pain erupts from the centre of your chest!")) - diff --git a/code/modules/organs/subtypes/parasite/_parasite.dm b/code/modules/organs/subtypes/parasite/_parasite.dm new file mode 100644 index 00000000000..763893bbc28 --- /dev/null +++ b/code/modules/organs/subtypes/parasite/_parasite.dm @@ -0,0 +1,75 @@ +/obj/item/organ/internal/parasite + name = "parasite" + + organ_tag = "parasite" + var/stage = 1 + var/max_stage = 4 + var/stage_ticker = 0 + var/recession = 0 + + ///Infection progress speed, will be determined by get_infect_speed() + var/infection_speed = 2 + + ///The fastest the parasite will advance its stage + var/infect_speed_high = 35 + + ///The fastest the parasite will advance its stage + var/infect_speed_low = 15 + + ///Time between stages, in seconds. interval of 300 allows convenient treating with antiparasitics, higher will require spaced dosing of medications. + var/stage_interval = 300 + + ///Boolean, if the body should reject the parasite naturally + var/subtle = FALSE + + ///Does the parasite have a reagent which seeds an infection? + var/egg = null + + //Boolean, if the parasite is resistant to antiparasitic medications + var/drug_resistance = FALSE + +/obj/item/organ/internal/parasite/Initialize() + . = ..() + get_infect_speed() + +/obj/item/organ/internal/parasite/proc/get_infect_speed() //Slightly randomizes how fast each infection progresses. + infection_speed = rand(infect_speed_low, infect_speed_high) / 10 + +/obj/item/organ/internal/parasite/process() + ..() + if(!owner) + return + + if(owner.chem_effects[CE_ANTIPARASITE] && !drug_resistance) + recession = owner.chem_effects[CE_ANTIPARASITE]/10 + + if((stage < max_stage) && !recession) + stage_ticker = Clamp(stage_ticker+=infection_speed, 0, stage_interval*max_stage) + if(stage_ticker >= stage*stage_interval) + process_stage() + get_infect_speed() //Each stage may progress faster or slower than the previous one + stage_effect() + + if(recession) + stage_ticker = Clamp(stage_ticker-=recession, 0, stage_interval*max_stage) + if(stage_ticker <= stage*stage_interval-stage_interval) + stage = max(stage-1, 1) + stage_effect() + if(!owner.chem_effects[CE_ANTIPARASITE] || drug_resistance) + recession = 0 + if(stage_ticker == 0) + qdel(src) + +/obj/item/organ/internal/parasite/handle_rejection() + if(subtle) + return ..() + else + if(rejecting) + rejecting = 0 + return + +/obj/item/organ/internal/parasite/proc/process_stage() + stage = min(stage+1,max_stage) + +/obj/item/organ/internal/parasite/proc/stage_effect() + return diff --git a/code/modules/organs/subtypes/parasite/black_kois.dm b/code/modules/organs/subtypes/parasite/black_kois.dm new file mode 100644 index 00000000000..12841bdaa0e --- /dev/null +++ b/code/modules/organs/subtypes/parasite/black_kois.dm @@ -0,0 +1,169 @@ +/obj/item/organ/internal/parasite/blackkois + name = "k'ois mycosis" + icon = 'icons/obj/organs/organs.dmi' + icon_state = "black-on" + dead_icon = "black-off" + subtle = 1 + + organ_tag = "blackkois" + + parent_organ = BP_HEAD + var/removed_langs = FALSE + var/full_zombie = FALSE + stage_interval = 150 + drug_resistance = 1 + max_stage = 5 + + origin_tech = list(TECH_BIO = 7) + + egg = /singleton/reagent/kois/black + +/obj/item/organ/internal/parasite/blackkois/process() + ..() + + if(prob(10) && (owner.can_feel_pain())) + if(stage < 3) + to_chat(owner, SPAN_WARNING("You feel a stinging pain in your head!")) + else + to_chat(owner, "A part of you tries to fight back, but the taste of the black k'ois puts you at ease.") + owner.visible_message("[owner] winces slightly.") + owner.adjustHalLoss(5) + + if(stage >= 2) + if(prob(10) && !(owner.species.flags & NO_BREATHE)) + owner.visible_message("[owner] gasps for air!") + owner.losebreath += 5 + + if(stage >= 3) + set_light(-1.5, 6, "#FFFFFF") + if(!(GLOB.all_languages[LANGUAGE_VAURCA] in owner.languages)) + owner.add_language(LANGUAGE_VAURCA) + to_chat(owner, SPAN_NOTICE("Your mind expands, and your thoughts join the unity of the Hivenet.")) + + if(prob(5)) + if(stage < 4) + to_chat(owner, SPAN_WARNING("You feel something squirming inside of you!")) + else + to_chat(owner, SPAN_GOOD("You feel it, within you. Its presence soothes, a constant companion. There is no need to resist. We are with you. We will not abandon you.")) + owner.reagents.add_reagent(/singleton/reagent/kois/black, 4) + + else if(prob(5)) + if(stage < 4) + to_chat(owner, SPAN_GOOD("In your struggle, a part of you wishes for the spread to continue.")) + else + to_chat(owner, SPAN_GOOD("You cannot ever believe that you struggled against this feeling. You are home. You are happy.")) + + else if(prob(10)) + to_chat(owner, SPAN_WARNING("You feel disorientated!")) + switch(rand(1,3)) + if(1) + owner.confused += 10 + owner.apply_effect(10,EYE_BLUR) + if(2) + owner.slurring += 30 + if(3) + owner.make_dizzy(10) + + if(stage >= 4) + + var/obj/item/organ/internal/brain/B = owner.internal_organs_by_name[BP_BRAIN] + + if(B && !B.prepared) + to_chat(owner, SPAN_GOOD("As the K'ois consumes your mind, you feel your past self, your memories, your very being slip away. Do not resist it. Let Us in.")) + to_chat(owner, SPAN_HIGHDANGER("You have been lobotomized by K'ois infection.")) + to_chat(owner, SPAN_NOTICE("All of your previous memories up until this point are gone, and all of your ambitions are nothing. You live for only one purpose; to serve the Lii'dra hive.")) + + B.prepared = 1 + + + if(!removed_langs) + for(var/datum/language/L in owner.languages) + owner.remove_language(L.name) + owner.add_language(LANGUAGE_VAURCA) + owner.add_language(LANGUAGE_LIIDRA) + owner.set_default_language(LANGUAGE_LIIDRA) + removed_langs = TRUE + + owner.set_see_invisible(SEE_INVISIBLE_NOLIGHTING) + owner.add_client_color(/datum/client_color/vaurca) + + if(prob(10)) + if(owner.can_feel_pain()) + to_chat(owner, SPAN_WARNING("You feel an unbearable pain in your mind!")) + owner.emote("scream") + else if(full_zombie) + to_chat(owner, SPAN_GOOD("Your mind hums with a billion voices that are not your own. You will never be alone again. Unbidden, a smile comes to your face.")) + owner.adjustBrainLoss(1, 55) + + else if(prob(0.5)) + if(stage <= 4) + to_chat(owner, SPAN_DANGER("You feel something alien coming up your throat!")) + else + to_chat(owner, SPAN_GOOD("You can feel it, flowering within your lungs. What a beautiful feeling, to share it with the Spur. You exhale, and the companion nestled within your flesh blooms, drowning out the light.")) + var/turf/T = get_turf(owner) + + var/datum/reagents/R = new/datum/reagents(20) + R.add_reagent(/singleton/reagent/kois/black,5) + var/datum/effect/effect/system/smoke_spread/chem/spores/S = new("blackkois") + + S.attach(T) + S.set_up(R, 20, 0, T, 40) + S.start() + + if(owner.can_feel_pain()) + owner.emote("scream") + owner.adjustHalLoss(5) + owner.drip(5) + owner.delayed_vomit() + + if(stage >= 5) + if(!full_zombie) + to_chat(owner, SPAN_GOOD("You are dying. But it does not matter. You will serve Our will, until you can serve no more.")) + to_chat(owner, SPAN_NOTICE("You are in the final stage of black k'ois mycosis. It cannot be removed, and you can no longer handle guns or other complex devices, as \ + your fine motor skills erode. However, you are stronger, and no longer affected by pain.")) + if(owner.can_feel_pain()) + owner.pain_immune = TRUE + if(!owner.lobotomized) + owner.lobotomized = TRUE //no guns for you, beat people to death as high queen lii'dra intended + owner.max_stamina = 500 //you are a discount zombie, you run good + add_verb(owner, /mob/living/carbon/human/proc/kois_cough) + add_verb(owner, /mob/living/carbon/human/proc/kois_infect) + full_zombie = TRUE + +/obj/item/organ/internal/parasite/blackkois/removed(var/mob/living/carbon/human/target) + var/obj/item/organ/internal/brain/B = target.internal_organs_by_name[BP_BRAIN] + switch(stage) + if(1) + target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] briefly, the black k'ois pulsing in [target.get_pronoun("his")] brain. You can see the scars it has left - it was not a clean surgery, but the damage is minor. [target.get_pronoun("He")] will likely fully recover, given time."),\ + SPAN_WARNING("They cut at your mind, removing the growth that infests it. Your head aches - but your mind is still intact.")) + to_chat(target, SPAN_CULT("A shiver passes over your mind, an odd sense of loneliness. But it lasts only a moment, and then it is gone.")) + B.max_damage = B.max_damage*0.95 //you got it early, good job + if(2) + target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, the black k'ois embedded in [target.get_pronoun("his")] brain. You can see the scars it has left - though it was not a clean surgery, the damage is not too great. [target.get_pronoun("He")] may well fully recover."),\ + SPAN_WARNING("They cut at your mind, removing the growth that infests it. Your head hurts, and you find some things hard to recall - but you remember who you are, and you are free from a truly terrible fate. Perhaps that is some comfort.")) + to_chat(target, SPAN_CULT("Something lingers on you. You are free of the mycosis before it could kill you - and why does that fact make you feel almost sad?")) + B.max_damage = B.max_damage*0.75 //bit more scarring but still ok + if(3) + target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, the black k'ois deeply embedded in [target.get_pronoun("his")] brain. You can see the scars it has left - it was not a clean surgery, and [target.get_pronoun("he")] may not fully recover - but [target.get_pronoun("he")] will live, at least."),\ + SPAN_WARNING("They cut at your mind, removing the growth that infests it. Your head hurts, and you find some things hard to recall - but you remember who you are, and you are free from a truly terrible fate. Perhaps that is some comfort.")) + to_chat(target, SPAN_CULT("A part of you misses the feeling of it, pulsing inside your skull like a friend, a constant companion. You have survived - but you cannot help but feel empty.")) + B.max_damage = B.max_damage*0.65 //ok, now you have permanent brain damage + if(4) + target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, the black k'ois deeply embedded in [target.get_pronoun("his")] brain. You can see the scars it has left - it was not a clean surgery, and [target.get_pronoun("he")] will never fully recover - but [target.get_pronoun("he")] will live, at least."),\ + SPAN_WARNING("They cut at your mind, removing the growth that infests it. It's hard to remember what you are, without it. Perhaps, if you think hard enough, you can remember your name.")) + to_chat(target, SPAN_CULT("You have failed Us. We will find another way. Releasing control.")) + B.max_damage = B.max_damage*0.45 //yeah your brain is severely messed up man + if(5) + target.visible_message(SPAN_WARNING("As the black k'ois is cut from [target.name], [target.get_pronoun("he")] spasm[target.get_pronoun("end")] wildly, [target.get_pronoun("his")] brain too damaged to function. A horrible choking sound emerges from [target.get_pronoun("his")] mouth, before it is mercifully silenced as [target.get_pronoun("he")] falls still."),\ + SPAN_DANGER("You feel the cold of the knife cutting and cutting, but it is too late. There is nothing left of you but the Lii'dra's will, now. A final cut - and then you are granted the only escape that still remains to you.")) + to_chat(target, SPAN_GOOD("Your function is fulfilled. We have no further need of you. Releasing control.")) + target.death(FALSE) //too late, there is no brain left to save + if(GLOB.all_languages[LANGUAGE_VAURCA] in target.languages && stage >= 3 && !isvaurca(target)) + target.remove_language(LANGUAGE_VAURCA) + to_chat(target, SPAN_WARNING("Your mind suddenly grows dark as the unity of the Hive is torn from you.")) + if(GLOB.all_languages[LANGUAGE_LIIDRA] in target.languages && stage >= 3) + target.remove_language(LANGUAGE_LIIDRA) + removed_langs = 0 + remove_verb(owner, /mob/living/carbon/human/proc/kois_cough) + remove_verb(owner, /mob/living/carbon/human/proc/kois_infect) + ..() diff --git a/code/modules/organs/subtypes/parasite/heart_fluke.dm b/code/modules/organs/subtypes/parasite/heart_fluke.dm new file mode 100644 index 00000000000..37ee03cfd5e --- /dev/null +++ b/code/modules/organs/subtypes/parasite/heart_fluke.dm @@ -0,0 +1,47 @@ +/obj/item/organ/internal/parasite/heartworm + name = "bundle of heart flukes" + icon = 'icons/obj/organs/organs.dmi' + icon_state = "helminth" + dead_icon = "helminth_dead" + + organ_tag = BP_WORM_HEART + parent_organ = BP_CHEST + subtle = 1 + + stage_interval = 450 //~7.5 minutes/stage + + origin_tech = list(TECH_BIO = 4) + + egg = /singleton/reagent/toxin/heartworm_eggs + +/obj/item/organ/internal/parasite/heartworm/process() + ..() + + var/obj/item/organ/internal/heart = owner.internal_organs_by_name[BP_HEART] + + if (!owner) + return + + if(BP_IS_ROBOTIC(heart)) + recession = 10 + return + + if(prob(4)) + owner.adjustNutritionLoss(10) + + if(stage >= 2) //after ~7.5 minutes + if(prob(2)) + owner.emote("cough") + + if(stage >= 3) //after ~15 minutes + if(prob(7)) + heart.take_damage(rand(2,5)) + if(prob(5)) + to_chat(owner, SPAN_WARNING(pick("Your chest feels tight.", "Your chest is aching.", "You feel a stabbing pain in your chest!", "You feel a painful, tickly sensation within your chest."))) + owner.adjustHalLoss(15) + + if(stage >= 4) //after ~22.5 minutes + if(prob(5)) + owner.reagents.add_reagent(/singleton/reagent/toxin/heartworm_eggs, 2) + owner.adjustHalLoss(15) + to_chat(owner, SPAN_WARNING("An extreme, nauseating pain erupts from the centre of your chest!")) diff --git a/code/modules/organs/subtypes/parasite/kois.dm b/code/modules/organs/subtypes/parasite/kois.dm new file mode 100644 index 00000000000..6424a6a1c35 --- /dev/null +++ b/code/modules/organs/subtypes/parasite/kois.dm @@ -0,0 +1,68 @@ +/obj/item/organ/internal/parasite/kois + name = "k'ois mycosis" + icon = 'icons/obj/organs/organs.dmi' + icon_state = "kois-on" + dead_icon = "kois-off" + + organ_tag = "kois" + + parent_organ = BP_CHEST + stage_interval = 150 + drug_resistance = 1 + + origin_tech = list(TECH_BIO = 3) + + egg = /singleton/reagent/kois + +/obj/item/organ/internal/parasite/kois/process() + ..() + + if (!owner) + return + + if(prob(10) && (owner.can_feel_pain())) + to_chat(owner, SPAN_WARNING("You feel a stinging pain in your abdomen!")) + owner.visible_message("[owner] winces slightly.") + owner.adjustHalLoss(5) + + else if(prob(10) && !(owner.species.flags & NO_BREATHE)) + owner.emote("cough") + + else if(prob(10) && !(owner.species.flags & NO_BREATHE)) + owner.visible_message("[owner] coughs up blood!") + owner.drip(10) + + if(stage >= 2) + if(prob(10) && !(owner.species.flags & NO_BREATHE)) + owner.visible_message("[owner] gasps for air!") + owner.losebreath += 5 + + if(stage >= 3) + set_light(1, l_color = "#E6E600") + if(prob(10)) + to_chat(owner, SPAN_WARNING("You feel something squirming inside of you!")) + owner.reagents.add_reagent(/singleton/reagent/toxin/phoron, 8) + owner.reagents.add_reagent(/singleton/reagent/kois, 5) + + if(stage >= 4) + if(prob(10)) + to_chat(owner, SPAN_DANGER("You feel something alien coming up your throat!")) + owner.emote("cough") + + var/turf/T = get_turf(owner) + + var/datum/reagents/R = new/datum/reagents(100) + R.add_reagent(/singleton/reagent/kois,10) + R.add_reagent(/singleton/reagent/toxin/phoron,10) + var/datum/effect/effect/system/smoke_spread/chem/spores/S = new("koisspore") + + S.attach(T) + S.set_up(R, 20, 0, T, 40) + S.start() + + if(owner.can_feel_pain()) + owner.emote("scream") + owner.adjustHalLoss(15) + owner.drip(15) + owner.delayed_vomit() + diff --git a/code/modules/organs/subtypes/parasite/worms.dm b/code/modules/organs/subtypes/parasite/worms.dm new file mode 100644 index 00000000000..8bcab6b8622 --- /dev/null +++ b/code/modules/organs/subtypes/parasite/worms.dm @@ -0,0 +1,45 @@ +/obj/item/organ/internal/parasite/nerveworm + name = "bundle of nerve flukes" + icon = 'icons/obj/organs/organs.dmi' + icon_state = "helminth" + dead_icon = "helminth_dead" + + organ_tag = BP_WORM_NERVE + parent_organ = BP_L_ARM //if desperate, can lop your arm off to remove the infection :) + subtle = 1 + + origin_tech = list(TECH_BIO = 4) + + egg = /singleton/reagent/toxin/nerveworm_eggs + +/obj/item/organ/internal/parasite/nerveworm/process() + ..() + + if (!owner) + return + + if(prob(4)) + owner.adjustNutritionLoss(10) + + if(stage >= 2) //after ~5 minutes + owner.confused = max(owner.confused, 10) + + if(stage >= 3) //after ~10 minutes + owner.slurring = max(owner.slurring, 100) + owner.drowsiness = max(owner.drowsiness, 20) + if(prob(1)) + owner.delayed_vomit() + if(prob(2)) + to_chat(owner, SPAN_WARNING(pick("You feel a tingly sensation in your fingers.", "Is that pins and needles?", "An electric sensation jolts its way up your left arm.", "You smell something funny.", "You taste something funny."))) + + if(stage >= 4) //after ~15 minutes + if(prob(2)) + to_chat(owner, SPAN_WARNING(pick("A deep, tingling sensation paralyses your left arm.", "You feel as if you just struck your funny bone.", "You feel a pulsing sensation within your left arm."))) + owner.emote(pick("twitch", "shiver")) + owner.stuttering = 20 + if(prob(1)) + owner.seizure() + if(prob(5)) + owner.reagents.add_reagent(/singleton/reagent/toxin/nerveworm_eggs, 2) + owner.adjustHalLoss(15) + to_chat(owner, SPAN_WARNING("An extreme, nauseating pain erupts from deep within your left arm!")) diff --git a/code/modules/organs/subtypes/parasite/zombie.dm b/code/modules/organs/subtypes/parasite/zombie.dm new file mode 100644 index 00000000000..e3d43a354d1 --- /dev/null +++ b/code/modules/organs/subtypes/parasite/zombie.dm @@ -0,0 +1,189 @@ +/obj/item/organ/internal/parasite/zombie + name = BP_ZOMBIE_PARASITE + icon = 'icons/obj/organs/organs.dmi' + icon_state = "black_tumour" + dead_icon = "black_tumour" + + organ_tag = BP_ZOMBIE_PARASITE + parent_organ = BP_L_HAND + stage_interval = 110 + drug_resistance = TRUE + relative_size = 0 + status = ORGAN_ZOMBIFIED + + egg = /singleton/reagent/toxin/hylemnomil + + /// If the parasite is currently being cured by an antidote. Stops progression. + var/curing = FALSE + /// If the parasite has been cured. Stops the cure from happening twice. + var/cured = FALSE + /// Tracker for cure progress. + var/cured_timer = 0 + /// The last world.time we were healed as a zombie. + var/last_heal = 0 + /// The amount of seconds between each heal. + var/heal_rate = 5 SECONDS + +/obj/item/organ/internal/parasite/zombie/process() + ..() + + if(!owner) + return + + if(owner.stat >= DEAD) + return + + if(!iszombie(owner)) + var/obj/item/organ/external/E = owner.organs_by_name[parent_organ] + if(curing && !cured) + cured_timer += 5 + switch(cured_timer) + if(10 to 40) + if(prob(10)) + to_chat(owner, SPAN_DANGER("The veins in your [E.name] feel like they're coming apart!")) + E.add_pain(10) + if(40 to 80) + if(prob(10)) + to_chat(owner, SPAN_DANGER(FONT_HUGE("Your [E.name] is burning as the black veins start retreating!"))) + E.add_pain(25) + if(E.status & ORGAN_ZOMBIFIED) + E.add_pain(25) + E.status &= ~ORGAN_ZOMBIFIED + owner.update_body(TRUE, TRUE) + if(90 to INFINITY) + to_chat(owner, SPAN_DANGER(FONT_HUGE("Your head, your whole body starts burning up...!"))) + owner.Paralyse(20) + owner.Stun(20) + owner.adjustHalLoss(50) + cured = TRUE + addtimer(CALLBACK(src, PROC_REF(cure_infection)), 10 SECONDS) + + return + + if(stage < 2) + if(prob(2)) + to_chat(owner, SPAN_WARNING("The skin on your [E.name] itches a bit.")) + + if(stage >= 2) + if(prob(2)) + to_chat(owner, SPAN_WARNING("Your stomach churns, and you feel sick. Something tries to come up your throat, but nothing comes out...")) + owner.adjustHalLoss(5) + + if(stage >= 3) + if(prob(10)) + to_chat(owner, SPAN_CULT("Every beat of your heart hurts - an aching, dull pain. Cold sweat continues falling down your brows. You feel like you have a fever...")) + owner.nutrition = max(0, owner.nutrition - 10) + owner.hydration = max(0, owner.hydration - 10) + E.add_pain(20) + + if(stage >= 4) + if(prob(10)) + if(!isundead(owner)) + if(owner.species.zombie_type) + for(var/datum/language/L in owner.languages) + owner.remove_language(L.name) + owner.visible_message(SPAN_DANGER("[owner] falls to the ground and begins convulsing, their flesh turning green and rotting!")) + owner.seizure(FALSE, 5, 10, FALSE) + addtimer(CALLBACK(src, PROC_REF(turn_into_zombie)), 5 SECONDS, TIMER_UNIQUE) + else + owner.adjustToxLoss(50) + else + if(length(owner.bad_external_organs) && last_heal + heal_rate < world.time) + var/list/organs_to_heal = owner.bad_external_organs + shuffle(organs_to_heal) + for(var/thing in organs_to_heal) + var/obj/item/organ/external/O = thing + if(istype(O, /obj/item/organ/external/head)) // the head is the weak point + continue + var/healed = FALSE + if(O.status & ORGAN_ARTERY_CUT) + O.status &= ~ORGAN_ARTERY_CUT + owner.visible_message(SPAN_WARNING("The severed artery in \the [owner]'s [O] stitches itself back together..."), SPAN_NOTICE("The severed artery in your [O] stitches itself back together...")) + healed = TRUE + else if((O.tendon_status() & TENDON_CUT) && O.tendon.can_recover()) + O.tendon.rejuvenate() + owner.visible_message(SPAN_WARNING("The severed tendon in \the [owner]'s [O] stitches itself back together..."), SPAN_NOTICE("The severed tendon in your [O] stitches itself back together...")) + healed = TRUE + else if(O.status & ORGAN_BROKEN) + var/list/brute_wounds = list() + for(var/wound in O.wounds) + var/datum/wound/W = wound + if(W.damage_type in list(CUT, BRUISE, PIERCE)) + brute_wounds += W + for(var/wound in brute_wounds) + var/datum/wound/W = wound + W.damage = max(min(W.damage, (O.min_broken_damage / length(brute_wounds))), 0) + O.status &= ~ORGAN_BROKEN + owner.visible_message(SPAN_WARNING("The shattered bone in \the [owner]'s [O] melds back together..."), SPAN_NOTICE("The shattered bone in your [O] melds back together...")) + healed = TRUE + if(healed) + last_heal = world.time + heal_rate += 2 SECONDS + O.update_damages() + break + + if(prob(10) && (owner.can_feel_pain())) + to_chat(owner, "You feel a burning sensation on your skin!") + owner.make_jittery(10) + +/obj/item/organ/internal/parasite/zombie/process_stage() + if(stage >= 4) + return + var/obj/item/organ/external/E = owner.organs_by_name[parent_organ] + if(iszombie(owner)) + if(parent_organ != BP_HEAD) + var/obj/item/organ/external/head = owner.organs_by_name[BP_HEAD] + move_to(owner, head, E) + stage = 4 + return + if(E.parent) + to_chat(owner, SPAN_WARNING("The veins in your [E.name] turn black...")) + E.status |= ORGAN_ZOMBIFIED + move_to(owner, E.parent, E) + owner.update_body(TRUE, TRUE) + else + if(parent_organ == BP_CHEST) + to_chat(owner, SPAN_DANGER("Every vein in your chest turns black. Your heart becomes heavy. Each breath becomes fatigued. \ + You feel your own breathing. Cold sweat goes down your brows...")) + E.status |= ORGAN_ZOMBIFIED + var/obj/item/organ/external/head = owner.organs_by_name[BP_HEAD] + move_to(owner, head, E) + owner.update_body(TRUE, TRUE) + . = ..() + E = owner.organs_by_name[parent_organ] + if(E.limb_name == BP_GROIN) + stage = 2 + stage_interval = 110 + +/obj/item/organ/internal/parasite/zombie/proc/move_to(mob/living/carbon/human/H, obj/item/organ/external/new_organ, obj/item/organ/external/old_organ) + to_chat(H, SPAN_WARNING("The veins in your [new_organ.name] start turning black, bit by bit...")) + parent_organ = new_organ.limb_name + old_organ.internal_organs -= src + replaced(H, new_organ) + +/obj/item/organ/internal/parasite/zombie/proc/turn_into_zombie() + var/r = owner.r_skin + var/g = owner.g_skin + var/b = owner.b_skin + to_chat(owner, "You feel your flesh burning as it rots, and your head exploding as the virus reaches it...") + to_chat(owner, "All that is left is a cruel hunger for the flesh of the living, and the desire to spread this infection. You must consume all the living!") + owner.set_species(owner.species.zombie_type, 0, 0, 0) + var/list/wakeup_sounds = list( + 'sound/effects/zombies/zombie_1.ogg', + 'sound/effects/zombies/zombie_2.ogg', + 'sound/effects/zombies/zombie_3.ogg', + 'sound/effects/zombies/zombie_4.ogg' + ) + playsound(owner, pick(wakeup_sounds)) + owner.change_skin_color(r, g, b) + owner.update_dna() + +/obj/item/organ/internal/parasite/zombie/proc/cure_infection() + to_chat(owner, SPAN_GOOD(FONT_HUGE("...finally, relief. Your body starts cooling down. All your blackened veins go back to normal. It's over..."))) + for(var/obj/item/organ/external/E in owner.organs_by_name) + if(E.status & ORGAN_ZOMBIFIED) + E.status &= ~ORGAN_ZOMBIFIED + owner.update_body(TRUE, TRUE) + removed(owner) + owner.adjustHalLoss(-50) + qdel(src) diff --git a/code/modules/organs/subtypes/zombie.dm b/code/modules/organs/subtypes/zombie.dm index cce2a4374c0..725427c284f 100644 --- a/code/modules/organs/subtypes/zombie.dm +++ b/code/modules/organs/subtypes/zombie.dm @@ -1,33 +1,34 @@ +// Zombie limbs must be really easy to delimb. /obj/item/organ/external/chest/zombie - max_damage = 100 - min_broken_damage = 35 + max_damage = 20 + min_broken_damage = 15 /obj/item/organ/external/groin/zombie - maim_bonus = 5 + maim_bonus = 99 /obj/item/organ/external/arm/zombie - maim_bonus = 15 + maim_bonus = 99 /obj/item/organ/external/arm/right/zombie - maim_bonus = 15 + maim_bonus = 99 /obj/item/organ/external/leg/zombie - maim_bonus = 15 + maim_bonus = 99 /obj/item/organ/external/leg/right/zombie - maim_bonus = 15 + maim_bonus = 99 /obj/item/organ/external/foot/zombie - maim_bonus = 15 + maim_bonus = 99 /obj/item/organ/external/foot/right/zombie - maim_bonus = 15 + maim_bonus = 99 /obj/item/organ/external/hand/zombie maim_bonus = 15 /obj/item/organ/external/hand/right/zombie - maim_bonus = 15 + maim_bonus = 99 /obj/item/organ/external/head/zombie - maim_bonus = 3 + maim_bonus = 99 diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index 63459eb7167..633d12b08e8 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -1831,3 +1831,22 @@ if(P) if(P.drug_resistance == 0) P.drug_resistance = 1 + +/singleton/reagent/antibodies + name = "Hylemnomil-Zeta Antibodies" + description = "A pure antibody compound that is capable of fighting against Hylemnomil-Zeta." + color = "#FFFFFF" + overdose = 100 + metabolism = REM*0.0001 + scannable = TRUE + taste_description = "pure death" + +/singleton/reagent/antibodies/affect_blood(mob/living/carbon/M, alien, removed, datum/reagents/holder) + . = ..() + M.add_chemical_effect(CE_ANTIBODIES, 10) + var/obj/item/organ/internal/parasite/zombie/Z = M.internal_organs_by_name[BP_ZOMBIE_PARASITE] + if(Z && !Z.curing) + if(prob(5)) + var/obj/item/organ/external/E = Z.parent_organ + Z.curing = TRUE + to_chat(M, SPAN_WARNING("Your [E.name] tightens, pulses, and squirms as \the [Z] fights back against the antibodies!")) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 7aea2489534..6909bf7106b 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -842,49 +842,68 @@ H.berserk_stop() berserked = FALSE -/singleton/reagent/toxin/trioxin - name = "Trioxin" - description = "A synthetic compound of unknown origins, designated originally as a performance enhancing substance." +/singleton/reagent/toxin/hylemnomil + name = "Hylemnomil-Zeta" + description = "An extraordinary synthetic compound created at Einstein Engines Research Base Omega-99. This compound is synthetically created to incorporate parts of \ + the Rampancy Signal on Konyang. It rewrites an organism's DNA at the base and, similarly to rabies, makes the infected organic have an \ + unstoppable need to feed on anything it sees. Instructions can be conveyed to some degree, such as information on who is an Einstein Engines \ + employee and to not hurt them. The process of DNA rewriting leads to rapid rotting of the flesh." reagent_state = LIQUID - color = "#E7E146" + color = "#551A8B" strength = 1 - taste_description = "old eggs" + taste_description = "unknown scientific concoction" metabolism = REM unaffected_species = IS_DIONA | IS_MACHINE | IS_UNDEAD affects_dead = TRUE -/singleton/reagent/toxin/trioxin/affect_blood(var/mob/living/carbon/M, var/removed, var/datum/reagents/holder) +/singleton/reagent/toxin/hylemnomil/affect_blood(var/mob/living/carbon/M, var/removed, var/datum/reagents/holder) ..() - if(istype(M,/mob/living/carbon/human)) + if(ishuman(M)) var/mob/living/carbon/human/H = M + /// Thetamycin is a temporary fix. if(H.reagents.has_reagent(/singleton/reagent/thetamycin, 15)) return + /// Antibodies are a more permanent one. + if(H.chem_effects[CE_ANTIBODIES]) + return + if(!H.internal_organs_by_name[BP_ZOMBIE_PARASITE] && prob(15)) - var/obj/item/organ/external/affected = H.get_organ(BP_CHEST) + var/to_infest + var/list/possible_organs = list() + var/obj/item/organ/external/organ_to_check + + /// The infection starts from the hands and feet. The closer it is to the brain, the higher the stage it starts at. + for(var/organ in list(BP_R_HAND, BP_L_HAND, BP_R_FOOT, BP_L_FOOT)) + organ_to_check = H.organs_by_name[organ] + if(organ_to_check && !BP_IS_ROBOTIC(organ_to_check)) + possible_organs[organ] = 1 + + if(!length(possible_organs)) + for(var/organ in list(BP_R_ARM, BP_L_ARM, BP_R_LEG, BP_L_LEG)) + organ_to_check = H.organs_by_name[organ] + if(organ_to_check && !BP_IS_ROBOTIC(organ_to_check)) + possible_organs[organ] = 2 + + /// In case there aren't any appendages, try the groin. + if(!length(possible_organs)) + organ_to_check = H.organs_by_name[BP_GROIN] + if(organ_to_check && !BP_IS_ROBOTIC(organ_to_check)) + possible_organs[BP_GROIN] = 2 + + /// I'm not even sure how you get to being a nugget, but the last resort is the chest. + if(!length(possible_organs)) + possible_organs[BP_CHEST] = 3 + + /// Infect the user, apply the right stage, then remove all hylemnomil from the user. + to_infest = pick(possible_organs) + var/obj/item/organ/external/affected = H.organs_by_name[to_infest] var/obj/item/organ/internal/parasite/zombie/infest = new() infest.replaced(H, affected) - - if(H.species.zombie_type) - if(!H.internal_organs_by_name[BP_BRAIN]) //destroying the brain stops trioxin from bringing the dead back to life - return - - if(H && H.stat != DEAD) - return - - for(var/datum/language/L in H.languages) - H.remove_language(L.name) - - var/r = H.r_skin - var/g = H.g_skin - var/b = H.b_skin - - H.set_species(H.species.zombie_type, 0, 0, 0) - H.revive() - H.change_skin_color(r, g, b) - playsound(H.loc, 'sound/hallucinations/far_noise.ogg', 50, 1) - to_chat(H,"You return back to life as the undead, all that is left is the hunger to consume the living and the will to spread the infection.") + infest.parent_organ = affected.limb_name + infest.stage = possible_organs[to_infest] + H.reagents.remove_reagent(type, REAGENT_VOLUME(H.reagents, type)) /singleton/reagent/toxin/dextrotoxin name = "Dextrotoxin" diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 82d1bcb4edd..50d63a7487b 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -194,6 +194,13 @@ possible_transfer_amounts = list(5,10,15,25) fragile = 1 // very fragile +/obj/item/reagent_containers/glass/beaker/vial/antidote + name = "hylemnomil-zeta antidote vial" + desc = "Your lifeline. Defend it with your life." + volume = 60 + amount_per_transfer_from_this = 5 + reagents_to_add = list(/singleton/reagent/antibodies = 60) + /obj/item/reagent_containers/glass/beaker/medcup name = "medicine cup" desc = "A glass medicine cup. Like a shot glass for medicine." diff --git a/code/modules/reagents/reagent_containers/glass/bottle.dm b/code/modules/reagents/reagent_containers/glass/bottle.dm index 1e42e9259fa..ccc05cd9fbf 100644 --- a/code/modules/reagents/reagent_containers/glass/bottle.dm +++ b/code/modules/reagents/reagent_containers/glass/bottle.dm @@ -228,11 +228,10 @@ reagents_to_add = list(/singleton/reagent/hyronalin = 60) /obj/item/reagent_containers/glass/bottle/trioxin - name = "trioxin bottle" - desc = "A small bottle. Contains a swirling, green liquid." - icon_state = "bottle-1" - reagents_to_add = list(/singleton/reagent/toxin/trioxin = 60) - + name = "hylemnomil-zeta vial" + desc = "A small, unmarked lial with an ominous label. It contains a swirling, purple liquid." + icon_state = "bouteille" + reagents_to_add = list(/singleton/reagent/toxin/hylemnomil = 60) //syrups /obj/item/reagent_containers/glass/bottle/syrup diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 35310f5ba52..e269c2c1416 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -202,6 +202,9 @@ var/list/attached_organs = list() for(var/organ in target.internal_organs_by_name) var/obj/item/organ/I = target.internal_organs_by_name[organ] + /// Zombie organs can't be removed. Mainly to stop the zombie parasite from being removed. + if(I && (I.status & ORGAN_ZOMBIFIED)) + continue if(I && !(I.status & ORGAN_CUT_AWAY) && I.parent_organ == target_zone) attached_organs |= organ diff --git a/html/changelogs/mattatlas-seizureofpower.yml b/html/changelogs/mattatlas-seizureofpower.yml new file mode 100644 index 00000000000..00dc6596eab --- /dev/null +++ b/html/changelogs/mattatlas-seizureofpower.yml @@ -0,0 +1,47 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +# admin +# backend +# security +# refactor +################################# + +# Your name. +author: MattAtlas + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: +- rscadd: "Zombie limbs are now ACTUALLY easier to delimb. There was a longstanding bug that made their maim bonus not apply." +- rscadd: "Added new abilities to every zombie type. Ported the Consume verb from bay, which zombies can use to refresh their HP, gib dead enemies, or simply kill alive ones and rip their ribcage open. Yes, it visibly rips their ribcage open." +- rscadd: "The zombie chem is now called Hylemnomil-Zeta and has Lore:tm:." +- rscadd: "Added the antibody extractor. The only way to get Hylemnomil-Zeta antibodies." +- rscadd: "Added barricade stacks." +- rscadd: "Reworked how the infection works. It starts from an appendage and progresses to the brain. An infection can be stopped by cutting off the infected limb." +- rscadd: "You cannot remove zombie parasites surgically anymore." diff --git a/icons/mob/human_races/zombie/r_zombie_bull.dmi b/icons/mob/human_races/zombie/r_zombie_bull.dmi index 9cbb45472fd..a0e13f5ff3a 100644 Binary files a/icons/mob/human_races/zombie/r_zombie_bull.dmi and b/icons/mob/human_races/zombie/r_zombie_bull.dmi differ diff --git a/icons/obj/barricade_stacks.dmi b/icons/obj/barricade_stacks.dmi new file mode 100644 index 00000000000..86bbf1b6ae4 Binary files /dev/null and b/icons/obj/barricade_stacks.dmi differ diff --git a/icons/obj/item/reagent_containers/glass.dmi b/icons/obj/item/reagent_containers/glass.dmi index 1209a034914..ae14666e3d6 100644 Binary files a/icons/obj/item/reagent_containers/glass.dmi and b/icons/obj/item/reagent_containers/glass.dmi differ diff --git a/icons/obj/machinery/extractor.dmi b/icons/obj/machinery/extractor.dmi new file mode 100644 index 00000000000..c5919e45306 Binary files /dev/null and b/icons/obj/machinery/extractor.dmi differ diff --git a/sound/attributions.txt b/sound/attributions.txt index 2a69c130e06..5f83d227f58 100644 --- a/sound/attributions.txt +++ b/sound/attributions.txt @@ -89,3 +89,8 @@ sound/music/lobby/konyang/konyang-2.ogg - BGM gongbang sound/music/lobby/konyang/konyang-3.ogg - zolezzi sound/music/lobby/konyang/konyang-4.ogg - Karl Casey @ White Bat Audio ATTRIBUTIONS SECTION END + +ATTRIBUTIONS SECTOR START +sound/effects/zombies/zombie_1.ogg to zombie_4_ogg - Mazellmi @ Youtube +sound/effects/rock_break.ogg - "Rock destroy". Obtained from https://freesound.org/people/Bertsz/sounds/524312/ +ATTRIBUTIONS SECTION END diff --git a/sound/effects/rock_break.ogg b/sound/effects/rock_break.ogg new file mode 100644 index 00000000000..727ce70cbcc Binary files /dev/null and b/sound/effects/rock_break.ogg differ diff --git a/sound/effects/zombies/zombie_1.ogg b/sound/effects/zombies/zombie_1.ogg new file mode 100644 index 00000000000..4656055df56 Binary files /dev/null and b/sound/effects/zombies/zombie_1.ogg differ diff --git a/sound/effects/zombies/zombie_2.ogg b/sound/effects/zombies/zombie_2.ogg new file mode 100644 index 00000000000..c0a20cd55ad Binary files /dev/null and b/sound/effects/zombies/zombie_2.ogg differ diff --git a/sound/effects/zombies/zombie_3.ogg b/sound/effects/zombies/zombie_3.ogg new file mode 100644 index 00000000000..1236d9bfb8b Binary files /dev/null and b/sound/effects/zombies/zombie_3.ogg differ diff --git a/sound/effects/zombies/zombie_4.ogg b/sound/effects/zombies/zombie_4.ogg new file mode 100644 index 00000000000..a05316c438d Binary files /dev/null and b/sound/effects/zombies/zombie_4.ogg differ