Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIRROR] Fixes zombies' destructive capabilities, cleans up NPC behaviour #1041

Merged
merged 1 commit into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/_onclick/other_mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
SPAN_DANGER("\The [user] smashes through \the [src]!"),
SPAN_DANGER("You smash through \the [src]!")
)
if (MUTATION_FERAL in user.mutations)
qdel(src)
else
user.visible_message(
SPAN_DANGER("\The [user] [attack_verb] \the [src]!"),
Expand Down
19 changes: 19 additions & 0 deletions code/game/machinery/doors/door.dm
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,26 @@
do_animate("deny")
return

/obj/machinery/door/airlock/attack_generic(mob/user)
if (MUTATION_FERAL in user.mutations)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN*2)
playsound(loc, damage_hitsound, 50, 1)
attack_animation(user)

if((MACHINE_IS_BROKEN(src)||!arePowerSystemsOn(src)) && density)
visible_message(SPAN_DANGER("\The [user] manages to pry \the [src] open!"))
open(1)
else
visible_message(SPAN_DANGER("\The [user] smashes into \the [src]!"))
damage_health(10)
return
..()

/obj/machinery/door/attack_hand(mob/user)
if (MUTATION_FERAL in user.mutations)
attack_generic(user, 15)
return

..()
if (allowed(user) && operable())
if (density)
Expand Down
6 changes: 3 additions & 3 deletions code/game/turfs/simulated/wall_attacks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@
playsound(src, pick(GLOB.punch_sound), 20)
if (MUTATION_FERAL in user.mutations)
M.visible_message(SPAN_DANGER("[M.name] slams into \the [src]!"), SPAN_DANGER("You slam into \the [src]!"))
playsound(src, pick(GLOB.punch_sound), 45)
damage_health(5, DAMAGE_BRUTE)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN*2) //Additional cooldown
playsound(src, 'sound/effects/clang.ogg', 45, 1)
damage_health(20, DAMAGE_BRUTE)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN*5) //Additional cooldown
attack_animation(user)
else
M.visible_message(SPAN_DANGER("[M.name] punches \the [src]!"), SPAN_DANGER("You punch \the [src]!"))
Expand Down
1 change: 1 addition & 0 deletions code/modules/mechs/mech_interaction.dm
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@
return

/mob/living/exosuit/attack_generic(mob/user, damage, attack_message = "smashes into")
..()
if(damage)
playsound(loc, body.damage_sound, 40, 1)

Expand Down
7 changes: 5 additions & 2 deletions code/modules/mob/living/silicon/robot/robot.dm
Original file line number Diff line number Diff line change
Expand Up @@ -839,8 +839,11 @@

if(istype(user,/mob/living/carbon/human))
var/mob/living/carbon/human/H = user
if(H.species.can_shred(H))
attack_generic(H, rand(30,50), "slashed")
if(H.species.can_shred(H) || (MUTATION_FERAL in H.mutations))
attack_generic(H, rand(10,20), "slashed")
playsound(loc, 'sound/weapons/bite.ogg', 50, 1)
if (prob(20))
playsound(loc, 'sound/effects/sparks1.ogg', 50, 1)
return

if(opened && !wiresexposed && (!istype(user, /mob/living/silicon)))
Expand Down
88 changes: 65 additions & 23 deletions code/modules/species/outsider/zombie.dm
Original file line number Diff line number Diff line change
Expand Up @@ -176,24 +176,61 @@ GLOBAL_LIST_INIT(zombie_species, list(\

addtimer(new Callback(src, .proc/handle_action, H), rand(10, 20))

/datum/species/zombie/proc/is_valid_target(mob/living/T)
if (!istype(T, /mob/living/carbon/human)) //Ignore Diona and unconscious non-humans
if (istype(T, /mob/living/carbon/alien/diona))
return FALSE
if (T.stat != CONSCIOUS)
return FALSE

var/mob/living/carbon/human/H = T
if (H.is_species(SPECIES_ZOMBIE) || H.is_species(SPECIES_DIONA))
return FALSE

if (H.isSynthetic() && H.stat != CONSCIOUS)
return FALSE

if (istype(T, /mob/living/exosuit))
var/mob/living/exosuit/X = T
if (!LAZYLEN(X.pilots))
return FALSE //Don't attack empty mechs

return TRUE

/datum/species/zombie/proc/is_consumable(mob/living/T)
if (!istype(T, /mob/living/carbon/human))
return FALSE

if (T.isSynthetic())
return FALSE

return is_valid_target(T)

/datum/species/zombie/proc/is_being_consumed(mob/living/T, mob/living/carbon/human/H)
//Will exclude consumption candidates if there's another zombie on top of them
if (!is_consumable(T))
return FALSE
for (var/mob/living/carbon/human/M in T.loc.contents)
if (M != H && M.stat == CONSCIOUS && M.is_species(SPECIES_ZOMBIE))
return TRUE
return FALSE

/datum/species/zombie/proc/handle_action(mob/living/carbon/human/H)
var/dist = 128
for(var/mob/living/M in hearers(H, 15))
if ((ishuman(M) || istype(M, /mob/living/exosuit)) && !M.is_species(SPECIES_ZOMBIE) && !M.is_species(SPECIES_DIONA)) //Don't attack fellow zombies, or diona
if (istype(M, /mob/living/exosuit))
var/mob/living/exosuit/MC = M
if (!LAZYLEN(MC.pilots))
continue //Don't attack empty mechs
if (M.stat == DEAD && target)
for (var/mob/living/M in hearers(H, 15))
if (is_valid_target(M)) //Don't attack fellow zombies, or diona
if (target && M.stat != CONSCIOUS)
continue //Only eat corpses when no living (and able) targets are around
if (is_being_consumed(M, H))
continue //Don't queue up to eat
var/D = get_dist(M, H)
if (D <= dist * 0.5) //Must be significantly closer to change targets
target = M //For closest target
target = M //Switch to closest target
dist = D

H.setClickCooldown(DEFAULT_ATTACK_COOLDOWN*2)
if (target)
if (target.is_species(SPECIES_ZOMBIE))
if (!is_valid_target(target) || is_being_consumed(target, H))
target = null
return

Expand All @@ -203,25 +240,24 @@ GLOBAL_LIST_INIT(zombie_species, list(\
var/obj/obstacle = locate(type) in dir
if (obstacle)
H.face_atom(obstacle)
obstacle.attack_generic(H, 10, "smashes")
obstacle.attack_hand(H)
break

walk_to(H, target.loc, 1, H.move_intent.move_delay * 1.25)

else
if (!target.lying) //Subdue meals
H.face_atom(target)
if ((is_consumable(target) && target.lying)) //Eat the victim
walk_to(H, target.loc, 0, H.move_intent.move_delay * 2.5) //Move over them
if (H.Adjacent(target)) //Check we're still next to them
H.consume()

else //Otherwise subdue them
H.face_atom(target)
if (!H.zone_sel)
H.zone_sel = new /obj/screen/zone_sel(null)
H.zone_sel.selecting = BP_CHEST
target.attack_hand(H)

else //Eat said meals
walk_to(H, target.loc, 0, H.move_intent.move_delay * 2.5) //Move over them
if (H.Adjacent(target)) //Check we're still next to them
H.consume()

for(var/mob/living/M in hearers(H, 15))
if (target == M) //If our target is still nearby
return
Expand Down Expand Up @@ -258,17 +294,17 @@ GLOBAL_LIST_INIT(zombie_species, list(\
. = ..()
if (!.)
return FALSE
if (!target || target.is_species(SPECIES_ZOMBIE))
if (istype(target, /mob/living/carbon/human) && target.is_species(SPECIES_ZOMBIE))
to_chat(usr, SPAN_WARNING("They don't look very appetizing!"))
return FALSE
return TRUE

/datum/unarmed_attack/bite/sharp/zombie/apply_effects(mob/living/carbon/human/user, mob/living/carbon/human/target, attack_damage, zone)
..()
admin_attack_log(user, target, "Bit their victim.", "Was bitten.", "bit")
if (!(target.species.name in GLOB.zombie_species) || target.is_species(SPECIES_DIONA) || target.isSynthetic()) //No need to check infection for FBPs
if (!istype(target, /mob/living/carbon/human) || !(target.species.name in GLOB.zombie_species) || target.is_species(SPECIES_DIONA) || target.isSynthetic()) //No need to check infection for FBPs
return
target.adjustHalLoss(9) //To help bring down targets in voidsuits
target.adjustHalLoss(6) //To help bring down targets in voidsuits
var/vuln = 1 - target.get_blocked_ratio(zone, DAMAGE_TOXIN, damage_flags = DAMAGE_FLAG_BIO) //Are they protected from bites?
if (vuln > 0.05)
if (prob(vuln * 100)) //Protective infection chance
Expand Down Expand Up @@ -309,8 +345,6 @@ GLOBAL_LIST_INIT(zombie_species, list(\
M.bodytemperature += 7.5
if (prob(3))
to_chat(M, SPAN_WARNING(FONT_NORMAL(pick(GLOB.zombie_messages["stage1"]))))
if (M.getBrainLoss() < 20)
M.adjustBrainLoss(rand(1, 2))

if (true_dose >= 90)
M.add_chemical_effect(CE_MIND, -2)
Expand Down Expand Up @@ -356,6 +390,13 @@ GLOBAL_LIST_INIT(zombie_species, list(\
new /obj/effect/decal/cleanable/vomit(T)
playsound(T, 'sound/effects/splat.ogg', 20, 1)

var/obj/item/held_l = get_equipped_item(slot_l_hand)
var/obj/item/held_r = get_equipped_item(slot_r_hand)
if(held_l)
drop_from_inventory(held_l)
if(held_r)
drop_from_inventory(held_r)

addtimer(new Callback(src, .proc/transform_zombie), 20)

/mob/living/carbon/human/proc/transform_zombie()
Expand Down Expand Up @@ -451,7 +492,7 @@ GLOBAL_LIST_INIT(zombie_species, list(\
src.visible_message(SPAN_DANGER("\The [src] hunkers down over \the [target], tearing into their flesh."))
playsound(loc, 'sound/effects/bonebreak3.ogg', 20, 1)

target.adjustHalLoss(50)
target.adjustHalLoss(25)

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")
Expand All @@ -476,6 +517,7 @@ GLOBAL_LIST_INIT(zombie_species, list(\
if (target.is_species(SPECIES_ZOMBIE)) //Just in case they turn whilst being eaten
return

target.adjustHalLoss(25)
target.apply_damage(rand(50, 60), DAMAGE_BRUTE, BP_CHEST)
target.adjustBruteLoss(20)
target.update_surgery() //Update broken ribcage sprites etc.
Expand Down