From d0f20dae013ecba394af59a1ce5fc08f6234cdff Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Fri, 4 Aug 2023 21:18:29 +0200 Subject: [PATCH] [MIRROR] Improved Imaginary Friend Smite [MDB IGNORE] (#22871) * Improved Imaginary Friend Smite (#77331) ## About The Pull Request By admin request: - The Imaginary Friend smite now allows you to select "Offer to ghosts" rather than selecting an observer mob manually. - If you select this option you can also select to bulk-accept multiple ghosts at a time, if you want to give people twelve imaginary friends without having to smite them 12 times. This necessitated changing the order of events a little bit so nwo you choose whether they have a randomised appearance before picking a candidate. ## Why It's Good For The Game Makes it easier to drop the entire end of round deadchat into one guy's brain with just a couple of clicks ## Changelog :cl: admin: The imaginary friend smite now allows selecting "offer to ghosts" instead of having to perform that poll yourself manually. When offering to ghosts you can also offer for several ghosts to volunteer at the same time. /:cl: * Improved Imaginary Friend Smite --------- Co-authored-by: Jacquerel --- code/datums/brain_damage/imaginary_friend.dm | 34 +++--- .../admin/smites/imaginary_friend_special.dm | 102 +++++++++++++----- 2 files changed, 96 insertions(+), 40 deletions(-) diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 81d250ef79f..03badb20ea5 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -92,30 +92,34 @@ * * imaginary_friend_owner - The living mob that owns the imaginary friend. * * appearance_from_prefs - If this is a valid set of prefs, the appearance of the imaginary friend is based on these prefs. */ -/mob/camera/imaginary_friend/Initialize(mapload, mob/living/imaginary_friend_owner, datum/preferences/appearance_from_prefs = null) +/mob/camera/imaginary_friend/Initialize(mapload) . = ..() - - owner = imaginary_friend_owner - - if(appearance_from_prefs) - INVOKE_ASYNC(src, PROC_REF(setup_friend_from_prefs), appearance_from_prefs) - else - INVOKE_ASYNC(src, PROC_REF(setup_friend)) - join = new join.Grant(src) hide = new hide.Grant(src) +/// Links this imaginary friend to the provided mob +/mob/camera/imaginary_friend/proc/attach_to_owner(mob/living/imaginary_friend_owner) + owner = imaginary_friend_owner if(!owner.imaginary_group) owner.imaginary_group = list(owner) owner.imaginary_group += src +/// Copies appearance from passed player prefs, or randomises them if none are provided +/mob/camera/imaginary_friend/proc/setup_appearance(datum/preferences/appearance_from_prefs = null) + if(appearance_from_prefs) + INVOKE_ASYNC(src, PROC_REF(setup_friend_from_prefs), appearance_from_prefs) + else + INVOKE_ASYNC(src, PROC_REF(setup_friend)) + +/// Randomise friend name and appearance /mob/camera/imaginary_friend/proc/setup_friend() var/gender = pick(MALE, FEMALE) real_name = random_unique_name(gender) name = real_name human_image = get_flat_human_icon(null, pick(SSjob.joinable_occupations)) + Show() /** * Sets up the imaginary friend's name and look using a set of datum preferences. @@ -146,13 +150,11 @@ if(istype(appearance_job, /datum/job/ai)) human_image = icon('icons/mob/silicon/ai.dmi', icon_state = resolve_ai_icon(appearance_from_prefs.read_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH) - return - - if(istype(appearance_job, /datum/job/cyborg)) + else if(istype(appearance_job, /datum/job/cyborg)) human_image = icon('icons/mob/silicon/robots.dmi', icon_state = "robot") - return - - human_image = get_flat_human_icon(null, appearance_job, appearance_from_prefs) + else + human_image = get_flat_human_icon(null, appearance_job, appearance_from_prefs) + Show() /// Returns all member clients of the imaginary_group /mob/camera/imaginary_friend/proc/group_clients() @@ -163,7 +165,7 @@ return group_clients /mob/camera/imaginary_friend/proc/Show() - if(!client) //nobody home + if(!client || !owner) //nobody home return var/list/friend_clients = group_clients() - src.client diff --git a/code/modules/admin/smites/imaginary_friend_special.dm b/code/modules/admin/smites/imaginary_friend_special.dm index b5784b3c527..e95f8c52cb1 100644 --- a/code/modules/admin/smites/imaginary_friend_special.dm +++ b/code/modules/admin/smites/imaginary_friend_special.dm @@ -1,3 +1,8 @@ +#define CHOICE_RANDOM_APPEARANCE "Random" +#define CHOICE_PREFS_APPEARANCE "Look-a-like" +#define CHOICE_POLL_GHOSTS "Offer to ghosts" +#define CHOICE_CANCEL "Cancel" + /** * Custom imaginary friend. * @@ -10,34 +15,67 @@ **/ /datum/smite/custom_imaginary_friend name = "Imaginary Friend (Special)" - var/client/friend_candidate_client + /// Who are we going to add to your head today? + var/list/friend_candidates + /// Do we randomise friend appearances or not? var/random_appearance /datum/smite/custom_imaginary_friend/configure(client/user) - friend_candidate_client = tgui_input_list(user, "Pick the player to put in control", "New Imaginary Friend", sort_list(GLOB.clients)) - if(isnull(friend_candidate_client)) + var/appearance_choice = tgui_alert(user, + "Do you want the imaginary friend(s) to share name and appearance with their currently selected character preferences?", + "Imaginary Friend Appearance?", + list(CHOICE_PREFS_APPEARANCE, CHOICE_RANDOM_APPEARANCE, CHOICE_CANCEL)) + if (isnull(appearance_choice) || appearance_choice == CHOICE_CANCEL) + return FALSE + random_appearance = appearance_choice == CHOICE_RANDOM_APPEARANCE + + var/picked_client = tgui_input_list(user, "Pick the player to put in control", "New Imaginary Friend", list(CHOICE_POLL_GHOSTS) + sort_list(GLOB.clients)) + if(isnull(picked_client)) return FALSE + if(picked_client == CHOICE_POLL_GHOSTS) + return poll_ghosts(user) + + var/client/friend_candidate_client = picked_client if(QDELETED(friend_candidate_client)) - to_chat(user, span_notice("Selected player no longer has a client, aborting.")) + to_chat(user, span_warning("Selected player no longer has a client, aborting.")) return FALSE if(isliving(friend_candidate_client.mob) && (tgui_alert(user, "This player already has a living mob ([friend_candidate_client.mob]). Do you still want to turn them into an Imaginary Friend?", "Remove player from mob?", list("Do it!", "Cancel")) != "Do it!")) return FALSE if(QDELETED(friend_candidate_client)) - to_chat(user, span_notice("Selected player no longer has a client, aborting.")) + to_chat(user, span_warning("Selected player no longer has a client, aborting.")) return FALSE - if(friend_candidate_client.prefs) - var/choice = tgui_alert(user, "Do you want the imaginary friend to look like and be named after [friend_candidate_client]'s current preferences ([friend_candidate_client.prefs.read_preference(/datum/preference/name/real_name)])?", "Imaginary Friend Appearance?", list("Look-a-like", "Random", "Cancel")) - if(choice != "Look-a-like" && choice != "Random") - return FALSE - random_appearance = choice == "Random" - else - if(tgui_alert(user, "The preferences for the friend could not be loaded, defaulting to random appearance. Is that okay?", "Preference error", list("Yes", "No")) != "Yes") - return FALSE - random_appearance = TRUE + friend_candidates = list(friend_candidate_client) + return TRUE + +/// Try to offer the role to ghosts +/datum/smite/custom_imaginary_friend/proc/poll_ghosts(client/user) + var/how_many = tgui_input_number(user, "How many imaginary friends should be added?", "Imaginary friend count", default = 1, min_value = 1) + if (isnull(how_many) || how_many < 1) + return FALSE + + var/list/volunteers = poll_ghost_candidates( + question = "Do you want to play as an imaginary friend?", + jobban_type = ROLE_PAI, + poll_time = 10 SECONDS, + ignore_category = POLL_IGNORE_IMAGINARYFRIEND, + ) + var/volunteer_count = length(volunteers) + if (volunteer_count == 0) + to_chat(user, span_warning("No candidates volunteered, aborting.")) + return FALSE + + shuffle_inplace(volunteers) + friend_candidates = list() + while (how_many > 0 && length(volunteers) > 0) + var/mob/dead/observer/lucky_ghost = pop(volunteers) + if (!lucky_ghost.client) + continue + how_many-- + friend_candidates += lucky_ghost.client return TRUE /datum/smite/custom_imaginary_friend/effect(client/user, mob/living/target) @@ -47,18 +85,34 @@ to_chat(user, span_warning("The target mob no longer exists, aborting.")) return - if(QDELETED(friend_candidate_client)) - to_chat(user, span_warning("Imaginary friend candidate no longer has a client, aborting.")) + if(!length(friend_candidates)) + to_chat(user, span_warning("No provided imaginary friend candidates, aborting.")) return - if(isliving(friend_candidate_client.mob)) - friend_candidate_client.mob.ghostize(can_reenter_corpse = TRUE) + var/list/final_clients = list() + for (var/client/client as anything in friend_candidates) + if (QDELETED(client)) + continue + final_clients += client - var/mob/camera/imaginary_friend/friend_mob + if(!length(final_clients)) + to_chat(user, span_warning("No provided imaginary friend candidates had clients, aborting.")) + return - if(random_appearance) - friend_mob = new /mob/camera/imaginary_friend(get_turf(target), target) - else - friend_mob = new /mob/camera/imaginary_friend(get_turf(target), target, friend_candidate_client.prefs) + for (var/client/friend_candidate_client as anything in final_clients) + var/mob/client_mob = friend_candidate_client.mob + if(isliving(client_mob)) + client_mob.ghostize() - friend_mob.key = friend_candidate_client.key + var/mob/camera/imaginary_friend/friend_mob = client_mob.change_mob_type( + new_type = /mob/camera/imaginary_friend, + location = get_turf(client_mob), + delete_old_mob = TRUE, + ) + friend_mob.attach_to_owner(target) + friend_mob.setup_appearance(random_appearance ? null : friend_candidate_client.prefs) + +#undef CHOICE_RANDOM_APPEARANCE +#undef CHOICE_PREFS_APPEARANCE +#undef CHOICE_POLL_GHOSTS +#undef CHOICE_CANCEL