From 25de7ded2b00001563108eb5fa0c49799d4d30f7 Mon Sep 17 00:00:00 2001 From: Timberpoes Date: Wed, 1 Sep 2021 13:01:54 +0100 Subject: [PATCH] Fixes being able to spam ghost role notifications. (#61085) There are methods that let players spam the everloving shit out of ghosts with BONG BONG BONG BONG BONG BONG BONG etc. through being able to constantly poll ghosts for roles with no restriction or cooldown. Examples are laughter and slaughter demon antag_spawners. It makes no sense to be able to concurrently poll for the same mob. As a result, I've now added a guard against this. The proc has been given a static list of mobs it's polling for ghost roles for. If it's already polling for ghost roles for a given mob, then it just early returns with an empty list, otherwise it adds the mob to the static list when the poll starts and removes it when the poll ends. I've also done a little cleanup in var names and the proc name, with a find-and-replace done. There weren't many things calling it and none used named keywords in the args so should be fine. There is also poll_candidates_for_mobs (also included in my cleanup) - This proc is basically only called by admins via sentience balloons and they have their own guards against spamming (the balloon pops and thus can only be used once) Also fixes an issue in /mob/living/silicon/robot/proc/replace_banned_cyborg() where incorrect args were used in the proc call to poll for candidates. --- code/__HELPERS/game.dm | 63 ++++++++++++++----- code/datums/brain_damage/imaginary_friend.dm | 2 +- code/datums/brain_damage/split_personality.dm | 4 +- code/datums/diseases/transformation.dm | 2 +- code/game/objects/effects/anomalies.dm | 2 +- code/modules/admin/fun_balloon.dm | 2 +- .../antagonists/_common/antag_datum.dm | 2 +- .../antagonists/_common/antag_spawner.dm | 4 +- code/modules/antagonists/cult/runes.dm | 2 +- .../eldritch_cult/eldritch_knowledge.dm | 4 +- .../eldritch_cult/knowledge/flesh_lore.dm | 2 +- code/modules/antagonists/revenant/revenant.dm | 2 +- .../awaymissions/mission_code/Academy.dm | 4 +- code/modules/clothing/head/helmet.dm | 2 +- .../hostile/mining_mobs/elites/elite.dm | 2 +- code/modules/mob/mob_helpers.dm | 4 +- code/modules/mob/transform_procs.dm | 2 +- code/modules/projectiles/projectile/magic.dm | 2 +- .../research/xenobiology/xenobiology.dm | 2 +- 19 files changed, 71 insertions(+), 38 deletions(-) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 2a6c79b8ebc..276d3fd4b99 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -433,22 +433,55 @@ return result -/proc/pollCandidatesForMob(Question, jobbanType, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category) - if(!M || QDELETED(M) || !M.loc) - return list() - return L +/** + * Returns a list of ghosts that are eligible to take over and wish to be considered for a mob. + * + * Arguments: + * * question - Question to show players as part of poll + * * jobban_type - Type of jobban to use to filter out potential candidates. + * * be_special_flag - Unknown/needs further documentation. + * * poll_time - Length of time in deciseconds that the poll input box exists before closing. + * * target_mob - The mob that is being polled for. + * * ignore_category - Unknown/needs further documentation. + */ +/proc/poll_candidates_for_mob(question, jobban_type, be_special_flag = 0, poll_time = 30 SECONDS, mob/target_mob, ignore_category = null) + var/static/list/mob/currently_polling_mobs = list() -/proc/pollCandidatesForMobs(Question, jobbanType, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category) - var/i=1 - for(var/v in mobs) - var/atom/A = v - if(!A || QDELETED(A) || !A.loc) - mobs.Cut(i,i+1) - else - ++i - return L + if(currently_polling_mobs.Find(target_mob)) + return list() + + currently_polling_mobs += target_mob + + var/list/possible_candidates = pollGhostCandidates(question, jobban_type, be_special_flag, poll_time, ignore_category) + + currently_polling_mobs -= target_mob + if(!target_mob || QDELETED(target_mob) || !target_mob.loc) + return list() + + return possible_candidates + +/** + * Returns a list of ghosts that are eligible to take over and wish to be considered for a mob. + * + * Arguments: + * * question - Question to show players as part of poll + * * jobban_type - Type of jobban to use to filter out potential candidates. + * * be_special_flag - Unknown/needs further documentation. + * * poll_time - Length of time in deciseconds that the poll input box exists before closing. + * * mobs - The list of mobs being polled for. This list is mutated and invalid mobs are removed from it before the proc returns. + * * ignore_category - Unknown/needs further documentation. + */ +/proc/poll_candidates_for_mobs(question, jobban_type, be_special_flag = 0, poll_time = 30 SECONDS, list/mobs, ignore_category = null) + var/list/candidate_list = pollGhostCandidates(question, jobban_type, be_special_flag, poll_time, ignore_category) + + for(var/mob/potential_mob as anything in mobs) + if(QDELETED(potential_mob) || !potential_mob.loc) + mobs -= potential_mob + + if(!length(mobs)) + return list() + + return candidate_list /proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character if(!G_found || !G_found.key) diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 2e244cecac2..1dfb3bc0903 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -47,7 +47,7 @@ /datum/brain_trauma/special/imaginary_friend/proc/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner.real_name]'s imaginary friend?", ROLE_PAI, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [owner.real_name]'s imaginary friend?", ROLE_PAI, null, 7.5 SECONDS, friend, POLL_IGNORE_IMAGINARYFRIEND) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index 5efe286e0f8..65f0d187aa0 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -33,7 +33,7 @@ /datum/brain_trauma/severe/split_personality/proc/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner.real_name]'s split personality?", ROLE_PAI, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [owner.real_name]'s split personality?", ROLE_PAI, null, 7.5 SECONDS, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key @@ -206,7 +206,7 @@ /datum/brain_trauma/severe/split_personality/brainwashing/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner.real_name]'s brainwashed mind?", null, null, 75, stranger_backseat) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [owner.real_name]'s brainwashed mind?", null, null, 7.5 SECONDS, stranger_backseat) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index b00af19e517..aaedc1e64c9 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -83,7 +83,7 @@ /datum/disease/transformation/proc/replace_banned_player(mob/living/new_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed. set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.real_name]?", bantype, bantype, 50, affected_mob) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [affected_mob.real_name]?", bantype, bantype, 5 SECONDS, affected_mob) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(affected_mob, span_userdanger("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/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 6addf60eccf..c415847b834 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -322,7 +322,7 @@ var/datum/action/innate/slime/reproduce/A = new A.Grant(S) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENCE, null, 100, S, POLL_IGNORE_PYROSLIME) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENCE, null, 10 SECONDS, S, POLL_IGNORE_PYROSLIME) if(LAZYLEN(candidates)) var/mob/dead/observer/chosen = pick(candidates) S.key = chosen.key diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index 4d7f08b6abc..f7cc9362689 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -88,7 +88,7 @@ bodies += possessable var/question = "Would you like to be [group_name]?" - var/list/candidates = pollCandidatesForMobs(question, ROLE_PAI, FALSE, 100, bodies) + var/list/candidates = poll_candidates_for_mobs(question, ROLE_PAI, FALSE, 10 SECONDS, bodies) while(LAZYLEN(candidates) && LAZYLEN(bodies)) var/mob/dead/observer/C = pick_n_take(candidates) var/mob/living/body = pick_n_take(bodies) diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index c9e43ac6734..a2b2e80977b 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -187,7 +187,7 @@ GLOBAL_LIST_EMPTY(antagonists) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", job_rank, 50, owner.current) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [name]?", "[name]", job_rank, 5 SECONDS, owner.current) 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 a530c0befe1..4b30be12f78 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -56,7 +56,7 @@ if(used) to_chat(H, span_warning("You already used this contract!")) return - var/list/candidates = pollCandidatesForMob("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, ROLE_WIZARD, 150, src) + var/list/candidates = poll_candidates_for_mob("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, ROLE_WIZARD, 15 SECONDS, src) if(LAZYLEN(candidates)) if(QDELETED(src)) return @@ -244,7 +244,7 @@ return if(used) return - var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", ROLE_ALIEN, ROLE_ALIEN, 50, src) + var/list/candidates = poll_candidates_for_mob("Do you want to play as a [initial(demon_type.name)]?", ROLE_ALIEN, ROLE_ALIEN, 5 SECONDS, src) if(LAZYLEN(candidates)) if(used || QDELETED(src)) return diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 499bbe676a8..c88e5bbe341 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -579,7 +579,7 @@ structure_check() searches for nearby cultist structures required for the invoca mob_to_revive.grab_ghost() if(!mob_to_revive.client || mob_to_revive.client.is_afk()) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.real_name], an inactive blood cultist?", ROLE_CULTIST, ROLE_CULTIST, 50, mob_to_revive) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [mob_to_revive.real_name], an inactive blood cultist?", ROLE_CULTIST, ROLE_CULTIST, 5 SECONDS, mob_to_revive) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.") diff --git a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm index 587f7e6557f..4167b50d84f 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm @@ -182,10 +182,10 @@ var/mob/living/mob_to_summon /datum/eldritch_knowledge/summon/on_finished_recipe(mob/living/user, list/atoms, loc) - //we need to spawn the mob first so that we can use it in pollCandidatesForMob, we will move it from nullspace down the code + //we need to spawn the mob first so that we can use it in poll_candidates_for_mob, we will move it from nullspace down the code var/mob/living/summoned = new mob_to_summon(loc) message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [summoned.real_name]", ROLE_HERETIC, FALSE, 100, summoned) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [summoned.real_name]", ROLE_HERETIC, FALSE, 10 SECONDS, summoned) if(!LAZYLEN(candidates)) to_chat(user,span_warning("No ghost could be found...")) qdel(summoned) diff --git a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm index 18050b32106..c8dd9284682 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm @@ -46,7 +46,7 @@ humie.grab_ghost() if(!humie.mind || !humie.client) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [humie.real_name], a voiceless dead", ROLE_HERETIC, ROLE_HERETIC, 50,humie) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [humie.real_name], a voiceless dead", ROLE_HERETIC, ROLE_HERETIC, 5 SECONDS, humie) if(!LAZYLEN(candidates)) return var/mob/dead/observer/chosen_candidate = pick(candidates) diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 6e5267021e5..a0d6c28ed9e 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -448,7 +448,7 @@ break 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 = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, ROLE_REVENANT, 50, revenant) + var/list/candidates = poll_candidates_for_mob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, ROLE_REVENANT, 5 SECONDS, revenant) if(!LAZYLEN(candidates)) qdel(revenant) message_admins("No candidates were found for the new revenant. Oh well!") diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index 1a68cbab5ba..24f8ef0d5a4 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -126,7 +126,7 @@ if(!current_wizard) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, ROLE_WIZARD, 50, current_wizard, POLL_IGNORE_ACADEMY_WIZARD) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, ROLE_WIZARD, 5 SECONDS, current_wizard, POLL_IGNORE_ACADEMY_WIZARD) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) @@ -325,7 +325,7 @@ A.setup_master(user) servant_mind.transfer_to(H) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, ROLE_WIZARD, 50, H) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, ROLE_WIZARD, 5 SECONDS, 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/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 5ac6e7b2cab..bb4a5068774 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -459,7 +459,7 @@ playsound(src, 'sound/machines/ping.ogg', 30, TRUE) RegisterSignal(magnification, COMSIG_SPECIES_LOSS, .proc/make_fall_off) polling = TRUE - var/list/candidates = pollCandidatesForMob("Do you want to play as a mind magnified monkey?", ROLE_SENTIENCE, ROLE_SENTIENCE, 50, magnification, POLL_IGNORE_SENTIENCE_POTION) + var/list/candidates = poll_candidates_for_mob("Do you want to play as a mind magnified monkey?", ROLE_SENTIENCE, ROLE_SENTIENCE, 5 SECONDS, magnification, POLL_IGNORE_SENTIENCE_POTION) polling = FALSE if(!magnification) return diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 6a596963fa1..c68492985d6 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -155,7 +155,7 @@ While using this makes the system rely on OnFire, it still gives options for tim addtimer(CALLBACK(src, .proc/spawn_elite), 30) return visible_message(span_boldwarning("Something within [src] stirs...")) - var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", ROLE_SENTIENCE, ROLE_SENTIENCE, 50, src, POLL_IGNORE_SENTIENCE_POTION) + var/list/candidates = poll_candidates_for_mob("Do you want to play as a lavaland elite?", ROLE_SENTIENCE, ROLE_SENTIENCE, 5 SECONDS, src, POLL_IGNORE_SENTIENCE_POTION) if(candidates.len) audible_message(span_boldwarning("The stirring sounds increase in volume!")) elitemind = pick(candidates) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index ecdf7743e1f..1e1f0ae2c73 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -407,7 +407,7 @@ /** * Offer control of the passed in mob to dead player * - * Automatic logging and uses pollCandidatesForMob, how convenient + * Automatic logging and uses poll_candidates_for_mob, how convenient */ /proc/offer_control(mob/M) to_chat(M, "Control of your mob has been offered to dead players.") @@ -423,7 +423,7 @@ var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/) if(A) poll_message = "[poll_message] Status: [A.name]." - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, FALSE, 100, M) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob(poll_message, ROLE_PAI, FALSE, 10 SECONDS, M) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 31572fc50f6..ee047c75173 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -165,7 +165,7 @@ to_chat(src, "You are job banned from cyborg! Appeal your job ban if you want to avoid this in the future!") ghostize(FALSE) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [src]?", "[src]", null, "Cyborg", 50, src) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [src]?", "Cyborg", null, 5 SECONDS, src) if(LAZYLEN(candidates)) var/mob/dead/observer/chosen_candidate = pick(candidates) message_admins("[key_name_admin(chosen_candidate)] has taken control of ([key_name_admin(src)]) to replace a jobbanned player.") diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 52d1528f59c..1296d8934bb 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -437,7 +437,7 @@ var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/) if(A) poll_message = "[poll_message] Status:[A.name]." - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, FALSE, 100, M) + var/list/mob/dead/observer/candidates = poll_candidates_for_mob(poll_message, ROLE_PAI, FALSE, 10 SECONDS, M) if(M.stat == DEAD)//boo. return if(LAZYLEN(candidates)) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 93fb4bc6cc4..50858b60e22 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -703,7 +703,7 @@ to_chat(user, span_notice("You offer [src] to [SM]...")) being_used = TRUE - var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]?", ROLE_SENTIENCE, ROLE_SENTIENCE, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm + var/list/candidates = poll_candidates_for_mob("Do you want to play as [SM.name]?", ROLE_SENTIENCE, ROLE_SENTIENCE, 5 SECONDS, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) SM.key = C.key