Merge pull request #11535 from Couls/zombies

[Ready for review] Romerol Virus, Zombies!
This commit is contained in:
variableundefined
2019-09-03 20:58:53 -04:00
committed by GitHub
25 changed files with 419 additions and 88 deletions
+10
View File
@@ -762,6 +762,16 @@
desc = "Bark bark bark."
key = "vu"
/datum/language/zombie
name = "Zombie"
desc = "BRAAAAAAINS!"
speech_verb = "moans"
whisper_verb = "mutters"
exclaim_verb = "wails"
colour = "zombie"
key = "zom"
syllables = list("BRAAAAAAAAAAAAAAAAINS", "BRAAINS", "BRAINS")
/mob/proc/grant_all_languages()
for(var/la in GLOB.all_languages)
add_language(la)
@@ -344,56 +344,5 @@ This function restores all organs.
//Handle BRUTE and BURN damage
handle_suit_punctures(damagetype, damage)
blocked = (100 - blocked) / 100
if(blocked <= 0)
return 0
var/obj/item/organ/external/organ = null
if(isorgan(def_zone))
organ = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
organ = get_organ(check_zone(def_zone))
if(!organ)
return 0
damage = damage * blocked
switch(damagetype)
if(BRUTE)
damageoverlaytemp = 20
if(dna.species)
damage = damage * dna.species.brute_mod
if(organ.receive_damage(damage, 0, sharp, used_weapon))
UpdateDamageIcon()
if(LAssailant && ishuman(LAssailant)) //superheros still get the comical hit markers
var/mob/living/carbon/human/H = LAssailant
if(H.mind && H.mind in (SSticker.mode.superheroes || SSticker.mode.supervillains || SSticker.mode.greyshirts))
var/list/attack_bubble_recipients = list()
var/mob/living/user
for(var/mob/O in viewers(user, src))
if(O.client && O.has_vision(information_only=TRUE))
attack_bubble_recipients.Add(O.client)
spawn(0)
var/image/dmgIcon = image('icons/effects/hit_blips.dmi', src, "dmg[rand(1,2)]",MOB_LAYER+1)
dmgIcon.pixel_x = (!lying) ? rand(-3,3) : rand(-11,12)
dmgIcon.pixel_y = (!lying) ? rand(-11,9) : rand(-10,1)
flick_overlay(dmgIcon, attack_bubble_recipients, 9)
if(BURN)
damageoverlaytemp = 20
if(dna.species)
damage = damage * dna.species.burn_mod
if(organ.receive_damage(0, damage, sharp, used_weapon))
UpdateDamageIcon()
// Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life().
updatehealth("apply damage")
return 1
//Handle species apply_damage procs
return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, sharp, used_weapon)
@@ -14,7 +14,6 @@
handle_heartbeat()
handle_drunk()
dna.species.handle_life(src)
if(!client)
dna.species.handle_npc(src)
@@ -52,9 +52,10 @@
var/brain_mod = 1 // Brain damage damage reduction/amplification
var/stamina_mod = 1
var/stun_mod = 1 // If a species is more/less impacated by stuns/weakens/paralysis
var/speedmod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster
var/armor = 0 // overall defense for the race... or less defense, if it's negative.
var/blood_damage_type = OXY //What type of damage does this species take if it's low on blood?
var/obj/item/mutanthands
var/total_health = 100
var/punchdamagelow = 0 //lowest possible punch damage
var/punchdamagehigh = 9 //highest possible punch damage
@@ -308,6 +309,63 @@
/datum/species/proc/handle_death(mob/living/carbon/human/H) //Handles any species-specific death events (such as dionaea nymph spawns).
return
/datum/species/proc/spec_death(gibbed, mob/living/carbon/human/C)
return
/datum/species/proc/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = 0, mob/living/carbon/human/H, sharp = 0, obj/used_weapon = null)
blocked = (100 - blocked) / 100
if(blocked <= 0)
return 0
var/obj/item/organ/external/organ = null
if(isorgan(def_zone))
organ = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
organ = H.get_organ(check_zone(def_zone))
if(!organ)
return 0
damage = damage * blocked
switch(damagetype)
if(BRUTE)
H.damageoverlaytemp = 20
damage = damage * brute_mod
if(organ.receive_damage(damage, 0, sharp, used_weapon))
H.UpdateDamageIcon()
if(H.LAssailant && ishuman(H.LAssailant)) //superheros still get the comical hit markers
var/mob/living/carbon/human/A = H.LAssailant
if(A.mind && A.mind in (SSticker.mode.superheroes || SSticker.mode.supervillains || SSticker.mode.greyshirts))
var/list/attack_bubble_recipients = list()
var/mob/living/user
for(var/mob/O in viewers(user, src))
if(O.client && O.has_vision(information_only=TRUE))
attack_bubble_recipients.Add(O.client)
spawn(0)
var/image/dmgIcon = image('icons/effects/hit_blips.dmi', src, "dmg[rand(1,2)]",MOB_LAYER+1)
dmgIcon.pixel_x = (!H.lying) ? rand(-3,3) : rand(-11,12)
dmgIcon.pixel_y = (!H.lying) ? rand(-11,9) : rand(-10,1)
flick_overlay(dmgIcon, attack_bubble_recipients, 9)
if(BURN)
H.damageoverlaytemp = 20
damage = damage * burn_mod
if(organ.receive_damage(0, damage, sharp, used_weapon))
H.UpdateDamageIcon()
// Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life().
H.updatehealth("apply damage")
return 1
/datum/species/proc/spec_stun(mob/living/carbon/human/H,amount)
return
/datum/species/proc/spec_electrocute_act(mob/living/carbon/human/H, shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
return
@@ -0,0 +1,91 @@
#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin
/datum/species/zombie
// 1spooky
name = "High-Functioning Zombie"
name_plural = "High-Functioning Zombies"
icobase = 'icons/mob/human_races/r_zombie.dmi'
deform = 'icons/mob/human_races/r_def_zombie.dmi'
dies_at_threshold = TRUE
language = "Zombie"
species_traits = list(NO_BLOOD,NOZOMBIE,NOTRANSSTING,NO_BREATHE, RADIMMUNE, NO_SCAN)
var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg')
warning_low_pressure = 50
hazard_low_pressure = 0
flesh_color = "#00FF00" // for green examine text
bodyflags = HAS_SKIN_COLOR
dietflags = DIET_CARN
has_organ = list(
"heart" = /obj/item/organ/internal/heart,
"lungs" = /obj/item/organ/internal/lungs,
"liver" = /obj/item/organ/internal/liver,
"kidneys" = /obj/item/organ/internal/kidneys,
"brain" = /obj/item/organ/internal/brain,
"appendix" = /obj/item/organ/internal/appendix,
"eyes" = /obj/item/organ/internal/eyes,
"ears" = /obj/item/organ/internal/ears,
"zombie_infection" = /obj/item/organ/internal/zombie_infection)
/datum/species/zombie/infectious
name = "Infectious Zombie"
mutanthands = /obj/item/zombie_hand
icobase = 'icons/mob/human_races/r_zombie.dmi'
deform = 'icons/mob/human_races/r_def_zombie.dmi'
armor = 20 // 120 damage to KO a zombie, which kills it
speedmod = 1.6
default_language = "Zombie"
var/heal_rate = 1
var/regen_cooldown = 0
/datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H, amount)
. = min(20, amount)
/datum/species/zombie/infectious/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = 0, sharp = 0, obj/used_weapon = null)
. = ..()
if(damage)
regen_cooldown = world.time + REGENERATION_DELAY
/datum/species/zombie/infectious/handle_life(mob/living/carbon/human/C)
. = ..()
//Zombies never actually die, they just fall down until they regenerate enough to rise back up.
//They must be restrained, beheaded or gibbed to stop being a threat.
if(regen_cooldown < world.time)
var/heal_amt = heal_rate
if(C.InCritical())
heal_amt *= 2
C.heal_overall_damage(heal_amt,heal_amt)
C.adjustToxLoss(-heal_amt)
if(!C.InCritical() && prob(4))
playsound(C, pick(spooks), 50, TRUE, 10)
//Congrats you somehow died so hard you stopped being a zombie
/datum/species/zombie/infectious/spec_death(gibbed, mob/living/carbon/C)
. = ..()
var/obj/item/organ/internal/zombie_infection/infection
infection = C.get_organ_slot("zombie_infection")
if(infection)
qdel(infection)
/datum/species/zombie/infectious/on_species_gain(mob/living/carbon/human/C, datum/species/old_species)
. = ..()
// Deal with the source of this zombie corruption
// Infection organ needs to be handled separately from mutant_organs
// because it persists through species transitions
if(mutanthands)
C.drop_l_hand()
C.drop_r_hand()
C.put_in_hands(new mutanthands())
C.put_in_hands(new mutanthands())
var/obj/item/organ/internal/zombie_infection/infection
infection = C.get_organ_slot("zombie_infection")
if(!infection)
infection = new()
infection.insert(C)
/datum/species/zombie/infectious/on_species_loss(mob/living/carbon/human/C, datum/species/old_species)
QDEL_NULL(C.r_hand)
QDEL_NULL(C.l_hand)
return ..()
#undef REGENERATION_DELAY
@@ -5,21 +5,29 @@
. = ..()
/mob/living/carbon/human/SetStunned(amount, updating = 1, force = 0)
if(dna.species)
if(dna.species.stun_mod)
amount = amount * dna.species.stun_mod
else
amount = dna.species.spec_stun(src,amount)
return ..()
/mob/living/carbon/human/SetWeakened(amount, updating = 1, force = 0)
if(dna.species)
if(dna.species.stun_mod)
amount = amount * dna.species.stun_mod
else
amount = dna.species.spec_stun(src,amount)
return ..()
/mob/living/carbon/human/SetParalysis(amount, updating = 1, force = 0)
if(dna.species)
if(dna.species.stun_mod)
amount = amount * dna.species.stun_mod
else
amount = dna.species.spec_stun(src,amount)
return ..()
/mob/living/carbon/human/SetSleeping(amount, updating = 1, no_alert = FALSE)
if(dna.species)
if(dna.species.stun_mod)
amount = amount * dna.species.stun_mod
return ..()
else
amount = dna.species.spec_stun(src,amount)
return ..()
+18
View File
@@ -113,6 +113,24 @@
return U.sensor_mode
return SUIT_SENSOR_OFF
/proc/offer_control(mob/M)
to_chat(M, "Control of your mob has been offered to dead players.")
log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.")
var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as num
message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime")
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [M.real_name ? M.real_name : M.name]?", poll_time = 100, min_hours = minhours)
var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates))
theghost = pick(candidates)
to_chat(M, "Your mob has been taken over by a ghost!")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])")
M.ghostize()
M.key = theghost.key
else
to_chat(M, "There were no ghosts willing to take control.")
message_admins("No ghosts were willing to take control of [key_name_admin(M)])")
/proc/check_zone(zone)
if(!zone) return "chest"
switch(zone)
@@ -48,7 +48,7 @@
var/gender = NEUTER //Determines if the accessory will be skipped or included in random hair generations
// Restrict some styles to specific species
var/list/species_allowed = list("Human", "Slime People")
var/list/species_allowed = list("Human", "Slime People", "Infectious Zombie", "High-Functioning Zombie")
var/list/sprite_sheets = list() //For accessories common across species but need to use 'fitted' sprites (like underwear). e.g. list("Vox" = 'icons/mob/species/vox/iconfile.dmi')
var/list/models_allowed = list() //Specifies which, if any, hairstyles or markings can be accessed by which prosthetics. Should equal the manufacturing company name in robolimbs.dm.
var/list/heads_allowed = null //Specifies which, if any, alt heads a head marking, hairstyle or facial hair style is compatible with.
@@ -207,6 +207,25 @@
return ..()
datum/reagent/romerol
name = "romerol"
// the REAL zombie powder
id = "romerol"
description = "Romerol is a highly experimental bioterror agent \
which causes dormant nodules to be etched into the grey matter of \
the subject. These nodules only become active upon death of the \
host, upon which, the secondary structures activate and take control \
of the host body."
color = "#123524" // RGB (18, 53, 36)
metabolization_rate = INFINITY
can_synth = FALSE
taste_description = "CAAAARL"
/datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H)
// Silently add the zombie infection organ to be activated upon death
new /obj/item/organ/internal/zombie_infection(H)
..()
/datum/reagent/stable_mutagen/on_tick()
var/datum/reagent/blood/B = locate() in holder.reagent_list
if(B && islist(B.data) && !data)
@@ -12,6 +12,11 @@
container_type = OPENCONTAINER
volume = 30
/obj/item/reagent_containers/glass/bottle/romerol
name = "romerol bottle"
desc = "A small bottle of Romerol. The REAL zombie powder."
list_reagents = list("romerol" = 30)
/obj/item/reagent_containers/glass/bottle/on_reagent_change()
update_icon()
+1 -1
View File
@@ -175,7 +175,7 @@
/obj/docking_port/mobile/emergency/proc/is_hijacked()
for(var/mob/living/player in GLOB.player_list)
if(player.mind)
if(player.stat != DEAD)
if(player.stat != DEAD || !iszombie(player))
if(issilicon(player)) //Borgs are technically dead anyways
continue
if(isanimal(player)) //Poly does not own the shuttle
+67
View File
@@ -0,0 +1,67 @@
/obj/item/zombie_hand
name = "zombie claw"
desc = "A zombie's claw is its primary tool, capable of infecting \
unconscious or dead humans, butchering all other living things to \
sustain the zombie, forcing open airlock doors and opening \
child-safe caps on bottles."
flags = NODROP|ABSTRACT|DROPDEL
icon = 'icons/effects/blood.dmi'
icon_state = "bloodhand_left"
var/icon_left = "bloodhand_left"
var/icon_right = "bloodhand_right"
hitsound = 'sound/hallucinations/growl1.ogg'
force = 25
damtype = "brute"
/obj/item/zombie_hand/equipped(mob/user, slot)
. = ..()
switch(slot)
// Yes, these intentionally don't match
if(slot_l_hand)
icon_state = icon_right
if(slot_r_hand)
icon_state = icon_left
/obj/item/zombie_hand/afterattack(atom/target, mob/user, proximity_flag)
. = ..()
if(!proximity_flag)
return
else if(isliving(target))
if(ishuman(target))
check_infection(target, user)
else
check_feast(target, user)
/obj/item/zombie_hand/proc/check_infection(var/mob/living/carbon/human/target, var/mob/user)
CHECK_DNA_AND_SPECIES(target)
if(NOZOMBIE in target.dna.species.species_traits)
// cannot infect any NOZOMBIE subspecies (such as high functioning
// zombies)
return
var/obj/item/organ/internal/zombie_infection/infection
infection = target.get_organ_slot("zombie_infection")
if(!infection)
if (target.InCritical() || (!target.check_death_method() && target.health <= HEALTH_THRESHOLD_DEAD))
infection = new(target)
/obj/item/zombie_hand/proc/check_feast(mob/living/target, mob/living/user)
if(target.stat == DEAD)
var/hp_gained = target.maxHealth
target.gib()
user.adjustBruteLoss(-hp_gained)
user.adjustToxLoss(-hp_gained)
user.adjustFireLoss(-hp_gained)
user.adjustCloneLoss(-hp_gained)
user.adjustBrainLoss(-hp_gained) // Zom Bee gibbers "BRAAAAISNSs!1!"
/obj/item/zombie_hand/suicide_act(mob/living/carbon/human/user)
user.visible_message("<span class='suicide'>[user] is ripping [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!</span>")
if(ishuman(user))
var/mob/living/carbon/human/L = user
var/obj/item/organ/external/O = L.get_organ("head")
if(O)
O.droplimb()
return (BRUTELOSS)
+93
View File
@@ -0,0 +1,93 @@
/obj/item/organ/internal/zombie_infection
name = "festering ooze"
desc = "A black web of pus and viscera."
parent_organ = "head"
slot = "zombie_infection"
icon_state = "blacktumor"
var/converts_living = FALSE
var/causes_damage = TRUE
var/datum/species/old_species = /datum/species/human
var/living_transformation_time = 3
var/revive_time_min = 450
var/revive_time_max = 700
var/timer_id
var/dont_delete = FALSE // to prevent set_species proc in zombify from deleting the infection organ immediately
/obj/item/organ/internal/zombie_infection/New()
. = ..()
if(ishuman(owner) && !ismachine(owner) && !iszombie(owner.dna.species))
old_species = owner.dna.species
GLOB.zombie_infection_list += src
/obj/item/organ/internal/zombie_infection/Destroy()
GLOB.zombie_infection_list -= src
. = ..()
/obj/item/organ/internal/zombie_infection/insert(mob/living/carbon/human/M, special = 0)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/organ/internal/zombie_infection/remove(mob/living/carbon/human/M, special = 0)
STOP_PROCESSING(SSobj, src)
if(iszombie(M) && old_species && !dont_delete)
M.set_species(old_species)
if(timer_id)
deltimer(timer_id)
..()
/obj/item/organ/internal/zombie_infection/on_find(mob/living/finder)
to_chat(finder,"<span class='warning'>Inside the head is a disgusting black \
web of pus and viscera, bound tightly around the brain like some \
biological harness.</span>")
/obj/item/organ/internal/zombie_infection/process()
if(!ishuman(owner)) // We do not support monkey or xeno zombies. Yet.
qdel(src)
return
if(owner.suiciding)
return
if(owner.stat != DEAD && !converts_living)
return
if(causes_damage && !iszombie(owner) && owner.stat != DEAD)
owner.adjustToxLoss(1)
if (prob(10))
to_chat(owner, "<span class='danger'>You feel sick...</span>")
if(!owner.get_int_organ(/obj/item/organ/internal/brain))
return
if(!iszombie(owner) && !timer_id)
to_chat(owner, "<span class='cultlarge'>You can feel your heart stopping, but something isn't right... \
life has not abandoned your broken form. You can only feel a deep and immutable hunger that \
not even death can stop, you will rise again!</span>")
var/revive_time = rand(revive_time_min, revive_time_max)
var/flags = TIMER_STOPPABLE
timer_id = addtimer(CALLBACK(src, .proc/zombify), revive_time, flags)
/obj/item/organ/internal/zombie_infection/proc/zombify()
timer_id = null
if(owner.stat != DEAD && !converts_living)
return
if(!iszombie(owner))
old_species = owner.dna.species
dont_delete = TRUE // make sure the remove code above does not run when the species code replaces all the organs
owner.set_species(/datum/species/zombie/infectious)
for(var/thing in owner.viruses) // cure any new crit viruses
var/datum/disease/D = thing
D.cure(0)
dont_delete = FALSE
//Fully heal the zombie's damage the first time they rise
owner.setToxLoss(0)
owner.setOxyLoss(0)
owner.setBrainLoss(0)
owner.setCloneLoss(0)
owner.SetLoseBreath(0)
owner.heal_overall_damage(INFINITY,INFINITY,TRUE,TRUE,FALSE)
if(owner.update_revive())
owner.grab_ghost()
owner.visible_message("<span class='danger'>[owner] suddenly convulses, as [owner.p_they()] stagger to [owner.p_their()] feet and gain a ravenous hunger in [owner.p_their()] eyes!</span>", "<span class='alien'>You HUNGER!</span>")
playsound(owner.loc, 'sound/hallucinations/far_noise.ogg', 50, 1)
owner.do_jitter_animation(living_transformation_time)
owner.Stun(living_transformation_time)
to_chat(owner, "<span class='alertalien'>You are now a zombie! Do not seek to be cured, do not help any non-zombies in any way, do not harm your zombie brethren and spread the disease by killing others. You are a creature of hunger and violence.</span>")