diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index fc230632bdd..48fabd701f4 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -45,15 +45,26 @@
/datum/brain_trauma/special/imaginary_friend/proc/make_friend()
friend = new(get_turf(owner), owner)
+/// Tries an orbit poll for the imaginary friend
/datum/brain_trauma/special/imaginary_friend/proc/get_ghost()
- set waitfor = FALSE
- 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
- friend_initialized = TRUE
- else
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(add_friend))
+ owner.AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_IMAGINARYFRIEND, \
+ job_bans = ROLE_PAI, \
+ title = "[owner.real_name]'s imaginary friend", \
+ to_call = to_call, \
+ )
+
+/// Yay more friends!
+/datum/brain_trauma/special/imaginary_friend/proc/add_friend(mob/dead/observer/ghost)
+ if(isnull(ghost))
qdel(src)
+ return
+
+ friend.key = ghost.key
+ friend_initialized = TRUE
+ friend.log_message("became [key_name(owner)]'s split personality.", LOG_GAME)
+ message_admins("[ADMIN_LOOKUPFLW(friend)] became [ADMIN_LOOKUPFLW(owner)]'s split personality.")
/mob/camera/imaginary_friend
name = "imaginary friend"
diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm
index 05b4343a01c..9176884b816 100644
--- a/code/datums/brain_damage/split_personality.dm
+++ b/code/datums/brain_damage/split_personality.dm
@@ -32,17 +32,26 @@
var/datum/action/cooldown/spell/personality_commune/owner_spell = new(src)
owner_spell.Grant(owner_backseat)
-
+/// Attempts to get a ghost to play the personality
/datum/brain_trauma/severe/split_personality/proc/get_ghost()
- set waitfor = FALSE
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [owner.real_name]'s [poll_role]?", 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
- stranger_backseat.log_message("became [key_name(owner)]'s split personality.", LOG_GAME)
- message_admins("[ADMIN_LOOKUPFLW(stranger_backseat)] became [ADMIN_LOOKUPFLW(owner)]'s split personality.")
- else
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(schism))
+ owner.AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_SPLITPERSONALITY, \
+ job_bans = ROLE_PAI, \
+ title = "[owner.real_name]'s [poll_role]", \
+ to_call = to_call, \
+ )
+
+/// Ghost poll has concluded
+/datum/brain_trauma/severe/split_personality/proc/schism(mob/dead/observer/ghost)
+ if(isnull(ghost))
qdel(src)
+ return
+
+ stranger_backseat.key = ghost.key
+ stranger_backseat.log_message("became [key_name(owner)]'s split personality.", LOG_GAME)
+ message_admins("[ADMIN_LOOKUPFLW(stranger_backseat)] became [ADMIN_LOOKUPFLW(owner)]'s split personality.")
+
/datum/brain_trauma/severe/split_personality/on_life(seconds_per_tick, times_fired)
if(owner.stat == DEAD)
diff --git a/code/datums/components/orbit_poll.dm b/code/datums/components/orbit_poll.dm
new file mode 100644
index 00000000000..d9563aaab95
--- /dev/null
+++ b/code/datums/components/orbit_poll.dm
@@ -0,0 +1,108 @@
+/**
+ * A replacement for the standard poll_ghost_candidate.
+ * Use this to subtly ask players to join - it picks from orbiters.
+ * Please use named arguments for this.
+ *
+ * @params ignore_key - Required so it doesn't spam
+ * @params job_bans - You can insert a list or single items here.
+ * @params cb - Invokes this proc and appends the poll winner as the last argument, mob/dead/observer/ghost
+ * @params title - Optional. Useful if the role name does not match the parent.
+ *
+ * @usage
+ * ```
+ * var/datum/callback/cb = CALLBACK(src, PROC_REF(do_stuff), arg1, arg2)
+ * AddComponent(/datum/component/orbit_poll, \
+ * ignore_key = POLL_IGNORE_EXAMPLE, \
+ * job_bans = ROLE_EXAMPLE or list(ROLE_EXAMPLE, ROLE_EXAMPLE2), \
+ * title = "Use this if you want something other than the parent name", \
+ * to_call = cb, \
+ * )
+ */
+/datum/component/orbit_poll
+ /// Prevent players with this ban from being selected
+ var/list/job_bans = list()
+ /// Title of the role to announce after it's done
+ var/title
+ /// Proc to invoke whenever the poll is complete
+ var/datum/callback/to_call
+
+/datum/component/orbit_poll/Initialize( \
+ ignore_key, \
+ list/job_bans, \
+ datum/callback/to_call, \
+ title, \
+ header = "Ghost Poll", \
+ custom_message, \
+ timeout = 20 SECONDS \
+)
+ . = ..()
+ if (!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ var/atom/owner = parent
+
+ src.job_bans |= job_bans
+ src.title = title || owner.name
+ src.to_call = to_call
+
+ var/message = custom_message || "[capitalize(src.title)] is looking for volunteers"
+
+ notify_ghosts("[message]. An orbiter will be chosen in [DisplayTimeText(timeout)].\n", \
+ action = NOTIFY_ORBIT, \
+ enter_link = "(Ignore)", \
+ flashwindow = FALSE, \
+ header = "Volunteers requested", \
+ ignore_key = ignore_key, \
+ source = parent \
+ )
+
+ addtimer(CALLBACK(src, PROC_REF(end_poll)), timeout, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE|TIMER_DELETE_ME)
+
+/datum/component/orbit_poll/Topic(href, list/href_list)
+ if(!href_list["ignore"])
+ return
+
+ var/mob/user = usr
+
+ var/ignore_key = href_list["ignore"]
+ if(tgui_alert(user, "Ignore further [title] alerts?", "Ignore Alert", list("Yes", "No"), 20 SECONDS, TRUE) != "Yes")
+ return
+
+ GLOB.poll_ignore[ignore_key] |= user.ckey
+
+/// Concludes the poll, picking one of the orbiters
+/datum/component/orbit_poll/proc/end_poll()
+ if(QDELETED(parent))
+ return
+
+ var/list/candidates = list()
+ var/atom/owner = parent
+
+ var/datum/component/orbiter/orbiter_comp = owner.GetComponent(/datum/component/orbiter)
+ if(isnull(orbiter_comp))
+ phone_home()
+ return
+
+ for(var/mob/dead/observer/ghost as anything in orbiter_comp.orbiter_list)
+ if(QDELETED(ghost) || isnull(ghost.client))
+ continue
+ if(is_banned_from(ghost.ckey, job_bans))
+ continue
+
+ candidates += ghost
+
+ if(!length(candidates))
+ phone_home()
+ return
+
+ var/mob/dead/observer/chosen = pick(candidates)
+
+ if(chosen)
+ deadchat_broadcast("[key_name(chosen, include_name = FALSE)] was selected for the role ([title]).", "Ghost Poll: ", parent)
+
+ phone_home(chosen)
+
+/// Make sure to call your parents my dude
+/datum/component/orbit_poll/proc/phone_home(mob/dead/observer/chosen)
+ to_call.Invoke(chosen)
+ qdel(src)
diff --git a/code/datums/components/spirit_holding.dm b/code/datums/components/spirit_holding.dm
index a4d0e029137..82c37e8f26b 100644
--- a/code/datums/components/spirit_holding.dm
+++ b/code/datums/components/spirit_holding.dm
@@ -57,21 +57,30 @@
attempting_awakening = TRUE
to_chat(awakener, span_notice("You attempt to wake the spirit of [parent]..."))
- var/mob/dead/observer/candidates = poll_ghost_candidates("Do you want to play as the spirit of [awakener.real_name]'s blade?", ROLE_PAI, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE)
- if(!LAZYLEN(candidates))
- to_chat(awakener, span_warning("[parent] is dormant. Maybe you can try again later."))
- attempting_awakening = FALSE
- return
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(affix_spirit), awakener)
+ parent.AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_POSSESSED_BLADE, \
+ job_bans = ROLE_PAI, \
+ to_call = to_call, \
+ title = "Spirit of [awakener.real_name]'s blade", \
+ )
//Immediately unregister to prevent making a new spirit
UnregisterSignal(parent, COMSIG_ITEM_ATTACK_SELF)
- var/mob/dead/observer/chosen_spirit = pick(candidates)
+/// On conclusion of the ghost poll
+/datum/component/spirit_holding/proc/affix_spirit(mob/awakener, mob/dead/observer/ghost)
+ if(isnull(ghost))
+ to_chat(awakener, span_warning("[parent] is dormant. Maybe you can try again later."))
+ attempting_awakening = FALSE
+ return
+
if(QDELETED(parent)) //if the thing that we're conjuring a spirit in has been destroyed, don't create a spirit
- to_chat(chosen_spirit, span_userdanger("The new vessel for your spirit has been destroyed! You remain an unbound ghost."))
+ to_chat(ghost, span_userdanger("The new vessel for your spirit has been destroyed! You remain an unbound ghost."))
return
+
bound_spirit = new(parent)
- bound_spirit.ckey = chosen_spirit.ckey
+ bound_spirit.ckey = ghost.ckey
bound_spirit.fully_replace_character_name(null, "The spirit of [parent]")
bound_spirit.status_flags |= GODMODE
bound_spirit.copy_languages(awakener, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the awakener.
diff --git a/code/game/objects/items/devices/aicard_evil.dm b/code/game/objects/items/devices/aicard_evil.dm
index 1a5fce6897a..f91150bb086 100644
--- a/code/game/objects/items/devices/aicard_evil.dm
+++ b/code/game/objects/items/devices/aicard_evil.dm
@@ -28,26 +28,29 @@
finding_candidate = FALSE
return TRUE
+/// Sets up the ghost poll
/obj/item/aicard/syndie/loaded/proc/procure_ai(mob/user)
var/datum/antagonist/nukeop/op_datum = user.mind?.has_antag_datum(/datum/antagonist/nukeop,TRUE)
if(isnull(op_datum))
balloon_alert(user, "invalid access!")
return
- var/list/nuke_candidates = poll_ghost_candidates(
- question = "Do you want to play as a nuclear operative MODsuit AI?",
- jobban_type = ROLE_OPERATIVE,
- be_special_flag = ROLE_OPERATIVE_MIDROUND,
- poll_time = 15 SECONDS,
- ignore_category = POLL_IGNORE_SYNDICATE,
+
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user, op_datum)
+ AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_SYNDICATE, \
+ job_bans = ROLE_OPERATIVE, \
+ to_call = to_call, \
+ title = "Nuclear Operative Modsuit AI" \
)
- if(QDELETED(src))
- return
- if(!LAZYLEN(nuke_candidates))
+
+/// Poll has concluded with a ghost, create the AI
+/obj/item/aicard/syndie/loaded/proc/on_poll_concluded(mob/user, datum/antagonist/nukeop/op_datum, mob/dead/observer/ghost)
+ if(isnull(ghost))
to_chat(user, span_warning("Unable to connect to S.E.L.F. dispatch. Please wait and try again later or use the intelliCard on your uplink to get your points refunded."))
return
+
// pick ghost, create AI and transfer
- var/mob/dead/observer/ghos = pick(nuke_candidates)
- var/mob/living/silicon/ai/weak_syndie/new_ai = new /mob/living/silicon/ai/weak_syndie(get_turf(src), new /datum/ai_laws/syndicate_override, ghos)
+ var/mob/living/silicon/ai/weak_syndie/new_ai = new /mob/living/silicon/ai/weak_syndie(get_turf(src), new /datum/ai_laws/syndicate_override, ghost)
// create and apply syndie datum
var/datum/antagonist/nukeop/nuke_datum = new()
nuke_datum.send_to_spawnpoint = FALSE
diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm
index 04054f6df85..b35308d092f 100644
--- a/code/modules/antagonists/blob/powers.dm
+++ b/code/modules/antagonists/blob/powers.dm
@@ -189,14 +189,22 @@
to_chat(src, span_notice("You attempt to produce a blobbernaut."))
pick_blobbernaut_candidate(factory)
-/** Polls ghosts to get a blobbernaut candidate. */
+/// Polls ghosts to get a blobbernaut candidate.
/mob/camera/blob/proc/pick_blobbernaut_candidate(obj/structure/blob/special/factory/factory)
- if(!factory)
+ if(isnull(factory))
return
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, ROLE_BLOB, 50)
-
- if(!length(candidates))
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), factory)
+ factory.AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_BLOB, \
+ job_bans = ROLE_BLOB, \
+ to_call = to_call, \
+ title = "Blobbernaut", \
+ )
+
+/// Called when the ghost poll concludes
+/mob/camera/blob/proc/on_poll_concluded(obj/structure/blob/special/factory/factory, mob/dead/observer/ghost)
+ if(isnull(ghost))
to_chat(src, span_warning("You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later."))
add_points(BLOBMOB_BLOBBERNAUT_RESOURCE_COST)
factory.assign_blobbernaut(null)
@@ -205,8 +213,7 @@
var/mob/living/basic/blob_minion/blobbernaut/minion/blobber = new(get_turf(factory))
assume_direct_control(blobber)
factory.assign_blobbernaut(blobber)
- var/mob/dead/observer/player = pick(candidates)
- blobber.assign_key(player.key, blobstrain)
+ blobber.assign_key(ghost.key, blobstrain)
RegisterSignal(blobber, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(on_blobbernaut_attacked))
/// When one of our boys attacked something, we sometimes want to perform extra effects
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index f7be579ad1a..80dd5a1b2d9 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -323,7 +323,15 @@
return TRUE
to_chat(user, "[span_userdanger("Capture failed!")]: The soul has already fled its mortal frame. You attempt to bring it back...")
- INVOKE_ASYNC(src, PROC_REF(get_ghost_to_replace_shade), victim, user)
+
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user, victim)
+ AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_SHADE, \
+ job_bans = ROLE_CULTIST, \
+ to_call = to_call, \
+ title = "A shade" \
+ )
+
return TRUE //it'll probably get someone ;)
///captures a shade that was previously released from a soulstone.
@@ -435,33 +443,19 @@
shade_datum = shade.mind.add_antag_datum(/datum/antagonist/shade_minion)
shade_datum.update_master(user.real_name)
-/**
- * Gets a ghost from dead chat to replace a missing player when a shade is created.
- *
- * Gets ran if a soulstone is used on a body that has no client to take over the shade.
- *
- * victim - the body that's being shaded
- * user - the mob shading the body
- *
- * Returns FALSE if no ghosts are available or the replacement fails.
- * Returns TRUE otherwise.
- */
-/obj/item/soulstone/proc/get_ghost_to_replace_shade(mob/living/carbon/victim, mob/user)
- var/mob/dead/observer/chosen_ghost
- var/list/consenting_candidates = poll_ghost_candidates("Would you like to play as a Shade?", "Cultist", ROLE_CULTIST, 5 SECONDS, POLL_IGNORE_SHADE)
- if(length(consenting_candidates))
- chosen_ghost = pick(consenting_candidates)
-
- if(!victim || user.incapacitated() || !user.is_holding(src) || !user.CanReach(victim, src))
+/// Called when a ghost is chosen to become a shade.
+/obj/item/soulstone/proc/on_poll_concluded(mob/living/master, mob/living/victim, mob/dead/observer/ghost)
+ if(isnull(victim) || master.incapacitated() || !master.is_holding(src) || !master.CanReach(victim, src))
return FALSE
- if(!chosen_ghost || !chosen_ghost.client)
- to_chat(user, span_danger("There were no spirits willing to become a shade."))
+ if(isnull(ghost?.client))
+ to_chat(master, span_danger("There were no spirits willing to become a shade."))
return FALSE
- if(contents.len) //If they used the soulstone on someone else in the meantime
+ if(length(contents)) //If they used the soulstone on someone else in the meantime
return FALSE
- to_chat(user, "[span_info("Capture successful!:")] A spirit has entered [src], \
+ to_chat(master, "[span_info("Capture successful!:")] A spirit has entered [src], \
taking upon the identity of [victim].")
- init_shade(victim, user, shade_controller = chosen_ghost)
+ init_shade(victim, master, shade_controller = ghost)
+
return TRUE
/proc/make_new_construct_from_class(construct_class, theme, mob/target, mob/creator, cultoverride, loc_override)
diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm
index bdcf089e606..554002fed54 100644
--- a/code/modules/mining/lavaland/megafauna_loot.dm
+++ b/code/modules/mining/lavaland/megafauna_loot.dm
@@ -425,19 +425,31 @@
using = TRUE
balloon_alert(user, "you hold the scythe up...")
ADD_TRAIT(src, TRAIT_NODROP, type)
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you want to play as [user.real_name]'s soulscythe?", ROLE_PAI, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE)
- if(LAZYLEN(candidates))
- var/mob/dead/observer/picked_ghost = pick(candidates)
- soul.ckey = picked_ghost.ckey
- soul.copy_languages(user, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the user.
- soul.faction = list("[REF(user)]")
- balloon_alert(user, "the scythe glows up")
- add_overlay("soulscythe_gem")
- density = TRUE
- if(!ismob(loc))
- reset_spin()
- else
- balloon_alert(user, "the scythe is dormant!")
+
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user)
+ AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_POSSESSED_BLADE, \
+ job_bans = ROLE_PAI, \
+ to_call = to_call, \
+ )
+
+/// Ghost poll has concluded and a candidate has been chosen.
+/obj/item/soulscythe/proc/on_poll_concluded(mob/living/master, mob/dead/observer/ghost)
+ if(isnull(ghost))
+ balloon_alert(master, "the scythe is dormant!")
+ REMOVE_TRAIT(src, TRAIT_NODROP, type)
+ using = FALSE
+ return
+
+ soul.ckey = ghost.ckey
+ soul.copy_languages(master, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the master.
+ soul.faction = list("[REF(master)]")
+ balloon_alert(master, "the scythe glows")
+ add_overlay("soulscythe_gem")
+ density = TRUE
+ if(!ismob(loc))
+ reset_spin()
+
REMOVE_TRAIT(src, TRAIT_NODROP, type)
using = FALSE
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 3f3809c89b2..19434d888a1 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -84,26 +84,33 @@
return
attempt_grow()
-///Attempt to burst an alien outside of the host, getting a ghost to play as the xeno.
+/// Attempt to burst an alien outside of the host, getting a ghost to play as the xeno.
/obj/item/organ/internal/body_egg/alien_embryo/proc/attempt_grow(gib_on_success = TRUE)
- if(!owner || bursting)
+ if(QDELETED(owner) || bursting)
return
bursting = TRUE
- var/list/candidates = poll_ghost_candidates("Do you want to play as an alien larva that will burst out of [owner.real_name]?", ROLE_ALIEN, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA)
-
- if(QDELETED(src) || QDELETED(owner))
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), gib_on_success)
+ owner.AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_ALIEN_LARVA, \
+ job_bans = ROLE_ALIEN, \
+ to_call = to_call, \
+ custom_message = "An alien is bursting out of [owner.real_name]", \
+ title = "alien larva" \
+ )
+
+/// Poll has concluded with a suitor
+/obj/item/organ/internal/body_egg/alien_embryo/proc/on_poll_concluded(gib_on_success, mob/dead/observer/ghost)
+ if(QDELETED(owner))
return
- if(!candidates.len || !owner)
+ if(isnull(ghost))
bursting = FALSE
stage = 5 // If no ghosts sign up for the Larva, let's regress our growth by one minute, we will try again!
addtimer(CALLBACK(src, PROC_REF(advance_embryo_stage)), growth_time)
return
- var/mob/dead/observer/ghost = pick(candidates)
-
var/mutable_appearance/overlay = mutable_appearance('icons/mob/nonhuman-player/alien.dmi', "burst_lie")
owner.add_overlay(overlay)
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index b3945fe60c4..2bbefb64ec3 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -705,19 +705,28 @@
balloon_alert(user, "offering...")
being_used = TRUE
- var/list/candidates = poll_candidates_for_mob("Do you want to play as [dumb_mob.name]?", ROLE_SENTIENCE, ROLE_SENTIENCE, 5 SECONDS, dumb_mob, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm
- if(!LAZYLEN(candidates))
+ var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user, dumb_mob)
+ dumb_mob.AddComponent(/datum/component/orbit_poll, \
+ ignore_key = POLL_IGNORE_SENTIENCE_POTION, \
+ job_bans = ROLE_SENTIENCE, \
+ to_call = to_call, \
+ )
+
+/// Assign the chosen ghost to the mob
+/obj/item/slimepotion/slime/sentience/proc/on_poll_concluded(mob/user, mob/living/dumb_mob, mob/dead/observer/ghost)
+ if(isnull(ghost))
balloon_alert(user, "try again later!")
being_used = FALSE
- return ..()
+ return
- var/mob/dead/observer/C = pick(candidates)
- dumb_mob.key = C.key
+ dumb_mob.key = ghost.key
dumb_mob.mind.enslave_mind_to_creator(user)
SEND_SIGNAL(dumb_mob, COMSIG_SIMPLEMOB_SENTIENCEPOTION, user)
+
if(isanimal(dumb_mob))
var/mob/living/simple_animal/smart_animal = dumb_mob
smart_animal.sentience_act()
+
dumb_mob.mind.add_antag_datum(/datum/antagonist/sentient_creature)
balloon_alert(user, "success")
after_success(user, dumb_mob)
diff --git a/tgstation.dme b/tgstation.dme
index 2034e030059..6dc3ffb2b2a 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1155,6 +1155,7 @@
#include "code\datums\components\omen.dm"
#include "code\datums\components\on_hit_effect.dm"
#include "code\datums\components\onwear_mood.dm"
+#include "code\datums\components\orbit_poll.dm"
#include "code\datums\components\orbiter.dm"
#include "code\datums\components\overlay_lighting.dm"
#include "code\datums\components\palette.dm"