GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts to play these roles ROLE_PAI = 0, ROLE_GUARDIAN = 0, ROLE_TRAITOR = 7, ROLE_CHANGELING = 14, ROLE_WIZARD = 14, ROLE_REV = 14, ROLE_VAMPIRE = 14, ROLE_BLOB = 14, ROLE_REVENANT = 14, ROLE_OPERATIVE = 21, ROLE_CULTIST = 21, ROLE_ALIEN = 21, ROLE_DEMON = 21, ROLE_SENTIENT = 21, ROLE_ELITE = 21, // ROLE_GANG = 21, ROLE_ABDUCTOR = 30 )) /proc/player_old_enough_antag(client/C, role) if(available_in_days_antag(C, role)) return 0 //available_in_days>0 = still some days required = player not old enough if(role_available_in_playtime(C, role)) return 0 //available_in_playtime>0 = still some more playtime required = they are not eligible return 1 /proc/available_in_days_antag(client/C, role) if(!C) return 0 if(!role) return 0 if(!GLOB.configuration.gamemode.antag_account_age_restriction) return 0 if(!isnum(C.player_age)) return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced var/minimal_player_age_antag = GLOB.special_role_times[num2text(role)] if(!isnum(minimal_player_age_antag)) return 0 return max(0, minimal_player_age_antag - C.player_age) /proc/check_client_age(client/C, days) // If days isn't provided, returns the age of the client. If it is provided, it returns the days until the player_age is equal to or greater than the days variable if(!days) return C.player_age else return max(0, days - C.player_age) /datum/preferences var/client/parent //doohickeys for savefiles var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used var/max_save_slots = MAX_SAVE_SLOTS var/max_gear_slots = 0 //game-preferences var/lastchangelog = "1" //Saved changlog timestamp (unix epoch) to detect if there was a change. Dont set this to 0 unless you want the last changelog date to be 4x longer than the expected lifespan of the universe. var/lastchangelog_2 = "1" // Clone of the above var for viewing changes since last connection. This is never overriden. Yes it needs to exist. var/exp var/ooccolor = "#b82e00" var/list/be_special = list() //Special role selection var/UI_style = "Midnight" var/toggles = TOGGLES_DEFAULT var/toggles2 = TOGGLES_2_DEFAULT // Created because 1 column has a bitflag limit of 24 (BYOND limitation not MySQL) var/sound = SOUND_DEFAULT var/UI_style_color = "#ffffff" var/UI_style_alpha = 255 var/clientfps = 63 var/atklog = ATKLOG_ALL var/fuid // forum userid /// Volume mixer, indexed by channel as TEXT (numerical indexes will not work). Volume goes from 0 to 100. var/list/volume_mixer = list( "1012" = 100, // CHANNEL_GENERAL //Note: This should stay on top because order in this list defines order of sliders in mixer's interface. "1024" = 100, // CHANNEL_LOBBYMUSIC "1023" = 100, // CHANNEL_ADMIN "1022" = 100, // CHANNEL_VOX "1021" = 100, // CHANNEL_JUKEBOX "1020" = 100, // CHANNEL_HEARTBEAT "1019" = 100, // CHANNEL_BUZZ "1018" = 100, // CHANNEL_AMBIENCE "1017" = 100, // CHANNEL_ENGINE "1016" = 100, // CHANNEL_FIREALARM "1015" = 100, // CHANNEL_ASH_STORM "1014" = 100, // CHANNEL_RADIO_NOISE "1013" = 100 // CHANNEL_BOSS_MUSIC ) /// The volume mixer save timer handle. Used to debounce the DB call to save, to avoid spamming. var/volume_mixer_saving = null // BYOND membership var/unlock_content = 0 var/gear_tab = "General" // Parallax var/parallax = PARALLAX_HIGH /// 2FA status var/_2fa_status = _2FA_DISABLED ///Screentip Mode, in pixels. 8 is small, 15 is mega big, 0 is off. var/screentip_mode = 8 ///Color of screentips at top of screen var/screentip_color = "#ffd391" /// Did we load successfully? var/successful_load = FALSE /// Have we loaded characters already? var/characters_loaded = FALSE // 0 = character settings, 1 = game preferences, 2 = loadout var/current_tab = TAB_CHAR /// List of all character saves we have. This is indexed based on the slot number var/list/datum/character_save/character_saves = list() /// The current active character var/datum/character_save/active_character /// How dark things are if client is a ghost, 0-255 var/ghost_darkness_level = LIGHTING_PLANE_ALPHA_VISIBLE /// Colourblind mode var/colourblind_mode = COLOURBLIND_MODE_NONE /// Active keybinds (currently useable by the mob/client) var/list/datum/keybindings = list() /// Keybinding overrides ("name" => ["key"...]) var/list/keybindings_overrides = null /// Player's region override for routing optimisation var/server_region = null /// List of admin ckeys this player wont hear sounds from var/list/admin_sound_ckey_ignore = list() /// View range preference for this client var/viewrange = DEFAULT_CLIENT_VIEWSIZE /datum/preferences/New(client/C, datum/db_query/Q) // Process our query parent = C max_gear_slots = GLOB.configuration.general.base_loadout_points parent?.set_macros() if(!SSdbcore.IsConnected()) init_keybindings() //we want default keybinds, even if DB is not connected return // Bail if(istype(C)) if(!IsGuestKey(C.key)) unlock_content = C.IsByondMember() if(unlock_content) max_save_slots = MAX_SAVE_SLOTS_MEMBER successful_load = load_preferences(Q) if(!successful_load) to_chat(C, "Your preferences failed to load. Please inform the server host immediately.") /datum/preferences/proc/color_square(colour) return "___" // Hello I am a proc full of snowflake species checks how are you /datum/preferences/proc/ShowChoices(mob/user) if(!user || !user.client) return if(SSatoms.initialized) // DNA won't be set up on our dummy mob if it's not ready active_character.update_preview_icon() user << browse_rsc(active_character.preview_icon_front, "previewicon.png") user << browse_rsc(active_character.preview_icon_side, "previewicon2.png") var/list/dat = list() dat += "
" dat += "Character Settings" dat += "Game Preferences" dat += "Antagonists" dat += "Loadout" dat += "Key Bindings" dat += "
" dat += "
" switch(current_tab) if(TAB_CHAR) // Character Settings var/datum/species/S = GLOB.all_species[active_character.species] if(!istype(S)) //The species was invalid. Set the species to the default, fetch the datum for that species and generate a random character. active_character.species = initial(active_character.species) S = GLOB.all_species[active_character.species] active_character.randomise() dat += "
" dat += "
" dat += "Name: " dat += "[active_character.real_name]" dat += "(Randomize)" dat += "(Always Randomize)
" dat += "
" dat += "
" dat += "Slot [default_slot][active_character.from_db ? "" : " (empty)"]
" dat += "Load slot - " dat += "Save slot" if(active_character.from_db) dat += "- Clear slot" dat += "
" dat += "
" dat += "
" dat += "

Identity

" dat += "Gender: [active_character.gender == MALE ? "Male" : (active_character.gender == FEMALE ? "Female" : "Genderless")]" dat += "
" dat += "Age: [active_character.age]
" dat += "Body: (®)
" dat += "Species: [active_character.species]
" if(active_character.species == "Vox") // Purge these bastards dat += "N2 Tank: [active_character.speciesprefs ? "Large N2 Tank" : "Specialized N2 Tank"]
" if(active_character.species == "Plasmaman") dat += "Plasma Tank: [active_character.speciesprefs ? "Large Plasma Tank" : "Specialized Plasma Tank"]
" if(active_character.species == "Grey") dat += "Wingdings: Set in disabilities
" dat += "Voice Translator: [active_character.speciesprefs ? "Yes" : "No"]
" dat += "Secondary Language: [active_character.language]
" if(S.autohiss_basic_map) dat += "Auto-accent: [active_character.autohiss_mode == AUTOHISS_FULL ? "Full" : (active_character.autohiss_mode == AUTOHISS_BASIC ? "Basic" : "Off")]
" if(NO_BLOOD in S.species_traits) // unique blood type for species with no_blood/unique_blood active_character.b_type = "None" else if(active_character.species == "Slime People") active_character.b_type = "Slime Jelly" else if(active_character.b_type == "None" || active_character.b_type == "Slime Jelly") active_character.b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") dat += "Blood Type: [active_character.b_type]
" if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE)) dat += "Skin Tone: [S.bodyflags & HAS_ICON_SKIN_TONE ? "[active_character.s_tone]" : "[-active_character.s_tone + 35]/220"]
" dat += "Disabilities: \[Set\]
" dat += "Nanotrasen Relation: [active_character.nanotrasen_relation]
" dat += "Physique: [active_character.physique]
" dat += "Height: [active_character.height]
" dat += "Set Flavor Text
" if(length(active_character.flavor_text) <= 40) if(!length(active_character.flavor_text)) dat += "\[...\]
" else dat += "[active_character.flavor_text]
" else dat += "[TextPreview(active_character.flavor_text)]...
" dat += "

Hair & Accessories

" if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories. var/headaccessoryname = "Head Accessory: " if(active_character.species == "Unathi") headaccessoryname = "Horns: " dat += "[headaccessoryname]" dat += "[active_character.ha_style] " dat += "Color [color_square(active_character.hacc_colour)]
" if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings. dat += "Head Markings: " dat += "[active_character.m_styles["head"]]" dat += "Color [color_square(active_character.m_colours["head"])]
" if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos. dat += "Body Markings: " dat += "[active_character.m_styles["body"]]" dat += "Color [color_square(active_character.m_colours["body"])]
" if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. dat += "Tail Markings: " dat += "[active_character.m_styles["tail"]]" dat += "Color [color_square(active_character.m_colours["tail"])]
" if(!(S.bodyflags & BALD)) dat += "Hair: " dat += "[active_character.h_style]" dat += "Color [color_square(active_character.h_colour)]" var/datum/sprite_accessory/temp_hair_style = GLOB.hair_styles_public_list[active_character.h_style] if(temp_hair_style && temp_hair_style.secondary_theme && !temp_hair_style.no_sec_colour) dat += " Color #2 [color_square(active_character.h_sec_colour)]" // Hair gradient dat += "
" dat += "- Gradient:" dat += " [active_character.h_grad_style]" dat += " Color [color_square(active_character.h_grad_colour)]" dat += " [active_character.h_grad_alpha]" dat += "
" dat += "- Gradient Offset: [active_character.h_grad_offset_x],[active_character.h_grad_offset_y]" dat += "
" else active_character.h_style = "Bald" if(!(S.bodyflags & SHAVED)) dat += "Facial Hair: " dat += "[active_character.f_style ? "[active_character.f_style]" : "Shaved"]" dat += "Color [color_square(active_character.f_colour)]" var/datum/sprite_accessory/temp_facial_hair_style = GLOB.facial_hair_styles_list[active_character.f_style] if(temp_facial_hair_style && temp_facial_hair_style.secondary_theme && !temp_facial_hair_style.no_sec_colour) dat += " Color #2 [color_square(active_character.f_sec_colour)]" dat += "
" else active_character.f_style = "Shaved" if(!(S.bodyflags & ALL_RPARTS)) dat += "Eyes: " dat += "Color [color_square(active_character.e_colour)]
" if((S.bodyflags & HAS_SKIN_COLOR) || ((S.bodyflags & HAS_BODYACC_COLOR) && GLOB.body_accessory_by_species[active_character.species]) || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins dat += "Body Color: " dat += "Color [color_square(active_character.s_colour)]
" if(GLOB.body_accessory_by_species[active_character.species] || check_rights(R_ADMIN, 0, user)) dat += "Body Accessory: " dat += "[active_character.body_accessory ? "[active_character.body_accessory]" : "None"]
" dat += "
" dat += "

Occupation Choices

" dat += "Set Occupation Preferences
" if(jobban_isbanned(user, ROLEBAN_RECORDS)) dat += "You are banned from using character records.
" else dat += "Character Records
" dat += "

Limbs

" if(S.bodyflags & HAS_ALT_HEADS) //Species with alt heads. dat += "Alternate Head: " dat += "[active_character.alt_head]
" dat += "Limbs and Parts: Adjust
" if(active_character.species != "Slime People" && active_character.species != "Machine") dat += "Internal Organs: Adjust
" //display limbs below var/ind = 0 for(var/name in active_character.organ_data) var/status = active_character.organ_data[name] var/organ_name = null switch(name) if("chest") organ_name = "torso" if("groin") organ_name = "lower body" if("head") organ_name = "head" if("l_arm") organ_name = "left arm" if("r_arm") organ_name = "right arm" if("l_leg") organ_name = "left leg" if("r_leg") organ_name = "right leg" if("l_foot") organ_name = "left foot" if("r_foot") organ_name = "right foot" if("l_hand") organ_name = "left hand" if("r_hand") organ_name = "right hand" if("eyes") organ_name = "eyes" if("ears") organ_name = "ears" if("heart") organ_name = "heart" if("lungs") organ_name = "lungs" if("liver") organ_name = "liver" if("kidneys") organ_name = "kidneys" if(status in list("cyborg", "amputated", "cybernetic")) ++ind if(ind > 1) dat += ", " switch(status) if("cyborg") var/datum/robolimb/R if(active_character.rlimb_data[name] && GLOB.all_robolimbs[active_character.rlimb_data[name]]) R = GLOB.all_robolimbs[active_character.rlimb_data[name]] else R = GLOB.basic_robolimb dat += "\t[R.company] [organ_name] prosthesis" if("amputated") dat += "\tAmputated [organ_name]" if("cybernetic") dat += "\tCybernetic [organ_name]" if(!ind) dat += "\[...\]
" else dat += "
" dat += "

Clothing

" if(S.clothing_flags & HAS_UNDERWEAR) dat += "Underwear: [active_character.underwear]
" if(S.clothing_flags & HAS_UNDERSHIRT) dat += "Undershirt: [active_character.undershirt]
" if(S.clothing_flags & HAS_SOCKS) dat += "Socks: [active_character.socks]
" dat += "Backpack Type: [active_character.backbag]
" var/datum/species/myspecies = GLOB.all_species[active_character.species] if(!isnull(myspecies)) dat += "

Species Information

" dat += "
Species Description: [myspecies.blurb]
" dat += "
" if(TAB_GAME) // General Preferences // LEFT SIDE OF THE PAGE dat += "
" dat += "

General Settings

" dat += "2FA Setup: [_2fastatus_to_text()]
" if(user.client.holder) dat += "Adminhelp sound: [(sound & SOUND_ADMINHELP)?"On":"Off"]
" dat += "AFK Cryoing: [(toggles2 & PREFTOGGLE_2_AFKWATCH) ? "Yes" : "No"]
" dat += "Ambient Occlusion: [toggles & PREFTOGGLE_AMBIENT_OCCLUSION ? "Enabled" : "Disabled"]
" dat += "Attack Animations: [(toggles2 & PREFTOGGLE_2_ITEMATTACK) ? "Yes" : "No"]
" if(unlock_content) dat += "BYOND Membership Publicity: [(toggles & PREFTOGGLE_MEMBER_PUBLIC) ? "Public" : "Hidden"]
" dat += "CKEY Anonymity: [toggles2 & PREFTOGGLE_2_ANON ? "Anonymous" : "Not Anonymous"]
" dat += "Colourblind Mode: [colourblind_mode]
" if(user.client.donator_level > 0) dat += "Donator Publicity: [(toggles & PREFTOGGLE_DONATOR_PUBLIC) ? "Public" : "Hidden"]
" dat += "FPS: [clientfps]
" dat += "Ghost Ears: [(toggles & PREFTOGGLE_CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" dat += "Ghost Radio: [(toggles & PREFTOGGLE_CHAT_GHOSTRADIO) ? "All Chatter" : "Nearest Speakers"]
" dat += "Ghost Sight: [(toggles & PREFTOGGLE_CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" dat += "Ghost PDA: [(toggles & PREFTOGGLE_CHAT_GHOSTPDA) ? "All PDA Messages" : "No PDA Messages"]
" if(check_rights(R_ADMIN,0)) dat += "OOC Color:     Change
" if(GLOB.configuration.general.allow_character_metadata) dat += "OOC Notes: Edit
" 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 += "Parallax in darkness: [toggles2 & PREFTOGGLE_2_PARALLAX_IN_DARKNESS ? "Enabled" : "Disabled"]
" dat += "Play Admin MIDIs: [(sound & SOUND_MIDI) ? "Yes" : "No"]
" dat += "Play Lobby Music: [(sound & SOUND_LOBBY) ? "Yes" : "No"]
" dat += "Randomized Character Slot: [toggles2 & PREFTOGGLE_2_RANDOMSLOT ? "Yes" : "No"]
" dat += "View Range: [viewrange]
" dat += "Window Flashing: [(toggles2 & PREFTOGGLE_2_WINDOWFLASHING) ? "Yes" : "No"]
" dat += "Modsuit Activation Method: [(toggles2 & PREFTOGGLE_2_MOD_ACTIVATION_METHOD) ? "Middle Click" : "Alt Click"]
" // RIGHT SIDE OF THE PAGE dat += "
" dat += "

Interface Settings

" dat += "Set screentip mode: [(screentip_mode == 0) ? "Disabled" : "[screentip_mode]px"]
" dat += "Screentip color:     Change
" dat += "Thought Bubble: [(toggles2 & PREFTOGGLE_2_THOUGHT_BUBBLE) ? "Yes" : "No"]
" dat += "Custom UI settings:
" dat += " - Alpha (transparency): [UI_style_alpha]
" dat += " - Color: [UI_style_color]    
" dat += " - UI Style: [UI_style]
" dat += "TGUI settings:
" dat += " - Fancy TGUI: [(toggles2 & PREFTOGGLE_2_FANCYUI) ? "Yes" : "No"]
" dat += " - Input Lists: [(toggles2 & PREFTOGGLE_2_DISABLE_TGUI_LISTS) ? "Default" : "TGUI"]
" dat += "
" if(TAB_ANTAG) // Antagonist's Preferences dat += "
" dat += "

Special Role Settings

" if(jobban_isbanned(user, ROLE_SYNDICATE)) dat += "You are banned from special roles." be_special = list() else for(var/i in GLOB.special_roles) if(jobban_isbanned(user, i)) dat += "Be [capitalize(i)]: \[BANNED]
" else if(!player_old_enough_antag(user.client, i)) var/available_in_days_antag = available_in_days_antag(user.client, i) var/role_available_in_playtime = get_exp_format(role_available_in_playtime(user.client, i)) if(available_in_days_antag) dat += "Be [capitalize(i)]: \[IN [(available_in_days_antag)] DAYS]
" else if(role_available_in_playtime) dat += "Be [capitalize(i)]: \[IN [(role_available_in_playtime)]]
" else dat += "Be [capitalize(i)]: \[ERROR]
" else var/is_special = (i in src.be_special) dat += "Be [capitalize(i)]:[(is_special) ? "Yes" : "No"]
" dat += "
" if(TAB_GEAR) var/total_cost = build_loadout() var/fcolor = "#3366CC" if(total_cost < max_gear_slots) fcolor = "#E67300" dat += "" dat += "" dat += "" var/datum/loadout_category/LC = GLOB.loadout_categories[gear_tab] dat += "" dat += "" dat += "" for(var/gear_name in LC.gear) var/datum/gear/G = LC.gear[gear_name] var/ticked = (G.type in active_character.loadout_gear) if(G.donator_tier > user.client.donator_level) dat += "" else dat += "" dat += "" if(ticked) . += "" dat += "
[total_cost]/[max_gear_slots] loadout points spent. \[Clear Loadout\]
" var/firstcat = 1 for(var/category in GLOB.loadout_categories) if(firstcat) firstcat = 0 else dat += " |" if(category == gear_tab) dat += " [category] " else dat += " [category] " dat += "

[LC.category]

[G.display_name]
[G.display_name][G.cost]" if(G.allowed_roles) dat += "Restrictions: " for(var/role in G.allowed_roles) dat += role + " " dat += "" dat += "[G.description]
" for(var/datum/gear_tweak/tweak in G.gear_tweaks) . += " [tweak.get_contents(active_character.get_tweak_metadata(G, tweak))]" . += "
" if(TAB_KEYS) dat += "
All Key Bindings: " dat += "Reset to Default " dat += "Clear
" dat += "
" dat += "
Please note, some keybinds are overridden by other categories.
" dat += "
Ensure you bind all of them, or the specific one you want.
" dat += "
" dat += "
Users of legacy mode can only rebind and use the following keys:
" dat += "
Arrow Keys, Function Keys, Insert, Del, Home, End, PageUp, PageDn.
" dat += "" // Lookup lists to make our life easier var/static/list/keybindings_by_cat if(!keybindings_by_cat) keybindings_by_cat = list() for(var/kb in GLOB.keybindings) var/datum/keybinding/KB = kb keybindings_by_cat["[KB.category]"] += list(KB) for(var/cat in GLOB.keybindings_groups) dat += "" dat += "" for(var/kb in keybindings_by_cat["[GLOB.keybindings_groups[cat]]"]) var/datum/keybinding/KB = kb var/kb_uid = KB.UID() // Cache this to reduce proc jumps var/override_keys = (keybindings_overrides && keybindings_overrides[KB.name]) var/list/keys = override_keys || KB.keys var/keys_buttons = "" for(var/key in keys) var/disp_key = key if(override_keys) disp_key = "[disp_key]" keys_buttons += "[disp_key] " dat += "" dat += "" dat += "" : ""]" dat += "" if(KB.category == KB_CATEGORY_EMOTE_CUSTOM) var/datum/keybinding/custom/custom_emote_keybind = kb if(custom_emote_keybind.donor_exclusive && !(user.client.donator_level || user.client.holder || unlock_content)) dat += "" dat += "" dat += "" dat += "" continue dat += "" dat += "" var/emote_text = active_character.custom_emotes[custom_emote_keybind.name] //check if this emote keybind has an associated value on the character save if(!emote_text) dat += "" else dat += "" dat += "" dat += "" dat += "" dat += "" dat += "" dat += "

[cat]

[KB.name][keys_buttons][(length(keys) < 5) ? "+Reset to Default Clear
The use of this emote is restricted to patrons and byond members.
[custom_emote_keybind.default_emote_text]\"[active_character.real_name] [emote_text]\"Change TextReset to Default


" dat += "
" if(!IsGuestKey(user.key)) dat += "Undo - " dat += "Save Setup - " dat += "Reset Setup" dat += "
" var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 820, 770) popup.set_content(dat.Join("")) popup.open(FALSE) /datum/preferences/proc/open_load_dialog(mob/user) var/dat = "" dat += "
" dat += "Select a character slot to load
" var/name for(var/i in 1 to length(character_saves)) var/datum/character_save/CS = character_saves[i] if(CS.from_db) name = CS.real_name else name = "Character [i]" if(i == default_slot) name = "[name]" dat += "[name]
" dat += "
" dat += "Close
" dat += "
" var/datum/browser/popup = new(user, "saves", "
Character Saves
", 300, 390) popup.set_content(dat) popup.open(0) /datum/preferences/proc/close_load_dialog(mob/user) user << browse(null, "window=saves") /datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) var/datum/job/job = SSjobs.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 if(role == "Assistant") if(active_character.job_support_low & job.flag) active_character.job_support_low &= ~job.flag else active_character.job_support_low |= job.flag active_character.SetChoices(user) return 1 active_character.SetJobPreferenceLevel(job, desiredLvl) active_character.SetChoices(user) return 1 // This is scoped here instead of /datum/character_save just because of ShowChoices() /datum/preferences/proc/SetJob(mob/user, role) var/datum/job/job = SSjobs.GetJob(role) if(!job) user << browse(null, "window=mob_occupation") ShowChoices(user) return if(role == "Assistant") if(active_character.job_support_low & job.flag) active_character.job_support_low &= ~job.flag else active_character.job_support_low |= job.flag active_character.SetChoices(user) return 1 if(active_character.GetJobDepartment(job, 1) & job.flag) active_character.SetJobDepartment(job, 1) else if(active_character.GetJobDepartment(job, 2) & job.flag) active_character.SetJobDepartment(job, 2) else if(active_character.GetJobDepartment(job, 3) & job.flag) active_character.SetJobDepartment(job, 3) else//job = Never active_character.SetJobDepartment(job, 4) active_character.SetChoices(user) return 1 /** * Rebuilds the `loadout_gear` list of the [active_character], and returns the total end cost. * * Caches and cuts the existing [/datum/character_save/var/loadout_gear] list and remakes it, checking the `subtype_selection_cost` and overall cost validity of each item. * * If the item's [/datum/gear/var/subtype_selection_cost] is `FALSE`, any future items with the same [/datum/gear/var/main_typepath] will have their cost skipped. * If adding the item will take the total cost over the maximum, it won't be added to the list. * * Arguments: * * new_item - A new [/datum/gear] item to be added to the `loadout_gear` list. */ /datum/preferences/proc/build_loadout(datum/gear/new_item) var/total_cost = 0 var/list/type_blacklist = list() var/list/loadout_cache = active_character.loadout_gear.Copy() active_character.loadout_gear.Cut() if(new_item) loadout_cache += new_item.type for(var/I in loadout_cache) var/datum/gear/G = GLOB.gear_datums[text2path(I) || I] if(!G) continue var/added_cost = G.cost if(!G.subtype_selection_cost) // If listings of the same subtype shouldn't have their cost added. if(G.main_typepath in type_blacklist) added_cost = 0 else type_blacklist += G.main_typepath if((total_cost + added_cost) > max_gear_slots) continue // If the final cost is too high, don't add the item. active_character.loadout_gear += G.type total_cost += added_cost return total_cost /datum/preferences/proc/init_keybindings(overrides, raw) if(raw) try overrides = json_decode(raw) catch overrides = list() keybindings = list() keybindings_overrides = overrides for(var/kb in GLOB.keybindings) var/datum/keybinding/KB = kb var/list/keys = (overrides && overrides[KB.name]) || KB.keys for(var/key in keys) LAZYADD(keybindings[key], kb) parent?.update_active_keybindings() return keybindings /datum/preferences/proc/capture_keybinding(mob/user, datum/keybinding/KB, old) var/HTML = {"
Keybinding: [KB.name]

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)