diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index cdb08e6764..49f80fd19a 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -31,6 +31,9 @@ ////////////////////////////////////////////////////////////////// +/// Signal that gets sent when a ghost query is completed +#define COMSIG_GHOST_QUERY_COMPLETE "ghost_query_complete" + // /datum signals /// when a component is added to a datum: (/datum/component) #define COMSIG_COMPONENT_ADDED "component_added" diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm index a4d4e1d397..746ce487f0 100644 --- a/code/datums/ghost_query.dm +++ b/code/datums/ghost_query.dm @@ -11,6 +11,10 @@ var/wait_time = 60 SECONDS // How long to wait until returning the list of candidates. var/cutoff_number = 0 // If above 0, when candidates list reaches this number, further potential candidates are rejected. +/datum/ghost_query/Destroy(force) + candidates = null + . = ..() + /// Begin the ghost asking /datum/ghost_query/proc/query() // First, ask all the ghosts who want to be asked. @@ -19,20 +23,21 @@ ask_question(D) // Then wait awhile. - while(!finished) - sleep(1 SECOND) - wait_time -= 1 SECOND - if(wait_time <= 0) - finished = TRUE + if(wait_time) + our_timer(wait_time) + return + +/datum/ghost_query/proc/our_timer(var/current_wait_time) + if(current_wait_time) + addtimer(CALLBACK(src, PROC_REF(our_timer), FALSE), current_wait_time, TIMER_DELETE_ME) + else + for(var/mob/observer/dead/D as anything in candidates) + if(!evaluate_candidate(D)) + candidates -= D + finished = TRUE + SEND_SIGNAL(src, COMSIG_GHOST_QUERY_COMPLETE) - // Prune the list after the wait, incase any candidates logged out. - for(var/mob/observer/dead/D as anything in candidates) - if(!evaluate_candidate(D)) - candidates -= D - // Now we're done. - finished = TRUE - return candidates /// Test a candidate for allowance to join as this /datum/ghost_query/proc/evaluate_candidate(mob/observer/dead/candidate) @@ -52,10 +57,8 @@ /// Send async alerts and ask for responses. Expects you to have tested D for client and type already /datum/ghost_query/proc/ask_question(var/mob/observer/dead/D) - //VOREStation Add Start Check the ban status before we ask if(jobban_isbanned(D, JOB_GHOSTROLES)) return - //VOREStation Add End var/client/C = D.client window_flash(C) diff --git a/code/game/objects/items/antag_spawners.dm b/code/game/objects/items/antag_spawners.dm index 2881753189..63c56d0b5c 100644 --- a/code/game/objects/items/antag_spawners.dm +++ b/code/game/objects/items/antag_spawners.dm @@ -4,6 +4,7 @@ var/ghost_query_type = null var/searching = FALSE var/datum/effect/effect/system/spark_spread/sparks + var/datum/ghost_query/Q //This is used so we can unregister ourself. /obj/item/antag_spawner/New() ..() @@ -28,13 +29,18 @@ return // Already searching. searching = TRUE - var/datum/ghost_query/Q = new ghost_query_type() - var/list/winner = Q.query() - if(winner.len) - var/mob/observer/dead/D = winner[1] + Q = new ghost_query_type() + RegisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE, PROC_REF(get_winner)) + Q.query() + +/obj/item/antag_spawner/proc/get_winner() + if(Q && Q.candidates.len) + var/mob/observer/dead/D = Q.candidates[1] spawn_antag(D.client, get_turf(src)) else reset_search() + UnregisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE) + qdel_null(Q) //get rid of the query return /obj/item/antag_spawner/proc/reset_search() diff --git a/code/game/objects/structures/ghost_pods/ghost_pods.dm b/code/game/objects/structures/ghost_pods/ghost_pods.dm index ca991da95e..3ac4fd7f74 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods.dm @@ -8,7 +8,10 @@ var/used = FALSE var/busy = FALSE // Don't spam ghosts by spamclicking. var/needscharger //For drone pods that want their pod to turn into a charger. + var/datum/ghost_query/Q //This is used so we can unregister ourself. unacidable = TRUE + var/delay_to_self_open = 0 // How long to wait for first attempt. Note that the timer by default starts when the pod is created. + var/delay_to_try_again = 0 // How long to wait if first attempt fails. Set to 0 to never try again. // Call this to get a ghost volunteer. /obj/structure/ghost_pod/proc/trigger(var/alert, var/adminalert) @@ -22,19 +25,27 @@ if(adminalert) log_and_message_admins(adminalert) busy = TRUE - var/datum/ghost_query/Q = new ghost_query_type() - var/list/winner = Q.query() + Q = new ghost_query_type() + RegisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE, PROC_REF(get_winner)) + Q.query() + +/obj/structure/ghost_pod/proc/get_winner() busy = FALSE - if(winner.len) - var/mob/observer/dead/D = winner[1] + var/deletion_candidate = FALSE + if(Q && Q.candidates.len) //Q should NEVER get deleted but...whatever, sanity. + var/mob/observer/dead/D = Q.candidates[1] create_occupant(D) icon_state = icon_state_opened if(needscharger) new /obj/machinery/recharge_station/ghost_pod_recharger(src.loc) - qdel(src) - return TRUE + deletion_candidate = TRUE else - return FALSE + if(delay_to_try_again) + addtimer(CALLBACK(src, PROC_REF(trigger)), delay_to_try_again) + UnregisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE) + qdel_null(Q) //get rid of the query + if(deletion_candidate) + qdel(src) // Override this to create whatever mob you need. Be sure to call ..() if you don't want it to make infinite mobs. /obj/structure/ghost_pod/proc/create_occupant(var/mob/M) @@ -68,8 +79,8 @@ // This type is triggered on a timer, as opposed to needing another player to 'open' the pod. Good for away missions. /obj/structure/ghost_pod/automatic - var/delay_to_self_open = 10 MINUTES // How long to wait for first attempt. Note that the timer by default starts when the pod is created. - var/delay_to_try_again = 20 MINUTES // How long to wait if first attempt fails. Set to 0 to never try again. + delay_to_self_open = 10 MINUTES + delay_to_try_again = 20 MINUTES /obj/structure/ghost_pod/automatic/Initialize(mapload) . = ..() diff --git a/code/modules/blob2/blobs/core.dm b/code/modules/blob2/blobs/core.dm index b2acf6e7fa..055569afa9 100644 --- a/code/modules/blob2/blobs/core.dm +++ b/code/modules/blob2/blobs/core.dm @@ -18,6 +18,7 @@ var/list/blob_cores = list() var/resource_delay = 0 var/point_rate = 2 var/ai_controlled = TRUE + var/datum/ghost_query/Q //This is used so we can unregister ourself. // Spawn this if you want a ghost to be able to play as the blob. /obj/structure/blob/core/player @@ -152,38 +153,8 @@ var/list/blob_cores = list() /obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay) if(overmind_get_delay > world.time && !override_delay) return - if(!ai_controlled) // Do we want a bona fide player blob? - overmind_get_delay = world.time + 15 SECONDS //if this fails, we'll try again in 15 seconds - if(overmind) - qdel(overmind) - - - var/client/C = null - if(!new_overmind) - var/datum/ghost_query/Q = new /datum/ghost_query/blob() - var/list/winner = Q.query() - if(winner.len) - var/mob/observer/dead/D = winner[1] - C = D.client - - else - C = new_overmind - - if(C) - if(!desired_blob_type && !isnull(difficulty_threshold)) - desired_blob_type = get_random_blob_type() - var/mob/observer/blob/B = new(loc, TRUE, 60, desired_blob_type) - B.key = C.key - B.blob_core = src - src.overmind = B - update_icon() - if(B.mind && !B.mind.special_role) - B.mind.special_role = "Blob Overmind" - return TRUE - return FALSE - - else // An AI opponent. + if(ai_controlled) if(!desired_blob_type && !isnull(difficulty_threshold)) desired_blob_type = get_random_blob_type() var/mob/observer/blob/B = new(loc, TRUE, 60, desired_blob_type) @@ -193,6 +164,47 @@ var/list/blob_cores = list() update_icon() return TRUE + overmind_get_delay = world.time + 15 SECONDS //if this fails, we'll try again in 15 seconds + + if(overmind) + qdel(overmind) + + + var/client/C = null + if(!new_overmind) + Q = new /datum/ghost_query/blob() + RegisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE, PROC_REF(get_winner)) + Q.query() + + else + C = new_overmind + overmind_creation(C) + +/obj/structure/blob/core/proc/get_winner() + if(Q && Q.candidates.len) //Q should NEVER get deleted but...whatever, sanity. + var/mob/observer/dead/D = Q.candidates[1] + var/client/C + C = D.client + overmind_creation(C) + UnregisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE) + qdel_null(Q) //get rid of the query + + + +/obj/structure/blob/core/proc/overmind_creation(var/client/new_overmind) + if(new_overmind) + if(!desired_blob_type && !isnull(difficulty_threshold)) + desired_blob_type = get_random_blob_type() + var/mob/observer/blob/B = new(loc, TRUE, 60, desired_blob_type) + B.key = new_overmind.key + B.blob_core = src + src.overmind = B + update_icon() + if(B.mind && !B.mind.special_role) + B.mind.special_role = "Blob Overmind" + return TRUE + return FALSE + /obj/structure/blob/core/proc/get_random_blob_type() if(!difficulty_threshold) return diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 83aca64cbd..37141ddb47 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -180,6 +180,7 @@ locked = 0 mecha = null//This does not appear to be used outside of reference in mecha.dm. var/ghost_query_type = null + var/datum/ghost_query/Q //This is used so we can unregister ourself. /obj/item/mmi/digital/New() src.brainmob = new(src) @@ -245,13 +246,18 @@ return searching = 1 - var/datum/ghost_query/Q = new ghost_query_type() - var/list/winner = Q.query() - if(winner.len) - var/mob/observer/dead/D = winner[1] + Q = new ghost_query_type() + RegisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE, PROC_REF(get_winner)) + Q.query() + +/obj/item/mmi/digital/proc/get_winner() + if(Q && Q.candidates.len) //Q should NEVER get deleted but...whatever, sanity. + var/mob/observer/dead/D = Q.candidates[1] transfer_personality(D) else reset_search() + UnregisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE) + qdel_null(Q) //get rid of the query /obj/item/mmi/digital/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. if(src.brainmob && src.brainmob.key) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm index 79a27c84a7..040a164665 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm @@ -42,6 +42,7 @@ var/has_reproduced = FALSE var/used_dominate // world.time when the dominate power was last used. + var/datum/ghost_query/Q // Used to unregister our signal /mob/living/simple_mob/animal/borer/roundstart roundstart = TRUE @@ -180,11 +181,16 @@ host = null /mob/living/simple_mob/animal/borer/proc/request_player() - var/datum/ghost_query/Q = new /datum/ghost_query/borer() - var/list/winner = Q.query() // This will sleep the proc for awhile. - if(winner.len) - var/mob/observer/dead/D = winner[1] + Q = new /datum/ghost_query/borer() + RegisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE, PROC_REF(get_winner)) + Q.query() // This will sleep the proc for awhile. + +/mob/living/simple_mob/animal/borer/proc/get_winner() + if(Q && Q.candidates.len) //Q should NEVER get deleted but...whatever, sanity. + var/mob/observer/dead/D = Q.candidates[1] transfer_personality(D) + UnregisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE) + qdel_null(Q) //get rid of the query /mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) if(!candidate || !candidate.mind)