Files
Paradise/code/datums/components/ghost_direct_control.dm
kyunkyunkyun b0463d3c83 Convert most spans to defines (#31080)
* spanish?

* aaaagain

* keep maptext

* Update robot_items.dm

* Update span_defines.dm

* compiles

* Update silicon_mob.dm

* compile
2025-12-13 23:55:48 +00:00

148 lines
5.6 KiB
Plaintext

/**
* Component which lets ghosts click on a mob to take control of it
*/
/datum/component/ghost_direct_control
/// Message to display upon successful possession
var/assumed_control_message
/// Type of ban you can get to prevent you from accepting this role
var/ban_type
/// Any extra checks which need to run before we take over
var/datum/callback/extra_control_checks
/// Callback run after someone successfully takes over the body
var/datum/callback/after_assumed_control
/// If we're currently awaiting the results of a ghost poll
var/awaiting_ghosts = FALSE
/// Is this an antagonist spawner, so we check ROLE_SYNDICATE
var/is_antag_spawner
/datum/component/ghost_direct_control/Initialize(
ban_type = ROLE_SENTIENT,
role_name = null,
poll_question = null,
poll_candidates = TRUE,
poll_length = 10 SECONDS,
assumed_control_message = null,
datum/callback/extra_control_checks,
datum/callback/after_assumed_control,
is_antag_spawner = TRUE,
)
. = ..()
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
src.ban_type = ban_type
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.is_antag_spawner = is_antag_spawner
if(poll_candidates)
INVOKE_ASYNC(src, PROC_REF(request_ghost_control), poll_question, role_name || "[parent]", poll_length)
/datum/component/ghost_direct_control/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, PROC_REF(on_ghost_clicked))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examined))
RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login))
/datum/component/ghost_direct_control/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_PARENT_EXAMINE, COMSIG_MOB_LOGIN))
return ..()
/datum/component/ghost_direct_control/Destroy(force)
extra_control_checks = null
after_assumed_control = null
return ..()
/// Inform ghosts that they can possess this
/datum/component/ghost_direct_control/proc/on_examined(datum/source, mob/user, list/examine_text)
SIGNAL_HANDLER //COMSIG_PARENT_EXAMINE
if(!isobserver(user))
return
var/mob/living/our_mob = parent
if(our_mob.stat == DEAD || our_mob.key || awaiting_ghosts)
return
examine_text += SPAN_BOLDNOTICE("You could take control of this mob by clicking on it.")
/// Send out a request for a brain
/datum/component/ghost_direct_control/proc/request_ghost_control(poll_question, role_name, poll_length)
awaiting_ghosts = TRUE
var/list/candidates = SSghost_spawns.poll_candidates(
question = poll_question,
role = ban_type,
poll_time = poll_length,
source = parent,
role_cleanname = role_name,
flash_window = FALSE,
dont_play_notice_sound = TRUE
)
awaiting_ghosts = FALSE
var/mob/chosen_one = null
if(length(candidates))
chosen_one = pick(candidates)
if(isnull(chosen_one))
return
assume_direct_control(chosen_one)
/// A ghost clicked on us, they want to get in this body
/datum/component/ghost_direct_control/proc/on_ghost_clicked(mob/our_mob, mob/dead/observer/hopeful_ghost)
SIGNAL_HANDLER // COMSIG_ATOM_ATTACK_GHOST
if(our_mob.key)
qdel(src)
return COMPONENT_CANCEL_ATTACK_CHAIN
if(!hopeful_ghost.client)
return COMPONENT_CANCEL_ATTACK_CHAIN
if(awaiting_ghosts)
to_chat(hopeful_ghost, SPAN_WARNING("Ghost candidate selection currently in progress!"))
return COMPONENT_CANCEL_ATTACK_CHAIN
if(!SSticker.HasRoundStarted())
to_chat(hopeful_ghost, SPAN_WARNING("You cannot assume control of this until after the round has started!"))
return COMPONENT_CANCEL_ATTACK_CHAIN
INVOKE_ASYNC(src, PROC_REF(attempt_possession), our_mob, hopeful_ghost)
return COMPONENT_CANCEL_ATTACK_CHAIN
/// We got far enough to establish that this mob is a valid target, let's try to posssess it
/datum/component/ghost_direct_control/proc/attempt_possession(mob/our_mob, mob/dead/observer/hopeful_ghost)
var/ghost_asked = tgui_alert(usr, "Become [our_mob]?", "Are you sure?", list("Yes", "No"))
if(ghost_asked != "Yes" || QDELETED(our_mob))
return
assume_direct_control(hopeful_ghost)
/// Grant possession of our mob, component is now no longer required
/datum/component/ghost_direct_control/proc/assume_direct_control(mob/harbinger)
if(QDELETED(src))
to_chat(harbinger, SPAN_WARNING("Offer to possess creature has expired!"))
return
if(jobban_isbanned(harbinger, ban_type) || jobban_isbanned(harbinger, ROLE_SENTIENT) || (is_antag_spawner && jobban_isbanned(harbinger, ROLE_SYNDICATE)))
to_chat(harbinger, SPAN_WARNING("You are banned from playing as this role!"))
return
var/mob/living/new_body = parent
if(new_body.stat == DEAD)
to_chat(harbinger, SPAN_WARNING("This body has passed away, it is of no use!"))
return
if(new_body.key)
to_chat(harbinger, SPAN_WARNING("[parent] has already become sapient!"))
qdel(src)
return
if(extra_control_checks && !extra_control_checks.Invoke(harbinger))
return
// doesn't transfer mind because that transfers antag datum as well
new_body.key = harbinger.key
// Already qdels due to below proc but just in case
if(!QDELETED(src))
qdel(src)
/// When someone assumes control, get rid of our component
/datum/component/ghost_direct_control/proc/on_login(mob/harbinger)
SIGNAL_HANDLER //COMSIG_MOB_LOGIN
// This proc is called the very moment .key is set, so we need to force mind to initialize here if we want the invoke to affect the mind of the mob
if(isnull(harbinger.mind))
harbinger.mind_initialize()
to_chat(harbinger, SPAN_BOLDNOTICE("[assumed_control_message]"))
after_assumed_control?.Invoke(harbinger)
qdel(src)