From 64b75c40483ca49be60a2d6780d57742daa045a3 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Sat, 2 Sep 2023 12:48:44 -0700 Subject: [PATCH] better admin tooling and character profiles (#5931) --- code/datums/character_profile.dm | 5 +- code/modules/admin/admin_verbs.dm | 1 - .../modules/admin/verbs/admin_set_headshot.dm | 82 +++++++++++++------ code/modules/mob/living/living_defines.dm | 3 + code/modules/preferences/_preferences.dm | 48 ++++++++--- .../preference_setup/general/01_basic.dm | 2 + 6 files changed, 99 insertions(+), 42 deletions(-) diff --git a/code/datums/character_profile.dm b/code/datums/character_profile.dm index d51edf72d67..6a512863e34 100644 --- a/code/datums/character_profile.dm +++ b/code/datums/character_profile.dm @@ -7,7 +7,6 @@ GLOBAL_LIST_EMPTY(cached_previews) . = ..() host = WEAKREF(host_mob) - /datum/description_profile/Destroy(force, ...) . = ..() host = null @@ -21,8 +20,8 @@ GLOBAL_LIST_EMPTY(cached_previews) var/mob/living/carbon/human/H = host.resolve() data["oocnotes"] = H?.ooc_notes || "" - data["headshot_url"] = H?.client?.prefs?.headshot_url - data["fullref_url"] = H?.client?.prefs?.full_ref_url + data["headshot_url"] = H?.headshot_url + data["fullref_url"] = H?.fullref_url data["fullref_toggle"] = H?.client?.prefs?.full_ref_toggle data["directory_visible"] = H?.client?.prefs?.show_in_directory data["vore_tag"] = H?.client?.prefs?.directory_tag || "Unset" diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 0cd3fb8cd29..ec56a4e3b71 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -112,7 +112,6 @@ var/list/admin_verbs_admin = list( /client/proc/toggle_AI_interact, /client/proc/list_event_volunteers, /client/proc/set_headshot_for_user, - /client/proc/set_fullref_for_user ) var/list/admin_verbs_ban = list( diff --git a/code/modules/admin/verbs/admin_set_headshot.dm b/code/modules/admin/verbs/admin_set_headshot.dm index c622e51d463..992da06a059 100644 --- a/code/modules/admin/verbs/admin_set_headshot.dm +++ b/code/modules/admin/verbs/admin_set_headshot.dm @@ -1,35 +1,65 @@ /client/proc/set_headshot_for_user() set category = "Admin" - set name = "Set Headshot For User" + set name = "Set Headshot / Full Reference For User" if(!check_rights(R_ADMIN)) return - var/list/mob_list = GLOB.clients - var/client/selection = input(usr, "Select a player to set a headshot for.", "Headshot Selection") as anything in mob_list - var input_link = input(usr, "Enter the URL for the headshot image.", "Headshot Selection") as text - if(!selection || !input_link || !selection.prefs) - to_chat(usr, SPAN_BOLDWARNING("Your selection was either invalid, or the link was blank.")) + var/their_ckey = input(src, "Enter the player's ckey", "Reference Selection") as text|null + if(isnull(their_ckey)) return - selection.prefs.headshot_url = input_link - to_chat(usr, SPAN_NOTICE("[selection]'s headshot URL for [selection.prefs.real_name] has been set.")) - to_chat(selection, SPAN_BOLDNOTICE("Your headshot URL for your currently selected character, [selection.prefs.real_name], has been set by an admin.")) - SScharacters.queue_preferences_save(selection.prefs) - -/client/proc/set_fullref_for_user() - set category = "Admin" - set name = "Set Full Reference For User" - - if(!check_rights(R_ADMIN)) + var/datum/preferences/their_prefs = SScharacters.fetch_preferences_datum(their_ckey, TRUE) + if(isnull(their_prefs)) + to_chat(src, SPAN_WARNING("[their_ckey] does not exist.")) return - - var/list/mob_list = GLOB.clients - var/client/selection = input(usr, "Select a player to set a full reference for for.", "Full Reference Selection") as anything in mob_list - var input_link = input(usr, "Enter the URL for the Full Reference image.", "Full Reference Selection") as text - if(!selection || !input_link || !selection.prefs) - to_chat(usr, SPAN_BOLDWARNING("Your selection was either invalid, or the link was blank.")) + //! WARNING WARNING FUCKING WARNING DIRECT SAVEFILE MANIPULATIONS DO NOT TOUCH THIS UNLESS YOU KNOW WHAT YOU ARE DOING + // todo: /datum/character so we don't do this atrocious shit WHEN + if(isnull(their_prefs.path)) + to_chat(src, SPAN_DANGER("[their_ckey] doesn't have a valid path?? how??")) return - selection.prefs.full_ref_url = input_link - to_chat(usr, SPAN_NOTICE("[selection]'s Full Reference URL for [selection.prefs.real_name] has been set.")) - to_chat(selection, SPAN_BOLDNOTICE("Your Full Reference URL for your currently selected character, [selection.prefs.real_name], has been set by an admin.")) - SScharacters.queue_preferences_save(selection.prefs) + var/savefile/their_savefile = new(their_prefs.path) + var/list/loaded_characters = list() + for(var/i in 1 to config_legacy.character_slots) + their_savefile.cd = "/character[i]" + var/iter_name + their_savefile["real_name"] >> iter_name + if(isnull(iter_name)) + continue + var/orig = iter_name + if(!isnull(loaded_characters[iter_name])) + var/what_the_heck = 1 + do + iter_name = "[orig] (#[what_the_heck++])" + while(!isnull(loaded_characters[iter_name])) + if(i == their_prefs.default_slot) + iter_name += " (Current)" + loaded_characters[iter_name] = list(i, orig) + loaded_characters = tim_sort(loaded_characters, /proc/cmp_text_asc) + var/chosen_character = input(src, "Choose the character to edit", "Reference Selection") as null|anything in loaded_characters + if(isnull(chosen_character)) + return + var/chosen_slot = loaded_characters[chosen_character][1] + var/chosen_original_name = loaded_characters[chosen_character][2] + var/input_headshot = input(src, "Enter the URL for the headshot image. (Cancel to skip, empty string to remove)", "Reference Selection") as text|null + var/input_fullref = input(src, "Enter the URL for the full reference image. (Cancel to skip, empty string to remove)", "Reference Selection") as text|null + if(!isnull(input_headshot)) + their_savefile["Headshot_URL"] << input_headshot + if(their_prefs.default_slot == chosen_slot) + their_prefs.headshot_url = input_headshot + if(!isnull(input_fullref)) + their_savefile["Full_Ref_URL"] << input_fullref + if(their_prefs.default_slot == chosen_slot) + their_prefs.full_ref_url = input_fullref + log_and_message_admins("[key_name(src)] set [their_ckey]'s references for [chosen_original_name] (slot [chosen_slot]) to \ + [isnull(input_headshot)? "UNCHANGED" : (input_headshot || "EMPTY")] and [isnull(input_fullref)? "UNCHANGED" : (input_fullref || "EMPTY")]") + var/client/theyre_here = GLOB.directory[their_ckey] + if(!isnull(theyre_here)) + to_chat(theyre_here, SPAN_BOLDNOTICE("[chosen_character]'s image references have been edited by an admin.")) + var/mob/their_mob = theyre_here?.mob + if(!isnull(their_mob) && (their_mob.real_name == chosen_original_name) && isliving(their_mob)) + var/mob/living/casted = their_mob + if(!isnull(input_headshot)) + casted.headshot_url = input_headshot + if(!isnull(input_fullref)) + casted.fullref_url = input_fullref + casted.profile?.update_static_data() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index f1c276e6b80..e4a55f48a38 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -102,8 +102,11 @@ // TODO: execute iamcrystalclear for making this var var/last_blood_warn = -INFINITY + // todo: refactor this shit along with characters, aough var/ooc_notes = null var/datum/description_profile/profile + var/fullref_url + var/headshot_url var/obj/structure/mob_spawner/source_spawner = null //custom say verbs diff --git a/code/modules/preferences/_preferences.dm b/code/modules/preferences/_preferences.dm index b484fad163f..c6f13140fbc 100644 --- a/code/modules/preferences/_preferences.dm +++ b/code/modules/preferences/_preferences.dm @@ -2,6 +2,32 @@ GLOBAL_LIST_EMPTY(preferences_datums) +/** + * Fetches a prefs datum by ckey + * + * @params + * * ckey - their canonical key + * * only_if_exists - only load / create if it already exists + * + * @return the preferences datum if found, or null + * + * todo: /datum/character + */ +/datum/controller/subsystem/characters/proc/fetch_preferences_datum(ckey, only_if_exists = FALSE) + RETURN_TYPE(/datum/preferences) + ckey = ckey(ckey) + if(!isnull(GLOB.preferences_datums[ckey])) + return GLOB.preferences_datums[ckey] + var/datum/preferences/creating = new + creating.client_ckey = ckey + if(only_if_exists && !creating.load_path()) + qdel(creating) + return + GLOB.preferences_datums[ckey] = creating + if(initialized) + creating.Initialize() + return creating + /datum/preferences //! Intrinsics /// did we load yet? @@ -227,7 +253,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(istype(C)) client = C client_ckey = C.ckey - if(SScharacters.initialized) + if(client_ckey && SScharacters.initialized) Initialize() /datum/preferences/proc/Initialize() @@ -242,17 +268,15 @@ GLOBAL_LIST_EMPTY(preferences_datums) real_name = random_name(identifying_gender, real_species_name()) b_type = RANDOM_BLOOD_TYPE - if(client) - if(!IsGuestKey(client.key)) - if(load_path(client.ckey)) - if(load_preferences()) - if(load_character()) - load_skin() - sanitize_everything() - player_setup.sanitize_setup() - client.update_movement_keys() - initialized = TRUE - return + if(load_path(client_ckey)) + if(load_preferences()) + if(load_character()) + load_skin() + sanitize_everything() + player_setup.sanitize_setup() + client?.update_movement_keys() + initialized = TRUE + return key_bindings = deep_copy_list(GLOB.hotkey_keybinding_list_by_key) // give them default keybinds and update their movement keys client?.update_movement_keys(src) diff --git a/code/modules/preferences/preference_setup/general/01_basic.dm b/code/modules/preferences/preference_setup/general/01_basic.dm index 8c9a29f0b24..0a4a8046a0a 100644 --- a/code/modules/preferences/preference_setup/general/01_basic.dm +++ b/code/modules/preferences/preference_setup/general/01_basic.dm @@ -72,6 +72,8 @@ character.gender = pref.biological_gender character.identifying_gender = pref.identifying_gender character.age = pref.age + character.headshot_url = pref.headshot_url + character.fullref_url = pref.full_ref_url return TRUE /datum/category_item/player_setup_item/general/basic/content(datum/preferences/prefs, mob/user, data)