From 5a6da15b1a65d7836db414059c4279c4566de95a Mon Sep 17 00:00:00 2001 From: wb13 <26555013+wb13@users.noreply.github.com> Date: Sun, 17 Sep 2023 20:55:59 +0300 Subject: [PATCH] [MIRROR] Fixes zombies' destructive capabilities, cleans up NPC behaviour --- code/_onclick/other_mobs.dm | 2 + code/game/machinery/doors/door.dm | 19 ++++ code/game/turfs/simulated/wall_attacks.dm | 6 +- code/modules/mechs/mech_interaction.dm | 1 + .../modules/mob/living/silicon/robot/robot.dm | 7 +- code/modules/species/outsider/zombie.dm | 88 ++++++++++++++----- 6 files changed, 95 insertions(+), 28 deletions(-) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index b879696a5b851..4e405e3aa5cef 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -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]!"), diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 15278284e8f8c..80d1a019f7430 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -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) diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index 4e83590c90e38..bfa606fd3f5b0 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -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]!")) diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm index b69062bd8f014..1b26188a0a9ef 100644 --- a/code/modules/mechs/mech_interaction.dm +++ b/code/modules/mechs/mech_interaction.dm @@ -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) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 8785bb07dc675..a73a3eff911c9 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -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))) diff --git a/code/modules/species/outsider/zombie.dm b/code/modules/species/outsider/zombie.dm index 92845268b1dd8..347806c253dc1 100644 --- a/code/modules/species/outsider/zombie.dm +++ b/code/modules/species/outsider/zombie.dm @@ -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 @@ -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 @@ -258,7 +294,7 @@ 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 @@ -266,9 +302,9 @@ GLOBAL_LIST_INIT(zombie_species, list(\ /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 @@ -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) @@ -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() @@ -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") @@ -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.