Backports the new zombification mechanics from the Konyang zombie event. (#18710)

* Reorder parasites

* sounds

* organ defines, dmdocs

* maiming stuff

* undead changes, abilities

* hylemnomil, undead sprites

* antibodies

* antibody extractor code

* icons

* antibody fixes

* antibodies vial, rock break sfx

* You're all going to die down here.

* hylemnomilz eta vial and attributions

* zombified organs can't be removed

* hylemnomil zeta edits

* fixes

* lmao

* easier check

---------

Co-authored-by: Matt Atlas <liermattia@gmail.com>
This commit is contained in:
Matt Atlas
2024-03-24 14:04:52 +00:00
committed by GitHub
co-authored by Matt Atlas
parent ee81fc9b24
commit 82beece3db
37 changed files with 1423 additions and 618 deletions
@@ -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, "<font size=4><span class='notice'>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!</font></span>")
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("<font size=4>\The [src] raises a giant, mutated hand and smashes \the [B]!</font>"))
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)
@@ -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))