diff --git a/code/__defines/preferences.dm b/code/__defines/preferences.dm index 9e6725d17b..2245a06c05 100644 --- a/code/__defines/preferences.dm +++ b/code/__defines/preferences.dm @@ -62,3 +62,7 @@ #define EMOTE_SOUND_NO_FREQ "Default" #define EMOTE_SOUND_VOICE_FREQ "Voice Frequency" #define EMOTE_SOUND_VOICE_LIST "Voice Sound" + +#define WRITE_PREF_NORMAL 1 +#define WRITE_PREF_INSTANT 2 +#define WRITE_PREF_MANUAL 3 diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index bc4a90a52a..5a48b9ef21 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -1651,3 +1651,5 @@ GLOBAL_LIST(chamelion_mask_choices) GLOBAL_LIST(chamelion_belt_choices) //Accessory GLOBAL_LIST(chamelion_accessory_choices) + +GLOBAL_LIST_INIT(tail_layer_options, list("Lower layer" = TAIL_UPPER_LAYER_LOW , "Default layer" = TAIL_UPPER_LAYER , "Upper layer" = TAIL_UPPER_LAYER_HIGH )) diff --git a/code/controllers/subsystems/asset_loading.dm b/code/controllers/subsystems/asset_loading.dm index ecdf406894..b949afff74 100644 --- a/code/controllers/subsystems/asset_loading.dm +++ b/code/controllers/subsystems/asset_loading.dm @@ -13,12 +13,15 @@ SUBSYSTEM_DEF(asset_loading) while(length(generate_queue)) var/datum/asset/to_load = generate_queue[generate_queue.len] + + last_queue_len = length(generate_queue) + generate_queue.len-- + to_load.queued_generation() if(MC_TICK_CHECK) return - last_queue_len = length(generate_queue) - generate_queue.len-- + // We just emptied the queue if(last_queue_len && !length(generate_queue)) // Clean up cached icons, freeing memory. diff --git a/code/controllers/subsystems/character_setup.dm b/code/controllers/subsystems/character_setup.dm index 272cd0b8ab..4894244c8a 100644 --- a/code/controllers/subsystems/character_setup.dm +++ b/code/controllers/subsystems/character_setup.dm @@ -38,4 +38,6 @@ SUBSYSTEM_DEF(character_setup) return /datum/controller/subsystem/character_setup/proc/queue_preferences_save(var/datum/preferences/prefs) + if(!prefs) + return save_queue |= prefs diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 0e134ae606..1252339eb3 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -49,7 +49,7 @@ * Arguments: * * datum/P the parent datum this component reacts to signals from */ -/datum/component/New(list/raw_args) +/datum/component/New(list/raw_args, manual) parent = raw_args[1] var/list/arguments = raw_args.Copy(2) if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE) @@ -58,6 +58,8 @@ return _JoinParent(parent) + if(manual) + lateload_pref_data() /** * Called during component creation with the same arguments as in new excluding parent. @@ -310,7 +312,7 @@ * * Properly handles duplicate situations based on the `dupe_mode` var */ -/datum/proc/_AddComponent(list/raw_args, source) +/datum/proc/_AddComponent(list/raw_args, source, manual) var/original_type = raw_args[1] var/datum/component/component_type = original_type @@ -349,14 +351,14 @@ switch(dupe_mode) if(COMPONENT_DUPE_UNIQUE) if(!new_component) - new_component = new component_type(raw_args) + new_component = new component_type(raw_args, manual) if(!QDELETED(new_component)) old_component.InheritComponent(new_component, TRUE) QDEL_NULL(new_component) if(COMPONENT_DUPE_HIGHLANDER) if(!new_component) - new_component = new component_type(raw_args) + new_component = new component_type(raw_args, manual) if(!QDELETED(new_component)) new_component.InheritComponent(old_component, FALSE) QDEL_NULL(old_component) @@ -378,7 +380,7 @@ return null else if(!new_component) - new_component = new component_type(raw_args) // There's a valid dupe mode but there's no old component, act like normal + new_component = new component_type(raw_args, manual) // There's a valid dupe mode but there's no old component, act like normal else if(dupe_mode == COMPONENT_DUPE_SELECTIVE) var/list/arguments = raw_args.Copy() @@ -390,19 +392,19 @@ QDEL_NULL(new_component) break if(!new_component && make_new_component) - new_component = new component_type(raw_args) + new_component = new component_type(raw_args, manual) else if(dupe_mode == COMPONENT_DUPE_SOURCES) - new_component = new component_type(raw_args) + new_component = new component_type(raw_args, manual) if(new_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE) stack_trace("incompatible source added to a [new_component.type]. Args: [json_encode(raw_args)]") return null else if(!new_component) - new_component = new component_type(raw_args) // Dupes are allowed, act like normal + new_component = new component_type(raw_args, manual) // Dupes are allowed, act like normal if(!old_component && !QDELETED(new_component)) // Nothing related to duplicate components happened and the new component is healthy - SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_component) + SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_component, manual) return new_component return old_component @@ -424,12 +426,13 @@ * * Arguments: * * component_type The typepath of the component to create or return + * * manual_load If we are manually adding this (post-spawn) or not. * * ... additional arguments to be passed when creating the component if it does not exist */ -/datum/proc/_LoadComponent(list/arguments) +/datum/proc/_LoadComponent(list/arguments, manual_load) . = GetComponent(arguments[1]) if(!.) - return _AddComponent(arguments) + return _AddComponent(arguments, manual = manual_load) /** * Removes the component from parent, ends up with a null parent * Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does @@ -495,3 +498,9 @@ */ /datum/component/tgui_host() return parent + +/** + * Loads lateload data for the component. Things such as preferences, which aren't applied if not done at character-spawn. Must be defined per-component. + */ +/datum/component/proc/lateload_pref_data() + return diff --git a/code/datums/components/antags/changeling/changeling.dm b/code/datums/components/antags/changeling/changeling.dm index 6ecc280d8b..25ef93a128 100644 --- a/code/datums/components/antags/changeling/changeling.dm +++ b/code/datums/components/antags/changeling/changeling.dm @@ -155,7 +155,7 @@ var/list/datum/power/changeling/powerinstances = list() if(!mind) return //The current mob is made a changeling AND the mind is made a changeling. - var/datum/component/antag/changeling/comp = LoadComponent(/datum/component/antag/changeling) + var/datum/component/antag/changeling/comp = LoadComponent(/datum/component/antag/changeling, TRUE) mind.antag_holder.changeling = comp var/lesser_form = !ishuman(src) @@ -423,10 +423,23 @@ var/list/datum/power/changeling/powerinstances = list() //Debug item. Here because during debugging I DO NOT want to have to open the player panel 5000 times. -/obj/item/toy/katana/changeling_debug +/obj/item/changeling_debug name = "Katana of the Changeling" desc = "A katana imbued with special powers. It is said that those who wield it will become a changeling." -/obj/item/toy/katana/changeling_debug/attack_self(mob/user) + icon = 'icons/obj/weapons.dmi' + icon_state = "katana" + item_state = "katana" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_material.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_material.dmi', + ) + slot_flags = SLOT_BELT | SLOT_BACK + force = 5 + throwforce = 5 + w_class = ITEMSIZE_NORMAL + attack_verb = list("attacked", "slashed", "stabbed", "sliced") + +/obj/item/changeling_debug/attack_self(mob/user) user.make_changeling() ///Changeling Panel diff --git a/code/datums/components/species/shadekin/helpers/comp_helpers.dm b/code/datums/components/species/shadekin/helpers/comp_helpers.dm index f007b99eec..b2e73246ba 100644 --- a/code/datums/components/species/shadekin/helpers/comp_helpers.dm +++ b/code/datums/components/species/shadekin/helpers/comp_helpers.dm @@ -255,3 +255,13 @@ to_chat(owner, span_danger("The VR systems cannot comprehend this power! This is useless to you!")) return TRUE return FALSE + +///Gets late-load preferences for the shadekin component. Used when the component is applied post-spawn. +/datum/component/shadekin/lateload_pref_data() + if(owner.client) + flicker_color = owner.read_preference(/datum/preference/color/living/flicker_color) + flicker_time = owner.read_preference(/datum/preference/numeric/living/flicker_time) + flicker_break_chance = owner.read_preference(/datum/preference/numeric/living/flicker_break_chance) + flicker_distance = owner.read_preference(/datum/preference/numeric/living/flicker_distance) + no_retreat = owner.read_preference(/datum/preference/toggle/living/dark_retreat_toggle) + nutrition_energy_conversion = owner.read_preference(/datum/preference/toggle/living/shadekin_nutrition_conversion) diff --git a/code/datums/components/species/shadekin/shadekin.dm b/code/datums/components/species/shadekin/shadekin.dm index e93e5476ba..d9cf598095 100644 --- a/code/datums/components/species/shadekin/shadekin.dm +++ b/code/datums/components/species/shadekin/shadekin.dm @@ -212,6 +212,9 @@ return data +/datum/component/shadekin/tgui_close(mob/user) + SScharacter_setup.queue_preferences_save(user?.client?.prefs) + . = ..() /datum/component/shadekin/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) @@ -224,14 +227,14 @@ if(!isnum(new_time)) return FALSE flicker_time = new_time - ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_time, new_time) + ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_time, new_time, WRITE_PREF_MANUAL) return TRUE if("adjust_color") var/set_new_color = tgui_color_picker(ui.user, "Select a color you wish the lights to flicker as (Default is #E0EFF0)", "Color Selector", flicker_color) if(!set_new_color) return FALSE flicker_color = set_new_color - ui.user.write_preference_directly(/datum/preference/color/living/flicker_color, set_new_color) + ui.user.write_preference_directly(/datum/preference/color/living/flicker_color, set_new_color, WRITE_PREF_MANUAL) return TRUE if("adjust_break") var/new_break_chance = text2num(params["val"]) @@ -239,7 +242,7 @@ if(!isnum(new_break_chance)) return FALSE flicker_break_chance = new_break_chance - ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_break_chance, new_break_chance) + ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_break_chance, new_break_chance, WRITE_PREF_MANUAL) return TRUE if("adjust_distance") var/new_distance = text2num(params["val"]) @@ -247,16 +250,16 @@ if(!isnum(new_distance)) return FALSE flicker_distance = new_distance - ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_distance, new_distance) + ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_distance, new_distance, WRITE_PREF_MANUAL) return TRUE if("toggle_retreat") var/new_retreat = !no_retreat no_retreat = !no_retreat - ui.user.write_preference_directly(/datum/preference/toggle/living/dark_retreat_toggle, new_retreat) + ui.user.write_preference_directly(/datum/preference/toggle/living/dark_retreat_toggle, new_retreat, WRITE_PREF_MANUAL) if("toggle_nutrition") var/new_retreat = !nutrition_energy_conversion nutrition_energy_conversion = !nutrition_energy_conversion - ui.user.write_preference_directly(/datum/preference/toggle/living/shadekin_nutrition_conversion, new_retreat) + ui.user.write_preference_directly(/datum/preference/toggle/living/shadekin_nutrition_conversion, new_retreat, WRITE_PREF_MANUAL) /mob/living/proc/shadekin_control_panel() set name = "Shadekin Control Panel" diff --git a/code/game/birthday.dm b/code/game/birthday.dm index 54e0ec8a07..10b0ade5c9 100644 --- a/code/game/birthday.dm +++ b/code/game/birthday.dm @@ -18,7 +18,7 @@ /mob/living/carbon/human/proc/birthday(var/birthday = 0) var/msg var/lastyear = read_preference(/datum/preference/numeric/human/last_bday_note) - write_preference_directly(/datum/preference/numeric/human/last_bday_note, GLOB.world_time_year) //We only want to ask once a year per character, this persists, update early in case of shenanigans + write_preference_directly(/datum/preference/numeric/human/last_bday_note, GLOB.world_time_year, WRITE_PREF_MANUAL) //We only want to ask once a year per character, this persists, update early in case of shenanigans if(birthday) //woo msg = "Today is your birthday! Do you want to increase your character's listed age?" /* //Chomp DISABLE - Absolutely not. @@ -36,6 +36,6 @@ var/howmuch = GLOB.world_time_year - lastyear age += howmuch to_chat(src, span_notice("You are now [age]! Happy birthday!")) - write_preference_directly(/datum/preference/numeric/human/age, age) //Set the age on the character sheet + write_preference_directly(/datum/preference/numeric/human/age, age, WRITE_PREF_MANUAL) //Set the age on the character sheet - client?.prefs.save_character() //Save the info + SScharacter_setup.queue_preferences_save(client?.prefs) diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm index 8742c13595..197200af72 100644 --- a/code/modules/admin/view_variables/topic_basic.dm +++ b/code/modules/admin/view_variables/topic_basic.dm @@ -88,7 +88,7 @@ lst.Insert(1, result) if(result in componentsubtypes) datumname = "component" - target._AddComponent(lst, add_source) + target._AddComponent(lst, add_source, TRUE) else datumname = "element" target._AddElement(lst) diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index 28012c694f..93b463e195 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -317,8 +317,9 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) return client?.prefs?.read_preference(preference_type) /// Write a /datum/preference type and return its value directly to the json. -/mob/proc/write_preference_directly(preference_type, preference_value) - var/success = client?.prefs?.write_preference_by_type(preference_type, preference_value) +/// Please use SScharacter_setup.queue_preferences_save(prefs) when you edit multiple at once and set direct_write to WRITE_PREF_MANUAL +/mob/proc/write_preference_directly(preference_type, preference_value, write_mode = WRITE_PREF_INSTANT) + var/success = client?.prefs?.write_preference_by_type(preference_type, preference_value, write_mode) if(success) client?.prefs?.value_cache[preference_type] = preference_value return success @@ -336,7 +337,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Writes a value and saves to disk immediately /// Used by things that need to directly write to the player savefile things that aren't "really" prefs -/datum/preferences/proc/write_preference_by_type(preference_type, preference_value) +/datum/preferences/proc/write_preference_by_type(preference_type, preference_value, write_mode = WRITE_PREF_NORMAL) var/datum/preference/preference_entry = GLOB.preference_entries[preference_type] if(isnull(preference_entry)) CRASH("Preference type `[preference_type]` is invalid!") @@ -344,7 +345,10 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) if(!write_preference(preference_entry, preference_entry.pref_serialize(preference_value))) return - if(preference_entry.savefile_identifier == PREFERENCE_CHARACTER) + if(write_mode == WRITE_PREF_MANUAL) + return TRUE + + if(preference_entry.savefile_identifier == PREFERENCE_CHARACTER && write_mode != WRITE_PREF_INSTANT) var/save_data = get_save_data_for_savefile_identifier(preference_entry.savefile_identifier) player_setup.save_character(save_data) else diff --git a/code/modules/client/preferences/types/character/general/03_body.dm b/code/modules/client/preferences/types/character/general/03_body.dm index 21fad7af15..a011ef15b8 100644 --- a/code/modules/client/preferences/types/character/general/03_body.dm +++ b/code/modules/client/preferences/types/character/general/03_body.dm @@ -213,16 +213,17 @@ return 255 //no randomization here. ///Tail style. -/datum/preference/numeric/human/tail_layering +/datum/preference/choiced/human/tail_layering category = PREFERENCE_CATEGORY_MANUALLY_RENDERED savefile_identifier = PREFERENCE_CHARACTER savefile_key = "tail_layering" can_randomize = FALSE - minimum = TAIL_UPPER_LAYER - maximum = TAIL_UPPER_LAYER_HIGH -/datum/preference/numeric/human/tail_layering/create_default_value() - return TAIL_UPPER_LAYER +/datum/preference/choiced/human/tail_layering/init_possible_values() + return assoc_to_keys(GLOB.tail_layer_options) -/datum/preference/numeric/human/tail_layering/apply_to_human(mob/living/carbon/human/target, value) - target.tail_layering = value +/datum/preference/choiced/human/tail_layering/create_default_value() + return GLOB.tail_layer_options[2] + +/datum/preference/choiced/human/tail_layering/apply_to_human(mob/living/carbon/human/target, value) + target.tail_layering = GLOB.tail_layer_options[value] diff --git a/code/modules/client/preferences_tgui.dm b/code/modules/client/preferences_tgui.dm index d6234392de..24dc0147f9 100644 --- a/code/modules/client/preferences_tgui.dm +++ b/code/modules/client/preferences_tgui.dm @@ -136,6 +136,9 @@ switch(action) // Basic actions if("load") + if(!isnewplayer(ui.user)) + to_chat(ui.user, span_userdanger("You can't change your character slot while being in round.")) + return FALSE if(!IsGuestKey(ui.user.key)) open_load_dialog(ui.user) . = TRUE @@ -152,6 +155,8 @@ sanitize_preferences() . = TRUE if("resetslot") + if(!isnewplayer(ui.user)) + to_chat(ui.user, span_userdanger("You can't change your character slot while being in round.")) if("Yes" != tgui_alert(ui.user, "This will reset the current slot. Continue?", "Reset current slot?", list("No", "Yes"))) return if("Yes" != tgui_alert(ui.user, "Are you completely sure that you want to reset this character slot?", "Reset current slot?", list("No", "Yes"))) @@ -160,6 +165,8 @@ sanitize_preferences() . = TRUE if("copy") + if(!isnewplayer(ui.user)) + to_chat(ui.user, span_userdanger("You can't change your character slot while being in round.")) if(!IsGuestKey(ui.user.key)) open_copy_dialog(ui.user) . = TRUE @@ -192,7 +199,7 @@ PMH.screen_loc = LAZYACCESS(preview_screen_locs, "PMH") /datum/preferences/tgui_close(mob/user) - // save_character() + load_character() save_preferences() /datum/preferences/proc/create_character_profiles() diff --git a/code/modules/client/ui_style.dm b/code/modules/client/ui_style.dm index 9401f2b015..e81733768b 100644 --- a/code/modules/client/ui_style.dm +++ b/code/modules/client/ui_style.dm @@ -30,9 +30,9 @@ usr.update_ui_style(UI_style_new, UI_style_alpha_new, UI_style_color_new) if(tgui_alert(src, "Like it? Save changes?","Save?",list("Yes", "No")) == "Yes") - usr.write_preference_directly(/datum/preference/choiced/ui_style, UI_style_new) - usr.write_preference_directly(/datum/preference/numeric/ui_style_alpha, UI_style_alpha_new) - usr.write_preference_directly(/datum/preference/color/ui_style_color, UI_style_color_new) + usr.write_preference_directly(/datum/preference/choiced/ui_style, UI_style_new, WRITE_PREF_MANUAL) + usr.write_preference_directly(/datum/preference/numeric/ui_style_alpha, UI_style_alpha_new, WRITE_PREF_MANUAL) + usr.write_preference_directly(/datum/preference/color/ui_style_color, UI_style_color_new, WRITE_PREF_MANUAL) SScharacter_setup.queue_preferences_save(prefs) to_chat(src, "UI was saved") return diff --git a/code/modules/mob/living/carbon/human/emote_vr.dm b/code/modules/mob/living/carbon/human/emote_vr.dm index 4802f41481..79173a72f8 100644 --- a/code/modules/mob/living/carbon/human/emote_vr.dm +++ b/code/modules/mob/living/carbon/human/emote_vr.dm @@ -69,19 +69,14 @@ set category = "IC.Game" set desc = "Switch tail layer to show below/above/between clothing or other things such as wings!." - var/input = tgui_input_list(src, "Select a tail layer.", "Set Tail Layer", list(SWITCH_TAIL_LAYER_UPPER, SWITCH_TAIL_LAYER_STANDARD, SWITCH_TAIL_LAYER_LOWER)) - if(isnull(input)) + var/input = tgui_input_list(src, "Select a tail layer.", "Set Tail Layer", GLOB.tail_layer_options, read_preference(/datum/preference/choiced/human/tail_layering)) + if(!input) return - switch(input) - if(SWITCH_TAIL_LAYER_UPPER) - tail_layering = input - write_preference_directly(/datum/preference/numeric/human/tail_layering, TAIL_UPPER_LAYER_HIGH) - if(SWITCH_TAIL_LAYER_STANDARD) - tail_layering = input - write_preference_directly(/datum/preference/numeric/human/tail_layering, TAIL_UPPER_LAYER) - if(SWITCH_TAIL_LAYER_LOWER) - tail_layering = input - write_preference_directly(/datum/preference/numeric/human/tail_layering, TAIL_UPPER_LAYER_LOW) + var/tail_option = GLOB.tail_layer_options[input] + if(!tail_option) + return + tail_layering = tail_option + write_preference_directly(/datum/preference/choiced/human/tail_layering, input) update_tail_showing()