Files
Bubberstation/code/datums/components/ghostrole_on_revive.dm
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

198 lines
7.0 KiB
Plaintext

/// Proc ghosts to enter the body when it get's revived
/datum/component/ghostrole_on_revive
/// If revived and no ghosts, just die again?
var/refuse_revival_if_failed
/// Callback for when the mob is revived and has their body occupied by a ghost
var/datum/callback/on_successful_revive
/// The chance to twitch when orbiting the spawn
var/twitch_chance = 30
/// The title shown to ghosts when polled to enter the body
var/revive_title
// Text displayed in the spawners menu
var/spawn_text
var/you_are_text
var/flavor_text
var/important_text
/datum/component/ghostrole_on_revive/Initialize(
refuse_revival_if_failed = FALSE,
datum/callback/on_successful_revive,
revive_title = "a recovered crewmember",
spawn_text = "Recovered Crew",
you_are_text = "You are a long dead crewmember, but are soon to be revived to rejoin the crew!",
flavor_text = "Get a job and get back to work!",
important_text = "Do your best to help the station. You still roll for midround antagonists."
)
src.refuse_revival_if_failed = refuse_revival_if_failed
src.on_successful_revive = on_successful_revive
src.revive_title = revive_title
src.spawn_text = spawn_text
src.you_are_text = you_are_text
src.flavor_text = flavor_text
src.important_text = important_text
if(ismob(parent))
prepare_mob(parent)
return
if(!istype(parent, /obj/item/organ/brain))
return COMPONENT_INCOMPATIBLE
var/obj/item/organ/brain/brain_parent = parent
if(brain_parent.owner)
prepare_mob(brain_parent.owner)
else
prepare_brain(brain_parent)
/// Give the appropriate signals, and watch for organ removal
/datum/component/ghostrole_on_revive/proc/prepare_mob(mob/living/to_prepare)
RegisterSignal(to_prepare, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
RegisterSignal(to_prepare, COMSIG_MOB_REAGENT_TICK, PROC_REF(block_formaldehyde_metabolism))
if(istype(parent, /obj/item/organ/brain))
RegisterSignal(to_prepare, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_remove))
ADD_TRAIT(to_prepare, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
add_orbit_twitching(to_prepare)
to_prepare.med_hud_set_status()
/datum/component/ghostrole_on_revive/proc/unprepare_mob(mob/living/to_unprepare)
REMOVE_TRAIT(to_unprepare, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
UnregisterSignal(to_unprepare, list(
COMSIG_LIVING_REVIVE,
COMSIG_MOB_REAGENT_TICK,
COMSIG_CARBON_LOSE_ORGAN,
))
to_unprepare.med_hud_set_status()
remove_orbit_twitching(to_unprepare)
/datum/component/ghostrole_on_revive/proc/on_remove(mob/living/carbon/source, obj/item/organ/removed_brain)
SIGNAL_HANDLER
if(!istype(removed_brain, /obj/item/organ/brain))
return
unprepare_mob(source)
// we might have some lingering blinking eyes
var/obj/item/bodypart/head/head = source.get_bodypart(BODY_ZONE_HEAD)
if(head)
var/soul_eyes = locate(/datum/bodypart_overlay/simple/soul_pending_eyes) in head.bodypart_overlays
if(soul_eyes)
head.remove_bodypart_overlay(soul_eyes)
prepare_brain(removed_brain)
/datum/component/ghostrole_on_revive/proc/prepare_brain(obj/item/organ/source)
ADD_TRAIT(source, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
RegisterSignal(source, COMSIG_ORGAN_IMPLANTED, PROC_REF(prepare_mob_from_brain))
RegisterSignal(source, COMSIG_ATOM_EXAMINE, PROC_REF(brain_examine))
UnregisterSignal(source, COMSIG_ORGAN_REMOVED)
/datum/component/ghostrole_on_revive/proc/unprepare_brain(obj/item/organ/source)
REMOVE_TRAIT(source, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
UnregisterSignal(source, COMSIG_ORGAN_IMPLANTED)
UnregisterSignal(source, COMSIG_ATOM_EXAMINE)
RegisterSignal(source, COMSIG_ORGAN_REMOVED, PROC_REF(prepare_brain))
source.owner?.med_hud_set_status()
/datum/component/ghostrole_on_revive/proc/prepare_mob_from_brain(obj/item/organ/brain/source, mob/living/owner)
SIGNAL_HANDLER
REMOVE_TRAIT(source, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
UnregisterSignal(source, COMSIG_ORGAN_IMPLANTED)
prepare_mob(owner)
/datum/component/ghostrole_on_revive/proc/brain_examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
examine_list += span_info("Another soul may take [source.p_their()] place if put in a body...")
/datum/component/ghostrole_on_revive/proc/on_revive(mob/living/source)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(poll_ghosts), source)
/datum/component/ghostrole_on_revive/proc/poll_ghosts(mob/living/reviving)
var/datum/bodypart_overlay/simple/soul_pending_eyes/soul_eyes
// adds soulful SOUL PENDING eyes to indicate what's happening to observers
var/obj/item/bodypart/head/head = reviving.get_bodypart(BODY_ZONE_HEAD)
if(head)
soul_eyes = new()
head.add_bodypart_overlay(soul_eyes)
var/list/jobbans = list(ROLE_RECOVERED_CREW)
if(reviving.mind)
jobbans |= reviving.mind.assigned_role.title
for(var/datum/antagonist/antag as anything in reviving.mind.antag_datums)
if(antag.jobban_flag)
jobbans |= antag.jobban_flag
if(antag.pref_flag)
jobbans |= antag.pref_flag
if(!(antag.antag_flags & ANTAG_FAKE))
jobbans |= ROLE_SYNDICATE
var/mob/dead/observer/chosen_one = SSpolling.poll_ghosts_for_target(
question = "Would you like to play as [revive_title]?",
check_jobban = jobbans,
poll_time = 15 SECONDS,
checked_target = reviving,
ignore_category = POLL_IGNORE_RECOVERED_CREW,
alert_pic = reviving,
role_name_text = revive_title,
)
if(head)
head.remove_bodypart_overlay(soul_eyes)
if(soul_eyes)
qdel(soul_eyes)
if(isobserver(chosen_one))
reviving.PossessByPlayer(chosen_one.ckey)
on_successful_revive?.Invoke(reviving)
qdel(src)
else if(refuse_revival_if_failed)
reviving.death()
reviving.visible_message(span_deadsay("[reviving]'s soul is struggling to return!"))
/datum/component/ghostrole_on_revive/proc/add_orbit_twitching(mob/living/parent_mob)
parent_mob.AddElement(/datum/element/orbit_twitcher, twitch_chance)
// Add it to the ghostrole spawner menu. Note that we can't directly spawn from it, but we can make it twitch to alert bystanders to defib it
LAZYADDASSOCLIST(GLOB.joinable_mobs, spawn_text, parent_mob)
RegisterSignal(parent_mob, COMSIG_LIVING_GHOSTROLE_INFO, PROC_REF(set_spawner_info))
/datum/component/ghostrole_on_revive/proc/set_spawner_info(datum/spawners_menu/menu, list/string_info)
SIGNAL_HANDLER
string_info["you_are_text"] = src.you_are_text
string_info["flavor_text"] = src.flavor_text
string_info["important_text"] = src.important_text
/datum/component/ghostrole_on_revive/proc/remove_orbit_twitching(mob/living/parent_mob)
parent_mob.RemoveElement(/datum/element/orbit_twitcher, twitch_chance)
LAZYREMOVEASSOC(GLOB.joinable_mobs, spawn_text, parent_mob)
UnregisterSignal(parent_mob, COMSIG_LIVING_GHOSTROLE_INFO)
// Block formaldehyde from being metabolized, Coroner QoL
/datum/component/ghostrole_on_revive/proc/block_formaldehyde_metabolism(mob/living/source, datum/reagent/chem)
SIGNAL_HANDLER
if(istype(chem, /datum/reagent/toxin/formaldehyde))
return COMSIG_MOB_STOP_REAGENT_TICK
/datum/component/ghostrole_on_revive/Destroy(force)
if(isliving(parent))
unprepare_mob(parent)
else if(istype(parent, /obj/item/organ/brain))
var/obj/item/organ/brain/brain = parent
if(brain.owner)
unprepare_mob(brain.owner)
else
unprepare_brain(brain)
return ..()