#define DEFAULT_SLOT_AMT 2 #define HANDS_SLOT_AMT 2 #define BACKPACK_SLOT_AMT 4 GLOBAL_LIST_EMPTY(preferences_datums) /datum/preferences var/client/parent //doohickeys for savefiles var/path var/vr_path var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used var/max_save_slots = 24 // Intra-round persistence begin /// Flags for admin mutes var/muted = NONE /// Last IP the person was seen on var/last_ip /// Last CID the person was seen on var/last_id /// Do we log their clicks to disk? var/log_clicks = FALSE /// Characters they have joined the round under - Lazylist of names var/list/characters_joined_as /// Slots they have joined the round under - Lazylist of numbers var/list/slots_joined_as /// Are we currently subject to respawn restrictions? Usually set by us using the "respawn" verb, but can be lifted by admins. var/respawn_restrictions_active = FALSE /// time of death we consider for respawns var/respawn_time_of_death = -INFINITY /// did they DNR? used to prevent respawns. var/dnr_triggered = FALSE /// did they cryo on their last ghost? var/respawn_did_cryo = FALSE // Intra-round persistence end var/icon/custom_holoform_icon var/list/cached_holoform_icons var/last_custom_holoform = 0 //Cooldowns for saving/loading. These are four are all separate due to loading code calling these one after another var/saveprefcooldown var/loadprefcooldown var/savecharcooldown var/loadcharcooldown //game-preferences var/lastchangelog = "" //Saved changlog filesize to detect if there was a change var/ooccolor = "#c43b23" var/aooccolor = "#ce254f" var/enable_tips = TRUE var/tip_delay = 500 //tip delay in milliseconds //Antag preferences var/list/be_special = list() //Special role selection. ROLE_SYNDICATE being missing means they will never be antag! var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, //autocorrected this round, not that you'd need to check that. var/UI_style = null var/outline_enabled = TRUE var/outline_color = COLOR_THEME_MIDNIGHT var/screentip_pref = SCREENTIP_PREFERENCE_ENABLED var/screentip_color = "#ffd391" var/screentip_images = TRUE var/buttons_locked = FALSE var/hotkeys = FALSE ///Runechat preference. If true, certain messages will be displayed on the map, not ust on the chat area. Boolean. var/chat_on_map = TRUE ///Limit preference on the size of the message. Requires chat_on_map to have effect. var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH ///Whether non-mob messages will be displayed, such as machine vendor announcements. Requires chat_on_map to have effect. Boolean. var/see_chat_non_mob = TRUE ///Whether emotes will be displayed on runechat. Requires chat_on_map to have effect. Boolean. var/see_rc_emotes = TRUE /// Custom Keybindings var/list/key_bindings = list() /// List with a key string associated to a list of keybindings. Unlike key_bindings, this one operates on raw key, allowing for binding a key that triggers regardless of if a modifier is depressed as long as the raw key is sent. var/list/modless_key_bindings = list() var/tgui_fancy = TRUE var/tgui_lock = TRUE var/windowflashing = TRUE var/toggles = TOGGLES_DEFAULT /// A separate variable for deadmin toggles, only deals with those. var/deadmin = NONE var/db_flags var/chat_toggles = TOGGLES_DEFAULT_CHAT var/ghost_form = "ghost" var/ghost_orbit = GHOST_ORBIT_CIRCLE var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION var/ghost_hud = 1 var/inquisitive_ghost = 1 var/allow_midround_antag = 1 var/preferred_map = null var/preferred_chaos = null var/pda_style = MONO var/pda_color = "#808000" var/pda_skin = PDA_SKIN_ALT var/uses_glasses_colour = 0 //character preferences var/real_name //our character's name var/nameless = FALSE //whether or not our character is nameless var/be_random_name = 0 //whether we'll have a random name every round var/be_random_body = 0 //whether we'll have a random body every round var/gender = MALE //gender of character (well duh) var/age = 30 //age of character var/underwear = "Nude" //underwear type var/undie_color = "FFFFFF" var/undershirt = "Nude" //undershirt type var/shirt_color = "FFFFFF" var/socks = "Nude" //socks type var/socks_color = "FFFFFF" var/backbag = DBACKPACK //backpack type var/jumpsuit_style = PREF_SUIT //suit/skirt var/hair_style = "Bald" //Hair type var/hair_color = "000000" //Hair color var/facial_hair_style = "Shaved" //Face hair type var/facial_hair_color = "000000" //Facial hair color var/grad_style //Hair gradient style var/grad_color = "FFFFFF" //Hair gradient color var/skin_tone = "caucasian1" //Skin color var/use_custom_skin_tone = FALSE var/left_eye_color = "000000" //Eye color var/right_eye_color = "000000" var/eye_type = DEFAULT_EYES_TYPE //Eye type var/split_eye_colors = FALSE var/datum/species/pref_species = new /datum/species/human() //Mutant race var/list/features = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF", "mcolor3" = "FFFFFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "horns_color" = "85615a", "ears" = "None", "wings" = "None", "wings_color" = "FFF", "frills" = "None", "deco_wings" = "None", "spines" = "None", "legs" = "Plantigrade", "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", "arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain", "mam_body_markings" = list(), "mam_ears" = "None", "mam_snouts" = "None", "mam_tail" = "None", "mam_tail_animated" = "None", "xenodorsal" = "Standard", "xenohead" = "Standard", "xenotail" = "Xenomorph Tail", "taur" = "None", "genitals_use_skintone" = FALSE, "has_cock" = FALSE, "cock_shape" = DEF_COCK_SHAPE, "cock_length" = COCK_SIZE_DEF, "cock_diameter_ratio" = COCK_DIAMETER_RATIO_DEF, "cock_color" = "ffffff", "cock_taur" = FALSE, "has_balls" = FALSE, "balls_color" = "ffffff", "balls_shape" = DEF_BALLS_SHAPE, "balls_size" = BALLS_SIZE_DEF, "balls_cum_rate" = CUM_RATE, "balls_cum_mult" = CUM_RATE_MULT, "balls_efficiency" = CUM_EFFICIENCY, "has_breasts" = FALSE, "breasts_color" = "ffffff", "breasts_size" = BREASTS_SIZE_DEF, "breasts_shape" = DEF_BREASTS_SHAPE, "breasts_producing" = FALSE, "has_vag" = FALSE, "vag_shape" = DEF_VAGINA_SHAPE, "vag_color" = "ffffff", "has_womb" = FALSE, "has_butt" = FALSE, "butt_color" = "ffffff", "butt_size" = BUTT_SIZE_DEF, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_visibility" = GEN_VISIBLE_NO_UNDIES, "butt_visibility" = GEN_VISIBLE_NO_UNDIES, "ipc_screen" = "Sunburst", "ipc_antenna" = "None", "flavor_text" = "", "silicon_flavor_text" = "", "ooc_notes" = "", "meat_type" = "Mammalian", "body_model" = MALE, "body_size" = RESIZE_DEFAULT_SIZE, "color_scheme" = OLD_CHARACTER_COLORING) var/custom_speech_verb = "default" //if your say_mod is to be something other than your races var/custom_tongue = "default" //if your tongue is to be something other than your races var/additional_language = "None" //additional language your character has var/modified_limbs = list() //prosthetic/amputated limbs var/chosen_limb_id //body sprite selected to load for the users limbs, null means default, is sanitized when loaded // Vocal bark prefs var/bark_id = "mutedc3" var/bark_speed = 4 var/bark_pitch = 1 var/bark_variance = 0.2 COOLDOWN_DECLARE(bark_previewing) /// Security record note section var/security_records /// Medical record note section var/medical_records var/list/custom_names = list() var/preferred_ai_core_display = "Blue" var/prefered_security_department = SEC_DEPT_RANDOM var/custom_species = null //Quirk list var/list/all_quirks = list() //Quirk category currently selected var/quirk_category = QUIRK_POSITIVE // defaults to positive, the first tab! //Job preferences 2.0 - indexed by job title , no key or value implies never var/list/job_preferences = list() // Want randomjob if preferences already filled - Donkie var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants // 0 = character settings, 1 = game preferences var/current_tab = SETTINGS_TAB var/unlock_content = 0 var/list/ignoring = list() var/clientfps = 0 var/parallax = PARALLAX_INSANE var/ambientocclusion = TRUE ///Should we automatically fit the viewport? var/auto_fit_viewport = FALSE ///Should we be in the widescreen mode set by the config? var/widescreenpref = TRUE ///Strip menu style var/long_strip_menu = FALSE ///What size should pixels be displayed as? 0 is strech to fit var/pixel_size = 0 ///What scaling method should we use? var/scaling_method = "normal" var/uplink_spawn_loc = UPLINK_PDA ///The playtime_reward_cloak variable can be set to TRUE from the prefs menu only once the user has gained over 5K playtime hours. If true, it allows the user to get a cool looking roundstart cloak. var/playtime_reward_cloak = FALSE var/hud_toggle_flash = TRUE var/hud_toggle_color = "#ffffff" var/list/exp = list() var/list/menuoptions var/action_buttons_screen_locs = list() //bad stuff var/vore_flags = 0 var/list/belly_prefs = list() var/vore_taste = "nothing in particular" var/vore_smell = null var/toggleeatingnoise = TRUE var/toggledigestionnoise = TRUE var/hound_sleeper = TRUE var/cit_toggles = TOGGLES_CITADEL //backgrounds var/mutable_appearance/character_background var/icon/bgstate = "steel" var/list/bgstate_options = list("000", "midgrey", "FFF", "white", "steel", "techmaint", "dark", "plating", "reinforced") var/show_mismatched_markings = FALSE //determines whether or not the markings lists should show markings that don't match the currently selected species. Intentionally left unsaved. var/no_tetris_storage = FALSE ///loadout stuff var/gear_points = 10 var/list/gear_categories var/list/loadout_data = list() var/list/unlockable_loadout_data = list() var/loadout_slot = 1 //goes from 1 to MAXIMUM_LOADOUT_SAVES var/gear_category var/gear_subcategory var/screenshake = 100 var/damagescreenshake = 2 var/recoil_screenshake = 100 var/arousable = TRUE var/autostand = TRUE var/auto_ooc = FALSE ///This var stores the amount of points the owner will get for making it out alive. var/hardcore_survival_score = 0 ///Someone thought we were nice! We get a little heart in OOC until we join the server past the below time (we can keep it until the end of the round otherwise) var/hearted ///If we have a hearted commendations, we honor it every time the player loads preferences until this time has been passed var/hearted_until /// If we have persistent scars enabled var/persistent_scars = TRUE ///If we want to broadcast deadchat connect/disconnect messages var/broadcast_login_logout = TRUE ///What outfit typepaths we've favorited in the SelectEquipment menu var/list/favorite_outfits = list() /// We have 5 slots for persistent scars, if enabled we pick a random one to load (empty by default) and scars at the end of the shift if we survived as our original person var/list/scars_list = list("1" = "", "2" = "", "3" = "", "4" = "", "5" = "") /// Which of the 5 persistent scar slots we randomly roll to load for this round, if enabled. Actually rolled in [/datum/preferences/proc/load_character(slot)] var/scars_index = 1 var/hide_ckey = FALSE //pref for hiding if your ckey shows round-end or not var/list/tcg_cards = list() var/list/tcg_decks = list() /datum/preferences/New(client/C) parent = C for(var/custom_name_id in GLOB.preferences_custom_names) custom_names[custom_name_id] = get_default_name(custom_name_id) UI_style = GLOB.available_ui_styles[1] if(istype(C)) if(!IsGuestKey(C.key)) load_path(C.ckey) unlock_content = C.IsByondMember() if(unlock_content) max_save_slots = 32 var/loaded_preferences_successfully = load_preferences() if(loaded_preferences_successfully) if(load_character()) return //we couldn't load character data so just randomize the character appearance + name random_character() //let's create a random character then - rather than a fat, bald and naked man. key_bindings = deepCopyList(GLOB.hotkey_keybinding_list_by_key) // give them default keybinds and update their movement keys C?.ensure_keys_set(src) real_name = pref_species.random_name(gender,1) if(!loaded_preferences_successfully) save_preferences() save_character() //let's save this new random character so it doesn't keep generating new ones. menuoptions = list() return #define APPEARANCE_CATEGORY_COLUMN "" #define MAX_MUTANT_ROWS 5 /datum/preferences/proc/ShowChoices(mob/user) if(!user || !user.client) return update_preview_icon(current_tab) var/list/dat = list("
") dat += "Character Settings" dat += "Character Appearance" dat += "Character Speech" dat += "Loadout" dat += "Game Preferences" dat += "Content Preferences" dat += "Keybindings" if(!path) dat += "
Please create an account to save your preferences
" dat += "
" dat += "
" switch(current_tab) if(SETTINGS_TAB) // Character Settings# if(path) var/savefile/S = new /savefile(path) if(S) dat += "
" var/name var/unspaced_slots = 0 for(var/i=1, i<=max_save_slots, i++) unspaced_slots++ if(unspaced_slots > 4) dat += "
" unspaced_slots = 0 S.cd = "/character[i]" S["real_name"] >> name if(!name) name = "Character[i]" dat += "[name] " dat += "
" dat += "

Occupation Choices

" dat += "Set Occupation Preferences
" if(CONFIG_GET(flag/roundstart_traits)) dat += "

Quirk Setup

" dat += "Configure Quirks
" dat += "
Current Quirks: [all_quirks.len ? all_quirks.Join(", ") : "None"]
" dat += "

Identity

" dat += "" dat += "
Records
" dat += "
Security Records
" if(length_char(security_records) <= 40) if(!length(security_records)) dat += "\[...\]" else dat += "[security_records]" else dat += "[TextPreview(security_records)]...
" dat += "
Medical Records
" if(length_char(medical_records) <= 40) if(!length(medical_records)) dat += "\[...\]
" else dat += "[medical_records]" else dat += "[TextPreview(medical_records)]...
" dat += "
Hide ckey: [hide_ckey ? "Enabled" : "Disabled"]
" dat += "
" if(jobban_isbanned(user, "appearance")) dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" dat += "Random Name " dat += "Always Random Name:[be_random_name ? "Yes" : "No"]
" dat += "[nameless ? "Default designation" : "Name"]:" dat += "[real_name]
" dat += "Be nameless: [nameless ? "Yes" : "No"]
" dat += "Gender: [gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]
" dat += "Age: [age]
" dat += "Special Names:
" var/old_group for(var/custom_name_id in GLOB.preferences_custom_names) var/namedata = GLOB.preferences_custom_names[custom_name_id] if(!old_group) old_group = namedata["group"] else if(old_group != namedata["group"]) old_group = namedata["group"] dat += "
" dat += "[namedata["pref_name"]]: [custom_names[custom_name_id]] " dat += "

" dat += "Custom job preferences:
" dat += "Preferred AI Core Display: [preferred_ai_core_display]
" dat += "Preferred Security Department: [prefered_security_department]
" //Character Appearance if(APPEARANCE_TAB) if(path) var/savefile/S = new /savefile(path) if(S) dat += "
" var/name var/unspaced_slots = 0 for(var/i=1, i<=max_save_slots, i++) unspaced_slots++ if(unspaced_slots > 4) dat += "
" unspaced_slots = 0 S.cd = "/character[i]" S["real_name"] >> name if(!name) name = "Character[i]" dat += "[name] " dat += "
" dat += "" else dat += "

Left Eye Color

" dat += "   Change" dat += "

Right Eye Color

" dat += "   Change
" dat += "" else if(use_skintones || mutant_colors) dat += "" if(HAIR in pref_species.species_traits) dat += APPEARANCE_CATEGORY_COLUMN dat += "

Hair Style

" dat += "[hair_style]" dat += "<>
" dat += "   Change
" dat += "

Facial Hair Style

" dat += "[facial_hair_style]" dat += "<>
" dat += "   Change
" dat += "

Hair Gradient

" dat += "[grad_style]" dat += "<>
" dat += "   Change
" dat += "" //Mutant stuff var/mutant_category = 0 dat += APPEARANCE_CATEGORY_COLUMN dat += "

Show mismatched markings

" dat += "[show_mismatched_markings ? "Yes" : "No"]" mutant_category++ if(mutant_category >= MAX_MUTANT_ROWS) //just in case someone sets the max rows to 1 or something dumb like that dat += "" mutant_category = 0 // rp marking selection // assume you can only have mam markings or regular markings or none, never both var/marking_type if(parent.can_have_part("mam_body_markings")) marking_type = "mam_body_markings" if(marking_type) dat += APPEARANCE_CATEGORY_COLUMN dat += "

[GLOB.all_mutant_parts[marking_type]]

" // give it the appropriate title for the type of marking dat += "Add marking" // list out the current markings you have if(length(features[marking_type])) dat += "
" dat += "

Flavor Text

" dat += "Set Examine Text
" if(length(features["flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN) if(!length(features["flavor_text"])) dat += "\[...\]" else dat += "[features["flavor_text"]]" else dat += "[TextPreview(features["flavor_text"])]...
" dat += "

Silicon Flavor Text

" dat += "Set Silicon Examine Text
" if(length(features["silicon_flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN) if(!length(features["silicon_flavor_text"])) dat += "\[...\]" else dat += "[features["silicon_flavor_text"]]" else dat += "[TextPreview(features["silicon_flavor_text"])]...
" dat += "

OOC notes

" dat += "Set OOC notes
" var/ooc_notes_len = length(features["ooc_notes"]) if(ooc_notes_len <= MAX_FLAVOR_PREVIEW_LEN) if(!ooc_notes_len) dat += "\[...\]" else dat += "[features["ooc_notes"]]" else dat += "[TextPreview(features["ooc_notes"])]...
" dat += "

Body

" dat += "Gender:[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]
" if(gender != NEUTER && pref_species.sexes) dat += "Body Model:[features["body_model"] == MALE ? "Masculine" : "Feminine"]
" dat += "Limb Modification:
" dat += "Modify Limbs
" for(var/modification in modified_limbs) if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC) dat += "[modification]: [modified_limbs[modification][2]]
" else dat += "[modification]: [modified_limbs[modification][1]]
" dat += "
" dat += "Species:[pref_species.name]
" dat += "Custom Species Name:[custom_species ? custom_species : "None"]
" dat += "Random Body:Randomize!
" dat += "Always Random Body:[be_random_body ? "Yes" : "No"]
" dat += "
Cycle background:[bgstate]
" var/use_skintones = pref_species.use_skintones if(use_skintones) dat += APPEARANCE_CATEGORY_COLUMN dat += "

Skin Tone

" dat += "[use_custom_skin_tone ? "custom:    " : skin_tone]
" var/mutant_colors if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits)) if(!use_skintones) dat += APPEARANCE_CATEGORY_COLUMN dat += "

Advanced Coloring

" dat += "[(features["color_scheme"] == ADVANCED_CHARACTER_COLORING) ? "Enabled" : "Disabled"]" dat += "

Body Colors

" dat += "Primary Color:
" dat += "    Change
" dat += "Secondary Color:
" dat += "    Change
" dat += "Tertiary Color:
" dat += "    Change
" mutant_colors = TRUE dat += "Sprite Size: [features["body_size"]*100]%
" if(!(NOEYES in pref_species.species_traits)) dat += "

Eye Type

" dat += "[eye_type]
" if((EYECOLOR in pref_species.species_traits)) if(!use_skintones && !mutant_colors) dat += APPEARANCE_CATEGORY_COLUMN if(left_eye_color != right_eye_color) split_eye_colors = TRUE dat += "

Heterochromia

" dat += "[split_eye_colors ? "Enabled" : "Disabled"]" if(!split_eye_colors) dat += "

Eye Color

" dat += "    Change" dat += "
" var/list/markings = features[marking_type] if(!islist(markings)) // something went terribly wrong markings = list() var/list/reverse_markings = reverseList(markings) for(var/list/marking_list in reverse_markings) var/marking_index = markings.Find(marking_list) // consider changing loop to go through indexes over lists instead of using Find here var/limb_value = marking_list[1] var/actual_name = GLOB.bodypart_names[num2text(limb_value)] // get the actual name from the bitflag representing the part the marking is applied to var/color_marking_dat = "" var/number_colors = 1 var/datum/sprite_accessory/mam_body_markings/S = GLOB.mam_body_markings_list[marking_list[2]] var/matrixed_sections = S.covered_limbs[actual_name] if(S && matrixed_sections) // if it has nothing initialize it to white if(length(marking_list) == 2) var/first = "#FFFFFF" var/second = "#FFFFFF" var/third = "#FFFFFF" if(features["mcolor"]) first = "#[features["mcolor"]]" if(features["mcolor2"]) second = "#[features["mcolor2"]]" if(features["mcolor3"]) third = "#[features["mcolor3"]]" marking_list += list(list(first, second, third)) // just assume its 3 colours if it isnt it doesnt matter we just wont use the other values // index magic var/primary_index = 1 var/secondary_index = 2 var/tertiary_index = 3 switch(matrixed_sections) if(MATRIX_GREEN) primary_index = 2 if(MATRIX_BLUE) primary_index = 3 if(MATRIX_RED_BLUE) secondary_index = 2 if(MATRIX_GREEN_BLUE) primary_index = 2 secondary_index = 3 // we know it has one matrixed section at minimum color_marking_dat += "   " // if it has a second section, add it if(matrixed_sections == MATRIX_RED_BLUE || matrixed_sections == MATRIX_GREEN_BLUE || matrixed_sections == MATRIX_RED_GREEN || matrixed_sections == MATRIX_ALL) color_marking_dat += "   " number_colors = 2 // if it has a third section, add it if(matrixed_sections == MATRIX_ALL) color_marking_dat += "   " number_colors = 3 color_marking_dat += " Change
" dat += "" dat += "
[marking_list[2]] - [actual_name] ˄ ˅ X [color_marking_dat]
" for(var/mutant_part in GLOB.all_mutant_parts) if(mutant_part == "mam_body_markings") continue if(parent.can_have_part(mutant_part)) if(!mutant_category) dat += APPEARANCE_CATEGORY_COLUMN dat += "

[GLOB.all_mutant_parts[mutant_part]]

" dat += "[features[mutant_part]]" var/color_type = GLOB.colored_mutant_parts[mutant_part] //if it can be coloured, show the appropriate button if(color_type) dat += "    Change
" else if(features["color_scheme"] == ADVANCED_CHARACTER_COLORING) //advanced individual part colouring system //is it matrixed or does it have extra parts to be coloured? var/find_part = features[mutant_part] || pref_species.mutant_bodyparts[mutant_part] var/find_part_list = GLOB.mutant_reference_list[mutant_part] if(find_part && find_part != "None" && find_part_list) var/datum/sprite_accessory/accessory = find_part_list[find_part] if(accessory) if(accessory.color_src == MATRIXED || accessory.color_src == MUTCOLORS || accessory.color_src == MUTCOLORS2 || accessory.color_src == MUTCOLORS3) //mutcolors1-3 are deprecated now, please don't rely on these in the future var/mutant_string = accessory.mutant_part_string var/primary_feature = "[mutant_string]_primary" var/secondary_feature = "[mutant_string]_secondary" var/tertiary_feature = "[mutant_string]_tertiary" if(!features[primary_feature]) features[primary_feature] = features["mcolor"] if(!features[secondary_feature]) features[secondary_feature] = features["mcolor2"] if(!features[tertiary_feature]) features[tertiary_feature] = features["mcolor3"] var/matrixed_sections = accessory.matrixed_sections if(accessory.color_src == MATRIXED && !matrixed_sections) message_admins("Sprite Accessory Failure (customization): Accessory [accessory.type] is a matrixed item without any matrixed sections set!") continue else if(accessory.color_src == MATRIXED) switch(matrixed_sections) if(MATRIX_GREEN) //only composed of a green section primary_feature = secondary_feature //swap primary for secondary, so it properly assigns the second colour, reserved for the green section if(MATRIX_BLUE) primary_feature = tertiary_feature //same as above, but the tertiary feature is for the blue section if(MATRIX_RED_BLUE) //composed of a red and blue section secondary_feature = tertiary_feature //swap secondary for tertiary, as blue should always be tertiary if(MATRIX_GREEN_BLUE) //composed of a green and blue section primary_feature = secondary_feature //swap primary for secondary, as first option is green, which is linked to the secondary secondary_feature = tertiary_feature //swap secondary for tertiary, as second option is blue, which is linked to the tertiary dat += "Primary Color
" dat += "    Change
" if((accessory.color_src == MATRIXED && (matrixed_sections == MATRIX_RED_BLUE || matrixed_sections == MATRIX_GREEN_BLUE || matrixed_sections == MATRIX_RED_GREEN || matrixed_sections == MATRIX_ALL)) || (accessory.extra && (accessory.extra_color_src == MUTCOLORS || accessory.extra_color_src == MUTCOLORS2 || accessory.extra_color_src == MUTCOLORS3))) dat += "Secondary Color
" dat += "    Change
" if((accessory.color_src == MATRIXED && matrixed_sections == MATRIX_ALL) || (accessory.extra2 && (accessory.extra2_color_src == MUTCOLORS || accessory.extra2_color_src == MUTCOLORS2 || accessory.extra2_color_src == MUTCOLORS3))) dat += "Tertiary Color
" dat += "    Change
" mutant_category++ if(mutant_category >= MAX_MUTANT_ROWS) dat += "" mutant_category = 0 if(length(pref_species.allowed_limb_ids)) if(!chosen_limb_id || !(chosen_limb_id in pref_species.allowed_limb_ids)) chosen_limb_id = pref_species.limbs_id || pref_species.id dat += "

Body sprite

" dat += "[chosen_limb_id]" if(mutant_category) dat += "" mutant_category = 0 dat += "" dat += "" dat += "" dat +="" dat += APPEARANCE_CATEGORY_COLUMN dat += "

Breasts

" dat += "[features["has_breasts"] == TRUE ? "Yes" : "No"]" if(features["has_breasts"]) if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) dat += "Color:
" dat += "   (Skin tone overriding)
" else dat += "Color:
" dat += "   Change
" dat += "Cup Size:[features["breasts_size"]]" dat += "Breasts Shape:[features["breasts_shape"]]" dat += "Breasts Visibility:[features["breasts_visibility"]]" dat += "Lactates:[features["breasts_producing"] == TRUE ? "Yes" : "No"]" dat += "" dat += APPEARANCE_CATEGORY_COLUMN dat += "

Butt

" dat += "[features["has_butt"] == TRUE ? "Yes" : "No"]" if(features["has_butt"]) if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) dat += "Color:
" dat += "   (Skin tone overriding)
" else dat += "Color:
" dat += "   Change
" dat += "Butt Size:[features["butt_size"]]" dat += "Butt Visibility:[features["butt_visibility"]]" dat += "" dat += "" dat += "
" dat += "

Clothing & Equipment

" dat += "Underwear:[underwear]" if(GLOB.underwear_list[underwear]?.has_color) dat += "Underwear Color:     Change
" dat += "Undershirt:[undershirt]" if(GLOB.undershirt_list[undershirt]?.has_color) dat += "Undershirt Color:     Change
" dat += "Socks:[socks]" if(GLOB.socks_list[socks]?.has_color) dat += "Socks Color:     Change
" dat += "Backpack:[backbag]" dat += "Jumpsuit:
[jumpsuit_style]
" if((HAS_FLESH in pref_species.species_traits) || (HAS_BONE in pref_species.species_traits)) dat += "
Temporal Scarring:
[(persistent_scars) ? "Enabled" : "Disabled"]" dat += "Clear scar slots" dat += "Uplink Location:[uplink_spawn_loc]" dat += "
" if(NOGENITALS in pref_species.species_traits) dat += "Your species ([pref_species.name]) does not support genitals!
" else if(pref_species.use_skintones) dat += "Genitals use skintone:[features["genitals_use_skintone"] == TRUE ? "Yes" : "No"]" dat += "

Penis

" dat += "[features["has_cock"] == TRUE ? "Yes" : "No"]" if(features["has_cock"]) if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) dat += "Penis Color:
" dat += "   (Skin tone overriding)
" else dat += "Penis Color:
" dat += "    Change
" var/tauric_shape = FALSE if(features["cock_taur"]) var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[features["cock_shape"]] if(P.taur_icon && parent.can_have_part("taur")) var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]] if(T.taur_mode & P.accepted_taurs) tauric_shape = TRUE dat += "Penis Shape: [features["cock_shape"]][tauric_shape ? " (Taur)" : ""]" dat += "Penis Length: [features["cock_length"]] inch(es)" dat += "Penis Visibility:[features["cock_visibility"]]" dat += "Has Testicles:[features["has_balls"] == TRUE ? "Yes" : "No"]" if(features["has_balls"]) if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) dat += "Testicles Color:
" dat += "   (Skin tone overriding)
" else dat += "Testicles Color:
" dat += "    Change
" dat += "Testicles Shape: [features["balls_shape"]]" dat += "Testicles Visibility:[features["balls_visibility"]]" dat += APPEARANCE_CATEGORY_COLUMN dat += "

Vagina

" dat += "[features["has_vag"] == TRUE ? "Yes" : "No"]" if(features["has_vag"]) dat += "Vagina Type: [features["vag_shape"]]" if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) dat += "Vagina Color:
" dat += "   (Skin tone overriding)
" else dat += "Vagina Color:
" dat += "    Change
" dat += "Vagina Visibility:[features["vag_visibility"]]" dat += "Has Womb:[features["has_womb"] == TRUE ? "Yes" : "No"]" dat += "
" if(SPEECH_TAB) if(path) var/savefile/S = new /savefile(path) if(S) dat += "
" var/name var/unspaced_slots = 0 for(var/i=1, i<=max_save_slots, i++) unspaced_slots++ if(unspaced_slots > 4) dat += "
" unspaced_slots = 0 S.cd = "/character[i]" S["real_name"] >> name if(!name) name = "Character[i]" dat += "[name] " dat += "
" dat += "" dat += "" dat += "
" dat += "

Speech preferences

" dat += "Custom Speech Verb:
" dat += "[custom_speech_verb]
" dat += "Custom Tongue:
" dat += "[custom_tongue]
" dat += "Additional Language
" dat += "[additional_language]
" dat += "
" dat += "

Vocal Bark preferences

" var/datum/bark/B = GLOB.bark_list[bark_id] dat += "Vocal Bark Sound:
" dat += "[B ? initial(B.name) : "INVALID"]
" dat += "Vocal Bark Speed: [bark_speed]
" dat += "Vocal Bark Pitch: [bark_pitch]
" dat += "Vocal Bark Variance: [bark_variance]
" dat += "
Preview Bark
" dat += "
" if(GAME_PREFERENCES_TAB) // Game Preferences dat += "" if(user.client.holder) dat +="" dat +="" dat += "
" dat += "

General Settings

" dat += "UI Style: [UI_style]
" dat += "Outline: [outline_enabled ? "Enabled" : "Disabled"]
" dat += "Outline Color: [outline_color ? "" : "Theme-based (null)"]    Change
" dat += "Screentip: [screentip_pref]
" dat += "Screentip Color:     Change
" dat += "\ Screentip context with images: [screentip_images ? "Allowed" : "Disallowed"]
" dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
" dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" dat += "Show Runechat Chat Bubbles: [chat_on_map ? "Enabled" : "Disabled"]
" dat += "Runechat message char limit: [max_chat_length]
" dat += "See Runechat for non-mobs: [see_chat_non_mob ? "Enabled" : "Disabled"]
" dat += "
" dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
" dat += "
" dat += "PDA Color:     Change
" dat += "PDA Style: [pda_style]
" dat += "PDA Reskin: [pda_skin]
" dat += "
" dat += "Ghost Ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" dat += "Ghost Radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "All Messages":"No Messages"]
" dat += "Ghost Sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" dat += "Ghost Whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
" dat += "Ghost PDA: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
" dat += "Window Flashing: [(windowflashing) ? "Enabled":"Disabled"]
" dat += "
" dat += "Play Admin MIDIs: [(toggles & SOUND_MIDI) ? "Enabled":"Disabled"]
" dat += "Play Lobby Music: [(toggles & SOUND_LOBBY) ? "Enabled":"Disabled"]
" dat += "See Pull Requests: [(chat_toggles & CHAT_PULLR) ? "Enabled":"Disabled"]
" dat += "
" if(user.client) if(unlock_content) dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" if(unlock_content || check_rights_for(user.client, R_ADMIN)) dat += "OOC Color:     Change
" dat += "Antag OOC Color:     Change
" dat += "
" dat += "

Admin Settings

" dat += "Adminhelp Sounds: [(toggles & SOUND_ADMINHELP)?"Enabled":"Disabled"]
" dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"Enabled":"Disabled"]
" dat += "
" dat += "Combo HUD Lighting: [(toggles & COMBOHUD_LIGHTING)?"Full-bright":"No Change"]
" dat += "
" //deadmin dat += "

Deadmin While Playing

" if(CONFIG_GET(flag/auto_deadmin_players)) dat += "Always Deadmin: FORCED
" else dat += "Always Deadmin: [(deadmin & DEADMIN_ALWAYS)?"Enabled":"Disabled"]
" if(!(deadmin & DEADMIN_ALWAYS)) dat += "
" if(!CONFIG_GET(flag/auto_deadmin_antagonists)) dat += "As Antag: [(deadmin & DEADMIN_ANTAGONIST)?"Deadmin":"Keep Admin"]
" else dat += "As Antag: FORCED
" if(!CONFIG_GET(flag/auto_deadmin_heads)) dat += "As Command: [(deadmin & DEADMIN_POSITION_HEAD)?"Deadmin":"Keep Admin"]
" else dat += "As Command: FORCED
" if(!CONFIG_GET(flag/auto_deadmin_security)) dat += "As Security: [(deadmin & DEADMIN_POSITION_SECURITY)?"Deadmin":"Keep Admin"]
" else dat += "As Security: FORCED
" if(!CONFIG_GET(flag/auto_deadmin_silicons)) dat += "As Silicon: [(deadmin & DEADMIN_POSITION_SILICON)?"Deadmin":"Keep Admin"]
" else dat += "As Silicon: FORCED
" dat += "
" dat += "

Citadel Preferences

" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe. dat += "Widescreen: [widescreenpref ? "Enabled ([CONFIG_GET(string/default_view)])" : "Disabled (15x15)"]
" dat += "Long strip menu: [long_strip_menu ? "Enabled" : "Disabled"]
" dat += "Auto stand: [autostand ? "Enabled" : "Disabled"]
" dat += "Auto OOC: [auto_ooc ? "Enabled" : "Disabled"]
" dat += "Force Slot Storage HUD: [no_tetris_storage ? "Enabled" : "Disabled"]
" dat += "Screen Shake: [(screenshake==100) ? "Full" : ((screenshake==0) ? "None" : "[screenshake]")]
" if (user && user.client && !user.client.prefs.screenshake==0) dat += "Damage Screen Shake: [(damagescreenshake==1) ? "On" : ((damagescreenshake==0) ? "Off" : "Only when down")]
" dat += "Recoil Screen Push: [(recoil_screenshake==100) ? "Full" : ((recoil_screenshake==0) ? "None" : "[screenshake]")]
" var/p_chaos if (!preferred_chaos) p_chaos = "No preference" else p_chaos = preferred_chaos dat += "Preferred Chaos Amount: [p_chaos]
" dat += "
" dat += "
" if(unlock_content) dat += "Ghost Form: [ghost_form]
" dat += "Ghost Orbit: [ghost_orbit]
" var/button_name = "If you see this something went wrong." switch(ghost_accs) if(GHOST_ACCS_FULL) button_name = GHOST_ACCS_FULL_NAME if(GHOST_ACCS_DIR) button_name = GHOST_ACCS_DIR_NAME if(GHOST_ACCS_NONE) button_name = GHOST_ACCS_NONE_NAME dat += "Ghost Accessories: [button_name]
" switch(ghost_others) if(GHOST_OTHERS_THEIR_SETTING) button_name = GHOST_OTHERS_THEIR_SETTING_NAME if(GHOST_OTHERS_DEFAULT_SPRITE) button_name = GHOST_OTHERS_DEFAULT_SPRITE_NAME if(GHOST_OTHERS_SIMPLE) button_name = GHOST_OTHERS_SIMPLE_NAME dat += "Ghosts of Others: [button_name]
" dat += "
" dat += "FPS: [clientfps]
" dat += "Income Updates: [(chat_toggles & CHAT_BANKCARD) ? "Allowed" : "Muted"]
" dat += "
" dat += "Parallax (Fancy Space): " switch (parallax) if (PARALLAX_LOW) dat += "Low" if (PARALLAX_MED) dat += "Medium" if (PARALLAX_INSANE) dat += "Insane" if (PARALLAX_DISABLE) dat += "Disabled" else dat += "High" dat += "
" dat += "Ambient Occlusion: [ambientocclusion ? "Enabled" : "Disabled"]
" dat += "Fit Viewport: [auto_fit_viewport ? "Auto" : "Manual"]
" dat += "HUD Button Flashes: [hud_toggle_flash ? "Enabled" : "Disabled"]
" dat += "HUD Button Flash Color:     Change
" /* CITADEL EDIT - We're using top menu instead button_name = pixel_size dat += "Pixel Scaling: [(button_name) ? "Pixel Perfect [button_name]x" : "Stretch to fit"]
" switch(scaling_method) if(SCALING_METHOD_NORMAL) button_name = "Nearest Neighbor" if(SCALING_METHOD_DISTORT) button_name = "Point Sampling" if(SCALING_METHOD_BLUR) button_name = "Bilinear" dat += "Scaling Method: [button_name]
" */ if (CONFIG_GET(flag/maprotation) && CONFIG_GET(flag/tgstyle_maprotation)) var/p_map = preferred_map if (!p_map) p_map = "Default" if (config.defaultmap) p_map += " ([config.defaultmap.map_name])" else if (p_map in config.maplist) var/datum/map_config/VM = config.maplist[p_map] if (!VM) p_map += " (No longer exists)" else p_map = VM.map_name else p_map += " (No longer exists)" if(CONFIG_GET(flag/allow_map_voting)) dat += "Preferred Map: [p_map]
" dat += "" dat += "

Special Role Settings

" if(jobban_isbanned(user, ROLE_SYNDICATE)) dat += "You are banned from antagonist roles." src.be_special = list() dat += "DISABLE ALL ANTAGONISM [(toggles & NO_ANTAG) ? "YES" : "NO"]
" for (var/i in GLOB.special_roles) if(jobban_isbanned(user, i)) dat += "Be [capitalize(i)]: BANNED
" else var/days_remaining = null if(ispath(GLOB.special_roles[i]) && CONFIG_GET(flag/use_age_restriction_for_jobs)) //If it's a game mode antag, check if the player meets the minimum age var/mode_path = GLOB.special_roles[i] var/datum/game_mode/temp_mode = new mode_path days_remaining = temp_mode.get_remaining_days(user.client) if(days_remaining) dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" else var/enabled_text = "" if(i in be_special) if(be_special[i] >= 1) enabled_text = "Enabled" else enabled_text = "Low" else enabled_text = "Disabled" dat += "Be [capitalize(i)]: [enabled_text]
" dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Enabled" : "Disabled"]
" dat += "
" if(LOADOUT_TAB) //calculate your gear points from the chosen item gear_points = CONFIG_GET(number/initial_gear_points) var/list/chosen_gear = loadout_data["SAVE_[loadout_slot]"] if(chosen_gear) for(var/loadout_item in chosen_gear) var/loadout_item_path = loadout_item[LOADOUT_ITEM] if(loadout_item_path) var/datum/gear/loadout_gear = text2path(loadout_item_path) if(loadout_gear) gear_points -= initial(loadout_gear.cost) else chosen_gear = list() dat += "" dat += "" dat += "" dat += "" dat += "" dat += "" dat += "" dat += "" dat += "" dat += "" for(var/name in GLOB.loadout_items[gear_category][gear_subcategory]) var/datum/gear/gear = GLOB.loadout_items[gear_category][gear_subcategory][name] var/donoritem = gear.donoritem if(donoritem && !gear.donator_ckey_check(user.ckey)) continue var/class_link = "" var/list/loadout_item = has_loadout_gear(loadout_slot, "[gear.type]") var/extra_loadout_data = "" if(loadout_item) class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=0'" if(gear.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC) extra_loadout_data += "
Color" for(var/loadout_color in loadout_item[LOADOUT_COLOR]) extra_loadout_data += "   " else var/loadout_color_non_poly = "#FFFFFF" if(length(loadout_item[LOADOUT_COLOR])) loadout_color_non_poly = loadout_item[LOADOUT_COLOR][1] extra_loadout_data += "
Color" extra_loadout_data += "   " if(gear.loadout_flags & LOADOUT_CAN_NAME) extra_loadout_data += "
Name [loadout_item[LOADOUT_CUSTOM_NAME] ? loadout_item[LOADOUT_CUSTOM_NAME] : "N/A"]" if(gear.loadout_flags & LOADOUT_CAN_DESCRIPTION) extra_loadout_data += "
Description" else if((gear_points - gear.cost) < 0) class_link = "style='white-space:normal;' class='linkOff'" else if(donoritem) class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'" else if(!istype(gear, /datum/gear/unlockable) || can_use_unlockable(gear)) class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'" else class_link = "style='white-space:normal;background:#eb2e2e;' class='linkOff'" dat += "" dat += "" else //we add the user's progress to the description assuming they have progress var/datum/gear/unlockable/unlockable = gear var/progress_made = unlockable_loadout_data[unlockable.progress_key] if(!progress_made) progress_made = 0 dat += "" dat += "
[gear_points] loadout points remaining. \[Clear Loadout\]
You can only choose one item per category, unless it's an item that spawns in your backpack or hands.
" if(!length(GLOB.loadout_items)) dat += "
ERROR: No loadout categories - something is horribly wrong!" else if(!GLOB.loadout_categories[gear_category]) gear_category = GLOB.loadout_categories[1] var/firstcat = TRUE for(var/category in GLOB.loadout_categories) if(firstcat) firstcat = FALSE else dat += " |" if(category == gear_category) dat += " [category] " else dat += " [category] " dat += "

" if(!length(GLOB.loadout_categories[gear_category])) dat += "No subcategories detected. Something is horribly wrong!" else var/list/subcategories = GLOB.loadout_categories[gear_category] if(!subcategories.Find(gear_subcategory)) gear_subcategory = subcategories[1] var/firstsubcat = TRUE for(var/subcategory in subcategories) if(firstsubcat) firstsubcat = FALSE else dat += " |" if(gear_subcategory == subcategory) dat += " [subcategory] " else dat += " [subcategory] " dat += "
NameCostRestrictionsDescription
[name][extra_loadout_data][gear.cost]" if(islist(gear.restricted_roles)) if(gear.restricted_roles.len) if(gear.restricted_desc) dat += "" dat += gear.restricted_desc dat += "" else dat += "" dat += gear.restricted_roles.Join(";") dat += "" if(!istype(gear, /datum/gear/unlockable)) // the below line essentially means "if the loadout item is picked by the user and has a custom description, give it the custom description, otherwise give it the default description" dat += "[loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description]
[loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] Progress: [min(progress_made, unlockable.progress_required)]/[unlockable.progress_required]
" if(CONTENT_PREFERENCES_TAB) // Content preferences dat += "" dat +="
" dat += "

Fetish content prefs

" dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" dat += "Genital examine text:[(cit_toggles & GENITAL_EXAMINE) ? "Enabled" : "Disabled"]
" dat += "Vore examine text:[(cit_toggles & VORE_EXAMINE) ? "Enabled" : "Disabled"]
" dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]
" dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"]
" dat += "Hear Vore Digestion Sounds: [(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"]
" dat += "Allow trash forcefeeding (requires Trashcan quirk) [(cit_toggles & TRASH_FORCEFEED) ? "Yes" : "No"]
" dat += "Forced Feminization: [(cit_toggles & FORCED_FEM) ? "Allowed" : "Disallowed"]
" dat += "Forced Masculinization: [(cit_toggles & FORCED_MASC) ? "Allowed" : "Disallowed"]
" dat += "Lewd Hypno: [(cit_toggles & HYPNO) ? "Allowed" : "Disallowed"]
" dat += "Bimbofication: [(cit_toggles & BIMBOFICATION) ? "Allowed" : "Disallowed"]
" dat += "
" dat += "

Other content prefs

" dat += "Breast Enlargement: [(cit_toggles & BREAST_ENLARGEMENT) ? "Allowed" : "Disallowed"]
" dat += "Penis Enlargement: [(cit_toggles & PENIS_ENLARGEMENT) ? "Allowed" : "Disallowed"]
" dat += "Butt Enlargement: [(cit_toggles & BUTT_ENLARGEMENT) ? "Allowed" : "Disallowed"]
" dat += "Hypno: [(cit_toggles & NEVER_HYPNO) ? "Disallowed" : "Allowed"]
" dat += "Aphrodisiacs: [(cit_toggles & NO_APHRO) ? "Disallowed" : "Allowed"]
" dat += "Ass Slapping: [(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"]
" dat += "Automatic Wagging: [(cit_toggles & NO_AUTO_WAG) ? "Disabled" : "Enabled"]
" dat += "
" dat += "
" if(KEYBINDINGS_TAB) // Custom keybindings dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Input"]
" dat += "Keybindings mode controls how the game behaves with tab and map/input focus.
If it is on Hotkeys, the game will always attempt to force you to map focus, meaning keypresses are sent \ directly to the map instead of the input. You will still be able to use the command bar, but you need to tab to do it every time you click on the game map.
\ If it is on Input, the game will not force focus away from the input bar, and you can switch focus using TAB between these two modes: If the input bar is pink, that means that you are in non-hotkey mode, sending all keypresses of the normal \ alphanumeric characters, punctuation, spacebar, backspace, enter, etc, typing keys into the input bar. If the input bar is white, you are in hotkey mode, meaning all keypresses go into the game's keybind handling system unless you \ manually click on the input bar to shift focus there.
\ Input mode is the closest thing to the old input system.
\ IMPORTANT: While in input mode's non hotkey setting (tab toggled), Ctrl + KEY will send KEY to the keybind system as the key itself, not as Ctrl + KEY. This means Ctrl + T/W/A/S/D/all your familiar stuff still works, but you \ won't be able to access any regular Ctrl binds.
" dat += "
Modifier-Independent binding - This is a singular bind that works regardless of if Ctrl/Shift/Alt are held down. For example, if combat mode is bound to C in modifier-independent binds, it'll trigger regardless of if you are \ holding down shift for sprint. Each keybind can only have one independent binding, and each key can only have one keybind independently bound to it." // Create an inverted list of keybindings -> key var/list/user_binds = list() var/list/user_modless_binds = list() for (var/key in key_bindings) for(var/kb_name in key_bindings[key]) user_binds[kb_name] += list(key) for (var/key in modless_key_bindings) user_modless_binds[modless_key_bindings[key]] = key var/list/kb_categories = list() // Group keybinds by category for (var/name in GLOB.keybindings_by_name) var/datum/keybinding/kb = GLOB.keybindings_by_name[name] kb_categories[kb.category] += list(kb) dat += {" "} for (var/category in kb_categories) dat += "

[category]

" for (var/i in kb_categories[category]) var/datum/keybinding/kb = i var/current_independent_binding = user_modless_binds[kb.name] || "Unbound" if(!length(user_binds[kb.name])) dat += "[kb.full_name]Unbound" var/list/default_keys = hotkeys ? kb.hotkey_keys : kb.classic_keys if(LAZYLEN(default_keys)) dat += "| Default: [default_keys.Join(", ")]" dat += "" if(!kb.special && !kb.clientside) dat += "Independent Binding: [current_independent_binding]" dat += "
" else var/bound_key = user_binds[kb.name][1] dat += "[kb.full_name][bound_key]" for(var/bound_key_index in 2 to length(user_binds[kb.name])) bound_key = user_binds[kb.name][bound_key_index] dat += " | [bound_key]" if(length(user_binds[kb.name]) < MAX_KEYS_PER_KEYBIND) dat += "| Add Secondary" var/list/default_keys = hotkeys ? kb.classic_keys : kb.hotkey_keys if(LAZYLEN(default_keys)) dat += "| Default: [default_keys.Join(", ")]" dat += "" if(!kb.special && !kb.clientside) dat += "Independent Binding: [current_independent_binding]" dat += "
" dat += "

" dat += "\[Reset to default\]" dat += "" dat += "
" if(!IsGuestKey(user.key)) dat += "Undo " dat += "Save Setup " dat += "Reset Setup" dat += "
" winshow(user, "preferences_window", TRUE) var/datum/browser/popup = new(user, "preferences_browser", "
Character Setup
", 640, 770) popup.set_content(dat.Join()) popup.open(FALSE) onclose(user, "preferences_window", src) #undef APPEARANCE_CATEGORY_COLUMN #undef MAX_MUTANT_ROWS /datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, old_key, independent = FALSE, special = FALSE) var/HTML = {"
Keybinding: [kb.full_name]
[kb.description]

Press any key to change
Press ESC to clear
"} winshow(user, "capturekeypress", TRUE) var/datum/browser/popup = new(user, "capturekeypress", "
Keybindings
", 350, 300) popup.set_content(HTML) popup.open(FALSE) onclose(user, "capturekeypress", src) /datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) if(!SSjob) return //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. //widthPerColumn - Screen's width for every column. //height - Screen's height. var/width = widthPerColumn var/HTML = "
" if(SSjob.occupations.len <= 0) HTML += "The job SSticker is not yet finished creating jobs, please try again later" HTML += "
Done

" // Easier to press up here. else HTML += "Choose occupation chances
" HTML += "
Left-click to raise an occupation preference, right-click to lower it.
" HTML += "
Done

" // Easier to press up here. HTML += "" HTML += "
" // Table within a table for alignment, also allows you to easily add more colomns. HTML += "" 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 for(var/datum/job/job in sortList(SSjob.occupations, /proc/cmp_job_display_asc)) index += 1 if((index >= limit) || (job.title in splitJobs)) width += widthPerColumn 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. Creating a rather nice effect. for(var/i = 0, i < (limit - index), i += 1) HTML += "" HTML += "
  
" index = 0 HTML += "" continue var/required_playtime_remaining = job.required_playtime_remaining(user.client) if(required_playtime_remaining) HTML += "[rank]" continue if(!job.player_old_enough(user.client)) var/available_in_days = job.available_in_days(user.client) HTML += "[rank]" continue if(!user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) if(user.client.prefs.pref_species.id == "human") HTML += "[rank]" else HTML += "[rank]" continue if((job_preferences["[SSjob.overflow_role]"] == JP_LOW) && (rank != SSjob.overflow_role) && !jobban_isbanned(user, SSjob.overflow_role)) HTML += "[rank]" continue if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs HTML += "[rank]" else HTML += "[rank]" HTML += "" continue HTML += "[prefLevelLabel]" HTML += "" for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even HTML += "" HTML += "
" var/rank = job.title lastJob = job if(jobban_isbanned(user, rank)) HTML += "[rank] BANNED
\[ [get_exp_format(required_playtime_remaining)] as [job.get_exp_req_type()] \]
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" var/prefLevelLabel = "ERROR" var/prefLevelColor = "pink" var/prefUpperLevel = -1 // level to assign on left click var/prefLowerLevel = -1 // level to assign on right click switch(job_preferences["[job.title]"]) if(JP_HIGH) prefLevelLabel = "High" prefLevelColor = "slateblue" prefUpperLevel = 4 prefLowerLevel = 2 if(JP_MEDIUM) prefLevelLabel = "Medium" prefLevelColor = "green" prefUpperLevel = 1 prefLowerLevel = 3 if(JP_LOW) prefLevelLabel = "Low" prefLevelColor = "orange" prefUpperLevel = 2 prefLowerLevel = 4 else prefLevelLabel = "NEVER" prefLevelColor = "red" prefUpperLevel = 3 prefLowerLevel = 1 HTML += "" if(rank == SSjob.overflow_role)//Overflow is special if(job_preferences["[SSjob.overflow_role]"] == JP_LOW) HTML += "Yes" else HTML += "No" HTML += "
  
" HTML += "
" var/message = "Be an [SSjob.overflow_role] if preferences unavailable" if(joblessrole == BERANDOMJOB) message = "Get random job if preferences unavailable" else if(joblessrole == RETURNTOLOBBY) message = "Return to lobby if preferences unavailable" HTML += "

[message]
" HTML += "
Reset Preferences
" var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) popup.set_window_options("can_close=0") popup.set_content(HTML) popup.open(FALSE) /datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) if (!job) return FALSE if (level == JP_HIGH) // to high //Set all other high to medium for(var/j in job_preferences) if(job_preferences["[j]"] == JP_HIGH) job_preferences["[j]"] = JP_MEDIUM //technically break here job_preferences["[job.title]"] = level return TRUE /datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) if(!SSjob || SSjob.occupations.len <= 0) return var/datum/job/job = SSjob.GetJob(role) if(!job) user << browse(null, "window=mob_occupation") ShowChoices(user) return if (!isnum(desiredLvl)) to_chat(user, "UpdateJobPreference - desired level was not a number. Please notify coders!") ShowChoices(user) return var/jpval = null switch(desiredLvl) if(3) jpval = JP_LOW if(2) jpval = JP_MEDIUM if(1) jpval = JP_HIGH if(role == SSjob.overflow_role) if(job_preferences["[job.title]"] == JP_LOW) jpval = null else jpval = JP_LOW SetJobPreferenceLevel(job, jpval) SetChoices(user) return 1 /datum/preferences/proc/ResetJobs() job_preferences = list() /datum/preferences/proc/SetQuirks(mob/user) if(!SSquirks) to_chat(user, "The quirk subsystem is still initializing! Try again in a minute.") return var/list/dat = list() if(!SSquirks.quirks.len) dat += "The quirk subsystem hasn't finished initializing, please hold..." dat += "
Done

" else dat += "
Choose quirk setup

" dat += "
Left-click to add or remove quirks. You need negative quirks to have positive ones.
\ Quirks are applied at roundstart and cannot normally be removed.
" dat += "
Done
" dat += "
" dat += "
Current quirks: [all_quirks.len ? all_quirks.Join(", ") : "None"]
" dat += "
[GetPositiveQuirkCount()] / [MAX_QUIRKS] max positive quirks
\ Quirk balance remaining: [GetQuirkBalance()]
" dat += " [QUIRK_POSITIVE] " dat += " [QUIRK_NEUTRAL] " dat += " [QUIRK_NEGATIVE] " dat += "

" for(var/V in SSquirks.quirks) var/datum/quirk/T = SSquirks.quirks[V] var/value = initial(T.value) if((value > 0 && quirk_category != QUIRK_POSITIVE) || (value < 0 && quirk_category != QUIRK_NEGATIVE) || (value == 0 && quirk_category != QUIRK_NEUTRAL)) continue var/quirk_name = initial(T.name) var/has_quirk var/quirk_cost = initial(T.value) * -1 var/lock_reason = "This trait is unavailable." var/quirk_conflict = FALSE for(var/_V in all_quirks) if(_V == quirk_name) has_quirk = TRUE if(initial(T.mood_quirk) && CONFIG_GET(flag/disable_human_mood)) lock_reason = "Mood is disabled." quirk_conflict = TRUE if(has_quirk) if(quirk_conflict) all_quirks -= quirk_name has_quirk = FALSE else quirk_cost *= -1 //invert it back, since we'd be regaining this amount if(quirk_cost > 0) quirk_cost = "+[quirk_cost]" var/font_color = "#AAAAFF" if(initial(T.value) != 0) font_color = value > 0 ? "#AAFFAA" : "#FFAAAA" if(quirk_conflict) dat += "[quirk_name] - [initial(T.desc)] \ LOCKED: [lock_reason]
" else if(has_quirk) dat += "[has_quirk ? "Remove" : "Take"] ([quirk_cost] pts.) \ [quirk_name] - [initial(T.desc)]
" else dat += "[has_quirk ? "Remove" : "Take"] ([quirk_cost] pts.) \ [quirk_name] - [initial(T.desc)]
" dat += "
Reset Quirks
" var/datum/browser/popup = new(user, "mob_occupation", "
Quirk Preferences
", 900, 600) //no reason not to reuse the occupation window, as it's cleaner that way popup.set_window_options("can_close=0") popup.set_content(dat.Join()) popup.open(FALSE) /datum/preferences/proc/GetQuirkBalance() var/bal = 0 for(var/V in all_quirks) var/datum/quirk/T = SSquirks.quirks[V] bal -= initial(T.value) for(var/modification in modified_limbs) if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC) return bal + 1 //max 1 point regardless of how many prosthetics return bal /datum/preferences/proc/GetPositiveQuirkCount() . = 0 for(var/q in all_quirks) if(SSquirks.quirk_points[q] > 0) .++ /datum/preferences/Topic(href, href_list, hsrc) //yeah, gotta do this I guess.. . = ..() if(href_list["close"]) var/client/C = usr.client if(C) C.clear_character_previews() /datum/preferences/proc/process_link(mob/user, list/href_list) if(href_list["jobbancheck"]) var/job = href_list["jobbancheck"] var/datum/db_query/query_get_jobban = SSdbcore.NewQuery({" SELECT reason, bantime, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey) FROM [format_table_name("ban")] WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = :job "}, list("ckey" = user.ckey, "job" = job)) if(!query_get_jobban.warn_execute()) qdel(query_get_jobban) return if(query_get_jobban.NextRow()) var/reason = query_get_jobban.item[1] var/bantime = query_get_jobban.item[2] var/duration = query_get_jobban.item[3] var/expiration_time = query_get_jobban.item[4] var/admin_key = query_get_jobban.item[5] var/text text = "You, or another user of this computer, ([user.key]) is banned from playing [job]. The ban reason is:
[reason]
This ban was applied by [admin_key] on [bantime]" if(text2num(duration) > 0) text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" text += ".
" to_chat(user, text, confidential = TRUE) qdel(query_get_jobban) return if(href_list["preference"] == "job") switch(href_list["task"]) if("close") user << browse(null, "window=mob_occupation") ShowChoices(user) if("reset") ResetJobs() SetChoices(user) if("random") switch(joblessrole) if(RETURNTOLOBBY) if(jobban_isbanned(user, SSjob.overflow_role)) joblessrole = BERANDOMJOB else joblessrole = BEOVERFLOW if(BEOVERFLOW) joblessrole = BERANDOMJOB if(BERANDOMJOB) joblessrole = RETURNTOLOBBY SetChoices(user) if("setJobLevel") UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) else SetChoices(user) return 1 else if(href_list["preference"] == "trait") switch(href_list["task"]) if("close") user << browse(null, "window=mob_occupation") ShowChoices(user) if("update") var/quirk = href_list["trait"] if(!SSquirks.quirks[quirk]) return for(var/V in SSquirks.quirk_blacklist) //V is a list var/list/L = V for(var/Q in all_quirks) if((quirk in L) && (Q in L) && !(Q == quirk)) //two quirks have lined up in the list of the list of quirks that conflict with each other, so return (see quirks.dm for more details) to_chat(user, "[quirk] is incompatible with [Q].") return var/value = SSquirks.quirk_points[quirk] var/balance = GetQuirkBalance() if(quirk in all_quirks) if(balance + value < 0) to_chat(user, "Refunding this would cause you to go below your balance!") return all_quirks -= quirk else if(GetPositiveQuirkCount() >= MAX_QUIRKS) to_chat(user, "You can't have more than [MAX_QUIRKS] positive quirks!") return if(balance - value < 0) to_chat(user, "You don't have enough balance to gain this quirk!") return all_quirks += quirk SetQuirks(user) if("reset") all_quirks = list() SetQuirks(user) else SetQuirks(user) return TRUE else if(href_list["quirk_category"]) var/temp_quirk_category = href_list["quirk_category"] if(temp_quirk_category == QUIRK_POSITIVE || temp_quirk_category == QUIRK_NEUTRAL || temp_quirk_category == QUIRK_NEGATIVE) quirk_category = temp_quirk_category SetQuirks(user) switch(href_list["task"]) if("random") switch(href_list["preference"]) if("name") real_name = pref_species.random_name(gender,1) if("age") age = rand(AGE_MIN, AGE_MAX) if("hair") hair_color = random_short_color() if("hair_style") hair_style = random_hair_style(gender) if("facial") facial_hair_color = random_short_color() if("facial_hair_style") facial_hair_style = random_facial_hair_style(gender) if("underwear") underwear = random_underwear(gender) undie_color = random_short_color() if("undershirt") undershirt = random_undershirt(gender) shirt_color = random_short_color() if("socks") socks = random_socks() socks_color = random_short_color() if(BODY_ZONE_PRECISE_EYES) var/random_eye_color = random_eye_color() left_eye_color = random_eye_color right_eye_color = random_eye_color if("s_tone") skin_tone = random_skin_tone() use_custom_skin_tone = null if("bag") backbag = pick(GLOB.backbaglist) if("suit") jumpsuit_style = pick(GLOB.jumpsuitlist) if("all") random_character() if("input") if(href_list["preference"] in GLOB.preferences_custom_names) ask_for_custom_name(user,href_list["preference"]) switch(href_list["preference"]) if("ghostform") if(unlock_content) var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in GLOB.ghost_forms if(new_form) ghost_form = new_form if("ghostorbit") if(unlock_content) var/new_orbit = input(user, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND", null) as null|anything in GLOB.ghost_orbits if(new_orbit) ghost_orbit = new_orbit if("ghostaccs") var/new_ghost_accs = alert("Do you want your ghost to show full accessories where possible, hide accessories but still use the directional sprites where possible, or also ignore the directions and stick to the default sprites?",,GHOST_ACCS_FULL_NAME, GHOST_ACCS_DIR_NAME, GHOST_ACCS_NONE_NAME) switch(new_ghost_accs) if(GHOST_ACCS_FULL_NAME) ghost_accs = GHOST_ACCS_FULL if(GHOST_ACCS_DIR_NAME) ghost_accs = GHOST_ACCS_DIR if(GHOST_ACCS_NONE_NAME) ghost_accs = GHOST_ACCS_NONE if("ghostothers") var/new_ghost_others = alert("Do you want the ghosts of others to show up as their own setting, as their default sprites or always as the default white ghost?",,GHOST_OTHERS_THEIR_SETTING_NAME, GHOST_OTHERS_DEFAULT_SPRITE_NAME, GHOST_OTHERS_SIMPLE_NAME) switch(new_ghost_others) if(GHOST_OTHERS_THEIR_SETTING_NAME) ghost_others = GHOST_OTHERS_THEIR_SETTING if(GHOST_OTHERS_DEFAULT_SPRITE_NAME) ghost_others = GHOST_OTHERS_DEFAULT_SPRITE if(GHOST_OTHERS_SIMPLE_NAME) ghost_others = GHOST_OTHERS_SIMPLE if("name") var/new_name = input(user, "Choose your character's name:", "Character Preference") as text|null if(new_name) new_name = reject_bad_name(new_name, allow_numbers = TRUE) if(new_name) real_name = new_name else to_chat(user, "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 .") if("age") var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX_INPUT])", "Character Preference") as num|null if(new_age) age = max(min( round(text2num(new_age)), AGE_MAX_INPUT),AGE_MIN) if("security_records") var/rec = stripped_multiline_input(usr, "Set your security record note section. This should be IC!", "Security Records", html_decode(security_records), MAX_FLAVOR_LEN, TRUE) if(!isnull(rec)) security_records = rec if("medical_records") var/rec = stripped_multiline_input(usr, "Set your medical record note section. This should be IC!", "Security Records", html_decode(medical_records), MAX_FLAVOR_LEN, TRUE) if(!isnull(rec)) medical_records = rec if("flavor_text") var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", html_decode(features["flavor_text"]), MAX_FLAVOR_LEN, TRUE) if(!isnull(msg)) features["flavor_text"] = msg if("silicon_flavor_text") var/msg = stripped_multiline_input(usr, "Set the silicon flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Silicon Flavor Text", html_decode(features["silicon_flavor_text"]), MAX_FLAVOR_LEN, TRUE) if(!isnull(msg)) features["silicon_flavor_text"] = msg if("ooc_notes") var/msg = stripped_multiline_input(usr, "Set always-visible OOC notes related to content preferences. THIS IS NOT FOR CHARACTER DESCRIPTIONS!", "OOC notes", html_decode(features["ooc_notes"]), MAX_FLAVOR_LEN, TRUE) if(!isnull(msg)) features["ooc_notes"] = msg if("hide_ckey") hide_ckey = !hide_ckey if(user) user.mind?.hide_ckey = hide_ckey if("hair") var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#"+hair_color) as color|null if(new_hair) hair_color = sanitize_hexcolor(new_hair, 6) if("hair_style") var/new_hair_style new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_list if(new_hair_style) hair_style = new_hair_style if("next_hair_style") hair_style = next_list_item(hair_style, GLOB.hair_styles_list) if("previous_hair_style") hair_style = previous_list_item(hair_style, GLOB.hair_styles_list) if("facial") var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference","#"+facial_hair_color) as color|null if(new_facial) facial_hair_color = sanitize_hexcolor(new_facial, 6) if("facial_hair_style") var/new_facial_hair_style new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_list if(new_facial_hair_style) facial_hair_style = new_facial_hair_style if("next_facehair_style") facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_list) if("previous_facehair_style") facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_list) if("grad_color") var/new_grad_color = input(user, "Choose your character's gradient colour:", "Character Preference","#"+grad_color) as color|null if(new_grad_color) grad_color = sanitize_hexcolor(new_grad_color, 6) if("grad_style") var/new_grad_style new_grad_style = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list if(new_grad_style) grad_style = new_grad_style if("next_grad_style") grad_style = next_list_item(grad_style, GLOB.hair_gradients_list) if("previous_grad_style") grad_style = previous_list_item(grad_style, GLOB.hair_gradients_list) if("cycle_bg") bgstate = next_list_item(bgstate, bgstate_options) if("modify_limbs") var/limb_type = input(user, "Choose the limb to modify:", "Character Preference") as null|anything in LOADOUT_ALLOWED_LIMB_TARGETS if(limb_type) var/modification_type = input(user, "Choose the modification to the limb:", "Character Preference") as null|anything in LOADOUT_LIMBS if(modification_type) if(modification_type == LOADOUT_LIMB_PROSTHETIC) var/prosthetic_type = input(user, "Choose the type of prosthetic", "Character Preference") as null|anything in (list("prosthetic") + GLOB.prosthetic_limb_types) if(prosthetic_type) var/number_of_prosthetics = 0 for(var/modified_limb in modified_limbs) if(modified_limbs[modified_limb][1] == LOADOUT_LIMB_PROSTHETIC && modified_limb != limb_type) number_of_prosthetics += 1 if(number_of_prosthetics == MAXIMUM_LOADOUT_PROSTHETICS) to_chat(user, "You can only have up to two prosthetic limbs!") else //save the actual prosthetic data modified_limbs[limb_type] = list(modification_type, prosthetic_type) else if(modification_type == LOADOUT_LIMB_NORMAL) modified_limbs -= limb_type else modified_limbs[limb_type] = list(modification_type) if("underwear") var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_list if(new_underwear) underwear = new_underwear if("undie_color") var/n_undie_color = input(user, "Choose your underwear's color.", "Character Preference", "#[undie_color]") as color|null if(n_undie_color) undie_color = sanitize_hexcolor(n_undie_color, 6) if("undershirt") var/new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_list if(new_undershirt) undershirt = new_undershirt if("shirt_color") var/n_shirt_color = input(user, "Choose your undershirt's color.", "Character Preference", "#[shirt_color]") as color|null if(n_shirt_color) shirt_color = sanitize_hexcolor(n_shirt_color, 6) if("socks") var/new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in GLOB.socks_list if(new_socks) socks = new_socks if("socks_color") var/n_socks_color = input(user, "Choose your socks' color.", "Character Preference", "#[socks_color]") as color|null if(n_socks_color) socks_color = sanitize_hexcolor(n_socks_color, 6) if("eyes") var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference","#"+left_eye_color) as color|null if(new_eyes) left_eye_color = sanitize_hexcolor(new_eyes, 6) right_eye_color = sanitize_hexcolor(new_eyes, 6) if("eye_left") var/new_eyes = input(user, "Choose your character's left eye colour:", "Character Preference","#"+left_eye_color) as color|null if(new_eyes) left_eye_color = sanitize_hexcolor(new_eyes, 6) if("eye_right") var/new_eyes = input(user, "Choose your character's right eye colour:", "Character Preference","#"+right_eye_color) as color|null if(new_eyes) right_eye_color = sanitize_hexcolor(new_eyes, 6) if("eye_type") var/new_eye_type = input(user, "Choose your character's eye type.", "Character Preference") as null|anything in GLOB.eye_types if(new_eye_type) eye_type = new_eye_type if("toggle_split_eyes") split_eye_colors = !split_eye_colors right_eye_color = left_eye_color if("species") var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_race_names if(result) var/newtype = GLOB.species_list[GLOB.roundstart_race_names[result]] pref_species = new newtype() //let's ensure that no weird shit happens on species swapping. custom_species = null if(!parent.can_have_part("mam_body_markings")) features["mam_body_markings"] = list() if(parent.can_have_part("mam_body_markings")) if(features["mam_body_markings"] == "None") features["mam_body_markings"] = list() if(parent.can_have_part("tail_lizard")) features["tail_lizard"] = "Smooth" if(pref_species.id == "felinid") features["mam_tail"] = "Cat" features["mam_ears"] = "Cat" //Now that we changed our species, we must verify that the mutant colour is still allowed. var/temp_hsv = RGBtoHSV(features["mcolor"]) if(features["mcolor"] == "#000000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) features["mcolor"] = pref_species.default_color if(features["mcolor2"] == "#000000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) features["mcolor2"] = pref_species.default_color if(features["mcolor3"] == "#000000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) features["mcolor3"] = pref_species.default_color //switch to the type of eyes the species uses eye_type = pref_species.eye_type if("custom_species") var/new_species = reject_bad_name(input(user, "Choose your species subtype, if unique. This will show up on examinations and health scans. Do not abuse this:", "Character Preference", custom_species) as null|text) if(new_species) custom_species = new_species else custom_species = null if("mutant_color") var/new_mutantcolor = input(user, "Choose your character's alien/mutant color:", "Character Preference","#"+features["mcolor"]) as color|null if(new_mutantcolor) var/temp_hsv = RGBtoHSV(new_mutantcolor) if(new_mutantcolor == "#000000") features["mcolor"] = pref_species.default_color else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin features["mcolor"] = sanitize_hexcolor(new_mutantcolor, 6) else to_chat(user, "Invalid color. Your color is not bright enough.") if("mutant_color2") var/new_mutantcolor = input(user, "Choose your character's secondary alien/mutant color:", "Character Preference","#"+features["mcolor2"]) as color|null if(new_mutantcolor) var/temp_hsv = RGBtoHSV(new_mutantcolor) if(new_mutantcolor == "#000000") features["mcolor2"] = pref_species.default_color else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin features["mcolor2"] = sanitize_hexcolor(new_mutantcolor, 6) else to_chat(user, "Invalid color. Your color is not bright enough.") if("mutant_color3") var/new_mutantcolor = input(user, "Choose your character's tertiary alien/mutant color:", "Character Preference","#"+features["mcolor3"]) as color|null if(new_mutantcolor) var/temp_hsv = RGBtoHSV(new_mutantcolor) if(new_mutantcolor == "#000000") features["mcolor3"] = pref_species.default_color else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin features["mcolor3"] = sanitize_hexcolor(new_mutantcolor, 6) else to_chat(user, "Invalid color. Your color is not bright enough.") if("mismatched_markings") show_mismatched_markings = !show_mismatched_markings if("ipc_screen") var/new_ipc_screen new_ipc_screen = input(user, "Choose your character's screen:", "Character Preference") as null|anything in GLOB.ipc_screens_list if(new_ipc_screen) features["ipc_screen"] = new_ipc_screen if("ipc_antenna") var/list/snowflake_antenna_list = list() //Potential todo: turn all of THIS into a define to reduce copypasta. for(var/path in GLOB.ipc_antennas_list) var/datum/sprite_accessory/antenna/instance = GLOB.ipc_antennas_list[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_antenna_list[S.name] = path var/new_ipc_antenna new_ipc_antenna = input(user, "Choose your character's antenna:", "Character Preference") as null|anything in snowflake_antenna_list if(new_ipc_antenna) features["ipc_antenna"] = new_ipc_antenna if("arachnid_legs") var/new_arachnid_legs new_arachnid_legs = input(user, "Choose your character's variant of arachnid legs:", "Character Preference") as null|anything in GLOB.arachnid_legs_list if(new_arachnid_legs) features["arachnid_legs"] = new_arachnid_legs if("arachnid_spinneret") var/new_arachnid_spinneret new_arachnid_spinneret = input(user, "Choose your character's spinneret markings:", "Character Preference") as null|anything in GLOB.arachnid_spinneret_list if(new_arachnid_spinneret) features["arachnid_spinneret"] = new_arachnid_spinneret if("arachnid_mandibles") var/new_arachnid_mandibles new_arachnid_mandibles = input(user, "Choose your character's variant of mandibles:", "Character Preference") as null|anything in GLOB.arachnid_mandibles_list if (new_arachnid_mandibles) features["arachnid_mandibles"] = new_arachnid_mandibles if("tail_lizard") var/new_tail new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard if(new_tail) features["tail_lizard"] = new_tail if(new_tail != "None") features["taur"] = "None" features["tail_human"] = "None" features["mam_tail"] = "None" if("tail_human") var/list/snowflake_tails_list = list() for(var/path in GLOB.tails_list_human) var/datum/sprite_accessory/tails/human/instance = GLOB.tails_list_human[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_tails_list[S.name] = path var/new_tail new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in snowflake_tails_list if(new_tail) features["tail_human"] = new_tail if(new_tail != "None") features["taur"] = "None" features["tail_lizard"] = "None" features["mam_tail"] = "None" if("mam_tail") var/list/snowflake_tails_list = list() for(var/path in GLOB.mam_tails_list) var/datum/sprite_accessory/tails/mam_tails/instance = GLOB.mam_tails_list[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_tails_list[S.name] = path var/new_tail new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in snowflake_tails_list if(new_tail) features["mam_tail"] = new_tail if(new_tail != "None") features["taur"] = "None" features["tail_human"] = "None" features["tail_lizard"] = "None" if("meat_type") var/new_meat new_meat = input(user, "Choose your character's meat type:", "Character Preference") as null|anything in GLOB.meat_types if(new_meat) features["meat_type"] = new_meat if("snout") var/list/snowflake_snouts_list = list() for(var/path in GLOB.snouts_list) var/datum/sprite_accessory/snouts/mam_snouts/instance = GLOB.snouts_list[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_snouts_list[S.name] = path var/new_snout new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in snowflake_snouts_list if(new_snout) features["snout"] = new_snout features["mam_snouts"] = "None" if("mam_snouts") var/list/snowflake_mam_snouts_list = list() for(var/path in GLOB.mam_snouts_list) var/datum/sprite_accessory/snouts/mam_snouts/instance = GLOB.mam_snouts_list[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_mam_snouts_list[S.name] = path var/new_mam_snouts new_mam_snouts = input(user, "Choose your character's snout:", "Character Preference") as null|anything in snowflake_mam_snouts_list if(new_mam_snouts) features["mam_snouts"] = new_mam_snouts features["snout"] = "None" if("horns") var/new_horns new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in GLOB.horns_list if(new_horns) features["horns"] = new_horns if("horns_color") var/new_horn_color = input(user, "Choose your character's horn colour:", "Character Preference","#"+features["horns_color"]) as color|null if(new_horn_color) if (new_horn_color == "#000000") features["horns_color"] = "85615A" else features["horns_color"] = sanitize_hexcolor(new_horn_color, 6) if("wings") var/new_wings new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list if(new_wings) features["wings"] = new_wings if("wings_color") var/new_wing_color = input(user, "Choose your character's wing colour:", "Character Preference","#"+features["wings_color"]) as color|null if(new_wing_color) if (new_wing_color == "#000000") features["wings_color"] = "#FFFFFF" else features["wings_color"] = sanitize_hexcolor(new_wing_color, 6) if("frills") var/new_frills new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in GLOB.frills_list if(new_frills) features["frills"] = new_frills if("spines") var/new_spines new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in GLOB.spines_list if(new_spines) features["spines"] = new_spines if("legs") var/new_legs new_legs = input(user, "Choose your character's legs:", "Character Preference") as null|anything in GLOB.legs_list if(new_legs) features["legs"] = new_legs if("insect_wings") var/new_insect_wings new_insect_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.insect_wings_list if(new_insect_wings) features["insect_wings"] = new_insect_wings if("deco_wings") var/new_deco_wings new_deco_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.deco_wings_list if(new_deco_wings) features["deco_wings"] = new_deco_wings if("insect_fluff") var/new_insect_fluff new_insect_fluff = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.insect_fluffs_list if(new_insect_fluff) features["insect_fluff"] = new_insect_fluff if("insect_markings") var/new_insect_markings new_insect_markings = input(user, "Choose your character's markings:", "Character Preference") as null|anything in GLOB.insect_markings_list if(new_insect_markings) features["insect_markings"] = new_insect_markings if("arachnid_legs") var/new_arachnid_legs new_arachnid_legs = input(user, "Choose your character's variant of arachnid legs:", "Character Preference") as null|anything in GLOB.arachnid_legs_list if(new_arachnid_legs) features["arachnid_legs"] = new_arachnid_legs if("arachnid_spinneret") var/new_arachnid_spinneret new_arachnid_spinneret = input(user, "Choose your character's spinneret markings:", "Character Preference") as null|anything in GLOB.arachnid_spinneret_list if(new_arachnid_spinneret) features["arachnid_spinneret"] = new_arachnid_spinneret if("arachnid_mandibles") var/new_arachnid_mandibles new_arachnid_mandibles = input(user, "Choose your character's variant of mandibles:", "Character Preference") as null|anything in GLOB.arachnid_mandibles_list if (new_arachnid_mandibles) features["arachnid_mandibles"] = new_arachnid_mandibles if("s_tone") var/list/choices = GLOB.skin_tones - GLOB.nonstandard_skin_tones if(CONFIG_GET(flag/allow_custom_skintones)) choices += "custom" var/new_s_tone = input(user, "Choose your character's skin tone:", "Character Preference") as null|anything in choices if(new_s_tone) if(new_s_tone == "custom") var/default = use_custom_skin_tone ? skin_tone : null var/custom_tone = input(user, "Choose your custom skin tone:", "Character Preference", default) as color|null if(custom_tone) var/temp_hsv = RGBtoHSV(custom_tone) if(ReadHSV(temp_hsv)[3] < ReadHSV("#333333")[3]) // rgb(50,50,50) to_chat(user,"Invalid color. Your color is not bright enough.") else use_custom_skin_tone = TRUE skin_tone = custom_tone else use_custom_skin_tone = FALSE skin_tone = new_s_tone if("taur") var/list/snowflake_taur_list = list() for(var/path in GLOB.taur_list) var/datum/sprite_accessory/taur/instance = GLOB.taur_list[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if(S.ignore) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_taur_list[S.name] = path var/new_taur new_taur = input(user, "Choose your character's tauric body:", "Character Preference") as null|anything in snowflake_taur_list if(new_taur) features["taur"] = new_taur if(new_taur != "None") features["mam_tail"] = "None" features["xenotail"] = "None" features["tail_human"] = "None" features["tail_lizard"] = "None" features["arachnid_spinneret"] = "None" if("ears") var/list/snowflake_ears_list = list() for(var/path in GLOB.ears_list) var/datum/sprite_accessory/ears/instance = GLOB.ears_list[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_ears_list[S.name] = path var/new_ears new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in snowflake_ears_list if(new_ears) features["ears"] = new_ears if("mam_ears") var/list/snowflake_ears_list = list() for(var/path in GLOB.mam_ears_list) var/datum/sprite_accessory/ears/mam_ears/instance = GLOB.mam_ears_list[path] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id)) continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_ears_list[S.name] = path var/new_ears new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in snowflake_ears_list if(new_ears) features["mam_ears"] = new_ears //Xeno Bodyparts if("xenohead")//Head or caste type var/new_head new_head = input(user, "Choose your character's caste:", "Character Preference") as null|anything in GLOB.xeno_head_list if(new_head) features["xenohead"] = new_head if("xenotail")//Currently one one type, more maybe later if someone sprites them. Might include animated variants in the future. var/new_tail new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.xeno_tail_list if(new_tail) features["xenotail"] = new_tail if(new_tail != "None") features["mam_tail"] = "None" features["taur"] = "None" features["tail_human"] = "None" features["tail_lizard"] = "None" if("xenodorsal") var/new_dors new_dors = input(user, "Choose your character's dorsal tube type:", "Character Preference") as null|anything in GLOB.xeno_dorsal_list if(new_dors) features["xenodorsal"] = new_dors //every single primary/secondary/tertiary colouring done at once if("xenodorsal_primary","xenodorsal_secondary","xenodorsal_tertiary","xhead_primary","xhead_secondary","xhead_tertiary","tail_primary","tail_secondary","tail_tertiary","insect_markings_primary","insect_markings_secondary","insect_markings_tertiary","insect_fluff_primary","insect_fluff_secondary","insect_fluff_tertiary","ears_primary","ears_secondary","ears_tertiary","frills_primary","frills_secondary","frills_tertiary","ipc_antenna_primary","ipc_antenna_secondary","ipc_antenna_tertiary","taur_primary","taur_secondary","taur_tertiary","snout_primary","snout_secondary","snout_tertiary","spines_primary","spines_secondary","spines_tertiary", "mam_body_markings_primary", "mam_body_markings_secondary", "mam_body_markings_tertiary") var/the_feature = features[href_list["preference"]] if(!the_feature) features[href_list["preference"]] = "FFFFFF" the_feature = "FFFFFF" var/new_feature_color = input(user, "Choose your character's mutant part colour:", "Character Preference","#"+features[href_list["preference"]]) as color|null if(new_feature_color) var/temp_hsv = RGBtoHSV(new_feature_color) if(new_feature_color == "#000000") features[href_list["preference"]] = pref_species.default_color else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) features[href_list["preference"]] = sanitize_hexcolor(new_feature_color, 6) else to_chat(user,"Invalid color. Your color is not bright enough.") //advanced color mode toggle if("color_scheme") if(features["color_scheme"] == ADVANCED_CHARACTER_COLORING) features["color_scheme"] = OLD_CHARACTER_COLORING else features["color_scheme"] = ADVANCED_CHARACTER_COLORING //Genital code if("cock_color") var/new_cockcolor = input(user, "Penis color:", "Character Preference","#"+features["cock_color"]) as color|null if(new_cockcolor) var/temp_hsv = RGBtoHSV(new_cockcolor) if(new_cockcolor == "#000000") features["cock_color"] = pref_species.default_color else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) features["cock_color"] = sanitize_hexcolor(new_cockcolor, 6) else to_chat(user,"Invalid color. Your color is not bright enough.") if("cock_length") var/min_D = CONFIG_GET(number/penis_min_inches_prefs) var/max_D = CONFIG_GET(number/penis_max_inches_prefs) var/new_length = input(user, "Penis length in inches:\n([min_D]-[max_D])", "Character Preference") as num|null if(new_length) features["cock_length"] = clamp(round(new_length), min_D, max_D) if("cock_shape") var/new_shape var/list/hockeys = list() if(parent.can_have_part("taur")) var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]] for(var/A in GLOB.cock_shapes_list) var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[A] if(P.taur_icon && T.taur_mode & P.accepted_taurs) LAZYSET(hockeys, "[A] (Taur)", A) new_shape = input(user, "Penis shape:", "Character Preference") as null|anything in (GLOB.cock_shapes_list + hockeys) if(new_shape) features["cock_taur"] = FALSE if(hockeys[new_shape]) new_shape = hockeys[new_shape] features["cock_taur"] = TRUE features["cock_shape"] = new_shape if("cock_visibility") var/n_vis = input(user, "Penis Visibility", "Character Preference") as null|anything in CONFIG_GET(keyed_list/safe_visibility_toggles) if(n_vis) features["cock_visibility"] = n_vis if("balls_color") var/new_ballscolor = input(user, "Testicles Color:", "Character Preference","#"+features["balls_color"]) as color|null if(new_ballscolor) var/temp_hsv = RGBtoHSV(new_ballscolor) if(new_ballscolor == "#000000") features["balls_color"] = pref_species.default_color else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) features["balls_color"] = sanitize_hexcolor(new_ballscolor, 6) else to_chat(user,"Invalid color. Your color is not bright enough.") if("balls_shape") var/new_shape new_shape = input(user, "Testicle Shape", "Character Preference") as null|anything in GLOB.balls_shapes_list if(new_shape) features["balls_shape"] = new_shape if("balls_visibility") var/n_vis = input(user, "Testicles Visibility", "Character Preference") as null|anything in CONFIG_GET(keyed_list/safe_visibility_toggles) if(n_vis) features["balls_visibility"] = n_vis if("breasts_size") var/new_size = input(user, "Breast Size", "Character Preference") as null|anything in CONFIG_GET(keyed_list/breasts_cups_prefs) if(new_size) features["breasts_size"] = new_size if("breasts_shape") var/new_shape new_shape = input(user, "Breast Shape", "Character Preference") as null|anything in GLOB.breasts_shapes_list if(new_shape) features["breasts_shape"] = new_shape if("breasts_color") var/new_breasts_color = input(user, "Breast Color:", "Character Preference","#"+features["breasts_color"]) as color|null if(new_breasts_color) var/temp_hsv = RGBtoHSV(new_breasts_color) if(new_breasts_color == "#000000") features["breasts_color"] = pref_species.default_color else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) features["breasts_color"] = sanitize_hexcolor(new_breasts_color, 6) else to_chat(user,"Invalid color. Your color is not bright enough.") if("breasts_visibility") var/n_vis = input(user, "Breasts Visibility", "Character Preference") as null|anything in CONFIG_GET(keyed_list/safe_visibility_toggles) if(n_vis) features["breasts_visibility"] = n_vis if("vag_shape") var/new_shape new_shape = input(user, "Vagina Type", "Character Preference") as null|anything in GLOB.vagina_shapes_list if(new_shape) features["vag_shape"] = new_shape if("vag_color") var/new_vagcolor = input(user, "Vagina color:", "Character Preference","#"+features["vag_color"]) as color|null if(new_vagcolor) var/temp_hsv = RGBtoHSV(new_vagcolor) if(new_vagcolor == "#000000") features["vag_color"] = pref_species.default_color else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) features["vag_color"] = sanitize_hexcolor(new_vagcolor, 6) else to_chat(user,"Invalid color. Your color is not bright enough.") if("vag_visibility") var/n_vis = input(user, "Vagina Visibility", "Character Preference") as null|anything in CONFIG_GET(keyed_list/safe_visibility_toggles) if(n_vis) features["vag_visibility"] = n_vis if("butt_color") var/new_buttcolor = input(user, "Butt color:", "Character Preference","#"+features["butt_color"]) as color|null if(new_buttcolor) var/temp_hsv = RGBtoHSV(new_buttcolor) if(new_buttcolor == "#000000") features["butt_color"] = pref_species.default_color else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) features["butt_color"] = sanitize_hexcolor(new_buttcolor, 6) else to_chat(user,"Invalid color. Your color is not bright enough.") if("butt_size") var/min_B = CONFIG_GET(number/butt_min_size_prefs) var/max_B = CONFIG_GET(number/butt_max_size_prefs) var/new_length = input(user, "Butt size:\n([min_B]-[max_B])", "Character Preference") as num|null if(new_length) features["butt_size"] = clamp(round(new_length), min_B, max_B) if("butt_visibility") var/n_vis = input(user, "Butt Visibility", "Character Preference") as null|anything in CONFIG_GET(keyed_list/safe_visibility_toggles) if(n_vis) features["butt_visibility"] = n_vis if("ooccolor") var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference",ooccolor) as color|null if(new_ooccolor) ooccolor = sanitize_ooccolor(new_ooccolor) if("aooccolor") var/new_aooccolor = input(user, "Choose your Antag OOC colour:", "Game Preference",ooccolor) as color|null if(new_aooccolor) aooccolor = sanitize_ooccolor(new_aooccolor) if("bag") var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist if(new_backbag) backbag = new_backbag if("suit") if(jumpsuit_style == PREF_SUIT) jumpsuit_style = PREF_SKIRT else jumpsuit_style = PREF_SUIT if("uplink_loc") var/new_loc = input(user, "Choose your character's traitor uplink spawn location:", "Character Preference") as null|anything in GLOB.uplink_spawn_loc_list if(new_loc) uplink_spawn_loc = new_loc if("ai_core_icon") var/ai_core_icon = input(user, "Choose your preferred AI core display screen:", "AI Core Display Screen Selection") as null|anything in GLOB.ai_core_display_screens if(ai_core_icon) preferred_ai_core_display = ai_core_icon if("sec_dept") var/department = input(user, "Choose your preferred security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs if(department) prefered_security_department = department if ("preferred_map") var/maplist = list() var/default = "Default" if (config.defaultmap) default += " ([config.defaultmap.map_name])" for (var/M in config.maplist) var/datum/map_config/VM = config.maplist[M] var/friendlyname = "[VM.map_name] " if (VM.voteweight <= 0) friendlyname += " (disabled)" maplist[friendlyname] = VM.map_name maplist[default] = null var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in maplist if (pickedmap) preferred_map = maplist[pickedmap] if ("preferred_chaos") var/pickedchaos = input(user, "Choose your preferred level of chaos. This will help with dynamic threat level ratings.", "Character Preference") as null|anything in list(CHAOS_NONE,CHAOS_LOW,CHAOS_MED,CHAOS_HIGH,CHAOS_MAX) preferred_chaos = pickedchaos if ("clientfps") var/desiredfps = input(user, "Choose your desired fps. (0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num if (!isnull(desiredfps)) clientfps = desiredfps parent.fps = desiredfps if("ui") var/pickedui = input(user, "Choose your UI style.", "Character Preference", UI_style) as null|anything in GLOB.available_ui_styles if(pickedui) UI_style = pickedui if (parent && parent.mob && parent.mob.hud_used) parent.mob.hud_used.update_ui_style(ui_style2icon(UI_style)) if("pda_style") var/pickedPDAStyle = input(user, "Choose your PDA style.", "Character Preference", pda_style) as null|anything in GLOB.pda_styles if(pickedPDAStyle) pda_style = pickedPDAStyle if("pda_color") var/pickedPDAColor = input(user, "Choose your PDA Interface color.", "Character Preference",pda_color) as color|null if(pickedPDAColor) pda_color = pickedPDAColor if("pda_skin") var/pickedPDASkin = input(user, "Choose your PDA reskin.", "Character Preference", pda_skin) as null|anything in GLOB.pda_reskins if(pickedPDASkin) pda_skin = pickedPDASkin if ("max_chat_length") var/desiredlength = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [initial(max_chat_length)]))", "Character Preference", max_chat_length) as null|num if (!isnull(desiredlength)) max_chat_length = clamp(desiredlength, 1, CHAT_MESSAGE_MAX_LENGTH) if("hud_toggle_color") var/new_toggle_color = input(user, "Choose your HUD toggle flash color:", "Game Preference",hud_toggle_color) as color|null if(new_toggle_color) hud_toggle_color = new_toggle_color if("gender") var/chosengender = input(user, "Select your character's gender.", "Gender Selection", gender) as null|anything in list(MALE,FEMALE,"nonbinary","object") if(!chosengender) return switch(chosengender) if("nonbinary") chosengender = PLURAL features["body_model"] = pick(MALE, FEMALE) if("object") chosengender = NEUTER features["body_model"] = MALE else features["body_model"] = chosengender gender = chosengender if("body_size") var/new_body_size = input(user, "Choose your desired sprite size: (90-125%)\nWarning: This may make your character look distorted. Additionally, any size under 100% takes a 10% maximum health penalty", "Character Preference", features["body_size"]*100) as num|null if(new_body_size) features["body_size"] = clamp(new_body_size * 0.01, CONFIG_GET(number/body_size_min), CONFIG_GET(number/body_size_max)) if("tongue") var/selected_custom_tongue = input(user, "Choose your desired tongue (none means your species tongue)", "Character Preference") as null|anything in GLOB.roundstart_tongues if(selected_custom_tongue) custom_tongue = selected_custom_tongue if("speech_verb") var/selected_custom_speech_verb = input(user, "Choose your desired speech verb (none means your species speech verb)", "Character Preference") as null|anything in GLOB.speech_verbs if(selected_custom_speech_verb) custom_speech_verb = selected_custom_speech_verb if("language") var/selected_language = input(user, "Choose your desired additional language", "Character Preference") as null|anything in GLOB.roundstart_languages if(selected_language) additional_language = selected_language if("barksound") var/list/woof_woof = list() for(var/path in GLOB.bark_list) var/datum/bark/B = GLOB.bark_list[path] if(initial(B.ignore)) continue if(initial(B.ckeys_allowed)) var/list/allowed = initial(B.ckeys_allowed) if(!allowed.Find(user.client.ckey)) continue woof_woof[initial(B.name)] = initial(B.id) var/new_bork = input(user, "Choose your desired vocal bark", "Character Preference") as null|anything in woof_woof if(new_bork) bark_id = woof_woof[new_bork] var/datum/bark/B = GLOB.bark_list[bark_id] //Now we need sanitization to take into account bark-specific min/max values bark_speed = round(clamp(bark_speed, initial(B.minspeed), initial(B.maxspeed)), 1) bark_pitch = clamp(bark_pitch, initial(B.minpitch), initial(B.maxpitch)) bark_variance = clamp(bark_variance, initial(B.minvariance), initial(B.maxvariance)) if("barkspeed") var/datum/bark/B = GLOB.bark_list[bark_id] var/borkset = input(user, "Choose your desired bark speed (Higher is slower, lower is faster). Min: [initial(B.minspeed)]. Max: [initial(B.maxspeed)]", "Character Preference") as null|num if(borkset) bark_speed = round(clamp(borkset, initial(B.minspeed), initial(B.maxspeed)), 1) if("barkpitch") var/datum/bark/B = GLOB.bark_list[bark_id] var/borkset = input(user, "Choose your desired baseline bark pitch. Min: [initial(B.minpitch)]. Max: [initial(B.maxpitch)]", "Character Preference") as null|num if(borkset) bark_pitch = clamp(borkset, initial(B.minpitch), initial(B.maxpitch)) if("barkvary") var/datum/bark/B = GLOB.bark_list[bark_id] var/borkset = input(user, "Choose your desired baseline bark pitch. Min: [initial(B.minvariance)]. Max: [initial(B.maxvariance)]", "Character Preference") as null|num if(borkset) bark_variance = clamp(borkset, initial(B.minvariance), initial(B.maxvariance)) if("bodysprite") var/selected_body_sprite = input(user, "Choose your desired body sprite", "Character Preference") as null|anything in pref_species.allowed_limb_ids if(selected_body_sprite) chosen_limb_id = selected_body_sprite //this gets sanitized before loading if("marking_down") // move the specified marking down var/index = text2num(href_list["marking_index"]) var/marking_type = href_list["marking_type"] if(index && marking_type && features[marking_type] && index != length(features[marking_type])) var/index_down = index + 1 var/markings = features[marking_type] var/first_marking = markings[index] var/second_marking = markings[index_down] markings[index] = second_marking markings[index_down] = first_marking if("marking_up") // move the specified marking up var/index = text2num(href_list["marking_index"]) var/marking_type = href_list["marking_type"] if(index && marking_type && features[marking_type] && index != 1) var/index_up = index - 1 var/markings = features[marking_type] var/first_marking = markings[index] var/second_marking = markings[index_up] markings[index] = second_marking markings[index_up] = first_marking if("marking_remove") // move the specified marking up var/index = text2num(href_list["marking_index"]) var/marking_type = href_list["marking_type"] if(index && marking_type && features[marking_type]) // because linters are just absolutely awful: var/list/L = features[marking_type] L.Cut(index, index + 1) if("marking_add") // add a marking var/marking_type = href_list["marking_type"] if(marking_type && features[marking_type]) var/selected_limb = input(user, "Choose the limb to apply to.", "Character Preference") as null|anything in list("Head", "Chest", "Left Arm", "Right Arm", "Left Leg", "Right Leg", "All") if(selected_limb) var/list/marking_list = GLOB.mam_body_markings_list var/list/snowflake_markings_list = list() for(var/path in marking_list) var/datum/sprite_accessory/S = marking_list[path] if(istype(S)) if(istype(S, /datum/sprite_accessory/mam_body_markings)) var/datum/sprite_accessory/mam_body_markings/marking = S if(!(selected_limb in marking.covered_limbs) && selected_limb != "All") continue if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) snowflake_markings_list[S.name] = path var/selected_marking = input(user, "Select the marking to apply to the limb.") as null|anything in snowflake_markings_list if(selected_marking) if(selected_limb != "All") var/limb_value = text2num(GLOB.bodypart_values[selected_limb]) features[marking_type] += list(list(limb_value, selected_marking)) else var/datum/sprite_accessory/mam_body_markings/S = marking_list[selected_marking] for(var/limb in S.covered_limbs) var/limb_value = text2num(GLOB.bodypart_values[limb]) features[marking_type] += list(list(limb_value, selected_marking)) if("marking_color") var/index = text2num(href_list["marking_index"]) var/marking_type = href_list["marking_type"] if(index && marking_type && features[marking_type]) // work out the input options to show the user var/list/options = list("Primary") var/number_colors = text2num(href_list["number_colors"]) var/color_number = 1 // 1-3 which color are we editing if(number_colors >= 2) options += "Secondary" if(number_colors == 3) options += "Tertiary" var/color_option = input(user, "Select the colour you wish to edit") as null|anything in options if(color_option) if(color_option == "Secondary") color_number = 2 if(color_option == "Tertiary") color_number = 3 // perform some magic on the color number var/list/marking_list = features[marking_type][index] var/datum/sprite_accessory/mam_body_markings/S = GLOB.mam_body_markings_list[marking_list[2]] var/matrixed_sections = S.covered_limbs[GLOB.bodypart_names[num2text(marking_list[1])]] if(color_number == 1) switch(matrixed_sections) if(MATRIX_GREEN) color_number = 2 if(MATRIX_BLUE) color_number = 3 else if(color_number == 2) switch(matrixed_sections) if(MATRIX_RED_BLUE) color_number = 3 if(MATRIX_GREEN_BLUE) color_number = 3 var/color_list = features[marking_type][index][3] var/new_marking_color = input(user, "Choose your character's marking color:", "Character Preference","#"+color_list[color_number]) as color|null if(new_marking_color) var/temp_hsv = RGBtoHSV(new_marking_color) if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin color_list[color_number] = "#[sanitize_hexcolor(new_marking_color, 6)]" else to_chat(user, "Invalid color. Your color is not bright enough.") else switch(href_list["preference"]) //CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj if("genital_colour") features["genitals_use_skintone"] = !features["genitals_use_skintone"] if("arousable") arousable = !arousable if("has_cock") features["has_cock"] = !features["has_cock"] if(features["has_cock"] == FALSE) features["has_balls"] = FALSE if("has_balls") features["has_balls"] = !features["has_balls"] if("has_breasts") features["has_breasts"] = !features["has_breasts"] if(features["has_breasts"] == FALSE) features["breasts_producing"] = FALSE if("breasts_producing") features["breasts_producing"] = !features["breasts_producing"] if("has_vag") features["has_vag"] = !features["has_vag"] if(features["has_vag"] == FALSE) features["has_womb"] = FALSE if("has_womb") features["has_womb"] = !features["has_womb"] if("has_butt") features["has_butt"] = !features["has_butt"] if("widescreenpref") widescreenpref = !widescreenpref user.client.view_size.setDefault(getScreenSize(widescreenpref)) if("long_strip_menu") long_strip_menu = !long_strip_menu if("pixel_size") switch(pixel_size) if(PIXEL_SCALING_AUTO) pixel_size = PIXEL_SCALING_1X if(PIXEL_SCALING_1X) pixel_size = PIXEL_SCALING_1_2X if(PIXEL_SCALING_1_2X) pixel_size = PIXEL_SCALING_2X if(PIXEL_SCALING_2X) pixel_size = PIXEL_SCALING_3X if(PIXEL_SCALING_3X) pixel_size = PIXEL_SCALING_AUTO user.client.view_size.apply() //Let's winset() it so it actually works if("scaling_method") switch(scaling_method) if(SCALING_METHOD_NORMAL) scaling_method = SCALING_METHOD_DISTORT if(SCALING_METHOD_DISTORT) scaling_method = SCALING_METHOD_BLUR if(SCALING_METHOD_BLUR) scaling_method = SCALING_METHOD_NORMAL user.client.view_size.setZoomMode() if("autostand") autostand = !autostand if("auto_ooc") auto_ooc = !auto_ooc if("no_tetris_storage") no_tetris_storage = !no_tetris_storage if ("screenshake") var/desiredshake = input(user, "Set the amount of screenshake you want. \n(0 = disabled, 100 = full, no maximum (at your own risk).)", "Character Preference", screenshake) as null|num if (!isnull(desiredshake)) screenshake = desiredshake if("damagescreenshake") switch(damagescreenshake) if(0) damagescreenshake = 1 if(1) damagescreenshake = 2 if(2) damagescreenshake = 0 else damagescreenshake = 1 if ("recoil_screenshake") var/desiredshake = input(user, "Set the amount of recoil screenshake/push you want. \n(0 = disabled, 100 = full, no maximum (at your own risk).)", "Character Preference", screenshake) as null|num if (!isnull(desiredshake)) recoil_screenshake = desiredshake if("nameless") nameless = !nameless //END CITADEL EDIT if("publicity") if(unlock_content) toggles ^= MEMBER_PUBLIC if("body_model") features["body_model"] = features["body_model"] == MALE ? FEMALE : MALE if("hotkeys") hotkeys = !hotkeys user.client.ensure_keys_set(src) if("keybindings_capture") var/datum/keybinding/kb = GLOB.keybindings_by_name[href_list["keybinding"]] CaptureKeybinding(user, kb, href_list["old_key"], text2num(href_list["independent"]), kb.special || kb.clientside) return if("keybindings_set") var/kb_name = href_list["keybinding"] if(!kb_name) user << browse(null, "window=capturekeypress") ShowChoices(user) return var/independent = href_list["independent"] var/clear_key = text2num(href_list["clear_key"]) var/old_key = href_list["old_key"] if(clear_key) if(independent) modless_key_bindings -= old_key else if(key_bindings[old_key]) key_bindings[old_key] -= kb_name LAZYADD(key_bindings["Unbound"], kb_name) if(!length(key_bindings[old_key])) key_bindings -= old_key user << browse(null, "window=capturekeypress") if(href_list["special"]) // special keys need a full reset user.client.ensure_keys_set(src) save_preferences() ShowChoices(user) return var/new_key = uppertext(href_list["key"]) var/AltMod = text2num(href_list["alt"]) ? "Alt" : "" var/CtrlMod = text2num(href_list["ctrl"]) ? "Ctrl" : "" var/ShiftMod = text2num(href_list["shift"]) ? "Shift" : "" var/numpad = text2num(href_list["numpad"]) ? "Numpad" : "" // var/key_code = text2num(href_list["key_code"]) if(GLOB._kbMap[new_key]) new_key = GLOB._kbMap[new_key] var/full_key switch(new_key) if("Alt") full_key = "[new_key][CtrlMod][ShiftMod]" if("Ctrl") full_key = "[AltMod][new_key][ShiftMod]" if("Shift") full_key = "[AltMod][CtrlMod][new_key]" else full_key = "[AltMod][CtrlMod][ShiftMod][numpad][new_key]" if(independent) modless_key_bindings -= old_key modless_key_bindings[full_key] = kb_name else if(key_bindings[old_key]) key_bindings[old_key] -= kb_name if(!length(key_bindings[old_key])) key_bindings -= old_key key_bindings[full_key] += list(kb_name) key_bindings[full_key] = sortList(key_bindings[full_key]) if(href_list["special"]) // special keys need a full reset user.client.ensure_keys_set(src) user << browse(null, "window=capturekeypress") save_preferences() if("keybindings_reset") var/choice = tgalert(user, "Would you prefer 'hotkey' or 'classic' defaults?", "Setup keybindings", "Hotkey", "Classic", "Cancel") if(choice == "Cancel") ShowChoices(user) return hotkeys = (choice == "Hotkey") key_bindings = (hotkeys) ? deepCopyList(GLOB.hotkey_keybinding_list_by_key) : deepCopyList(GLOB.classic_keybinding_list_by_key) modless_key_bindings = list() user.client.ensure_keys_set(src) if("chat_on_map") chat_on_map = !chat_on_map if("see_chat_non_mob") see_chat_non_mob = !see_chat_non_mob if("action_buttons") buttons_locked = !buttons_locked if("tgui_fancy") tgui_fancy = !tgui_fancy if("outline_enabled") outline_enabled = !outline_enabled if("outline_color") var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null if(pickedOutlineColor != pickedOutlineColor) outline_color = pickedOutlineColor // nullable if("screentip_pref") var/choice = input(user, "Choose your screentip preference", "Screentipping?", screentip_pref) as null|anything in GLOB.screentip_pref_options if(choice) screentip_pref = choice if("screentip_color") var/pickedScreentipColor = input(user, "Choose your screentip color.", "General Preference", screentip_color) as color|null if(pickedScreentipColor) screentip_color = pickedScreentipColor if("screentip_images") screentip_images = !screentip_images if("tgui_lock") tgui_lock = !tgui_lock if("winflash") windowflashing = !windowflashing if("hear_adminhelps") toggles ^= SOUND_ADMINHELP if("announce_login") toggles ^= ANNOUNCE_LOGIN if("combohud_lighting") toggles ^= COMBOHUD_LIGHTING // Deadmin preferences if("toggle_deadmin_always") deadmin ^= DEADMIN_ALWAYS if("toggle_deadmin_antag") deadmin ^= DEADMIN_ANTAGONIST if("toggle_deadmin_head") deadmin ^= DEADMIN_POSITION_HEAD if("toggle_deadmin_security") deadmin ^= DEADMIN_POSITION_SECURITY if("toggle_deadmin_silicon") deadmin ^= DEADMIN_POSITION_SILICON // if("disable_antag") toggles ^= NO_ANTAG if("be_special") var/be_special_type = href_list["be_special_type"] if(be_special_type in be_special) if(be_special[be_special_type] >= 1) be_special -= be_special_type else be_special[be_special_type] = 1 else be_special += be_special_type be_special[be_special_type] = 0 if("name") be_random_name = !be_random_name if("all") be_random_body = !be_random_body if("hear_midis") toggles ^= SOUND_MIDI if("persistent_scars") persistent_scars = !persistent_scars if("clear_scars") to_chat(user, "All scar slots cleared. Please save character to confirm.") scars_list["1"] = "" scars_list["2"] = "" scars_list["3"] = "" scars_list["4"] = "" scars_list["5"] = "" if("lobby_music") toggles ^= SOUND_LOBBY if((toggles & SOUND_LOBBY) && user.client && isnewplayer(user)) user.client.playtitlemusic() else user.stop_sound_channel(CHANNEL_LOBBYMUSIC) if("ghost_ears") chat_toggles ^= CHAT_GHOSTEARS if("ghost_sight") chat_toggles ^= CHAT_GHOSTSIGHT if("ghost_whispers") chat_toggles ^= CHAT_GHOSTWHISPER if("ghost_radio") chat_toggles ^= CHAT_GHOSTRADIO if("ghost_pda") chat_toggles ^= CHAT_GHOSTPDA if("income_pings") chat_toggles ^= CHAT_BANKCARD if("pull_requests") chat_toggles ^= CHAT_PULLR if("allow_midround_antag") toggles ^= MIDROUND_ANTAG if("parallaxup") parallax = WRAP(parallax + 1, PARALLAX_DISABLE, PARALLAX_INSANE + 1) parent?.parallax_holder?.Reset() if("parallaxdown") parallax = WRAP(parallax - 1, PARALLAX_DISABLE, PARALLAX_INSANE + 1) parent?.parallax_holder?.Reset() // Citadel edit - Prefs don't work outside of this. :c if("genital_examine") cit_toggles ^= GENITAL_EXAMINE if("vore_examine") cit_toggles ^= VORE_EXAMINE if("hound_sleeper") cit_toggles ^= MEDIHOUND_SLEEPER if("toggleeatingnoise") cit_toggles ^= EATING_NOISES if("toggledigestionnoise") cit_toggles ^= DIGESTION_NOISES if("toggleforcefeedtrash") cit_toggles ^= TRASH_FORCEFEED if("breast_enlargement") cit_toggles ^= BREAST_ENLARGEMENT if("penis_enlargement") cit_toggles ^= PENIS_ENLARGEMENT if("butt_enlargement") cit_toggles ^= BUTT_ENLARGEMENT if("feminization") cit_toggles ^= FORCED_FEM if("masculinization") cit_toggles ^= FORCED_MASC if("hypno") cit_toggles ^= HYPNO if("never_hypno") cit_toggles ^= NEVER_HYPNO if("aphro") cit_toggles ^= NO_APHRO if("ass_slap") cit_toggles ^= NO_ASS_SLAP if("bimbo") cit_toggles ^= BIMBOFICATION if("auto_wag") cit_toggles ^= NO_AUTO_WAG //END CITADEL EDIT if("ambientocclusion") ambientocclusion = !ambientocclusion if(parent && parent.screen && parent.screen.len) var/atom/movable/screen/plane_master/game_world/G = parent.mob.hud_used.plane_masters["[GAME_PLANE]"] var/atom/movable/screen/plane_master/above_wall/A = parent.mob.hud_used.plane_masters["[ABOVE_WALL_PLANE]"] var/atom/movable/screen/plane_master/wall/W = parent.mob.hud_used.plane_masters["[WALL_PLANE]"] G.backdrop(parent.mob) A.backdrop(parent.mob) W.backdrop(parent.mob) if("auto_fit_viewport") auto_fit_viewport = !auto_fit_viewport if(auto_fit_viewport && parent) parent.fit_viewport() if("hud_toggle_flash") hud_toggle_flash = !hud_toggle_flash if("barkpreview") if(SSticker.current_state == GAME_STATE_STARTUP) //Timers don't tick at all during game startup, so let's just give an error message to_chat(user, "Bark previews can't play during initialization!") return if(!COOLDOWN_FINISHED(src, bark_previewing)) return if(!parent || !parent.mob) return COOLDOWN_START(src, bark_previewing, (5 SECONDS)) var/atom/movable/barkbox = new(get_turf(parent.mob)) barkbox.set_bark(bark_id) var/total_delay for(var/i in 1 to (round((32 / bark_speed)) + 1)) addtimer(CALLBACK(barkbox, /atom/movable/proc/bark, list(parent.mob), 7, 70, BARK_DO_VARY(bark_pitch, bark_variance)), total_delay) total_delay += rand(DS2TICKS(bark_speed/4), DS2TICKS(bark_speed/4) + DS2TICKS(bark_speed/4)) TICKS QDEL_IN(barkbox, total_delay) if("save") save_preferences() save_character() if("load") load_preferences() load_character() if("changeslot") if(!load_character(text2num(href_list["num"]))) random_character() real_name = random_unique_name(gender) save_character() if("tab") if(href_list["tab"]) current_tab = text2num(href_list["tab"]) if(href_list["preference"] == "gear") if(href_list["clear_loadout"]) loadout_data["SAVE_[loadout_slot]"] = list() save_preferences() if(href_list["select_category"]) gear_category = html_decode(href_list["select_category"]) gear_subcategory = GLOB.loadout_categories[gear_category][1] if(href_list["select_subcategory"]) gear_subcategory = html_decode(href_list["select_subcategory"]) if(href_list["toggle_gear_path"]) var/name = html_decode(href_list["toggle_gear_path"]) var/datum/gear/G = GLOB.loadout_items[gear_category][gear_subcategory][name] if(!G) return var/toggle = text2num(href_list["toggle_gear"]) if(!toggle && has_loadout_gear(loadout_slot, "[G.type]"))//toggling off and the item effectively is in chosen gear) remove_gear_from_loadout(loadout_slot, "[G.type]") else if(toggle && !(has_loadout_gear(loadout_slot, "[G.type]"))) if(!is_loadout_slot_available(G.category)) to_chat(user, "You cannot take this loadout, as you've already chosen too many of the same category!") return if(G.donoritem && !G.donator_ckey_check(user.ckey)) to_chat(user, "This is an item intended for donator use only. You are not authorized to use this item.") return if(istype(G, /datum/gear/unlockable) && !can_use_unlockable(G)) to_chat(user, "To use this item, you need to meet the defined requirements!") return if(gear_points >= initial(G.cost)) var/list/new_loadout_data = list(LOADOUT_ITEM = "[G.type]") if(length(G.loadout_initial_colors)) new_loadout_data[LOADOUT_COLOR] = G.loadout_initial_colors else new_loadout_data[LOADOUT_COLOR] = list("#FFFFFF") if(loadout_data["SAVE_[loadout_slot]"]) loadout_data["SAVE_[loadout_slot]"] += list(new_loadout_data) //double packed because it does the union of the CONTENTS of the lists else loadout_data["SAVE_[loadout_slot]"] = list(new_loadout_data) //double packed because you somehow had no save slot in your loadout? if(href_list["loadout_color"] || href_list["loadout_color_polychromic"] || href_list["loadout_rename"] || href_list["loadout_redescribe"]) //if the gear doesn't exist, or they don't have it, ignore the request var/name = html_decode(href_list["loadout_gear_name"]) var/datum/gear/G = GLOB.loadout_items[gear_category][gear_subcategory][name] if(!G) return var/user_gear = has_loadout_gear(loadout_slot, "[G.type]") if(!user_gear) return //possible requests: recolor, recolor (polychromic), rename, redescribe //always make sure the gear allows said request before proceeding //non-poly coloring can only be done by non-poly items if(href_list["loadout_color"] && !(G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC)) if(!length(user_gear[LOADOUT_COLOR])) user_gear[LOADOUT_COLOR] = list("#FFFFFF") var/current_color = user_gear[LOADOUT_COLOR][1] var/new_color = input(user, "Polychromic options", "Choose Color", current_color) as color|null user_gear[LOADOUT_COLOR][1] = sanitize_hexcolor(new_color, 6, TRUE, current_color) //poly coloring can only be done by poly items if(href_list["loadout_color_polychromic"] && (G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC)) var/list/color_options = list() for(var/i=1, i<=length(G.loadout_initial_colors), i++) color_options += "Color [i]" var/color_to_change = input(user, "Polychromic options", "Recolor [name]") as null|anything in color_options if(color_to_change) var/color_index = text2num(copytext(color_to_change, 7)) var/current_color = user_gear[LOADOUT_COLOR][color_index] var/new_color = input(user, "Polychromic options", "Choose [color_to_change] Color", current_color) as color|null if(new_color) user_gear[LOADOUT_COLOR][color_index] = sanitize_hexcolor(new_color, 6, TRUE, current_color) //both renaming and redescribing strip the input to stop html injection //renaming is only allowed if it has the flag for it if(href_list["loadout_rename"] && (G.loadout_flags & LOADOUT_CAN_NAME)) var/new_name = stripped_input(user, "Enter new name for item. Maximum [MAX_NAME_LEN] characters.", "Loadout Item Naming", null, MAX_NAME_LEN) if(new_name) user_gear[LOADOUT_CUSTOM_NAME] = new_name //redescribing is only allowed if it has the flag for it if(href_list["loadout_redescribe"] && (G.loadout_flags & LOADOUT_CAN_DESCRIPTION)) //redescribe isnt a real word but i can't think of the right term to use var/new_description = stripped_input(user, "Enter new description for item. Maximum 500 characters.", "Loadout Item Redescribing", null, 500) if(new_description) user_gear[LOADOUT_CUSTOM_DESCRIPTION] = new_description ShowChoices(user) return 1 /datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE, initial_spawn = FALSE) if(be_random_name) real_name = pref_species.random_name(gender) if(be_random_body) random_character(gender) if(roundstart_checks) if(CONFIG_GET(flag/humans_need_surnames) && (pref_species.id == "human")) var/firstspace = findtext(real_name, " ") var/name_length = length(real_name) if(!firstspace) //we need a surname real_name += " [pick(GLOB.last_names)]" else if(firstspace == name_length) real_name += "[pick(GLOB.last_names)]" //reset size if applicable if(character.dna.features["body_size"]) var/initial_old_size = character.dna.features["body_size"] character.dna.features["body_size"] = RESIZE_DEFAULT_SIZE character.dna.update_body_size(initial_old_size) character.real_name = nameless ? "[real_name] #[rand(10000, 99999)]" : real_name character.name = character.real_name character.nameless = nameless character.custom_species = custom_species character.gender = gender character.age = age character.left_eye_color = left_eye_color character.right_eye_color = right_eye_color var/obj/item/organ/eyes/organ_eyes = character.getorgan(/obj/item/organ/eyes) if(organ_eyes) if(!initial(organ_eyes.left_eye_color)) organ_eyes.left_eye_color = left_eye_color organ_eyes.right_eye_color = right_eye_color organ_eyes.old_left_eye_color = left_eye_color organ_eyes.old_right_eye_color = right_eye_color character.hair_color = hair_color character.facial_hair_color = facial_hair_color character.skin_tone = skin_tone character.dna.skin_tone_override = use_custom_skin_tone ? skin_tone : null character.hair_style = hair_style character.facial_hair_style = facial_hair_style character.grad_style = grad_style character.grad_color = grad_color character.underwear = underwear character.saved_underwear = underwear character.undershirt = undershirt character.saved_undershirt = undershirt character.socks = socks character.saved_socks = socks character.undie_color = undie_color character.shirt_color = shirt_color character.socks_color = socks_color var/datum/species/chosen_species if(!roundstart_checks || (pref_species.id in GLOB.roundstart_races)) chosen_species = pref_species.type else chosen_species = /datum/species/human pref_species = new /datum/species/human save_character() character.dna.features = features.Copy() character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE) character.dna.species.eye_type = eye_type if(chosen_limb_id && (chosen_limb_id in character.dna.species.allowed_limb_ids)) character.dna.species.mutant_bodyparts["limbs_id"] = chosen_limb_id character.dna.real_name = character.real_name character.dna.nameless = character.nameless character.dna.custom_species = character.custom_species var/old_size = RESIZE_DEFAULT_SIZE if(isdwarf(character)) character.dna.features["body_size"] = RESIZE_DEFAULT_SIZE if((parent && parent.can_have_part("meat_type")) || pref_species.mutant_bodyparts["meat_type"]) character.type_of_meat = GLOB.meat_types[features["meat_type"]] if(((parent && parent.can_have_part("legs")) || pref_species.mutant_bodyparts["legs"]) && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian")) pref_species.species_traits |= DIGITIGRADE else pref_species.species_traits -= DIGITIGRADE if(DIGITIGRADE in pref_species.species_traits) character.Digitigrade_Leg_Swap(FALSE) else character.Digitigrade_Leg_Swap(TRUE) character.give_genitals(TRUE) //character.update_genitals() is already called on genital.update_appearance() character.dna.update_body_size(old_size) //speech stuff if(custom_tongue != "default") var/new_tongue = GLOB.roundstart_tongues[custom_tongue] if(new_tongue) character.dna.species.mutanttongue = new_tongue //this means we get our tongue when we clone var/obj/item/organ/tongue/T = character.getorganslot(ORGAN_SLOT_TONGUE) if(T) qdel(T) var/obj/item/organ/tongue/new_custom_tongue = new new_tongue new_custom_tongue.Insert(character) if(custom_speech_verb != "default") character.dna.species.say_mod = custom_speech_verb if(additional_language && additional_language != "None") var/language_entry = GLOB.roundstart_languages[additional_language] if(language_entry) character.additional_language = language_entry character.grant_language(language_entry, TRUE, TRUE) character.set_bark(bark_id) character.vocal_speed = bark_speed character.vocal_pitch = bark_pitch character.vocal_pitch_range = bark_variance //limb stuff, only done when initially spawning in if(initial_spawn) //delete any existing prosthetic limbs to make sure no remnant prosthetics are left over - But DO NOT delete those that are species-related for(var/obj/item/bodypart/part in character.bodyparts) if(part.is_robotic_limb(FALSE)) qdel(part) character.regenerate_limbs() //regenerate limbs so now you only have normal limbs for(var/modified_limb in modified_limbs) var/modification = modified_limbs[modified_limb][1] var/obj/item/bodypart/old_part = character.get_bodypart(modified_limb) if(modification == LOADOUT_LIMB_PROSTHETIC) var/obj/item/bodypart/new_limb switch(modified_limb) if(BODY_ZONE_L_ARM) new_limb = new/obj/item/bodypart/l_arm/robot/surplus(character) if(BODY_ZONE_R_ARM) new_limb = new/obj/item/bodypart/r_arm/robot/surplus(character) if(BODY_ZONE_L_LEG) new_limb = new/obj/item/bodypart/l_leg/robot/surplus(character) if(BODY_ZONE_R_LEG) new_limb = new/obj/item/bodypart/r_leg/robot/surplus(character) var/prosthetic_type = modified_limbs[modified_limb][2] if(prosthetic_type != "prosthetic") //lets just leave the old sprites as they are new_limb.icon = file("icons/mob/augmentation/cosmetic_prosthetic/[prosthetic_type].dmi") new_limb.replace_limb(character) qdel(old_part) SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks) //let's be sure the character updates if(icon_updates) character.update_body() character.update_hair() /datum/preferences/proc/post_copy_to(mob/living/carbon/human/character) //if no legs, and not a paraplegic or a slime, give them a free wheelchair if(modified_limbs[BODY_ZONE_L_LEG] == LOADOUT_LIMB_AMPUTATED && modified_limbs[BODY_ZONE_R_LEG] == LOADOUT_LIMB_AMPUTATED && !character.has_quirk(/datum/quirk/paraplegic) && !isjellyperson(character)) if(character.buckled) character.buckled.unbuckle_mob(character) var/turf/T = get_turf(character) var/obj/structure/chair/spawn_chair = locate() in T var/obj/vehicle/ridden/wheelchair/wheels = new(T) if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking wheels.setDir(spawn_chair.dir) wheels.buckle_mob(character) /datum/preferences/proc/get_default_name(name_id) switch(name_id) if("human") return random_unique_name() if("ai") return pick(GLOB.ai_names) if("cyborg") return DEFAULT_CYBORG_NAME if("clown") return pick(GLOB.clown_names) if("mime") return pick(GLOB.mime_names) return random_unique_name() /datum/preferences/proc/ask_for_custom_name(mob/user,name_id) var/namedata = GLOB.preferences_custom_names[name_id] if(!namedata) return var/raw_name = input(user, "Choose your character's [namedata["qdesc"]]:","Character Preference") as text|null if(!raw_name) if(namedata["allow_null"]) custom_names[name_id] = get_default_name(name_id) else return else var/sanitized_name = reject_bad_name(raw_name,namedata["allow_numbers"]) if(!sanitized_name) to_chat(user, "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,[namedata["allow_numbers"] ? ",0-9," : ""] -, ' and .") return else custom_names[name_id] = sanitized_name /datum/preferences/proc/get_filtered_holoform(filter_type) if(!custom_holoform_icon) return LAZYINITLIST(cached_holoform_icons) if(!cached_holoform_icons[filter_type]) cached_holoform_icons[filter_type] = process_holoform_icon_filter(custom_holoform_icon, filter_type) return cached_holoform_icons[filter_type] /// Resets the client's keybindings. Asks them for which /datum/preferences/proc/force_reset_keybindings() var/choice = tgalert(parent.mob, "Your basic keybindings need to be reset, emotes will remain as before. Would you prefer 'hotkey' or 'classic' mode?", "Reset keybindings", "Hotkey", "Classic") hotkeys = (choice != "Classic") force_reset_keybindings_direct(hotkeys) /// Does the actual reset /datum/preferences/proc/force_reset_keybindings_direct(hotkeys = TRUE) var/list/oldkeys = key_bindings key_bindings = (hotkeys) ? deepCopyList(GLOB.hotkey_keybinding_list_by_key) : deepCopyList(GLOB.classic_keybinding_list_by_key) for(var/key in oldkeys) if(!key_bindings[key]) key_bindings[key] = oldkeys[key] parent?.ensure_keys_set(src) /datum/preferences/proc/is_loadout_slot_available(slot) var/list/L LAZYINITLIST(L) for(var/i in loadout_data["SAVE_[loadout_slot]"]) var/datum/gear/G = i[LOADOUT_ITEM] var/occupied_slots = L[initial(G.category)] ? L[initial(G.category)] + 1 : 1 LAZYSET(L, initial(G.category), occupied_slots) switch(slot) if(ITEM_SLOT_BACKPACK) if(L[LOADOUT_CATEGORY_BACKPACK] < BACKPACK_SLOT_AMT) return TRUE if(ITEM_SLOT_HANDS) if(L[LOADOUT_CATEGORY_HANDS] < HANDS_SLOT_AMT) return TRUE else if(L[slot] < DEFAULT_SLOT_AMT) return TRUE /datum/preferences/proc/has_loadout_gear(save_slot, gear_type) var/list/gear_list = loadout_data["SAVE_[save_slot]"] for(var/loadout_gear in gear_list) if(loadout_gear[LOADOUT_ITEM] == gear_type) return loadout_gear return FALSE /datum/preferences/proc/remove_gear_from_loadout(save_slot, gear_type) var/find_gear = has_loadout_gear(save_slot, gear_type) if(find_gear) loadout_data["SAVE_[save_slot]"] -= list(find_gear) /datum/preferences/proc/can_use_unlockable(datum/gear/unlockable/unlockable_gear) if(unlockable_loadout_data[unlockable_gear.progress_key] >= unlockable_gear.progress_required) return TRUE return FALSE #undef DEFAULT_SLOT_AMT #undef HANDS_SLOT_AMT #undef BACKPACK_SLOT_AMT