DNR'd / playerless bodies risen as zombies are ghost controllable (though mindless bodies are weaker) (#95544)

## About The Pull Request

- A body that revives as a zombie but has no player (IE never had a
player or the player DNR'd) becomes is ghost controllable: A poll is
immediately offered, and the body itself appears in the spawners menu.

- Zombies created from **mindless** mobs (such as monkeys or roundstart
morgue cadavers) are **Mindless Zombies**, a subtype of zombie which is
weaker (heal slower, move slower, not guaranteed to infect on hit)

## Why It's Good For The Game

- When zombies get rolling, there will no doubt be a few players that
DNR or a few DNR'd bodies that get infected, and it results in a lot of
zombies without players standing around, which is kinda lame. Giving
ghosts the options to take over seems like a no-brains-er to me, keeps
the horde a bit active even if some get decapitated.

- However, one big problem this allows for is that it provides a clear
vector for people spamming zombies out of hu-monkeys. Thus I changed
mindless bodies (bodies which never had a player, even a DNR'd one
(hu-monkeys)) to be easier to handle and far less deadly. That way
traitors are still encouraged to infect players over npcs.

## Changelog

🆑 Melbert
add: If a playerless body (DNR'd or otherwise) is raised as a zombie,
ghosts are given the ability to take control of it
add: If a body that never had a player control it is risen as a zombie,
they are risen as a weaker "mindless zombie" (moves and heals slower,
deals less damage, infection is not guaranteed on hit)
/🆑
This commit is contained in:
MrMelbert
2026-04-16 12:26:22 +10:00
committed by GitHub
parent 941c766205
commit bade006302
7 changed files with 93 additions and 22 deletions
@@ -20,7 +20,7 @@
hears_us = null
QDEL_LIST(possessed_souls)
SSpoints_of_interest.remove_point_of_interest(src)
LAZYREMOVE(GLOB.joinable_mobs[format_text("[initial(name)]")], src)
LAZYREMOVEASSOC(GLOB.joinable_mobs, initial(name), src)
return ..()
/obj/item/clothing/neck/tie/disco/examine(mob/user)
@@ -34,12 +34,12 @@
return
if(user.client && (isnull(hears_us) || user == hears_us))
SSpoints_of_interest.make_point_of_interest(src)
LAZYADD(GLOB.joinable_mobs[format_text("[initial(name)]")], src)
LAZYADDASSOCLIST(GLOB.joinable_mobs, initial(name), src)
/obj/item/clothing/neck/tie/disco/dropped(mob/living/user)
if(!QDELETED(src))
SSpoints_of_interest.remove_point_of_interest(src)
LAZYREMOVE(GLOB.joinable_mobs[format_text("[initial(name)]")], src)
LAZYREMOVEASSOC(GLOB.joinable_mobs, initial(name), src)
return ..()
/obj/item/clothing/neck/tie/disco/attack_self(mob/living/user, modifiers)
@@ -134,6 +134,10 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/zombie/infectious,
)
var/regen_time = 6 SECONDS
var/regen_amount = 0.5
var/zombie_hand = /obj/item/mutant_hand/zombie
/datum/species/zombie/infectious/on_species_gain(mob/living/carbon/human/new_zombie, datum/species/old_species, pref_load, regenerate_icons)
. = ..()
new_zombie.set_combat_mode(TRUE)
@@ -150,15 +154,15 @@
new_zombie.AddComponent( \
/datum/component/mutant_hands, \
mutant_hand_path = /obj/item/mutant_hand/zombie, \
mutant_hand_path = zombie_hand, \
)
new_zombie.AddComponent( \
/datum/component/regenerator, \
regeneration_delay = 6 SECONDS, \
brute_per_second = 0.5, \
burn_per_second = 0.5, \
tox_per_second = 0.5, \
oxy_per_second = 0.25, \
regeneration_delay = regen_time, \
brute_per_second = regen_amount, \
burn_per_second = regen_amount, \
tox_per_second = regen_amount, \
oxy_per_second = regen_amount * 0.5, \
heals_wounds = TRUE, \
)
@@ -179,6 +183,25 @@
if(!HAS_TRAIT(carbon_mob, TRAIT_CRITICAL_CONDITION) && SPT_PROB(2, seconds_per_tick))
playsound(carbon_mob, pick(spooks), 50, TRUE, 10)
// Weaker subtype - less healing, weaker attacks, etc
/datum/species/zombie/infectious/mindless
name = "Mindless Infectious Zombie"
id = SPECIES_ZOMBIE_INFECTIOUS_MINDLESS
regen_time = 10 SECONDS
regen_amount = 0.2
zombie_hand = /obj/item/mutant_hand/zombie/weak
/datum/species/zombie/infectious/mindless/on_species_gain(mob/living/carbon/human/new_zombie, datum/species/old_species, pref_load, regenerate_icons)
. = ..()
new_zombie.add_movespeed_modifier(/datum/movespeed_modifier/mindless_zombie)
/datum/species/zombie/infectious/mindless/on_species_loss(mob/living/carbon/human/was_zombie, datum/species/new_species, pref_load)
. = ..()
was_zombie.remove_movespeed_modifier(/datum/movespeed_modifier/mindless_zombie)
/datum/movespeed_modifier/mindless_zombie
multiplicative_slowdown = 0.75
// Your skin falls off
/datum/species/human/krokodil_addict
name = "\improper Krokodil Human"
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

+12 -2
View File
@@ -10,17 +10,27 @@
wound_bonus = -30
exposed_wound_bonus = 15
sharpness = SHARP_EDGED
/// Chance of infection on hit
var/infect_chance = 100
/obj/item/mutant_hand/zombie/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers)
if(QDELETED(target))
return
if(ishuman(target))
try_to_zombie_infect(target, user, user.zone_selected)
try_to_zombie_infect(target, user, user.zone_selected, infect_chance)
else if(isliving(target))
check_feast(target, user)
/proc/try_to_zombie_infect(mob/living/carbon/human/target, mob/living/user, def_zone = BODY_ZONE_CHEST)
/obj/item/mutant_hand/zombie/weak
force = 16
demolition_mod = 1.33
exposed_wound_bonus = 10
infect_chance = 50
/proc/try_to_zombie_infect(mob/living/carbon/human/target, mob/living/user, def_zone = BODY_ZONE_CHEST, base_chance = 100)
CHECK_DNA_AND_SPECIES(target)
if(!prob(base_chance))
return
// Can't zombify with no head
if(!target.get_bodypart(BODY_ZONE_HEAD))
+42 -6
View File
@@ -13,6 +13,8 @@
var/revive_time_max = 70 SECONDS
var/timer_id
var/datum/weakref/ghost_poll
/obj/item/organ/zombie_infection/Initialize(mapload)
. = ..()
if(iscarbon(loc))
@@ -42,6 +44,7 @@
if(timer_id)
deltimer(timer_id)
UnregisterSignal(new_owner, COMSIG_LIVING_DEATH)
QDEL_NULL(ghost_poll)
/obj/item/organ/zombie_infection/proc/organ_owner_died(mob/living/carbon/source, gibbed)
SIGNAL_HANDLER
@@ -84,7 +87,8 @@
if(!iszombie(owner))
old_species = owner.dna.species.type
target.set_species(/datum/species/zombie/infectious)
// mindless zombies are nerfed a bit to encourage infecting real players instead of random morgue bodies
target.set_species(isnull(target.mind) ? /datum/species/zombie/infectious/mindless : /datum/species/zombie/infectious)
var/stand_up = (target.stat == DEAD) || (target.stat == UNCONSCIOUS)
@@ -92,11 +96,43 @@
if(!target.heal_and_revive(0, span_danger("[target] suddenly convulses, as [target.p_they()][stand_up ? " stagger to [target.p_their()] feet and" : ""] gain a ravenous hunger in [target.p_their()] eyes!")))
return
to_chat(target, span_alien("You HUNGER!"))
to_chat(target, span_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."))
playsound(target, 'sound/effects/hallucinations/far_noise.ogg', 50, 1)
target.do_jitter_animation(living_transformation_time)
target.Stun(living_transformation_time)
zombie_welcome(target)
if(target.mind || target.client)
if(stand_up)
target.get_up()
return
ghost_poll = WEAKREF(target.AddComponent( \
/datum/component/ghost_direct_control, \
ban_type = NONE, \
poll_ignore_key = POLL_IGNORE_RECOVERED_CREW, \
poll_question = "Do you want to be a [span_green("[isnull(target.mind) ? "mindless zombie" : "zombie"]")]?", \
role_name = "zombie", \
extra_control_checks = CALLBACK(src, PROC_REF(check_ghost_control_eligibility)), \
after_assumed_control = CALLBACK(src, PROC_REF(after_ghost_control)), \
joinable_mobs_title = "Zombies", \
))
/obj/item/organ/zombie_infection/proc/check_ghost_control_eligibility(mob/candidate)
if(QDELETED(src) || QDELETED(owner))
return FALSE
if(!iszombie(owner) || owner.stat == DEAD)
return FALSE
return TRUE
/obj/item/organ/zombie_infection/proc/after_ghost_control(mob/candidate)
owner.Knockdown(living_transformation_time)
zombie_welcome(owner)
ghost_poll = null
/obj/item/organ/zombie_infection/proc/zombie_welcome(mob/living/carbon/new_zombie)
if(new_zombie.client)
to_chat(new_zombie, span_alien("You HUNGER!"))
to_chat(new_zombie, span_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."))
playsound(new_zombie, 'sound/effects/hallucinations/far_noise.ogg', 50, 1)
new_zombie.do_jitter_animation(living_transformation_time)
new_zombie.Stun(living_transformation_time)
/obj/item/organ/zombie_infection/nodamage
causes_damage = FALSE