From 189f77cbb7c78697050237090a65dd000c36fecc Mon Sep 17 00:00:00 2001 From: "Anthony \"Shifty Rail" <31417754+ShiftyRail@users.noreply.github.com> Date: Sat, 7 Jun 2025 14:54:09 +0100 Subject: [PATCH] Refactors player preferences for modularity + SQLite Unit Test (#37615) * Pref code refactor * Empty database reference * Unit testing SQLite * Everything else * Disable unit testing. * Equivalent * more robust unit tests --- __DEFINES/limb_defines.dm | 6 + __DEFINES/mobs.dm | 4 +- __DEFINES/preferences.dm | 44 +- code/__HELPERS/logging.dm | 2 +- code/__HELPERS/mobs.dm | 46 + code/__HELPERS/unsorted.dm | 16 +- code/_onclick/hud/hud.dm | 6 +- code/_onclick/hud/parallax.dm | 8 +- code/_onclick/hud/screen_alarms.dm | 2 +- code/controllers/subsystem/ambient_sound.dm | 4 +- code/datums/chat_message.dm | 2 +- code/datums/emotes.dm | 8 +- .../dynamic/dynamic_rulesets_roundstart.dm | 4 +- code/datums/gamemode/misc_gamemode_procs.dm | 4 +- code/datums/gamemode/role/changeling.dm | 2 +- code/datums/gamemode/role/syndicate.dm | 2 +- code/datums/gamemode/role/vampire_role.dm | 2 +- code/datums/gamemode/role/wizard.dm | 2 +- code/game/gamemodes/intercept_report.dm | 2 +- code/game/objects/effects/misc_effects.dm | 2 +- .../objects/items/devices/PDA/apps/general.dm | 2 +- .../items/devices/PDA/apps/messenger.dm | 2 +- .../objects/items/devices/radio/headset.dm | 4 +- code/game/objects/objs.dm | 2 +- code/game/objects/structures/musician.dm | 4 +- code/game/sound.dm | 2 +- code/game/verbs/ooc.dm | 14 +- code/modules/admin/admin.dm | 2 +- code/modules/admin/admin_verbs.dm | 38 +- code/modules/admin/verbs/adminhelp.dm | 2 +- code/modules/admin/verbs/adminpm.dm | 2 +- code/modules/admin/verbs/deadsay.dm | 8 +- code/modules/admin/verbs/modifyvariables.dm | 6 + code/modules/admin/verbs/playsound.dm | 2 +- code/modules/admin/verbs/pray.dm | 2 +- code/modules/bayinstruments/sound_player.dm | 4 +- code/modules/client/client_procs.dm | 22 +- code/modules/client/preferences.dm | 1620 ----------------- .../modules/client/preferences/pref_datums.dm | 227 +++ .../preferences/pref_datums_character.dm | 861 +++++++++ .../client/preferences/pref_datums_client.dm | 582 ++++++ .../client/preferences/pref_db_workflow.dm | 200 ++ .../modules/client/preferences/preferences.dm | 602 ++++++ .../preferences/preferences_savefile.dm | 639 +++++++ .../client/preferences/preferences_ui.dm | 658 +++++++ .../client/preferences/subsections/limbs.dm | 4 + .../client/preferences/subsections/organs.dm | 1 + code/modules/client/preferences_savefile.dm | 551 ------ code/modules/credits/credits.dm | 4 +- code/modules/credits/credits_clientprocs.dm | 26 +- code/modules/jobs/job_controller.dm | 28 +- code/modules/media/mediamanager.dm | 21 +- code/modules/mob/dead/observer/observer.dm | 2 +- code/modules/mob/dead/observer/say.dm | 10 +- code/modules/mob/emote.dm | 12 +- code/modules/mob/living/carbon/brain/MMI.dm | 11 +- code/modules/mob/living/living.dm | 2 +- code/modules/mob/living/say.dm | 6 +- .../mob/living/silicon/ai/freelook/eye.dm | 2 +- .../modules/mob/living/simple_animal/borer.dm | 2 +- .../simple_animal/borer/captive_brain.dm | 2 +- code/modules/mob/mob.dm | 4 +- code/modules/mob/mob_movement.dm | 2 +- code/modules/mob/new_player/login.dm | 6 +- code/modules/mob/new_player/new_player.dm | 87 +- .../mob/new_player/preferences_setup.dm | 188 +- code/modules/mob/say.dm | 4 +- code/modules/mob/typing_indicator.dm | 2 +- code/modules/multiz/ventcrawl.dm | 2 +- code/modules/paperwork/papers_dynamic.dm | 6 +- code/modules/power/singularity/singularity.dm | 18 +- .../finds/finds_talkingitem.dm | 2 +- code/modules/spells/spell_code.dm | 4 +- code/modules/telesci/gps.dm | 2 +- code/modules/tgui/external.dm | 2 +- code/modules/tgui/tgui.dm | 12 +- code/modules/tgui_input/alert.dm | 8 +- code/modules/tgui_input/checkboxes.dm | 6 +- code/modules/tgui_input/keycombo.dm | 6 +- code/modules/tgui_input/list.dm | 6 +- code/modules/tgui_input/number.dm | 6 +- code/modules/tgui_input/text.dm | 6 +- code/modules/tooltip/tooltip.dm | 6 +- .../unit_tests/__unit_test_includes.dm | 1 + code/modules/unit_tests/sqlite_prefs.dm | 162 ++ .../unit_tests/sqlite_unit_testing_procs.dm | 319 ++++ goon/code/datums/browserOutput.dm | 2 +- players2_empty.sqlite | Bin 23552 -> 23552 bytes vgstation13.dme | 9 +- 89 files changed, 4694 insertions(+), 2543 deletions(-) delete mode 100644 code/modules/client/preferences.dm create mode 100644 code/modules/client/preferences/pref_datums.dm create mode 100644 code/modules/client/preferences/pref_datums_character.dm create mode 100644 code/modules/client/preferences/pref_datums_client.dm create mode 100644 code/modules/client/preferences/pref_db_workflow.dm create mode 100644 code/modules/client/preferences/preferences.dm create mode 100644 code/modules/client/preferences/preferences_savefile.dm create mode 100644 code/modules/client/preferences/preferences_ui.dm delete mode 100644 code/modules/client/preferences_savefile.dm create mode 100644 code/modules/unit_tests/sqlite_prefs.dm create mode 100644 code/modules/unit_tests/sqlite_unit_testing_procs.dm diff --git a/__DEFINES/limb_defines.dm b/__DEFINES/limb_defines.dm index c5263f638fc..4dd3efa3696 100644 --- a/__DEFINES/limb_defines.dm +++ b/__DEFINES/limb_defines.dm @@ -13,6 +13,12 @@ #define TARGET_MOUTH "mouth" #define TARGET_EYES "eyes" +#define LIMB_HEART "heart" +#define LIMB_EYES "eyes" +#define LIMB_LUNG "lungs" +#define LIMB_LIVER "liver" +#define LIMB_KIDNEYS "kidneys" + #define UNCUFF_LEGS -1 #define UNCUFF_BOTH 0 #define UNCUFF_HANDS 1 diff --git a/__DEFINES/mobs.dm b/__DEFINES/mobs.dm index e7ae1fb745d..4dd52e825a9 100644 --- a/__DEFINES/mobs.dm +++ b/__DEFINES/mobs.dm @@ -16,11 +16,13 @@ #define MOB_NO_PETRIFY 64 //can't get petrified #define MOB_NO_LAZ 128 //Can not be revived via lazarus injector +#define NO_BACKPACK 1 #define BACKPACK 2 #define SATCHEL_NORM 3 #define SATCHEL_ALT 4 #define MESSENGER_BAG 5 +#define NO_BACKPACK_STRING "1" #define BACKPACK_STRING "2" #define SATCHEL_NORM_STRING "3" #define SATCHEL_ALT_STRING "4" @@ -46,4 +48,4 @@ #define CONFUSED_MAGIC 1 #define SLIME_BABY 1 -#define SLIME_ADULT 2 \ No newline at end of file +#define SLIME_ADULT 2 diff --git a/__DEFINES/preferences.dm b/__DEFINES/preferences.dm index 90c4538d29f..6a11ef5aa7f 100644 --- a/__DEFINES/preferences.dm +++ b/__DEFINES/preferences.dm @@ -18,4 +18,46 @@ // /datum/preferences/var/headset_sound #define HEADSET_SOUND_DISABLED 0 #define HEADSET_SOUND_TRANSMIT 1 -#define HEADSET_SOUND_ALL 2 \ No newline at end of file +#define HEADSET_SOUND_ALL 2 + +// list("None", "White Briefs", "Green Briefs", "Blue Briefs", "Black Briefs", "Grey Briefs", "Mankini", "Love-Hearts Boxers", "Black Boxers", "Grey Boxers", "Stripey Boxers", "Kinky", "Freedom Boxers", "Tea Boxers", "Communist Boxers", "Cowprint Boxers", "Green Wifebeater", "White Wifebeater", "Black Wifebeater") +// Curse whoever made male/female underwear different colours +#define UNDERWEAR_MALE_NONE 1 +#define UNDERWEAR_MALE_WHITE_BRIEFS 2 +#define UNDERWEAR_MALE_GREEN_BRIEFS 3 +#define UNDERWEAR_MALE_BLUE_BRIEFS 4 +#define UNDERWEAR_MALE_BLACK_BRIEFS 5 +#define UNDERWEAR_MALE_GREY_BRIEFS 6 +#define UNDERWEAR_MALE_MANKINI 7 +#define UNDERWEAR_MALE_LOVE_HEARTS_BOXERS 8 +#define UNDERWEAR_MALE_BLACK_BOXERS 9 +#define UNDERWEAR_MALE_GREY_BOXERS 10 +#define UNDERWEAR_MALE_STRIPEY_BOXERS 11 +#define UNDERWEAR_MALE_KINKY 12 +#define UNDERWEAR_MALE_FREEDOM_BOXERS 13 +#define UNDERWEAR_MALE_TEA_BOXERS 14 +#define UNDERWEAR_MALE_COMMUNIST_BOXERS 15 +#define UNDERWEAR_MALE_COWPRINT_BOXERS 16 +#define UNDERWEAR_MALE_GREEN_WIFEBEATER 17 +#define UNDERWEAR_MALE_WHITE_WIFEBEATER 18 + +// list("None", "White", "Green", "Blue", "Black", "Yellow", "Thong", "Baby-Blue", "Babydoll", "Red", "Pink", "Kinky", "Freedom", "Tea", "Communist", "Cowprint", "Pink Husbandbeater", "White Husbandbeater", "Black Husbandbeater") +#define UNDERWEAR_FEMALE_NONE 1 +#define UNDERWEAR_FEMALE_WHITE 2 +#define UNDERWEAR_FEMALE_GREEN 3 +#define UNDERWEAR_FEMALE_BLUE 4 +#define UNDERWEAR_FEMALE_BLACK 5 +#define UNDERWEAR_FEMALE_YELLOW 6 +#define UNDERWEAR_FEMALE_THONG 7 +#define UNDERWEAR_FEMALE_BABY_BLUE 8 +#define UNDERWEAR_FEMALE_BABYDOLL 9 +#define UNDERWEAR_FEMALE_RED 10 +#define UNDERWEAR_FEMALE_PINK 11 +#define UNDERWEAR_FEMALE_KINKY 12 +#define UNDERWEAR_FEMALE_FREEDOM 13 +#define UNDERWEAR_FEMALE_TEA 14 +#define UNDERWEAR_FEMALE_COMMUNIST 15 +#define UNDERWEAR_FEMALE_COWPRINT 16 +#define UNDERWEAR_FEMALE_PINK_HUSBANDBEATER 17 +#define UNDERWEAR_FEMALE_WHITE_HUSBANDBEATER 18 +#define UNDERWEAR_FEMALE_BLACK_HUSBANDBEATER 19 diff --git a/code/__HELPERS/logging.dm b/code/__HELPERS/logging.dm index 6f5e4591ea9..c78f2289ec6 100644 --- a/code/__HELPERS/logging.dm +++ b/code/__HELPERS/logging.dm @@ -52,7 +52,7 @@ if(send2chat) for(var/client/C in admins) - if(C.prefs.toggles & CHAT_DEBUGLOGS) + if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEBUGLOGS) to_chat(C, "DEBUG: [text]") /proc/log_sql(text) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 2a1426192a1..9096f7b8e10 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -13,6 +13,52 @@ var/static/list/ALL_LIMBS = list(LIMB_HEAD,LIMB_CHEST,LIMB_GROIN, return h_style +/proc/random_hair_color() + var/red = 0 + var/green = 0 + var/blue = 0 + + var/col = pick("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", 15;"punk") + switch(col) + if("blonde") + red = 255 + green = 255 + blue = 0 + if("black") + red = 0 + green = 0 + blue = 0 + if("chestnut") + red = 153 + green = 102 + blue = 51 + if("copper") + red = 255 + green = 153 + blue = 0 + if("brown") + red = 102 + green = 51 + blue = 0 + if("wheat") + red = 255 + green = 255 + blue = 153 + if("old") + red = rand (100, 255) + green = red + blue = red + if("punk") + red = rand(0, 255) + green = rand(0, 255) + blue = rand(0, 255) + + red = max(min(red + rand (-25, 25), 255), 0) + green = max(min(green + rand (-25, 25), 255), 0) + blue = max(min(blue + rand (-25, 25), 255), 0) + return list(red, green, blue) + + /proc/GetOppositeDir(var/dir) switch(dir) if(NORTH) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index c58dfff03f3..587fbec3b69 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -515,7 +515,7 @@ var/holding = user.get_active_hand() var/delayfraction = round(delay/numticks) var/image/progbar - if(user && user.client && user.client.prefs.progress_bars) + if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars)) if(!progbar) progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0") progbar.plane = HUD_PLANE @@ -526,7 +526,7 @@ //barbar.pixel_y = 36 //var/oldstate for (var/i = 1 to numticks) - if(user && user.client && user.client.prefs.progress_bars && progbar) + if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars) && progbar) //oldstate = progbar.icon_state progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]" user.client.images |= progbar @@ -568,7 +568,7 @@ for(var/atom/target in targets) initial_target_locations[target] = target.loc - if(user.client && user.client.prefs.progress_bars) + if(user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars)) for(var/target in targets) if(!targets[target]) var/image/new_progress_bar = create_progress_bar_on(target) @@ -672,7 +672,7 @@ var/target_location = target.loc var/image/progbar //var/image/barbar - if(user && user.client && user.client.prefs.progress_bars && target) + if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars) && target) if(!progbar) progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0") progbar.pixel_z = WORLD_ICON_SIZE @@ -680,7 +680,7 @@ progbar.layer = HUD_ABOVE_ITEM_LAYER progbar.appearance_flags = RESET_COLOR | RESET_TRANSFORM for (var/i = 1 to numticks) - if(user && user.client && user.client.prefs.progress_bars && target) + if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars) && target) if(!progbar) progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0") progbar.pixel_z = WORLD_ICON_SIZE @@ -1222,12 +1222,12 @@ Game Mode config tags: . += M.client /client/proc/output_to_special_tab(msg, force_focus = FALSE) - if(prefs.special_popup) + if(prefs.get_pref(/datum/preference_setting/enum/special_popup)) src << output("\[[time_stamp()]] [msg]", "window1.msay_output") if(!holder) //Force normal players to see the admin message when it gets sent to them winset(src, "rpane.special_button", "is-checked=true") winset(src, null, "rpanewindow.left=window1") - if(prefs.special_popup == SPECIAL_POPUP_EXCLUSIVE) + if(prefs.get_pref(/datum/preference_setting/enum/special_popup) == SPECIAL_POPUP_EXCLUSIVE) return to_chat(src, msg) @@ -1352,7 +1352,7 @@ Game Mode config tags: var/mob/M = C if(M.client) C = M.client - if(!istype(C) || (!C.prefs.window_flashing && !ignorepref)) + if(!istype(C) || (!C.prefs.get_pref(/datum/preference_setting/toggle/window_flashing) && !ignorepref)) return winset(C, "mainwindow", "flash=5") diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index b57dcb9d894..2209cf29c2f 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -209,9 +209,9 @@ var/global/obj/abstract/screen/clicker/catcher = new() ui_color = null ui_alpha = 255 else - ui_style = ui_style2icon(mymob.client.prefs.UI_style) - ui_color = mymob.client.prefs.UI_style_color - ui_alpha = mymob.client.prefs.UI_style_alpha + ui_style = ui_style2icon(mymob.client.prefs.get_pref(/datum/preference_setting/string/UI_style)) + ui_color = mymob.client.prefs.get_pref(/datum/preference_setting/string/UI_style_color) + ui_alpha = mymob.client.prefs.get_pref(/datum/preference_setting/numerical/UI_style_alpha) if(ishuman(mymob)) human_hud(ui_style, ui_color, ui_alpha) // Pass the player the UI style chosen in preferences diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 88d187c984a..ea7f66e5a48 100644 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -111,7 +111,7 @@ var/list/cult_parallax[(GRID_WIDTH**2)] /datum/hud/proc/update_parallax() var/client/C = mymob.client - if(C.prefs.space_parallax) + if(C.prefs.get_pref(/datum/preference_setting/toggle/space_parallax)) parallax_on_clients |= C for(var/obj/abstract/screen/parallax/bgobj in C.parallax) C.screen |= bgobj @@ -168,7 +168,7 @@ var/list/cult_parallax[(GRID_WIDTH**2)] C.screen |= C.parallax_master C.screen |= C.parallax_spacemaster - if(C.prefs.space_dust) + if(C.prefs.get_pref(/datum/preference_setting/toggle/space_dust)) switch(starlight) if (STARLIGHT_REDSHIFT) C.parallax_dustmaster.color = list( @@ -226,8 +226,8 @@ var/list/cult_parallax[(GRID_WIDTH**2)] C.previous_turf = posobj for(var/obj/abstract/screen/parallax/bgobj in C.parallax_movable) - var/accumulated_offset_x = bgobj.base_offset_x - round(offsetx * bgobj.parallax_speed * C.prefs.parallax_speed) - var/accumulated_offset_y = bgobj.base_offset_y - round(offsety * bgobj.parallax_speed * C.prefs.parallax_speed) + var/accumulated_offset_x = bgobj.base_offset_x - round(offsetx * bgobj.parallax_speed * C.prefs.get_pref(/datum/preference_setting/numerical/parallax_speed)) + var/accumulated_offset_y = bgobj.base_offset_y - round(offsety * bgobj.parallax_speed * C.prefs.get_pref(/datum/preference_setting/numerical/parallax_speed)) //This part makes the Parallax wrap around while(accumulated_offset_x > PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE) diff --git a/code/_onclick/hud/screen_alarms.dm b/code/_onclick/hud/screen_alarms.dm index cd6c8efdb6d..0efc31dcf3a 100644 --- a/code/_onclick/hud/screen_alarms.dm +++ b/code/_onclick/hud/screen_alarms.dm @@ -103,7 +103,7 @@ var/global/list/screen_alarms_locs = list( var/obj/abstract/screen/alert/alert = mobalerts[mobalerts[i]] if(alert.icon_state == "template") if(!icon_pref) - icon_pref = ui_style2icon(mymob.client.prefs.UI_style) + icon_pref = ui_style2icon(mymob.client.prefs.get_pref(/datum/preference_setting/string/UI_style)) alert.icon = icon_pref alert.screen_loc = screen_alarms_locs[i] mymob.client.screen |= alert diff --git a/code/controllers/subsystem/ambient_sound.dm b/code/controllers/subsystem/ambient_sound.dm index cc91089cc6c..16c7099adf5 100644 --- a/code/controllers/subsystem/ambient_sound.dm +++ b/code/controllers/subsystem/ambient_sound.dm @@ -18,7 +18,7 @@ var/datum/subsystem/ambientsound/SSambience if(config.no_ambience) return for (var/client/C in clients) - if(C && (C.prefs.toggles & SOUND_AMBIENCE)) + if(C && (C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_AMBIENCE)) var/mob/new_player/NP = C.mob if(!istype(NP)) C.handle_ambience() @@ -59,7 +59,7 @@ Ambience system. if(prob(initial(picked_ambience_datum.prob_fire))) ambience_buffer = world.timeofday+initial(picked_ambience_datum.length) last_ambient_noise = initial(picked_ambience_datum.sound) - src << sound(last_ambient_noise, 0, 0, CHANNEL_AMBIENCE, prefs.ambience_volume) + src << sound(last_ambient_noise, 0, 0, CHANNEL_AMBIENCE, prefs.get_pref(/datum/preference_setting/numerical/ambience_volume)) /client/proc/get_ambience() var/list/personal = mob.get_personal_ambience() diff --git a/code/datums/chat_message.dm b/code/datums/chat_message.dm index 42e65f696db..55f8b00efa8 100644 --- a/code/datums/chat_message.dm +++ b/code/datums/chat_message.dm @@ -84,7 +84,7 @@ var/runechat_icon = null owner.register_event(/event/destroyed, src, nameof(src::qdel_self())) // Clip message - var/maxlen = owned_by.prefs.max_chat_length + var/maxlen = owned_by.prefs.get_pref(/datum/preference_setting/numerical/max_chat_length) if (length_char(text) > maxlen) text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 24321390b85..bcda7e069eb 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -62,9 +62,9 @@ if(!M.client || isnewplayer(M)) continue var/T = get_turf(user) - if(isobserver(M) && M.client && (M.client.prefs.toggles & CHAT_GHOSTSIGHT) && !(M in viewers(T))) + if(isobserver(M) && M.client && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTSIGHT) && !(M in viewers(T))) M.show_message(formatFollow(user) + " " + msg) - if (user.client && M?.client?.prefs.mob_chat_on_map && get_dist(M, user) < M?.client.view) + if (user.client && M?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && get_dist(M, user) < M?.client.view) M.create_chat_message(user, null, msg_runechat, "", list("italics")) if(emote_type & EMOTE_VISIBLE) @@ -72,13 +72,13 @@ if(!(emote_type & EMOTE_NO_RUNECHAT)) for(var/z0 in GetOpenConnectedZlevels(user)) for (var/mob/O in viewers(world.view, locate(user.x,user.y,z0))) - if (user.client && O?.client?.prefs.mob_chat_on_map && O.stat != UNCONSCIOUS && !(isinvisible(user))) + if (user.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && O.stat != UNCONSCIOUS && !(isinvisible(user))) O.create_chat_message(user, null, msg_runechat, "", list("italics")) else if(emote_type & EMOTE_AUDIBLE) for(var/mob/O in get_hearers_in_view(world.view, user)) O.show_message(msg) if(!(emote_type & EMOTE_NO_RUNECHAT)) - if(user.client && O?.client?.prefs.mob_chat_on_map && O.stat != UNCONSCIOUS && !O.is_deaf()) + if(user.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && O.stat != UNCONSCIOUS && !O.is_deaf()) O.create_chat_message(user, null, msg_runechat, "", list("italics")) var/turf/T = get_turf(user) diff --git a/code/datums/gamemode/dynamic/dynamic_rulesets_roundstart.dm b/code/datums/gamemode/dynamic/dynamic_rulesets_roundstart.dm index 9fef9847a1d..aa502576a6a 100644 --- a/code/datums/gamemode/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/datums/gamemode/dynamic/dynamic_rulesets_roundstart.dm @@ -564,9 +564,9 @@ Assign your candidates in choose_candidates() instead. if(job_master.TryAssignJob(old_AI,level,job)) break if(!old_AI.mind.assigned_role) // still no job - if(old_AI.client.prefs.alternate_option == GET_RANDOM_JOB) + if(old_AI.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_RANDOM_JOB) job_master.GiveRandomJob(old_AI) - else if(old_AI.client.prefs.alternate_option == BE_ASSISTANT) + else if(old_AI.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == BE_ASSISTANT) job_master.AssignRole(old_AI, "Assistant") if(!old_AI.mind.assigned_role) to_chat(old_AI, "You have been returned to lobby due to your job preferences being filled.") diff --git a/code/datums/gamemode/misc_gamemode_procs.dm b/code/datums/gamemode/misc_gamemode_procs.dm index c17fbe1efd4..bab395bd58a 100644 --- a/code/datums/gamemode/misc_gamemode_procs.dm +++ b/code/datums/gamemode/misc_gamemode_procs.dm @@ -61,9 +61,9 @@ if (man.mind.assigned_role == "MODE") // Wiz, nukies, ... continue - if(man.client.prefs.nanotrasen_relation == "Opposed") + if(man.client.prefs.get_pref(/datum/preference_setting/enum/string/nanotrasen_relation) == "Opposed") dudes += man - else if(man.client.prefs.nanotrasen_relation == "Skeptical" && prob(50)) + else if(man.client.prefs.get_pref(/datum/preference_setting/enum/string/nanotrasen_relation) == "Skeptical" && prob(50)) dudes += man else if( (man.mind.antag_roles[CULTIST] && prob(40)) || \ diff --git a/code/datums/gamemode/role/changeling.dm b/code/datums/gamemode/role/changeling.dm index b2f567a6ac0..5beb9332449 100644 --- a/code/datums/gamemode/role/changeling.dm +++ b/code/datums/gamemode/role/changeling.dm @@ -71,7 +71,7 @@ antag.current << sound('sound/effects/ling_intro.ogg') /datum/role/changeling/ForgeObjectives() - if(!antag.current.client.prefs.antag_objectives) + if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives)) AppendObjective(/datum/objective/freeform/changeling) return AppendObjective(/datum/objective/absorb) diff --git a/code/datums/gamemode/role/syndicate.dm b/code/datums/gamemode/role/syndicate.dm index dd7be741dd8..3032de0fa9b 100644 --- a/code/datums/gamemode/role/syndicate.dm +++ b/code/datums/gamemode/role/syndicate.dm @@ -39,7 +39,7 @@ .=..() /datum/role/traitor/ForgeObjectives() - if(!antag.current.client.prefs.antag_objectives) + if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives)) AppendObjective(/datum/objective/freeform/syndicate) return if(istype(antag.current, /mob/living/silicon)) diff --git a/code/datums/gamemode/role/vampire_role.dm b/code/datums/gamemode/role/vampire_role.dm index 1bc3362dae2..b463dfa6faa 100644 --- a/code/datums/gamemode/role/vampire_role.dm +++ b/code/datums/gamemode/role/vampire_role.dm @@ -107,7 +107,7 @@ . += ..() // Who he was, his objectives... /datum/role/vampire/ForgeObjectives() - if(!antag.current.client.prefs.antag_objectives) + if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives)) AppendObjective(/datum/objective/freeform/vampire) return diff --git a/code/datums/gamemode/role/wizard.dm b/code/datums/gamemode/role/wizard.dm index d0829be7e56..13b96f07f6f 100644 --- a/code/datums/gamemode/role/wizard.dm +++ b/code/datums/gamemode/role/wizard.dm @@ -20,7 +20,7 @@ var/list/potions_bought = list() /datum/role/wizard/ForgeObjectives() - if(!antag.current.client.prefs.antag_objectives) + if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives)) AppendObjective(/datum/objective/freeform/wizard) return switch(rand(1,100)) diff --git a/code/game/gamemodes/intercept_report.dm b/code/game/gamemodes/intercept_report.dm index 0748becb095..060985a314a 100644 --- a/code/game/gamemodes/intercept_report.dm +++ b/code/game/gamemodes/intercept_report.dm @@ -115,7 +115,7 @@ /datum/intercept_text/proc/get_suspect() var/list/dudes = list() - for(var/mob/living/carbon/human/man in player_list) if(man.client && man.client.prefs.nanotrasen_relation == "Opposed") + for(var/mob/living/carbon/human/man in player_list) if(man.client && man.client.prefs.get_pref(/datum/preference_setting/enum/string/nanotrasen_relation) == "Opposed") dudes += man for(var/i = 0, i < max(player_list.len/10,2), i++) dudes += pick(player_list) diff --git a/code/game/objects/effects/misc_effects.dm b/code/game/objects/effects/misc_effects.dm index 80115cf43fe..c2d5838e7f9 100644 --- a/code/game/objects/effects/misc_effects.dm +++ b/code/game/objects/effects/misc_effects.dm @@ -326,7 +326,7 @@ We don't care about names, DNA, accounts, activity, any of that. We're just gonn if(!player.ready) continue - var/list/jobs = player.client.prefs.jobs + var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs) for(var/job in jobs) if((jobs[job] == JOB_PREF_HIGH) && GetJob(job)) diff --git a/code/game/objects/items/devices/PDA/apps/general.dm b/code/game/objects/items/devices/PDA/apps/general.dm index f1419a06b9c..df200723529 100644 --- a/code/game/objects/items/devices/PDA/apps/general.dm +++ b/code/game/objects/items/devices/PDA/apps/general.dm @@ -79,7 +79,7 @@ var/log = replacetext(n, "\n", "(new line)")//no intentionally spamming admins with 100 lines, nice try log_say("[pda_device] notes - [U] changed the text to: [log]") for(var/mob/dead/observer/M in player_list) - if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTPDA)) + if(M.stat == DEAD && M.client && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTPDA)) M.show_message("[pda_device] notes - [U] changed the text to: [log]") else U << browse(null, "window=pda") diff --git a/code/game/objects/items/devices/PDA/apps/messenger.dm b/code/game/objects/items/devices/PDA/apps/messenger.dm index 9cf15e587de..1175ca9b8ec 100644 --- a/code/game/objects/items/devices/PDA/apps/messenger.dm +++ b/code/game/objects/items/devices/PDA/apps/messenger.dm @@ -273,7 +273,7 @@ P_app.tnote["[msg_id]"] = "← From [pda_device.owner] ([pda_device.ownjob]):
[t]
" msg_id++ for(var/mob/dead/observer/M in player_list) - if(!multicast_message && M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTPDA)) // src.client is so that ghosts don't have to listen to mice + if(!multicast_message && M.stat == DEAD && M.client && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTPDA)) // src.client is so that ghosts don't have to listen to mice M.show_message("[formatFollow(U)] PDA Message - \ [U.real_name][U.real_name == pda_device.owner ? "" : " (as [pda_device.owner])"] -> [P.owner]: [t]\ [pda_device.photo ? " (View Photo)" : ""]") diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index bbc54e23ffc..3e0d3d54e0e 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -35,7 +35,7 @@ /obj/item/device/radio/headset/talk_into(datum/speech/speech_orig, channel=null) if(!broadcasting) return - if(usr?.client?.prefs.headset_sound) + if(usr?.client?.prefs.get_pref(/datum/preference_setting/numerical/headset_sound)) playsound(usr, 'sound/effects/radio_chatter.ogg', 100, 1, vary = 0) return ..() @@ -43,7 +43,7 @@ if(ishuman(src.loc)) var/mob/living/carbon/human/H = src.loc if(H.ears == src) - if(H.client && (H.client.prefs.headset_sound == HEADSET_SOUND_ALL)) + if(H.client && (H.client.prefs.get_pref(/datum/preference_setting/numerical/headset_sound) == HEADSET_SOUND_ALL)) playsound(H, 'sound/effects/radio_chatter.ogg', 100, 1, vary = 0) return ..(freq, level) return -1 diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index a8181b3524d..31197f8b755 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -237,7 +237,7 @@ var/global/list/reagents_to_always_log = list(AMUTATIONTOXIN, CYANIDE, CHEFSPECI return 0 else delayNextpAIMove(getpAIMovementDelay()) - if (user.client.prefs.stumble && ((world.time - user.last_movement) > 5) && getpAIMovementDelay() < 2) + if (user.client.prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - user.last_movement) > 5) && getpAIMovementDelay() < 2) delayNextpAIMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier user.last_movement=world.time return 1 diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm index b804e3a0102..25beca4ecd0 100644 --- a/code/game/objects/structures/musician.dm +++ b/code/game/objects/structures/musician.dm @@ -72,7 +72,7 @@ if(istype(instrumentObj,/obj/item/device/instrument)) var/obj/item/device/instrument/INS = instrumentObj INS.OnPlayed(user,M) - if(!M.client.prefs.hear_instruments) + if(!M.client.prefs.get_pref(/datum/preference_setting/toggle/hear_instruments)) continue M.playsound_local(source, soundfile, 100, falloff = 5) if(recording && live) @@ -120,7 +120,7 @@ //to_chat(world, "beat: [beat] / beat length: [length(beat)]") if(!length(beat)) //This occurs when a comma is at the end of a line beat = " " //It's intended to be a space so here we make it a space - + var/list/notes = splittext(beat, "/") var/delta = length(notes)==2 && text2num(notes[2]) ? text2num(notes[2]) : 1 var/note_str = splittext(notes[1], "-") diff --git a/code/game/sound.dm b/code/game/sound.dm index 40de6876750..933651dde86 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -169,7 +169,7 @@ var/const/SURROUND_CAP = 7 /client/proc/playtitlemusic() if(!ticker || !ticker.login_music || config.no_lobby_music) return - if(prefs.toggles & SOUND_LOBBY) + if(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_LOBBY) if(istype(src)) src << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBY) // MAD JAMS diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index 9b1f25cb8d2..23128a71f8a 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -21,7 +21,7 @@ var/adminbus_ooc_color if(!msg) return - if(!(prefs.toggles & CHAT_OOC)) + if(!(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_OOC)) to_chat(src, "You have OOC muted.") return @@ -59,10 +59,10 @@ var/adminbus_ooc_color admin_color = global.adminbus_ooc_color if(holder && !holder.fakekey && (holder.rights & R_ADMIN) && config.allow_admin_ooccolor) - admin_color = src.prefs.ooccolor + admin_color = src.prefs.get_pref(/datum/preference_setting/string/ooc_color) for(var/client/C in clients) - if(!(C.prefs.toggles & CHAT_OOC) || iscluwnebanned(C.mob)) + if(!(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_OOC) || iscluwnebanned(C.mob)) continue var/display_name = src.key @@ -111,7 +111,7 @@ var/adminbus_ooc_color if(!msg) return - if(!(prefs.toggles & CHAT_LOOC)) + if(!(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC)) to_chat(src, "You have LOOC muted.") return @@ -159,7 +159,7 @@ var/adminbus_ooc_color var/mob/camera/aiEye/E = M if(E.ai) C = E.ai.client - if(C.prefs.toggles & CHAT_LOOC) + if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC) var/display_name = src.key var/is_living = isliving(src.mob) //Ghosts will show up with their ckey, living people show up with their names if(holder) @@ -171,7 +171,7 @@ var/adminbus_ooc_color to_chat(C, "LOOC: [is_living ? src.mob.name : display_name]: [msg]") for(var/client/C in admins) - if(C.prefs.toggles & CHAT_LOOC) + if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC) var/prefix = "(R)LOOC" if (C.mob in heard) prefix = "LOOC" @@ -181,7 +181,7 @@ var/adminbus_ooc_color if (C in admins) return //already been handled - if(C.prefs.toggles & CHAT_LOOC) + if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC) var/display_name = src.key var/is_living = isliving(src.mob) if(holder) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index f70f2f3b3d9..36497534cc4 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -16,7 +16,7 @@ var/global/floorIsLava = 0 log_adminwarn(rendered) for(var/client/C in admins) if(R_ADMIN & C.holder.rights) - if(C.prefs.toggles & CHAT_ATTACKLOGS) + if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_ATTACKLOGS) var/msg = rendered to_chat(C, msg) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 1372cd4d884..d51adaa7499 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -528,7 +528,8 @@ var/list/admin_verbs_mod = list( return var/new_ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color|null if(new_ooccolor) - prefs.ooccolor = new_ooccolor + var/datum/preference_setting/ooc_color = prefs.get_pref_datum(/datum/preference_setting/string/ooc_color) + ooc_color.setting = new_ooccolor prefs.save_preferences_sqlite(src, ckey) feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -577,11 +578,13 @@ var/list/admin_verbs_mod = list( if(!warn_reason) return holder.notes_add(warned_ckey, warn_reason) - if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) + var/datum/preference_setting/warns = D.get_pref_datum(/datum/preference_setting/numerical/warns) + if(++warns.setting >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) var/bantime = AUTOBANTIME//= (++D.warnbans * AUTOBANTIME) - D.warns = 0 - ++D.warnbans - for(var/i = 1; i < D.warnbans; i++) + warns.setting = 0 + var/datum/preference_setting/warnbans = D.get_pref_datum(/datum/preference_setting/numerical/warnbans) + ++warnbans.setting + for(var/i = 1; i < warnbans.setting; i++) bantime *= 2 ban_unban_log_save("[ckey] warned [warned_ckey] - [warn_reason], resulting in a [bantime] minute autoban.") if(C) @@ -595,14 +598,18 @@ var/list/admin_verbs_mod = list( D.save_preferences_sqlite(C, C.ckey) del(C) else + var/warnbans = D.get_pref(/datum/preference_setting/numerical/warnbans) if(C) to_chat(C, "You have been formally warned by an administrator - Reason: [warn_reason].
Further warnings will result in an autoban.") - message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] - [warn_reason]. They have [MAX_WARNS-D.warns] strikes remaining. And have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]") + message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] - [warn_reason]. They have [MAX_WARNS-warnbans] strikes remaining. And have been warn banned [warnbans] [warnbans == 1 ? "time" : "times"]") else - message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC) - [warn_reason]. They have [MAX_WARNS-D.warns] strikes remaining. And have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]") - D.show_warning_next_time = 1 - D.last_warned_message = warn_reason - D.warning_admin = ckey + message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC) - [warn_reason]. They have [MAX_WARNS-warnbans] strikes remaining. And have been warn banned [warnbans] [warnbans == 1 ? "time" : "times"]") + var/datum/preference_setting/show_warning_next_time = D.get_pref_datum(/datum/preference_setting/toggle/show_warning_next_time) + show_warning_next_time.setting = 1 + var/datum/preference_setting/last_warned_message = D.get_pref_datum(/datum/preference_setting/string/last_warned_message) + last_warned_message.setting = warn_reason + var/datum/preference_setting/warning_admin = D.get_pref_datum(/datum/preference_setting/string/last_warned_message) + warning_admin.setting = ckey D.save_preferences_sqlite(C, warned_ckey) feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -627,17 +634,18 @@ var/list/admin_verbs_mod = list( to_chat(src, "Error: unwarn(): No such ckey found.") return - if(D.warns == 0) + if(D.get_pref(/datum/preference_setting/numerical/warns) == 0) to_chat(src, "Error: unwarn(): You can't unwarn someone with 0 warnings, you big dummy.") return - D.warns-=1 - var/strikesleft = MAX_WARNS-D.warns + var/datum/preference_setting/warns = D.get_pref_datum(/datum/preference_setting/numerical/warns) + warns.setting-=1 + var/strikesleft = MAX_WARNS-warns.setting if(C) to_chat(C, "One of your warnings has been removed.
You currently have [strikesleft] strike\s left
") - message_admins("[key_name_admin(src)] has unwarned [key_name_admin(C)]. They have [strikesleft] strike(s) remaining, and have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]") + message_admins("[key_name_admin(src)] has unwarned [key_name_admin(C)]. They have [strikesleft] strike(s) remaining, and have been warn banned [D.get_pref(/datum/preference_setting/numerical/warnbans)] [D.get_pref(/datum/preference_setting/numerical/warnbans) == 1 ? "time" : "times"]") else - message_admins("[key_name_admin(src)] has unwarned [warned_ckey] (DC). They have [strikesleft] strike(s) remaining, and have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]") + message_admins("[key_name_admin(src)] has unwarned [warned_ckey] (DC). They have [strikesleft] strike(s) remaining, and have been warn banned [D.get_pref(/datum/preference_setting/numerical/warnbans)] [D.get_pref(/datum/preference_setting/numerical/warnbans) == 1 ? "time" : "times"]") D.save_preferences_sqlite(C, C.ckey) feedback_add_details("admin_verb","UNWARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 37acebc007d..3ab0afdefd4 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -96,7 +96,7 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey"," if((R_ADMIN|R_MOD) & X.holder.rights) if(X.is_afk()) admin_number_afk++ - if(X.prefs.toggles & SOUND_ADMINHELP) + if(X.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_ADMINHELP) X << 'sound/effects/adminhelp.ogg' X.output_to_special_tab(msg) diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 3f10be75a3e..482abbad8cd 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -137,7 +137,7 @@ //play the receiving admin the adminhelp sound (if they have them enabled) //non-admins shouldn't be able to disable this - if(C.prefs.toggles & SOUND_ADMINHELP) + if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_ADMINHELP) C << 'sound/effects/adminhelp.ogg' /* diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index d291d6e9a7e..21a8d68e1b5 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -11,7 +11,7 @@ to_chat(src, "You cannot send DSAY messages (muted).") return - if(!(prefs.toggles & CHAT_DEAD)) + if(!(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) to_chat(src, "You have deadchat muted.") return @@ -30,10 +30,10 @@ if (istype(M, /mob/new_player) || !M.client) continue - if(M.client && M.client.holder && (M.client.prefs.toggles & CHAT_DEAD)) // show the message to admins who have deadchat toggled on + if(M.client && M.client.holder && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // show the message to admins who have deadchat toggled on M.show_message(rendered, 2) - else if(M.client && M.stat == DEAD && (M.client) && (M.client.prefs.toggles & CHAT_DEAD)) // show the message to regular ghosts who have deadchat toggled on + else if(M.client && M.stat == DEAD && (M.client) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // show the message to regular ghosts who have deadchat toggled on M.show_message(rendered, 2) - feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file + feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index fd40944c2f9..a886f8bdc3b 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -5,6 +5,9 @@ var/list/forbidden_varedit_object_types = list( /datum/subsystem/dbcore/, // No messing with the database. ) +/datum/proc/can_edit_var(var/edited_variable) + return TRUE + //Interface for editing a variable. It returns its new value. If edited_datum, it automatically changes the edited datum's value //If called with just [user] argument, it allows you to create a value such as a string, a number, an empty list, a nearby object, etc... //If called with [edited_datum] and [edited_variable], you gain the ability to get the variable's initial value. @@ -24,6 +27,9 @@ var/list/forbidden_varedit_object_types = list( if(!C.can_edit_var(edited_variable, edited_datum?.type)) return + if(!edited_datum.can_edit_var(edited_variable)) + return + //Special case for "appearance", because appearance values can't be stored anywhere. //It's impossible for this proc to return an appearance value, so just set it directly here if((isimage(edited_datum) || isatom(edited_datum)) && edited_variable == "appearance") diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 5e481caf209..c7e738bf71e 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -26,7 +26,7 @@ for(var/mob/M in player_list) if(!M.client) continue - if(M.client.prefs.toggles & SOUND_MIDI) + if(M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_MIDI) M << uploaded_sound feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 94c28e58328..67dd494ad2a 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -60,7 +60,7 @@ if((R_ADMIN|R_MOD) & C.holder.rights) if(C.is_afk()) admin_number_afk++ - if(C.prefs.toggles & CHAT_PRAYER) + if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_PRAYER) C.output_to_special_tab(msg) C << sound diff --git a/code/modules/bayinstruments/sound_player.dm b/code/modules/bayinstruments/sound_player.dm index 6560c8b0dc6..3df45cc9544 100644 --- a/code/modules/bayinstruments/sound_player.dm +++ b/code/modules/bayinstruments/sound_player.dm @@ -88,7 +88,7 @@ if (some_hearer.ear_deaf > 0) continue // var/dist = get_dist(some_hearer, src) - if (!some_hearer.client.prefs.hear_instruments) + if (!some_hearer.client.prefs.get_pref(/datum/preference_setting/toggle/hear_instruments)) continue present_listeners += some_hearer last_updated_listeners = world.time @@ -103,4 +103,4 @@ // Cease playing return 0 -#undef REFRESH_FREQUENCY \ No newline at end of file +#undef REFRESH_FREQUENCY diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 759a88d4bd7..7e978f5d48d 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -23,6 +23,7 @@ var/bunker_up = 0 //this round var/bunker_saved = 0 //saved to file var/bunker_setting = 0 var/updated_stats = 0 + /client var/account_joined = "" var/account_age @@ -238,7 +239,7 @@ var/updated_stats = 0 holder = admin_datums[ckey] if(holder) - if(prefs.toggles & AUTO_DEADMIN) + if(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & AUTO_DEADMIN) message_admins("[src] was automatically de-admined.") deadmin() verbs += /client/proc/readmin @@ -322,15 +323,16 @@ var/updated_stats = 0 qdel(update_query) qdel(query) - if (prefs && prefs.show_warning_next_time) - to_chat(src, "You, or another user of this ckey ([ckey]) were warned by [prefs.warning_admin].") - to_chat(src, "The reason was: '[prefs.last_warned_message]'.") + if (prefs && prefs.get_pref(/datum/preference_setting/toggle/show_warning_next_time)) + to_chat(src, "You, or another user of this ckey ([ckey]) were warned by [prefs.get_pref(/datum/preference_setting/string/warning_admin)].") + to_chat(src, "The reason was: '[prefs.get_pref(/datum/preference_setting/string/last_warned_message)]'.") to_chat(src, "For more information, you can ask admins in ahelps or at https://ss13.moe. ") to_chat(src, "You can now play the game.") - prefs.show_warning_next_time = 0 + var/datum/preference_setting/show_warning_next_time = prefs.get_pref_datum(/datum/preference_setting/toggle/show_warning_next_time) + show_warning_next_time.setting = 0 prefs.save_preferences_sqlite(src, src.ckey) - if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. + if(prefs.get_pref(/datum/preference_setting/string/changelog) != changelog_hash) //bolds the changelog button on the interface so we know there are updates. winset_wrapper("rpane.changelog", "background-color=#eaeaea;font-style=bold") prefs.SetChangelog(ckey,changelog_hash) to_chat(src, "Changelog has changed since your last visit.") @@ -357,7 +359,7 @@ var/updated_stats = 0 if(!tooltips) tooltips = new /datum/tooltip(src) - fps = (prefs.fps < 0) ? RECOMMENDED_CLIENT_FPS : prefs.fps + fps = (prefs.get_pref(/datum/preference_setting/numerical/fps) < 0) ? RECOMMENDED_CLIENT_FPS : prefs.get_pref(/datum/preference_setting/numerical/fps) // This is wrapped so that if the winset fucks up somehow, this doesn't crash the entire client login proc. /client/proc/winset_wrapper(control_id, params) @@ -501,7 +503,7 @@ var/updated_stats = 0 player_age = 0 if(age < MINIMUM_NON_SUS_ACCOUNT_AGE) for(var/client/X in admins) - if(X.prefs.toggles & SOUND_ADMINHELP) + if(X.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_ADMINHELP) X << 'sound/effects/adminhelp.ogg' message_admins("[ckey(key)]/([src]) is a relatively new player, may consider watching them. AGE = [age] First seen = [player_age]") log_admin(("[ckey(key)]/([src]) is a relatively new player, may consider watching them. AGE = [age] First seen = [player_age]")) @@ -752,7 +754,7 @@ NOTE: You will only be polled about this role once per round. To change your ch return /client/proc/update_special_views() - if(prefs.space_parallax) //Updating parallax for clients that have parallax turned on. + if(prefs.get_pref(/datum/preference_setting/toggle/space_parallax)) //Updating parallax for clients that have parallax turned on. if(parallax_initialized) mob.hud_used.update_parallax_values() @@ -803,7 +805,7 @@ NOTE: You will only be polled about this role once per round. To change your ch ViewFilter = newimages /client/proc/handle_hear_voice(var/mob/origin) - if(prefs.hear_voicesound) + if(prefs.get_pref(/datum/preference_setting/toggle/hear_voicesound)) if(issilicon(origin)) mob.playsound_local(get_turf(origin), get_sfx("voice-silicon"),50,1) if(isvox(origin)) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm deleted file mode 100644 index 7704f11ee6c..00000000000 --- a/code/modules/client/preferences.dm +++ /dev/null @@ -1,1620 +0,0 @@ -#define CHARACTER_SETUP 0 -#define UI_SETUP 1 -#define GENERAL_SETUP 2 -#define SPECIAL_ROLES_SETUP 3 - -var/list/preferences_datums = list() -var/global/list/special_roles = list( - ROLE_ALIEN = 1, - BLOBOVERMIND = 1, - ROLE_BORER = 1, - CHANGELING = 1, - CULTIST = 1, - ROLE_PLANT = 1, - MALF = 1, - NUKE_OP = 1, - ROLE_PAI = 1, - ROLE_POSIBRAIN = 1, - REV = 1, - TRAITOR = 1, - CHALLENGER = 1, - VAMPIRE = 1, - VOXRAIDER = 1, - WIZARD = 1, - ROLE_STRIKE = 1, - GRINCH = 1, - NINJA = 1, - TIMEAGENT = 1, - PULSEDEMON = 1, - ROLE_MINOR = 1, - ROLE_PRISONER = 1, - ROLE_GRUE = 1, - DIVERGENTCLONE = 1, -) - -/var/list/antag_roles = list( - ROLE_ALIEN = 1, - BLOBOVERMIND = 1, - CHANGELING = 1, - CULTIST = 1, - MALF = 1, - NUKE_OP = 1, - REV = 1, - TRAITOR = 1, - CHALLENGER = 1, - VAMPIRE = 1, - VOXRAIDER = 1, - WIZARD = 1, - ROLE_STRIKE = 1, - GRINCH = 1, - NINJA = 1, - TIMEAGENT = 1, - PULSEDEMON = 1, - ROLE_MINOR = 1, - ROLE_PRISONER = 1, - ROLE_GRUE = 1, - DIVERGENTCLONE = 1, -) - -var/list/nonantag_roles = list( - ROLE_BORER = 1, - ROLE_PLANT = 1, - ROLE_PAI = 1, - ROLE_POSIBRAIN = 1, -) - -var/list/role_wiki=list( - ROLE_ALIEN = "Xenomorph", - BLOBOVERMIND = "Blob", - ROLE_BORER = "Cortical_Borer", - CHANGELING = "Changeling", - CULTIST = "Cult", - ROLE_PLANT = "Dionaea", - MALF = "Guide_to_Malfunction", - NUKE_OP = "Nuclear_Agent", - ROLE_PAI = "Personal_AI", - ROLE_POSIBRAIN = "Guide_to_Silicon_Laws", - REV = "Revolution", - TRAITOR = "Traitor", - CHALLENGER = "Challengers", - VAMPIRE = "Vampire", - VOXRAIDER = "Vox_Raider", - WIZARD = "Wizard", - GRINCH = "Grinch", - NINJA = "Space_Ninja", - TIMEAGENT = "Time_Agent", - PULSEDEMON = "Pulse_Demon", - ROLE_MINOR = "Minor_Roles", - ROLE_PRISONER = "Minor_Roles", - ROLE_GRUE = "Grue", - DIVERGENTCLONE = "Divergent_Clone", -) - -var/list/special_popup_text2num = list( - "Only use chat" = SPECIAL_POPUP_DISABLED, - "Only use special" = SPECIAL_POPUP_EXCLUSIVE, - "Use both chat and special" = SPECIAL_POPUP_USE_BOTH, -) - -var/list/headset_sound_text2num = list( - "Disabled" = HEADSET_SOUND_DISABLED, - "Transmit Only" = HEADSET_SOUND_TRANSMIT, - "All" = HEADSET_SOUND_ALL, -) - -var/const/MAX_SAVE_SLOTS = 16 - -#define POLLED_LIMIT 100 - -/datum/preferences - var/list/subsections - //doohickeys for savefiles - var/database/db = ("players2.sqlite") - var/path - var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used - var/slot = 1 - var/list/slot_names = new - var/lastPolled = 0 - - var/savefile_version = 0 - - //non-preference stuff - var/warns = 0 - var/show_warning_next_time = 0 - var/last_warned_message = "" - var/warning_admin = "" - var/warnbans = 0 - var/muted = 0 - var/last_ip - var/last_id - - //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change - var/ooccolor = "#b82e00" - var/UI_style = "Midnight" - var/toggles = TOGGLES_DEFAULT - var/UI_style_color = "#ffffff" - var/UI_style_alpha = 255 - var/space_parallax = 1 - var/space_dust = 1 - var/parallax_speed = 2 - var/special_popup = SPECIAL_POPUP_USE_BOTH - var/tooltips = 1 - var/stumble = 0 //whether the player pauses after their first step - var/hear_voicesound = 0 //Whether the player hears noises when somebody speaks. - //character preferences - var/real_name //our character's name - var/be_random_name = 0 //whether we are a random name every round - var/be_random_body = 0 //whether we'll have a random body every round - var/gender = MALE //gender of character (well duh) - var/age = 30 //age of character - var/underwear = 1 //underwear type - var/backbag = 2 //backpack type - var/h_style = "Bald" //Hair type - var/r_hair = 0 //Hair color - var/g_hair = 0 //Hair color - var/b_hair = 0 //Hair color - var/f_style = "Shaved" //Face hair type - var/r_facial = 0 //Face hair color - var/g_facial = 0 //Face hair color - var/b_facial = 0 //Face hair color - var/s_tone = 0 //Skin color - var/r_eyes = 0 //Eye color - var/g_eyes = 0 //Eye color - var/b_eyes = 0 //Eye color - var/species = "Human" - var/language = "None" //Secondary language - var/hear_instruments = 1 - var/ambience_volume = 25 - var/credits_volume = 75 - var/window_flashing = 1 - var/antag_objectives = 1 //If set to 1, solo antag roles will get the standard objectives. If set to 0, will give them a freeform objective instead. - var/typing_indicator = 1 - - //Mob preview - var/icon/preview_icon = null - var/icon/preview_icon_front = null - var/icon/preview_icon_side = null - var/preview_background = null - var/list/background_options = list("Black", "White", "Tile") - - var/list/jobs = list() - - //Keeps track of preferrence for not getting any wanted jobs - var/alternate_option = RETURN_TO_LOBBY - - var/used_skillpoints = 0 - var/skill_specialization = null - var/list/skills = list() // skills can range from 0 to 3 - - // maps each organ to either null(intact), "cyborg" or "amputated" - // will probably not be able to do this for head and torso ;) - var/list/organ_data = list() - - var/list/player_alt_titles = new() // the default name of a job like "Medical Doctor" - - var/flavor_text = "" - var/med_record = "" - var/sec_record = "" - var/gen_record = "" - var/disabilities = 0 // NOW A BITFIELD, SEE ABOVE - - var/nanotrasen_relation = "Neutral" - var/bank_security = 1 //for bank accounts, 0-2, no-pin,pin,pin&card - var/wage_ratio = 50 - - - // 0 = character settings, 1 = game preferences - var/current_tab = 0 - - // OOC Metadata: - var/metadata = "" - var/slot_name = "" - - // Whether or not to use randomized character slots - var/randomslot = 0 - - // Radio headset static sound - var/headset_sound = HEADSET_SOUND_TRANSMIT - - // jukebox volume - var/volume = 100 - var/usewmp = 0 //whether to use WMP or VLC - - var/list/roles=list() // "role" => ROLEPREF_* - - //attack animation type - var/attack_animation = ITEM_ANIMATION - - var/usenanoui = 1 //Whether or not this client will use nanoUI, this doesn't do anything other than objects being able to check this. - - var/progress_bars = 1 //Whether to show progress bars when doing delayed actions. - - var/pulltoggle = 1 //If 1, the "pull" verb toggles between pulling/not pulling. If 0, the "pull" verb will always try to pull, and do nothing if already pulling. - - var/credits = CREDITS_ALWAYS - var/jingle = JINGLE_CLASSIC - - // Runscape-like chat - var/mob_chat_on_map = TRUE - var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH - var/obj_chat_on_map = FALSE - var/no_goonchat_for_obj = FALSE - - // TGUI things - var/tgui_fancy = TRUE - //// -- Unimplemented for tgui alerts -- - var/tgui_input = FALSE - var/tgui_input_large = FALSE - var/tgui_input_swapped = FALSE - var/tgui_lock = FALSE - var/tgui_scale = TRUE - var/layout_prefs_used = FALSE - - var/fps = -1 - - var/client/client - var/saveloaded = 0 - -/datum/preferences/New(client/C) - client=C - if(istype(C)) - init_subsections() - var/theckey = C.ckey - var/thekey = C.key - spawn() - if(!IsGuestKey(thekey)) - var/load_pref = load_preferences_sqlite(theckey) - if(load_pref) - while(!SS_READY(SShumans)) - sleep(1) - try_load_save_sqlite(theckey, C, default_slot) - return - - while(!SS_READY(SShumans)) - sleep(1) - randomize_appearance_for() - real_name = random_name(gender, species) - save_character_sqlite(theckey, C, default_slot) - saveloaded = 1 - -/datum/preferences/Destroy() - for(var/entry in subsections) - var/datum/preferences_subsection/prefs_ss = subsections[entry] - if(prefs_ss && !prefs_ss.gcDestroyed) - QDEL_NULL(prefs_ss) - ..() - -/datum/preferences/proc/try_load_save_sqlite(var/theckey, var/theclient, var/theslot) - var/attempts = 0 - while(!load_save_sqlite(theckey, theclient, theslot) && attempts < 5) - sleep(15) - attempts++ - if(attempts >= 5)//failsafe so people don't get locked out of the round forever - randomize_appearance_for() - real_name = random_name(gender, species) - log_debug("Player [theckey] FAILED to load save 5 times and has been randomized.") - log_admin("Player [theckey] FAILED to load save 5 times and has been randomized.") - if(theclient) - alert(theclient, "For some reason you've failed to load your save slot 5 times now, so you've been generated a random character. Don't worry, it didn't overwrite your old one.","Randomized Character", "OK") - saveloaded = 1 - theclient << 'sound/misc/prefsready.wav' - -/datum/preferences/proc/setup_character_options(var/dat, var/user) - - - dat += {"

Occupation Choices

- Set Occupation Preferences
-

Identity

-
- Random Name - Always Random Name: [be_random_name ? "Yes" : "No"]
- Name: [real_name]
- Gender: [gender == MALE ? "Male" : "Female"]
- Age: [age] -
-
- Background < >
-
-

Body

- Random Body - Always Random Body: [be_random_body ? "Yes" : "No"]
-
- Species: [species]
- Tertiary Language: [language]
- Skin Tone: [species == "Human" ? "[-s_tone + 35]/220" : "[s_tone]"] - [skintone2racedescription(s_tone, species)]

- Handicaps: Set
- Limbs: Set
- Organs: Set
- Underwear: [gender == MALE ? "[underwear_m[underwear]]" : "[underwear_f[underwear]]"]
- Backpack: [backbaglist[backbag]]
- Nanotrasen Relation:
[nanotrasen_relation]
- Flavor Text:Set
- Character records: - [jobban_isbanned(user, "Records") ? "Banned" : "Set"]
- Bank account security preference:[bank_security_num2text(bank_security)]
- Percent of wages sent to ID virtual wallet:[wage_ratio]
-
-

Hair Style

- [h_style]
- < >
-     Change
-
-

Facial Hair Style

- [f_style]
- < >
-     Change
-
-

Eye Color

-     Change
-
- "} - - return dat - -/datum/preferences/proc/setup_UI(var/dat, var/user) - - - dat += {"UI Style: [UI_style]
- Custom UI(recommended for White UI):    
Color: [UI_style_color]
- Alpha(transparency): [UI_style_alpha]
- "} - - return dat - -/datum/preferences/proc/setup_special(var/dat, var/mob/user) - if(user.client.holder) - dat += {" -

Admin Only Settings

-
- - -
"} - - dat += {" -

General Settings

-
-
- FPS: - [fps]
- Space Parallax: - [space_parallax ? "Enabled" : "Disabled"]
- Parallax Speed: - [parallax_speed]
- Space Dust: - [space_dust ? "Yes" : "No"]
- Play admin midis: - [(toggles & SOUND_MIDI) ? "Yes" : "No"]
- Play lobby music: - [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
- Play Ambience: - [(toggles & SOUND_AMBIENCE) ? "Yes" : "No"]
- [(toggles & SOUND_AMBIENCE)? \ - "Ambience Volume:[ambience_volume]
":""] - Radio Headset Sounds: - [headset_sound_text2num[headset_sound+1]]
- Hear streamed media: - [(toggles & SOUND_STREAMING) ? "Yes" : "No"]
- Streaming Program: - [(usewmp) ? "WMP (compatibility)" : "VLC (requires plugin)"]
- Streaming Volume - [volume]
- Hear player voices - [(hear_voicesound) ? "Yes" : "No"]
- Hear instruments - [(hear_instruments) ? "Yes":"No"]
- Progress Bars: - [(progress_bars) ? "Yes" : "No"]
- Pause after first step: - [(stumble) ? "Yes" : "No"]
- Pulling action: - [(pulltoggle) ? "Toggle Pulling" : "Always Pull"]
- Solo Antag Objectives: - [(antag_objectives) ? "Standard" : "Freeform"]
- Say bubbles: - [(typing_indicator) ? "Active" : "Inactive"]
-
- -
"} - - if(config.allow_Metadata) - dat += "OOC Notes: Edit
" - - return dat - -/datum/preferences/proc/getPrefLevelText(var/datum/job/job) - switch(jobs[job.title]) - if(JOB_PREF_HIGH) - return "High" - if(JOB_PREF_MED) - return "Medium" - if(JOB_PREF_LOW) - return "Low" - return "NEVER" - -/datum/preferences/proc/SetChoices(mob/user, limit = 16, list/splitJobs = list("Chief Engineer", "Head of Security"), widthPerColumn = 295, height = 620) - if(!job_master) - return - - //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. - //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. - //width - Screen' width. Defaults to 550 to make it look nice. - //height - Screen's height. Defaults to 500 to make it look nice. - var/width = widthPerColumn - - - var/HTML = "" - HTML += {""} //the event.button == 1 check is brought to you by legacy IE running in wine - - - HTML += {"
- Choose occupation chances
-
Left-click to raise an occupation preference, right-click to lower it.
- Done

-
- "} - - - var/index = -1 - - //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. - var/datum/job/lastJob - if (!job_master) - return - for(var/datum/job/job in job_master.occupations) - index += 1 - if((index >= limit) || (job.title in splitJobs)) - width += widthPerColumn - if((index < limit) && (lastJob != null)) - //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with - //the last job's selection color. Creating a rather nice effect. - for(var/i = 0, i < (limit - index), i += 1) - HTML += "" - HTML += "
  
" - index = 0 - - HTML += "" - continue - if(!job.player_old_enough(user.client)) - var/available_in_days = job.available_in_days(user.client) - HTML += "[rank]" - continue - if((rank in command_positions) || (rank == "AI"))//Bold head jobs - if(job.alt_titles) - HTML += "[GetPlayerAltTitle(job)]" - else - HTML += "[rank]" - else - if(job.alt_titles) - HTML += "[GetPlayerAltTitle(job)]" - else - HTML += "[rank]" - - - HTML += "" - - - for(var/i = 1, i < (limit - index), i += 1) - HTML += "" - HTML += {"
" - var/rank = job.title - lastJob = job - if(jobban_isbanned(user, rank)) - HTML += "[rank] \[BANNED]
\[IN [(available_in_days)] DAYS]
" - - var/prefLevelLabel = "NEVER" - var/prefLevelColor = "red" - - if(job.species_whitelist.len && !job.species_whitelist.Find(src.species)) - prefLevelLabel = "Unavailable" - prefLevelColor = "gray" - else if(job.species_blacklist.Find(src.species)) - prefLevelLabel = "Unavailable" - prefLevelColor = "gray" - else - switch(jobs[job.title]) - if(JOB_PREF_HIGH) - prefLevelLabel = "High" - prefLevelColor = "slateblue" - if(JOB_PREF_MED) - prefLevelLabel = "Medium" - prefLevelColor = "green" - if(JOB_PREF_LOW) - prefLevelLabel = "Low" - prefLevelColor = "orange" - - HTML += "" - HTML += "[prefLevelLabel]" - HTML += "
  
-
"} - switch(alternate_option) - if(GET_EMPTY_JOB) - HTML += "

Get unique job

" - if(GET_RANDOM_JOB) - HTML += "

Get random job if preferences unavailable

" - if(BE_ASSISTANT) - HTML += "

Be assistant if preference unavailable

" - if(RETURN_TO_LOBBY) - HTML += "

Return to lobby if preference unavailable

" - - - HTML += {"
Reset
- "} - user << browse(null, "window=preferences") - //user << browse(HTML, "window=mob_occupation;size=[width]x[height]") - var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) - popup.set_content(HTML) - popup.open(0) - return - -/datum/preferences/proc/ShowChoices(mob/user) - if(!user || !user.client) - return - update_preview_icon() - var/preview_front = fcopy_rsc(preview_icon_front) - var/preview_side = fcopy_rsc(preview_icon_side) - user << browse_rsc(preview_front, "previewicon.png") - user << browse_rsc(preview_side, "previewicon2.png") - var/dat = "" - - if(!IsGuestKey(user.key)) - - dat += {"
- Slot [slot_name] - - Load slot - - Save slot - - Reload slot -

"} - else - dat += "Please create an account to save your preferences." - - dat += "
Character Settings | " - dat += "UI Settings | " - dat += "General Settings | " - dat += "Special Roles

" - - if(appearance_isbanned(user)) - dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" - - switch(current_tab) - if(CHARACTER_SETUP) - dat = setup_character_options(dat, user) - if(UI_SETUP) - dat = setup_UI(dat, user) - if(GENERAL_SETUP) - dat = setup_special(dat, user) - if(SPECIAL_ROLES_SETUP) - dat = configure_special_roles(dat, user) - - dat += "


" - - if(!IsGuestKey(user.key)) - dat += {"Undo | - Save Setup | "} - - dat += {"Reset Setup -
"} - - //user << browse(HTML_SKELETON(dat), "window=preferences;size=560x580") - var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 680, 640) - popup.set_content(dat) - popup.open(0) - -/datum/preferences/proc/ShowDisabilityState(mob/user,flag,label) - if(flag==DISABILITY_FLAG_FAT && species!="Human") - return "
  • [species] cannot be fat.
  • " - return "
  • [label]: [disabilities & flag ? "Yes" : "No"]
  • " - -/datum/preferences/proc/SetDisabilities(mob/user) - var/HTML = "" - - HTML += {"
    - Choose disabilities
      "} - HTML += ShowDisabilityState(user,DISABILITY_FLAG_NEARSIGHTED,"Needs Glasses") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_FAT, "Obese") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_EPILEPTIC, "Seizures") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_DEAF, "Deaf") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_BLIND, "Blind") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_MUTE, "Mute") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_VEGAN, "Vegan") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_ASTHMA, "Asthma") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_LACTOSE, "Lactose Intolerant") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_LISP, "Lisp") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_ANEMIA, "Anemia") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_EHS, "Electromagnetic Hypersensitivity") - /*HTML += ShowDisabilityState(user,DISABILITY_FLAG_COUGHING, "Coughing") - HTML += ShowDisabilityState(user,DISABILITY_FLAG_TOURETTES, "Tourettes") Still working on it! -Angelite*/ - - - HTML += {"
    - \[Done\] - \[Reset\] -
    "} - user << browse(null, "window=preferences") - user << browse(HTML_SKELETON(HTML), "window=disabil;size=350x300") - return - -/datum/preferences/proc/SetRecords(mob/user) - var/HTML = "" - - HTML += {"
    - Set Character Records
    - Medical Records
    "} - if(length(med_record) <= 40) - HTML += "[med_record]" - else - HTML += "[copytext(med_record, 1, 37)]..." - - HTML += "

    Employment Records
    " - - if(length(gen_record) <= 40) - HTML += "[gen_record]" - else - HTML += "[copytext(gen_record, 1, 37)]..." - - HTML += "

    Security Records
    " - - if(length(sec_record) <= 40) - HTML += "[sec_record]
    " - else - HTML += "[copytext(sec_record, 1, 37)]...
    " - - - HTML += {"
    - \[Done\] -
    "} - user << browse(null, "window=preferences") - user << browse(HTML_SKELETON(HTML), "window=records;size=350x300") - return - - -/datum/preferences/proc/GetPlayerAltTitle(datum/job/job) - var/alt_title = player_alt_titles[job.title] - if(!alt_title || !(alt_title in job.alt_titles)) - return job.title - return alt_title - -/datum/preferences/proc/SetPlayerAltTitle(datum/job/job, new_title) - // remove existing entry - if(player_alt_titles.Find(job.title)) - player_alt_titles -= job.title - // add one if it's not default - if(job.title != new_title) - player_alt_titles[job.title] = new_title - -/datum/preferences/proc/SetJob(mob/user, role, increase) - var/datum/job/job = job_master.GetJob(role) - if(!job) - user << browse(null, "window=mob_occupation") - ShowChoices(user) - return - - if(job.species_blacklist.Find(src.species)) //Check if our species is in the blacklist - to_chat(user, "Your species ("+src.species+") can't have this job!") - return - - if(job.species_whitelist.len) //Whitelist isn't empty - check if our species is in the whitelist - if(!job.species_whitelist.Find(src.species)) - var/allowed_species = "" - for(var/S in job.species_whitelist) - allowed_species += "[S]" - - if(job.species_whitelist.Find(S) != job.species_whitelist.len) - allowed_species += ", " - - to_chat(user, "Only the following species can have this job: [allowed_species]. Your species is ([src.species]).") - return - - var/new_value = jobs[job.title] - if(increase) - new_value += 1 - if(new_value > JOB_PREF_HIGH) - new_value = JOB_PREF_NEVER - else - new_value -= 1 - if(new_value < JOB_PREF_NEVER) - new_value = JOB_PREF_HIGH - - // If setting a job to high, - // set any other job that is currently high to med - if(new_value == JOB_PREF_HIGH) - for(var/some_job in jobs) - if(jobs[some_job] == JOB_PREF_HIGH) - jobs[some_job] = JOB_PREF_MED - jobs[job.title] = new_value - else if(new_value == JOB_PREF_NEVER) - jobs -= job.title - else - jobs[job.title] = new_value - SetChoices(user) - return 1 - -/datum/preferences/proc/ResetJobs() - jobs.Cut() - - -/datum/preferences/proc/process_link(mob/user, list/href_list) - if(!user) - return - var/datum/preferences_subsection/subsection = subsections[href_list["subsection"]] - if(subsection) - var/result = subsection.process_link(user, href_list) - if(result) - return result - //testing("preference=[href_list["preference"]]") - if(href_list["preference"] == "job") - switch(href_list["task"]) - if("close") - user << browse(null, "window=mob_occupation") - ShowChoices(user) - if("reset") - ResetJobs() - SetChoices(user) - if("random") - if(alternate_option == GET_RANDOM_JOB || alternate_option == BE_ASSISTANT || alternate_option == RETURN_TO_LOBBY) - alternate_option += 1 - else if(alternate_option == GET_EMPTY_JOB) - alternate_option = 0 - else - return 0 - SetChoices(user) - if ("alt_title") - var/datum/job/job = locate(href_list["job"]) - if (job) - var/choices = list(job.title) + job.alt_titles - var/choice = input("Pick a title for [job.title].", "Character Generation", GetPlayerAltTitle(job)) as null|anything in choices - if(choice) - SetPlayerAltTitle(job, choice) - SetChoices(user) - if("input") - SetJob(user, href_list["text"], href_list["level"] == "1") - else - SetChoices(user) - return 1 - else if(href_list["preference"] == "disabilities") - - switch(href_list["task"]) - if("close") - user << browse(null, "window=disabil") - ShowChoices(user) - if("reset") - disabilities=0 - SetDisabilities(user) - if("input") - var/dflag=text2num(href_list["disability"]) - if(dflag >= 0) - if(!(dflag==DISABILITY_FLAG_FAT && species!="Human")) - disabilities ^= text2num(href_list["disability"]) //MAGIC - SetDisabilities(user) - else - SetDisabilities(user) - return 1 - - else if(href_list["preference"] == "records") - if(text2num(href_list["record"]) >= 1) - SetRecords(user) - return - else - user << browse(null, "window=records") - if(href_list["task"] == "med_record") - var/medmsg = input(usr,"Set your medical notes here.","Medical Records",html_decode(med_record)) as message - - if(medmsg != null) - medmsg = copytext(medmsg, 1, MAX_PAPER_MESSAGE_LEN) - medmsg = html_encode(medmsg) - - med_record = medmsg - SetRecords(user) - - if(href_list["task"] == "sec_record") - var/secmsg = input(usr,"Set your security notes here.","Security Records",html_decode(sec_record)) as message - - if(secmsg != null) - secmsg = copytext(secmsg, 1, MAX_PAPER_MESSAGE_LEN) - secmsg = html_encode(secmsg) - - sec_record = secmsg - SetRecords(user) - if(href_list["task"] == "gen_record") - var/genmsg = input(usr,"Set your employment notes here.","Employment Records",html_decode(gen_record)) as message - - if(genmsg != null) - genmsg = copytext(genmsg, 1, MAX_PAPER_MESSAGE_LEN) - genmsg = html_encode(genmsg) - - gen_record = genmsg - SetRecords(user) - - else if(href_list["preference"] == "set_roles") - return SetRoles(user,href_list) - - switch(href_list["task"]) - if("random") - switch(href_list["preference"]) - if("name") - real_name = random_name(gender,species) - if("age") - age = rand(AGE_MIN, AGE_MAX) - if("hair") - r_hair = rand(0,255) - g_hair = rand(0,255) - b_hair = rand(0,255) - if("h_style") - h_style = random_hair_style(gender, species) - if("facial") - r_facial = rand(0,255) - g_facial = rand(0,255) - b_facial = rand(0,255) - if("f_style") - f_style = random_facial_hair_style(gender, species) - if("underwear") - underwear = rand(1,underwear_m.len) - ShowChoices(user) - if("eyes") - r_eyes = rand(0,255) - g_eyes = rand(0,255) - b_eyes = rand(0,255) - if("s_tone") - s_tone = random_skin_tone(species) - if("bag") - backbag = rand(1,5) - /*if("skin_style") - h_style = random_skin_style(gender)*/ - if("all") - randomize_appearance_for() //no params needed - if("input") - switch(href_list["preference"]) - if("name") - var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) - if(new_name) - real_name = new_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ', some diacritics, and .") - if("next_hair_style") - h_style = next_list_item(h_style, valid_sprite_accessories(hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress - if("previous_hair_style") - h_style = previous_list_item(h_style, valid_sprite_accessories(hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress - if("next_facehair_style") - f_style = next_list_item(f_style, valid_sprite_accessories(facial_hair_styles_list, gender, species)) - if("previous_facehair_style") - f_style = previous_list_item(f_style, valid_sprite_accessories(facial_hair_styles_list, gender, species)) - if("next_preview_background") - preview_background = next_list_item(preview_background, background_options) - if("previous_preview_background") - preview_background = previous_list_item(preview_background, background_options) - if("age") - var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null - if(new_age) - age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) - if("species") - var/prev_species = species - if(check_rights(R_ADMIN,0)) - species = input("Please select a species", "Character Generation", null) in whitelisted_species - else - species = input("Please select a species", "Character Generation", null) in playable_species - - if(prev_species != species) - //grab one of the valid hair styles for the newly chosen species - var/list/valid_hairstyles = valid_sprite_accessories(hair_styles_list, gender, species, HAIRSTYLE_CANTRIP) - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - else - //this shouldn't happen - h_style = hair_styles_list["Bald"] - - //grab one of the valid facial hair styles for the newly chosen species - var/list/valid_facialhairstyles = valid_sprite_accessories(facial_hair_styles_list, gender, species) - if(valid_facialhairstyles.len) - f_style = pick(valid_facialhairstyles) - else - //this shouldn't happen - f_style = facial_hair_styles_list["Shaved"] - - //reset hair colour and skin colour - r_hair = 0//hex2num(copytext(new_hair, 2, 4)) - g_hair = 0//hex2num(copytext(new_hair, 4, 6)) - b_hair = 0//hex2num(copytext(new_hair, 6, 8)) - - s_tone = 0 - - for(var/datum/job/job in job_master.occupations) - if(job.species_blacklist.Find(species)) //If new species is in a job's blacklist - jobs -= job.title - to_chat(usr, "Your new species ([species]) is blacklisted from [job.title].") - - if(job.species_whitelist.len) //If the job has a species whitelist - if(!job.species_whitelist.Find(species)) //And it doesn't include our new species - if(jobs.Remove(job.title)) - to_chat(usr, "Your new species ([species]) can't be [job.title]. Your preferences have been adjusted.") - - if("language") - var/list/new_languages = list("None") - - for(var/L in all_languages) - var/datum/language/lang = all_languages[L] - if(lang.flags & CAN_BE_SECONDARY_LANGUAGE) - new_languages += lang.name - - language = input("Please select a secondary language", "Character Generation", null) in new_languages - - if("metadata") - var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null - if(new_metadata) - metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) - - if("hair") - if(species == "Human" || species == "Unathi" || species == "Diona" || species == "Mushroom") - var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference", rgb(r_hair, g_hair, b_hair)) as color|null - if(new_hair) - r_hair = hex2num(copytext(new_hair, 2, 4)) - g_hair = hex2num(copytext(new_hair, 4, 6)) - b_hair = hex2num(copytext(new_hair, 6, 8)) - if(species == "Vox") - var/new_hair_vox = input(user, "Choose your character's hair color:", "Character Preference") as null|anything in list("Green", "Azure", "Brown", "Emerald", "Gray", "Light Green", "Green-Brown") - if(new_hair_vox) - r_hair = haircolordesc(new_hair_vox) - to_chat(user,"Your hair will now be [new_hair_vox] in color.") - if(species == "Insectoid") - var/carapace = input(user, "Choose your character's carapace colour, color values will be adjusted to between 35 and 80:", "Character Preference", rgb(r_hair, g_hair, b_hair)) as color|null - if(carapace) - r_hair = hex2num(copytext(carapace, 2, 4)) - g_hair = hex2num(copytext(carapace, 4, 6)) - b_hair = hex2num(copytext(carapace, 6, 8)) - r_hair = clamp(r_hair, 0, 80) - g_hair = clamp(g_hair, 0, 50) - b_hair = clamp(b_hair, 0, 35) - if("h_style") - var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_sprite_accessories(hair_styles_list, null, species) //gender intentionally left null so speshul snowflakes can cross-hairdress - if(new_h_style) - h_style = new_h_style - - if("facial") - if(species == "Human" || species == "Unathi") - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", rgb(r_facial, g_facial, b_facial)) as color|null - if(new_facial) - r_facial = hex2num(copytext(new_facial, 2, 4)) - g_facial = hex2num(copytext(new_facial, 4, 6)) - b_facial = hex2num(copytext(new_facial, 6, 8)) - - if("f_style") - var/new_f_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in valid_sprite_accessories(facial_hair_styles_list, gender, species) - if(new_f_style) - f_style = new_f_style - - if("underwear") - var/list/underwear_options - if(gender == MALE) - underwear_options = underwear_m - else - underwear_options = underwear_f - - 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) - ShowChoices(user) - - if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", rgb(r_eyes, g_eyes, b_eyes)) as color|null - if(new_eyes) - r_eyes = hex2num(copytext(new_eyes, 2, 4)) - g_eyes = hex2num(copytext(new_eyes, 4, 6)) - b_eyes = hex2num(copytext(new_eyes, 6, 8)) - - if("s_tone") - if(species == "Human") - var/new_s_tone = input(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference") as num|null - if(new_s_tone) - s_tone = 35 - clamp(new_s_tone,1,220) - to_chat(user,"You're now [skintone2racedescription(s_tone, species)].") - else if(species == "Vox")//Can't reference species flags here, sorry. - var/skin_c = input(user, "Choose your Vox's skin color:\n(1 = Green, 2 = Brown, 3 = Gray, 4 = Light Green, 5 = Azure, 6 = Emerald)", "Character Preference") as num|null - if(skin_c) - s_tone = clamp(skin_c,1,6) - to_chat(user,"You will now be [skintone2racedescription(s_tone,species)] in color.") - else if(species == "Grey") - var/skin_c = input(user, "Choose your Grey's skin color:\n(1 = Gray, 2 = Light, 3 = Green, 4 = Blue)", "Character Preference") as num|null - if(skin_c) - s_tone = clamp(skin_c,1,4) - to_chat(user,"You will now be [skintone2racedescription(s_tone,species)] in color.") - else if(species == "Insectoid") - var/carapace = input(user, "Choose your character's carapace colour, color values will be adjusted to between 35 and 80:", "Character Preference", rgb(r_hair, g_hair, b_hair)) as color|null - if(carapace) - r_hair = hex2num(copytext(carapace, 2, 4)) - g_hair = hex2num(copytext(carapace, 4, 6)) - b_hair = hex2num(copytext(carapace, 6, 8)) - r_hair = clamp(r_hair, 0, 80) - g_hair = clamp(g_hair, 0, 50) - b_hair = clamp(b_hair, 0, 35) - else - to_chat(user,"Your species doesn't have different skin tones. Yet?") - return - - if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null - if(new_ooccolor) - ooccolor = new_ooccolor - - if("bag") - var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in backbaglist - if(new_backbag) - backbag = backbaglist.Find(new_backbag) - - if("nt_relation") - var/new_relation = input(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference") as null|anything in list("Loyal", "Supportive", "Neutral", "Skeptical", "Opposed") - if(new_relation) - nanotrasen_relation = new_relation - - if("bank_security") - var/new_bank_security = input(user, BANK_SECURITY_EXPLANATION, "Character Preference") as null|anything in bank_security_text2num_associative - if(!isnull(new_bank_security)) - bank_security = bank_security_text2num_associative[new_bank_security] - - if("wage_ratio") - var/new_wage_ratio = input(user, "Input what % of wages end up in virtual wallets, from 0-100", "Character Preference",wage_ratio) as num - if(!isnull(new_wage_ratio)) - new_wage_ratio = clamp(new_wage_ratio,0,100) - wage_ratio = new_wage_ratio - - if("flavor_text") - flavor_text = input(user,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(flavor_text)) as message - - if("skin_style") - var/skin_style_name = input(user, "Select a new skin style") as null|anything in list("default1", "default2", "default3") - if(!skin_style_name) - return - - else - switch(href_list["preference"]) - if("fps") - var/desired_fps = input(user, "Choose your desired frames per second.\n\ -WARNING: BYOND versions earlier than 513.1523 might not work properly with values other than 0.\n\ -Set this to -1 to use the recommended value.\n\ -Set this to 0 to use the server's FPS (currently [world.fps])\n\ -Values up to 1000 are allowed.", "FPS", fps) as null|num - if(isnull(desired_fps)) - return - if(desired_fps < 0) - desired_fps = -1 - desired_fps = sanitize_integer(desired_fps, -1, 1000, fps) - fps = desired_fps - client.fps = (fps < 0) ? RECOMMENDED_CLIENT_FPS : fps - if("gender") - if(gender == MALE) - gender = FEMALE - else - gender = MALE - f_style = random_facial_hair_style(gender, species) - h_style = random_hair_style(gender, species) - - if("hear_adminhelps") - toggles ^= SOUND_ADMINHELP - - if("ui") - switch(UI_style) - if("Midnight") - UI_style = "Orange" - if("Orange") - UI_style = "old" - if("old") - UI_style = "White" - else - UI_style = "Midnight" - - if("UIcolor") - var/UI_style_color_new = input(user, "Choose your UI colour, dark colours are not recommended!") as color|null - if(!UI_style_color_new) - return - UI_style_color = UI_style_color_new - - if("UIalpha") - var/UI_style_alpha_new = input(user, "Select a new alpha(transparency) parameter for UI, between 50 and 255") as num - if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) - return - UI_style_alpha = UI_style_alpha_new - - if("parallax") - space_parallax = !space_parallax - if(user && user.hud_used) - user.hud_used.update_parallax_existence() - - if("dust") - space_dust = !space_dust - if(user && user.hud_used) - user.hud_used.update_parallax_existence() - - if("p_speed") - parallax_speed = min(max(input(user, "Enter a number between 0 and 5 included (default=2)","Parallax Speed Preferences",parallax_speed),0),5) - - if("name") - be_random_name = !be_random_name - - if("all") - be_random_body = !be_random_body - - if("special_popup") - var/choice = input(user, "Set your special tab preferences:", "Settings") as null|anything in special_popup_text2num - if(!isnull(choice)) - special_popup = special_popup_text2num[choice] - - if("randomslot") - randomslot = !randomslot - - if("hear_midis") - toggles ^= SOUND_MIDI - if(!(toggles & SOUND_MIDI)) - user << sound(null, repeat = 0, wait = 0, volume = 0, channel = CHANNEL_ADMINMUSIC) - - if("lobby_music") - if(config.no_lobby_music) - to_chat(user, "DEBUG: Lobby music is globally disabled via server config.") - toggles ^= SOUND_LOBBY - if(toggles & SOUND_LOBBY) - if(istype(user,/mob/new_player)) - user << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBY) - else - user << sound(null, repeat = 0, wait = 0, volume = 0, channel = CHANNEL_LOBBY) - - if("volume") - user.client.set_new_volume() - - if("ambience") - if(config.no_ambience) - to_chat(user, "DEBUG: Ambience is globally disabled via server config.") - toggles ^= SOUND_AMBIENCE - if(!(toggles & SOUND_AMBIENCE)) - user << sound(null, repeat = 0, wait = 0, volume = 0, channel = CHANNEL_AMBIENCE) - if("ambience_volume") - ambience_volume = min(max(input(user, "Enter the new volume you wish to use. (0-100)","Ambience Volume Preferences", ambience_volume), 0), 100) - - if("headset_sound") - var/choice = input(user, "Set your radio headset sound preferences:", "Settings") as null|anything in headset_sound_text2num - if(!isnull(choice)) - headset_sound = headset_sound_text2num[choice] - - if("jukebox") - toggles ^= SOUND_STREAMING - - if("wmp") - usewmp = !usewmp - if(!user.client.media) - return - user.client.media.stop_music() - user.client.media.playerstyle = (usewmp ? PLAYER_OLD_HTML : PLAYER_HTML) - if(toggles & SOUND_STREAMING) - user.client.media.open() - user.client.media.update_music() - - if("tooltips") - tooltips = !tooltips - if("progbar") - progress_bars = !progress_bars - if("stumble") - stumble = !stumble - if("hear_voicesound") - hear_voicesound = !hear_voicesound - if("hear_instruments") - hear_instruments = !hear_instruments - if("pulltoggle") - pulltoggle = !pulltoggle - - if("ghost_deadchat") - toggles ^= CHAT_DEAD - - if("ghost_ears") - toggles ^= CHAT_GHOSTEARS - - if("ghost_sight") - toggles ^= CHAT_GHOSTSIGHT - - if("ghost_radio") - toggles ^= CHAT_GHOSTRADIO - - if("ghost_pda") - toggles ^= CHAT_GHOSTPDA - - if("show_ooc") - toggles ^= CHAT_OOC - - if("show_looc") - toggles ^= CHAT_LOOC - - if("save") - if(world.timeofday >= (lastPolled + POLLED_LIMIT) || user.client.holder) - save_preferences_sqlite(user, user.ckey) - save_character_sqlite(user.ckey, user, default_slot) - lastPolled = world.timeofday - else - to_chat(user, "You need to wait [round((((lastPolled + POLLED_LIMIT) - world.timeofday) / 10))] seconds before you can save again.") - //random_character_sqlite(user, user.ckey) - - if("reload") - load_preferences_sqlite(user.ckey) - load_save_sqlite(user.ckey, user, default_slot) - - if("open_load_dialog") - if(!IsGuestKey(user.key)) - open_load_dialog(user) - // DO NOT update window as it'd steal focus. - return - - if("close_load_dialog") - close_load_dialog(user) - - if("changeslot") - var/num = text2num(href_list["num"]) - load_save_sqlite(user.ckey, user, num) - default_slot = num - close_load_dialog(user) - - if("tab") - if(href_list["tab"]) - current_tab = text2num(href_list["tab"]) - - if("attack_animation") - if(attack_animation == NO_ANIMATION) - item_animation_viewers |= client - attack_animation = ITEM_ANIMATION - - else if(attack_animation == ITEM_ANIMATION) - attack_animation = PERSON_ANIMATION - person_animation_viewers |= client - item_animation_viewers -= client - - else if(attack_animation == PERSON_ANIMATION) - attack_animation = NO_ANIMATION - person_animation_viewers -= client - - if("credits") - switch(credits) - if(CREDITS_NEVER) - credits = CREDITS_ALWAYS - if(CREDITS_ALWAYS) - credits = CREDITS_NO_RERUNS - if(CREDITS_NO_RERUNS) - credits = CREDITS_NEVER - - if("jingle") - switch(jingle) - if(JINGLE_NEVER) - jingle = JINGLE_CLASSIC - if(JINGLE_CLASSIC) - jingle = JINGLE_ALL - if(JINGLE_ALL) - jingle = JINGLE_NEVER - - if("credits_volume") - credits_volume = min(max(input(user, "Enter the new volume you wish to use. (0-100, default is 75)","Credits/Jingle Volume", credits_volume), 0), 100) - - if("window_flashing") - window_flashing = !window_flashing - - if("antag_objectives") - antag_objectives = !antag_objectives - - if("typing_indicator") - typing_indicator = !typing_indicator - - if("tgui_fancy") - tgui_fancy = !tgui_fancy - - if ("mob_chat_on_map") - mob_chat_on_map = !mob_chat_on_map - - if ("obj_chat_on_map") - obj_chat_on_map = !obj_chat_on_map - - if ("max_chat_length") - max_chat_length = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [initial(max_chat_length)]))", "Character Preference", max_chat_length) as null|num - - if ("no_goonchat_for_obj") - no_goonchat_for_obj = !no_goonchat_for_obj - - - if(user.client.holder) - switch(href_list["preference"]) - if("hear_ahelp") - toggles ^= SOUND_ADMINHELP - - if("hear_prayer") - toggles ^= CHAT_PRAYER - - if("hear_radio") - toggles ^= CHAT_GHOSTRADIO - - if("hear_attack") - toggles ^= CHAT_ATTACKLOGS - - if("hear_debug") - toggles ^= CHAT_DEBUGLOGS - - if("auto_deadmin") - toggles ^= AUTO_DEADMIN - - ShowChoices(user) - return 1 - -/datum/preferences/proc/copy_to(mob/living/carbon/human/character, safety = 0) - if(be_random_name) - real_name = random_name(gender,species) - if(config.humans_need_surnames && species == "Human") - var/firstspace = findtext(real_name, " ") - var/name_length = length(real_name) - if(!firstspace) //we need a surname - real_name += " [pick(last_names)]" - else if(firstspace == name_length) - real_name += "[pick(last_names)]" - - character.real_name = real_name - character.name = character.real_name - character.flavor_text = flavor_text - if(character.dna) - character.dna.real_name = character.real_name - character.dna.flavor_text = character.flavor_text - - character.med_record = med_record - character.sec_record = sec_record - character.gen_record = gen_record - - - if(be_random_body) - //random_character(gender) - This just selects a random character from the OLD character database. - randomize_appearance_for() // Correct. - - character.setGender(gender) - character.age = age - - character.my_appearance.r_eyes = r_eyes - character.my_appearance.g_eyes = g_eyes - character.my_appearance.b_eyes = b_eyes - - character.my_appearance.r_hair = r_hair - character.my_appearance.g_hair = g_hair - character.my_appearance.b_hair = b_hair - - character.my_appearance.r_facial = r_facial - character.my_appearance.g_facial = g_facial - character.my_appearance.b_facial = b_facial - - character.my_appearance.s_tone = s_tone - - character.my_appearance.h_style = h_style - character.my_appearance.f_style = f_style - - character.dna.ResetUIFrom(character) - - character.skills = skills - - // Destroy/cyborgize organs - - for(var/name in organ_data) - var/datum/organ/external/O = character.organs_by_name[name] - var/datum/organ/internal/I = character.internal_organs_by_name[name] - var/status = organ_data[name] - - if(status == "amputated") - O.status &= ~ORGAN_ROBOT - O.status &= ~ORGAN_PEG - O.amputated = 1 - O.status |= ORGAN_DESTROYED - O.destspawn = 1 - else if(status == "cyborg") - O.status &= ~ORGAN_PEG - O.status |= ORGAN_ROBOT - else if(status == "peg") - O.status &= ~ORGAN_ROBOT - O.status |= ORGAN_PEG - else if(status == "assisted") - I?.mechassist() - else if(status == "mechanical") - I?.mechanize() - else - continue - var/datum/species/chosen_species = all_species[species] - if( (disabilities & DISABILITY_FLAG_FAT) && (chosen_species.anatomy_flags & CAN_BE_FAT) ) - character.mutations += M_FAT - if(disabilities & DISABILITY_FLAG_NEARSIGHTED) - character.disabilities|=NEARSIGHTED - if(disabilities & DISABILITY_FLAG_EPILEPTIC) - character.disabilities|=EPILEPSY - if(disabilities & DISABILITY_FLAG_EHS) - character.disabilities|=ELECTROSENSE - if(disabilities & DISABILITY_FLAG_DEAF) - character.sdisabilities|=DEAF - if(disabilities & DISABILITY_FLAG_BLIND) - character.sdisabilities|=BLIND - /*if(disabilities & DISABILITY_FLAG_COUGHING) - character.sdisabilities|=COUGHING - if(disabilities & DISABILITY_FLAG_TOURETTES) - character.sdisabilities|=TOURETTES Still working on it. - Angelite */ - - if(underwear > underwear_m.len || underwear < 1) - underwear = 0 //I'm sure this is 100% unnecessary, but I'm paranoid... sue me. //HAH NOW NO MORE MAGIC CLONING UNDIES - character.underwear = underwear - - if(backbag > 5 || 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(character) && !ismushroom(character)) //Ghosts and mushroom people are neuter by default - message_admins("[character] ([character.ckey]) has spawned with their gender as plural or neuter. Please notify coders.") - character.setGender(MALE) - -/datum/preferences/proc/open_load_dialog(mob/user) - var/database/query/q = new - var/list/name_list[MAX_SAVE_SLOTS] - - q.Add("select real_name, player_slot from players where player_ckey=?", user.ckey) - if(q.Execute(db)) - while(q.NextRow()) - name_list[q.GetColumn(2)] = q.GetColumn(1) - else - message_admins("Error in open_load_dialog [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - warning("Error in open_load_dialog [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - var/dat = "
    Select a character slot to load
    " - var/counter = 1 - while(counter <= MAX_SAVE_SLOTS) - if(counter==default_slot) - dat += "[name_list[counter]]
    " - else - if(!name_list[counter]) - dat += "Character[counter]
    " - else - dat += "[name_list[counter]]
    " - counter++ - - dat += "
    " - - var/datum/browser/browser = new(user, "saves", null, 300, 340) - browser.set_content(dat) - browser.open(use_onclose=FALSE) - -/datum/preferences/proc/close_load_dialog(mob/user) - user << browse(null, "window=saves") - -/datum/preferences/proc/configure_special_roles(var/dat, var/mob/user) - dat+={" -

    Special Role Preferences

    -

    Please note that this also handles in-round polling for things like Raging Mages and Borers.

    -
    - Legend -
    -
    Never:
    -
    Decline this role for this round and all future rounds. You will not be polled again.
    -
    No:
    -
    Default. Decline this role for this round only.
    -
    Yes:
    -
    Accept this role for this round only.
    -
    Always:
    -
    Accept this role for this round and all future rounds. You will not be polled again.
    -
    -
    -
    - "} - - for(var/list/table_type in list(antag_roles,nonantag_roles)) - dat += {" -
    - - - "} - if(table_type == antag_roles && isantagbanned(user)) - dat += "" - else - for(var/role_id in table_type) - dat += "" - if(table_type[role_id]) //if mode is available on the server - if(jobban_isbanned(user, role_id) || (role_id == "pai candidate" && jobban_isbanned(user, "pAI")) || (role_id == MALF && jobban_isbanned(user, "AI"))) - dat += "" - else - var/wikiroute = role_wiki[role_id] - var/desire = get_role_desire_str(roles[role_id]) - dat += {" - - - - - "} - dat += "

    [table_type == nonantag_roles ? "Non-" : ""]Antagonist Roles

    You are banned from antagonist roles

    [capitalize(role_id)]\[BANNED][wikiroute ? "(Wiki)" : "(Wiki)"]NeverNoYesAlways
    " - dat += "

    " - - return dat - -/datum/preferences/proc/SetRoles(var/mob/user, var/list/href_list) - // We just grab the role from the POST(?) data. - for(var/role_id in special_roles) - if(role_id in href_list) - roles[role_id] = text2num(href_list[role_id]) - ShowChoices(user) - return 1 - -/client/verb/modify_preferences(page as num) - set name = "modifypreferences" - set hidden = 1 - if(!prefs.saveloaded) - to_chat(src, "Your character preferences have not yet loaded.") - return - switch(page) - if(1) - prefs.current_tab = GENERAL_SETUP - if(2) - prefs.current_tab = SPECIAL_ROLES_SETUP - prefs.ShowChoices(usr) diff --git a/code/modules/client/preferences/pref_datums.dm b/code/modules/client/preferences/pref_datums.dm new file mode 100644 index 00000000000..83c1ea9d06e --- /dev/null +++ b/code/modules/client/preferences/pref_datums.dm @@ -0,0 +1,227 @@ +// -- Preference datums for easy, maintainable prefs to add -- + +/datum/preference_setting + var/name = "Abstract preference setting" // Actual name + var/sql_name = "" // Name in the sql query + var/sql_table = "abstract" + + var/enabled = FALSE // Is it actually used in game? + + var/default_setting + var/setting = null + + var/saved_as_string = FALSE // This is ENTIRELY from historical reasons! Do not mess with it, or risk the pref not working! + var/datum/preferences/parent + +/datum/preference_setting/New(var/datum/preferences/parent_prefs) + parent = parent_prefs + setting = default_setting + return ..() + +/datum/preference_setting/Destroy() + parent = null + return ..() + +// This is to prevent admins `accidentally` breaking the database. +/datum/preference_setting/can_edit_var(var/edited_variable) + switch (edited_variable) + // Block varedits related to SQL + if ("sql_table", "sql_name", "enabled", "parent", "saved_as_string") + return + else + return ..() + +// Change setting to new_setting +/datum/preference_setting/proc/set_setting(var/new_setting) + new_setting = sanitize_setting(new_setting) + setting = new_setting + return setting + +// Sanity checks +/datum/preference_setting/proc/sanitize_setting(var/new_setting) + return new_setting ? new_setting : default_setting // Return the default setting in case of null + +// Convert the raw SQL value into something byond-readable +// For HISTORICAL REASONS (forma de technical debt), a lot of stuff is saved in the DB as a string +// so usually, this means converting whatever you have from a string into a number. +// Other examples include: the job list is loaded as a json string. +/datum/preference_setting/proc/load_sql(var/sql_value) + return sql_value + +// Opposite operation. +// At the moment, this is only relevant for: +// - Player alt-titles, saved as some weird string; +// - jobs list, saved as JSON +/datum/preference_setting/proc/save_sql() + return setting + +// Handles the href changes (including things like input(), randomisation, etc) + +/datum/preference_setting/proc/process_link(var/task, var/mob/user, var/list/href_list) // href_list is for extra data. + switch (task) + if ("random") + randomise(user) + return TRUE + if ("input") + choose_setting(user) + return TRUE + return FALSE // Need to do something more + +// These are wrapped +/datum/preference_setting/proc/randomise(var/mob/user) + +/datum/preference_setting/proc/choose_setting(var/mob/user) + +// -- Abstract categories + +// Binary toggles +/datum/preference_setting/toggle + default_setting = TRUE + +/datum/preference_setting/toggle/sanitize_setting(var/new_setting) + return sanitize_integer(new_setting, 0, 1, default_setting) + +/datum/preference_setting/toggle/load_sql(var/sql_value) + if (saved_as_string) + return text2num(sql_value) + else + return sql_value + +/datum/preference_setting/toggle/save_sql() + if (saved_as_string) + return num2text(setting) + else + return setting + +/datum/preference_setting/toggle/choose_setting(var/mob/user) + setting = !setting + parent.ShowChoices(user) + +// Numerical values +/datum/preference_setting/numerical + default_setting = 0 + var/min_value = 0 + var/max_value = 100 + +/datum/preference_setting/numerical/sanitize_setting(var/new_setting) + return sanitize_integer(new_setting, min_value, max_value, default_setting) + +/datum/preference_setting/numerical/load_sql(var/sql_value) + if (saved_as_string) + return text2num(sql_value) + else + return sql_value + +/datum/preference_setting/numerical/save_sql() + if (saved_as_string) + return num2text(setting) + else + return setting + +/datum/preference_setting/numerical/randomise() + setting = rand(min_value, max_value) + +// Floating point values + +// Numerical values +/datum/preference_setting/float + default_setting = 0 + var/min_value = 0 + var/max_value = 1 + +/datum/preference_setting/float/sanitize_setting(var/new_setting) + if (isnum(new_setting)) + return clamp(new_setting, min_value, max_value) + return default_setting + +/datum/preference_setting/float/load_sql(var/sql_value) + if (saved_as_string) + return text2num(sql_value) + else + return sql_value + +/datum/preference_setting/float/save_sql() + if (saved_as_string) + return num2text(setting) + else + return setting + +// Binary flags +/datum/preference_setting/binary_flag + default_setting = 0 + var/max_binary_value = (1 << 12) + var/toggles = list() // The actual flag toggles this is checking against + +/datum/preference_setting/binary_flag/sanitize_setting(var/new_setting) + return sanitize_integer(new_setting, 0, max_binary_value, default_setting) + +/datum/preference_setting/binary_flag/load_sql(var/sql_value) + if (saved_as_string) + return text2num(sql_value) + else + return sql_value + +/datum/preference_setting/binary_flag/save_sql() + if (saved_as_string) + return num2text(setting) + else + return setting + +// Strings +/datum/preference_setting/string + default_setting = "" + var/max_length = 0 + +/datum/preference_setting/string/sanitize_setting(var/new_setting) + return sanitize_text(new_setting, default_setting) + + +// "Enums" +/datum/preference_setting/enum + default_setting = null + var/allowed_values = list(null) + +/datum/preference_setting/enum/sanitize_setting(var/new_setting) + if (!(new_setting in allowed_values)) + return default_setting + return new_setting + +/datum/preference_setting/enum/load_sql(var/sql_value) + if (saved_as_string) + return text2num(sql_value) + else + return sql_value + +/datum/preference_setting/enum/save_sql() + if (saved_as_string) + return num2text(setting) + else + return setting + +/datum/preference_setting/enum/string + saved_as_string = FALSE + +// -- Typically of the form: +/* list( + * ROLE_ONE = JOB_PREF_HIGH, + * ROLE_TWO = JOB_PREF_LOW, + * ) + * + */ + +/datum/preference_setting/assoc_list_setting + default_setting = list() + var/allowed_values_for_list_items = list() // Alllowed values for the assoc list in the setting. First value = default. + +/datum/preference_setting/assoc_list_setting/sanitize_setting(var/new_setting) + if (!islist(new_setting)) + return default_setting + for (var/data in new_setting) + if (!(new_setting[data] in allowed_values_for_list_items)) + new_setting[data] = allowed_values_for_list_items[1] + return new_setting + +// -- For general lists (this is only used for alt-titles now) -- + +/datum/preference_setting/list_values + default_setting = list() diff --git a/code/modules/client/preferences/pref_datums_character.dm b/code/modules/client/preferences/pref_datums_character.dm new file mode 100644 index 00000000000..7401303a2d6 --- /dev/null +++ b/code/modules/client/preferences/pref_datums_character.dm @@ -0,0 +1,861 @@ +//our character's name +/datum/preference_setting/string/real_name + name = "Character name" + sql_name = "real_name" + + sql_table = "players" + + enabled = TRUE + + default_setting = "John D. Spessman" + +/datum/preference_setting/string/real_name/sanitize_setting(var/new_setting) + return reject_bad_name(new_setting) + +/datum/preference_setting/string/real_name/randomise(var/mob/user) + var/gender = parent.get_pref(/datum/preference_setting/enum/gender) + var/species = parent.get_pref(/datum/preference_setting/string/species) + setting = random_name(gender, species) + parent.ShowChoices(user) + return setting + +/datum/preference_setting/string/real_name/choose_setting(var/mob/user) + var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) + if(new_name) + setting = new_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ', some diacritics, and .") + parent.ShowChoices(user) + +//whether we are a random name every round +/datum/preference_setting/toggle/be_random_name + name = "Be random name" + sql_name = "random_name" + + sql_table = "players" + + enabled = TRUE + + default_setting = FALSE + +//whether we are a random name every round +/datum/preference_setting/toggle/be_random_body + name = "Be random body" + sql_name = "random_body" + + sql_table = "players" + + enabled = TRUE + + default_setting = FALSE + +//gender crticial theory [gender of character (well duh)] +/datum/preference_setting/enum/gender + name = "Gender" + sql_name = "gender" + + sql_table = "players" + + enabled = TRUE + + default_setting = MALE // smh + allowed_values = list(MALE, FEMALE) + +/datum/preference_setting/enum/gender/choose_setting(mob/user) + if(setting == MALE) + setting = FEMALE + else + setting = MALE + + var/datum/preference_setting/species = parent.get_pref_datum(/datum/preference_setting/string/species) + var/datum/preference_setting/f_style = parent.get_pref_datum(/datum/preference_setting/string/f_style) + var/datum/preference_setting/h_style = parent.get_pref_datum(/datum/preference_setting/string/h_style) + + f_style.setting = random_facial_hair_style(setting, species) + h_style.setting = random_hair_style(setting, species) + parent.ShowChoices(user) + +/datum/preference_setting/enum/gender/sanitize_setting(var/new_setting) + return sanitize_gender(new_setting) // Historical reasons + +//age of character +/datum/preference_setting/numerical/age + name = "Age" + sql_name = "age" + + sql_table = "players" + + enabled = TRUE + + default_setting = 30 // smh + min_value = AGE_MIN + max_value = AGE_MAX + +/datum/preference_setting/numerical/age/choose_setting(mob/user) + var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null + if(new_age) + setting = clamp(round(new_age), min_value, max_value) + parent.ShowChoices(user) + +//underwear +// This is an integer for historical reasons (you'll read that a lot in this file) +/datum/preference_setting/numerical/underwear + name = "Underwear" + sql_name = "underwear" + + sql_table = "body" + + enabled = TRUE + + default_setting = UNDERWEAR_MALE_NONE + min_value = UNDERWEAR_MALE_NONE + max_value = UNDERWEAR_FEMALE_BLACK_HUSBANDBEATER + +/datum/preference_setting/numerical/underwear/choose_setting(var/mob/user) + var/datum/preference_setting/gender = parent.get_pref_datum(/datum/preference_setting/enum/gender) + var/list/underwear_options + if(gender.setting == MALE) + underwear_options = underwear_m + else + underwear_options = underwear_f + + var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_options + if(new_underwear) + setting = underwear_options.Find(new_underwear) + parent.ShowChoices(user) + +/datum/preference_setting/numerical/backbag + name = "Backbag" + sql_name = "backbag" + sql_table = "body" + enabled = TRUE + + default_setting = BACKPACK + min_value = NO_BACKPACK + max_value = MESSENGER_BAG + +/datum/preference_setting/numerical/backbag/choose_setting(var/mob/user) + var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in backbaglist + if(new_backbag) + setting = backbaglist.Find(new_backbag) + parent.ShowChoices(user) + +/datum/preference_setting/string/h_style + name = "Hair style" + sql_name = "hair_style_name" + sql_table = "body" + enabled = TRUE + + default_setting = "Bald" + +/datum/preference_setting/string/h_style/sanitize_setting(var/new_setting) + return sanitize_inlist(new_setting, hair_styles_list, default_setting) + +/datum/preference_setting/string/h_style/randomise() + var/datum/preference_setting/gender = parent.get_pref_datum(/datum/preference_setting/enum/gender) + var/datum/preference_setting/species = parent.get_pref_datum(/datum/preference_setting/string/species) + setting = random_hair_style(gender.setting, species.setting) + + var/list/colours = random_hair_color() + + var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair) + r_hair.setting = colours[1] + var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair) + g_hair.setting = colours[2] + var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair) + b_hair.setting = colours[3] + +/datum/preference_setting/string/h_style/proc/input_hair_color(var/mob/user) + + var/datum/preference_setting/species_setting = parent.get_pref_datum(/datum/preference_setting/string/species) + var/species = species_setting.setting + + var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair) + var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair) + var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair) + + switch (species) + if("Human", "Unathi", "Diona", "Mushroom") + var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference", rgb(r_hair, g_hair, b_hair)) as color|null + if(new_hair) + r_hair.setting = hex2num(copytext(new_hair, 2, 4)) + g_hair.setting = hex2num(copytext(new_hair, 4, 6)) + b_hair.setting = hex2num(copytext(new_hair, 6, 8)) + + if("Vox") + var/new_hair_vox = input(user, "Choose your character's hair color:", "Character Preference") as null|anything in list("Green", "Azure", "Brown", "Emerald", "Gray", "Light Green", "Green-Brown") + if(new_hair_vox) + r_hair = haircolordesc(new_hair_vox) // Yeah, vox uses r_hair number as an index for discrete colour values. + // Why is that? Historical reasons + to_chat(user,"Your hair will now be [new_hair_vox] in color.") + + if("Insectoid") + var/carapace = input(user, "Choose your character's carapace colour, color values will be adjusted to between 35 and 80:", "Character Preference", rgb(r_hair, g_hair, b_hair)) as color|null + if(carapace) + var/datum/preference_setting/numerical/r_facial = parent.get_pref_datum(/datum/preference_setting/numerical/r_facial) + var/datum/preference_setting/numerical/g_facial = parent.get_pref_datum(/datum/preference_setting/numerical/g_facial) + var/datum/preference_setting/numerical/b_facial = parent.get_pref_datum(/datum/preference_setting/numerical/b_facial) + + r_facial.setting = hex2num(copytext(carapace, 2, 4)) + g_facial.setting = hex2num(copytext(carapace, 4, 6)) + b_facial.setting = hex2num(copytext(carapace, 6, 8)) + r_facial.setting = clamp(r_hair, 0, 80) + g_facial.setting = clamp(g_hair, 0, 50) + b_facial.setting = clamp(b_hair, 0, 35) + parent.ShowChoices(user) + +/datum/preference_setting/string/h_style/choose_setting(var/mob/user) + var/species = parent.get_pref(/datum/preference_setting/string/species) + var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_sprite_accessories(hair_styles_list, null, species) //gender intentionally left null so speshul snowflakes can cross-hairdress + if(new_h_style) + setting = new_h_style + parent.ShowChoices(user) + +/datum/preference_setting/string/h_style/process_link(var/task, var/mob/user, var/list/href_list) + . = ..() + if (.) + return + var/species = parent.get_pref(/datum/preference_setting/string/species) + switch (task) + if ("next_hair_style") + setting = next_list_item(setting, valid_sprite_accessories(hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress + parent.ShowChoices(user) + if("previous_hair_style") + setting = previous_list_item(setting, valid_sprite_accessories(hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress + parent.ShowChoices(user) + if("input_hair_color") + input_hair_color(user) + +// Those are split in 3 integer rather than a colour string for historical reasons. +/datum/preference_setting/numerical/r_hair + name = "Red comp. RGB hair" + sql_name = "hair_red" // They're also inconsistently named between DB and DM + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/r_hair/randomise() + return + +/datum/preference_setting/numerical/g_hair + name = "Green comp. RGB hair" + sql_name = "hair_green" + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/b_hair/randomise() + return + +/datum/preference_setting/numerical/b_hair + name = "Blue comp. RGB hair" + sql_name = "hair_blue" + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/string/f_style + name = "Facial Hair style" + sql_name = "facial_style_name" //ew + sql_table = "body" + + enabled = TRUE + + default_setting = "Bald" + +/datum/preference_setting/string/f_style/sanitize_setting(var/new_setting) + return sanitize_inlist(new_setting, facial_hair_styles_list, default_setting) + +/datum/preference_setting/string/f_style/randomise() + var/datum/preference_setting/gender = parent.get_pref_datum(/datum/preference_setting/enum/gender) + var/datum/preference_setting/species = parent.get_pref_datum(/datum/preference_setting/string/species) + setting = random_facial_hair_style(gender.setting, species.setting) + + var/datum/preference_setting/numerical/r_facial = parent.get_pref_datum(/datum/preference_setting/numerical/r_facial) + var/datum/preference_setting/numerical/g_facial = parent.get_pref_datum(/datum/preference_setting/numerical/g_facial) + var/datum/preference_setting/numerical/b_facial = parent.get_pref_datum(/datum/preference_setting/numerical/b_facial) + + if (prob(75)) + var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair) + var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair) + var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair) + + r_facial.setting = r_hair.setting + g_facial.setting = g_hair.setting + b_facial.setting = b_hair.setting + else + var/list/colours = random_hair_color() + r_facial.setting = colours[1] + g_facial.setting = colours[2] + b_facial.setting = colours[3] + +/datum/preference_setting/string/f_style/proc/input_facial_hair_color(var/mob/user) + message_admins("begin input facial hair colour") + var/species = parent.get_pref(/datum/preference_setting/string/species) + var/datum/preference_setting/numerical/r_facial = parent.get_pref_datum(/datum/preference_setting/numerical/r_facial) + var/datum/preference_setting/numerical/g_facial = parent.get_pref_datum(/datum/preference_setting/numerical/g_facial) + var/datum/preference_setting/numerical/b_facial = parent.get_pref_datum(/datum/preference_setting/numerical/b_facial) + + switch(species) + if("Human", "Unathi") + var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", rgb(r_facial.setting, g_facial.setting, b_facial.setting)) as color|null + if(new_facial) + r_facial.setting = hex2num(copytext(new_facial, 2, 4)) + g_facial.setting = hex2num(copytext(new_facial, 4, 6)) + b_facial.setting = hex2num(copytext(new_facial, 6, 8)) + else + to_chat(user, "No facial hair colour setting for speices [species] yet.") + parent.ShowChoices(user) + +/datum/preference_setting/string/f_style/choose_setting(var/mob/user) + var/species = parent.get_pref(/datum/preference_setting/string/species) + var/gender = parent.get_pref(/datum/preference_setting/enum/gender) + var/new_f_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in valid_sprite_accessories(facial_hair_styles_list, gender, species) + if(new_f_style) + setting = new_f_style + parent.ShowChoices(user) + +/datum/preference_setting/string/f_style/process_link(var/task, var/mob/user, var/list/href_list) + . = ..() + if (.) + return + var/species = parent.get_pref(/datum/preference_setting/string/species) + switch (task) + if ("next_facehair_style") + setting = next_list_item(setting, valid_sprite_accessories(facial_hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress + parent.ShowChoices(user) + if("next_facehair_style") + setting = previous_list_item(setting, valid_sprite_accessories(facial_hair_styles_list, null, species)) + parent.ShowChoices(user) + if("input_facial_hair_color") + input_facial_hair_color(user) + +/datum/preference_setting/numerical/r_facial + name = "Red comp. RGB facial hair" + sql_name = "facial_red" + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/r_facial/randomise() + return + +/datum/preference_setting/numerical/g_facial + name = "Green comp. RGB facial hair" + sql_name = "facial_green" + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/g_facial/randomise() + return + +/datum/preference_setting/numerical/b_facial + name = "Blue comp. RGB hair" + sql_name = "facial_blue" + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/b_facial/randomise() + return + +// Because it's all stored as r_eyes, g_eyes, b_eyes +// We have to pick one color which acts as the `input` catcher for all eye colour +// This should probably be reworked but that'll be in a later PR. +/datum/preference_setting/numerical/r_eyes + name = "Red comp. RGB eyes" + sql_name = "eyes_red" // WHY IS YOUR SQL NAME DIFFERENT FROM YOUR REAL HANDLE??? + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/r_eyes/choose_setting(var/mob/user) + var/red_value = parent.get_pref(/datum/preference_setting/numerical/r_eyes) + var/green_value = parent.get_pref(/datum/preference_setting/numerical/g_eyes) + var/blue_value = parent.get_pref(/datum/preference_setting/numerical/b_eyes) + var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", rgb(red_value, green_value, blue_value)) as color|null + if(new_eyes) + var/datum/preference_setting/numerical/g_eyes = parent.get_pref_datum(/datum/preference_setting/numerical/g_eyes) + var/datum/preference_setting/numerical/b_eyes = parent.get_pref_datum(/datum/preference_setting/numerical/b_eyes) + setting = hex2num(copytext(new_eyes, 2, 4)) + g_eyes.setting = hex2num(copytext(new_eyes, 4, 6)) + b_eyes.setting = hex2num(copytext(new_eyes, 6, 8)) + parent.ShowChoices(user) + +/datum/preference_setting/numerical/g_eyes + name = "Green comp. RGB eyes" + sql_name = "eyes_green" + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/b_eyes + name = "Blue comp. RGB eyes" + sql_name = "eyes_blue" + sql_table = "body" + enabled = TRUE + + default_setting = 0 + min_value = 0 + max_value = 255 + +/datum/preference_setting/numerical/s_tone + name = "Skin tone" + sql_name = "skin_tone" + sql_table = "body" + enabled = TRUE + + default_setting = -100 + min_value = -185 + max_value = 35 + +/datum/preference_setting/numerical/s_tone/choose_setting(var/mob/user) + var/datum/preference_setting/species_datum = parent.get_pref_datum(/datum/preference_setting/string/species) + var/species = species_datum.setting + switch (species) + if("Human") + var/new_s_tone = input(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference") as num|null + if(new_s_tone) + setting = 35 - clamp(new_s_tone,1,220) + to_chat(user,"You're now [skintone2racedescription(setting, species)].") + if("Vox")//Can't reference species flags here, sorry. + var/skin_c = input(user, "Choose your Vox's skin color:\n(1 = Green, 2 = Brown, 3 = Gray, 4 = Light Green, 5 = Azure, 6 = Emerald)", "Character Preference") as num|null + if(skin_c) + setting = clamp(skin_c,1,6) + to_chat(user,"You will now be [skintone2racedescription(setting,species)] in color.") + if("Grey") + var/skin_c = input(user, "Choose your Grey's skin color:\n(1 = Gray, 2 = Light, 3 = Green, 4 = Blue)", "Character Preference") as num|null + if(skin_c) + setting = clamp(skin_c,1,4) + to_chat(user,"You will now be [skintone2racedescription(setting,species)] in color.") + if("Insectoid") + var/datum/preference_setting/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair) + var/datum/preference_setting/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair) + var/datum/preference_setting/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair) + var/carapace = input(user, "Choose your character's carapace colour, color values will be adjusted to between 35 and 80:", "Character Preference", rgb(r_hair.setting, g_hair.setting, b_hair.setting)) as color|null + if(carapace) + r_hair.setting = hex2num(copytext(carapace, 2, 4)) + g_hair.setting = hex2num(copytext(carapace, 4, 6)) + b_hair.setting = hex2num(copytext(carapace, 6, 8)) + r_hair.setting = clamp(r_hair.setting, 0, 80) + g_hair.setting = clamp(g_hair.setting, 0, 50) + b_hair.setting = clamp(b_hair.setting, 0, 35) + else + to_chat(user,"Your species doesn't have different skin tones. Yet?") + parent.ShowChoices(user) + +/datum/preference_setting/numerical/s_tone/randomise() + var/species = parent.get_pref(/datum/preference_setting/string/species) + setting = random_skin_tone(species) + +/datum/preference_setting/string/species + name = "Species" + sql_name = "species" + sql_table = "players" + enabled = TRUE + + default_setting = "Human" + +/datum/preference_setting/string/species/choose_setting(var/mob/user) + var/prev_species = setting + var/gender = parent.get_pref(/datum/preference_setting/enum/gender) + if(check_rights(R_ADMIN,0)) // `usr` my beloved :] // usr still sort of works here because this in direct line from a TOPIC() call, but still + setting = input("Please select a species", "Character Generation", null) in whitelisted_species + else + setting = input("Please select a species", "Character Generation", null) in playable_species + if(prev_species != setting) + //grab one of the valid hair styles for the newly chosen species + var/list/valid_hairstyles = valid_sprite_accessories(hair_styles_list, gender, setting, HAIRSTYLE_CANTRIP) + var/datum/preference_setting/h_style = parent.get_pref_datum(/datum/preference_setting/string/h_style) + if(valid_hairstyles.len) + h_style.setting = pick(valid_hairstyles) + else + //this shouldn't happen + h_style.setting = hair_styles_list["Bald"] + + //grab one of the valid facial hair styles for the newly chosen species + var/list/valid_facialhairstyles = valid_sprite_accessories(facial_hair_styles_list, gender, setting) + var/datum/preference_setting/f_style = parent.get_pref_datum(/datum/preference_setting/string/f_style) + if(valid_facialhairstyles.len) + f_style.setting = pick(valid_facialhairstyles) + else + //this shouldn't happen + f_style.setting = facial_hair_styles_list["Shaved"] + + + var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair) + var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair) + var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair) + + //reset hair colour and skin colour + r_hair.setting = 0//hex2num(copytext(new_hair, 2, 4)) + g_hair.setting = 0//hex2num(copytext(new_hair, 4, 6)) + b_hair.setting = 0//hex2num(copytext(new_hair, 6, 8)) + + var/datum/preference_setting/numerical/s_tone = parent.get_pref_datum(/datum/preference_setting/numerical/s_tone) + s_tone.setting = s_tone.default_setting + + // Job list check + var/datum/preference_setting/jobs = parent.get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs) + var/list/jobs_list = jobs.setting + + for(var/datum/job/job in job_master.occupations) + if(job.species_blacklist.Find(setting)) //If new species is in a job's blacklist + jobs_list -= job.title + to_chat(user, "Your new species ([setting]) is blacklisted from [job.title].") + + if(job.species_whitelist.len) //If the job has a species whitelist + if(!job.species_whitelist.Find(setting)) //And it doesn't include our new species + if(jobs_list.Remove(job.title)) + to_chat(user, "Your new species ([setting]) can't be [job.title]. Your preferences have been adjusted.") + parent.ShowChoices(user) + +// The sanity check here is handled at Topic() level with `input()` +/datum/preference_setting/string/language + name = "language" + sql_name = "language" + sql_table = "players" + enabled = TRUE + + default_setting = "None" + +/datum/preference_setting/string/language/choose_setting(var/mob/user) + var/list/new_languages = list("None") + for(var/L in all_languages) + var/datum/language/lang = all_languages[L] + if(lang.flags & CAN_BE_SECONDARY_LANGUAGE) + new_languages += lang.name + + setting = input("Please select a secondary language", "Character Generation", null) in new_languages + parent.ShowChoices(user) + +/datum/preference_setting/string/flavor_text + name = "Flavor Text" + sql_name = "flavor_text" + sql_table = "players" + enabled = TRUE + + default_setting = "" + max_length = 3000 + +/datum/preference_setting/string/flavor_text/choose_setting(var/mob/user) + setting = input(user,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(setting)) as message + parent.ShowChoices(user) + +/datum/preference_setting/string/med_record + name = "Medical Records" + sql_name = "med_record" + sql_table = "players" + enabled = TRUE + + default_setting = "" + max_length = 3000 + +/datum/preference_setting/string/sec_record + name = "Security Records" + sql_name = "sec_record" + sql_table = "players" + enabled = TRUE + + default_setting = "" + max_length = 3000 + +/datum/preference_setting/string/gen_record + name = "General Records" + sql_name = "gen_record" + sql_table = "players" + enabled = TRUE + + default_setting = "" + max_length = 3000 + +// Why is it named like this, I have no idea. +/datum/preference_setting/string/metadata + name = "OOC Notes" + sql_name = "ooc_notes" + sql_table = "players" + enabled = TRUE + + default_setting = "" + max_length = MAX_MESSAGE_LEN + +/datum/preference_setting/string/metadata/choose_setting(var/mob/user) + var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , setting) as message|null + if(new_metadata) + setting = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) + +/datum/preference_setting/list_values/player_alt_titles + name = "Player alt titles" + sql_name = "player_alt_titles" + sql_table = "players" + enabled = TRUE + + default_setting = list() + +// For some reason this is saved as a raw string like "Title":"Alt Title";"Title2":"Alt-Title2" +// So we just need to do some splittext + +/datum/preference_setting/list_values/player_alt_titles/load_sql(var/sql_value) + var/list/temporary_list = list() + var/list/returned_list = list() + temporary_list.Add(splittext(sql_value, ";")) // we're getting the first part of the string for each job. + for(var/item in temporary_list) // iterating through the list + if(!findtext(item, ":")) + continue + var/delim_location = findtext(item, ":") // getting the second part of the string that will be handled for titles + var/job = copytext(item, 1, delim_location) // getting where the job is, it's in the first slot so we want to get that position. + var/title = copytext(item, delim_location + 1, 0) // getting where the job title is, it's in the second slot so we want to get that position. + returned_list[job] = title // we assign the alt_titles here to specific job titles and hope everything works. + return returned_list + +/datum/preference_setting/list_values/player_alt_titles/save_sql() + var/return_string + + // From Nexis, circa 2017 + // The FUCK is this shit + for(var/a in setting) + return_string += "[a]:[setting[a]];" + + return return_string + +// The organ data is a bit strange: all these are saved as individual setting flags +// But the whole of it is read as a single list. +// It's annoying to deal with but that's just technical debt + +/datum/preference_setting/enum/organ_data + name = "Organ data" + sql_name = "" + sql_table = "limbs" + enabled = FALSE + + default_setting = null + allowed_values = list(null, "cyborg", "amputated") + +/datum/preference_setting/enum/organ_data/limb_left_arm + name = "Left Arm" + sql_name = LIMB_LEFT_ARM + enabled = TRUE + + +/datum/preference_setting/enum/organ_data/limb_right_arm + name = "Right Arm" + sql_name = LIMB_RIGHT_ARM + enabled = TRUE + + +/datum/preference_setting/enum/organ_data/limb_left_leg + name = "Left Leg" + sql_name = LIMB_LEFT_LEG + enabled = TRUE + + +/datum/preference_setting/enum/organ_data/limb_right_leg + name = "Right Leg" + sql_name = LIMB_RIGHT_LEG + enabled = TRUE + +/datum/preference_setting/enum/organ_data/limb_left_hand + name = "Left Hand" + sql_name = LIMB_LEFT_HAND + enabled = TRUE + +/datum/preference_setting/enum/organ_data/limb_right_hand + name = "Right Hand" + sql_name = LIMB_RIGHT_HAND + enabled = TRUE + +/datum/preference_setting/enum/organ_data/limb_left_foot + name = "Left Foot" + sql_name = LIMB_LEFT_FOOT + enabled = TRUE + +/datum/preference_setting/enum/organ_data/limb_right_foot + name = "Right Foot" + sql_name = LIMB_RIGHT_FOOT + enabled = TRUE + +/datum/preference_setting/enum/organ_data/organ + enabled = FALSE + allowed_values = list(null, "assisted", "mechanical") + +/datum/preference_setting/enum/organ_data/organ/heart + name = "Heart" + sql_name = LIMB_HEART + enabled = TRUE + +/datum/preference_setting/enum/organ_data/organ/eyes + name = "Eyes" + sql_name = LIMB_EYES + enabled = TRUE + +/datum/preference_setting/enum/organ_data/organ/lung + name = "Lung" + sql_name = LIMB_LUNG + enabled = TRUE + +/datum/preference_setting/enum/organ_data/organ/liver + name = "Liver" + sql_name = LIMB_LIVER + enabled = TRUE + +/datum/preference_setting/enum/organ_data/organ/kidneys + name = "Kidneys" + sql_name = LIMB_KIDNEYS + enabled = TRUE + +//Keeps track of preferrence for not getting any wanted jobs +/datum/preference_setting/enum/alternate_option + name = "Alternate Option if jobs filled" + sql_name = "alternate_option" + sql_table = "jobs" + enabled = TRUE + + default_setting = RETURN_TO_LOBBY + allowed_values = list(RETURN_TO_LOBBY, GET_RANDOM_JOB, BE_ASSISTANT, GET_EMPTY_JOB) + +// Stored as JSON +/datum/preference_setting/assoc_list_setting/jobs + name = "Jobs" + sql_name = "jobs" + sql_table = "jobs" + enabled = TRUE + + default_setting = list() + allowed_values_for_list_items = list(JOB_PREF_NEVER, JOB_PREF_LOW, JOB_PREF_MED, JOB_PREF_HIGH) + +/datum/preference_setting/assoc_list_setting/jobs/save_sql() + return json_encode(setting) + +/datum/preference_setting/assoc_list_setting/jobs/load_sql(var/sql_value) + if (sql_value) + return json_decode(sql_value) + return list() + +/datum/preference_setting/assoc_list_setting/jobs/process_link(var/task, var/mob/user, var/list/href_list) + var/datum/preference_setting/alternate_option = parent.get_pref(/datum/preference_setting/enum/alternate_option) + switch(task) + if("close") + user << browse(null, "window=mob_occupation") + parent.ShowChoices(user) + if("reset") + parent.ResetJobs() + if("random") // This just changes the alternate option. + if(alternate_option.setting == GET_RANDOM_JOB || alternate_option.setting == BE_ASSISTANT || alternate_option.setting == RETURN_TO_LOBBY) + alternate_option.setting += 1 + else if(alternate_option == GET_EMPTY_JOB) + alternate_option.setting = 0 + else + return 0 + parent.SetJobsChoice(user) + if ("alt_title") + var/datum/job/job = locate(href_list["job"]) + if (job) + var/choices = list(job.title) + job.alt_titles + var/choice = input("Pick a title for [job.title].", "Character Generation", parent.GetPlayerAltTitle(job)) as anything in choices | null + if(choice) + parent.SetPlayerAltTitle(job, choice) + parent.SetJobsChoice(user) + if("input") + parent.SetJob(user, href_list["text"], href_list["level"] == "1") + else // Menu + parent.SetJobsChoice(user) + return 1 + +/datum/preference_setting/enum/string/nanotrasen_relation + name = "Nanotrasen Relationship" + sql_name = "nanotrasen_relation" + sql_table = "players" + enabled = TRUE + + default_setting = "Neutral" + allowed_values = list("Loyal", "Supportive", "Neutral", "Skeptical") + +/datum/preference_setting/enum/string/nanotrasen_relation/choose_setting(var/mob/user) + var/new_relation = input(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference") as null|anything in list("Loyal", "Supportive", "Neutral", "Skeptical", ) + if(new_relation) + setting = new_relation + parent.ShowChoices(user) + +/datum/preference_setting/enum/bank_security + name = "Bank security" + sql_name = "bank_security" + sql_table = "players" + enabled = TRUE + + default_setting = SECURITY_AUTO_LOGIN + allowed_values = list(SECURITY_AUTO_LOGIN, SECURITY_MANUAL_LOGIN, SECURITY_CARD_AND_MANUAL_LOGIN) + +/datum/preference_setting/enum/bank_security/choose_setting(var/mob/user) + var/new_bank_security = input(user, BANK_SECURITY_EXPLANATION, "Character Preference") as null|anything in bank_security_text2num_associative + if(!isnull(new_bank_security)) + setting = bank_security_text2num_associative[new_bank_security] + parent.ShowChoices(user) + +/datum/preference_setting/numerical/wage_ratio + name = "Wage ratio" + sql_name = "wage_ratio" + sql_table = "players" + enabled = TRUE + + default_setting = 50 + min_value = 0 + max_value = 100 + +/datum/preference_setting/numerical/wage_ratio/randomise() + return + +/datum/preference_setting/numerical/wage_ratio/choose_setting(var/mob/user) + var/new_wage_ratio = input(user, "Input what % of wages end up in virtual wallets, from 0-100", "Character Preference",setting) as num + if(!isnull(new_wage_ratio)) + new_wage_ratio = clamp(new_wage_ratio,0,100) + setting = new_wage_ratio + parent.ShowChoices(user) + +/datum/preference_setting/binary_flag/disabilities + name = "Disabilities" + sql_name = "disabilities" + sql_table = "players" + enabled = TRUE + +/datum/preference_setting/binary_flag/disabilities/process_link(var/task, var/mob/user, var/list/href_list) + switch(task) + if("close") + user << browse(null, "window=disabil") + parent.ShowChoices(user) + if("reset") + setting=0 + parent.SetDisabilities(user) + if("input") + var/dflag=text2num(href_list["disability"]) + var/species = parent.get_pref(/datum/preference_setting/string/species) + if(dflag >= 0) + if(!(dflag==DISABILITY_FLAG_FAT && species!="Human")) + setting ^= text2num(href_list["disability"]) //MAGIC + parent.SetDisabilities(user) + else + parent.SetDisabilities(user) diff --git a/code/modules/client/preferences/pref_datums_client.dm b/code/modules/client/preferences/pref_datums_client.dm new file mode 100644 index 00000000000..b1f722e9b75 --- /dev/null +++ b/code/modules/client/preferences/pref_datums_client.dm @@ -0,0 +1,582 @@ +// -- ACTUAL PREFS -- + +// non-prefs stuff + +/datum/preference_setting/numerical/warns + name = "Warns" + sql_name = "warns" + sql_table = "client" + enabled = TRUE + + default_setting = 0 + +/datum/preference_setting/numerical/warnbans + name = "Warn bans" + sql_name = "warnbans" + sql_table = "client" + enabled = TRUE + + default_setting = 0 + +/datum/preference_setting/toggle/show_warning_next_time + name = "Show warning next time" + sql_name = "show_warning_next_time" + sql_table = "client" + enabled = TRUE + + default_setting = 0 + +/datum/preference_setting/string/last_warned_message + name = "Last warned message" + sql_name = "last_warned_message" + + sql_table = "client" + + enabled = TRUE + + default_setting = "Default warning message" + +/datum/preference_setting/string/warning_admin + name = "Warning admin" + sql_name = "warning_admin" + sql_table = "client" + enabled = TRUE + + default_setting = "Admin" + +/datum/preference_setting/numerical/default_slot + name = "Default slot" + sql_name = "default_slot" + sql_table = "client" + enabled = TRUE + + default_setting = 1 + min_value = 1 + max_value = MAX_SAVE_SLOTS + +// game-preferences +//Saved changlog filesize to detect if there was a change +/datum/preference_setting/string/changelog + name = "Last changelog" + sql_name = "lastchangelog" + sql_table = "client" + enabled = TRUE + + default_setting = "" + +/datum/preference_setting/string/ooc_color + name = "OOC Color" + sql_name = "ooc_color" + sql_table = "client" + enabled = TRUE + + default_setting = "#b82e00" + +// For all that matters, this ISN'T in the pref UI for some reason? +/datum/preference_setting/string/ooc_color/choose_setting(var/mob/user) + var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null + if(new_ooccolor) + setting = new_ooccolor + +/datum/preference_setting/string/UI_style + name = "UI Style" + sql_name = "UI_style" + sql_table = "client" + enabled = TRUE + + default_setting = "Midnight" + +// This is just cycling +/datum/preference_setting/string/UI_style/choose_setting(mob/user) + . = ..() + switch(setting) + if("Midnight") + setting = "Orange" + if("Orange") + setting = "old" + if("old") + setting = "White" + else + setting = "Midnight" + + +/datum/preference_setting/binary_flag/toggles + name = "Toggles" + sql_name = "toggles" + sql_table = "client" + enabled = TRUE + + default_setting = TOGGLES_DEFAULT + +// Annoying overwrite but I can't think of a more elegant way to do it. +/datum/preference_setting/binary_flag/toggles/process_link(var/task, var/mob/user, var/list/href_list) + if (task == "input") + var/toggle = text2num(href_list["toggle"]) + + // I can't think of any other way to check this pre-toggling. Probably overloaded but I want to replicate it, just ion case. + switch (toggle) + // Admin toggle, do not pass if we're not admin + if (SOUND_ADMINHELP, CHAT_PRAYER, CHAT_GHOSTRADIO, CHAT_ATTACKLOGS, CHAT_DEBUGLOGS, AUTO_DEADMIN) + if (!user.client.holder) + return + + setting ^= toggle + + // Annoying but have to do it. + switch (toggle) + if (SOUND_MIDI) + user << sound(null, repeat = 0, wait = 0, volume = 0, channel = CHANNEL_ADMINMUSIC) + if (SOUND_AMBIENCE) + if(config.no_ambience) + to_chat(user, "DEBUG: Ambience is globally disabled via server config.") + user << sound(null, repeat = 0, wait = 0, volume = 0, channel = CHANNEL_AMBIENCE) + if (SOUND_LOBBY) + if(config.no_lobby_music) + to_chat(user, "DEBUG: Lobby music is globally disabled via server config.") + if(istype(user,/mob/new_player)) + user << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBY) + +/datum/preference_setting/string/UI_style_color + name = "UI Style Color" + sql_name = "UI_style_color" + sql_table = "client" + enabled = TRUE + + default_setting = "#ffffff" + +/datum/preference_setting/string/UI_style_color/choose_setting(var/mob/user) + var/UI_style_color_new = input(user, "Choose your UI colour, dark colours are not recommended!") as color|null + if(!UI_style_color_new) + return + setting = UI_style_color_new + +/datum/preference_setting/numerical/UI_style_alpha + name = "UI Style Alpha" + sql_name = "UI_style_alpha" + sql_table = "client" + enabled = TRUE + + default_setting = 255 + min_value = 50 + max_value = 255 + +/datum/preference_setting/numerical/UI_style_alpha/choose_setting(var/mob/user) + var/UI_style_alpha_new = input(user, "Select a new alpha(transparency) parameter for UI, between 50 and 255") as num + if(!UI_style_alpha_new || !(UI_style_alpha_new <= max_value && UI_style_alpha_new >= min_value)) + return + setting = UI_style_alpha_new + +/datum/preference_setting/toggle/space_parallax + name = "Space parallax" + sql_name = "space_parallax" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/toggle/space_parallax/choose_setting(var/mob/user) + . = ..() + if(user && user.hud_used) + user.hud_used.update_parallax_existence() + +/datum/preference_setting/toggle/space_dust + name = "Space dust" + sql_name = "space_dust" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/toggle/space_dust/choose_setting(var/mob/user) + . = ..() + if(user && user.hud_used) + user.hud_used.update_parallax_existence() + +/datum/preference_setting/numerical/parallax_speed + name = "Parallax speed" + sql_name = "parallax_speed" + sql_table = "client" + enabled = TRUE + + default_setting = 2 + min_value = 0 + max_value = 5 + +/datum/preference_setting/numerical/parallax_speed/choose_setting(var/mob/user) + var/p_speed = input(user, "Enter a number between 0 and 5 included (default=2)","Parallax Speed Preferences",setting) + setting = clamp(p_speed, min_value, max_value) + +/datum/preference_setting/enum/special_popup + name = "Special popup" + sql_name = "special" // HISTORICAL REASONS :tm: + sql_table = "client" + enabled = TRUE + + default_setting = SPECIAL_POPUP_USE_BOTH + allowed_values = list(SPECIAL_POPUP_DISABLED, SPECIAL_POPUP_EXCLUSIVE, SPECIAL_POPUP_USE_BOTH) + +/datum/preference_setting/enum/special_popup/choose_setting(mob/user) + var/choice = input(user, "Set your special tab preferences:", "Settings") as null|anything in special_popup_text2num + if(!isnull(choice)) + setting = special_popup_text2num[choice] + +/datum/preference_setting/toggle/tooltips + name = "Tooltips" + sql_name = "tooltips" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/toggle/stumble + name = "Stumble" + sql_name = "stumble" + sql_table = "client" + enabled = TRUE + + default_setting = FALSE + +/datum/preference_setting/toggle/hear_voicesound + name = "Hear voicesound" + sql_name = "hear_voicesound" + sql_table = "client" + enabled = TRUE + + default_setting = FALSE + +/datum/preference_setting/numerical/volume + name = "Music volume" + sql_name = "volume" + sql_table = "client" + enabled = TRUE + + default_setting = 100 + min_value = 0 + max_value = 100 + +/datum/preference_setting/numerical/volume/choose_setting(var/mob/user) + user.client.set_new_volume() + +/datum/preference_setting/toggle/usewmp + name = "Use WMP" + sql_name = "usewmp" + sql_table = "client" + enabled = TRUE + + default_setting = FALSE + +/datum/preference_setting/toggle/usewmp/choose_setting(var/mob/user) + . = ..() + if(!user.client.media) + return + user.client.media.stop_music() + user.client.media.playerstyle = (setting ? PLAYER_OLD_HTML : PLAYER_HTML) + var/datum/preference_setting/toggles = parent.get_pref_datum(/datum/preference_setting/binary_flag/toggles) + if(toggles.setting & SOUND_STREAMING) + user.client.media.open() + user.client.media.update_music() + + +/datum/preference_setting/toggle/randomslot + name = "Use random character slot" + sql_name = "randomslot" + sql_table = "client" + enabled = TRUE + + default_setting = FALSE + +/datum/preference_setting/toggle/usenanoui + name = "Use nanoUI" + sql_name = "usenanoui" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/toggle/progress_bars + name = "Progress bars" + sql_name = "progress_bars" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +// TRAINS!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +/datum/preference_setting/enum/attack_animations + name = "Attack animations" + sql_name = "attack_animation" + sql_table = "client" + enabled = TRUE + + default_setting = ITEM_ANIMATION + allowed_values = list(ITEM_ANIMATION, NO_ANIMATION, PERSON_ANIMATION) + +/datum/preference_setting/enum/attack_animations/choose_setting(var/mob/user) + if(setting == NO_ANIMATION) + item_animation_viewers |= parent.client + setting = ITEM_ANIMATION + + else if(setting == ITEM_ANIMATION) + setting = PERSON_ANIMATION + person_animation_viewers |= parent.client + item_animation_viewers -= parent.client + + else if(setting == PERSON_ANIMATION) + setting = NO_ANIMATION + item_animation_viewers -= parent.client + +/datum/preference_setting/toggle/pulltoggle + name = "Pull toggle" + sql_name = "pulltoggle" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/toggle/hear_instruments + name = "Hear instruments" + sql_name = "hear_instruments" + sql_table = "client" + enabled = TRUE + + default_setting = FALSE + +/datum/preference_setting/numerical/ambience_volume + name = "Ambience volume" + sql_name = "ambience_volume" + sql_table = "client" + enabled = TRUE + + default_setting = 100 + min_value = 0 + max_value = 100 + +/datum/preference_setting/numerical/ambience_volume/choose_setting(var/mob/user) + var/new_volume = input(user, "Enter the new volume you wish to use. (0-100)","Ambience Volume Preferences", setting) + setting = clamp(new_volume, min_value, max_value) + +/datum/preference_setting/numerical/credits_volume + name = "Ambience volume" + sql_name = "credits_volume" + sql_table = "client" + enabled = TRUE + + default_setting = 75 + min_value = 0 + max_value = 100 + +/datum/preference_setting/numerical/credits_volume/choose_setting(var/mob/user) + var/credits_volume = input(user, "Enter the new volume you wish to use. (0-100, default is 75)","Credits/Jingle Volume", setting) + setting = clamp(credits_volume, min_value, max_value) + +/datum/preference_setting/enum/credits + name = "Credits" + sql_name = "credits" + sql_table = "client" + enabled = TRUE + + default_setting = CREDITS_ALWAYS + allowed_values = list(CREDITS_NEVER, CREDITS_ALWAYS, CREDITS_NO_RERUNS) + + saved_as_string = FALSE + +/datum/preference_setting/enum/credits/choose_setting(var/mob/user) + switch(setting) + if(CREDITS_NEVER) + setting = CREDITS_ALWAYS + if(CREDITS_ALWAYS) + setting = CREDITS_NO_RERUNS + if(CREDITS_NO_RERUNS) + setting = CREDITS_NEVER + +/datum/preference_setting/enum/jingle + name = "Jingle" + sql_name = "jingle" + sql_table = "client" + enabled = TRUE + + default_setting = JINGLE_CLASSIC + allowed_values = list(JINGLE_NEVER, JINGLE_CLASSIC, JINGLE_ALL) + + saved_as_string = FALSE + +/datum/preference_setting/enum/jingle/choose_setting(var/mob/user) + switch(setting) + if(JINGLE_NEVER) + setting = JINGLE_CLASSIC + if(JINGLE_CLASSIC) + setting = JINGLE_ALL + if(JINGLE_ALL) + setting = JINGLE_NEVER + +/datum/preference_setting/numerical/headset_sound + name = "Headset sound" + sql_name = "headset_sound" + sql_table = "client" + enabled = TRUE + + default_setting = HEADSET_SOUND_TRANSMIT + min_value = HEADSET_SOUND_DISABLED + max_value = HEADSET_SOUND_ALL + + saved_as_string = FALSE + +/datum/preference_setting/numerical/headset_sound/choose_setting(var/mob/user) + var/choice = input(user, "Set your radio headset sound preferences:", "Settings") as null|anything in headset_sound_text2num + if(!isnull(choice)) + setting = headset_sound_text2num[choice] + +/datum/preference_setting/toggle/window_flashing + name = "Window flashing" + sql_name = "window_flashing" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/toggle/antag_objectives + name = "Antag objectives" + sql_name = "antag_objectives" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE // This also has "historical reasons" but not code-related + +/datum/preference_setting/toggle/typing_indicator + name = "Typing indicator" + sql_name = "typing_indicator" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE // This also has "historical reasons" but not code-related + +// -- Runechat things +/datum/preference_setting/toggle/mob_chat_on_map + name = "Runechat on map" + sql_name = "mob_chat_on_map" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE // This also has "historical reasons" but not code-related + +/datum/preference_setting/numerical/max_chat_length + name = "Max runechat message length" + sql_name = "max_chat_length" + sql_table = "client" + enabled = TRUE + + default_setting = CHAT_MESSAGE_MAX_LENGTH + min_value = 0 + max_value = CHAT_MESSAGE_MAX_LENGTH + +/datum/preference_setting/numerical/max_chat_length/choose_setting(mob/user) + var/max_chat_length = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [default_setting]))", "Character Preference", setting) as null|num + setting = clamp(max_chat_length, min_value, max_value) + +/datum/preference_setting/toggle/obj_chat_on_map + name = "Object runechat" + sql_name = "obj_chat_on_map" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/toggle/no_goonchat_for_obj + name = "No goonchat for object" + sql_name = "no_goonchat_for_obj" + sql_table = "client" + enabled = TRUE + + default_setting = FALSE + +/datum/preference_setting/toggle/tgui_fancy + name = "Fancy TGUI" + sql_name = "tgui_fancy" + sql_table = "client" + enabled = TRUE + + default_setting = TRUE + +/datum/preference_setting/numerical/fps + name = "FPS" + sql_name = "fps" + sql_table = "client" + enabled = TRUE + + default_setting = -1 + min_value = -1 + max_value = 1000 + + saved_as_string = FALSE + +/datum/preference_setting/numerical/fps/choose_setting(var/mob/user) + var/desired_fps = input(user, "Choose your desired frames per second.\n\ + WARNING: BYOND versions earlier than 513.1523 might not work properly with values other than 0.\n\ + Set this to -1 to use the recommended value.\n\ + Set this to 0 to use the server's FPS (currently [world.fps])\n\ + Values up to 1000 are allowed.", "FPS", setting) as null|num + if(isnull(desired_fps)) + return + if(desired_fps < 0) + desired_fps = -1 + desired_fps = sanitize_integer(desired_fps, -1, 1000, setting) + setting = desired_fps + parent.client.fps = (setting < 0) ? RECOMMENDED_CLIENT_FPS : setting + +// THESE ARE UNIMPLEMENTED FROM tgui MIGRATIONS! TOFIX +/datum/preference_setting/toggle/tgui_input + name = "tgui_input" + sql_name = "tgui_input" + sql_table = "client" + enabled = FALSE + + default_setting = FALSE + +/datum/preference_setting/toggle/tgui_input_swapped + name = "tgui_input_swapped" + sql_name = "tgui_input_swapped" + sql_table = "client" + enabled = FALSE + + default_setting = FALSE + + +/datum/preference_setting/toggle/tgui_input_large + name = "tgui_input_large" + sql_name = "tgui_input_large" + + sql_table = "client" + + enabled = FALSE + + default_setting = FALSE + +/datum/preference_setting/toggle/tgui_lock + name = "tgui_lock" + sql_name = "tgui_lock" + + sql_table = "client" + + enabled = FALSE + + default_setting = FALSE + +/datum/preference_setting/toggle/tgui_scale + name = "tgui_scale" + sql_name = "tgui_scale" + + sql_table = "client" + + enabled = FALSE + + default_setting = FALSE + +/datum/preference_setting/toggle/layout_prefs_used + name = "tgui_scale" + sql_name = "tgui_scale" + + sql_table = "client" + + enabled = FALSE + + default_setting = FALSE diff --git a/code/modules/client/preferences/pref_db_workflow.dm b/code/modules/client/preferences/pref_db_workflow.dm new file mode 100644 index 00000000000..557a532eefd --- /dev/null +++ b/code/modules/client/preferences/pref_db_workflow.dm @@ -0,0 +1,200 @@ +/* + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +---------------------------------- SQLite DB workflow ---------------------------------- +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +"ShiftyRail", 2025 + +////////////////////////////////////////////// +// // +// New player Login // +// // +////////////////////////////////////////////// + +1. client/New() + +Called automatically by BYOND + +2. /datum/preferences/New(client/C) + +3. init_datums() + +Creates a list of /datum/preference_setting with the default values + +4. init_subsections() + +Creates the subsection helpers, who handle menu generation, etc, etc. + +5. try_load_preferences(Client.ckey, client.mob) + + a) Executes a DB query to check if the client is registered into the database + "SELECT ckey FROM client WHERE ckey = [client.ckey]", + + b) + 1) If that fails: + save_preferences_sqlite(ckey, user) + + Which will create and save a new entry + + 2) If that works + + load_preferences_sqlite(ckey) + + Which load the `client` preferences, such as FPS, toggles, etc. + +6) If preferences have been correctly loaded + try_load_save_sqlite(Client.ckey, Client, default_slot) -- Where default_slot has been read from `client` preferences + + a) Existing character slot check : + "SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?" + + b) + + 1) If that fails: + create_character_sqlite(Client.ckey, Client, default_slot) + + Which will create a new (random body) character + + 2) If that works: + load_character_sqlite(Client.ckey, Client, default_slot) + + Which just load the character at that slot. + + 3) If both queries fail: + fallback_random_character(theckey, theclient) + + Which generates an ad-hoc random body for that round. + If we are on this path, it is assumed that SQLite has failed. + Saving the fallback random character may overwrite an existing. + +7) Play loading sound and end of flow. + +////////////////////////////////////////////// +// // +// Loading a new character slot // +// // +////////////////////////////////////////////// + +1) Click on the href with '?_src_=prefs;action=changeslot' + +2) Since there is no `src`, this goes to Client/Topic() + +3) Client/Topic() directs it to /datum/preferences/process_link() + +4) Directly inside the decision tree, it calls: + + try_load_slot(user.ckey, user, num) -- where `num` is the slot number passed by href + + a) Exisiting character slot check: + "SELECT player_ckey FROM players WHERE player_ckey = [ckey] AND player_slot = [num]" + + b) + + 1) If that fails: + create_character_sqlite(Client.ckey, client, num) + + Which will create a new (random body) character + + 2) If that works: + load_character_sqlite(Client.ckey, client, num) + + Which just load the character at that slot. + +5) Change the default slot preference, and calls: + save_preferences_sqlite(user, ckey) + So that it is remembered for next time + +6) Update the UI: + ShowChoices(user) + +////////////////////////////////////////////// +// // +// Updating a preference // +// // +////////////////////////////////////////////// + +1) Click on the href with '?_src_=prefs;preference=[something];task=[something]' + OR '?_src_=prefs;action=[something]' + +2) Since there is no `src`, this goes to Client/Topic() + +3) Client/Topic() directs it to /datum/preferences/process_link(user, list/href_list) + +4) Existing preference datums are scanned for the correct `sql_name` of /datum/preference + + a) If there's a match: + /datum/preference_setting/process_link(user, task, list/href_list) + + -- Depending on task + - "random" = /datum/preference_setting/randomise(user) + - "input" = /datum/preference_setting/choose_setting(user) + + - Special tasks : randomise hair colour, etc. + + ShowChoices(user) is either called on process_link (most of the `character` prefs) + Or manually after (`client` prefs, which are mostly toggles) + + b) If there isn't : + + - Hardcoded tasks remaining: + * updating records + * changing role preferences (for antags) + * random body + + c) Other thing : changing character preview background + +5) `action` called by hrefs: + - Save slot + - Load slot + - changing preference tab + +6) Still not handled the link? Coding error, throw a runtime. + +////////////////////////////////////////////// +// // +// Roundstart/latejoin // +// // +////////////////////////////////////////////// + +1) Roundstart: +/datum/controller/gameticker/proc/setup() + +Latejoin: +/mob/new_player/AttemptLateSpawn() + +They both call + +2) H = mob/new_player/create_human() +This creates a client-less human mob in the famous cuck cube + +3) Player is not appearance banned: + + a) /datum/preferences/copy_to(H) + + On the human `H`, the /datum/my_appearance is loaded with the values in `/datum/preferences` + + b) If the player has `Be Random Body`, /datum/my_apperance/randomise() is called + + Which randomises their appearance but respecting the `GENDER` variable in prefs. + +4) Player is appearance banned: + + a) /datum/preferences/randomize_appearance_for(H) + + This scrambles their prefs. The code has an explicit provision for randomising gender in this case. + +////////////////////////////////////////////// +// // +// Other notes // +// // +////////////////////////////////////////////// + +- The DB queries for `body`, `players`, and `client` are all generated at runtime. +- They are setup to take into account changes in the code structure so you don't need to modify the procs "generating" them. + +- The DB queries for `client_roles` and `jobs` are (still) hardcoded and not generated at runtime. +- Further improvements will be neded to converted those to runtime-generated code. +- However, it is less critical as those tables have much less collumns. + +*/ diff --git a/code/modules/client/preferences/preferences.dm b/code/modules/client/preferences/preferences.dm new file mode 100644 index 00000000000..b59d2fcc728 --- /dev/null +++ b/code/modules/client/preferences/preferences.dm @@ -0,0 +1,602 @@ +#define CHARACTER_SETUP 0 +#define UI_SETUP 1 +#define GENERAL_SETUP 2 +#define SPECIAL_ROLES_SETUP 3 + +var/list/preferences_datums = list() +var/global/list/special_roles = list( + ROLE_ALIEN = 1, + BLOBOVERMIND = 1, + ROLE_BORER = 1, + CHANGELING = 1, + CULTIST = 1, + ROLE_PLANT = 1, + MALF = 1, + NUKE_OP = 1, + ROLE_PAI = 1, + ROLE_POSIBRAIN = 1, + REV = 1, + TRAITOR = 1, + CHALLENGER = 1, + VAMPIRE = 1, + VOXRAIDER = 1, + WIZARD = 1, + ROLE_STRIKE = 1, + GRINCH = 1, + NINJA = 1, + TIMEAGENT = 1, + PULSEDEMON = 1, + ROLE_MINOR = 1, + ROLE_PRISONER = 1, + ROLE_GRUE = 1, + DIVERGENTCLONE = 1, +) + +/var/list/antag_roles = list( + ROLE_ALIEN = 1, + BLOBOVERMIND = 1, + CHANGELING = 1, + CULTIST = 1, + MALF = 1, + NUKE_OP = 1, + REV = 1, + TRAITOR = 1, + CHALLENGER = 1, + VAMPIRE = 1, + VOXRAIDER = 1, + WIZARD = 1, + ROLE_STRIKE = 1, + GRINCH = 1, + NINJA = 1, + TIMEAGENT = 1, + PULSEDEMON = 1, + ROLE_MINOR = 1, + ROLE_PRISONER = 1, + ROLE_GRUE = 1, + DIVERGENTCLONE = 1, +) + +var/list/nonantag_roles = list( + ROLE_BORER = 1, + ROLE_PLANT = 1, + ROLE_PAI = 1, + ROLE_POSIBRAIN = 1, +) + +var/list/role_wiki=list( + ROLE_ALIEN = "Xenomorph", + BLOBOVERMIND = "Blob", + ROLE_BORER = "Cortical_Borer", + CHANGELING = "Changeling", + CULTIST = "Cult", + ROLE_PLANT = "Dionaea", + MALF = "Guide_to_Malfunction", + NUKE_OP = "Nuclear_Agent", + ROLE_PAI = "Personal_AI", + ROLE_POSIBRAIN = "Guide_to_Silicon_Laws", + REV = "Revolution", + TRAITOR = "Traitor", + CHALLENGER = "Challengers", + VAMPIRE = "Vampire", + VOXRAIDER = "Vox_Raider", + WIZARD = "Wizard", + GRINCH = "Grinch", + NINJA = "Space_Ninja", + TIMEAGENT = "Time_Agent", + PULSEDEMON = "Pulse_Demon", + ROLE_MINOR = "Minor_Roles", + ROLE_PRISONER = "Minor_Roles", + ROLE_GRUE = "Grue", + DIVERGENTCLONE = "Divergent_Clone", +) + +var/list/special_popup_text2num = list( + "Only use chat" = SPECIAL_POPUP_DISABLED, + "Only use special" = SPECIAL_POPUP_EXCLUSIVE, + "Use both chat and special" = SPECIAL_POPUP_USE_BOTH, +) + +var/list/headset_sound_text2num = list( + "Disabled" = HEADSET_SOUND_DISABLED, + "Transmit Only" = HEADSET_SOUND_TRANSMIT, + "All" = HEADSET_SOUND_ALL, +) + +var/const/MAX_SAVE_SLOTS = 16 + +#define POLLED_LIMIT 100 + +/datum/preferences + var/list/subsections + //doohickeys for savefiles + var/database/db = ("players2.sqlite") + var/path + + // Which character slot + var/slot = 1 + var/list/slot_names = new + var/slot_name = "" + + // 0 = character settings, 1 = game preferences + var/current_tab = 0 + + // Last time the guy saved their prefs + var/lastPolled = 0 + + var/savefile_version = 0 + + // Alist = associative lists. This is a new 516 thing. Woo! + var/alist/preference_settings_client = alist() + var/alist/preference_settings_character = alist() + + // Don't like hardcoding this but I can't find a way.. + var/list/organ_data = list() + + // REALLY don't like hardcoding this. Will be for another time + var/list/roles = list() + + //non-preference stuff + // Shouldn't these by on client? + var/last_ip + var/last_id + var/muted + + //Mob preview + var/icon/preview_icon = null + var/icon/preview_icon_front = null + var/icon/preview_icon_side = null + var/preview_background = null + var/list/background_options = list("Black", "White", "Tile") + + var/client/client + var/saveloaded = 0 + +/datum/preferences/New(client/C) + client=C + if(istype(C)) + init_datums() + init_subsections() + var/theckey = C.ckey + var/thekey = C.key + if(!IsGuestKey(thekey)) + var/load_pref = try_load_preferences(theckey, C.mob) + var/default_slot = get_pref(/datum/preference_setting/numerical/default_slot) + if(load_pref) + to_chat(C, "Successfully loaded preferences.") + while(!SS_READY(SShumans)) + sleep(1) + try_load_save_sqlite(theckey, C, default_slot) + return + CRASH("Could not load preferences!") + + while(!SS_READY(SShumans)) + sleep(1) + randomize_appearance_for(random_gender = TRUE) + var/default_slot = get_pref(/datum/preference_setting/numerical/default_slot) + var/gender = get_pref(/datum/preference_setting/enum/gender) + var/species = get_pref(/datum/preference_setting/string/species) + var/datum/preference_setting/real_name = get_pref_datum(/datum/preference_setting/string/real_name) + real_name.setting = random_name(gender, species) + save_character_sqlite(theckey, C, default_slot) + saveloaded = 1 + +/datum/preferences/proc/init_datums() + for (var/database_setting in typesof(/datum/preference_setting)) + var/datum/preference_setting/setting_datum = database_setting + if (!initial(setting_datum.enabled)) + continue + setting_datum = new database_setting(src) + if (setting_datum.sql_table == "client") + preference_settings_client[database_setting] = setting_datum + else + preference_settings_character[database_setting] = setting_datum + +/datum/preferences/Destroy() + for(var/entry in subsections) + var/datum/preferences_subsection/prefs_ss = subsections[entry] + if(prefs_ss && !prefs_ss.gcDestroyed) + QDEL_NULL(prefs_ss) + for(var/key, setting in preference_settings_character) + QDEL_NULL(setting) + preference_settings_character -= key + for(var/key, setting in preference_settings_client) + QDEL_NULL(setting) + preference_settings_client -= key + ..() + +// Try to load a SQLite save for this character, creating it if there's nothing. +/datum/preferences/proc/try_load_save_sqlite(var/theckey, var/theclient, var/theslot) + var/attempts = 0 + var/database/query/existing_player_check = new + + existing_player_check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", theckey, theslot) + + if(existing_player_check.Execute(db)) + if(!existing_player_check.NextRow()) + while(!create_character_sqlite(theckey, theclient, theslot) && attempts < 5) + sleep(15) + attempts++ + if(attempts >= 5)//failsafe so people don't get locked out of the round forever + fallback_random_character(theckey, theclient) + save_character_sqlite(theckey, theclient, theslot) + else + while(!load_character_sqlite(theckey, theclient, theslot) && attempts < 5) + sleep(15) + attempts++ + if(attempts >= 5)//failsafe so people don't get locked out of the round forever + fallback_random_character(theckey, theclient) + + saveloaded = 1 + theclient << 'sound/misc/prefsready.wav' + +/datum/preferences/proc/fallback_random_character(var/theclient, var/theckey) + randomize_appearance_for(random_gender = TRUE) + var/species = get_pref(/datum/preference_setting/string/species) + var/gender = get_pref(/datum/preference_setting/enum/gender) + var/datum/preference_setting/name_setting = get_pref_datum(/datum/preference_setting/string/real_name) + name_setting.setting = random_name(gender, species) + log_debug("Player [theckey] FAILED to load save 5 times and has been randomized.") + log_admin("Player [theckey] FAILED to load save 5 times and has been randomized.") + if(theclient) + alert(theclient, "For some reason you've failed to load your save slot 5 times now, so you've been generated a random character. Don't worry, it didn't overwrite your old one. Saving it may overwrite it, so be careful.","Randomized Character", "OK") + +/datum/preferences/proc/GetPlayerAltTitle(datum/job/job) + var/list/player_alt_titles = get_pref(/datum/preference_setting/list_values/player_alt_titles) + var/alt_title = player_alt_titles[job.title] + if(!alt_title || !(alt_title in job.alt_titles)) + return job.title + return alt_title + +/datum/preferences/proc/SetPlayerAltTitle(datum/job/job, new_title) + // remove existing entry + var/datum/preference_setting/p_alt_tiltes_set = get_pref_datum(/datum/preference_setting/list_values/player_alt_titles) + var/list/player_alt_titles = p_alt_tiltes_set.setting + if(player_alt_titles.Find(job.title)) + player_alt_titles -= job.title + // add one if it's not default + if(job.title != new_title) + player_alt_titles[job.title] = new_title + +/datum/preferences/proc/SetJob(mob/user, role, increase) + var/datum/job/job = job_master.GetJob(role) + var/species = get_pref(/datum/preference_setting/string/species) + if(!job) + user << browse(null, "window=mob_occupation") + ShowChoices(user) + return + + if(job.species_blacklist.Find(species)) //Check if our species is in the blacklist + to_chat(user, "Your species ([species]) can't have this job!") + return + + if(job.species_whitelist.len) //Whitelist isn't empty - check if our species is in the whitelist + if(!job.species_whitelist.Find(species)) + var/allowed_species = "" + for(var/S in job.species_whitelist) + allowed_species += "[S]" + + if(job.species_whitelist.Find(S) != job.species_whitelist.len) + allowed_species += ", " + + to_chat(user, "Only the following species can have this job: [allowed_species]. Your species is ([species]).") + return + var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs) + var/new_value = jobs[job.title] + if(increase) + new_value += 1 + if(new_value > JOB_PREF_HIGH) + new_value = JOB_PREF_NEVER + else + new_value -= 1 + if(new_value < JOB_PREF_NEVER) + new_value = JOB_PREF_HIGH + + // If setting a job to high, + // set any other job that is currently high to med + if(new_value == JOB_PREF_HIGH) + for(var/some_job in jobs) + if(jobs[some_job] == JOB_PREF_HIGH) + jobs[some_job] = JOB_PREF_MED + jobs[job.title] = new_value + else if(new_value == JOB_PREF_NEVER) + jobs -= job.title + else + jobs[job.title] = new_value + SetJobsChoice(user) + return 1 + +/datum/preferences/proc/ResetJobs() + // For reference `get_pref` would be equivalent here but I am doing this for clarity. + var/datum/preference_setting/jobs_setting = get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs) + var/list/jobs = jobs_setting.setting + jobs.Cut() + +/datum/preferences/proc/process_link(mob/user, list/href_list) + if(!user) + return + var/datum/preferences_subsection/subsection = subsections[href_list["subsection"]] + if(subsection) + var/result = subsection.process_link(user, href_list) + if(result) + return result + + // General soft-coding stuff. + // Relatively inelegant. Any better idea? + for (var/key, value in preference_settings_client) + var/datum/preference_setting/setting = value + if (setting.sql_name == href_list["preference"]) + setting.process_link(href_list["task"], user, href_list) + ShowChoices(user) + return + + for (var/key, value in preference_settings_character) + var/datum/preference_setting/setting = value + if (setting.sql_name == href_list["preference"]) + setting.process_link(href_list["task"], user, href_list) + return + + if(href_list["task"] == "random_body") + for (var/key, value in preference_settings_character) + var/datum/preference_setting/setting = value + if (setting.sql_name == "real_name") // Bit ugly but have to do it + continue + setting.randomise(user) + ShowChoices(user) + return + + // Some unfortunate hard-coding stuff + if(href_list["preference"] == "records") + if(text2num(href_list["record"]) >= 1) + SetRecords(user) + else + user << browse(null, "window=records") + ShowChoices(user) + + if(href_list["task"] == "med_record") + var/datum/preference_setting/med_record = get_pref_datum(/datum/preference_setting/string/med_record) + var/medmsg = input(usr,"Set your medical notes here.","Medical Records",html_decode(med_record.setting)) as message + + if(medmsg != null) + medmsg = copytext(medmsg, 1, MAX_PAPER_MESSAGE_LEN) + medmsg = html_encode(medmsg) + med_record.setting = medmsg + SetRecords(user) + + if(href_list["task"] == "sec_record") + var/datum/preference_setting/sec_record = get_pref_datum(/datum/preference_setting/string/sec_record) + var/secmsg = input(usr,"Set your security notes here.","Security Records",html_decode(sec_record.setting)) as message + + if(secmsg != null) + secmsg = copytext(secmsg, 1, MAX_PAPER_MESSAGE_LEN) + secmsg = html_encode(secmsg) + + sec_record.setting = secmsg + SetRecords(user) + + if(href_list["task"] == "gen_record") + var/datum/preference_setting/gen_record = get_pref_datum(/datum/preference_setting/string/gen_record) + var/genmsg = input(usr,"Set your employment notes here.","Employment Records",html_decode(gen_record.setting)) as message + + if(genmsg != null) + genmsg = copytext(genmsg, 1, MAX_PAPER_MESSAGE_LEN) + genmsg = html_encode(genmsg) + + gen_record.setting = genmsg + SetRecords(user) + + return + + // Roles + if(href_list["preference"] == "set_roles") + return SetRoles(user,href_list) + + if(href_list["preference"] == "next_preview_background") + preview_background = next_list_item(preview_background, background_options) + return ShowChoices(user) + if(href_list["preference"] == "previous_preview_background") + preview_background = previous_list_item(preview_background, background_options) + return ShowChoices(user) + + // Special actions + if (href_list["action"]) + switch (href_list["action"]) + if("save") + if(world.timeofday >= (lastPolled + POLLED_LIMIT) || user.client.holder) + save_preferences_sqlite(user, user.ckey) + save_character_sqlite(user.ckey, user, slot) + lastPolled = world.timeofday + else + to_chat(user, "You need to wait [round((((lastPolled + POLLED_LIMIT) - world.timeofday) / 10))] seconds before you can save again.") + //random_character_sqlite(user, user.ckey) + + if("reload") + load_preferences_sqlite(user.ckey) + load_character_sqlite(user.ckey, user, slot) + + if("open_load_dialog") + if(!IsGuestKey(user.key)) + open_load_dialog(user) + // DO NOT update window as it'd steal focus. + return + + if("close_load_dialog") + close_load_dialog(user) + + if("changeslot") + var/num = text2num(href_list["num"]) + try_load_slot(user.ckey, user, num) + var/datum/preference_setting/numerical/default_slot/slot_pref = get_pref_datum(/datum/preference_setting/numerical/default_slot) + slot_pref.setting = num + slot = num + close_load_dialog(user) + ShowChoices(user) + + if("tab") + if(href_list["tab"]) + current_tab = text2num(href_list["tab"]) + + ShowChoices(user) + return + // We made it this far, means link was unprocessed + CRASH("unprocessed href for [client]; data=[json_encode(href_list)]") + +/datum/preferences/proc/copy_to(mob/living/carbon/human/character, safety = 0) + var/datum/preference_setting/name_setting = get_pref_datum(/datum/preference_setting/string/real_name) + var/species = get_pref(/datum/preference_setting/string/species) + var/gender= get_pref(/datum/preference_setting/enum/gender) + if(get_pref(/datum/preference_setting/toggle/be_random_name)) + name_setting.setting = random_name(gender,species) + if(config.humans_need_surnames && species == "Human") + var/firstspace = findtext(name_setting.setting, " ") + var/name_length = length(name_setting.setting) + if(!firstspace) //we need a surname + name_setting.setting += " [pick(last_names)]" + else if(firstspace == name_length) + name_setting.setting += "[pick(last_names)]" + + character.real_name = name_setting.setting + character.name = character.real_name + character.flavor_text = get_pref(/datum/preference_setting/string/flavor_text) + if(character.dna) + character.dna.real_name = character.real_name + character.dna.flavor_text = character.flavor_text + + character.med_record = get_pref(/datum/preference_setting/string/med_record) + character.sec_record = get_pref(/datum/preference_setting/string/sec_record) + character.gen_record = get_pref(/datum/preference_setting/string/gen_record) + + character.setGender(gender) + character.age = get_pref(/datum/preference_setting/numerical/age) + + character.my_appearance.r_eyes = get_pref(/datum/preference_setting/numerical/r_eyes) + character.my_appearance.g_eyes = get_pref(/datum/preference_setting/numerical/g_eyes) + character.my_appearance.b_eyes = get_pref(/datum/preference_setting/numerical/b_eyes) + + character.my_appearance.r_hair = get_pref(/datum/preference_setting/numerical/r_hair) + character.my_appearance.g_hair = get_pref(/datum/preference_setting/numerical/g_hair) + character.my_appearance.b_hair = get_pref(/datum/preference_setting/numerical/b_hair) + + character.my_appearance.r_facial = get_pref(/datum/preference_setting/numerical/r_facial) + character.my_appearance.g_facial = get_pref(/datum/preference_setting/numerical/g_facial) + character.my_appearance.b_facial = get_pref(/datum/preference_setting/numerical/b_facial) + + character.my_appearance.s_tone = get_pref(/datum/preference_setting/numerical/s_tone) + + character.my_appearance.h_style = get_pref(/datum/preference_setting/string/h_style) + character.my_appearance.f_style = get_pref(/datum/preference_setting/string/f_style) + + character.dna.ResetUIFrom(character) + + if(get_pref(/datum/preference_setting/toggle/be_random_body)) + character.my_appearance.randomise() + + // Destroy/cyborgize organs + + for(var/name in organ_data) + var/datum/organ/external/O = character.organs_by_name[name] + var/datum/organ/internal/I = character.internal_organs_by_name[name] + var/status = organ_data[name] + + if(status == "amputated") + O.status &= ~ORGAN_ROBOT + O.status &= ~ORGAN_PEG + O.amputated = 1 + O.status |= ORGAN_DESTROYED + O.destspawn = 1 + else if(status == "cyborg") + O.status &= ~ORGAN_PEG + O.status |= ORGAN_ROBOT + else if(status == "peg") + O.status &= ~ORGAN_ROBOT + O.status |= ORGAN_PEG + else if(status == "assisted") + I?.mechassist() + else if(status == "mechanical") + I?.mechanize() + else + continue + var/disabilities = get_pref(/datum/preference_setting/binary_flag/disabilities) + var/datum/species/chosen_species = all_species[species] + if( (disabilities & DISABILITY_FLAG_FAT) && (chosen_species.anatomy_flags & CAN_BE_FAT) ) + character.mutations += M_FAT + if(disabilities & DISABILITY_FLAG_NEARSIGHTED) + character.disabilities|=NEARSIGHTED + if(disabilities & DISABILITY_FLAG_EPILEPTIC) + character.disabilities|=EPILEPSY + if(disabilities & DISABILITY_FLAG_EHS) + character.disabilities|=ELECTROSENSE + if(disabilities & DISABILITY_FLAG_DEAF) + character.sdisabilities|=DEAF + if(disabilities & DISABILITY_FLAG_BLIND) + character.sdisabilities|=BLIND + /*if(disabilities & DISABILITY_FLAG_COUGHING) + character.sdisabilities|=COUGHING + if(disabilities & DISABILITY_FLAG_TOURETTES) + character.sdisabilities|=TOURETTES Still working on it. - Angelite */ + + var/underwear = get_pref(/datum/preference_setting/numerical/underwear) + if(underwear > underwear_m.len || underwear < 1) + underwear = 0 //I'm sure this is 100% unnecessary, but I'm paranoid... sue me. //HAH NOW NO MORE MAGIC CLONING UNDIES + character.underwear = underwear + + var/backbag = get_pref(/datum/preference_setting/numerical/backbag) + if(backbag > 5 || 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(character) && !ismushroom(character)) //Ghosts and mushroom people are neuter by default + message_admins("[character] ([character.ckey]) has spawned with their gender as plural or neuter. Please notify coders.") + character.setGender(MALE) + +/datum/preferences/proc/SetRoles(var/mob/user, var/list/href_list) + // We just grab the role from the POST(?) data. + for(var/role_id in special_roles) + if(role_id in href_list) + roles[role_id] = text2num(href_list[role_id]) + ShowChoices(user) + return 1 + +/datum/preferences/proc/get_pref_datum(var/datum/preference_setting/type) as /datum/preference_setting + if (type in preference_settings_client) + var/datum/preference_setting/the_setting = preference_settings_client[type] + if (!the_setting) + stack_trace("unset client preference [type] on [client]:[client.mob.type]") + return initial(type.default_setting) + return the_setting + if (type in preference_settings_character) + var/datum/preference_setting/the_setting = preference_settings_character[type] + if (!the_setting) + stack_trace("unset character preference [type] on [client]:[client.mob.type]") + return initial(type.default_setting) + return the_setting + CRASH("invalid preference setting requested: [type] on [src.client]") + +/datum/preferences/proc/get_pref(var/datum/preference_setting/type) + var/datum/preference_setting/the_setting = get_pref_datum(type) + return the_setting.setting + +// limbs & organs +/datum/preferences/proc/change_pref_datum_limb(var/limb_internal_name, var/limb_internal_state) + for (var/key, setting_datum in preference_settings_character) + var/datum/preference_setting/setting_type = key + if (initial(setting_type.sql_table) != "limbs") + continue + if (initial(setting_type.sql_name) == limb_internal_name) + var/datum/preference_setting/limb = preference_settings_character[setting_type] + limb.setting = limb_internal_state + return + +/client/verb/modify_preferences(page as num) + set name = "modifypreferences" + set hidden = 1 + if(!prefs.saveloaded) + to_chat(src, "Your character preferences have not yet loaded.") + return + switch(page) + if(1) + prefs.current_tab = GENERAL_SETUP + if(2) + prefs.current_tab = SPECIAL_ROLES_SETUP + prefs.ShowChoices(usr) diff --git a/code/modules/client/preferences/preferences_savefile.dm b/code/modules/client/preferences/preferences_savefile.dm new file mode 100644 index 00000000000..7aefb46ea9b --- /dev/null +++ b/code/modules/client/preferences/preferences_savefile.dm @@ -0,0 +1,639 @@ +/// IF YOU NEED A FIELD ADDED TO THE DATABASE, CREATE A MIGRATION SO SHIT GETS UPDATED. +/// Also update SQL/players2.sql. +/// SEE code/modules/migrations/SS13_Prefs/ + +/datum/preferences/proc/try_load_preferences(var/ckey, var/mob/user) + // Check client: + var/database/query/client_check = new + client_check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey) + if(client_check.Execute(db)) + if(!client_check.NextRow()) + return save_preferences_sqlite(user, ckey) + else + return load_preferences_sqlite(ckey) + else + WARNING("Error in try_load_preferences [__FILE__] ln:[__LINE__] #:[client_check.Error()] - [client_check.ErrorMsg()]") + +/datum/preferences/proc/load_preferences_sqlite(var/ckey) + var/list/database_data = execute_load_pref_query(ckey) + read_database_data_client(database_data) + initialize_preferences() + if (islist(database_data)) + return 1 + else + return 0 + +/datum/preferences/proc/execute_load_pref_query(var/ckey) + var/list/preference_list_client = new + var/database/query/check = new + var/database/query/q = new + check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey) + if(check.Execute(db)) + if(!check.NextRow()) + WARNING("Empty client setting for [ckey]") + stack_trace("Empty client setting for [ckey]!") + return 0 + else + WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]") + stack_trace("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + q.Add("SELECT * FROM client WHERE ckey = ?", ckey) + if(q.Execute(db)) + while(q.NextRow()) + var/list/row = q.GetRowData() + for(var/a in row) + preference_list_client[a] = row[a] + else + WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + return preference_list_client + +/datum/preferences/proc/read_database_data_client(var/list/database_data) + for (var/key, value in preference_settings_client) + var/datum/preference_setting/setting_datum = value + try + setting_datum.setting = setting_datum.load_sql(database_data[setting_datum.sql_name]) // First we load... + setting_datum.setting = setting_datum.sanitize_setting(setting_datum.setting) // Then we sanitize + catch + CRASH("wrong setting loaded [key] which got [setting_datum.sql_name], wasn't in list: [json_encode(database_data)]") + +/datum/preferences/proc/initialize_preferences(client_login = 0) + var/attack_animation = get_pref(/datum/preference_setting/enum/attack_animations) + if(attack_animation == PERSON_ANIMATION) + person_animation_viewers |= client + item_animation_viewers -= client + else if(attack_animation == ITEM_ANIMATION) + item_animation_viewers |= client + person_animation_viewers -= client + else + item_animation_viewers -= client + person_animation_viewers -= client + +/datum/preferences/proc/save_preferences_sqlite(var/user, var/ckey) + var/database/query/check = new + var/database/query/q = new + check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey) + if(check.Execute(db)) + if(!check.NextRow()) + var/list/arguments_query = new_db_entry_query_args(ckey) + q.Add(arglist(arguments_query)) + if(!q.Execute(db)) + WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]. sql= [arguments_query[1]]") + return 0 + else + var/list/arguments_query = save_db_entry_query_args(ckey) + q.Add(arglist(arguments_query)) + if(!q.Execute(db)) + WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]. sql= [arguments_query[1]]") + return 0 + else + WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + to_chat(user, "Preferences Updated.") + lastPolled = world.timeofday + return 1 + +/datum/preferences/proc/load_character_sqlite(var/ckey, var/user, var/slot) + var/list/preference_list = new + var/database/query/q = new + var/database/query/check = new + + check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot) + if(check.Execute(db)) + if(!check.NextRow()) + to_chat(user, "You have no character file to load, please save one first.") + WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") + return 0 + else + WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") + stack_trace("load_save_sqlite Check Error #: [check.Error()] - [check.ErrorMsg()]") + + return 0 + + var/sql = load_character_sql() + // Ckey and slot and ?/joker parameters in the prepared query + q.Add(sql, ckey, slot) + + if(q.Execute(db)) + while(q.NextRow()) + var/list/row = q.GetRowData() + for(var/a in row) + preference_list[a] = row[a] + else + WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") + stack_trace("load_save_sqlite Error [__LINE__] #: [q.Error()] - [q.ErrorMsg()]. Dump [sql]") + return 0 + + for (var/key, setting in preference_settings_character) + var/datum/preference_setting/the_setting = setting + the_setting.setting = the_setting.load_sql(preference_list[the_setting.sql_name]) + the_setting.setting = the_setting.sanitize_setting(the_setting.setting) + + // Antag roles. This is still a bit hardcoded but meeeeeh + for(var/role_id in special_roles) + roles[role_id]=0 + q = new + q.Add("SELECT role, preference FROM client_roles WHERE ckey=? AND slot=?", ckey, slot) + if(q.Execute(db)) + while(q.NextRow()) + var/list/row = q.GetRowData() + roles[row["role"]] = text2num(row["preference"]) + else + WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") + stack_trace("Error in load_save_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + + // Unused. + //if(!skills) + // skills = list() + //if(!used_skillpoints) + // used_skillpoints= 0 + + if(user) + to_chat(user, "Successfully loaded [get_pref(/datum/preference_setting/string/real_name)].") + + return 1 + +/datum/preferences/proc/save_character_sqlite(var/ckey, var/user, var/slot_chosen) + if(slot > MAX_SAVE_SLOTS) + to_chat(user, "You are limited to [MAX_SAVE_SLOTS] character slots.") + message_admins("[ckey] attempted to override character slot limit") + return 0 + + var/database/query/q = new + + // This checks if the DB is still connected to us. + var/database/query/check = new + + // General player + check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(!check.NextRow()) + CRASH("Trying to save a character slot but there's no slot. Ckey = [ckey], slot_chosen = [slot_chosen]") + else + var/list/sql_arguments_update_character = update_db_entry_query_character_args(ckey, slot_chosen) + q.Add(arglist(sql_arguments_update_character)) + if(!q.Execute(db)) + WARNING("Error in update_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in update_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()] sql=[sql_arguments_update_character[1]]") + return 0 + to_chat(user, "Updated Character") + else + WARNING("Error at character creation/update: [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error at character creation/update: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]") + return 0 + + check.Add("SELECT player_ckey FROM body WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(!check.NextRow()) + CRASH("Trying to save a character slot but there's no slot") + else + var/list/sql_arguments_update_body = update_db_entry_query_body_args(ckey, slot_chosen) + q.Add(arglist(sql_arguments_update_body)) + if(!q.Execute(db)) + WARNING("Error in update_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in update_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + return 0 + to_chat(user, "Updated Body") + else + WARNING("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + + // Jobs are left hardcoded for now since they not likely to be fundamentally changed + // However the same proc logic could apply here if we had some job-per-character specific sett + var/datum/preference_setting/jobs = get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs) + var/alternate_option = get_pref(/datum/preference_setting/enum/alternate_option) + check.Add("SELECT player_ckey FROM jobs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(!check.NextRow()) + CRASH("Trying to save a character slot but there's no slot") + else + // 1 2 + q.Add("UPDATE jobs SET alternate_option=?,jobs=? WHERE player_ckey = ? AND player_slot = ?",\ + alternate_option, jobs.save_sql(jobs.setting), ckey, slot_chosen) + if(!q.Execute(db)) + WARNING("Error in update_jobs_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in update_jobs_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + to_chat(user, "Updated Job List") + else + WARNING("Error at jobs selection sqlite ln [__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error at jobs selection sqlite ln [__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + + // Limbs + check.Add("SELECT player_ckey FROM limbs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(!check.NextRow()) + CRASH("Trying to save a character slot but there's no slot") + else + for(var/key, setting in preference_settings_character) + var/datum/preference_setting/the_setting = setting + if (the_setting.sql_table != "limbs") + continue + q.Add("UPDATE limbs SET [the_setting.sql_name] = ? WHERE player_ckey = ? AND player_slot = ?", the_setting.setting, ckey, slot_chosen) + if(!q.Execute(db)) + WARNING("Error in update limbs sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in update limbs sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + to_chat(user, "Updated Limbs") + else + WARNING("Error at savelimbs selection from ckey sqlite character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error at limbs selection from ckey sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + + for(var/role_id in roles) + if(!(roles[role_id] & ROLEPREF_SAVE)) + continue + q = new + q.Add("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES (?,?,?,?)", ckey, slot, role_id, (roles[role_id] & ROLEPREF_VALMASK)) + //testing("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES ('[ckey]',[slot],'[role_id]',[roles[role_id] & ROLEPREF_VALMASK])") + if(!q.Execute(db)) // This never triggers on error, for some reason. + WARNING("ClientRoleInsert: Error #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("ClientRoleInsert: Error #: [q.Error()] - [q.ErrorMsg()]") + return 0 + + to_chat(user, "Successfully saved [get_pref(/datum/preference_setting/string/real_name)]") + + return 1 + +/datum/preferences/proc/create_character_sqlite(var/ckey, var/user, var/slot_chosen) + if(slot > MAX_SAVE_SLOTS) + to_chat(user, "You are limited to [MAX_SAVE_SLOTS] character slots.") + stack_trace("[ckey] attempted to override character slot limit") + return 0 + + var/database/query/q = new + + // This checks if the DB is still connected to us. + var/database/query/check = new + + // General player + check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(check.NextRow()) + CRASH("creating a character where there is already a slot! [slot_chosen]") + var/list/sql_arguments_new_character = new_db_entry_query_character_args(ckey, slot_chosen) + q.Add(arglist(sql_arguments_new_character)) + if (!q.Execute(db)) + WARNING("Error in create_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in create_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()] - sql:[sql_arguments_new_character[1]]") + return 0 + to_chat(user, "Created Character") + else + WARNING("Error at character creation: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]") + stack_trace("Error at character creation: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]") + return 0 + + check.Add("SELECT player_ckey FROM body WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(check.NextRow()) + CRASH("creating a body where there is already a slot!") + var/list/sql_arguments_new_body = new_db_entry_query_body_args(ckey, slot_chosen) + q.Add(arglist(sql_arguments_new_body)) + if(!q.Execute(db)) + WARNING("Error in create_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in create_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + return 0 + to_chat(user, "Created Body") + else + WARNING("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]") + stack_trace("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + + // Jobs are left hardcoded for now since they not likely to be fundamentally changed + // However the same proc logic could apply here if we had some job-per-character specific sett + var/datum/preference_setting/jobs = get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs) + var/alternate_option = get_pref(/datum/preference_setting/enum/alternate_option) + check.Add("SELECT player_ckey FROM jobs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(check.NextRow()) + CRASH("creating a body where there is already a slot : [ckey], [slot_chosen]") + // 1 2 3 4 + q.Add("INSERT INTO jobs (player_ckey,player_slot,alternate_option,jobs) \ + VALUES (?, ?, ?, ?)", \ + ckey, slot_chosen, alternate_option, jobs.save_sql(jobs.setting)) + if(!q.Execute(db)) + WARNING("Error in create_jobs_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in create_jobs_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + to_chat(user, "Created Job list") + else + WARNING("Error at jobs selection sqlite ln [__LINE__] #:[check.Error()] - [check.ErrorMsg()]") + stack_trace("Error at jobs selection sqlite ln [__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + + // Limbs + check.Add("SELECT player_ckey FROM limbs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen) + if(check.Execute(db)) + if(check.NextRow()) + CRASH("creating limbs where there is already a slot!") + q.Add("INSERT INTO limbs (player_ckey, player_slot) VALUES (?,?)", ckey, slot_chosen) + if(!q.Execute(db)) + WARNING("Error in insert into Limbs sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in insert into Limbs sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + for(var/key, setting in preference_settings_character) + var/datum/preference_setting/the_setting = setting + if (the_setting.sql_table != "limbs") + continue + q.Add("UPDATE limbs SET [the_setting.sql_name]=? WHERE player_ckey = ? AND player_slot = ?", the_setting.default_setting, ckey, slot_chosen) + if(!q.Execute(db)) + WARNING("Error in update limbs (creation) sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in update limbs (creation) sqlite [__FILE__] ln:[__LINE__] #; [q.Error()] - [q.ErrorMsg()]") + return 0 + organ_data[the_setting.sql_name] = the_setting.setting + to_chat(user, "Created Limbs") + else + WARNING("Error at save limbs [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]") + stack_trace("Error at save limbs [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") + return 0 + + for(var/role_id in roles) + if(!(roles[role_id] & ROLEPREF_SAVE)) + continue + q = new + q.Add("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES (?,?,?,?)", ckey, slot, role_id, (roles[role_id] & ROLEPREF_VALMASK)) + //testing("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES ('[ckey]',[slot],'[role_id]',[roles[role_id] & ROLEPREF_VALMASK])") + if(!q.Execute(db)) // This never triggers on error, for some reason. + WARNING("ClientRoleInsert: Error #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("ClientRoleInsert: Error #: [q.Error()] - [q.ErrorMsg()]") + return 0 + + randomize_appearance_for(random_gender = TRUE) + var/gender = get_pref(/datum/preference_setting/enum/gender) + var/species = get_pref(/datum/preference_setting/string/species) + var/datum/preference_setting/name_setting = get_pref_datum(/datum/preference_setting/string/real_name) + name_setting.setting = random_name(gender, species) + + to_chat(user, "Successfully created [get_pref(/datum/preference_setting/string/real_name)]") + + return 1 + + +/datum/preferences/proc/try_load_slot(var/ckey, var/user, var/num) + var/database/query/check = new + + check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, num) + if(check.Execute(db)) + if(!check.NextRow()) // No slot + return create_character_sqlite(ckey, user, num) + else // Has a slot + return load_character_sqlite(ckey, user, num) + else + WARNING("[__LINE__]: datum/preferences/try_load_slot has returned") + stack_trace("try_load_slot Check Error #: [check.Error()] - [check.ErrorMsg()]") + + return 0 + +// ============================ MISC PROCS HELPER ============================ + +/datum/preferences/proc/SetChangelog(ckey,hash) + var/datum/preference_setting/lastchangelog = get_pref_datum(/datum/preference_setting/string/changelog) + lastchangelog.setting=hash + var/database/query/q = new + q.Add("UPDATE client SET lastchangelog=? WHERE ckey=?",lastchangelog.setting,ckey) + if(!q.Execute(db)) + WARNING("Error in Setchangelog [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in SetChangelog [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + + +/datum/preferences/proc/random_character_sqlite(var/user, var/ckey) + var/database/query/q = new + var/list/slot_list = new + q.Add("SELECT player_slot FROM players WHERE player_ckey=?", ckey) + if(q.Execute(db)) + while(q.NextRow()) + slot_list.Add(q.GetColumn(1)) + else + WARNING("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + stack_trace("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + return 0 + var/random_slot = pick(slot_list) + save_character_sqlite(ckey, user, random_slot) + return 1 + +// -- DB SQL HELPERS -- + +// The sql text this outputs looks like this : +// "INSERT into players (player_ckey, player_slot, var1, ...)" +// And the list it returns is like: +// list(sql, ckey, slot, var1, var2, ...) +// This list is automatically converted into arguments via arglist + + +// ======= LOADER ======= + +// This loads all the character data from all SQL tables at once +// Looks like SELECT players.real_name, ..., body.r_eyes, ... FROM players INNER JOIN {joining operation on ckeys} +// WHERE players.player_ckey = ? and players.player_slot = ? + +/datum/preferences/proc/load_character_sql() + var/sql = "SELECT " + + for (var/database_setting in typesof(/datum/preference_setting)) + var/datum/preference_setting/setting_datum = database_setting + if (!initial(setting_datum.enabled)) + continue + if (initial(setting_datum.sql_table) == "client") + continue + sql += "[initial(setting_datum.sql_table)].[initial(setting_datum.sql_name)], " + sql = copytext(sql, 1, length(sql) - 1) // Get rid of the last , + sql += " " // Empty space + // This is still relatively hard-coded but it's more or less fine for now + sql += {" + FROM + players + INNER JOIN + limbs + ON + ( + players.player_ckey = limbs.player_ckey) + AND ( + players.player_slot = limbs.player_slot) + INNER JOIN + jobs + ON + ( + limbs.player_ckey = jobs.player_ckey) + AND ( + limbs.player_slot = jobs.player_slot) + INNER JOIN + body + ON + ( + jobs.player_ckey = body.player_ckey) + AND ( + jobs.player_slot = body.player_slot) + WHERE + players.player_ckey = ? + AND players.player_slot = ? ;"} + return sql + +// ============================ FOR CLIENT PREFS ============================ + +// The sql text this outputs looks like this : +// "INSERT INTO client (ckey, var1, var2, ...) VALUES (?, ?, ?, ...)" +// And the list it returns is like: +// list(sql, ckey, var1, var2, ...) +// This list is automatically converted into arguments via arglist + +// -- You should not need to modify this - this automatically reads all settings and save them. +/datum/preferences/proc/new_db_entry_query_args(var/ckey) + var/list/returned_list = list() + var/sql_text = "INSERT into client (ckey" + var/sql_text_end = ") VALUES (?" + returned_list.Add(sql_text) // First item in the query is the SQL + returned_list.Add(ckey) // Second item is the ckey (first joker) + // This is a prepared queries. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list + // This MEANS that the order of the param list is pretty important! + for (var/key, setting in preference_settings_client) + var/datum/preference_setting/the_setting = setting + sql_text += ", [the_setting.sql_name]" + sql_text_end += ", ?" + returned_list.Add(the_setting.setting) + sql_text_end += ")" + returned_list[1] = "[sql_text][sql_text_end]" + return returned_list + +// The sql text this outputs looks like this : +// "UPDATE client SET var1=?, var2=?, ... WHERE CKEY=?" +// And the list it returns is like: +// list(sql, var1, var2, ..., ckey) +// This list is automatically converted into arguments via arglist + +/datum/preferences/proc/save_db_entry_query_args(var/ckey) + var/list/returned_list = list() + var/sql_text = "UPDATE client SET " + var/sql_text_end = " WHERE ckey = ?" + returned_list.Add(sql_text) // First item in the query is the SQL + // This is a prepared queries. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list + // This MEANS that the order of the param list is pretty important! + for (var/key, setting in preference_settings_client) + var/datum/preference_setting/the_setting = setting + sql_text += "[the_setting.sql_name]=?, " + returned_list.Add(the_setting.setting) + sql_text = copytext(sql_text, 1, length(sql_text)-1) // Remove the last , + returned_list.Add(ckey) // This time, the ckey is the last joker + returned_list[1] = "[sql_text][sql_text_end]" + return returned_list + + +// ============================ FOR JOBS, ALT-TITLES, ETC ============================ + +// The sql text this outputs looks like this : +// "INSERT INTO players (player_ckey=?, player_slot=?, var1=?, var2=? ...) VALUES (?, ?, ?, ...)" +// And the list it returns is like: +// list(sql, ckey, slot, var1, ...) +// This list is automatically converted into arguments via arglist + +/datum/preferences/proc/new_db_entry_query_character_args(var/ckey, var/slot) + // -- You should not need to modify this - this automatically reads all settings and save them. + var/list/returned_list = list() + var/sql_text = "INSERT into players (player_ckey,player_slot" + var/sql_text_end = ") VALUES (?,?" + returned_list.Add(sql_text) // First item in the query is the SQL + returned_list.Add(ckey) // Second item is the ckey (first joker) + returned_list.Add(slot) // Third item is the slot (second joker) + // This is a prepared queries. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list + // This MEANS that the order of the param list is pretty important! + for (var/key, setting in preference_settings_character) + var/datum/preference_setting/the_setting = setting + if (the_setting.sql_table != "players") + continue + sql_text += ",[the_setting.sql_name]" + sql_text_end += ",?" + var/data = the_setting.save_sql(the_setting.default_setting) + returned_list.Add(data) + sql_text_end += ")" + returned_list[1] = "[sql_text][sql_text_end]" + return returned_list + +// The sql text this outputs looks like this : +// "UPDATE players SET var1=?, var2=? ... WHERE player_ckey = ? AND player_slot = ?" +// And the list it returns is like: +// list(sql, var1, var2, ..., ckey, slot) +// This list is automatically converted into arguments via arglist + +/datum/preferences/proc/update_db_entry_query_character_args(var/ckey, var/slot) + // -- You should not need to modify this - this automatically reads all settings and save them. + var/list/returned_list = list() + var/sql_text = "UPDATE players SET " // White space is important + var/sql_text_end = " WHERE player_ckey = ? AND player_slot = ?" + returned_list.Add(sql_text) // First item in the query is the SQL + + // This is a prepared query. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list + // This MEANS that the order of the param list is pretty important! + for (var/key, setting in preference_settings_character) + var/datum/preference_setting/the_setting = setting + if (the_setting.sql_table != "players") + continue + sql_text += "[the_setting.sql_name]=?," + returned_list.Add(the_setting.save_sql(the_setting.setting)) + sql_text = copytext(sql_text, 1, length(sql_text)) // Remove the last , + returned_list.Add(ckey) // Second-to-last item is the ckey (second to last joker) + returned_list.Add(slot) // Last item is the slot (last joker) + returned_list[1] = "[sql_text][sql_text_end]" + return returned_list + +// ============================ FOR BODIES ============================ + +// The sql text this outputs looks like this : +// "INSERT into body (player_ckey, player_slot, var1, ...)" +// And the list it returns is like: +// list(sql, ckey, slot, var1, var2, ...) +// This list is automatically converted into arguments via arglist + +/datum/preferences/proc/new_db_entry_query_body_args(var/ckey, var/slot) + // -- You should not need to modify this - this automatically reads all settings and save them. + var/list/returned_list = list() + var/sql_text = "INSERT into body (player_ckey,player_slot" + var/sql_text_end = ") VALUES (?,?" + returned_list.Add(sql_text) // First item in the query is the SQL + returned_list.Add(ckey) // Second item is the ckey (first joker) + returned_list.Add(slot) // Third item is the slot (second joker) + // This is a prepared query. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list + // This MEANS that the order of the param list is pretty important! + for (var/key, setting in preference_settings_character) + var/datum/preference_setting/the_setting = setting + if (the_setting.sql_table != "body") + continue + sql_text += ",[the_setting.sql_name]" + sql_text_end += ",?" + returned_list.Add(the_setting.save_sql(the_setting.default_setting)) + sql_text_end += ")" + returned_list[1] = "[sql_text][sql_text_end]" + return returned_list + +// The sql text this outputs looks like this : +// "UPDATE body SET var1=?, var2=? ... WHERE player_ckey = ? AND player_slot = ?" +// And the list it returns is like: +// list(sql, var1, var2, ..., ckey, slot) +// This list is automatically converted into arguments via arglist + +/datum/preferences/proc/update_db_entry_query_body_args(var/ckey, var/slot) + // -- You should not need to modify this - this automatically reads all settings and save them. + var/list/returned_list = list() + var/sql_text = "UPDATE body SET " // White space is important + var/sql_text_end = " WHERE player_ckey = ? AND player_slot = ?" + returned_list.Add(sql_text) // First item in the query is the SQL + + // This is a prepared query. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list + // This MEANS that the order of the param list is pretty important! + for (var/key, setting in preference_settings_character) + var/datum/preference_setting/the_setting = setting + if (the_setting.sql_table != "body") + continue + sql_text += "[the_setting.sql_name]=?," + returned_list.Add(the_setting.save_sql(the_setting.setting)) + sql_text = copytext(sql_text, 1, length(sql_text)) // Remove the last , + returned_list.Add(ckey) // Second-to-last item is the ckey (second to last joker) + returned_list.Add(slot) // Last item is the slot (last joker) + returned_list[1] = "[sql_text][sql_text_end]" + return returned_list diff --git a/code/modules/client/preferences/preferences_ui.dm b/code/modules/client/preferences/preferences_ui.dm new file mode 100644 index 00000000000..86217a7d530 --- /dev/null +++ b/code/modules/client/preferences/preferences_ui.dm @@ -0,0 +1,658 @@ + +/datum/preferences/proc/setup_character_options(var/dat, var/user) + + var/race_skin_tone_desc = skintone2racedescription(get_pref(/datum/preference_setting/numerical/s_tone), get_pref(/datum/preference_setting/string/species)) + dat += {"
    +

    Occupation Choices

    + Set Occupation Preferences
    +
    + +

    Identity

    +
    + Random Name + + Always Random Name: [get_pref(/datum/preference_setting/toggle/be_random_name) ? "Yes" : "No"]
    + + Name: [get_pref(/datum/preference_setting/string/real_name)]
    + + Gender: [get_pref(/datum/preference_setting/enum/gender) == MALE ? "Male" : "Female"]
    + + Age: [get_pref(/datum/preference_setting/numerical/age)] +
    +
    + Background < >
    +
    + +

    Body

    + Random Body + + Always Random Body: [get_pref(/datum/preference_setting/toggle/be_random_body) ? "Yes" : "No"] +
    + + + + + + + + + + + +
    +
    + Species: + + [get_pref(/datum/preference_setting/string/species)] +
    + + Tertiary Language: + + [get_pref(/datum/preference_setting/string/language)] +
    + + Skin Tone: + + [get_pref(/datum/preference_setting/string/species) == "Human" ? "[-get_pref(/datum/preference_setting/numerical/s_tone) + 35]/220" : "[get_pref(/datum/preference_setting/numerical/s_tone)]"] - [race_skin_tone_desc] +

    +
    +
    +

    Hair Style

    + + [get_pref(/datum/preference_setting/string/h_style)] +
    + < + >
    +     + Change
    +
    +
    +

    Facial Hair Style

    +
    + + [get_pref(/datum/preference_setting/string/f_style)] +
    + < + >
    +     + Change
    +
    +
    +

    Eye Color

    +
    +     + Change
    +
    +
    + + Handicaps: + Set
    + + Limbs: + Set
    + + Organs: + Set
    + + Underwear: + [get_pref(/datum/preference_setting/enum/gender) == MALE ? "[underwear_m[get_pref(/datum/preference_setting/numerical/underwear)]]" : "[underwear_f[get_pref(/datum/preference_setting/numerical/underwear)]]"] + +
    + + Backpack: + + [backbaglist[get_pref(/datum/preference_setting/numerical/backbag)]] +
    + + Nanotrasen Relation: + + [get_pref(/datum/preference_setting/enum/string/nanotrasen_relation)] +
    + + Flavor Text: + Set
    + + Character records: + [jobban_isbanned(user, "Records") ? "Banned" : "Set"]
    + + Bank account security preference: + + [bank_security_num2text(get_pref(/datum/preference_setting/enum/bank_security))] +
    + + Percent of wages sent to ID virtual wallet: + + [get_pref(/datum/preference_setting/numerical/wage_ratio)] +
    + "} + + + return dat + +/datum/preferences/proc/setup_UI(var/dat, var/user) + + + dat += {"UI Style: [get_pref(/datum/preference_setting/string/UI_style)]
    + Custom UI(recommended for White UI):    
    Color: [get_pref(/datum/preference_setting/string/UI_style_color)]
    + Alpha(transparency): [get_pref(/datum/preference_setting/numerical/UI_style_alpha)]
    + "} + + return dat + +/datum/preferences/proc/setup_special(var/dat, var/mob/user) + if(user.client.holder) + dat += {" +

    Admin Only Settings

    + + "} + + dat += {" +

    General Settings

    +
    +
    + + FPS: + [get_pref(/datum/preference_setting/numerical/fps)]
    + + Space Parallax: + [get_pref(/datum/preference_setting/toggle/space_parallax) ? "Enabled" : "Disabled"]
    + + Parallax Speed: + [get_pref(/datum/preference_setting/numerical/parallax_speed) ]
    + + Space Dust: + [get_pref(/datum/preference_setting/toggle/space_dust) ? "Yes" : "No"]
    + + Play admin midis: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_MIDI) ? "Yes" : "No"]
    + + Play lobby music: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_LOBBY) ? "Yes" : "No"]
    + + Play Ambience: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_AMBIENCE) ? "Yes" : "No"]
    + [(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_AMBIENCE)? \ + "Ambience Volume:[get_pref(/datum/preference_setting/numerical/ambience_volume)]
    ":""] + + Radio Headset Sounds: + [headset_sound_text2num[get_pref(/datum/preference_setting/numerical/headset_sound)+1]]
    + + Hear streamed media: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_STREAMING) ? "Yes" : "No"]
    + + Streaming Program: + [get_pref(/datum/preference_setting/toggle/usewmp) ? "WMP (compatibility)" : "VLC (requires plugin)"]
    + + Streaming Volume + [get_pref(/datum/preference_setting/numerical/volume) ]
    + + Hear player voices + [(get_pref(/datum/preference_setting/toggle/hear_voicesound)) ? "Yes" : "No"]
    + + Hear instruments + [(get_pref(/datum/preference_setting/toggle/hear_instruments)) ? "Yes":"No"]
    + + Progress Bars: + [get_pref(/datum/preference_setting/toggle/progress_bars) ? "Yes" : "No"]
    + + Pause after first step: + [get_pref(/datum/preference_setting/toggle/stumble) ? "Yes" : "No"]
    + + Pulling action: + [get_pref(/datum/preference_setting/toggle/pulltoggle) ? "Toggle Pulling" : "Always Pull"]
    + + Solo Antag Objectives: + [get_pref(/datum/preference_setting/toggle/antag_objectives) ? "Standard" : "Freeform"]
    + + Say bubbles: + [get_pref(/datum/preference_setting/toggle/typing_indicator) ? "Active" : "Inactive"]
    +
    + +
    + + Randomized Character Slot: + [get_pref(/datum/preference_setting/toggle/randomslot) ? "Yes" : "No"]
    + + Show Deadchat: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD) ? "On" : "Off"]
    + + Ghost Hearing: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS) ? "All Speech" : "Nearby Speech"]
    + + Ghost Sight: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearby Emotes"]
    + + Ghost Radio: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTRADIO) ? "All Chatter" : "Nearby Speakers"]
    + + Ghost PDA: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTPDA) ? "All PDA Messages" : "No PDA Messages"]
    + + Show OOC: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_OOC) ? "Enabled" : "Disabled"]
    + + Show LOOC: + [(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC) ? "Enabled" : "Disabled"]
    + + Show Tooltips: + [get_pref(/datum/preference_setting/toggle/tooltips) ? "Yes" : "No"]
    + + Adminhelp Special Tab: + [special_popup_text2num[get_pref(/datum/preference_setting/enum/special_popup)+1]]
    + + Attack Animations: + [get_pref(/datum/preference_setting/enum/attack_animations) ? (get_pref(/datum/preference_setting/enum/attack_animations) == ITEM_ANIMATION? "Item Anim." : "Person Anim.") : "No"]
    + + Show Credits (?): + [get_pref(/datum/preference_setting/enum/credits)]
    + + Server Shutdown Jingle (?): + [get_pref(/datum/preference_setting/enum/jingle)]
    + Credits/Jingle Volume: + + [get_pref(/datum/preference_setting/numerical/credits_volume)]
    + + Window Flashing + [get_pref(/datum/preference_setting/toggle/window_flashing) ? "Yes":"No"]
    + + Fancy tgui: + [get_pref(/datum/preference_setting/toggle/tgui_fancy) ? "Enabled" : "Disabled"]
    + +
    Runechat prefererences
    + + Chat on map for mobs: + [get_pref(/datum/preference_setting/toggle/mob_chat_on_map) ? "Enabled" : "Disabled"]
    + + Chat on map for objects: + [get_pref(/datum/preference_setting/toggle/obj_chat_on_map) ? "Enabled" : "Disabled"]
    + + No goonchat messages for objects: + [get_pref(/datum/preference_setting/toggle/no_goonchat_for_obj) ? "Enabled" : "Disabled"]
    + + Runechat message char limit: + [get_pref(/datum/preference_setting/numerical/max_chat_length)]
    +
    +
    "} + + if(config.allow_Metadata) + dat += "OOC Notes: Edit
    " + + return dat + +/datum/preferences/proc/getPrefLevelText(var/datum/job/job) + var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs) + switch(jobs[job.title]) + if(JOB_PREF_HIGH) + return "High" + if(JOB_PREF_MED) + return "Medium" + if(JOB_PREF_LOW) + return "Low" + return "NEVER" + +/datum/preferences/proc/SetJobsChoice(mob/user, limit = 16, list/splitJobs = list("Chief Engineer", "Head of Security"), widthPerColumn = 295, height = 620) + if(!job_master) + return + + //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. + //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. + //width - Screen' width. Defaults to 550 to make it look nice. + //height - Screen's height. Defaults to 500 to make it look nice. + var/width = widthPerColumn + + + var/HTML = "" + HTML += {""} //the event.button == 1 check is brought to you by legacy IE running in wine + + + HTML += {"
    + Choose occupation chances
    +
    Left-click to raise an occupation preference, right-click to lower it.
    + Done

    +
    + "} + + + var/index = -1 + + //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. + var/datum/job/lastJob + if (!job_master) + return + for(var/datum/job/job in job_master.occupations) + index += 1 + if((index >= limit) || (job.title in splitJobs)) + width += widthPerColumn + if((index < limit) && (lastJob != null)) + //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with + //the last job's selection color. Creating a rather nice effect. + for(var/i = 0, i < (limit - index), i += 1) + HTML += "" + HTML += "
      
    " + index = 0 + + HTML += "" + continue + if(!job.player_old_enough(user.client)) + var/available_in_days = job.available_in_days(user.client) + HTML += "[rank]" + continue + if((rank in command_positions) || (rank == "AI"))//Bold head jobs + if(job.alt_titles) + HTML += "[GetPlayerAltTitle(job)]" + else + HTML += "[rank]" + else + if(job.alt_titles) + HTML += "[GetPlayerAltTitle(job)]" + else + HTML += "[rank]" + + + HTML += "" + + + for(var/i = 1, i < (limit - index), i += 1) + HTML += "" + HTML += {"
    " + var/rank = job.title + lastJob = job + if(jobban_isbanned(user, rank)) + HTML += "[rank] \[BANNED]
    \[IN [(available_in_days)] DAYS]
    " + + var/prefLevelLabel = "NEVER" + var/prefLevelColor = "red" + var/species = get_pref(/datum/preference_setting/string/species) + var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs) + + if(job.species_whitelist.len && !job.species_whitelist.Find(species)) + prefLevelLabel = "Unavailable" + prefLevelColor = "gray" + else if(job.species_blacklist.Find(species)) + prefLevelLabel = "Unavailable" + prefLevelColor = "gray" + else + switch(jobs[job.title]) + if(JOB_PREF_HIGH) + prefLevelLabel = "High" + prefLevelColor = "slateblue" + if(JOB_PREF_MED) + prefLevelLabel = "Medium" + prefLevelColor = "green" + if(JOB_PREF_LOW) + prefLevelLabel = "Low" + prefLevelColor = "orange" + + HTML += "" + HTML += "[prefLevelLabel]" + HTML += "
      
    +
    "} + var/alternate_option = get_pref(/datum/preference_setting/enum/alternate_option) + switch(alternate_option) + if(GET_EMPTY_JOB) + HTML += "

    Get unique job

    " + if(GET_RANDOM_JOB) + HTML += "

    Get random job if preferences unavailable

    " + if(BE_ASSISTANT) + HTML += "

    Be assistant if preference unavailable

    " + if(RETURN_TO_LOBBY) + HTML += "

    Return to lobby if preference unavailable

    " + + + HTML += {"
    Reset
    + "} + user << browse(null, "window=preferences") + //user << browse(HTML, "window=mob_occupation;size=[width]x[height]") + var/datum/browser/popup = new(user, "mob_occupation", "
    Occupation Preferences
    ", width, height) + popup.set_content(HTML) + popup.open(0) + return + +/datum/preferences/proc/ShowChoices(mob/user) + if(!user || !user.client) + return + update_preview_icon() + var/preview_front = fcopy_rsc(preview_icon_front) + var/preview_side = fcopy_rsc(preview_icon_side) + user << browse_rsc(preview_front, "previewicon.png") + user << browse_rsc(preview_side, "previewicon2.png") + var/dat = "" + + if(!IsGuestKey(user.key)) + + dat += {"
    + Slot [slot_name] - + Load slot - + Save slot - + Reload slot +

    "} + else + dat += "Please create an account to save your preferences." + + dat += "
    Character Settings | " + dat += "UI Settings | " + dat += "General Settings | " + dat += "Special Roles

    " + + if(appearance_isbanned(user)) + dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
    " + + switch(current_tab) + if(CHARACTER_SETUP) + dat = setup_character_options(dat, user) + if(UI_SETUP) + dat = setup_UI(dat, user) + if(GENERAL_SETUP) + dat = setup_special(dat, user) + if(SPECIAL_ROLES_SETUP) + dat = configure_special_roles(dat, user) + + dat += "


    " + + if(!IsGuestKey(user.key)) + dat += {"Undo | + Save Setup | "} + + dat += {"Reset Setup +
    "} + + //user << browse(HTML_SKELETON(dat), "window=preferences;size=560x580") + var/datum/browser/popup = new(user, "preferences", "
    Character Setup
    ", 680, 720) + popup.set_content(dat) + popup.open(0) + +/datum/preferences/proc/ShowDisabilityState(mob/user,flag,label) + var/species = get_pref(/datum/preference_setting/string/species) + var/disabilities = get_pref(/datum/preference_setting/binary_flag/disabilities) + if(flag==DISABILITY_FLAG_FAT && species!="Human") + return "
  • [species] cannot be fat.
  • " + return "
  • [label]: [disabilities & flag ? "Yes" : "No"]
  • " + +/datum/preferences/proc/SetDisabilities(mob/user) + var/HTML = "" + + HTML += {"
    + Choose disabilities
      "} + HTML += ShowDisabilityState(user,DISABILITY_FLAG_NEARSIGHTED,"Needs Glasses") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_FAT, "Obese") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_EPILEPTIC, "Seizures") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_DEAF, "Deaf") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_BLIND, "Blind") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_MUTE, "Mute") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_VEGAN, "Vegan") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_ASTHMA, "Asthma") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_LACTOSE, "Lactose Intolerant") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_LISP, "Lisp") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_ANEMIA, "Anemia") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_EHS, "Electromagnetic Hypersensitivity") + /*HTML += ShowDisabilityState(user,DISABILITY_FLAG_COUGHING, "Coughing") + HTML += ShowDisabilityState(user,DISABILITY_FLAG_TOURETTES, "Tourettes") Still working on it! -Angelite*/ + + + HTML += {"
    + \[Done\] + \[Reset\] +
    "} + user << browse(null, "window=preferences") + user << browse(HTML_SKELETON(HTML), "window=disabil;size=350x300") + return + +/datum/preferences/proc/SetRecords(mob/user) + var/HTML = "" + + HTML += {"
    + Set Character Records
    + Medical Records
    "} + var/med_record = get_pref(/datum/preference_setting/string/med_record) + if(length(med_record) <= 40) + HTML += "[med_record]" + else + HTML += "[copytext(med_record, 1, 37)]..." + + HTML += "

    Employment Records
    " + + var/gen_record = get_pref(/datum/preference_setting/string/gen_record) + if(length(gen_record) <= 40) + HTML += "[gen_record]" + else + HTML += "[copytext(gen_record, 1, 37)]..." + + HTML += "

    Security Records
    " + + var/sec_record = get_pref(/datum/preference_setting/string/sec_record) + if(length(sec_record) <= 40) + HTML += "[sec_record]
    " + else + HTML += "[copytext(sec_record, 1, 37)]...
    " + + + HTML += {"
    + \[Done\] +
    "} + user << browse(null, "window=preferences") + user << browse(HTML_SKELETON(HTML), "window=records;size=350x300") + return + + +/datum/preferences/proc/open_load_dialog(mob/user) + var/database/query/q = new + var/list/name_list[MAX_SAVE_SLOTS] + message_admins("open load dialog for [user]") + q.Add("select real_name, player_slot from players where player_ckey=?", user.ckey) + if(q.Execute(db)) + while(q.NextRow()) + name_list[q.GetColumn(2)] = q.GetColumn(1) + else + message_admins("Error in open_load_dialog [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") + warning("Error in open_load_dialog [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") + return 0 + var/dat = "
    Select a character slot to load
    " + var/counter = 1 + while(counter <= MAX_SAVE_SLOTS) + if(counter==get_pref(/datum/preference_setting/numerical/default_slot)) + dat += "[name_list[counter]]
    " + else + if(!name_list[counter]) + dat += "Character[counter]
    " + else + dat += "[name_list[counter]]
    " + counter++ + + dat += "
    " + + var/datum/browser/browser = new(user, "saves", null, 300, 340) + browser.set_content(dat) + browser.open(use_onclose=FALSE) + +/datum/preferences/proc/close_load_dialog(mob/user) + user << browse(null, "window=saves") + +/datum/preferences/proc/configure_special_roles(var/dat, var/mob/user) + dat+={" +

    Special Role Preferences

    +

    Please note that this also handles in-round polling for things like Raging Mages and Borers.

    +
    + Legend +
    +
    Never:
    +
    Decline this role for this round and all future rounds. You will not be polled again.
    +
    No:
    +
    Default. Decline this role for this round only.
    +
    Yes:
    +
    Accept this role for this round only.
    +
    Always:
    +
    Accept this role for this round and all future rounds. You will not be polled again.
    +
    +
    +
    + "} + + for(var/list/table_type in list(antag_roles,nonantag_roles)) + dat += {" +
    + + + "} + if(table_type == antag_roles && isantagbanned(user)) + dat += "" + else + for(var/role_id in table_type) + dat += "" + if(table_type[role_id]) //if mode is available on the server + if(jobban_isbanned(user, role_id) || (role_id == "pai candidate" && jobban_isbanned(user, "pAI")) || (role_id == MALF && jobban_isbanned(user, "AI"))) + dat += "" + else + var/wikiroute = role_wiki[role_id] + var/desire = get_role_desire_str(roles[role_id]) + dat += {" + + + + + "} + dat += "

    [table_type == nonantag_roles ? "Non-" : ""]Antagonist Roles

    You are banned from antagonist roles

    [capitalize(role_id)]\[BANNED][wikiroute ? "(Wiki)" : "(Wiki)"]NeverNoYesAlways
    " + dat += "

    " + + return dat diff --git a/code/modules/client/preferences/subsections/limbs.dm b/code/modules/client/preferences/subsections/limbs.dm index 44a4b11eed8..03bbc878bc7 100644 --- a/code/modules/client/preferences/subsections/limbs.dm +++ b/code/modules/client/preferences/subsections/limbs.dm @@ -122,16 +122,20 @@ var/limb_internal_state = state_data["internal_name"] prefs.organ_data[limb_internal_name] = limb_internal_state + prefs.change_pref_datum_limb(limb_internal_name, limb_internal_state) switch(state_data["mode"]) if(LIMB_MODE_AFFECT_CHILD) var/child_limb = configurable_limb_data["child_limb"] if(child_limb) prefs.organ_data[child_limb] = limb_internal_state + prefs.change_pref_datum_limb(child_limb, limb_internal_state) + if(LIMB_MODE_AFFECT_PARENT) var/parent_limb = configurable_limb_data["parent_limb"] if(parent_limb) prefs.organ_data[parent_limb] = limb_internal_state + prefs.change_pref_datum_limb(parent_limb, limb_internal_state) return TRUE diff --git a/code/modules/client/preferences/subsections/organs.dm b/code/modules/client/preferences/subsections/organs.dm index 509136bc254..2403993d687 100644 --- a/code/modules/client/preferences/subsections/organs.dm +++ b/code/modules/client/preferences/subsections/organs.dm @@ -62,6 +62,7 @@ var/organ_internal_state = valid_states[target_state] prefs.organ_data[organ_internal_name] = organ_internal_state + prefs.change_pref_datum_limb(organ_internal_name, organ_internal_state) return TRUE /datum/preferences_subsection/organs/process_link(var/mob/user, var/list/href_list) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm deleted file mode 100644 index 58b135385d9..00000000000 --- a/code/modules/client/preferences_savefile.dm +++ /dev/null @@ -1,551 +0,0 @@ -/// IF YOU NEED A FIELD ADDED TO THE DATABASE, CREATE A MIGRATION SO SHIT GETS UPDATED. -/// Also update SQL/players2.sql. -/// SEE code/modules/migrations/SS13_Prefs/ - -/datum/preferences/proc/SetChangelog(ckey,hash) - lastchangelog=hash - var/database/query/q = new - q.Add("UPDATE client SET lastchangelog=? WHERE ckey=?",lastchangelog,ckey) - if(!q.Execute(db)) - message_admins("Error in SetChangelog [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in Setchangelog [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - -/datum/preferences/proc/load_preferences_sqlite(var/ckey) - var/list/preference_list_client = new - var/database/query/check = new - var/database/query/q = new - check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey) - if(check.Execute(db)) - if(!check.NextRow()) - message_admins("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") - WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - else - message_admins("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") - WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - q.Add("SELECT * FROM client WHERE ckey = ?", ckey) - if(q.Execute(db)) - while(q.NextRow()) - var/list/row = q.GetRowData() - for(var/a in row) - preference_list_client[a] = row[a] - else - message_admins("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - - ooccolor = preference_list_client["ooc_color"] - lastchangelog = preference_list_client["lastchangelog"] - UI_style = preference_list_client["UI_style"] - default_slot = text2num(preference_list_client["default_slot"]) - toggles = text2num(preference_list_client["toggles"]) - UI_style_color = preference_list_client["UI_style_color"] - UI_style_alpha = text2num(preference_list_client["UI_style_alpha"]) - warns = text2num(preference_list_client["warns"]) - warnbans = text2num(preference_list_client["warnsbans"]) - volume = text2num(preference_list_client["volume"]) - usewmp = text2num(preference_list_client["usewmp"]) - special_popup = text2num(preference_list_client["special"]) - randomslot = text2num(preference_list_client["randomslot"]) - usenanoui = text2num(preference_list_client["usenanoui"]) - tooltips = text2num(preference_list_client["tooltips"]) - progress_bars = text2num(preference_list_client["progress_bars"]) - space_parallax = text2num(preference_list_client["space_parallax"]) - space_dust = text2num(preference_list_client["space_dust"]) - parallax_speed = text2num(preference_list_client["parallax_speed"]) - stumble = text2num(preference_list_client["stumble"]) - attack_animation= text2num(preference_list_client["attack_animation"]) - pulltoggle = text2num(preference_list_client["pulltoggle"]) - hear_voicesound = text2num(preference_list_client["hear_voicesound"]) - hear_instruments = text2num(preference_list_client["hear_instruments"]) - ambience_volume = text2num(preference_list_client["ambience_volume"]) - headset_sound = text2num(preference_list_client["headset_sound"]) - credits_volume = text2num(preference_list_client["credits_volume"]) - credits = preference_list_client["credits"] - jingle = preference_list_client["jingle"] - window_flashing = text2num(preference_list_client["window_flashing"]) - antag_objectives = text2num(preference_list_client["antag_objectives"]) - typing_indicator = text2num(preference_list_client["typing_indicator"]) - mob_chat_on_map = text2num(preference_list_client["mob_chat_on_map"]) - max_chat_length = text2num(preference_list_client["max_chat_length"]) - obj_chat_on_map = text2num(preference_list_client["obj_chat_on_map"]) - no_goonchat_for_obj = text2num(preference_list_client["no_goonchat_for_obj"]) - tgui_fancy = text2num(preference_list_client["tgui_fancy"]) - show_warning_next_time = text2num(preference_list_client["show_warning_next_time"]) - last_warned_message = preference_list_client["last_warned_message"] - warning_admin = preference_list_client["warning_admin"] - fps = preference_list_client["fps"] - - ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor)) - lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) - UI_style = sanitize_inlist(UI_style, list("White", "Midnight","Orange","old"), initial(UI_style)) - //be_special = sanitize_integer(be_special, 0, 65535, initial(be_special)) - default_slot = sanitize_integer(default_slot, 1, MAX_SAVE_SLOTS, initial(default_slot)) - toggles = sanitize_integer(toggles, 0, 131071, initial(toggles)) - UI_style_color = sanitize_hexcolor(UI_style_color, initial(UI_style_color)) - UI_style_alpha = sanitize_integer(UI_style_alpha, 0, 255, initial(UI_style_alpha)) - randomslot = sanitize_integer(randomslot, 0, 1, initial(randomslot)) - volume = sanitize_integer(volume, 0, 100, initial(volume)) - usewmp = sanitize_integer(usewmp, 0, 1, initial(usewmp)) - special_popup = sanitize_integer(special_popup, 0, 2, initial(special_popup)) - usenanoui = sanitize_integer(usenanoui, 0, 1, initial(usenanoui)) - progress_bars = sanitize_integer(progress_bars, 0, 1, initial(progress_bars)) - space_parallax = sanitize_integer(space_parallax, 0, 1, initial(space_parallax)) - space_dust = sanitize_integer(space_dust, 0, 1, initial(space_dust)) - parallax_speed = sanitize_integer(parallax_speed, 0, 5, initial(parallax_speed)) - stumble = sanitize_integer(stumble, 0, 1, initial(stumble)) - attack_animation= sanitize_integer(attack_animation, 0, 65535, initial(attack_animation)) - pulltoggle = sanitize_integer(pulltoggle, 0, 1, initial(pulltoggle)) - credits = sanitize_inlist(credits, list(CREDITS_NEVER, CREDITS_ALWAYS, CREDITS_NO_RERUNS), initial(credits)) - jingle = sanitize_inlist(jingle, list(JINGLE_NEVER, JINGLE_CLASSIC, JINGLE_ALL), initial(jingle)) - hear_voicesound = sanitize_integer(hear_voicesound, 0, 1, initial(hear_voicesound)) - hear_instruments = sanitize_integer(hear_instruments, 0, 1, initial(hear_instruments)) - ambience_volume = sanitize_integer(ambience_volume, 0, 100, initial(ambience_volume)) - headset_sound = sanitize_integer(headset_sound, 0, 2, initial(headset_sound)) - credits_volume = sanitize_integer(credits_volume, 0, 100, initial(credits_volume)) - window_flashing = sanitize_integer(window_flashing, 0, 1, initial(window_flashing)) - antag_objectives = sanitize_integer(antag_objectives, 0, 1, initial(antag_objectives)) - typing_indicator = sanitize_integer(typing_indicator, 0, 1, initial(typing_indicator)) - mob_chat_on_map = sanitize_integer(mob_chat_on_map, 0, 1, initial(mob_chat_on_map)) - max_chat_length = sanitize_integer(max_chat_length, 0, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length)) - obj_chat_on_map = sanitize_integer(obj_chat_on_map, 0, 1, initial(obj_chat_on_map)) - no_goonchat_for_obj = sanitize_integer(no_goonchat_for_obj, 0, 1, initial(no_goonchat_for_obj)) - tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy)) - show_warning_next_time = sanitize_integer(show_warning_next_time, 0, 1, initial(show_warning_next_time)) - fps = sanitize_integer(fps, -1, 1000, initial(fps)) - initialize_preferences() - return 1 - -/datum/preferences/proc/initialize_preferences(client_login = 0) - if(attack_animation == PERSON_ANIMATION) - person_animation_viewers |= client - item_animation_viewers -= client - else if(attack_animation == ITEM_ANIMATION) - item_animation_viewers |= client - person_animation_viewers -= client - else - item_animation_viewers -= client - person_animation_viewers -= client - -/datum/preferences/proc/save_preferences_sqlite(var/user, var/ckey) - var/database/query/check = new - var/database/query/q = new - check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey) - if(check.Execute(db)) - if(!check.NextRow()) - q.Add("INSERT into client (ckey, ooc_color, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, headset_sound, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map, no_goonchat_for_obj, tgui_fancy, show_warning_next_time, last_warned_message, warning_admin, fps) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",\ - ckey, ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, headset_sound, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map, no_goonchat_for_obj, tgui_fancy, show_warning_next_time, last_warned_message, warning_admin, fps) - if(!q.Execute(db)) - message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - else - q.Add("UPDATE client SET ooc_color=?,lastchangelog=?,UI_style=?,default_slot=?,toggles=?,UI_style_color=?,UI_style_alpha=?,warns=?,warnbans=?,randomslot=?,volume=?,usewmp=?,special=?,usenanoui=?,tooltips=?,progress_bars=?,space_parallax=?,space_dust=?,parallax_speed=?, stumble=?, attack_animation=?, pulltoggle=?, credits=?, jingle=?, hear_voicesound=?, hear_instruments=?, ambience_volume=?, headset_sound=?, credits_volume=?, window_flashing=?, antag_objectives=? , typing_indicator=? , mob_chat_on_map=? , max_chat_length=?, obj_chat_on_map=?, no_goonchat_for_obj=?, tgui_fancy=?, show_warning_next_time=?, last_warned_message=?, warning_admin=?, fps=? WHERE ckey = ?",\ - ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, headset_sound, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map,no_goonchat_for_obj, tgui_fancy, show_warning_next_time, last_warned_message, warning_admin, fps, ckey) - if(!q.Execute(db)) - message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - else - message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") - WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Preferences Updated.") - lastPolled = world.timeofday - return 1 - -/datum/preferences/proc/load_save_sqlite(var/ckey, var/user, var/slot) - var/list/preference_list = new - var/database/query/q = new - var/database/query/check = new - - check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot) - if(check.Execute(db)) - if(!check.NextRow()) - to_chat(user, "You have no character file to load, please save one first.") - WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") - return 0 - else - message_admins("load_save_sqlite Check Error #: [check.Error()] - [check.ErrorMsg()]") - WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") - - return 0 - - q.Add({" -SELECT - limbs.player_ckey, - limbs.player_slot, - limbs.l_arm, - limbs.r_arm, - limbs.l_leg, - limbs.r_leg, - limbs.l_foot, - limbs.r_foot, - limbs.l_hand, - limbs.r_hand, - limbs.heart, - limbs.eyes, - limbs.lungs, - limbs.liver, - limbs.kidneys, - players.player_ckey, - players.player_slot, - players.ooc_notes, - players.real_name, - players.random_name, - players.random_body, - players.gender, - players.age, - players.species, - players.language, - players.flavor_text, - players.med_record, - players.sec_record, - players.gen_record, - players.player_alt_titles, - players.disabilities, - players.nanotrasen_relation, - players.bank_security, - players.wage_ratio, - jobs.player_ckey, - jobs.player_slot, - jobs.alternate_option, - jobs.jobs, - body.player_ckey, - body.player_slot, - body.hair_red, - body.hair_green, - body.hair_blue, - body.facial_red, - body.facial_green, - body.facial_blue, - body.skin_tone, - body.hair_style_name, - body.facial_style_name, - body.eyes_red, - body.eyes_green, - body.eyes_blue, - body.underwear, - body.backbag -FROM - players -INNER JOIN - limbs -ON - ( - players.player_ckey = limbs.player_ckey) -AND ( - players.player_slot = limbs.player_slot) -INNER JOIN - jobs -ON - ( - limbs.player_ckey = jobs.player_ckey) -AND ( - limbs.player_slot = jobs.player_slot) -INNER JOIN - body -ON - ( - jobs.player_ckey = body.player_ckey) -AND ( - jobs.player_slot = body.player_slot) -WHERE - players.player_ckey = ? -AND players.player_slot = ? ;"}, ckey, slot) - if(q.Execute(db)) - while(q.NextRow()) - var/list/row = q.GetRowData() - for(var/a in row) - preference_list[a] = row[a] - else - message_admins("load_save_sqlite Error #: [q.Error()] - [q.ErrorMsg()]") - WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") - return 0 - - var/list/player_alt_list1 = new - var/list/player_alt_list2 = new() - player_alt_list1.Add(splittext(preference_list["player_alt_titles"], ";")) // we're getting the first part of the string for each job. - for(var/item in player_alt_list1) // iterating through the list - if(!findtext(item, ":")) - continue - var/delim_location = findtext(item, ":") // getting the second part of the string that will be handled for titles - var/job = copytext(item, 1, delim_location) // getting where the job is, it's in the first slot so we want to get that position. - var/title = copytext(item, delim_location + 1, 0) // getting where the job title is, it's in the second slot so we want to get that position. - player_alt_list2[job] = title // we assign the alt_titles here to specific job titles and hope everything works. - - metadata = preference_list["ooc_notes"] - real_name = preference_list["real_name"] - be_random_name = text2num(preference_list["random_name"]) - be_random_body = text2num(preference_list["random_body"]) - gender = preference_list["gender"] - age = text2num(preference_list["age"]) - species = preference_list["species"] - language = preference_list["language"] - flavor_text = preference_list["flavor_text"] - med_record = preference_list["med_record"] - sec_record = preference_list["sec_record"] - gen_record = preference_list["gen_record"] - player_alt_titles = player_alt_list2 - disabilities = text2num(preference_list["disabilities"]) - nanotrasen_relation = preference_list["nanotrasen_relation"] - bank_security = preference_list["bank_security"] - wage_ratio = preference_list["wage_ratio"] - - r_hair = text2num(preference_list["hair_red"]) - g_hair = text2num(preference_list["hair_green"]) - b_hair = text2num(preference_list["hair_blue"]) - h_style = preference_list["hair_style_name"] - - r_facial = text2num(preference_list["facial_red"]) - g_facial = text2num(preference_list["facial_green"]) - b_facial = text2num(preference_list["facial_blue"]) - f_style = preference_list["facial_style_name"] - - r_eyes = text2num(preference_list["eyes_red"]) - g_eyes = text2num(preference_list["eyes_green"]) - b_eyes = text2num(preference_list["eyes_blue"]) - - s_tone = text2num(preference_list["skin_tone"]) - - underwear = text2num(preference_list["underwear"]) - backbag = text2num(preference_list["backbag"]) - - organ_data[LIMB_LEFT_ARM] = preference_list[LIMB_LEFT_ARM] - organ_data[LIMB_RIGHT_ARM] = preference_list[LIMB_RIGHT_ARM] - organ_data[LIMB_LEFT_LEG] = preference_list[LIMB_LEFT_LEG] - organ_data[LIMB_RIGHT_LEG] = preference_list[LIMB_RIGHT_LEG] - organ_data[LIMB_LEFT_FOOT]= preference_list[LIMB_LEFT_FOOT] - organ_data[LIMB_RIGHT_FOOT]= preference_list[LIMB_RIGHT_FOOT] - organ_data[LIMB_LEFT_HAND]= preference_list[LIMB_LEFT_HAND] - organ_data[LIMB_RIGHT_HAND]= preference_list[LIMB_RIGHT_HAND] - organ_data["heart"] = preference_list["heart"] - organ_data["eyes"] = preference_list["eyes"] - organ_data["lungs"] = preference_list["lungs"] - organ_data["kidneys"]=preference_list["kidneys"] - organ_data["liver"] = preference_list["liver"] - - alternate_option = text2num(preference_list["alternate_option"]) - if(preference_list["jobs"] && preference_list["jobs"] != "") - jobs = json_decode(preference_list["jobs"]) - else - jobs = list() - metadata = sanitize_text(metadata, initial(metadata)) - real_name = reject_bad_name(real_name) - - if(isnull(species)) - species = "Human" - if(isnull(language)) - language = "None" - if(isnull(nanotrasen_relation)) - nanotrasen_relation = initial(nanotrasen_relation) - if(isnull(bank_security)) - bank_security = initial(bank_security) - if(isnull(wage_ratio)) - wage_ratio = initial(wage_ratio) - if(!real_name) - real_name = random_name(gender,species) - wage_ratio = clamp(wage_ratio,0,100) - - 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)) - gender = sanitize_gender(gender) - 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)) - b_hair = sanitize_integer(b_hair, 0, 255, initial(b_hair)) - - r_facial = sanitize_integer(r_facial, 0, 255, initial(r_facial)) - 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)) - //be_special = sanitize_integer(be_special, 0, 65535, initial(be_special)) - - alternate_option = sanitize_integer(alternate_option, 0, 2, initial(alternate_option)) - - for(var/role_id in special_roles) - roles[role_id]=0 - q = new - q.Add("SELECT role, preference FROM client_roles WHERE ckey=? AND slot=?", ckey, slot) - if(q.Execute(db)) - while(q.NextRow()) - var/list/row = q.GetRowData() - roles[row["role"]] = text2num(row["preference"]) - else - message_admins("Error in load_save_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned") - return 0 - - if(!skills) - skills = list() - if(!used_skillpoints) - used_skillpoints= 0 - if(isnull(disabilities)) - disabilities = 0 - if(!player_alt_titles) - player_alt_titles = new() - if(!organ_data) - src.organ_data = list() - - if(user) - to_chat(user, "Successfully loaded [real_name].") - - return 1 - -/datum/preferences/proc/random_character_sqlite(var/user, var/ckey) - var/database/query/q = new - var/list/slot_list = new - q.Add("SELECT player_slot FROM players WHERE player_ckey=?", ckey) - if(q.Execute(db)) - while(q.NextRow()) - slot_list.Add(q.GetColumn(1)) - else - message_admins("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - var/random_slot = pick(slot_list) - load_save_sqlite(ckey, user, random_slot) - return 1 - -/datum/preferences/proc/save_character_sqlite(var/ckey, var/user, var/slot) - if(slot > MAX_SAVE_SLOTS) - to_chat(user, "You are limited to 8 character slots.") - message_admins("[ckey] attempted to override character slot limit") - return 0 - - var/database/query/q = new - var/database/query/check = new - - var/altTitles - - // The FUCK is this shit - for(var/a in player_alt_titles) - altTitles += "[a]:[player_alt_titles[a]];" - - check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot) - if(check.Execute(db)) - if(!check.NextRow()) - q.Add("INSERT INTO players (player_ckey,player_slot,ooc_notes,real_name, random_name, gender, age, species, language, flavor_text, med_record, sec_record, gen_record, player_alt_titles, disabilities, nanotrasen_relation, bank_security, wage_ratio, random_body)\ - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - ckey, slot, metadata, real_name, be_random_name, gender, age, species, language, flavor_text, med_record, sec_record, gen_record, altTitles, disabilities, nanotrasen_relation, bank_security, wage_ratio, be_random_body) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Created Character") - else - q.Add("UPDATE players SET ooc_notes=?,real_name=?,random_name=?, gender=?,age=?,species=?,language=?,flavor_text=?,med_record=?,sec_record=?,gen_record=?,player_alt_titles=?,disabilities=?,nanotrasen_relation=?,bank_security=?,wage_ratio=?,random_body=? WHERE player_ckey = ? AND player_slot = ?",\ - metadata, real_name, be_random_name, gender, age, species, language, flavor_text, med_record, sec_record, gen_record, altTitles, disabilities, nanotrasen_relation, bank_security, wage_ratio, be_random_body, ckey, slot) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Updated Character") - else - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - - check.Add("SELECT player_ckey FROM body WHERE player_ckey = ? AND player_slot = ?", ckey, slot) - if(check.Execute(db)) - if(!check.NextRow()) - q.Add("INSERT INTO body (player_ckey, player_slot, hair_red, hair_green, hair_blue, facial_red, facial_green, facial_blue, skin_tone, hair_style_name, facial_style_name, eyes_red, eyes_green, eyes_blue, underwear, backbag) \ - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - ckey, slot, r_hair, g_hair, b_hair, r_facial, g_facial, b_facial, s_tone, h_style, f_style, r_eyes, g_eyes, b_eyes, underwear, backbag) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Created Body") - else - q.Add("UPDATE body SET hair_red=?, hair_green=?, hair_blue=?, facial_red=?, facial_green=?, facial_blue=?, skin_tone=?, hair_style_name=?, facial_style_name=?, eyes_red=?, eyes_green=?, eyes_blue=?, underwear=?, backbag=? WHERE player_ckey = ? AND player_slot = ?",\ - r_hair, g_hair, b_hair, r_facial, g_facial, b_facial, s_tone, h_style, f_style, r_eyes, g_eyes, b_eyes, underwear, backbag, ckey, slot) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Updated Body") - else - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - - check.Add("SELECT player_ckey FROM jobs WHERE player_ckey = ? AND player_slot = ?", ckey, slot) - if(check.Execute(db)) - if(!check.NextRow()) - // 1 2 3 4 - q.Add("INSERT INTO jobs (player_ckey,player_slot,alternate_option,jobs) \ - VALUES (?, ?, ?, ?)", \ - ckey, slot, alternate_option,json_encode(jobs)) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Created Job list") - else - // 1 2 - q.Add("UPDATE jobs SET alternate_option=?,jobs=? WHERE player_ckey = ? AND player_slot = ?",\ - alternate_option, json_encode(jobs), ckey, slot) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Updated Job List") - else - message_admins("Error in save_character_sqlite ln [__LINE__] #: [check.Error()] - [check.ErrorMsg()]") - WARNING("Error in save_character_sqlite ln [__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - - check.Add("SELECT player_ckey FROM limbs WHERE player_ckey = ? AND player_slot = ?", ckey, slot) - if(check.Execute(db)) - if(!check.NextRow()) - q.Add("INSERT INTO limbs (player_ckey, player_slot) VALUES (?,?)", ckey, slot) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - for(var/stuff in organ_data) - q.Add("UPDATE limbs SET [stuff]=? WHERE player_ckey = ? AND player_slot = ?", organ_data[stuff], ckey, slot) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #; [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Created Limbs") - else - for(var/stuff in organ_data) - q.Add("UPDATE limbs SET [stuff] = ? WHERE player_ckey = ? AND player_slot = ?", organ_data[stuff], ckey, slot) - if(!q.Execute(db)) - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - to_chat(user, "Updated Limbs") - else - message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]") - WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]") - return 0 - - for(var/role_id in roles) - if(!(roles[role_id] & ROLEPREF_SAVE)) - continue - q = new - q.Add("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES (?,?,?,?)", ckey, slot, role_id, (roles[role_id] & ROLEPREF_VALMASK)) - //testing("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES ('[ckey]',[slot],'[role_id]',[roles[role_id] & ROLEPREF_VALMASK])") - if(!q.Execute(db)) // This never triggers on error, for some reason. - message_admins("ClientRoleInsert: Error #: [q.Error()] - [q.ErrorMsg()]") - WARNING("ClientRoleInsert: Error #:[q.Error()] - [q.ErrorMsg()]") - return 0 - - return 1 diff --git a/code/modules/credits/credits.dm b/code/modules/credits/credits.dm index a9dd8882956..c966ed192dc 100644 --- a/code/modules/credits/credits.dm +++ b/code/modules/credits/credits.dm @@ -160,7 +160,7 @@ var/global/datum/credits/end_credits = new for(var/client/C in clients) if(!C.prefs) continue - switch(C.prefs.credits) + switch(C.prefs.get_pref(/datum/preference_setting/enum/credits)) if(CREDITS_ALWAYS) C.credits_audio() if(CREDITS_NO_RERUNS) //The time has come to decide. Shall we play credits audio, or preload the jingle audio instead? @@ -171,7 +171,7 @@ var/global/datum/credits/end_credits = new if(CREDITS_NEVER) C.jingle_audio(preload_only = TRUE) else - log_debug("[C] somehow had an unknown credits preference of: [C.prefs.credits]") + log_debug("[C] somehow had an unknown credits preference of: [C.prefs.get_pref(/datum/preference_setting/enum/credits)]") /* * on_world_reboot_end: diff --git a/code/modules/credits/credits_clientprocs.dm b/code/modules/credits/credits_clientprocs.dm index c8be20ff9c4..62f89c8d85d 100644 --- a/code/modules/credits/credits_clientprocs.dm +++ b/code/modules/credits/credits_clientprocs.dm @@ -6,49 +6,49 @@ src << output(end_credits.file, end_credits.control) /client/proc/download_credits() - if(prefs.credits == CREDITS_NEVER) + if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NEVER) return if(!end_credits.finalized) - log_debug("[src] tried to download credits before the credits were ever finalized! Credits preference: [prefs.credits]") + log_debug("[src] tried to download credits before the credits were ever finalized! Credits preference: [prefs.get_pref(/datum/preference_setting/enum/credits)]") return src << output(list2params(end_credits.js_args), "[end_credits.control]:setupCredits") received_credits = TRUE /client/proc/play_downloaded_credits() - if(prefs.credits == CREDITS_NEVER) + if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NEVER) return - if(prefs.credits == CREDITS_NO_RERUNS && end_credits.is_rerun()) + if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NO_RERUNS && end_credits.is_rerun()) return if(!received_credits) - log_debug("[src] tried to play credits without having ever received them! Credits preference: [prefs.credits]") + log_debug("[src] tried to play credits without having ever received them! Credits preference: [prefs.get_pref(/datum/preference_setting/enum/credits)]") return src << output("", "[end_credits.control]:startCredits") //Execute the startCredits() function in credits.html with no parameters. /client/proc/credits_audio(var/preload_only = FALSE) - if(prefs.credits == CREDITS_NEVER) + if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NEVER) return if(preload_only) src << output(list2params(list(end_credits.audio_link, FALSE)), "[end_credits.control]:setAudio") received_roundend_audio = TRUE else - if(prefs.credits == CREDITS_NO_RERUNS && end_credits.is_rerun()) + if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NO_RERUNS && end_credits.is_rerun()) return if(received_roundend_audio) src << output("", "[end_credits.control]:startAudio") //Execute the playAudio() function in credits.html with no parameters. else src << output(list2params(list(end_credits.audio_link, TRUE)), "[end_credits.control]:setAudio") - src << output(list2params(list(prefs.credits_volume)), "[end_credits.control]:setVolume") + src << output(list2params(list(prefs.get_pref(/datum/preference_setting/numerical/credits_volume))), "[end_credits.control]:setVolume") /client/proc/jingle_audio(var/preload_only = FALSE) - if(prefs.credits == CREDITS_ALWAYS) + if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_ALWAYS) return var/link = "" - switch(prefs.jingle) + switch(prefs.get_pref(/datum/preference_setting/enum/jingle)) if(JINGLE_NEVER) return if(JINGLE_CLASSIC) @@ -56,19 +56,19 @@ if(JINGLE_ALL) link = pick(end_credits.classic_roundend_jingles + end_credits.new_roundend_jingles) if(!link) - log_debug("[src] somehow had a null jingle link! Jingle preference: [prefs.jingle]") + log_debug("[src] somehow had a null jingle link! Jingle preference: [prefs.get_pref(/datum/preference_setting/enum/jingle)]") if(preload_only) src << output(list2params(list(link, FALSE)), "[end_credits.control]:setAudio") received_roundend_audio = TRUE else - if(prefs.credits == CREDITS_NO_RERUNS && !end_credits.is_rerun()) + if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NO_RERUNS && !end_credits.is_rerun()) return if(received_roundend_audio) src << output("", "[end_credits.control]:startAudio") //Execute the playAudio() function in credits.html with no parameters. else src << output(list2params(list(link, TRUE)), "[end_credits.control]:setAudio") - src << output(list2params(list(prefs.credits_volume)), "[end_credits.control]:setVolume") + src << output(list2params(list(prefs.get_pref(/datum/preference_setting/numerical/credits_volume))), "[end_credits.control]:setVolume") /* /client/verb/credits_debug() diff --git a/code/modules/jobs/job_controller.dm b/code/modules/jobs/job_controller.dm index b37ba4e4c40..a62eb408882 100644 --- a/code/modules/jobs/job_controller.dm +++ b/code/modules/jobs/job_controller.dm @@ -222,7 +222,8 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires if(flag && !player.client.desires_role(job.title)) Debug("FOC flag failed, Player: [player], Flag: [flag], ") continue - if(player.client.prefs.jobs[job.title] == level) + var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs) + if(jobs[job.title] == level) Debug("FOC pass, Player: [player], Level:[level]") candidates += player return candidates @@ -306,7 +307,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires for(var/mob/new_player/player in player_list) if(player.ready && player.mind && !player.mind.assigned_role) unassigned += player - if(player.client.prefs.randomslot) + if(player.client.prefs.get_pref(/datum/preference_setting/toggle/randomslot)) player.client.prefs.random_character_sqlite(player, player.ckey) Debug("DO, Len: [unassigned.len]") if(unassigned.len == 0) @@ -333,7 +334,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires // Loop through all unassigned players for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == GET_EMPTY_JOB) + if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_EMPTY_JOB) continue //This player doesn't want to share a job title. We need to deal with them last. // Loop through all jobs @@ -346,7 +347,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires // Also makes sure that they got their preference correct for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == GET_RANDOM_JOB) + if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_RANDOM_JOB) GiveRandomJob(player) Debug("DO, Standard Check end") @@ -359,7 +360,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires // For those who wanted to be assistant if their preferences were filled, here you go. for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == BE_ASSISTANT) + if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == BE_ASSISTANT) if(config.assistantlimit) if(master_assistant.current_positions-FREE_ASSISTANTS_BRUT > (config.assistantratio * count)) // Not enough sec... if(count < 5) // if theres more than 5 security on the station just let assistants join regardless, they should be able to handle the tide ; this block then doesn't get checked. @@ -368,7 +369,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires unassigned -= player continue - if(master_assistant.species_blacklist.len && master_assistant.species_blacklist.Find(player.client.prefs.species)) + if(master_assistant.species_blacklist.len && master_assistant.species_blacklist.Find(player.client.prefs.get_pref(/datum/preference_setting/string/species))) to_chat(player, "You have been returned to lobby because your species is blacklisted from assistant.") player.ready = 0 unassigned -= player @@ -410,7 +411,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires //Final pass - first deal with the empty job group, otherwise send any leftovers to the lobby final_pass: //this is a loop label for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == GET_EMPTY_JOB) + if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_EMPTY_JOB) for(var/level = 3 to 1 step -1) for(var/datum/job/job in shuffledoccupations) if(job.current_positions) //already someone in this job title @@ -434,7 +435,8 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires Debug("DO player not old enough, Player: [player], Job:[job.title]") return FALSE // If the player wants that job on this level, then try give it to him. - if(player.client.prefs.jobs[job.title] == level) + var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs) + if(jobs[job.title] == level) if (job.title == "Assistant" && !CheckAssistantCount(player, level)) return FALSE // If the job isn't filled @@ -453,8 +455,9 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires if(not_enough_sec && (count < 5)) Debug("AC1 failed, not enough sec.") // Does he want anything else...? + var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs) for (var/datum/job/J in occupations) - if (player.client.prefs.jobs[J.title] == level) + if (jobs[J.title] == level) Debug("AC1 failed, but other job slots for [player]. Adding them to the list of backup assistant slots.") assistant_second_chance[player.ckey] = level return FALSE @@ -475,9 +478,9 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires // Total between $200 and $500 var/balance_bank = rand(100,250) var/balance_wallet = rand(100,250) - var/bank_pref_number = H.client.prefs.bank_security + var/bank_pref_number = H.client.prefs.get_pref(/datum/preference_setting/enum/bank_security) var/bank_pref = bank_security_num2text(bank_pref_number) - var/pref_wage_ratio = H.client.prefs.wage_ratio + var/pref_wage_ratio = H.client.prefs.get_pref(/datum/preference_setting/numerical/wage_ratio) if(centcomm_account_db) var/wage = job.get_wage() var/datum/money_account/M = create_account(H.real_name, balance_bank, null, wage_payout = wage, security_pref = bank_pref_number, ratio_pref = pref_wage_ratio) @@ -588,7 +591,8 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires if(!job.player_old_enough(player.client)) level6++ continue - switch(player.client.prefs.jobs[job.title]) + var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs) + switch(jobs[job.title]) if(JOB_PREF_LOW) level1++ if(JOB_PREF_MED) diff --git a/code/modules/media/mediamanager.dm b/code/modules/media/mediamanager.dm index 58e724f3800..b9c1ca8d039 100644 --- a/code/modules/media/mediamanager.dm +++ b/code/modules/media/mediamanager.dm @@ -163,9 +163,9 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML src.mob=holder owner=src.mob.client if(owner.prefs) - if(!isnull(owner.prefs.volume)) - volume = owner.prefs.volume - if(owner.prefs.usewmp) + if(!isnull(owner.prefs.get_pref(/datum/preference_setting/numerical/volume))) + volume = owner.prefs.get_pref(/datum/preference_setting/numerical/volume) + if(owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp)) playerstyle = PLAYER_OLD_HTML else playerstyle = PLAYER_HTML @@ -186,11 +186,11 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML /datum/media_manager/proc/send_update(var/target_url) if(!(owner.prefs)) return - if(!(owner.prefs.toggles & SOUND_STREAMING) && target_url != "") + if(!(owner.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_STREAMING) && target_url != "") return // Nope. MP_DEBUG("Sending update to media player ([target_url])...") var/window_playing - if(owner.prefs.usewmp) + if(owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp)) stop_music() MP_DEBUG("WMP user, no switching, going to even window.") currently_broadcasting = JUKEBOX_EVEN_PLAYER @@ -226,7 +226,7 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML /datum/media_manager/proc/push_music(var/targetURL,var/targetStartTime,var/targetVolume) var/current_url - if (owner && owner.prefs.usewmp) + if (owner && owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp)) current_url = url_even else switch (currently_broadcasting) @@ -266,7 +266,7 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML var/obj/machinery/media/M = A.media_source // TODO: turn into a list, then only play the first one that's playing. var/current_url - if (owner.prefs.usewmp) // WMP only uses the even broadcaster + if (owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp)) // WMP only uses the even broadcaster current_url = url_even else switch (currently_broadcasting) @@ -311,12 +311,13 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML /client/proc/set_new_volume() if(!media || !istype(media)) - to_chat(usr, "You have no media datum to change, if you're not in the lobby tell an admin.") + to_chat(usr, "You have no media datum to change, if you're not in the lobby tell an admin.") return - var/oldvolume = prefs.volume + var/oldvolume = prefs.get_pref(/datum/preference_setting/numerical/volume) var/value = input("Choose your Jukebox volume.", "Jukebox volume", media.volume) value = round(max(0, min(100, value))) media.update_volume(value) if(prefs && (oldvolume != value)) - prefs.volume = value + var/datum/preference_setting/volume_pref = prefs.get_pref_datum(/datum/preference_setting/numerical/volume) + volume_pref.setting = value prefs.save_preferences_sqlite(src, ckey) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index e375a6d7bb2..7681f9b7bc5 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -577,7 +577,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp sHuman.real_name = real_name concrete_outfit.equip(sHuman, TRUE) client?.prefs.copy_to(sHuman) - sHuman.add_language(client?.prefs.language) + sHuman.add_language(client?.prefs.get_pref(/datum/preference_setting/string/language)) sHuman.dna.UpdateSE() sHuman.dna.UpdateUI() sHuman.ckey = ckey diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index a8ef7c4bb66..98441613085 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -41,17 +41,17 @@ if (get_dist(source_turf, src) <= get_view_range()) rendered_speech = "[rendered_speech]" - if (client?.prefs.mob_chat_on_map && (client.prefs.obj_chat_on_map || ismob(speech.speaker))) + if (client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && (client.prefs.get_pref(/datum/preference_setting/toggle/obj_chat_on_map) || ismob(speech.speaker))) create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes) else if(client && client.prefs) if (!speech.frequency) - if ((client.prefs.toggles & CHAT_GHOSTEARS) != CHAT_GHOSTEARS) - say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTEARS is disabled, blocking. ([client.prefs.toggles] & [CHAT_GHOSTEARS]) = [client.prefs.toggles & CHAT_GHOSTEARS]") + if ((client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS) != CHAT_GHOSTEARS) + say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTEARS is disabled, blocking. ([client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles)] & [CHAT_GHOSTEARS]) = [client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS]") return else - if ((client.prefs.toggles & CHAT_GHOSTRADIO) != CHAT_GHOSTRADIO) - say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTRADIO is disabled, blocking. ([client.prefs.toggles] & [CHAT_GHOSTRADIO]) = [client.prefs.toggles & CHAT_GHOSTRADIO]") + if ((client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTRADIO) != CHAT_GHOSTRADIO) + say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTRADIO is disabled, blocking. ([client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles)] & [CHAT_GHOSTRADIO]) = [client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTRADIO]") return to_chat(src, "[formatFollow(source)] [rendered_speech]") diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 6d979f03cd9..0b38ec1f31b 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -72,20 +72,20 @@ for(var/mob/M in dead_mob_list) if (!M.client) continue //skip leavers - if(isobserver(M) && M.client.prefs && (M.client.prefs.toggles & CHAT_GHOSTSIGHT) && !(M in viewers(user))) + if(isobserver(M) && M.client.prefs && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTSIGHT) && !(M in viewers(user))) M.show_message(formatFollow(user) + " " + msg) if(emote_type & EMOTE_VISIBLE) user.visible_message(msg) if(!(emote_type & EMOTE_NO_RUNECHAT)) for(var/mob/O in viewers(world.view, user)) - if(O.client && O?.client?.prefs.mob_chat_on_map && get_dist(O, user) < O?.client.view) + if(O.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && get_dist(O, user) < O?.client.view) O.create_chat_message(user, null, message, "", list("italics")) else if(emote_type & EMOTE_AUDIBLE) for(var/mob/O in get_hearers_in_view(world.view, user)) O.show_message(msg) if(!(emote_type & EMOTE_NO_RUNECHAT)) - if(O.client && O?.client?.prefs.mob_chat_on_map && get_dist(O, user) < O?.client.view) + if(O.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && get_dist(O, user) < O?.client.view) O.create_chat_message(user, null, message, "", list("italics")) var/location = T ? "[T.x],[T.y],[T.z]" : "nullspace" @@ -96,7 +96,7 @@ to_chat(src, "You cannot send deadchat emotes (muted).") return - if(!(client.prefs.toggles & CHAT_DEAD)) + if(!(client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) to_chat(src, "You have deadchat muted.") return @@ -117,8 +117,8 @@ if(istype(M, /mob/new_player)) continue - if(M.client && M.client.holder && (M.client.holder.rights & R_ADMIN|R_MOD) && (M.client.prefs.toggles & CHAT_DEAD)) // Show the emote to admins/mods + if(M.client && M.client.holder && (M.client.holder.rights & R_ADMIN|R_MOD) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // Show the emote to admins/mods to_chat(M, message) - else if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_DEAD)) // Show the emote to regular ghosts with deadchat toggled on + else if(M.stat == DEAD && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // Show the emote to regular ghosts with deadchat toggled on M.show_message(message, 2) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 9332aaaa3cd..ffa60807eef 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -238,10 +238,13 @@ brainmob.dna = new() brainmob.dna.ResetUI() brainmob.dna.ResetSE() - if(P.be_random_name) - P.real_name = random_name(P.gender, P.species) - brainmob.name = P.real_name - brainmob.real_name = P.real_name + var/datum/preference_setting/name_pref = P.get_pref_datum(/datum/preference_setting/string/real_name) + var/species = P.get_pref(/datum/preference_setting/string/species) + var/gender = P.get_pref(/datum/preference_setting/enum/gender) + if(P.get_pref(/datum/preference_setting/toggle/be_random_name)) + name_pref.setting = random_name(gender, species) + brainmob.name = name_pref.setting + brainmob.real_name = name_pref.setting brainmob.container = src name = "Man-Machine Interface: [brainmob.real_name]" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index e56596c2118..b216e716e9c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -657,7 +657,7 @@ Thanks. if(config.allow_Metadata) if(client) - to_chat(usr, "[src]'s Metainfo:
    [client.prefs.metadata]") + to_chat(usr, "[src]'s Metainfo:
    [client.prefs.get_pref(/datum/preference_setting/string/metadata)]") else to_chat(usr, "[src] does not have any stored infomation!") else diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 80de0a3fd13..e8f1c61499e 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -319,13 +319,13 @@ var/list/headset_modes = list( rendered_message = replacetext(rendered_message, ai.real_name, "[ai.real_name]") // Runechat messages - if (ismob(speech.speaker) && client?.prefs.mob_chat_on_map && stat != UNCONSCIOUS && !is_deaf()) + if (ismob(speech.speaker) && client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && stat != UNCONSCIOUS && !is_deaf()) create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes) - else if (client?.prefs.obj_chat_on_map && stat != UNCONSCIOUS && !is_deaf()) + else if (client?.prefs.get_pref(/datum/preference_setting/toggle/obj_chat_on_map) && stat != UNCONSCIOUS && !is_deaf()) create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes) if (ismob(speech.speaker)) show_message(rendered_message, type, deaf_message, deaf_type, src) - else if (!client.prefs.no_goonchat_for_obj || length_char(speech.message) > client?.prefs.max_chat_length) // Objects : only display if no goonchat on map or if the runemessage is too small. + else if (!client.prefs.get_pref(/datum/preference_setting/toggle/no_goonchat_for_obj) || length_char(speech.message) > client?.prefs.get_pref(/datum/preference_setting/numerical/max_chat_length)) // Objects : only display if no goonchat on map or if the runemessage is too small. show_message(rendered_message, type, deaf_message, deaf_type, src) else if (istype(speech.speaker, /obj/item/device/assembly/speaker) || istype(speech.speaker, /obj/item/device/assembly_frame)) //Speakers will still work if no_goonchat_for_obj is set to TRUE show_message(rendered_message, type, deaf_message, deaf_type, src) diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index f91350e72eb..06b89fc48a3 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -128,7 +128,7 @@ for(var/i = 0; i < max(user.sprint, initial); i += 20) var/turf/step = get_turf(get_step(user.eyeobj, direct)) if(step) - if (user.client.prefs.stumble && ((world.time - user.last_movement) > 4)) + if (user.client.prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - user.last_movement) > 4)) user.delayNextMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier else if(istype(H) && H.advancedholo) H.holo.dir = direct diff --git a/code/modules/mob/living/simple_animal/borer.dm b/code/modules/mob/living/simple_animal/borer.dm index fb77f10005c..f816176881a 100644 --- a/code/modules/mob/living/simple_animal/borer.dm +++ b/code/modules/mob/living/simple_animal/borer.dm @@ -331,7 +331,7 @@ var/global/borer_unlock_types_leg = typesof(/datum/unlockable/borer/leg) - /datu for(var/mob/M in player_list) if(istype(M, /mob/new_player)) continue - if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS || (get_turf(src) in view(M)))) + if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS || (get_turf(src) in view(M)))) var/controls = formatFollow(src,"Follow") if(M.client.holder) controls+= " | ?" diff --git a/code/modules/mob/living/simple_animal/borer/captive_brain.dm b/code/modules/mob/living/simple_animal/borer/captive_brain.dm index 0b94d4bfa74..6f70b178014 100644 --- a/code/modules/mob/living/simple_animal/borer/captive_brain.dm +++ b/code/modules/mob/living/simple_animal/borer/captive_brain.dm @@ -22,7 +22,7 @@ for(var/mob/M in player_list) if(istype(M, /mob/new_player)) continue - if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS)) + if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS)) var/controls = formatFollow(src,"Follow") if(M.client.holder) controls+= " | ?" diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 8af1f8f2221..88cac177d87 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1010,7 +1010,7 @@ Use this proc preferably at the end of an equipment loadout // Are we trying to pull something we are already pulling? // Then we want to either toggle pulling (stop pulling and quit), or keep pulling (just quit) if client preferences want otherwise. if(pulling == P) - if(client && !client.prefs.pulltoggle) + if(client && !client.prefs.get_pref(/datum/preference_setting/toggle/pulltoggle)) return else stop_pulling() @@ -1276,7 +1276,7 @@ Use this proc preferably at the end of an equipment loadout ) src << browse('html/changelog.html', "window=changes;size=675x650") - if(prefs.lastchangelog != changelog_hash) + if(prefs.get_pref(/datum/preference_setting/string/changelog) != changelog_hash) prefs.SetChangelog(ckey, changelog_hash) winset(src, "rpane.changelog", "background-color=none;font-style=;") diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 3f0924f50a5..5c9239d0667 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -393,7 +393,7 @@ else if (mob.process_confused(Dir)) return - if (prefs.stumble && ((world.time - mob.last_movement) > 5 && move_delay < 2)) + if (prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - mob.last_movement) > 5 && move_delay < 2)) mob.delayNextMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier // to_chat(src, "First Step") step(mob, Dir) diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index 8fd00cdcc7c..190582c1435 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -4,7 +4,7 @@ to_chat(src, "
    [join_motd]
    ") client.reset_screen() - + if(!mind) mind = new /datum/mind(key) mind.active = 1 @@ -14,7 +14,7 @@ loc = pick(newplayer_start) else loc = locate(1,1,1) - + change_sight(adding = SEE_TURFS) player_list |= src @@ -43,7 +43,7 @@ spawn(0) if(client) //If the changelog has changed, show it to them - if(client.prefs.lastchangelog != changelog_hash) + if(client.prefs.get_pref(/datum/preference_setting/string/changelog) != changelog_hash) // Need to send them the CSS and images :V client.getFiles( 'html/postcardsmall.jpg', diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index ea14133c773..44c2cd1eda2 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -307,15 +307,19 @@ observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly. // Has to be done here so we can get our random icon. - if(client.prefs.be_random_body) + if(client.prefs.get_pref(/datum/preference_setting/toggle/be_random_body)) client.prefs.randomize_appearance_for() // No argument means just the prefs are randomized. client.prefs.update_preview_icon(1) observer.icon = client.prefs.preview_icon observer.alpha = 127 - if(client.prefs.be_random_name) - client.prefs.real_name = random_name(client.prefs.gender,client.prefs.species) - observer.real_name = client.prefs.real_name + var/datum/preference_setting/name_pref = client.prefs.get_pref_datum(/datum/preference_setting/string/real_name) + + if(client.prefs.get_pref(/datum/preference_setting/toggle/be_random_name)) + var/gender = client.prefs.get_pref(/datum/preference_setting/enum/gender) + var/species = client.prefs.get_pref(/datum/preference_setting/string/species) + name_pref.setting = random_name(gender,species) + observer.real_name = name_pref.setting observer.name = observer.real_name if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! @@ -346,12 +350,12 @@ return 0 var/datum/job/job = job_master.GetJob(rank) if(job.species_whitelist.len) - if(!job.species_whitelist.Find(client.prefs.species)) - to_chat(src, alert("[rank] is not available for [client.prefs.species].")) + if(!job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) + to_chat(src, alert("[rank] is not available for [client.prefs.get_pref(/datum/preference_setting/string/species)].")) return 0 if(job.species_blacklist.len) - if(job.species_blacklist.Find(client.prefs.species)) - to_chat(src, alert("[rank] is not available for [client.prefs.species].")) + if(job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) + to_chat(src, alert("[rank] is not available for [client.prefs.get_pref(/datum/preference_setting/string/species)].")) return 0 job_master.AssignRole(src, rank, 1) @@ -361,7 +365,7 @@ job = job_master.GetJob(rank) var/mob/living/carbon/human/character = create_human(client.prefs) //creates the human and transfers vars and mind - if(character.client.prefs.randomslot) + if(character.client.prefs.get_pref(/datum/preference_setting/toggle/randomslot)) character.client.prefs.random_character_sqlite(character, character.ckey) var/atom/movable/what_to_move = character.locked_to || character @@ -566,7 +570,7 @@ if(highprior.len > 0) dat += "High Priority Jobs" for(var/datum/job/job in highprior) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue @@ -578,7 +582,7 @@ if(heads.len > 0) dat += "Heads" for(var/datum/job/job in heads) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue if(job.department_prioritized) @@ -590,7 +594,7 @@ if(sec.len > 0) dat += "Security" for(var/datum/job/job in sec) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue @@ -600,7 +604,7 @@ if(eng.len > 0) dat += "Engineering" for(var/datum/job/job in eng) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue @@ -610,7 +614,7 @@ if(med.len > 0) dat += "Medical" for(var/datum/job/job in med) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue @@ -620,7 +624,7 @@ if(sci.len > 0) dat += "Science" for(var/datum/job/job in sci) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue @@ -630,7 +634,7 @@ if(cgo.len > 0) dat += "Cargo" for(var/datum/job/job in cgo) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue if(job.department_prioritized) @@ -642,7 +646,7 @@ if(civ.len > 0) dat += "Civilian" for(var/datum/job/job in civ) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue @@ -653,7 +657,7 @@ if(misc.len > 0) dat += "Miscellaneous" for(var/datum/job/job in misc) - if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species))) + if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))) dat += "[job.title][job.current_positions][highprior[job]]" continue @@ -673,24 +677,30 @@ var/datum/species/chosen_species var/late_join = ticker.current_state == GAME_STATE_PLAYING ? TRUE : FALSE - if(prefs.species) - chosen_species = all_species[prefs.species] + var/species = prefs.get_pref(/datum/preference_setting/string/species) + var/language = prefs.get_pref(/datum/preference_setting/string/language) + var/datum/preference_setting/name_pref = prefs.get_pref_datum(/datum/preference_setting/string/real_name) + + if(species) + chosen_species = all_species[species] if(chosen_species && (check_rights(R_ADMIN, 0) || chosen_species.flags & PLAYABLE || chosen_species.conditional_playable())) - new_character.set_species(prefs.species) + new_character.set_species(species) else if(chosen_species.fallback()) to_chat(usr, "Your preferences had a non-playable species, so you were reverted to [chosen_species.fallback()] (default for [chosen_species]).") new_character.set_species(chosen_species.fallback()) var/datum/language/chosen_language - if(prefs.language) - chosen_language = all_languages["[prefs.language]"] + // This is a bit backwards, but: we check if language exists, and then add it as a string. + if(language) + chosen_language = all_languages[language] if(chosen_language) - new_character.add_language("[prefs.language]") + new_character.add_language(language) if(ticker.random_players || appearance_isbanned(src)) //disabling ident bans for now + var/datum/preference_setting/flavor_text = prefs.get_pref_datum(/datum/preference_setting/string/flavor_text) new_character.setGender(pick(MALE, FEMALE)) - prefs.real_name = random_name(new_character.gender, new_character.species.name) + name_pref.setting = random_name(new_character.gender, new_character.species.name) prefs.randomize_appearance_for(new_character) - prefs.flavor_text = "" + flavor_text.setting = "" else prefs.copy_to(new_character) @@ -701,43 +711,45 @@ mind.active = 0 // we wish to transfer the key manually mind.transfer_to(new_character) // won't transfer key since the mind is not active - new_character.name = prefs.real_name + new_character.name = name_pref.setting new_character.dna.ready_dna(new_character) if(new_character.mind) new_character.mind.store_memory("Your blood type is: [new_character.dna.b_type]
    ", category=MIND_MEMORY_GENERAL, forced=TRUE) - if(prefs.disabilities & DISABILITY_FLAG_NEARSIGHTED) + var/disabilities = prefs.get_pref(/datum/preference_setting/binary_flag/disabilities) + + if(disabilities & DISABILITY_FLAG_NEARSIGHTED) new_character.dna.SetSEState(GLASSESBLOCK,1,1) new_character.disabilities |= NEARSIGHTED - if(prefs.disabilities & DISABILITY_FLAG_VEGAN) + if(disabilities & DISABILITY_FLAG_VEGAN) new_character.dna.SetSEState(VEGANBLOCK, 1, 1) - if(prefs.disabilities & DISABILITY_FLAG_ASTHMA) + if(disabilities & DISABILITY_FLAG_ASTHMA) new_character.dna.SetSEState(ASTHMABLOCK, 1, 1) - chosen_species = all_species[prefs.species] - if( (prefs.disabilities & DISABILITY_FLAG_FAT) && (chosen_species.anatomy_flags & CAN_BE_FAT) ) + chosen_species = all_species[species] + if( (disabilities & DISABILITY_FLAG_FAT) && (chosen_species.anatomy_flags & CAN_BE_FAT) ) new_character.mutations += M_FAT new_character.overeatduration = 600 - if(prefs.disabilities & DISABILITY_FLAG_EPILEPTIC) + if(disabilities & DISABILITY_FLAG_EPILEPTIC) new_character.dna.SetSEState(EPILEPSYBLOCK,1,1) new_character.disabilities |= EPILEPSY - if(prefs.disabilities & DISABILITY_FLAG_DEAF) + if(disabilities & DISABILITY_FLAG_DEAF) new_character.dna.SetSEState(DEAFBLOCK,1,1) new_character.sdisabilities |= DEAF - if(prefs.disabilities & DISABILITY_FLAG_MUTE) + if(disabilities & DISABILITY_FLAG_MUTE) new_character.dna.SetSEState(MUTEBLOCK,1,1) new_character.sdisabilities |= MUTE - if(prefs.disabilities & DISABILITY_FLAG_LISP) + if(disabilities & DISABILITY_FLAG_LISP) new_character.dna.SetSEState(LISPBLOCK, 1, 1) - if(prefs.disabilities & DISABILITY_FLAG_ANEMIA) + if(disabilities & DISABILITY_FLAG_ANEMIA) new_character.dna.SetSEState(ANEMIABLOCK, 1, 1) new_character.dna.UpdateSE() @@ -778,6 +790,7 @@ break //Only autoconvert them once, and only if they aren't leading their own faith. if(late_join) + message_admins("latejoin, key transfer.") new_character.key = key return new_character diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index ce8a3919510..965700cfcce 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -1,136 +1,43 @@ /datum/preferences //The mob should have a gender you want before running this proc. Will run fine without H -/datum/preferences/proc/randomize_appearance_for(var/mob/living/carbon/human/H) +// I mean ideally we should want to use appearance_datums... ? +/datum/preferences/proc/randomize_appearance_for(var/mob/living/carbon/human/H, var/random_gender = FALSE) + var/datum/preference_setting/gender_pref = get_pref_datum(/datum/preference_setting/enum/gender) if(H) - if(H.gender == MALE) - gender = MALE - else - gender = FEMALE - s_tone = random_skin_tone(species) - h_style = random_hair_style(gender, species) - f_style = random_facial_hair_style(gender, species) - randomize_hair_color("hair") - randomize_hair_color("facial") - randomize_eyes_color() - underwear = rand(1,underwear_m.len) - backbag = 2 - age = rand(AGE_MIN,AGE_MAX) + gender_pref.setting = H.gender + else if (random_gender) + gender_pref.setting = pick(MALE, FEMALE) + + var/datum/preference_setting/s_tone = get_pref_datum(/datum/preference_setting/numerical/s_tone) + var/datum/preference_setting/h_style = get_pref_datum(/datum/preference_setting/string/h_style) + var/datum/preference_setting/f_style = get_pref_datum(/datum/preference_setting/string/f_style) + + s_tone.randomise() + h_style.randomise() // This also handles colours + f_style.randomise() + + var/datum/preference_setting/r_eyes = get_pref_datum(/datum/preference_setting/numerical/r_eyes) + var/datum/preference_setting/g_eyes = get_pref_datum(/datum/preference_setting/numerical/g_eyes) + var/datum/preference_setting/b_eyes = get_pref_datum(/datum/preference_setting/numerical/b_eyes) + + r_eyes.randomise() + g_eyes.randomise() + b_eyes.randomise() + + var/datum/preference_setting/underwear = get_pref_datum(/datum/preference_setting/numerical/underwear) + underwear.setting = rand(1, underwear_m.len) // Bug-for-bug compatibility, the last underwear_f values aren't available here + + var/datum/preference_setting/backbag = get_pref_datum(/datum/preference_setting/numerical/backbag) + backbag.setting = BACKPACK + + var/datum/preference_setting/age = get_pref_datum(/datum/preference_setting/numerical/age) + age.randomise() if(H) copy_to(H,1) - -/datum/preferences/proc/randomize_hair_color(var/target = "hair") - if(species == "Vox") - r_hair = rand(1,7) - if(prob (75) && target == "facial") // Chance to inherit hair color - r_facial = r_hair - g_facial = g_hair - b_facial = b_hair - return - - var/red - var/green - var/blue - - var/col = pick("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", 15;"punk") - switch(col) - if("blonde") - red = 255 - green = 255 - blue = 0 - if("black") - red = 0 - green = 0 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 51 - if("copper") - red = 255 - green = 153 - blue = 0 - if("brown") - red = 102 - green = 51 - blue = 0 - if("wheat") - red = 255 - green = 255 - blue = 153 - if("old") - red = rand (100, 255) - green = red - blue = red - if("punk") - red = rand(0, 255) - green = rand(0, 255) - blue = rand(0, 255) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - switch(target) - if("hair") - r_hair = red - g_hair = green - b_hair = blue - if("facial") - r_facial = red - g_facial = green - b_facial = blue - -/datum/preferences/proc/randomize_eyes_color() - var/red - var/green - var/blue - - var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") - switch(col) - if("black") - red = 0 - green = 0 - blue = 0 - if("grey") - red = rand (100, 200) - green = red - blue = red - if("brown") - red = 102 - green = 51 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 0 - if("blue") - red = 51 - green = 102 - blue = 204 - if("lightblue") - red = 102 - green = 204 - blue = 255 - if("green") - red = 0 - green = 102 - blue = 0 - if("albino") - red = rand (200, 255) - green = rand (0, 150) - blue = rand (0, 150) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - r_eyes = red - g_eyes = green - b_eyes = blue - /datum/preferences/proc/blend_backpack(var/icon/clothes_s,var/backbag,var/satchel,var/backpack="backpack",var/messenger_bag) + // I'm not even going to pretend what these fucking magic values mean switch(backbag) if(2) clothes_s.Blend(new /icon('icons/mob/back.dmi', backpack), ICON_OVERLAY) @@ -164,11 +71,32 @@ preview_icon = null var/g = "m" - if(gender == FEMALE) + if(get_pref(/datum/preference_setting/enum/gender) == FEMALE) g = "f" var/icon/icobase + var/species = get_pref(/datum/preference_setting/string/species) var/datum/species/current_species = all_species[species] + var/s_tone = get_pref(/datum/preference_setting/numerical/s_tone) + + var/r_hair = get_pref(/datum/preference_setting/numerical/r_hair) + var/g_hair = get_pref(/datum/preference_setting/numerical/g_hair) + var/b_hair = get_pref(/datum/preference_setting/numerical/b_hair) + + var/r_eyes = get_pref(/datum/preference_setting/numerical/r_eyes) + var/g_eyes = get_pref(/datum/preference_setting/numerical/b_eyes) + var/b_eyes = get_pref(/datum/preference_setting/numerical/g_eyes) + + var/r_facial = get_pref(/datum/preference_setting/numerical/r_facial) + var/g_facial = get_pref(/datum/preference_setting/numerical/g_facial) + var/b_facial = get_pref(/datum/preference_setting/numerical/b_facial) + + var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs) + + var/h_style = get_pref(/datum/preference_setting/string/h_style) + var/f_style = get_pref(/datum/preference_setting/string/f_style) + var/backbag = get_pref(/datum/preference_setting/numerical/backbag) + var/disabilites = get_pref(/datum/preference_setting/binary_flag/disabilities) //icon based species color if(current_species) @@ -202,7 +130,7 @@ icobase = 'icons/mob/human_races/r_human.dmi' var/fat="" - if(disabilities&DISABILITY_FLAG_FAT && current_species.anatomy_flags & CAN_BE_FAT) + if(disabilites&DISABILITY_FLAG_FAT && current_species.anatomy_flags & CAN_BE_FAT) fat="_fat" preview_icon = new /icon(icobase, "torso_[g][fat]") @@ -296,13 +224,13 @@ // UNIFORM DMI if(current_species) uniform_dmi=current_species.uniform_icons - if(disabilities&DISABILITY_FLAG_FAT && current_species.fat_uniform_icons) + if(disabilites&DISABILITY_FLAG_FAT && current_species.fat_uniform_icons) uniform_dmi=current_species.fat_uniform_icons // SUIT DMI if(current_species) suit_dmi=current_species.wear_suit_icons - if(disabilities&DISABILITY_FLAG_FAT && current_species.fat_wear_suit_icons) + if(disabilites&DISABILITY_FLAG_FAT && current_species.fat_wear_suit_icons) suit_dmi=current_species.fat_wear_suit_icons @@ -566,7 +494,7 @@ var/drink_icon_state = pick("coffee", "cafe_latte", "beer", "alebottle", "gargleblasterglass", "energy_drink", "sangria", "gintonicglass") clothes_s.Blend(new /icon(drink_icon, drink_icon_state), ICON_OVERLAY) - if(disabilities & NEARSIGHTED) + if(disabilites & NEARSIGHTED) preview_icon.Blend(new /icon('icons/mob/eyes.dmi', "glasses"), ICON_OVERLAY) preview_icon.Blend(eyes_s, ICON_OVERLAY) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 2299cde8724..031a3b9c072 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -55,7 +55,7 @@ var/list/global_deadchat_listeners = list() to_chat(usr, "Speech is currently admin-disabled.") return - if(client && !(client.prefs.toggles & CHAT_DEAD)) + if(client && !(client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) to_chat(usr, "You have deadchat muted.") return @@ -174,4 +174,4 @@ var/list/global_deadchat_listeners = list() temp += pick(append) say(temp) - winset(client, "input", "text=[null]") \ No newline at end of file + winset(client, "input", "text=[null]") diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index a2b5da4238f..f54b8041454 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -17,7 +17,7 @@ var/atom/movable/typing_indicator/typing_indicator return ..() /mob/proc/create_typing_indicator() - if(client && !stat && client.prefs.typing_indicator && src.is_visible() && isturf(src.loc)) + if(client && !stat && client.prefs.get_pref(/datum/preference_setting/toggle/typing_indicator) && src.is_visible() && isturf(src.loc)) vis_contents |= typing_indicator /mob/proc/remove_typing_indicator() diff --git a/code/modules/multiz/ventcrawl.dm b/code/modules/multiz/ventcrawl.dm index 8f9b30811ed..2a66cb9cf91 100644 --- a/code/modules/multiz/ventcrawl.dm +++ b/code/modules/multiz/ventcrawl.dm @@ -54,7 +54,7 @@ if(target_move.return_network(target_move) != return_network(src)) user.remove_ventcrawl() user.add_ventcrawl(target_move) - if (user.client.prefs.stumble && ((world.time - user.last_movement) > 5)) + if (user.client.prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - user.last_movement) > 5)) user.delayNextMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier user.forceMove(target_move) user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement diff --git a/code/modules/paperwork/papers_dynamic.dm b/code/modules/paperwork/papers_dynamic.dm index b44842679ba..9663a492dc9 100644 --- a/code/modules/paperwork/papers_dynamic.dm +++ b/code/modules/paperwork/papers_dynamic.dm @@ -80,7 +80,7 @@ ..() /obj/item/weapon/paper/merchant/proc/apply_text(mob/living/carbon/human/merchant) - identity = merchant.client.prefs.real_name + identity = merchant.client.prefs.get_pref(/datum/preference_setting/string/real_name) icon = 'icons/obj/items.dmi' icon_state = "permit" name = "Merchant's Licence - [identity]" @@ -105,7 +105,7 @@ display_y = 700 /obj/item/weapon/paper/merchant/report/apply_text(mob/living/carbon/human/merchant) - identity = merchant.client.prefs.real_name + identity = merchant.client.prefs.get_pref(/datum/preference_setting/string/real_name) name = "Licensed Merchant Report - [identity]" info = {"