diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index bd077204f00..181d2a5140c 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -425,66 +425,8 @@
if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
. = TRUE
-proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
- var/roletext = be_special_type ? get_roletext(be_special_type) : null
- var/list/mob/dead/observer/candidates = list()
- var/time_passed = world.time
- if(!Question)
- Question = "Would you like to be a special role?"
-
- for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
- if(!G.key || !G.client)
- continue
- if(be_special_type)
- if(!(be_special_type in G.client.prefs.be_special))
- continue
- if(antag_age_check)
- if(!player_old_enough_antag(G.client, be_special_type))
- continue
- if(roletext)
- if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate"))
- continue
- if(config.use_exp_restrictions && min_hours)
- if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
- continue
- if(check_antaghud && cannotPossess(G))
- continue
- spawn(0)
- G << 'sound/misc/notice2.ogg'//Alerting them to their consideration
- if(flashwindow)
- window_flash(G.client)
- var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round")
- if(!G?.client)
- return
- switch(ans)
- if("Yes")
- to_chat(G, "Choice registered: Yes.")
- if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed.
- to_chat(G, "Sorry, you were too late for the consideration!")
- G << 'sound/machines/buzz-sigh.ogg'
- return
- candidates += G
- if("No")
- to_chat(G, "Choice registered: No.")
- return
- if("Not This Round")
- to_chat(G, "Choice registered: No.")
- to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.")
- G.client.prefs.be_special -= be_special_type
- return
- else
- return
- sleep(poll_time)
-
- //Check all our candidates, to make sure they didn't log off during the 30 second wait period.
- for(var/mob/dead/observer/G in candidates)
- if(!G.key || !G.client)
- candidates.Remove(G)
-
- return candidates
-
-/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
- var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud)
+/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = FALSE, flashwindow = TRUE, check_antaghud = TRUE, source)
+ var/list/willing_ghosts = SSghost_spawns.poll_candidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud, source)
var/list/selected_ghosts = list()
if(!willing_ghosts.len)
return selected_ghosts
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index a93e65c762b..531f510e3cb 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -3,7 +3,7 @@
//PUBLIC - call these wherever you want
-/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE)
+/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim)
/*
Proc to create or update an alert. Returns the alert if the alert is new or updated, 0 if it was thrown already
@@ -59,14 +59,16 @@
LAZYSET(alerts, category, alert) // This also creates the list if it doesn't exist
if(client && hud_used)
hud_used.reorganize_alerts()
- alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
- animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
- if(alert.timeout)
- spawn(alert.timeout)
- if(alert.timeout && alerts[category] == alert && world.time >= alert.timeout)
- clear_alert(category)
- alert.timeout = world.time + alert.timeout - world.tick_lag
+ if(!no_anim)
+ alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
+ animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
+
+ var/timeout = timeout_override || alert.timeout
+ if(timeout)
+ addtimer(CALLBACK(alert, /obj/screen/alert/.proc/do_timeout, src, category), timeout)
+ alert.timeout = world.time + timeout - world.tick_lag
+
return alert
// Proc to clear an existing alert.
@@ -94,7 +96,6 @@
var/alerttooltipstyle = ""
var/override_alerts = FALSE //If it is overriding other alerts of the same type
-
/obj/screen/alert/MouseEntered(location,control,params)
openToolTip(usr, src, params, title = name, content = desc, theme = alerttooltipstyle)
@@ -102,6 +103,12 @@
/obj/screen/alert/MouseExited()
closeToolTip(usr)
+/obj/screen/alert/proc/do_timeout(mob/M, category)
+ if(!M || !M.alerts)
+ return
+
+ if(timeout && M.alerts[category] == src && world.time >= timeout)
+ M.clear_alert(category)
//Gas alerts
/obj/screen/alert/not_enough_oxy
@@ -507,6 +514,34 @@ so as to remain in compliance with the most up-to-date laws."
timeout = 300
var/atom/target = null
var/action = NOTIFY_JUMP
+ var/show_time_left = FALSE // If true you need to call START_PROCESSING manually
+ var/image/time_left_overlay // The last image showing the time left
+ var/datum/candidate_poll/poll // If set, on Click() it'll register the player as a candidate
+
+/obj/screen/alert/notify_action/process()
+ if(show_time_left)
+ var/timeleft = timeout - world.time
+ if(timeleft <= 0)
+ return PROCESS_KILL
+
+ if(time_left_overlay)
+ overlays -= time_left_overlay
+
+ var/obj/O = new
+ O.maptext = "[CEILING(timeleft / 10, 1)]"
+ O.maptext_width = O.maptext_height = 128
+ var/matrix/M = new
+ M.Translate(4, 16)
+ O.transform = M
+
+ var/image/I = image(O)
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE + 1
+ overlays += I
+
+ time_left_overlay = I
+ qdel(O)
+ ..()
/obj/screen/alert/notify_action/Destroy()
target = null
@@ -515,20 +550,48 @@ so as to remain in compliance with the most up-to-date laws."
/obj/screen/alert/notify_action/Click()
if(!usr || !usr.client)
return
- if(!target)
- return
var/mob/dead/observer/G = usr
if(!istype(G))
return
- switch(action)
- if(NOTIFY_ATTACK)
- target.attack_ghost(G)
- if(NOTIFY_JUMP)
- var/turf/T = get_turf(target)
- if(T && isturf(T))
- G.loc = T
- if(NOTIFY_FOLLOW)
- G.ManualFollow(target)
+
+ if(poll)
+ if(poll.sign_up(G))
+ // Add a small overlay to indicate we've signed up
+ display_signed_up()
+ else if(target)
+ switch(action)
+ if(NOTIFY_ATTACK)
+ target.attack_ghost(G)
+ if(NOTIFY_JUMP)
+ var/turf/T = get_turf(target)
+ if(T && isturf(T))
+ G.loc = T
+ if(NOTIFY_FOLLOW)
+ G.ManualFollow(target)
+
+/obj/screen/alert/notify_action/proc/display_signed_up()
+ var/image/I = image('icons/mob/screen_gen.dmi', icon_state = "selector")
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE + 2
+ overlays += I
+
+/obj/screen/alert/notify_action/proc/display_stacks(stacks = 1)
+ if(stacks <= 1)
+ return
+
+ var/obj/O = new
+ O.maptext = "[stacks]x"
+ O.maptext_width = O.maptext_height = 128
+ var/matrix/M = new
+ M.Translate(4, 2)
+ O.transform = M
+
+ var/image/I = image(O)
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE + 1
+ overlays += I
+
+ qdel(O)
/obj/screen/alert/notify_soulstone
name = "Soul Stone"
diff --git a/code/controllers/subsystem/ghost_spawns.dm b/code/controllers/subsystem/ghost_spawns.dm
new file mode 100644
index 00000000000..6063d6995e5
--- /dev/null
+++ b/code/controllers/subsystem/ghost_spawns.dm
@@ -0,0 +1,268 @@
+SUBSYSTEM_DEF(ghost_spawns)
+ name = "Ghost Spawns"
+ init_order = INIT_ORDER_EVENTS
+ flags = SS_BACKGROUND
+ wait = 1 SECONDS
+ runlevels = RUNLEVEL_GAME
+ offline_implications = "Ghosts will no longer be able to respawn as event mobs (Blob, etc..). Shuttle call recommended."
+
+ /// List of polls currently ongoing, to be checked on next fire()
+ var/list/datum/candidate_poll/currently_polling
+ /// Whether there are active polls or not
+ var/polls_active = FALSE
+ /// Number of polls performed since the start
+ var/total_polls = 0
+ /// The poll that's closest to finishing
+ var/datum/candidate_poll/next_poll_to_finish
+
+/datum/controller/subsystem/ghost_spawns/fire()
+ if(!polls_active)
+ return
+ if(!currently_polling) // if polls_active is TRUE then this shouldn't happen, but still..
+ currently_polling = list()
+
+ for(var/poll in currently_polling)
+ var/datum/candidate_poll/P = poll
+ if(P.time_left() <= 0)
+ polling_finished(P)
+
+/**
+ * Polls for candidates with a question and a preview of the role
+ *
+ * This proc replaces /proc/pollCandidates.
+ * Should NEVER be used in a proc that has waitfor set to FALSE/0 (due to #define UNTIL)
+ * Arguments:
+ * * question - The question to ask to potential candidates
+ * * role - The role to poll for. Should be a ROLE_x enum. If set, potential candidates who aren't eligible will be ignored
+ * * antag_age_check - Whether to filter out potential candidates who don't have an old enough account
+ * * poll_time - How long to poll for in deciseconds
+ * * ignore_respawnability - Whether to ignore the player's respawnability
+ * * min_hours - The amount of hours needed for a potential candidate to be eligible
+ * * flash_window - Whether the poll should flash a potential candidate's game window
+ * * check_antaghud - Whether to filter out potential candidates who enabled AntagHUD
+ * * source - The atom, atom prototype, icon or mutable appearance to display as an icon in the alert
+ */
+/datum/controller/subsystem/ghost_spawns/proc/poll_candidates(question = "Would you like to play a special role?", role, antag_age_check = FALSE, poll_time = 30 SECONDS, ignore_respawnability = FALSE, min_hours = 0, flash_window = TRUE, check_antaghud = TRUE, source)
+ log_debug("Polling candidates [role ? "for [get_roletext(role)]" : "\"[question]\""] for [poll_time / 10] seconds")
+
+ // Start firing
+ polls_active = TRUE
+ total_polls++
+
+ var/datum/candidate_poll/P = new(role, question, poll_time)
+ LAZYADD(currently_polling, P)
+
+ // We're the poll closest to completion
+ if(!next_poll_to_finish || poll_time < next_poll_to_finish.time_left())
+ next_poll_to_finish = P
+
+ var/category = "[P.hash]_notify_action"
+
+ for(var/mob/dead/observer/M in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
+ if(!is_eligible(M))
+ continue
+
+ SEND_SOUND(M, 'sound/misc/notice2.ogg')
+ if(flash_window)
+ window_flash(M.client)
+
+ // If we somehow send two polls for the same mob type, but with a duration on the second one shorter than the time left on the first one,
+ // we need to keep the first one's timeout rather than use the shorter one
+ var/obj/screen/alert/notify_action/current_alert = LAZYACCESS(M.alerts, category)
+ var/alert_time = poll_time
+ var/alert_poll = P
+ if(current_alert && current_alert.timeout > (world.time + poll_time - world.tick_lag))
+ alert_time = current_alert.timeout - world.time + world.tick_lag
+ alert_poll = current_alert.poll
+
+ // Send them an on-screen alert
+ var/obj/screen/alert/notify_action/A = M.throw_alert(category, /obj/screen/alert/notify_action, timeout_override = alert_time, no_anim = TRUE)
+ if(!A)
+ continue
+
+ A.icon = ui_style2icon(M.client?.prefs.UI_style)
+ A.name = "Looking for candidates"
+ A.desc = "[question]\n\n(expires in [poll_time / 10] seconds)"
+ A.show_time_left = TRUE
+ A.poll = alert_poll
+
+ // Sign up inheritance and stacking
+ var/inherited_sign_up = FALSE
+ var/num_stack = 1
+ for(var/existing_poll in currently_polling)
+ var/datum/candidate_poll/P2 = existing_poll
+ if(P != P2 && P.hash == P2.hash)
+ // If there's already a poll for an identical mob type ongoing and the client is signed up for it, sign them up for this one
+ if(!inherited_sign_up && (M in P2.signed_up) && P.sign_up(M, TRUE))
+ A.display_signed_up()
+ inherited_sign_up = TRUE
+ // This number is used to display the number of polls the alert regroups
+ num_stack++
+ if(num_stack > 1)
+ A.display_stacks(num_stack)
+
+ // Image to display
+ var/image/I
+ if(source)
+ if(!ispath(source))
+ var/atom/S = source
+ var/old_layer = S.layer
+ var/old_plane = S.plane
+
+ S.layer = FLOAT_LAYER
+ S.plane = FLOAT_PLANE
+ A.overlays += S
+ S.layer = old_layer
+ S.plane = old_plane
+ else
+ I = image(source, layer = FLOAT_LAYER, dir = SOUTH)
+ else
+ // Just use a generic image
+ I = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER, dir = SOUTH)
+
+ if(I)
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE
+ A.overlays += I
+
+ // Start processing it so it updates visually the timer
+ START_PROCESSING(SSprocessing, A)
+ A.process()
+
+ // Sleep until the time is up
+ UNTIL(P.finished)
+ return P.signed_up
+
+/**
+ * Returns whether an observer is eligible to be an event mob
+ *
+ * Arguments:
+ * * M - The mob to check eligibility
+ * * role - The role to check eligibility for. Checks 1. the client has enabled the role 2. the account's age for this role if antag_age_check is TRUE
+ * * antag_age_check - Whether to check the account's age or not for the given role.
+ * * role_text - The role's clean text. Used for checking job bans to determine eligibility
+ * * min_hours - The amount of minimum hours the client needs before being eligible
+ * * check_antaghud - Whether to consider a client who enabled AntagHUD ineligible or not
+ */
+/datum/controller/subsystem/ghost_spawns/proc/is_eligible(mob/M, role, antag_age_check, role_text, min_hours, check_antaghud)
+ . = FALSE
+ if(!M.key || !M.client)
+ return
+ if(role)
+ if(!(role in M.client.prefs.be_special))
+ return
+ if(antag_age_check)
+ if(!player_old_enough_antag(M.client, role))
+ return
+ if(role_text)
+ if(jobban_isbanned(M, role_text) || jobban_isbanned(M, "Syndicate"))
+ return
+ if(config.use_exp_restrictions && min_hours)
+ if(M.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
+ return
+ if(check_antaghud && cannotPossess(M))
+ return
+
+ return TRUE
+
+/**
+ * Called by the subsystem when a poll's timer runs out
+ *
+ * Can be called manually to finish a poll prematurely
+ * Arguments:
+ * * P - The poll to finish
+ */
+/datum/controller/subsystem/ghost_spawns/proc/polling_finished(datum/candidate_poll/P)
+ // Trim players who aren't eligible anymore
+ var/len_pre_trim = length(P.signed_up)
+ P.trim_candidates()
+ log_debug("Candidate poll [P.role ? "for [get_roletext(P.role)]" : "\"[P.question]\""] finished. [len_pre_trim] players signed up, [length(P.signed_up)] after trimming")
+
+ P.finished = TRUE
+ currently_polling -= P
+
+ // Determine which is the next poll closest the completion or "disable" firing if there's none
+ if(!length(currently_polling))
+ polls_active = FALSE
+ next_poll_to_finish = null
+ else if(P == next_poll_to_finish)
+ next_poll_to_finish = null
+ for(var/poll in currently_polling)
+ var/datum/candidate_poll/P2 = poll
+ if(!next_poll_to_finish || P2.time_left() < next_poll_to_finish.time_left())
+ next_poll_to_finish = P2
+
+/datum/controller/subsystem/ghost_spawns/stat_entry(msg)
+ msg += "Active: [length(currently_polling)] | Total: [total_polls]"
+ if(next_poll_to_finish)
+ msg += " | Next: [DisplayTimeText(next_poll_to_finish.time_left())] ([length(next_poll_to_finish.signed_up)] candidates)"
+ ..(msg)
+
+// The datum that describes one instance of candidate polling
+/datum/candidate_poll
+ var/role // The role the poll is for
+ var/question // The question asked to observers
+ var/duration // The duration of the poll
+ var/list/mob/dead/observer/signed_up // The players who signed up to this poll
+ var/time_started // The world.time at which the poll was created
+ var/finished = FALSE // Whether the polling is finished
+ var/hash // Used to categorize in the alerts system
+
+/datum/candidate_poll/New(polled_role, polled_question, poll_duration)
+ role = polled_role
+ question = polled_question
+ duration = poll_duration
+ signed_up = list()
+ time_started = world.time
+ hash = copytext(md5("[question]_[role ? role : "0"]"), 1, 7)
+ return ..()
+
+/**
+ * Attempts to sign a (controlled) mob up
+ *
+ * Will fail if the mob is already signed up or the poll's timer ran out.
+ * Does not check for eligibility
+ * Arguments:
+ * * M - The (controlled) mob to sign up
+ * * silent - Whether no messages should appear or not. If not TRUE, signing up to this poll will also sign the mob up for identical polls
+ */
+/datum/candidate_poll/proc/sign_up(mob/dead/observer/M, silent = FALSE)
+ . = FALSE
+ if(!istype(M) || !M.key || !M.client)
+ return
+ if(M in signed_up)
+ if(!silent)
+ to_chat(M, "You have already signed up for this!")
+ return
+ if(time_left() <= 0)
+ if(!silent)
+ to_chat(M, "Sorry, you were too late for the consideration!")
+ SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
+ return
+
+ signed_up += M
+ if(!silent)
+ to_chat(M, "You have signed up for this role! A candidate will be picked randomly soon..")
+ // Sign them up for any other polls with the same mob type
+ for(var/existing_poll in SSghost_spawns.currently_polling)
+ var/datum/candidate_poll/P = existing_poll
+ if(src != P && hash == P.hash && !(M in P.signed_up))
+ P.sign_up(M, TRUE)
+
+ return TRUE
+
+/**
+ * Deletes any candidates who may have disconnected from the list
+ */
+/datum/candidate_poll/proc/trim_candidates()
+ listclearnulls(signed_up)
+ for(var/mob in signed_up)
+ var/mob/M = mob
+ if(!M.key || !M.client)
+ signed_up -= M
+
+/**
+ * Returns the time left for a poll
+ */
+/datum/candidate_poll/proc/time_left()
+ return duration - (world.time - time_started)
diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm
index be794b9092b..b4c01609653 100644
--- a/code/game/gamemodes/blob/blobs/core.dm
+++ b/code/game/gamemodes/blob/blobs/core.dm
@@ -104,10 +104,18 @@
var/mob/C = null
var/list/candidates = list()
if(!new_overmind)
+ // Create icon
+ var/mutable_appearance/MA = new
+ var/mutable_appearance/MA1 = new(icon, "blob")
+ MA1.color = overmind ? overmind.blob_reagent_datum.color : color
+ MA.overlays += MA1
+ var/mutable_appearance/MA2 = new(icon, "blob_core_overlay")
+ MA.overlays += MA2
+ // sendit
if(is_offspring)
- candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1)
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = MA)
else
- candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1)
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = MA)
if(length(candidates))
C = pick(candidates)
diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm
index 6f2eed39499..ec354d4d1c4 100644
--- a/code/game/gamemodes/blob/powers.dm
+++ b/code/game/gamemodes/blob/powers.dm
@@ -237,7 +237,7 @@
blobber.AIStatus = AI_OFF
blobber.LoseTarget()
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a blobbernaut?", ROLE_BLOB, 1, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber)
if(candidates.len)
var/mob/C = pick(candidates)
if(C)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 57a77167346..1e2dd3d554b 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -424,9 +424,9 @@ proc/display_roundstart_logout_report()
return nukecode
/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type)
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [role_type]?", role_type, FALSE, 10 SECONDS)
var/mob/dead/observer/theghost = null
- if(candidates.len)
+ if(length(candidates))
theghost = pick(candidates)
to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.")
diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index f647ae5a6d1..0bb48fc55ed 100644
--- a/code/game/gamemodes/miniantags/guardian/guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/guardian.dm
@@ -246,7 +246,7 @@
src.verbs -= /mob/living/proc/guardian_reset
for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list)
if(G.summoner == src)
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = G)
var/mob/dead/observer/new_stand = null
if(candidates.len)
new_stand = pick(candidates)
@@ -312,7 +312,7 @@
used = FALSE
return
to_chat(user, "[use_message]")
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = /mob/living/simple_animal/hostile/guardian)
var/mob/dead/observer/theghost = null
if(candidates.len)
diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm
index e9465492a27..7fc9e134195 100644
--- a/code/game/gamemodes/miniantags/morph/morph_event.dm
+++ b/code/game/gamemodes/miniantags/morph/morph_event.dm
@@ -3,7 +3,7 @@
/datum/event/spawn_morph/proc/get_morph()
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a morph?", ROLE_MORPH, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, source = /mob/living/simple_animal/hostile/morph)
if(!candidates.len)
key_of_morph = null
return kill()
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index 637ab69bf04..d4e912aa781 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -141,7 +141,7 @@
giveObjectivesandGoals()
giveSpells()
else
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a revenant?", poll_time = 15 SECONDS)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", poll_time = 15 SECONDS, source = /mob/living/simple_animal/revenant)
var/mob/dead/observer/theghost = null
if(candidates.len)
theghost = pick(candidates)
@@ -397,7 +397,7 @@
spawn()
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
- var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
qdel(R)
message_admins("No candidates were found for the new revenant. Oh well!")
diff --git a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
index 5e5f0f87a79..aa76f3620ac 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
@@ -13,7 +13,7 @@
return
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
key_of_revenant = null
return kill()
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
index d7e53bdae28..e58be201521 100644
--- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm
+++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
@@ -157,7 +157,7 @@
/mob/living/simple_animal/slaughter/cult/New()
..()
spawn(5)
- var/list/demon_candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1, 100)
+ var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/slaughter/cult)
if(!demon_candidates.len)
visible_message("[src] disappears in a flash of red light!")
qdel(src)
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index a81b12ed041..b3092b04e3b 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -50,7 +50,8 @@
to_chat(H, "You already used this contract!")
return
used = 1
- var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, 1)
+ var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, TRUE, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
new /obj/effect/particle_effect/smoke(H.loc)
@@ -307,7 +308,8 @@ GLOBAL_LIST_EMPTY(multiverse)
if(M.assigned == assigned)
M.cooldown = cooldown
- var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, 1, 100)
+ var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, TRUE, 10 SECONDS, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
spawn_copy(C.client, get_turf(user.loc), user)
diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm
index cc52b431249..b189c98524f 100644
--- a/code/game/gamemodes/wizard/raginmages.dm
+++ b/code/game/gamemodes/wizard/raginmages.dm
@@ -118,7 +118,8 @@
return FALSE
making_mage = TRUE
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS)
+ var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS, source = source)
var/mob/dead/observer/harry = null
message_admins("SWF is still pissed, sending another wizard - [max_mages - mages_made] left.")
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index 4bda254d930..aab4318c774 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -363,7 +363,7 @@
break
if(!chosen_ghost) //Failing that, we grab a ghost
- var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100)
+ var/list/consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade)
if(consenting_candidates.len)
chosen_ghost = pick(consenting_candidates)
if(!T)
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 9dd1c771b17..ee2c6f9546d 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -180,7 +180,7 @@
if(player_spiders && !selecting_player)
selecting_player = 1
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a spider?", ROLE_GSPIDER, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S)
if(candidates.len)
var/mob/C = pick(candidates)
diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm
index 6423bbeb6ff..974bd3b677f 100644
--- a/code/game/objects/items/weapons/holy_weapons.dm
+++ b/code/game/objects/items/weapons/holy_weapons.dm
@@ -255,7 +255,7 @@
possessed = TRUE
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, FALSE, 10 SECONDS, source = src)
var/mob/dead/observer/theghost = null
if(candidates.len)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 0649c03fd14..ecc60464949 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1947,7 +1947,7 @@
var/petchoice = input("Select pet type", "Pets") as null|anything in pets
if(isnull(petchoice))
return
- var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Play as the special event pet [H]?", poll_time = 20 SECONDS, min_hours = 10, source = petchoice)
var/mob/dead/observer/theghost = null
if(candidates.len)
var/mob/living/simple_animal/pet/P = new petchoice(H.loc)
@@ -3573,7 +3573,8 @@
return
var/datum/outfit/O = hunter_outfits[dresscode]
message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...")
- var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1)
+ var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_traitor")
+ var/list/candidates = SSghost_spawns.poll_candidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, TRUE, source = source)
if(!candidates.len)
to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.")
return
diff --git a/code/modules/admin/verbs/gimmick_team.dm b/code/modules/admin/verbs/gimmick_team.dm
index 37afe35ef6a..3403a4417cf 100644
--- a/code/modules/admin/verbs/gimmick_team.dm
+++ b/code/modules/admin/verbs/gimmick_team.dm
@@ -39,6 +39,7 @@
if(alert("Do you want these characters automatically classified as antagonists?",,"Yes","No")=="Yes")
is_syndicate = 1
+ var/datum/outfit/O = outfit_list[dresscode]
var/list/players_to_spawn = list()
if(pick_manually)
var/list/possible_ghosts = list()
@@ -52,14 +53,12 @@
players_to_spawn += candidate
else
to_chat(src, "Polling candidates...")
- players_to_spawn = pollCandidates("Do you want to play as an event character?")
+ players_to_spawn = SSghost_spawns.poll_candidates("Do you want to play as \a [O.name]?")
if(!players_to_spawn.len)
to_chat(src, "Nobody volunteered.")
return 0
- var/datum/outfit/O = outfit_list[dresscode]
-
var/players_spawned = 0
for(var/mob/thisplayer in players_to_spawn)
var/mob/living/carbon/human/H = new /mob/living/carbon/human(T)
diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
index 54ddf85045e..d4ea1a0dd2e 100644
--- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm
+++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
@@ -55,7 +55,8 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0)
infiltrators += candidate
else
to_chat(src, "Polling candidates...")
- infiltrators = pollCandidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, 1)
+ var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate")
+ infiltrators = SSghost_spawns.poll_candidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, TRUE, source = ma)
if(!infiltrators.len)
to_chat(src, "Nobody volunteered.")
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 3e89a6ac18d..5d9d218c90e 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -137,7 +137,8 @@ client/proc/one_click_antag()
var/confirm = alert("Are you sure?", "Confirm creation", "Yes", "No")
if(confirm != "Yes")
return 0
- var/list/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard")
+ var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "wizard")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", source = ma)
log_admin("[key_name(owner)] tried making a Wizard with One-Click-Antag")
message_admins("[key_name_admin(owner)] tried making a Wizard with One-Click-Antag")
diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm
index 0b84c96e71a..b3ea2924709 100644
--- a/code/modules/admin/verbs/striketeam.dm
+++ b/code/modules/admin/verbs/striketeam.dm
@@ -37,7 +37,8 @@ GLOBAL_VAR_INIT(sent_strike_team, 0)
break
// Find ghosts willing to be DS
- var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
+ var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_deathsquad")
+ var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE, source = source)
if(!commando_ghosts.len)
to_chat(usr, "Nobody volunteered to join the DeathSquad.")
return
diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm
index 12df7467cbc..405c1d3c3d2 100644
--- a/code/modules/admin/verbs/striketeam_syndicate.dm
+++ b/code/modules/admin/verbs/striketeam_syndicate.dm
@@ -45,7 +45,7 @@ GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0)
break
// Find ghosts willing to be SST
- var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
+ var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
if(!commando_ghosts.len)
to_chat(usr, "Nobody volunteered to join the SST.")
return
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 4a6fe7d6640..41333760f2d 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -73,7 +73,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [name]?", job_rank, TRUE, 50)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index 8e29326632f..82f492d214b 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -46,7 +46,8 @@
checking = TRUE
to_chat(user, "You activate [src] and wait for confirmation.")
- var/list/nuke_candidates = pollCandidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 150)
+ var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate_space_sword")
+ var/list/nuke_candidates = SSghost_spawns.poll_candidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 15 SECONDS, source = ma)
if(LAZYLEN(nuke_candidates))
checking = FALSE
if(QDELETED(src) || !check_usability(user))
@@ -180,7 +181,7 @@
var/type = "slaughter"
if(demon_type == /mob/living/simple_animal/slaughter/laughter)
type = "laughter"
- var/list/candidates = pollCandidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, 1, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, TRUE, 10 SECONDS, source = demon_type)
if(candidates.len > 0)
var/mob/C = pick(candidates)
@@ -252,7 +253,7 @@
used = TRUE
to_chat(user, "You break the seal on the bottle, calling upon the dire sludge to awaken...")
- var/list/candidates = pollCandidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 10 SECONDS, source = morph_type)
if(candidates.len > 0)
var/mob/C = pick(candidates)
diff --git a/code/modules/awaymissions/mission_code/academy.dm b/code/modules/awaymissions/mission_code/academy.dm
index 7cddddd8b08..65c64edd83f 100644
--- a/code/modules/awaymissions/mission_code/academy.dm
+++ b/code/modules/awaymissions/mission_code/academy.dm
@@ -193,7 +193,7 @@
servant_mind.objectives += O
servant_mind.transfer_to(H)
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 300)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm
index 7fe60e8b77f..4ad8fb4eb90 100644
--- a/code/modules/events/abductor.dm
+++ b/code/modules/events/abductor.dm
@@ -9,7 +9,7 @@
makeAbductorTeam()
/datum/event/abductor/proc/makeAbductorTeam()
- var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, 1)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, TRUE)
if(candidates.len >= 2)
//Oh god why we can't have static functions
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index 8c01d5c7cc2..23e4e266819 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -23,7 +23,7 @@
vents += temp_vent
spawn()
- var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, TRUE, source = /mob/living/carbon/alien/larva)
while(spawncount > 0 && vents.len && candidates.len)
var/obj/vent = pick_n_take(vents)
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
index caec11de9f2..de2da9fbf64 100644
--- a/code/modules/events/blob.dm
+++ b/code/modules/events/blob.dm
@@ -12,14 +12,14 @@
if(!T)
return kill()
- var/list/candidates = pollCandidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, 1)
- if(!candidates.len)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/simple_animal/mouse/blobinfected)
+ if(!length(candidates))
return kill()
var/list/vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in GLOB.all_vent_pumps)
if(is_station_level(temp_vent.loc.z) && !temp_vent.welded)
- if(temp_vent.parent.other_atmosmch.len > 50)
+ if(length(temp_vent.parent.other_atmosmch) > 50)
vents += temp_vent
var/obj/vent = pick(vents)
diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm
index ab8a405785f..7007a99f0ec 100644
--- a/code/modules/events/sentience.dm
+++ b/code/modules/events/sentience.dm
@@ -3,8 +3,7 @@
/datum/event/sentience/start()
processing = FALSE //so it won't fire again in next tick
- var/ghostmsg = "Do you want to awaken as a sentient being?"
- var/list/candidates = pollCandidates(ghostmsg, ROLE_SENTIENT, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to awaken as a sentient being?", ROLE_SENTIENT, TRUE)
var/list/potential = list()
var/sentience_type = SENTIENCE_ORGANIC
diff --git a/code/modules/events/slaughterevent.dm b/code/modules/events/slaughterevent.dm
index 91918894cfd..85f0f8aa555 100644
--- a/code/modules/events/slaughterevent.dm
+++ b/code/modules/events/slaughterevent.dm
@@ -3,7 +3,7 @@
/datum/event/spawn_slaughter/proc/get_slaughter(var/end_if_fail = 0)
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, source = /mob/living/simple_animal/slaughter)
if(!candidates.len)
key_of_slaughter = null
return kill()
diff --git a/code/modules/events/traders.dm b/code/modules/events/traders.dm
index 0beeb3691e2..d4ae1de3b9c 100644
--- a/code/modules/events/traders.dm
+++ b/code/modules/events/traders.dm
@@ -34,7 +34,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol"))
trader_objectives = forge_trader_objectives()
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a trader?", ROLE_TRADER, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a Sol Trader?", ROLE_TRADER, TRUE)
var/index = 1
while(spawn_count > 0 && candidates.len > 0)
if(index > spawnlocs.len)
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index 5d828e7c181..79ed90a61b8 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -70,7 +70,7 @@
return
polling = 1
spawn()
- var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 0)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, FALSE, source = /mob/living/carbon/alien/larva)
var/mob/C = null
// To stop clientless larva, we will check that our host has a client
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 5fb5bcb06c8..ff01fbe5cc9 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -113,7 +113,7 @@
var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]"
if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes")
question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]"
- var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M)
var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates))
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 7c35730f4aa..ac7249a8b63 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -198,7 +198,7 @@
being_used = 1
var/ghostmsg = "Play as [SM.name], pet of [user.name]?"
- var/list/candidates = pollCandidates(ghostmsg, ROLE_SENTIENT, 0, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates(ghostmsg, ROLE_SENTIENT, FALSE, 10 SECONDS, source = M)
if(!src)
return
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index a25be3814a7..f2d559e8b1f 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -63,7 +63,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
GLOB.active_team.setSlots(commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots)
GLOB.send_emergency_team = TRUE
- var/list/ert_candidates = shuffle(pollCandidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 600, 1, GLOB.role_playtime_requirements[ROLE_ERT]))
+ var/list/ert_candidates = shuffle(SSghost_spawns.poll_candidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_ERT]))
if(!ert_candidates.len)
GLOB.active_team.cannot_send_team()
GLOB.send_emergency_team = FALSE
diff --git a/paradise.dme b/paradise.dme
index de2efe2b45b..5e88b673231 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -219,6 +219,7 @@
#include "code\controllers\subsystem\events.dm"
#include "code\controllers\subsystem\fires.dm"
#include "code\controllers\subsystem\garbage.dm"
+#include "code\controllers\subsystem\ghost_spawns.dm"
#include "code\controllers\subsystem\holiday.dm"
#include "code\controllers\subsystem\icon_smooth.dm"
#include "code\controllers\subsystem\idlenpcpool.dm"