mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
1306 lines
56 KiB
Plaintext
1306 lines
56 KiB
Plaintext
//This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped.
|
|
#define SAVEFILE_VERSION_MIN 18
|
|
|
|
//This is the current version, anything below this will attempt to update (if it's not obsolete)
|
|
// 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 58
|
|
|
|
/*
|
|
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
|
This proc checks if the current directory of the savefile S needs updating
|
|
It is to be used by the load_character and load_preferences procs.
|
|
(S.cd=="/" is preferences, S.cd=="/character[integer]" is a character slot, etc)
|
|
|
|
if the current directory's version is below SAVEFILE_VERSION_MIN it will simply wipe everything in that directory
|
|
(if we're at root "/" then it'll just wipe the entire savefile, for instance.)
|
|
|
|
if its version is below SAVEFILE_VERSION_MAX but above the minimum, it will load data but later call the
|
|
respective update_preferences() or update_character() proc.
|
|
Those procs allow coders to specify format changes so users do not lose their setups and have to redo them again.
|
|
|
|
Failing all that, the standard sanity checks are performed. They simply check the data is suitable, reverting to
|
|
initial() values if necessary.
|
|
*/
|
|
/datum/preferences/proc/savefile_needs_update(savefile/S)
|
|
var/savefile_version
|
|
S["version"] >> savefile_version
|
|
|
|
if(savefile_version < SAVEFILE_VERSION_MIN)
|
|
S.dir.Cut()
|
|
return -2
|
|
if(savefile_version < SAVEFILE_VERSION_MAX)
|
|
return savefile_version
|
|
return -1
|
|
|
|
//should these procs get fairly long
|
|
//just increase SAVEFILE_VERSION_MIN so it's not as far behind
|
|
//SAVEFILE_VERSION_MAX and then delete any obsolete if clauses
|
|
//from these procs.
|
|
//This only really meant to avoid annoying frequent players
|
|
//if your savefile is 3 months out of date, then 'tough shit'.
|
|
|
|
/datum/preferences/proc/update_preferences(current_version, savefile/S)
|
|
if(current_version < 30)
|
|
outline_enabled = TRUE
|
|
outline_color = COLOR_THEME_MIDNIGHT
|
|
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.
|
|
if(current_version < 55) //Bitflag toggles don't set their defaults when they're added, always defaulting to off instead.
|
|
toggles |= SOUND_BARK
|
|
if(current_version < 56)
|
|
if("NO_ANTAGS" in be_special)
|
|
toggles |= NO_ANTAG
|
|
be_special -= "NO_ANTAGS"
|
|
for(var/be_special_type in be_special)
|
|
be_special[be_special_type] = 1
|
|
if(current_version < 57)
|
|
if(screentip_pref)
|
|
screentip_pref = SCREENTIP_PREFERENCE_ENABLED
|
|
else
|
|
// Let's give it a little chance okay, change if you don't like still.
|
|
screentip_pref = SCREENTIP_PREFERENCE_CONTEXT_ONLY
|
|
|
|
/datum/preferences/proc/update_character(current_version, savefile/S)
|
|
if(current_version < 19)
|
|
pda_style = "mono"
|
|
if(current_version < 20)
|
|
pda_color = "#808000"
|
|
if((current_version < 21) && features["meat_type"] && (features["meat_type"] == null))
|
|
features["meat_type"] = "Mammalian"
|
|
if(current_version < 22)
|
|
|
|
job_preferences = list() //It loaded null from nonexistant savefile field.
|
|
|
|
var/job_civilian_high = 0
|
|
var/job_civilian_med = 0
|
|
var/job_civilian_low = 0
|
|
|
|
var/job_medsci_high = 0
|
|
var/job_medsci_med = 0
|
|
var/job_medsci_low = 0
|
|
|
|
var/job_engsec_high = 0
|
|
var/job_engsec_med = 0
|
|
var/job_engsec_low = 0
|
|
|
|
S["job_civilian_high"] >> job_civilian_high
|
|
S["job_civilian_med"] >> job_civilian_med
|
|
S["job_civilian_low"] >> job_civilian_low
|
|
S["job_medsci_high"] >> job_medsci_high
|
|
S["job_medsci_med"] >> job_medsci_med
|
|
S["job_medsci_low"] >> job_medsci_low
|
|
S["job_engsec_high"] >> job_engsec_high
|
|
S["job_engsec_med"] >> job_engsec_med
|
|
S["job_engsec_low"] >> job_engsec_low
|
|
|
|
//Can't use SSjob here since this happens right away on login
|
|
for(var/job in subtypesof(/datum/job))
|
|
var/datum/job/J = job
|
|
var/new_value
|
|
var/fval = initial(J.flag)
|
|
switch(initial(J.department_flag))
|
|
if(CIVILIAN)
|
|
if(job_civilian_high & fval)
|
|
new_value = JP_HIGH
|
|
else if(job_civilian_med & fval)
|
|
new_value = JP_MEDIUM
|
|
else if(job_civilian_low & fval)
|
|
new_value = JP_LOW
|
|
if(MEDSCI)
|
|
if(job_medsci_high & fval)
|
|
new_value = JP_HIGH
|
|
else if(job_medsci_med & fval)
|
|
new_value = JP_MEDIUM
|
|
else if(job_medsci_low & fval)
|
|
new_value = JP_LOW
|
|
if(ENGSEC)
|
|
if(job_engsec_high & fval)
|
|
new_value = JP_HIGH
|
|
else if(job_engsec_med & fval)
|
|
new_value = JP_MEDIUM
|
|
else if(job_engsec_low & fval)
|
|
new_value = JP_LOW
|
|
if(new_value)
|
|
job_preferences["[initial(J.title)]"] = new_value
|
|
else if(current_version < 23) // we are fixing a gamebreaking bug.
|
|
job_preferences = list() //It loaded null from nonexistant savefile field.
|
|
|
|
if(current_version < 25)
|
|
var/digi
|
|
S["feature_lizard_legs"] >> digi
|
|
if(digi == "Digitigrade Legs")
|
|
WRITE_FILE(S["feature_lizard_legs"], "Digitigrade")
|
|
|
|
if(current_version < 26)
|
|
var/vr_path = "data/player_saves/[parent.ckey[1]]/[parent.ckey]/vore/character[default_slot].json"
|
|
if(fexists(vr_path))
|
|
var/list/json_from_file = json_decode(file2text(vr_path))
|
|
if(json_from_file)
|
|
if(json_from_file["digestable"])
|
|
vore_flags |= DIGESTABLE
|
|
if(json_from_file["devourable"])
|
|
vore_flags |= DEVOURABLE
|
|
if(json_from_file["feeding"])
|
|
vore_flags |= FEEDING
|
|
if(json_from_file["lickable"])
|
|
vore_flags |= LICKABLE
|
|
belly_prefs = json_from_file["belly_prefs"]
|
|
vore_taste = json_from_file["vore_taste"]
|
|
|
|
for(var/V in all_quirks) // quirk migration
|
|
switch(V)
|
|
if("Acute hepatic pharmacokinesis")
|
|
cit_toggles &= ~(PENIS_ENLARGEMENT)
|
|
cit_toggles &= ~(BREAST_ENLARGEMENT)
|
|
cit_toggles |= FORCED_FEM
|
|
cit_toggles |= FORCED_MASC
|
|
all_quirks -= V
|
|
if("Crocin Immunity")
|
|
cit_toggles |= NO_APHRO
|
|
all_quirks -= V
|
|
if("Buns of Steel")
|
|
cit_toggles |= NO_ASS_SLAP
|
|
all_quirks -= V
|
|
|
|
if(features["meat_type"] == "Inesct")
|
|
features["meat_type"] = "Insect"
|
|
|
|
if(current_version < 27)
|
|
var/tennis
|
|
S["feature_balls_shape"] >> tennis
|
|
if(tennis == "Hidden")
|
|
features["balls_visibility"] = GEN_VISIBLE_NEVER
|
|
|
|
if(current_version < 28)
|
|
var/hockey
|
|
S["feature_cock_shape"] >> hockey
|
|
var/list/malformed_hockeys = list("Taur, Flared" = "Flared", "Taur, Knotted" = "Knotted", "Taur, Tapered" = "Tapered")
|
|
if(malformed_hockeys[hockey])
|
|
features["cock_shape"] = malformed_hockeys[hockey]
|
|
features["cock_taur"] = TRUE
|
|
|
|
if(current_version < 29)
|
|
var/digestable
|
|
var/devourable
|
|
var/feeding
|
|
var/lickable
|
|
S["digestable"] >> digestable
|
|
S["devourable"] >> devourable
|
|
S["feeding"] >> feeding
|
|
S["lickable"] >> lickable
|
|
if(digestable)
|
|
vore_flags |= DIGESTABLE
|
|
if(devourable)
|
|
vore_flags |= DEVOURABLE
|
|
if(feeding)
|
|
vore_flags |= FEEDING
|
|
if(lickable)
|
|
vore_flags |= LICKABLE
|
|
|
|
if(current_version < 30)
|
|
switch(features["taur"])
|
|
if("Husky", "Lab", "Shepherd", "Fox", "Wolf")
|
|
features["taur"] = "Canine"
|
|
if("Panther", "Tiger")
|
|
features["taur"] = "Feline"
|
|
if("Cow")
|
|
features["taur"] = "Cow (Spotted)"
|
|
|
|
if(current_version < 31)
|
|
S["wing_color"] >> features["wings_color"]
|
|
S["horn_color"] >> features["horns_color"]
|
|
|
|
if(current_version < 33)
|
|
features["flavor_text"] = html_encode(features["flavor_text"])
|
|
features["silicon_flavor_text"] = html_encode(features["silicon_flavor_text"])
|
|
features["ooc_notes"] = html_encode(features["ooc_notes"])
|
|
|
|
if(current_version < 35)
|
|
if(S["species"] == "lizard")
|
|
features["mam_snouts"] = features["snout"]
|
|
|
|
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"] = loadout_data
|
|
|
|
if(current_version < 48) //unlockable loadout items but we need to clear bad data from a mistake
|
|
S["unlockable_loadout"] = list()
|
|
|
|
if(current_version < 50)
|
|
var/list/L
|
|
S["be_special"] >> L
|
|
if(islist(L))
|
|
L -= ROLE_SYNDICATE
|
|
S["be_special"] << L
|
|
|
|
if(current_version < 51) //humans can have digi legs now, make sure they dont default to them or human players will murder me in my sleep
|
|
if(S["species"] == SPECIES_HUMAN)
|
|
features["legs"] = "Plantigrade"
|
|
|
|
if(current_version < 52) // rp markings means markings are now stored as a list, lizard markings now mam like the rest
|
|
var/marking_type
|
|
var/species_id = S["species"]
|
|
var/datum/species/actual_species = GLOB.species_datums[species_id]
|
|
|
|
// convert lizard markings to lizard markings
|
|
if(species_id == SPECIES_LIZARD && S["feature_lizard_body_markings"])
|
|
features["mam_body_markings"] = features["body_markings"]
|
|
|
|
// convert mam body marking data to the new rp marking data
|
|
if(actual_species.mutant_bodyparts["mam_body_markings"] && S["feature_mam_body_markings"]) marking_type = "feature_mam_body_markings"
|
|
|
|
if(marking_type)
|
|
var/old_marking_value = S[marking_type]
|
|
var/list/color_list = list("#FFFFFF","#FFFFFF","#FFFFFF")
|
|
|
|
if(S["feature_mcolor"]) color_list[1] = "#" + S["feature_mcolor"]
|
|
if(S["feature_mcolor2"]) color_list[2] = "#" + S["feature_mcolor2"]
|
|
if(S["feature_mcolor3"]) color_list[3] = "#" + S["feature_mcolor3"]
|
|
|
|
var/list/marking_list = list()
|
|
for(var/part in list(ARM_LEFT, ARM_RIGHT, LEG_LEFT, LEG_RIGHT, CHEST, HEAD))
|
|
var/list/copied_color_list = color_list.Copy()
|
|
var/datum/sprite_accessory/mam_body_markings/mam_marking = GLOB.mam_body_markings_list[old_marking_value]
|
|
var/part_name = GLOB.bodypart_names[num2text(part)]
|
|
if(length(mam_marking.covered_limbs) && mam_marking.covered_limbs[part_name])
|
|
var/matrixed_sections = mam_marking.covered_limbs[part_name]
|
|
// just trust me this is fine
|
|
switch(matrixed_sections)
|
|
if(MATRIX_GREEN)
|
|
copied_color_list[1] = copied_color_list[2]
|
|
if(MATRIX_BLUE)
|
|
copied_color_list[1] = copied_color_list[3]
|
|
if(MATRIX_RED_BLUE)
|
|
copied_color_list[2] = copied_color_list[3]
|
|
if(MATRIX_GREEN_BLUE)
|
|
copied_color_list[1] = copied_color_list[2]
|
|
copied_color_list[2] = copied_color_list[3]
|
|
marking_list += list(list(part, old_marking_value, copied_color_list))
|
|
features["mam_body_markings"] = marking_list
|
|
|
|
if(current_version < 53)
|
|
parallax = PARALLAX_INSANE
|
|
|
|
// Some genius decided to make features update on the loading part, go figure.
|
|
if(current_version < 54)
|
|
var/old_silicon_flavor = S["silicon_feature_flavor_text"]
|
|
if(length(old_silicon_flavor))
|
|
features["feature_silicon_flavor_text"] = old_silicon_flavor
|
|
var/old_flavor_text = S["flavor_text"]
|
|
// If they have the old flavor text but also have the new one, i suppose the new one is more important.
|
|
if(length(old_flavor_text) && !length(features["feature_flavor_text"]))
|
|
features["feature_flavor_text"] = old_flavor_text
|
|
|
|
// hey what happened to 55
|
|
|
|
// dullahans as a species cease to exist
|
|
if(current_version < 56)
|
|
var/species_id = S["species"]
|
|
if(species_id == SPECIES_DULLAHAN)
|
|
S["species"] = SPECIES_HUMAN
|
|
if(islist(S["all_quirks"]))
|
|
S["all_quirks"] += "Dullahan"
|
|
else
|
|
S["all_quirks"] = list("Dullahan")
|
|
|
|
// So, we're already on 57 even though we were meant to be on like, 56? i'm gonna try to correct this,
|
|
// And i'm so sorry for this.
|
|
if(current_version < 58)
|
|
S["screentip_images"] = TRUE // This was meant to default active, i'm so sorry. Turn it off if you must.
|
|
|
|
/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(bypass_cooldown = FALSE)
|
|
if(!path)
|
|
return FALSE
|
|
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
|
|
|
|
var/savefile/S = new /savefile(path)
|
|
if(!S)
|
|
return FALSE
|
|
S.cd = "/"
|
|
|
|
var/needs_update = savefile_needs_update(S)
|
|
if(needs_update == -2) //fatal, can't load any data
|
|
var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
|
|
if (fexists(bacpath))
|
|
fdel(bacpath) //only keep 1 version of backup
|
|
fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
|
return FALSE
|
|
|
|
. = TRUE
|
|
|
|
//general preferences
|
|
S["ooccolor"] >> ooccolor
|
|
S["lastchangelog"] >> lastchangelog
|
|
S["UI_style"] >> UI_style
|
|
S["outline_color"] >> outline_color
|
|
S["outline_enabled"] >> outline_enabled
|
|
S["screentip_pref"] >> screentip_pref
|
|
S["screentip_color"] >> screentip_color
|
|
S["screentip_images"] >> screentip_images
|
|
S["hotkeys"] >> hotkeys
|
|
S["chat_on_map"] >> chat_on_map
|
|
S["max_chat_length"] >> max_chat_length
|
|
S["see_chat_non_mob"] >> see_chat_non_mob
|
|
S["tgui_fancy"] >> tgui_fancy
|
|
S["tgui_lock"] >> tgui_lock
|
|
S["buttons_locked"] >> buttons_locked
|
|
S["windowflash"] >> windowflashing
|
|
S["be_special"] >> be_special
|
|
|
|
|
|
S["default_slot"] >> default_slot
|
|
S["chat_toggles"] >> chat_toggles
|
|
S["toggles"] >> toggles
|
|
S["deadmin"] >> deadmin
|
|
S["ghost_form"] >> ghost_form
|
|
S["ghost_orbit"] >> ghost_orbit
|
|
S["ghost_accs"] >> ghost_accs
|
|
S["ghost_others"] >> ghost_others
|
|
S["preferred_map"] >> preferred_map
|
|
S["ignoring"] >> ignoring
|
|
S["ghost_hud"] >> ghost_hud
|
|
S["inquisitive_ghost"] >> inquisitive_ghost
|
|
S["uses_glasses_colour"]>> uses_glasses_colour
|
|
S["clientfps"] >> clientfps
|
|
S["parallax"] >> parallax
|
|
S["ambientocclusion"] >> ambientocclusion
|
|
S["auto_fit_viewport"] >> auto_fit_viewport
|
|
S["widescreenpref"] >> widescreenpref
|
|
S["long_strip_menu"] >> long_strip_menu
|
|
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
|
|
S["enable_tips"] >> enable_tips
|
|
S["tip_delay"] >> tip_delay
|
|
S["pda_style"] >> pda_style
|
|
S["pda_color"] >> pda_color
|
|
S["pda_skin"] >> pda_skin
|
|
|
|
// Custom hotkeys
|
|
S["key_bindings"] >> key_bindings
|
|
S["modless_key_bindings"] >> modless_key_bindings
|
|
|
|
//citadel code
|
|
S["arousable"] >> arousable
|
|
S["screenshake"] >> screenshake
|
|
S["damagescreenshake"] >> damagescreenshake
|
|
S["autostand"] >> autostand
|
|
S["cit_toggles"] >> cit_toggles
|
|
S["preferred_chaos"] >> preferred_chaos
|
|
S["auto_ooc"] >> auto_ooc
|
|
S["no_tetris_storage"] >> no_tetris_storage
|
|
S["recoil_screenshake"] >> recoil_screenshake
|
|
|
|
//favorite outfits
|
|
S["favorite_outfits"] >> favorite_outfits
|
|
|
|
var/list/parsed_favs = list()
|
|
for(var/typetext in favorite_outfits)
|
|
var/datum/outfit/path = text2path(typetext)
|
|
if(ispath(path)) //whatever typepath fails this check probably doesn't exist anymore
|
|
parsed_favs += path
|
|
favorite_outfits = uniqueList(parsed_favs)
|
|
|
|
//try to fix any outdated data if necessary
|
|
if(needs_update >= 0)
|
|
var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
|
|
if (fexists(bacpath))
|
|
fdel(bacpath) //only keep 1 version of backup
|
|
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))
|
|
UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1])
|
|
hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
|
|
chat_on_map = sanitize_integer(chat_on_map, 0, 1, initial(chat_on_map))
|
|
max_chat_length = sanitize_integer(max_chat_length, 1, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length))
|
|
see_chat_non_mob = sanitize_integer(see_chat_non_mob, 0, 1, initial(see_chat_non_mob))
|
|
tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
|
|
tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
|
|
buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked))
|
|
windowflashing = sanitize_integer(windowflashing, 0, 1, initial(windowflashing))
|
|
default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot))
|
|
toggles = sanitize_integer(toggles, 0, 16777215, initial(toggles))
|
|
deadmin = sanitize_integer(deadmin, 0, 16777215, initial(deadmin))
|
|
clientfps = sanitize_integer(clientfps, 0, 1000, 0)
|
|
parallax = sanitize_integer(parallax, PARALLAX_DISABLE, PARALLAX_INSANE, 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))
|
|
long_strip_menu = sanitize_integer(long_strip_menu, 0, 1, initial(long_strip_menu))
|
|
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))
|
|
ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit))
|
|
ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION)
|
|
ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION)
|
|
menuoptions = SANITIZE_LIST(menuoptions)
|
|
be_special = SANITIZE_LIST(be_special)
|
|
pda_style = sanitize_inlist(pda_style, GLOB.pda_styles, initial(pda_style))
|
|
pda_color = sanitize_hexcolor(pda_color, 6, 1, initial(pda_color))
|
|
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))
|
|
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))
|
|
no_tetris_storage = sanitize_integer(no_tetris_storage, 0, 1, initial(no_tetris_storage))
|
|
recoil_screenshake = sanitize_integer(recoil_screenshake, 0, 800, initial(recoil_screenshake))
|
|
key_bindings = sanitize_islist(key_bindings, list())
|
|
modless_key_bindings = sanitize_islist(modless_key_bindings, list())
|
|
favorite_outfits = SANITIZE_LIST(favorite_outfits)
|
|
screentip_color = sanitize_hexcolor(screentip_color, 6, 1, initial(screentip_color))
|
|
screentip_pref = sanitize_inlist(screentip_pref, GLOB.screentip_pref_options, SCREENTIP_PREFERENCE_ENABLED)
|
|
|
|
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
|
|
|
|
for (var/slot in S.dir) //but first, update all current character slots.
|
|
if (copytext(slot, 1, 10) != "character")
|
|
continue
|
|
var/slotnum = text2num(copytext(slot, 10))
|
|
if (!slotnum)
|
|
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(null, TRUE)) // this updtates char slots
|
|
save_character(TRUE)
|
|
default_slot = old_default_slot
|
|
max_save_slots = old_max_save_slots
|
|
save_preferences(TRUE)
|
|
|
|
return TRUE
|
|
|
|
/datum/preferences/proc/verify_keybindings_valid()
|
|
// Sanitize the actual keybinds to make sure they exist.
|
|
for(var/key in key_bindings)
|
|
if(!islist(key_bindings[key]))
|
|
key_bindings -= key
|
|
var/list/binds = key_bindings[key]
|
|
for(var/bind in binds)
|
|
if(!GLOB.keybindings_by_name[bind])
|
|
binds -= bind
|
|
if(!length(binds))
|
|
key_bindings -= key
|
|
// End
|
|
// I hate copypaste but let's do it again but for modless ones
|
|
for(var/key in modless_key_bindings)
|
|
var/bindname = modless_key_bindings[key]
|
|
if(!GLOB.keybindings_by_name[bindname])
|
|
modless_key_bindings -= key
|
|
|
|
/datum/preferences/proc/save_preferences(bypass_cooldown = FALSE)
|
|
if(!path)
|
|
return 0
|
|
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
|
|
S.cd = "/"
|
|
|
|
WRITE_FILE(S["version"] , SAVEFILE_VERSION_MAX) //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date
|
|
|
|
//general preferences
|
|
WRITE_FILE(S["ooccolor"], ooccolor)
|
|
WRITE_FILE(S["lastchangelog"], lastchangelog)
|
|
WRITE_FILE(S["UI_style"], UI_style)
|
|
WRITE_FILE(S["outline_enabled"], outline_enabled)
|
|
WRITE_FILE(S["outline_color"], outline_color)
|
|
WRITE_FILE(S["screentip_pref"], screentip_pref)
|
|
WRITE_FILE(S["screentip_color"], screentip_color)
|
|
WRITE_FILE(S["screentip_images"], screentip_images)
|
|
WRITE_FILE(S["hotkeys"], hotkeys)
|
|
WRITE_FILE(S["chat_on_map"], chat_on_map)
|
|
WRITE_FILE(S["max_chat_length"], max_chat_length)
|
|
WRITE_FILE(S["see_chat_non_mob"], see_chat_non_mob)
|
|
WRITE_FILE(S["tgui_fancy"], tgui_fancy)
|
|
WRITE_FILE(S["tgui_lock"], tgui_lock)
|
|
WRITE_FILE(S["buttons_locked"], buttons_locked)
|
|
WRITE_FILE(S["windowflash"], windowflashing)
|
|
WRITE_FILE(S["be_special"], be_special)
|
|
WRITE_FILE(S["default_slot"], default_slot)
|
|
WRITE_FILE(S["toggles"], toggles)
|
|
WRITE_FILE(S["deadmin"], deadmin)
|
|
WRITE_FILE(S["chat_toggles"], chat_toggles)
|
|
WRITE_FILE(S["ghost_form"], ghost_form)
|
|
WRITE_FILE(S["ghost_orbit"], ghost_orbit)
|
|
WRITE_FILE(S["ghost_accs"], ghost_accs)
|
|
WRITE_FILE(S["ghost_others"], ghost_others)
|
|
WRITE_FILE(S["preferred_map"], preferred_map)
|
|
WRITE_FILE(S["ignoring"], ignoring)
|
|
WRITE_FILE(S["ghost_hud"], ghost_hud)
|
|
WRITE_FILE(S["inquisitive_ghost"], inquisitive_ghost)
|
|
WRITE_FILE(S["uses_glasses_colour"], uses_glasses_colour)
|
|
WRITE_FILE(S["clientfps"], clientfps)
|
|
WRITE_FILE(S["parallax"], parallax)
|
|
WRITE_FILE(S["ambientocclusion"], ambientocclusion)
|
|
WRITE_FILE(S["auto_fit_viewport"], auto_fit_viewport)
|
|
WRITE_FILE(S["hud_toggle_flash"], hud_toggle_flash)
|
|
WRITE_FILE(S["hud_toggle_color"], hud_toggle_color)
|
|
WRITE_FILE(S["menuoptions"], menuoptions)
|
|
WRITE_FILE(S["enable_tips"], enable_tips)
|
|
WRITE_FILE(S["tip_delay"], tip_delay)
|
|
WRITE_FILE(S["pda_style"], pda_style)
|
|
WRITE_FILE(S["pda_color"], pda_color)
|
|
WRITE_FILE(S["pda_skin"], pda_skin)
|
|
WRITE_FILE(S["key_bindings"], key_bindings)
|
|
WRITE_FILE(S["modless_key_bindings"], modless_key_bindings)
|
|
WRITE_FILE(S["favorite_outfits"], favorite_outfits)
|
|
|
|
//citadel code
|
|
WRITE_FILE(S["screenshake"], screenshake)
|
|
WRITE_FILE(S["damagescreenshake"], damagescreenshake)
|
|
WRITE_FILE(S["arousable"], arousable)
|
|
WRITE_FILE(S["widescreenpref"], widescreenpref)
|
|
WRITE_FILE(S["long_strip_menu"], long_strip_menu)
|
|
WRITE_FILE(S["autostand"], autostand)
|
|
WRITE_FILE(S["cit_toggles"], cit_toggles)
|
|
WRITE_FILE(S["preferred_chaos"], preferred_chaos)
|
|
WRITE_FILE(S["auto_ooc"], auto_ooc)
|
|
WRITE_FILE(S["no_tetris_storage"], no_tetris_storage)
|
|
WRITE_FILE(S["recoil_screenshake"], recoil_screenshake)
|
|
|
|
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, bypass_cooldown = FALSE)
|
|
if(!path)
|
|
return FALSE
|
|
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", "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, "has_butt" = FALSE, "butt_color" = "ffffff", "butt_size" = BUTT_SIZE_DEF, "balls_visibility" = GEN_VISIBLE_NO_UNDIES, "breasts_visibility"= GEN_VISIBLE_NO_UNDIES, "cock_visibility" = GEN_VISIBLE_NO_UNDIES, "vag_visibility" = GEN_VISIBLE_NO_UNDIES, "butt_visibility" = GEN_VISIBLE_NO_UNDIES, "ipc_screen" = "Sunburst", "ipc_antenna" = "None", "flavor_text" = "", "silicon_flavor_text" = "", "ooc_notes" = "", "meat_type" = "Mammalian", "body_model" = MALE, "body_size" = RESIZE_DEFAULT_SIZE, "color_scheme" = OLD_CHARACTER_COLORING)
|
|
|
|
S.cd = "/"
|
|
if(!slot)
|
|
slot = default_slot
|
|
slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
|
|
if(slot != default_slot)
|
|
default_slot = slot
|
|
WRITE_FILE(S["default_slot"] , slot)
|
|
|
|
S.cd = "/character[slot]"
|
|
var/needs_update = savefile_needs_update(S)
|
|
if(needs_update == -2) //fatal, can't load any data
|
|
return FALSE
|
|
|
|
. = TRUE
|
|
|
|
//Species
|
|
var/species_id
|
|
S["species"] >> species_id
|
|
if(species_id)
|
|
if(species_id == "avian" || species_id == "aquatic")
|
|
species_id = "mammal"
|
|
else if(species_id == "moth")
|
|
species_id = "insect"
|
|
|
|
var/newtype = GLOB.species_list[species_id]
|
|
if(newtype)
|
|
pref_species = new newtype
|
|
|
|
|
|
scars_index = rand(1,5) // WHY
|
|
|
|
//Character
|
|
S["real_name"] >> real_name
|
|
S["nameless"] >> nameless
|
|
S["custom_species"] >> custom_species
|
|
S["name_is_always_random"] >> be_random_name
|
|
S["body_is_always_random"] >> be_random_body
|
|
S["gender"] >> gender
|
|
S["body_model"] >> features["body_model"]
|
|
S["body_size"] >> features["body_size"]
|
|
S["age"] >> age
|
|
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
|
|
S["skin_tone"] >> skin_tone
|
|
S["hair_style_name"] >> hair_style
|
|
S["facial_style_name"] >> facial_hair_style
|
|
S["grad_style"] >> grad_style
|
|
S["grad_color"] >> grad_color
|
|
S["underwear"] >> underwear
|
|
S["undie_color"] >> undie_color
|
|
S["undershirt"] >> undershirt
|
|
S["shirt_color"] >> shirt_color
|
|
S["socks"] >> socks
|
|
S["socks_color"] >> socks_color
|
|
S["backbag"] >> backbag
|
|
S["jumpsuit_style"] >> jumpsuit_style
|
|
S["uplink_loc"] >> uplink_spawn_loc
|
|
S["custom_speech_verb"] >> custom_speech_verb
|
|
S["custom_tongue"] >> custom_tongue
|
|
S["additional_language"] >> additional_language
|
|
S["feature_mcolor"] >> features["mcolor"]
|
|
S["feature_lizard_tail"] >> features["tail_lizard"]
|
|
S["feature_lizard_snout"] >> features["snout"]
|
|
S["feature_lizard_horns"] >> features["horns"]
|
|
S["feature_lizard_frills"] >> features["frills"]
|
|
S["feature_lizard_spines"] >> features["spines"]
|
|
S["feature_lizard_legs"] >> features["legs"]
|
|
S["feature_human_tail"] >> features["tail_human"]
|
|
S["feature_human_ears"] >> features["ears"]
|
|
S["feature_deco_wings"] >> features["deco_wings"]
|
|
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"]
|
|
S["scars3"] >> scars_list["3"]
|
|
S["scars4"] >> scars_list["4"]
|
|
S["scars5"] >> scars_list["5"]
|
|
var/limbmodstr
|
|
S["modified_limbs"] >> limbmodstr
|
|
if(length(limbmodstr))
|
|
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
|
|
|
|
//Custom names
|
|
for(var/custom_name_id in GLOB.preferences_custom_names)
|
|
var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
|
|
S[savefile_slot_name] >> custom_names[custom_name_id]
|
|
|
|
S["preferred_ai_core_display"] >> preferred_ai_core_display
|
|
S["prefered_security_department"] >> prefered_security_department
|
|
|
|
//Jobs
|
|
S["joblessrole"] >> joblessrole
|
|
|
|
//Load prefs
|
|
S["job_preferences"] >> job_preferences
|
|
|
|
//Quirks
|
|
S["all_quirks"] >> all_quirks
|
|
|
|
//Records
|
|
S["security_records"] >> security_records
|
|
S["medical_records"] >> medical_records
|
|
|
|
//Citadel code
|
|
S["feature_genitals_use_skintone"] >> features["genitals_use_skintone"]
|
|
S["feature_mcolor2"] >> features["mcolor2"]
|
|
S["feature_mcolor3"] >> features["mcolor3"]
|
|
// note safe json decode will runtime the first time it migrates but this is fine and it solves itself don't worry about it if you see it error
|
|
features["mam_body_markings"] = safe_json_decode(S["feature_mam_body_markings"])
|
|
S["feature_mam_tail"] >> features["mam_tail"]
|
|
S["feature_mam_ears"] >> features["mam_ears"]
|
|
S["feature_mam_tail_animated"] >> features["mam_tail_animated"]
|
|
S["feature_taur"] >> features["taur"]
|
|
S["feature_mam_snouts"] >> features["mam_snouts"]
|
|
S["feature_meat"] >> features["meat_type"]
|
|
//Xeno features
|
|
S["feature_xeno_tail"] >> features["xenotail"]
|
|
S["feature_xeno_dors"] >> features["xenodorsal"]
|
|
S["feature_xeno_head"] >> features["xenohead"]
|
|
//cock features
|
|
S["feature_has_cock"] >> features["has_cock"]
|
|
S["feature_cock_shape"] >> features["cock_shape"]
|
|
S["feature_cock_color"] >> features["cock_color"]
|
|
S["feature_cock_length"] >> features["cock_length"]
|
|
S["feature_cock_diameter"] >> features["cock_diameter"]
|
|
S["feature_cock_taur"] >> features["cock_taur"]
|
|
S["feature_cock_visibility"] >> features["cock_visibility"]
|
|
//balls features
|
|
S["feature_has_balls"] >> features["has_balls"]
|
|
S["feature_balls_color"] >> features["balls_color"]
|
|
S["feature_balls_shape"] >> features["balls_shape"]
|
|
S["feature_balls_size"] >> features["balls_size"]
|
|
S["feature_balls_visibility"] >> features["balls_visibility"]
|
|
//breasts features
|
|
S["feature_has_breasts"] >> features["has_breasts"]
|
|
S["feature_breasts_size"] >> features["breasts_size"]
|
|
S["feature_breasts_shape"] >> features["breasts_shape"]
|
|
S["feature_breasts_color"] >> features["breasts_color"]
|
|
S["feature_breasts_producing"] >> features["breasts_producing"]
|
|
S["feature_breasts_visibility"] >> features["breasts_visibility"]
|
|
//vagina features
|
|
S["feature_has_vag"] >> features["has_vag"]
|
|
S["feature_vag_shape"] >> features["vag_shape"]
|
|
S["feature_vag_color"] >> features["vag_color"]
|
|
S["feature_vag_visibility"] >> features["vag_visibility"]
|
|
//womb features
|
|
S["feature_has_womb"] >> features["has_womb"]
|
|
//butt features
|
|
S["feature_has_butt"] >> features["has_butt"]
|
|
S["feature_butt_color"] >> features["butt_color"]
|
|
S["feature_butt_size"] >> features["butt_size"]
|
|
S["feature_butt_visibility"] >> features["butt_visibility"]
|
|
|
|
// Flavor texts, Made into a standard.
|
|
S["feature_flavor_text"] >> features["flavor_text"]
|
|
S["feature_silicon_flavor_text"] >> features["silicon_flavor_text"]
|
|
S["feature_ooc_notes"] >> features["ooc_notes"]
|
|
|
|
// Barks
|
|
S["bark_id"] >> bark_id
|
|
S["bark_speed"] >> bark_speed
|
|
S["bark_pitch"] >> bark_pitch
|
|
S["bark_variance"] >> bark_variance
|
|
|
|
S["vore_flags"] >> vore_flags
|
|
S["vore_taste"] >> vore_taste
|
|
S["vore_smell"] >> vore_smell
|
|
var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json"
|
|
if(fexists(char_vr_path))
|
|
var/list/json_from_file = json_decode(file2text(char_vr_path))
|
|
if(json_from_file)
|
|
belly_prefs = json_from_file["belly_prefs"]
|
|
|
|
//gear loadout
|
|
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.
|
|
if(needs_update >= 0)
|
|
update_character(needs_update, S) //needs_update == savefile_version if we need an update (positive integer)
|
|
|
|
//Sanitize
|
|
|
|
real_name = reject_bad_name(real_name)
|
|
gender = sanitize_gender(gender, TRUE, TRUE)
|
|
features["body_model"] = sanitize_gender(features["body_model"], FALSE, FALSE, gender == FEMALE ? FEMALE : MALE)
|
|
if(!real_name)
|
|
real_name = random_unique_name(gender)
|
|
custom_species = reject_bad_name(custom_species)
|
|
for(var/custom_name_id in GLOB.preferences_custom_names)
|
|
var/namedata = GLOB.preferences_custom_names[custom_name_id]
|
|
custom_names[custom_name_id] = reject_bad_name(custom_names[custom_name_id],namedata["allow_numbers"])
|
|
if(!custom_names[custom_name_id])
|
|
custom_names[custom_name_id] = get_default_name(custom_name_id)
|
|
|
|
nameless = sanitize_integer(nameless, 0, 1, initial(nameless))
|
|
be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
|
|
be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body))
|
|
|
|
hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_list)
|
|
facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_list)
|
|
underwear = sanitize_inlist(underwear, GLOB.underwear_list)
|
|
undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list)
|
|
undie_color = sanitize_hexcolor(undie_color, 6, FALSE, initial(undie_color))
|
|
shirt_color = sanitize_hexcolor(shirt_color, 6, FALSE, initial(shirt_color))
|
|
socks = sanitize_inlist(socks, GLOB.socks_list)
|
|
socks_color = sanitize_hexcolor(socks_color, 6, FALSE, initial(socks_color))
|
|
age = sanitize_integer(age, AGE_MIN, AGE_MAX_INPUT, initial(age))
|
|
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
|
|
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
|
|
grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list, "None")
|
|
grad_color = sanitize_hexcolor(grad_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)
|
|
|
|
var/static/allow_custom_skintones
|
|
if(isnull(allow_custom_skintones))
|
|
allow_custom_skintones = CONFIG_GET(flag/allow_custom_skintones)
|
|
use_custom_skin_tone = allow_custom_skintones ? sanitize_integer(use_custom_skin_tone, FALSE, TRUE, initial(use_custom_skin_tone)) : FALSE
|
|
if(use_custom_skin_tone)
|
|
skin_tone = sanitize_hexcolor(skin_tone, 6, TRUE, "#FFFFFF")
|
|
else
|
|
skin_tone = sanitize_inlist(skin_tone, GLOB.skin_tones - GLOB.nonstandard_skin_tones, initial(skin_tone))
|
|
|
|
features["horns_color"] = sanitize_hexcolor(features["horns_color"], 6, FALSE, "85615a")
|
|
features["wings_color"] = sanitize_hexcolor(features["wings_color"], 6, FALSE, "FFFFFF")
|
|
backbag = sanitize_inlist(backbag, GLOB.backbaglist, initial(backbag))
|
|
jumpsuit_style = sanitize_inlist(jumpsuit_style, GLOB.jumpsuitlist, initial(jumpsuit_style))
|
|
uplink_spawn_loc = sanitize_inlist(uplink_spawn_loc, GLOB.uplink_spawn_loc_list, initial(uplink_spawn_loc))
|
|
features["mcolor"] = sanitize_hexcolor(features["mcolor"], 6, FALSE)
|
|
features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], GLOB.tails_list_lizard)
|
|
features["tail_human"] = sanitize_inlist(features["tail_human"], GLOB.tails_list_human)
|
|
features["snout"] = sanitize_inlist(features["snout"], GLOB.snouts_list)
|
|
features["horns"] = sanitize_inlist(features["horns"], GLOB.horns_list)
|
|
features["ears"] = sanitize_inlist(features["ears"], GLOB.ears_list)
|
|
features["frills"] = sanitize_inlist(features["frills"], GLOB.frills_list)
|
|
features["spines"] = sanitize_inlist(features["spines"], GLOB.spines_list)
|
|
features["legs"] = sanitize_inlist(features["legs"], GLOB.legs_list, "Plantigrade")
|
|
features["deco_wings"] = sanitize_inlist(features["deco_wings"], GLOB.deco_wings_list, "None")
|
|
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)
|
|
size_min = CONFIG_GET(number/body_size_min)
|
|
var/static/size_max
|
|
if(!size_max)
|
|
size_max = CONFIG_GET(number/body_size_max)
|
|
features["body_size"] = sanitize_num_clamp(features["body_size"], size_min, size_max, RESIZE_DEFAULT_SIZE, 0.01)
|
|
|
|
var/static/list/B_sizes
|
|
if(!B_sizes)
|
|
var/list/L = CONFIG_GET(keyed_list/breasts_cups_prefs)
|
|
B_sizes = L.Copy()
|
|
var/static/min_D
|
|
if(!min_D)
|
|
min_D = CONFIG_GET(number/penis_min_inches_prefs)
|
|
var/static/max_D
|
|
if(!max_D)
|
|
max_D = CONFIG_GET(number/penis_max_inches_prefs)
|
|
var/static/min_B
|
|
if(!min_B)
|
|
min_B = CONFIG_GET(number/butt_min_size_prefs)
|
|
var/static/max_B
|
|
if(!max_B)
|
|
max_B = CONFIG_GET(number/butt_max_size_prefs)
|
|
|
|
var/static/safe_visibilities
|
|
if(!safe_visibilities)
|
|
var/list/L = CONFIG_GET(keyed_list/safe_visibility_toggles)
|
|
safe_visibilities = L.Copy()
|
|
|
|
features["breasts_size"] = sanitize_inlist(features["breasts_size"], B_sizes, BREASTS_SIZE_DEF)
|
|
features["cock_length"] = sanitize_integer(features["cock_length"], min_D, max_D, COCK_SIZE_DEF)
|
|
features["butt_size"] = sanitize_integer(features["butt_size"], min_B, max_B, BUTT_SIZE_DEF)
|
|
features["breasts_shape"] = sanitize_inlist(features["breasts_shape"], GLOB.breasts_shapes_list, DEF_BREASTS_SHAPE)
|
|
features["cock_shape"] = sanitize_inlist(features["cock_shape"], GLOB.cock_shapes_list, DEF_COCK_SHAPE)
|
|
features["balls_shape"] = sanitize_inlist(features["balls_shape"], GLOB.balls_shapes_list, DEF_BALLS_SHAPE)
|
|
features["vag_shape"] = sanitize_inlist(features["vag_shape"], GLOB.vagina_shapes_list, DEF_VAGINA_SHAPE)
|
|
features["breasts_color"] = sanitize_hexcolor(features["breasts_color"], 6, FALSE, "FFFFFF")
|
|
features["cock_color"] = sanitize_hexcolor(features["cock_color"], 6, FALSE, "FFFFFF")
|
|
features["balls_color"] = sanitize_hexcolor(features["balls_color"], 6, FALSE, "FFFFFF")
|
|
features["vag_color"] = sanitize_hexcolor(features["vag_color"], 6, FALSE, "FFFFFF")
|
|
features["breasts_visibility"] = sanitize_inlist(features["breasts_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES)
|
|
features["cock_visibility"] = sanitize_inlist(features["cock_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES)
|
|
features["balls_visibility"] = sanitize_inlist(features["balls_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES)
|
|
features["vag_visibility"] = sanitize_inlist(features["vag_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES)
|
|
features["butt_visibility"] = sanitize_inlist(features["butt_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES)
|
|
|
|
custom_speech_verb = sanitize_inlist(custom_speech_verb, GLOB.speech_verbs, "default")
|
|
custom_tongue = sanitize_inlist(custom_tongue, GLOB.roundstart_tongues, "default")
|
|
additional_language = sanitize_inlist(additional_language, GLOB.roundstart_languages, "None")
|
|
|
|
security_records = copytext(security_records, 1, MAX_FLAVOR_LEN)
|
|
medical_records = copytext(medical_records, 1, MAX_FLAVOR_LEN)
|
|
|
|
features["flavor_text"] = copytext(features["flavor_text"], 1, MAX_FLAVOR_LEN)
|
|
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"])
|
|
scars_list["3"] = sanitize_text(scars_list["3"])
|
|
scars_list["4"] = sanitize_text(scars_list["4"])
|
|
scars_list["5"] = sanitize_text(scars_list["5"])
|
|
|
|
bark_id = sanitize_inlist(bark_id, GLOB.bark_list, pick(GLOB.bark_random_list))
|
|
var/datum/bark/bark_path = GLOB.bark_list[bark_id]
|
|
bark_speed = sanitize_num_clamp(bark_speed, initial(bark_path.minspeed), initial(bark_path.maxspeed), initial(bark_speed))
|
|
bark_pitch = sanitize_num_clamp(bark_pitch, initial(bark_path.minpitch), initial(bark_path.maxpitch), BARK_PITCH_RAND(gender))
|
|
bark_variance = sanitize_num_clamp(bark_variance, initial(bark_path.minvariance), initial(bark_path.maxvariance), BARK_VARIANCE_RAND)
|
|
|
|
joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole))
|
|
//Validate job prefs
|
|
for(var/j in job_preferences)
|
|
if(job_preferences["[j]"] != JP_LOW && job_preferences["[j]"] != JP_MEDIUM && job_preferences["[j]"] != JP_HIGH)
|
|
job_preferences -= j
|
|
|
|
all_quirks = SANITIZE_LIST(all_quirks)
|
|
|
|
vore_flags = sanitize_integer(vore_flags, 0, MAX_VORE_FLAG, 0)
|
|
vore_taste = copytext(vore_taste, 1, MAX_TASTE_LEN)
|
|
vore_smell = copytext(vore_smell, 1, MAX_TASTE_LEN)
|
|
belly_prefs = SANITIZE_LIST(belly_prefs)
|
|
|
|
cit_character_pref_load(S)
|
|
|
|
return 1
|
|
|
|
/datum/preferences/proc/save_character(bypass_cooldown = FALSE)
|
|
if(!path)
|
|
return 0
|
|
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
|
|
S.cd = "/character[default_slot]"
|
|
|
|
WRITE_FILE(S["version"] , SAVEFILE_VERSION_MAX) //load_character will sanitize any bad data, so assume up-to-date.)
|
|
|
|
//Character
|
|
WRITE_FILE(S["real_name"] , real_name)
|
|
WRITE_FILE(S["nameless"] , nameless)
|
|
WRITE_FILE(S["custom_species"] , custom_species)
|
|
WRITE_FILE(S["name_is_always_random"] , be_random_name)
|
|
WRITE_FILE(S["body_is_always_random"] , be_random_body)
|
|
WRITE_FILE(S["gender"] , gender)
|
|
WRITE_FILE(S["body_model"] , features["body_model"])
|
|
WRITE_FILE(S["body_size"] , features["body_size"])
|
|
WRITE_FILE(S["age"] , age)
|
|
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)
|
|
WRITE_FILE(S["skin_tone"] , skin_tone)
|
|
WRITE_FILE(S["hair_style_name"] , hair_style)
|
|
WRITE_FILE(S["facial_style_name"] , facial_hair_style)
|
|
WRITE_FILE(S["grad_style"] , grad_style)
|
|
WRITE_FILE(S["grad_color"] , grad_color)
|
|
WRITE_FILE(S["underwear"] , underwear)
|
|
WRITE_FILE(S["undie_color"] , undie_color)
|
|
WRITE_FILE(S["undershirt"] , undershirt)
|
|
WRITE_FILE(S["shirt_color"] , shirt_color)
|
|
WRITE_FILE(S["socks"] , socks)
|
|
WRITE_FILE(S["socks_color"] , socks_color)
|
|
WRITE_FILE(S["backbag"] , backbag)
|
|
WRITE_FILE(S["jumpsuit_style"] , jumpsuit_style)
|
|
WRITE_FILE(S["uplink_loc"] , uplink_spawn_loc)
|
|
WRITE_FILE(S["species"] , pref_species.id)
|
|
WRITE_FILE(S["custom_speech_verb"] , custom_speech_verb)
|
|
WRITE_FILE(S["custom_tongue"] , custom_tongue)
|
|
WRITE_FILE(S["additional_language"] , additional_language)
|
|
WRITE_FILE(S["bark_id"] , bark_id)
|
|
WRITE_FILE(S["bark_speed"] , bark_speed)
|
|
WRITE_FILE(S["bark_pitch"] , bark_pitch)
|
|
WRITE_FILE(S["bark_variance"] , bark_variance)
|
|
|
|
// records
|
|
WRITE_FILE(S["security_records"] , security_records)
|
|
WRITE_FILE(S["medical_records"] , medical_records)
|
|
|
|
WRITE_FILE(S["feature_mcolor"] , features["mcolor"])
|
|
WRITE_FILE(S["feature_lizard_tail"] , features["tail_lizard"])
|
|
WRITE_FILE(S["feature_human_tail"] , features["tail_human"])
|
|
WRITE_FILE(S["feature_lizard_snout"] , features["snout"])
|
|
WRITE_FILE(S["feature_lizard_horns"] , features["horns"])
|
|
WRITE_FILE(S["feature_human_ears"] , features["ears"])
|
|
WRITE_FILE(S["feature_lizard_frills"] , features["frills"])
|
|
WRITE_FILE(S["feature_lizard_spines"] , features["spines"])
|
|
WRITE_FILE(S["feature_lizard_legs"] , features["legs"])
|
|
WRITE_FILE(S["feature_deco_wings"] , features["deco_wings"])
|
|
WRITE_FILE(S["feature_horns_color"] , features["horns_color"])
|
|
WRITE_FILE(S["feature_wings_color"] , features["wings_color"])
|
|
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"])
|
|
WRITE_FILE(S["feature_cock_shape"], features["cock_shape"])
|
|
WRITE_FILE(S["feature_cock_color"], features["cock_color"])
|
|
WRITE_FILE(S["feature_cock_length"], features["cock_length"])
|
|
WRITE_FILE(S["feature_cock_taur"], features["cock_taur"])
|
|
WRITE_FILE(S["feature_cock_visibility"], features["cock_visibility"])
|
|
|
|
WRITE_FILE(S["feature_has_balls"], features["has_balls"])
|
|
WRITE_FILE(S["feature_balls_color"], features["balls_color"])
|
|
WRITE_FILE(S["feature_balls_shape"], features["balls_shape"])
|
|
WRITE_FILE(S["feature_balls_size"], features["balls_size"])
|
|
WRITE_FILE(S["feature_balls_visibility"], features["balls_visibility"])
|
|
|
|
WRITE_FILE(S["feature_has_breasts"], features["has_breasts"])
|
|
WRITE_FILE(S["feature_breasts_size"], features["breasts_size"])
|
|
WRITE_FILE(S["feature_breasts_shape"], features["breasts_shape"])
|
|
WRITE_FILE(S["feature_breasts_color"], features["breasts_color"])
|
|
WRITE_FILE(S["feature_breasts_producing"], features["breasts_producing"])
|
|
WRITE_FILE(S["feature_breasts_visibility"], features["breasts_visibility"])
|
|
|
|
WRITE_FILE(S["feature_has_vag"], features["has_vag"])
|
|
WRITE_FILE(S["feature_vag_shape"], features["vag_shape"])
|
|
WRITE_FILE(S["feature_vag_color"], features["vag_color"])
|
|
WRITE_FILE(S["feature_vag_visibility"], features["vag_visibility"])
|
|
|
|
WRITE_FILE(S["feature_has_womb"], features["has_womb"])
|
|
|
|
WRITE_FILE(S["feature_has_butt"], features["has_butt"])
|
|
WRITE_FILE(S["feature_butt_color"], features["butt_color"])
|
|
WRITE_FILE(S["feature_butt_size"], features["butt_size"])
|
|
WRITE_FILE(S["feature_butt_visibility"], features["butt_visibility"])
|
|
|
|
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
|
|
WRITE_FILE(S[savefile_slot_name],custom_names[custom_name_id])
|
|
|
|
WRITE_FILE(S["preferred_ai_core_display"] , preferred_ai_core_display)
|
|
WRITE_FILE(S["prefered_security_department"] , prefered_security_department)
|
|
|
|
//Jobs
|
|
WRITE_FILE(S["joblessrole"] , joblessrole)
|
|
//Write prefs
|
|
WRITE_FILE(S["job_preferences"] , job_preferences)
|
|
WRITE_FILE(S["hide_ckey"] , hide_ckey)
|
|
|
|
//Quirks
|
|
WRITE_FILE(S["all_quirks"] , all_quirks)
|
|
|
|
WRITE_FILE(S["vore_flags"] , vore_flags)
|
|
WRITE_FILE(S["vore_taste"] , vore_taste)
|
|
WRITE_FILE(S["vore_smell"] , vore_smell)
|
|
var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json"
|
|
var/belly_prefs_json = safe_json_encode(list("belly_prefs" = belly_prefs))
|
|
if(fexists(char_vr_path))
|
|
fdel(char_vr_path)
|
|
text2file(belly_prefs_json,char_vr_path)
|
|
|
|
WRITE_FILE(S["persistent_scars"] , persistent_scars)
|
|
WRITE_FILE(S["scars1"] , scars_list["1"])
|
|
WRITE_FILE(S["scars2"] , scars_list["2"])
|
|
WRITE_FILE(S["scars3"] , scars_list["3"])
|
|
WRITE_FILE(S["scars4"] , scars_list["4"])
|
|
WRITE_FILE(S["scars5"] , scars_list["5"])
|
|
if(islist(modified_limbs))
|
|
WRITE_FILE(S["modified_limbs"] , safe_json_encode(modified_limbs))
|
|
WRITE_FILE(S["chosen_limb_id"], chosen_limb_id)
|
|
|
|
|
|
//gear loadout
|
|
if(length(loadout_data))
|
|
S["loadout"] << safe_json_encode(loadout_data)
|
|
else
|
|
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)
|
|
|
|
return 1
|
|
|
|
|
|
#undef SAVEFILE_VERSION_MAX
|
|
#undef SAVEFILE_VERSION_MIN
|
|
|
|
#ifdef TESTING
|
|
//DEBUG
|
|
//Some crude tools for testing savefiles
|
|
//path is the savefile path
|
|
/client/verb/savefile_export(path as text)
|
|
var/savefile/S = new /savefile(path)
|
|
S.ExportText("/",file("[path].txt"))
|
|
//path is the savefile path
|
|
/client/verb/savefile_import(path as text)
|
|
var/savefile/S = new /savefile(path)
|
|
S.ImportText("/",file("[path].txt"))
|
|
|
|
#endif
|