mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-25 17:12:12 +00:00
* Trimming the fat * Trimming the fat * Update mushpeople.dm * Adds colorblindness as a mild brain trauma (#76527) What the title says. The brain trauma makes the whole screen monochrome until cured.  I feel like the current pool for mild brain traumas is quite lame, this helps spice it up a bit with something that is quite annoying and distracting but not game breaking (as mild brain traumas should generally be). 🆑 add: Added colorblindness as a mild brain trauma. /🆑 --------- Co-authored-by: san7890 <the@san7890.com> * Fix species `var/hair_color` not being used for, well, hair color (#82168) `var/hair_color` for species was intended to be used as a "this species uses this type of hair color thing" But at some point that got completely lost and now it's only used for sprite accessories This fixes that. That means Slimepeople now have properly slimey hair. And Ethereals are less snowflake once more. 🆑 Melbert fix: Fixed Slimepeople's hair not matching their slimey colors. /🆑 * Revert "Fix species `var/hair_color` not being used for, well, hair color (#82168)" This reverts commit c4cb756. * Revert "Adds colorblindness as a mild brain trauma (#76527)" This reverts commit eb815f5. * Update _species.dm * unused var * Caps list.. * Update mushpeople.dm * Update mushpeople.dm * Update mushpeople.dm * Update mushpeople.dm * Update mushpeople.dm * Attempts to fix CI errors * Update cap.dm * Update _external_organ.dm * Update monkey.dm * Revert "Update monkey.dm" This reverts commit 29f54c8. * Revert "Update _external_organ.dm" This reverts commit 8de5ea7. * Update _external_organ.dm * Revert "Update _external_organ.dm" This reverts commit 644cc56. * Fix CI maybe? * Update cap.dm * Update DNA.dm * Some cleanup/updating to upstream * Update global_lists.dm * Mush * Update mushpeople.dm * Hopefully the last fix * Doing this differently * Update organ.dm * Update organ.dm * Update organ.dm * Update organ.dm * Update organ.dm * OK * Update organ.dm * Update podpeople.dm * maybe * Hm * Hm * Will this break things? * Revert "Will this break things?" This reverts commit bd288c6. * Test * Update organ.dm * Update organ.dm * Revert "Update organ.dm" This reverts commit ca77ff9. * Update organ.dm * . * . * . * Update snail.dm * Monkeys now use height offset (and monkey tail works) (#81598) This PR adds the ability for monkeys to wear any jumpsuit in the game, and adds support for them to wear things like coats, gloves, and shoes (though this cannot be obtained in-game and is solely achieved through admins, which I also improved a bit upon by adding a defined bitfield for no equip flags). This reverts a lot of changes from tgstation/tgstation#73325 - We no longer check height from limbs and such to bring sprites down, instead monkeys now work more similarly to humans, so the entire PR was made irrelevant, and I didn't really want to leave around dead code for the sake of having a human with longer legs. I've now also added support for Dwarfism, which makes monkeys look even smaller. Very minor change but at least now the mutation doesn't feel like it does literally nothing to monkeys (since they can already walk over tables). Here's a few examples of how it can appear in game (purely for demonstration, as it is currently intentionally made impossible to obtain in-game, though if someone wants to change that post-this PR now that support is added, feel free): Tails have been broken for a while now, the only reason you see them in-game is because they are baked into the monkey sprites. This fixes that, which means humans can now get monkey tails implanted into them (hell yeah) and monkeys can have their tails removed (also hell yeah) * Removes unneeded files * Revert "Removes unneeded files" This reverts commit 6469d37. * . * ok * Update tails.dm * Update monkey.dm * Fix monkey screenshot test * Update species.dm * Update reinf_walls.dm * Maintenance --------- Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com> Co-authored-by: Mal <13398309+vinylspiders@users.noreply.github.com> Co-authored-by: ChungusGamer666 <82850673+ChungusGamer666@users.noreply.github.com> Co-authored-by: san7890 <the@san7890.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: vinylspiders <13398309+vinylspiders@users.noreply.github.com>
385 lines
14 KiB
Plaintext
385 lines
14 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 32
|
|
|
|
//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 44
|
|
|
|
/*
|
|
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/save_data_needs_update(list/save_data)
|
|
if(!save_data) // empty list, either savefile isnt loaded or its a new char
|
|
return -1
|
|
if(save_data["version"] < SAVEFILE_VERSION_MIN)
|
|
return -2
|
|
if(save_data["version"] < SAVEFILE_VERSION_MAX)
|
|
return save_data["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, datum/json_savefile/S)
|
|
if(current_version < 34)
|
|
write_preference(/datum/preference/toggle/auto_fit_viewport, TRUE)
|
|
|
|
if(current_version < 35) //makes old keybinds compatible with #52040, sets the new default
|
|
var/newkey = FALSE
|
|
for(var/list/key in key_bindings)
|
|
for(var/bind in key)
|
|
if(bind == "quick_equipbelt")
|
|
key -= "quick_equipbelt"
|
|
key |= "quick_equip_belt"
|
|
|
|
if(bind == "bag_equip")
|
|
key -= "bag_equip"
|
|
key |= "quick_equip_bag"
|
|
|
|
if(bind == "quick_equip_suit_storage")
|
|
newkey = TRUE
|
|
if(!newkey && !key_bindings["ShiftQ"])
|
|
key_bindings["ShiftQ"] = list("quick_equip_suit_storage")
|
|
|
|
if(current_version < 36)
|
|
if(key_bindings["ShiftQ"] == "quick_equip_suit_storage")
|
|
key_bindings["ShiftQ"] = list("quick_equip_suit_storage")
|
|
|
|
if(current_version < 37)
|
|
if(read_preference(/datum/preference/numeric/fps) == 0)
|
|
write_preference(GLOB.preference_entries[/datum/preference/numeric/fps], -1)
|
|
|
|
if (current_version < 38)
|
|
var/found_block_movement = FALSE
|
|
|
|
for (var/list/key in key_bindings)
|
|
for (var/bind in key)
|
|
if (bind == "block_movement")
|
|
found_block_movement = TRUE
|
|
break
|
|
if (found_block_movement)
|
|
break
|
|
|
|
if (!found_block_movement)
|
|
LAZYADD(key_bindings["Ctrl"], "block_movement")
|
|
|
|
if (current_version < 39)
|
|
LAZYADD(key_bindings["F"], "toggle_combat_mode")
|
|
LAZYADD(key_bindings["4"], "toggle_combat_mode")
|
|
if (current_version < 40)
|
|
LAZYADD(key_bindings["Space"], "hold_throw_mode")
|
|
|
|
if (current_version < 41)
|
|
migrate_preferences_to_tgui_prefs_menu()
|
|
|
|
if (current_version < 44)
|
|
update_tts_blip_prefs()
|
|
|
|
/datum/preferences/proc/update_character(current_version, list/save_data)
|
|
if (current_version < 41)
|
|
migrate_character_to_tgui_prefs_menu()
|
|
|
|
if (current_version < 42)
|
|
// migrate_body_types(save_data) // SKYRAT EDIT - This'll fuck up savefiles
|
|
migrate_mentor() // SKYRAT EDIT - Make mentors alive again
|
|
|
|
if (current_version < 43)
|
|
migrate_legacy_sound_toggles(savefile)
|
|
|
|
/// checks through keybindings for outdated unbound keys and updates them
|
|
/datum/preferences/proc/check_keybindings()
|
|
if(!parent)
|
|
return
|
|
var/list/binds_by_key = get_key_bindings_by_key(key_bindings)
|
|
var/list/notadded = list()
|
|
for (var/name in GLOB.keybindings_by_name)
|
|
var/datum/keybinding/kb = GLOB.keybindings_by_name[name]
|
|
if(kb.name in key_bindings)
|
|
continue // key is unbound and or bound to something
|
|
|
|
var/addedbind = FALSE
|
|
key_bindings[kb.name] = list()
|
|
|
|
if(parent.hotkeys)
|
|
for(var/hotkeytobind in kb.hotkey_keys)
|
|
if(hotkeytobind == "Unbound")
|
|
addedbind = TRUE
|
|
else if(!length(binds_by_key[hotkeytobind])) //Only bind to the key if nothing else is bound
|
|
key_bindings[kb.name] |= hotkeytobind
|
|
addedbind = TRUE
|
|
else
|
|
for(var/classickeytobind in kb.classic_keys)
|
|
if(classickeytobind == "Unbound")
|
|
addedbind = TRUE
|
|
else if(!length(binds_by_key[classickeytobind])) //Only bind to the key if nothing else is bound
|
|
key_bindings[kb.name] |= classickeytobind
|
|
addedbind = TRUE
|
|
|
|
if(!addedbind)
|
|
notadded += kb
|
|
save_preferences() //Save the players pref so that new keys that were set to Unbound as default are permanently stored
|
|
if(length(notadded))
|
|
addtimer(CALLBACK(src, PROC_REF(announce_conflict), notadded), 5 SECONDS)
|
|
|
|
/datum/preferences/proc/announce_conflict(list/notadded)
|
|
to_chat(parent, "<span class='warningplain'><b><u>Keybinding Conflict</u></b></span>\n\
|
|
<span class='warningplain'><b>There are new <a href='?src=[REF(src)];open_keybindings=1'>keybindings</a> that default to keys you've already bound. The new ones will be unbound.</b></span>")
|
|
for(var/item in notadded)
|
|
var/datum/keybinding/conflicted = item
|
|
to_chat(parent, span_danger("[conflicted.category]: [conflicted.full_name] needs updating"))
|
|
|
|
/datum/preferences/proc/load_path(ckey, filename="preferences.json")
|
|
if(!ckey || !load_and_save)
|
|
return
|
|
path = "data/player_saves/[ckey[1]]/[ckey]/[filename]"
|
|
|
|
/datum/preferences/proc/load_savefile()
|
|
if(load_and_save && !path)
|
|
CRASH("Attempted to load savefile without first loading a path!")
|
|
savefile = new /datum/json_savefile(load_and_save ? path : null)
|
|
|
|
/datum/preferences/proc/load_preferences()
|
|
if(!savefile)
|
|
stack_trace("Attempted to load the preferences of [parent] without a savefile; did you forget to call load_savefile?")
|
|
load_savefile()
|
|
if(!savefile)
|
|
stack_trace("Failed to load the savefile for [parent] after manually calling load_savefile; something is very wrong.")
|
|
return FALSE
|
|
|
|
var/needs_update = save_data_needs_update(savefile.get_entry())
|
|
if(load_and_save && (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(savefile.path, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
|
return FALSE
|
|
|
|
apply_all_client_preferences()
|
|
|
|
//general preferences
|
|
lastchangelog = savefile.get_entry("lastchangelog")
|
|
be_special = savefile.get_entry("be_special")
|
|
default_slot = savefile.get_entry("default_slot")
|
|
chat_toggles = savefile.get_entry("chat_toggles")
|
|
toggles = savefile.get_entry("toggles")
|
|
ignoring = savefile.get_entry("ignoring")
|
|
|
|
// OOC commendations
|
|
hearted_until = savefile.get_entry("hearted_until")
|
|
if(hearted_until > world.realtime)
|
|
hearted = TRUE
|
|
//favorite outfits
|
|
favorite_outfits = savefile.get_entry("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 = unique_list(parsed_favs)
|
|
|
|
// Custom hotkeys
|
|
key_bindings = savefile.get_entry("key_bindings")
|
|
|
|
//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(savefile.path, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
|
update_preferences(needs_update, savefile) //needs_update = savefile_version if we need an update (positive integer)
|
|
|
|
check_keybindings() // this apparently fails every time and overwrites any unloaded prefs with the default values, so don't load anything after this line or it won't actually save
|
|
key_bindings_by_key = get_key_bindings_by_key(key_bindings)
|
|
|
|
//Sanitize
|
|
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
|
|
default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot))
|
|
toggles = sanitize_integer(toggles, 0, SHORT_REAL_LIMIT-1, initial(toggles))
|
|
be_special = sanitize_be_special(SANITIZE_LIST(be_special))
|
|
key_bindings = sanitize_keybindings(key_bindings)
|
|
favorite_outfits = SANITIZE_LIST(favorite_outfits)
|
|
|
|
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 savefile.get_entry()) //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())
|
|
save_character()
|
|
default_slot = old_default_slot
|
|
max_save_slots = old_max_save_slots
|
|
save_preferences()
|
|
|
|
return TRUE
|
|
|
|
/datum/preferences/proc/save_preferences()
|
|
if(!savefile)
|
|
CRASH("Attempted to save the preferences of [parent] without a savefile. This should have been handled by load_preferences()")
|
|
savefile.set_entry("version", SAVEFILE_VERSION_MAX) //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date
|
|
|
|
for (var/preference_type in GLOB.preference_entries)
|
|
var/datum/preference/preference = GLOB.preference_entries[preference_type]
|
|
if (preference.savefile_identifier != PREFERENCE_PLAYER)
|
|
continue
|
|
|
|
if (!(preference.type in recently_updated_keys))
|
|
continue
|
|
|
|
recently_updated_keys -= preference.type
|
|
|
|
if (preference_type in value_cache)
|
|
write_preference(preference, preference.serialize(value_cache[preference_type]))
|
|
|
|
savefile.set_entry("lastchangelog", lastchangelog)
|
|
savefile.set_entry("be_special", be_special)
|
|
savefile.set_entry("default_slot", default_slot)
|
|
savefile.set_entry("toggles", toggles)
|
|
savefile.set_entry("chat_toggles", chat_toggles)
|
|
savefile.set_entry("ignoring", ignoring)
|
|
savefile.set_entry("key_bindings", key_bindings)
|
|
savefile.set_entry("hearted_until", (hearted_until > world.realtime ? hearted_until : null))
|
|
savefile.set_entry("favorite_outfits", favorite_outfits)
|
|
savefile.save()
|
|
return TRUE
|
|
|
|
/datum/preferences/proc/load_character(slot)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
if(!slot)
|
|
slot = default_slot
|
|
slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
|
|
if(slot != default_slot)
|
|
default_slot = slot
|
|
savefile.set_entry("default_slot", slot)
|
|
|
|
var/tree_key = "character[slot]"
|
|
var/list/save_data = savefile.get_entry(tree_key)
|
|
var/needs_update = save_data_needs_update(save_data)
|
|
if(needs_update == -2) //fatal, can't load any data
|
|
return FALSE
|
|
|
|
// Read everything into cache
|
|
for (var/preference_type in GLOB.preference_entries)
|
|
var/datum/preference/preference = GLOB.preference_entries[preference_type]
|
|
if (preference.savefile_identifier != PREFERENCE_CHARACTER)
|
|
continue
|
|
|
|
value_cache -= preference_type
|
|
read_preference(preference_type)
|
|
|
|
//Character
|
|
randomise = save_data?["randomise"]
|
|
|
|
//Load prefs
|
|
job_preferences = save_data?["job_preferences"]
|
|
|
|
//Quirks
|
|
all_quirks = save_data?["all_quirks"]
|
|
load_character_skyrat(save_data) // SKYRAT EDIT ADDITION
|
|
|
|
//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, save_data) //needs_update == savefile_version if we need an update (positive integer)
|
|
|
|
//Sanitize
|
|
randomise = SANITIZE_LIST(randomise)
|
|
job_preferences = SANITIZE_LIST(job_preferences)
|
|
all_quirks = SANITIZE_LIST(all_quirks)
|
|
|
|
//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 = SSquirks.filter_invalid_quirks(SANITIZE_LIST(all_quirks), augments)// SKYRAT EDIT - AUGMENTS+
|
|
validate_quirks()
|
|
|
|
return TRUE
|
|
|
|
/datum/preferences/proc/save_character()
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
if(!path)
|
|
return FALSE
|
|
var/tree_key = "character[default_slot]"
|
|
if(!(tree_key in savefile.get_entry()))
|
|
savefile.set_entry(tree_key, list())
|
|
var/save_data = savefile.get_entry(tree_key)
|
|
|
|
for (var/datum/preference/preference as anything in get_preferences_in_priority_order())
|
|
if (preference.savefile_identifier != PREFERENCE_CHARACTER)
|
|
continue
|
|
|
|
if (!(preference.type in recently_updated_keys))
|
|
continue
|
|
|
|
recently_updated_keys -= preference.type
|
|
|
|
if (preference.type in value_cache)
|
|
write_preference(preference, preference.serialize(value_cache[preference.type]))
|
|
|
|
save_data["version"] = SAVEFILE_VERSION_MAX //load_character will sanitize any bad data, so assume up-to-date.
|
|
|
|
// This is the version when the random security department was removed.
|
|
// When the minimum is higher than that version, it's impossible for someone to have the "Random" department.
|
|
#if SAVEFILE_VERSION_MIN > 40
|
|
#warn The prefered_security_department check in code/modules/client/preferences/security_department.dm is no longer necessary.
|
|
#endif
|
|
|
|
//Character
|
|
save_data["randomise"] = randomise
|
|
|
|
//Write prefs
|
|
save_data["job_preferences"] = job_preferences
|
|
|
|
//Quirks
|
|
save_data["all_quirks"] = all_quirks
|
|
save_character_skyrat(save_data) // SKYRAT EDIT ADDITION
|
|
|
|
return TRUE
|
|
|
|
/datum/preferences/proc/sanitize_be_special(list/input_be_special)
|
|
var/list/output = list()
|
|
|
|
for (var/role in input_be_special)
|
|
if (role in GLOB.special_roles)
|
|
output += role
|
|
|
|
return output.len == input_be_special.len ? input_be_special : output
|
|
|
|
/proc/sanitize_keybindings(value)
|
|
var/list/base_bindings = sanitize_islist(value,list())
|
|
for(var/keybind_name in base_bindings)
|
|
if (!(keybind_name in GLOB.keybindings_by_name))
|
|
base_bindings -= keybind_name
|
|
return base_bindings
|
|
|
|
#undef SAVEFILE_VERSION_MAX
|
|
#undef SAVEFILE_VERSION_MIN
|