mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-20 11:07:14 +01:00
319 lines
13 KiB
Plaintext
319 lines
13 KiB
Plaintext
/**
|
|
* @params
|
|
* - S - the savefile we operate on
|
|
* - current_version - the version it's at
|
|
* - errors - put errors into this list to be displayed to the player
|
|
* - options - the loaded global options list
|
|
* - prefs - prefs if any; if null, we're probably doing a clientless migration.
|
|
*/
|
|
/datum/controller/subsystem/characters/proc/perform_global_migrations(savefile/S, current_version, list/errors, list/options, datum/preferences/prefs)
|
|
return
|
|
|
|
/**
|
|
* @params
|
|
* - S - the savefile we operate on
|
|
* - current_version - the version it's at
|
|
* - errors - put errors into this list to be displayed to the player
|
|
* - options - the loaded character options list
|
|
* - prefs - prefs if any; if null, we're probably doing a clientless migration.
|
|
*/
|
|
/datum/controller/subsystem/characters/proc/perform_character_migrations(savefile/S, current_version, list/errors, list/character, datum/preferences/prefs)
|
|
if(current_version < 1)
|
|
// MIGRATE JOBS
|
|
var/alternative_option
|
|
var/job_civilian_high
|
|
var/job_civilian_med
|
|
var/job_civilian_low
|
|
var/job_medsci_high
|
|
var/job_medsci_low
|
|
var/job_medsci_med
|
|
var/job_talon_high
|
|
var/job_talon_low
|
|
var/job_talon_med
|
|
var/job_engsec_high
|
|
var/job_engsec_med
|
|
var/job_engsec_low
|
|
var/list/player_alt_titles
|
|
READ_FILE(S["alternate_option"], alternative_option)
|
|
READ_FILE(S["job_civilian_high"], job_civilian_high)
|
|
READ_FILE(S["job_civilian_med"], job_civilian_med)
|
|
READ_FILE(S["job_civilian_low"], job_civilian_low)
|
|
READ_FILE(S["job_medsci_high"], job_medsci_high)
|
|
READ_FILE(S["job_medsci_med"], job_medsci_med)
|
|
READ_FILE(S["job_medsci_low"], job_medsci_low)
|
|
READ_FILE(S["job_engsec_high"], job_engsec_high)
|
|
READ_FILE(S["job_engsec_med"], job_engsec_med)
|
|
READ_FILE(S["job_engsec_low"], job_engsec_low)
|
|
READ_FILE(S["job_talon_low"], job_talon_low)
|
|
READ_FILE(S["job_talon_med"], job_talon_med)
|
|
READ_FILE(S["job_talon_high"], job_talon_high)
|
|
READ_FILE(S["player_alt_titles"], player_alt_titles)
|
|
var/list/assembled = list()
|
|
var/list/assembled_titles = list()
|
|
if(!islist(player_alt_titles))
|
|
player_alt_titles = list()
|
|
for(var/datum/prototype/role/job/J as anything in RSroles.legacy_all_job_datums())
|
|
switch(J.department_flag)
|
|
if(CIVILIAN)
|
|
if(job_civilian_high & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_HIGH
|
|
else if(job_civilian_med & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_MEDIUM
|
|
else if(job_civilian_low & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_LOW
|
|
if(MEDSCI)
|
|
if(job_medsci_high & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_HIGH
|
|
else if(job_medsci_med & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_MEDIUM
|
|
else if(job_medsci_low & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_LOW
|
|
if(ENGSEC)
|
|
if(job_engsec_high & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_HIGH
|
|
else if(job_engsec_med & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_MEDIUM
|
|
else if(job_engsec_low & J.flag)
|
|
assembled[J.id] = JOB_PRIORITY_LOW
|
|
if(player_alt_titles[J.title])
|
|
assembled_titles[J.id] = player_alt_titles[J.title]
|
|
var/overflow
|
|
switch(alternative_option)
|
|
if(0)
|
|
overflow = JOB_ALTERNATIVE_GET_RANDOM
|
|
if(1)
|
|
overflow = JOB_ALTERNATIVE_BE_ASSISTANT
|
|
if(2)
|
|
overflow = JOB_ALTERNATIVE_RETURN_LOBBY
|
|
character[CHARACTER_DATA_JOBS] = assembled
|
|
character[CHARACTER_DATA_OVERFLOW_MODE] = overflow
|
|
character[CHARACTER_DATA_ALT_TITLES] = assembled_titles
|
|
// BACKGROUNDS: NOT MIGRATED
|
|
// MIGRATE SPECIES
|
|
var/name_species
|
|
S["species"] >> name_species
|
|
var/datum/species/RS = SScharacters.resolve_species_name(name_species)
|
|
if(!RS)
|
|
if(name_species) // if they had any at all
|
|
errors?.Add(SPAN_WARNING("Species reset to human - no species found of old species name ([name_species])"))
|
|
RS = SScharacters.resolve_species_path(/datum/species/human)
|
|
// todo: default species?
|
|
if(RS)
|
|
character[CHARACTER_DATA_REAL_SPECIES] = RS.uid
|
|
character[CHARACTER_DATA_CHAR_SPECIES] = RS.uid
|
|
else
|
|
errors?.Add(SPAN_DANGER("Species migration failed - no species datum. Report this to a coder."))
|
|
// GRAB CHARACTER SPECIES - WE'LL NEED IT
|
|
var/datum/species/CS = SScharacters.resolve_character_species(RS.uid)
|
|
// MIGRATE LANGUAGES
|
|
var/list/alternate_languages
|
|
S["language"] >> alternate_languages
|
|
if(!islist(alternate_languages))
|
|
alternate_languages = list()
|
|
var/list/translated_languages = list()
|
|
character[CHARACTER_DATA_LANGUAGES] = translated_languages
|
|
var/list/innate = CS.get_intrinsic_language_ids()
|
|
for(var/name in alternate_languages)
|
|
var/datum/prototype/language/L = RSlanguages.legacy_resolve_language_name(name)
|
|
if(L.id in innate)
|
|
continue
|
|
translated_languages += L.id
|
|
translated_languages.len = clamp(translated_languages.len, 0, CS.get_max_additional_languages())
|
|
if(current_version < 2)
|
|
character -= GLOBAL_DATA_LANGUAGE_PREFIX
|
|
if(current_version < 3)
|
|
// hair_style_name
|
|
// facial_style_name
|
|
// ear_style (type)
|
|
// horn_style (type)
|
|
// tail_style (type)
|
|
// wing_style (type)
|
|
// body_markings (list[name = color])
|
|
//! get
|
|
var/old_hname
|
|
var/old_fname
|
|
var/old_etype
|
|
var/old_htype
|
|
var/old_ttype
|
|
var/old_wtype
|
|
var/old_mnames_colors
|
|
READ_FILE(S["hair_style_name"], old_hname)
|
|
READ_FILE(S["facial_style_name"], old_fname)
|
|
READ_FILE(S["ear_style"], old_etype)
|
|
READ_FILE(S["horn_style"], old_htype)
|
|
READ_FILE(S["wing_style"], old_wtype)
|
|
READ_FILE(S["tail_style"], old_ttype)
|
|
READ_FILE(S["body_markings"], old_mnames_colors)
|
|
old_mnames_colors = sanitize_islist(old_mnames_colors)
|
|
//! gather
|
|
var/datum/sprite_accessory/resolved_hair = GLOB.legacy_hair_lookup[old_hname]
|
|
var/datum/sprite_accessory/resolved_facial = GLOB.legacy_facial_hair_lookup[old_fname]
|
|
var/datum/sprite_accessory/resolved_ears = GLOB.legacy_ears_lookup[old_etype]
|
|
var/datum/sprite_accessory/resolved_horns = GLOB.legacy_ears_lookup[old_htype]
|
|
var/datum/sprite_accessory/resolved_tail = GLOB.legacy_tail_lookup[old_ttype]
|
|
var/datum/sprite_accessory/resolved_wings = GLOB.legacy_wing_lookup[old_wtype]
|
|
var/list/resolved_markings = list()
|
|
for(var/name in old_mnames_colors)
|
|
var/datum/sprite_accessory/resolved_marking = GLOB.legacy_marking_lookup[name]
|
|
if(!resolved_marking)
|
|
continue
|
|
resolved_markings[resolved_marking.id] = old_mnames_colors[name]
|
|
//! write
|
|
WRITE_FILE(S["hair_style"], resolved_hair?.id)
|
|
WRITE_FILE(S["facial_style"], resolved_facial?.id)
|
|
WRITE_FILE(S["ear_style_id"], resolved_ears?.id)
|
|
WRITE_FILE(S["wing_style_id"], resolved_wings?.id)
|
|
WRITE_FILE(S["tail_style_id"], resolved_tail?.id)
|
|
WRITE_FILE(S["horn_style_id"], resolved_horns?.id)
|
|
WRITE_FILE(S["body_marking_ids"], resolved_markings)
|
|
if (current_version < 4)
|
|
var/old_species = character[CHARACTER_DATA_REAL_SPECIES]
|
|
var/datum/species/RS
|
|
switch (old_species)
|
|
if(SPECIES_ID_ZORREN_FLAT)
|
|
errors?.Add(SPAN_WARNING("[old_species] has been removed - Character species changed to custom species, icon base [SPECIES_ZORREN_FLAT]"))
|
|
RS = SScharacters.resolve_species_path(/datum/species/custom)
|
|
WRITE_FILE(S["custom_base"], SPECIES_ZORREN_FLAT)
|
|
if(SPECIES_ID_ZORREN_HIGH)
|
|
errors?.Add(SPAN_WARNING("[old_species] has been removed - Character species changed to custom species, icon base [SPECIES_ZORREN_HIGH]"))
|
|
RS = SScharacters.resolve_species_path(/datum/species/custom)
|
|
WRITE_FILE(S["custom_base"], SPECIES_ZORREN_HIGH)
|
|
if(SPECIES_ID_NEVREAN)
|
|
errors?.Add(SPAN_WARNING("[old_species] has been removed - Character species changed to custom species, icon base [SPECIES_NEVREAN]"))
|
|
RS = SScharacters.resolve_species_path(/datum/species/custom)
|
|
WRITE_FILE(S["custom_base"], SPECIES_NEVREAN)
|
|
else
|
|
RS = SScharacters.resolve_species_id(old_species)
|
|
if(!RS)
|
|
errors?.Add(SPAN_WARNING("Species reset to human - no species found of old species name ([old_species])"))
|
|
RS = SScharacters.resolve_species_path(/datum/species/human)
|
|
|
|
if(RS)
|
|
character[CHARACTER_DATA_REAL_SPECIES] = RS.uid
|
|
character[CHARACTER_DATA_CHAR_SPECIES] = RS.uid
|
|
else
|
|
errors?.Add(SPAN_DANGER("Species migration failed - no species datum. Report this to a coder."))
|
|
if(current_version < 5)
|
|
var/gear_slot
|
|
READ_FILE(S["gear_slot"], gear_slot)
|
|
if(isnum(gear_slot))
|
|
character[CHARACTER_DATA_LOADOUT_SLOT] = sanitize_integer(gear_slot, 1, LOADOUT_MAX_SLOTS, 1)
|
|
var/list/gear_data
|
|
READ_FILE(S["gear_list"], gear_data)
|
|
var/list/translated_slots = LAZYGETLIST(character[CHARACTER_DATA_LOADOUT])
|
|
if(islist(gear_data))
|
|
for(var/i in 1 to LOADOUT_MAX_SLOTS)
|
|
var/list/data = gear_data["[i]"]
|
|
if(!islist(data))
|
|
continue
|
|
var/list/translated_slot = LAZYGETLIST(translated_slots["[i]"])
|
|
var/list/translated_entries = LAZYGETLIST(translated_slot[LOADOUT_SLOTDATA_ENTRIES])
|
|
for(var/name in data)
|
|
var/list/assembled = list()
|
|
var/datum/loadout_entry/entry = global.gear_datums[name]
|
|
if(isnull(entry))
|
|
errors?.Add("unable to translate loadout slot-entry [i]-[name]: name not found.")
|
|
continue
|
|
var/list/old_tweaks = data[name]
|
|
for(var/old_tweak in old_tweaks)
|
|
if(!old_tweaks[old_tweak])
|
|
continue
|
|
switch(old_tweak)
|
|
if("/datum/gear_tweak/custom_name")
|
|
assembled[LOADOUT_ENTRYDATA_RENAME] = old_tweaks[old_tweak]
|
|
if("/datum/gear_tweak/custom_desc")
|
|
assembled[LOADOUT_ENTRYDATA_REDESC] = old_tweaks[old_tweak]
|
|
if("/datum/gear_tweak/color", "/datum/gear_tweak/matrix_recolor")
|
|
assembled[LOADOUT_ENTRYDATA_RECOLOR] = old_tweaks[old_tweak]
|
|
else
|
|
var/replaced = old_tweak
|
|
if(findtext(replaced, "gear_tweak"))
|
|
replaced = replacetext(replaced, "gear_tweak", "loadout_tweak")
|
|
LAZYSET(assembled[LOADOUT_ENTRYDATA_TWEAKS], replaced, old_tweaks[old_tweak])
|
|
translated_entries[entry.legacy_get_id()] = assembled
|
|
var/all_underwear_metadata
|
|
READ_FILE(S["all_underwear_metadata"], all_underwear_metadata)
|
|
for(var/category in all_underwear_metadata)
|
|
var/list/catlist = all_underwear_metadata[category]
|
|
if(!islist(catlist))
|
|
continue
|
|
for(var/key in catlist)
|
|
if(key != "/datum/gear_tweak/color")
|
|
continue
|
|
var/val = catlist[key]
|
|
catlist -= key
|
|
catlist["/datum/loadout_tweak/color"] = val
|
|
WRITE_FILE(S["all_underwear_metadata"], all_underwear_metadata)
|
|
if(current_version < 6)
|
|
if(character[CHARACTER_DATA_REAL_SPECIES] == "blackeyedshadekin")
|
|
character[CHARACTER_DATA_REAL_SPECIES] = "shadekin_blackeyed"
|
|
if(character[CHARACTER_DATA_CHAR_SPECIES] == "blackeyedshadekin")
|
|
character[CHARACTER_DATA_CHAR_SPECIES] = "shadekin_blackeyed"
|
|
|
|
/**
|
|
* clientless migration of savefiles
|
|
*/
|
|
/datum/controller/subsystem/characters/proc/migrate_savefile(path, list/errors = list())
|
|
var/savefile/S
|
|
if(istype(path, /savefile))
|
|
S = path
|
|
else
|
|
S = new(path)
|
|
// perform global - we should be on / when migrations fire
|
|
S.cd = "/"
|
|
var/global_version
|
|
READ_FILE(S["version"], global_version)
|
|
if(!isnum(global_version))
|
|
// no version? assume we don't have one.
|
|
global_version = SAVEFILE_VERSION_MAX
|
|
var/list/global_data
|
|
READ_FILE(S["global"], global_data)
|
|
global_data = sanitize_islist(global_data)
|
|
perform_global_migrations(S, global_version, errors, global_data, null)
|
|
// save global
|
|
global_version = SAVEFILE_VERSION_MAX
|
|
WRITE_FILE(S["version"], global_version)
|
|
WRITE_FILE(S["global"], global_data)
|
|
// migrate all slots
|
|
for(var/i in 1 to config_legacy.character_slots)
|
|
// perform character slot migrations - we should be on /character[slot] when migrations fire
|
|
S.cd = "/"
|
|
var/list/character_data
|
|
var/character_version
|
|
READ_FILE(S["slot_[i]"], character_data)
|
|
character_data = sanitize_islist(character_data)
|
|
character_version = character_data[CHARACTER_DATA_VERSION]
|
|
if(!isnum(character_version))
|
|
// no version? assume it's a legacy file as all new files should have one.
|
|
character_version = CHARACTER_VERSION_LEGACY
|
|
S.cd = "/character[i]"
|
|
perform_character_migrations(S, character_version, errors, character_data, null)
|
|
character_data[CHARACTER_DATA_VERSION] = CHARACTER_VERSION_MAX
|
|
// save character
|
|
S.cd = "/"
|
|
WRITE_FILE(S["slot_[i]"], character_data)
|
|
|
|
// the oh god oh fuck proc that migrates every file at once
|
|
/*
|
|
/proc/___migrate_all_savefiles()
|
|
// oh fuck you
|
|
. = 0
|
|
var/timer = REALTIMEOFDAY
|
|
to_chat(world, SPAN_DANGER("Migrating all savefiles. Please do not attempt to use character setup, lest you risk data loss."))
|
|
for(var/path in directory_walk(list("data/player_saves/")))
|
|
var/list/split = splittext(path, "/")
|
|
if(!split.len || (split[split.len] != "preferences.sav"))
|
|
continue
|
|
var/list/what_fucked_up = list()
|
|
SScharacters.migrate_savefile(path, what_fucked_up)
|
|
++.
|
|
if(!(. % 100))
|
|
to_chat(world, SPAN_DANGER("Count: [.]"))
|
|
if(length(what_fucked_up))
|
|
SScharacters.subsystem_log("Migration errored for [path]:\n\t[jointext(what_fucked_up, "\n\t")]")
|
|
CHECK_TICK
|
|
var/took = REALTIMEOFDAY - timer
|
|
to_chat(world, SPAN_DANGER("Migrated [.] savefiles in [round(took * 0.1, 0.1)] seconds."))
|
|
*/
|
|
|