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
@@ -12,6 +12,8 @@
var/datum/callback/after_assumed_control
/// If we're currently awaiting the results of a ghost poll
var/awaiting_ghosts = FALSE
/// Entry in the spawners menu, optional
var/joinable_mobs_title
/datum/component/ghost_direct_control/Initialize(
ban_type = ROLE_SENTIENCE,
@@ -25,6 +27,7 @@
assumed_control_message = null,
datum/callback/extra_control_checks,
datum/callback/after_assumed_control,
joinable_mobs_title = null,
)
. = ..()
if (!isliving(parent))
@@ -34,9 +37,10 @@
src.assumed_control_message = assumed_control_message || "You are [parent]!"
src.extra_control_checks = extra_control_checks
src.after_assumed_control = after_assumed_control
src.joinable_mobs_title = joinable_mobs_title
var/mob/mob_parent = parent
LAZYADD(GLOB.joinable_mobs[format_text("[initial(mob_parent.name)]")], mob_parent)
LAZYADDASSOCLIST(GLOB.joinable_mobs, joinable_mobs_title || format_text(initial(mob_parent.name)), mob_parent)
if (poll_candidates)
INVOKE_ASYNC(src, PROC_REF(request_ghost_control), poll_question, role_name || "[parent]", poll_length, poll_ignore_key, poll_announce_chosen, poll_chat_border_icon)
@@ -56,10 +60,7 @@
after_assumed_control = null
var/mob/mob_parent = parent
var/list/spawners = GLOB.joinable_mobs[format_text("[initial(mob_parent.name)]")]
LAZYREMOVE(spawners, mob_parent)
if(!LAZYLEN(spawners))
GLOB.joinable_mobs -= format_text("[initial(mob_parent.name)]")
LAZYREMOVEASSOC(GLOB.joinable_mobs, joinable_mobs_title || format_text(initial(mob_parent.name)), mob_parent)
return ..()
/// Inform ghosts that they can possess this