Merge branch 'master' into language-tab

This commit is contained in:
Timothy Teakettle
2021-03-01 20:49:16 +00:00
1890 changed files with 88328 additions and 35824 deletions
+13 -3
View File
@@ -31,6 +31,7 @@
var/datum/preferences/prefs = null
var/last_turn = 0
var/move_delay = 0
var/last_move = 0
var/area = null
/// Last time we Click()ed. No clicking twice in one tick!
@@ -89,6 +90,8 @@
var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen.
/// Last time they used fix macros
var/last_macro_fix = 0
/// Keys currently held
var/list/keys_held = list()
/// These next two vars are to apply movement for keypresses and releases made while move delayed.
@@ -126,9 +129,8 @@
/// Messages currently seen by this client
var/list/seen_messages
/// datum wrapper for client view
var/datum/view_data/view_size
/// viewsize datum for holding our view size
var/datum/viewData/view_size
/// our current tab
var/stat_tab
@@ -162,6 +164,14 @@
var/parallax_layers_max = 3
var/parallax_animate_timer
/**
* Assoc list with all the active maps - when a screen obj is added to
* a map, it's put in here as well.
*
* Format: list(<mapname> = list(/obj/screen))
*/
var/list/screen_maps = list()
// List of all asset filenames sent to this client by the asset cache, along with their assoicated md5s
var/list/sent_assets = list()
/// List of all completed blocking send jobs awaiting acknowledgement by send_asset
+107 -78
View File
@@ -76,9 +76,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
to_chat(src, "<span class='danger'>Your previous action was ignored because you've done too many in a second</span>")
return
//Logs all hrefs, except chat pings
if(!(href_list["_src_"] == "chat" && href_list["proc"] == "ping" && LAZYLEN(href_list) == 2))
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
// Tgui Topic middleware
if(tgui_Topic(href_list))
if(CONFIG_GET(flag/emergency_tgui_logging))
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
return
//Logs all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
//byond bug ID:2256651
if (asset_cache_job && (asset_cache_job in completed_asset_jobs))
@@ -101,8 +107,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
keyUp(keycode)
return
// Tgui Topic middleware
if(tgui_Topic(href_list))
if(href_list["statpanel_item_target"])
handle_statpanel_click(href_list)
return
// Admin PM
@@ -141,6 +147,12 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
..() //redirect to hsrc.Topic()
/client/proc/handle_statpanel_click(list/href_list)
var/atom/target = locate(href_list["statpanel_item_target"])
if(!target)
return
Click(target, target.loc, null, "[href_list["statpanel_item_shiftclick"]?"shift=1;":null][href_list["statpanel_item_ctrlclick"]?"ctrl=1;":null]&alt=[href_list["statpanel_item_altclick"]?"alt=1;":null]", FALSE, "statpanel")
/client/proc/is_content_unlocked()
if(!prefs.unlock_content)
to_chat(src, "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! <a href=\"https://secure.byond.com/membership\">Click Here to find out more</a>.")
@@ -268,7 +280,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else
prefs = new /datum/preferences(src)
GLOB.preferences_datums[ckey] = prefs
addtimer(CALLBACK(src, .proc/ensure_keys_set), 0) //prevents possible race conditions
addtimer(CALLBACK(src, .proc/ensure_keys_set, prefs), 10) //prevents possible race conditions
prefs.last_ip = address //these are gonna be used for banning
prefs.last_id = computer_id //these are gonna be used for banning
@@ -336,10 +349,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
qdel(src)
return
// if(SSinput.initialized) placed here on tg.
// set_macros()
// update_movement_keys()
// Initialize tgui panel
tgui_panel.initialize()
src << browse(file('html/statbrowser.html'), "window=statbrowser")
@@ -446,7 +455,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them.
to_chat(src, "<span class='warning'>Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.</span>")
//This is down here because of the browse() calls in tooltip/New()
if(!tooltips)
tooltips = new /datum/tooltip(src)
@@ -472,17 +480,12 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (menuitem)
menuitem.Load_checked(src)
// view_size = new(src, getScreenSize(prefs.widescreenpref))
// view_size.resetFormat()
// view_size.setZoomMode()
// fit_viewport()
view_size = new(src, getScreenSize(prefs.widescreenpref))
view_size.resetFormat()
view_size.setZoomMode()
fit_viewport()
Master.UpdateTickRate()
/client/proc/ensure_keys_set()
if(SSinput.initialized)
set_macros()
update_movement_keys(prefs)
//////////////
//DISCONNECT//
//////////////
@@ -497,7 +500,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
GLOB.directory -= ckey
log_access("Logout: [key_name(src)]")
GLOB.ahelp_tickets.ClientLogout(src)
// SSserver_maint.UpdateHubStatus()
SSserver_maint.UpdateHubStatus()
if(credits)
QDEL_LIST(credits)
if(holder)
@@ -564,36 +567,49 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(!query_client_in_db.Execute())
qdel(query_client_in_db)
return
if(!query_client_in_db.NextRow())
if (CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[ckey] && !(ckey in GLOB.bunker_passthrough))
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
to_chat(src, "<span class='notice'>You must first join the Discord to verify your account before joining this server.<br>To do so, read the rules and post a request in the #station-access-requests channel under the \"Main server\" category in the Discord server linked here: <a href='https://discord.gg/E6SQuhz'>https://discord.gg/E6SQuhz</a><br>If you have already done so, wait a few minutes then try again; sometimes the server needs to fully load before you can join.</span>") //CIT CHANGE - makes the panic bunker disconnect message point to the discord
var/list/connectiontopic_a = params2list(connectiontopic)
var/list/panic_addr = CONFIG_GET(string/panic_server_address)
if(panic_addr && !connectiontopic_a["redirect"])
var/panic_name = CONFIG_GET(string/panic_server_name)
to_chat(src, "<span class='notice'>Sending you to [panic_name ? panic_name : panic_addr].</span>")
winset(src, null, "command=.options")
src << link("[panic_addr]?redirect=1")
qdel(query_client_in_db)
qdel(src)
return
if(!query_client_in_db.NextRow()) //new user detected
if(!holder && !GLOB.deadmins[ckey])
if(CONFIG_GET(flag/panic_bunker) && !(ckey in GLOB.bunker_passthrough))
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
to_chat(src, "<span class='notice'>You must first join the Discord to verify your account before joining this server.<br>To do so, read the rules and post a request in the #station-access-requests channel under the \"Main server\" category in the Discord server linked here: <a href='https://discord.gg/E6SQuhz'>https://discord.gg/E6SQuhz</a><br>If you have already done so, wait a few minutes then try again; sometimes the server needs to fully load before you can join.</span>") //CIT CHANGE - makes the panic bunker disconnect message point to the discord
var/list/connectiontopic_a = params2list(connectiontopic)
var/list/panic_addr = CONFIG_GET(string/panic_server_address)
if(panic_addr && !connectiontopic_a["redirect"])
var/panic_name = CONFIG_GET(string/panic_server_name)
to_chat(src, "<span class='notice'>Sending you to [panic_name ? panic_name : panic_addr].</span>")
winset(src, null, "command=.options")
src << link("[panic_addr]?redirect=1")
qdel(query_client_in_db)
qdel(src)
return
new_player = 1
account_join_date = sanitizeSQL(findJoinDate())
var/sql_key = sanitizeSQL(key)
var/datum/DBQuery/query_add_player = SSdbcore.NewQuery("INSERT INTO [format_table_name("player")] (`ckey`, `byond_key`, `firstseen`, `firstseen_round_id`, `lastseen`, `lastseen_round_id`, `ip`, `computerid`, `lastadminrank`, `accountjoindate`) VALUES ('[sql_ckey]', '[sql_key]', Now(), '[GLOB.round_id]', Now(), '[GLOB.round_id]', INET_ATON('[sql_ip]'), '[sql_computerid]', '[sql_admin_rank]', [account_join_date ? "'[account_join_date]'" : "NULL"])")
if(!query_add_player.Execute())
qdel(query_client_in_db)
new_player = 1
account_join_date = sanitizeSQL(findJoinDate())
var/sql_key = sanitizeSQL(key)
var/datum/DBQuery/query_add_player = SSdbcore.NewQuery("INSERT INTO [format_table_name("player")] (`ckey`, `byond_key`, `firstseen`, `firstseen_round_id`, `lastseen`, `lastseen_round_id`, `ip`, `computerid`, `lastadminrank`, `accountjoindate`) VALUES ('[sql_ckey]', '[sql_key]', Now(), '[GLOB.round_id]', Now(), '[GLOB.round_id]', INET_ATON('[sql_ip]'), '[sql_computerid]', '[sql_admin_rank]', [account_join_date ? "'[account_join_date]'" : "NULL"])")
if(!query_add_player.Execute())
qdel(query_client_in_db)
qdel(query_add_player)
return
qdel(query_add_player)
return
qdel(query_add_player)
if(!account_join_date)
account_join_date = "Error"
account_age = -1
else if(ckey in GLOB.bunker_passthrough)
GLOB.bunker_passthrough -= ckey
if(!account_join_date)
account_join_date = "Error"
account_age = -1
else if(ckey in GLOB.bunker_passthrough)
GLOB.bunker_passthrough -= ckey
if(CONFIG_GET(flag/age_verification)) //setup age verification
if(!set_db_player_flags())
message_admins(usr, "<span class='danger'>ERROR: Unable to read player flags from database. Please check logs.</span>")
return
else
var/dbflags = prefs.db_flags
if(!(dbflags & DB_FLAG_AGE_CONFIRMATION_COMPLETE)) //they have not completed age verification
if((ckey in GLOB.bunker_passthrough)) //they're verified in the panic bunker though
update_flag_db(DB_FLAG_AGE_CONFIRMATION_COMPLETE, TRUE)
else
update_flag_db(DB_FLAG_AGE_CONFIRMATION_INCOMPLETE, TRUE)
qdel(query_client_in_db)
var/datum/DBQuery/query_get_client_age = SSdbcore.NewQuery("SELECT firstseen, DATEDIFF(Now(),firstseen), accountjoindate, DATEDIFF(Now(),accountjoindate) FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
if(!query_get_client_age.Execute())
@@ -630,7 +646,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
query_log_connection.Execute()
qdel(query_log_connection)
// SSserver_maint.UpdateHubStatus()
SSserver_maint.UpdateHubStatus()
if(new_player)
player_age = -1
@@ -794,7 +810,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
message_admins("<span class='adminnotice'>Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.</span>")
ip_intel = res.intel
/client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE)
/client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE, extra_info)
if(last_click > world.time - world.tick_lag)
return
last_click = world.time
@@ -847,7 +863,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return
if(prefs.log_clicks)
log_click(object, location, control, params, src)
log_click(object, location, control, params, src, extra_info? "clicked ([extra_info])" : null)
if (prefs.hotkeys)
// If hotkey mode is enabled, then clicking the map will automatically
@@ -914,46 +930,37 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return FALSE
if (NAMEOF(src, key))
return FALSE
if(NAMEOF(src, view))
change_view(var_value)
if (NAMEOF(src, view))
view_size.setDefault(var_value)
return TRUE
if(NAMEOF(src, computer_id))
return FALSE
if(NAMEOF(src, address))
return FALSE
. = ..()
/client/vv_get_var(var_name)
. = ..()
switch(var_name)
if(NAMEOF(src, computer_id))
if(!check_rights(R_SENSITIVE, FALSE))
return "SENSITIVE"
if(NAMEOF(src, address))
if(!check_rights(R_SENSITIVE, FALSE))
return "SENSITIVE"
/client/proc/rescale_view(change, min, max)
var/viewscale = getviewsize(view)
var/x = viewscale[1]
var/y = viewscale[2]
x = clamp(x+change, min, max)
y = clamp(y+change, min,max)
change_view("[x]x[y]")
/client/proc/update_movement_keys(datum/preferences/direct_prefs)
var/datum/preferences/D = prefs || direct_prefs
if(!D?.key_bindings)
return
movement_keys = list()
for(var/key in D.key_bindings)
for(var/kb_name in D.key_bindings[key])
switch(kb_name)
if("North")
movement_keys[key] = NORTH
if("East")
movement_keys[key] = EAST
if("West")
movement_keys[key] = WEST
if("South")
movement_keys[key] = SOUTH
view_size.setDefault("[x]x[y]")
/client/proc/change_view(new_size)
if (isnull(new_size))
CRASH("change_view called without argument.")
//CIT CHANGES START HERE - makes change_view change DEFAULT_VIEW to 15x15 depending on preferences
if(prefs && CONFIG_GET(string/default_view))
if(!prefs.widescreenpref && new_size == CONFIG_GET(string/default_view))
new_size = "15x15"
//END OF CIT CHANGES
var/list/old_view = getviewsize(view)
view = new_size
var/list/actualview = getviewsize(view)
@@ -1021,3 +1028,25 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
verb_tabs |= verb_to_init.category
verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name)
src << output("[url_encode(json_encode(verb_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
//increment progress for an unlockable loadout item
/client/proc/increment_progress(key, amount)
if(prefs)
var/savefile/S = new /savefile(prefs.path)
var/list/unlockable_loadout_data = prefs.unlockable_loadout_data
if(!length(unlockable_loadout_data))
unlockable_loadout_data = list()
unlockable_loadout_data[key] = amount
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(unlockable_loadout_data))
prefs.unlockable_loadout_data = unlockable_loadout_data
return TRUE
else
if(unlockable_loadout_data[key])
unlockable_loadout_data[key] += amount
else
unlockable_loadout_data[key] = amount
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(unlockable_loadout_data))
prefs.unlockable_loadout_data = unlockable_loadout_data
return TRUE
return FALSE
+1 -1
View File
@@ -7,4 +7,4 @@ GLOBAL_LIST_EMPTY(clientmessages)
var/list/L = GLOB.clientmessages[ckey]
if(!L)
GLOB.clientmessages[ckey] = L = list()
L += message
L += message
+1 -1
View File
@@ -3,4 +3,4 @@
var/list/logging = list()
var/list/post_login_callbacks = list()
var/list/post_logout_callbacks = list()
var/byond_version = "Unknown"
var/byond_version = "Unknown"
+417 -142
View File
@@ -10,13 +10,31 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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 = 16
var/max_save_slots = 24
//non-preference stuff
var/muted = 0
// 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
@@ -53,7 +71,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/// 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
@@ -82,6 +99,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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/language = "Random" //bonus language
var/choselanguage = "Random" //language appearance
var/underwear = "Nude" //underwear type
var/undie_color = "FFFFFF"
var/undershirt = "Nude" //undershirt type
@@ -98,72 +117,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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",
"body_markings" = "None",
"legs" = "Plantigrade",
"insect_wings" = "Plain",
"insect_fluff" = "None",
"insect_markings" = "None",
"mam_body_markings" = "Plain",
"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,
"balls_visibility" = GEN_VISIBLE_NO_UNDIES,
"breasts_visibility"= GEN_VISIBLE_NO_UNDIES,
"cock_visibility" = GEN_VISIBLE_NO_UNDIES,
"vag_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
)
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", "body_markings" = "None", "legs" = "Plantigrade", "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", "arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain", "mam_body_markings" = "Plain", "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, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_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
@@ -201,7 +159,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/parallax
var/ambientocclusion = TRUE
var/auto_fit_viewport = 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
///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
@@ -234,14 +200,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
///loadout stuff
var/gear_points = 10
var/list/gear_categories
var/list/chosen_gear = list()
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/arousable = TRUE
var/widescreenpref = TRUE
var/autostand = TRUE
var/auto_ooc = FALSE
@@ -254,6 +221,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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
@@ -266,7 +236,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
load_path(C.ckey)
unlock_content = C.IsByondMember()
if(unlock_content)
max_save_slots = 24
max_save_slots = 32
var/loaded_preferences_successfully = load_preferences()
if(loaded_preferences_successfully)
if(load_character())
@@ -274,7 +244,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
//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?.update_movement_keys(src)
C?.ensure_keys_set(src)
real_name = pref_species.random_name(gender,1)
if(!loaded_preferences_successfully)
save_preferences()
@@ -288,7 +258,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/datum/preferences/proc/ShowChoices(mob/user)
if(!user || !user.client)
return
update_preview_icon(current_tab != 2)
update_preview_icon(current_tab)
var/list/dat = list("<center>")
dat += "<a href='?_src_=prefs;preference=tab;tab=0' [current_tab == 0 ? "class='linkOn'" : ""]>Character Settings</a>"
@@ -345,6 +315,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Gender:</b> <a href='?_src_=prefs;preference=gender;task=input'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
dat += "<b>Age:</b> <a style='display:block;width:30px' href='?_src_=prefs;preference=age;task=input'>[age]</a><BR>"
dat += "<b>Language:</b> <a href='?_src_=prefs;preference=language;task=input'>[choselanguage]</a><BR>"
dat += "<b>Special Names:</b><BR>"
var/old_group
@@ -461,6 +432,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(!use_skintones)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Advanced Coloring</h3>"
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=color_scheme;task=input'>[(features["color_scheme"] == ADVANCED_CHARACTER_COLORING) ? "Enabled" : "Disabled"]</a>"
dat += "<h2>Body Colors</h2>"
dat += "<b>Primary Color:</b><BR>"
@@ -473,28 +447,30 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<span style='border: 1px solid #161616; background-color: #[features["mcolor3"]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=mutant_color3;task=input'>Change</a><BR>"
mutant_colors = TRUE
if (CONFIG_GET(number/body_size_min) != CONFIG_GET(number/body_size_max))
dat += "<b>Sprite Size:</b> <a href='?_src_=prefs;preference=body_size;task=input'>[features["body_size"]*100]%</a><br>"
if((EYECOLOR in pref_species.species_traits) && !(NOEYES 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 += "<h3>Heterochromia</h3>"
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=toggle_split_eyes;task=input'>[split_eye_colors ? "Enabled" : "Disabled"]</a>"
if(!split_eye_colors)
dat += "<h3>Eye Color</h3>"
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=eyes;task=input'>Change</a>"
if(!(NOEYES in pref_species.species_traits))
dat += "<h3>Eye Type</h3>"
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=eye_type;task=input'>[eye_type]</a><BR>"
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 += "<h3>Heterochromia</h3>"
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=toggle_split_eyes;task=input'>[split_eye_colors ? "Enabled" : "Disabled"]</a>"
if(!split_eye_colors)
dat += "<h3>Eye Color</h3>"
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=eyes;task=input'>Change</a>"
dat += "</td>"
else
dat += "<h3>Left Eye Color</h3>"
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=eye_left;task=input'>Change</a>"
dat += "<h3>Right Eye Color</h3>"
dat += "<span style='border: 1px solid #161616; background-color: #[right_eye_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=eye_right;task=input'>Change</a><BR>"
dat += "</td>"
else if(use_skintones || mutant_colors)
dat += "</td>"
else
dat += "<h3>Left Eye Color</h3>"
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=eye_left;task=input'>Change</a>"
dat += "<h3>Right Eye Color</h3>"
dat += "<span style='border: 1px solid #161616; background-color: #[right_eye_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=eye_right;task=input'>Change</a><BR>"
dat += "</td>"
else if(use_skintones || mutant_colors)
dat += "</td>"
if(HAIR in pref_species.species_traits)
@@ -533,6 +509,52 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/color_type = GLOB.colored_mutant_parts[mutant_part] //if it can be coloured, show the appropriate button
if(color_type)
dat += "<span style='border:1px solid #161616; background-color: #[features[color_type]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=[color_type];task=input'>Change</a><BR>"
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
if(istype(accessory, /datum/sprite_accessory/mam_body_markings) || istype(accessory, /datum/sprite_accessory/body_markings))
continue
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 += "<b>Primary Color</b><BR>"
dat += "<span style='border:1px solid #161616; background-color: #[features[primary_feature]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=[primary_feature];task=input'>Change</a><BR>"
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 += "<b>Secondary Color</b><BR>"
dat += "<span style='border:1px solid #161616; background-color: #[features[secondary_feature]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=[secondary_feature];task=input'>Change</a><BR>"
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 += "<b>Tertiary Color</b><BR>"
dat += "<span style='border:1px solid #161616; background-color: #[features[tertiary_feature]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=[tertiary_feature];task=input'>Change</a><BR>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
@@ -775,6 +797,20 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>HUD Button Flashes:</b> <a href='?_src_=prefs;preference=hud_toggle_flash'>[hud_toggle_flash ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>HUD Button Flash Color:</b> <span style='border: 1px solid #161616; background-color: [hud_toggle_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=hud_toggle_color;task=input'>Change</a><br>"
/* CITADEL EDIT - We're using top menu instead
button_name = pixel_size
dat += "<b>Pixel Scaling:</b> <a href='?_src_=prefs;preference=pixel_size'>[(button_name) ? "Pixel Perfect [button_name]x" : "Stretch to fit"]</a><br>"
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 += "<b>Scaling Method:</b> <a href='?_src_=prefs;preference=scaling_method'>[button_name]</a><br>"
*/
if (CONFIG_GET(flag/maprotation) && CONFIG_GET(flag/tgstyle_maprotation))
var/p_map = preferred_map
if (!p_map)
@@ -821,6 +857,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<br>"
if(4)
//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 += "<table align='center' width='100%'>"
dat += "<tr><td colspan=4><center><b><font color='[gear_points == 0 ? "#E62100" : "#CCDDFF"]'>[gear_points]</font> loadout points remaining.</b> \[<a href='?_src_=prefs;preference=gear;clear_loadout=1'>Clear Loadout</a>\]</center></td></tr>"
dat += "<tr><td colspan=4><center>You can only choose one item per category, unless it's an item that spawns in your backpack or hands.</center></td></tr>"
@@ -876,15 +925,33 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(donoritem && !gear.donator_ckey_check(user.ckey))
continue
var/class_link = ""
if(gear.type in chosen_gear)
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'"
else if(gear_points <= 0)
if(gear.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC)
extra_loadout_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_color_polychromic=1;loadout_gear_name=[html_encode(gear.name)];'>Color</a>"
for(var/loadout_color in loadout_item[LOADOUT_COLOR])
extra_loadout_data += "<span style='border: 1px solid #161616; background-color: [loadout_color];'>&nbsp;&nbsp;&nbsp;</span>"
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 += "<BR><a href='?_src_=prefs;preference=gear;loadout_color=1;loadout_gear_name=[html_encode(gear.name)];'>Color</a>"
extra_loadout_data += "<span style='border: 1px solid #161616; background-color: [loadout_color_non_poly];'>&nbsp;&nbsp;&nbsp;</span>"
if(gear.loadout_flags & LOADOUT_CAN_NAME)
extra_loadout_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_rename=1;loadout_gear_name=[html_encode(gear.name)];'>Name</a> [loadout_item[LOADOUT_CUSTOM_NAME] ? loadout_item[LOADOUT_CUSTOM_NAME] : "N/A"]"
if(gear.loadout_flags & LOADOUT_CAN_DESCRIPTION)
extra_loadout_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_redescribe=1;loadout_gear_name=[html_encode(gear.name)];'>Description</a>"
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
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'"
dat += "<tr style='vertical-align:top;'><td width=15%><a [class_link]>[name]</a></td>"
else
class_link = "style='white-space:normal;background:#eb2e2e;' class='linkOff'"
dat += "<tr style='vertical-align:top;'><td width=15%><a [class_link]>[name]</a>[extra_loadout_data]</td>"
dat += "<td width = 5% style='vertical-align:top'>[gear.cost]</td><td>"
if(islist(gear.restricted_roles))
if(gear.restricted_roles.len)
@@ -896,7 +963,17 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<font size=2>"
dat += gear.restricted_roles.Join(";")
dat += "</font>"
dat += "</td><td><font size=2><i>[gear.description]</i></font></td></tr>"
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 += "</td><td><font size=2><i>[loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description]</i></font></td></tr>"
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 += "</td><td><font size=2><i>[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]</i></font></td></tr>"
dat += "</table>"
if(5) // Content preferences
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
@@ -917,6 +994,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Breast Enlargement:</b> <a href='?_src_=prefs;preference=breast_enlargement'>[(cit_toggles & BREAST_ENLARGEMENT) ? "Allowed" : "Disallowed"]</a><br>"
dat += "<b>Penis Enlargement:</b> <a href='?_src_=prefs;preference=penis_enlargement'>[(cit_toggles & PENIS_ENLARGEMENT) ? "Allowed" : "Disallowed"]</a><br>"
dat += "<b>Hypno:</b> <a href='?_src_=prefs;preference=never_hypno'>[(cit_toggles & NEVER_HYPNO) ? "Disallowed" : "Allowed"]</a><br>"
dat += "<b>Aphrodisiacs:</b> <a href='?_src_=prefs;preference=aphro'>[(cit_toggles & NO_APHRO) ? "Disallowed" : "Allowed"]</a><br>"
dat += "<b>Ass Slapping:</b> <a href='?_src_=prefs;preference=ass_slap'>[(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"]</a><br>"
dat += "<b>Automatic Wagging:</b> <a href='?_src_=prefs;preference=auto_wag'>[(cit_toggles & NO_AUTO_WAG) ? "Disabled" : "Enabled"]</a><br>"
dat += "</tr></table>"
@@ -966,7 +1044,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/list/default_keys = hotkeys ? kb.hotkey_keys : kb.classic_keys
if(LAZYLEN(default_keys))
dat += "| Default: [default_keys.Join(", ")]"
dat += "</span><span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
dat += "</span>"
if(!kb.special && !kb.clientside)
dat += "<span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
dat += "<br>"
else
var/bound_key = user_binds[kb.name][1]
@@ -979,7 +1059,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/list/default_keys = hotkeys ? kb.classic_keys : kb.hotkey_keys
if(LAZYLEN(default_keys))
dat += "| Default: [default_keys.Join(", ")]"
dat += "</span><span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
dat += "</span>"
if(!kb.special && !kb.clientside)
dat += "<span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
dat += "<br>"
dat += "<br><br>"
@@ -1005,7 +1087,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
#undef APPEARANCE_CATEGORY_COLUMN
#undef MAX_MUTANT_ROWS
/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, old_key, independent = FALSE)
/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, old_key, independent = FALSE, special = FALSE)
var/HTML = {"
<div id='focus' style="outline: 0;" tabindex=0>Keybinding: [kb.full_name]<br>[kb.description]<br><br><b>Press any key to change<br>Press ESC to clear</b></div>
<script>
@@ -1017,7 +1099,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var shift = e.shiftKey ? 1 : 0;
var numpad = (95 < e.keyCode && e.keyCode < 112) ? 1 : 0;
var escPressed = e.keyCode == 27 ? 1 : 0;
var url = 'byond://?_src_=prefs;preference=keybindings_set;keybinding=[kb.name];old_key=[old_key];[independent?"independent=1":""];clear_key='+escPressed+';key='+e.key+';alt='+alt+';ctrl='+ctrl+';shift='+shift+';numpad='+numpad+';key_code='+e.keyCode;
var url = 'byond://?_src_=prefs;preference=keybindings_set;keybinding=[kb.name];old_key=[old_key];[independent?"independent=1;":""][special?"special=1;":""]clear_key='+escPressed+';key='+e.key+';alt='+alt+';ctrl='+ctrl+';shift='+shift+';numpad='+numpad+';key_code='+e.keyCode;
window.location=url;
deedDone = true;
}
@@ -1549,10 +1631,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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/modification in modified_limbs)
if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
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 && !(limb_type in modified_limbs && modified_limbs[limb_type][1] == LOADOUT_LIMB_PROSTHETIC))
if(number_of_prosthetics > MAXIMUM_LOADOUT_PROSTHETICS)
to_chat(user, "<span class='danger'>You can only have up to two prosthetic limbs!</span>")
else
//save the actual prosthetic data
@@ -1609,6 +1691,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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
@@ -1642,6 +1729,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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)
@@ -1707,6 +1797,24 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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
@@ -1872,6 +1980,24 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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))
@@ -1911,6 +2037,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["xenotail"] = "None"
features["tail_human"] = "None"
features["tail_lizard"] = "None"
features["arachnid_spinneret"] = "None"
if("ears")
var/list/snowflake_ears_list = list()
@@ -1986,6 +2113,30 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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","body_markings_primary","body_markings_secondary","body_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,"<span class='danger'>Invalid color. Your color is not bright enough.</span>")
//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
@@ -2196,22 +2347,33 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["body_model"] = chosengender
gender = chosengender
if("language")
choselanguage = input(user, "Select a language.", "Language", language) as null|anything in list("Beachtongue","Draconic","Dwarven",
"Chimpanzee","Space Sign Language","Random")
if(!choselanguage)
return
switch(choselanguage)
if("Rachidian")
language = /datum/language/arachnid
if("Beachtongue")
language = /datum/language/beachbum
if("Draconic")
language = /datum/language/draconic
if("Dwarven")
language = /datum/language/dwarf
if("Chimpanzee")
language = /datum/language/monkey
if("Space Sign Language")
language = /datum/language/signlanguage
if("Random")
language = pick(list("Rachidian", "Beachtongue","Draconic","Dwarven",
"Chimpanzee","Space Sign Language"))
if("body_size")
var/min = CONFIG_GET(number/body_size_min)
var/max = CONFIG_GET(number/body_size_max)
var/danger = CONFIG_GET(number/threshold_body_size_slowdown)
var/new_body_size = input(user, "Choose your desired sprite size: ([min*100]%-[max*100]%)\nWarning: This may make your character look distorted[danger > min ? "! Additionally, a proportional movement speed penalty will be applied to characters smaller than [danger*100]%." : "!"]", "Character Preference", features["body_size"]*100) as num|null
if (new_body_size)
new_body_size = clamp(new_body_size * 0.01, min, max)
var/dorfy
if((new_body_size + 0.01) < danger) // Adding 0.01 as a dumb fix to prevent the warning message from appearing when exactly at threshold... Not sure why that happens in the first place.
dorfy = alert(user, "You have chosen a size below the slowdown threshold of [danger*100]%. For balancing purposes, the further you go below this percentage, the slower your character will be. Do you wish to keep this size?", "Speed Penalty Alert", "Yes", "Move it to the threshold", "No")
if(dorfy == "Move it to the threshold")
new_body_size = danger
if(!dorfy) //Aborts if this var is somehow empty
return
if(dorfy != "No")
features["body_size"] = new_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)
@@ -2258,7 +2420,32 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["has_womb"] = !features["has_womb"]
if("widescreenpref")
widescreenpref = !widescreenpref
user.client.change_view(CONFIG_GET(string/default_view))
user.client.view_size.setDefault(getScreenSize(widescreenpref))
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")
@@ -2291,11 +2478,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("hotkeys")
hotkeys = !hotkeys
user.client.set_macros()
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"]))
CaptureKeybinding(user, kb, href_list["old_key"], text2num(href_list["independent"]), kb.special || kb.clientside)
return
if("keybindings_set")
@@ -2315,9 +2502,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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
@@ -2352,7 +2542,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
key_bindings -= old_key
key_bindings[full_key] += list(kb_name)
key_bindings[full_key] = sortList(key_bindings[full_key])
user.client.update_movement_keys()
if(href_list["special"]) // special keys need a full reset
user.client.ensure_keys_set(src)
user << browse(null, "window=capturekeypress")
save_preferences()
@@ -2364,7 +2555,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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.update_movement_keys()
user.client.ensure_keys_set(src)
if("chat_on_map")
chat_on_map = !chat_on_map
@@ -2539,8 +2730,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
current_tab = text2num(href_list["tab"])
if(href_list["preference"] == "gear")
if(href_list["clear_loadout"])
chosen_gear = list()
gear_points = CONFIG_GET(number/initial_gear_points)
loadout_data["SAVE_[loadout_slot]"] = list()
save_preferences()
if(href_list["select_category"])
gear_category = html_decode(href_list["select_category"])
@@ -2553,19 +2743,76 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(!G)
return
var/toggle = text2num(href_list["toggle_gear"])
if(!toggle && (G.type in chosen_gear))//toggling off and the item effectively is in chosen gear)
chosen_gear -= G.type
gear_points += initial(G.cost)
else if(toggle && (!(is_type_in_ref_list(G, chosen_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, "<span class='danger'>You cannot take this loadout, as you've already chosen too many of the same category!</span>")
return
if(G.donoritem && !G.donator_ckey_check(user.ckey))
to_chat(user, "<span class='danger'>This is an item intended for donator use only. You are not authorized to use this item.</span>")
return
if(istype(G, /datum/gear/unlockable) && !can_use_unlockable(G))
to_chat(user, "<span class='danger'>To use this item, you need to meet the defined requirements!</span>")
return
if(gear_points >= initial(G.cost))
chosen_gear += G.type
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
@@ -2586,6 +2833,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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
@@ -2628,16 +2881,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
pref_species = new /datum/species/human
save_character()
var/old_size = character.dna.features["body_size"]
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"]]
@@ -2676,7 +2932,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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.status == BODYPART_ROBOTIC && !part.render_like_organic)
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)
@@ -2699,8 +2955,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
new_limb.replace_limb(character)
qdel(old_part)
character.regenerate_icons()
SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks)
//let's be sure the character updates
@@ -2761,23 +3015,27 @@ GLOBAL_LIST_EMPTY(preferences_datums)
cached_holoform_icons[filter_type] = process_holoform_icon_filter(custom_holoform_icon, filter_type)
return cached_holoform_icons[filter_type]
//Used in savefile update 32, can be removed once that is no longer relevant.
/// 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.update_movement_keys()
parent?.ensure_keys_set(src)
/datum/preferences/proc/is_loadout_slot_available(slot)
var/list/L
LAZYINITLIST(L)
for(var/i in chosen_gear)
var/datum/gear/G = i
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)
@@ -2791,6 +3049,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
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
+237 -51
View File
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
#define SAVEFILE_VERSION_MAX 36
#define SAVEFILE_VERSION_MAX 48
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -42,7 +42,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//if your savefile is 3 months out of date, then 'tough shit'.
/datum/preferences/proc/update_preferences(current_version, savefile/S)
if(current_version < 32) //If you remove this, remove force_reset_keybindings() too.
if(current_version < 46) //If you remove this, remove force_reset_keybindings() too.
force_reset_keybindings_direct(TRUE)
addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
/datum/preferences/proc/update_character(current_version, savefile/S)
@@ -204,24 +205,104 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(S["species"] == "lizard")
features["mam_snouts"] = features["snout"]
if(current_version < 36)
if(current_version < 36) //introduction of heterochromia
left_eye_color = S["eye_color"]
right_eye_color = S["eye_color"]
if(current_version < 37) //introduction of chooseable eye types/sprites
if(S["species"] == "insect")
left_eye_color = "#000000"
right_eye_color = "#000000"
if(chosen_limb_id == "moth" || chosen_limb_id == "moth_not_greyscale") //these actually have slightly different eyes!
eye_type = "moth"
else
eye_type = "insect"
if(current_version < 38) //further eye sprite changes
if(S["species"] == "plasmaman")
left_eye_color = "#FFC90E"
right_eye_color = "#FFC90E"
else
if(S["species"] == "skeleton")
left_eye_color = "#BAB99E"
right_eye_color = "#BAB99E"
if(current_version < 43) //extreme changes to how things are coloured (the introduction of the advanced coloring system)
features["color_scheme"] = OLD_CHARACTER_COLORING //disable advanced coloring system by default
for(var/feature in features)
var/feature_value = features[feature]
if(feature_value)
var/ref_list = GLOB.mutant_reference_list[feature]
if(ref_list)
var/datum/sprite_accessory/accessory = ref_list[feature_value]
if(accessory)
var/mutant_string = accessory.mutant_part_string
if(!mutant_string)
if(istype(accessory, /datum/sprite_accessory/mam_body_markings))
mutant_string = "mam_body_markings"
var/primary_string = "[mutant_string]_primary"
var/secondary_string = "[mutant_string]_secondary"
var/tertiary_string = "[mutant_string]_tertiary"
if(accessory.color_src == MATRIXED && !accessory.matrixed_sections && feature_value != "None")
message_admins("Sprite Accessory Failure (migration from [current_version] to 39): Accessory [accessory.type] is a matrixed item without any matrixed sections set!")
continue
var/primary_exists = features[primary_string]
var/secondary_exists = features[secondary_string]
var/tertiary_exists = features[tertiary_string]
if(accessory.color_src == MATRIXED && !primary_exists && !secondary_exists && !tertiary_exists)
features[primary_string] = features["mcolor"]
features[secondary_string] = features["mcolor2"]
features[tertiary_string] = features["mcolor3"]
else if(accessory.color_src == MUTCOLORS && !primary_exists)
features[primary_string] = features["mcolor"]
else if(accessory.color_src == MUTCOLORS2 && !secondary_exists)
features[secondary_string] = features["mcolor2"]
else if(accessory.color_src == MUTCOLORS3 && !tertiary_exists)
features[tertiary_string] = features["mcolor3"]
features["color_scheme"] = OLD_CHARACTER_COLORING //advanced is off by default
if(current_version < 47) //loadout save gets changed to json
var/text_to_load
S["loadout"] >> text_to_load
var/list/saved_loadout_paths = splittext(text_to_load, "|")
//MAXIMUM_LOADOUT_SAVES save slots per loadout now
for(var/i=1, i<= MAXIMUM_LOADOUT_SAVES, i++)
loadout_data["SAVE_[i]"] = list()
for(var/some_gear_item in saved_loadout_paths)
if(!ispath(text2path(some_gear_item)))
log_game("Failed to copy item [some_gear_item] to new loadout system when migrating from version [current_version] to 40, issue: item is not a path")
continue
var/datum/gear/gear_item = text2path(some_gear_item)
if(!(initial(gear_item.loadout_flags) & LOADOUT_CAN_COLOR_POLYCHROMIC))
loadout_data["SAVE_1"] += list(list(LOADOUT_ITEM = some_gear_item)) //for the migration we put their old save into the first save slot, which is loaded by default!
else
//the same but we setup some new polychromic data (you can't get the initial value for a list so we have to do this horrible thing here)
var/datum/gear/temporary_gear_item = new gear_item
loadout_data["SAVE_1"] += list(list(LOADOUT_ITEM = some_gear_item, LOADOUT_COLOR = temporary_gear_item.loadout_initial_colors))
qdel(temporary_gear_item)
//it's double packed into a list because += will union the two lists contents
S["loadout"] = safe_json_encode(loadout_data)
if(current_version < 48) //unlockable loadout items but we need to clear bad data from a mistake
S["unlockable_loadout"] = list()
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
path = "data/player_saves/[ckey[1]]/[ckey]/[filename]"
vr_path = "data/player_saves/[ckey[1]]/[ckey]/vore"
/datum/preferences/proc/load_preferences()
/datum/preferences/proc/load_preferences(bypass_cooldown = FALSE)
if(!path)
return FALSE
if(world.time < loadprefcooldown)
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to load your preferences a little too fast. Wait half a second, then try again.</span>")
return FALSE
loadprefcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!bypass_cooldown)
if(world.time < loadprefcooldown)
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to load your preferences a little too fast. Wait half a second, then try again.</span>")
return FALSE
loadprefcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!fexists(path))
return FALSE
@@ -271,6 +352,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["parallax"] >> parallax
S["ambientocclusion"] >> ambientocclusion
S["auto_fit_viewport"] >> auto_fit_viewport
S["widescreenpref"] >> widescreenpref
S["pixel_size"] >> pixel_size
S["scaling_method"] >> scaling_method
S["hud_toggle_flash"] >> hud_toggle_flash
S["hud_toggle_color"] >> hud_toggle_color
S["menuoptions"] >> menuoptions
@@ -288,7 +372,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["arousable"] >> arousable
S["screenshake"] >> screenshake
S["damagescreenshake"] >> damagescreenshake
S["widescreenpref"] >> widescreenpref
S["autostand"] >> autostand
S["cit_toggles"] >> cit_toggles
S["preferred_chaos"] >> preferred_chaos
@@ -304,8 +387,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
update_preferences(needs_update, S) //needs_update = savefile_version if we need an update (positive integer)
//Sanitize
ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor)))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
@@ -324,6 +405,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null)
ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport))
widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref))
pixel_size = sanitize_integer(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, initial(pixel_size))
scaling_method = sanitize_text(scaling_method, initial(scaling_method))
hud_toggle_flash = sanitize_integer(hud_toggle_flash, 0, 1, initial(hud_toggle_flash))
hud_toggle_color = sanitize_hexcolor(hud_toggle_color, 6, 1, initial(hud_toggle_color))
ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form))
@@ -337,7 +421,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
pda_skin = sanitize_inlist(pda_skin, GLOB.pda_reskins, PDA_SKIN_ALT)
screenshake = sanitize_integer(screenshake, 0, 800, initial(screenshake))
damagescreenshake = sanitize_integer(damagescreenshake, 0, 2, initial(damagescreenshake))
widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref))
autostand = sanitize_integer(autostand, 0, 1, initial(autostand))
cit_toggles = sanitize_integer(cit_toggles, 0, 16777215, initial(cit_toggles))
auto_ooc = sanitize_integer(auto_ooc, 0, 1, initial(auto_ooc))
@@ -347,6 +430,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped
if(S["unlockable_loadout"])
unlockable_loadout_data = safe_json_decode(S["unlockable_loadout"])
else
unlockable_loadout_data = list()
if(needs_update >= 0) //save the updated version
var/old_default_slot = default_slot
var/old_max_save_slots = max_save_slots
@@ -359,11 +447,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
continue
max_save_slots = max(max_save_slots, slotnum) //so we can still update byond member slots after they lose memeber status
default_slot = slotnum
if (load_character()) // this updtates char slots
save_character()
if (load_character(null, TRUE)) // this updtates char slots
save_character(TRUE)
default_slot = old_default_slot
max_save_slots = old_max_save_slots
save_preferences()
save_preferences(TRUE)
return TRUE
@@ -385,14 +473,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(!GLOB.keybindings_by_name[bindname])
modless_key_bindings -= key
/datum/preferences/proc/save_preferences()
/datum/preferences/proc/save_preferences(bypass_cooldown = FALSE)
if(!path)
return 0
if(world.time < saveprefcooldown)
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to save your preferences a little too fast. Wait half a second, then try again.</span>")
return 0
saveprefcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!bypass_cooldown)
if(world.time < saveprefcooldown)
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to save your preferences a little too fast. Wait half a second, then try again.</span>")
return 0
saveprefcooldown = world.time + PREF_SAVELOAD_COOLDOWN
var/savefile/S = new /savefile(path)
if(!S)
return 0
@@ -451,21 +540,29 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["auto_ooc"], auto_ooc)
WRITE_FILE(S["no_tetris_storage"], no_tetris_storage)
if(length(unlockable_loadout_data))
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(unlockable_loadout_data))
else
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(list()))
return 1
/datum/preferences/proc/load_character(slot)
/datum/preferences/proc/load_character(slot, bypass_cooldown = FALSE)
if(!path)
return FALSE
if(world.time < loadcharcooldown) //This is before the check to see if the filepath exists to ensure that BYOND can't get hung up on read attempts when the hard drive is a little slow
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to load your character a little too fast. Wait half a second, then try again.</span>")
return "SLOW THE FUCK DOWN" //the reason this isn't null is to make sure that people don't have their character slots overridden by random chars if they accidentally double-click a slot
loadcharcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!bypass_cooldown)
if(world.time < loadcharcooldown) //This is before the check to see if the filepath exists to ensure that BYOND can't get hung up on read attempts when the hard drive is a little slow
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to load your character a little too fast. Wait half a second, then try again.</span>")
return "SLOW THE FUCK DOWN" //the reason this isn't null is to make sure that people don't have their character slots overridden by random chars if they accidentally double-click a slot
loadcharcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!fexists(path))
return FALSE
var/savefile/S = new /savefile(path)
if(!S)
return FALSE
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", "body_markings" = "None", "legs" = "Plantigrade", "insect_wings" = "Plain", "insect_fluff" = "None", "insect_markings" = "None", "arachnid_legs" = "Plain", "arachnid_spinneret" = "Plain", "arachnid_mandibles" = "Plain", "mam_body_markings" = "Plain", "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, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_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)
S.cd = "/"
if(!slot)
slot = default_slot
@@ -507,8 +604,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["body_model"] >> features["body_model"]
S["body_size"] >> features["body_size"]
S["age"] >> age
S["language"] >> language
S["choselanguage"] >> choselanguage
S["hair_color"] >> hair_color
S["facial_hair_color"] >> facial_hair_color
S["eye_type"] >> eye_type
S["left_eye_color"] >> left_eye_color
S["right_eye_color"] >> right_eye_color
S["use_custom_skin_tone"] >> use_custom_skin_tone
@@ -541,8 +641,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["feature_insect_wings"] >> features["insect_wings"]
S["feature_insect_fluff"] >> features["insect_fluff"]
S["feature_insect_markings"] >> features["insect_markings"]
S["feature_arachnid_legs"] >> features["arachnid_legs"]
S["feature_arachnid_spinneret"] >> features["arachnid_spinneret"]
S["feature_arachnid_mandibles"] >> features["arachnid_mandibles"]
S["feature_horns_color"] >> features["horns_color"]
S["feature_wings_color"] >> features["wings_color"]
S["feature_color_scheme"] >> features["color_scheme"]
S["persistent_scars"] >> persistent_scars
S["scars1"] >> scars_list["1"]
S["scars2"] >> scars_list["2"]
@@ -555,6 +659,21 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
modified_limbs = safe_json_decode(limbmodstr)
else
modified_limbs = list()
var/tcgcardstr
S["tcg_cards"] >> tcgcardstr
if(length(tcgcardstr))
tcg_cards = safe_json_decode(tcgcardstr)
else
tcg_cards = list()
var/tcgdeckstr
S["tcg_decks"] >> tcgdeckstr
if(length(tcgdeckstr))
tcg_decks = safe_json_decode(tcgdeckstr)
else
tcg_decks = list()
S["chosen_limb_id"] >> chosen_limb_id
S["hide_ckey"] >> hide_ckey //saved per-character
@@ -568,6 +687,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//Jobs
S["joblessrole"] >> joblessrole
//Load prefs
S["job_preferences"] >> job_preferences
@@ -620,6 +740,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["feature_vag_visibility"] >> features["vag_visibility"]
//womb features
S["feature_has_womb"] >> features["has_womb"]
//flavor text
//Let's make our players NOT cry desperately as we wipe their savefiles of their special snowflake texts:
if((S["flavor_text"] != "") && (S["flavor_text"] != null) && S["flavor_text"]) //If old text isn't null and isn't "" but still exists.
@@ -644,20 +765,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
var/list/json_from_file = json_decode(file2text(char_vr_path))
if(json_from_file)
belly_prefs = json_from_file["belly_prefs"]
//gear loadout
var/text_to_load
S["loadout"] >> text_to_load
var/list/saved_loadout_paths = splittext(text_to_load, "|")
chosen_gear = list()
gear_points = CONFIG_GET(number/initial_gear_points)
for(var/i in saved_loadout_paths)
var/datum/gear/path = text2path(i)
if(path)
var/init_cost = initial(path.cost)
if(init_cost > gear_points)
continue
chosen_gear += path
gear_points -= init_cost
if(S["loadout"])
loadout_data = safe_json_decode(S["loadout"])
else
loadout_data = list()
//try to fix any outdated data if necessary
//preference updating will handle saving the updated data for us.
@@ -693,6 +806,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE)
left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
@@ -724,6 +838,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["insect_fluff"] = sanitize_inlist(features["insect_fluff"], GLOB.insect_fluffs_list)
features["insect_markings"] = sanitize_inlist(features["insect_markings"], GLOB.insect_markings_list, "None")
features["insect_wings"] = sanitize_inlist(features["insect_wings"], GLOB.insect_wings_list)
features["arachnid_legs"] = sanitize_inlist(features["arachnid_legs"], GLOB.arachnid_legs_list, "Plain")
features["arachnid_spinneret"] = sanitize_inlist(features["arachnid_spinneret"], GLOB.arachnid_spinneret_list, "Plain")
features["arachnid_mandibles"] = sanitize_inlist(features["arachnid_mandibles"], GLOB.arachnid_mandibles_list, "Plain")
var/static/size_min
if(!size_min)
@@ -774,6 +891,32 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["silicon_flavor_text"] = copytext(features["silicon_flavor_text"], 1, MAX_FLAVOR_LEN)
features["ooc_notes"] = copytext(features["ooc_notes"], 1, MAX_FLAVOR_LEN)
//load every advanced coloring mode thing in one go
//THIS MUST BE DONE AFTER ALL FEATURE SAVES OR IT WILL NOT WORK
for(var/feature in features)
var/feature_value = features[feature]
if(feature_value)
var/ref_list = GLOB.mutant_reference_list[feature]
if(ref_list)
var/datum/sprite_accessory/accessory = ref_list[feature_value]
if(accessory)
var/mutant_string = accessory.mutant_part_string
if(!mutant_string)
if(istype(accessory, /datum/sprite_accessory/mam_body_markings))
mutant_string = "mam_body_markings"
var/primary_string = "[mutant_string]_primary"
var/secondary_string = "[mutant_string]_secondary"
var/tertiary_string = "[mutant_string]_tertiary"
if(accessory.color_src == MATRIXED && !accessory.matrixed_sections && feature_value != "None")
message_admins("Sprite Accessory Failure (loading data): Accessory [accessory.type] is a matrixed item without any matrixed sections set!")
continue
if(S["feature_[primary_string]"])
S["feature_[primary_string]"] >> features[primary_string]
if(S["feature_[secondary_string]"])
S["feature_[secondary_string]"] >> features[secondary_string]
if(S["feature_[tertiary_string]"])
S["feature_[tertiary_string]"] >> features[tertiary_string]
persistent_scars = sanitize_integer(persistent_scars)
scars_list["1"] = sanitize_text(scars_list["1"])
scars_list["2"] = sanitize_text(scars_list["2"])
@@ -797,14 +940,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return 1
/datum/preferences/proc/save_character()
/datum/preferences/proc/save_character(bypass_cooldown = FALSE)
if(!path)
return 0
if(world.time < savecharcooldown)
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to save your character a little too fast. Wait half a second, then try again.</span>")
return 0
savecharcooldown = world.time + PREF_SAVELOAD_COOLDOWN
if(!bypass_cooldown)
if(world.time < savecharcooldown)
if(istype(parent))
to_chat(parent, "<span class='warning'>You're attempting to save your character a little too fast. Wait half a second, then try again.</span>")
return 0
savecharcooldown = world.time + PREF_SAVELOAD_COOLDOWN
var/savefile/S = new /savefile(path)
if(!S)
return 0
@@ -822,8 +966,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["body_model"] , features["body_model"])
WRITE_FILE(S["body_size"] , features["body_size"])
WRITE_FILE(S["age"] , age)
WRITE_FILE(S["language"] , language)
WRITE_FILE(S["choselanguage"] , choselanguage)
WRITE_FILE(S["hair_color"] , hair_color)
WRITE_FILE(S["facial_hair_color"] , facial_hair_color)
WRITE_FILE(S["eye_type"] , eye_type)
WRITE_FILE(S["left_eye_color"] , left_eye_color)
WRITE_FILE(S["right_eye_color"] , right_eye_color)
WRITE_FILE(S["use_custom_skin_tone"] , use_custom_skin_tone)
@@ -864,6 +1011,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_insect_wings"] , features["insect_wings"])
WRITE_FILE(S["feature_insect_fluff"] , features["insect_fluff"])
WRITE_FILE(S["feature_insect_markings"] , features["insect_markings"])
WRITE_FILE(S["feature_arachnid_legs"] , features["arachnid_legs"])
WRITE_FILE(S["feature_arachnid_spinneret"] , features["arachnid_spinneret"])
WRITE_FILE(S["feature_arachnid_mandibles"] , features["arachnid_mandibles"])
WRITE_FILE(S["feature_meat"] , features["meat_type"])
WRITE_FILE(S["feature_has_cock"], features["has_cock"])
@@ -894,6 +1044,33 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_ooc_notes"], features["ooc_notes"])
WRITE_FILE(S["feature_color_scheme"], features["color_scheme"])
//save every advanced coloring mode thing in one go
for(var/feature in features)
var/feature_value = features[feature]
if(feature_value)
var/ref_list = GLOB.mutant_reference_list[feature]
if(ref_list)
var/datum/sprite_accessory/accessory = ref_list[feature_value]
if(accessory)
var/mutant_string = accessory.mutant_part_string
if(!mutant_string)
if(istype(accessory, /datum/sprite_accessory/mam_body_markings))
mutant_string = "mam_body_markings"
var/primary_string = "[mutant_string]_primary"
var/secondary_string = "[mutant_string]_secondary"
var/tertiary_string = "[mutant_string]_tertiary"
if(accessory.color_src == MATRIXED && !accessory.matrixed_sections && feature_value != "None")
message_admins("Sprite Accessory Failure (saving data): Accessory [accessory.type] is a matrixed item without any matrixed sections set!")
continue
if(features[primary_string])
WRITE_FILE(S["feature_[primary_string]"], features[primary_string])
if(features[secondary_string])
WRITE_FILE(S["feature_[secondary_string]"], features[secondary_string])
if(features[tertiary_string])
WRITE_FILE(S["feature_[tertiary_string]"], features[tertiary_string])
//Custom names
for(var/custom_name_id in GLOB.preferences_custom_names)
var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
@@ -931,11 +1108,20 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//gear loadout
if(chosen_gear.len)
var/text_to_save = chosen_gear.Join("|")
S["loadout"] << text_to_save
if(length(loadout_data))
S["loadout"] << safe_json_encode(loadout_data)
else
S["loadout"] << "" //empty string to reset the value
S["loadout"] << safe_json_encode(list())
if(length(tcg_cards))
S["tcg_cards"] << safe_json_encode(tcg_cards)
else
S["tcg_cards"] << safe_json_encode(list())
if(length(tcg_decks))
S["tcg_decks"] << safe_json_encode(tcg_decks)
else
S["tcg_decks"] << safe_json_encode(list())
cit_character_pref_save(S)
+5 -5
View File
@@ -386,7 +386,7 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS
//Admin Preferences
/client/proc/toggleadminhelpsound()
set name = "Hear/Silence Adminhelps"
set category = "Preferences"
set category = "Preferences.Admin"
set desc = "Toggle hearing a notification when admin PMs are received"
if(!holder)
return
@@ -397,7 +397,7 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS
/client/proc/toggleannouncelogin()
set name = "Do/Don't Announce Login"
set category = "Preferences"
set category = "Preferences.Admin"
set desc = "Toggle if you want an announcement to admins when you login during a round"
if(!holder)
return
@@ -408,7 +408,7 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS
/client/proc/toggle_hear_radio()
set name = "Show/Hide Radio Chatter"
set category = "Preferences"
set category = "Preferences.Admin"
set desc = "Toggle seeing radiochatter from nearby radios and speakers"
if(!holder)
return
@@ -419,7 +419,7 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS
/client/proc/deadchat()
set name = "Show/Hide Deadchat"
set category = "Preferences"
set category = "Preferences.Admin"
set desc ="Toggles seeing deadchat"
prefs.chat_toggles ^= CHAT_DEAD
prefs.save_preferences()
@@ -428,7 +428,7 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS
/client/proc/toggleprayers()
set name = "Show/Hide Prayers"
set category = "Preferences"
set category = "Preferences.Admin"
set desc = "Toggles seeing prayers"
prefs.chat_toggles ^= CHAT_PRAYER
prefs.save_preferences()
+4 -4
View File
@@ -17,7 +17,7 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC")
if(!msg)
return
if(!(prefs.toggles & CHAT_OOC))
if(!(prefs.chat_toggles & CHAT_OOC))
to_chat(src, "<span class='danger'> You have OOC muted.</span>")
return
if(jobban_isbanned(mob, "OOC"))
@@ -50,7 +50,7 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC")
msg = emoji_parse(msg)
mob.log_talk(msg,LOG_OOC, tag="(LOOC)")
mob.log_talk(msg,LOG_OOC, tag="LOOC")
var/list/heard = get_hearers_in_view(7, get_top_level_mob(src.mob))
for(var/mob/M in heard)
@@ -63,14 +63,14 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC")
if (isobserver(M))
continue //Also handled later.
if(C.prefs.toggles & CHAT_OOC)
if(C.prefs.chat_toggles & CHAT_OOC)
if(GLOB.LOOC_COLOR)
to_chat(C, "<font color='[GLOB.LOOC_COLOR]'><b><span class='prefix'>LOOC:</span> <EM>[src.mob.name]:</EM> <span class='message'>[msg]</span></b></font>")
else
to_chat(C, "<span class='looc'><span class='prefix'>LOOC:</span> <EM>[src.mob.name]:</EM> <span class='message'>[msg]</span></span>")
for(var/client/C in GLOB.admins)
if(C.prefs.toggles & CHAT_OOC)
if(C.prefs.chat_toggles & CHAT_OOC)
var/prefix = "(R)LOOC"
if (C.mob in heard)
prefix = "LOOC"
+4 -4
View File
@@ -111,16 +111,16 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
/client/proc/set_ooc(newColor as color)
set name = "Set Player OOC Color"
set desc = "Modifies player OOC Color"
set category = "Fun"
set category = "Admin.Fun"
GLOB.OOC_COLOR = sanitize_ooccolor(newColor)
/client/proc/reset_ooc()
set name = "Reset Player OOC Color"
set desc = "Returns player OOC Color to default"
set category = "Fun"
set category = "Admin.Fun"
GLOB.OOC_COLOR = null
/client/verb/colorooc()
/client/verb/colorooc() //this is admin and people who bought byond.
set name = "Set Your OOC Color"
set category = "Preferences"
@@ -179,7 +179,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
to_chat(usr, "<span class='notice'>Sorry, that function is not enabled on this server.</span>")
return
browse_messages(null, usr.ckey, null, TRUE)
browse_messages(null, usr.ckey, null, TRUE, override = TRUE)
/client/proc/self_playtime()
set name = "View tracked playtime"
+1 -1
View File
@@ -87,4 +87,4 @@
msg += "<span class='info'>Adminhelps are also sent to Discord. If no admins are available in game adminhelp anyways and an admin on Discord will see it and respond.</span>"
to_chat(src, msg)
*/
*/