Refactored underwear to use datum/sprite_accessory/underwear similar to hair and facial_hair.

People may select any underwear (regardless of gender) at a dresser, but not in preferences.
random_underwear(gender) will return a random pair of underwear suitable for your gender.
Removed an unused variable in sprite_accessory

The new_player preferences screen now randomizes character slots for which there is no data (so no more bald, fat, white guys in diapers greeting new players)
datum/preferences character settings are randomised at New().
replaced /datum/preferences/proc/randomize_appearance_for(human/H) with /datum/preferences/proc/random_character(), it does the same stuff without the copying to a mob. Basically, now when you want to make a random character you just do var/datum/preferences/A = new(); A.copy_to(human_mob), randomisation of appearance and name will already be done. Easy.

Reworked the savefile updating/versioning code to make it easier to work with. I've used it to update underwear preferences to the new system as an example.

Signed-off-by: carnie <elly1989@rocketmail.com>
This commit is contained in:
carnie
2013-03-28 11:11:40 +00:00
parent 2c9283dd05
commit 5ca166cae0
23 changed files with 387 additions and 212 deletions
+25 -22
View File
@@ -24,7 +24,6 @@ datum/preferences
//doohickeys for savefiles
var/path
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
var/savefile_version = 0
//non-preference stuff
var/warns = 0
@@ -45,7 +44,7 @@ datum/preferences
var/gender = MALE //gender of character (well duh)
var/age = 30 //age of character
var/b_type = "A+" //blood type (not-chooseable)
var/underwear = 1 //underwear type
var/underwear = "Nude" //underwear type
var/backbag = 2 //backpack type
var/h_style = "Bald" //Hair type
var/r_hair = 0 //Hair color
@@ -91,11 +90,17 @@ datum/preferences
if(istype(C))
if(!IsGuestKey(C.key))
load_path(C.ckey)
if(load_preferences())
if(load_character())
return
gender = pick(MALE, FEMALE)
var/loaded_preferences_successfully = load_preferences()
if(loaded_preferences_successfully)
if(load_character())
return
//we couldn't load character data so just randomize the character appearance + name
random_character() //let's create a random character then - rather than a fat, bald and naked man.
real_name = random_name(gender)
if(!loaded_preferences_successfully)
save_preferences()
save_character() //let's save this new random character so it doesn't keep generating new ones.
return
/datum/preferences
proc/ShowChoices(mob/user)
@@ -157,12 +162,7 @@ datum/preferences
dat += "<b>Blood Type:</b> [b_type]<BR>"
dat += "<b>Skin Tone:</b><BR><a href='?_src_=prefs;preference=s_tone;task=input'>[-s_tone + 35]/220</a><BR>"
if(gender == MALE)
dat += "<b>Underwear:</b><BR><a href ='?_src_=prefs;preference=underwear;task=input'>[underwear_m[underwear]]</a><BR>"
else
dat += "<b>Underwear:</b><BR><a href ='?_src_=prefs;preference=underwear;task=input'>[underwear_f[underwear]]</a><BR>"
dat += "<b>Underwear:</b><BR><a href ='?_src_=prefs;preference=underwear;task=input'>[underwear]</a><BR>"
dat += "<b>Backpack:</b><BR><a href ='?_src_=prefs;preference=bag;task=input'>[backbaglist[backbag]]</a><BR>"
@@ -497,7 +497,7 @@ datum/preferences
if("f_style")
f_style = random_facial_hair_style(gender)
if("underwear")
underwear = rand(1,underwear_m.len)
underwear = random_underwear(gender)
if("eyes")
r_eyes = rand(0,255)
g_eyes = rand(0,255)
@@ -507,7 +507,7 @@ datum/preferences
if("bag")
backbag = rand(1,3)
if("all")
randomize_appearance_for() //no params needed
random_character()
if("input")
switch(href_list["preference"])
@@ -561,7 +561,7 @@ datum/preferences
var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_options
if(new_underwear)
underwear = underwear_options.Find(new_underwear)
underwear = new_underwear
if("eyes")
var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null
@@ -591,6 +591,8 @@ datum/preferences
gender = FEMALE
else
gender = MALE
underwear = random_underwear(gender)
if("hear_adminhelps")
toggles ^= SOUND_ADMINHELP
@@ -636,7 +638,10 @@ datum/preferences
load_character()
if("changeslot")
load_character(text2num(href_list["num"]))
if(!load_character(text2num(href_list["num"])))
random_character()
real_name = random_name(gender)
save_character()
if("tab")
if (href_list["tab"])
@@ -645,9 +650,9 @@ datum/preferences
ShowChoices(user)
return 1
proc/copy_to(mob/living/carbon/human/character, safety = 0)
proc/copy_to(mob/living/carbon/human/character)
if(be_random_name)
real_name = random_name()
real_name = random_name(gender)
if(config.humans_need_surnames)
var/firstspace = findtext(real_name, " ")
@@ -679,21 +684,19 @@ datum/preferences
character.b_facial = b_facial
character.s_tone = s_tone
character.h_style = h_style
character.f_style = f_style
if(underwear > underwear_m.len || underwear < 1)
underwear = 1 //I'm sure this is 100% unnecessary, but I'm paranoid... sue me.
character.underwear = underwear
if(backbag > 3 || backbag < 1)
backbag = 1 //Same as above
character.backbag = backbag
/*
//Debugging report to track down a bug, which randomly assigned the plural gender to people.
if(character.gender in list(PLURAL, NEUTER))
if(isliving(src)) //Ghosts get neuter by default
message_admins("[character] ([character.ckey]) has spawned with their gender as plural or neuter. Please notify coders.")
character.gender = MALE
*/
+115 -39
View File
@@ -1,51 +1,108 @@
//This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped.
#define SAVEFILE_VERSION_MIN 8
#define SAVEFILE_VERSION_MAX 8
//handles converting savefiles to new formats
//MAKE SURE YOU KEEP THIS UP TO DATE!
//If the sanity checks are capable of handling any issues. Only increase SAVEFILE_VERSION_MAX,
//this will mean that savefile_version will still be over SAVEFILE_VERSION_MIN, meaning
//this savefile update doesn't run everytime we load from the savefile.
//This is mainly for format changes, such as the bitflags in toggles changing order or something.
//if a file can't be updated, return 0 to delete it and start again
//if a file was updated, return 1
/datum/preferences/proc/savefile_update()
if(savefile_version < 8) //lazily delete everything + additional files so they can be saved in the new format
for(var/ckey in preferences_datums)
var/datum/preferences/D = preferences_datums[ckey]
if(D == src)
var/delpath = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/"
if(delpath && fexists(delpath))
fdel(delpath)
break
return 0
//This is the current version, anything below this will attempt to update (if it's not obsolete)
#define SAVEFILE_VERSION_MAX 9
if(savefile_version == SAVEFILE_VERSION_MAX) //update successful.
save_preferences()
save_character()
return 1
return 0
/*//This will allow you to print savefiles
/client/verb/read_savefile(client/C in clients)
var/savefile/S = new /savefile(prefs.path)
S.ExportText("/",file("savefile.txt"))
*/
/*
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
/datum/preferences/proc/update_preferences(current_version)
return
//should this proc get fairly long (say 3 versions long),
//just increase SAVEFILE_VERSION_MIN so it's not as far behind
//SAVEFILE_VERSION_MAX and then delete any obsolete if clauses
//from this proc.
//It's only really meant to avoid annoying frequent players
//if your savefile is 3 months out of date, then 'tough shit'.
/datum/preferences/proc/update_character(current_version)
if(current_version < 9) //an example, underwear were an index for a hardcoded list, converting to a string
if(gender == MALE)
switch(underwear)
if(1) underwear = "Mens White"
if(2) underwear = "Mens Grey"
if(3) underwear = "Mens Green"
if(4) underwear = "Mens Blue"
if(5) underwear = "Mens Black"
if(6) underwear = "Mankini"
if(7) underwear = "Mens Hearts Boxer"
if(8) underwear = "Mens Black Boxer"
if(9) underwear = "Mens Grey Boxer"
if(10) underwear = "Mens Striped Boxer"
if(11) underwear = "Mens Kinky"
if(12) underwear = "Mens Red"
if(13) underwear = "Nude"
else
switch(underwear)
if(1) underwear = "Ladies Red"
if(2) underwear = "Ladies White"
if(3) underwear = "Ladies Yellow"
if(4) underwear = "Ladies Blue"
if(5) underwear = "Ladies Black"
if(6) underwear = "Ladies Thong"
if(7) underwear = "Babydoll"
if(8) underwear = "Ladies Baby-Blue"
if(9) underwear = "Ladies Green"
if(10) underwear = "Ladies Pink"
if(11) underwear = "Ladies Kinky"
if(12) underwear = "Tankini"
if(13) underwear = "Nude"
/*
if(current_version < 10) //would be the step to upgrade 9 to 10
//do stuff
if(current_version < 11) //and so on...
//more stuff
*/
return
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey) return
path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]"
savefile_version = SAVEFILE_VERSION_MAX
/datum/preferences/proc/load_preferences()
if(!path) return 0
if(!fexists(path)) return 0
var/savefile/S = new /savefile(path)
if(!S) return 0
S.cd = "/"
S["version"] >> savefile_version
//Conversion
if(!savefile_version || !isnum(savefile_version) || savefile_version < SAVEFILE_VERSION_MIN || savefile_version > SAVEFILE_VERSION_MAX)
if(!savefile_update()) //handles updates
savefile_version = SAVEFILE_VERSION_MAX
save_preferences()
save_character()
return 0
var/needs_update = savefile_needs_update(S)
if(needs_update == -2) //fatal, can't load any data
return 0
//general preferences
S["ooccolor"] >> ooccolor
@@ -55,6 +112,10 @@
S["default_slot"] >> default_slot
S["toggles"] >> toggles
//try to fix any outdated data if necessary
if(needs_update >= 0)
update_preferences(needs_update) //needs_update = savefile_version if we need an update (positive integer)
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
@@ -71,7 +132,7 @@
if(!S) return 0
S.cd = "/"
S["version"] << savefile_version
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
S["ooccolor"] << ooccolor
@@ -94,7 +155,11 @@
if(slot != default_slot)
default_slot = slot
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 0
//Character
S["OOC_Notes"] >> metadata
@@ -130,12 +195,24 @@
S["job_engsec_med"] >> job_engsec_med
S["job_engsec_low"] >> job_engsec_low
//try to fix any outdated data if necessary
if(needs_update >= 0)
update_character(needs_update) //needs_update == savefile_version if we need an update (positive integer)
//Sanitize
metadata = sanitize_text(metadata, initial(metadata))
real_name = reject_bad_name(real_name)
if(!real_name) real_name = random_name()
if(!real_name) real_name = random_name(gender)
be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
gender = sanitize_gender(gender)
if(gender == MALE)
h_style = sanitize_inlist(h_style, hair_styles_male_list)
f_style = sanitize_inlist(f_style, facial_hair_styles_male_list)
underwear = sanitize_inlist(underwear, underwear_m)
else
h_style = sanitize_inlist(h_style, hair_styles_female_list)
f_style = sanitize_inlist(f_style, facial_hair_styles_female_list)
underwear = sanitize_inlist(underwear, underwear_f)
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
r_hair = sanitize_integer(r_hair, 0, 255, initial(r_hair))
g_hair = sanitize_integer(g_hair, 0, 255, initial(g_hair))
@@ -144,12 +221,9 @@
g_facial = sanitize_integer(g_facial, 0, 255, initial(g_facial))
b_facial = sanitize_integer(b_facial, 0, 255, initial(b_facial))
s_tone = sanitize_integer(s_tone, -185, 34, initial(s_tone))
h_style = sanitize_inlist(h_style, hair_styles_list, initial(h_style))
f_style = sanitize_inlist(f_style, facial_hair_styles_list, initial(f_style))
r_eyes = sanitize_integer(r_eyes, 0, 255, initial(r_eyes))
g_eyes = sanitize_integer(g_eyes, 0, 255, initial(g_eyes))
b_eyes = sanitize_integer(b_eyes, 0, 255, initial(b_eyes))
underwear = sanitize_integer(underwear, 1, underwear_m.len, initial(underwear))
backbag = sanitize_integer(backbag, 1, backbaglist.len, initial(backbag))
userandomjob = sanitize_integer(userandomjob, 0, 1, initial(userandomjob))
@@ -170,6 +244,8 @@
var/savefile/S = new /savefile(path)
if(!S) return 0
S.cd = "/character[default_slot]"
S["version"] << SAVEFILE_VERSION_MAX //load_character will sanitize any bad data, so assume up-to-date.
//Character
S["OOC_Notes"] << metadata