diff --git a/code/__defines/preferences.dm b/code/__defines/preferences.dm index 5e03f3217c..a9a45c079b 100644 --- a/code/__defines/preferences.dm +++ b/code/__defines/preferences.dm @@ -59,3 +59,8 @@ /// These preferences will not be rendered on the preferences page, and are practically invisible unless specifically rendered. Used for quirks, currently. #define PREFERENCE_CATEGORY_MANUALLY_RENDERED "manually_rendered_features" + +/// Emote switch preferences +#define EMOTE_SOUND_NO_FREQ "Default" +#define EMOTE_SOUND_VOICE_FREQ "Voice Frequency" +#define EMOTE_SOUND_VOICE_LIST "Voice Sound" diff --git a/code/game/antagonist/outsider/deathsquad.dm b/code/game/antagonist/outsider/deathsquad.dm index 54ab78dd36..2ea908d23c 100644 --- a/code/game/antagonist/outsider/deathsquad.dm +++ b/code/game/antagonist/outsider/deathsquad.dm @@ -70,9 +70,6 @@ var/datum/antagonist/deathsquad/deathsquad var/syndicate_commando_name = pick(last_names) - var/datum/preferences/A = new() //Randomize appearance for the commando. - A.randomize_appearance_and_body_for(player.current) - player.name = "[syndicate_commando_rank] [syndicate_commando_name]" player.current.name = player.name player.current.real_name = player.current.name diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index edb1785fdb..90c9237e06 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -79,7 +79,6 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait) var/species = SPECIES_HUMAN var/list/body_markings = list() var/list/body_markings_genetic = list() - var/list/body_descriptors = null var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance. // Make a copy of this strand. @@ -191,12 +190,10 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait) src.offset_override = character.offset_override src.synth_markings = character.synth_markings src.custom_speech_bubble = character.custom_speech_bubble - /* Currently not implemented on virgo src.species_sounds = character.species.species_sounds src.gender_specific_species_sounds = character.species.gender_specific_species_sounds src.species_sounds_male = character.species.species_sounds_male src.species_sounds_female = character.species.species_sounds_female - */ src.grad_style = character.grad_style src.r_grad = character.r_grad src.g_grad = character.g_grad diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index d689d4e962..53a52cd020 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -284,12 +284,10 @@ H.dna.blood_color = dna.blood_color H.species.blood_reagents = H.dna.blood_reagents H.species.blood_color = H.dna.blood_color - /*//TODO: Get these to work as well. H.species.species_sounds = dna.species_sounds H.species.gender_specific_species_sounds = dna.gender_specific_species_sounds H.species.species_sounds_male = dna.species_sounds_male H.species.species_sounds_female = dna.species_sounds_female - *///TODO End H.force_update_organs() //VOREStation Add - Gotta do this too H.force_update_limbs() diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index 3485931df0..4ff878e536 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -18,7 +18,6 @@ var/languages=null var/list/flavor=null var/gender = null - var/list/body_descriptors = null // Guess we'll keep null. var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance. /datum/dna2/record/proc/GetData() @@ -48,8 +47,6 @@ newrecord.implant = implant newrecord.flavor = flavor newrecord.gender = gender - if(body_descriptors) - newrecord.body_descriptors = body_descriptors.Copy() newrecord.genetic_modifiers = genetic_modifiers.Copy() return newrecord @@ -617,7 +614,6 @@ var/mob/living/carbon/human/H = WC databuf.mydna.dna.real_name = H.dna.real_name databuf.mydna.gender = H.gender - databuf.mydna.body_descriptors = H.descriptors buffers[bufferId] = databuf return TRUE if("clear") diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 699ad5ade5..5b37221b49 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -201,17 +201,6 @@ return ideal_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species //return (brain_type && LAZYACCESS(ideal_age_by_species, brain_type)) || LAZYACCESS(ideal_age_by_species, brain_type) || ideal_character_age //VOREStation Removal -/datum/job/proc/is_species_banned(species_name, brain_type) - return FALSE // VOREStation Edit - Any species can be any job. - /* VOREStation Removal - if(banned_job_species == null) - return - if(species_name in banned_job_species) - return TRUE - if(brain_type in banned_job_species) - return TRUE - */ - /datum/job/proc/update_limit(var/comperator) return diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index cbcf40ef91..bf0e3b6c3e 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -111,9 +111,6 @@ var/global/datum/controller/occupations/job_master Debug("FOC is_job_whitelisted failed, Player: [player]") continue //VOREStation Code End - if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data[O_BRAIN]) == TRUE) - Debug("FOC character species invalid for job, Player: [player]") - continue if(flag && !(player.client.prefs.be_special & flag)) Debug("FOC flag failed, Player: [player], Flag: [flag], ") continue @@ -131,9 +128,6 @@ var/global/datum/controller/occupations/job_master if((job.minimum_character_age || job.min_age_by_species) && (player.read_preference(/datum/preference/numeric/human/age) < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data[O_BRAIN]))) continue - if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data[O_BRAIN]) == TRUE) - continue - if(istype(job, GetJob(JOB_ALT_VISITOR))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant continue @@ -395,8 +389,9 @@ var/global/datum/controller/occupations/job_master //Equip custom gear loadout. var/list/custom_equip_slots = list() var/list/custom_equip_leftovers = list() - if(H.client && H.client.prefs && H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON)) - for(var/thing in H.client.prefs.gear) + if(H?.client?.prefs && !(job.mob_type & JOB_SILICON)) + var/list/active_gear_list = LAZYACCESS(H.client.prefs.gear_list, "[H.client.prefs.gear_slot]") + for(var/thing in active_gear_list) var/datum/gear/G = gear_datums[thing] if(!G) //Not a real gear datum (maybe removed, as this is loaded from their savefile) continue @@ -421,14 +416,14 @@ var/global/datum/controller/occupations/job_master // Implants get special treatment if(G.slot == "implant") - var/obj/item/implant/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) + var/obj/item/implant/I = G.spawn_item(H, active_gear_list[G.display_name]) I.invisibility = INVISIBILITY_MAXIMUM I.implant_loadout(H) continue // Try desperately (and sorta poorly) to equip the item. Now with increased desperation! if(G.slot && !(G.slot in custom_equip_slots)) - var/metadata = H.client.prefs.gear[G.display_name] + var/metadata = active_gear_list[G.display_name] //if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) // custom_equip_leftovers += thing //else @@ -468,7 +463,8 @@ var/global/datum/controller/occupations/job_master if(G.slot in custom_equip_slots) spawn_in_storage += thing else - var/metadata = H.client.prefs.gear[G.display_name] + var/list/active_gear_list = LAZYACCESS(H.client.prefs.gear_list, "[H.client.prefs.gear_slot]") + var/metadata = active_gear_list[G.display_name] if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) to_chat(H, span_notice("Equipping you with \the [thing]!")) custom_equip_slots.Add(G.slot) @@ -516,11 +512,12 @@ var/global/datum/controller/occupations/job_master B = S break + var/list/active_gear_list = LAZYACCESS(H.client.prefs.gear_list, "[H.client.prefs.gear_slot]") if(!isnull(B)) for(var/thing in spawn_in_storage) to_chat(H, span_notice("Placing \the [thing] in your [B.name]!")) var/datum/gear/G = gear_datums[thing] - var/metadata = H.client.prefs.gear[G.display_name] + var/metadata = active_gear_list[G.display_name] G.spawn_item(B, metadata) else to_chat(H, span_danger("Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug.")) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 9b6c3952d4..83bb1e11a5 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -109,7 +109,6 @@ R.dna.real_name = "clone ([rand(0,999)])" H.real_name = R.dna.real_name H.gender = R.gender - H.descriptors = R.body_descriptors //Get the clone body ready H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index da4e264154..5bb3dd67a6 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -422,7 +422,6 @@ R.types = DNA2_BUF_UI|DNA2_BUF_UE|DNA2_BUF_SE R.languages = subject.languages R.gender = subject.gender - R.body_descriptors = subject.descriptors R.flavor = subject.flavor_texts.Copy() for(var/datum/modifier/mod in subject.modifiers) if(mod.flags & MODIFIER_GENETIC) diff --git a/code/modules/asset_cache/assets/preferences.dm b/code/modules/asset_cache/assets/preferences.dm index acc1a349b5..61b0ebcdee 100644 --- a/code/modules/asset_cache/assets/preferences.dm +++ b/code/modules/asset_cache/assets/preferences.dm @@ -1,3 +1,9 @@ +/datum/asset/simple/preferences + assets = list( + "preview_protean_animation.gif" = 'icons/mob/human_races/preview_protean_animation.gif', + "preview_custom_animation.gif" = 'icons/mob/human_races/preview_custom_animation.gif', + ) + /// Assets generated from `/datum/preference` icons /datum/asset/spritesheet/preferences name = "preferences" diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index f6525e1ec8..61113f224c 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -580,14 +580,18 @@ /client/verb/character_setup() set name = "Character Setup" set category = "Preferences.Character" - if(prefs) - prefs.ShowChoices(usr) + + prefs.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES + prefs.update_tgui_static_data(mob) + prefs.tgui_interact(mob) /client/verb/game_options() set name = "Game Options" set category = "Preferences.Game" - if(prefs) - prefs.tgui_interact(usr) + + prefs.current_window = PREFERENCE_TAB_GAME_PREFERENCES + prefs.update_tgui_static_data(mob) + prefs.tgui_interact(mob) /client/proc/findJoinDate() var/list/http = world.Export("http://byond.com/members/[ckey]?format=text") diff --git a/code/modules/client/preference_setup/antagonism/01_basic.dm b/code/modules/client/preference_setup/antagonism/01_basic.dm deleted file mode 100644 index 1f9b61a20a..0000000000 --- a/code/modules/client/preference_setup/antagonism/01_basic.dm +++ /dev/null @@ -1,124 +0,0 @@ -/datum/category_item/player_setup_item/antagonism/basic - name = "Basic" - sort_order = 1 - - var/datum/paiCandidate/candidate - -/datum/category_item/player_setup_item/antagonism/basic/load_character(list/save_data) - pref.exploit_record = save_data["exploit_record"] - pref.antag_faction = save_data["antag_faction"] - pref.antag_vis = save_data["antag_vis"] - -/datum/category_item/player_setup_item/antagonism/basic/save_character(list/save_data) - save_data["exploit_record"] = pref.exploit_record - save_data["antag_faction"] = pref.antag_faction - save_data["antag_vis"] = pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/load_preferences(datum/json_savefile/savefile) - if(!candidate) - candidate = new() - var/preference_mob = preference_mob() - if(!preference_mob)// No preference mob - this happens when we're called from client/New() before it calls ..() (via datum/preferences/New()) - spawn() - preference_mob = preference_mob() - if(!preference_mob) - return - candidate.savefile_load(preference_mob) - return - - candidate.savefile_load(preference_mob) - -/datum/category_item/player_setup_item/antagonism/basic/save_preferences(datum/json_savefile/savefile) - if(!candidate) - return - - if(!preference_mob()) - return - - candidate.savefile_save(preference_mob()) - -/datum/category_item/player_setup_item/antagonism/basic/sanitize_character() - if(!pref.antag_faction) pref.antag_faction = "None" - if(!pref.antag_vis) pref.antag_vis = "Hidden" - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/antagonism/basic/copy_to_mob(var/mob/living/carbon/human/character) - character.exploit_record = pref.exploit_record - character.antag_faction = pref.antag_faction - character.antag_vis = pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/content(var/mob/user) - . += "Faction: [pref.antag_faction]
" - . += "Visibility: [pref.antag_vis]
" - . += span_bold("Uplink Type:") + " [pref.read_preference(/datum/preference/choiced/uplinklocation)]" - . +="
" - . +=span_bold("Exploitable information:") + "
" - if(jobban_isbanned(user, "Records")) - . += span_bold("You are banned from using character records.") + "
" - else - . +="[TextPreview(pref.exploit_record,40)]
" - . += "
" - . += span_bold("pAI:") + "
" - if(!candidate) - log_debug("[user] pAI prefs have a null candidate var.") - return . - . += "Name: [candidate.name ? candidate.name : "None Set"]
" - . += "Description: [candidate.description ? TextPreview(candidate.description, 40) : "None Set"]
" - . += "Role: [candidate.role ? TextPreview(candidate.role, 40) : "None Set"]
" - . += "OOC Comments: [candidate.comments ? TextPreview(candidate.comments, 40) : "None Set"]
" - -/datum/category_item/player_setup_item/antagonism/basic/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["uplinklocation"]) - var/new_uplinklocation = tgui_input_list(user, "Choose your uplink location:", "Character Preference", GLOB.uplink_locations, pref.read_preference(/datum/preference/choiced/uplinklocation)) - if(new_uplinklocation && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/choiced/uplinklocation, new_uplinklocation) - return TOPIC_REFRESH - - if(href_list["exploitable_record"]) - var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0) - if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.exploit_record = exploitmsg - return TOPIC_REFRESH - - if(href_list["antagfaction"]) - var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.antag_faction = raw_choice - else - pref.antag_faction = choice - return TOPIC_REFRESH - - if(href_list["antagvis"]) - var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - else - pref.antag_vis = choice - return TOPIC_REFRESH - - if(href_list["option"]) - var/t - switch(href_list["option"]) - if("name") - t = sanitizeName(tgui_input_text(user, "Enter a name for your pAI", "Global Preference", candidate.name, MAX_NAME_LEN), MAX_NAME_LEN, 1) - if(t && CanUseTopic(user)) - candidate.name = t - if("desc") - t = tgui_input_text(user, "Enter a description for your pAI", "Global Preference", html_decode(candidate.description), multiline = TRUE, prevent_enter = TRUE) - if(!isnull(t) && CanUseTopic(user)) - candidate.description = sanitize(t) - if("role") - t = tgui_input_text(user, "Enter a role for your pAI", "Global Preference", html_decode(candidate.role)) - if(!isnull(t) && CanUseTopic(user)) - candidate.role = sanitize(t) - if("ooc") - t = tgui_input_text(user, "Enter any OOC comments", "Global Preference", html_decode(candidate.comments), multiline = TRUE, prevent_enter = TRUE) - if(!isnull(t) && CanUseTopic(user)) - candidate.comments = sanitize(t) - return TOPIC_REFRESH - - return ..() diff --git a/code/modules/client/preference_setup/antagonism/02_candidacy.dm b/code/modules/client/preference_setup/antagonism/02_candidacy.dm deleted file mode 100644 index 7a1b279edd..0000000000 --- a/code/modules/client/preference_setup/antagonism/02_candidacy.dm +++ /dev/null @@ -1,64 +0,0 @@ -var/global/list/special_roles = list( //keep synced with the defines BE_* in setup.dm --rastaf -//some autodetection here. -// Change these to 0 if the equivalent mode is disabled for whatever reason! - "traitor" = 0, // 0 - "operative" = 0, // 1 - "changeling" = 0, // 2 - "wizard" = 0, // 3 - "malf AI" = 0, // 4 - "revolutionary" = 0, // 5 - "alien candidate" = 0, // 6 - "positronic brain" = 1, // 7 - "cultist" = 0, // 8 - "renegade" = 0, // 9 - "ninja" = 0, // 10 - "raider" = 0, // 11 - "diona" = 0, // 12 - "mutineer" = 0, // 13 - "loyalist" = 0, // 14 - "pAI candidate" = 1, // 15 - //VOREStation Add - "lost drone" = 1, // 16 - "maint pred" = 1, // 17 - "maint lurker" = 1, // 18 - "morph" = 1, // 19 - "corgi" = 1, // 20 - "cursed sword" = 1, // 21 - "Ship Survivor" = 1, // 22 - //VOREStation Add End -) - -/datum/category_item/player_setup_item/antagonism/candidacy - name = "Candidacy" - sort_order = 2 - -/datum/category_item/player_setup_item/antagonism/candidacy/load_character(list/save_data) - pref.be_special = save_data["be_special"] - -/datum/category_item/player_setup_item/antagonism/candidacy/save_character(list/save_data) - save_data["be_special"] = pref.be_special - -/datum/category_item/player_setup_item/antagonism/candidacy/sanitize_character() - pref.be_special = sanitize_integer(pref.be_special, 0, 16777215, initial(pref.be_special)) //VOREStation Edit - 24 bits of support - -/datum/category_item/player_setup_item/antagonism/candidacy/content(var/mob/user) - if(jobban_isbanned(user, JOB_SYNDICATE)) - . += span_bold("You are banned from antagonist roles.") - pref.be_special = 0 - else - var/n = 0 - for (var/i in special_roles) - if(special_roles[i]) //if mode is available on the server - if(jobban_isbanned(user, i) || (i == "positronic brain" && jobban_isbanned(user, JOB_AI) && jobban_isbanned(user, JOB_CYBORG)) || (i == "pAI candidate" && jobban_isbanned(user, JOB_PAI))) - . += span_bold("Be [i]:") + " " + span_red(span_bold(" \[BANNED]")) + "
" - else - . += span_bold("Be [i]:") + " " + span_bold("[pref.be_special&(1<
" - n++ - -/datum/category_item/player_setup_item/antagonism/candidacy/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["be_special"]) - var/num = text2num(href_list["be_special"]) - pref.be_special ^= (1<
" - . += span_bold("Nickname:") + " " - . += "
[pref.nickname]" - . += "(Clear)" - . += "
" - . += span_bold("Biological Sex:") + " [gender2text(pref.biological_gender)]
" - . += span_bold("Pronouns:") + " [genders_to_pronoun_set[pref.identifying_gender]]
" - . += span_bold("Age:") + " [pref.read_preference(/datum/preference/numeric/human/age)] Birthday: [pref.read_preference(/datum/preference/numeric/human/bday_month)]/[pref.read_preference(/datum/preference/numeric/human/bday_day)] - Announce?: [pref.read_preference(/datum/preference/toggle/human/bday_announce) ? "Yes" : "Disabled"]
" - . += span_bold("Spawn Point:") + " [pref.read_preference(/datum/preference/choiced/living/spawnpoint)]
" - if(CONFIG_GET(flag/allow_metadata)) - . += span_bold("OOC Notes: EditFavsLikesMaybesDislikes") + "
" - . += "Detailed field or short list system? [pref.read_preference(/datum/preference/toggle/living/ooc_notes_style) ? "Lists" : "Fields"]

" - . = jointext(.,null) + character.vore_egg_type = pref.vore_egg_type + // VOREStation Add + if(pref.client) // Safety, just in case so we don't runtime. + if(!pref.autohiss) + pref.client.autohiss_mode = AUTOHISS_FULL + else + switch(pref.autohiss) + if("Full") + pref.client.autohiss_mode = AUTOHISS_FULL + if("Basic") + pref.client.autohiss_mode = AUTOHISS_BASIC + if("Off") + pref.client.autohiss_mode = AUTOHISS_OFF -/datum/category_item/player_setup_item/general/basic/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["rename"]) - var/raw_name = tgui_input_text(user, "Choose your character's name:", "Character Name") - if (!isnull(raw_name) && CanUseTopic(user)) - var/new_name = sanitize_name(raw_name, pref.species, is_FBP()) - if(new_name) - pref.real_name = new_name - return TOPIC_REFRESH - else - to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .")) - return TOPIC_NOACTION +/datum/category_item/player_setup_item/general/basic/tgui_data(mob/user) + var/list/data = ..() - else if(href_list["random_name"]) - pref.real_name = random_name(pref.identifying_gender, pref.species) - return TOPIC_REFRESH + data["real_name"] = pref.real_name + data["be_random_name"] = pref.read_preference(/datum/preference/toggle/human/name_is_always_random) + data["nickname"] = pref.nickname + data["biological_sex"] = gender2text(pref.biological_gender) + data["identifying_gender"] = gender2text(pref.identifying_gender) + data["age"] = pref.read_preference(/datum/preference/numeric/human/age) + data["bday_month"] = pref.read_preference(/datum/preference/numeric/human/bday_month) + data["bday_day"] = pref.read_preference(/datum/preference/numeric/human/bday_day) + data["bday_announce"] = pref.read_preference(/datum/preference/toggle/human/bday_announce) + data["spawnpoint"] = pref.read_preference(/datum/preference/choiced/living/spawnpoint) + data["ooc_notes_length"] = LAZYLEN(pref.read_preference(/datum/preference/text/living/ooc_notes)) + data["vore_egg_type"] = pref.vore_egg_type + data["autohiss"] = pref.autohiss + data["emote_sound_mode"] = pref.read_preference(/datum/preference/choiced/living/emote_sound_mode) - else if(href_list["always_random_name"]) - pref.update_preference_by_type(/datum/preference/toggle/human/name_is_always_random, !pref.read_preference(/datum/preference/toggle/human/name_is_always_random)) - return TOPIC_REFRESH + return data - else if(href_list["nickname"]) - var/raw_nickname = tgui_input_text(user, "Choose your character's nickname:", "Character Nickname", pref.nickname) - if (!isnull(raw_nickname) && CanUseTopic(user)) - var/new_nickname = sanitize_name(raw_nickname, pref.species, is_FBP()) - if(new_nickname) - pref.nickname = new_nickname - return TOPIC_REFRESH - else - to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .")) - return TOPIC_NOACTION +/datum/category_item/player_setup_item/general/basic/tgui_static_data(mob/user) + var/list/data = ..() - else if(href_list["reset_nickname"]) - var/nick_choice = tgui_alert(user, "Wipe your Nickname? This will completely remove any chosen nickname(s).","Wipe Nickname",list("Yes","No")) - if(nick_choice == "Yes") - pref.nickname = null - return TOPIC_REFRESH + data["allow_metadata"] = CONFIG_GET(flag/allow_metadata) - else if(href_list["bio_gender"]) - var/new_gender = tgui_input_list(user, "Choose your character's biological sex:", "Character Preference", get_genders(), pref.biological_gender) - if(new_gender && CanUseTopic(user)) - pref.set_biological_gender(new_gender) - return TOPIC_REFRESH_UPDATE_PREVIEW + return data - else if(href_list["id_gender"]) - var/new_gender = tgui_input_list(user, "Choose your character's pronouns:", "Character Preference", pronoun_set_to_genders, genders_to_pronoun_set[pref.identifying_gender]) - if(new_gender && CanUseTopic(user)) - pref.identifying_gender = pronoun_set_to_genders[new_gender] - return TOPIC_REFRESH +/datum/category_item/player_setup_item/general/basic/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return - else if(href_list["age"]) - var/min_age = get_min_age() - var/max_age = get_max_age() - var/new_age = tgui_input_number(user, "Choose your character's age:\n([min_age]-[max_age])", "Character Preference", pref.read_preference(/datum/preference/numeric/human/age), max_age, min_age) - if(new_age && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/numeric/human/age, max(min(round(text2num(new_age)), max_age), min_age)) + var/mob/user = ui.user + + switch(action) + if("rename") + var/raw_name = tgui_input_text(user, "Choose your character's name:", "Character Name", pref.real_name) + if(!isnull(raw_name)) + var/new_name = sanitize_name(raw_name, pref.species, is_FBP()) + if(new_name) + pref.real_name = new_name + return TOPIC_REFRESH + else + to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .")) + return TOPIC_NOACTION + + if("random_name") + pref.real_name = random_name(pref.identifying_gender, pref.species) return TOPIC_REFRESH - else if(href_list["bday_month"]) - var/new_month = tgui_input_number(user, "Choose your character's birth month (number)", "Birthday Month", pref.read_preference(/datum/preference/numeric/human/bday_month), 12, 0) - if(new_month && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, new_month) - else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes") && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0) - pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0) - return TOPIC_REFRESH + if("always_random_name") + pref.update_preference_by_type(/datum/preference/toggle/human/name_is_always_random, !pref.read_preference(/datum/preference/toggle/human/name_is_always_random)) + return TOPIC_REFRESH - else if(href_list["bday_day"]) - if(!pref.read_preference(/datum/preference/numeric/human/bday_month)) - tgui_alert(user,"You must set a birth month before you can set a day.", "Error", list("Okay")) - return - var/max_days - switch(pref.read_preference(/datum/preference/numeric/human/bday_month)) - if(1) - max_days = 31 - if(2) - max_days = 29 - if(3) - max_days = 31 - if(4) - max_days = 30 - if(5) - max_days = 31 - if(6) - max_days = 30 - if(7) - max_days = 31 - if(8) - max_days = 31 - if(9) - max_days = 30 - if(10) - max_days = 31 - if(11) - max_days = 30 - if(12) - max_days = 31 + if("nickname") + var/raw_nickname = tgui_input_text(user, "Choose your character's nickname:", "Character Nickname", pref.nickname) + if(!isnull(raw_nickname)) + var/new_nickname = sanitize_name(raw_nickname, pref.species, is_FBP()) + if(new_nickname) + pref.nickname = new_nickname + return TOPIC_REFRESH + else + to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .")) + return TOPIC_NOACTION - var/new_day = tgui_input_number(user, "Choose your character's birth day (number, 1-[max_days])", "Birthday Day", pref.read_preference(/datum/preference/numeric/human/bday_day), max_days, 0) - if(new_day && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, new_day) - else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes") && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0) - pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0) - return TOPIC_REFRESH + if("reset_nickname") + var/nick_choice = tgui_alert(user, "Wipe your Nickname? This will completely remove any chosen nickname(s).","Wipe Nickname",list("Yes","No")) + if(nick_choice == "Yes") + pref.nickname = null + return TOPIC_REFRESH - else if(href_list["bday_announce"]) - pref.update_preference_by_type(/datum/preference/toggle/human/bday_announce, !pref.read_preference(/datum/preference/toggle/human/bday_announce)) - return TOPIC_REFRESH + if("bio_gender") + var/new_gender = lowertext(params["gender"]) + if(new_gender in get_genders()) + pref.set_biological_gender(new_gender) + return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["spawnpoint"]) - var/list/spawnkeys = list() - for(var/spawntype in spawntypes) - spawnkeys += spawntype - var/choice = tgui_input_list(user, "Where would you like to spawn when late-joining?", "Late-Join Choice", spawnkeys) - if(!choice || !spawntypes[choice] || !CanUseTopic(user)) return TOPIC_NOACTION - pref.update_preference_by_type(/datum/preference/choiced/living/spawnpoint, choice) - return TOPIC_REFRESH + if("id_gender") + var/new_gender = lowertext(params["gender"]) + if(new_gender in all_genders_define_list) + pref.identifying_gender = new_gender + return TOPIC_REFRESH + + if("age") + var/min_age = get_min_age() + var/max_age = get_max_age() + var/new_age = tgui_input_number(user, "Choose your character's age:\n([min_age]-[max_age])", "Character Preference", pref.read_preference(/datum/preference/numeric/human/age), max_age, min_age) + if(new_age) + pref.update_preference_by_type(/datum/preference/numeric/human/age, max(min(round(text2num(new_age)), max_age), min_age)) + return TOPIC_REFRESH + + if("bday_month") + var/new_month = tgui_input_number(user, "Choose your character's birth month (number)", "Birthday Month", pref.read_preference(/datum/preference/numeric/human/bday_month), 12, 0) + if(new_month) + pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, new_month) + else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes")) + pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0) + pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0) + + if("bday_day") + if(!pref.read_preference(/datum/preference/numeric/human/bday_month)) + tgui_alert(user,"You must set a birth month before you can set a day.", "Error", list("Okay")) + return + var/max_days + switch(pref.read_preference(/datum/preference/numeric/human/bday_month)) + if(1) + max_days = 31 + if(2) + max_days = 29 + if(3) + max_days = 31 + if(4) + max_days = 30 + if(5) + max_days = 31 + if(6) + max_days = 30 + if(7) + max_days = 31 + if(8) + max_days = 31 + if(9) + max_days = 30 + if(10) + max_days = 31 + if(11) + max_days = 30 + if(12) + max_days = 31 + + var/new_day = tgui_input_number(user, "Choose your character's birth day (number, 1-[max_days])", "Birthday Day", pref.read_preference(/datum/preference/numeric/human/bday_day), max_days, 0) + if(new_day) + pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, new_day) + else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes")) + pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0) + pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0) + return TOPIC_REFRESH + + if("bday_announce") + pref.update_preference_by_type(/datum/preference/toggle/human/bday_announce, !pref.read_preference(/datum/preference/toggle/human/bday_announce)) + return TOPIC_REFRESH + + if("spawnpoint") + var/list/spawnkeys = list() + for(var/spawntype in spawntypes) + spawnkeys += spawntype + var/choice = tgui_input_list(user, "Where would you like to spawn when late-joining?", "Late-Join Choice", spawnkeys) + if(!choice || !spawntypes[choice]) + return TOPIC_NOACTION + pref.update_preference_by_type(/datum/preference/choiced/living/spawnpoint, choice) + return TOPIC_REFRESH + + if("edit_ooc_notes") + var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see, such as Roleplay-preferences. This will not be saved permanently unless you click save in the Character Setup panel!", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes)), multiline = TRUE, prevent_enter = TRUE)) + if(new_metadata) + pref.update_preference_by_type(/datum/preference/text/living/ooc_notes, new_metadata) + + if("edit_ooc_note_favs") + var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your FAVOURITE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_favs)), multiline = TRUE, prevent_enter = TRUE)) + if(new_metadata) + if(new_metadata == "!clear") + new_metadata = "" + pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_favs, new_metadata) + + if("edit_ooc_note_likes") + var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your LIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_likes)), multiline = TRUE, prevent_enter = TRUE)) + if(new_metadata) + if(new_metadata == "!clear") + new_metadata = "" + pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_likes, new_metadata) + + if("edit_ooc_note_maybes") + var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your MAYBE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_maybes)), multiline = TRUE, prevent_enter = TRUE)) + if(new_metadata && CanUseTopic(user)) + if(new_metadata == "!clear") + new_metadata = "" + pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_maybes, new_metadata) + + if("edit_ooc_note_dislikes") + var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your DISLIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_dislikes)), multiline = TRUE, prevent_enter = TRUE)) + if(new_metadata) + if(new_metadata == "!clear") + new_metadata = "" + pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_dislikes, new_metadata) + + if("edit_ooc_note_style") + pref.update_preference_by_type(/datum/preference/toggle/living/ooc_notes_style, !pref.read_preference(/datum/preference/toggle/living/ooc_notes_style)) + return TOPIC_REFRESH + + if("vore_egg_type") + var/list/vore_egg_types = GLOB.global_vore_egg_types + var/selection = tgui_input_list(user, "Choose your character's egg type:", "Character Preference", vore_egg_types, pref.vore_egg_type) + if(selection) + pref.vore_egg_type = selection + return TOPIC_REFRESH + + if("autohiss") + var/list/autohiss_selection = list("Full", "Basic", "Off") + var/selection = tgui_input_list(user, "Choose your default autohiss setting:", "Character Preference", autohiss_selection, pref.autohiss) + if(selection) + pref.autohiss = selection + else if(!selection) + pref.autohiss = "Full" + return TOPIC_REFRESH + + if("emote_sound_mode") + var/datum/preference/choiced/living/emote_sound_mode = GLOB.preference_entries_by_key["emote_sound_mode"] + var/selection = tgui_input_list(user, "Choose your emote sound mode", "Emote Sound Mode", emote_sound_mode.get_choices()) + if(selection) + pref.update_preference(emote_sound_mode, selection) + return TOPIC_REFRESH - else if(href_list["edit_ooc_notes"]) - var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see, such as Roleplay-preferences. This will not be saved permanently unless you click save in the Character Setup panel!", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes)), multiline = TRUE, prevent_enter = TRUE)) - if(new_metadata && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/text/living/ooc_notes, new_metadata) - else if(href_list["edit_ooc_note_likes"]) - var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your LIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_likes)), multiline = TRUE, prevent_enter = TRUE)) - if(new_metadata && CanUseTopic(user)) - if(new_metadata == "!clear") - new_metadata = "" - pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_likes, new_metadata) - else if(href_list["edit_ooc_note_dislikes"]) - var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your DISLIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_dislikes)), multiline = TRUE, prevent_enter = TRUE)) - if(new_metadata && CanUseTopic(user)) - if(new_metadata == "!clear") - new_metadata = "" - pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_dislikes, new_metadata) - else if(href_list["edit_ooc_note_favs"]) - var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your FAVOURITE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_favs)), multiline = TRUE, prevent_enter = TRUE)) - if(new_metadata && CanUseTopic(user)) - if(new_metadata == "!clear") - new_metadata = "" - pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_favs, new_metadata) - else if(href_list["edit_ooc_note_maybes"]) - var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your MAYBE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_maybes)), multiline = TRUE, prevent_enter = TRUE)) - if(new_metadata && CanUseTopic(user)) - if(new_metadata == "!clear") - new_metadata = "" - pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_maybes, new_metadata) - else if(href_list["edit_ooc_note_style"]) - pref.update_preference_by_type(/datum/preference/toggle/living/ooc_notes_style, !pref.read_preference(/datum/preference/toggle/living/ooc_notes_style)) - return TOPIC_REFRESH return ..() /datum/category_item/player_setup_item/general/basic/proc/get_genders() @@ -232,3 +303,7 @@ possible_genders = possible_genders.Copy() possible_genders |= NEUTER return possible_genders + +#undef AUTOHISS_OFF +#undef AUTOHISS_BASIC +#undef AUTOHISS_FULL diff --git a/code/modules/client/preference_setup/general/02_language.dm b/code/modules/client/preference_setup/general/02_language.dm index 1d58f6ead9..c2fc93bcf3 100644 --- a/code/modules/client/preference_setup/general/02_language.dm +++ b/code/modules/client/preference_setup/general/02_language.dm @@ -68,42 +68,59 @@ pref.runechat_color = sanitize_hexcolor(pref.runechat_color, COLOR_BLACK) -/datum/category_item/player_setup_item/general/language/content() - . += span_bold("Languages") + "
" +/datum/category_item/player_setup_item/general/language/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/list/languages_list = list() + var/datum/species/S = GLOB.all_species[pref.species] - if(pref.alternate_languages.len > (S.num_alternate_languages + pref.extra_languages)) - testing("LANGSANI: Truncated [pref.client]'s character [pref.real_name || "-name not yet loaded-"] language list because it was too long (len: [pref.alternate_languages.len], allowed: [S.num_alternate_languages])") - pref.alternate_languages.len = (S.num_alternate_languages + pref.extra_languages) // Truncate to allowed length if(S.language) - . += "- [S.language] - Set Custom Key
" + UNTYPED_LIST_ADD(languages_list, list( + "name" = S.language, + "removable" = FALSE + )) if(S.default_language && S.default_language != S.language) - . += "- [S.default_language] - Set Custom Key
" - if(S.num_alternate_languages + pref.extra_languages) - if(pref.alternate_languages.len) - for(var/i = 1 to pref.alternate_languages.len) - var/lang = pref.alternate_languages[i] - . += "- [lang] - remove - Set Custom Key
" + UNTYPED_LIST_ADD(languages_list, list( + "name" = S.default_language, + "removable" = FALSE + )) + for(var/lang in pref.alternate_languages) + UNTYPED_LIST_ADD(languages_list, list( + "name" = lang, + "removable" = TRUE + )) - if(pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages)) - . += "- add ([(S.num_alternate_languages + pref.extra_languages) - pref.alternate_languages.len] remaining)
" - else - . += "- [pref.species] cannot choose secondary languages.
" + data["languages"] = languages_list - . += span_bold("Language Keys") + "
" - . += " [jointext(pref.language_prefixes, " ")] Change Reset
" - . += span_bold("Preferred Language") + " [pref.preferred_language]
" // VOREStation Add - . += span_bold("Runechat Color") + " Change Runechat Color [color_square(hex = pref.runechat_color)]" + data["languages_can_add"] = LAZYLEN(pref.alternate_languages) < (S.num_alternate_languages + pref.extra_languages) + data["language_keys"] = pref.language_prefixes + data["preferred_language"] = pref.preferred_language + data["runechat_color"] = pref.runechat_color + + return data + +/datum/category_item/player_setup_item/general/language/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + var/datum/species/S = GLOB.all_species[pref.species] + + switch(action) + if("remove_language") + var/lang_to_remove = params["lang"] + var/index = pref.alternate_languages.Find(lang_to_remove) + if(!index) + return TOPIC_REFRESH + pref.alternate_languages.Cut(index, index+1) + return TOPIC_REFRESH + + if("add_language") + if(pref.alternate_languages.len >= (S.num_alternate_languages + pref.extra_languages)) + tgui_alert_async(user, "You have already selected the maximum number of alternate languages for this species!") + return TOPIC_REFRESH -/datum/category_item/player_setup_item/general/language/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["remove_language"]) - var/index = text2num(href_list["remove_language"]) - pref.alternate_languages.Cut(index, index+1) - return TOPIC_REFRESH - else if(href_list["add_language"]) - var/datum/species/S = GLOB.all_species[pref.species] - if(pref.alternate_languages.len >= (S.num_alternate_languages + pref.extra_languages)) - tgui_alert_async(user, "You have already selected the maximum number of alternate languages for this species!") - else var/list/available_languages = S.secondary_langs.Copy() for(var/L in GLOB.all_languages) var/datum/language/lang = GLOB.all_languages[L] @@ -117,74 +134,73 @@ if(!available_languages.len) tgui_alert_async(user, "There are no additional languages available to select.") - else - var/new_lang = tgui_input_list(user, "Select an additional language", "Character Generation", available_languages) - if(new_lang && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages)) - var/datum/language/chosen_lang = GLOB.all_languages[new_lang] - if(istype(chosen_lang)) - var/choice = tgui_alert(user, "[chosen_lang.desc]",chosen_lang.name, list("Take","Cancel")) - if(choice != "Cancel" && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages)) - pref.alternate_languages |= new_lang - return TOPIC_REFRESH + return TOPIC_REFRESH - else if(href_list["change_prefix"]) - var/char - var/keys[0] - do - char = tgui_input_text(user, "Enter a single special character.\nYou may re-select the same characters.\nThe following characters are already in use by radio: ; : .\nThe following characters are already in use by special say commands: ! * ^", "Enter Character - [3 - keys.len] remaining") - if(char) - if(length(char) > 1) - tgui_alert_async(user, "Only single characters allowed.", "Error") - else if(char in list(";", ":", ".")) - tgui_alert_async(user, "Radio character. Rejected.", "Error") - else if(char in list("!","*","^","-")) - tgui_alert_async(user, "Say character. Rejected.", "Error") - else if(contains_az09(char)) - tgui_alert_async(user, "Non-special character. Rejected.", "Error") - else - keys.Add(char) - while(char && keys.len < 3) + var/new_lang = tgui_input_list(user, "Select an additional language", "Character Generation", available_languages) + if(new_lang && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages)) + var/datum/language/chosen_lang = GLOB.all_languages[new_lang] + if(istype(chosen_lang)) + var/choice = tgui_alert(user, "[chosen_lang.desc]",chosen_lang.name, list("Take","Cancel")) + if(choice != "Cancel" && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages)) + pref.alternate_languages |= new_lang + return TOPIC_REFRESH - if(keys.len == 3) - pref.language_prefixes = keys - return TOPIC_REFRESH - else if(href_list["reset_prefix"]) - var/list/prefixes = CONFIG_GET(str_list/language_prefixes) - pref.language_prefixes = prefixes.Copy() - return TOPIC_REFRESH + if("change_prefix") + var/char + var/keys[0] + do + char = tgui_input_text(user, "Enter a single special character.\nYou may re-select the same characters.\nThe following characters are already in use by radio: ; : .\nThe following characters are already in use by special say commands: ! * ^", "Enter Character - [3 - keys.len] remaining") + if(char) + if(length(char) > 1) + tgui_alert_async(user, "Only single characters allowed.", "Error") + else if(char in list(";", ":", ".")) + tgui_alert_async(user, "Radio character. Rejected.", "Error") + else if(char in list("!","*","^","-")) + tgui_alert_async(user, "Say character. Rejected.", "Error") + else if(contains_az09(char)) + tgui_alert_async(user, "Non-special character. Rejected.", "Error") + else + keys.Add(char) + while(char && keys.len < 3) - else if(href_list["set_custom_key"]) - var/lang = href_list["set_custom_key"] - if(!(lang in GLOB.all_languages)) + if(keys.len == 3) + pref.language_prefixes = keys + return TOPIC_REFRESH + + if("reset_prefix") + var/list/prefixes = CONFIG_GET(str_list/language_prefixes) + pref.language_prefixes = prefixes.Copy() return TOPIC_REFRESH - var/oldkey = "" - for(var/key in pref.language_custom_keys) - if(pref.language_custom_keys[key] == lang) - oldkey = key - break + if("set_custom_key") + var/lang = params["lang"] + if(!(lang in GLOB.all_languages)) + return TOPIC_REFRESH - var/char = tgui_input_text(user, "Input a language key for [lang]. Input a single space to reset.", "Language Custom Key", oldkey) - if(length(char) != 1) - return TOPIC_REFRESH - else if(char == " ") + var/oldkey = "" for(var/key in pref.language_custom_keys) if(pref.language_custom_keys[key] == lang) - pref.language_custom_keys -= key + oldkey = key break - else if(contains_az09(char)) - if(!(char in pref.language_custom_keys)) - pref.language_custom_keys += char - pref.language_custom_keys[char] = lang - else - tgui_alert_async(user, "Improper language key. Rejected.", "Error") - return TOPIC_REFRESH + var/char = tgui_input_text(user, "Input a language key for [lang]. Input a single space to reset.", "Language Custom Key", oldkey) + if(length(char) != 1) + return TOPIC_REFRESH + else if(char == " ") + for(var/key in pref.language_custom_keys) + if(pref.language_custom_keys[key] == lang) + pref.language_custom_keys -= key + break + else if(contains_az09(char)) + if(!(char in pref.language_custom_keys)) + pref.language_custom_keys += char + pref.language_custom_keys[char] = lang + else + tgui_alert_async(user, "Improper language key. Rejected.", "Error") - // VOREStation Add: Preferred Language - else if(href_list["pref_lang"]) - if(pref.species) // Safety to prevent a null runtime here - var/datum/species/S = GLOB.all_species[pref.species] + return TOPIC_REFRESH + + if("pref_lang") var/list/lang_opts = list(S.language) + pref.alternate_languages + LANGUAGE_GALCOM var/selection = tgui_input_list(user, "Choose your preferred spoken language:", "Preferred Spoken Language", lang_opts, pref.preferred_language) if(!selection) // Set our preferred to default, just in case. @@ -196,19 +212,15 @@ else // Did they set anything else? tgui_alert_async(user, "You will now speak [pref.preferred_language] if you do not specify a language when speaking.", "Preferred Language Set") return TOPIC_REFRESH - // VOREStation Add End - else if(href_list["pref_runechat_color"]) - var/new_runechat_color = tgui_color_picker(user, "Choose your character's runechat colour (#000000 for random):", "Character Preference", pref.runechat_color) - if(new_runechat_color && CanUseTopic(user)) - pref.runechat_color = new_runechat_color - // whenever we change this, we update our mob - var/mob/pref_mob = preference_mob() - if(pref_mob) - pref_mob.chat_color = new_runechat_color - pref_mob.chat_color_darkened = new_runechat_color - pref_mob.chat_color_name = pref_mob.name - return TOPIC_REFRESH - - - return ..() + if("pref_runechat_color") + var/new_runechat_color = tgui_color_picker(user, "Choose your character's runechat colour (#000000 for random):", "Character Preference", pref.runechat_color) + if(new_runechat_color) + pref.runechat_color = new_runechat_color + // whenever we change this, we update our mob + var/mob/pref_mob = preference_mob() + if(pref_mob) + pref_mob.chat_color = new_runechat_color + pref_mob.chat_color_darkened = new_runechat_color + pref_mob.chat_color_name = pref_mob.name + return TOPIC_REFRESH diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index fd3b9d99f6..848afc3b73 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -1,4 +1,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-") +// Not actually used; just forces this into the RSC for TGUI. +var/const/preview_icons = 'icons/mob/human_races/preview.dmi' /datum/preferences var/equip_preview_mob = EQUIP_PREVIEW_ALL @@ -95,7 +97,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.synth_color = save_data["synth_color"] pref.synth_markings = save_data["synth_markings"] pref.bgstate = save_data["bgstate"] - pref.body_descriptors = check_list_copy(save_data["body_descriptors"]) pref.ear_style = save_data["ear_style"] pref.ear_secondary_style = save_data["ear_secondary_style"] pref.ear_secondary_colors = save_data["ear_secondary_colors"] @@ -121,7 +122,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O save_data["synth_color"] = pref.synth_color save_data["synth_markings"] = pref.synth_markings save_data["bgstate"] = pref.bgstate - save_data["body_descriptors"] = check_list_copy(pref.body_descriptors) save_data["ear_style"] = pref.ear_style save_data["ear_secondary_style"] = pref.ear_secondary_style save_data["ear_secondary_colors"] = pref.ear_secondary_colors @@ -139,13 +139,19 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.b_type = sanitize_text(pref.b_type, initial(pref.b_type)) if(!pref.organ_data) pref.organ_data = list() - if(!pref.rlimb_data) pref.rlimb_data = list() + if(!pref.rlimb_data || !islist(pref.rlimb_data)) pref.rlimb_data = list() if(!pref.body_markings) pref.body_markings = list() else pref.body_markings &= body_marking_styles_list for (var/M in pref.body_markings) if (!islist(pref.body_markings[M])) var/col = istext(pref.body_markings[M]) ? pref.body_markings[M] : "#000000" pref.body_markings[M] = pref.mass_edit_marking_list(M,color=col) + for(var/limb in pref.rlimb_data) + var/key = pref.rlimb_data[limb] + if(!istext(key)) + pref.rlimb_data -= limb + if(!LAZYACCESS(GLOB.all_robolimbs, key)) + pref.rlimb_data -= limb if(!pref.bgstate || !(pref.bgstate in pref.bgstate_options)) pref.bgstate = "000" @@ -251,948 +257,10 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O O.markings[M] = list("color" = pref.body_markings[M][BP]["color"], "datum" = mark_datum, "priority" = priority, "on" = pref.body_markings[M][BP]["on"]) character.markings_len = priority - var/list/last_descriptors = list() - if(islist(pref.body_descriptors)) - last_descriptors = pref.body_descriptors.Copy() - pref.body_descriptors = list() - - var/datum/species/mob_species = GLOB.all_species[pref.species] - if(LAZYLEN(mob_species.descriptors)) - for(var/entry in mob_species.descriptors) - var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] - if(istype(descriptor)) - if(isnull(last_descriptors[entry])) - pref.body_descriptors[entry] = descriptor.default_value // Species datums have initial default value. - else - pref.body_descriptors[entry] = CLAMP(last_descriptors[entry], 1, LAZYLEN(descriptor.standalone_value_descriptors)) - -/datum/category_item/player_setup_item/general/body/content(var/mob/user) - . = list() - - var/datum/species/mob_species = GLOB.all_species[pref.species] - . += "
" + span_bold("Body") + " " - . += "(®)" - . += "
" - . += "Species: [pref.species]
" - . += "Blood Type: [pref.b_type]
" - if(has_flag(mob_species, HAS_SKIN_TONE)) - . += "Skin Tone: [-pref.s_tone + 35]/220
" - . += "Limbs: Adjust Reset
" - . += "Internal Organs: Adjust
" - //display limbs below - var/ind = 0 - for(var/name in pref.organ_data) - var/status = pref.organ_data[name] - var/organ_name = null - - switch(name) - if(BP_TORSO) - organ_name = "torso" - if(BP_GROIN) - organ_name = "groin" - if(BP_HEAD) - organ_name = "head" - if(BP_L_ARM) - organ_name = "left arm" - if(BP_R_ARM) - organ_name = "right arm" - if(BP_L_LEG) - organ_name = "left leg" - if(BP_R_LEG) - organ_name = "right leg" - if(BP_L_FOOT) - organ_name = "left foot" - if(BP_R_FOOT) - organ_name = "right foot" - if(BP_L_HAND) - organ_name = "left hand" - if(BP_R_HAND) - organ_name = "right hand" - if(O_HEART) - organ_name = "heart" - if(O_EYES) - organ_name = "eyes" - if(O_VOICE) - organ_name = "larynx" - if(O_BRAIN) - organ_name = "brain" - if(O_LUNGS) - organ_name = "lungs" - if(O_LIVER) - organ_name = "liver" - if(O_KIDNEYS) - organ_name = "kidneys" - if(O_SPLEEN) - organ_name = "spleen" - if(O_STOMACH) - organ_name = "stomach" - if(O_INTESTINE) - organ_name = "intestines" - - if(status == "cyborg") - ++ind - if(ind > 1) - . += ", " - - var/datum/robolimb/R = GLOB.basic_robolimb - var/key = pref.rlimb_data[name] - if(!istext(key)) - log_debug("Bad rlimb_data for [key_name(pref.client)], [name] was set to [key]") - to_chat(usr, span_warning("Error loading robot limb data for `[name]`, clearing pref.")) - pref.rlimb_data -= name - else - R = LAZYACCESS(GLOB.all_robolimbs, key) - if(!istype(R)) - R = GLOB.basic_robolimb - . += "\t[R.company] [organ_name] prosthesis" - else if(status == "amputated") - ++ind - if(ind > 1) - . += ", " - . += "\tAmputated [organ_name]" - else if(status == "mechanical") - ++ind - if(ind > 1) - . += ", " - switch(organ_name) - if ("brain") - . += "\tPositronic [organ_name]" - else - . += "\tSynthetic [organ_name]" - else if(status == "digital") - ++ind - if(ind > 1) - . += ", " - . += "\tDigital [organ_name]" - else if(status == "assisted") - ++ind - if(ind > 1) - . += ", " - switch(organ_name) - if("heart") - . += "\tPacemaker-assisted [organ_name]" - if("lungs") - . += "\tAssisted [organ_name]" - if("voicebox") //on adding voiceboxes for speaking skrell/similar replacements - . += "\tSurgically altered [organ_name]" - if("eyes") - . += "\tRetinal overlayed [organ_name]" - if("brain") - . += "\tAssisted-interface [organ_name]" - else - . += "\tMechanically assisted [organ_name]" - if(!ind) - . += "\[...\]

" - else - . += "

" - - if(LAZYLEN(pref.body_descriptors)) - . += "" - for(var/entry in pref.body_descriptors) - var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] - . += "" - . += "
" + span_bold("[capitalize(descriptor.chargen_label)]:") + "[descriptor.get_standalone_value_descriptor(pref.body_descriptors[entry])]Change

" - - . += "
" + span_bold("Preview") + "
" - . += "
Cycle background" - . += "
[pref.equip_preview_mob & EQUIP_PREVIEW_LOADOUT ? "Hide loadout" : "Show loadout"]" - . += "
[pref.equip_preview_mob & EQUIP_PREVIEW_JOB ? "Hide job gear" : "Show job gear"]" - . += "
[pref.animations_toggle ? "Stop animations" : "Show animations"]" - . += "
" - - . += span_bold("Hair") + "
" - if(has_flag(mob_species, HAS_HAIR_COLOR)) - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/hair_color))] " - . += " Style: < > [pref.h_style]
" //The < & > in this line is correct-- those extra characters are the arrows you click to switch between styles. - - . += span_bold("Gradient") + "
" - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/grad_color))] " - . += " Style: < > [pref.grad_style]
" - - . += "
" + span_bold("Facial") + "
" - if(has_flag(mob_species, HAS_HAIR_COLOR)) - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/facial_color))] " - . += " Style: < > [pref.f_style]
" //Same as above with the extra > & < characters - - if(has_flag(mob_species, HAS_EYE_COLOR)) - . += "
" + span_bold("Eyes") + "
" - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/eyes_color))]
" - - if(has_flag(mob_species, HAS_SKIN_COLOR)) - . += "
" + span_bold("Body Color") + "
" - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/skin_color))]
" - - if(mob_species.digi_allowed) - . += "
" + span_bold("Digitigrade?:") + " " + span_bold("[pref.digitigrade ? "Yes" : "No"]") + "
" - - . += "

Genetics Settings

" - - var/list/ear_styles = pref.get_available_styles(global.ear_styles_list) - var/datum/sprite_accessory/ears/ear = ear_styles[pref.ear_style] - . += span_bold("Ears") + "
" - if(istype(ear)) - . += " Style: [ear.name]
" - if(ear.do_colouration) - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/ears_color1))]
" - if(ear.extra_overlay) - . += "Change Secondary Color [color_square(hex = pref.read_preference(/datum/preference/color/human/ears_color2))]
" - if(ear.extra_overlay2) - . += "Change Tertiary Color [color_square(hex = pref.read_preference(/datum/preference/color/human/ears_color3))]
" - .+= "Change Ears Alpha Current:[pref.read_preference(/datum/preference/numeric/human/ears_alpha)]
" - else - . += " Style: Select
" - - var/datum/sprite_accessory/ears/ears_secondary = ear_styles[pref.ear_secondary_style] - . += span_bold("Horns") + "
" - if(istype(ears_secondary)) - . += " Style: [ears_secondary.name]
" - for(var/channel in 1 to min(ears_secondary.get_color_channel_count(), length(GLOB.fancy_sprite_accessory_color_channel_names))) - . += "Change [GLOB.fancy_sprite_accessory_color_channel_names[channel]] Color [color_square(hex = LAZYACCESS(pref.ear_secondary_colors, channel) || "#ffffff")]
" - .+= "Change Horns Alpha Current:[pref.read_preference(/datum/preference/numeric/human/ears_alpha/secondary)]
" - else - . += " Style: Select
" - - var/list/tail_styles = pref.get_available_styles(global.tail_styles_list) - var/datum/sprite_accessory/tail/tail = tail_styles[pref.tail_style] - . += span_bold("Tail") + "
" - if(istype(tail)) - . += " Style: [tail.name]
" - if(tail.do_colouration) - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/tail_color1))]
" - if(tail.extra_overlay) - . += "Change Secondary Color [color_square(hex = pref.read_preference(/datum/preference/color/human/tail_color2))]
" - if(tail.extra_overlay2) - . += "Change Tertiary Color [color_square(hex = pref.read_preference(/datum/preference/color/human/tail_color3))]
" - .+= "Change Tail Alpha Current:[pref.read_preference(/datum/preference/numeric/human/tail_alpha)]
" - else - . += " Style: Select
" - - var/list/wing_styles = pref.get_available_styles(global.wing_styles_list) - var/datum/sprite_accessory/wing/wings = wing_styles[pref.wing_style] - . += span_bold("Wing") + "
" - if(istype(wings)) - . += " Style: [wings.name]
" - if(wings.do_colouration) - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/wing_color1))]
" - if(wings.extra_overlay) - . += "Change Secondary Color [color_square(hex = pref.read_preference(/datum/preference/color/human/wing_color2))]
" - if(wings.extra_overlay2) - . += "Change Secondary Color [color_square(hex = pref.read_preference(/datum/preference/color/human/wing_color3))]
" - .+= "Change Wing Alpha Current:[pref.read_preference(/datum/preference/numeric/human/wing_alpha)]
" - else - . += " Style: Select
" - - . += "
Body Markings +
" - . += "" - for(var/M in pref.body_markings) - . += "" - - . += "
[M][pref.body_markings.len > 1 ? "˄ ˅ mv " : ""]- Color[color_square(hex = pref.body_markings[M]["color"] ? pref.body_markings[M]["color"] : "#000000")] - Customize
" - . += "
" - . += span_bold("Allow Synth markings:") + " " + span_bold("[pref.synth_markings ? "Yes" : "No"]") + "
" - . += span_bold("Allow Synth color:") + " " + span_bold("[pref.synth_color ? "Yes" : "No"]") + "
" - if(pref.synth_color) - . += "Change Color [color_square(hex = pref.read_preference(/datum/preference/color/human/synth_color))]" - - . = jointext(.,null) - /datum/category_item/player_setup_item/general/body/proc/has_flag(var/datum/species/mob_species, var/flag) return mob_species && (mob_species.appearance_flags & flag) -/datum/category_item/player_setup_item/general/body/OnTopic(var/href,var/list/href_list, var/mob/user) - var/datum/species/mob_species = GLOB.all_species[pref.species] - - if(href_list["random"]) - pref.randomize_appearance_and_body_for() - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["change_descriptor"]) - if(mob_species.descriptors) - var/desc_id = href_list["change_descriptor"] - if(pref.body_descriptors[desc_id]) - var/datum/mob_descriptor/descriptor = mob_species.descriptors[desc_id] - var/choice = tgui_input_list(user, "Please select a descriptor.", "Descriptor", descriptor.chargen_value_descriptors) - if(choice && mob_species.descriptors[desc_id]) // Check in case they sneakily changed species. - pref.body_descriptors[desc_id] = descriptor.chargen_value_descriptors[choice] - return TOPIC_REFRESH - - else if(href_list["blood_type"]) - var/new_b_type = tgui_input_list(user, "Choose your character's blood-type:", "Character Preference", valid_bloodtypes) - if(new_b_type && CanUseTopic(user)) - pref.b_type = new_b_type - return TOPIC_REFRESH - - else if(href_list["show_species"]) - // Actual whitelist checks are handled elsewhere, this is just for accessing the preview window. - var/choice = tgui_input_list(user, "Which species would you like to look at?", "Species Choice", GLOB.playable_species) - if(!choice) return - pref.species_preview = choice - SetSpecies(preference_mob()) - pref.alternate_languages.Cut() // Reset their alternate languages. Todo: attempt to just fix it instead? - return TOPIC_HANDLED - - else if(href_list["set_species"]) - user << browse(null, "window=species") - if(!pref.species_preview || !(pref.species_preview in GLOB.all_species)) - return TOPIC_NOACTION - - var/datum/species/setting_species - - if(GLOB.all_species[href_list["set_species"]]) - setting_species = GLOB.all_species[href_list["set_species"]] - else - return TOPIC_NOACTION - - if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(user.client,setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) - return TOPIC_NOACTION - - var/prev_species = pref.species - pref.species = href_list["set_species"] - if(prev_species != pref.species) - if(!(pref.biological_gender in mob_species.genders)) - pref.set_biological_gender(mob_species.genders[1]) - pref.custom_species = null - //grab one of the valid hair styles for the newly chosen species - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) - - if(valid_hairstyles.len) - if(!(pref.h_style in valid_hairstyles)) - pref.h_style = pick(valid_hairstyles) - else - //this shouldn't happen - pref.h_style = hair_styles_list["Bald"] - - //grab one of the valid facial hair styles for the newly chosen species - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - - if(valid_facialhairstyles.len) - if(!(pref.f_style in valid_facialhairstyles)) - pref.f_style = pick(valid_facialhairstyles) - else - //this shouldn't happen - pref.f_style = facial_hair_styles_list["Shaved"] - - //reset hair colour and skin colour - pref.update_preference_by_type(/datum/preference/color/human/hair_color, "#000000") - pref.s_tone = -75 - - reset_limbs() // Safety for species with incompatible manufacturers; easier than trying to do it case by case. - pref.body_markings.Cut() // Basically same as above. - - pref.sanitize_body_styles() - - var/min_age = get_min_age() - var/max_age = get_max_age() - pref.update_preference_by_type(/datum/preference/numeric/human/age, max(min(pref.read_preference(/datum/preference/numeric/human/age), max_age), min_age)) - pref.blood_color = setting_species.blood_color - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_color"]) - if(!has_flag(mob_species, HAS_HAIR_COLOR)) - return TOPIC_NOACTION - var/new_hair = tgui_color_picker(user, "Choose your character's hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/hair_color)) - if(new_hair && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/color/human/hair_color, new_hair) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["grad_color"]) - if(!has_flag(mob_species, HAS_HAIR_COLOR)) - return TOPIC_NOACTION - var/new_grad = tgui_color_picker(user, "Choose your character's secondary hair color:", "Character Preference", pref.read_preference(/datum/preference/color/human/grad_color)) - if(new_grad && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/color/human/grad_color, new_grad) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_style"]) - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) - - var/new_h_style = tgui_input_list(user, "Choose your character's hair style:", "Character Preference", valid_hairstyles, pref.h_style) - if(new_h_style && CanUseTopic(user)) - pref.h_style = new_h_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["grad_style"]) - var/list/valid_gradients = GLOB.hair_gradients - - var/new_grad_style = tgui_input_list(user, "Choose a color pattern for your hair:", "Character Preference", valid_gradients, pref.grad_style) - if(new_grad_style && CanUseTopic(user)) - pref.grad_style = new_grad_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["grad_style_left"]) - var/H = href_list["grad_style_left"] - var/list/valid_gradients = GLOB.hair_gradients - var/start = valid_gradients.Find(H) - - if(start != 1) //If we're not the beginning of the list, become the previous element. - pref.grad_style = valid_gradients[start-1] - else //But if we ARE, become the final element. - pref.grad_style = valid_gradients[valid_gradients.len] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["grad_style_right"]) - var/H = href_list["grad_style_right"] - var/list/valid_gradients = GLOB.hair_gradients - var/start = valid_gradients.Find(H) - - if(start != valid_gradients.len) //If we're not the end of the list, become the next element. - pref.grad_style = valid_gradients[start+1] - else //But if we ARE, become the first element. - pref.grad_style = valid_gradients[1] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_style_left"]) - var/H = href_list["hair_style_left"] - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) - var/start = valid_hairstyles.Find(H) - - if(start != 1) //If we're not the beginning of the list, become the previous element. - pref.h_style = valid_hairstyles[start-1] - else //But if we ARE, become the final element. - pref.h_style = valid_hairstyles[valid_hairstyles.len] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_style_right"]) - var/H = href_list["hair_style_right"] - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) - var/start = valid_hairstyles.Find(H) - - if(start != valid_hairstyles.len) //If we're not the end of the list, become the next element. - pref.h_style = valid_hairstyles[start+1] - else //But if we ARE, become the first element. - pref.h_style = valid_hairstyles[1] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_color"]) - if(!has_flag(mob_species, HAS_HAIR_COLOR)) - return TOPIC_NOACTION - var/new_facial = tgui_color_picker(user, "Choose your character's facial-hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/facial_color)) - if(new_facial && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/color/human/facial_color, new_facial) - return TOPIC_REFRESH_UPDATE_PREVIEW - - if(href_list["digitigrade"]) - pref.digitigrade = !pref.digitigrade - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["eye_color"]) - if(!has_flag(mob_species, HAS_EYE_COLOR)) - return TOPIC_NOACTION - var/new_eyes = tgui_color_picker(user, "Choose your character's eye colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/eyes_color)) - if(new_eyes && has_flag(mob_species, HAS_EYE_COLOR) && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/color/human/eyes_color, new_eyes) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["skin_tone"]) - if(!has_flag(mob_species, HAS_SKIN_TONE)) - return TOPIC_NOACTION - var/new_s_tone = tgui_input_number(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference", (-pref.s_tone) + 35, 220, 1) - if(new_s_tone && has_flag(mob_species, HAS_SKIN_TONE) && CanUseTopic(user)) - pref.s_tone = 35 - max(min( round(new_s_tone), 220),1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["skin_color"]) - if(!has_flag(mob_species, HAS_SKIN_COLOR)) - return TOPIC_NOACTION - var/new_skin = tgui_color_picker(user, "Choose your character's skin colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/skin_color)) - if(new_skin && has_flag(mob_species, HAS_SKIN_COLOR) && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/color/human/skin_color, new_skin) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_style"]) - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - - var/new_f_style = tgui_input_list(user, "Choose your character's facial-hair style:", "Character Preference", valid_facialhairstyles, pref.f_style) - if(new_f_style && CanUseTopic(user)) - pref.f_style = new_f_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_style_left"]) - var/F = href_list["facial_style_left"] - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - var/start = valid_facialhairstyles.Find(F) - - if(start != 1) //If we're not the beginning of the list, become the previous element. - pref.f_style = valid_facialhairstyles[start-1] - else //But if we ARE, become the final element. - pref.f_style = valid_facialhairstyles[valid_facialhairstyles.len] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_style_right"]) - var/F = href_list["facial_style_right"] - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - var/start = valid_facialhairstyles.Find(F) - - if(start != valid_facialhairstyles.len) //If we're not the end of the list, become the next element. - pref.f_style = valid_facialhairstyles[start+1] - else //But if we ARE, become the first element. - pref.f_style = valid_facialhairstyles[1] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_style"]) - var/list/usable_markings = pref.body_markings.Copy() ^ body_marking_styles_list.Copy() - var/new_marking = tgui_input_list(user, "Choose a body marking:", "Character Preference", usable_markings) - if(new_marking && CanUseTopic(user)) - pref.body_markings[new_marking] = pref.mass_edit_marking_list(new_marking) //New markings start black - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_up"]) - var/M = href_list["marking_up"] - var/start = pref.body_markings.Find(M) - if(start != 1) //If we're not the beginning of the list, swap with the previous element. - moveElement(pref.body_markings, start, start-1) - else //But if we ARE, become the final element -ahead- of everything else. - moveElement(pref.body_markings, start, pref.body_markings.len+1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_down"]) - var/M = href_list["marking_down"] - var/start = pref.body_markings.Find(M) - if(start != pref.body_markings.len) //If we're not the end of the list, swap with the next element. - moveElement(pref.body_markings, start, start+2) - else //But if we ARE, become the first element -behind- everything else. - moveElement(pref.body_markings, start, 1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_move"]) - var/M = href_list["marking_move"] - var/start = pref.body_markings.Find(M) - var/list/move_locs = pref.body_markings - M - if(start != 1) - move_locs -= pref.body_markings[start-1] - - var/inject_after = tgui_input_list(user, "Move [M] ahead of...", "Character Preference", move_locs) //Move ahead of any marking that isn't the current or previous one. - var/newpos = pref.body_markings.Find(inject_after) - if(newpos) - moveElement(pref.body_markings, start, newpos+1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_remove"]) - var/M = href_list["marking_remove"] - winshow(user, "prefs_markings_subwindow", FALSE) - pref.body_markings -= M - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_color"]) - var/M = href_list["marking_color"] - if (isnull(pref.body_markings[M]["color"])) - if (tgui_alert(user, "You currently have customized marking colors. This will reset each bodypart's color. Are you sure you want to continue?","Reset Bodypart Colors",list("Yes","No")) != "Yes") - return TOPIC_NOACTION - var/current = pref.body_markings[M] ? pref.body_markings[M]["color"] : "#000000" - var/mark_color = tgui_color_picker(user, "Choose the [M] color: ", "Character Preference", current) - if(mark_color && CanUseTopic(user)) - pref.body_markings[M] = pref.mass_edit_marking_list(M,FALSE,TRUE,pref.body_markings[M],color="[mark_color]") - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["marking_submenu"]) - var/M = href_list["marking_submenu"] - markings_subwindow(user, M) - return TOPIC_NOACTION - - else if (href_list["toggle_all_marking_selection"]) - var/toggle = text2num(href_list["toggle"]) - var/marking = href_list["toggle_all_marking_selection"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - pref.body_markings[marking] = pref.mass_edit_marking_list(marking,TRUE,FALSE,pref.body_markings[marking],on=toggle) - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["color_all_marking_selection"]) - var/marking = href_list["color_all_marking_selection"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - var/mark_color = tgui_color_picker(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking]["color"]) - if(mark_color && CanUseTopic(user)) - pref.body_markings[marking] = pref.mass_edit_marking_list(marking,FALSE,TRUE,pref.body_markings[marking],color="[mark_color]") - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["zone_marking_color"]) - var/marking = href_list["zone_marking_color"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - var/zone = href_list["zone"] - pref.body_markings[marking]["color"] = null //turn off the color button outside the submenu - var/mark_color = tgui_color_picker(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking][zone]["color"]) - if(mark_color && CanUseTopic(user)) - pref.body_markings[marking][zone]["color"] = "[mark_color]" - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["zone_marking_toggle"]) - var/marking = href_list["zone_marking_toggle"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - var/zone = href_list["zone"] - pref.body_markings[marking][zone]["on"] = text2num(href_list["toggle"]) - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["reset_limbs"]) - reset_limbs() - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["limbs"]) - - var/list/limb_selection_list = list("Left Leg","Right Leg","Left Arm","Right Arm","Left Foot","Right Foot","Left Hand","Right Hand","Full Body") - - // Full prosthetic bodies without a brain are borderline unkillable so make sure they have a brain to remove/destroy. - var/datum/species/current_species = GLOB.all_species[pref.species] - if(!current_species.has_organ[O_BRAIN]) - limb_selection_list -= "Full Body" - else if(pref.organ_data[BP_TORSO] == "cyborg") - limb_selection_list |= "Head" - - var/organ_tag = tgui_input_list(user, "Which limb do you want to change?", "Limb Choice", limb_selection_list) - - if(!organ_tag || !CanUseTopic(user)) return TOPIC_NOACTION - - var/limb = null - var/second_limb = null // if you try to change the arm, the hand should also change - var/third_limb = null // if you try to unchange the hand, the arm should also change - - // Do not let them amputate their entire body, ty. - var/list/choice_options = list("Normal","Amputated","Prosthesis") - switch(organ_tag) - if("Left Leg") - limb = BP_L_LEG - second_limb = BP_L_FOOT - if("Right Leg") - limb = BP_R_LEG - second_limb = BP_R_FOOT - if("Left Arm") - limb = BP_L_ARM - second_limb = BP_L_HAND - if("Right Arm") - limb = BP_R_ARM - second_limb = BP_R_HAND - if("Left Foot") - limb = BP_L_FOOT - third_limb = BP_L_LEG - if("Right Foot") - limb = BP_R_FOOT - third_limb = BP_R_LEG - if("Left Hand") - limb = BP_L_HAND - third_limb = BP_L_ARM - if("Right Hand") - limb = BP_R_HAND - third_limb = BP_R_ARM - if("Head") - limb = BP_HEAD - choice_options = list("Prosthesis") - if("Full Body") - limb = BP_TORSO - second_limb = BP_HEAD - third_limb = BP_GROIN - choice_options = list("Normal","Prosthesis") - - var/new_state = tgui_input_list(user, "What state do you wish the limb to be in?", "State Choice", choice_options) - if(!new_state || !CanUseTopic(user)) return TOPIC_NOACTION - - switch(new_state) - if("Normal") - pref.organ_data[limb] = null - pref.rlimb_data[limb] = null - if(limb == BP_TORSO) - for(var/other_limb in BP_ALL - BP_TORSO) - pref.organ_data[other_limb] = null - pref.rlimb_data[other_limb] = null - for(var/internal in O_STANDARD) - pref.organ_data[internal] = null - pref.rlimb_data[internal] = null - if(third_limb) - pref.organ_data[third_limb] = null - pref.rlimb_data[third_limb] = null - - if("Amputated") - if(limb == BP_TORSO) - return - pref.organ_data[limb] = "amputated" - pref.rlimb_data[limb] = null - if(second_limb) - pref.organ_data[second_limb] = "amputated" - pref.rlimb_data[second_limb] = null - - if("Prosthesis") - var/tmp_species = pref.species ? pref.species : SPECIES_HUMAN - var/list/usable_manufacturers = list() - for(var/company in GLOB.chargen_robolimbs) - var/datum/robolimb/M = GLOB.chargen_robolimbs[company] - if(!(limb in M.parts)) - continue - if(tmp_species in M.species_cannot_use) - continue - if(M.whitelisted_to && !(user.ckey in M.whitelisted_to)) - continue - usable_manufacturers[company] = M - if(!usable_manufacturers.len) - return - var/choice = tgui_input_list(user, "Which manufacturer do you wish to use for this limb?", "Manufacturer Choice", usable_manufacturers) - if(!choice) - return - - pref.rlimb_data[limb] = choice - pref.organ_data[limb] = "cyborg" - - if(second_limb) - pref.rlimb_data[second_limb] = choice - pref.organ_data[second_limb] = "cyborg" - if(third_limb && pref.organ_data[third_limb] == "amputated") - pref.organ_data[third_limb] = null - - if(limb == BP_TORSO) - for(var/other_limb in BP_ALL - BP_TORSO) - if(pref.organ_data[other_limb]) - continue - pref.organ_data[other_limb] = "cyborg" - pref.rlimb_data[other_limb] = choice - if(!pref.organ_data[O_BRAIN]) - pref.organ_data[O_BRAIN] = "assisted" - for(var/internal_organ in list(O_HEART,O_EYES)) - pref.organ_data[internal_organ] = "mechanical" - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["organs"]) - - var/organ_name = tgui_input_list(user, "Which internal function do you want to change?", "Internal Organ", list("Heart", "Eyes", "Larynx", "Lungs", "Liver", "Kidneys", "Spleen", "Intestines", "Stomach", "Brain")) - if(!organ_name) return - - var/organ = null - switch(organ_name) - if("Heart") - organ = O_HEART - if("Eyes") - organ = O_EYES - if("Larynx") - organ = O_VOICE - if("Lungs") - organ = O_LUNGS - if("Liver") - organ = O_LIVER - if("Kidneys") - organ = O_KIDNEYS - if("Spleen") - organ = O_SPLEEN - if("Intestines") - organ = O_INTESTINE - if("Stomach") - organ = O_STOMACH - if("Brain") - if(pref.organ_data[BP_HEAD] != "cyborg") - to_chat(user, span_warning("You may only select a cybernetic or synthetic brain if you have a full prosthetic body.")) - return - organ = O_BRAIN - - var/datum/species/current_species = GLOB.all_species[pref.species] - var/list/organ_choices = list("Normal") - if(pref.organ_data[BP_TORSO] == "cyborg") - organ_choices -= "Normal" - if(organ_name == "Brain") - organ_choices += "Cybernetic" - if(!(current_species.spawn_flags & SPECIES_NO_POSIBRAIN)) - organ_choices += "Positronic" - if(!(current_species.spawn_flags & SPECIES_NO_DRONEBRAIN)) - organ_choices += "Drone" - else - organ_choices += "Assisted" - organ_choices += "Mechanical" - else - organ_choices += "Assisted" - organ_choices += "Mechanical" - - var/new_state = tgui_input_list(user, "What state do you wish the organ to be in?", "State Choice", organ_choices) - if(!new_state) return - - switch(new_state) - if("Normal") - pref.organ_data[organ] = null - if("Assisted") - pref.organ_data[organ] = "assisted" - if("Cybernetic") - pref.organ_data[organ] = "assisted" - if("Mechanical") - pref.organ_data[organ] = "mechanical" - if("Drone") - pref.organ_data[organ] = "digital" - if("Positronic") - pref.organ_data[organ] = "mechanical" - - return TOPIC_REFRESH - - else if(href_list["toggle_preview_value"]) - pref.equip_preview_mob ^= text2num(href_list["toggle_preview_value"]) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["toggle_animations"]) - pref.animations_toggle = !pref.animations_toggle - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["synth_color"]) - pref.synth_color = !pref.synth_color - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["synth2_color"]) - var/new_color = tgui_color_picker(user, "Choose your character's synth colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/synth_color)) - if(new_color && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/color/human/synth_color, new_color) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["synth_markings"]) - pref.synth_markings = !pref.synth_markings - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["cycle_bg"]) - pref.bgstate = next_in_list(pref.bgstate, pref.bgstate_options) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_style"]) - var/new_ear_style = tgui_input_list(user, "Select an ear style for this character:", "Character Preference", pref.get_available_styles(global.ear_styles_list), pref.ear_style) - if(new_ear_style) - pref.ear_style = new_ear_style - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_color"]) - var/new_earc = tgui_color_picker(user, "Choose your character's ear colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/ears_color1)) - if(new_earc) - pref.update_preference_by_type(/datum/preference/color/human/ears_color1, new_earc) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_color2"]) - var/new_earc2 = tgui_color_picker(user, "Choose your character's secondary ear colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/ears_color2)) - if(new_earc2) - pref.update_preference_by_type(/datum/preference/color/human/ears_color2, new_earc2) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_color3"]) - var/new_earc3 = tgui_color_picker(user, "Choose your character's tertiary ear colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/ears_color3)) - if(new_earc3) - pref.update_preference_by_type(/datum/preference/color/human/ears_color3, new_earc3) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["ears_alpha"]) - var/new_ear_alpha = tgui_input_number(user, "Choose how transparent your character's primary ears are.", "Character Preference", - pref.read_preference(/datum/preference/numeric/human/ears_alpha), 255, 0) - if(new_ear_alpha) - pref.update_preference_by_type(/datum/preference/numeric/human/ears_alpha, new_ear_alpha) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["secondary_ears_alpha"]) - var/new_ear_alpha = tgui_input_number(user, "Choose how transparent your character's horns are.", "Character Preference", - pref.read_preference(/datum/preference/numeric/human/ears_alpha/secondary), 255, 0) - if(new_ear_alpha) - pref.update_preference_by_type(/datum/preference/numeric/human/ears_alpha/secondary, new_ear_alpha) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_secondary_style"]) - var/new_style = tgui_input_list(user, "Select an ear style for this character:", "Character Preference", pref.get_available_styles(global.ear_styles_list), pref.ear_secondary_style) - if(!new_style) - return TOPIC_NOACTION - pref.ear_secondary_style = new_style - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["ear_secondary_color"]) - var/channel = text2num(href_list["ear_secondary_color"]) - // very important sanity check; this makes sure someone can't crash the server by setting channel to some insanely high value - if(channel > GLOB.fancy_sprite_accessory_color_channel_names.len) - return TOPIC_NOACTION - // this would say 'secondary ears' but you'd get 'choose your character's primary secondary ear colour' which sounds silly - var/new_color = tgui_color_picker( - user, - "Choose your character's [lowertext(GLOB.fancy_sprite_accessory_color_channel_names[channel])] ear colour:", - "Secondary Ear Coloration", - LAZYACCESS(pref.ear_secondary_colors, channel) || "#ffffff", - ) - if(!new_color) - return TOPIC_NOACTION - // ensures color channel list is at least that long - // the upper bound is to have a secondary safety check because list index set is a dangerous call - pref.ear_secondary_colors.len = clamp(length(pref.ear_secondary_colors), channel, length(GLOB.fancy_sprite_accessory_color_channel_names)) - pref.ear_secondary_colors[channel] = new_color - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_style"]) - var/new_tail_style = tgui_input_list(user, "Select a tail style for this character:", "Character Preference", pref.get_available_styles(global.tail_styles_list), pref.tail_style) - if(new_tail_style) - pref.tail_style = new_tail_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_color"]) - var/new_tailc = tgui_color_picker(user, "Choose your character's tail/taur colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/tail_color1)) - if(new_tailc) - pref.update_preference_by_type(/datum/preference/color/human/tail_color1, new_tailc) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_color2"]) - var/new_tailc2 = tgui_color_picker(user, "Choose your character's secondary tail/taur colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/tail_color2)) - if(new_tailc2) - pref.update_preference_by_type(/datum/preference/color/human/tail_color2, new_tailc2) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_color3"]) - var/new_tailc3 = tgui_color_picker(user, "Choose your character's tertiary tail/taur colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/tail_color3)) - if(new_tailc3) - pref.update_preference_by_type(/datum/preference/color/human/tail_color3, new_tailc3) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_alpha"]) - var/new_tail_alpha = tgui_input_number(user, "Choose how transparent your character's tail is.", "Character Preference", - pref.read_preference(/datum/preference/numeric/human/tail_alpha), 255, 0) - if(new_tail_alpha) - pref.update_preference_by_type(/datum/preference/numeric/human/tail_alpha, new_tail_alpha) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_style"]) - var/new_wing_style = tgui_input_list(user, "Select a wing style for this character:", "Character Preference", pref.get_available_styles(global.wing_styles_list), pref.wing_style) - if(new_wing_style) - pref.wing_style = new_wing_style - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_color"]) - var/new_wingc = tgui_color_picker(user, "Choose your character's wing colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/wing_color1)) - if(new_wingc) - pref.update_preference_by_type(/datum/preference/color/human/wing_color1, new_wingc) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_color2"]) - var/new_wingc = tgui_color_picker(user, "Choose your character's secondary wing colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/wing_color2)) - if(new_wingc) - pref.update_preference_by_type(/datum/preference/color/human/wing_color2, new_wingc) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_color3"]) - var/new_wingc = tgui_color_picker(user, "Choose your character's tertiary wing colour:", "Character Preference", - pref.read_preference(/datum/preference/color/human/wing_color3)) - if(new_wingc) - pref.update_preference_by_type(/datum/preference/color/human/wing_color3, new_wingc) - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["wing_alpha"]) - var/new_wing_alpha = tgui_input_number(user, "Choose how transparent your character's wings are.", "Character Preference", - pref.read_preference(/datum/preference/numeric/human/wing_alpha), 255, 0) - if(new_wing_alpha) - pref.update_preference_by_type(/datum/preference/numeric/human/wing_alpha, new_wing_alpha) - return TOPIC_REFRESH_UPDATE_PREVIEW - return ..() - /datum/category_item/player_setup_item/general/body/proc/reset_limbs() - for(var/organ in pref.organ_data) pref.organ_data[organ] = null while(null in pref.organ_data) @@ -1208,98 +276,742 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O if(!pref.real_name) pref.real_name = random_name(pref.identifying_gender, pref.species) -/datum/category_item/player_setup_item/general/body/proc/SetSpecies(mob/user) - if(!pref.species_preview || !(pref.species_preview in GLOB.all_species)) - pref.species_preview = SPECIES_HUMAN - var/datum/species/current_species = GLOB.all_species[pref.species_preview] - var/dat = "" - dat += "

[current_species.name] \[change\]


" - dat += "" - dat += "" - if(current_species.wikilink) - dat += "" - else - dat += "" - dat += "" - dat += "" - dat += "
[current_species.blurb]

See the wiki for more details.
[current_species.blurb]" - if("preview" in cached_icon_states(current_species.icobase)) - user << browse_rsc(icon(current_species.icobase,"preview"), "species_preview_[current_species.name].png") - dat += "

" - dat += span_bold("Language:") + " [current_species.species_language]
" - dat += "" - if(current_species.spawn_flags & SPECIES_CAN_JOIN) - switch(current_species.rarity_value) - if(1 to 2) - dat += "
" + span_bold("Often present on human stations.") - if(3 to 4) - dat += "
" + span_bold("Rarely present on human stations.") - if(5) - dat += "
" + span_bold("Unheard of on human stations.") +/datum/category_item/player_setup_item/general/body/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + + data["species"] = pref.species + data["organ_data"] = pref.organ_data + data["rlimb_data"] = pref.rlimb_data + + data["s_tone"] = -pref.s_tone + 35 + data["eyes_color"] = pref.read_preference(/datum/preference/color/human/eyes_color) + data["skin_color"] = pref.read_preference(/datum/preference/color/human/skin_color) + + data["h_style"] = pref.h_style + data["hair_color"] = pref.read_preference(/datum/preference/color/human/hair_color) + + data["f_style"] = pref.f_style + data["facial_color"] = pref.read_preference(/datum/preference/color/human/facial_color) + + data["grad_style"] = pref.grad_style + data["grad_color"] = pref.read_preference(/datum/preference/color/human/grad_color) + + data["ear_style"] = pref.ear_style + data["ears_color1"] = pref.read_preference(/datum/preference/color/human/ears_color1) + data["ears_color2"] = pref.read_preference(/datum/preference/color/human/ears_color2) + data["ears_color3"] = pref.read_preference(/datum/preference/color/human/ears_color3) + data["ears_alpha"] = pref.read_preference(/datum/preference/numeric/human/ears_alpha) + + data["ear_secondary_style"] = pref.ear_secondary_style + data["ear_secondary_colors"] = pref.ear_secondary_colors + data["ear_secondary_alpha"] = pref.read_preference(/datum/preference/numeric/human/ears_alpha/secondary) + + data["body_markings"] = pref.body_markings + + data["tail_style"] = pref.tail_style + data["tail_color1"] = pref.read_preference(/datum/preference/color/human/tail_color1) + data["tail_color2"] = pref.read_preference(/datum/preference/color/human/tail_color2) + data["tail_color3"] = pref.read_preference(/datum/preference/color/human/tail_color3) + data["tail_alpha"] = pref.read_preference(/datum/preference/numeric/human/tail_alpha) + + data["wing_style"] = pref.wing_style + data["wing_color1"] = pref.read_preference(/datum/preference/color/human/wing_color1) + data["wing_color2"] = pref.read_preference(/datum/preference/color/human/wing_color2) + data["wing_color3"] = pref.read_preference(/datum/preference/color/human/wing_color3) + data["wing_alpha"] = pref.read_preference(/datum/preference/numeric/human/wing_alpha) + + data["b_type"] = pref.b_type + data["digitigrade"] = pref.digitigrade + + data["synth_color_toggle"] = pref.synth_color + data["synth_color"] = pref.read_preference(/datum/preference/color/human/synth_color) + data["synth_markings"] = pref.synth_markings + + return data + +/datum/category_item/player_setup_item/general/body/tgui_static_data(mob/user) + var/list/data = ..() + + var/list/can_play_list = list() + for(var/species in GLOB.playable_species) + var/datum/species/S = GLOB.all_species[species] + var/restricted = 0 + if(!(S.spawn_flags & SPECIES_CAN_JOIN)) + restricted = 2 + else if(!is_alien_whitelisted(user.client, S)) + restricted = 1 + + var/can_select = !restricted + if(check_rights(R_ADMIN|R_EVENT, 0) || S.spawn_flags & SPECIES_WHITELIST_SELECTABLE) + can_select = TRUE + + can_play_list[species] = list( + "restricted" = restricted, + "can_select" = can_select, + ) + data["can_play"] = can_play_list + + data["digi_allowed"] = GLOB.all_species[pref.species].digi_allowed + + var/list/available_hair_styles = list() + for(var/path in pref.get_available_styles(hair_styles_list)) + UNTYPED_LIST_ADD(available_hair_styles, path) + data["available_hair_styles"] = available_hair_styles + + var/list/available_facial_styles = list() + for(var/path in pref.get_available_styles(facial_hair_styles_list)) + UNTYPED_LIST_ADD(available_facial_styles, path) + data["available_facial_styles"] = available_facial_styles + + // WARNING: Depends on adding "None" + var/list/available_ear_styles = list("None") + for(var/path in pref.get_available_styles(ear_styles_list)) + UNTYPED_LIST_ADD(available_ear_styles, path) + data["available_ear_styles"] = available_ear_styles + + var/list/available_tail_styles = list() + for(var/path in pref.get_available_styles(tail_styles_list)) + UNTYPED_LIST_ADD(available_tail_styles, path) + data["available_tail_styles"] = available_tail_styles + + var/list/available_wing_styles = list() + for(var/path in pref.get_available_styles(wing_styles_list)) + UNTYPED_LIST_ADD(available_wing_styles, path) + data["available_wing_styles"] = available_wing_styles + + return data + +/datum/category_item/player_setup_item/general/body/tgui_constant_data() + var/list/data = ..() + + var/list/species_list = list() + for(var/species in GLOB.playable_species) + var/datum/species/S = GLOB.all_species[species] + UNTYPED_LIST_ADD(species_list, list( + "name" = S.name, + "wikilink" = S.wikilink, + "blurb" = S.blurb, + "species_language" = S.species_language, + "icobase" = S.icobase, + "rarity" = S.rarity_value, + "has_organ" = S.has_organ, + "flags" = S.flags, + "spawn_flags" = S.spawn_flags, + "appearance_flags" = S.appearance_flags, + )) + data["species"] = species_list + + var/list/hair_styles = list() + for(var/path in hair_styles_list) + var/datum/sprite_accessory/hair/S = hair_styles_list[path] + hair_styles[path] = list( + "name" = S.name, + "icon" = REF(S.icon), + "icon_state" = S.icon_state, + ) + + data["hair_styles"] = hair_styles + + var/list/facial_styles = list() + for(var/path in facial_hair_styles_list) + var/datum/sprite_accessory/facial_hair/S = facial_hair_styles_list[path] + facial_styles[path] = list( + "name" = S.name, + "icon" = REF(S.icon), + "icon_state" = S.icon_state, + ) + + data["facial_styles"] = facial_styles + + var/list/grad_styles = list() + for(var/name in GLOB.hair_gradients) + var/icon_state = GLOB.hair_gradients[name] + grad_styles[name] = list( + "name" = name, + "icon" = REF('icons/mob/hair_gradients.dmi'), + "icon_state" = icon_state, + ) + data["grad_styles"] = grad_styles + + var/list/ear_styles = list() + for(var/path in ear_styles_list) + var/datum/sprite_accessory/ears/S = ear_styles_list[path] + ear_styles[S.name] = list( + "name" = S.name, + "type" = S.type, + "icon" = REF(S.icon), + "icon_state" = S.icon_state, + "do_colouration" = S.do_colouration, + "extra_overlay" = S.extra_overlay, + "extra_overlay2" = S.extra_overlay2, + ) + data["ear_styles"] = ear_styles + + var/list/body_markings = list() + for(var/path in body_marking_styles_list) + var/datum/sprite_accessory/marking/S = body_marking_styles_list[path] + + var/icon_state = S.icon_state + if(LAZYLEN(S.body_parts)) + icon_state += "-[S.body_parts[1]]" + + body_markings[path] = list( + "name" = S.name, + "icon" = REF(S.icon), + "icon_state" = icon_state, + "genetic" = S.genetic, + "body_parts" = S.body_parts, + ) + data["body_markings"] = body_markings + + var/list/tail_styles = list() + for(var/path in tail_styles_list) + var/datum/sprite_accessory/tail/S = tail_styles_list[path] + tail_styles[S.name] = list( + "name" = name, + "icon" = REF(S.icon), + "icon_state" = S.icon_state, + "do_colouration" = S.do_colouration, + "extra_overlay" = S.extra_overlay, + "extra_overlay2" = S.extra_overlay2, + ) + data["tail_styles"] = tail_styles + + var/list/wing_styles = list() + for(var/path in wing_styles_list) + var/datum/sprite_accessory/wing/S = wing_styles_list[path] + wing_styles[S.name] = list( + "name" = S.name, + "icon" = REF(S.icon), + "icon_state" = S.icon_state, + "do_colouration" = S.do_colouration, + "extra_overlay" = S.extra_overlay, + "extra_overlay2" = S.extra_overlay2, + ) + data["wing_styles"] = wing_styles + + return data + +/datum/category_item/player_setup_item/general/body/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/datum/species/mob_species = GLOB.all_species[pref.species] + var/mob/user = ui.user + + switch(action) + /* Hair */ + if("set_hair_style") + var/new_h_style = params["hair_style"] + if(new_h_style in pref.get_available_styles(hair_styles_list)) + pref.h_style = new_h_style + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_hair_color") + if(!has_flag(mob_species, HAS_HAIR_COLOR)) + return TOPIC_NOACTION + var/new_hair = tgui_color_picker(user, "Choose your character's hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/hair_color)) + if(new_hair && has_flag(mob_species, HAS_HAIR_COLOR)) + pref.update_preference_by_type(/datum/preference/color/human/hair_color, new_hair) + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Facial Hair */ + if("set_facial_hair_style") + var/new_f_style = params["facial_hair_style"] + if(new_f_style in pref.get_available_styles(facial_hair_styles_list)) + pref.f_style = new_f_style + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_facial_hair_color") + if(!has_flag(mob_species, HAS_HAIR_COLOR)) + return TOPIC_NOACTION + var/new_facial = tgui_color_picker(user, "Choose your character's facial-hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/facial_color)) + if(new_facial && has_flag(mob_species, HAS_HAIR_COLOR)) + pref.update_preference_by_type(/datum/preference/color/human/facial_color, new_facial) + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Gradient */ + if("set_grad_style") + // Note: Must pass in NAME not icon_state + var/new_grad_style = params["grad_style"] + if(new_grad_style in GLOB.hair_gradients) + pref.grad_style = new_grad_style + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_grad_color") + if(!has_flag(mob_species, HAS_HAIR_COLOR)) + return TOPIC_NOACTION + var/new_grad = tgui_color_picker(user, "Choose your character's secondary hair color:", "Character Preference", pref.read_preference(/datum/preference/color/human/grad_color)) + if(new_grad && has_flag(mob_species, HAS_HAIR_COLOR)) + pref.update_preference_by_type(/datum/preference/color/human/grad_color, new_grad) + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Markings */ + if("add_marking") + var/list/usable_markings = pref.body_markings.Copy() ^ body_marking_styles_list.Copy() + var/new_marking = params["new_marking"] + if(new_marking && (new_marking in usable_markings)) + pref.body_markings[new_marking] = pref.mass_edit_marking_list(new_marking) //New markings start black + return TOPIC_REFRESH_UPDATE_PREVIEW + if("marking_up") + var/M = params["marking"] + var/start = pref.body_markings.Find(M) + if(start != 1) //If we're not the beginning of the list, swap with the previous element. + moveElement(pref.body_markings, start, start-1) + else //But if we ARE, become the final element -ahead- of everything else. + moveElement(pref.body_markings, start, pref.body_markings.len+1) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("marking_down") + var/M = params["marking"] + var/start = pref.body_markings.Find(M) + if(start != pref.body_markings.len) //If we're not the end of the list, swap with the next element. + moveElement(pref.body_markings, start, start+2) + else //But if we ARE, become the first element -behind- everything else. + moveElement(pref.body_markings, start, 1) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("marking_remove") + var/M = params["marking"] + winshow(user, "prefs_markings_subwindow", FALSE) + pref.body_markings -= M + return TOPIC_REFRESH_UPDATE_PREVIEW + if("marking_color") + var/M = params["marking"] + if(isnull(pref.body_markings[M]["color"])) + if(tgui_alert(user, "You currently have customized marking colors. This will reset each bodypart's color. Are you sure you want to continue?","Reset Bodypart Colors",list("Yes","No")) != "Yes") + return TOPIC_NOACTION + var/mark_color = tgui_color_picker(user, "Choose the [M] color: ", "Character Preference", pref.body_markings[M]["color"]) + if(mark_color) + pref.body_markings[M] = pref.mass_edit_marking_list(M,FALSE,TRUE,pref.body_markings[M],color="[mark_color]") + return TOPIC_REFRESH_UPDATE_PREVIEW + if("toggle_all_marking_selection") + var/toggle = text2num(params["toggle"]) + var/marking = params["marking"] + if(pref.body_markings.Find(marking) == 0) + return TOPIC_NOACTION + pref.body_markings[marking] = pref.mass_edit_marking_list(marking,TRUE,FALSE,pref.body_markings[marking],on=toggle) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("color_all_marking_selection") + var/marking = params["marking"] + if(pref.body_markings.Find(marking) == 0) + return TOPIC_NOACTION + var/mark_color = tgui_color_picker(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking]["color"]) + if(mark_color) + pref.body_markings[marking] = pref.mass_edit_marking_list(marking,FALSE,TRUE,pref.body_markings[marking],color="[mark_color]") + return TOPIC_REFRESH_UPDATE_PREVIEW + if("zone_marking_color") + var/marking = params["marking"] + if(pref.body_markings.Find(marking) == 0) + return TOPIC_NOACTION + var/zone = params["zone"] + pref.body_markings[marking]["color"] = null //turn off the color button outside the submenu + var/mark_color = tgui_color_picker(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking][zone]["color"]) + if(mark_color) + pref.body_markings[marking][zone]["color"] = "[mark_color]" + return TOPIC_REFRESH_UPDATE_PREVIEW + if("zone_marking_toggle") + var/marking = params["marking"] + if(pref.body_markings.Find(marking) == 0) + return TOPIC_NOACTION + var/zone = params["zone"] + pref.body_markings[marking][zone]["on"] = !pref.body_markings[marking][zone]["on"] + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Ears */ + if("set_ear_style") + var/new_ear_style = params["ear_style"] + if(new_ear_style == "None") + pref.ear_style = null + return TOPIC_REFRESH_UPDATE_PREVIEW + if(new_ear_style in pref.get_available_styles(global.ear_styles_list)) + pref.ear_style = new_ear_style + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_ear_color") + var/new_earc = tgui_color_picker(user, "Choose your character's ear colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/ears_color1)) + if(new_earc) + pref.update_preference_by_type(/datum/preference/color/human/ears_color1, new_earc) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_ear_color2") + var/new_earc2 = tgui_color_picker(user, "Choose your character's secondary ear colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/ears_color2)) + if(new_earc2) + pref.update_preference_by_type(/datum/preference/color/human/ears_color2, new_earc2) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_ear_color3") + var/new_earc3 = tgui_color_picker(user, "Choose your character's tertiary ear colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/ears_color3)) + if(new_earc3) + pref.update_preference_by_type(/datum/preference/color/human/ears_color3, new_earc3) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_ear_secondary_style") + var/new_ear_style = params["ear_style"] + if(new_ear_style == "None") + pref.ear_secondary_style = null + return TOPIC_REFRESH_UPDATE_PREVIEW + if(new_ear_style in pref.get_available_styles(ear_styles_list)) + pref.ear_secondary_style = new_ear_style + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_ear_secondary_color") + var/channel = text2num(params["ear_secondary_color"]) + // very important sanity check; this makes sure someone can't crash the server by setting channel to some insanely high value + if(channel > GLOB.fancy_sprite_accessory_color_channel_names.len) + return TOPIC_NOACTION + // this would say 'secondary ears' but you'd get 'choose your character's primary secondary ear colour' which sounds silly + var/new_color = tgui_color_picker( + user, + "Choose your character's [lowertext(GLOB.fancy_sprite_accessory_color_channel_names[channel])] ear colour:", + "Secondary Ear Coloration", + LAZYACCESS(pref.ear_secondary_colors, channel) || "#ffffff", + ) + if(!new_color) + return TOPIC_NOACTION + // ensures color channel list is at least that long + // the upper bound is to have a secondary safety check because list index set is a dangerous call + pref.ear_secondary_colors.len = clamp(length(pref.ear_secondary_colors), channel, length(GLOB.fancy_sprite_accessory_color_channel_names)) + pref.ear_secondary_colors[channel] = new_color + return TOPIC_REFRESH_UPDATE_PREVIEW + if("ears_alpha") + var/new_ear_alpha = tgui_input_number(user, "Choose how transparent your character's primary ears are.", "Character Preference", + pref.read_preference(/datum/preference/numeric/human/ears_alpha), 255, 0) + if(new_ear_alpha) + pref.update_preference_by_type(/datum/preference/numeric/human/ears_alpha, new_ear_alpha) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("secondary_ears_alpha") + var/new_ear_alpha = tgui_input_number(user, "Choose how transparent your character's horns are.", "Character Preference", + pref.read_preference(/datum/preference/numeric/human/ears_alpha/secondary), 255, 0) + if(new_ear_alpha) + pref.update_preference_by_type(/datum/preference/numeric/human/ears_alpha/secondary, new_ear_alpha) + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Wings */ + if("set_wing_style") + var/new_style = params["style"] + if(new_style in pref.get_available_styles(global.wing_styles_list)) + pref.wing_style = new_style + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_wing_color") + var/new_wingc = tgui_color_picker(user, "Choose your character's wing colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/wing_color1)) + if(new_wingc) + pref.update_preference_by_type(/datum/preference/color/human/wing_color1, new_wingc) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_wing_color2") + var/new_wingc = tgui_color_picker(user, "Choose your character's secondary wing colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/wing_color2)) + if(new_wingc) + pref.update_preference_by_type(/datum/preference/color/human/wing_color2, new_wingc) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_wing_color3") + var/new_wingc = tgui_color_picker(user, "Choose your character's tertiary wing colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/wing_color3)) + if(new_wingc) + pref.update_preference_by_type(/datum/preference/color/human/wing_color3, new_wingc) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_wing_alpha") + var/new_wing_alpha = tgui_input_number(user, "Choose how transparent your character's wings are.", "Character Preference", + pref.read_preference(/datum/preference/numeric/human/wing_alpha), 255, 0) + if(new_wing_alpha) + pref.update_preference_by_type(/datum/preference/numeric/human/wing_alpha, new_wing_alpha) + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Tail */ + if("set_tail_style") + var/new_tail_style = params["style"] + if(new_tail_style in pref.get_available_styles(global.tail_styles_list)) + pref.tail_style = new_tail_style + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_tail_color") + var/new_tailc = tgui_color_picker(user, "Choose your character's tail/taur colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/tail_color1)) + if(new_tailc) + pref.update_preference_by_type(/datum/preference/color/human/tail_color1, new_tailc) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_tail_color2") + var/new_tailc2 = tgui_color_picker(user, "Choose your character's secondary tail/taur colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/tail_color2)) + if(new_tailc2) + pref.update_preference_by_type(/datum/preference/color/human/tail_color2, new_tailc2) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_tail_color3") + var/new_tailc3 = tgui_color_picker(user, "Choose your character's tertiary tail/taur colour:", "Character Preference", + pref.read_preference(/datum/preference/color/human/tail_color3)) + if(new_tailc3) + pref.update_preference_by_type(/datum/preference/color/human/tail_color3, new_tailc3) + return TOPIC_REFRESH_UPDATE_PREVIEW + if("set_tail_alpha") + var/new_tail_alpha = tgui_input_number(user, "Choose how transparent your character's tail is.", "Character Preference", + pref.read_preference(/datum/preference/numeric/human/tail_alpha), 255, 0) + if(new_tail_alpha) + pref.update_preference_by_type(/datum/preference/numeric/human/tail_alpha, new_tail_alpha) + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Species */ + if("set_species") + var/datum/species/setting_species + + if(!GLOB.all_species[params["species"]]) + return TOPIC_NOACTION + + setting_species = GLOB.all_species[params["species"]] + + if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(user.client,setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) + return TOPIC_NOACTION + + var/prev_species = pref.species + pref.species = params["species"] + if(prev_species != pref.species) + if(!(pref.biological_gender in mob_species.genders)) + pref.set_biological_gender(mob_species.genders[1]) + pref.custom_species = null + //grab one of the valid hair styles for the newly chosen species + var/list/valid_hairstyles = pref.get_valid_hairstyles(user) + + if(valid_hairstyles.len) + if(!(pref.h_style in valid_hairstyles)) + pref.h_style = pick(valid_hairstyles) + else + //this shouldn't happen + pref.h_style = hair_styles_list["Bald"] + + //grab one of the valid facial hair styles for the newly chosen species + var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() + + if(valid_facialhairstyles.len) + if(!(pref.f_style in valid_facialhairstyles)) + pref.f_style = pick(valid_facialhairstyles) + else + //this shouldn't happen + pref.f_style = facial_hair_styles_list["Shaved"] + + //reset hair colour and skin colour + pref.update_preference_by_type(/datum/preference/color/human/hair_color, "#000000") + pref.s_tone = -75 + + reset_limbs() // Safety for species with incompatible manufacturers; easier than trying to do it case by case. + pref.body_markings.Cut() // Basically same as above. + + pref.sanitize_body_styles() + + var/min_age = get_min_age() + var/max_age = get_max_age() + pref.update_preference_by_type(/datum/preference/numeric/human/age, max(min(pref.read_preference(/datum/preference/numeric/human/age), max_age), min_age)) + pref.blood_color = setting_species.blood_color + + update_static_data_for_all_viewers() + return TOPIC_REFRESH_UPDATE_PREVIEW + + // Colors + if("eye_color") + if(!has_flag(mob_species, HAS_EYE_COLOR)) + return TOPIC_NOACTION + var/new_eyes = tgui_color_picker(user, "Choose your character's eye colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/eyes_color)) + if(new_eyes && has_flag(mob_species, HAS_EYE_COLOR)) + pref.update_preference_by_type(/datum/preference/color/human/eyes_color, new_eyes) + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("skin_tone") + if(!has_flag(mob_species, HAS_SKIN_TONE)) + return TOPIC_NOACTION + var/new_s_tone = tgui_input_number(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference", (-pref.s_tone) + 35, 220, 1) + if(new_s_tone && has_flag(mob_species, HAS_SKIN_TONE)) + pref.s_tone = 35 - max(min( round(new_s_tone), 220),1) + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("skin_color") + if(!has_flag(mob_species, HAS_SKIN_COLOR)) + return TOPIC_NOACTION + var/new_skin = tgui_color_picker(user, "Choose your character's skin colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/skin_color)) + if(new_skin && has_flag(mob_species, HAS_SKIN_COLOR)) + pref.update_preference_by_type(/datum/preference/color/human/skin_color, new_skin) + return TOPIC_REFRESH_UPDATE_PREVIEW + + /* Robolimbs */ + if("robolimb_select_bodypart") + var/zone = params["zone"] + if(!(zone in BP_ALL)) + return TOPIC_HANDLED + + var/is_brainless = !mob_species.has_organ[O_BRAIN] + + // Full prosthetic bodies without a brain are borderline unkillable so make sure they have a brain to remove/destroy. + if(is_brainless && (zone in list(BP_HEAD, BP_TORSO, BP_GROIN))) + return TOPIC_HANDLED + + // we have a valid zone, let's figure out what options are avaliable for it + var/list/options = list() + switch(zone) + if(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM) + options = list("Normal", "Amputated", "Prosthesis") + if(BP_HEAD) + if(is_FBP()) + options = list("Prosthesis") + else + options = list("Normal") + if(BP_TORSO, BP_GROIN) + options = list("Normal", "Prosthesis") + + var/new_state = tgui_input_list(user, "What state do you wish the limb to be in?", "State Choice", options) + if(!new_state) + return TOPIC_HANDLED + + var/limb = zone + var/second_limb = null // if you try to change the arm, the hand should also change + var/third_limb = null // if you try to unchange the hand, the arm should also change + + switch(zone) + if(BP_L_FOOT) + third_limb = BP_L_LEG + if(BP_R_FOOT) + third_limb = BP_R_LEG + if(BP_L_LEG) + second_limb = BP_L_FOOT + if(BP_R_LEG) + second_limb = BP_R_FOOT + if(BP_L_HAND) + third_limb = BP_L_ARM + if(BP_R_HAND) + third_limb = BP_R_ARM + if(BP_L_ARM) + second_limb = BP_L_HAND + if(BP_R_ARM) + second_limb = BP_R_HAND + // standardize these to the same settings for full body + if(BP_HEAD, BP_TORSO, BP_GROIN) + limb = BP_TORSO + second_limb = BP_HEAD + third_limb = BP_GROIN + + switch(new_state) + if("Normal") + pref.organ_data[limb] = null + pref.rlimb_data[limb] = null + if(limb == BP_TORSO) // depends on standardization + for(var/other_limb in BP_ALL - BP_TORSO) + pref.organ_data[other_limb] = null + pref.rlimb_data[other_limb] = null + for(var/internal in O_STANDARD) + pref.organ_data[internal] = null + pref.rlimb_data[internal] = null + if(third_limb) + pref.organ_data[third_limb] = null + pref.rlimb_data[third_limb] = null + + if("Amputated") + if(limb == BP_TORSO) // depends on standardization + return + pref.organ_data[limb] = "amputated" + pref.rlimb_data[limb] = null + if(second_limb) + pref.organ_data[second_limb] = "amputated" + pref.rlimb_data[second_limb] = null + + if("Prosthesis") + var/list/usable_manufacturers = list() + for(var/company in GLOB.chargen_robolimbs) + var/datum/robolimb/M = GLOB.chargen_robolimbs[company] + if(!(limb in M.parts)) + continue + if(pref.species in M.species_cannot_use) + continue + if(M.whitelisted_to && !(user.ckey in M.whitelisted_to)) + continue + usable_manufacturers[company] = M + if(!usable_manufacturers.len) + return + var/choice = tgui_input_list(user, "Which manufacturer do you wish to use for this limb?", "Manufacturer Choice", usable_manufacturers) + if(!choice) + return + + pref.rlimb_data[limb] = choice + pref.organ_data[limb] = "cyborg" + + if(second_limb) + pref.rlimb_data[second_limb] = choice + pref.organ_data[second_limb] = "cyborg" + if(third_limb && pref.organ_data[third_limb] == "amputated") + pref.organ_data[third_limb] = null + + if(limb == BP_TORSO) + for(var/other_limb in BP_ALL - BP_TORSO) + if(pref.organ_data[other_limb]) + continue + pref.organ_data[other_limb] = "cyborg" + pref.rlimb_data[other_limb] = choice + if(!pref.organ_data[O_BRAIN]) + pref.organ_data[O_BRAIN] = "assisted" + for(var/internal_organ in list(O_HEART,O_EYES)) + pref.organ_data[internal_organ] = "mechanical" + + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("robolimb_select_organ") + var/zone = params["zone"] + if(!(zone in O_STANDARD)) + return + + if(zone == O_BRAIN && pref.organ_data[BP_HEAD] != "cyborg") + to_chat(user, span_warning("You may only select a cybernetic or synthetic brain if you have a full prosthetic body.")) + return + + var/list/organ_choices = list("Normal") + if(is_FBP()) + organ_choices -= "Normal" + if(zone == O_BRAIN) + organ_choices += "Cybernetic" + if(!(mob_species.spawn_flags & SPECIES_NO_POSIBRAIN)) + organ_choices += "Positronic" + if(!(mob_species.spawn_flags & SPECIES_NO_DRONEBRAIN)) + organ_choices += "Drone" + else + organ_choices += "Assisted" + organ_choices += "Mechanical" else - dat += "
" + span_bold("May be present on human stations.") - if(current_species.spawn_flags & SPECIES_IS_WHITELISTED) - dat += "
" + span_bold("Whitelist restricted.") - if(!current_species.has_organ[O_HEART]) - dat += "
" + span_bold("Does not have a circulatory system.") - if(!current_species.has_organ[O_LUNGS]) - dat += "
" + span_bold("Does not have a respiratory system.") - if(current_species.flags & NO_DNA) - dat += "
" + span_bold("Does not have DNA.") - if(current_species.flags & NO_SLEEVE) - dat += "
" + span_bold("Cannot be cloned.") - if(current_species.flags & NO_DEFIB) - dat += "
" + span_bold("Cannot be defibrillated.") - if(current_species.flags & NO_PAIN) - dat += "
" + span_bold("Does not feel pain.") - if(current_species.flags & NO_SLIP) - dat += "
" + span_bold("Has excellent traction.") - if(current_species.flags & NO_POISON) - dat += "
" + span_bold("Immune to most poisons.") - if(current_species.appearance_flags & HAS_SKIN_TONE) - dat += "
" + span_bold("Has a variety of skin tones.") - if(current_species.appearance_flags & HAS_SKIN_COLOR) - dat += "
" + span_bold("Has a variety of skin colours.") - if(current_species.appearance_flags & HAS_EYE_COLOR) - dat += "
" + span_bold("Has a variety of eye colours.") - if(current_species.flags & IS_PLANT) - dat += "
" + span_bold("Has a plantlike physiology.") - dat += "

" + organ_choices += "Assisted" + organ_choices += "Mechanical" - var/restricted = 0 + var/new_state = tgui_input_list(user, "What state do you wish the organ to be in?", "State Choice", organ_choices) + if(!new_state) + return - if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) - restricted = 2 - else if(!is_alien_whitelisted(user.client,current_species)) - restricted = 1 + switch(new_state) + if("Normal") + pref.organ_data[zone] = null + if("Assisted") + pref.organ_data[zone] = "assisted" + if("Cybernetic") + pref.organ_data[zone] = "assisted" + if("Mechanical") + pref.organ_data[zone] = "mechanical" + if("Drone") + pref.organ_data[zone] = "digital" + if("Positronic") + pref.organ_data[zone] = "mechanical" - if(restricted) - if(restricted == 1) - dat += span_red(span_bold("You cannot play as this species.
" + span_small("If you wish to be whitelisted, you can make an application post on the forums."))) + "
" - else if(restricted == 2) - dat += span_red(span_bold("You cannot play as this species.
" + span_small("This species is not available for play as a station race.."))) + "
" - if(!restricted || check_rights(R_ADMIN|R_EVENT, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) - dat += "\[select\]" - dat += "
" + return TOPIC_REFRESH_UPDATE_PREVIEW - user << browse(dat, "window=species;size=700x400") + if("reset_limbs") + reset_limbs() + return TOPIC_REFRESH_UPDATE_PREVIEW -/datum/category_item/player_setup_item/general/body/proc/markings_subwindow(mob/user, marking) - var/static/list/part_to_string = list(BP_HEAD = "Head", BP_TORSO = "Upper Body", BP_GROIN = "Lower Body", BP_R_ARM = "Right Arm", BP_L_ARM = "Left Arm", BP_R_HAND = "Right Hand", BP_L_HAND = "Left Hand", BP_R_LEG = "Right Leg", BP_L_LEG = "Left Leg", BP_R_FOOT = "Right Foot", BP_L_FOOT = "Left Foot") - var/dat = "

Editing '[marking]'


" - dat += "Enable All " - dat += "Disable All " - dat += "Change Color of All
" - dat += "
" - for (var/bodypart in pref.body_markings[marking]) - if (!islist(pref.body_markings[marking][bodypart])) continue - dat += "[part_to_string[bodypart]]: [color_square(hex = pref.body_markings[marking][bodypart]["color"])] " - dat += "Change " - dat += "[pref.body_markings[marking][bodypart]["on"] ? "Toggle Off" : "Toggle On"]
" + if("blood_type") + var/new_b_type = tgui_input_list(user, "Choose your character's blood-type:", "Character Preference", valid_bloodtypes, pref.b_type) + if(new_b_type) + pref.b_type = new_b_type + return TOPIC_REFRESH - dat += "" - winshow(user, "prefs_markings_subwindow", TRUE) - pref.markings_subwindow = new(user, "prefs_markings_browser", "Marking Editor", 400, 400) - pref.markings_subwindow.set_content(dat) - pref.markings_subwindow.open(FALSE) - onclose(user, "prefs_markings_subwindow", src) + if("digitigrade") + pref.digitigrade = !pref.digitigrade + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("synth_color_toggle") + pref.synth_color = !pref.synth_color + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("synth_color") + var/new_color = tgui_color_picker(user, "Choose your character's synth colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/synth_color)) + if(new_color && CanUseTopic(user)) + pref.update_preference_by_type(/datum/preference/color/human/synth_color, new_color) + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("synth_markings") + pref.synth_markings = !pref.synth_markings + return TOPIC_REFRESH_UPDATE_PREVIEW diff --git a/code/modules/client/preference_setup/general/05_background.dm b/code/modules/client/preference_setup/general/05_background.dm index 3987b69611..6458f14807 100644 --- a/code/modules/client/preference_setup/general/05_background.dm +++ b/code/modules/client/preference_setup/general/05_background.dm @@ -43,130 +43,133 @@ character.personal_faction = pref.faction character.religion = pref.religion -/datum/category_item/player_setup_item/general/background/content(var/mob/user) - . += span_bold("Background Information") + "
" - . += "Economic Status: [pref.economic_status]
" - . += "Home: [pref.home_system]
" - . += "Birthplace: [pref.birthplace]
" - . += "Citizenship: [pref.citizenship]
" - . += "Faction: [pref.faction]
" - . += "Religion: [pref.religion]
" +/datum/category_item/player_setup_item/general/background/tgui_data(mob/user) + var/list/data = ..() + + data["economic_status"] = pref.economic_status + data["home_system"] = pref.home_system + data["birthplace"] = pref.birthplace + data["citizenship"] = pref.citizenship + data["faction"] = pref.faction + data["religion"] = pref.religion + data["ooc_note_style"] = pref.read_preference(/datum/preference/toggle/living/ooc_notes_style) - . += "
Records:
" if(jobban_isbanned(user, "Records")) - . += span_danger("You are banned from using character records.") + "
" + data["records_banned"] = TRUE else - . += "Medical Records:
" - . += "[TextPreview(pref.med_record,40)]
" - . += " (Reset)

" - . += "Employment Records:
" - . += "[TextPreview(pref.gen_record,40)]
" - . += "(Reset)

" - . += "Security Records:
" - . += "[TextPreview(pref.sec_record,40)]
" - . += "(Reset)" + data["records_banned"] = FALSE -/datum/category_item/player_setup_item/general/background/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["econ_status"]) - var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status) - if(new_class && CanUseTopic(user)) - pref.economic_status = new_class + data["med_record"] = TextPreview(pref.med_record,40) + data["gen_record"] = TextPreview(pref.gen_record,40) + data["sec_record"] = TextPreview(pref.sec_record,40) + + return data + +/datum/category_item/player_setup_item/general/background/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + switch(action) + if("econ_status") + var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status) + if(new_class) + pref.economic_status = new_class + return TOPIC_REFRESH + + if("home_system") + var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.home_system = raw_choice + else + pref.home_system = choice return TOPIC_REFRESH - else if(href_list["home_system"]) - var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.home_system = raw_choice - else - pref.home_system = choice - return TOPIC_REFRESH + if("birthplace") + var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.birthplace = raw_choice + else + pref.birthplace = choice + return TOPIC_REFRESH - else if(href_list["birthplace"]) - var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.birthplace = raw_choice - else - pref.birthplace = choice - return TOPIC_REFRESH + if("citizenship") + var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.citizenship = raw_choice + else + pref.citizenship = choice + return TOPIC_REFRESH - else if(href_list["citizenship"]) - var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.citizenship = raw_choice - else - pref.citizenship = choice - return TOPIC_REFRESH + if("faction") + var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.faction = raw_choice + else + pref.faction = choice + return TOPIC_REFRESH - else if(href_list["faction"]) - var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.faction = raw_choice - else - pref.faction = choice - return TOPIC_REFRESH + if("religion") + var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.religion = sanitize(raw_choice) + else + pref.religion = choice + return TOPIC_REFRESH - else if(href_list["religion"]) - var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.religion = sanitize(raw_choice) - else - pref.religion = choice - return TOPIC_REFRESH + if("set_medical_records") + var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(new_medical && !jobban_isbanned(user, JOB_RECORDS)) + pref.med_record = new_medical + return TOPIC_REFRESH - else if(href_list["set_medical_records"]) - var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(new_medical && !jobban_isbanned(user, JOB_RECORDS) && CanUseTopic(user)) - pref.med_record = new_medical - return TOPIC_REFRESH + if("set_general_records") + var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(new_general && !jobban_isbanned(user, JOB_RECORDS)) + pref.gen_record = new_general + return TOPIC_REFRESH - else if(href_list["set_general_records"]) - var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(new_general && !jobban_isbanned(user, JOB_RECORDS) && CanUseTopic(user)) - pref.gen_record = new_general - return TOPIC_REFRESH + if("set_security_records") + var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(sec_medical && !jobban_isbanned(user, JOB_RECORDS)) + pref.sec_record = sec_medical + return TOPIC_REFRESH - else if(href_list["set_security_records"]) - var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(sec_medical && !jobban_isbanned(user, JOB_RECORDS) && CanUseTopic(user)) - pref.sec_record = sec_medical - return TOPIC_REFRESH + if("reset_medrecord") + var/resetmed_choice = tgui_alert(user, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetmed_choice == "Yes") + pref.med_record = null + return TOPIC_REFRESH - else if(href_list["reset_medrecord"]) - var/resetmed_choice = tgui_alert(user, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetmed_choice == "Yes") - pref.med_record = null - return TOPIC_REFRESH + if("reset_emprecord") + var/resetemp_choice = tgui_alert(user, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetemp_choice == "Yes") + pref.gen_record = null + return TOPIC_REFRESH - else if(href_list["reset_emprecord"]) - var/resetemp_choice = tgui_alert(user, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetemp_choice == "Yes") - pref.gen_record = null - return TOPIC_REFRESH - - else if(href_list["reset_secrecord"]) - var/resetsec_choice = tgui_alert(user, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetsec_choice == "Yes") - pref.sec_record = null - return TOPIC_REFRESH - - return ..() + if("reset_secrecord") + var/resetsec_choice = tgui_alert(user, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetsec_choice == "Yes") + pref.sec_record = null + return TOPIC_REFRESH diff --git a/code/modules/client/preference_setup/general/06_flavor.dm b/code/modules/client/preference_setup/general/06_flavor.dm index ad38334179..409b5f0ba7 100644 --- a/code/modules/client/preference_setup/general/06_flavor.dm +++ b/code/modules/client/preference_setup/general/06_flavor.dm @@ -50,12 +50,37 @@ character.flavor_texts["feet"] = pref.flavor_texts["feet"] character.custom_link = pref.custom_link -/datum/category_item/player_setup_item/general/flavor/content(var/mob/user) - . += span_bold("Flavor:") + "
" - . += "Set Flavor Text
" - . += "Set Robot Flavor Text
" - . += "Set Custom Link
" +/datum/category_item/player_setup_item/general/flavor/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["flavor_text_length"] = LAZYLEN(pref.flavor_texts["general"]) + + return data + +/datum/category_item/player_setup_item/general/flavor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + + switch(action) + if("flavor_text") + SetFlavorText(user) + return TOPIC_HANDLED + if("flavour_text_robot") + SetFlavourTextRobot(user) + return TOPIC_HANDLED + if("custom_link") + var/new_link = strip_html_simple(tgui_input_text(user, "Enter a link to add on to your examine text! This should be a related image link/gallery, or things like your F-list. This is not the place for memes.", "Custom Link" , html_decode(pref.custom_link), max_length = 100, encode = TRUE, prevent_enter = TRUE)) + if(new_link) + if(length(new_link) > 100) + to_chat(user, span_warning("Your entry is too long, it must be 100 characters or less.")) + return + pref.custom_link = new_link + log_admin("[user]/[user.ckey] set their custom link to [pref.custom_link]") + +// README: This must stay for SetFlavorText to work! /datum/category_item/player_setup_item/general/flavor/OnTopic(var/href,var/list/href_list, var/mob/user) if(href_list["flavor_text"]) switch(href_list["flavor_text"]) @@ -75,7 +100,6 @@ pref.flavor_texts[href_list["flavor_text"]] = msg SetFlavorText(user) return TOPIC_HANDLED - else if(href_list["flavour_text_robot"]) switch(href_list["flavour_text_robot"]) if("open") diff --git a/code/modules/client/preference_setup/general/07_antagonism.dm b/code/modules/client/preference_setup/general/07_antagonism.dm new file mode 100644 index 0000000000..830650db7a --- /dev/null +++ b/code/modules/client/preference_setup/general/07_antagonism.dm @@ -0,0 +1,128 @@ +/datum/category_item/player_setup_item/general/basic_antagonism + name = "Basic" + sort_order = 7 + + var/datum/paiCandidate/candidate + +/datum/category_item/player_setup_item/general/basic_antagonism/load_character(list/save_data) + pref.exploit_record = save_data["exploit_record"] + pref.antag_faction = save_data["antag_faction"] + pref.antag_vis = save_data["antag_vis"] + +/datum/category_item/player_setup_item/general/basic_antagonism/save_character(list/save_data) + save_data["exploit_record"] = pref.exploit_record + save_data["antag_faction"] = pref.antag_faction + save_data["antag_vis"] = pref.antag_vis + +/datum/category_item/player_setup_item/general/basic_antagonism/load_preferences(datum/json_savefile/savefile) + if(!candidate) + candidate = new() + var/preference_mob = preference_mob() + if(!preference_mob)// No preference mob - this happens when we're called from client/New() before it calls ..() (via datum/preferences/New()) + spawn() + preference_mob = preference_mob() + if(!preference_mob) + return + candidate.savefile_load(preference_mob) + return + + candidate.savefile_load(preference_mob) + +/datum/category_item/player_setup_item/general/basic_antagonism/save_preferences(datum/json_savefile/savefile) + if(!candidate) + return + + if(!preference_mob()) + return + + candidate.savefile_save(preference_mob()) + +/datum/category_item/player_setup_item/general/basic_antagonism/sanitize_character() + if(!pref.antag_faction) pref.antag_faction = "None" + if(!pref.antag_vis) pref.antag_vis = "Hidden" + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/general/basic_antagonism/copy_to_mob(var/mob/living/carbon/human/character) + character.exploit_record = pref.exploit_record + character.antag_faction = pref.antag_faction + character.antag_vis = pref.antag_vis + +/datum/category_item/player_setup_item/general/basic_antagonism/tgui_data(mob/user) + var/list/data = ..() + + data["antag_faction"] = pref.antag_faction + data["antag_vis"] = pref.antag_vis + data["uplink_type"] = pref.read_preference(/datum/preference/choiced/uplinklocation) + data["record_banned"] = jobban_isbanned(user, "Records") + if(!jobban_isbanned(user, "Records")) + data["exploitable_record"] = TextPreview(pref.exploit_record, 40) + + if(!candidate) + CRASH("[user] pAI prefs have a null candidate var.") + + data["pai_name"] = candidate.name ? candidate.name : "None Set" + data["pai_desc"] = candidate.description ? TextPreview(candidate.description, 40) : "None Set" + data["pai_role"] = candidate.role ? TextPreview(candidate.role, 40) : "None Set" + data["pai_comments"] = candidate.comments ? TextPreview(candidate.comments, 40) : "None Set" + + return data + +/datum/category_item/player_setup_item/general/basic_antagonism/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + switch(action) + if("uplinklocation") + var/new_uplinklocation = tgui_input_list(user, "Choose your uplink location:", "Character Preference", GLOB.uplink_locations, pref.read_preference(/datum/preference/choiced/uplinklocation)) + if(new_uplinklocation && CanUseTopic(user)) + pref.update_preference_by_type(/datum/preference/choiced/uplinklocation, new_uplinklocation) + return TOPIC_REFRESH + + if("exploitable_record") + var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0) + if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.exploit_record = exploitmsg + return TOPIC_REFRESH + + if("antagfaction") + var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.antag_faction = raw_choice + else + pref.antag_faction = choice + return TOPIC_REFRESH + + if("antagvis") + var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + else + pref.antag_vis = choice + return TOPIC_REFRESH + + if("option") + var/t + switch(params["option"]) + if("name") + t = sanitizeName(tgui_input_text(user, "Enter a name for your pAI", "Global Preference", candidate.name, MAX_NAME_LEN), MAX_NAME_LEN, 1) + if(t && CanUseTopic(user)) + candidate.name = t + if("desc") + t = tgui_input_text(user, "Enter a description for your pAI", "Global Preference", html_decode(candidate.description), multiline = TRUE, prevent_enter = TRUE) + if(!isnull(t) && CanUseTopic(user)) + candidate.description = sanitize(t) + if("role") + t = tgui_input_text(user, "Enter a role for your pAI", "Global Preference", html_decode(candidate.role)) + if(!isnull(t) && CanUseTopic(user)) + candidate.role = sanitize(t) + if("ooc") + t = tgui_input_text(user, "Enter any OOC comments", "Global Preference", html_decode(candidate.comments), multiline = TRUE, prevent_enter = TRUE) + if(!isnull(t) && CanUseTopic(user)) + candidate.comments = sanitize(t) + return TOPIC_REFRESH diff --git a/code/modules/client/preference_setup/general/08_candidacy.dm b/code/modules/client/preference_setup/general/08_candidacy.dm new file mode 100644 index 0000000000..cd569c8ab7 --- /dev/null +++ b/code/modules/client/preference_setup/general/08_candidacy.dm @@ -0,0 +1,81 @@ +var/global/list/special_roles = list( //keep synced with the defines BE_* in setup.dm --rastaf +//some autodetection here. +// Change these to 0 if the equivalent mode is disabled for whatever reason! + "traitor" = 0, // 0 + "operative" = 0, // 1 + "changeling" = 0, // 2 + "wizard" = 0, // 3 + "malf AI" = 0, // 4 + "revolutionary" = 0, // 5 + "alien candidate" = 0, // 6 + "positronic brain" = 1, // 7 + "cultist" = 0, // 8 + "renegade" = 0, // 9 + "ninja" = 0, // 10 + "raider" = 0, // 11 + "diona" = 0, // 12 + "mutineer" = 0, // 13 + "loyalist" = 0, // 14 + "pAI candidate" = 1, // 15 + //VOREStation Add + "lost drone" = 1, // 16 + "maint pred" = 1, // 17 + "maint lurker" = 1, // 18 + "morph" = 1, // 19 + "corgi" = 1, // 20 + "cursed sword" = 1, // 21 + "Ship Survivor" = 1, // 22 + //VOREStation Add End +) + +/datum/category_item/player_setup_item/general/candidacy + name = "Candidacy" + sort_order = 8 + +/datum/category_item/player_setup_item/general/candidacy/load_character(list/save_data) + pref.be_special = save_data["be_special"] + +/datum/category_item/player_setup_item/general/candidacy/save_character(list/save_data) + save_data["be_special"] = pref.be_special + +/datum/category_item/player_setup_item/general/candidacy/sanitize_character() + pref.be_special = sanitize_integer(pref.be_special, 0, 16777215, initial(pref.be_special)) //VOREStation Edit - 24 bits of support + +/datum/category_item/player_setup_item/general/candidacy/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + switch(action) + if("be_special") + var/num = text2num(params["be_special"]) + pref.be_special ^= (1< RESIZE_HUGE) + pref.size_multiplier = initial(pref.size_multiplier) + if(!(pref.custom_speech_bubble in GLOB.selectable_speech_bubbles)) + pref.custom_speech_bubble = "default" + if(!(pref.custom_footstep)) + pref.custom_footstep = "Default" + if(!(pref.species_sound)) + pref.species_sound = "Unset" + +/datum/category_item/player_setup_item/general/size/copy_to_mob(var/mob/living/carbon/human/character) + character.weight = pref.weight_vr + character.weight_gain = pref.weight_gain + character.weight_loss = pref.weight_loss + character.fuzzy = pref.fuzzy + character.offset_override = pref.offset_override + character.voice_freq = pref.voice_freq + character.resize(pref.size_multiplier, animate = FALSE, ignore_prefs = TRUE) + if(!pref.voice_sound) + character.voice_sounds_list = DEFAULT_TALK_SOUNDS + else + character.voice_sounds_list = get_talk_sound(pref.voice_sound) + character.custom_speech_bubble = pref.custom_speech_bubble + character.custom_footstep = pref.custom_footstep + +/datum/category_item/player_setup_item/general/size/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["size_multiplier"] = round(pref.size_multiplier * 100) + data["fuzzy"] = pref.fuzzy + data["offset_override"] = pref.offset_override + data["voice_freq"] = pref.voice_freq + data["voice_sound"] = pref.voice_sound + data["custom_speech_bubble"] = pref.custom_speech_bubble + data["custom_footstep"] = pref.custom_footstep + data["custom_species_sound"] = pref.species_sound + data["weight_vr"] = pref.weight_vr + data["weight_gain"] = pref.weight_gain + data["weight_loss"] = pref.weight_loss + + return data + +/datum/category_item/player_setup_item/general/size/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + switch(action) + if("size_multiplier") + var/new_size = tgui_input_number(user, "Choose your character's size, ranging from [RESIZE_MINIMUM * 100]% to [RESIZE_MAXIMUM * 100]%", "Set Size", pref.size_multiplier * 100, RESIZE_MAXIMUM * 100, RESIZE_MINIMUM * 100) + if (!ISINRANGE(new_size, RESIZE_MINIMUM * 100, RESIZE_MAXIMUM * 100)) + pref.size_multiplier = 1 + to_chat(user, span_notice("Invalid size.")) + return TOPIC_REFRESH_UPDATE_PREVIEW + else if(new_size) + pref.size_multiplier = (new_size / 100) + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("toggle_fuzzy") + pref.fuzzy = pref.fuzzy ? 0 : 1; + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("toggle_offset_override") + pref.offset_override = pref.offset_override ? 0 : 1; + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("weight") + var/new_weight = tgui_input_number(user, "Choose your character's relative body weight.\n\ + Note: Scifi characters come in all shapes and sizes in this game, and not all follow the traditional shape of a human. Like a naga or a taur or a giant will weigh a\n\ + lot more than what this allows, or a micro will weigh a lot less. Just ignore all of that for a second and PRETEND the weight you're setting is visually for an \n\ + average human. This is the best solution we have at the moment.!\n\ + ([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE) + if(new_weight) + var/unit_of_measurement = tgui_alert(user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms")) + if(!unit_of_measurement) + return TOPIC_NOACTION + if(unit_of_measurement == "Pounds") + new_weight = round(text2num(new_weight),4) + if(unit_of_measurement == "Kilograms") + new_weight = round(2.20462*text2num(new_weight),4) + pref.weight_vr = sanitize_integer(new_weight, WEIGHT_MIN, WEIGHT_MAX, pref.weight_vr) + return TOPIC_REFRESH + + if("weight_gain") + var/weight_gain_rate = tgui_input_number(user, "Choose your character's rate of weight gain between 100% \ + (full realism body fat gain) and 0% (no body fat gain).\n\ + (If you want to disable weight gain, set this to 0.01 to round it to 0%.)\ + ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) + if(weight_gain_rate) + pref.weight_gain = round(text2num(weight_gain_rate),1) + return TOPIC_REFRESH + + if("weight_loss") + var/weight_loss_rate = tgui_input_number(user, "Choose your character's rate of weight loss between 100% \ + (full realism body fat loss) and 0% (no body fat loss).\n\ + (If you want to disable weight loss, set this to 0.01 round it to 0%.)\ + ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) + if(weight_loss_rate) + pref.weight_loss = round(text2num(weight_loss_rate),1) + return TOPIC_REFRESH + + if("voice_freq") + var/list/preset_voice_freqs = list("high" = MAX_VOICE_FREQ, "middle-high" = 56250, "middle" = 42500, "middle-low"= 28750, "low" = MIN_VOICE_FREQ, "custom" = 1, "random" = 0) + var/choice = tgui_input_list(user, "What would you like to set your voice frequency to? ([MIN_VOICE_FREQ] - [MAX_VOICE_FREQ])", "Voice Frequency", preset_voice_freqs) + if(!choice) + return + choice = preset_voice_freqs[choice] + if(choice == 0) + pref.voice_freq = choice + return TOPIC_REFRESH + else if(choice == 1) + choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ) + if(choice > MAX_VOICE_FREQ) + choice = MAX_VOICE_FREQ + else if(choice < MIN_VOICE_FREQ) + choice = MIN_VOICE_FREQ + + pref.voice_freq = choice + return TOPIC_REFRESH + + if("voice_sounds_list") + var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's speech sounds?", "Voice Sounds", SSsounds.talk_sound_map) + if(!choice) + pref.voice_sound = "beep-boop" + else + pref.voice_sound = choice + return TOPIC_REFRESH + + if("customize_speech_bubble") + var/choice = tgui_input_list(user, "What speech bubble style do you want to use?", "Custom Speech Bubble", GLOB.selectable_speech_bubbles) + if(choice) + pref.custom_speech_bubble = choice + return TOPIC_REFRESH + + if("customize_footsteps") + var/list/footstep_choice = selectable_footstep + var/choice = tgui_input_list(user, "What footstep sounds would your character make?", "Custom Foostep Sounds", footstep_choice) + if(choice) + pref.custom_footstep = footstep_choice[choice] + return TOPIC_REFRESH + + if("voice_test") + var/sound/S = sound(pick(SSsounds.talk_sound_map[pref.voice_sound])) // talk_sound_map returns a list of sounds + if(S) + S.frequency = pick(pref.voice_freq) + S.volume = 50 + SEND_SOUND(user, S) + + if("customize_species_sounds") // You shouldn't be able to see this option if you don't have the option to select a custom icon base, so we don't need to re-check for safety here. + var/list/possible_species_sound_types = species_sound_map + var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's species sounds? (Cough, Sneeze, Scream, Pain, Gasp, Death)", "Species Sounds", possible_species_sound_types) + if(choice) + pref.species_sound = choice + return TOPIC_REFRESH + + if("cough_test") + var/sound/S + var/ourpref = pref.species_sound + var/oursound = get_species_sound(ourpref)["cough"] + S = sound(pick(oursound)) + if(pref.species_sound == "Unset") + oursound = get_species_sound(select_default_species_sound(pref))["cough"] + S = sound(pick(oursound)) + if(pref.species_sound == "None" || oursound == null) + to_chat(user, span_warning("This set does not have cough sounds!")) + return TOPIC_REFRESH + S.frequency = pick(pref.voice_freq) + S.volume = 20 + SEND_SOUND(user, S) + + if("sneeze_test") + var/sound/S + var/ourpref = pref.species_sound + var/oursound = get_species_sound(ourpref)["sneeze"] + S = sound(pick(oursound)) + if(pref.species_sound == "Unset") + oursound = get_species_sound(select_default_species_sound(pref))["sneeze"] + S = sound(pick(oursound)) + if(pref.species_sound == "None" || oursound == null) + to_chat(user, span_warning("This set does not have sneeze sounds!")) + return TOPIC_REFRESH + S.frequency = pick(pref.voice_freq) + S.volume = 20 + SEND_SOUND(user, S) + + if("scream_test") + var/sound/S + var/ourpref = pref.species_sound + var/oursound = get_species_sound(ourpref)["scream"] + S = sound(pick(oursound)) + if(pref.species_sound == "Unset") + oursound = get_species_sound(select_default_species_sound(pref))["scream"] + S = sound(pick(oursound)) + if(pref.species_sound == "None" || oursound == null) + to_chat(user, span_warning("This set does not have scream sounds!")) + return TOPIC_REFRESH + S.frequency = pick(pref.voice_freq) + S.volume = 20 + SEND_SOUND(user, S) + + if("pain_test") + var/sound/S + var/ourpref = pref.species_sound + var/oursound = get_species_sound(ourpref)["pain"] + S = sound(pick(oursound)) + if(pref.species_sound == "Unset") + oursound = get_species_sound(select_default_species_sound(pref))["pain"] + S = sound(pick(oursound)) + if(pref.species_sound == "None" || oursound == null) + to_chat(user, span_warning("This set does not have pain sounds!")) + return TOPIC_REFRESH + S.frequency = pick(pref.voice_freq) + S.volume = 20 + SEND_SOUND(user, S) + + if("gasp_test") + var/sound/S + var/ourpref = pref.species_sound + var/oursound = get_species_sound(ourpref)["gasp"] + S = sound(pick(oursound)) + if(pref.species_sound == "Unset") + oursound = get_species_sound(select_default_species_sound(pref))["gasp"] + S = sound(pick(oursound)) + if(pref.species_sound == "None" || oursound == null) + to_chat(user, span_warning("This set does not have gasp sounds!")) + return TOPIC_REFRESH + S.frequency = pick(pref.voice_freq) + S.volume = 20 + SEND_SOUND(user, S) + + if("death_test") + var/sound/S + var/ourpref = pref.species_sound + var/oursound = get_species_sound(ourpref)["death"] + S = sound(pick(oursound)) + if(pref.species_sound == "Unset") + oursound = get_species_sound(select_default_species_sound(pref))["death"] + S = sound(pick(oursound)) + if(pref.species_sound == "None" || oursound == null) + to_chat(user, span_warning("This set does not have death sounds!")) + return TOPIC_REFRESH + S.frequency = pick(pref.voice_freq) + S.volume = 20 + SEND_SOUND(user, S) + +#undef WEIGHT_CHANGE_MIN +#undef WEIGHT_CHANGE_MAX diff --git a/code/modules/client/preference_setup/general/10_persistence.dm b/code/modules/client/preference_setup/general/10_persistence.dm new file mode 100644 index 0000000000..55bf904e9d --- /dev/null +++ b/code/modules/client/preference_setup/general/10_persistence.dm @@ -0,0 +1,39 @@ +// Define a place to save in character setup +/datum/preferences + var/persistence_settings = PERSIST_DEFAULT // Control what if anything is persisted for this character between rounds. + +// Definition of the stuff for Sizing +/datum/category_item/player_setup_item/general/persistence + name = "Persistence" + sort_order = 10 + +/datum/category_item/player_setup_item/general/persistence/load_character(list/save_data) + pref.persistence_settings = save_data["persistence_settings"] + sanitize_character() // Don't let new characters start off with nulls + +/datum/category_item/player_setup_item/general/persistence/save_character(list/save_data) + save_data["persistence_settings"] = pref.persistence_settings + +/datum/category_item/player_setup_item/general/persistence/sanitize_character() + pref.persistence_settings = sanitize_integer(pref.persistence_settings, 0, (1<<(PERSIST_COUNT+1)-1), initial(pref.persistence_settings)) + +/datum/category_item/player_setup_item/general/persistence/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["persistence_settings"] = pref.persistence_settings + + return data + +/datum/category_item/player_setup_item/general/persistence/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + switch(action) + if("toggle_persist") + var/bit = text2num(params["bit"]) + if(pref.persistence_settings & bit) + pref.persistence_settings &= ~bit + else + pref.persistence_settings |= bit + return TOPIC_REFRESH diff --git a/code/modules/client/preference_setup/general/11_misc.dm b/code/modules/client/preference_setup/general/11_misc.dm new file mode 100644 index 0000000000..a0732df3c9 --- /dev/null +++ b/code/modules/client/preference_setup/general/11_misc.dm @@ -0,0 +1,333 @@ +// Define a place to save in character setup +/datum/preferences + var/vantag_volunteer = 0 // What state I want to be in, in terms of being affected by antags. + var/vantag_preference = VANTAG_NONE // Whether I'd like to volunteer to be an antag at some point. + var/resleeve_lock = 0 // Whether movs should have OOC reslieving protection. Default false. + var/resleeve_scan = 1 // Whether mob should start with a pre-spawn body scan. Default true. + var/mind_scan = 1 // Whether mob should start with a pre-spawn mind scan. Default true. + + var/custom_species // Custom species name, can't be changed due to it having been used in savefiles already. + + var/custom_say = null + var/custom_whisper = null + var/custom_ask = null + var/custom_exclaim = null + + var/list/custom_heat = list() + var/list/custom_cold = list() + +/datum/category_item/player_setup_item/general/vore_misc + name = "Misc Settings" + sort_order = 1 + +/datum/category_item/player_setup_item/general/vore_misc/load_character(list/save_data) + pref.show_in_directory = save_data["show_in_directory"] + pref.directory_tag = save_data["directory_tag"] + pref.directory_gendertag = save_data["directory_gendertag"] + pref.directory_sexualitytag = save_data["directory_sexualitytag"] + pref.directory_erptag = save_data["directory_erptag"] + pref.directory_ad = save_data["directory_ad"] + pref.sensorpref = save_data["sensorpref"] + pref.capture_crystal = save_data["capture_crystal"] + pref.auto_backup_implant = save_data["auto_backup_implant"] + pref.borg_petting = save_data["borg_petting"] + pref.resleeve_lock = save_data["resleeve_lock"] + pref.resleeve_scan = save_data["resleeve_scan"] + pref.mind_scan = save_data["mind_scan"] + pref.vantag_volunteer = save_data["vantag_volunteer"] + pref.vantag_preference = save_data["vantag_preference"] + + pref.custom_species = save_data["custom_species"] + + pref.custom_say = save_data["custom_say"] + pref.custom_whisper = save_data["custom_whisper"] + pref.custom_ask = save_data["custom_ask"] + pref.custom_exclaim = save_data["custom_exclaim"] + pref.custom_heat = check_list_copy(save_data["custom_heat"]) + pref.custom_cold = check_list_copy(save_data["custom_cold"]) + +/datum/category_item/player_setup_item/general/vore_misc/save_character(list/save_data) + save_data["show_in_directory"] = pref.show_in_directory + save_data["directory_tag"] = pref.directory_tag + save_data["directory_gendertag"] = pref.directory_gendertag + save_data["directory_sexualitytag"] = pref.directory_sexualitytag + save_data["directory_erptag"] = pref.directory_erptag + save_data["directory_ad"] = pref.directory_ad + save_data["sensorpref"] = pref.sensorpref + save_data["capture_crystal"] = pref.capture_crystal + save_data["auto_backup_implant"] = pref.auto_backup_implant + save_data["borg_petting"] = pref.borg_petting + save_data["resleeve_lock"] = pref.resleeve_lock + save_data["resleeve_scan"] = pref.resleeve_scan + save_data["mind_scan"] = pref.mind_scan + save_data["vantag_volunteer"] = pref.vantag_volunteer + save_data["vantag_preference"] = pref.vantag_preference + + save_data["custom_species"] = pref.custom_species + + save_data["custom_say"] = pref.custom_say + save_data["custom_whisper"] = pref.custom_whisper + save_data["custom_ask"] = pref.custom_ask + save_data["custom_exclaim"] = pref.custom_exclaim + save_data["custom_heat"] = check_list_copy(pref.custom_heat) + save_data["custom_cold"] = check_list_copy(pref.custom_cold) + +/datum/category_item/player_setup_item/general/vore_misc/copy_to_mob(var/mob/living/carbon/human/character) + character.custom_species = pref.custom_species + + character.custom_say = lowertext(trim(pref.custom_say)) + character.custom_ask = lowertext(trim(pref.custom_ask)) + character.custom_whisper = lowertext(trim(pref.custom_whisper)) + character.custom_exclaim = lowertext(trim(pref.custom_exclaim)) + character.custom_heat = pref.custom_heat + character.custom_cold = pref.custom_cold + + if(pref.sensorpref > 5 || pref.sensorpref < 1) + pref.sensorpref = 5 + character.sensorpref = pref.sensorpref + character.capture_crystal = pref.capture_crystal + //Vore Stomach Sprite Preference + character.recalculate_vis() + + if(character && !istype(character,/mob/living/carbon/human/dummy)) + character.vantag_pref = pref.vantag_preference + BITSET(character.hud_updateflag, VANTAG_HUD) + + spawn(50) + if(QDELETED(character) || QDELETED(pref)) + return // They might have been deleted during the wait + if(!character.virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in character.verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore + if(pref.resleeve_scan) + var/datum/transhuman/body_record/BR = new() + BR.init_from_mob(character, pref.resleeve_scan, pref.resleeve_lock) + if(pref.mind_scan) + var/datum/transcore_db/our_db = SStranscore.db_by_key(null) + if(our_db) + our_db.m_backup(character.mind,character.nif,one_time = TRUE) + if(pref.resleeve_lock) + character.resleeve_lock = character.ckey + character.original_player = character.ckey + +/datum/category_item/player_setup_item/general/vore_misc/sanitize_character() + pref.show_in_directory = sanitize_integer(pref.show_in_directory, 0, 1, initial(pref.show_in_directory)) + pref.directory_tag = sanitize_inlist(pref.directory_tag, GLOB.char_directory_tags, initial(pref.directory_tag)) + pref.directory_gendertag = sanitize_inlist(pref.directory_gendertag, GLOB.char_directory_gendertags, initial(pref.directory_gendertag)) + pref.directory_sexualitytag = sanitize_inlist(pref.directory_sexualitytag, GLOB.char_directory_sexualitytags, initial(pref.directory_sexualitytag)) + pref.directory_erptag = sanitize_inlist(pref.directory_erptag, GLOB.char_directory_erptags, initial(pref.directory_erptag)) + pref.sensorpref = sanitize_integer(pref.sensorpref, 1, GLOB.sensorpreflist.len, initial(pref.sensorpref)) + pref.capture_crystal = sanitize_integer(pref.capture_crystal, 0, 1, initial(pref.capture_crystal)) + pref.auto_backup_implant = sanitize_integer(pref.auto_backup_implant, 0, 1, initial(pref.auto_backup_implant)) + pref.borg_petting = sanitize_integer(pref.borg_petting, 0, 1, initial(pref.borg_petting)) + pref.resleeve_lock = sanitize_integer(pref.resleeve_lock, 0, 1, initial(pref.resleeve_lock)) + pref.resleeve_scan = sanitize_integer(pref.resleeve_scan, 0, 1, initial(pref.resleeve_scan)) + pref.mind_scan = sanitize_integer(pref.mind_scan, 0, 1, initial(pref.mind_scan)) + pref.vantag_volunteer = sanitize_integer(pref.vantag_volunteer, 0, 1, initial(pref.vantag_volunteer)) + pref.vantag_preference = sanitize_inlist(pref.vantag_preference, GLOB.vantag_choices_list, initial(pref.vantag_preference)) + + pref.custom_say = lowertext(trim(pref.custom_say)) + pref.custom_whisper = lowertext(trim(pref.custom_whisper)) + pref.custom_ask = lowertext(trim(pref.custom_ask)) + pref.custom_exclaim = lowertext(trim(pref.custom_exclaim)) + if (islist(pref.custom_heat)) //don't bother checking these for actual singular message length, they should already have been checked and it'd take too long every time it's sanitized + if (length(pref.custom_heat) > 10) + pref.custom_heat.Cut(11) + else + pref.custom_heat = list() + if (islist(pref.custom_cold)) + if (length(pref.custom_cold) > 10) + pref.custom_cold.Cut(11) + else + pref.custom_cold = list() + +/datum/category_item/player_setup_item/general/vore_misc/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["show_in_directory"] = pref.show_in_directory + data["directory_tag"] = pref.directory_tag + data["directory_gendertag"] = pref.directory_gendertag + data["directory_sexualitytag"] = pref.directory_sexualitytag + data["directory_erptag"] = pref.directory_erptag + data["sensorpref"] = GLOB.sensorpreflist[pref.sensorpref] + data["capture_crystal"] = pref.capture_crystal + data["auto_backup_implant"] = pref.auto_backup_implant + data["borg_petting"] = pref.borg_petting + + data["resleeve_lock"] = pref.resleeve_lock + data["resleeve_scan"] = pref.resleeve_scan + data["mind_scan"] = pref.mind_scan + + data["vantag_volunteer"] = pref.vantag_volunteer + data["vantag_preference"] = GLOB.vantag_choices_list[pref.vantag_preference] + + data["custom_species"] = pref.custom_species + + return data + +/datum/category_item/player_setup_item/general/vore_misc/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + + switch(action) + if("toggle_show_in_directory") + pref.show_in_directory = pref.show_in_directory ? 0 : 1; + return TOPIC_REFRESH + if("directory_tag") + var/new_tag = tgui_input_list(user, "Pick a new Vore tag for the character directory", "Character Vore Tag", GLOB.char_directory_tags, pref.directory_tag) + if(!new_tag) + return + pref.directory_tag = new_tag + return TOPIC_REFRESH + if("directory_gendertag") + var/new_gendertag = tgui_input_list(user, "Pick a new Gender tag for the character directory. This is YOUR gender, not what you prefer.", "Character Gender Tag", GLOB.char_directory_gendertags, pref.directory_gendertag) + if(!new_gendertag) + return + pref.directory_gendertag = new_gendertag + return TOPIC_REFRESH + if("directory_sexualitytag") + var/new_sexualitytag = tgui_input_list(user, "Pick a new Sexuality/Orientation tag for the character directory", "Character Sexuality/Orientation Tag", GLOB.char_directory_sexualitytags, pref.directory_sexualitytag) + if(!new_sexualitytag) + return + pref.directory_sexualitytag = new_sexualitytag + return TOPIC_REFRESH + if("directory_erptag") + var/new_erptag = tgui_input_list(user, "Pick a new ERP tag for the character directory", "Character ERP Tag", GLOB.char_directory_erptags, pref.directory_erptag) + if(!new_erptag) + return + pref.directory_erptag = new_erptag + return TOPIC_REFRESH + if("directory_ad") + var/msg = sanitize(tgui_input_text(user,"Write your advertisement here!", "Flavor Text", html_decode(pref.directory_ad), multiline = TRUE, prevent_enter = TRUE), extra = 0) //VOREStation Edit: separating out OOC notes + if(!msg) + return + pref.directory_ad = msg + return TOPIC_REFRESH + if("toggle_sensor_setting") + var/new_sensorpref = tgui_input_list(user, "Choose your character's sensor preferences:", "Character Preferences", GLOB.sensorpreflist, GLOB.sensorpreflist[pref.sensorpref]) + if (!isnull(new_sensorpref)) + pref.sensorpref = GLOB.sensorpreflist.Find(new_sensorpref) + return TOPIC_REFRESH + if("toggle_capture_crystal") + pref.capture_crystal = pref.capture_crystal ? 0 : 1; + return TOPIC_REFRESH + if("toggle_implant") + pref.auto_backup_implant = pref.auto_backup_implant ? 0 : 1; + return TOPIC_REFRESH + if("toggle_borg_petting") + pref.borg_petting = pref.borg_petting ? 0 : 1; + return TOPIC_REFRESH + if("edit_private_notes") + var/new_metadata = sanitize(tgui_input_text(user,"Write some notes for yourself. These can be anything that is useful, whether it's character events that you want to remember or a bit of lore. Things that you would normally stick in a txt file for yourself!", "Private Notes", html_decode(pref.read_preference(/datum/preference/text/living/private_notes)), multiline = TRUE, prevent_enter = TRUE), extra = 0) + if(new_metadata) + pref.update_preference_by_type(/datum/preference/text/living/private_notes, new_metadata) + return TOPIC_REFRESH + if("toggle_resleeve_lock") + pref.resleeve_lock = pref.resleeve_lock ? 0 : 1; + return TOPIC_REFRESH + if("toggle_resleeve_scan") + pref.resleeve_scan = pref.resleeve_scan ? 0 : 1; + return TOPIC_REFRESH + if("toggle_mind_scan") + pref.mind_scan = pref.mind_scan ? 0 : 1; + return TOPIC_REFRESH + if("toggle_vantag_volunteer") + pref.vantag_volunteer = pref.vantag_volunteer ? 0 : 1 + return TOPIC_REFRESH + if("change_vantag") + var/list/names_list = list() + for(var/C in GLOB.vantag_choices_list) + names_list[GLOB.vantag_choices_list[C]] = C + + var/selection = tgui_input_list(user, "How do you want to be involved with VS Event Characters, ERP-wise? They will see this choice on you in a HUD. Event characters are admin-selected and spawned players, possibly with assigned objectives, who are obligated to respect ERP prefs and RP their actions like any other player, though it may be a slightly shorter RP if they are pressed for time or being caught.", "Event Preference", names_list) + if(selection && selection != "Normal") + pref.vantag_preference = names_list[selection] + return TOPIC_REFRESH + if("custom_say") + var/say_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'says': [pref.real_name] says, \"Hi.\"", "Custom Say", pref.custom_say, 12), 12) + if(say_choice) + pref.custom_say = say_choice + return TOPIC_REFRESH + if("custom_whisper") + var/whisper_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'whispers': [pref.real_name] whispers, \"Hi...\"", "Custom Whisper", pref.custom_whisper, 12), 12) + if(whisper_choice) + pref.custom_whisper = whisper_choice + return TOPIC_REFRESH + if("custom_ask") + var/ask_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'asks': [pref.real_name] asks, \"Hi?\"", "Custom Ask", pref.custom_ask, 12), 12) + if(ask_choice) + pref.custom_ask = ask_choice + return TOPIC_REFRESH + if("custom_exclaim") + var/exclaim_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'exclaims', 'shouts' or 'yells': [pref.real_name] exclaims, \"Hi!\"", "Custom Exclaim", pref.custom_exclaim, 12), 12) + if(exclaim_choice) + pref.custom_exclaim = exclaim_choice + return TOPIC_REFRESH + if("custom_heat") + tgui_alert(user, "You are setting custom heat messages. These will overwrite your species' defaults. To return to defaults, click reset.") + var/old_message = pref.custom_heat.Join("\n\n") + var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Heat Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0) + if(length(new_message) > 0) + var/list/raw_list = splittext(new_message,"\n\n") + if(raw_list.len > 10) + raw_list.Cut(11) + for(var/i = 1, i <= raw_list.len, i++) + if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160) + raw_list.Cut(i,i) + else + raw_list[i] = readd_quotes(raw_list[i]) + ASSERT(raw_list.len <= 10) + pref.custom_heat = raw_list + return TOPIC_REFRESH + if("custom_cold") + tgui_alert(user, "You are setting custom cold messages. These will overwrite your species' defaults. To return to defaults, click reset.") + var/old_message = pref.custom_heat.Join("\n\n") + var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Cold Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0) + if(length(new_message) > 0) + var/list/raw_list = splittext(new_message,"\n\n") + if(raw_list.len > 10) + raw_list.Cut(11) + for(var/i = 1, i <= raw_list.len, i++) + if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160) + raw_list.Cut(i,i) + else + raw_list[i] = readd_quotes(raw_list[i]) + ASSERT(raw_list.len <= 10) + pref.custom_cold = raw_list + return TOPIC_REFRESH + if("reset_say") + var/say_choice = tgui_alert(user, "Reset your Custom Say Verb?","Reset Verb",list("Yes","No")) + if(say_choice == "Yes") + pref.custom_say = null + return TOPIC_REFRESH + if("reset_whisper") + var/whisper_choice = tgui_alert(user, "Reset your Custom Whisper Verb?","Reset Verb",list("Yes","No")) + if(whisper_choice == "Yes") + pref.custom_whisper = null + return TOPIC_REFRESH + if("reset_ask") + var/ask_choice = tgui_alert(user, "Reset your Custom Ask Verb?","Reset Verb",list("Yes","No")) + if(ask_choice == "Yes") + pref.custom_ask = null + return TOPIC_REFRESH + if("reset_exclaim") + var/exclaim_choice = tgui_alert(user, "Reset your Custom Exclaim Verb?","Reset Verb",list("Yes","No")) + if(exclaim_choice == "Yes") + pref.custom_exclaim = null + return TOPIC_REFRESH + if("reset_cold") + var/cold_choice = tgui_alert(user, "Reset your Custom Cold Discomfort messages?", "Reset Discomfort",list("Yes","No")) + if(cold_choice == "Yes") + pref.custom_cold = list() + return TOPIC_REFRESH + if("reset_heat") + var/heat_choice = tgui_alert(user, "Reset your Custom Heat Discomfort messages?", "Reset Discomfort",list("Yes","No")) + if(heat_choice == "Yes") + pref.custom_heat = list() + return TOPIC_REFRESH + if("custom_species") + var/raw_choice = sanitize(tgui_input_text(user, "Input your custom species name:", + "Character Preference", pref.custom_species, MAX_NAME_LEN), MAX_NAME_LEN) + pref.custom_species = raw_choice + return TOPIC_REFRESH diff --git a/code/modules/client/preference_setup/general/12_traits.dm b/code/modules/client/preference_setup/general/12_traits.dm new file mode 100644 index 0000000000..f8d418636c --- /dev/null +++ b/code/modules/client/preference_setup/general/12_traits.dm @@ -0,0 +1,412 @@ +#define POSITIVE_MODE 1 +#define NEUTRAL_MODE 2 +#define NEGATIVE_MODE 3 + +var/global/list/valid_bloodreagents = list("default",REAGENT_ID_IRON,REAGENT_ID_COPPER,REAGENT_ID_PHORON,REAGENT_ID_SILVER,REAGENT_ID_GOLD,REAGENT_ID_SLIMEJELLY) //allowlist-based so people don't make their blood restored by alcohol or something really silly. use reagent IDs! + +/datum/preferences + var/custom_base // What to base the custom species on + var/blood_color = "#A10808" + + var/list/pos_traits = list() // What traits they've selected for their custom species + var/list/neu_traits = list() + var/list/neg_traits = list() + + var/traits_cheating = 0 //Varedit by admins allows saving new maximums on people who apply/etc + var/starting_trait_points = 0 + var/max_traits = MAX_SPECIES_TRAITS + var/dirty_synth = 0 //Are you a synth + var/gross_meatbag = 0 //Where'd I leave my Voight-Kampff test kit? + +/datum/preferences/proc/get_custom_bases_for_species(var/new_species) + if (!new_species) + new_species = species + var/list/choices + var/datum/species/spec = GLOB.all_species[new_species] + if (spec.selects_bodytype == SELECTS_BODYTYPE_SHAPESHIFTER) + choices = spec.get_valid_shapeshifter_forms() + choices = choices.Copy() + else if (spec.selects_bodytype == SELECTS_BODYTYPE_CUSTOM) + choices = GLOB.custom_species_bases.Copy() + if(new_species != SPECIES_CUSTOM) + choices = (choices | new_species) + return choices + +/datum/category_item/player_setup_item/general/traits/proc/get_pref_choice_from_trait(var/mob/user, var/datum/trait/trait, var/preference) + if (!trait || !preference) + return + var/list/trait_prefs + var/datum/trait/instance = GLOB.all_traits[trait] + var/list/traitlist + switch(instance.category) + if (1) + traitlist = pref.pos_traits + if (0) + traitlist = pref.neu_traits + if (-1) + traitlist = pref.neg_traits + if (!LAZYLEN(instance.has_preferences) || !(preference in instance.has_preferences) || !traitlist) + return + if (!LAZYLEN(traitlist[trait])) + traitlist[trait] = instance.get_default_prefs() + trait_prefs = traitlist[trait] + if (!(preference in trait_prefs)) + trait_prefs[preference] = instance.default_value_for_pref(preference) //won't be called at all often + switch(instance.has_preferences[preference][1]) + if (1) //TRAIT_PREF_TYPE_BOOLEAN + trait_prefs[preference] = !trait_prefs[preference] + if (2) //TRAIT_PREF_TYPE_COLOR + var/new_color = tgui_color_picker(user, "Choose the color for this trait preference:", "Trait Preference", trait_prefs[preference]) + if (new_color) + trait_prefs[preference] = new_color + if (3) //TRAIT_PREF_TYPE_STRING + var/new_string = instance.apply_sanitization_to_string(preference, tgui_input_text(user, "What should the new value be?", instance.has_preferences[preference][2], trait_prefs[preference], MAX_NAME_LEN)) + trait_prefs[preference] = new_string + +// Definition of the stuff for Ears +/datum/category_item/player_setup_item/general/traits + name = "Traits" + sort_order = 7 + +/datum/category_item/player_setup_item/general/traits/load_character(list/save_data) + pref.custom_base = save_data["custom_base"] + pref.pos_traits = text2path_list(save_data["pos_traits"]) + pref.neu_traits = text2path_list(save_data["neu_traits"]) + pref.neg_traits = text2path_list(save_data["neg_traits"]) + pref.blood_color = save_data["blood_color"] + pref.blood_reagents = save_data["blood_reagents"] + + pref.traits_cheating = save_data["traits_cheating"] + pref.max_traits = save_data["max_traits"] + pref.starting_trait_points = save_data["trait_points"] + + +/datum/category_item/player_setup_item/general/traits/save_character(list/save_data) + save_data["custom_base"] = pref.custom_base + save_data["pos_traits"] = check_list_copy(pref.pos_traits) + save_data["neu_traits"] = check_list_copy(pref.neu_traits) + save_data["neg_traits"] = check_list_copy(pref.neg_traits) + save_data["blood_color"] = pref.blood_color + save_data["blood_reagents"] = pref.blood_reagents + + save_data["traits_cheating"] = pref.traits_cheating + save_data["max_traits"] = pref.max_traits + save_data["trait_points"] = pref.starting_trait_points + +/datum/category_item/player_setup_item/general/traits/sanitize_character() + if(!pref.pos_traits) pref.pos_traits = list() + if(!pref.neu_traits) pref.neu_traits = list() + if(!pref.neg_traits) pref.neg_traits = list() + + pref.blood_color = sanitize_hexcolor(pref.blood_color, default="#A10808") + pref.blood_reagents = sanitize_text(pref.blood_reagents, initial(pref.blood_reagents)) + + if(!pref.traits_cheating) + var/datum/species/S = GLOB.all_species[pref.species] + if(S) + pref.starting_trait_points = S.trait_points + else + pref.starting_trait_points = 0 + pref.max_traits = MAX_SPECIES_TRAITS + + if(pref.organ_data[O_BRAIN]) //Checking if we have a synth on our hands, boys. + pref.dirty_synth = 1 + pref.gross_meatbag = 0 + else + pref.gross_meatbag = 1 + pref.dirty_synth = 0 + + // Clean up positive traits + for(var/datum/trait/path as anything in pref.pos_traits) + if(!(path in GLOB.positive_traits)) + pref.pos_traits -= path + continue + if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_positive)) + pref.pos_traits -= path + continue + var/take_flags = initial(path.can_take) + if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS))) + pref.pos_traits -= path + //Neutral traits + for(var/datum/trait/path as anything in pref.neu_traits) + if(!(path in GLOB.neutral_traits)) + to_world_log("removing [path] for not being in neutral_traits") + pref.neu_traits -= path + continue + if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_neutral)) + to_world_log("removing [path] for not being a custom species") + pref.neu_traits -= path + continue + var/take_flags = initial(path.can_take) + if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS))) + to_world_log("removing [path] for being a dirty synth") + pref.neu_traits -= path + //Negative traits + for(var/datum/trait/path as anything in pref.neg_traits) + if(!(path in GLOB.negative_traits)) + pref.neg_traits -= path + continue + if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_negative)) + pref.neg_traits -= path + continue + var/take_flags = initial(path.can_take) + if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS))) + pref.neg_traits -= path + + var/datum/species/selected_species = GLOB.all_species[pref.species] + if(selected_species.selects_bodytype) + if (!(pref.custom_base in pref.get_custom_bases_for_species())) + pref.custom_base = SPECIES_HUMAN + //otherwise, allowed! + else if(!pref.custom_base || !(pref.custom_base in GLOB.custom_species_bases)) + pref.custom_base = SPECIES_HUMAN + + +/datum/category_item/player_setup_item/general/traits/copy_to_mob(var/mob/living/carbon/human/character) + if(character.isSynthetic()) //Checking if we have a synth on our hands, boys. + pref.dirty_synth = 1 + pref.gross_meatbag = 0 + else + pref.gross_meatbag = 1 + pref.dirty_synth = 0 + + var/datum/species/S = character.species + var/datum/species/new_S = S.produceCopy(pref.pos_traits + pref.neu_traits + pref.neg_traits, character, pref.custom_base, TRUE) + + for(var/datum/trait/T in new_S.traits) + T.apply_pref(src) + + //Any additional non-trait settings can be applied here + new_S.blood_color = pref.blood_color + if(!(pref.blood_reagents == "default")) + new_S.blood_reagents = pref.blood_reagents + + var/species_sounds_to_copy = pref.species_sound // What sounds are we using? + if(species_sounds_to_copy == "Unset") // Are we unset? + species_sounds_to_copy = select_default_species_sound(pref) // This will also grab gendered versions of the sounds, if they exist. + + new_S.species_sounds = species_sounds_to_copy // Now we send our sounds over to the mob + + if(pref.species == SPECIES_CUSTOM) + //Statistics for this would be nice + var/english_traits = english_list(new_S.traits, and_text = ";", comma_text = ";") + log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet + +/datum/category_item/player_setup_item/general/traits/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/datum/species/selected_species = GLOB.all_species[pref.species] + data["selects_bodytype"] = selected_species.selects_bodytype + data["custom_base"] = pref.custom_base + data["blood_color"] = pref.blood_color + data["blood_reagents"] = pref.blood_reagents + + data["pos_traits"] = pref.pos_traits + data["neu_traits"] = pref.neu_traits + data["neg_traits"] = pref.neg_traits + data["traits_cheating"] = pref.traits_cheating + data["max_traits"] = pref.max_traits + data["trait_points"] = pref.starting_trait_points + + return data + +/datum/category_item/player_setup_item/general/traits/tgui_constant_data() + var/list/data = ..() + + var/list/all_traits = list() + for(var/path in GLOB.all_traits) + var/datum/trait/T = GLOB.all_traits[path] + all_traits[path] = list( + "cost" = T.cost, + "name" = T.name, + "category" = T.category, + "has_preferences" = T.has_preferences, + ) + + data["all_traits"] = all_traits + + return data + +/datum/category_item/player_setup_item/general/traits/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + switch(action) + if("custom_base") + var/list/choices = pref.get_custom_bases_for_species() + var/text_choice = tgui_input_list(user, "Pick an icon set for your species:","Icon Base", choices) + if(text_choice in choices) + pref.custom_base = text_choice + return TOPIC_REFRESH_UPDATE_PREVIEW + if("blood_color") + var/color_choice = tgui_color_picker(user, "Pick a blood color (does not apply to synths)","Blood Color",pref.blood_color) + if(color_choice) + pref.blood_color = sanitize_hexcolor(color_choice, default="#A10808") + return TOPIC_REFRESH + if("blood_reset") + var/datum/species/spec = GLOB.all_species[pref.species] + var/new_blood = spec.blood_color ? spec.blood_color : "#A10808" + var/choice = tgui_alert(user, "Reset blood color to species default ([new_blood])?","Reset Blood Color",list("Reset","Cancel")) + if(choice == "Reset") + pref.blood_color = new_blood + return TOPIC_REFRESH + if("blood_reagents") + var/new_blood_reagents = tgui_input_list(user, "Choose your character's blood restoration reagent:", "Character Preference", valid_bloodreagents) + if(new_blood_reagents && CanUseTopic(user)) + pref.blood_reagents = new_blood_reagents + return TOPIC_REFRESH + if("clicked_pos_trait") + var/datum/trait/trait = text2path(params["trait"]) + var/choice = tgui_alert(user, "Remove [initial(trait.name)] and regain [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel")) + if(choice == "Remove") + pref.pos_traits -= trait + var/datum/trait/instance = GLOB.all_traits[trait] + instance.remove_pref(pref) + return TOPIC_REFRESH + if("clicked_neu_trait") + var/datum/trait/trait = text2path(params["trait"]) + var/choice = tgui_alert(user, "Remove [initial(trait.name)]?","Remove Trait",list("Remove","Cancel")) + if(choice == "Remove") + pref.neu_traits -= trait + var/datum/trait/instance = GLOB.all_traits[trait] + instance.remove_pref(pref) + return TOPIC_REFRESH + if("clicked_neg_trait") + var/datum/trait/trait = text2path(params["trait"]) + var/choice = tgui_alert(user, "Remove [initial(trait.name)] and lose [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel")) + if(choice == "Remove") + pref.neg_traits -= trait + var/datum/trait/instance = GLOB.all_traits[trait] + instance.remove_pref(pref) + return TOPIC_REFRESH + if("clicked_trait_pref") + var/datum/trait/trait = text2path(params["clicked_trait_pref"]) + get_pref_choice_from_trait(user, trait, params["pref"]) + return TOPIC_REFRESH + if("add_trait") + var/mode = text2num(params["add_trait"]) + var/list/picklist + var/list/mylist + switch(mode) + if(POSITIVE_MODE) + if(pref.species == SPECIES_CUSTOM) + picklist = GLOB.positive_traits.Copy() - pref.pos_traits + mylist = pref.pos_traits + else + picklist = GLOB.everyone_traits_positive.Copy() - pref.pos_traits + mylist = pref.pos_traits + if(NEUTRAL_MODE) + if(pref.species == SPECIES_CUSTOM) + picklist = GLOB.neutral_traits.Copy() - pref.neu_traits + mylist = pref.neu_traits + else + picklist = GLOB.everyone_traits_neutral.Copy() - pref.neu_traits + mylist = pref.neu_traits + if(NEGATIVE_MODE) + if(pref.species == SPECIES_CUSTOM) + picklist = GLOB.negative_traits.Copy() - pref.neg_traits + mylist = pref.neg_traits + else + picklist = GLOB.everyone_traits_negative.Copy() - pref.neg_traits + mylist = pref.neg_traits + + if(isnull(picklist)) + return TOPIC_REFRESH + + if(isnull(mylist)) + return TOPIC_REFRESH + + var/list/nicelist = list() + for(var/P in picklist) + var/datum/trait/T = picklist[P] + nicelist[T.name] = P + + var/points_left = pref.starting_trait_points + for(var/T in pref.pos_traits + pref.neu_traits + pref.neg_traits) + points_left -= GLOB.traits_costs[T] + + var/traits_left = pref.max_traits - (pref.pos_traits.len + pref.neg_traits.len) + + var/message = "Select a trait to learn more." + if(mode != NEUTRAL_MODE) + message = "\[Remaining: [points_left] points, [traits_left] traits\]\n" + message + var/title = "Traits" + switch(mode) + if(POSITIVE_MODE) + title = "Positive Traits" + if(NEUTRAL_MODE) + title = "Neutral Traits" + if(NEGATIVE_MODE) + title = "Negative Traits" + + var/trait_choice + var/done = FALSE + while(!done) + trait_choice = tgui_input_list(user, message, title, nicelist) + if(!trait_choice) + done = TRUE + if(trait_choice in nicelist) + var/datum/trait/path = nicelist[trait_choice] + var/choice = tgui_alert(user, "\[Cost:[initial(path.cost)]\] [initial(path.desc)]",initial(path.name), list("Take Trait","Go Back")) + if(choice == "Take Trait") + done = TRUE + + if(!trait_choice) + return TOPIC_REFRESH + else if(trait_choice in nicelist) + var/datum/trait/path = nicelist[trait_choice] + var/datum/trait/instance = GLOB.all_traits[path] + + var/conflict = FALSE + + if(pref.dirty_synth && !(instance.can_take & SYNTHETICS)) + tgui_alert_async(user, "The trait you've selected can only be taken by organic characters!", "Error") + return TOPIC_REFRESH + + if(pref.gross_meatbag && !(instance.can_take & ORGANICS)) + tgui_alert_async(user, "The trait you've selected can only be taken by synthetic characters!", "Error") + return TOPIC_REFRESH + + if(pref.species in instance.banned_species) + tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error") + return TOPIC_REFRESH + + if( LAZYLEN(instance.allowed_species) && !(pref.species in instance.allowed_species)) + tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error") + return TOPIC_REFRESH + + if(trait_choice in (pref.pos_traits + pref.neu_traits + pref.neg_traits)) + conflict = instance.name + + varconflict: + for(var/P in (pref.pos_traits + pref.neu_traits + pref.neg_traits)) + var/datum/trait/instance_test = GLOB.all_traits[P] + if(path in instance_test.excludes) + conflict = instance_test.name + break varconflict + + for(var/V in instance.var_changes) + if(V == "flags") + continue + if(V in instance_test.var_changes) + conflict = instance_test.name + break varconflict + + for(var/V in instance.var_changes_pref) + if(V in instance_test.var_changes_pref) + conflict = instance_test.name + break varconflict + + if(conflict) + tgui_alert_async(user, "You cannot take this trait and [conflict] at the same time. Please remove that trait, or pick another trait to add.", "Error") + return TOPIC_REFRESH + + instance.apply_pref(pref) + mylist[path] = instance.get_default_prefs() + return TOPIC_REFRESH + +#undef POSITIVE_MODE +#undef NEUTRAL_MODE +#undef NEGATIVE_MODE diff --git a/code/modules/client/preference_setup/vore/08_nif.dm b/code/modules/client/preference_setup/general/13_nif.dm similarity index 79% rename from code/modules/client/preference_setup/vore/08_nif.dm rename to code/modules/client/preference_setup/general/13_nif.dm index 4075e0db0d..6cc9c48c44 100644 --- a/code/modules/client/preference_setup/vore/08_nif.dm +++ b/code/modules/client/preference_setup/general/13_nif.dm @@ -6,7 +6,7 @@ // Definition of the stuff for NIFs // Magic bullshit, they're stored separately from everything else -/datum/category_item/player_setup_item/vore/nif +/datum/category_item/player_setup_item/general/nif name = "NIF Data" sort_order = 8 @@ -15,7 +15,7 @@ return return "data/player_saves/[copytext(ckey,1,2)]/[ckey]/nif.json" -/datum/category_item/player_setup_item/vore/nif/load_character() +/datum/category_item/player_setup_item/general/nif/load_character() var/datum/json_savefile/savefile = new /datum/json_savefile(nif_savefile_path(pref.client_ckey)) var/list/save_data_file = savefile.get_entry("character[pref.default_slot]", list()) @@ -23,7 +23,7 @@ pref.nif_durability = save_data_file["nif_durability"] pref.nif_savedata = check_list_copy(save_data_file["nif_savedata"]) -/datum/category_item/player_setup_item/vore/nif/save_character() +/datum/category_item/player_setup_item/general/nif/save_character() var/datum/json_savefile/savefile = new /datum/json_savefile(nif_savefile_path(pref.client_ckey)) var/list/save_data_file = savefile.get_entry("character[pref.default_slot]", list()) @@ -34,7 +34,7 @@ savefile.set_entry("character[pref.default_slot]", save_data_file) savefile.save() -/datum/category_item/player_setup_item/vore/nif/sanitize_character() +/datum/category_item/player_setup_item/general/nif/sanitize_character() if(pref.nif_path && !ispath(pref.nif_path)) //We have at least a text string that should be a path. pref.nif_path = text2path(pref.nif_path) //Try to convert it to a hard path. if(!pref.nif_path) //If we couldn't, kill it. @@ -51,10 +51,14 @@ if(!islist(pref.nif_savedata)) pref.nif_savedata = list() -/datum/category_item/player_setup_item/vore/nif/copy_to_mob(var/mob/living/carbon/human/character) +/datum/category_item/player_setup_item/general/nif/copy_to_mob(var/mob/living/carbon/human/character) //If you had a NIF... if(istype(character) && ispath(pref.nif_path) && pref.nif_durability) new pref.nif_path(character, pref.nif_durability, pref.nif_savedata) -/datum/category_item/player_setup_item/vore/nif/content(var/mob/user) - . += span_bold("NIF:") + " [ispath(pref.nif_path) ? "Present" : "None"]" +/datum/category_item/player_setup_item/general/nif/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["nif"] = ispath(pref.nif_path) + + return data diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/loadout/01_equipment.dm similarity index 52% rename from code/modules/client/preference_setup/general/04_equipment.dm rename to code/modules/client/preference_setup/loadout/01_equipment.dm index 01004fcfa4..1584152a6c 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/loadout/01_equipment.dm @@ -2,11 +2,11 @@ var/list/all_underwear var/list/all_underwear_metadata -/datum/category_item/player_setup_item/general/equipment - name = "Clothing" - sort_order = 4 +/datum/category_item/player_setup_item/loadout/equipment + name = "Equipment" + sort_order = 1 -/datum/category_item/player_setup_item/general/equipment/load_character(list/save_data) +/datum/category_item/player_setup_item/loadout/equipment/load_character(list/save_data) pref.all_underwear = check_list_copy(save_data["all_underwear"]) pref.all_underwear_metadata = check_list_copy(save_data["all_underwear_metadata"]) for(var/i in pref.all_underwear_metadata) @@ -19,7 +19,7 @@ pref.shoe_hater = save_data["shoe_hater"] pref.no_jacket = save_data["no_jacket"] -/datum/category_item/player_setup_item/general/equipment/save_character(list/save_data) +/datum/category_item/player_setup_item/loadout/equipment/save_character(list/save_data) save_data["all_underwear"] = pref.all_underwear var/list/underwear = list() for(var/i in pref.all_underwear_metadata) @@ -61,7 +61,7 @@ var/global/list/valid_ringtones = list( ) // Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/general/equipment/copy_to_mob(var/mob/living/carbon/human/character) +/datum/category_item/player_setup_item/loadout/equipment/copy_to_mob(var/mob/living/carbon/human/character) character.all_underwear.Cut() character.all_underwear_metadata.Cut() @@ -88,9 +88,7 @@ var/global/list/valid_ringtones = list( pref.pdachoice = 1 character.pdachoice = pref.pdachoice -/datum/category_item/player_setup_item/general/equipment/sanitize_character() - if(!islist(pref.gear)) pref.gear = list() - +/datum/category_item/player_setup_item/loadout/equipment/sanitize_character() if(!istype(pref.all_underwear)) pref.all_underwear = list() @@ -120,29 +118,53 @@ var/global/list/valid_ringtones = list( pref.pdachoice = sanitize_integer(pref.pdachoice, 1, pdachoicelist.len, initial(pref.pdachoice)) pref.ringtone = sanitize(pref.ringtone, 20) -/datum/category_item/player_setup_item/general/equipment/content() - . = list() - . += span_bold("Equipment:") + "
" +/datum/category_item/player_setup_item/loadout/equipment/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/list/underwear_data = list() for(var/datum/category_group/underwear/UWC in global_underwear.categories) - var/item_name = pref.all_underwear[UWC.name] ? pref.all_underwear[UWC.name] : "None" - . += "[UWC.name]: [item_name]" + var/item_name = LAZYACCESS(pref.all_underwear, UWC.name) || "None" + + var/list/tweaks = list() var/datum/category_item/underwear/UWI = UWC.items_by_name[item_name] if(UWI) for(var/datum/gear_tweak/gt in UWI.tweaks) - . += " [gt.get_contents(get_metadata(UWC.name, gt))]" + UNTYPED_LIST_ADD(tweaks, list( + "ref" = REF(gt), + "contents" = gt.get_contents(get_metadata(UWC.name, gt)) + )) - . += "
" - . += "Headset Type: [GLOB.headsetlist[pref.headset]]
" - . += "Backpack Type: [backbaglist[pref.backbag]]
" - . += "PDA Type: [pdachoicelist[pref.pdachoice]]
" - . += "Communicator Visibility: [(pref.communicator_visibility) ? "Yes" : "No"]
" - . += "Ringtone (leave blank for job default): [pref.ringtone]
" - . += "Spawn With Shoes:[(pref.shoe_hater) ? "No" : "Yes"]
" - . += "Spawn With Jacket:[(pref.no_jacket) ? "No" : "Yes"]
" + UNTYPED_LIST_ADD(underwear_data, list( + "category" = UWC.name, + "name" = item_name, + "tweaks" = tweaks + )) + data["underwear"] = underwear_data - return jointext(.,null) + data["headset_type"] = GLOB.headsetlist[pref.headset] + data["backpack_type"] = backbaglist[pref.backbag] + data["pda_type"] = pdachoicelist[pref.pdachoice] + data["communicator_visibility"] = pref.communicator_visibility // boolean + data["ringtone"] = pref.ringtone + data["shoes"] = pref.shoe_hater + data["jacket"] = pref.no_jacket -/datum/category_item/player_setup_item/general/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt) + return data + +/datum/category_item/player_setup_item/loadout/equipment/tgui_static_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + return data + +/datum/category_item/player_setup_item/loadout/equipment/tgui_constant_data() + var/list/data = ..() + + data["headsetlist"] = GLOB.headsetlist + data["backbaglist"] = backbaglist + data["pdachoicelist"] = pdachoicelist + + return data + +/datum/category_item/player_setup_item/loadout/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt) var/metadata = pref.all_underwear_metadata[underwear_category] if(!metadata) metadata = list() @@ -154,72 +176,80 @@ var/global/list/valid_ringtones = list( metadata["[gt]"] = tweak_data return tweak_data -/datum/category_item/player_setup_item/general/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata) +/datum/category_item/player_setup_item/loadout/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata) var/list/metadata = pref.all_underwear_metadata[underwear_category] metadata["[gt]"] = new_metadata +/datum/category_item/player_setup_item/loadout/equipment/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return -/datum/category_item/player_setup_item/general/equipment/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["change_headset"]) - var/new_headset = tgui_input_list(user, "Choose your character's style of headset:", "Character Preference", GLOB.headsetlist, GLOB.headsetlist[pref.headset]) - if(!isnull(new_headset) && CanUseTopic(user)) - pref.headset = GLOB.headsetlist.Find(new_headset) + var/mob/user = ui.user + + switch(action) + if("change_headset") + // Takes the JS index + var/new_headset = text2num(params["headset"]) + 1 + if(LAZYACCESS(GLOB.headsetlist, new_headset)) + pref.headset = new_headset + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("change_backpack") + // Takes the JS index + var/new_backbag = text2num(params["backbag"]) + 1 + if(LAZYACCESS(backbaglist, new_backbag)) + pref.backbag = new_backbag + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("change_pda") + // Takes the JS index + var/new_pdachoice = text2num(params["pda"]) + 1 + if(LAZYACCESS(backbaglist, new_pdachoice)) + pref.pdachoice = new_pdachoice + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("change_underwear") + var/datum/category_group/underwear/UWC = LAZYACCESS(global_underwear.categories_by_name, params["underwear"]) + if(!UWC) + return + var/datum/category_item/underwear/selected_underwear = tgui_input_list(user, "Choose underwear:", "Character Preference", UWC.items, pref.all_underwear[UWC.name]) + if(selected_underwear) + pref.all_underwear[UWC.name] = selected_underwear.name return TOPIC_REFRESH_UPDATE_PREVIEW - if(href_list["change_backpack"]) - var/new_backbag = tgui_input_list(user, "Choose your character's style of bag:", "Character Preference", backbaglist, backbaglist[pref.backbag]) - if(!isnull(new_backbag) && CanUseTopic(user)) - pref.backbag = backbaglist.Find(new_backbag) - return TOPIC_REFRESH_UPDATE_PREVIEW + if("underwear_tweak") + var/underwear = params["underwear"] + if(!(underwear in pref.all_underwear)) + return TOPIC_NOACTION + var/datum/gear_tweak/gt = locate(params["tweak"]) + if(!gt) + return TOPIC_NOACTION + var/new_metadata = gt.get_metadata(user, get_metadata(underwear, gt)) + if(new_metadata) + set_metadata(underwear, gt, new_metadata) + return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["change_pda"]) - var/new_pdachoice = tgui_input_list(user, "Choose your character's style of PDA:", "Character Preference", pdachoicelist, pdachoicelist[pref.pdachoice]) - if(!isnull(new_pdachoice) && CanUseTopic(user)) - pref.pdachoice = pdachoicelist.Find(new_pdachoice) - return TOPIC_REFRESH - - else if(href_list["change_underwear"]) - var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]] - if(!UWC) - return - var/datum/category_item/underwear/selected_underwear = tgui_input_list(user, "Choose underwear:", "Character Preference", UWC.items, pref.all_underwear[UWC.name]) - if(selected_underwear && CanUseTopic(user)) - pref.all_underwear[UWC.name] = selected_underwear.name - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["underwear"] && href_list["tweak"]) - var/underwear = href_list["underwear"] - if(!(underwear in pref.all_underwear)) - return TOPIC_NOACTION - var/datum/gear_tweak/gt = locate(href_list["tweak"]) - if(!gt) - return TOPIC_NOACTION - var/new_metadata = gt.get_metadata(user, get_metadata(underwear, gt)) - if(new_metadata) - set_metadata(underwear, gt, new_metadata) - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["toggle_comm_visibility"]) - if(CanUseTopic(user)) + if("toggle_comm_visibility") pref.communicator_visibility = !pref.communicator_visibility return TOPIC_REFRESH - else if(href_list["set_ringtone"]) - var/choice = tgui_input_list(user, "Please select a ringtone. All of these choices come with an associated preset sound. Alternately, select \"Other\" to specify manually.", "Character Preference", valid_ringtones + "Other", pref.ringtone) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = sanitize(tgui_input_text(user, "Please enter a custom ringtone. If this doesn't match any of the other listed choices, your PDA will use the default (\"beep\") sound.", "Character Preference", null, 20), 20) - if(raw_choice && CanUseTopic(user)) - pref.ringtone = raw_choice - else - pref.ringtone = choice - return TOPIC_REFRESH - else if(href_list["toggle_shoes"]) - if(CanUseTopic(user)) - pref.shoe_hater = !pref.shoe_hater - return TOPIC_REFRESH - else if(href_list["toggle_jacket"]) - if(CanUseTopic(user)) - pref.no_jacket = !pref.no_jacket + + if("set_ringtone") + var/choice = tgui_input_list(user, "Please select a ringtone. All of these choices come with an associated preset sound. Alternately, select \"Other\" to specify manually.", "Character Preference", valid_ringtones + "Other", pref.ringtone) + if(!choice) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = sanitize(tgui_input_text(user, "Please enter a custom ringtone. If this doesn't match any of the other listed choices, your PDA will use the default (\"beep\") sound.", "Character Preference", null, 20), 20) + if(raw_choice) + pref.ringtone = raw_choice + else + pref.ringtone = choice return TOPIC_REFRESH - return ..() + if("toggle_shoes") + pref.shoe_hater = !pref.shoe_hater + return TOPIC_REFRESH + + if("toggle_jacket") + pref.no_jacket = !pref.no_jacket + return TOPIC_REFRESH diff --git a/code/modules/client/preference_setup/loadout/02_loadout.dm b/code/modules/client/preference_setup/loadout/02_loadout.dm new file mode 100644 index 0000000000..5a754046ab --- /dev/null +++ b/code/modules/client/preference_setup/loadout/02_loadout.dm @@ -0,0 +1,295 @@ +var/list/loadout_categories = list() +var/list/gear_datums = list() + +/datum/loadout_category + var/category = "" + var/list/gear = list() + +/datum/loadout_category/New(var/cat) + category = cat + ..() + +/hook/startup/proc/populate_gear_list() + + //create a list of gear datums to sort + for(var/datum/gear/G as anything in subtypesof(/datum/gear)) + if(initial(G.type_category) == G) + continue + var/use_name = initial(G.display_name) + var/use_category = initial(G.sort_category) + + if(!use_name) + error("Loadout - Missing display name: [G]") + continue + if(isnull(initial(G.cost))) + error("Loadout - Missing cost: [G]") + continue + if(!initial(G.path)) + error("Loadout - Missing path definition: [G]") + continue + + if(!loadout_categories[use_category]) + loadout_categories[use_category] = new /datum/loadout_category(use_category) + var/datum/loadout_category/LC = loadout_categories[use_category] + gear_datums[use_name] = new G + LC.gear[use_name] = gear_datums[use_name] + + loadout_categories = sortAssoc(loadout_categories) + for(var/loadout_category in loadout_categories) + var/datum/loadout_category/LC = loadout_categories[loadout_category] + LC.gear = sortAssoc(LC.gear) + return 1 + +/datum/category_item/player_setup_item/loadout/loadout + name = "Loadout" + sort_order = 2 + var/current_tab = "General" + +/datum/category_item/player_setup_item/loadout/loadout/load_character(list/save_data) + pref.gear_list = list() + var/all_gear = check_list_copy(save_data["gear_list"]) + for(var/i in all_gear) + var/list/entries = check_list_copy(all_gear["[i]"]) + for(var/j in entries) + entries["[j]"] = path2text_list(entries["[j]"]) + pref.gear_list["[i]"] = entries + pref.gear_slot = save_data["gear_slot"] + +/datum/category_item/player_setup_item/loadout/loadout/save_character(list/save_data) + var/list/all_gear = list() + var/worn_gear = check_list_copy(pref.gear_list) + for(var/i in worn_gear) + var/list/entries = check_list_copy(worn_gear["[i]"]) + if(!length(entries)) + entries = check_list_copy(worn_gear[i]) + for(var/j in entries) + entries["[j]"] = check_list_copy(entries["[j]"]) + all_gear["[i]"] = entries + save_data["gear_list"] = all_gear + save_data["gear_slot"] = pref.gear_slot + +/datum/category_item/player_setup_item/loadout/loadout/proc/is_valid_gear(datum/gear/G, max_cost) + if(G.whitelisted && CONFIG_GET(flag/loadout_whitelist) != LOADOUT_WHITELIST_OFF && pref.client) //VOREStation Edit. + if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_STRICT && G.whitelisted != pref.species) + return FALSE + if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_LAX && !is_alien_whitelisted(pref.client, GLOB.all_species[G.whitelisted])) + return FALSE + + if(max_cost && G.cost > max_cost) + return FALSE + if(pref.client) + if(G.ckeywhitelist && !(pref.client_ckey in G.ckeywhitelist)) + return FALSE + if(G.character_name && !(pref.real_name in G.character_name)) + return FALSE + return TRUE + +/datum/category_item/player_setup_item/loadout/loadout/sanitize_character() + var/mob/preference_mob = preference_mob() + if(LAZYLEN(pref.gear_list) < 0) + return + + if(pref.gear_slot > LAZYLEN(pref.gear_list)) + pref.gear_slot = 1 + + var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]") + var/total_cost = 0 + for(var/gear_name in active_gear_list) + if(!gear_datums[gear_name]) + to_chat(preference_mob, span_warning("You cannot have the \the [gear_name].")) + active_gear_list -= gear_name + continue + + var/datum/gear/G = gear_datums[gear_name] + if(!is_valid_gear(G)) + to_chat(preference_mob, span_warning("You cannot take \the [gear_name] as you are not whitelisted for the species or item.")) + active_gear_list -= gear_name + continue + + if(total_cost + G.cost > MAX_GEAR_COST) + active_gear_list -= gear_name + to_chat(preference_mob, span_warning("You cannot afford to take \the [gear_name]")) + continue + total_cost += G.cost + +/datum/category_item/player_setup_item/loadout/loadout/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["total_cost"] = get_total() + data["gear_slot"] = pref.gear_slot + var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]") + data["active_gear_list"] = active_gear_list + + var/list/gear_tweaks = list() + for(var/item in active_gear_list) + var/datum/gear/G = gear_datums[item] + var/list/tweaks = list() + for(var/datum/gear_tweak/tweak in G.gear_tweaks) + UNTYPED_LIST_ADD(tweaks, list( + "ref" = REF(tweak), + "contents" = tweak.get_contents(get_tweak_metadata(G, tweak)) + )) + gear_tweaks[G.display_name] = tweaks + data["gear_tweaks"] = gear_tweaks + + return data + +/datum/category_item/player_setup_item/loadout/loadout/tgui_static_data(mob/user) + var/list/data = ..() + + var/list/categories = list() + for(var/category in loadout_categories) + var/datum/loadout_category/LC = loadout_categories[category] + var/list/items = list() + for(var/gear in LC.gear) + var/datum/gear/G = LC.gear[gear] + if(!is_valid_gear(G)) + continue + + UNTYPED_LIST_ADD(items, list( + "name" = G.display_name, + "desc" = G.description, + "cost" = G.cost, + "show_roles" = G.show_roles, + "allowed_roles" = G.allowed_roles + )) + categories[category] = items + data["categories"] = categories + + data["max_gear_cost"] = MAX_GEAR_COST + + return data + +/datum/category_item/player_setup_item/loadout/loadout/proc/get_gear_metadata(var/datum/gear/G) + var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]") + + // {"/datum/gear_tweak/custom_name": "" } + . = LAZYACCESS(active_gear_list, G.display_name) + if(!.) + . = list() + +/datum/category_item/player_setup_item/loadout/loadout/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak) + var/list/metadata = get_gear_metadata(G) + . = metadata["[tweak]"] + if(!.) + . = tweak.get_default() + metadata["[tweak]"] = . + +/datum/category_item/player_setup_item/loadout/loadout/proc/set_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak, var/new_metadata) + var/list/metadata = get_gear_metadata(G) + metadata["[tweak]"] = new_metadata + +/datum/category_item/player_setup_item/loadout/loadout/proc/get_total() + var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]") + . = 0 + for(var/gear_name in active_gear_list) + var/datum/gear/G = gear_datums[gear_name] + if(G) + . += G.cost + +/datum/category_item/player_setup_item/loadout/loadout/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]") + var/mob/user = ui.user + + switch(action) + if("next_slot") + pref.gear_slot = pref.gear_slot + 1 + if(pref.gear_slot > CONFIG_GET(number/loadout_slots)) + pref.gear_slot = 1 + if(!("[pref.gear_slot]" in pref.gear_list)) + pref.gear_list["[pref.gear_slot]"] = list() + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("prev_slot") + pref.gear_slot = pref.gear_slot - 1 + if(pref.gear_slot <= 0) + pref.gear_slot = CONFIG_GET(number/loadout_slots) + if(!("[pref.gear_slot]" in pref.gear_list)) + pref.gear_list["[pref.gear_slot]"] = list() + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("clear_loadout") + active_gear_list.Cut() + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("copy_loadout") + var/copy_to = tgui_input_number(user, "What slot would you like to copy slot [pref.gear_slot] to?", "Copy Slot", 1, CONFIG_GET(number/loadout_slots), 1) + if(!copy_to) + return TOPIC_HANDLED + + if("[copy_to]" in pref.gear_list) + var/confirm = tgui_alert(user, "This will overwrite slot [copy_to], are you sure?", "Are you sure?", list("No", "Yes")) + if(confirm != "Yes") + return TOPIC_HANDLED + + pref.gear_list["[copy_to]"] = check_list_copy(active_gear_list) + return TOPIC_REFRESH + + if("toggle_gear") + var/datum/gear/TG = gear_datums[params["gear"]] + if(TG) + if(TG.display_name in active_gear_list) + active_gear_list -= TG.display_name + else if(get_total() + TG.cost <= MAX_GEAR_COST) + active_gear_list[TG.display_name] = list() + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("gear_tweak") + var/datum/gear/gear = gear_datums[params["gear"]] + var/datum/gear_tweak/tweak = locate(params["tweak"]) + if(!tweak || !gear || !(tweak in gear.gear_tweaks)) + return TOPIC_HANDLED + var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak)) + if(!metadata) + return TOPIC_HANDLED + set_tweak_metadata(gear, tweak, metadata) + return TOPIC_REFRESH_UPDATE_PREVIEW + +/datum/gear + var/display_name //Name/index. Must be unique. + var/description //Description of this gear. If left blank will default to the description of the pathed item. + var/path //Path to item. + var/cost = 1 //Number of points used. Items in general cost 1 point, storage/armor/gloves/special use costs 2 points. + var/slot //Slot to equip to. + var/list/allowed_roles //Roles that can spawn with this item. + var/show_roles = TRUE //Show the role restrictions on this item? + var/whitelisted //Term to check the whitelist for.. + var/sort_category = "General" + var/list/gear_tweaks = list() //List of datums which will alter the item after it has been spawned. + var/exploitable = 0 //Does it go on the exploitable information list? + var/type_category = null + var/list/ckeywhitelist //restricted based on these ckeys? + var/list/character_name //restricted to these character names? + +/datum/gear/New() + ..() + if(!description) + var/obj/O = path + description = initial(O.desc) + gear_tweaks = list(gear_tweak_free_name, gear_tweak_free_desc, GLOB.gear_tweak_item_tf_spawn, GLOB.gear_tweak_free_matrix_recolor) + +/datum/gear_data + var/path + var/location + +/datum/gear_data/New(var/path, var/location) + src.path = path + src.location = location + +/datum/gear/proc/spawn_item(var/location, var/metadata) + var/datum/gear_data/gd = new(path, location) + if(length(gear_tweaks) && metadata) + for(var/datum/gear_tweak/gt in gear_tweaks) + gt.tweak_gear_data(metadata["[gt]"], gd) + var/item = new gd.path(gd.location) + if(length(gear_tweaks) && metadata) + for(var/datum/gear_tweak/gt in gear_tweaks) + gt.tweak_item(item, metadata["[gt]"]) + var/mob/M = location + if(istype(M) && exploitable) //Update exploitable info records for the mob without creating a duplicate object at their feet. + M.amend_exploitable(item) + return item diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm deleted file mode 100644 index 80a797a49c..0000000000 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ /dev/null @@ -1,304 +0,0 @@ -var/list/loadout_categories = list() -var/list/gear_datums = list() - -/datum/loadout_category - var/category = "" - var/list/gear = list() - -/datum/loadout_category/New(var/cat) - category = cat - ..() - -/hook/startup/proc/populate_gear_list() - - //create a list of gear datums to sort - for(var/datum/gear/G as anything in subtypesof(/datum/gear)) - if(initial(G.type_category) == G) - continue - var/use_name = initial(G.display_name) - var/use_category = initial(G.sort_category) - - if(!use_name) - error("Loadout - Missing display name: [G]") - continue - if(isnull(initial(G.cost))) - error("Loadout - Missing cost: [G]") - continue - if(!initial(G.path)) - error("Loadout - Missing path definition: [G]") - continue - - if(!loadout_categories[use_category]) - loadout_categories[use_category] = new /datum/loadout_category(use_category) - var/datum/loadout_category/LC = loadout_categories[use_category] - gear_datums[use_name] = new G - LC.gear[use_name] = gear_datums[use_name] - - loadout_categories = sortAssoc(loadout_categories) - for(var/loadout_category in loadout_categories) - var/datum/loadout_category/LC = loadout_categories[loadout_category] - LC.gear = sortAssoc(LC.gear) - return 1 - -/datum/category_item/player_setup_item/loadout - name = "Loadout" - sort_order = 1 - var/current_tab = "General" - -/datum/category_item/player_setup_item/loadout/load_character(list/save_data) - pref.gear_list = list() - var/all_gear = check_list_copy(save_data["gear_list"]) - for(var/i in all_gear) - var/list/entries = check_list_copy(all_gear["[i]"]) - for(var/j in entries) - entries["[j]"] = path2text_list(entries["[j]"]) - pref.gear_list["[i]"] = entries - pref.gear_slot = save_data["gear_slot"] - if(pref.gear_list!=null && pref.gear_slot!=null) - pref.gear = pref.gear_list["[pref.gear_slot]"] - else - pref.gear = check_list_copy(save_data["gear"]) - pref.gear_slot = 1 - -/datum/category_item/player_setup_item/loadout/save_character(list/save_data) - pref.gear_list["[pref.gear_slot]"] = pref.gear - var/list/all_gear = list() - var/worn_gear = check_list_copy(pref.gear_list) - for(var/i in worn_gear) - var/list/entries = check_list_copy(worn_gear["[i]"]) - if(!length(entries)) - entries = check_list_copy(worn_gear[i]) - for(var/j in entries) - entries["[j]"] = check_list_copy(entries["[j]"]) - all_gear["[i]"] = entries - save_data["gear_list"] = all_gear - save_data["gear_slot"] = pref.gear_slot - -/datum/category_item/player_setup_item/loadout/proc/valid_gear_choices(var/max_cost) - . = list() - for(var/gear_name in gear_datums) - var/datum/gear/G = gear_datums[gear_name] - - if(G.whitelisted && CONFIG_GET(flag/loadout_whitelist) != LOADOUT_WHITELIST_OFF && pref.client) //VOREStation Edit. - if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_STRICT && G.whitelisted != pref.species) - continue - if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_LAX && !is_alien_whitelisted(pref.client, GLOB.all_species[G.whitelisted])) - continue - - if(max_cost && G.cost > max_cost) - continue - if(pref.client) - if(G.ckeywhitelist && !(pref.client_ckey in G.ckeywhitelist)) - continue - if(G.character_name && pref.client.prefs && !(pref.client.prefs.real_name in G.character_name)) - continue - . += gear_name - -/datum/category_item/player_setup_item/loadout/sanitize_character() - var/mob/preference_mob = preference_mob() - if(!islist(pref.gear)) - pref.gear = list() - if(!islist(pref.gear_list)) - pref.gear_list = list() - - for(var/gear_name in pref.gear) - if(!(gear_name in gear_datums)) - pref.gear -= gear_name - var/total_cost = 0 - for(var/gear_name in pref.gear) - if(!gear_datums[gear_name]) - to_chat(preference_mob, span_warning("You cannot have more than one of the \the [gear_name]")) - pref.gear -= gear_name - else if(!(gear_name in valid_gear_choices())) - to_chat(preference_mob, span_warning("You cannot take \the [gear_name] as you are not whitelisted for the species or item.")) //Vorestation Edit - pref.gear -= gear_name - else - var/datum/gear/G = gear_datums[gear_name] - if(total_cost + G.cost > MAX_GEAR_COST) - pref.gear -= gear_name - to_chat(preference_mob, span_warning("You cannot afford to take \the [gear_name]")) - else - total_cost += G.cost - -/datum/category_item/player_setup_item/loadout/content() - . = list() - var/mob/preference_mob = preference_mob() //Vorestation Edit - var/total_cost = 0 - if(pref.gear && pref.gear.len) - for(var/i = 1; i <= pref.gear.len; i++) - var/datum/gear/G = gear_datums[pref.gear[i]] - if(G) - total_cost += G.cost - - var/fcolor = "#3366CC" - if(total_cost < MAX_GEAR_COST) - fcolor = "#E67300" - - . += "" - . += "" - - . += "" - - var/datum/loadout_category/LC = loadout_categories[current_tab] - . += "" - . += "" - . += "" - for(var/gear_name in LC.gear) - var/datum/gear/G = LC.gear[gear_name] - if(preference_mob && preference_mob.client) - if(G.ckeywhitelist && !(preference_mob.ckey in G.ckeywhitelist)) - continue - if(G.character_name && preference_mob.client.prefs && !(preference_mob.client.prefs.real_name in G.character_name)) - continue - var/ticked = (G.display_name in pref.gear) - . += "" - . += "" - . += "" - if(G.show_roles && G.allowed_roles) - . += "" - if(ticked) - . += "" - . += "
\<\<" + span_bold("\[[pref.gear_slot]\] ") + "\>\>" + span_bold("[total_cost]/[MAX_GEAR_COST] loadout points spent.") + " \[Clear Loadout\]
" - var/firstcat = 1 - for(var/category in loadout_categories) - - if(firstcat) - firstcat = 0 - else - . += " |" - - var/datum/loadout_category/LC = loadout_categories[category] - var/category_cost = 0 - for(var/gear in LC.gear) - if(gear in pref.gear) - var/datum/gear/G = LC.gear[gear] - category_cost += G.cost - - if(category == current_tab) - . += " " + span_linkOn("[category] - [category_cost]") + " " - else - if(category_cost) - . += " [category] - [category_cost] " - else - . += " [category] - 0 " - . += "

" + span_bold("
[LC.category]
") + "

[G.display_name][G.cost]" + span_normal(span_italics("[G.description]")) + "
        Restricted to: [list2text(G.allowed_roles,", ")]
" - for(var/datum/gear_tweak/tweak in G.gear_tweaks) - . += " [tweak.get_contents(get_tweak_metadata(G, tweak))]" - . += "
" - . = jointext(., null) - -/datum/category_item/player_setup_item/loadout/proc/get_gear_metadata(var/datum/gear/G) - . = pref.gear[G.display_name] - if(!.) - . = list() - pref.gear[G.display_name] = . - -/datum/category_item/player_setup_item/loadout/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak) - var/list/metadata = get_gear_metadata(G) - . = metadata["[tweak]"] - if(!.) - . = tweak.get_default() - metadata["[tweak]"] = . - -/datum/category_item/player_setup_item/loadout/proc/set_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak, var/new_metadata) - var/list/metadata = get_gear_metadata(G) - metadata["[tweak]"] = new_metadata - -/datum/category_item/player_setup_item/loadout/OnTopic(href, href_list, user) - if(href_list["toggle_gear"]) - var/datum/gear/TG = gear_datums[href_list["toggle_gear"]] - if(TG.display_name in pref.gear) - pref.gear -= TG.display_name - else - var/total_cost = 0 - for(var/gear_name in pref.gear) - var/datum/gear/G = gear_datums[gear_name] - if(istype(G)) total_cost += G.cost - if((total_cost+TG.cost) <= MAX_GEAR_COST) - pref.gear += TG.display_name - return TOPIC_REFRESH_UPDATE_PREVIEW - if(href_list["gear"] && href_list["tweak"]) - var/datum/gear/gear = gear_datums[url_decode(href_list["gear"])] - var/datum/gear_tweak/tweak = locate(href_list["tweak"]) - if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks)) - return TOPIC_NOACTION - var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak)) - if(!metadata || !CanUseTopic(user)) - return TOPIC_NOACTION - set_tweak_metadata(gear, tweak, metadata) - return TOPIC_REFRESH_UPDATE_PREVIEW - if(href_list["next_slot"] || href_list["prev_slot"]) - //Set the current slot in the gear list to the currently selected gear - pref.gear_list["[pref.gear_slot]"] = pref.gear - //If we're moving up a slot.. - if(href_list["next_slot"]) - //change the current slot number - pref.gear_slot = pref.gear_slot+1 - if(pref.gear_slot > CONFIG_GET(number/loadout_slots)) - pref.gear_slot = 1 - //If we're moving down a slot.. - else if(href_list["prev_slot"]) - //change current slot one down - pref.gear_slot = pref.gear_slot-1 - if(pref.gear_slot<1) - pref.gear_slot = CONFIG_GET(number/loadout_slots) - // Set the currently selected gear to whatever's in the new slot - if(pref.gear_list["[pref.gear_slot]"]) - pref.gear = pref.gear_list["[pref.gear_slot]"] - else - pref.gear = list() - pref.gear_list["[pref.gear_slot]"] = list() - // Refresh? - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["select_category"]) - current_tab = href_list["select_category"] - return TOPIC_REFRESH - else if(href_list["clear_loadout"]) - pref.gear.Cut() - return TOPIC_REFRESH_UPDATE_PREVIEW - return ..() - -/datum/gear - var/display_name //Name/index. Must be unique. - var/description //Description of this gear. If left blank will default to the description of the pathed item. - var/path //Path to item. - var/cost = 1 //Number of points used. Items in general cost 1 point, storage/armor/gloves/special use costs 2 points. - var/slot //Slot to equip to. - var/list/allowed_roles //Roles that can spawn with this item. - var/show_roles = TRUE //Show the role restrictions on this item? - var/whitelisted //Term to check the whitelist for.. - var/sort_category = "General" - var/list/gear_tweaks = list() //List of datums which will alter the item after it has been spawned. - var/exploitable = 0 //Does it go on the exploitable information list? - var/type_category = null - var/list/ckeywhitelist //restricted based on these ckeys? - var/list/character_name //restricted to these character names? - -/datum/gear/New() - ..() - if(!description) - var/obj/O = path - description = initial(O.desc) - gear_tweaks = list(gear_tweak_free_name, gear_tweak_free_desc, GLOB.gear_tweak_item_tf_spawn, GLOB.gear_tweak_free_matrix_recolor) - -/datum/gear_data - var/path - var/location - -/datum/gear_data/New(var/path, var/location) - src.path = path - src.location = location - -/datum/gear/proc/spawn_item(var/location, var/metadata) - var/datum/gear_data/gd = new(path, location) - if(length(gear_tweaks) && metadata) - for(var/datum/gear_tweak/gt in gear_tweaks) - gt.tweak_gear_data(metadata["[gt]"], gd) - var/item = new gd.path(gd.location) - if(length(gear_tweaks) && metadata) - for(var/datum/gear_tweak/gt in gear_tweaks) - gt.tweak_item(item, metadata["[gt]"]) - var/mob/M = location - if(istype(M) && exploitable) //Update exploitable info records for the mob without creating a duplicate object at their feet. - M.amend_exploitable(item) - return item diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 631201cd83..3d48ed30b2 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -64,26 +64,11 @@ if(alt_title && !(alt_title in job.alt_titles)) pref.player_alt_titles -= job.title -/datum/category_item/player_setup_item/occupation/content(mob/user, limit = 25, list/splitJobs = list()) - if(!job_master) - return - - . = list() - . += "
" - . += span_bold("Choose occupation chances") + "
Unavailable occupations are crossed out.
" - . += "" - . += "
" // Table within a table for alignment, also allows you to easily add more columns. - . += "" - var/index = -1 - - //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. - var/datum/job/lastJob - var/datum/department/last_department = null // Used to avoid repeating the look-ahead check for if a whole department can fit. +/datum/category_item/player_setup_item/occupation/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + var/list/jobs_data = list() var/list/all_valid_jobs = list() - // If the occupation window gets opened before SSJob initializes, then it'll just be blank, with no runtimes. - // It will work once init is finished. - for(var/D in SSjob.department_datums) var/datum/department/department = SSjob.department_datums[D] if(department.centcom_only) // No joining as a centcom role, if any are ever added. @@ -92,194 +77,141 @@ for(var/J in department.primary_jobs) all_valid_jobs += department.jobs[J] + jobs_data[department.name] = list() + for(var/datum/job/job in all_valid_jobs) - if(job.latejoin_only) continue //VOREStation Code + if(job.latejoin_only) + continue var/datum/department/current_department = SSjob.get_primary_department_of_job(job) + // for the is_job_whitelisted check.. + usr = user + var/list/job_data = list( + "title" = job.title, + "ref" = REF(job), + "selection_color" = job.selection_color, + // reasons you can't select it + "banned" = !!jobban_isbanned(user, job.title), + "denylist_days" = !job.player_old_enough(user.client), + "available_in_days" = !job.available_in_days(user.client), + "denylist_playtime" = !job.player_has_enough_playtime(user.client), + "available_in_hours" = job.available_in_playhours(user.client), + "denylist_whitelist" = !is_job_whitelisted(user, job.title), + // tigercat2000 - these shouldn't exist >:( + "denylist_character_age" = FALSE, + "min_age" = job.get_min_age(pref.species, pref.organ_data[O_BRAIN]), + "special_color" = "", + "selected" = 0, + "selected_title" = "", + "alt_titles" = list(), + ) - // Should we add a new column? - var/make_new_column = FALSE - if(++index > limit) - // Ran out of rows, make a new column. - make_new_column = TRUE - - else if(job.title in splitJobs) - // Is hardcoded to split at this job title. - make_new_column = TRUE - - else if(current_department != last_department) - // If the department is bigger than the limit then we have to split. - if(limit >= current_department.primary_jobs.len) - // Look ahead to see if we would need to split, and if so, avoid it. - if(index + current_department.primary_jobs.len > limit) - // Looked ahead, and determined that a new column is needed to avoid splitting the department into two. - make_new_column = TRUE - - - if(make_new_column) -/******* - if((index < limit) && (lastJob != null)) - //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with - //the last job's selection color and blank buttons that do nothing. Creating a rather nice effect. - for(var/i = 0, i < (limit - index), i++) - . += "" -*******/ - . += "
//>  
" - index = 0 - last_department = current_department - - . += "" - continue - if(!job.player_old_enough(user.client)) - var/available_in_days = job.available_in_days(user.client) - . += "[rank]" - continue - //VOREStation Add - if(!job.player_has_enough_playtime(user.client)) - var/available_in_hours = job.available_in_playhours(user.client) - . += "[rank]" - continue - if(!is_job_whitelisted(user,rank)) - . += "[rank]" - continue - //VOREStation Add End - if(job.is_species_banned(user.client.prefs.species, user.client.prefs.organ_data[O_BRAIN]) == TRUE) - . += "[rank]" - continue if((job.minimum_character_age || job.min_age_by_species) && user.client && (user.read_preference(/datum/preference/numeric/human/age) < job.get_min_age(user.client.prefs.species, user.client.prefs.organ_data[O_BRAIN]))) - . += "[rank]" - continue + job_data["denylist_character_age"] = TRUE + if((pref.job_civilian_low & ASSISTANT) && job.type != /datum/job/assistant) - . += span_gray("[rank]") + "" - continue - if((rank in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND) ) || (rank == JOB_AI))//Bold head jobs - . += span_bold("[rank]") + "" - else - . += "[rank]" - - . += "" - . += "" - continue + job_data["selected"] = 0 + else if(pref.GetJobDepartment(job, 1) & job.flag) + job_data["selected"] = 3 + else if(pref.GetJobDepartment(job, 2) & job.flag) + job_data["selected"] = 2 + else if(pref.GetJobDepartment(job, 3) & job.flag) + job_data["selected"] = 1 + else + job_data["selected"] = 0 - . += " \[[prefLevelLabel]]" - if(LAZYLEN(job.alt_titles)) - . += "" - . += "" - . += "
" - - var/rank = job.title - lastJob = job - . += "" - if(jobban_isbanned(user, rank)) - . += "[rank]" + span_bold(" \[BANNED]") + "
\[IN [(available_in_days)] DAYS]
\[IN [round(available_in_hours, 0.1)] DEPTHOURS]
" + span_bold(" \[WHITELIST ONLY]") + "
\[THIS RACE/BRAIN TYPE CANNOT TAKE THIS ROLE.\]
\[MINIMUM CHARACTER AGE FOR SELECTED RACE/BRAIN TYPE: [job.get_min_age(user.client.prefs.species, user.client.prefs.organ_data[O_BRAIN])]\]
" - - var/prefLevelLabel = "ERROR" - var/prefLevelColor = "pink" - var/prefUpperLevel = -1 // level to assign on left click - var/prefLowerLevel = -1 // level to assign on right click - if(pref.GetJobDepartment(job, 1) & job.flag) - prefLevelLabel = "High" - prefLevelColor = "55cc55" - prefUpperLevel = 4 - prefLowerLevel = 2 - else if(pref.GetJobDepartment(job, 2) & job.flag) - prefLevelLabel = "Medium" - prefLevelColor = "eecc22" - prefUpperLevel = 1 - prefLowerLevel = 3 - else if(pref.GetJobDepartment(job, 3) & job.flag) - prefLevelLabel = "Low" - prefLevelColor = "cc5555" - prefUpperLevel = 2 - prefLowerLevel = 4 - else - prefLevelLabel = "NEVER" - prefLevelColor = "black" - prefUpperLevel = 3 - prefLowerLevel = 1 - - . += "" + job_data["special_color"] = "gray" + if((job.title in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND) ) || (job.title == JOB_AI))//Bold head jobs + job_data["special_color"] = "bold" if(job.type == /datum/job/assistant)//Assistant is special if(pref.job_civilian_low & ASSISTANT) - . += " \[Yes]" + job_data["selected"] = 4 else - . += " " + span_black("\[No]") - if(LAZYLEN(job.alt_titles)) //Blatantly cloned from a few lines down. - . += "
 \[[pref.GetPlayerAltTitle(job)]\]
 \[[pref.GetPlayerAltTitle(job)]\]
" - . += "
" + job_data["selected_title"] = pref.GetPlayerAltTitle(job) + for(var/title in job.alt_titles) + job_data["alt_titles"] += title - switch(pref.alternate_option) - if(GET_RANDOM_JOB) - . += span_underline("Get random job if preferences unavailable") - if(BE_ASSISTANT) - . += span_underline("Be assistant if preference unavailable") - if(RETURN_TO_LOBBY) - . += span_underline("Return to lobby if preference unavailable") + UNTYPED_LIST_ADD(jobs_data[current_department.name], job_data) - . += "\[Reset\]
" - . += "
" - . = jointext(.,null) + data["jobs"] = jobs_data + data["alternate_option"] = pref.alternate_option + return data + +/datum/category_item/player_setup_item/occupation/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + + switch(action) + if("reset_jobs") + ResetJobs() + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("job_alternative") + if(pref.alternate_option == GET_RANDOM_JOB || pref.alternate_option == BE_ASSISTANT) + pref.alternate_option += 1 + else if(pref.alternate_option == RETURN_TO_LOBBY) + pref.alternate_option = 0 + return TOPIC_REFRESH + + if("select_alt_title") + var/datum/job/J = locate(params["job"]) + if(istype(J)) + var/choices = list(J.title) + J.alt_titles + var/choice = tgui_input_list(user, "Choose a title for [J.title].", "Choose Title", choices, pref.GetPlayerAltTitle(J)) + if(choice) + SetPlayerAltTitle(J, choice) + return TOPIC_REFRESH_UPDATE_PREVIEW + + if("set_job") + if(SetJob(user, params["set_job"], text2num(params["level"]))) + return TOPIC_REFRESH_UPDATE_PREVIEW + return TOPIC_HANDLED + + if("job_info") + var/rank = params["rank"] + var/datum/job/job = job_master.GetJob(rank) + if(!istype(job)) + return TOPIC_NOACTION + + var/dat = list() + + dat += "



" + if(job.alt_titles) + dat += span_italics(span_bold("Alternate titles:") + " [english_list(job.alt_titles)].") + send_rsc(user, job.get_job_icon(), "job[ckey(rank)].png") + dat += "" + if(job.departments) + dat += span_bold("Departments:") + " [english_list(job.departments)]." + if(LAZYLEN(job.departments_managed)) + dat += "You manage these departments: [english_list(job.departments_managed)]" + + dat += "You answer to " + span_bold("[job.supervisors]") + " normally." + + dat += "


" + if(CONFIG_GET(string/wikiurl)) + dat += "Open wiki page in browser" + + var/alt_title = pref.GetPlayerAltTitle(job) + var/list/description = job.get_description_blurb(alt_title) + if(LAZYLEN(description)) + dat += html_encode(description[1]) + if(description.len > 1) + if(!isnull(description[2])) + dat += "
" + dat += html_encode(description[2]) + + var/datum/browser/popup = new(user, "Job Info", "[capitalize(rank)]", 430, 520, src) + popup.set_content(jointext(dat,"
")) + popup.open() + return TOPIC_HANDLED + +// must stay for job popup to work /datum/category_item/player_setup_item/occupation/OnTopic(href, href_list, user) - if(href_list["reset_jobs"]) - ResetJobs() - return TOPIC_REFRESH - - else if(href_list["job_alternative"]) - if(pref.alternate_option == GET_RANDOM_JOB || pref.alternate_option == BE_ASSISTANT) - pref.alternate_option += 1 - else if(pref.alternate_option == RETURN_TO_LOBBY) - pref.alternate_option = 0 - return TOPIC_REFRESH - - else if(href_list["select_alt_title"]) - var/datum/job/job = locate(href_list["select_alt_title"]) - if (job) - var/choices = list(job.title) + job.alt_titles - var/choice = tgui_input_list(user, "Choose a title for [job.title].", "Choose Title", choices, pref.GetPlayerAltTitle(job)) - if(choice && CanUseTopic(user)) - SetPlayerAltTitle(job, choice) - return (pref.equip_preview_mob ? TOPIC_REFRESH_UPDATE_PREVIEW : TOPIC_REFRESH) - - else if(href_list["set_job"]) - if(SetJob(user, href_list["set_job"], text2num(href_list["level"]))) - return (pref.equip_preview_mob ? TOPIC_REFRESH_UPDATE_PREVIEW : TOPIC_REFRESH) - - else if(href_list["job_info"]) - var/rank = href_list["job_info"] - var/datum/job/job = job_master.GetJob(rank) - var/dat = list() - - dat += "



" - if(job.alt_titles) - dat += span_italics(span_bold("Alternate titles:") + " [english_list(job.alt_titles)].") - send_rsc(user, job.get_job_icon(), "job[ckey(rank)].png") - dat += "" - if(job.departments) - dat += span_bold("Departments:") + " [english_list(job.departments)]." - if(LAZYLEN(job.departments_managed)) - dat += "You manage these departments: [english_list(job.departments_managed)]" - - dat += "You answer to " + span_bold("[job.supervisors]") + " normally." - - dat += "


" - if(CONFIG_GET(string/wikiurl)) - dat += "Open wiki page in browser" - - var/alt_title = pref.GetPlayerAltTitle(job) - var/list/description = job.get_description_blurb(alt_title) - if(LAZYLEN(description)) - dat += html_encode(description[1]) - if(description.len > 1) - if(!isnull(description[2])) - dat += "
" - dat += html_encode(description[2]) - - var/datum/browser/popup = new(user, "Job Info", "[capitalize(rank)]", 430, 520, src) - popup.set_content(jointext(dat,"
")) - popup.open() - - else if(href_list["job_wiki"]) + if(href_list["job_wiki"]) var/rank = href_list["job_wiki"] open_link(user,"[CONFIG_GET(string/wikiurl)][rank]") diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm index 9debf34698..660a27e17f 100644 --- a/code/modules/client/preference_setup/preference_setup.dm +++ b/code/modules/client/preference_setup/preference_setup.dm @@ -12,11 +12,6 @@ sort_order = 3 category_item_type = /datum/category_item/player_setup_item/occupation -/datum/category_group/player_setup_category/appearance_preferences - name = "Special Roles" - sort_order = 4 - category_item_type = /datum/category_item/player_setup_item/antagonism - /datum/category_group/player_setup_category/loadout_preferences name = "Loadout" sort_order = 5 @@ -86,10 +81,6 @@ dat += "Game Options" return dat -/datum/category_collection/player_setup_collection/proc/content(var/mob/user) - if(selected_category) - return selected_category.content(user) - /datum/category_collection/player_setup_collection/Topic(var/href,var/list/href_list) if(..()) return 1 @@ -104,7 +95,7 @@ . = 1 else if(href_list["game_prefs"]) - user.client.prefs.tgui_interact(user) + user.client.game_options() if(.) user.client.prefs.ShowChoices(user) @@ -152,21 +143,6 @@ for(var/datum/category_item/player_setup_item/PI in items) PI.copy_to_mob(C) -/datum/category_group/player_setup_category/proc/content(var/mob/user) - . = "
" - var/current = 0 - var/halfway = items.len / 2 - for(var/datum/category_item/player_setup_item/PI in items) - if(halfway && current++ >= halfway) - halfway = 0 - . += "" - . += "[PI.content(user)]
" - . += "
" - -/datum/category_group/player_setup_category/occupation_preferences/content(var/mob/user) - for(var/datum/category_item/player_setup_item/PI in items) - . += "[PI.content(user)]
" - /********************** * Category Item Setup * **********************/ @@ -216,9 +192,6 @@ /datum/category_item/player_setup_item/proc/copy_to_mob(var/mob/living/carbon/human/C) return -/datum/category_item/player_setup_item/proc/content() - return - /datum/category_item/player_setup_item/proc/sanitize_character() return diff --git a/code/modules/client/preference_setup/traits/trait_defines.dm b/code/modules/client/preference_setup/traits/trait_defines.dm deleted file mode 100644 index 720ce52f84..0000000000 --- a/code/modules/client/preference_setup/traits/trait_defines.dm +++ /dev/null @@ -1,305 +0,0 @@ -// This contains character setup datums for traits. -// The actual modifiers (if used) for these are stored inside code/modules/mob/_modifiers/traits.dm - -/datum/trait/modifier - abstract_type = /datum/trait/modifier - var/modifier_type = null // Type to add to the mob post spawn. - -/datum/trait/modifier/apply_trait_post_spawn(mob/living/L) - L.add_modifier(modifier_type) - -/datum/trait/modifier/is_available() - return !!modifier_type - -/datum/trait/modifier/generate_desc() - var/new_desc = desc - if(!is_available()) - new_desc = "[new_desc] This trait is not implemented yet." - return new_desc - var/datum/modifier/M = new modifier_type() - if(!desc) - new_desc = M.desc // Use the modifier's description, if the trait doesn't have one defined. - var/modifier_effects = M.describe_modifier_effects() - new_desc = "[new_desc][modifier_effects ? "
[modifier_effects]":""]" // Now describe what the trait actually does. - qdel(M) - return new_desc - - -// Physical traits are what they sound like, and involve the character's physical body, as opposed to their mental state. -/datum/trait/modifier/physical - abstract_type = /datum/trait/modifier/physical - name = "Physical" - category = "Physical" - - -/datum/trait/modifier/physical/flimsy - name = "Flimsy" - desc = "You're more fragile than most, and have less of an ability to endure harm." - modifier_type = /datum/modifier/trait/flimsy - mutually_exclusive = list(/datum/trait/modifier/physical/frail) - - -/datum/trait/modifier/physical/frail - name = "Frail" - desc = "Your body is very fragile, and has even less of an ability to endure harm." - modifier_type = /datum/modifier/trait/frail - mutually_exclusive = list(/datum/trait/modifier/physical/flimsy) - - -/datum/trait/modifier/physical/haemophilia - name = "Haemophilia" - desc = "Some say that when it rains, it pours. Unfortunately, this is also true for yourself if you get cut." - modifier_type = /datum/modifier/trait/haemophilia - -/datum/trait/modifier/physical/haemophilia/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - if(setup.is_FBP()) - return "Full Body Prosthetics cannot bleed." - // If a species lacking blood is added, it is suggested to add a check for them here. - return ..() - - -/datum/trait/modifier/physical/weak - name = "Weak" - desc = "A lack of physical strength causes a diminshed capability in close quarters combat." - modifier_type = /datum/modifier/trait/weak - mutually_exclusive = list(/datum/trait/modifier/physical/wimpy) - - -/datum/trait/modifier/physical/wimpy - name = "Wimpy" - desc = "An extreme lack of physical strength causes a greatly diminished capability in close quarters combat." - modifier_type = /datum/modifier/trait/wimpy - mutually_exclusive = list(/datum/trait/modifier/physical/weak) - - -/datum/trait/modifier/physical/inaccurate - name = "Inaccurate" - desc = "You're rather inexperienced with guns, you've never used one in your life, or you're just really rusty. \ - Regardless, you find it quite difficult to land shots where you wanted them to go." - modifier_type = /datum/modifier/trait/inaccurate - -/datum/trait/modifier/physical/shorter - name = "Shorter" - modifier_type = /datum/modifier/trait/shorter - mutually_exclusive = list(/datum/trait/modifier/physical/short, /datum/trait/modifier/physical/tall, /datum/trait/modifier/physical/taller) - -/datum/trait/modifier/physical/short - name = "Short" - modifier_type = /datum/modifier/trait/short - mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/tall, /datum/trait/modifier/physical/taller) - -/datum/trait/modifier/physical/tall - name = "Tall" - modifier_type = /datum/modifier/trait/tall - mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/short, /datum/trait/modifier/physical/taller) - -/datum/trait/modifier/physical/taller - name = "Taller" - modifier_type = /datum/modifier/trait/taller - mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/short, /datum/trait/modifier/physical/tall) - -/datum/trait/modifier/physical/thin - name = "Thin" - modifier_type = /datum/modifier/trait/thin - mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thinner) - -/datum/trait/modifier/physical/thinner - name = "Rail Thin" - modifier_type = /datum/modifier/trait/thinner - mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thin) - -/datum/trait/modifier/physical/fat - name = "Broad-Shouldered" - modifier_type = /datum/modifier/trait/fat - mutually_exclusive = list(/datum/trait/modifier/physical/thin, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thinner) - -/datum/trait/modifier/physical/obese - name = "Heavily Built" - modifier_type = /datum/modifier/trait/obese - mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/thinner, /datum/trait/modifier/physical/thin) - -/datum/trait/modifier/physical/colorblind_protanopia - name = "Protanopia" - desc = "You have a form of red-green colorblindness. You cannot see reds, and have trouble distinguishing them from yellows and greens." - modifier_type = /datum/modifier/trait/colorblind_protanopia - mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_deuteranopia, /datum/trait/modifier/physical/colorblind_tritanopia, /datum/trait/modifier/physical/colorblind_monochrome) - -/datum/trait/modifier/physical/colorblind_deuteranopia - name = "Deuteranopia" - desc = "You have a form of red-green colorblindness. You cannot see greens, and have trouble distinguishing them from yellows and reds." - modifier_type = /datum/modifier/trait/colorblind_deuteranopia - mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_protanopia, /datum/trait/modifier/physical/colorblind_tritanopia, /datum/trait/modifier/physical/colorblind_monochrome) - -/datum/trait/modifier/physical/colorblind_tritanopia - name = "Tritanopia" - desc = "You have a form of blue-yellow colorblindness. You have trouble distinguishing between blues, greens, and yellows, and see blues and violets as dim." - modifier_type = /datum/modifier/trait/colorblind_tritanopia - mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_protanopia, /datum/trait/modifier/physical/colorblind_deuteranopia, /datum/trait/modifier/physical/colorblind_monochrome) - -/datum/trait/modifier/physical/colorblind_monochrome - name = "Monochromacy" - desc = "You are fully colorblind. Your condition is rare, but you can see no colors at all." - modifier_type = /datum/modifier/trait/colorblind_monochrome - mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_protanopia, /datum/trait/modifier/physical/colorblind_deuteranopia, /datum/trait/modifier/physical/colorblind_tritanopia) - -// These two traits might be borderline, feel free to remove if they get abused. -/datum/trait/modifier/physical/high_metabolism - name = "High Metabolism" - modifier_type = /datum/modifier/trait/high_metabolism - mutually_exclusive = list(/datum/trait/modifier/physical/low_metabolism) - -/datum/trait/modifier/physical/high_metabolism/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - if(setup.is_FBP()) - return "Full Body Prosthetics do not have a metabolism." - return ..() - - -/datum/trait/modifier/physical/low_metabolism - name = "Low Metabolism" - modifier_type = /datum/modifier/trait/low_metabolism - mutually_exclusive = list(/datum/trait/modifier/physical/high_metabolism) - -/datum/trait/modifier/physical/low_metabolism/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - if(setup.is_FBP()) - return "Full Body Prosthetics do not have a metabolism." - return ..() - - -/datum/trait/modifier/physical/cloned - name = "Cloned" - desc = "At some point in your life, you died and were cloned." - modifier_type = /datum/modifier/cloned - -/datum/trait/modifier/physical/cloned/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - if(setup.is_FBP()) - return "Full Body Prosthetics cannot be cloned." - return ..() - - -/datum/trait/modifier/physical/no_clone - name = "Cloning Incompatibility" - modifier_type = /datum/modifier/no_clone - -/datum/trait/modifier/physical/no_clone/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - if(setup.is_FBP()) - return "Full Body Prosthetics cannot be cloned anyways." - return ..() - - -/datum/trait/modifier/physical/no_borg - name = "Cybernetic Incompatibility" - modifier_type = /datum/modifier/no_borg - -/datum/trait/modifier/physical/no_borg/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - if(setup.is_FBP()) - return "Full Body Prosthetics are already partly or fully mechanical." - return ..() - - - -// 'Mental' traits are just those that only sapients can have, for now, and generally involves fears. -// So far, all of them are just for fluff/don't have mechanical effects. -/datum/trait/modifier/mental - abstract_type = /datum/trait/modifier/mental - name = "Mental" - category = "Mental" - -/datum/trait/modifier/mental/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - if(setup.is_FBP()) - if(setup.get_FBP_type() == PREF_FBP_SOFTWARE) - return "Drone Intelligences cannot feel emotions." - return ..() - - -/datum/trait/modifier/mental/arachnophobe - name = "Arachnophobic" - desc = "Spiders are quite creepy to most people, however for you, those chitters of pure evil inspire pure dread and fear." - modifier_type = /datum/modifier/trait/phobia/arachnophobe - - -/datum/trait/modifier/mental/nyctophobe - name = "Nyctophobic" - desc = "More commonly known as the fear of darkness. The shadows can hide many dangers, which makes the prospect of going into the depths of Maintenance rather worrisome." - modifier_type = /datum/modifier/trait/phobia/nyctophobe - - -/datum/trait/modifier/mental/haemophobe - name = "Haemophobia" - desc = "Not to be confused with Haemophilia (which makes you bleed faster), Haemophobia is the fear of blood. Seeing a bunch of blood isn't really \ - pleasant for most people, but for you, it is very distressing." - modifier_type = /datum/modifier/trait/phobia/haemophobia - - -/datum/trait/modifier/mental/claustrophobe - name = "Claustrophobic" - desc = "Small spaces and tight quarters makes you feel distressed. Unfortunately both are rather common when living in space." - modifier_type = /datum/modifier/trait/phobia/claustrophobe - - -/datum/trait/modifier/mental/blennophobe - name = "Blennophobia" - desc = "Slimes are quite dangerous, but just the aspect of something being slimey is uncomfortable." - modifier_type = /datum/modifier/trait/phobia/blennophobe - -/datum/trait/modifier/mental/trypanophobe - name = "Trypanophobia" - desc = "Syringes and needles make you very distressed. You really don't want to get sick..." - modifier_type = /datum/modifier/trait/phobia/trypanophobe - -/* -// Uncomment this when/if these get finished. -/datum/trait/modifier/mental/synthphobe - name = "Synthphobic" - desc = "You know, deep down, that synthetics cannot be trusted, and so you are always on guard whenever you see one wandering around. No one knows how a Positronic's mind works, \ - Drones are just waiting for the right time for Emergence, and the poor brains trapped in the cage of Man Machine Interfaces are now soulless, despite being unaware of it. None \ - can be trusted." - -/datum/trait/modifier/mental/xenophobe - name = "Xenophobic" - desc = "The mind of the Alien is unknowable, and as such, their intentions cannot be known. You always watch the xenos closely, as they most certainly are watching you \ - closely, waiting to strike." - mutually_exclusive = list( - /datum/trait/modifier/mental/humanphobe, - /datum/trait/modifier/mental/skrellphobe, - /datum/trait/modifier/mental/tajaraphobe, - /datum/trait/modifier/mental/unathiphobe, - /datum/trait/modifier/mental/teshariphobe, - /datum/trait/modifier/mental/prometheanphobe - ) - -/datum/trait/modifier/mental/humanphobe - name = "Human-phobic" - desc = "Boilerplate racism for monkeys goes here." - mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe) - -/datum/trait/modifier/mental/skrellphobe - name = "Skrell-phobic" - desc = "Boilerplate racism for squid goes here." - mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe) - -/datum/trait/modifier/mental/tajaraphobe - name = "Tajaran-phobic" - desc = "Boilerplate racism for cats goes here." - mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe) - -/datum/trait/modifier/mental/unathiphobe - name = "Unathi-phobic" - desc = "Boilerplate racism for lizards goes here." - mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe) - -// Not sure why anyone would hate/fear these guys but for the sake of completeness here we are. -/datum/trait/modifier/mental/dionaphobe - name = "Diona-phobic" - desc = "Boilerplate racism for trees goes here." - mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe) - -/datum/trait/modifier/mental/teshariphobe - name = "Teshari-phobic" - desc = "Boilerplate racism for birds goes here." - mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe) - -/datum/trait/modifier/mental/prometheanphobe - name = "Promethean-phobic" - desc = "Boilerplate racism for jellos goes here." - mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe) -*/ diff --git a/code/modules/client/preference_setup/traits/traits.dm b/code/modules/client/preference_setup/traits/traits.dm deleted file mode 100644 index fe217b5a26..0000000000 --- a/code/modules/client/preference_setup/traits/traits.dm +++ /dev/null @@ -1,205 +0,0 @@ -var/list/trait_datums = list() // Assoc list using name = instance. Traits are saved as a list of strings. -var/list/trait_type_to_ref = list() // Similar to above but uses paths, which is more reliable but more risky to save. -var/list/trait_categories = list() // The categories available for the trait menu. - -/hook/startup/proc/populate_trait_list() - - //create a list of trait datums - for(var/trait_type as anything in subtypesof(/datum/trait)) - if (is_abstract(trait_type)) - continue - var/datum/trait/T = new trait_type - if(!T.is_available()) - qdel(T) - continue - - if(!T.name) - error("Trait Menu - Missing name: [T.type]") - continue - - if(!T.category) - error("Trait Menu - Missing category: [T.type]") - continue - - T.desc = T.generate_desc() - - trait_datums[T.name] = T - trait_type_to_ref[T.type] = T - if(!(T.category in trait_categories)) - trait_categories += T.category - - return 1 - -/datum/category_item/player_setup_item/traits - name = "Quirks" //VOREStation Edit - sort_order = 1 - var/current_tab = "Physical" - -/datum/category_item/player_setup_item/traits/load_character(list/save_data) - pref.traits = save_data["traits"] - -/datum/category_item/player_setup_item/traits/save_character(list/save_data) - save_data["traits"] = pref.traits - - -/datum/category_item/player_setup_item/traits/content() - . = list() - . += "" - . += "" - . += "" - . += "" - - . += "" - - - for(var/trait_name in trait_datums) - var/datum/trait/T = trait_datums[trait_name] - if(T.category != current_tab) - continue - - var/ticked = (T.name in pref.traits) - var/style_class - if(!T.validate(pref.traits, src)) - style_class = "linkOff" - else if(ticked) - style_class = "linkOn" - . += "" -// . += "" - - var/invalidity = T.test_for_invalidity(src) - var/conflicts = T.test_for_trait_conflict(pref.traits) - var/invalid = "" - if(invalidity) - invalid += "[invalidity] " - if(conflicts) - invalid += "This trait is mutually exclusive with [conflicts]." - - . += "" -// if(ticked) -// . += "" - . += "

" + span_bold("
Traits
") + "

" - var/firstcat = 1 - for(var/category in trait_categories) - if(firstcat) - firstcat = 0 - else - . += " |" - - if(category == current_tab) - . += " " + span_linkOn("[category]") + " " - else - . += " [category] " - . += "
[G.cost]" + span_italics("[T.desc]") + "\ - [invalid ? span_red("
Cannot take trait. Reason: [invalid]"):""]
" -// for(var/datum/gear_tweak/tweak in G.gear_tweaks) -// . += " [tweak.get_contents(get_tweak_metadata(G, tweak))]" -// . += "
" - . = jointext(., null) - -/datum/category_item/player_setup_item/traits/sanitize_character() - var/mob/preference_mob = preference_mob() - if(!islist(pref.traits)) - pref.traits = list() - - for(var/trait_name in pref.traits) - if(!(trait_name in trait_datums)) - pref.traits -= trait_name - - for(var/trait_name in pref.traits) - if(!trait_datums[trait_name]) - to_chat(preference_mob, span_warning("You cannot have more than one of trait: [trait_name]")) - pref.traits -= trait_name - else - var/datum/trait/T = trait_datums[trait_name] - var/invalidity = T.test_for_invalidity(src) - if(invalidity) - pref.traits -= trait_name - to_chat(preference_mob, span_warning("You cannot take the [trait_name] trait. Reason: [invalidity]")) - - var/conflicts = T.test_for_trait_conflict(pref.traits) - if(conflicts) - pref.traits -= trait_name - to_chat(preference_mob, span_warning("The [trait_name] trait is mutually exclusive with [conflicts].")) - -/datum/category_item/player_setup_item/traits/OnTopic(href, href_list, user) - if(href_list["toggle_trait"]) - var/datum/trait/T = trait_datums[href_list["toggle_trait"]] - if(T.name in pref.traits) - pref.traits -= T.name - else - var/invalidity = T.test_for_invalidity(src) - if(invalidity) - to_chat(user, span_warning("You cannot take the [T.name] trait. Reason: [invalidity]")) - return TOPIC_NOACTION - - var/conflicts = T.test_for_trait_conflict(pref.traits) - if(conflicts) - to_chat(user, span_warning("The [T.name] trait is mutually exclusive with [conflicts].")) - return TOPIC_NOACTION - - pref.traits += T.name - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["select_category"]) - current_tab = href_list["select_category"] - return TOPIC_REFRESH - return ..() - - -/datum/trait - abstract_type = /datum/trait - var/name = null // Name to show on UI - var/desc = null // Description of what it does, also shown on UI. - var/list/mutually_exclusive = list() // List of trait types which cannot be taken alongside this trait. - var/category = null // What section to place this trait inside. - -// Applies effects to the newly spawned mob. -/datum/trait/proc/apply_trait_post_spawn(var/mob/living/L) - return - -// Used to forbid a trait based on certain criteria (e.g. if they are an FBP). -// It receives the player_setup_item datum since some reasons for being invalid depend on the currently loaded preferences. -// Returns a string explaining why the trait is invalid. Returns null if valid. -/datum/trait/proc/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup) - return null - -// Checks mutually_exclusive. current_traits needs to be a list of strings. -// Returns null if everything is well, similar to the above proc. Otherwise returns an english_list() of conflicting traits. -/datum/trait/proc/test_for_trait_conflict(var/list/current_traits) - var/list/conflicts = list() - var/result - - if(mutually_exclusive.len) - for(var/trait_name in current_traits) - var/datum/trait/T = trait_datums[trait_name] - if(T.type in mutually_exclusive) - conflicts.Add(T.name) - - if(conflicts.len) - result = english_list(conflicts) - - return result - -/datum/trait/proc/is_available() - return TRUE - -// Similar to above, but uses the above two procs, in one place. -// Returns TRUE is everything is well. -/datum/trait/proc/validate(var/list/current_traits, var/datum/category_item/player_setup_item/traits/setup) - if(test_for_invalidity(setup)) - return FALSE - if(test_for_trait_conflict(current_traits)) - return FALSE - return TRUE - -// Creates a description, if one doesn't exist. -// This one is for inheritence, and so doesn't do anything. -/datum/trait/proc/generate_desc() - return desc - -/mob/living/proc/apply_traits() - if(!mind || !mind.traits || !mind.traits.len) - return - for(var/trait in mind.traits) - var/datum/trait/T = trait_datums[trait] - if(istype(T)) - T.apply_trait_post_spawn(src) diff --git a/code/modules/client/preference_setup/volume_sliders/01_volume.dm b/code/modules/client/preference_setup/volume_sliders/01_volume.dm deleted file mode 100644 index 4c63fc7abb..0000000000 --- a/code/modules/client/preference_setup/volume_sliders/01_volume.dm +++ /dev/null @@ -1,106 +0,0 @@ -/datum/category_group/player_setup_category/volume_sliders - name = "Sound" - sort_order = 7 - category_item_type = /datum/category_item/player_setup_item/volume_sliders - -/datum/category_item/player_setup_item/volume_sliders/volume - name = "General Volume" - sort_order = 1 - -/datum/category_item/player_setup_item/volume_sliders/volume/load_preferences(datum/json_savefile/savefile) - pref.volume_channels = savefile.get_entry("volume_channels") - -/datum/category_item/player_setup_item/volume_sliders/volume/save_preferences(datum/json_savefile/savefile) - savefile.set_entry("volume_channels", pref.volume_channels) - -/datum/category_item/player_setup_item/volume_sliders/volume/sanitize_preferences() - if(isnull(pref.volume_channels)) - pref.volume_channels = list() - - for(var/channel in pref.volume_channels) - if(!(channel in GLOB.all_volume_channels)) - // Channel no longer exists, yeet - pref.volume_channels.Remove(channel) - - for(var/channel in GLOB.all_volume_channels) - if(!(channel in pref.volume_channels)) - pref.volume_channels["[channel]"] = 1 - else - pref.volume_channels["[channel]"] = clamp(pref.volume_channels["[channel]"], 0, 2) - -/datum/category_item/player_setup_item/volume_sliders/volume/content(var/mob/user) - . += span_bold("Volume Settings") + "
" - for(var/channel in pref.volume_channels) - . += "[channel]: [pref.volume_channels[channel] * 100]%
" - . += "
" - -/datum/category_item/player_setup_item/volume_sliders/volume/OnTopic(var/href, var/list/href_list, var/mob/user) - if(href_list["change_volume"]) - if(CanUseTopic(user)) - var/channel = href_list["change_volume"] - if(!(channel in pref.volume_channels)) - pref.volume_channels["[channel]"] = 1 - var/value = tgui_input_number(user, "Choose your volume for [channel] (0-200%)", "[channel] volume", (pref.volume_channels[channel] * 100), 200, 0) - if(isnum(value)) - value = CLAMP(value, 0, 200) - pref.volume_channels["[channel]"] = (value / 100) - return TOPIC_REFRESH - return ..() - -/mob/proc/get_preference_volume_channel(volume_channel) - if(!client) - return 0 - return client.get_preference_volume_channel(volume_channel) - -/client/proc/get_preference_volume_channel(volume_channel) - if(!volume_channel || !prefs) - return 1 - if(!(volume_channel in prefs.volume_channels)) - prefs.volume_channels["[volume_channel]"] = 1 - return prefs.volume_channels["[volume_channel]"] - - -// Neat little volume adjuster thing in case you don't wanna touch preferences by hand you lazy fuck -/datum/volume_panel -/datum/volume_panel/tgui_state(mob/user) - return GLOB.tgui_always_state - -/datum/volume_panel/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "VolumePanel", "Volume Panel") - ui.open() - -/datum/volume_panel/tgui_data(mob/user) - if(!user.client || !user.client.prefs) - return list("error" = TRUE) - - var/list/data = ..() - data["volume_channels"] = user.client.prefs.volume_channels - return data - -/datum/volume_panel/tgui_act(action, params, datum/tgui/ui) - if(..()) - return TRUE - - if(!ui.user?.client?.prefs) - return TRUE - - var/datum/preferences/P = ui.user.client.prefs - switch(action) - if("adjust_volume") - var/channel = params["channel"] - if(channel in P.volume_channels) - P.volume_channels["[channel]"] = clamp(params["vol"], 0, 2) - SScharacter_setup.queue_preferences_save(P) - return TRUE - -/client/verb/volume_panel() - set name = "Volume Panel" - set category = "Preferences.Sounds" - set desc = "Allows you to adjust volume levels on the fly." - - if(!volume_panel) - volume_panel = new(src) - - volume_panel.tgui_interact(mob) diff --git a/code/modules/client/preference_setup/volume_sliders/02_media.dm b/code/modules/client/preference_setup/volume_sliders/02_media.dm deleted file mode 100644 index c6620133a8..0000000000 --- a/code/modules/client/preference_setup/volume_sliders/02_media.dm +++ /dev/null @@ -1,53 +0,0 @@ -/datum/preferences - var/media_volume = 1 - var/media_player = 2 // 0 = VLC, 1 = WMP, 2 = HTML5, 3+ = unassigned - -/datum/category_item/player_setup_item/volume_sliders/media - name = "Media" - sort_order = 2 - -/datum/category_item/player_setup_item/volume_sliders/media/load_preferences(datum/json_savefile/savefile) - pref.media_volume = savefile.get_entry("media_volume") - pref.media_player = savefile.get_entry("media_player") - -/datum/category_item/player_setup_item/volume_sliders/media/save_preferences(datum/json_savefile/savefile) - savefile.set_entry("media_volume", pref.media_volume) - savefile.set_entry("media_player", pref.media_player) - -/datum/category_item/player_setup_item/volume_sliders/media/sanitize_preferences() - pref.media_volume = isnum(pref.media_volume) ? CLAMP(pref.media_volume, 0, 1) : initial(pref.media_volume) - pref.media_player = sanitize_inlist(pref.media_player, list(0, 1, 2), initial(pref.media_player)) - -/datum/category_item/player_setup_item/volume_sliders/media/content(var/mob/user) - . += span_bold("Jukebox Volume:") - . += "[round(pref.media_volume * 100)]%
" - . += span_bold("Media Player Type:") + " Depending on you operating system, one of these might work better. " - . += "Use HTML5 if it works for you. If neither HTML5 nor WMP work, you'll have to fall back to using VLC, " - . += "but this requires you have the VLC client installed on your comptuer." - . += "Try the others if you want but you'll probably just get no music.
" - . += (pref.media_player == 2) ? (span_linkOn(span_bold("HTML5")) + " ") : "HTML5 " - . += (pref.media_player == 1) ? (span_linkOn(span_bold("WMP")) + " ") : "WMP " - . += (pref.media_player == 0) ? (span_linkOn(span_bold("VLC")) + " ") : "VLC " - . += "
" - -/datum/category_item/player_setup_item/volume_sliders/media/OnTopic(var/href, var/list/href_list, var/mob/user) - if(href_list["change_media_volume"]) - if(CanUseTopic(user)) - var/value = tgui_input_number(user, "Choose your Jukebox volume (0-100%)", "Jukebox volume", round(pref.media_volume * 100), 100, 0) - if(isnum(value)) - value = CLAMP(value, 0, 100) - pref.media_volume = value/100.0 - if(user.client && user.client.media) - user.client.media.update_volume(pref.media_volume) - return TOPIC_REFRESH - else if(href_list["set_media_player"]) - if(CanUseTopic(user)) - var/newval = sanitize_inlist(text2num(href_list["set_media_player"]), list(0, 1, 2), pref.media_player) - if(newval != pref.media_player) - pref.media_player = newval - if(user.client && user.client.media) - user.client.media.open() - spawn(10) - user.update_music() - return TOPIC_REFRESH - return ..() diff --git a/code/modules/client/preference_setup/vore/01_ears.dm b/code/modules/client/preference_setup/vore/01_ears.dm deleted file mode 100644 index c9998dece0..0000000000 --- a/code/modules/client/preference_setup/vore/01_ears.dm +++ /dev/null @@ -1,5 +0,0 @@ -// Global stuff that will put us on the map -/datum/category_group/player_setup_category/vore - name = "VORE" - sort_order = 8 - category_item_type = /datum/category_item/player_setup_item/vore diff --git a/code/modules/client/preference_setup/vore/02_size.dm b/code/modules/client/preference_setup/vore/02_size.dm deleted file mode 100644 index 8a950a6381..0000000000 --- a/code/modules/client/preference_setup/vore/02_size.dm +++ /dev/null @@ -1,314 +0,0 @@ -// Body weight limits on a character. -#define WEIGHT_CHANGE_MIN 0 -#define WEIGHT_CHANGE_MAX 100 - -// Define a place to save in character setup -/datum/preferences - var/size_multiplier = RESIZE_NORMAL - // Body weight stuff. - var/weight_vr = 137 // bodyweight of character (pounds, because I'm not doing the math again -Spades) - var/weight_gain = 100 // Weight gain rate. - var/weight_loss = 50 // Weight loss rate. - var/fuzzy = 0 // Preference toggle for sharp/fuzzy icon. Default sharp. - var/offset_override = FALSE - var/voice_freq = 42500 - var/voice_sound = "goon speak 1" - var/custom_speech_bubble = "default" - var/custom_footstep = "Default" - var/species_sound = "Unset" - -// Definition of the stuff for Sizing -/datum/category_item/player_setup_item/vore/size - name = "Size" - sort_order = 2 - -/datum/category_item/player_setup_item/vore/size/load_character(list/save_data) - pref.size_multiplier = save_data["size_multiplier"] - pref.weight_vr = save_data["weight_vr"] - pref.weight_gain = save_data["weight_gain"] - pref.weight_loss = save_data["weight_loss"] - pref.fuzzy = save_data["fuzzy"] - pref.offset_override = save_data["offset_override"] - pref.voice_freq = save_data["voice_freq"] - pref.voice_sound = save_data["voice_sound"] - pref.custom_speech_bubble = save_data["custom_speech_bubble"] - pref.custom_footstep = save_data["custom_footstep"] - pref.species_sound = save_data["species_sound"] - -/datum/category_item/player_setup_item/vore/size/save_character(list/save_data) - save_data["size_multiplier"] = pref.size_multiplier - save_data["weight_vr"] = pref.weight_vr - save_data["weight_gain"] = pref.weight_gain - save_data["weight_loss"] = pref.weight_loss - save_data["fuzzy"] = pref.fuzzy - save_data["offset_override"] = pref.offset_override - save_data["voice_freq"] = pref.voice_freq - save_data["voice_sound"] = pref.voice_sound - save_data["custom_speech_bubble"] = pref.custom_speech_bubble - save_data["custom_footstep"] = pref.custom_footstep - save_data["species_sound"] = pref.species_sound - -/datum/category_item/player_setup_item/vore/size/sanitize_character() - pref.weight_vr = sanitize_integer(pref.weight_vr, WEIGHT_MIN, WEIGHT_MAX, initial(pref.weight_vr)) - pref.weight_gain = sanitize_integer(pref.weight_gain, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_gain)) - pref.weight_loss = sanitize_integer(pref.weight_loss, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_loss)) - pref.fuzzy = sanitize_integer(pref.fuzzy, 0, 1, initial(pref.fuzzy)) - pref.offset_override = sanitize_integer(pref.offset_override, 0, 1, initial(pref.offset_override)) - if(pref.voice_freq != 0) - pref.voice_freq = sanitize_integer(pref.voice_freq, MIN_VOICE_FREQ, MAX_VOICE_FREQ, initial(pref.voice_freq)) - if(pref.size_multiplier == null || pref.size_multiplier < RESIZE_TINY || pref.size_multiplier > RESIZE_HUGE) - pref.size_multiplier = initial(pref.size_multiplier) - if(!(pref.custom_speech_bubble in GLOB.selectable_speech_bubbles)) - pref.custom_speech_bubble = "default" - if(!(pref.custom_footstep)) - pref.custom_footstep = "Default" - // var/datum/species/selected_species = GLOB.all_species[pref.species] - if(!(pref.species_sound)) - pref.species_sound = "Unset" - -/datum/category_item/player_setup_item/vore/size/copy_to_mob(var/mob/living/carbon/human/character) - character.weight = pref.weight_vr - character.weight_gain = pref.weight_gain - character.weight_loss = pref.weight_loss - character.fuzzy = pref.fuzzy - character.offset_override = pref.offset_override - character.voice_freq = pref.voice_freq - character.resize(pref.size_multiplier, animate = FALSE, ignore_prefs = TRUE) - - if(!pref.voice_sound) - character.voice_sounds_list = DEFAULT_TALK_SOUNDS - else - character.voice_sounds_list = get_talk_sound(pref.voice_sound) - character.custom_speech_bubble = pref.custom_speech_bubble - character.custom_footstep = pref.custom_footstep - -/datum/category_item/player_setup_item/vore/size/content(var/mob/user) - . += "
" - . += span_bold("Scale:") + " [round(pref.size_multiplier*100)]%
" - . += span_bold("Scaled Appearance:") + " [pref.fuzzy ? "Fuzzy" : "Sharp"]
" - . += span_bold("Scaling Center:") + " [pref.offset_override ? "Odd" : "Even"]
" - . += "
" - . += span_bold("Mob Speech/Noise Customization") + "" - . += "
" - . += span_bold("Voice Frequency:") + " [pref.voice_freq]
" - . += span_bold("Voice Sounds:") + " [pref.voice_sound]
" - . += "Test Selected Voice
" - . += span_bold("Custom Speech Bubble:") + " [pref.custom_speech_bubble]
" - . += span_bold("Custom Footstep Sounds:") + "[pref.custom_footstep]
" - . += "
" - . += span_bold("Species Sounds:") + " [pref.species_sound]
" - . += "Test Cough Sounds
" - . += "Test Sneeze Sounds
" - . += "Test Scream Sounds
" - . += "Test Pain Sounds
" - . += "Test Gasp Sounds
" - . += "Test Death Sounds
" - . += "
" - . += span_bold("Relative Weight:") + " [pref.weight_vr]
" - . += span_bold("Weight Gain Rate:") + " [pref.weight_gain]
" - . += span_bold("Weight Loss Rate:") + " [pref.weight_loss]
" - -/datum/category_item/player_setup_item/vore/size/OnTopic(var/href, var/list/href_list, var/mob/user) - if(href_list["size_multiplier"]) - var/new_size = tgui_input_number(user, "Choose your character's size, ranging from [RESIZE_MINIMUM * 100]% to [RESIZE_MAXIMUM * 100]%", "Set Size", pref.size_multiplier * 100, RESIZE_MAXIMUM * 100, RESIZE_MINIMUM * 100) - if (!ISINRANGE(new_size, RESIZE_MINIMUM * 100, RESIZE_MAXIMUM * 100)) - pref.size_multiplier = 1 - to_chat(user, span_notice("Invalid size.")) - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(new_size) - pref.size_multiplier = (new_size / 100) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["toggle_fuzzy"]) - pref.fuzzy = pref.fuzzy ? 0 : 1; - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["toggle_offset_override"]) - pref.offset_override = pref.offset_override ? 0 : 1; - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["weight"]) - var/new_weight = tgui_input_number(user, "Choose your character's relative body weight.\n\ - Note: Scifi characters come in all shapes and sizes in this game, and not all follow the traditional shape of a human. Like a naga or a taur or a giant will weigh a\n\ - lot more than what this allows, or a micro will weigh a lot less. Just ignore all of that for a second and PRETEND the weight you're setting is visually for an \n\ - average human. This is the best solution we have at the moment.!\n\ - ([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE) - if(new_weight) - var/unit_of_measurement = tgui_alert(user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms")) - if(!unit_of_measurement) - return TOPIC_NOACTION - if(unit_of_measurement == "Pounds") - new_weight = round(text2num(new_weight),4) - if(unit_of_measurement == "Kilograms") - new_weight = round(2.20462*text2num(new_weight),4) - pref.weight_vr = sanitize_integer(new_weight, WEIGHT_MIN, WEIGHT_MAX, pref.weight_vr) - return TOPIC_REFRESH - - else if(href_list["weight_gain"]) - var/weight_gain_rate = tgui_input_number(user, "Choose your character's rate of weight gain between 100% \ - (full realism body fat gain) and 0% (no body fat gain).\n\ - (If you want to disable weight gain, set this to 0.01 to round it to 0%.)\ - ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) - if(weight_gain_rate) - pref.weight_gain = round(text2num(weight_gain_rate),1) - return TOPIC_REFRESH - - else if(href_list["weight_loss"]) - var/weight_loss_rate = tgui_input_number(user, "Choose your character's rate of weight loss between 100% \ - (full realism body fat loss) and 0% (no body fat loss).\n\ - (If you want to disable weight loss, set this to 0.01 round it to 0%.)\ - ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) - if(weight_loss_rate) - pref.weight_loss = round(text2num(weight_loss_rate),1) - return TOPIC_REFRESH - - else if(href_list["voice_freq"]) - var/list/preset_voice_freqs = list("high" = MAX_VOICE_FREQ, "middle-high" = 56250, "middle" = 42500, "middle-low"= 28750, "low" = MIN_VOICE_FREQ, "custom" = 1, "random" = 0) - var/choice = tgui_input_list(user, "What would you like to set your voice frequency to? ([MIN_VOICE_FREQ] - [MAX_VOICE_FREQ])", "Voice Frequency", preset_voice_freqs) - if(!choice) - return - choice = preset_voice_freqs[choice] - if(choice == 0) - pref.voice_freq = 42500 - return TOPIC_REFRESH - else if(choice == 1) - choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ) - if(choice > MAX_VOICE_FREQ) - choice = MAX_VOICE_FREQ - else if(choice < MIN_VOICE_FREQ) - choice = MIN_VOICE_FREQ - - pref.voice_freq = choice - return TOPIC_REFRESH - else if(href_list["voice_sounds_list"]) - var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's speech sounds?", "Voice Sounds", SSsounds.talk_sound_map) - if(!pref.voice_sound) - pref.voice_sound = "goon speak 1" - else if(!choice) - return TOPIC_REFRESH - else - pref.voice_sound = choice - return TOPIC_REFRESH - else if(href_list["customize_speech_bubble"]) - var/choice = tgui_input_list(user, "What speech bubble style do you want to use? (default for automatic selection)", "Custom Speech Bubble", GLOB.selectable_speech_bubbles) - if(!choice) - pref.custom_speech_bubble = "default" - else - pref.custom_speech_bubble = choice - return TOPIC_REFRESH - - else if(href_list["customize_footsteps"]) - var/list/footstep_choice = selectable_footstep - var/choice = tgui_input_list(user, "What footstep sounds would your character make?", "Custom Foostep Sounds", footstep_choice) - if(choice) - pref.custom_footstep = footstep_choice[choice] - return TOPIC_REFRESH - - else if(href_list["voice_test"]) - var/sound/S = sound(pick(SSsounds.talk_sound_map[pref.voice_sound])) // talk_sound_map returns a list of sounds - if(S) - S.frequency = pick(pref.voice_freq) - S.volume = 50 - SEND_SOUND(user, S) - else if(href_list["species_sound_options"]) // You shouldn't be able to see this option if you don't have the option to select a custom icon base, so we don't need to re-check for safety here. - var/list/possible_species_sound_types = species_sound_map - var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's species sounds? (Cough, Sneeze, Scream, Pain, Gasp, Death)", "Species Sounds", possible_species_sound_types) - if(!choice) - return TOPIC_REFRESH // No choice? Don't reset our selection - else - pref.species_sound = choice - return TOPIC_REFRESH - else if(href_list["cough_test"]) - var/sound/S - var/ourpref = pref.species_sound - var/oursound = get_species_sound(ourpref)["cough"] - S = sound(pick(oursound)) - if(pref.species_sound == "Unset") - oursound = get_species_sound(select_default_species_sound(pref))["cough"] - S = sound(pick(oursound)) - if(pref.species_sound == "None" || oursound == null) - to_chat(user, span_warning("This set does not have cough sounds!")) - return TOPIC_REFRESH - S.frequency = pick(pref.voice_freq) - S.volume = 20 - SEND_SOUND(user, S) - return TOPIC_REFRESH - else if(href_list["sneeze_test"]) - var/sound/S - var/ourpref = pref.species_sound - var/oursound = get_species_sound(ourpref)["sneeze"] - S = sound(pick(oursound)) - if(pref.species_sound == "Unset") - oursound = get_species_sound(select_default_species_sound(pref))["sneeze"] - S = sound(pick(oursound)) - if(pref.species_sound == "None" || oursound == null) - to_chat(user, span_warning("This set does not have sneeze sounds!")) - return TOPIC_REFRESH - S.frequency = pick(pref.voice_freq) - S.volume = 20 - SEND_SOUND(user, S) - return TOPIC_REFRESH - else if(href_list["scream_test"]) - var/sound/S - var/ourpref = pref.species_sound - var/oursound = get_species_sound(ourpref)["scream"] - S = sound(pick(oursound)) - if(pref.species_sound == "Unset") - oursound = get_species_sound(select_default_species_sound(pref))["scream"] - S = sound(pick(oursound)) - if(pref.species_sound == "None" || oursound == null) - to_chat(user, span_warning("This set does not have scream sounds!")) - return TOPIC_REFRESH - S.frequency = pick(pref.voice_freq) - S.volume = 20 - SEND_SOUND(user, S) - return TOPIC_REFRESH - else if(href_list["pain_test"]) - var/sound/S - var/ourpref = pref.species_sound - var/oursound = get_species_sound(ourpref)["pain"] - S = sound(pick(oursound)) - if(pref.species_sound == "Unset") - oursound = get_species_sound(select_default_species_sound(pref))["pain"] - S = sound(pick(oursound)) - if(pref.species_sound == "None" || oursound == null) - to_chat(user, span_warning("This set does not have pain sounds!")) - return TOPIC_REFRESH - S.frequency = pick(pref.voice_freq) - S.volume = 20 - SEND_SOUND(user, S) - return TOPIC_REFRESH - else if(href_list["gasp_test"]) - var/sound/S - var/ourpref = pref.species_sound - var/oursound = get_species_sound(ourpref)["gasp"] - S = sound(pick(oursound)) - if(pref.species_sound == "Unset") - oursound = get_species_sound(select_default_species_sound(pref))["gasp"] - S = sound(pick(oursound)) - if(pref.species_sound == "None" || oursound == null) - to_chat(user, span_warning("This set does not have gasp sounds!")) - return TOPIC_REFRESH - S.frequency = pick(pref.voice_freq) - S.volume = 20 - SEND_SOUND(user, S) - return TOPIC_REFRESH - else if(href_list["death_test"]) - var/sound/S - var/ourpref = pref.species_sound - var/oursound = get_species_sound(ourpref)["death"] - S = sound(pick(oursound)) - if(pref.species_sound == "Unset") - oursound = get_species_sound(select_default_species_sound(pref))["death"] - S = sound(pick(oursound)) - if(pref.species_sound == "None" || oursound == null) - to_chat(user, span_warning("This set does not have death sounds!")) - return TOPIC_REFRESH - S.frequency = pick(pref.voice_freq) - S.volume = 20 - SEND_SOUND(user, S) - return TOPIC_REFRESH - return ..(); - -#undef WEIGHT_CHANGE_MIN -#undef WEIGHT_CHANGE_MAX diff --git a/code/modules/client/preference_setup/vore/03_egg.dm b/code/modules/client/preference_setup/vore/03_egg.dm deleted file mode 100644 index b462116d92..0000000000 --- a/code/modules/client/preference_setup/vore/03_egg.dm +++ /dev/null @@ -1,76 +0,0 @@ -// Define a place to save appearance in character setup -// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # -#define AUTOHISS_OFF 0 -#define AUTOHISS_BASIC 1 -#define AUTOHISS_FULL 2 -// VOREStation Add End - -/datum/preferences - var/vore_egg_type = "Egg" //The egg type they have. - var/autohiss = "Full" // VOREStation Add: Whether we have Autohiss on. I'm hijacking the egg panel bc this one has a shitton of unused space. - -// Definition of the stuff for the egg type. -/datum/category_item/player_setup_item/vore/egg - name = "Egg appearance." - sort_order = 3 - -/datum/category_item/player_setup_item/vore/egg/load_character(list/save_data) - pref.vore_egg_type = save_data["vore_egg_type"] - pref.autohiss = save_data["autohiss"] - -/datum/category_item/player_setup_item/vore/egg/save_character(list/save_data) - save_data["vore_egg_type"] = pref.vore_egg_type - save_data["autohiss"] = pref.autohiss - -/datum/category_item/player_setup_item/vore/egg/sanitize_character() - pref.vore_egg_type = sanitize_inlist(pref.vore_egg_type, GLOB.global_vore_egg_types, initial(pref.vore_egg_type)) - -/datum/category_item/player_setup_item/vore/egg/copy_to_mob(var/mob/living/carbon/human/character) - character.vore_egg_type = pref.vore_egg_type - // VOREStation Add - if(pref.client) // Safety, just in case so we don't runtime. - if(!pref.autohiss) - pref.client.autohiss_mode = AUTOHISS_FULL - else - switch(pref.autohiss) - if("Full") - pref.client.autohiss_mode = AUTOHISS_FULL - if("Basic") - pref.client.autohiss_mode = AUTOHISS_BASIC - if("Off") - pref.client.autohiss_mode = AUTOHISS_OFF - // VOREStation Add - -/datum/category_item/player_setup_item/vore/egg/content(var/mob/user) - . += "
" - . += " Egg Type: [pref.vore_egg_type]
" - . += span_bold("Autohiss Default Setting:") + " [pref.autohiss]
" // VOREStation Add - -/datum/category_item/player_setup_item/vore/egg/OnTopic(var/href, var/list/href_list, var/mob/user) - if(!CanUseTopic(user)) - return TOPIC_NOACTION - - else if(href_list["vore_egg_type"]) - var/list/vore_egg_types = GLOB.global_vore_egg_types - var/selection = tgui_input_list(user, "Choose your character's egg type:", "Character Preference", vore_egg_types, pref.vore_egg_type) - if(selection) - pref.vore_egg_type = selection - return TOPIC_REFRESH - // VOREStation Add Start - else if(href_list["autohiss"]) - var/list/autohiss_selection = list("Full", "Basic", "Off") - var/selection = tgui_input_list(user, "Choose your default autohiss setting:", "Character Preference", autohiss_selection, pref.autohiss) - if(selection) - pref.autohiss = selection - else if(!selection) - pref.autohiss = "Full" - return TOPIC_REFRESH - // VOREStation Add End - else - return - -// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # -#undef AUTOHISS_OFF -#undef AUTOHISS_BASIC -#undef AUTOHISS_FULL -// VOREStation Add End diff --git a/code/modules/client/preference_setup/vore/04_resleeving.dm b/code/modules/client/preference_setup/vore/04_resleeving.dm deleted file mode 100644 index 67cf72792f..0000000000 --- a/code/modules/client/preference_setup/vore/04_resleeving.dm +++ /dev/null @@ -1,60 +0,0 @@ -// Define a place to save in character setup -/datum/preferences - var/resleeve_lock = 0 // Whether movs should have OOC reslieving protection. Default false. - var/resleeve_scan = 1 // Whether mob should start with a pre-spawn body scan. Default true. - var/mind_scan = 1 // Whether mob should start with a pre-spawn mind scan. Default true. - -// Definition of the stuff for Sizing -/datum/category_item/player_setup_item/vore/resleeve - name = "Resleeving" - sort_order = 4 - -/datum/category_item/player_setup_item/vore/resleeve/load_character(list/save_data) - pref.resleeve_lock = save_data["resleeve_lock"] - pref.resleeve_scan = save_data["resleeve_scan"] - pref.mind_scan = save_data["mind_scan"] - -/datum/category_item/player_setup_item/vore/resleeve/save_character(list/save_data) - save_data["resleeve_lock"] = pref.resleeve_lock - save_data["resleeve_scan"] = pref.resleeve_scan - save_data["mind_scan"] = pref.mind_scan - -/datum/category_item/player_setup_item/vore/resleeve/sanitize_character() - pref.resleeve_lock = sanitize_integer(pref.resleeve_lock, 0, 1, initial(pref.resleeve_lock)) - pref.resleeve_scan = sanitize_integer(pref.resleeve_scan, 0, 1, initial(pref.resleeve_scan)) - pref.mind_scan = sanitize_integer(pref.mind_scan, 0, 1, initial(pref.mind_scan)) - -/datum/category_item/player_setup_item/vore/resleeve/copy_to_mob(var/mob/living/carbon/human/character) - if(character && !istype(character,/mob/living/carbon/human/dummy)) - spawn(50) - if(QDELETED(character) || QDELETED(pref)) - return // They might have been deleted during the wait - if(!character.virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in character.verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore - if(pref.resleeve_scan) - var/datum/transhuman/body_record/BR = new() - BR.init_from_mob(character, pref.resleeve_scan, pref.resleeve_lock) - if(pref.mind_scan) - var/datum/transcore_db/our_db = SStranscore.db_by_key(null) - if(our_db) - our_db.m_backup(character.mind,character.nif,one_time = TRUE) - if(pref.resleeve_lock) - character.resleeve_lock = character.ckey - character.original_player = character.ckey - -/datum/category_item/player_setup_item/vore/resleeve/content(var/mob/user) - . += "
" - . += span_bold("Start With Body Scan:") + " [pref.resleeve_scan ? "Yes" : "No"]
" - . += span_bold("Start With Mind Scan:") + " [pref.mind_scan ? "Yes" : "No"]
" - . += span_bold("Prevent Body Impersonation:") + " [pref.resleeve_lock ? "Yes" : "No"]
" - -/datum/category_item/player_setup_item/vore/resleeve/OnTopic(var/href, var/list/href_list, var/mob/user) - if(href_list["toggle_resleeve_lock"]) - pref.resleeve_lock = pref.resleeve_lock ? 0 : 1; - return TOPIC_REFRESH - else if(href_list["toggle_resleeve_scan"]) - pref.resleeve_scan = pref.resleeve_scan ? 0 : 1; - return TOPIC_REFRESH - else if(href_list["toggle_mind_scan"]) - pref.mind_scan = pref.mind_scan ? 0 : 1; - return TOPIC_REFRESH - return ..(); diff --git a/code/modules/client/preference_setup/vore/05_persistence.dm b/code/modules/client/preference_setup/vore/05_persistence.dm deleted file mode 100644 index 3b04126b50..0000000000 --- a/code/modules/client/preference_setup/vore/05_persistence.dm +++ /dev/null @@ -1,63 +0,0 @@ -// Define a place to save in character setup -/datum/preferences - var/persistence_settings = PERSIST_DEFAULT // Control what if anything is persisted for this character between rounds. - -// Definition of the stuff for Sizing -/datum/category_item/player_setup_item/vore/persistence - name = "Persistence" - sort_order = 5 - -/datum/category_item/player_setup_item/vore/persistence/load_character(list/save_data) - pref.persistence_settings = save_data["persistence_settings"] - sanitize_character() // Don't let new characters start off with nulls - -/datum/category_item/player_setup_item/vore/persistence/save_character(list/save_data) - save_data["persistence_settings"] = pref.persistence_settings - -/datum/category_item/player_setup_item/vore/persistence/sanitize_character() - pref.persistence_settings = sanitize_integer(pref.persistence_settings, 0, (1<<(PERSIST_COUNT+1)-1), initial(pref.persistence_settings)) - -/datum/category_item/player_setup_item/vore/persistence/content(var/mob/user) - . = list() - . += span_bold("Round-to-Round Persistence") + "
" - . += "" - - . += "" - . += make_yesno(PERSIST_SPAWN) - . += "" - - . += "" - . += make_yesno(PERSIST_WEIGHT) - . += "" - - . += "" - . += make_yesno(PERSIST_ORGANS) - . += "" - - . += "" - . += make_yesno(PERSIST_MARKINGS) - . += "" - - . += "" - . += make_yesno(PERSIST_SIZE) - . += "" - - . += "
Save Spawn Location:
Save Weight:
Save Organs:
Save Markings:
Save Scale:
" - return jointext(., "") - -/datum/category_item/player_setup_item/vore/persistence/proc/make_yesno(var/bit) - if(pref.persistence_settings & bit) - return "" + span_linkOn(span_bold("Yes")) + " No" - else - return "Yes " + span_linkOn(span_bold("No")) + "" - -/datum/category_item/player_setup_item/vore/persistence/OnTopic(var/href, var/list/href_list, var/mob/user) - if(href_list["toggle_on"]) - var/bit = text2num(href_list["toggle_on"]) - pref.persistence_settings |= bit - return TOPIC_REFRESH - else if(href_list["toggle_off"]) - var/bit = text2num(href_list["toggle_off"]) - pref.persistence_settings &= ~bit - return TOPIC_REFRESH - return ..() diff --git a/code/modules/client/preference_setup/vore/06_vantag.dm b/code/modules/client/preference_setup/vore/06_vantag.dm deleted file mode 100644 index 70a17d6d14..0000000000 --- a/code/modules/client/preference_setup/vore/06_vantag.dm +++ /dev/null @@ -1,48 +0,0 @@ -// Define a place to save in character setup -/datum/preferences - var/vantag_volunteer = 0 // What state I want to be in, in terms of being affected by antags. - var/vantag_preference = VANTAG_NONE // Whether I'd like to volunteer to be an antag at some point. - -// Definition of the stuff for Sizing -/datum/category_item/player_setup_item/vore/vantag - name = "VS Events" - sort_order = 6 - -/datum/category_item/player_setup_item/vore/vantag/load_character(list/save_data) - pref.vantag_volunteer = save_data["vantag_volunteer"] - pref.vantag_preference = save_data["vantag_preference"] - -/datum/category_item/player_setup_item/vore/vantag/save_character(list/save_data) - save_data["vantag_volunteer"] = pref.vantag_volunteer - save_data["vantag_preference"] = pref.vantag_preference - -/datum/category_item/player_setup_item/vore/vantag/sanitize_character() - pref.vantag_volunteer = sanitize_integer(pref.vantag_volunteer, 0, 1, initial(pref.vantag_volunteer)) - pref.vantag_preference = sanitize_inlist(pref.vantag_preference, GLOB.vantag_choices_list, initial(pref.vantag_preference)) - -/datum/category_item/player_setup_item/vore/vantag/copy_to_mob(var/mob/living/carbon/human/character) - if(character && !istype(character,/mob/living/carbon/human/dummy)) - character.vantag_pref = pref.vantag_preference - BITSET(character.hud_updateflag, VANTAG_HUD) - -/datum/category_item/player_setup_item/vore/vantag/content(var/mob/user) - . += "
" - . += span_bold("Event Volunteer:") + " " + span_bold("[pref.vantag_volunteer ? "Yes" : "No"]") + "
" - . += span_bold("Event Pref:") + " " + span_bold("[GLOB.vantag_choices_list[pref.vantag_preference]]") + "
" - -/datum/category_item/player_setup_item/vore/vantag/OnTopic(var/href, var/list/href_list, var/mob/user) - if(href_list["toggle_vantag_volunteer"]) - pref.vantag_volunteer = pref.vantag_volunteer ? 0 : 1 - return TOPIC_REFRESH - - else if(href_list["change_vantag"]) - var/list/names_list = list() - for(var/C in GLOB.vantag_choices_list) - names_list[GLOB.vantag_choices_list[C]] = C - - var/selection = tgui_input_list(user, "How do you want to be involved with VS Event Characters, ERP-wise? They will see this choice on you in a HUD. Event characters are admin-selected and spawned players, possibly with assigned objectives, who are obligated to respect ERP prefs and RP their actions like any other player, though it may be a slightly shorter RP if they are pressed for time or being caught.", "Event Preference", names_list) - if(selection && selection != "Normal") - pref.vantag_preference = names_list[selection] - - return TOPIC_REFRESH - return ..() diff --git a/code/modules/client/preference_setup/vore/07_traits.dm b/code/modules/client/preference_setup/vore/07_traits.dm deleted file mode 100644 index ff87b52282..0000000000 --- a/code/modules/client/preference_setup/vore/07_traits.dm +++ /dev/null @@ -1,651 +0,0 @@ -#define POSITIVE_MODE 1 -#define NEUTRAL_MODE 2 -#define NEGATIVE_MODE 3 - -var/global/list/valid_bloodreagents = list("default",REAGENT_ID_IRON,REAGENT_ID_COPPER,REAGENT_ID_PHORON,REAGENT_ID_SILVER,REAGENT_ID_GOLD,REAGENT_ID_SLIMEJELLY) //allowlist-based so people don't make their blood restored by alcohol or something really silly. use reagent IDs! - -/datum/preferences - var/custom_species // Custom species name, can't be changed due to it having been used in savefiles already. - var/custom_base // What to base the custom species on - var/blood_color = "#A10808" - - var/custom_say = null - var/custom_whisper = null - var/custom_ask = null - var/custom_exclaim = null - - var/list/custom_heat = list() - var/list/custom_cold = list() - - var/list/pos_traits = list() // What traits they've selected for their custom species - var/list/neu_traits = list() - var/list/neg_traits = list() - - var/traits_cheating = 0 //Varedit by admins allows saving new maximums on people who apply/etc - var/starting_trait_points = 0 - var/max_traits = MAX_SPECIES_TRAITS - var/dirty_synth = 0 //Are you a synth - var/gross_meatbag = 0 //Where'd I leave my Voight-Kampff test kit? - -/datum/preferences/proc/get_custom_bases_for_species(var/new_species) - if (!new_species) - new_species = species - var/list/choices - var/datum/species/spec = GLOB.all_species[new_species] - if (spec.selects_bodytype == SELECTS_BODYTYPE_SHAPESHIFTER) - choices = spec.get_valid_shapeshifter_forms() - choices = choices.Copy() - else if (spec.selects_bodytype == SELECTS_BODYTYPE_CUSTOM) - choices = GLOB.custom_species_bases.Copy() - if(new_species != SPECIES_CUSTOM) - choices = (choices | new_species) - return choices - -/datum/category_item/player_setup_item/vore/traits/proc/get_html_for_trait(var/datum/trait/trait, var/list/trait_prefs = null) - . = "" - if (!LAZYLEN(trait.has_preferences)) - return - . = "
" - if (altered) - switch(trait.category) - if (1) //TRAIT_TYPE_POSITIVE - pref.pos_traits[trait.type] = trait_prefs - if (0) //TRAIT_TYPE_NEUTRAL - pref.neu_traits[trait.type] = trait_prefs - if (-1)//TRAIT_TYPE_NEGATIVE - pref.neg_traits[trait.type] = trait_prefs - -/datum/category_item/player_setup_item/vore/traits/proc/get_pref_choice_from_trait(var/mob/user, var/datum/trait/trait, var/preference) - if (!trait || !preference) - return - var/list/trait_prefs - var/datum/trait/instance = GLOB.all_traits[trait] - var/list/traitlist - switch(instance.category) - if (1) - traitlist = pref.pos_traits - if (0) - traitlist = pref.neu_traits - if (-1) - traitlist = pref.neg_traits - if (!LAZYLEN(instance.has_preferences) || !(preference in instance.has_preferences) || !traitlist) - return - if (!LAZYLEN(traitlist[trait])) - traitlist[trait] = instance.get_default_prefs() - trait_prefs = traitlist[trait] - if (!(preference in trait_prefs)) - trait_prefs[preference] = instance.default_value_for_pref(preference) //won't be called at all often - switch(instance.has_preferences[preference][1]) - if (1) //TRAIT_PREF_TYPE_BOOLEAN - trait_prefs[preference] = !trait_prefs[preference] - if (2) //TRAIT_PREF_TYPE_COLOR - var/new_color = tgui_color_picker(user, "Choose the color for this trait preference:", "Trait Preference", trait_prefs[preference]) - if (new_color) - trait_prefs[preference] = new_color - if (3) //TRAIT_PREF_TYPE_STRING - var/new_string = instance.apply_sanitization_to_string(preference, tgui_input_text(user, "What should the new value be?", instance.has_preferences[preference][2], trait_prefs[preference], MAX_NAME_LEN)) - trait_prefs[preference] = new_string - -// Definition of the stuff for Ears -/datum/category_item/player_setup_item/vore/traits - name = "Traits" - sort_order = 7 - -/datum/category_item/player_setup_item/vore/traits/load_character(list/save_data) - pref.custom_species = save_data["custom_species"] - pref.custom_base = save_data["custom_base"] - pref.pos_traits = text2path_list(save_data["pos_traits"]) - pref.neu_traits = text2path_list(save_data["neu_traits"]) - pref.neg_traits = text2path_list(save_data["neg_traits"]) - pref.blood_color = save_data["blood_color"] - pref.blood_reagents = save_data["blood_reagents"] - - pref.traits_cheating = save_data["traits_cheating"] - pref.max_traits = save_data["max_traits"] - pref.starting_trait_points = save_data["trait_points"] - - pref.custom_say = save_data["custom_say"] - pref.custom_whisper = save_data["custom_whisper"] - pref.custom_ask = save_data["custom_ask"] - pref.custom_exclaim = save_data["custom_exclaim"] - - pref.custom_heat = check_list_copy(save_data["custom_heat"]) - pref.custom_cold = check_list_copy(save_data["custom_cold"]) - -/datum/category_item/player_setup_item/vore/traits/save_character(list/save_data) - save_data["custom_species"] = pref.custom_species - save_data["custom_base"] = pref.custom_base - save_data["pos_traits"] = check_list_copy(pref.pos_traits) - save_data["neu_traits"] = check_list_copy(pref.neu_traits) - save_data["neg_traits"] = check_list_copy(pref.neg_traits) - save_data["blood_color"] = pref.blood_color - save_data["blood_reagents"] = pref.blood_reagents - - save_data["traits_cheating"] = pref.traits_cheating - save_data["max_traits"] = pref.max_traits - save_data["trait_points"] = pref.starting_trait_points - - save_data["custom_say"] = pref.custom_say - save_data["custom_whisper"] = pref.custom_whisper - save_data["custom_ask"] = pref.custom_ask - save_data["custom_exclaim"] = pref.custom_exclaim - - save_data["custom_heat"] = check_list_copy(pref.custom_heat) - save_data["custom_cold"] = check_list_copy(pref.custom_cold) - -/datum/category_item/player_setup_item/vore/traits/sanitize_character() - if(!pref.pos_traits) pref.pos_traits = list() - if(!pref.neu_traits) pref.neu_traits = list() - if(!pref.neg_traits) pref.neg_traits = list() - - pref.blood_color = sanitize_hexcolor(pref.blood_color, default="#A10808") - pref.blood_reagents = sanitize_text(pref.blood_reagents, initial(pref.blood_reagents)) - - if(!pref.traits_cheating) - var/datum/species/S = GLOB.all_species[pref.species] - if(S) - pref.starting_trait_points = S.trait_points - else - pref.starting_trait_points = 0 - pref.max_traits = MAX_SPECIES_TRAITS - - if(pref.organ_data[O_BRAIN]) //Checking if we have a synth on our hands, boys. - pref.dirty_synth = 1 - pref.gross_meatbag = 0 - else - pref.gross_meatbag = 1 - pref.dirty_synth = 0 - - // Clean up positive traits - for(var/datum/trait/path as anything in pref.pos_traits) - if(!(path in GLOB.positive_traits)) - pref.pos_traits -= path - continue - if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_positive)) - pref.pos_traits -= path - continue - var/take_flags = initial(path.can_take) - if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS))) - pref.pos_traits -= path - //Neutral traits - for(var/datum/trait/path as anything in pref.neu_traits) - if(!(path in GLOB.neutral_traits)) - to_world_log("removing [path] for not being in neutral_traits") - pref.neu_traits -= path - continue - if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_neutral)) - to_world_log("removing [path] for not being a custom species") - pref.neu_traits -= path - continue - var/take_flags = initial(path.can_take) - if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS))) - to_world_log("removing [path] for being a dirty synth") - pref.neu_traits -= path - //Negative traits - for(var/datum/trait/path as anything in pref.neg_traits) - if(!(path in GLOB.negative_traits)) - pref.neg_traits -= path - continue - if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_negative)) - pref.neg_traits -= path - continue - var/take_flags = initial(path.can_take) - if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS))) - pref.neg_traits -= path - - var/datum/species/selected_species = GLOB.all_species[pref.species] - if(selected_species.selects_bodytype) - if (!(pref.custom_base in pref.get_custom_bases_for_species())) - pref.custom_base = SPECIES_HUMAN - //otherwise, allowed! - else if(!pref.custom_base || !(pref.custom_base in GLOB.custom_species_bases)) - pref.custom_base = SPECIES_HUMAN - - pref.custom_say = lowertext(trim(pref.custom_say)) - pref.custom_whisper = lowertext(trim(pref.custom_whisper)) - pref.custom_ask = lowertext(trim(pref.custom_ask)) - pref.custom_exclaim = lowertext(trim(pref.custom_exclaim)) - - if (islist(pref.custom_heat)) //don't bother checking these for actual singular message length, they should already have been checked and it'd take too long every time it's sanitized - if (length(pref.custom_heat) > 10) - pref.custom_heat.Cut(11) - else - pref.custom_heat = list() - if (islist(pref.custom_cold)) - if (length(pref.custom_cold) > 10) - pref.custom_cold.Cut(11) - else - pref.custom_cold = list() - -/datum/category_item/player_setup_item/vore/traits/copy_to_mob(var/mob/living/carbon/human/character) - character.custom_species = pref.custom_species - character.custom_say = lowertext(trim(pref.custom_say)) - character.custom_ask = lowertext(trim(pref.custom_ask)) - character.custom_whisper = lowertext(trim(pref.custom_whisper)) - character.custom_exclaim = lowertext(trim(pref.custom_exclaim)) - character.custom_heat = pref.custom_heat - character.custom_cold = pref.custom_cold - - - if(character.isSynthetic()) //Checking if we have a synth on our hands, boys. - pref.dirty_synth = 1 - pref.gross_meatbag = 0 - else - pref.gross_meatbag = 1 - pref.dirty_synth = 0 - - var/datum/species/S = character.species - var/datum/species/new_S = S.produceCopy(pref.pos_traits + pref.neu_traits + pref.neg_traits, character, pref.custom_base, TRUE) - - for(var/datum/trait/T in new_S.traits) - T.apply_pref(src) - - //Any additional non-trait settings can be applied here - new_S.blood_color = pref.blood_color - if(!(pref.blood_reagents == "default")) - new_S.blood_reagents = pref.blood_reagents - - //Any additional non-trait settings can be applied here - new_S.blood_color = pref.blood_color - - var/species_sounds_to_copy = pref.species_sound // What sounds are we using? - if(species_sounds_to_copy == "Unset") // Are we unset? - species_sounds_to_copy = select_default_species_sound(pref) // This will also grab gendered versions of the sounds, if they exist. - - new_S.species_sounds = species_sounds_to_copy // Now we send our sounds over to the mob - - if(pref.species == SPECIES_CUSTOM) - //Statistics for this would be nice - var/english_traits = english_list(new_S.traits, and_text = ";", comma_text = ";") - log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet - -/datum/category_item/player_setup_item/vore/traits/content(var/mob/user) - . += span_bold("Custom Species Name:") + " " - . += "[pref.custom_species ? pref.custom_species : "-Input Name-"]
" - - var/datum/species/selected_species = GLOB.all_species[pref.species] - if(selected_species.selects_bodytype) - . += span_bold("Icon Base:") + " " - . += "[pref.custom_base ? pref.custom_base : "Human"]
" - - var/traits_left = pref.max_traits - - - var/points_left = pref.starting_trait_points - - - for(var/T in pref.pos_traits + pref.neg_traits) - points_left -= GLOB.traits_costs[T] - if(T in pref.pos_traits) - traits_left-- - . += span_bold("Traits Left:") + " [traits_left]
" - . += span_bold("Points Left:") + " [points_left]
" - if(points_left < 0 || traits_left < 0 || (!pref.custom_species && pref.species == SPECIES_CUSTOM)) - . += span_red(span_bold("^ Fix things! ^")) + "
" - - . += "Positive Trait(s) (Limited) +
" - . += "" - - . += "Neutral Trait(s) (No Limit) +
" - . += "" - - . += "Negative Trait(s) (No Limit) +
" - . += "" - - . += span_bold("Blood Color: ") //People that want to use a certain species to have that species traits (xenochimera/promethean/spider) should be able to set their own blood color. - . += "Set Color " - . += "R
" - . += span_bold("Blood Reagent: ") //Wanna be copper-based? Go ahead. - . += "[pref.blood_reagents]
" - . += "
" - - . += span_bold("Custom Say: ") - . += "Set Say Verb" - . += "(Reset)" - . += "
" - . += span_bold("Custom Whisper: ") - . += "Set Whisper Verb" - . += "(Reset)" - . += "
" - . += span_bold("Custom Ask: ") - . += "Set Ask Verb" - . += "(Reset)" - . += "
" - . += span_bold("Custom Exclaim: ") - . += "Set Exclaim Verb" - . += "(Reset)" - . += "
" - . += span_bold("Custom Heat Discomfort: ") - . += "Set Heat Messages" - . += "(Reset)" - . += "
" - . += span_bold("Custom Cold Discomfort: ") - . += "Set Cold Messages" - . += "(Reset)" - . += "
" - -/datum/category_item/player_setup_item/vore/traits/OnTopic(var/href,var/list/href_list, var/mob/user) - if(!CanUseTopic(user)) - return TOPIC_NOACTION - - else if(href_list["custom_species"]) - var/raw_choice = sanitize(tgui_input_text(user, "Input your custom species name:", - "Character Preference", pref.custom_species, MAX_NAME_LEN), MAX_NAME_LEN) - if (CanUseTopic(user)) - pref.custom_species = raw_choice - return TOPIC_REFRESH - - else if(href_list["custom_base"]) - var/list/choices = pref.get_custom_bases_for_species() - var/text_choice = tgui_input_list(user, "Pick an icon set for your species:","Icon Base", choices) - if(text_choice in choices) - pref.custom_base = text_choice - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["blood_color"]) - var/color_choice = tgui_color_picker(user, "Pick a blood color (does not apply to synths)","Blood Color",pref.blood_color) - if(color_choice) - pref.blood_color = sanitize_hexcolor(color_choice, default="#A10808") - return TOPIC_REFRESH - - else if(href_list["blood_reset"]) - var/datum/species/spec = GLOB.all_species[pref.species] - var/new_blood = spec.blood_color ? spec.blood_color : "#A10808" - var/choice = tgui_alert(user, "Reset blood color to species default ([new_blood])?","Reset Blood Color",list("Reset","Cancel")) - if(choice == "Reset") - pref.blood_color = new_blood - return TOPIC_REFRESH - - else if(href_list["blood_reagents"]) - var/new_blood_reagents = tgui_input_list(user, "Choose your character's blood restoration reagent:", "Character Preference", valid_bloodreagents) - if(new_blood_reagents && CanUseTopic(user)) - pref.blood_reagents = new_blood_reagents - return TOPIC_REFRESH - - else if(href_list["clicked_pos_trait"]) - var/datum/trait/trait = text2path(href_list["clicked_pos_trait"]) - var/choice = tgui_alert(user, "Remove [initial(trait.name)] and regain [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel")) - if(choice == "Remove") - pref.pos_traits -= trait - var/datum/trait/instance = GLOB.all_traits[trait] - instance.remove_pref(pref) - return TOPIC_REFRESH - - else if(href_list["clicked_neu_trait"]) - var/datum/trait/trait = text2path(href_list["clicked_neu_trait"]) - var/choice = tgui_alert(user, "Remove [initial(trait.name)]?","Remove Trait",list("Remove","Cancel")) - if(choice == "Remove") - pref.neu_traits -= trait - var/datum/trait/instance = GLOB.all_traits[trait] - instance.remove_pref(pref) - return TOPIC_REFRESH - - else if(href_list["clicked_neg_trait"]) - var/datum/trait/trait = text2path(href_list["clicked_neg_trait"]) - var/choice = tgui_alert(user, "Remove [initial(trait.name)] and lose [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel")) - if(choice == "Remove") - pref.neg_traits -= trait - var/datum/trait/instance = GLOB.all_traits[trait] - instance.remove_pref(pref) - return TOPIC_REFRESH - - else if(href_list["clicked_trait_pref"]) - var/datum/trait/trait = text2path(href_list["clicked_trait_pref"]) - get_pref_choice_from_trait(user, trait, href_list["pref"]) - return TOPIC_REFRESH - - else if(href_list["custom_say"]) - var/say_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'says': [pref.real_name] says, \"Hi.\"", "Custom Say", pref.custom_say, 12), 12) - if(say_choice) - pref.custom_say = say_choice - return TOPIC_REFRESH - - else if(href_list["custom_whisper"]) - var/whisper_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'whispers': [pref.real_name] whispers, \"Hi...\"", "Custom Whisper", pref.custom_whisper, 12), 12) - if(whisper_choice) - pref.custom_whisper = whisper_choice - return TOPIC_REFRESH - - else if(href_list["custom_ask"]) - var/ask_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'asks': [pref.real_name] asks, \"Hi?\"", "Custom Ask", pref.custom_ask, 12), 12) - if(ask_choice) - pref.custom_ask = ask_choice - return TOPIC_REFRESH - - else if(href_list["custom_exclaim"]) - var/exclaim_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'exclaims', 'shouts' or 'yells': [pref.real_name] exclaims, \"Hi!\"", "Custom Exclaim", pref.custom_exclaim, 12), 12) - if(exclaim_choice) - pref.custom_exclaim = exclaim_choice - return TOPIC_REFRESH - - else if(href_list["custom_heat"]) - tgui_alert(user, "You are setting custom heat messages. These will overwrite your species' defaults. To return to defaults, click reset.") - var/old_message = pref.custom_heat.Join("\n\n") - var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Heat Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0) - if(length(new_message) > 0) - var/list/raw_list = splittext(new_message,"\n\n") - if(raw_list.len > 10) - raw_list.Cut(11) - for(var/i = 1, i <= raw_list.len, i++) - if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160) - raw_list.Cut(i,i) - else - raw_list[i] = readd_quotes(raw_list[i]) - ASSERT(raw_list.len <= 10) - pref.custom_heat = raw_list - return TOPIC_REFRESH - - else if(href_list["custom_cold"]) - tgui_alert(user, "You are setting custom cold messages. These will overwrite your species' defaults. To return to defaults, click reset.") - var/old_message = pref.custom_heat.Join("\n\n") - var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Cold Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0) - if(length(new_message) > 0) - var/list/raw_list = splittext(new_message,"\n\n") - if(raw_list.len > 10) - raw_list.Cut(11) - for(var/i = 1, i <= raw_list.len, i++) - if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160) - raw_list.Cut(i,i) - else - raw_list[i] = readd_quotes(raw_list[i]) - ASSERT(raw_list.len <= 10) - pref.custom_cold = raw_list - return TOPIC_REFRESH - - else if(href_list["reset_say"]) - var/say_choice = tgui_alert(user, "Reset your Custom Say Verb?","Reset Verb",list("Yes","No")) - if(say_choice == "Yes") - pref.custom_say = null - return TOPIC_REFRESH - - else if(href_list["reset_whisper"]) - var/whisper_choice = tgui_alert(user, "Reset your Custom Whisper Verb?","Reset Verb",list("Yes","No")) - if(whisper_choice == "Yes") - pref.custom_whisper = null - return TOPIC_REFRESH - - else if(href_list["reset_ask"]) - var/ask_choice = tgui_alert(user, "Reset your Custom Ask Verb?","Reset Verb",list("Yes","No")) - if(ask_choice == "Yes") - pref.custom_ask = null - return TOPIC_REFRESH - - else if(href_list["reset_exclaim"]) - var/exclaim_choice = tgui_alert(user, "Reset your Custom Exclaim Verb?","Reset Verb",list("Yes","No")) - if(exclaim_choice == "Yes") - pref.custom_exclaim = null - return TOPIC_REFRESH - - else if(href_list["reset_cold"]) - var/cold_choice = tgui_alert(user, "Reset your Custom Cold Discomfort messages?", "Reset Discomfort",list("Yes","No")) - if(cold_choice == "Yes") - pref.custom_cold = list() - return TOPIC_REFRESH - - else if(href_list["reset_heat"]) - var/heat_choice = tgui_alert(user, "Reset your Custom Heat Discomfort messages?", "Reset Discomfort",list("Yes","No")) - if(heat_choice == "Yes") - pref.custom_heat = list() - return TOPIC_REFRESH - - else if(href_list["add_trait"]) - var/mode = text2num(href_list["add_trait"]) - var/list/picklist - var/list/mylist - switch(mode) - if(POSITIVE_MODE) - if(pref.species == SPECIES_CUSTOM) - picklist = GLOB.positive_traits.Copy() - pref.pos_traits - mylist = pref.pos_traits - else - picklist = GLOB.everyone_traits_positive.Copy() - pref.pos_traits - mylist = pref.pos_traits - if(NEUTRAL_MODE) - if(pref.species == SPECIES_CUSTOM) - picklist = GLOB.neutral_traits.Copy() - pref.neu_traits - mylist = pref.neu_traits - else - picklist = GLOB.everyone_traits_neutral.Copy() - pref.neu_traits - mylist = pref.neu_traits - if(NEGATIVE_MODE) - if(pref.species == SPECIES_CUSTOM) - picklist = GLOB.negative_traits.Copy() - pref.neg_traits - mylist = pref.neg_traits - else - picklist = GLOB.everyone_traits_negative.Copy() - pref.neg_traits - mylist = pref.neg_traits - - if(isnull(picklist)) - return TOPIC_REFRESH - - if(isnull(mylist)) - return TOPIC_REFRESH - - var/list/nicelist = list() - for(var/P in picklist) - var/datum/trait/T = picklist[P] - nicelist[T.name] = P - - var/points_left = pref.starting_trait_points - for(var/T in pref.pos_traits + pref.neu_traits + pref.neg_traits) - points_left -= GLOB.traits_costs[T] - - var/traits_left = pref.max_traits - pref.pos_traits.len - - var/message = "Select a trait to learn more." - if(mode != NEUTRAL_MODE) - message = "\[Remaining: [points_left] points, [traits_left] traits\]\n" + message - var/title = "Traits" - switch(mode) - if(POSITIVE_MODE) - title = "Positive Traits" - if(NEUTRAL_MODE) - title = "Neutral Traits" - if(NEGATIVE_MODE) - title = "Negative Traits" - - var/trait_choice - var/done = FALSE - while(!done) - trait_choice = tgui_input_list(user, message, title, nicelist) - if(!trait_choice) - done = TRUE - if(trait_choice in nicelist) - var/datum/trait/path = nicelist[trait_choice] - var/choice = tgui_alert(user, "\[Cost:[initial(path.cost)]\] [initial(path.desc)]",initial(path.name), list("Take Trait","Go Back")) - if(choice == "Take Trait") - done = TRUE - - if(!trait_choice) - return TOPIC_REFRESH - else if(trait_choice in nicelist) - var/datum/trait/path = nicelist[trait_choice] - var/datum/trait/instance = GLOB.all_traits[path] - - var/conflict = FALSE - - if(pref.dirty_synth && !(instance.can_take & SYNTHETICS)) - tgui_alert_async(user, "The trait you've selected can only be taken by organic characters!", "Error") - return TOPIC_REFRESH - - if(pref.gross_meatbag && !(instance.can_take & ORGANICS)) - tgui_alert_async(user, "The trait you've selected can only be taken by synthetic characters!", "Error") - return TOPIC_REFRESH - - if(pref.species in instance.banned_species) - tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error") - return TOPIC_REFRESH - - if( LAZYLEN(instance.allowed_species) && !(pref.species in instance.allowed_species)) - tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error") - return TOPIC_REFRESH - - if(trait_choice in (pref.pos_traits + pref.neu_traits + pref.neg_traits)) - conflict = instance.name - - varconflict: - for(var/P in (pref.pos_traits + pref.neu_traits + pref.neg_traits)) - var/datum/trait/instance_test = GLOB.all_traits[P] - if(path in instance_test.excludes) - conflict = instance_test.name - break varconflict - - for(var/V in instance.var_changes) - if(V == "flags") - continue - if(V in instance_test.var_changes) - conflict = instance_test.name - break varconflict - - for(var/V in instance.var_changes_pref) - if(V in instance_test.var_changes_pref) - conflict = instance_test.name - break varconflict - - if(conflict) - tgui_alert_async(user, "You cannot take this trait and [conflict] at the same time. Please remove that trait, or pick another trait to add.", "Error") - return TOPIC_REFRESH - - instance.apply_pref(pref) - mylist[path] = instance.get_default_prefs() - return TOPIC_REFRESH - - return ..() - -#undef POSITIVE_MODE -#undef NEUTRAL_MODE -#undef NEGATIVE_MODE diff --git a/code/modules/client/preference_setup/vore/09_misc.dm b/code/modules/client/preference_setup/vore/09_misc.dm deleted file mode 100644 index d68b659602..0000000000 --- a/code/modules/client/preference_setup/vore/09_misc.dm +++ /dev/null @@ -1,115 +0,0 @@ -/datum/category_item/player_setup_item/vore/misc - name = "Misc Settings" - sort_order = 9 - -/datum/category_item/player_setup_item/vore/misc/load_character(list/save_data) - pref.show_in_directory = save_data["show_in_directory"] - pref.directory_tag = save_data["directory_tag"] - pref.directory_gendertag = save_data["directory_gendertag"] - pref.directory_sexualitytag = save_data["directory_sexualitytag"] - pref.directory_erptag = save_data["directory_erptag"] - pref.directory_ad = save_data["directory_ad"] - pref.sensorpref = save_data["sensorpref"] - pref.capture_crystal = save_data["capture_crystal"] - pref.auto_backup_implant = save_data["auto_backup_implant"] - pref.borg_petting = save_data["borg_petting"] - -/datum/category_item/player_setup_item/vore/misc/save_character(list/save_data) - save_data["show_in_directory"] = pref.show_in_directory - save_data["directory_tag"] = pref.directory_tag - save_data["directory_gendertag"] = pref.directory_gendertag - save_data["directory_sexualitytag"] = pref.directory_sexualitytag - save_data["directory_erptag"] = pref.directory_erptag - save_data["directory_ad"] = pref.directory_ad - save_data["sensorpref"] = pref.sensorpref - save_data["capture_crystal"] = pref.capture_crystal - save_data["auto_backup_implant"] = pref.auto_backup_implant - save_data["borg_petting"] = pref.borg_petting - -/datum/category_item/player_setup_item/vore/misc/copy_to_mob(var/mob/living/carbon/human/character) - if(pref.sensorpref > 5 || pref.sensorpref < 1) - pref.sensorpref = 5 - character.sensorpref = pref.sensorpref - character.capture_crystal = pref.capture_crystal - //Vore Stomach Sprite Preference - character.recalculate_vis() - -/datum/category_item/player_setup_item/vore/misc/sanitize_character() - pref.show_in_directory = sanitize_integer(pref.show_in_directory, 0, 1, initial(pref.show_in_directory)) - pref.directory_tag = sanitize_inlist(pref.directory_tag, GLOB.char_directory_tags, initial(pref.directory_tag)) - pref.directory_gendertag = sanitize_inlist(pref.directory_gendertag, GLOB.char_directory_gendertags, initial(pref.directory_gendertag)) - pref.directory_sexualitytag = sanitize_inlist(pref.directory_sexualitytag, GLOB.char_directory_sexualitytags, initial(pref.directory_sexualitytag)) - pref.directory_erptag = sanitize_inlist(pref.directory_erptag, GLOB.char_directory_erptags, initial(pref.directory_erptag)) - pref.sensorpref = sanitize_integer(pref.sensorpref, 1, GLOB.sensorpreflist.len, initial(pref.sensorpref)) - pref.capture_crystal = sanitize_integer(pref.capture_crystal, 0, 1, initial(pref.capture_crystal)) - pref.auto_backup_implant = sanitize_integer(pref.auto_backup_implant, 0, 1, initial(pref.auto_backup_implant)) - pref.borg_petting = sanitize_integer(pref.borg_petting, 0, 1, initial(pref.borg_petting)) - -/datum/category_item/player_setup_item/vore/misc/content(var/mob/user) - . += "
" - . += span_bold("Appear in Character Directory:") + " [pref.show_in_directory ? "Yes" : "No"]
" - . += span_bold("Character Directory Vore Tag:") + " [pref.directory_tag]
" - . += span_bold("Character Directory Gender:") + " [pref.directory_gendertag]
" - . += span_bold("Character Directory Sexuality:") + " [pref.directory_sexualitytag]
" - . += span_bold("Character Directory ERP Tag:") + " [pref.directory_erptag]
" - . += span_bold("Character Directory Advertisement:") + " Set Directory Ad
" - . += span_bold("Suit Sensors Preference:") + " [GLOB.sensorpreflist[pref.sensorpref]]
" - . += span_bold("Capture Crystal Preference:") + " [pref.capture_crystal ? "Yes" : "No"]
" - . += span_bold("Spawn With Backup Implant:") + " [pref.auto_backup_implant ? "Yes" : "No"]
" - . += span_bold("Allow petting as robot:") + " [pref.borg_petting ? "Yes" : "No"]
" - if(CONFIG_GET(flag/allow_metadata)) - . += span_bold("Private Notes: Edit") + "
" - -/datum/category_item/player_setup_item/vore/misc/OnTopic(var/href, var/list/href_list, var/mob/user) - if(href_list["toggle_show_in_directory"]) - pref.show_in_directory = pref.show_in_directory ? 0 : 1; - return TOPIC_REFRESH - else if(href_list["directory_tag"]) - var/new_tag = tgui_input_list(user, "Pick a new Vore tag for the character directory", "Character Vore Tag", GLOB.char_directory_tags, pref.directory_tag) - if(!new_tag) - return - pref.directory_tag = new_tag - return TOPIC_REFRESH - else if(href_list["directory_gendertag"]) - var/new_gendertag = tgui_input_list(user, "Pick a new Gender tag for the character directory. This is YOUR gender, not what you prefer.", "Character Gender Tag", GLOB.char_directory_gendertags, pref.directory_gendertag) - if(!new_gendertag) - return - pref.directory_gendertag = new_gendertag - return TOPIC_REFRESH - else if(href_list["directory_sexualitytag"]) - var/new_sexualitytag = tgui_input_list(user, "Pick a new Sexuality/Orientation tag for the character directory", "Character Sexuality/Orientation Tag", GLOB.char_directory_sexualitytags, pref.directory_sexualitytag) - if(!new_sexualitytag) - return - pref.directory_sexualitytag = new_sexualitytag - return TOPIC_REFRESH - else if(href_list["directory_erptag"]) - var/new_erptag = tgui_input_list(user, "Pick a new ERP tag for the character directory", "Character ERP Tag", GLOB.char_directory_erptags, pref.directory_erptag) - if(!new_erptag) - return - pref.directory_erptag = new_erptag - return TOPIC_REFRESH - else if(href_list["directory_ad"]) - var/msg = sanitize(tgui_input_text(user,"Write your advertisement here!", "Flavor Text", html_decode(pref.directory_ad), multiline = TRUE, prevent_enter = TRUE), extra = 0) //VOREStation Edit: separating out OOC notes - if(!msg) - return - pref.directory_ad = msg - return TOPIC_REFRESH - else if(href_list["toggle_sensor_setting"]) - var/new_sensorpref = tgui_input_list(user, "Choose your character's sensor preferences:", "Character Preferences", GLOB.sensorpreflist, GLOB.sensorpreflist[pref.sensorpref]) - if (!isnull(new_sensorpref) && CanUseTopic(user)) - pref.sensorpref = GLOB.sensorpreflist.Find(new_sensorpref) - return TOPIC_REFRESH - else if(href_list["toggle_capture_crystal"]) - pref.capture_crystal = pref.capture_crystal ? 0 : 1; - return TOPIC_REFRESH - else if(href_list["toggle_implant"]) - pref.auto_backup_implant = pref.auto_backup_implant ? 0 : 1; - return TOPIC_REFRESH - else if(href_list["toggle_borg_petting"]) - pref.borg_petting = pref.borg_petting ? 0 : 1; - return TOPIC_REFRESH - else if(href_list["edit_private_notes"]) - var/new_metadata = sanitize(tgui_input_text(user,"Write some notes for yourself. These can be anything that is useful, whether it's character events that you want to remember or a bit of lore. Things that you would normally stick in a txt file for yourself!", "Private Notes", html_decode(pref.read_preference(/datum/preference/text/living/private_notes)), multiline = TRUE, prevent_enter = TRUE), extra = 0) - if(new_metadata && CanUseTopic(user)) - pref.update_preference_by_type(/datum/preference/text/living/private_notes, new_metadata) - return ..(); diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 8ee2f069c7..508f319645 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -13,6 +13,7 @@ var/list/preferences_datums = list() var/muted = 0 var/last_ip var/last_id + var/saved_notification = FALSE //game-preferences var/be_special = 0 //Special role selection @@ -36,7 +37,6 @@ var/list/preferences_datums = list() var/list/alternate_languages = list() //Secondary language(s) var/list/language_prefixes = list() //Language prefix keys var/list/language_custom_keys = list() //Language custom call keys - var/list/gear //Left in for Legacy reasons, will no longer save. var/list/gear_list = list() //Custom/fluff item loadouts. var/gear_slot = 1 //The current gear save slot var/list/traits //Traits which modifier characters for better or worse (mostly worse). @@ -61,7 +61,8 @@ var/list/preferences_datums = list() "4" = "character_preview_map:2,3", "8" = "character_preview_map:2,1", "BG" = "character_preview_map:1,1 to 3,8", - "PMH" = "character_preview_map:2,7" + "PMH" = "character_preview_map:2,7", + "PMHjiggle" = "character_preview_map:102,7:107", ) //Jobs, uses bitflags @@ -97,8 +98,6 @@ var/list/preferences_datums = list() var/list/flavour_texts_robot = list() var/custom_link = null - var/list/body_descriptors = list() - var/med_record = "" var/sec_record = "" var/gen_record = "" @@ -128,8 +127,6 @@ var/list/preferences_datums = list() // WE JUST HAVE NOWHERE ELSE TO STORE IT var/list/action_button_screen_locs - var/list/volume_channels = list() - ///If they are currently in the process of swapping slots, don't let them open 999 windows for it and get confused var/selecting_slots = FALSE @@ -153,7 +150,6 @@ var/list/preferences_datums = list() load_savefile() // Legacy code - gear = list() gear_list = list() gear_slot = 1 // End legacy code @@ -194,31 +190,9 @@ var/list/preferences_datums = list() update_preview_icon() show_character_previews() - var/dat = "
" - - if(path) - dat += "Slot - " - dat += "Load slot - " - dat += "Save slot - " - dat += "Reload slot - " - dat += "Reset slot - " - dat += "Copy slot" - - else - dat += "Please create an account to save your preferences." - - dat += "
" - dat += player_setup.header() - dat += "

" - dat += player_setup.content(user) - - dat += "" - //user << browse(dat, "window=preferences;size=635x736") - winshow(user, "preferences_window", TRUE) - var/datum/browser/popup = new(user, "preferences_browser", "Character Setup", 800, 800) - popup.set_content(dat) - popup.open(FALSE) // Skip registring onclose on the browser pane - onclose(user, "preferences_window", src) // We want to register on the window itself + current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES + update_tgui_static_data(user) + tgui_interact(user) /datum/preferences/proc/update_character_previews(var/mob/living/carbon/human/mannequin) if(!client) @@ -351,10 +325,6 @@ var/list/preferences_datums = list() character.update_underwear() character.update_hair() - if(LAZYLEN(character.descriptors)) - for(var/entry in body_descriptors) - character.descriptors[entry] = body_descriptors[entry] - /datum/preferences/proc/open_load_dialog(mob/user) if(selecting_slots) to_chat(user, span_warning("You already have a slot selection dialog open!")) @@ -583,22 +553,6 @@ var/list/preferences_datums = list() O.markings[M] = list("color" = body_markings[M][BP]["color"], "datum" = mark_datum, "priority" = priority, "on" = body_markings[M][BP]["on"]) character.markings_len = priority - var/list/last_descriptors = list() - if(islist(body_descriptors)) - last_descriptors = body_descriptors.Copy() - body_descriptors = list() - - var/datum/species/mob_species = GLOB.all_species[species] - if(LAZYLEN(mob_species.descriptors)) - for(var/entry in mob_species.descriptors) - var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] - if(istype(descriptor)) - if(isnull(last_descriptors[entry])) - body_descriptors[entry] = descriptor.default_value // Species datums have initial default value. - else - body_descriptors[entry] = CLAMP(last_descriptors[entry], 1, LAZYLEN(descriptor.standalone_value_descriptors)) - character.descriptors = body_descriptors - if (copy_flavour) character.flavor_texts["general"] = flavor_texts["general"] character.flavor_texts["head"] = flavor_texts["head"] diff --git a/code/modules/client/preferences/middleware/bay_adapter.dm b/code/modules/client/preferences/middleware/bay_adapter.dm new file mode 100644 index 0000000000..da6392b1fb --- /dev/null +++ b/code/modules/client/preferences/middleware/bay_adapter.dm @@ -0,0 +1,90 @@ +/datum/preference_middleware/bay_adapter + key = "legacy" + +/datum/preference_middleware/bay_adapter/get_ui_data(mob/user) + var/list/data = ..() + + if(preferences.current_window != PREFERENCE_TAB_CHARACTER_PREFERENCES) + return data + + var/list/legacy = list() + if(preferences.player_setup.selected_category) + for(var/datum/category_item/player_setup_item/item as anything in preferences.player_setup.selected_category.items) + legacy += item.tgui_data(user) + data["selected_category"] = list( + "name" = preferences.player_setup.selected_category.name, + "items" = legacy, + ) + + data["preview_loadout"] = preferences.equip_preview_mob & EQUIP_PREVIEW_LOADOUT + data["preview_job_gear"] = preferences.equip_preview_mob & EQUIP_PREVIEW_JOB + data["preview_animations"] = preferences.animations_toggle + + return data + +/datum/preference_middleware/bay_adapter/get_ui_static_data(mob/user) + var/list/data = ..() + + if(preferences.current_window != PREFERENCE_TAB_CHARACTER_PREFERENCES) + return data + + var/list/categories_data = list() + for(var/datum/category_group/player_setup_category/category as anything in preferences.player_setup.categories) + UNTYPED_LIST_ADD(categories_data, category.name) + data["categories"] = categories_data + + var/list/legacy = list() + if(preferences.player_setup.selected_category) + var/list/items = preferences.player_setup.selected_category.items + for(var/datum/category_item/player_setup_item/item as anything in items) + legacy += item.tgui_static_data(user) + data["selected_category_static"] = legacy + + return data + +/datum/preference_middleware/bay_adapter/get_constant_data() + var/list/data = list() + + var/datum/category_collection/player_setup_collection/collection = new() + + var/list/categories = collection.categories + for(var/datum/category_group/player_setup_category/category as anything in categories) + for(var/datum/category_item/player_setup_item/item as anything in category.items) + data += item.tgui_constant_data() + + return data + +/datum/category_item/player_setup_item/proc/tgui_constant_data() + return list() + + +/datum/preference_middleware/bay_adapter/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + switch(action) + if("cycle_background") + preferences.bgstate = next_in_list(preferences.bgstate, preferences.bgstate_options) + preferences.update_preview_icon() + return TRUE + if("toggle_preview_loadout") + preferences.equip_preview_mob ^= EQUIP_PREVIEW_LOADOUT + preferences.update_preview_icon() + return TRUE + if("toggle_preview_job_gear") + preferences.equip_preview_mob ^= EQUIP_PREVIEW_JOB + preferences.update_preview_icon() + return TRUE + if("toggle_preview_animations") + preferences.animations_toggle = !preferences.animations_toggle + preferences.update_preview_icon() + return TRUE + + for(var/datum/category_group/player_setup_category/category as anything in preferences.player_setup.categories) + for(var/datum/category_item/player_setup_item/item as anything in category.items) + . = item.tgui_act(action, params, ui, state) + if(.) + if(. & TOPIC_UPDATE_PREVIEW) + preferences.update_preview_icon() + return diff --git a/code/modules/client/preferences/migrations/18_jukebox.dm b/code/modules/client/preferences/migrations/18_jukebox.dm new file mode 100644 index 0000000000..f437fa9eb7 --- /dev/null +++ b/code/modules/client/preferences/migrations/18_jukebox.dm @@ -0,0 +1,4 @@ +/// Remaps jukebox volume from 0-1 to 0-100. +/datum/preferences/proc/migration_18_jukebox(datum/json_savefile/S) + S.set_entry("media_volume", S.get_entry("media_volume") * 100) + S.save() diff --git a/code/modules/client/preferences/types/character/general/01_basic.dm b/code/modules/client/preferences/types/character/general/01_basic.dm index d466b39b90..022407bf5d 100644 --- a/code/modules/client/preferences/types/character/general/01_basic.dm +++ b/code/modules/client/preferences/types/character/general/01_basic.dm @@ -183,3 +183,21 @@ /datum/preference/text/living/private_notes/apply_to_living(mob/living/target, value) target.private_notes = value return + +/datum/preference/choiced/living/emote_sound_mode + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "emote_sound_mode" + +/datum/preference/choiced/living/emote_sound_mode/init_possible_values() + return list( + EMOTE_SOUND_NO_FREQ, + EMOTE_SOUND_VOICE_FREQ, + EMOTE_SOUND_VOICE_LIST, + ) + +/datum/preference/choiced/living/emote_sound_mode/apply_to_living(mob/living/target, value) + target.emote_sound_mode = value + +/datum/preference/choiced/living/emote_sound_mode/create_informed_default_value(datum/preferences/preferences) + return EMOTE_SOUND_VOICE_FREQ diff --git a/code/modules/client/preferences/types/game/sound.dm b/code/modules/client/preferences/types/game/sound.dm index 405588c74a..09e87cecf9 100644 --- a/code/modules/client/preferences/types/game/sound.dm +++ b/code/modules/client/preferences/types/game/sound.dm @@ -148,3 +148,102 @@ /datum/preference/numeric/ambience_chance/create_default_value() return 35 + +// Volume channels +/datum/preference/volume_channels + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "volume_channels" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/volume_channels/pref_deserialize(input, datum/preferences/preferences) + if(!islist(input)) + return list() + + for(var/channel in input) + if(!(channel in GLOB.all_volume_channels)) + // Channel no longer exists, yeet + input -= channel + + for(var/channel in GLOB.all_volume_channels) + if(!(channel in input)) + input["[channel]"] = 1 + else + input["[channel]"] = clamp(input["[channel]"], 0, 2) + + return input + +/datum/preference/volume_channels/is_valid(value) + return islist(value) + + +/mob/proc/get_preference_volume_channel(volume_channel) + if(!client) + return 0 + return client.get_preference_volume_channel(volume_channel) + +/client/proc/get_preference_volume_channel(volume_channel) + if(!volume_channel || !prefs) + return 1 + + var/list/volume_channels = prefs.read_preference(/datum/preference/volume_channels) + return volume_channels["[volume_channel]"] + +// Neat little volume adjuster thing in case you don't wanna touch preferences by hand you lazy fuck +/datum/volume_panel +/datum/volume_panel/tgui_state(mob/user) + return GLOB.tgui_always_state + +/datum/volume_panel/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "VolumePanel", "Volume Panel") + ui.open() + +/datum/volume_panel/tgui_data(mob/user) + if(!user.client || !user.client.prefs) + return list("error" = TRUE) + + var/list/data = ..() + data["volume_channels"] = user.client.prefs.read_preference(/datum/preference/volume_channels) + return data + +/datum/volume_panel/tgui_act(action, params, datum/tgui/ui) + if(..()) + return TRUE + + if(!ui.user?.client?.prefs) + return TRUE + + var/datum/preferences/P = ui.user.client.prefs + var/list/volume_channels = P.read_preference(/datum/preference/volume_channels) + + switch(action) + if("adjust_volume") + var/channel = params["channel"] + if(channel in volume_channels) + volume_channels["[channel]"] = clamp(params["vol"], 0, 2) + P.write_preference_by_type(/datum/preference/volume_channels, volume_channels) + return TRUE + +/client/verb/volume_panel() + set name = "Volume Panel" + set category = "Preferences.Sounds" + set desc = "Allows you to adjust volume levels on the fly." + + if(!volume_panel) + volume_panel = new(src) + + volume_panel.tgui_interact(mob) + +// Jukebox volume +/datum/preference/numeric/living/jukebox_volume + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "media_volume" + savefile_identifier = PREFERENCE_PLAYER + + minimum = 0 + maximum = 100 + step = 1 + +/datum/preference/numeric/living/jukebox_volume/apply_to_client_updated(client/client, value) + client?.media?.update_volume(value) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index d8d947e0ae..80e60ee123 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1,5 +1,5 @@ #define SAVEFILE_VERSION_MIN 8 -#define SAVEFILE_VERSION_MAX 17 +#define SAVEFILE_VERSION_MAX 18 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -89,6 +89,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car log_debug("[client_ckey] preferences successfully migrated from [current_version] to v17.") to_chat(client, span_danger("v17 savefile migration complete.")) + // Migration for jukebox volume from 0-1 to 0-100 + if(current_version < 18) + log_debug("[client_ckey] preferences migrating from [current_version] to v18....") + to_chat(client, span_danger("Migrating savefile from version [current_version] to v18...")) + + migration_18_jukebox(S) + + log_debug("[client_ckey] preferences successfully migrated from [current_version] to v18.") + to_chat(client, span_danger("v18 savefile migration complete.")) /datum/preferences/proc/update_character(current_version, list/save_data) // Migration from BYOND savefiles to JSON: Important milemark. if(current_version == -3) diff --git a/code/modules/client/preferences_tgui.dm b/code/modules/client/preferences_tgui.dm index 83e752d337..839a1107a7 100644 --- a/code/modules/client/preferences_tgui.dm +++ b/code/modules/client/preferences_tgui.dm @@ -1,9 +1,17 @@ +/datum/preferences + COOLDOWN_DECLARE(ui_refresh_cooldown) + /datum/preferences/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, custom_state) + if(!char_render_holders) + update_preview_icon() + show_character_previews() + ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "PreferencesMenu", "Preferences") ui.set_autoupdate(FALSE) ui.open() + CallAsync(src, PROC_REF(jiggle_map)) /datum/preferences/tgui_state(mob/user) return GLOB.tgui_always_state @@ -13,6 +21,7 @@ /datum/preferences/ui_assets(mob/user) var/list/assets = list( + get_asset_datum(/datum/asset/simple/preferences), get_asset_datum(/datum/asset/spritesheet/preferences), get_asset_datum(/datum/asset/json/preferences), ) @@ -32,6 +41,7 @@ data["character_preferences"] = compile_character_preferences(user) data["active_slot"] = default_slot + data["saved_notification"] = saved_notification for(var/datum/preference_middleware/preference_middleware as anything in middleware) data += preference_middleware.get_ui_data(user) @@ -45,6 +55,7 @@ // data["character_preview_view"] = character_preview_view.assigned_map // data["overflow_role"] = SSjob.GetJobType(SSjob.overflow_role).title + data["window"] = current_window for(var/datum/preference_middleware/preference_middleware as anything in middleware) @@ -57,6 +68,10 @@ if(.) return + . = bay_act(action, params, ui, state) + if(.) + return + switch(action) if("set_preference") var/requested_preference_key = params["preference"] @@ -108,12 +123,74 @@ return TRUE for(var/datum/preference_middleware/preference_middleware as anything in middleware) - var/delegation = preference_middleware.action_delegations[action] - if(!isnull(delegation)) - return call(preference_middleware, delegation)(params, ui.user) + . = preference_middleware.tgui_act(action, params, ui, state) + if(.) + return return FALSE +/// Actions pertaining to the old bay system +/datum/preferences/proc/bay_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = FALSE + + switch(action) + // Basic actions + if("load") + if(!IsGuestKey(usr.key)) + open_load_dialog(usr) + . = TRUE + if("save") + save_character() + save_preferences() + saved_notification = TRUE + VARSET_IN(src, saved_notification, FALSE, 1 SECONDS) + . = TRUE + if("reload") + load_preferences() + load_character() + attempt_vr(client.prefs_vr,"load_vore","") //VOREStation Edit + sanitize_preferences() + . = TRUE + if("resetslot") + if("Yes" != tgui_alert(usr, "This will reset the current slot. Continue?", "Reset current slot?", list("No", "Yes"))) + return + if("Yes" != tgui_alert(usr, "Are you completely sure that you want to reset this character slot?", "Reset current slot?", list("No", "Yes"))) + return + reset_slot() + sanitize_preferences() + . = TRUE + if("copy") + if(!IsGuestKey(usr.key)) + open_copy_dialog(usr) + . = TRUE + // More specific stuff + if("switch_category") + var/new_category = params["category"] + for(var/datum/category_group/player_setup_category/PS in player_setup.categories) + if(PS.name == new_category) + player_setup.selected_category = PS + update_tgui_static_data(usr, ui) + break + . = TRUE + if("game_prefs") + usr.client.game_options() + . = TRUE + if("refresh_character_preview") + if(!COOLDOWN_FINISHED(src, ui_refresh_cooldown)) + return + update_preview_icon() + COOLDOWN_START(src, ui_refresh_cooldown, 5 SECONDS) + CallAsync(src, PROC_REF(jiggle_map)) + . = TRUE + +/datum/preferences/proc/jiggle_map() + // Fix for weird byond bug, jiggles the map around a little + var/obj/screen/setup_preview/pm_helper/PMH = LAZYACCESS(char_render_holders, "PMH") + sleep(0.1 SECONDS) + PMH.screen_loc = LAZYACCESS(preview_screen_locs, "PMHjiggle") + sleep(0.1 SECONDS) + PMH.screen_loc = LAZYACCESS(preview_screen_locs, "PMH") + /datum/preferences/tgui_close(mob/user) save_character() save_preferences() diff --git a/code/modules/emotes/custom_emote.dm b/code/modules/emotes/custom_emote.dm index d64c7652be..53472bcdd1 100644 --- a/code/modules/emotes/custom_emote.dm +++ b/code/modules/emotes/custom_emote.dm @@ -79,7 +79,13 @@ if(!T) return if(client) - playsound(T, pick(GLOB.emote_sound), 75, TRUE, falloff = 1 , is_global = TRUE, frequency = voice_freq, ignore_walls = TRUE, preference = /datum/preference/toggle/emote_sounds) + switch(emote_sound_mode) + if(EMOTE_SOUND_NO_FREQ) + playsound(T, pick(GLOB.emote_sound), 75, TRUE, falloff = 1 , is_global = TRUE, frequency = 0, ignore_walls = TRUE, preference = /datum/preference/toggle/emote_sounds) + if(EMOTE_SOUND_VOICE_FREQ) + playsound(T, pick(GLOB.emote_sound), 75, TRUE, falloff = 1 , is_global = TRUE, frequency = voice_freq, ignore_walls = TRUE, preference = /datum/preference/toggle/emote_sounds) + if(EMOTE_SOUND_VOICE_LIST) + playsound(T, pick(voice_sounds_list), 75, TRUE, falloff = 1 , is_global = TRUE, frequency = voice_freq, ignore_walls = TRUE, preference = /datum/preference/toggle/emote_sounds) var/list/in_range = get_mobs_and_objs_in_view_fast(T,range,2,remote_ghosts = client ? TRUE : FALSE) var/list/m_viewers = in_range["mobs"] diff --git a/code/modules/media/mediamanager.dm b/code/modules/media/mediamanager.dm index 0717fb639a..58e9e81529 100644 --- a/code/modules/media/mediamanager.dm +++ b/code/modules/media/mediamanager.dm @@ -122,15 +122,9 @@ /datum/media_manager/proc/open() if(!owner.prefs) return - if(isnum(owner.prefs.media_volume)) - volume = owner.prefs.media_volume - switch(owner.prefs.media_player) - if(0) - playerstyle = PLAYER_VLC_HTML - if(1) - playerstyle = PLAYER_WMP_HTML - if(2) - playerstyle = PLAYER_HTML5_HTML + if(isnum(owner.prefs.read_preference(/datum/preference/numeric/living/jukebox_volume))) + volume = owner.prefs.read_preference(/datum/preference/numeric/living/jukebox_volume) / 100 + playerstyle = PLAYER_HTML5_HTML // we're in the 516 era baby owner << browse(null, "window=[WINDOW_ID]") owner << browse(playerstyle, "window=[WINDOW_ID]") send_update() diff --git a/code/modules/mob/living/carbon/human/descriptors/_descriptors.dm b/code/modules/mob/living/carbon/human/descriptors/_descriptors.dm deleted file mode 100644 index 44e58d41aa..0000000000 --- a/code/modules/mob/living/carbon/human/descriptors/_descriptors.dm +++ /dev/null @@ -1,108 +0,0 @@ -/* - Small, mechanically supported physical customisation options. - Also allows for per-species physical information ('his neck markings are more important than yours'). - ETA till a downstream ports this and adds boob and penis size: 2 days. -*/ - -/mob/living/carbon/human/proc/show_descriptors_to(var/mob/user) - if(LAZYLEN(descriptors)) - if(user == src) - for(var/entry in descriptors) - var/datum/mob_descriptor/descriptor = species.descriptors[entry] - LAZYADD(., "[descriptor.get_first_person_message_start()] [descriptor.get_standalone_value_descriptor(descriptors[entry])].") - else - for(var/entry in descriptors) - var/datum/mob_descriptor/descriptor = species.descriptors[entry] - LAZYADD(., descriptor.get_comparative_value_descriptor(descriptors[entry], user, src)) - -/datum/mob_descriptor - var/name // String ident. - var/chargen_label // String ident for chargen. - var/default_value // Initial value for this descriptor. - var/comparison_offset = 0 // Used for examining similar properties between different species. - var/comparative_value_descriptor_equivalent // String for looking at someone with roughly the same property. - var/list/standalone_value_descriptors // String set for initial descriptor text. - var/list/comparative_value_descriptors_smaller // String set for looking at someone smaller than you. - var/list/comparative_value_descriptors_larger // String set for looking at someone larger than you. - var/list/chargen_value_descriptors // Used for chargen selection of values in cases where there is a hidden meaning. - var/skip_species_mention - -/datum/mob_descriptor/New() - if(!chargen_label) - chargen_label = name - if(!chargen_value_descriptors) - chargen_value_descriptors = list() - for(var/i = 1 to LAZYLEN(standalone_value_descriptors)) - chargen_value_descriptors[standalone_value_descriptors[i]] = i - default_value = CEILING(LAZYLEN(standalone_value_descriptors) * 0.5, 1) - ..() - -/datum/mob_descriptor/proc/get_third_person_message_start(var/datum/gender/my_gender) - return "They are" -// return "[my_gender.He] [my_gender.is]" // Doesn't respect ambiguous_genders species var, can't figure out a fix at the moment - -/datum/mob_descriptor/proc/get_first_person_message_start() - return "You are" - -/datum/mob_descriptor/proc/get_standalone_value_descriptor(var/check_value) - if(isnull(check_value)) - check_value = default_value - if(check_value && LAZYLEN(standalone_value_descriptors) >= check_value) - return standalone_value_descriptors[check_value] - -// Build a species-specific descriptor string. -/datum/mob_descriptor/proc/get_initial_comparison_component(var/mob/me, var/datum/gender/my_gender, var/datum/gender/other_gender, var/my_value) - var/species_text - if(ishuman(me) && !skip_species_mention) - var/mob/living/carbon/human/H = me - var/use_name = "\improper [H.species.name]" - species_text = " for \a [use_name]" - . = "[get_third_person_message_start(my_gender)] [get_standalone_value_descriptor(my_value)][species_text]" - -/datum/mob_descriptor/proc/get_secondary_comparison_component(var/datum/gender/my_gender, var/datum/gender/other_gender, var/my_value, var/comparing_value) - var/raw_value = my_value - my_value += comparison_offset - var/variance = abs((my_value)-comparing_value) - if(variance < 1) - . = "[.], [get_comparative_value_string_equivalent(raw_value, my_gender, other_gender)]" - else - variance = variance / LAZYLEN(standalone_value_descriptors) - if(my_value < comparing_value) - . = "[.], [get_comparative_value_string_smaller(variance, my_gender, other_gender)]" - else if(my_value > comparing_value) - . = "[.], [get_comparative_value_string_larger(variance, my_gender, other_gender)]" - -/datum/mob_descriptor/proc/get_comparative_value_descriptor(var/my_value, var/mob/observer, var/mob/me) - - // Store our gender info for later. - var/datum/gender/my_gender = GLOB.gender_datums[me.get_gender()] - var/datum/gender/other_gender = GLOB.gender_datums[observer.get_gender()] - - . = get_initial_comparison_component(me, my_gender, other_gender, my_value) - - // Append the same-descriptor comparison text. - var/comparing_value - if(ishuman(observer)) - var/mob/living/carbon/human/human_observer = observer - if(LAZYLEN(human_observer.descriptors) && !isnull(human_observer.species.descriptors[name]) && !isnull(human_observer.descriptors[name])) - var/datum/mob_descriptor/obs_descriptor = human_observer.species.descriptors[name] - comparing_value = human_observer.descriptors[name] + obs_descriptor.comparison_offset - - if(. && !isnull(comparing_value)) - . = "[.][get_secondary_comparison_component(my_gender, other_gender, my_value, comparing_value)]" - - // We're done, add a full stop. - . = "[.]. " - -/datum/mob_descriptor/proc/get_comparative_value_string_equivalent(var/my_value, var/datum/gender/my_gender, var/datum/gender/other_gender) - return comparative_value_descriptor_equivalent - -/datum/mob_descriptor/proc/get_comparative_value_string_smaller(var/value, var/datum/gender/my_gender, var/datum/gender/other_gender) - var/maxval = LAZYLEN(comparative_value_descriptors_smaller) - value = CLAMP(CEILING(value * maxval, 1), 1, maxval) - return comparative_value_descriptors_smaller[value] - -/datum/mob_descriptor/proc/get_comparative_value_string_larger(var/value, var/datum/gender/my_gender, var/datum/gender/other_gender) - var/maxval = LAZYLEN(comparative_value_descriptors_larger) - value = CLAMP(CEILING(value * maxval, 1), 1, maxval) - return comparative_value_descriptors_larger[value] diff --git a/code/modules/mob/living/carbon/human/descriptors/descriptors_generic.dm b/code/modules/mob/living/carbon/human/descriptors/descriptors_generic.dm deleted file mode 100644 index 934e7c79bc..0000000000 --- a/code/modules/mob/living/carbon/human/descriptors/descriptors_generic.dm +++ /dev/null @@ -1,45 +0,0 @@ -/datum/mob_descriptor/height - name = "height" - standalone_value_descriptors = list( - "very short", - "short", - "of average height", - "tall", - "very tall" - ) - comparative_value_descriptor_equivalent = "around the same height as you" - comparative_value_descriptors_smaller = list( - "slightly shorter than you", - "shorter than you", - "much shorter than you", - "tiny and insignificant next to you" - ) - comparative_value_descriptors_larger = list( - "slightly taller than you", - "taller than you", - "much taller than you", - "towering over you" - ) - -/datum/mob_descriptor/build - name = "build" - comparative_value_descriptor_equivalent = "around the same build as you" - standalone_value_descriptors = list( - "rail thin", - "thin", - "of average build", - "broad-shouldered", - "heavily built" - ) - comparative_value_descriptors_smaller = list( - "a bit smaller in build than you", - "smaller in build than you", - "much smaller in build than you", - "dwarfed by your height" - ) - comparative_value_descriptors_larger = list( - "slightly larger than you in build", - "built larger than you", - "built much larger than you", - "dwarfing you" - ) diff --git a/code/modules/mob/living/carbon/human/descriptors/descriptors_skrell.dm b/code/modules/mob/living/carbon/human/descriptors/descriptors_skrell.dm deleted file mode 100644 index 6414430417..0000000000 --- a/code/modules/mob/living/carbon/human/descriptors/descriptors_skrell.dm +++ /dev/null @@ -1,27 +0,0 @@ -/datum/mob_descriptor/headtail_length - name = "headtail length" - chargen_label = "headtails (gender)" - skip_species_mention = TRUE - standalone_value_descriptors = list( - "short", - "long" - ) - chargen_value_descriptors = list( - "short (male)" = 1, - "long (female)" = 2 - ) - -/datum/mob_descriptor/headtail_length/get_first_person_message_start() - . = "Your headtails are" - -/datum/mob_descriptor/headtail_length/get_third_person_message_start(var/datum/gender/my_gender) - . = "[my_gender.His] headtails are" - -/datum/mob_descriptor/headtail_length/get_comparative_value_string_equivalent(var/my_value, var/datum/gender/my_gender, var/datum/gender/other_gender) - . = "indicating [other_gender.he] [other_gender.is] [my_value == 1 ? "male" : "female"] like you" - -/datum/mob_descriptor/headtail_length/get_comparative_value_string_smaller(var/value, var/datum/gender/my_gender, var/datum/gender/other_gender) - . = "indicating [other_gender.he] [other_gender.is] male" - -/datum/mob_descriptor/headtail_length/get_comparative_value_string_larger(var/value, var/datum/gender/my_gender, var/datum/gender/other_gender) - . = "indicating [other_gender.he] [other_gender.is] female" diff --git a/code/modules/mob/living/carbon/human/descriptors/descriptors_vox.dm b/code/modules/mob/living/carbon/human/descriptors/descriptors_vox.dm deleted file mode 100644 index fa007fdaa5..0000000000 --- a/code/modules/mob/living/carbon/human/descriptors/descriptors_vox.dm +++ /dev/null @@ -1,35 +0,0 @@ -/datum/mob_descriptor/vox_markings - name = "neck markings" - chargen_label = "neck markings (rank)" - skip_species_mention = TRUE - standalone_value_descriptors = list( - "very simplistic", - "rather simple", - "complex", - "moderately complex", - "bewilderingly complex" - ) - chargen_value_descriptors = list( - "servitor" = 1, - "labourer" = 2, - "cannon fodder" = 3, - "raider" = 4, - "leader" = 5 - ) - comparative_value_descriptor_equivalent = "around the same importance as yours" - comparative_value_descriptors_smaller = list( - "slightly less important than yours", - "much less important than yours", - "insignificant and beneath your notice" - ) - comparative_value_descriptors_larger = list( - "slightly more important than yours", - "much more important than yours", - "commanding your unquestioning obedience and respect" - ) - -/datum/mob_descriptor/vox_markings/get_first_person_message_start() - . = "Your neck markings are" - -/datum/mob_descriptor/vox_markings/get_third_person_message_start(var/datum/gender/my_gender) - . = "[my_gender.His] neck markings are" diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index b3e8f46840..b43a5da4fe 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -454,10 +454,6 @@ if(applying_pressure) msg += applying_pressure - var/show_descs = show_descriptors_to(user) - if(show_descs) - msg += span_notice("[jointext(show_descs, "
")]") - if(pose) if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] pose = addtext(pose,".") //Makes sure all emotes end with a period. diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 9915e1017d..f36597f2e8 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1321,15 +1321,6 @@ pixel_y = default_pixel_y center_offset = species.center_offset - if(LAZYLEN(descriptors)) - descriptors = null - - if(LAZYLEN(species.descriptors)) - descriptors = list() - for(var/desctype in species.descriptors) - var/datum/mob_descriptor/descriptor = species.descriptors[desctype] - descriptors[desctype] = descriptor.default_value - if(vessel) initialize_vessel() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 6243b54459..805f9e044d 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -103,8 +103,6 @@ var/identifying_gender // In case the human identifies as another gender than it's biological - var/list/descriptors // For comparative examine code - var/step_count = 0 // Track how many footsteps have been taken to know when to play footstep sounds can_be_antagged = TRUE diff --git a/code/modules/mob/living/carbon/human/human_helpers_vr.dm b/code/modules/mob/living/carbon/human/human_helpers_vr.dm index 6cab0b6e35..361938568a 100644 --- a/code/modules/mob/living/carbon/human/human_helpers_vr.dm +++ b/code/modules/mob/living/carbon/human/human_helpers_vr.dm @@ -236,8 +236,6 @@ var/static/icon/ingame_hud_med_vr = icon('icons/mob/hud_med_vr.dmi') markings_len = character.markings_len - descriptors = character.descriptors?.Copy() - if (copy_flavour) flavor_texts = character.flavor_texts?.Copy() diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 0e7cad0642..42ca30d9cd 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -302,8 +302,6 @@ var/pass_flags = 0 - var/list/descriptors = list() - //This is used in character setup preview generation (prefences_setup.dm) and human mob //rendering (update_icons.dm) var/color_mult = 0 @@ -385,15 +383,6 @@ else hud = new() - // Prep the descriptors for the species - if(LAZYLEN(descriptors)) - var/list/descriptor_datums = list() - for(var/desctype in descriptors) - var/datum/mob_descriptor/descriptor = new desctype - descriptor.comparison_offset = descriptors[desctype] - descriptor_datums[descriptor.name] = descriptor - descriptors = descriptor_datums - //If the species has eyes, they are the default vision organ if(!vision_organ && has_organ[O_EYES]) vision_organ = O_EYES diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index f01ebc118c..8f5516e167 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -405,7 +405,6 @@ if(ticker.random_players) new_character.gender = pick(MALE, FEMALE) client.prefs.real_name = random_name(new_character.gender) - client.prefs.randomize_appearance_and_body_for(new_character) else client.prefs.copy_to(new_character, icon_updates = TRUE) diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 0a59aa6482..58eebed466 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -33,37 +33,6 @@ write_preference(preference, preference.create_random_value(src)) */ - //The mob should have a gender you want before running this proc. Will run fine without H -/datum/preferences/proc/randomize_appearance_and_body_for(var/mob/living/carbon/human/H) - var/datum/species/current_species = GLOB.all_species[species ? species : "Human"] - set_biological_gender(pick(current_species.genders)) - - h_style = random_hair_style(biological_gender, species) - f_style = random_facial_hair_style(biological_gender, species) - if(current_species) - if(current_species.appearance_flags & HAS_SKIN_TONE) - s_tone = random_skin_tone() - if(current_species.appearance_flags & HAS_SKIN_COLOR) - update_preference_by_type(/datum/preference/color/human/skin_color, rgb(rand(0, 255), rand(0, 255), rand(0, 255))) - if(current_species.appearance_flags & HAS_EYE_COLOR) - randomize_eyes_color() - if(current_species.appearance_flags & HAS_HAIR_COLOR) - randomize_hair_color("hair") - randomize_hair_color("facial") - if(current_species.appearance_flags & HAS_UNDERWEAR) - all_underwear.Cut() - for(var/datum/category_group/underwear/WRC in global_underwear.categories) - var/datum/category_item/underwear/WRI = pick(WRC.items) - all_underwear[WRC.name] = WRI.name - - - headset = rand(1,3) - backbag = rand(1,6) - pdachoice = rand(1,7) - randomise_appearance_prefs_update(current_species = current_species) - b_type = RANDOM_BLOOD_TYPE - if(H) - copy_to(H,1) /datum/preferences/proc/randomize_hair_color(var/target = "hair") @@ -242,7 +211,8 @@ if((equip_preview_mob & EQUIP_PREVIEW_LOADOUT) && !(previewJob && (equip_preview_mob & EQUIP_PREVIEW_JOB) && (previewJob.type == /datum/job/ai || previewJob.type == /datum/job/cyborg))) var/list/equipped_slots = list() - for(var/thing in gear) + var/list/active_gear_list = LAZYACCESS(gear_list, "[gear_slot]") + for(var/thing in active_gear_list) var/datum/gear/G = gear_datums[thing] if(G) var/permitted = 0 @@ -262,7 +232,7 @@ continue if(G.slot && !(G.slot in equipped_slots)) - var/metadata = gear[G.display_name] + var/metadata = active_gear_list[G.display_name] if(mannequin.equip_to_slot_or_del(G.spawn_item(mannequin, metadata), G.slot)) if(G.slot != slot_tie) equipped_slots += G.slot diff --git a/code/modules/mob/new_player/preferences_setup_vr.dm b/code/modules/mob/new_player/preferences_setup_vr.dm deleted file mode 100644 index 4de77e3f4b..0000000000 --- a/code/modules/mob/new_player/preferences_setup_vr.dm +++ /dev/null @@ -1,2 +0,0 @@ -/datum/preferences/randomize_appearance_and_body_for(var/mob/living/carbon/human/H) - sensorpref = rand(1,5) diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 880854585f..5395da0d92 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -79,8 +79,6 @@ H.set_species(randomize) H.universal_speak = 1 - var/datum/preferences/A = new() //Randomize appearance for the human - A.randomize_appearance_and_body_for(H) if(new_mob) for (var/spell/S in M.spell_list) diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index 2c3b439fb7..521a995252 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -767,7 +767,7 @@ data["digitigrade"] = owner.digitigrade data["blood_reagent"] = owner.dna.blood_reagents data["blood_color"] = owner.dna.blood_color - data["species_sound"] = owner.species.species_sounds //TODO: RAISE UP FROM CHOMP + data["species_sound"] = owner.species.species_sounds // Are these needed? It seems to be only used if above is unset?? data["species_sounds_gendered"] = owner.species.gender_specific_species_sounds data["species_sounds_female"] = owner.species.species_sounds_female diff --git a/code/modules/vore/eating/mob_vr.dm b/code/modules/vore/eating/mob_vr.dm index 145ac4edae..4172b1e3d2 100644 --- a/code/modules/vore/eating/mob_vr.dm +++ b/code/modules/vore/eating/mob_vr.dm @@ -89,6 +89,7 @@ var/soulcatcher_pref_flags = 0 //Default disabled var/voice_freq = 42500 // Preference for character voice frequency + var/emote_sound_mode = EMOTE_SOUND_VOICE_FREQ var/list/voice_sounds_list = list() // The sound list containing our voice sounds! var/enabled = TRUE //Pauses a mob if disabled (Prevents life ticks from happening) var/died_in_vr = FALSE //For virtual reality sleepers diff --git a/code/modules/vore/persist/persist_vr.dm b/code/modules/vore/persist/persist_vr.dm index f8eea79612..d056f058cc 100644 --- a/code/modules/vore/persist/persist_vr.dm +++ b/code/modules/vore/persist/persist_vr.dm @@ -265,6 +265,6 @@ // If they still have the same character loaded, update prefs if(H?.client?.prefs?.default_slot == slot) - var/datum/category_group/player_setup_category/vore_cat = H.client.prefs.player_setup.categories_by_name["VORE"] - var/datum/category_item/player_setup_item/vore/nif/nif_prefs = vore_cat.items_by_name["NIF Data"] + var/datum/category_group/player_setup_category/vore_cat = H.client.prefs.player_setup.categories_by_name["General"] + var/datum/category_item/player_setup_item/general/nif/nif_prefs = vore_cat.items_by_name["NIF Data"] nif_prefs.load_character() diff --git a/icons/mob/human_races/preview.dmi b/icons/mob/human_races/preview.dmi new file mode 100644 index 0000000000..87e5c5e4a9 Binary files /dev/null and b/icons/mob/human_races/preview.dmi differ diff --git a/icons/mob/human_races/preview_custom_animation.gif b/icons/mob/human_races/preview_custom_animation.gif new file mode 100644 index 0000000000..21db5dec08 Binary files /dev/null and b/icons/mob/human_races/preview_custom_animation.gif differ diff --git a/icons/mob/human_races/preview_protean_animation.gif b/icons/mob/human_races/preview_protean_animation.gif new file mode 100644 index 0000000000..1277c5b6dd Binary files /dev/null and b/icons/mob/human_races/preview_protean_animation.gif differ diff --git a/interface/skin.dmf b/interface/skin.dmf index 2c9c24b7be..3d0b598247 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1513,32 +1513,6 @@ window "rpane" is-visible = false saved-params = "" -window "preferences_window" - elem "preferences_window" - type = MAIN - pos = 281,0 - size = 1000x800 - anchor1 = -1,-1 - anchor2 = -1,-1 - is-visible = false - saved-params = "pos;size;is-minimized;is-maximized" - statusbar = false - elem "preferences_browser" - type = BROWSER - pos = 0,0 - size = 800x800 - anchor1 = 0,0 - anchor2 = 80,100 - saved-params = "" - elem "character_preview_map" - type = MAP - pos = 800,0 - size = 200x800 - anchor1 = 80,0 - anchor2 = 100,100 - right-click = true - saved-params = "zoom;letterbox;zoom-mode" - window "ooc_notes" elem "ooc_notes" type = MAIN diff --git a/tgui/packages/tgui/interfaces/CharacterSetup/index.tsx b/tgui/packages/tgui/interfaces/CharacterSetup/index.tsx new file mode 100644 index 0000000000..586976fb9d --- /dev/null +++ b/tgui/packages/tgui/interfaces/CharacterSetup/index.tsx @@ -0,0 +1,21 @@ +/* eslint-disable react/no-danger */ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; + +type Data = { + header: string; + content: string; +}; + +export const CharacterSetup = (props) => { + const { act, data } = useBackend(); + + return ( + + +
+
+ + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx new file mode 100644 index 0000000000..2c9a16272e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx @@ -0,0 +1,141 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Box, Button, ByondUi, Stack } from 'tgui-core/components'; +import { type BooleanLike } from 'tgui-core/react'; + +import { BayPrefsEntryPoint } from './bay_prefs'; + +type Data = { + categories: string[]; + selected_category: { + name: string; + items: any; + }; + selected_category_static: any; + + saved_notification: BooleanLike; + preview_loadout: BooleanLike; + preview_job_gear: BooleanLike; + preview_animations: BooleanLike; +}; + +export const CharacterPreferenceWindow = (props) => { + const { act, data } = useBackend(); + + const { + categories, + selected_category, + selected_category_static, + preview_loadout, + preview_job_gear, + preview_animations, + saved_notification, + } = data; + + return ( + { + Byond.winset(Byond.windowId, { + 'is-maximized': !(await Byond.winget( + Byond.windowId, + 'is-maximized', + )), + }); + }} + /> + } + > + + + + {saved_notification ? ( + + Saved! + + ) : null} +
+ + + + + + + + + {categories.map((category) => ( + + ))} + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx index 02ac0fbf6a..6d3b98fd52 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx @@ -1,94 +1,68 @@ -import { - Component, - type ComponentProps, - createRef, - type ReactNode, - type RefObject, -} from 'react'; -import { Button, Section, Stack } from 'tgui-core/components'; +import { type ComponentProps, type ReactNode, useRef } from 'react'; +import { Button, type Flex, Section, Stack } from 'tgui-core/components'; type TabbedMenuProps = { categoryEntries: [string, ReactNode][]; - contentProps?: ComponentProps; + contentProps?: ComponentProps; }; -export class TabbedMenu extends Component { - categoryRefs: Record> = {}; - sectionRef: RefObject = createRef(); +export function TabbedMenu(props: TabbedMenuProps) { + const sectionRef = useRef(null); + const categoryRefs = useRef>({}); - getCategoryRef(category: string): RefObject { - if (!this.categoryRefs[category]) { - this.categoryRefs[category] = createRef(); - } + return ( + + + + {props.categoryEntries.map(([category]) => ( + + + + ))} + + - if (offsetTop === undefined) { - return; - } - - const currentSection = this.sectionRef.current; - - if (!currentSection) { - return; - } - - currentSection.scrollTop = offsetTop; - }} - > - {category} - - - ); - })} - - - - - - {this.props.categoryEntries.map(([category, children]) => { - return ( - -
- {children} -
-
- ); - })} -
-
- - ); - } + + + {props.categoryEntries.map(([category, children]) => ( +
{ + categoryRefs.current[category] = ref; + }} + > +
+ {children} +
+
+ ))} +
+
+ + ); } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/data.ts new file mode 100644 index 0000000000..1b81ab74a7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/data.ts @@ -0,0 +1,7 @@ +import { type GeneralDataConstant } from './general/data'; +import { type LoadoutDataConstant } from './loadout/data'; +import { type OccupationDataConstant } from './occupation/data'; + +export type LegacyConstant = GeneralDataConstant & + LoadoutDataConstant & + OccupationDataConstant; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabBody.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabBody.tsx new file mode 100644 index 0000000000..374090ad7d --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabBody.tsx @@ -0,0 +1,577 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + ColorBox, + Icon, + LabeledList, + Stack, +} from 'tgui-core/components'; + +import { CustomImageButton } from '../helper_components'; +import { EarsDimmer, EarsImageButton } from './body/Ears'; +import { EarsSecondaryDimmer } from './body/EarsSecondary'; +import { FacialDimmer, FacialImageButton } from './body/Facial'; +import { GradientDimmer, GradientImageButton } from './body/Gradient'; +import { HairDimmer, HairImageButton } from './body/Hair'; +import { MarkingsPopup } from './body/Markings'; +import { TailDimmer, TailImageButton } from './body/Tail'; +import { WingsDimmer, WingsImageButton } from './body/Wings'; +import { + AppearanceFlags, + BodypartFlags, + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + is_organ, + OrganStatus, + proper_organ_name, + type Species, +} from './data'; + +export const SubtabBody = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { data, staticData, serverData } = props; + const { + species, + eyes_color, + skin_color, + s_tone, + b_type, + digitigrade, + synth_color, + synth_color_toggle, + synth_markings, + voice_freq, + voice_sound, + blood_color, + blood_reagents, + emote_sound_mode, + } = data; + const { digi_allowed } = staticData; + const { species: species_list } = serverData; + // if it's not there our entire UI is fucked anyways + const our_species = species_list.find((x) => x.name === species) as Species; + const { act } = useBackend(); + + const [visiblePopup, setVisiblePopup] = useState(BodyPopup.None); + + if (visiblePopup !== BodyPopup.None) { + return ( + + ); + } + + return ( + + + + + + + + + + + + + + {our_species.appearance_flags & + AppearanceFlags.HAS_EYE_COLOR ? ( + + + {' '} + + ) : null} + + {our_species.appearance_flags & + AppearanceFlags.HAS_SKIN_TONE ? ( + + {s_tone}/220 + + + ) : null} + {our_species.appearance_flags & + AppearanceFlags.HAS_SKIN_COLOR ? ( + + + + + ) : null} + {digi_allowed ? ( + + + + ) : null} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Synthetic Options + + + + + + + + + {synth_color_toggle ? ( + + {' '} + + + ) : null} + + + +
+
+
+
+
+ ); +}; + +const SizePrefs = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; + setVisiblePopup: React.Dispatch>; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData, setVisiblePopup } = props; + const { + size_multiplier, + fuzzy, + offset_override, + weight_vr, + weight_gain, + weight_loss, + } = data; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +const BodyBuilder = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; + setVisiblePopup: React.Dispatch>; +}) => { + const { data, staticData, serverData, setVisiblePopup } = props; + + return ( + + + setVisiblePopup(BodyPopup.Ears)} + tooltip={data.ear_style || 'None'} + > + Ears + + setVisiblePopup(BodyPopup.Ears2)} + tooltip={data.ear_secondary_style || 'None'} + > + Horns + + + + setVisiblePopup(BodyPopup.Wings)} + tooltip={data.wing_style} + > + Wings + + setVisiblePopup(BodyPopup.Hair)} + tooltip={data.h_style} + > + Hair + + setVisiblePopup(BodyPopup.Gradient)} + tooltip={data.grad_style} + > + Gradient + + + + setVisiblePopup(BodyPopup.Tail)} + tooltip={data.tail_style} + > + Tail + + setVisiblePopup(BodyPopup.Facial)} + tooltip={data.f_style} + > + Facial + + + + ); +}; + +export enum BodyPopup { + None, + Hair, + Facial, + Gradient, + Ears, + Ears2, + Markings, + Tail, + Wings, +} + +export const BodyPopupDimmer = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; + visiblePopup: BodyPopup; + setVisiblePopup: React.Dispatch>; +}) => { + const { data, staticData, serverData, visiblePopup, setVisiblePopup } = props; + + switch (visiblePopup) { + case BodyPopup.Hair: { + return ( + + ); + } + case BodyPopup.Facial: { + return ( + + ); + } + case BodyPopup.Gradient: { + return ( + + ); + } + case BodyPopup.Ears: { + return ( + + ); + } + case BodyPopup.Ears2: { + return ( + + ); + } + case BodyPopup.Markings: { + return ( + + ); + } + case BodyPopup.Wings: { + return ( + + ); + } + case BodyPopup.Tail: { + return ( + + ); + } + } +}; + +const OrganBuilder = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { data, staticData, serverData } = props; + const { organ_data, rlimb_data } = data; + const { act } = useBackend(); + + return ( + + {Object.values(BodypartFlags).map((flag: BodypartFlags) => { + const status = organ_data[flag]; + const robolimb = rlimb_data[flag] || 'Unbranded'; + + let status_text = 'Normal'; + let icon = 'dna'; + switch (status) { + case OrganStatus.Cyborg: + icon = 'robot'; + status_text = `${robolimb} Prosthesis`; + break; + case OrganStatus.Amputated: + icon = 'bone'; + status_text = 'Amputated'; + break; + case OrganStatus.Mechanical: + icon = 'robot'; + if (flag === BodypartFlags.O_BRAIN) { + status_text = 'Positronic'; + } else { + status_text = 'Synthetic'; + } + break; + case OrganStatus.Digital: + icon = 'wifi'; + status_text = 'Digital'; + break; + case OrganStatus.Assisted: + switch (flag) { + case BodypartFlags.O_HEART: + icon = 'heart-circle-bolt'; + status_text = 'Pacemaker-assisted'; + break; + case BodypartFlags.O_LUNGS: + icon = 'lungs-virus'; + status_text = 'Assisted'; + break; + case BodypartFlags.O_VOICE: + icon = 'phone'; + status_text = 'Surgically Altered'; + break; + case BodypartFlags.O_EYES: + icon = 'eye'; + status_text = 'Retinal overlayed'; + break; + case BodypartFlags.O_BRAIN: + icon = 'brain'; + status_text = 'Assisted-interface'; + break; + default: + icon = 'robot'; + status_text = 'Mechanically assisted'; + break; + } + break; + } + + return ( + } + tooltip={status_text} + onClick={() => + act( + is_organ(flag) + ? 'robolimb_select_organ' + : 'robolimb_select_bodypart', + { zone: flag }, + ) + } + > + + {proper_organ_name(flag)} + + + ); + })} + } + onClick={() => act('reset_limbs')} + > + Reset + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabInfo.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabInfo.tsx new file mode 100644 index 0000000000..129917fe3b --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabInfo.tsx @@ -0,0 +1,283 @@ +import { Fragment } from 'react'; +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + ColorBox, + Divider, + Floating, + LabeledList, + Stack, + Tooltip, +} from 'tgui-core/components'; + +import { + Gender, + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from './data'; + +export const gender2icon = (gender: Gender) => { + switch (gender) { + case Gender.Female: { + return 'venus'; + } + case Gender.Male: { + return 'mars'; + } + case Gender.Plural: { + return 'transgender'; + } + case Gender.Neuter: { + return 'neuter'; + } + } +}; + +export const gender2pronouns = (gender: Gender) => { + switch (gender) { + case Gender.Female: { + return 'She/Her'; + } + case Gender.Male: { + return 'He/Him'; + } + case Gender.Plural: { + return 'They/Them'; + } + case Gender.Neuter: { + return 'It/Its'; + } + } +}; + +export const GenderButton = (props: { + gender: Gender; + usePronouns?: boolean; + setGender: (gender: Gender) => void; +}) => { + return ( + + + {Object.keys(Gender).map((x) => ( + + + + ); +}; + +export const SubtabInfo = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData } = props; + const { + age, + bday_day, + bday_month, + bday_announce, + languages, + languages_can_add, + language_keys, + preferred_language, + runechat_color, + vore_egg_type, + autohiss, + custom_species, + selects_bodytype, + custom_base, + } = data; + + return ( + + + + + + + + + {data.nickname ? ( + + + + / + + + + + + + + + + + + + + + {selects_bodytype ? ( + + + + ) : null} + + + + + + Languages + + + + {languages.map((language) => ( + + - {language.name} + + -{' '} + + + ) : ( +
+ )} + + ))} + + {languages_can_add ? ( + + + + ) : null} + + + + + + Language Keys + {language_keys.map((key) => key + ' ')} + + + Preferred Language + + + + + + + Runechat Color + + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabNotes.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabNotes.tsx new file mode 100644 index 0000000000..30ab7b4cae --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabNotes.tsx @@ -0,0 +1,217 @@ +import { useBackend } from 'tgui/backend'; +import { Box, Button, LabeledList, Stack } from 'tgui-core/components'; + +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + REQUIRED_FLAVOR_TEXT_LENGTH, + REQUIRED_OOC_LENGTH, +} from './data'; + +export const SubtabNotes = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData } = props; + const { + economic_status, + home_system, + birthplace, + citizenship, + faction, + religion, + ooc_note_style, + records_banned, + med_record, + gen_record, + sec_record, + nif, + } = data; + + return ( + + + + + + + Background Information + + + + + + + + + + + + + + + + + + + + + + + + + + {records_banned ? ( + + You are banned from using character records. + + ) : ( + <> + + Medical Records + {' '} + ( + + ) + + + Employment Records + {' '} + ( + + ) + + + Security Records + {' '} + ( + + ) + + + )} + NIF: {nif ? 'Installed' : 'None'} + + + + + + + + Flavor Text + + + + + + + + + + + + + {staticData.allow_metadata ? ( + + OOC Notes + + + + + + + + + + + + + + + + + + + + + + + + + ) : null} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSettings.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSettings.tsx new file mode 100644 index 0000000000..72111d15ee --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSettings.tsx @@ -0,0 +1,530 @@ +import { useBackend } from 'tgui/backend'; +import { Box, Button, LabeledList, Stack } from 'tgui-core/components'; + +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + PersistanceEnum, +} from './data'; + +const extra_desc = (name: string) => { + switch (name) { + case 'Be positronic brain': + return 'This unlocks opportunities to become a positronic borg brain when you are a ghost.'; + case 'Be pAI candidate': + return 'This unlocks opportunities to become a personal AI assistant when you are a ghost.'; + case 'Be lost drone': + return 'This unlocks opportunities to become a spooky lost drone when you are a ghost.'; + case 'Be maint pred': + return 'This unlocks opportunities to become use a maintenance predator spawner.'; + case 'Be maint lurker': + return 'This unlocks opportunities to become use a maintenance mob spawner.'; + case 'Be morph': + return 'This is totally useless, but once allowed you to become a shapeshifting mimic.'; + case 'Be corgi': + return 'This is totally useless, but once allowed you to become a corgi.'; + case 'Be cursed sword': + return 'This is totally useless, but once allowed you to become a cursed sword.'; + case 'Be Ship Survivor': + return 'This is totally useless, but once allowed you to become a survivor mob in a ship crash POI.'; + default: + return null; + } +}; + +export const SubtabSettings = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData } = props; + const { + antag_faction, + antag_vis, + uplink_type, + record_banned, + exploitable_record, + pai_name, + pai_desc, + pai_role, + pai_comments, + syndicate_ban, + special_roles, + custom_footstep, + custom_species_sound, + custom_speech_bubble, + persistence_settings, + show_in_directory, + directory_tag, + directory_gendertag, + directory_sexualitytag, + directory_erptag, + sensorpref, + capture_crystal, + auto_backup_implant, + borg_petting, + resleeve_lock, + resleeve_scan, + mind_scan, + vantag_volunteer, + vantag_preference, + } = data; + + return ( + + + + + + + Antag Settings + + + + + + + + + + + + {record_banned ? ( + You are record-banned. + ) : ( + + )} + + + + + Misc Settings + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Special Roles + {syndicate_ban ? ( + + You are banned from antagonist roles. + + ) : ( + + {special_roles.map((role) => ( + + + + ))} + + )} + + + + + + + + Character Directory + + + + + + + + + + + + + + + + + + + + + + + Custom Text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pAI Settings + + + + + + + + + + + + + + + + + Persistence Settings + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSpecies.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSpecies.tsx new file mode 100644 index 0000000000..0be24d13f1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSpecies.tsx @@ -0,0 +1,264 @@ +import { useState } from 'react'; +import { resolveAsset } from 'tgui/assets'; +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + Dimmer, + DmIcon, + Image, + ImageButton, + Stack, +} from 'tgui-core/components'; +import { type BooleanLike } from 'tgui-core/react'; + +import { + AppearanceFlags, + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + SpawnFlags, + type Species, + SpeciesFlags, +} from './data'; + +const rarity2text = (rarity: number) => { + switch (rarity) { + case 1: + case 2: + return 'Often present on human stations.'; + case 3: + case 4: + return 'Rarely present on human stations.'; + case 5: + return 'Unheard of on human stations.'; + default: + return 'May be present on human stations.'; + } +}; + +const SpeciesImage = (props: { species_name: string }) => { + const { species_name } = props; + if (species_name === 'Custom Species') { + return ( + + ); + } else if (species_name === 'Protean') { + return ( + + ); + } + return ( + + ); +}; + +const SpeciesImageButton = (props: { + species_name: string; + onClick: () => void; + selected: BooleanLike; +}) => { + const { species_name, onClick, selected } = props; + if (species_name === 'Custom Species') { + return ( + + {species_name} + + ); + } else if (species_name === 'Protean') { + return ( + + {species_name} + + ); + } + + return ( + + {species_name} + + ); +}; + +export const SubtabSpecies = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData } = props; + const { species } = serverData; + const { can_play } = staticData; + const [viewingSpecies, setViewingSpecies] = useState(''); + + species.sort((a, b) => a.name.localeCompare(b.name)); + const viewed_species: Species | undefined = species.find( + (x) => x.name === viewingSpecies, + ); + + return ( + + {viewed_species ? ( + + + + + + {viewed_species.name} + + + {/* eslint-disable react/no-danger */} +
+ + + + + + + + + + + + + ) : null} + + {species.map((species) => ( + setViewingSpecies(species.name)} + selected={species.name === data.species} + species_name={species.name} + /> + ))} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabTraits.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabTraits.tsx new file mode 100644 index 0000000000..f5241b448e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabTraits.tsx @@ -0,0 +1,262 @@ +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + ColorBox, + Divider, + LabeledList, + Stack, +} from 'tgui-core/components'; +import { type BooleanLike } from 'tgui-core/react'; + +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + type Trait, + TraitCategory, + TraitPrefType, + type TraitSubpref, +} from './data'; + +function ensureRecord( + trait: string[] | Record, +): Record { + if (Array.isArray(trait)) { + const new_obj: Record = {}; + for (let i = 0; i < trait.length; i++) { + new_obj[trait[i]] = null; + } + return new_obj; + } else { + return trait; + } +} + +export const SubtabTraits = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData } = props; + const { + pos_traits, + neu_traits, + neg_traits, + traits_cheating, + max_traits, + trait_points, + } = data; + const { all_traits } = serverData; + + const pos_traits_object = ensureRecord(pos_traits); + const neu_traits_object = ensureRecord(neu_traits); + const neg_traits_object = ensureRecord(neg_traits); + + const pos_trait_paths = Object.keys(pos_traits_object); + const neu_trait_paths = Object.keys(neu_traits_object); + const neg_trait_paths = Object.keys(neg_traits_object); + + const positive_and_negative_traits = pos_trait_paths.concat(neg_trait_paths); + + const traits_left = max_traits - pos_trait_paths.length; + + let points_left = trait_points; + for (const path of positive_and_negative_traits) { + points_left -= all_traits[path].cost; + } + + const pos_traits_total = pos_trait_paths + .map((x) => all_traits[x].cost) + .reduce((a, b) => a + b, 0); + const neg_traits_total = neg_trait_paths + .map((x) => all_traits[x].cost) + .reduce((a, b) => a + b, 0); + + return ( + + + + Traits Left: {traits_left} + {traits_cheating ? ( + + + ADMIN + + + ) : null} + Points Left: {points_left} + + + + + + + Positive Traits ({pos_traits_total}) + + + + + {Object.entries(pos_traits_object).map(([key, data]) => ( + + + + ))} + + + + + + Negative Traits ({neg_traits_total}) + + + + + {Object.entries(neg_traits_object).map(([key, data]) => ( + + + + ))} + + + + + + + + Neutral Traits + + + + + {Object.entries(neu_traits_object).map(([key, data]) => ( + + + + ))} + + + + ); +}; + +export const TraitComponent = (props: { + traitPath: string; + trait: Trait; + data: any; +}) => { + const { act } = useBackend(); + const { traitPath, trait, data } = props; + + return ( + + + {trait.has_preferences ? ( + + + {Object.entries(trait.has_preferences).map( + ([prefKey, prefData]) => ( + + + + ), + )} + + + ) : null} + + ); +}; + +export const TraitSubprefSelector = (props: { + trait: string; + prefKey: string; + prefData: TraitSubpref; + data: any; +}) => { + const { act } = useBackend(); + const { trait, prefKey, prefData, data } = props; + + switch (prefData[0]) { + case TraitPrefType.TRAIT_PREF_TYPE_BOOLEAN: + return ( + + ); + case TraitPrefType.TRAIT_PREF_TYPE_COLOR: + return ( + <> + + + + ); + case TraitPrefType.TRAIT_PREF_TYPE_STRING: + return ( + + ); + } +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Ears.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Ears.tsx new file mode 100644 index 0000000000..05856e8eda --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Ears.tsx @@ -0,0 +1,177 @@ +import { type PropsWithChildren, useCallback, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, ImageButton, Input, Section } from 'tgui-core/components'; + +import { + ColorizedImageButton, + ColorPicker, + ColorType, + drawColorizedIconToOffscreenCanvas, +} from '../../helper_components'; +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; + +export const EarsImageButton = ( + props: PropsWithChildren<{ + serverData: GeneralDataConstant; + style: string; + color?: string | null; + color2?: string | null; + color3?: string | null; + onClick: () => void; + tooltip?: string; + selected?: boolean; + }>, +) => { + const { serverData, style, color, color2, color3, onClick } = props; + + if (!(style in serverData.ear_styles)) { + return ( + } + > + {props.children} + + ); + } + + const data = serverData.ear_styles[style]; + + // Must be wrapped with useCallback or else it'll rerender every frame + const postRender = useCallback( + async (ctx: OffscreenCanvasRenderingContext2D) => { + ctx.globalCompositeOperation = 'source-over'; + + if (data.extra_overlay) { + const overlay = await drawColorizedIconToOffscreenCanvas( + color2 || '#ffffff', + data.icon, + data.extra_overlay, + ); + if (overlay) { + ctx.drawImage(overlay, 0, 0, 64, 64); + } + } + + if (data.extra_overlay2) { + const overlay = await drawColorizedIconToOffscreenCanvas( + color3 || '#ffffff', + data.icon, + data.extra_overlay2, + ); + if (overlay) { + ctx.drawImage(overlay, 0, 0, 64, 64); + } + } + }, + [data.extra_overlay, data.extra_overlay2, color2, color3], + ); + + return ( + + {props.children} + + ); +}; + +export const EarsDimmer = (props: { + setShow: React.Dispatch>; + data: GeneralData; + serverData: GeneralDataConstant; + staticData: GeneralDataStatic; +}) => { + const { act } = useBackend(); + const { setShow, data, serverData, staticData } = props; + const color = data.ears_color1; + const color2 = data.ears_color2; + const color3 = data.ears_color3; + const alpha = data.ears_alpha; + + const [search, setSearch] = useState(''); + + const styles = staticData.available_ear_styles.filter((x) => + search ? x.toLowerCase().includes(search.toLowerCase()) : true, + ); + styles.sort(); + + return ( +
setShow(BodyPopup.None)} color="bad"> + Close + + } + > + { + switch (type) { + case ColorType.First: + act('set_ear_color'); + break; + case ColorType.Second: + act('set_ear_color2'); + break; + case ColorType.Third: + act('set_ear_color3'); + break; + case ColorType.Alpha: + act('ears_alpha'); + break; + } + }} + color_one={color || '#FFFFFF'} + color_two={color2 || '#FFFFFF'} + color_three={color3 || '#FFFFFF'} + alpha={alpha} + /> + setSearch(val)} + value={search} + mt={1} + /> + + {styles.map((style) => ( + act('set_ear_style', { ear_style: style })} + > + {style} + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/EarsSecondary.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/EarsSecondary.tsx new file mode 100644 index 0000000000..7b9cc321f0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/EarsSecondary.tsx @@ -0,0 +1,97 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Button, Input, Section } from 'tgui-core/components'; + +import { ColorPicker, ColorType } from '../../helper_components'; +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; +import { EarsImageButton } from './Ears'; + +// Flavored as "Horns" +export const EarsSecondaryDimmer = (props: { + setShow: React.Dispatch>; + data: GeneralData; + serverData: GeneralDataConstant; + staticData: GeneralDataStatic; +}) => { + const { act } = useBackend(); + const { setShow, data, serverData, staticData } = props; + + const colors = data.ear_secondary_colors; + + const color1 = colors[0] || null; + const color2 = colors[1] || null; + const color3 = colors[2] || null; + const alpha = data.ear_secondary_alpha; + + const [search, setSearch] = useState(''); + + const styles = staticData.available_ear_styles.filter((x) => + search ? x.toLowerCase().includes(search.toLowerCase()) : true, + ); + styles.sort(); + + return ( +
setShow(BodyPopup.None)} color="bad"> + Close + + } + > + { + switch (type) { + case ColorType.First: + act('set_ear_secondary_color', { ear_secondary_color: 1 }); + break; + case ColorType.Second: + act('set_ear_secondary_color', { ear_secondary_color: 2 }); + break; + case ColorType.Third: + act('set_ear_secondary_color', { ear_secondary_color: 3 }); + break; + case ColorType.Alpha: + act('secondary_ears_alpha'); + } + }} + color_one={color1 || '#FFFFFF'} + color_two={color2 || '#FFFFFF'} + color_three={color3 || '#FFFFFF'} + alpha={alpha} + /> + setSearch(val)} + value={search} + mt={1} + /> + + {styles.map((style) => ( + act('set_ear_secondary_style', { ear_style: style })} + > + {style} + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Facial.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Facial.tsx new file mode 100644 index 0000000000..1753a33a53 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Facial.tsx @@ -0,0 +1,132 @@ +import { type PropsWithChildren, useCallback, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Button, ImageButton, Input, Section } from 'tgui-core/components'; + +import { + ColorizedImageButton, + ColorPicker, + getImage, +} from '../../helper_components'; +import { + AppearanceFlags, + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + type Species, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; + +export const FacialImageButton = ( + props: PropsWithChildren<{ + serverData: GeneralDataConstant; + hairStyle: string; + hairColor: string; + onClick: () => void; + tooltip?: string; + selected?: boolean; + }>, +) => { + const { serverData, hairStyle, hairColor, onClick } = props; + + const renderHuman = useCallback( + async (ctx: OffscreenCanvasRenderingContext2D) => { + ctx.globalCompositeOperation = 'destination-over'; + const image = await getImage( + Byond.iconRefMap['icons/mob/human.dmi'] + '?state=body_f_s&dir=2', + ); + + ctx.drawImage(image, 0, 0, 32, 10, 0, 0, 64, 20); + }, + [], + ); + + if (!(hairStyle in serverData.facial_styles)) { + return ( + + {props.children} + + ); + } + + const data = serverData.facial_styles[hairStyle]; + return ( + + {props.children} + + ); +}; + +export const FacialDimmer = (props: { + setShow: React.Dispatch>; + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { act } = useBackend(); + const { setShow, data, serverData, staticData } = props; + // if the data is missing our UI is fucked anyways + const our_species = serverData.species.find( + (x) => x.name === data.species, + ) as Species; + const hairColor = data.facial_color; + + const [search, setSearch] = useState(''); + const hair_styles = staticData.available_facial_styles.filter((x) => + search ? x.toLowerCase().includes(search.toLowerCase()) : true, + ); + hair_styles.sort(); + + return ( +
setShow(BodyPopup.None)} color="bad"> + Close + + } + > + {our_species.appearance_flags & AppearanceFlags.HAS_HAIR_COLOR ? ( + { + act('set_facial_hair_color'); + }} + color_one={hairColor} + /> + ) : null} + setSearch(val)} + value={search} + mt={1} + /> + + {hair_styles.map((hairStyle) => ( + { + act('set_facial_hair_style', { facial_hair_style: hairStyle }); + }} + selected={hairStyle === data.f_style} + > + {hairStyle} + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Gradient.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Gradient.tsx new file mode 100644 index 0000000000..d39c985dbd --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Gradient.tsx @@ -0,0 +1,105 @@ +import { type PropsWithChildren, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Button, ImageButton, Input, Section } from 'tgui-core/components'; + +import { ColorizedImageButton, ColorPicker } from '../../helper_components'; +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; + +export const GradientImageButton = ( + props: PropsWithChildren<{ + serverData: GeneralDataConstant; + style: string; + color: string; + onClick: () => void; + tooltip?: string; + selected?: boolean; + }>, +) => { + const { serverData, style, color, onClick } = props; + + if (!(style in serverData.grad_styles)) { + return ( + + {props.children} + + ); + } + + const data = serverData.grad_styles[style]; + return ( + + {props.children} + + ); +}; + +export const GradientDimmer = (props: { + setShow: React.Dispatch>; + data: GeneralData; + serverData: GeneralDataConstant; + staticData: GeneralDataStatic; +}) => { + const { act } = useBackend(); + const { setShow, data, serverData, staticData } = props; + const color = data.grad_color; + + const [search, setSearch] = useState(''); + const grad_styles = Object.keys(serverData.grad_styles).filter((x) => + search ? x.toLowerCase().includes(search.toLowerCase()) : true, + ); + grad_styles.sort(); + + return ( +
setShow(BodyPopup.None)} color="bad"> + Close + + } + > + { + act('set_grad_color'); + }} + color_one={color} + /> + setSearch(val)} + value={search} + mt={1} + /> + + {grad_styles.map((style) => ( + act('set_grad_style', { grad_style: style })} + > + {style} + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Hair.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Hair.tsx new file mode 100644 index 0000000000..159e2dd60e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Hair.tsx @@ -0,0 +1,131 @@ +import { type PropsWithChildren, useCallback, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Button, ImageButton, Input, Section } from 'tgui-core/components'; + +import { + ColorizedImageButton, + ColorPicker, + getImage, +} from '../../helper_components'; +import { + AppearanceFlags, + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + type Species, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; + +export const HairImageButton = ( + props: PropsWithChildren<{ + serverData: GeneralDataConstant; + hairStyle: string; + hairColor: string; + onClick: () => void; + tooltip?: string; + selected?: boolean; + }>, +) => { + const { serverData, hairStyle, hairColor, onClick } = props; + + const renderHuman = useCallback( + async (ctx: OffscreenCanvasRenderingContext2D) => { + ctx.globalCompositeOperation = 'destination-over'; + const image = await getImage( + Byond.iconRefMap['icons/mob/human.dmi'] + '?state=body_f_s&dir=2', + ); + + ctx.drawImage(image, 0, 0, 32, 10, 0, 0, 64, 20); + }, + [], + ); + + if (!(hairStyle in serverData.hair_styles)) { + return ( + + {props.children} + + ); + } + const data = serverData.hair_styles[hairStyle]; + return ( + + {props.children} + + ); +}; + +export const HairDimmer = (props: { + setShow: React.Dispatch>; + data: GeneralData; + serverData: GeneralDataConstant; + staticData: GeneralDataStatic; +}) => { + const { act } = useBackend(); + const { setShow, data, serverData, staticData } = props; + // if the data is missing our UI is fucked anyways + const our_species = serverData.species.find( + (x) => x.name === data.species, + ) as Species; + const hairColor = data.hair_color; + + const [search, setSearch] = useState(''); + const hair_styles = staticData.available_hair_styles.filter((x) => + search ? x.toLowerCase().includes(search.toLowerCase()) : true, + ); + hair_styles.sort(); + + return ( +
setShow(BodyPopup.None)} color="bad"> + Close + + } + > + {our_species.appearance_flags & AppearanceFlags.HAS_HAIR_COLOR ? ( + { + act('set_hair_color'); + }} + color_one={hairColor} + /> + ) : null} + setSearch(val)} + value={search} + mt={1} + /> + + {hair_styles.map((hairStyle) => ( + { + act('set_hair_style', { hair_style: hairStyle }); + }} + selected={hairStyle === data.h_style} + > + {hairStyle} + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Markings.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Markings.tsx new file mode 100644 index 0000000000..6ec8feaf8b --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Markings.tsx @@ -0,0 +1,381 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + ColorBox, + Dimmer, + Input, + LabeledList, + Section, + Stack, + Tabs, +} from 'tgui-core/components'; +import { type BooleanLike } from 'tgui-core/react'; +import { capitalize } from 'tgui-core/string'; + +import { + ColorizedImage, + ColorizedImageButton, + getImage, +} from '../../helper_components'; +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, + type MarkingStyle, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; + +const BP_TO_NAME = { + r_hand: 'right hand', + l_hand: 'left hand', + l_arm: 'left arm', + r_arm: 'right arm', + l_leg: 'left leg', + r_leg: 'right leg', + l_foot: 'left foot', + r_foot: 'right foot', +}; + +export const MarkingsPopup = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; + setShow: React.Dispatch>; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData, setShow } = props; + + const { body_markings } = data; + const { body_markings: available_body_markings } = serverData; + + const markings = Object.entries(body_markings); + + const [showAddMenu, setShowAddMenu] = useState(false); + const [showExtra, setShowExtra] = useState(''); + const [showHuman, setShowHuman] = useState(true); + + return ( +
+ setShowHuman((x) => !x)} + > + Show Human + + + + + } + > + {markings.length === 0 && No Markings Selected.} + {markings.map(([name, data]) => ( + + + + + + ))} + {!!showExtra && ( + + )} + {showAddMenu && ( + + )} +
+ ); +}; + +export const ExtraWindow = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; + name: string; + setShow: React.Dispatch>; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData, name, setShow } = props; + const { body_markings } = data; + const { body_markings: available_body_markings } = serverData; + + const our_marking = body_markings[name]; + const our_marking_server = available_body_markings[name]; + + if (!our_marking) { + setShow(''); + return Error: Invalid marking {name}; + } + + return ( + +
setShow('')}> + Close + + } + > + + + + + + + + + {Object.entries(our_marking) + .filter(([zone, value]) => { + return value && typeof value === 'object'; + }) + .map( + ([zone, value]: [string, { on: BooleanLike; color: string }]) => ( + + + + act('zone_marking_toggle', { marking: name, zone }) + } + checked={value.on} + selected={value.on} + tooltip={value.on ? 'Disable Part' : 'Enable Part'} + /> + + ), + )} + +
+
+ ); +}; + +const MARKINGS_PER_PAGE = 30; + +export const AddMarkingWindow = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; + setShow: React.Dispatch>; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData, setShow } = props; + const { body_markings } = serverData; + const [showList, setShowList] = useState(false); + const [showHuman, setShowHuman] = useState(true); + const [search, setSearch] = useState(''); + const [tabIndex, setTabIndex] = useState(0); + + const body_markings_list = Object.entries(body_markings); + body_markings_list.sort((a, b) => a[0].localeCompare(b[0])); + + const filteredStyles = body_markings_list.filter(([name, data]) => { + if (!search) { + return true; + } + return name.toLowerCase().includes(search.toLowerCase()); + }); + + const styleTabCount = Math.ceil(filteredStyles.length / MARKINGS_PER_PAGE); + const shownStyles: [string, MarkingStyle][][] = []; + + for (let i = 0; i < styleTabCount; i++) { + shownStyles[i] = filteredStyles.slice( + i * MARKINGS_PER_PAGE, + i * MARKINGS_PER_PAGE + MARKINGS_PER_PAGE, + ); + } + + return ( + + + + + + setSearch(val)} + value={search} + /> + + + setShowHuman(!showHuman)} + > + Show Human + + + + + + + + {shownStyles.map((_, i) => ( + + setTabIndex(i)} + > + Page {i + 1} + + + ))} + + + {shownStyles[tabIndex]?.map(([name, data]) => ( + { + act('add_marking', { new_marking: name }); + setShow(false); + }} + iconRef={data.icon} + iconState={data.icon_state} + tooltip={name} + color="#ffffff" + postRender={async (ctx) => { + if (showHuman) { + ctx.save(); + ctx.globalCompositeOperation = 'destination-over'; + const background = await getImage( + Byond.iconRefMap['icons/mob/human.dmi'] + + '?state=body_m_s&dir=2', + ); + ctx.drawImage(background, 0, 0, 64, 64); + ctx.restore(); + } + }} + > + {name} + + ))} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Tail.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Tail.tsx new file mode 100644 index 0000000000..05592b6466 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Tail.tsx @@ -0,0 +1,177 @@ +import { type PropsWithChildren, useCallback, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, ImageButton, Input, Section } from 'tgui-core/components'; + +import { + ColorizedImageButton, + ColorPicker, + ColorType, + drawColorizedIconToOffscreenCanvas, +} from '../../helper_components'; +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; + +export const TailImageButton = ( + props: PropsWithChildren<{ + serverData: GeneralDataConstant; + style: string; + color?: string | null; + color2?: string | null; + color3?: string | null; + onClick: () => void; + tooltip?: string; + selected?: boolean; + }>, +) => { + const { serverData, style, color, color2, color3, onClick } = props; + + if (!(style in serverData.tail_styles)) { + return ( + } + > + {props.children} + + ); + } + const data = serverData.tail_styles[style]; + + // Must be wrapped with useCallback or else it'll rerender every frame + const postRender = useCallback( + async (ctx: OffscreenCanvasRenderingContext2D) => { + ctx.globalCompositeOperation = 'source-over'; + + if (data.extra_overlay) { + const overlay = await drawColorizedIconToOffscreenCanvas( + color2 || '#ffffff', + data.icon, + data.extra_overlay, + '4', + ); + if (overlay) { + ctx.drawImage(overlay, 0, 0, 64, 64); + } + } + + if (data.extra_overlay2) { + const overlay = await drawColorizedIconToOffscreenCanvas( + color3 || '#ffffff', + data.icon, + data.extra_overlay2, + '4', + ); + if (overlay) { + ctx.drawImage(overlay, 0, 0, 64, 64); + } + } + }, + [data.extra_overlay, data.extra_overlay2, color2, color3], + ); + + return ( + + {props.children} + + ); +}; + +export const TailDimmer = (props: { + setShow: React.Dispatch>; + data: GeneralData; + serverData: GeneralDataConstant; + staticData: GeneralDataStatic; +}) => { + const { act } = useBackend(); + const { setShow, data, serverData, staticData } = props; + const color = data.tail_color1; + const color2 = data.tail_color2; + const color3 = data.tail_color3; + const alpha = data.tail_alpha; + + const [search, setSearch] = useState(''); + const styles = staticData.available_tail_styles.filter((x) => + search ? x.toLowerCase().includes(search.toLowerCase()) : true, + ); + styles.sort(); + + return ( +
setShow(BodyPopup.None)} color="bad"> + Close + + } + > + { + switch (type) { + case ColorType.First: + act('set_tail_color'); + break; + case ColorType.Second: + act('set_tail_color2'); + break; + case ColorType.Third: + act('set_tail_color3'); + break; + case ColorType.Alpha: + act('set_tail_alpha'); + break; + } + }} + color_one={color || '#FFFFFF'} + color_two={color2 || '#FFFFFF'} + color_three={color3 || '#FFFFFF'} + alpha={alpha} + /> + setSearch(val)} + value={search} + mt={1} + /> + + {styles.map((style) => ( + { + act('set_tail_style', { style: style }); + }} + selected={style === data.tail_style} + > + {style} + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Wings.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Wings.tsx new file mode 100644 index 0000000000..6e1020027f --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/body/Wings.tsx @@ -0,0 +1,174 @@ +import { type PropsWithChildren, useCallback, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, ImageButton, Input, Section } from 'tgui-core/components'; + +import { + ColorizedImageButton, + ColorPicker, + ColorType, + drawColorizedIconToOffscreenCanvas, +} from '../../helper_components'; +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from '../data'; +import { BodyPopup } from '../SubtabBody'; + +export const WingsImageButton = ( + props: PropsWithChildren<{ + serverData: GeneralDataConstant; + style: string; + color?: string | null; + color2?: string | null; + color3?: string | null; + onClick: () => void; + tooltip?: string; + selected?: boolean; + }>, +) => { + const { serverData, style, color, color2, color3, onClick } = props; + + if (!(style in serverData.wing_styles)) { + return ( + } + > + {props.children} + + ); + } + const data = serverData.wing_styles[style]; + + // Must be wrapped with useCallback or else it'll rerender every frame + const postRender = useCallback( + async (ctx: OffscreenCanvasRenderingContext2D) => { + ctx.globalCompositeOperation = 'source-over'; + + if (data.extra_overlay) { + const overlay = await drawColorizedIconToOffscreenCanvas( + color2 || '#ffffff', + data.icon, + data.extra_overlay, + ); + if (overlay) { + ctx.drawImage(overlay, 0, 0, 64, 64); + } + } + + if (data.extra_overlay2) { + const overlay = await drawColorizedIconToOffscreenCanvas( + color3 || '#ffffff', + data.icon, + data.extra_overlay2, + ); + if (overlay) { + ctx.drawImage(overlay, 0, 0, 64, 64); + } + } + }, + [data.extra_overlay, data.extra_overlay2, color2, color3], + ); + + return ( + + {props.children} + + ); +}; + +export const WingsDimmer = (props: { + setShow: React.Dispatch>; + data: GeneralData; + serverData: GeneralDataConstant; + staticData: GeneralDataStatic; +}) => { + const { act } = useBackend(); + const { setShow, data, serverData, staticData } = props; + const color = data.wing_color1; + const color2 = data.wing_color2; + const color3 = data.wing_color3; + const alpha = data.wing_alpha; + + const [search, setSearch] = useState(''); + const styles = staticData.available_wing_styles.filter((x) => + search ? x.toLowerCase().includes(search.toLowerCase()) : true, + ); + styles.sort(); + + return ( +
setShow(BodyPopup.None)} color="bad"> + Close + + } + > + { + switch (type) { + case ColorType.First: + act('set_wing_color'); + break; + case ColorType.Second: + act('set_wing_color2'); + break; + case ColorType.Third: + act('set_wing_color3'); + break; + case ColorType.Alpha: + act('set_wing_alpha'); + break; + } + }} + color_one={color || '#FFFFFF'} + color_two={color2 || '#FFFFFF'} + color_three={color3 || '#FFFFFF'} + alpha={alpha} + /> + setSearch(val)} + value={search} + mt={1} + /> + + {styles.map((style) => ( + { + act('set_wing_style', { style: style }); + }} + selected={style === data.wing_style} + > + {style} + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/data.ts new file mode 100644 index 0000000000..d23751b900 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/data.ts @@ -0,0 +1,437 @@ +import { type BooleanLike } from 'tgui-core/react'; + +export type BodyMarking = Record< + string, + { + on: BooleanLike; + color: string; + } +> & { + color: string; +}; + +export enum Gender { + Male = 'Male', + Female = 'Female', + Neuter = 'Neuter', + Plural = 'Plural', +} + +export enum PersistanceEnum { + PERSIST_SPAWN = 0x01, + PERSIST_WEIGHT = 0x02, + PERSIST_ORGANS = 0x04, + PERSIST_MARKINGS = 0x08, + PERSIST_SIZE = 0x10, +} + +export type BasicData = { + real_name: string; + be_random_name: BooleanLike; + nickname: string; + biological_sex: Gender; + identifying_gender: Gender; + age: number; + bday_month: number; + bday_day: number; + bday_announce: BooleanLike; + spawnpoint: string; + ooc_notes_length: number; + + languages: { name: string; removable: BooleanLike }[]; + languages_can_add: BooleanLike; + language_keys: string[]; + preferred_language: string; + runechat_color: string; + + vore_egg_type: string; + autohiss: string; + emote_sound_mode: string; + + persistence_settings: PersistanceEnum; +}; + +export enum BodypartFlags { + BP_L_FOOT = 'l_foot', + BP_R_FOOT = 'r_foot', + BP_L_LEG = 'l_leg', + BP_R_LEG = 'r_leg', + BP_L_HAND = 'l_hand', + BP_R_HAND = 'r_hand', + BP_L_ARM = 'l_arm', + BP_R_ARM = 'r_arm', + BP_HEAD = 'head', + BP_TORSO = 'torso', + BP_GROIN = 'groin', + O_EYES = 'eyes', + O_HEART = 'heart', + O_LUNGS = 'lungs', + O_BRAIN = 'brain', + O_LIVER = 'liver', + O_KIDNEYS = 'kidneys', + O_VOICE = 'voicebox', + O_SPLEEN = 'spleen', + O_STOMACH = 'stomach', + O_INTESTINE = 'intestine', +} + +export const is_organ = (organ: BodypartFlags): boolean => { + switch (organ) { + case BodypartFlags.O_EYES: + case BodypartFlags.O_HEART: + case BodypartFlags.O_LUNGS: + case BodypartFlags.O_BRAIN: + case BodypartFlags.O_LIVER: + case BodypartFlags.O_KIDNEYS: + case BodypartFlags.O_VOICE: + case BodypartFlags.O_SPLEEN: + case BodypartFlags.O_STOMACH: + case BodypartFlags.O_INTESTINE: + return true; + default: + return false; + } +}; + +export const proper_organ_name = (organ: BodypartFlags): string => { + switch (organ) { + case BodypartFlags.BP_L_FOOT: + return 'left foot'; + case BodypartFlags.BP_R_FOOT: + return 'right foot'; + case BodypartFlags.BP_L_LEG: + return 'left leg'; + case BodypartFlags.BP_R_LEG: + return 'right leg'; + case BodypartFlags.BP_L_HAND: + return 'left hand'; + case BodypartFlags.BP_R_HAND: + return 'right hand'; + case BodypartFlags.BP_L_ARM: + return 'left arm'; + case BodypartFlags.BP_R_ARM: + return 'right arm'; + case BodypartFlags.BP_HEAD: + return 'head'; + case BodypartFlags.BP_TORSO: + return 'full body'; + case BodypartFlags.BP_GROIN: + return 'groin'; + case BodypartFlags.O_EYES: + return 'eyes'; + case BodypartFlags.O_HEART: + return 'heart'; + case BodypartFlags.O_LUNGS: + return 'lungs'; + case BodypartFlags.O_BRAIN: + return 'brain'; + case BodypartFlags.O_LIVER: + return 'liver'; + case BodypartFlags.O_KIDNEYS: + return 'kidneys'; + case BodypartFlags.O_VOICE: + return 'larynx'; + case BodypartFlags.O_SPLEEN: + return 'spleen'; + case BodypartFlags.O_STOMACH: + return 'stomach'; + case BodypartFlags.O_INTESTINE: + return 'intestines'; + } + return 'unknown'; +}; + +export enum OrganStatus { + Cyborg = 'cyborg', + Amputated = 'amputated', + Mechanical = 'mechanical', + Digital = 'digital', + Assisted = 'assisted', +} + +export type BodyData = { + species: string; + organ_data: Record; + rlimb_data: Record; + + s_tone: number; + eyes_color: string; + skin_color: string; + + h_style: string; + hair_color: string; + + f_style: string; + facial_color: string; + + grad_style: string; + grad_color: string; + + ear_style: string | null; + ears_color1: string; + ears_color2: string; + ears_color3: string; + + ears_alpha: number; + + ear_secondary_style: string | null; + ear_secondary_colors: string[]; + ear_secondary_alpha: number; + + body_markings: Record; + + tail_style: string; + tail_color1: string; + tail_color2: string; + tail_color3: string; + tail_alpha: number; + + wing_style: string; + wing_color1: string; + wing_color2: string; + wing_color3: string; + wing_alpha: number; + + b_type: string; + digitigrade: BooleanLike; + + synth_color_toggle: BooleanLike; + synth_color: string; + synth_markings: BooleanLike; + + selects_bodytype: BooleanLike; + custom_base: string; + blood_color: string; + blood_reagents: string; +}; + +export type BackgroundData = { + economic_status: string; + home_system: string; + birthplace: string; + citizenship: string; + faction: string; + religion: string; + ooc_note_style: BooleanLike; + records_banned: BooleanLike; + med_record: string; + gen_record: string; + sec_record: string; +}; + +export type FlavorData = { + flavor_text_length: number; +}; + +export type SpecialRole = { + idx: number; + name: string; + selected: BooleanLike; + banned: BooleanLike; +}; + +export type AntagonismData = { + antag_faction: string; + antag_vis: string; + uplink_type: string; + record_banned: BooleanLike; + exploitable_record?: string; + pai_name: string; + pai_desc: string; + pai_role: string; + pai_comments: string; + syndicate_ban: BooleanLike; + special_roles: SpecialRole[]; +}; + +export const REQUIRED_FLAVOR_TEXT_LENGTH = 30; +export const REQUIRED_OOC_LENGTH = 15; + +export type SizeData = { + size_multiplier: number; + fuzzy: BooleanLike; + offset_override: BooleanLike; + voice_freq: number; + voice_sound: string; + custom_speech_bubble: string; + custom_species_sound: string; + custom_footstep: string; + weight_vr: number; + weight_gain: number; + weight_loss: number; +}; + +export type MiscData = { + show_in_directory: BooleanLike; + directory_tag: string; + directory_gendertag: string; + directory_sexualitytag: string; + directory_erptag: string; + sensorpref: string; + capture_crystal: BooleanLike; + auto_backup_implant: BooleanLike; + borg_petting: BooleanLike; + + resleeve_lock: BooleanLike; + resleeve_scan: BooleanLike; + mind_scan: BooleanLike; + + vantag_volunteer: BooleanLike; + vantag_preference: string; + + nif: BooleanLike; + + custom_species: string; + pos_traits: string[] | Record | null>; + neu_traits: string[] | Record | null>; + neg_traits: string[] | Record | null>; + traits_cheating: BooleanLike; + max_traits: number; + trait_points: number; +}; + +export type GeneralData = BasicData & + BodyData & + BackgroundData & + FlavorData & + AntagonismData & + SizeData & + MiscData; + +export type GeneralDataStatic = { + allow_metadata: BooleanLike; + can_play: Record; + digi_allowed: BooleanLike; + available_hair_styles: string[]; + available_facial_styles: string[]; + available_ear_styles: string[]; + available_tail_styles: string[]; + available_wing_styles: string[]; +}; + +export type StandardStyle = { name: string; icon: string; icon_state: string }; + +export type EarStyle = StandardStyle & { + type: string; + do_colouration: BooleanLike; + extra_overlay: string; + extra_overlay2: string; +}; + +export type MarkingStyle = StandardStyle & { + genetic: BooleanLike; + body_parts: string[]; +}; + +export type WingStyle = StandardStyle & { + do_colouration: BooleanLike; + extra_overlay: string; + extra_overlay2: string; +}; + +export type TailStyle = StandardStyle & { + do_colouration: BooleanLike; + extra_overlay: string; + extra_overlay2: string; +}; + +export enum SpeciesFlags { + NONE = 0, + NO_MINOR_CUT = 1 << 0, + PLANT = 1 << 1, + NO_SLEEVE = 1 << 2, + NO_PAIN = 1 << 3, + NO_SLIP = 1 << 4, + NO_POISON = 1 << 5, + NO_EMBED = 1 << 6, + NO_HALLUCINATION = 1 << 7, + NO_BLOOD = 1 << 8, + UNDEAD = 1 << 9, + NO_INFECT = 1 << 10, + NO_DEFIB = 1 << 11, + NO_DNA = 1 << 12, + THICK_SKIN = 1 << 13, +} + +export enum SpawnFlags { + NONE = 0, + SPECIES_IS_WHITELISTED = 1 << 0, + SPECIES_IS_RESTRICTED = 1 << 1, + SPECIES_CAN_JOIN = 1 << 2, + SPECIES_NO_FBP_CONSTRUCTION = 1 << 3, + SPECIES_NO_FBP_CHARGEN = 1 << 4, + SPECIES_NO_POSIBRAIN = 1 << 5, + SPECIES_NO_DRONEBRAIN = 1 << 6, +} + +export enum AppearanceFlags { + NONE = 0, + HAS_SKIN_TONE = 1 << 0, + HAS_SKIN_COLOR = 1 << 1, + HAS_LIPS = 1 << 2, + HAS_UNDERWEAR = 1 << 3, + HAS_EYE_COLOR = 1 << 4, + HAS_HAIR_COLOR = 1 << 5, + RADIATION_GLOWS = 1 << 6, +} + +export type Species = { + name: string; + wikilink: string; + blurb: string; + species_language: string; + icobase: string; + rarity: number; + has_organ: string; + flags: SpeciesFlags; + spawn_flags: SpawnFlags; + appearance_flags: AppearanceFlags; +}; + +export enum TraitPrefType { + TRAIT_PREF_TYPE_BOOLEAN = 1, + TRAIT_PREF_TYPE_COLOR = 2, + TRAIT_PREF_TYPE_STRING = 3, +} + +export enum TraitVareditTarget { + TRAIT_NO_VAREDIT_TARGET = 0, + TRAIT_VAREDIT_TARGET_SPECIES = 1, + TRAIT_VAREDIT_TARGET_MOB = 2, +} + +export enum TraitCategory { + Positive = 1, + Neutral = 0, + Negative = -1, +} + +export type TraitSubpref = [TraitPrefType, string, TraitVareditTarget, string]; + +export type Trait = { + cost: number; + name: string; + category: TraitCategory; + /** + * list( + * "identifier/name of var to edit" = list( + * typeofpref, // typeofpref should follow the defines in _traits.dm (eg. TRAIT_PREF_TYPE_BOOLEAN) + * "text to display in prefs", + * TRAIT_NO_VAREDIT_TARGET/TRAIT_VAREDIT_TARGET_SPECIES/etc, + * (optional: default value) + * ), etc) + */ + has_preferences: Record; +}; + +export type GeneralDataConstant = { + species: Species[]; + hair_styles: Record; + facial_styles: Record; + grad_styles: Record; + ear_styles: Record; + body_markings: Record; + tail_styles: Record; + wing_styles: Record; + all_traits: Record; +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/index.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/index.tsx new file mode 100644 index 0000000000..14cd9130bd --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/index.tsx @@ -0,0 +1,131 @@ +import { type ReactNode, useState } from 'react'; +import { Box, Icon, Section, Tabs } from 'tgui-core/components'; + +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from './data'; +import { SubtabBody } from './SubtabBody'; +import { SubtabInfo } from './SubtabInfo'; +import { SubtabNotes } from './SubtabNotes'; +import { SubtabSettings } from './SubtabSettings'; +import { SubtabSpecies } from './SubtabSpecies'; +import { SubtabTraits } from './SubtabTraits'; + +// /////////////// +// Main Components +// /////////////// + +export const General = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + return ( + + ); +}; + +enum SubtabState { + Info = 'Info', + Species = 'Species', + Body = 'Body', + CharSettings = 'CharSettings', + Notes = 'Notes', + Traits = 'Traits', +} + +const SUBTAB_TO_NAME = { + [SubtabState.Info]: ( + + Info + + ), + [SubtabState.Species]: ( + + Species + + ), + [SubtabState.Body]: ( + + Physical Attributes + + ), + [SubtabState.CharSettings]: ( + + Character Settings + + ), + [SubtabState.Notes]: ( + + Notes + + ), + [SubtabState.Traits]: ( + + Traits + + ), +}; + +const SUBTAB_TO_COMPONENT: Record< + SubtabState, + (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; + }) => ReactNode +> = { + [SubtabState.Info]: (props) => , + [SubtabState.Species]: (props) => , + [SubtabState.Body]: (props) => , + [SubtabState.CharSettings]: (props) => , + [SubtabState.Notes]: (props) => , + [SubtabState.Traits]: (props) => , +}; + +export const GeneralContent = (props: { + data: GeneralData; + staticData: GeneralDataStatic; + serverData: GeneralDataConstant; +}) => { + const { data, staticData, serverData } = props; + + const all_subtabs = Object.keys(SubtabState).map( + (string) => string as any as SubtabState, + ); + + const [subtab, setSubtab] = useState(SubtabState.Info); + + return ( +
+ {all_subtabs.map((state) => { + const title = SUBTAB_TO_NAME[state]; + return ( + setSubtab(state)} + > + {title} + + ); + })} + + } + fill + scrollable + mt={1} + position="relative" + > + {SUBTAB_TO_COMPONENT[subtab](props)} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/helper_components.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/helper_components.tsx new file mode 100644 index 0000000000..13a75f27a5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/helper_components.tsx @@ -0,0 +1,272 @@ +import React, { + type PropsWithChildren, + type ReactNode, + useCallback, + useEffect, + useState, +} from 'react'; +import { Button, ColorBox, ImageButton, Stack } from 'tgui-core/components'; + +export const getImage = async (url: string): Promise => { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => { + resolve(image); + }; + image.onerror = (error) => { + reject(error); + }; + image.src = url; + }); +}; + +// This component +export const CanvasBackedImage = (props: { + render: ( + canvas: OffscreenCanvas, + ctx: OffscreenCanvasRenderingContext2D, + ) => Promise; +}) => { + const [bitmap, setBitmap] = useState(''); + + useEffect(() => { + const offscreenCanvas: OffscreenCanvas = new OffscreenCanvas(64, 64); + + const ctx = offscreenCanvas.getContext('2d'); + if (!ctx) { + return; + } + + const drawImage = async () => { + // Render + await props.render(offscreenCanvas, ctx); + + // Convert to a blob and put in our tag + const bitmap = await offscreenCanvas.convertToBlob(); + setBitmap(URL.createObjectURL(bitmap)); + }; + + drawImage(); + + return () => { + if (bitmap !== '') { + URL.revokeObjectURL(bitmap); + } + }; + }, [props.render]); + + return ; +}; + +export const drawColorizedIconToOffscreenCanvas = async ( + color: string, + icon: string, + icon_state: string, + dir?: string, +): Promise => { + const canvas = new OffscreenCanvas(64, 64); + const ctx = canvas.getContext('2d'); + if (!ctx) { + return null; + } + + ctx.imageSmoothingEnabled = false; + + let image; + try { + image = await getImage( + icon + '?state=' + icon_state + '&dir=' + (dir || '2'), + ); + } catch (e) { + return null; + } + + ctx.drawImage(image, 0, 0, 64, 64); + + // Draw a square over the image with the color + ctx.globalCompositeOperation = 'multiply'; + ctx.fillStyle = color || '#ffffff'; + ctx.fillRect(0, 0, 64, 64); + + // Use the image as a mask + ctx.globalCompositeOperation = 'destination-in'; + ctx.drawImage(image, 0, 0, 64, 64); + + return canvas; +}; + +export const ColorizedImage = (props: { + iconRef: string; + iconState: string; + preRender?: (ctx: OffscreenCanvasRenderingContext2D) => Promise; + postRender?: (ctx: OffscreenCanvasRenderingContext2D) => Promise; + color?: string | null; + dir?: string; +}) => { + const { iconRef, iconState, color, dir, preRender, postRender } = props; + + const render = useCallback( + async (canvas: OffscreenCanvas, ctx: OffscreenCanvasRenderingContext2D) => { + // Pixel art please + ctx.imageSmoothingEnabled = false; + + if (preRender) await preRender(ctx); + + const finalDir = dir || '2'; + + // Load the image from the server + let image; + try { + image = await getImage( + `${iconRef}?state=${iconState}&dir=${finalDir}&frame=1`, + ); + } catch (e) { + ctx.fillStyle = '#ff0000'; + ctx.fillRect(0, 0, 64, 64); + return; + } + + // Draw the image to the canvas + ctx.drawImage(image, 0, 0, 64, 64); + + // Draw a square over the image with the color + ctx.globalCompositeOperation = 'multiply'; + ctx.fillStyle = color || '#ffffff'; + ctx.fillRect(0, 0, 64, 64); + + // Use the image as a mask + ctx.globalCompositeOperation = 'destination-in'; + ctx.drawImage(image, 0, 0, 64, 64); + + // Color background white + // ctx.globalCompositeOperation = 'source-out'; + // ctx.fillStyle = '#ffffff'; + // ctx.fillRect(0, 0, 64, 64); + + if (postRender) await postRender(ctx); + }, + [iconRef, iconState, color, preRender, postRender, dir], + ); + + return ; +}; + +export const CustomImageButton = ( + props: PropsWithChildren<{ + image: ReactNode; + tooltip?: string; + selected?: boolean; + onClick: () => void; + buttons?: ReactNode; + }>, +) => { + return ( + + {props.children} + + ); +}; + +export const ColorizedImageButton = ( + props: PropsWithChildren<{ + iconRef: string; + iconState: string; + color?: string | null; + dir?: string; + onClick: () => void; + preRender?: (ctx: OffscreenCanvasRenderingContext2D) => Promise; + postRender?: (ctx: OffscreenCanvasRenderingContext2D) => Promise; + selected?: boolean; + tooltip?: string; + buttons?: ReactNode; + }>, +) => { + const { + iconRef, + iconState, + color, + dir, + onClick, + selected, + preRender, + postRender, + } = props; + + return ( + + } + onClick={onClick} + selected={selected} + tooltip={props.tooltip} + buttons={props.buttons} + > + {props.children} + + ); +}; + +export enum ColorType { + First, + Second, + Third, + Alpha, +} + +export const ColorPicker = (props: { + onClick: (type: ColorType) => void; + color_one?: string | null; + color_two?: string | null; + color_three?: string | null; + alpha?: number; +}) => { + const { onClick, color_one, color_two, color_three, alpha } = props; + + return ( + + + + + {!!color_two && ( + + + + )} + {!!color_three && ( + + + + )} + {alpha !== undefined && ( + + + + )} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/index.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/index.tsx new file mode 100644 index 0000000000..40fd6506e3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/index.tsx @@ -0,0 +1,79 @@ +import { Section } from 'tgui-core/components'; + +import { ServerPreferencesFetcher } from '../ServerPreferencesFetcher'; +import { + type GeneralData, + type GeneralDataConstant, + type GeneralDataStatic, +} from './general/data'; +import { General } from './general/index'; +import { Loadout } from './loadout'; +import { + type LoadoutData, + type LoadoutDataConstant, + type LoadoutDataStatic, +} from './loadout/data'; +import { Occupation } from './occupation'; +import { + type OccupationData, + type OccupationDataConstant, + type OccupationDataStatic, +} from './occupation/data'; + +enum Tabs { + General = 'General', + Occupation = 'Occupation', + SpecialRoles = 'Special Roles', + Loadout = 'Loadout', + Sound = 'Sound', + Vore = 'Vore', +} + +export const BayPrefsEntryPoint = (props: { + type: string; + data: any; + staticData: any; +}) => { + const { type, data, staticData } = props; + + return ( + { + if (!serverData) { + return
; + } + + const relevantData = serverData.legacy; + + switch (type as Tabs) { + case Tabs.General: + return ( + + ); + case Tabs.Loadout: + return ( + + ); + case Tabs.Occupation: + return ( + + ); + default: + return null; + } + }} + /> + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/SubtabEquipment.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/SubtabEquipment.tsx new file mode 100644 index 0000000000..036bb9ee4f --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/SubtabEquipment.tsx @@ -0,0 +1,123 @@ +import { useBackend } from 'tgui/backend'; +import { Box, Button, LabeledList, Stack } from 'tgui-core/components'; + +import { + type LoadoutData, + type LoadoutDataConstant, + type LoadoutDataStatic, +} from './data'; + +export const SubtabEquipment = (props: { + data: LoadoutData; + staticData: LoadoutDataStatic; + serverData: LoadoutDataConstant; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData } = props; + const { + headset_type, + backpack_type, + pda_type, + communicator_visibility, + ringtone, + shoes, + jacket, + } = data; + + const { backbaglist, headsetlist, pdachoicelist } = serverData; + + return ( + + + + {data.underwear.map((underwear, i) => ( + + + {underwear.tweaks.map((tweak) => ( + + ))} + + ))} + + + + + + + + + + + + + + + Headset Type + {headsetlist.map((item, i) => ( + + ))} + + + Backpack Type + {backbaglist.map((item, i) => ( + + ))} + + + PDA Type + {pdachoicelist.map((item, i) => ( + + ))} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/SubtabLoadout.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/SubtabLoadout.tsx new file mode 100644 index 0000000000..8ea5adf9d2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/SubtabLoadout.tsx @@ -0,0 +1,131 @@ +/* eslint-disable react/no-danger */ +import { Fragment, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, Section, Stack, Table } from 'tgui-core/components'; + +import { + type LoadoutData, + type LoadoutDataConstant, + type LoadoutDataStatic, +} from './data'; + +export const SubtabLoadout = (props: { + data: LoadoutData; + staticData: LoadoutDataStatic; + serverData: LoadoutDataConstant; +}) => { + const { act } = useBackend(); + const { data, staticData, serverData } = props; + const { gear_slot, total_cost, active_gear_list, gear_tweaks } = data; + const { categories, max_gear_cost } = staticData; + + const [activeCategory, setActiveCategory] = useState( + Object.keys(categories)[0], + ); + + const activeCategoryItems = categories[activeCategory]; + + return ( + + + + + + + [{gear_slot}] + + + + + + {total_cost}/{max_gear_cost} + + loadout points spent. + + + [] [ + ] + + + + + {Object.keys(categories).map((key) => ( + + ))} + + + + + {activeCategory} + + + + +
+ + {activeCategoryItems.map((item) => ( + + + + + + {item.cost} + + {item.desc} + + + {item.show_roles && item.allowed_roles?.length ? ( + + + Allowed Roles: + + {item.allowed_roles.join(',')} + + ) : null} + {item.name in gear_tweaks ? ( + + + {gear_tweaks[item.name].map((tweak) => ( + + + + ))} + + + ) : null} + + ))} +
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/data.ts new file mode 100644 index 0000000000..aa16723216 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/data.ts @@ -0,0 +1,55 @@ +import { type BooleanLike } from 'tgui-core/react'; + +// Updated every tick +export type UnsafeHtml = string; + +export type GearTweak = { + ref: string; + // Unsafe html + contents: UnsafeHtml; +}; + +export type Underwear = { + category: string; + name: string; + tweaks: GearTweak[]; +}; + +export type LoadoutData = { + underwear: Underwear[]; + + headset_type: string; + backpack_type: string; + pda_type: string; + communicator_visibility: BooleanLike; + ringtone: string; + shoes: BooleanLike; + jacket: BooleanLike; + + // loadout + gear_slot: number; + total_cost: number; + active_gear_list: Record; // we don't actually care about the subkeys + gear_tweaks: Record; +}; + +// Updated every reload/UI open +export type Gear = { + name: string; + desc: string; + cost: number; + show_roles: string; + allowed_roles: string[]; +}; + +export type LoadoutDataStatic = { + categories: Record; + max_gear_cost: number; +}; + +// Never changes +export type LoadoutDataConstant = { + headsetlist: string[]; + backbaglist: string[]; + pdachoicelist: string[]; +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/index.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/index.tsx new file mode 100644 index 0000000000..861137d888 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/loadout/index.tsx @@ -0,0 +1,81 @@ +import { useState } from 'react'; +import { Box, Section, Tabs } from 'tgui-core/components'; + +import { + type LoadoutData, + type LoadoutDataConstant, + type LoadoutDataStatic, +} from './data'; +import { SubtabEquipment } from './SubtabEquipment'; +import { SubtabLoadout } from './SubtabLoadout'; + +export const Loadout = (props: { + data: LoadoutData; + staticData: LoadoutDataStatic; + serverData: LoadoutDataConstant; +}) => { + const { data, staticData, serverData } = props; + + return ( + + ); +}; + +enum Subtabs { + Equipment, + Loadout, +} + +export const LoadoutContent = (props: { + data: LoadoutData; + staticData: LoadoutDataStatic; + serverData: LoadoutDataConstant; +}) => { + const { data, staticData, serverData } = props; + + const [subtab, setSubtab] = useState(Subtabs.Equipment); + + return ( +
+ setSubtab(Subtabs.Equipment)} + > + Equipment + + setSubtab(Subtabs.Loadout)} + > + Loadout + + + } + mt={1} + > + {subtab === Subtabs.Equipment ? ( + + ) : subtab === Subtabs.Loadout ? ( + + ) : ( + Error + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/occupation/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/occupation/data.ts new file mode 100644 index 0000000000..d793111860 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/occupation/data.ts @@ -0,0 +1,42 @@ +import { type BooleanLike } from 'tgui-core/react'; + +export enum AlternateOption { + GET_RANDOM_JOB = 0, + BE_ASSISTANT = 1, + RETURN_TO_LOBBY = 2, +} + +export enum Selected { + Yes = 4, + High = 3, + Medium = 2, + Low = 1, + Never = 0, +} + +export type Job = { + title: string; + ref: string; + selection_color: string; + banned: BooleanLike; + denylist_days: BooleanLike; + available_in_days: number; + denylist_playtime: BooleanLike; + available_in_hours: number; + denylist_whitelist: BooleanLike; + // evil + denylist_character_age: BooleanLike; + min_age: number; + special_color: string; + selected: Selected; + selected_title: string; + alt_titles: string[]; +}; + +export type OccupationData = { + alternate_option: AlternateOption; + jobs: Job[]; +}; + +export type OccupationDataStatic = {}; +export type OccupationDataConstant = {}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/occupation/index.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/occupation/index.tsx new file mode 100644 index 0000000000..786a18bf82 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/occupation/index.tsx @@ -0,0 +1,199 @@ +import { type ReactNode } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, Section, Stack } from 'tgui-core/components'; + +import { + type Job, + type OccupationData, + type OccupationDataConstant, + type OccupationDataStatic, + Selected, +} from './data'; + +export const Occupation = (props: { + data: OccupationData; + staticData: OccupationDataStatic; + serverData: OccupationDataConstant; +}) => { + const { data, staticData, serverData } = props; + + return ( + + ); +}; + +export const OccupationContent = (props: { + data: OccupationData; + staticData: OccupationDataStatic; + serverData: OccupationDataConstant; +}) => { + const { data, staticData, serverData } = props; + const { alternate_option, jobs } = data; + + const departments = Object.keys(jobs); + + const first_half: Job[] = []; + const second_half: Job[] = []; + + for (let i = 0; i < departments.length; i++) { + if (i <= departments.length / 2) { + first_half.push(...jobs[departments[i]]); + } else { + second_half.push(...jobs[departments[i]]); + } + } + + return ( +
+ } + > + + + {first_half.map((job) => ( + + ))} + + + {second_half.map((job) => ( + + ))} + + +
+ ); +}; + +export const selected2text = (selected: Selected) => { + switch (selected) { + case Selected.Yes: + return [YES]; + case Selected.High: + return [HIGH]; + case Selected.Medium: + return [MEDIUM]; + case Selected.Low: + return [LOW]; + case Selected.Never: + return [NEVER]; + } +}; + +export const selected_next = (selected: Selected) => { + switch (selected) { + case Selected.Yes: + return 4; + case Selected.Medium: + return 1; + case Selected.Low: + return 2; + case Selected.Never: + return 3; + } +}; + +export const selected_prev = (selected: Selected) => { + switch (selected) { + case Selected.Yes: + return 4; // to never + case Selected.High: + return 2; // to medium + case Selected.Medium: + return 3; // to low + case Selected.Low: + return 4; // to never + case Selected.Never: + return 4; // to never + } +}; + +export const JobBox = (props: { job: Job }) => { + const { act } = useBackend(); + const { job } = props; + + let disabledReason: ReactNode | null = null; + if (job.banned) { + disabledReason = [BANNED]; + } else if (job.denylist_days) { + disabledReason = [IN {job.available_in_days} DAYS]; + } else if (job.denylist_playtime) { + disabledReason = ( + [IN {job.available_in_hours.toFixed(1)} DEPTHOURS] + ); + } else if (job.denylist_whitelist) { + disabledReason = [WHITELIST ONLY]; + // EVIL + } else if (job.denylist_character_age) { + disabledReason = ( + [MIN CHARACTER AGE FOR SELECTED RACE/BRAIN: {job.min_age}] + ); + } + + return ( + + + + + + + {disabledReason || ( + + + + + {job.alt_titles.length ? ( + + + + ) : null} + + )} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts index 46cd6ff967..279a9faf4c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts @@ -1,4 +1,6 @@ -import type { sendAct } from '../../backend'; +import type { sendAct } from 'tgui/backend'; + +import type { LegacyConstant } from './bay_prefs/data'; export enum GamePreferencesSelectedPage { Settings, @@ -32,5 +34,6 @@ export type PreferencesMenuData = { }; export type ServerData = { + legacy: LegacyConstant; [otheyKey: string]: unknown; }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx index 22df23192c..1630b7a4a1 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx @@ -1,8 +1,6 @@ -// import { exhaustiveCheck } from 'common/exhaustive'; - import { useBackend } from 'tgui/backend'; -// import { CharacterPreferenceWindow } from './CharacterPreferenceWindow'; +import { CharacterPreferenceWindow } from './CharacterPreferenceWindow'; import { GamePreferencesSelectedPage, type PreferencesMenuData, @@ -16,8 +14,8 @@ export const PreferencesMenu = (props) => { const window = data.window; switch (window) { - // case Window.Character: - // return ; + case Window.Character: + return ; case Window.Game: return ; case Window.Keybindings: @@ -26,7 +24,5 @@ export const PreferencesMenu = (props) => { startingPage={GamePreferencesSelectedPage.Keybindings} /> ); - // default: - // exhaustiveCheck(window); } }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx index fa8cb8095b..61d1e9da32 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx @@ -153,3 +153,10 @@ export const ambience_chance: FeatureNumeric = { "The chance you'd like to hear ambience played to you (On area change, or by random ambience). 35 means a 35% chance to play ambience. This is a range from 0-100. 0 disables ambience playing entirely. This is also affected by Ambience Frequency.", component: FeatureNumberInput, }; + +export const media_volume: FeatureNumeric = { + name: 'Jukebox Volume', + category: 'SOUNDS', + description: 'The percentage volume of the Jukebox from 0% to 100%.', + component: FeatureNumberInput, +}; diff --git a/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx b/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx index f852586f1f..bb56196a1e 100644 --- a/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx +++ b/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx @@ -1,6 +1,6 @@ +import { useBackend } from 'tgui/backend'; import { Box, Button, LabeledList, Section } from 'tgui-core/components'; -import { useBackend } from '../../backend'; import type { ActiveBodyRecordData } from './types'; export const BodyRecordModal = (props: { data: ActiveBodyRecordData }) => { diff --git a/tgui/packages/tgui/styles/interfaces/Preferences.scss b/tgui/packages/tgui/styles/interfaces/Preferences.scss new file mode 100644 index 0000000000..af37eb32bb --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/Preferences.scss @@ -0,0 +1,33 @@ +.PreferencesMenu__SaveNotif { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 3em; + z-index: 10; + height: 100px; + overflow: hidden; + color: #00ba00; + user-select: none; +} + +@keyframes slide-in-out { + 0% { + transform: translate(0, 100px); + } + + 50% { + transform: translate(0, 0); + } + + 100% { + transform: translate(0, -100px); + } +} + +.PreferencesMenu__SaveNotif__Inner { + background-color: rgba(0, 0, 0, 0.8); + border-radius: 16px; + padding: 16px; + animation: slide-in-out 1s ease-in-out forwards; +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 52f3d6149f..cd9c975a70 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -41,6 +41,7 @@ @include meta.load-css('./interfaces/NuclearBomb.scss'); @include meta.load-css('./interfaces/Paper.scss'); @include meta.load-css('./interfaces/Pda.scss'); +@include meta.load-css('./interfaces/Preferences.scss'); @include meta.load-css('./interfaces/ResearchConsole.scss'); @include meta.load-css('./interfaces/ResleevingConsole.scss'); @include meta.load-css('./interfaces/RIGSuit.scss'); diff --git a/vorestation.dme b/vorestation.dme index aae00602ab..73041cc7c4 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2164,16 +2164,21 @@ #include "code\modules\client\preference_setup\_defines.dm" #include "code\modules\client\preference_setup\preference_setup.dm" #include "code\modules\client\preference_setup\preference_setup_vr.dm" -#include "code\modules\client\preference_setup\antagonism\01_basic.dm" -#include "code\modules\client\preference_setup\antagonism\02_candidacy.dm" #include "code\modules\client\preference_setup\general\01_basic.dm" #include "code\modules\client\preference_setup\general\02_language.dm" #include "code\modules\client\preference_setup\general\03_body.dm" -#include "code\modules\client\preference_setup\general\04_equipment.dm" #include "code\modules\client\preference_setup\general\05_background.dm" #include "code\modules\client\preference_setup\general\06_flavor.dm" +#include "code\modules\client\preference_setup\general\07_antagonism.dm" +#include "code\modules\client\preference_setup\general\08_candidacy.dm" +#include "code\modules\client\preference_setup\general\09_size.dm" +#include "code\modules\client\preference_setup\general\10_persistence.dm" +#include "code\modules\client\preference_setup\general\11_misc.dm" +#include "code\modules\client\preference_setup\general\12_traits.dm" +#include "code\modules\client\preference_setup\general\13_nif.dm" +#include "code\modules\client\preference_setup\loadout\01_equipment.dm" +#include "code\modules\client\preference_setup\loadout\02_loadout.dm" #include "code\modules\client\preference_setup\loadout\gear_tweaks.dm" -#include "code\modules\client\preference_setup\loadout\loadout.dm" #include "code\modules\client\preference_setup\loadout\loadout_accessories.dm" #include "code\modules\client\preference_setup\loadout\loadout_cosmetics.dm" #include "code\modules\client\preference_setup\loadout\loadout_cyberware.dm" @@ -2193,26 +2198,17 @@ #include "code\modules\client\preference_setup\loadout\loadout_utility.dm" #include "code\modules\client\preference_setup\loadout\loadout_xeno.dm" #include "code\modules\client\preference_setup\occupation\occupation.dm" -#include "code\modules\client\preference_setup\volume_sliders\01_volume.dm" -#include "code\modules\client\preference_setup\volume_sliders\02_media.dm" -#include "code\modules\client\preference_setup\vore\01_ears.dm" -#include "code\modules\client\preference_setup\vore\02_size.dm" -#include "code\modules\client\preference_setup\vore\03_egg.dm" -#include "code\modules\client\preference_setup\vore\04_resleeving.dm" -#include "code\modules\client\preference_setup\vore\05_persistence.dm" -#include "code\modules\client\preference_setup\vore\06_vantag.dm" -#include "code\modules\client\preference_setup\vore\07_traits.dm" -#include "code\modules\client\preference_setup\vore\08_nif.dm" -#include "code\modules\client\preference_setup\vore\09_misc.dm" #include "code\modules\client\preferences\_preference.dm" #include "code\modules\client\preferences\preferences_tg.dm" #include "code\modules\client\preferences\middleware\_middleware.dm" +#include "code\modules\client\preferences\middleware\bay_adapter.dm" #include "code\modules\client\preferences\middleware\random.dm" #include "code\modules\client\preferences\migrations\13_preferences.dm" #include "code\modules\client\preferences\migrations\14_nifs.dm" #include "code\modules\client\preferences\migrations\15_nif_path.dm" #include "code\modules\client\preferences\migrations\16_colors.dm" #include "code\modules\client\preferences\migrations\17_tails.dm" +#include "code\modules\client\preferences\migrations\18_jukebox.dm" #include "code\modules\client\preferences\types\character\antagonism\01_basic.dm" #include "code\modules\client\preferences\types\character\general\01_basic.dm" #include "code\modules\client\preferences\types\character\general\03_body.dm" @@ -3180,10 +3176,6 @@ #include "code\modules\mob\living\carbon\human\update_icons.dm" #include "code\modules\mob\living\carbon\human\ai_controlled\ai_controlled.dm" #include "code\modules\mob\living\carbon\human\ai_controlled\ai_controlled_vr.dm" -#include "code\modules\mob\living\carbon\human\descriptors\_descriptors.dm" -#include "code\modules\mob\living\carbon\human\descriptors\descriptors_generic.dm" -#include "code\modules\mob\living\carbon\human\descriptors\descriptors_skrell.dm" -#include "code\modules\mob\living\carbon\human\descriptors\descriptors_vox.dm" #include "code\modules\mob\living\carbon\human\species\species.dm" #include "code\modules\mob\living\carbon\human\species\species_attack.dm" #include "code\modules\mob\living\carbon\human\species\species_getters.dm" @@ -3595,7 +3587,6 @@ #include "code\modules\mob\new_player\news.dm" #include "code\modules\mob\new_player\poll.dm" #include "code\modules\mob\new_player\preferences_setup.dm" -#include "code\modules\mob\new_player\preferences_setup_vr.dm" #include "code\modules\mob\new_player\sprite_accessories.dm" #include "code\modules\mob\new_player\sprite_accessories_beards.dm" #include "code\modules\mob\new_player\sprite_accessories_ear.dm"