From faafdcfb5b4a942ad2c2a6a25537c7fc01d44cec Mon Sep 17 00:00:00 2001 From: Aronai Sieyes Date: Sat, 26 Jun 2021 17:37:22 -0400 Subject: [PATCH] Move some ghosty stuff to async --- code/_helpers/global_lists.dm | 1 + code/datums/ghost_query.dm | 100 ++++++++++++------ .../objects/items/devices/denecrotizer_vr.dm | 62 ++++++++--- code/modules/mob/dead/observer/observer.dm | 2 + 4 files changed, 115 insertions(+), 50 deletions(-) diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index ca5a4e005f..a71515643b 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -8,6 +8,7 @@ var/global/list/silicon_mob_list = list() //List of all silicon mobs, includin var/global/list/ai_list = list() //List of all AIs, including clientless var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player +var/global/list/observer_mob_list = list() //List of all /mob/observer/dead, including clientless. var/global/list/listening_objects = list() //List of all objects which care about receiving messages (communicators, radios, etc) var/global/list/cleanbot_reserved_turfs = list() //List of all turfs currently targeted by some cleanbot diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm index 454b009271..d40ef7bab8 100644 --- a/code/datums/ghost_query.dm +++ b/code/datums/ghost_query.dm @@ -11,18 +11,13 @@ 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. +/// Begin the ghost asking /datum/ghost_query/proc/query() // First, ask all the ghosts who want to be asked. - for(var/mob/observer/dead/D in player_list) - if(!D.MayRespawn()) - continue // They can't respawn for whatever reason. - if(D.client) - if(be_special_flag && !(D.client.prefs.be_special & be_special_flag) ) - continue // They don't want to see the prompt. - for(var/ban in check_bans) - if(jobban_isbanned(D, ban)) - continue // They're banned from this role. - ask_question(D.client) + for(var/mob/observer/dead/D as anything in observer_mob_list) + if(evaluate_candidate(D)) + ask_question(D) + // Then wait awhile. while(!finished) sleep(1 SECOND) @@ -31,36 +26,73 @@ finished = TRUE // Prune the list after the wait, incase any candidates logged out. - for(var/mob/observer/dead/D in candidates) - if(!D.client || !D.key) - candidates.Remove(D) + for(var/mob/observer/dead/D as anything in candidates) + if(!evaluate_candidate(D)) + candidates -= D // Now we're done. finished = TRUE return candidates -/datum/ghost_query/proc/ask_question(var/client/C) - spawn(0) - if(!C) - return - window_flash(C) - if(query_sound) - SEND_SOUND(C, sound(query_sound)) - var/response = tgui_alert(C, question, "[role_name] request", list("Yes", "No", "Never for this round")) - if(response == "Yes") - response = tgui_alert(C, "Are you sure you want to play as a [role_name]?", "[role_name] request", list("Yes", "No")) // Protection from a misclick. - if(!C || !src) - return - if(response == "Yes") - if(finished || (cutoff_number && candidates.len >= cutoff_number) ) - to_chat(C, "Unfortunately, you were not fast enough, and there are no more available roles. Sorry.") - return - candidates.Add(C.mob) - if(cutoff_number && candidates.len >= cutoff_number) - finished = TRUE // Finish now if we're full. - else if(response == "Never for this round") +/// Test a candidate for allowance to join as this +/datum/ghost_query/proc/evaluate_candidate(mob/observer/dead/candidate) + if(!istype(candidate)) + return FALSE // Changed mobs or something who knows + if(!candidate.client) + return FALSE // No client to ask + if(!candidate.MayRespawn()) + return FALSE // They can't respawn for whatever reason. + if(be_special_flag && !(candidate.client.prefs.be_special & be_special_flag) ) + return FALSE // They don't want to see the prompt. + for(var/ban in check_bans) + if(jobban_isbanned(candidate, ban)) + return FALSE // They're banned from this role. + + return TRUE + +/// 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) + var/client/C = D.client + window_flash(C) + + if(query_sound) + SEND_SOUND(C, sound(query_sound)) + + tgui_alert_async(D, question, "[role_name] request", list("Yes", "No", "Never for this round"), CALLBACK(src, .proc/get_reply), wait_time SECONDS) + +/// Process an async alert response +/datum/ghost_query/proc/get_reply(response) + var/mob/observer/dead/D = usr + if(!D?.client) + return + + // Unhandled are "No" and "Nevermind" responses, which should just do nothing + + // This response is always fine, doesn't warrant retesting + switch(response) + if("Never for this round") if(be_special_flag) - C.prefs.be_special ^= be_special_flag + D.client.prefs.be_special ^= be_special_flag + to_chat(D, "You will not be prompted to join similar roles to [role_name] for the rest of this round. Note: If you save your character now, it will save this permanently.") + else + to_chat(D, "This type of ghost-joinable role doesn't have a role type flag associated with it, so I can't prevent future requests, sorry. Bug a dev!") + if("Yes") + if(!evaluate_candidate(D)) // Failed revalidation + to_chat(D, "Unfortunately, you no longer qualify for this role. Sorry.") + else if(finished) // Already finished candidate list + to_chat(D, "Unfortunately, you were not fast enough, and there are no more available roles. Sorry.") + else // Prompt a second time + tgui_alert_async(D, "Are you sure you want to play as a [role_name]?", "[role_name] request", list("I'm Sure", "Nevermind"), CALLBACK(src, .proc/get_reply), wait_time SECONDS) + + if("I'm Sure") + if(!evaluate_candidate(D)) // Failed revalidation + to_chat(D, "Unfortunately, you no longer qualify for this role. Sorry.") + else if(finished) // Already finished candidate list + to_chat(D, "Unfortunately, you were not fast enough, and there are no more available roles. Sorry.") + else // Accept their nomination + candidates.Add(D) + if(cutoff_number && candidates.len >= cutoff_number) + finished = TRUE // Finish now if we're full. // Normal things. /datum/ghost_query/promethean diff --git a/code/game/objects/items/devices/denecrotizer_vr.dm b/code/game/objects/items/devices/denecrotizer_vr.dm index 4c59cd7208..188dd80dbd 100644 --- a/code/game/objects/items/devices/denecrotizer_vr.dm +++ b/code/game/objects/items/devices/denecrotizer_vr.dm @@ -16,33 +16,63 @@ /mob/living/simple_mob/animal/giant_spider/carrier //or the ones who fart babies when they die ic_revivable = 0 -//WHEN GHOSTS ATTACK!!!!! +/// A ghost has clicked us /mob/living/simple_mob/attack_ghost(mob/observer/dead/user as mob) if(!ghostjoin) return ..() - var/reply = tgui_alert(usr, "Would you like to become [src]? It is bound to [revivedby].","Bind To",list("Yes","No")) - if(reply == "No") - return + if(!evaluate_ghost_join(user)) + return ..() - if(ckey) //FIRST ONE TO CLICK YES GETS IT!!!!!! Channel your inner youtube commenter. - to_chat(src, "Sorry, someone else has already inhabited [src].") - return - - log_and_message_admins("[key_name_admin(user)] joined [src] as a ghost [ADMIN_FLW(src)]") + tgui_alert_async(usr, "Would you like to become [src]? It is bound to [revivedby].", "Become Mob", list("Yes","No"), CALLBACK(src, .proc/reply_ghost_join), 20 SECONDS) + +/// A reply to an async alert request was received +/mob/living/simple_mob/proc/reply_ghost_join(response) + if(response != "Yes") + return // ok + + var/mob/observer/dead/D = usr + if(evaluate_ghost_join(D)) + ghost_join(D) + +/// Inject a ghost into this mob. Assumes you've done all sanity before this point. +/mob/living/simple_mob/proc/ghost_join(mob/observer/dead/D) + log_and_message_admins("[key_name_admin(D)] joined [src] as a ghost [ADMIN_FLW(src)]") active_ghost_pods -= src - if(user.mind) - user.mind.active = TRUE - user.mind.transfer_to(src) + + // Move the ghost in + if(D.mind) + D.mind.active = TRUE + D.mind.transfer_to(src) else - src.ckey = user.ckey - qdel(user) - ghostjoin = 0 + src.ckey = D.ckey + qdel(D) + + // Clean up the simplemob + ghostjoin = FALSE ghostjoin_icon() if(revivedby != "no one") to_chat(src, "Where once your life had been rough and scary, you have been assisted by [revivedby]. They seem to be the reason you are on your feet again... so perhaps you should help them out. Being as you were revived, you are allied with the station. Do not attack anyone unless they are threatening the one who revived you. And try to listen to the one who revived you within reason. Of course, you may do scenes as you like, but you must still respect preferences.") - visible_message("[src]'s eyes flicker with a curious intelligence.") + visible_message("[src]'s eyes flicker with a curious intelligence.", runemessage = "looks around") +/// Evaluate someone for being allowed to join as this mob from being a ghost +/mob/living/simple_mob/proc/evaluate_ghost_join(mob/observer/dead/D) + if(!istype(D) || !D.client) + stack_trace("A non-ghost mob was evaluated for joining into a simplemob...") + return FALSE + + // At this point we can at least send them messages as to why they can't join, since they are a mob with a client + if(!ghostjoin) + to_chat(D, "Sorry, [src] is no longer ghost-joinable.") + return FALSE + + if(ckey) + to_chat(D, "Sorry, someone else has already inhabited [src].") + return FALSE + + // Insert whatever ban checks you want here if we ever add simplemob bans + + return TRUE /obj/item/device/denecrotizer //Away map reward. FOR TRAINED NECROMANCERS ONLY. >:C name = "experimental denecrotizer" diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 8fac13480e..74b3e6dc0e 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -130,6 +130,7 @@ name = capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) real_name = name animate(src, pixel_y = 2, time = 10, loop = -1) + observer_mob_list += src ..() /mob/observer/dead/Topic(href, href_list) @@ -504,6 +505,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/mob/M = following M.following_mobs -= src stop_following() + observer_mob_list -= src return ..() /mob/Moved(atom/old_loc, direction, forced = FALSE)