diff --git a/.github/COMPONENTOWNERS.yml b/.github/COMPONENTOWNERS.yml
index 6c01aa197263..8e29ba4702a6 100644
--- a/.github/COMPONENTOWNERS.yml
+++ b/.github/COMPONENTOWNERS.yml
@@ -16,7 +16,7 @@ components:
## Код ##
# /code/
- code/modules/client/preferences_savefile.dm: volas
+ code/modules/client/: volas
## Конфиги ##
config/: volas
diff --git a/code/__DEFINES/chat.dm b/code/__DEFINES/chat.dm
index e07d939ff4b9..3ea59a986124 100644
--- a/code/__DEFINES/chat.dm
+++ b/code/__DEFINES/chat.dm
@@ -14,7 +14,6 @@
#define MESSAGE_TYPE_ATTACKLOG "attacklog"
#define MESSAGE_TYPE_DEBUG "debug"
-
// To chat defines
#define to_chat_private(usr, msg, type) to_chat(usr, msg, type, confidential = TRUE)
diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm
index 7ce18ed805a8..b8a958ebcec3 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -210,3 +210,14 @@
0, 0, 0, 1, \
0, 0, 0, 0)
+/**
+* CHAT COLORS
+*
+* These are used in both dark and light chat themes and override default (css) colors,
+* so should be accessible with both background colors
+* (#ffffff for light, #131313 for dark, test it with any color contrast checker)
+*/
+#define OOC_COLOR_SUPPORTER "#cca000" // we use it just for nickname
+#define OOC_COLOR_EVENTADMIN "#ae566d"
+#define OOC_COLOR_CODEADMIN "#257227"
+#define OOC_COLOR_ADMIN "#b82b00"
diff --git a/code/__DEFINES/fonts.dm b/code/__DEFINES/fonts.dm
new file mode 100644
index 000000000000..d28ce7e35d72
--- /dev/null
+++ b/code/__DEFINES/fonts.dm
@@ -0,0 +1,6 @@
+#define FONT_SYSTEM "System"
+#define FONT_FIXEDSYS "Fixedsys"
+#define FONT_SMALL_FONTS "Small Fonts"
+#define FONT_TIMES_NEW_ROMAN "Times New Roman"
+#define FONT_SERIF "Serif"
+#define FONT_VERDANA "Verdana"
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 274d54af8b73..2bf1b3c27804 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -1,43 +1,77 @@
-//Preference toggles (it had more bits, but updating player saves without wiping method is a bit more complex).
-#define SHOW_ANIMATIONS 16
-#define SHOW_PROGBAR 32
-
-#define TOGGLES_DEFAULT (SHOW_ANIMATIONS|SHOW_PROGBAR)
-
-//Chat toggles
-#define CHAT_OOC 1
-#define CHAT_DEAD 2
-#define CHAT_GHOSTEARS 4
-#define CHAT_NOCLIENT_ATTACK 8
-#define CHAT_PRAYER 16
-#define CHAT_RADIO 32
-#define CHAT_ATTACKLOGS 64
-#define CHAT_DEBUGLOGS 128
-#define CHAT_LOOC 256
-#define CHAT_GHOSTRADIO 512
-#define CHAT_GHOSTNPC 1024
-#define CHAT_CKEY 2048
-
-#define TOGGLES_DEFAULT_CHAT (CHAT_OOC|CHAT_DEAD|CHAT_NOCLIENT_ATTACK|CHAT_GHOSTEARS|CHAT_PRAYER|CHAT_RADIO|CHAT_GHOSTRADIO|CHAT_GHOSTNPC|CHAT_ATTACKLOGS|CHAT_LOOC|CHAT_CKEY)
-
-#define GLOW_HIGH 0
-#define GLOW_MED 1 //default.
-#define GLOW_LOW 2
-#define GLOW_DISABLE 3 //this option must be the highest number
-
-#define PARALLAX_INSANE -1 //for show offs
-#define PARALLAX_HIGH 0 //default.
-#define PARALLAX_MED 1
-#define PARALLAX_LOW 2
-#define PARALLAX_DISABLE 3 //this option must be the highest number
+// this can be little confusing, but call it as you would call
+// /datum/preferences/proc/get_pref(type)
+// ex. client.prefs.get_pref()
+#define get_pref(type) prefs_player[type].value
+
+// maximum binds per keybind
+#define KEYBINDS_MAXIMUM 3
+
+// pref types
+#define PREF_TYPE_BOOLEAN "boolean"
+#define PREF_TYPE_TEXT "text"
+//#define PREF_TYPE_INTEGER "integer"
+#define PREF_TYPE_RANGE "range"
+#define PREF_TYPE_SELECT "select"
+#define PREF_TYPE_HEX "hex"
+#define PREF_TYPE_KEYBIND "keybind"
+#define PREF_TYPE_CUSTOM "custom"
+
+// pref domains (categories)
+#define PREF_DOMAIN_PLAYER "player"
+#define PREF_DOMAIN_KEYBINDS "keybinds"
+#define PREF_DOMAIN_META "meta"
+#define PREF_DOMAIN_CHARACTER "character" // todo
+
+// player pref domain subcategories
+#define PREF_PLAYER_DISPLAY "display"
+#define PREF_PLAYER_EFFECTS "effects"
+#define PREF_PLAYER_AUDIO "audio"
+#define PREF_PLAYER_UI "ui"
+#define PREF_PLAYER_CHAT "chat"
+#define PREF_PLAYER_GAME "game"
+
+// keybinds pref domain subcategories
+#define PREF_KEYBINDS_CLIENT "CLIENT"
+#define PREF_KEYBINDS_COMMUNICATION "COMMUNICATION"
+#define PREF_KEYBINDS_MOVEMENT "MOVEMENT"
+#define PREF_KEYBINDS_CARBON "CARBON"
+#define PREF_KEYBINDS_HUMAN "HUMAN"
+#define PREF_KEYBINDS_ROBOT "ROBOT"
+#define PREF_KEYBINDS_EMOTE "EMOTE"
+#define PREF_KEYBINDS_MISC "MISC"
+
+///datum/pref/player/display/zoom_mode
+#define SCALING_METHOD_NORMAL "normal"
+#define SCALING_METHOD_DISTORT "distort"
+//#define SCALING_METHOD_BLUR "blur" // gives worst result so currently not used and players can't choice it
+
+///datum/pref/player/effects/glowlevel
+#define GLOW_HIGH "high"
+#define GLOW_MED "med"
+#define GLOW_LOW "low"
+#define GLOW_DISABLE "disable"
+
+///datum/pref/player/effects/parallax
+#define PARALLAX_INSANE "insane" //for show offs
+#define PARALLAX_HIGH "high" //default.
+#define PARALLAX_MED "med"
+#define PARALLAX_LOW "low"
+#define PARALLAX_DISABLE "disable"
#define PARALLAX_DELAY_DEFAULT world.tick_lag
#define PARALLAX_DELAY_MED 1
#define PARALLAX_DELAY_LOW 2
-#define CHAT_GHOSTSIGHT_ALL 1
-#define CHAT_GHOSTSIGHT_ALLMANUAL 2
-#define CHAT_GHOSTSIGHT_NEARBYMOBS 3
+///datum/pref/player/chat/attack_log
+#define ATTACK_LOG_DISABLED "disabled"
+#define ATTACK_LOG_BY_CLIENT "by_client"
+//#define ATTACK_LOG_BOTH_CLIENT "both_client" // todo
+#define ATTACK_LOG_ALL "all"
+
+///datum/pref/player/game/ghost_skin
+#define GHOST_SKIN_CHARACTER "character"
+#define GHOST_SKIN_GHOST "ghost"
+#define GHOST_SKIN_FLUFF "fluff"
//used for alternate_option
#define GET_RANDOM_JOB 0
@@ -55,3 +89,22 @@
//recommened client FPS
#define RECOMMENDED_FPS 100
+
+// ui themes
+#define UI_STYLE_WHITE "White"
+#define UI_STYLE_MIDNIGHT "Midnight"
+#define UI_STYLE_OLD "Old"
+#define UI_STYLE_ORANGE "Orange"
+
+// keybinds weight
+#define WEIGHT_HIGHEST 0
+#define WEIGHT_ADMIN 10
+#define WEIGHT_CLIENT 20
+#define WEIGHT_ROBOT 30
+#define WEIGHT_MOB 40
+#define WEIGHT_LIVING 50
+#define WEIGHT_DEAD 60
+#define WEIGHT_EMOTE 70
+#define WEIGHT_LOWEST 999
+
+
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index c6daf402629e..ea355aeb8807 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -1,22 +1,25 @@
//volume channels for client's volume sliders.
-#define VOL_MUSIC (1<<0) // lobby.
-#define VOL_AMBIENT (1<<1) // ambient music/environment sounds.
-#define VOL_EFFECTS_MASTER (1<<2) // anything that doesn't go into sub categories (this acts as a master channel for all subs of this type).
-//effects sub categories (VOL_EFFECTS_MASTER included, so that we don't need to type it everytime when we want to use just a sub category).
-#define VOL_EFFECTS_VOICE_ANNOUNCEMENT (VOL_EFFECTS_MASTER | 1<<3) // voiced global announcements.
-#define VOL_EFFECTS_MISC (VOL_EFFECTS_MASTER | 1<<4) // for any sound that may annoy players (like tesla engine).
-#define VOL_EFFECTS_INSTRUMENT (VOL_EFFECTS_MASTER | 1<<5) // music instruments! actually this could be merged into spam category.
+// OOC audio
+#define VOL_LOBBY_MUSIC (1<<0) // music in lobby
+#define VOL_NOTIFICATIONS (1<<1) // mainly for ghosts, such as cloning ready, admin pm, etc
+#define VOL_ADMIN_SOUNDS (1<<2) // admin sounds or music (fun category)
-#define VOL_NOTIFICATIONS (1<<6) // mainly for ghosts, such as cloning ready, admin pm, etc.
-#define VOL_ADMIN (1<<7) // admin sounds or music (fun category).
-
-// jukebox not a VOL_MUSIC sub category because jukebox plays thru javascript, which is not boynd's sound datum.
+// IC audio
+#define VOL_AMBIENT (1<<3) // ambient music/environment sounds
+#define VOL_EFFECTS_MASTER (1<<4) // effects sounds // todo: rename to VOL_EFFECTS when you have time to push 500+ files change
+#define VOL_VOICE_ANNOUNCEMENTS (1<<5) // voiced global announcements
+#define VOL_MUSIC_INSTRUMENTS (1<<6) // music instruments!
+#define VOL_SPAM_EFFECTS (1<<7) // separated channel for some spamming and annoying sounds so player can setup them separately (ex. tesla engine).
#define VOL_JUKEBOX (1<<8)
-//Misc
-#define VOL_LINEAR_TO_NON(vol_raw) ((20 ** clamp(vol_raw * 0.01, 0, 1.0)) - 0.99) / (20 - 0.99) // this converts byond's linear volume into non linear (don't change anything without heavy testing with debug, even 0.01 difference may break the sound or functions that connects with this).
-#define SANITIZE_VOL(vol) vol * 0.5 // environment setting can overload sound that use 100% volume (0.5 actually is max, if you want pure sound with anything).
+// converts raw preference volume (1-100) to non linear coefficient (0-1) we apply for SANITIZE_VOL(playsound volume)
+// to get final client volume before any others environment effects
+// (don't change anything without heavy testing with debug, even 0.01 difference may break the sound or functions that connects with this).
+#define VOL_LINEAR_TO_NON(vol_raw) ((20 ** clamp(vol_raw * 0.01, 0, 1.0)) - 0.99) / (20 - 0.99)
+// environment settings can overload sound that uses 100% volume, so we cap default volume at 50%
+// (which means our build is quieter than others!!!)
+#define SANITIZE_VOL(vol) vol * 0.5
//sound channels, max is 1024
#define CHANNEL_AMBIENT 1
diff --git a/code/__DEFINES/subsystem.dm b/code/__DEFINES/subsystem.dm
index f9b91ad1fdd6..bce85fafcfef 100644
--- a/code/__DEFINES/subsystem.dm
+++ b/code/__DEFINES/subsystem.dm
@@ -109,6 +109,7 @@
#define SS_WAIT_GNAW 20
#define SS_WAIT_DEFAULT 20
#define SS_WAIT_UNIT_TESTS 20
+#define SS_WAIT_PREFERENCES 30
#define SS_WAIT_SUN 600
#define SS_WAIT_SMARTLIGHT 600
diff --git a/code/__HELPERS/colors.dm b/code/__HELPERS/colors.dm
index b81c2ff1aa63..052ede70903c 100644
--- a/code/__HELPERS/colors.dm
+++ b/code/__HELPERS/colors.dm
@@ -70,17 +70,17 @@
/// Ensures that the lightness value of a color must be greater than the provided minimum.
/proc/color_lightness_max(color, min_lightness)
- var/list/rgb = rgb2num(color)
- var/list/hsl = rgb2hsl(rgb[1], rgb[2], rgb[3])
+ var/list/hsl = rgb2num(color, COLORSPACE_HSL)
hsl[3] = max(hsl[3], min_lightness)
- var/list/transformed_rgb = hsl2rgb(hsl[1], hsl[2], hsl[3])
- return rgb(transformed_rgb[1], transformed_rgb[2], transformed_rgb[3])
+ return rgb(hsl[1], hsl[2], hsl[3], space=COLORSPACE_HSL)
/// Ensures that the lightness value of a color must be less than the provided maximum.
/proc/color_lightness_min(color, max_lightness)
- var/list/rgb = rgb2num(color)
- var/list/hsl = rgb2hsl(rgb[1], rgb[2], rgb[3])
- // Ensure high lightness (Minimum of 90%)
+ var/list/hsl = rgb2num(color, COLORSPACE_HSL)
hsl[3] = min(hsl[3], max_lightness)
- var/list/transformed_rgb = hsl2rgb(hsl[1], hsl[2], hsl[3])
- return rgb(transformed_rgb[1], transformed_rgb[2], transformed_rgb[3])
+ return rgb(hsl[1], hsl[2], hsl[3], space=COLORSPACE_HSL)
+
+/proc/color_lightness_clamp(color, min_lightness, max_lightness)
+ var/list/hsl = rgb2num(color, COLORSPACE_HSL)
+ hsl[3] = clamp(hsl[3], min_lightness, max_lightness)
+ return rgb(hsl[1], hsl[2], hsl[3], space=COLORSPACE_HSL)
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index d07f24b357f8..640f7a90575f 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -255,7 +255,7 @@
var/turf/ear = get_turf(M)
if(ear)
// Ghostship is magic: Ghosts can hear radio chatter from anywhere
- if(speaker_coverage[ear] || (isobserver(M) && (M.client) && (M.client.prefs.chat_toggles & CHAT_GHOSTRADIO)))
+ if(speaker_coverage[ear] || (isobserver(M) && (M.client) && M.client.prefs.get_pref(/datum/pref/player/chat/ghostradio)))
. |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down.
return .
@@ -506,7 +506,7 @@
C = M.client
if(!C)
return
- winset(C, "mainwindow", "flash=5")
+ winset(C, "tcmainwindow", "flash=5")
//============VG PORTS============
/proc/recursive_type_check(atom/O, type = /atom)
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 0f48cd1cc809..5073edaffc47 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -53,7 +53,7 @@
sort_surgeries()
// Keybindings
- for(var/KB in subtypesof(/datum/keybinding))
+/* for(var/KB in subtypesof(/datum/keybinding))
var/datum/keybinding/keybinding = KB
if(!initial(keybinding.name))
continue
@@ -61,7 +61,7 @@
global.keybindings_by_name[instance.name] = instance
if(length(instance.hotkey_keys))
for(var/bound_key in instance.hotkey_keys)
- global.hotkey_keybinding_list_by_key[bound_key] += list(instance.name)
+ global.hotkey_keybinding_list_by_key[bound_key] += list(instance.name)*/
init_subtypes(/datum/crafting_recipe, crafting_recipes)
init_subtypes(/datum/dirt_cover, global.all_dirt_covers)
@@ -248,8 +248,12 @@
global.skillset_names_aliases[skillset.name] = s
global.all_emotes = list()
- for(var/emote_type in subtypesof(/datum/emote))
- global.all_emotes[emote_type] = new emote_type
+ global.all_emotes_keys = list()
+ for(var/emote_type as anything in subtypesof(/datum/emote))
+ var/datum/emote/E = new emote_type
+ global.all_emotes[emote_type] = E
+ if(E.key && E.key != "list")
+ global.all_emotes_keys |= E.key
global.emotes_for_emote_panel = list()
var/emote_icons = 'icons/misc/emotes.dmi'
@@ -297,6 +301,12 @@
for(var/datum/ringtone/Ring as anything in subtypesof(/datum/ringtone))
global.ringtones_by_names["[initial(Ring.name)]"] = new Ring
+ for(var/datum/pref/keybinds/KB as anything in subtypesof(/datum/pref/keybinds))
+ if(initial(KB.legacy_keyname))
+ legacy_keyname_to_pref[initial(KB.legacy_keyname)] = KB
+ // little fuckup happened and leap key was not converted on time and lost
+ legacy_keyname_to_pref["leap"] = /datum/pref/keybinds/human/race_ability
+
init_washing_items_list()
/proc/init_washing_items_list()
diff --git a/code/__HELPERS/keys.dm b/code/__HELPERS/keys.dm
new file mode 100644
index 000000000000..6a0711d2e969
--- /dev/null
+++ b/code/__HELPERS/keys.dm
@@ -0,0 +1,61 @@
+// list of valid byond keyboard keys based on https://www.byond.com/docs/ref/#/{skin}/macros
+// + some missing from the table that were found experimentally
+// not all of them are mapped with js keys and available to bind currently
+var/global/list/byond_valid_keys = list (
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
+ "`", "-", "=", "\\",
+ "\[", "\]", ";", "'", ",", ".", "/",
+ "Numpad0", "Numpad1", "Numpad2", "Numpad3", "Numpad4", "Numpad5", "Numpad6", "Numpad7", "Numpad8", "Numpad9",
+ "North", "South", "East", "West", "Northwest", "Southwest", "Northeast", "Southeast",
+ "Center", "Return", "Escape", "Tab", "Space", "Back", "Insert", "Delete", "Pause", "Snapshot",
+ "LWin", "RWin", "Apps",
+ "Multiply", "Add", "Subtract", "Divide", "Separator",
+ "Shift", "Ctrl", "Alt",
+ "VolumeMute", "VolumeUp", "VolumeDown", "MediaPlayPause", "MediaStop", "MediaNext", "MediaPrev",
+ )
+
+// Converts (some) browser keycodes to BYOND keycodes.
+/proc/browser_keycode_to_byond(keycode)
+ keycode = text2num(keycode)
+ switch(keycode)
+ // letters and numbers
+ if(65 to 90, 48 to 57)
+ return ascii2text(keycode)
+ if(17)
+ return "Ctrl"
+ if(18)
+ return "Alt"
+ if(16)
+ return "Shift"
+ if(37)
+ return "West"
+ if(38)
+ return "North"
+ if(39)
+ return "East"
+ if(40)
+ return "South"
+ if(45)
+ return "Insert"
+ if(46)
+ return "Delete"
+ if(36)
+ return "Northwest"
+ if(35)
+ return "Southwest"
+ if(33)
+ return "Northeast"
+ if(34)
+ return "Southeast"
+ if(112 to 123)
+ return "F[keycode-111]"
+ if(96 to 105)
+ return "Numpad[keycode-96]"
+ if(188)
+ return ","
+ if(190)
+ return "."
+ if(189)
+ return "-"
diff --git a/code/__HELPERS/logging.dm b/code/__HELPERS/logging.dm
index b17d6b782c4f..20b5d30d03bd 100644
--- a/code/__HELPERS/logging.dm
+++ b/code/__HELPERS/logging.dm
@@ -48,7 +48,7 @@
global.game_log << "\[[time_stamp()]]DEBUG: [text][log_end]"
for(var/client/C as anything in admins)
- if(C.prefs.chat_toggles & CHAT_DEBUGLOGS)
+ if(C.prefs.get_pref(/datum/pref/player/chat/debug_log))
to_chat_debug(C, "DEBUG: [text]")
/proc/log_asset(text)
@@ -170,7 +170,7 @@
/proc/log_gc(text)
global.gc_log << "\[[time_stamp()]] [text][log_end]"
for(var/client/C in global.admins)
- if(C.prefs.chat_toggles & CHAT_DEBUGLOGS)
+ if(C.prefs.get_pref(/datum/pref/player/chat/debug_log))
to_chat(C, "GC DEBUG: [text]")
#endif
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 907597810240..09050b9f4d01 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -232,7 +232,7 @@
var/holding = user.get_active_hand()
var/datum/progressbar/progbar
if (progress)
- if(user.client && (user.client.prefs.toggles & SHOW_PROGBAR))
+ if(user.client?.prefs.get_pref(/datum/pref/player/game/progressbar))
progbar = new(user, time, target)
else
progress = FALSE
@@ -309,7 +309,7 @@
var/datum/progressbar/progbar
if (progress)
- if(user.client && (user.client.prefs.toggles & SHOW_PROGBAR))
+ if(user.client?.prefs.get_pref(/datum/pref/player/game/progressbar))
progbar = new(user, delay, target)
else
progress = FALSE
diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm
index a9d398eaa9b8..dd66f3d53771 100644
--- a/code/__HELPERS/sanitize_values.dm
+++ b/code/__HELPERS/sanitize_values.dm
@@ -45,18 +45,25 @@
return default
/proc/sanitize_hexcolor(color, default="#000000")
- if(!istext(color)) return default
+ if(!istext(color))
+ return default
var/len = length(color)
- if(len != 7 && len !=4) return default
- if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#"
+ if(len != 7 && len !=4)
+ return default
+ if(text2ascii(color,1) != 35)
+ return default //35 is the ascii code for "#"
. = "#"
for(var/i=2,i<=len,i++)
var/ascii = text2ascii(color,i)
switch(ascii)
- if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9
- if(97 to 102) . += ascii2text(ascii) //letters a to f
- if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase
- else return default
+ if(48 to 57)
+ . += ascii2text(ascii) //numbers 0 to 9
+ if(97 to 102)
+ . += ascii2text(ascii) //letters a to f
+ if(65 to 70)
+ . += ascii2text(ascii+32) //letters A to F - translates to lowercase
+ else
+ return default
return .
var/global/regex/IP_pattern = regex(@"^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$")
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index 4d9beb7a3d61..892058d2b3e7 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -179,6 +179,10 @@
return findtext(text, suffix, start, null)
return
+/proc/endswith(input_text, ending)
+ var/input_length = length(ending)
+ return !!findtext(input_text, ending, -input_length)
+
/*
* Text modification
*/
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index e6f6ee88218a..1a624e7f3e06 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -276,59 +276,6 @@
return /datum
return text2path(copytext(string_type, 1, last_slash))
-/// Converts an RGB color to an HSL color
-/proc/rgb2hsl(red, green, blue)
- red /= 255;green /= 255;blue /= 255;
- var/max = max(red,green,blue)
- var/min = min(red,green,blue)
- var/range = max-min
-
- var/hue=0;var/saturation=0;var/lightness=0;
- lightness = (max + min)/2
- if(range != 0)
- if(lightness < 0.5)
- saturation = range/(max+min)
- else
- saturation = range/(2-max-min)
-
- var/dred = ((max-red)/(6*max)) + 0.5
- var/dgreen = ((max-green)/(6*max)) + 0.5
- var/dblue = ((max-blue)/(6*max)) + 0.5
-
- if(max==red)
- hue = dblue - dgreen
- else if(max==green)
- hue = dred - dblue + (1/3)
- else
- hue = dgreen - dred + (2/3)
- if(hue < 0)
- hue++
- else if(hue > 1)
- hue--
-
- return list(hue, saturation, lightness)
-
-/// Converts an HSL color to an RGB color
-/proc/hsl2rgb(hue, saturation, lightness)
- var/red;var/green;var/blue;
- if(saturation == 0)
- red = lightness * 255
- green = red
- blue = red
- else
- var/a;var/b;
- if(lightness < 0.5)
- b = lightness*(1+saturation)
- else
- b = (lightness+saturation) - (saturation*lightness)
- a = 2*lightness - b
-
- red = round(255 * hue2rgb(a, b, hue+(1/3)))
- green = round(255 * hue2rgb(a, b, hue))
- blue = round(255 * hue2rgb(a, b, hue-(1/3)))
-
- return list(red, green, blue)
-
/// Converts an ABH color to an RGB color
/proc/hue2rgb(a, b, hue)
if(hue < 0)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 0c3785628578..f2da3cb6d0ce 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1598,50 +1598,6 @@ var/global/list/WALLITEMS = typecacheof(list(
for(var/mob/dead/new_player/N as anything in new_player_list)
INVOKE_ASYNC(N, TYPE_PROC_REF(/mob/dead/new_player, show_titlescreen))
-// Converts browser keycodes to BYOND keycodes.
-/proc/browser_keycode_to_byond(keycode)
- keycode = text2num(keycode)
- switch(keycode)
- // letters and numbers
- if(65 to 90, 48 to 57)
- return ascii2text(keycode)
- if(17)
- return "Ctrl"
- if(18)
- return "Alt"
- if(16)
- return "Shift"
- if(37)
- return "West"
- if(38)
- return "North"
- if(39)
- return "East"
- if(40)
- return "South"
- if(45)
- return "Insert"
- if(46)
- return "Delete"
- if(36)
- return "Northwest"
- if(35)
- return "Southwest"
- if(33)
- return "Northeast"
- if(34)
- return "Southeast"
- if(112 to 123)
- return "F[keycode-111]"
- if(96 to 105)
- return "Numpad[keycode-96]"
- if(188)
- return ","
- if(190)
- return "."
- if(189)
- return "-"
-
// Format a power value in W, kW, MW, or GW
/proc/DisplayPower(powerused)
if(powerused < 1000) // Less than a kW
diff --git a/code/_globalvars/lists/client.dm b/code/_globalvars/lists/client.dm
deleted file mode 100644
index ba32eca66c2e..000000000000
--- a/code/_globalvars/lists/client.dm
+++ /dev/null
@@ -1,32 +0,0 @@
-var/global/list/hotkey_keybinding_list_by_key = list()
-var/global/list/keybindings_by_name = list()
-
-// This is a mapping from JS keys to Byond - ref: https://keycode.info/
-var/global/list/_kbMap = list(
- "UP" = "North",
- "RIGHT" = "East",
- "DOWN" = "South",
- "LEFT" = "West",
- "INSERT" = "Insert",
- "HOME" = "Northwest",
- "PAGEUP" = "Northeast",
- "DEL" = "Delete",
- "END" = "Southwest",
- "PAGEDOWN" = "Southeast",
- "SPACEBAR" = "Space",
- "ALT" = "Alt",
- "SHIFT" = "Shift",
- "CONTROL" = "Ctrl"
- )
-
-// Without alt, shift, ctrl and etc because its not necessary
-var/global/list/_kbMap_reverse = list(
- "North" = "Up",
- "East" = "Right",
- "South" = "Down",
- "West" = "Left",
- "Northwest" = "Home",
- "Northeast" = "PageUp",
- "Southwest" = "End",
- "Southeast" = "PageDown",
- )
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 7c5f720e4324..58b55dfd8140 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -9,16 +9,21 @@
including inventories and item quick actions.
*/
-// The default UI style is the first one in the list
+// The default UI styles client can choice with color/alpha customization
+// some mob types use own style and we don't override it
var/global/list/available_ui_styles = list(
- "White" = 'icons/hud/screen1_White.dmi',
- "Midnight" = 'icons/hud/screen1_Midnight.dmi',
- "old" = 'icons/hud/screen1_old.dmi',
- "Orange" = 'icons/hud/screen1_Orange.dmi'
+ UI_STYLE_WHITE = 'icons/hud/screen1_White.dmi',
+ UI_STYLE_MIDNIGHT = 'icons/hud/screen1_Midnight.dmi',
+ UI_STYLE_OLD = 'icons/hud/screen1_old.dmi',
+ UI_STYLE_ORANGE = 'icons/hud/screen1_Orange.dmi',
)
-/proc/ui_style2icon(ui_style)
- return global.available_ui_styles[ui_style] || global.available_ui_styles[global.available_ui_styles[1]]
+var/global/list/customizable_ui_styles = list(
+ 'icons/hud/screen1_White.dmi',
+ 'icons/hud/screen1_Midnight.dmi',
+ 'icons/hud/screen1_old.dmi',
+ 'icons/hud/screen1_Orange.dmi',
+ )
/datum/hud
var/mob/mymob
@@ -46,7 +51,7 @@ var/global/list/available_ui_styles = list(
if (!ui_style)
// will fall back to the default if any of these are null
- ui_style = ui_style2icon(mymob.client?.prefs?.UI_style)
+ ui_style = global.available_ui_styles[mymob.client?.prefs?.get_pref(/datum/pref/player/ui/ui_style) || UI_STYLE_WHITE]
instantiate()
@@ -182,6 +187,15 @@ var/global/list/available_ui_styles = list(
hud_version = version
+/mob/proc/refresh_hud()
+ var/list/screens = hud_used.main + hud_used.adding + hud_used.hotkeybuttons
+ for(var/atom/movable/screen/complex/complex as anything in hud_used.complex)
+ screens += complex
+ screens += complex.screens
+
+ for(var/atom/movable/screen/screen as anything in screens)
+ screen.update_by_hud(hud_used)
+
//Triggered when F12 is pressed (Unless someone changed something in the DMF)
/mob/verb/button_pressed_F12()
set name = "F12"
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index e495346337cc..b4583e5c5d23 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -1,6 +1,6 @@
/mob/living/carbon/human/add_to_hud(datum/hud/hud)
- hud.ui_color = client.prefs.UI_style_color
- hud.ui_alpha = client.prefs.UI_style_alpha
+ hud.ui_color = client.prefs.get_pref(/datum/pref/player/ui/ui_style_color)
+ hud.ui_alpha = 255 - floor(255*client.prefs.get_pref(/datum/pref/player/ui/ui_style_opacity)/100)
..(hud, FALSE)
diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm
index 780de628c3e6..6a8a9781d2b3 100644
--- a/code/_onclick/hud/parallax.dm
+++ b/code/_onclick/hud/parallax.dm
@@ -44,7 +44,7 @@
return FALSE
var/client/C = mymob.client
- switch(C.prefs.parallax)
+ switch(C.prefs.get_pref(/datum/pref/player/effects/parallax))
if (PARALLAX_INSANE)
C.parallax_throttle = FALSE
C.parallax_layers_max = 4
diff --git a/code/_onclick/hud/rendering/planes/_planes.dm b/code/_onclick/hud/rendering/planes/_planes.dm
index d991694e1bb3..458d0d13dd80 100644
--- a/code/_onclick/hud/rendering/planes/_planes.dm
+++ b/code/_onclick/hud/rendering/planes/_planes.dm
@@ -20,17 +20,18 @@
if(!..())
return
- if(client.prefs?.ambientocclusion)
+ if(client.prefs?.get_pref(/datum/pref/player/effects/ambientocclusion))
add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA"))
if(!assigned_map) // todo: don't reapply filter if it's already exists?
client.mob.clear_fullscreen("blurry")
if(client.mob.eye_blurry)
- if(client.prefs?.eye_blur_effect)
+ if(client.prefs?.get_pref(/datum/pref/player/effects/legacy_blur))
+ // alternative filter for users with weak PC
+ client.mob.overlay_fullscreen("blurry", /atom/movable/screen/fullscreen/blurry)
+ else
add_filter("eye_blur_angular", 1, angular_blur_filter(16, 16, clamp(client.mob.eye_blurry * 0.1, 0.2, 0.6)))
add_filter("eye_blur_gauss", 1, gauss_blur_filter(clamp(client.mob.eye_blurry * 0.05, 0.1, 0.25)))
- else // alternative filter for users with weak PC
- client.mob.overlay_fullscreen("blurry", /atom/movable/screen/fullscreen/blurry)
/atom/movable/screen/plane_master/game_world_above
name = "above game world plane master"
diff --git a/code/_onclick/hud/rendering/planes/lighting_planes.dm b/code/_onclick/hud/rendering/planes/lighting_planes.dm
index f59538246fd3..f4f663b99872 100644
--- a/code/_onclick/hud/rendering/planes/lighting_planes.dm
+++ b/code/_onclick/hud/rendering/planes/lighting_planes.dm
@@ -117,7 +117,7 @@
if(!..())
return
- var/enabled = client.prefs?.lampsexposure || FALSE
+ var/enabled = client.prefs?.get_pref(/datum/pref/player/effects/lampsexposure)
if(enabled)
alpha = 255
@@ -139,7 +139,7 @@
if(!..())
return
- var/level = client.prefs?.glowlevel || FALSE
+ var/level = client.prefs?.get_pref(/datum/pref/player/effects/glowlevel)
if(isnull(level))
return
@@ -178,7 +178,7 @@
if(!..())
return
- var/enabled = client.prefs?.lampsglare || FALSE
+ var/enabled = client.prefs?.get_pref(/datum/pref/player/effects/lampsglare)
if(enabled)
add_filter("add_lamps_to_glare", 1, layering_filter(render_source = LIGHTING_LAMPS_RENDER_TARGET, blend_mode = BLEND_OVERLAY))
diff --git a/code/_onclick/hud/tooltip.dm b/code/_onclick/hud/tooltip.dm
index f672cee83f9d..710cf0a809f1 100644
--- a/code/_onclick/hud/tooltip.dm
+++ b/code/_onclick/hud/tooltip.dm
@@ -11,9 +11,8 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
var/looking_at
var/state = TRUE
- var/font_size = 8
-/atom/movable/screen/tooltip/proc/SetMapText(newValue, font, forcedFontColor = "#ffffff")
+/atom/movable/screen/tooltip/proc/SetMapText(newValue, font, font_size, forcedFontColor = "#ffffff")
var/style = "font-family:'[font]'; color:[forcedFontColor]; -dm-text-outline: 1px [invertHTMLcolor(forcedFontColor)]; font-weight: bold; font-size: [font_size]px;"
maptext = "
[uppertext(newValue)]"
@@ -24,14 +23,14 @@
/client/New(TopicData)
. = ..()
tooltip = new /atom/movable/screen/tooltip()
- if(prefs.tooltip)
+ if(prefs.get_pref(/datum/pref/player/ui/tooltip))
tooltip.set_state(TRUE)
/client/MouseEntered(atom/hoverOn, location, control, params)
SHOULD_CALL_PARENT(TRUE)
. = ..()
tooltip.looking_at = "\ref[hoverOn]"
- if(prefs.tooltip && tooltip?.state)
+ if(prefs.get_pref(/datum/pref/player/ui/tooltip) && tooltip?.state)
var/text_in_tooltip = hoverOn.get_name()
screen |= tooltip
- tooltip.SetMapText(text_in_tooltip, prefs.tooltip_font)
+ tooltip.SetMapText(text_in_tooltip, prefs.get_pref(/datum/pref/player/ui/tooltip_font), prefs.get_pref(/datum/pref/player/ui/tooltip_size))
diff --git a/code/_onclick/hud/ui_screens/basic.dm b/code/_onclick/hud/ui_screens/basic.dm
index de6875e0ca7b..4125261573bb 100644
--- a/code/_onclick/hud/ui_screens/basic.dm
+++ b/code/_onclick/hud/ui_screens/basic.dm
@@ -395,7 +395,7 @@
screen_loc = ui_holomap
plane = HUD_PLANE
layer = HUD_LAYER
- copy_flags = HUD_COPY_ALPHA || HUD_COPY_COLOR
+ copy_flags = HUD_COPY_ALPHA | HUD_COPY_COLOR
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/atom/movable/screen/holomap/add_to_hud(datum/hud/hud)
diff --git a/code/_onclick/hud/ui_screens/complex.dm b/code/_onclick/hud/ui_screens/complex.dm
index 4ff0dd0e9e31..5cdeac05cc51 100644
--- a/code/_onclick/hud/ui_screens/complex.dm
+++ b/code/_onclick/hud/ui_screens/complex.dm
@@ -70,7 +70,7 @@
screen_loc = ui_acti
hud_slot = HUD_SLOT_MAIN
- copy_flags = HUD_COPY_ICON
+ copy_flags = HUD_COPY_ICON | HUD_COPY_ALPHA
types = list(
/atom/movable/screen/intent/help, /atom/movable/screen/intent/push,
diff --git a/code/_onclick/hud/ui_screens/inventory.dm b/code/_onclick/hud/ui_screens/inventory.dm
index 2af7885a5f07..34224c9eb77f 100644
--- a/code/_onclick/hud/ui_screens/inventory.dm
+++ b/code/_onclick/hud/ui_screens/inventory.dm
@@ -29,7 +29,7 @@
remove_stored_outline()
/atom/movable/screen/inventory/proc/add_stored_outline()
- if(!slot_id || !usr.client.prefs.outline_enabled)
+ if(!slot_id || !usr.client.prefs.get_pref(/datum/pref/player/ui/outline))
return
var/obj/item/inv_item = usr.get_item_by_slot(slot_id)
if(!inv_item)
@@ -114,7 +114,7 @@
icon_state = "craft"
screen_loc = ui_crafting
- copy_flags = NONE
+ copy_flags = HUD_COPY_ALPHA
/atom/movable/screen/inventory/craft/action()
if(check_state())
diff --git a/code/_onclick/hud/ui_screens/screen_objects.dm b/code/_onclick/hud/ui_screens/screen_objects.dm
index 08b07800c71a..0475bf02fa24 100644
--- a/code/_onclick/hud/ui_screens/screen_objects.dm
+++ b/code/_onclick/hud/ui_screens/screen_objects.dm
@@ -52,7 +52,7 @@
/atom/movable/screen/proc/update_by_hud(datum/hud/hud)
if((copy_flags & HUD_COPY_ICON) && hud.ui_style)
icon = hud.ui_style
- if((copy_flags & HUD_COPY_ALPHA) && hud.ui_alpha)
+ if((copy_flags & HUD_COPY_ALPHA) && !isnull(hud.ui_alpha)) // alpha can be 0
alpha = hud.ui_alpha
if((copy_flags & HUD_COPY_COLOR) && hud.ui_color)
color = hud.ui_color
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index afaaf5e2a345..40ba179cf9fe 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -268,7 +268,7 @@ SUBSYSTEM_DEF(job)
for(var/mob/dead/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/pref/player/meta/random_slot))
player.client.prefs.random_character()
Debug("DO, Len: [unassigned.len]")
if(unassigned.len == 0)
diff --git a/code/controllers/subsystem/preferences.dm b/code/controllers/subsystem/preferences.dm
new file mode 100644
index 000000000000..6c6c8f0b6a4f
--- /dev/null
+++ b/code/controllers/subsystem/preferences.dm
@@ -0,0 +1,18 @@
+/*SUBSYSTEM_DEF(preferences)
+ name = "Preferences"
+ wait = SS_WAIT_EXPLOSION
+ flags = SS_TICKER | SS_SHOW_IN_MC_TAB//| SS_NO_INIT
+
+ wait = SS_WAIT_PREFERENCES
+
+ var/list/processing = list()
+
+/datum/controller/subsystem/preferences/stat_entry()
+ ..("PTS:[processing.len]")
+
+/datum/controller/subsystem/preferences/mark_dirty(/datum/preferences/P)
+
+// PreShutdown & Shutdown
+
+/datum/controller/subsystem/preferences/fire(resumed = 0)
+*/
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index ef634507afb3..ac8ef62e8e77 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -301,7 +301,7 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, "Приятной игры!")
for(var/mob/M as anything in player_list)
- M.playsound_local(null, 'sound/AI/enjoyyourstay.ogg', VOL_EFFECTS_VOICE_ANNOUNCEMENT, vary = FALSE, frequency = null, ignore_environment = TRUE)
+ M.playsound_local(null, 'sound/AI/enjoyyourstay.ogg', VOL_VOICE_ANNOUNCEMENTS, vary = FALSE, frequency = null, ignore_environment = TRUE)
if(length(SSholiday.holidays))
to_chat(world, "и...")
@@ -608,7 +608,7 @@ SUBSYSTEM_DEF(ticker)
if(!config.deathmatch_arena)
return
for(var/mob/living/M in global.player_list)
- if(!M.client.prefs.eorg_enabled)
+ if(!M.client.prefs.get_pref(/datum/pref/player/game/endroundarena))
continue
spawn_gladiator(M)
@@ -618,7 +618,7 @@ SUBSYSTEM_DEF(ticker)
M.mind.transfer_to(L)
else
L.key = M.key
- L.playsound_local(null, 'sound/lobby/Thunderdome.ogg', VOL_MUSIC, vary = FALSE, frequency = null, ignore_environment = TRUE)
+ L.playsound_local(null, 'sound/lobby/Thunderdome.ogg', VOL_LOBBY_MUSIC, vary = FALSE, frequency = null, ignore_environment = TRUE)
L.equipOutfit(/datum/outfit/arena)
L.name = L.key
L.real_name = L.name
diff --git a/code/datums/announcements/_announcements.dm b/code/datums/announcements/_announcements.dm
index a08d7b3a9846..6756ef6fcac9 100644
--- a/code/datums/announcements/_announcements.dm
+++ b/code/datums/announcements/_announcements.dm
@@ -199,7 +199,7 @@ var/global/list/announcement_sounds = list(
if((sound == "emer_shut_left" || sound == "crew_shut_left") && IS_ON_ESCAPE_SHUTTLE)
continue
- M.playsound_local(null, announce_sound, VOL_EFFECTS_VOICE_ANNOUNCEMENT, volume, FALSE, null, channel = CHANNEL_ANNOUNCE, wait = TRUE)
+ M.playsound_local(null, announce_sound, VOL_VOICE_ANNOUNCEMENTS, volume, FALSE, null, channel = CHANNEL_ANNOUNCE, wait = TRUE)
if(faction_filter) // antag announce, don't print it in machinery
return
diff --git a/code/datums/browser.dm b/code/datums/browser.dm
index a02dd5fdfd27..ba3c2d1b6100 100644
--- a/code/datums/browser.dm
+++ b/code/datums/browser.dm
@@ -102,7 +102,7 @@
if(scripts.len)
send_asset_list(user, scripts)
user << browse(get_content(), "window=[window_id];[window_size][window_options]")
- winset(user, "mapwindow.map", "focus=true") // return keyboard focus to map
+ winset(user, "tcmapwindow.map", "focus=true") // return keyboard focus to map
onclose(user, window_id, ref)
/datum/browser/proc/close()
@@ -160,7 +160,7 @@
if(focusedwindow)
winset(user, focusedwindow, "focus=true")
else
- winset(user, "mapwindow", "focus=true")
+ winset(user, "tcmapwindow", "focus=true")
break
if(timeout)
addtimer(CALLBACK(src, PROC_REF(close)), timeout)
diff --git a/code/datums/components/talking_atom.dm b/code/datums/components/talking_atom.dm
index c07b0c2c2f0a..9c0a50a51613 100644
--- a/code/datums/components/talking_atom.dm
+++ b/code/datums/components/talking_atom.dm
@@ -112,7 +112,7 @@
for(var/mob/M as anything in observer_list)
if (!M.client)
continue //skip leavers
- if(M.client.prefs.chat_toggles & CHAT_GHOSTEARS)
+ if(M.client.prefs.get_pref(/datum/pref/player/chat/ghostears))
listening |= M
for(var/mob/M in listening)
diff --git a/code/datums/emotes/emote.dm b/code/datums/emotes/emote.dm
index 38aa7fc6d71e..fcfd8d6626d8 100644
--- a/code/datums/emotes/emote.dm
+++ b/code/datums/emotes/emote.dm
@@ -1,4 +1,5 @@
var/global/list/all_emotes
+var/global/list/all_emotes_keys // for emote keybinds, contains unique emote keys + "None"
var/global/list/emotes_for_emote_panel // for custom emote panel
/*
@@ -163,16 +164,17 @@ var/global/list/emotes_for_emote_panel // for custom emote panel
if(!M.client)
continue
+ var/is_in_view = FALSE
if(M in viewers(get_turf(user), world.view))
M.show_runechat_message(user, null, get_emote_message_3p(user), null, SHOWMSG_VISUAL)
+ is_in_view = TRUE
- switch(M.client.prefs.chat_ghostsight)
- if(CHAT_GHOSTSIGHT_ALL)
- // ghosts don't need to be checked for deafness, type of message, etc. So to_chat() is better here
+ // ghosts don't need to be checked for deafness, type of message, etc. So to_chat() is better here
+ if(is_in_view)
+ to_chat(M, "[FOLLOW_LINK(M, user)] [msg_3p]")
+ else if (M.client.prefs.get_pref(/datum/pref/player/chat/ghostsight))
+ if(intentional || !M.client.prefs.get_pref(/datum/pref/player/chat/ghostantispam))
to_chat(M, "[FOLLOW_LINK(M, user)] [msg_3p]")
- if(CHAT_GHOSTSIGHT_ALLMANUAL)
- if(intentional)
- to_chat(M, "[FOLLOW_LINK(M, user)] [msg_3p]")
if(cloud)
add_cloud(user)
diff --git a/code/datums/keybinding/_defines.dm b/code/datums/keybinding/_defines.dm
deleted file mode 100644
index c2868f904d36..000000000000
--- a/code/datums/keybinding/_defines.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-#define CATEGORY_CLIENT "CLIENT"
-#define CATEGORY_EMOTE "EMOTE"
-#define CATEGORY_ADMIN "ADMIN"
-#define CATEGORY_XENO "XENO"
-#define CATEGORY_CARBON "CARBON"
-#define CATEGORY_HUMAN "HUMAN"
-#define CATEGORY_ROBOT "ROBOT"
-#define CATEGORY_MISC "MISC"
-#define CATEGORY_MOVEMENT "MOVEMENT"
-#define CATEGORY_COMMUNICATION "COMMUNICATION"
-
-#define WEIGHT_HIGHEST 0
-#define WEIGHT_ADMIN 10
-#define WEIGHT_CLIENT 20
-#define WEIGHT_ROBOT 30
-#define WEIGHT_MOB 40
-#define WEIGHT_LIVING 50
-#define WEIGHT_DEAD 60
-#define WEIGHT_EMOTE 70
-#define WEIGHT_LOWEST 999
diff --git a/code/datums/keybinding/_keybindings.dm b/code/datums/keybinding/_keybindings.dm
deleted file mode 100644
index aa59d9a719c2..000000000000
--- a/code/datums/keybinding/_keybindings.dm
+++ /dev/null
@@ -1,16 +0,0 @@
-/datum/keybinding
- var/list/hotkey_keys
- var/name
- var/full_name
- var/description = ""
- var/category = CATEGORY_MISC
- var/weight = WEIGHT_LOWEST
-
-/datum/keybinding/proc/down(client/user)
- return FALSE
-
-/datum/keybinding/proc/up(client/user)
- return FALSE
-
-/datum/keybinding/proc/can_use(client/user)
- return TRUE
diff --git a/code/datums/keybinding/admin.dm b/code/datums/keybinding/admin.dm
deleted file mode 100644
index 05826768e5ac..000000000000
--- a/code/datums/keybinding/admin.dm
+++ /dev/null
@@ -1,107 +0,0 @@
-/datum/keybinding/admin
- category = CATEGORY_ADMIN
- weight = WEIGHT_ADMIN
-
-/datum/keybinding/admin/can_use(client/user)
- return user.holder ? TRUE : FALSE
-
-/datum/keybinding/admin/admin_say
- hotkey_keys = list("F5")
- name = "admin_say"
- full_name = "Admin say"
- description = "Talk with other admins."
-
-/datum/keybinding/admin/admin_say/down(client/user)
- user.get_admin_say()
- return TRUE
-
-/datum/keybinding/admin/admin_ghost
- hotkey_keys = list("None")
- name = "admin_ghost"
- full_name = "Aghost"
- description = "Go ghost"
-
-/datum/keybinding/admin/admin_ghost/down(client/user)
- user.admin_ghost()
- return TRUE
-
-/datum/keybinding/admin/player_panel_new
- hotkey_keys = list("F6")
- name = "player_panel_new"
- full_name = "Player Panel New"
- description = "Opens up the new player panel"
-
-/datum/keybinding/admin/player_panel_new/down(client/user)
- user.holder.player_panel_new()
- return TRUE
-
-/datum/keybinding/admin/toggle_buildmode_self
- hotkey_keys = list("F7")
- name = "toggle_buildmode_self"
- full_name = "Toggle Buildmode Self"
- description = "Toggles buildmode"
-
-/datum/keybinding/admin/toggle_buildmode_self/down(client/user)
- user.togglebuildmodeself()
- return TRUE
-
-/datum/keybinding/admin/stealthmode
- hotkey_keys = list("CtrlF8")
- name = "stealth_mode"
- full_name = "Stealth mode"
- description = "Enters stealth mode"
-
-/datum/keybinding/admin/stealthmode/down(client/user)
- user.stealth()
- return TRUE
-
-/datum/keybinding/admin/invisimin
- hotkey_keys = list("F8")
- name = "invisimin"
- full_name = "Admin invisibility"
- description = "Toggles ghost-like invisibility (Don't abuse this)"
-
-/datum/keybinding/admin/invisimin/down(client/user)
- user.invisimin()
- return TRUE
-
-/datum/keybinding/admin/deadsay
- hotkey_keys = list("F10")
- name = "dsay"
- full_name = "deadsay"
- description = "Allows you to send a message to dead chat"
-
-/datum/keybinding/admin/deadsay/down(client/user)
- user.get_dead_say()
- return TRUE
-
-/datum/keybinding/admin/deadmin
- hotkey_keys = list("None")
- name = "deadmin"
- full_name = "Deadmin"
- description = "Shed your admin powers"
-
-/datum/keybinding/admin/deadmin/down(client/user)
- user.deadmin_self()
- return TRUE
-
-/datum/keybinding/admin/readmin
- hotkey_keys = list("None")
- name = "readmin"
- full_name = "Readmin"
- description = "Regain your admin powers"
-
-/datum/keybinding/admin/readmin/down(client/user)
- user.readmin_self()
- return TRUE
-
-
-/datum/keybinding/admin/toggle_combo_hud
- hotkey_keys = list("None")
- name = "toggle_combo_hud"
- full_name = "Toggle Combo HUD"
- description = ""
-
-/datum/keybinding/admin/toggle_combo_hud/down(client/user)
- user.toggle_combo_hud()
- return TRUE
diff --git a/code/datums/keybinding/carbon.dm b/code/datums/keybinding/carbon.dm
deleted file mode 100644
index 2da5e0cf33b9..000000000000
--- a/code/datums/keybinding/carbon.dm
+++ /dev/null
@@ -1,41 +0,0 @@
-/datum/keybinding/carbon
- category = CATEGORY_CARBON
- weight = WEIGHT_MOB
-
-/datum/keybinding/carbon/can_use(client/user)
- return iscarbon(user.mob)
-
-/datum/keybinding/carbon/toggle_throw_mode
- hotkey_keys = list("R", "Southwest") // PAGEDOWN
- name = "toggle_throw_mode"
- full_name = "Toggle throw mode"
- description = "Toggle throwing the current item or not."
-
-/datum/keybinding/carbon/toggle_throw_mode/down(client/user)
- var/mob/living/carbon/C = user.mob
- C.toggle_throw_mode()
- return TRUE
-
-
-/datum/keybinding/carbon/give
- hotkey_keys = list("None")
- name = "Give_Item"
- full_name = "Give item"
- description = "Give the item you're currently holding"
-
-/datum/keybinding/carbon/give/down(client/user)
- var/mob/living/carbon/C = user.mob
- C.give()
- return TRUE
-
-
-/datum/keybinding/carbon/crawl
- hotkey_keys = list("None")
- name = "crawl"
- full_name = "Crawl"
- description = ""
-
-/datum/keybinding/carbon/crawl/down(client/user)
- var/mob/living/carbon/C = user.mob
- C.crawl()
- return TRUE
diff --git a/code/datums/keybinding/client.dm b/code/datums/keybinding/client.dm
deleted file mode 100644
index 698b989453d2..000000000000
--- a/code/datums/keybinding/client.dm
+++ /dev/null
@@ -1,54 +0,0 @@
-/datum/keybinding/client
- category = CATEGORY_CLIENT
- weight = WEIGHT_HIGHEST
-
-
-/datum/keybinding/client/admin_help
- hotkey_keys = list("F1")
- name = "admin_help"
- full_name = "Admin Help"
- description = "Ask an admin for help."
-
-/datum/keybinding/client/admin_help/down(client/user)
- user.adminhelp()
- return TRUE
-
-/datum/keybinding/client/mentor_help
- hotkey_keys = list("F9")
- name = "mentor_help"
- full_name = "Mentor Help"
- description = "Ask an mentors for help."
-
-/datum/keybinding/client/mentor_help/down(client/user)
- user.get_mentorhelp()
- return TRUE
-
-/datum/keybinding/client/screenshot
- hotkey_keys = list("None")
- name = "screenshot"
- full_name = "Screenshot"
- description = "Take a screenshot."
-
-/datum/keybinding/client/screenshot/down(client/user)
- winset(user, null, "command=.screenshot [!user.keys_held["shift"] ? "auto" : ""]")
- return TRUE
-
-/datum/keybinding/client/minimal_hud
- hotkey_keys = list("F12")
- name = "minimal_hud"
- full_name = "Minimal HUD"
- description = "Hide most HUD features"
-
-/datum/keybinding/client/minimal_hud/down(client/user)
- user.mob.button_pressed_F12()
- return TRUE
-
-/datum/keybinding/client/toggle_fullscreen
- hotkey_keys = list("F11")
- name = "toggle_fullscreen"
- full_name = "Toggle Fullscreen"
- description = "Toggle Fullscreen"
-
-/datum/keybinding/client/toggle_fullscreen/down(client/user)
- user.toggle_fullscreen()
- return TRUE
diff --git a/code/datums/keybinding/communication.dm b/code/datums/keybinding/communication.dm
deleted file mode 100644
index cd51c1fc196d..000000000000
--- a/code/datums/keybinding/communication.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/datum/keybinding/client/communication
- category = CATEGORY_COMMUNICATION
-
-/datum/keybinding/client/communication/say
- hotkey_keys = list("F3", "T")
- name = "Say"
- full_name = "IC Say"
-
-/datum/keybinding/client/communication/say/down(client/user)
- user.mob.say_wrapper()
- return TRUE
-
-/datum/keybinding/client/communication/ooc
- hotkey_keys = list("F2", "O")
- name = "OOC"
- full_name = "Out Of Character Say (OOC)"
-
-/datum/keybinding/client/communication/ooc/down(client/user)
- user.ooc_wrapper()
- return TRUE
-
-/datum/keybinding/client/communication/looc
- hotkey_keys = list("L")
- name = "LOOC"
- full_name = "Local Out Of Character Say (LOOC)"
-
-/datum/keybinding/client/communication/looc/down(client/user)
- user.looc_wrapper()
- return TRUE
-
-/datum/keybinding/client/communication/me
- hotkey_keys = list("F4", "M")
- name = "Me"
- full_name = "Custom Emote (/Me)"
-
-/datum/keybinding/client/communication/me/down(client/user)
- user.mob.me_wrapper()
- return TRUE
diff --git a/code/datums/keybinding/human.dm b/code/datums/keybinding/human.dm
deleted file mode 100644
index 76b01548bf6c..000000000000
--- a/code/datums/keybinding/human.dm
+++ /dev/null
@@ -1,49 +0,0 @@
-/datum/keybinding/human
- category = CATEGORY_HUMAN
- weight = WEIGHT_MOB
-
-/datum/keybinding/human/can_use(client/user)
- return ishuman(user.mob)
-
-/datum/keybinding/human/quick_equip
- hotkey_keys = list("E")
- name = "quick_equip"
- full_name = "Quick Equip"
- description = "Quickly puts an item in the best slot available"
-
-/datum/keybinding/human/quick_equip/down(client/user)
- var/mob/living/carbon/human/H = user.mob
- H.quick_equip()
- return TRUE
-
-/datum/keybinding/human/holster
- hotkey_keys = list("H")
- name = "holster"
- full_name = "Holster"
- description = "Draw or holster weapon."
-
-/datum/keybinding/human/holster/down(client/user)
- var/mob/living/carbon/human/H = user.mob
- H.holster_weapon()
-
-/datum/keybinding/human/emote_panel
- hotkey_keys = list("J")
- name = "emote_panel"
- full_name = "Emote Panel"
- description = "Shows you emote panel."
-
-/datum/keybinding/human/emote_panel/down(client/user)
- var/mob/living/carbon/human/H = user.mob
- H.emote_panel()
-
-/datum/keybinding/human/race_ability
- hotkey_keys = list("U")
- name = "race_ability"
- full_name = "Race Ability"
- description = "Activates your racial ability."
-
-/datum/keybinding/human/race_ability/down(client/user)
- var/mob/living/carbon/human/H = user.mob
- var/datum/action/A = locate(H.species.race_ability) in H.actions
- if(A)
- A.Trigger()
diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm
deleted file mode 100644
index 089cd8990456..000000000000
--- a/code/datums/keybinding/living.dm
+++ /dev/null
@@ -1,115 +0,0 @@
-
-/datum/keybinding/living
- category = CATEGORY_HUMAN
- weight = WEIGHT_MOB
-
-/datum/keybinding/living/can_use(client/user)
- return isliving(user.mob)
-
-/datum/keybinding/living/resist
- hotkey_keys = list("N")
- name = "resist"
- full_name = "Resist"
- description = "Break free of your current state. Handcuffed? On fire? Resist!"
-
-/datum/keybinding/living/resist/down(client/user)
- var/mob/living/L = user.mob
- L.resist()
- return TRUE
-
-/datum/keybinding/living/toggle_move_intent
- hotkey_keys = list("C")
- name = "toggle_move_intent"
- full_name = "Hold to toggle move intent"
- description = "Held down to cycle to the other move intent, release to cycle back"
-
-/datum/keybinding/living/toggle_move_intent/down(client/user)
- var/mob/living/L = user.mob
- L.set_m_intent(L.m_intent == MOVE_INTENT_WALK ? MOVE_INTENT_RUN : MOVE_INTENT_WALK)
- return TRUE
-
-/datum/keybinding/living/toggle_move_intent/up(client/user)
- var/mob/living/L = user.mob
- L.set_m_intent(L.m_intent == MOVE_INTENT_WALK ? MOVE_INTENT_RUN : MOVE_INTENT_WALK)
- return TRUE
-
-/datum/keybinding/living/drop_item
- hotkey_keys = list("Q", "Northwest") // HOME
- name = "drop_item"
- full_name = "Drop Item"
- description = ""
-
-/datum/keybinding/living/drop_item/down(client/user)
- var/mob/living/L = user.mob
- L.drop_item()
- return TRUE
-
-/datum/keybinding/living/crawl
- hotkey_keys = list("None")
- name = "crawl"
- full_name = "Crawl"
- description = "You lay down/get up"
-
-/datum/keybinding/living/crawl/down(client/user)
- var/mob/living/L = user.mob
- L.crawl()
- return TRUE
-
-/datum/keybinding/living/swap_hands
- hotkey_keys = list("X", "Northeast") // PAGEUP
- name = "swap_hands"
- full_name = "Swap hands"
- description = ""
-
-/datum/keybinding/living/swap_hands/down(client/user)
- var/mob/living/L = user.mob
- L.swap_hand()
- return TRUE
-
-/datum/keybinding/living/select_help_intent
- hotkey_keys = list("1")
- name = "select_help_intent"
- full_name = "Select help intent"
- description = ""
-
-/datum/keybinding/living/select_help_intent/down(client/user)
- if(issilicon(user.mob))
- return
- user.mob?.a_intent_change(INTENT_HELP)
- return TRUE
-
-/datum/keybinding/living/select_disarm_intent
- hotkey_keys = list("2")
- name = "select_push_intent"
- full_name = "Select push intent"
- description = ""
-
-/datum/keybinding/living/select_disarm_intent/down(client/user)
- if(issilicon(user.mob))
- return
- user.mob?.a_intent_change(INTENT_PUSH)
- return TRUE
-
-/datum/keybinding/living/select_grab_intent
- hotkey_keys = list("3")
- name = "select_grab_intent"
- full_name = "Select grab intent"
- description = ""
-
-/datum/keybinding/living/select_grab_intent/down(client/user)
- if(issilicon(user.mob))
- return
- user.mob?.a_intent_change(INTENT_GRAB)
- return TRUE
-
-/datum/keybinding/living/select_harm_intent
- hotkey_keys = list("4")
- name = "select_harm_intent"
- full_name = "Select harm intent"
- description = ""
-
-/datum/keybinding/living/select_harm_intent/down(client/user)
- if(issilicon(user.mob))
- return
- user.mob?.a_intent_change(INTENT_HARM)
- return TRUE
diff --git a/code/datums/keybinding/mob.dm b/code/datums/keybinding/mob.dm
deleted file mode 100644
index f34749cb67d4..000000000000
--- a/code/datums/keybinding/mob.dm
+++ /dev/null
@@ -1,142 +0,0 @@
-/datum/keybinding/mob
- category = CATEGORY_HUMAN
- weight = WEIGHT_MOB
-
-/datum/keybinding/mob/stop_pulling
- hotkey_keys = list("Delete")
- name = "stop_pulling"
- full_name = "Stop pulling"
- description = ""
-
-/datum/keybinding/mob/stop_pulling/down(client/user)
- var/mob/M = user.mob
- if(!M.pulling)
- to_chat(user, "You are not pulling anything.")
- else
- M.stop_pulling()
- return TRUE
-
-/datum/keybinding/mob/cycle_intent_right
- hotkey_keys = list("G", "Insert")
- name = "cycle_intent_right"
- full_name = "Cycle intent right"
- description = ""
-
-/datum/keybinding/mob/cycle_intent_right/down(client/user)
- var/mob/M = user.mob
- M.a_intent_change(INTENT_HOTKEY_RIGHT)
- return TRUE
-
-/datum/keybinding/mob/cycle_intent_left
- hotkey_keys = list("F")
- name = "cycle_intent_left"
- full_name = "Cycle intent left"
- description = ""
-
-/datum/keybinding/mob/cycle_intent_left/down(client/user)
- var/mob/M = user.mob
- M.a_intent_change(INTENT_HOTKEY_LEFT)
- return TRUE
-
-/datum/keybinding/mob/activate_inhand
- hotkey_keys = list("Z", "Y","Southeast") // Southeast = PAGEDOWN
- name = "activate_inhand"
- full_name = "Activate in-hand"
- description = "Uses whatever item you have inhand"
-
-/datum/keybinding/mob/activate_inhand/down(client/user)
- var/mob/M = user.mob
- M.mode()
- return TRUE
-
-/datum/keybinding/mob/target_head_cycle
- hotkey_keys = list("Numpad8")
- name = "target_head_cycle"
- full_name = "Target: Cycle head"
- description = ""
-
-/datum/keybinding/mob/target_head_cycle/down(client/user)
- user.body_toggle_head()
- return TRUE
-
-/datum/keybinding/mob/target_r_arm
- hotkey_keys = list("Numpad4")
- name = "target_r_arm"
- full_name = "Target: right arm"
- description = ""
-
-/datum/keybinding/mob/target_r_arm/down(client/user)
- user.body_r_arm()
- return TRUE
-
-/datum/keybinding/mob/target_body_chest
- hotkey_keys = list("Numpad5")
- name = "target_body_chest"
- full_name = "Target: Body"
- description = ""
-
-/datum/keybinding/mob/target_body_chest/down(client/user)
- user.body_chest()
- return TRUE
-
-/datum/keybinding/mob/target_left_arm
- hotkey_keys = list("Numpad6")
- name = "target_left_arm"
- full_name = "Target: left arm"
- description = ""
-
-/datum/keybinding/mob/target_left_arm/down(client/user)
- user.body_l_arm()
- return TRUE
-
-/datum/keybinding/mob/target_right_leg
- hotkey_keys = list("Numpad1")
- name = "target_right_leg"
- full_name = "Target: Right leg"
- description = ""
-
-/datum/keybinding/mob/target_right_leg/down(client/user)
- user.body_r_leg()
- return TRUE
-
-/datum/keybinding/mob/target_body_groin
- hotkey_keys = list("Numpad2")
- name = "target_body_groin"
- full_name = "Target: Groin"
- description = ""
-
-/datum/keybinding/mob/target_body_groin/down(client/user)
- user.body_groin()
- return TRUE
-
-/datum/keybinding/mob/target_left_leg
- hotkey_keys = list("Numpad3")
- name = "target_left_leg"
- full_name = "Target: left leg"
- description = ""
-
-/datum/keybinding/mob/target_left_leg/down(client/user)
- user.body_l_leg()
- return TRUE
-
-/datum/keybinding/mob/prevent_movement
- hotkey_keys = list("Ctrl")
- name = "block_movement"
- full_name = "Block movement"
- description = "Prevents you from moving"
-
-/datum/keybinding/mob/prevent_movement/down(client/user)
- user.movement_locked = TRUE
-
-/datum/keybinding/mob/prevent_movement/up(client/user)
- user.movement_locked = FALSE
-
-/datum/keybinding/mob/click_on_self
- hotkey_keys = list("B")
- name = "click_on_self"
- full_name = "Click On Self"
- description = ""
-
-/datum/keybinding/mob/click_on_self/down(client/user)
- var/mob/M = user.mob
- M.click_on_self()
diff --git a/code/datums/keybinding/movement.dm b/code/datums/keybinding/movement.dm
deleted file mode 100644
index efa1b556d22e..000000000000
--- a/code/datums/keybinding/movement.dm
+++ /dev/null
@@ -1,27 +0,0 @@
-/datum/keybinding/movement
- category = CATEGORY_MOVEMENT
- weight = WEIGHT_HIGHEST
-
-/datum/keybinding/movement/north
- hotkey_keys = list("W", "North")
- name = "North"
- full_name = "Move North"
- description = "Moves your character north"
-
-/datum/keybinding/movement/south
- hotkey_keys = list("S", "South")
- name = "South"
- full_name = "Move South"
- description = "Moves your character south"
-
-/datum/keybinding/movement/west
- hotkey_keys = list("A", "West")
- name = "West"
- full_name = "Move West"
- description = "Moves your character left"
-
-/datum/keybinding/movement/east
- hotkey_keys = list("D", "East")
- name = "East"
- full_name = "Move East"
- description = "Moves your character east"
diff --git a/code/datums/keybinding/robot.dm b/code/datums/keybinding/robot.dm
deleted file mode 100644
index 686b42249868..000000000000
--- a/code/datums/keybinding/robot.dm
+++ /dev/null
@@ -1,62 +0,0 @@
-/datum/keybinding/robot
- category = CATEGORY_ROBOT
- weight = WEIGHT_ROBOT
-
-/datum/keybinding/robot/can_use(client/user)
- return isrobot(user.mob)
-
-/datum/keybinding/robot/moduleone
- hotkey_keys = list("1")
- name = "module_one"
- full_name = "Toggle module 1"
- description = "Equips or unequips the first module"
-
-/datum/keybinding/robot/moduleone/down(client/user)
- var/mob/living/silicon/robot/R = user.mob
- R.toggle_module(1)
- return TRUE
-
-/datum/keybinding/robot/moduletwo
- hotkey_keys = list("2")
- name = "module_two"
- full_name = "Toggle module 2"
- description = "Equips or unequips the second module"
-
-/datum/keybinding/robot/moduletwo/down(client/user)
- var/mob/living/silicon/robot/R = user.mob
- R.toggle_module(2)
- return TRUE
-
-/datum/keybinding/robot/modulethree
- hotkey_keys = list("3")
- name = "module_three"
- full_name = "Toggle module 3"
- description = "Equips or unequips the third module"
-
-/datum/keybinding/robot/modulethree/down(client/user)
- var/mob/living/silicon/robot/R = user.mob
- R.toggle_module(3)
- return TRUE
-
-/datum/keybinding/robot/intent_cycle
- hotkey_keys = list("4")
- name = "cycle_intent"
- full_name = "Cycle intent left"
- description = "Cycles the intent left"
-
-/datum/keybinding/robot/intent_cycle/down(client/user)
- var/mob/living/silicon/robot/R = user.mob
- R.a_intent_change(INTENT_HOTKEY_LEFT)
- return TRUE
-
-/datum/keybinding/robot/unequip_module
- hotkey_keys = list("Q")
- name = "unequip_module"
- full_name = "Unequip module"
- description = "Unequips the active module"
-
-/datum/keybinding/robot/unequip_module/down(client/user)
- var/mob/living/silicon/robot/R = user.mob
- if(R.module)
- R.uneq_active()
- return TRUE
diff --git a/code/datums/lighting/level_light.dm b/code/datums/lighting/level_light.dm
index b3c2c1a29b58..111b2702a8cb 100644
--- a/code/datums/lighting/level_light.dm
+++ b/code/datums/lighting/level_light.dm
@@ -35,7 +35,7 @@ var/global/list/datum/level_lighting_effect/lighting_effects
/datum/level_lighting_effect/random_aurora/New(duration = 60 SECONDS)
var/transitions = ceil(duration/transition_delay)
for(var/i in 1 to transitions)
- colors += list(color_lightness_max(random_color(), 0.70))
+ colors += list(color_lightness_max(random_color(), 70))
/* Planetary lighting */
/datum/level_lighting_effect/snow_map_random
diff --git a/code/datums/music_player.dm b/code/datums/music_player.dm
index 1e3ed0bd3c07..35b939f49c67 100644
--- a/code/datums/music_player.dm
+++ b/code/datums/music_player.dm
@@ -276,7 +276,7 @@ var/global/datum/notes_storage/note_cache_storage = new
var/sound/S = global.note_cache_storage.instrument_sound_notes["[sound_path]/[current_note]"]
if(!S)
S = global.note_cache_storage.instrument_sound_notes["[sound_path]/[current_note]"] = sound("[sound_path]/[current_note].ogg")
- playsound(instrument, S, VOL_EFFECTS_INSTRUMENT, volume, FALSE, null, null, falloff = 5)
+ playsound(instrument, S, VOL_MUSIC_INSTRUMENTS, volume, FALSE, null, null, falloff = 5)
var/pause_time = COUNT_PAUSE(song_tempo)
diff --git a/code/datums/runechat.dm b/code/datums/runechat.dm
index 6a36e7f27d95..5b23737fa229 100644
--- a/code/datums/runechat.dm
+++ b/code/datums/runechat.dm
@@ -226,7 +226,7 @@
if(!speaker || isobserver(speaker))
return
- if(!client || !client.prefs.show_runechat)
+ if(!client || !client.prefs.get_pref(/datum/pref/player/ui/runechat))
return
if(SSlag_switch.measures[DISABLE_RUNECHAT] && !HAS_TRAIT(speaker, TRAIT_BYPASS_MEASURES))
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 37ef32c74c87..7feeed7e85ca 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -18,7 +18,7 @@
return
RAI.next_music_start = world.time + REPLICATOR_MUSIC_LENGTH
- mob_viewer.playsound_local(null, 'sound/music/storm_resurrection.ogg', VOL_MUSIC, null, null, CHANNEL_MUSIC, vary = FALSE, frequency = null, ignore_environment = TRUE)
+ mob_viewer.playsound_local(null, 'sound/music/storm_resurrection.ogg', VOL_AMBIENT, null, null, CHANNEL_MUSIC, vary = FALSE, frequency = null, ignore_environment = TRUE)
/datum/status_effect/swarm_gift
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index e677fa876c7c..ee01d0f86d38 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -451,10 +451,10 @@
/client/var/list/image/outlined_item = list()
/atom/movable/proc/apply_outline(color)
- if(anchored || !usr.client.prefs.outline_enabled)
+ if(anchored || !usr.client.prefs.get_pref(/datum/pref/player/ui/outline))
return
if(!color)
- color = usr.client.prefs.outline_color || COLOR_BLUE_LIGHT
+ color = usr.client.prefs.get_pref(/datum/pref/player/ui/outline_color) || COLOR_BLUE_LIGHT
if(usr.client.outlined_item[src])
return
diff --git a/code/game/gamemodes/factions/replicators.dm b/code/game/gamemodes/factions/replicators.dm
index 3d534446f26e..b16a9779a4f4 100644
--- a/code/game/gamemodes/factions/replicators.dm
+++ b/code/game/gamemodes/factions/replicators.dm
@@ -272,7 +272,7 @@ Message ends."}
return
RAI.next_music_start = world.time + REPLICATOR_MUSIC_LENGTH
- R.playsound_local(null, 'sound/music/storm_resurrection.ogg', VOL_MUSIC, null, null, CHANNEL_MUSIC, vary = FALSE, frequency = null, ignore_environment = TRUE)
+ R.playsound_local(null, 'sound/music/storm_resurrection.ogg', VOL_AMBIENT, null, null, CHANNEL_MUSIC, vary = FALSE, frequency = null, ignore_environment = TRUE)
/datum/faction/replicators/proc/victory_animation(turf/T)
SSticker.explosion_in_progress = TRUE
diff --git a/code/game/gamemodes/modes_gameplays/cult/narsie.dm b/code/game/gamemodes/modes_gameplays/cult/narsie.dm
index 2ff14fd2cfb1..f72251940754 100644
--- a/code/game/gamemodes/modes_gameplays/cult/narsie.dm
+++ b/code/game/gamemodes/modes_gameplays/cult/narsie.dm
@@ -34,7 +34,7 @@
for(var/mob/M in player_list)
if(!isnewplayer(M))
to_chat(M, "Н́̿̚Ӓ́̈́Р̔̚͘-̽̔͆С̈́͛͛И̓͊̕ В͒̚͝О̓͒̓С̓̾͑С̔̓͝Т̈́͘̚А͒͑͘Л͐͌̾")
- M.playsound_local(null, pick('sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg'), VOL_EFFECTS_VOICE_ANNOUNCEMENT, vary = FALSE, frequency = null, ignore_environment = TRUE)
+ M.playsound_local(null, pick('sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg'), VOL_VOICE_ANNOUNCEMENTS, vary = FALSE, frequency = null, ignore_environment = TRUE)
if(!iscultist(M))
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "narsie", /datum/mood_event/narsie)
else
diff --git a/code/game/gamemodes/modes_gameplays/cult/rune_datum.dm b/code/game/gamemodes/modes_gameplays/cult/rune_datum.dm
index 685126a7a56c..e1b2a0d6c848 100644
--- a/code/game/gamemodes/modes_gameplays/cult/rune_datum.dm
+++ b/code/game/gamemodes/modes_gameplays/cult/rune_datum.dm
@@ -268,7 +268,7 @@
var/turf = get_turf(all_items[i])
var/list/viewing = list()
for(var/mob/M in viewers(turf))
- if(M.client && (M.client.prefs.toggles & SHOW_ANIMATIONS))
+ if(M.client?.prefs.get_pref(/datum/pref/player/game/melee_animation))
viewing |= M.client
var/image/I = image(uristrune_cache[pick(uristrune_cache)], turf, layer = FLY_LAYER)
diff --git a/code/game/gamemodes/modes_gameplays/ninja/suit/SpiderOS/topic.dm b/code/game/gamemodes/modes_gameplays/ninja/suit/SpiderOS/topic.dm
index b98f84d03025..cd1ba26fdbc3 100644
--- a/code/game/gamemodes/modes_gameplays/ninja/suit/SpiderOS/topic.dm
+++ b/code/game/gamemodes/modes_gameplays/ninja/suit/SpiderOS/topic.dm
@@ -71,10 +71,11 @@
useMS.send_pda_message("[P.owner]",sender,"[t]")
for(var/mob/M in player_list)
- if(M.stat == DEAD && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTEARS)) // src.client is so that ghosts don't have to listen to mice
- if(isnewplayer(M))
- continue
- to_chat(M, "PDA Message - [U] -> [P.owner]: [t]")
+ if(M.stat != DEAD || isnewplayer(M))
+ continue
+ if(!M.client.prefs.get_pref(/datum/pref/player/chat/ghostears))
+ continue
+ to_chat(M, "PDA Message - [U] -> [P.owner]: [t]")
if (!P.message_silent)
playsound(P, 'sound/machines/twobeep.ogg', VOL_EFFECTS_MASTER)
diff --git a/code/game/gamemodes/modes_gameplays/nuclear/nuclearbomb.dm b/code/game/gamemodes/modes_gameplays/nuclear/nuclearbomb.dm
index 664b93448826..1cb6116ffd92 100644
--- a/code/game/gamemodes/modes_gameplays/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/modes_gameplays/nuclear/nuclearbomb.dm
@@ -51,7 +51,7 @@ var/global/bomb_set
else
bomb_set = TRUE //So long as there is one nuke timing, it means one nuke is armed.
timeleft = max(timeleft - 2, 0) // 2 seconds per process()
- playsound(src, 'sound/items/timer.ogg', VOL_EFFECTS_MISC, 30, FALSE)
+ playsound(src, 'sound/items/timer.ogg', VOL_SPAM_EFFECTS, 30, FALSE)
if(timeleft <= 120 && COOLDOWN_FINISHED(global, nuclear_siren_cooldown))
for(var/mob/M in player_list)
if(!isnewplayer(M))
@@ -469,7 +469,7 @@ var/global/bomb_set
playsound(src, 'sound/machines/Alarm.ogg', VOL_EFFECTS_MASTER, null, FALSE, null, 30)
for(var/mob/M in player_list)
if(!isnewplayer(M))
- M.playsound_local(null, 'sound/machines/Alarm_reverb.ogg', VOL_EFFECTS_VOICE_ANNOUNCEMENT, vary = FALSE, frequency = null, ignore_environment = TRUE)
+ M.playsound_local(null, 'sound/machines/Alarm_reverb.ogg', VOL_VOICE_ANNOUNCEMENTS, vary = FALSE, frequency = null, ignore_environment = TRUE)
update_icon()
addtimer(CALLBACK(src, PROC_REF(fail)), 13 SECONDS) //Good taste, right?
diff --git a/code/game/gamemodes/roles/changeling.dm b/code/game/gamemodes/roles/changeling.dm
index 5313c870c62e..0122a2056ae2 100644
--- a/code/game/gamemodes/roles/changeling.dm
+++ b/code/game/gamemodes/roles/changeling.dm
@@ -206,6 +206,6 @@
for(var/mob/M in player_list)
if(!isnewplayer(M))
to_chat(M, "A terrible roar is coming from somewhere around the station.")
- M.playsound_local(null, 'sound/antag/abomination_start.ogg', VOL_EFFECTS_VOICE_ANNOUNCEMENT, vary = FALSE, frequency = null, ignore_environment = TRUE)
+ M.playsound_local(null, 'sound/antag/abomination_start.ogg', VOL_VOICE_ANNOUNCEMENTS, vary = FALSE, frequency = null, ignore_environment = TRUE)
#undef OVEREATING_AMOUNT
diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm
index 0297c3de3760..7ae5a0d366d8 100644
--- a/code/game/machinery/telecomms/broadcaster.dm
+++ b/code/game/machinery/telecomms/broadcaster.dm
@@ -306,14 +306,14 @@ var/global/message_delay = 0 // To make sure restarting the recentmessages list
/* --- Loop through the receivers and categorize them --- */
- if (R.client && !(R.client.prefs.chat_toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
+ if (R.client && !R.client.prefs.get_pref(/datum/pref/player/chat/radio)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
continue
if(isnewplayer(R)) // we don't want new players to hear messages. rare but generates runtimes.
continue
// Ghosts hearing all radio chat don't want to hear syndicate intercepts, they're duplicates
- if(data == BROADCAST_MODE_SYNDICATE && isobserver(R) && R.client && (R.client.prefs.chat_toggles & CHAT_GHOSTRADIO))
+ if(data == BROADCAST_MODE_SYNDICATE && isobserver(R) && R.client && R.client.prefs.get_pref(/datum/pref/player/chat/ghostradio))
continue
// --- Check for compression ---
@@ -586,7 +586,7 @@ var/global/message_delay = 0 // To make sure restarting the recentmessages list
/* --- Loop through the receivers and categorize them --- */
- if (R.client && !(R.client.prefs.chat_toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
+ if (R.client && !R.client.prefs.get_pref(/datum/pref/player/chat/radio)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
continue
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index ce34416ce86c..e18e48c5d507 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -1564,10 +1564,12 @@
tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]")))
P.tnote.Add(list(list("sent" = 0, "owner" = "[owner]", "job" = "[ownjob]", "message" = "[t]", "target" = "\ref[src]")))
for(var/mob/M in player_list)
- if(M.stat == DEAD && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTEARS)) // src.client is so that ghosts don't have to listen to mice
- if(isnewplayer(M))
- continue
- to_chat(M, "PDA Message - [owner] -> [P.owner]: [t]")
+ if(M.stat != DEAD || isnewplayer(M))
+ continue
+ if(!M.client.prefs.get_pref(/datum/pref/player/chat/ghostears))
+ continue
+
+ to_chat(M, "PDA Message - [owner] -> [P.owner]: [t]")
if(!conversations.Find("\ref[P]"))
conversations.Add("\ref[P]")
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 8db7f4cc6880..83a3408cf1a2 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -471,7 +471,7 @@ var/global/GLOBAL_RADIO_TYPE = 1 // radio type to use
var/list/heard_garbled = list() // garbled message
for (var/mob/R in receive)
- if (R.client && !(R.client.prefs.chat_toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
+ if (R.client && !R.client.prefs.get_pref(/datum/pref/player/chat/radio)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
continue
if (R.say_understands(M))
if (ishuman(M) && M.GetVoice() != M.real_name)
diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm
index 63944c940776..cb168766b824 100644
--- a/code/game/objects/items/weapons/clown_items.dm
+++ b/code/game/objects/items/weapons/clown_items.dm
@@ -216,7 +216,7 @@
var/cooldown = FALSE
/obj/item/weapon/bikehorn/proc/honk(mob/user)
- playsound(src, 'sound/items/bikehorn.ogg', VOL_EFFECTS_MISC)
+ playsound(src, 'sound/items/bikehorn.ogg', VOL_SPAM_EFFECTS)
if(user.can_waddle())
user.waddle(pick(-14, 0, 14), 4)
diff --git a/code/game/objects/items/weapons/hydroponics.dm b/code/game/objects/items/weapons/hydroponics.dm
index 42abbac48010..e84fc24da4c8 100644
--- a/code/game/objects/items/weapons/hydroponics.dm
+++ b/code/game/objects/items/weapons/hydroponics.dm
@@ -243,7 +243,7 @@ var/global/gourd_name = null
if(!COOLDOWN_FINISHED(src, last_maraca))
return
COOLDOWN_START(src, last_maraca, MARACA_COOLDOWN)
- playsound(src, 'sound/musical_instruments/maraca/maraca.ogg', VOL_EFFECTS_INSTRUMENT, 100, TRUE, falloff = 5)
+ playsound(src, 'sound/musical_instruments/maraca/maraca.ogg', VOL_MUSIC_INSTRUMENTS, 100, TRUE, falloff = 5)
#undef MARACA_COOLDOWN
diff --git a/code/game/objects/structures/chapel.dm b/code/game/objects/structures/chapel.dm
index 8412989331fa..2ccb54014dc4 100644
--- a/code/game/objects/structures/chapel.dm
+++ b/code/game/objects/structures/chapel.dm
@@ -207,7 +207,7 @@ ADD_TO_GLOBAL_LIST(/obj/effect/effect/bell, bells)
for(var/mob/M in player_list)
if(M.z == z)
// Why do they call them voice announcements if it's just global announcements?
- M.playsound_local(null, 'sound/effects/big_bell.ogg', VOL_EFFECTS_VOICE_ANNOUNCEMENT, 75)
+ M.playsound_local(null, 'sound/effects/big_bell.ogg', VOL_VOICE_ANNOUNCEMENTS, 75)
to_chat(M, "[bicon(src)] [src] rings, \"[text]\"")
var/swing_angle = adjust_strength(12, strength, 0.25, 32)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 39b127d2e516..0878c888af18 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -167,7 +167,7 @@ voluminosity = if FALSE, removes the difference between left and right ear.
/mob/proc/playsound_lobbymusic()
if(!SSticker || !SSticker.login_music || !client)
return
- playsound_music(SSticker.login_music, VOL_MUSIC, null, null, CHANNEL_MUSIC) // MAD JAMS
+ playsound_music(SSticker.login_music, VOL_LOBBY_MUSIC, null, null, CHANNEL_MUSIC) // MAD JAMS
/mob/proc/playsound_music(soundin, volume_channel = NONE, repeat = FALSE, wait = FALSE, channel = 0, priority = 0, status = 0) // byond vars sorted by ref order.
if(!client || !client.prefs_ready)
@@ -180,7 +180,7 @@ voluminosity = if FALSE, removes the difference between left and right ear.
but still keep ability to resume admin music on the fly mid position
*/
- if(!vol && volume_channel != VOL_ADMIN)
+ if(!vol && volume_channel != VOL_ADMIN_SOUNDS)
return
var/sound/S
@@ -222,248 +222,28 @@ voluminosity = if FALSE, removes the difference between left and right ear.
if(!isnum(volume_channel) || !volume_channel)
CRASH("type mismatch for volume_channel or volume channel is not set.")
- if(volume_channel & VOL_MUSIC)
- . = prefs.snd_music_vol
- else if(volume_channel & VOL_AMBIENT)
- . = prefs.snd_ambient_vol
- else if(volume_channel & VOL_EFFECTS_MASTER)
- . = prefs.snd_effects_master_vol
- switch(volume_channel) // now for sub categories
- if(VOL_EFFECTS_VOICE_ANNOUNCEMENT)
- . *= prefs.snd_effects_voice_announcement_vol * 0.01
- if(VOL_EFFECTS_MISC)
- . *= prefs.snd_effects_misc_vol * 0.01
- if(VOL_EFFECTS_INSTRUMENT)
- . *= prefs.snd_effects_instrument_vol * 0.01
- else if(volume_channel & VOL_NOTIFICATIONS)
- . = prefs.snd_notifications_vol
- else if(volume_channel & VOL_ADMIN)
- . = prefs.snd_admin_vol
- else if(volume_channel & VOL_JUKEBOX)
- . = prefs.snd_jukebox_vol
- else
- CRASH("unknown volume_channel: [volume_channel]")
-
- if(. > 0)
- . = max(0.002, VOL_LINEAR_TO_NON(.)) // max(master slider won't kill sub slider's volume if both are less than max value).
-
-/client/proc/set_sound_volume(volume_channel, vol)
- vol = clamp(vol, 0, 100)
-
switch(volume_channel)
- if(VOL_MUSIC)
- prefs.snd_music_vol = vol
- mob.playsound_music_update_volume(volume_channel, CHANNEL_MUSIC)
+ if(VOL_LOBBY_MUSIC)
+ . = prefs.get_pref(/datum/pref/player/audio/lobby)
if(VOL_AMBIENT)
- prefs.snd_ambient_vol = vol
- mob.playsound_music_update_volume(volume_channel, CHANNEL_AMBIENT)
- mob.playsound_music_update_volume(volume_channel, CHANNEL_AMBIENT_LOOP)
+ . = prefs.get_pref(/datum/pref/player/audio/ambient)
if(VOL_EFFECTS_MASTER)
- prefs.snd_effects_master_vol = vol
- if(VOL_EFFECTS_VOICE_ANNOUNCEMENT)
- prefs.snd_effects_voice_announcement_vol = vol
- if(VOL_EFFECTS_MISC)
- prefs.snd_effects_misc_vol = vol
- if(VOL_EFFECTS_INSTRUMENT)
- prefs.snd_effects_instrument_vol = vol
+ . = prefs.get_pref(/datum/pref/player/audio/effects)
+ if(VOL_VOICE_ANNOUNCEMENTS)
+ . = prefs.get_pref(/datum/pref/player/audio/voice_announcements)
+ if(VOL_SPAM_EFFECTS)
+ // spam effects is just additional coefficient, by default it uses effects setting
+ . = prefs.get_pref(/datum/pref/player/audio/effects) * prefs.get_pref(/datum/pref/player/audio/spam_effects) * 0.01
+ if(VOL_MUSIC_INSTRUMENTS)
+ . = prefs.get_pref(/datum/pref/player/audio/instruments)
if(VOL_NOTIFICATIONS)
- prefs.snd_notifications_vol = vol
- if(VOL_ADMIN)
- prefs.snd_admin_vol = vol
- mob.playsound_music_update_volume(volume_channel, CHANNEL_ADMIN)
+ . = prefs.get_pref(/datum/pref/player/audio/notifications)
+ if(VOL_ADMIN_SOUNDS)
+ . = prefs.get_pref(/datum/pref/player/audio/admin_sounds)
if(VOL_JUKEBOX)
- var/old_vol = prefs.snd_jukebox_vol
-
- prefs.snd_jukebox_vol = vol
-
- if(istype(media)) // will be updated in "/mob/living/Login()" if changed in lobby.
- media.update_volume()
-
- if(!vol && old_vol) // only play/stop if last change is a mute or unmute state.
- media.stop_music()
- else if(vol && !old_vol)
- media.update_music()
-
-/client/proc/update_volume(href_list)
- var/slider
- var/vol_raw
-
- switch(href_list["proc"])
- if("sliderMoved")
- slider = text2num(href_list["slider"])
- vol_raw = text2num(href_list["volume"])
- if("save")
- if(prefs.save_preferences())
- to_chat(src, "Preferences Saved.")
- else
- to_chat(src, "Preferences saving failed due to unknown reason.")
- return
- if("testVolume")
- mob.playsound_local(null, 'sound/weapons/saberon.ogg', text2num(href_list["slider"]), vary = FALSE, channel = CHANNEL_VOLUMETEST)
- return
+ . = prefs.get_pref(/datum/pref/player/audio/jukebox)
else
- return
-
- if(!isnum(vol_raw) || !isnum(slider))
- return
-
- set_sound_volume(slider, vol_raw)
+ CRASH("unknown volume_channel: [volume_channel]")
-/client/verb/show_volume_controls()
- set name = ".showvolumecontrols"
- set hidden = TRUE
-
- if(!prefs_ready)
- to_chat(src, "Preferences not ready, please wait and try again.")
- return
-
- var/list/tables_data = list(
- "Music" = list(
- "Master" = "[VOL_MUSIC]"
- ),
- "Ambient" = list(
- "Master" = "[VOL_AMBIENT]"
- ),
- "Effects" = list(
- "Master" = "[VOL_EFFECTS_MASTER]",
- "Voice" = "[VOL_EFFECTS_VOICE_ANNOUNCEMENT]",
- "Misc" = "[VOL_EFFECTS_MISC]",
- "Music Instruments" = "[VOL_EFFECTS_INSTRUMENT]"
- ),
- "Notifications" = list(
- "Master" = "[VOL_NOTIFICATIONS]"
- ),
- "Admin Music/Sounds" = list(
- "Master" = "[VOL_ADMIN]"
- ),
- "Jukebox" = list(
- "Master" = "[VOL_JUKEBOX]"
- )
- )
-
- var/list/prefs_vol_values = list(
- "[VOL_MUSIC]" = prefs.snd_music_vol,
- "[VOL_AMBIENT]" = prefs.snd_ambient_vol,
- "[VOL_EFFECTS_MASTER]" = prefs.snd_effects_master_vol,
- "[VOL_EFFECTS_VOICE_ANNOUNCEMENT]" = prefs.snd_effects_voice_announcement_vol,
- "[VOL_EFFECTS_MISC]" = prefs.snd_effects_misc_vol,
- "[VOL_EFFECTS_INSTRUMENT]" = prefs.snd_effects_instrument_vol,
- "[VOL_NOTIFICATIONS]" = prefs.snd_notifications_vol,
- "[VOL_ADMIN]" = prefs.snd_admin_vol,
- "[VOL_JUKEBOX]" = prefs.snd_jukebox_vol
- )
-
- var/list/sliders_hint = list(
- "[VOL_MUSIC]" = "Lobby music.",
- "[VOL_AMBIENT]" = "Music and sound effects of ambient type.",
- "[VOL_EFFECTS_MASTER]" = "Controls all sound effects.",
- "[VOL_EFFECTS_VOICE_ANNOUNCEMENT]" = "Voiced global announcements.",
- "[VOL_EFFECTS_MISC]" = "Anything spammy that may annoy e.g.: tesla engine.",
- "[VOL_EFFECTS_INSTRUMENT]" = "Music instruments.",
- "[VOL_NOTIFICATIONS]" = "OOC notifications such as admin PM, cloning.",
- "[VOL_ADMIN]" = "Admin sounds and music.",
- "[VOL_JUKEBOX]" = "In-game jukebox's volume."
- )
-
- var/dat = {"
-
- "}
-
- for(var/category in tables_data)
- dat += {"
-
- "}
-
- dat +={"
-
-
-
-
- "}
-
- var/datum/browser/popup = new(usr, "volcontrols", "Audio Settings:", 620, 500, null, CSS_THEME_LIGHT)
- popup.set_content(dat)
- popup.open()
+ if(. > 0)
+ . = max(0.002, VOL_LINEAR_TO_NON(.)) // max(master slider won't kill sub slider's volume if both are less than max value).
diff --git a/code/game/turfs/turf_snow.dm b/code/game/turfs/turf_snow.dm
index caec52a5050c..dc5dfa86c07a 100644
--- a/code/game/turfs/turf_snow.dm
+++ b/code/game/turfs/turf_snow.dm
@@ -172,7 +172,7 @@
to_chat(user, "No power!")
return
- playsound(user, P.usesound, VOL_EFFECTS_INSTRUMENT)
+ playsound(user, P.usesound, VOL_MUSIC_INSTRUMENTS)
to_chat(user, "You start [P.drill_verb].")
if(!user.is_busy(src) && P.use_tool(src, user, 10, volume = 100))
diff --git a/code/game/verbs/fitviewport.dm b/code/game/verbs/fitviewport.dm
deleted file mode 100644
index c584bf6b819c..000000000000
--- a/code/game/verbs/fitviewport.dm
+++ /dev/null
@@ -1,70 +0,0 @@
-/client/verb/onresize()
- set hidden = TRUE
-
- if(prefs.auto_fit_viewport && !isnewplayer(mob))
- fit_viewport()
-
-/client/verb/fit_viewport()
- set name = "Fit viewport"
- set category = "OOC"
- set desc = "Fit the width of the map window to match the viewport"
-
- if(isnewplayer(mob)) // no mapwindow in lobby
- to_chat(usr, "You can't fix viewport while in lobby.")
- return
-
- // Fetch aspect ratio
- var/view_size = getviewsize(view)
- var/aspect_ratio = view_size[1] / view_size[2]
-
- // Calculate desired pixel width using window size and aspect ratio
- var/list/sizes = params2list(winget(src, "mainwindow.mainvsplit;mapwindow", "size"))
-
- // Client closed the window? Some other error? This is unexpected behaviour, let's
- // CRASH with some info.
- if(!sizes["mapwindow.size"])
- CRASH("sizes does not contain mapwindow.size key. This means a winget failed to return what we wanted. --- sizes var: [sizes] --- sizes length: [length(sizes)]")
-
- var/list/map_size = splittext(sizes["mapwindow.size"], "x")
-
- // Looks like we expect mapwindow.size to be "ixj" where i and j are numbers.
- // If we don't get our expected 2 outputs, let's give some useful error info.
- if(length(map_size) != 2)
- CRASH("map_size of incorrect length --- map_size var: [map_size] --- map_size length: [length(map_size)]")
-
- var/height = text2num(map_size[2])
- var/desired_width = round(height * aspect_ratio)
- if (text2num(map_size[1]) == desired_width)
- // Nothing to do
- return
-
- var/split_size = splittext(sizes["mainwindow.mainvsplit.size"], "x")
- var/split_width = text2num(split_size[1])
-
- // Avoid auto-resizing the statpanel and chat into nothing.
- desired_width = min(desired_width, split_width - 300)
-
- // Calculate and apply a best estimate
- // +4 pixels are for the width of the splitter's handle
- var/pct = 100 * (desired_width + 4) / split_width
- winset(src, "mainwindow.mainvsplit", "splitter=[pct]")
-
- // Apply an ever-lowering offset until we finish or fail
- var/delta
- for(var/safety in 1 to 10)
- var/after_size = winget(src, "mapwindow", "size")
- map_size = splittext(after_size, "x")
- var/got_width = text2num(map_size[1])
-
- if (got_width == desired_width)
- // success
- return
- else if (isnull(delta))
- // calculate a probable delta value based on the difference
- delta = 100 * (desired_width - got_width) / split_width
- else if ((delta > 0 && got_width > desired_width) || (delta < 0 && got_width < desired_width))
- // if we overshot, halve the delta and reverse direction
- delta = -delta/2
-
- pct += delta
- winset(src, "mainwindow.split", "splitter=[pct]")
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index f32f9faecb7c..f5c74d93c11c 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -1,5 +1,4 @@
-var/global/normal_ooc_colour = null
var/global/bridge_ooc_colour = "#7b804f"
/client/verb/ooc(msg as text)
@@ -17,7 +16,7 @@ var/global/bridge_ooc_colour = "#7b804f"
if(!msg) return
- if(!(prefs.chat_toggles & CHAT_OOC))
+ if(!prefs.get_pref(/datum/pref/player/chat/ooc))
to_chat(src, "You have OOC muted.")
return
@@ -53,25 +52,22 @@ var/global/bridge_ooc_colour = "#7b804f"
message_admins("[key_name_admin(src)] has attempted to advertise in OOC: [msg]")
return
- var/display_colour = normal_ooc_colour
+ var/display_colour = null
var/ooc_name = key
if(holder && !holder.fakekey)
- display_colour = "#704f80"
+ display_colour = OOC_COLOR_EVENTADMIN // we don't use it anymore?
if(holder.rights & R_DEBUG && !(holder.rights & R_ADMIN))
- display_colour = "#1b521f" //dark green
+ display_colour = OOC_COLOR_CODEADMIN
else if(holder.rights & R_ADMIN)
- if(config.allow_admin_ooccolor)
- display_colour = src.prefs.aooccolor
- else
- display_colour = "#b82e00" //orange
+ display_colour = (config.allow_admin_ooccolor && prefs.get_pref(/datum/pref/player/chat/aooccolor)) || OOC_COLOR_ADMIN
send2ooc(msg, ooc_name, display_colour, src)
world.send2bridge(
type = list(BRIDGE_OOC),
attachment_msg = "OOC: **[(holder && holder.fakekey)? holder.fakekey : ooc_name ]**: [msg]",
- attachment_color = (supporter && prefs.ooccolor) ? prefs.ooccolor : display_colour,
+ attachment_color = (supporter && prefs.get_pref(/datum/pref/player/chat/ooccolor)) || display_colour
)
/proc/send2ooc(msg, name, colour, client/sender, display_name, prefix = "OOC")
@@ -88,8 +84,8 @@ var/global/bridge_ooc_colour = "#7b804f"
display_name = name
if(sender)
- if(sender.supporter && sender.prefs.ooccolor)
- display_name = "[display_name]"
+ if(sender.supporter && sender.prefs.get_pref(/datum/pref/player/chat/ooccolor))
+ display_name = "[display_name]"
if(sender.holder && sender.holder.fakekey)
if(C.holder)
@@ -97,30 +93,9 @@ var/global/bridge_ooc_colour = "#7b804f"
else
display_name = sender.holder.fakekey
- if(C.prefs.chat_toggles & CHAT_OOC)
+ if(C.prefs.get_pref(/datum/pref/player/chat/ooc))
to_chat(C, "[msg_start]: [display_name?"[display_name]: ":""][msg_end]")
-/client/proc/set_global_ooc(newColor as color)
- set name = "Set Global OOC Colour"
- set desc = "Set to yellow for eye burning goodness. #000000 reset colour."
- set category = "OOC"
- if(!holder)
- return
- normal_ooc_colour = newColor != "#000000" ? newColor : null
-
-/client/verb/set_name_ooc()
- set name = "Set Name OOC Colour"
- set category = "OOC"
-
- if(!supporter)
- to_chat(usr, "This is only for [config.donate_info_url ? "supporters" : "supporters"][config.allow_byond_membership ? " and Byond Members" : ""].")
- return
-
- var/new_ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color|null
- if(new_ooccolor)
- prefs.ooccolor = normalize_color(new_ooccolor)
- prefs.save_preferences()
-
/client/verb/looc(msg as text)
set name = "LOOC" //Gave this shit a shorter name so you only have to time out "ooc" rather than "ooc message" to use it --NeoFite
set desc = "Local OOC, seen only by those in view."
@@ -135,7 +110,7 @@ var/global/bridge_ooc_colour = "#7b804f"
msg = sanitize(msg)
if(!msg) return
- if(!(prefs.chat_toggles & CHAT_LOOC))
+ if(!prefs.get_pref(/datum/pref/player/chat/looc))
to_chat(src, "You have LOOC muted.")
return
@@ -164,7 +139,7 @@ var/global/bridge_ooc_colour = "#7b804f"
is_fake_key = TRUE
if(isobserver(mob))
display_name = "(Ghost) [key]"
- else if(prefs.chat_toggles & CHAT_CKEY)
+ else if(prefs.get_pref(/datum/pref/player/chat/show_ckey))
display_name += " ([key])"
log_ooc("(LOCAL) [key_name(mob)] : [msg]")
@@ -188,13 +163,13 @@ var/global/bridge_ooc_colour = "#7b804f"
if (C in admins)
continue //they are handled after that
- if(C.prefs.chat_toggles & CHAT_LOOC)
+ if(C.prefs.get_pref(/datum/pref/player/chat/looc))
if(is_fake_key && C.holder)
display_name = "[holder.fakekey]/([key])"
to_chat(C, "[prefix]: [display_name]: [msg]")
for(var/client/C as anything in admins)
- if(C.prefs.chat_toggles & CHAT_LOOC)
+ if(C.prefs.get_pref(/datum/pref/player/chat/looc))
var/track = ""
if(isobserver(C.mob) && !isnewplayer(mob))
track = FOLLOW_LINK(C.mob, mob)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 77a24c3d71de..d3184846c7cf 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -18,22 +18,27 @@ var/global/BSACooldown = 0
log_admin("[key_name(usr)] " + message)
message_admins("[key_name_admin(usr)] " + message, 1)
+// todo: target can be the one who attacked and the one who was attacked
+// need to fix misuse and add both mobs here, and ATTACK_LOG_BOTH_CLIENT config
/proc/msg_admin_attack(msg, mob/living/target) //Toggleable Attack Messages
log_attack(msg)
msg = "ATTACK: [msg] [ADMIN_PPJMPFLW(target)]"
-
- var/require_flags = CHAT_ATTACKLOGS
+ var/no_client = FALSE
if(!target.client && !ishuman(target))
- require_flags |= CHAT_NOCLIENT_ATTACK
+ no_client = TRUE
for(var/client/C as anything in admins)
if(!(R_ADMIN & C.holder.rights))
continue
- if((C.prefs.chat_toggles & require_flags) != require_flags)
+
+ var/pref_level = C.prefs.get_pref(/datum/pref/player/chat/attack_log)
+ if(pref_level == ATTACK_LOG_DISABLED)
+ continue
+ if(pref_level == ATTACK_LOG_BY_CLIENT && no_client)
continue
- to_chat_attack_log(C, msg)
+ to_chat_attack_log(C, msg)
///////////////////////////////////////////////////////////////////////////////////////////////Panels
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index c88211e92e56..0f50c7a14160 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -13,7 +13,6 @@ var/global/list/admin_verbs_admin = list(
/datum/admins/proc/toggleenter, //toggles whether people can join the current game,
/datum/admins/proc/toggleguests, //toggles whether guests can join the current game,
/datum/admins/proc/announce, //priority announce something to all clients,
- /client/proc/colorooc, //allows us to set a custom colour for everythign we say in ooc,
/client/proc/admin_ghost, //allows us to ghost/reenter body at will,
/client/proc/toggle_view_range, //changes how far we can see,
/client/proc/cmd_admin_pm_context, //right-click adminPM interface,
@@ -44,8 +43,6 @@ var/global/list/admin_verbs_admin = list(
/client/proc/check_antagonists,
/client/proc/admin_memo, //admin memo system. show/delete/write. +SERVER needed to delete admin memos of others,
/client/proc/dsay, //talk in deadchat using our ckey/fakekey,
- /client/proc/toggleprayers, //toggles prayers on/off,
- /client/proc/toggle_hear_radio, //toggles whether we hear the radio,
/client/proc/secrets,
/datum/admins/proc/toggleooc, //toggles ooc on/off for everyone,
/datum/admins/proc/togglelooc, //toggles looc on/off for everyone,
@@ -55,9 +52,6 @@ var/global/list/admin_verbs_admin = list(
/client/proc/cmd_admin_say, //admin-only ooc chat,
/client/proc/free_slot, //frees slot for chosen job,
/client/proc/cmd_admin_change_custom_event,
- /client/proc/toggleattacklogs,
- /client/proc/toggle_noclient_attacklogs,
- /client/proc/toggledebuglogs,
/client/proc/toggleghostwriters,
/client/proc/toggledrones,
/client/proc/man_up,
@@ -118,7 +112,6 @@ var/global/list/admin_verbs_fun = list(
/client/proc/cmd_admin_add_random_ai_law,
// /client/proc/make_sound,
/client/proc/toggle_random_events,
- /client/proc/set_global_ooc,
/client/proc/editappear,
/client/proc/roll_dices,
/client/proc/epileptic_anomaly,
@@ -187,7 +180,6 @@ var/global/list/admin_verbs_debug = list(
/client/proc/enable_debug_verbs,
/*/client/proc/callproc,*/
// /proc/machine_upgrade,
- /client/proc/toggledebuglogs,
/client/proc/view_runtimes,
/client/proc/getdebuglogsbyid,
/client/proc/cmd_display_del_log,
@@ -233,17 +225,13 @@ var/global/list/admin_verbs_event = list(
//verbs which can be hidden - needs work
var/global/list/admin_verbs_hideable = list(
- /client/proc/set_global_ooc,
/datum/admins/proc/library_recycle_bin,
/client/proc/deadmin_self,
// /client/proc/deadchat,
- /client/proc/toggleprayers,
- /client/proc/toggle_hear_radio,
/datum/admins/proc/show_traitor_panel,
/datum/admins/proc/toggleenter,
/datum/admins/proc/toggleguests,
/datum/admins/proc/announce,
- /client/proc/colorooc,
/client/proc/admin_ghost,
/client/proc/toggle_view_range,
/client/proc/getserverlogs,
@@ -405,10 +393,6 @@ var/global/list/admin_verbs_hideable = list(
to_chat(src, "All of your adminverbs are now visible.")
feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-
-
-
/client/proc/admin_ghost()
set category = "Admin"
set name = "Aghost"
@@ -501,20 +485,6 @@ var/global/list/admin_verbs_hideable = list(
feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
-/client/proc/colorooc()
- set category = "OOC"
- set name = "Set Admin OOC Color"
- if(!holder)
- return
- if(!config.allow_admin_ooccolor)
- to_chat(usr, "Currently disabled by config.")
- var/new_aooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color|null
- if(new_aooccolor)
- prefs.aooccolor = normalize_color(new_aooccolor)
- prefs.save_preferences()
- feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- return
-
/client/proc/stealth()
set category = "Admin"
set name = "Stealth Mode"
@@ -929,24 +899,6 @@ var/global/list/admin_verbs_hideable = list(
var/datum/atom_hud/A = global.huds[ANTAG_HUD_TRAITOR]
return A.hudusers[mob]
-/client/proc/toggleattacklogs()
- set name = "Toggle Attack Log Messages"
- set category = "Preferences"
-
- prefs.chat_toggles ^= CHAT_ATTACKLOGS
- prefs.save_preferences()
- to_chat(src, "You now [(prefs.chat_toggles & CHAT_ATTACKLOGS) ? "will" : "won't"] get attack log messages.")
- feedback_add_details("admin_verb","TALM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/proc/toggle_noclient_attacklogs()
- set name = "Toggle No Client Attack Log Messages"
- set category = "Preferences"
-
- prefs.chat_toggles ^= CHAT_NOCLIENT_ATTACK
- prefs.save_preferences()
- to_chat(src, "You now [(prefs.chat_toggles & CHAT_NOCLIENT_ATTACK) ? "will" : "won't"] get attack log messages for mobs that don't have a client.")
- feedback_add_details("admin_verb","TNCALM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
/client/proc/toggleghostwriters()
set name = "Toggle ghost writers"
set category = "Server"
@@ -975,15 +927,6 @@ var/global/list/admin_verbs_hideable = list(
to_chat(src, "Enabled maint drones.")
message_admins("Admin [key_name_admin(usr)] has enabled maint drones.")
-/client/proc/toggledebuglogs()
- set name = "Toggle Debug Log Messages"
- set category = "Preferences"
-
- prefs.chat_toggles ^= CHAT_DEBUGLOGS
- prefs.save_preferences()
- to_chat(src, "You now [(prefs.chat_toggles & CHAT_DEBUGLOGS) ? "will" : "won't"] get debug log messages.")
- feedback_add_details("admin_verb","TDLM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
/client/proc/man_up(mob/T as mob in player_list)
set category = "Fun"
set name = "Man Up"
@@ -991,7 +934,7 @@ var/global/list/admin_verbs_hideable = list(
to_chat(T, "Man up and deal with it.")
to_chat(T, "Move on.")
- T.playsound_local(null, 'sound/voice/ManUp1.ogg', VOL_ADMIN, vary = FALSE, ignore_environment = TRUE)
+ T.playsound_local(null, 'sound/voice/ManUp1.ogg', VOL_ADMIN_SOUNDS, vary = FALSE, ignore_environment = TRUE)
log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.")
message_admins("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it.")
@@ -1003,7 +946,7 @@ var/global/list/admin_verbs_hideable = list(
for (var/mob/T in player_list)
to_chat(T, "
Man up.
Deal with it.
Move on.
")
- T.playsound_local(null, 'sound/voice/ManUp1.ogg', VOL_ADMIN, vary = FALSE, ignore_environment = TRUE)
+ T.playsound_local(null, 'sound/voice/ManUp1.ogg', VOL_ADMIN_SOUNDS, vary = FALSE, ignore_environment = TRUE)
log_admin("[key_name(usr)] told everyone to man up and deal with it.")
message_admins("[key_name_admin(usr)] told everyone to man up and deal with it.")
@@ -1043,10 +986,10 @@ var/global/list/admin_verbs_hideable = list(
var/icon/cup = icon('icons/obj/drinks.dmi', "golden_cup")
if(glob == "No!")
- winner.playsound_local(null, 'sound/misc/achievement.ogg', VOL_ADMIN, vary = FALSE, ignore_environment = TRUE)
+ winner.playsound_local(null, 'sound/misc/achievement.ogg', VOL_ADMIN_SOUNDS, vary = FALSE, ignore_environment = TRUE)
else
for(var/mob/M in player_list)
- M.playsound_local(null, 'sound/misc/achievement.ogg', VOL_ADMIN, vary = FALSE, ignore_environment = TRUE)
+ M.playsound_local(null, 'sound/misc/achievement.ogg', VOL_ADMIN_SOUNDS, vary = FALSE, ignore_environment = TRUE)
to_chat(world, "[bicon(cup)] [winner.name] wins \"[name]\"!")
to_chat(winner, "Congratulations!")
diff --git a/code/modules/admin/secrets/tgui_secrets/custom_announcement.dm b/code/modules/admin/secrets/tgui_secrets/custom_announcement.dm
index 625cc5d19524..f2973384a9a7 100644
--- a/code/modules/admin/secrets/tgui_secrets/custom_announcement.dm
+++ b/code/modules/admin/secrets/tgui_secrets/custom_announcement.dm
@@ -97,7 +97,7 @@ var/global/list/datum/announcement/announcements_list
sound_file = pick(sound_file)
else
WARNING("No sound file for [sound_name]")
- U.playsound_local(null, sound_file, VOL_EFFECTS_VOICE_ANNOUNCEMENT, volume, FALSE, channel = CHANNEL_ANNOUNCE, wait = TRUE)
+ U.playsound_local(null, sound_file, VOL_VOICE_ANNOUNCEMENTS, volume, FALSE, channel = CHANNEL_ANNOUNCE, wait = TRUE)
if("preset_select")
if(!(C.holder.rights & (R_FUN | R_EVENT)))
return
diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm
index 671111e7d69b..124de3627b7a 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.chat_toggles & CHAT_DEAD))
+ if(!prefs.get_pref(/datum/pref/player/chat/dead))
to_chat(src, "You have deadchat muted.")
return
@@ -35,10 +35,13 @@
if (isnewplayer(M))
continue
- if(M.client && M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD)) // show the message to admins who have deadchat toggled on
+ if(!M.client)
+ continue
+
+ if(M.client.holder && (M.client.prefs.get_pref(/datum/pref/player/chat/dead))) // show the message to admins who have deadchat toggled on
to_chat(M, rendered)
- else if(M.stat == DEAD && (M.client.prefs.chat_toggles & CHAT_DEAD)) // show the message to regular ghosts who have deadchat toggled on
+ else if(M.stat == DEAD && M.client.prefs.get_pref(/datum/pref/player/chat/dead)) // show the message to regular ghosts who have deadchat toggled on
to_chat(M, rendered)
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/playsound.dm b/code/modules/admin/verbs/playsound.dm
index b925c75e0aac..4dcacc8f1602 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -34,7 +34,7 @@ var/global/list/sounds_cache_local = list()
message_admins("[key_name_admin(src)] played sound [S].")
for(var/mob/M in player_list)
- M.playsound_music(S, VOL_ADMIN, null, TRUE, CHANNEL_ADMIN, 250, SOUND_STREAM)
+ M.playsound_music(S, VOL_ADMIN_SOUNDS, null, TRUE, CHANNEL_ADMIN, 250, SOUND_STREAM)
feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -112,7 +112,7 @@ var/global/list/sounds_cache_local = list()
message_admins("[key_name_admin(src)] played server sound [sound_path].")
for(var/mob/M in player_list)
- M.playsound_music(sound_path, VOL_ADMIN, null, TRUE, CHANNEL_ADMIN, 250, 0)
+ M.playsound_music(sound_path, VOL_ADMIN_SOUNDS, null, TRUE, CHANNEL_ADMIN, 250, 0)
feedback_add_details("admin_verb","PSS") //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 d81a841f697a..c565fbf53f06 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -72,11 +72,11 @@
if(!M.client)
continue
- if(M.client.holder && (M.client.prefs.chat_toggles & CHAT_PRAYER)) // Show the message to admins with prayer toggled on
+ if(M.client.holder && M.client.prefs.get_pref(/datum/pref/player/chat/prayers)) // Show the message to admins with prayer toggled on
to_chat(M, admin_msg)//Admins can hear deadchat, if they choose to, no matter if they're blind/deaf or not.
continue
- if(M.stat == DEAD && (M.client.prefs.chat_toggles & CHAT_DEAD))
+ if(M.stat == DEAD && M.client.prefs.get_pref(/datum/pref/player/chat/dead))
if(M.fake_death) //Our changeling with fake_death status must not hear dead chat!!
continue
@@ -86,7 +86,7 @@
for(var/mob/living/simple_animal/shade/god/G as anything in gods_list)
- if(G.client && (G.client.prefs.chat_toggles & CHAT_PRAYER))
+ if(G.client && G.client.prefs.get_pref(/datum/pref/player/chat/prayers))
if(G == src) // Don't hear your own prayer.
continue
if(G.say_understands(src, speaking))
@@ -133,7 +133,7 @@
var/list/viewing = list()
for(var/mob/M in viewers(src))
- if(M.client && (M.client.prefs.toggles & SHOW_ANIMATIONS))
+ if(M.client?.prefs.get_pref(/datum/pref/player/game/melee_animation))
viewing |= M.client
flick_overlay(I, viewing, 10)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index b239318357a3..65d3f2b6e6a9 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -771,7 +771,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
var/viewy = clamp(input("Enter view height (1-127)") as num, 1, 127) * 2 + 1
change_view("[viewx]x[viewy]")
- if(prefs.auto_fit_viewport)
+ if(prefs.get_pref(/datum/pref/player/display/auto_fit_viewport))
fit_viewport()
log_admin("[key_name(usr)] changed their view range to [viewx]x[viewy].")
diff --git a/code/modules/client/character_menu/customkeybindings.dm b/code/modules/client/character_menu/customkeybindings.dm
deleted file mode 100644
index 5597344ab2f4..000000000000
--- a/code/modules/client/character_menu/customkeybindings.dm
+++ /dev/null
@@ -1,194 +0,0 @@
-/datum/preferences/proc/ShowCustomKeybindings(mob/user)
- if(!key_bindings.len)
- . += "Этот текст вы можете видеть только при ошибке со стороны кода.
"
- . += "Попробуйте нажать сверху кнопку Reload Slot. Если это не помогло, то подождите рестарт."
- . += "Можете так же сообщить о проблеме в гитхаб репозитория."
- return
-
- // Create an inverted list of keybindings -> key
- var/list/user_binds = list()
- for (var/key in key_bindings)
- for(var/kb_name in key_bindings[key])
- user_binds[kb_name] += list(key)
-
- var/list/kb_categories = list()
- // Group keybinds by category
- for (var/name in global.keybindings_by_name)
- var/datum/keybinding/kb = global.keybindings_by_name[name]
- kb_categories[kb.category] += list(kb)
- . += "Hotkeys Mode: [hotkeys ? "Focus on Game" : "Focus on Chat"] |
"
- . += ""
- for (var/category in kb_categories)
- . += "[category]
"
- . += ""
- for (var/i in kb_categories[category])
- var/datum/keybinding/kb = i
- if(!length(user_binds[kb.name]) || (user_binds[kb.name][1] == "None" && length(user_binds[kb.name]) == 1))
- . += "[kb.full_name] | None | "
- var/list/default_keys = kb.hotkey_keys
- var/class
- if(compare_list(user_binds[kb.name], default_keys))
- class = "class='disabled fluid'"
- else
- class = "class='white fluid' href ='?_src_=prefs;preference=keybinding_reset;keybinding=[kb.name];old_keys=[jointext(user_binds[kb.name], ",")]"
-
- . += {" | | Reset | "}
- . += "
"
- else
- var/bound_key = user_binds[kb.name][1]
- var/normal_name = _kbMap_reverse[bound_key] ? _kbMap_reverse[bound_key] : bound_key
- . += "[kb.full_name] | [normal_name] | "
- for(var/bound_key_index in 2 to length(user_binds[kb.name]))
- bound_key = user_binds[kb.name][bound_key_index]
- normal_name = _kbMap_reverse[bound_key] ? _kbMap_reverse[bound_key] : bound_key
- . += "[normal_name] | "
- if(length(user_binds[kb.name]) < MAX_KEYS_PER_KEYBIND)
- . += "None | "
- for(var/j in 1 to MAX_KEYS_PER_KEYBIND - (length(user_binds[kb.name]) + 1))
- . += " | "
- var/list/default_keys = kb.hotkey_keys
- . += {"Reset | "}
- . += "
"
- . += "
"
-
- . += "
"
- . += "Reset to default"
- . += ""
-
-/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, old_key)
- var/HTML = {"
-
- Keybinding: [kb.full_name]
[kb.description]
-
- Press any key to change
Press ESC to clear
-
-
- "}
- winshow(user, "capturekeypress", TRUE)
- var/datum/browser/popup = new(user, "capturekeypress", "Keybindings
", 350, 300)
- popup.set_content(HTML)
- popup.open(FALSE)
-
-/datum/preferences/proc/toggle_hotkeys_mode()
- hotkeys = !hotkeys
- if(hotkeys)
- winset(usr, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED]")
- else
- winset(usr, null, "input.focus=true input.background-color=[COLOR_INPUT_DISABLED]")
- save_preferences()
-
-
-/datum/preferences/proc/process_link_custom_keybindings(mob/user, list/href_list)
- if(!user)
- return
- switch(href_list["preference"])
- if("hotkeys")
- toggle_hotkeys_mode()
-
- if("keybindings_capture")
- var/datum/keybinding/kb = global.keybindings_by_name[href_list["keybinding"]]
- var/old_key = href_list["old_key"]
- CaptureKeybinding(user, kb, old_key)
- return
-
- if("keybindings_set")
- var/kb_name = href_list["keybinding"]
- if(!kb_name)
- user << browse(null, "window=capturekeypress")
- ShowChoices(user)
- return
-
- var/clear_key = text2num(href_list["clear_key"])
- var/old_key = href_list["old_key"]
- if(clear_key)
- if(key_bindings[old_key])
- key_bindings[old_key] -= kb_name
- if(!(kb_name in key_bindings["None"]))
- LAZYADD(key_bindings["None"], kb_name)
- if(!length(key_bindings[old_key]))
- key_bindings -= old_key
- user << browse(null, "window=capturekeypress")
- user.client.set_macros()
- save_preferences()
- ShowChoices(user)
- return
-
- var/new_key = uppertext(href_list["key"])
- var/AltMod = text2num(href_list["alt"]) ? "Alt" : ""
- var/CtrlMod = text2num(href_list["ctrl"]) ? "Ctrl" : ""
- var/ShiftMod = text2num(href_list["shift"]) ? "Shift" : ""
- var/numpad = text2num(href_list["numpad"]) ? "Numpad" : ""
-
- if(!new_key) // Just in case (; - not work although keyCode 186 and nothing should break)
- user << browse(null, "window=capturekeypress")
- return
-
- if(global._kbMap[new_key])
- new_key = global._kbMap[new_key]
-
- var/full_key
- switch(new_key)
- if("Alt")
- full_key = "[new_key][CtrlMod][ShiftMod]"
- if("Ctrl")
- full_key = "[AltMod][new_key][ShiftMod]"
- if("Shift")
- full_key = "[AltMod][CtrlMod][new_key]"
- else
- full_key = "[AltMod][CtrlMod][ShiftMod][numpad][new_key]"
- if(kb_name in key_bindings[full_key]) //We pressed the same key combination that was already bound here, so let's remove to re-add and re-sort.
- key_bindings[full_key] -= kb_name
- if(key_bindings[old_key])
- key_bindings[old_key] -= kb_name
- if(!length(key_bindings[old_key]))
- key_bindings -= old_key
- key_bindings[full_key] += list(kb_name)
- key_bindings[full_key] = sortList(key_bindings[full_key])
-
- user << browse(null, "window=capturekeypress")
- user.client.set_macros()
- save_preferences()
-
- if("keybindings_reset")
- key_bindings = deepCopyList(global.hotkey_keybinding_list_by_key)
- user.client.set_macros()
- save_preferences()
-
- if("keybinding_reset")
- var/kb_name = href_list["keybinding"]
- var/list/old_keys = splittext(href_list["old_keys"], ",")
-
- for(var/old_key in old_keys)
- if(!key_bindings[old_key])
- continue
- key_bindings[old_key] -= kb_name
- if(!length(key_bindings[old_key]))
- key_bindings -= old_key
-
- var/datum/keybinding/kb = global.keybindings_by_name[kb_name]
- for(var/key in kb.hotkey_keys)
- key_bindings[key] += list(kb_name)
- key_bindings[key] = sortList(key_bindings[key])
- user.client.set_macros()
- save_preferences()
diff --git a/code/modules/client/character_menu/emote_panel_editor.dm b/code/modules/client/character_menu/emote_panel_editor.dm
index 613e45f04133..37fc194f623f 100644
--- a/code/modules/client/character_menu/emote_panel_editor.dm
+++ b/code/modules/client/character_menu/emote_panel_editor.dm
@@ -2,12 +2,12 @@
var/datum/emote_panel_editor
/datum/emote_panel_editor
- var/list/custom_emote_panel
+ var/list/enabled_emotes
var/datum/preferences/prefs
/datum/emote_panel_editor/New(client/user)
src.prefs = user.prefs
- src.custom_emote_panel = prefs.custom_emote_panel
+ src.enabled_emotes = prefs.enabled_emotes_emote_panel
/datum/emote_panel_editor/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -27,12 +27,13 @@
if(!(emote in global.emotes_for_emote_panel))
return
- if(emote in custom_emote_panel)
- custom_emote_panel -= emote
+ if(emote in enabled_emotes)
+ enabled_emotes -= emote
else
- custom_emote_panel += emote
+ enabled_emotes += emote
- prefs.save_preferences()
+ var/list/disabled_emotes = global.emotes_for_emote_panel - enabled_emotes
+ prefs.set_pref(/datum/pref/player/meta/disabled_emotes_emote_panel, disabled_emotes)
return TRUE
@@ -42,7 +43,7 @@
/datum/emote_panel_editor/tgui_data(mob/user)
var/list/data = list()
- data["customEmotes"] = custom_emote_panel
+ data["customEmotes"] = enabled_emotes
data["allHumanEmotes"] = global.emotes_for_emote_panel
return data
diff --git a/code/modules/client/character_menu/general.dm b/code/modules/client/character_menu/general.dm
index fe457852f4e4..cd7390cd346c 100644
--- a/code/modules/client/character_menu/general.dm
+++ b/code/modules/client/character_menu/general.dm
@@ -18,7 +18,7 @@
if(species == IPC) // only ipc can change their voice at this moment
. += "
Voice: [neuter_gender_voice == MALE ? "Male" : "Female"]"
. += "
Height: [height]"
- . += "
Randomized Character Slot: [randomslot ? "Yes" : "No"]"
+ . += "
Randomized Character Slot: [get_pref(/datum/pref/player/meta/random_slot) ? "Yes" : "No"]"
. += "
"
. += ""
. += ""
@@ -617,7 +617,8 @@
neuter_gender_voice = neuter_gender_voice == MALE ? FEMALE : MALE
if("randomslot")
- randomslot = !randomslot
+ var/random_slot = get_pref(/datum/pref/player/meta/random_slot)
+ set_pref(/datum/pref/player/meta/random_slot, !random_slot)
if("name")
be_random_name = !be_random_name
diff --git a/code/modules/client/character_menu/global.dm b/code/modules/client/character_menu/global.dm
deleted file mode 100644
index bd05ffe4a670..000000000000
--- a/code/modules/client/character_menu/global.dm
+++ /dev/null
@@ -1,221 +0,0 @@
-/datum/preferences/proc/ShowGlobal(mob/user)
- . = ""
- . += ""
- . += ""
- . += ""
- . += " | "
- . += ""
- . += ""
- . += " | "
- . += "
"
- . += "
"
-
-/datum/preferences/proc/process_link_glob(mob/user, list/href_list)
- switch(href_list["task"])
- if("input")
- if(href_list["preference"] == "metadata")
- var/new_metadata = sanitize(input(user, "Enter any OOC information you'd like others to see:", "Game Preference", input_default(metadata)) as message|null)
- if(new_metadata)
- metadata = new_metadata
-
- if("reset")
- UI_style_color = initial(UI_style_color)
- UI_style_alpha = initial(UI_style_alpha)
-
- switch(href_list["preference"])
-
- if("UIcolor")
- var/UI_style_color_new = input(user, "Choose your UI color, dark colors 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(transparence) parametr for UI, between 50 and 255") as num|null
- if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) return
- UI_style_alpha = UI_style_alpha_new
-
- if("ui")
- var/pickedui = input(user, "Choose your UI style.", "Character Preference", UI_style) as null|anything in sortList(global.available_ui_styles)
- if(pickedui)
- UI_style = pickedui
-
- if("tgui_fancy")
- tgui_fancy = !tgui_fancy
-
- if("tgui_lock")
- tgui_lock = !tgui_lock
-
- if("tooltip_show")
- parent?.toggle_tooltip()
-
- if("change_size_tooltip")
- parent?.change_size_tooltip()
-
- if("change_font_tooltip")
- parent?.change_font_tooltip()
-
- if("outline_enabled")
- outline_enabled = !outline_enabled
-
- if("outline_color")
- var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null
- if(pickedOutlineColor)
- outline_color = pickedOutlineColor
-
- if("change_fps")
- var/desiredfps = input(user, "Choose your desired fps.\n-1 means recommended value (currently:[RECOMMENDED_FPS])\n0 means world fps (currently:[world.fps])", "Character Preference", clientfps) as null|num
- if (!isnull(desiredfps))
- clientfps = sanitize_integer(desiredfps, -1, 1000, clientfps)
- parent.fps = (clientfps < 0) ? RECOMMENDED_FPS : clientfps
-
- if("parallaxup")
- parallax = WRAP(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1)
- if (parent && parent.mob && parent.mob.hud_used)
- parent.mob.hud_used.update_parallax_pref()
-
- if("parallaxdown")
- parallax = WRAP(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1)
- if (parent && parent.mob && parent.mob.hud_used)
- parent.mob.hud_used.update_parallax_pref()
-
- if("ambientocclusion")
- ambientocclusion = !ambientocclusion
- if(parent && parent.screen && parent.screen.len)
- parent.update_plane_masters(/atom/movable/screen/plane_master/game_world)
-
- if("lobbyanimation")
- lobbyanimation = !lobbyanimation // shouldn't prefs changes be saved somewhere here?
- if(isnewplayer(user))
- var/mob/dead/new_player/M = user
- M.show_titlescreen()
-
- if("auto_fit_viewport")
- auto_fit_viewport = !auto_fit_viewport
- if(auto_fit_viewport && parent)
- parent.fit_viewport()
-
- if("ghost_sight")
- switch(chat_ghostsight)
- if(CHAT_GHOSTSIGHT_ALL)
- chat_ghostsight = CHAT_GHOSTSIGHT_ALLMANUAL
- if(CHAT_GHOSTSIGHT_ALLMANUAL)
- chat_ghostsight = CHAT_GHOSTSIGHT_NEARBYMOBS
- if(CHAT_GHOSTSIGHT_NEARBYMOBS)
- chat_ghostsight = CHAT_GHOSTSIGHT_ALL
-
- if("ghost_ears")
- chat_toggles ^= CHAT_GHOSTEARS
-
- if("npc_ghost_ears")
- chat_toggles ^= CHAT_GHOSTNPC
-
- if("ghost_radio")
- chat_toggles ^= CHAT_GHOSTRADIO
-
- if("see_ooc")
- chat_toggles ^= CHAT_OOC
-
- if("see_looc")
- chat_toggles ^= CHAT_LOOC
-
- if("see_animations")
- toggles ^= SHOW_ANIMATIONS
-
- if("see_progbar")
- toggles ^= SHOW_PROGBAR
diff --git a/code/modules/client/character_menu/load_slot.dm b/code/modules/client/character_menu/load_slot.dm
index f477b7fd9c38..61f47795bc14 100644
--- a/code/modules/client/character_menu/load_slot.dm
+++ b/code/modules/client/character_menu/load_slot.dm
@@ -9,7 +9,7 @@
S["real_name"] >> name
if(!name)
name = "Character [i]"
- if(i==default_slot)
+ if(i==get_pref(/datum/pref/player/meta/default_slot))
name = "[name]"
. += "[name]
"
. += ""
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 6b470e732a73..5fd5cb993be1 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -27,7 +27,7 @@
var/area = null
var/mentorhelped = FALSE
var/supporter = 0
- var/prefs_ready = FALSE
+ var/prefs_ready = FALSE // drop it to datum
///////////////
//SOUND STUFF//
@@ -109,8 +109,6 @@
// Last world.time that the player tried to request their resources.
var/last_ui_resource_send = 0
- var/fullscreen = NONE
-
/// Messages currently seen by this client
var/list/seen_messages
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 5614b68d3049..8f526c8be9b0 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -106,12 +106,7 @@ var/global/list/blacklisted_builds = list(
asset_cache_preload_data(href_list["asset_cache_preload_data"])
return
- //byond bug ID:2694120
- if(href_list["reset_macros"])
- reset_macros(skip_alert = TRUE)
- return
-
- // Keypress passthrough
+ // Keypress passthrough for some non-tgui interfaces (we should remove it in the future)
if(href_list["__keydown"])
var/keycode = browser_keycode_to_byond(href_list["__keydown"])
if(keycode)
@@ -147,7 +142,6 @@ var/global/list/blacklisted_builds = list(
if("usr") hsrc = mob
if("prefs") return prefs.process_link(usr,href_list)
if("vars") return view_var_Topic(href,href_list,hsrc)
- if("updateVolume") return update_volume(href_list)
switch(href_list["action"])
if ("openLink")
@@ -247,16 +241,16 @@ var/global/list/blacklisted_builds = list(
update_supporter_status()
//preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
- if(preferences_datums[ckey])
- prefs = preferences_datums[ckey]
+ if(global.preferences_datums[ckey])
+ prefs = global.preferences_datums[ckey]
prefs.reattach_to_client(src)
else
prefs = new /datum/preferences(src)
- preferences_datums[ckey] = prefs
+ global.preferences_datums[ckey] = prefs
prefs.last_ip = address //these are gonna be used for banning
prefs.last_id = computer_id //these are gonna be used for banning
- fps = (prefs.clientfps < 0) ? RECOMMENDED_FPS : prefs.clientfps
+ fps = prefs.get_pref(/datum/pref/player/display/fps)
prefs.save_preferences()
@@ -307,11 +301,11 @@ var/global/list/blacklisted_builds = list(
// Set config based title for main window
if (config.server_name)
- winset(src, "mainwindow", "title='[world.name]: [config.server_name]'")
+ winset(src, "tcmainwindow", "title='[world.name]: [config.server_name]'")
else
- winset(src, "mainwindow", "title='[world.name]'")
+ winset(src, "tcmainwindow", "title='[world.name]'")
- if(prefs.lastchangelog != changelog_hash) // Bolds the changelog button on the interface so we know there are updates.
+ if(prefs.get_pref(/datum/pref/player/meta/lastchangelog) != changelog_hash) // Bolds the changelog button on the interface so we know there are updates.
to_chat(src, "You have unread updates in the changelog.")
winset(src, "rpane.changelog", "font-style=bold")
@@ -319,8 +313,9 @@ var/global/list/blacklisted_builds = list(
if(!tooltips)
tooltips = new /datum/tooltip(src)
- if(prefs.auto_fit_viewport)
- fit_viewport()
+ // client screen options
+ update_fullscreen() // we need it?
+ update_map_zoom()
if(!cob)
cob = new()
@@ -686,7 +681,7 @@ var/global/list/blacklisted_builds = list(
var/list/modifiers = params2list(params)
if(modifiers[DRAG])
return
- if (prefs.hotkeys)
+ if (prefs.get_pref(/datum/pref/player/game/hotkey_mode))
winset(src, null, "input.background-color=[COLOR_INPUT_DISABLED]")
else
winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED]")
@@ -783,51 +778,31 @@ var/global/list/blacklisted_builds = list(
* Arguments:
* * direct_prefs - the preference we're going to get keybinds from
*/
-/client/proc/update_special_keybinds(datum/preferences/direct_prefs)
+/client/proc/update_movement_keybinds(datum/preferences/direct_prefs)
var/datum/preferences/D = prefs || direct_prefs
- if(!D?.key_bindings)
+ if(!D?.prefs_keybinds)
return
movement_keys = list()
- for(var/key in D.key_bindings)
- for(var/kb_name in D.key_bindings[key])
- switch(kb_name)
- if("North")
- movement_keys[key] = NORTH
- if("East")
- movement_keys[key] = EAST
- if("West")
- movement_keys[key] = WEST
- if("South")
- movement_keys[key] = SOUTH
-
-#define MAXIMAZED (1<<0)
-#define FULLSCREEN (1<<1)
-
-/client/verb/toggle_fullscreen()
- set name = "Toggle Fullscreen"
- set category = "OOC"
-
- fullscreen ^= FULLSCREEN
-
- if(fullscreen & FULLSCREEN)
- if(winget(usr, "mainwindow", "is-maximized") == "true")
- fullscreen |= MAXIMAZED
- else
- fullscreen &= ~MAXIMAZED
- winset(usr, "mainwindow", "titlebar=false")
- winset(usr, "mainwindow", "can-resize=false")
- winset(usr, "mainwindow", "is-maximized=false")
- winset(usr, "mainwindow", "is-maximized=true")
- winset(usr, "mainwindow", "menu=")
- else
- if(!(fullscreen & MAXIMAZED))
- winset(usr, "mainwindow", "is-maximized=false")
- winset(usr, "mainwindow", "titlebar=true")
- winset(usr, "mainwindow", "can-resize=true")
- winset(usr, "mainwindow", "menu=menu")
-#undef MAXIMAZED
-#undef FULLSCREEN
+ var/datum/pref/keybinds/P = D.prefs_keybinds[/datum/pref/keybinds/movement/north]
+ for(var/key in splittext(P.value, " "))
+ if(length(key))
+ movement_keys[key] = NORTH
+
+ P = D.prefs_keybinds[/datum/pref/keybinds/movement/east]
+ for(var/key in splittext(P.value, " "))
+ if(length(key))
+ movement_keys[key] = EAST
+
+ P = D.prefs_keybinds[/datum/pref/keybinds/movement/west]
+ for(var/key in splittext(P.value, " "))
+ if(length(key))
+ movement_keys[key] = WEST
+
+ P = D.prefs_keybinds[/datum/pref/keybinds/movement/south]
+ for(var/key in splittext(P.value, " "))
+ if(length(key))
+ movement_keys[key] = SOUTH
// ckey = datum/stat/leave_stat
var/global/list/disconnected_ckey_by_stat = list()
@@ -847,13 +822,6 @@ var/global/list/disconnected_ckey_by_stat = list()
global.disconnected_ckey_by_stat[ckey] = stat
-/client/proc/change_view(new_size)
- if (isnull(new_size))
- CRASH("change_view called without argument.")
-
- view = new_size
- mob.reload_fullscreen()
-
/client/proc/open_filter_editor(atom/in_atom)
if(holder)
holder.filteriffic = new /datum/filter_editor(in_atom)
diff --git a/code/modules/client/client_settings_menu.dm b/code/modules/client/client_settings_menu.dm
new file mode 100644
index 000000000000..a939e245d04e
--- /dev/null
+++ b/code/modules/client/client_settings_menu.dm
@@ -0,0 +1,169 @@
+/client
+ var/datum/client_settings/settings
+
+/client/verb/client_settings()
+ set name = "Settings"
+ set category = "OOC"
+
+ open_settings_menu()
+
+/client/proc/open_settings_menu(tab)
+ if(!prefs_ready)
+ to_chat(usr, "Need more time for initialization!")
+ return
+
+ if(!settings)
+ settings = new /datum/client_settings(src)
+ settings.tgui_interact(usr, tab)
+
+/datum/client_settings
+ var/active_tab = PREF_PLAYER_DISPLAY
+
+/datum/client_settings/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ClientSettings", "Client Settings")
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/datum/client_settings/tgui_data(mob/user, tab)
+ if(tab)
+ active_tab = tab
+ var/list/data = list("active_tab" = active_tab, "settings" = list())
+
+ var/client/C = user.client
+
+ // todo: more static data can be moved to tgui_static
+ switch(active_tab)
+ if("keybinds")
+ var/datum/pref/keybinds/P
+ for(var/type in user.client.prefs.prefs_keybinds)
+ P = user.client.prefs.prefs_keybinds[type]
+
+ if(P.admins_only && !C.holder)
+ continue
+ if(P.supporters_only && !C.supporter)
+ continue
+
+ data["settings"] += list(list(
+ "type" = "[P.type]",
+ "name" = P.name,
+ "category" = P.category,
+ "description" = P.description,
+ "value" = P.value,
+ "v_type" = P.value_type,
+ "v_parameters" = P.value_parameters,
+ "default" = P.value == initial(P.value),
+ "admins_only" = P.admins_only,
+ "supporters_only" = P.supporters_only,
+ ))
+ else
+ var/datum/pref/player/P
+ for(var/type in user.client.prefs.prefs_player)
+ P = user.client.prefs.prefs_player[type]
+ if(P.category != active_tab)
+ continue
+
+ if(P.admins_only && !C.holder)
+ continue
+ if(P.supporters_only && !C.supporter)
+ continue
+
+ data["settings"] += list(list(
+ "type" = "[P.type]",
+ "name" = P.name,
+ "description" = P.description,
+ "value" = P.value,
+ "v_type" = P.value_type,
+ "v_parameters" = P.value_parameters,
+ "default" = P.value == initial(P.value),
+ "admins_only" = P.admins_only,
+ "supporters_only" = P.supporters_only,
+ ))
+
+ return data
+
+/datum/client_settings/tgui_static_data(mob/user)
+ var/static/tabs = list(
+ PREF_PLAYER_DISPLAY = "Экран",
+ PREF_PLAYER_EFFECTS = "Эффекты",
+ PREF_PLAYER_AUDIO = "Аудио",
+ PREF_PLAYER_UI = "Интерфейс",
+ PREF_PLAYER_CHAT = "Чат",
+ PREF_PLAYER_GAME = "Игра",
+ PREF_DOMAIN_KEYBINDS = "Управление",
+ )
+
+ var/static/pref_keybinds_ordered = list(
+ PREF_KEYBINDS_CLIENT,
+ PREF_KEYBINDS_COMMUNICATION,
+ PREF_KEYBINDS_MOVEMENT,
+ PREF_KEYBINDS_CARBON,
+ PREF_KEYBINDS_HUMAN,
+ PREF_KEYBINDS_ROBOT,
+ PREF_KEYBINDS_EMOTE,
+ PREF_KEYBINDS_MISC,
+ )
+
+ var/static/tabs_tips = list(
+ PREF_PLAYER_EFFECTS = "Рекомендуется изменять настройки во время игры - так вы сможете сразу увидеть результат.",
+ "keybinds" = "Некоторые клавиши или сочетания могут быть не доступны для назначения, проблема будет решена с Byond 516.",
+ )
+
+ var/list/data = list()
+ data["tabs"] = tabs
+ data["tabs_tips"] = tabs_tips
+ data["keybinds_order"] = pref_keybinds_ordered
+ return data
+
+/datum/client_settings/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ var/client/C = ui.user.client
+
+ if(action == "set_tab")
+ active_tab = params["tab"]
+ return TRUE
+
+ var/datum/pref/pref_type = text2path(params["type"])
+ if(!pref_type)
+ return FALSE
+
+ // first validate that we have permissions to change this pref
+ if(initial(pref_type.admins_only) && !C.holder)
+ return FALSE
+ if(initial(pref_type.supporters_only) && !C.supporter)
+ return FALSE
+
+ if(ispath(pref_type, /datum/pref/player))
+ if(ispath(pref_type, /datum/pref/player/meta))
+ return
+
+ switch(action)
+ if("set_value")
+ C.prefs.set_pref(pref_type, params["value"])
+ if("modify_color_value")
+ var/current_color = C.prefs.get_pref(pref_type)
+ var/new_color = input(C, "Выберите новый цвет", "Выбор цвета", current_color) as color|null
+ if(!new_color)
+ return FALSE
+ else
+ C.prefs.set_pref(pref_type, new_color)
+ if("reset_value")
+ C.prefs.set_pref(pref_type, initial(pref_type.value))
+
+ else if(ispath(pref_type, /datum/pref/keybinds))
+ switch(action)
+ if("set_value")
+ C.prefs.set_keybind_pref(pref_type, params["value"])
+ if("set_keybind_value")
+ C.prefs.set_keybind_pref(pref_type, params["key"], params["index"], params["altMod"], params["ctrlMod"], params["shiftMod"])
+ if("reset_value")
+ C.prefs.set_keybind_pref(pref_type, initial(pref_type.value))
+
+ return TRUE
+
+/datum/client_settings/tgui_state(mob/user)
+ return global.always_state
diff --git a/code/modules/client/client_view.dm b/code/modules/client/client_view.dm
new file mode 100644
index 000000000000..68cd52ec2744
--- /dev/null
+++ b/code/modules/client/client_view.dm
@@ -0,0 +1,111 @@
+// see also: /datum/component/zoom
+
+// fetches prefs and reloads client zoom settings
+// note: this has nothing to do with zoom component, zoom component works with change_view
+/client/proc/update_map_zoom()
+ var/autozoom = prefs.get_pref(/datum/pref/player/display/auto_zoom)
+ var/zoom = autozoom ? 0 : prefs.get_pref(/datum/pref/player/display/zoom) * 0.01
+ var/zoom_mode = prefs.get_pref(/datum/pref/player/display/zoom_mode)
+ winset(src, "tcmapwindow.map", "zoom=[zoom];zoom-mode=[zoom_mode]")
+
+// changes tiles count client can see
+/client/proc/change_view(new_size)
+ if (isnull(new_size))
+ CRASH("change_view called without argument.")
+
+ view = new_size
+ mob.reload_fullscreen()
+
+/client/verb/toggle_fullscreen()
+ set name = "Toggle Fullscreen"
+ set category = "OOC"
+
+ var/fullscreen = prefs.get_pref(/datum/pref/player/display/fullscreen)
+ prefs.set_pref(/datum/pref/player/display/fullscreen, !fullscreen)
+
+ update_fullscreen()
+
+/client/proc/update_fullscreen()
+ set name = "Toggle Fullscreen"
+ set category = "OOC"
+
+ if(prefs.get_pref(/datum/pref/player/display/fullscreen))
+ winset(src, "tcmainwindow", "menu=")
+ winset(src, "tcmainwindow", "is-fullscreen=true")
+ else
+ winset(src, "tcmainwindow", "menu=menu")
+ winset(src, "tcmainwindow", "is-fullscreen=false")
+
+// basically, resizes right column so that map windows don't have black bars
+/client/verb/fit_viewport()
+ set name = "Fit viewport"
+ set category = "OOC"
+ set desc = "Fit the width of the map window to match the viewport"
+
+ if(isnewplayer(mob)) // no tcmapwindow in lobby
+ to_chat(usr, "You can't fix viewport while in lobby.")
+ return
+
+ // Fetch aspect ratio
+ var/view_size = getviewsize(view)
+ var/aspect_ratio = view_size[1] / view_size[2]
+
+ // Calculate desired pixel width using window size and aspect ratio
+ var/list/sizes = params2list(winget(src, "tcmainwindow.split;tcmapwindow", "size"))
+
+ // Client closed the window? Some other error? This is unexpected behaviour, let's
+ // CRASH with some info.
+ if(!sizes["tcmapwindow.size"])
+ CRASH("sizes does not contain tcmapwindow.size key. This means a winget failed to return what we wanted. --- sizes var: [sizes] --- sizes length: [length(sizes)]")
+
+ var/list/map_size = splittext(sizes["tcmapwindow.size"], "x")
+
+ // Looks like we expect tcmapwindow.size to be "ixj" where i and j are numbers.
+ // If we don't get our expected 2 outputs, let's give some useful error info.
+ if(length(map_size) != 2)
+ CRASH("map_size of incorrect length --- map_size var: [map_size] --- map_size length: [length(map_size)]")
+
+ var/height = text2num(map_size[2])
+ var/desired_width = round(height * aspect_ratio)
+ if (text2num(map_size[1]) == desired_width)
+ // Nothing to do
+ return
+
+ var/split_size = splittext(sizes["tcmainwindow.split.size"], "x")
+ var/split_width = text2num(split_size[1])
+
+ // Avoid auto-resizing the statpanel and chat into nothing.
+ desired_width = min(desired_width, split_width - 300)
+
+ // Calculate and apply a best estimate
+ // +4 pixels are for the width of the splitter's handle
+ var/pct = 100 * (desired_width + 4) / split_width
+ winset(src, "tcmainwindow.split", "splitter=[pct]")
+
+ // Apply an ever-lowering offset until we finish or fail
+ var/delta
+ for(var/safety in 1 to 10)
+ var/after_size = winget(src, "tcmapwindow", "size")
+ map_size = splittext(after_size, "x")
+ var/got_width = text2num(map_size[1])
+
+ if (got_width == desired_width)
+ // success
+ return
+ else if (isnull(delta))
+ // calculate a probable delta value based on the difference
+ delta = 100 * (desired_width - got_width) / split_width
+ else if ((delta > 0 && got_width > desired_width) || (delta < 0 && got_width < desired_width))
+ // if we overshot, halve the delta and reverse direction
+ delta = -delta/2
+
+ pct += delta
+ winset(src, "tcmainwindow.split", "splitter=[pct]")
+
+// called automatically from skin on any columns resize,
+/client/verb/handle_autofit()
+ set name = ".handleautofit"
+ set hidden = TRUE
+
+ if(prefs.get_pref(/datum/pref/player/display/auto_fit_viewport) && !isnewplayer(mob))
+ fit_viewport()
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index da3ada93aecb..091322f3e41e 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -1,3 +1,4 @@
+// list contains all clients /datum/preferences so we can keep them persistent in case of client disconnects
var/global/list/datum/preferences/preferences_datums = list()
#define MAX_SAVE_SLOTS 10
@@ -12,9 +13,14 @@ var/global/list/datum/preferences/preferences_datums = list()
// todo: after moving preferences to new datumized system we should rename this to something like client_data
/datum/preferences
var/client/parent
+
+ // list of new datumized preferences
+ var/list/datum/pref/prefs_player = list()
+ var/list/datum/pref/prefs_keybinds = list()
+ var/prefs_dirty = FALSE // list of dirty DOMAINS!!
+
//doohickeys for savefiles
var/path
- var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
var/savefile_version = 0
//non-preference stuff
@@ -32,51 +38,10 @@ var/global/list/datum/preferences/preferences_datums = list()
var/admin_cid_request_cache
var/admin_ip_request_cache
- //game-preferences
- var/UI_style = null
- var/UI_style_color = "#ffffff"
- var/UI_style_alpha = 255
- var/aooccolor = "#b82e00"
- var/ooccolor = "#002eb8"
- var/toggles = TOGGLES_DEFAULT
- var/chat_toggles = TOGGLES_DEFAULT_CHAT
- var/chat_ghostsight = CHAT_GHOSTSIGHT_ALL
- var/lastchangelog = "" //Saved changlog filesize to detect if there was a change
- var/clientfps = -1
-
- // Custom Keybindings
- var/list/key_bindings = list()
- // If hotkey mode is enabled, then clicking the map will automatically
- // unfocus the text bar. This removes the red color from the text bar
- // so that the visual focus indicator matches reality.
- var/hotkeys = TRUE
-
- var/tooltip = TRUE
- var/tooltip_font = "Small Fonts"
- var/tooltip_size = 8
-
- var/outline_enabled = TRUE
- var/outline_color = COLOR_BLUE_LIGHT
- var/eorg_enabled = TRUE
-
- var/show_runechat = TRUE
-
- var/list/custom_emote_panel = list()
-
- //TGUI
- var/tgui_fancy = TRUE
- var/tgui_lock = FALSE
-
- //sound volume preferences
- var/snd_music_vol = 100
- var/snd_ambient_vol = 100
- var/snd_effects_master_vol = 100
- var/snd_effects_voice_announcement_vol = 100
- var/snd_effects_misc_vol = 100
- var/snd_effects_instrument_vol = 100
- var/snd_notifications_vol = 100
- var/snd_admin_vol = 100
- var/snd_jukebox_vol = 100
+ /// Cached list of keybindings, keys mapped to /datum/pref/keybinds
+ var/list/key_bindings_by_key = list()
+
+ var/list/enabled_emotes_emote_panel
//antag preferences
var/list/be_role = list()
@@ -160,25 +125,8 @@ var/global/list/datum/preferences/preferences_datums = list()
var/list/allowed_quirks = list()
- // OOC Metadata:
- var/metadata = ""
var/slot_name = ""
- // Whether or not to use randomized character slots
- var/randomslot = 0
- // jukebox volume
- var/volume = 100
- var/parallax = PARALLAX_HIGH
- var/ambientocclusion = TRUE
- var/auto_fit_viewport = TRUE
- var/lobbyanimation = FALSE
- // lighting settings
- var/glowlevel = GLOW_MED // or bloom
- var/lampsexposure = TRUE // idk how we should name it
- var/lampsglare = FALSE // aka lens flare
- //Impacts performance clientside
- var/eye_blur_effect = TRUE
-
//custom loadout
var/list/gear = list()
var/gear_tab = "General"
@@ -196,18 +144,43 @@ var/global/list/datum/preferences/preferences_datums = list()
if(!parent.holder)
init_chat_bans()
- UI_style = global.available_ui_styles[1]
- custom_emote_panel = global.emotes_for_emote_panel
+ // todo: rewrite this part
+ for(var/datum/pref/player/P as anything in subtypesof(/datum/pref/player))
+ if(initial(P.name))
+ prefs_player[initial(P.type)] = new P
+ for(var/datum/pref/keybinds/P as anything in subtypesof(/datum/pref/keybinds))
+ if(initial(P.name))
+ prefs_keybinds[initial(P.type)] = new P
+
+ var/character_loaded = FALSE
+
if(istype(C))
if(!IsGuestKey(C.key))
load_path(C.ckey)
if(load_preferences())
- if(load_character())
- return
- gender = pick(MALE, FEMALE)
- real_name = random_name(gender)
- key_bindings = deepCopyList(global.hotkey_keybinding_list_by_key) // give them default keybinds too
- C?.set_macros()
+ character_loaded = load_character()
+
+ // part below is init of default parameters
+ // in case if we can't load it from saves
+ if(!character_loaded)
+ gender = pick(MALE, FEMALE)
+ real_name = random_name(gender)
+
+ init_keybinds(C) // todo: update it in load
+ init_emote_panel(C)
+
+/datum/preferences/proc/init_emote_panel(client/client)
+ var/list/disabled_emotes = params2list(get_pref(/datum/pref/player/meta/disabled_emotes_emote_panel))
+ enabled_emotes_emote_panel = global.emotes_for_emote_panel - disabled_emotes
+
+/datum/preferences/proc/init_keybinds(client/client)
+ for(var/type in prefs_keybinds)
+ var/datum/pref/keybinds/KB = prefs_keybinds[type]
+ if(KB.value_type != PREF_TYPE_KEYBIND)
+ continue
+ var/list/keybinds = splittext(KB.value, " ")
+ for(var/key in keybinds)
+ key_bindings_by_key[key] += list(KB)
// reattach existing datum to client if client was disconnected and connects again
/datum/preferences/proc/reattach_to_client(client/client)
@@ -227,12 +200,45 @@ var/global/list/datum/preferences/preferences_datums = list()
return
muted = MUTE_NONE
while(query.NextRow())
- world.log << "NR [query.item[1]] : [mute_ban_bitfield[query.item[1]]]"
muted |= mute_ban_bitfield[query.item[1]]
+// replaced with macro for speed
+///datum/preferences/proc/get_pref(type)
+// return prefs_player[type].value
+
+/datum/preferences/proc/set_pref(type, new_value)
+ var/datum/pref/player/write_pref = prefs_player[type]
+
+ if(write_pref.update_value(new_value, parent))
+ mark_dirty()
+
+/datum/preferences/proc/set_keybind_pref(type, new_value, index, altMod, ctrlMod, shiftMod)
+ var/datum/pref/keybinds/write_pref = prefs_keybinds[type]
+
+ if(index)
+ var/sanitized_key = write_pref.satinize_key(new_value, altMod, ctrlMod, shiftMod)
+ new_value = ""
+ var/list/keybinds = splittext(write_pref.value, " ")
+ for(var/i in 1 to KEYBINDS_MAXIMUM)
+ if(i == index)
+ new_value += sanitized_key
+ else if(i <= keybinds.len)
+ new_value += keybinds[i]
+ new_value += " "
+
+ if(write_pref.update_value(new_value, parent))
+ mark_dirty()
+
+// mark as needed to update
+/datum/preferences/proc/mark_dirty()
+ prefs_dirty = TRUE
+ //SSpreferences.mark_dirty(src)
+
/datum/preferences/proc/ShowChoices(mob/user)
if(!user || !user.client) return
- update_preview_icon()
+
+ // generates and updates preview icon, with tgui update should be replaced for map_view element
+ parent.show_character_previews(generate_preview_icon())
var/dat = ""
dat += ""
@@ -253,11 +259,9 @@ var/global/list/datum/preferences/preferences_datums = list()
dat += "[menu_type=="general"?"General":"General"] - "
dat += "[menu_type=="occupation"?"Occupation":"Occupation"] - "
dat += "[menu_type=="roles"?"Roles":"Roles"] - "
- dat += "[menu_type=="glob"?"Global":"Global"] - "
dat += "[menu_type=="loadout"?"Loadout":"Loadout"] - "
dat += "[menu_type=="quirks"?"Quirks":"Quirks"] - "
dat += "[menu_type=="fluff"?"Fluff":"Fluff"] - "
- dat += "[menu_type=="custom_keybindings"?"Custom Keybindings":"Custom Keybindings"]"
dat += "
Close"
dat += ""
else
@@ -271,8 +275,6 @@ var/global/list/datum/preferences/preferences_datums = list()
dat += ShowOccupation(user)
if("roles")
dat += ShowRoles(user)
- if("glob")
- dat += ShowGlobal(user)
if("load_slot")
dat += ShowLoadSlot(user)
if("loadout")
@@ -281,8 +283,6 @@ var/global/list/datum/preferences/preferences_datums = list()
dat += ShowQuirks(user)
if("fluff")
dat += ShowFluffMenu(user)
- if("custom_keybindings")
- dat += ShowCustomKeybindings(user)
dat += ""
winshow(user, "preferences_window", TRUE)
@@ -312,7 +312,13 @@ var/global/list/datum/preferences/preferences_datums = list()
load_character()
if("changeslot")
- load_character(text2num(href_list["num"]))
+ var/slot = text2num(href_list["num"])
+
+ if(isnum(slot) && slot >= 1 && slot <= GET_MAX_SAVE_SLOTS(parent))
+ set_pref(/datum/pref/player/meta/default_slot, slot)
+ load_character()
+ else
+ to_chat(user, "You have no access to this slot, please contact maintainers.")
if("general")
menu_type = "general"
@@ -335,9 +341,6 @@ var/global/list/datum/preferences/preferences_datums = list()
if("fluff")
menu_type = "fluff"
- if("custom_keybindings")
- menu_type = "custom_keybindings"
-
if("load_slot")
if(!IsGuestKey(user.key))
menu_type = "load_slot"
@@ -356,9 +359,6 @@ var/global/list/datum/preferences/preferences_datums = list()
if("roles")
process_link_roles(user, href_list)
- if("glob")
- process_link_glob(user, href_list)
-
if("loadout")
process_link_loadout(user, href_list)
@@ -369,9 +369,6 @@ var/global/list/datum/preferences/preferences_datums = list()
process_link_fluff(user, href_list)
return 1
- if("custom_keybindings")
- process_link_custom_keybindings(user, href_list)
-
ShowChoices(user)
return 1
@@ -395,7 +392,6 @@ var/global/list/datum/preferences/preferences_datums = list()
character.set_species(species)
character.flavor_text = flavor_text
- character.metadata = metadata
character.med_record = med_record
character.sec_record = sec_record
character.gen_record = gen_record
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 451201be5e9e..1c0c4debe425 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -1,3 +1,5 @@
+var/global/list/legacy_keyname_to_pref = list()
+
//This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped.
#define SAVEFILE_VERSION_MIN 8
@@ -44,36 +46,149 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return SAVEFILE_UP_TO_DATE
-/datum/preferences/proc/update_preferences(current_version, savefile/S)
- /* JUST AN EXAMPLE for future updates.
- if(current_version < 10)
- toggles |= MEMBER_PUBLIC
- */
- if(current_version < 15)
- S["warns"] << null
- S["warnbans"] << null
-
- if(current_version < 16)
- S["aooccolor"] << S["ooccolor"]
- aooccolor = ooccolor
-
- if(current_version < 25)
- var/const/SOUND_ADMINHELP = 1
- var/const/SOUND_MIDI = 2
- var/const/SOUND_AMBIENCE = 4
- var/const/SOUND_LOBBY = 8
- var/const/SOUND_STREAMING = 64
-
- toggles &= ~(SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_STREAMING)
- S["toggles"] << toggles
- if(current_version < 26)
- for(var/role in be_role)
- if(!CanBeRole(role))
- be_role -= role
-
- if(current_version < 44)
- custom_emote_panel = global.emotes_for_emote_panel
+/datum/preferences/proc/convert_preferences(savefile/S)
+ // audio
+ set_pref(/datum/pref/player/audio/lobby, S["snd_music_vol"])
+ set_pref(/datum/pref/player/audio/ambient, S["snd_ambient_vol"])
+ set_pref(/datum/pref/player/audio/notifications, S["snd_notifications_vol"])
+ set_pref(/datum/pref/player/audio/admin_sounds, S["snd_admin_vol"])
+ set_pref(/datum/pref/player/audio/jukebox, S["snd_jukebox_vol"])
+
+ set_pref(/datum/pref/player/audio/effects, S["snd_effects_master_vol"])
+ var/effects_coeff = S["snd_effects_master_vol"] * 0.01
+ set_pref(/datum/pref/player/audio/voice_announcements, S["snd_effects_voice_announcement_vol"] * effects_coeff)
+ set_pref(/datum/pref/player/audio/instruments, S["snd_effects_instrument_vol"] * effects_coeff)
+ set_pref(/datum/pref/player/audio/spam_effects, S["snd_effects_misc_vol"]) // no coefficient as this still depends on the effects audio slider
+
+ // ui
+ set_pref(/datum/pref/player/display/auto_fit_viewport, S["auto_fit_viewport"])
+ set_pref(/datum/pref/player/ui/ui_style, S["UI_style"])
+ set_pref(/datum/pref/player/ui/ui_style_color, S["UI_style_color"])
+ var/converted_alpha = 100 - floor(100*S["UI_style_alpha"]/255)
+ set_pref(/datum/pref/player/ui/ui_style_opacity, converted_alpha)
+ set_pref(/datum/pref/player/ui/outline, S["outline_enabled"])
+ set_pref(/datum/pref/player/ui/outline_color, S["outline_color"])
+ set_pref(/datum/pref/player/ui/runechat, S["show_runechat"])
+ set_pref(/datum/pref/player/ui/tooltip, S["tooltip"])
+ set_pref(/datum/pref/player/ui/tooltip_font, S["tooltip_font"])
+ set_pref(/datum/pref/player/ui/tooltip_size, S["tooltip_size"])
+
+ //set_pref(/datum/pref/player/ui/..., S["tgui_fancy"]) // removed, we don't support ie8 already and 516 is coming
+ set_pref(/datum/pref/player/ui/tgui_lock, S["tgui_lock"])
+
+ // graphics
+ var/converted_fps = S["clientfps"] == -1 ? RECOMMENDED_FPS : S["clientfps"] // before -1 was for default, but it's confusing and we don't change it too often
+ set_pref(/datum/pref/player/display/fps, converted_fps)
+
+ var/converted_parallax
+ switch(S["parallax"])
+ if(-1)
+ converted_parallax = PARALLAX_INSANE
+ if(0)
+ converted_parallax = PARALLAX_HIGH
+ if(1)
+ converted_parallax = PARALLAX_MED
+ if(2)
+ converted_parallax = PARALLAX_LOW
+ if(3)
+ converted_parallax = PARALLAX_DISABLE
+ set_pref(/datum/pref/player/effects/parallax, converted_parallax)
+ set_pref(/datum/pref/player/effects/lobbyanimation, S["lobbyanimation"])
+
+ var/converted_blur_effect = !S["eye_blur_effect"]
+ set_pref(/datum/pref/player/effects/legacy_blur, converted_blur_effect)
+
+ set_pref(/datum/pref/player/effects/ambientocclusion, S["ambientocclusion"])
+
+ var/converted_glowlevel
+ switch(S["glowlevel"])
+ if(0)
+ converted_glowlevel = GLOW_HIGH
+ if(1)
+ converted_glowlevel = GLOW_MED
+ if(2)
+ converted_glowlevel = GLOW_LOW
+ if(3)
+ converted_glowlevel = GLOW_DISABLE
+ set_pref(/datum/pref/player/effects/glowlevel, converted_glowlevel)
+ set_pref(/datum/pref/player/effects/lampsexposure, S["lampsexposure"])
+ set_pref(/datum/pref/player/effects/lampsglare, S["lampsglare"])
+
+ // game
+ #define SHOW_ANIMATIONS 16
+ #define SHOW_PROGBAR 32
+ set_pref(/datum/pref/player/game/melee_animation, S["toggles"] & SHOW_ANIMATIONS)
+ set_pref(/datum/pref/player/game/progressbar, S["toggles"] & SHOW_PROGBAR)
+ #undef SHOW_ANIMATIONS
+ #undef SHOW_PROGBAR
+
+ set_pref(/datum/pref/player/game/endroundarena, S["eorg_enabled"])
+
+ // chat
+ set_pref(/datum/pref/player/chat/ooccolor, S["ooccolor"])
+ set_pref(/datum/pref/player/chat/aooccolor, S["aooccolor"])
+
+ var/const/CHAT_OOC = 1
+ var/const/CHAT_DEAD = 2
+ var/const/CHAT_GHOSTEARS = 4 // merged into /ghostears
+ //var/const/CHAT_NOCLIENT_ATTACK = 8 // merged into new /attack_log
+ var/const/CHAT_PRAYER = 16
+ var/const/CHAT_RADIO = 32
+ //var/const/CHAT_ATTACKLOGS = 64 // merged into new /attack_log
+ var/const/CHAT_DEBUGLOGS = 128
+ var/const/CHAT_LOOC = 256
+ var/const/CHAT_GHOSTRADIO = 512
+ //var/const/CHAT_GHOSTNPC = 1024 // merged into new /ghostantispam
+ var/const/CHAT_CKEY = 2048
+
+ set_pref(/datum/pref/player/chat/ooc, S["chat_toggles"] & CHAT_OOC)
+ set_pref(/datum/pref/player/chat/dead, S["chat_toggles"] & CHAT_DEAD)
+ set_pref(/datum/pref/player/chat/ghostears, S["chat_toggles"] & CHAT_GHOSTEARS)
+ set_pref(/datum/pref/player/chat/prayers, S["chat_toggles"] & CHAT_PRAYER)
+ set_pref(/datum/pref/player/chat/radio, S["chat_toggles"] & CHAT_RADIO)
+ set_pref(/datum/pref/player/chat/debug_log, S["chat_toggles"] & CHAT_DEBUGLOGS)
+ set_pref(/datum/pref/player/chat/looc, S["chat_toggles"] & CHAT_LOOC)
+ set_pref(/datum/pref/player/chat/ghostradio, S["chat_toggles"] & CHAT_GHOSTRADIO)
+ set_pref(/datum/pref/player/chat/show_ckey, S["chat_toggles"] & CHAT_CKEY)
+
+ var/const/CHAT_GHOSTSIGHT_ALL = 1
+ //var/const/CHAT_GHOSTSIGHT_ALLMANUAL = 2 // merged into new /ghostantispam
+ var/const/CHAT_GHOSTSIGHT_NEARBYMOBS = 3
+
+ var/converted_chat_ghostsight
+ switch(S["chat_ghostsight"])
+ if(CHAT_GHOSTSIGHT_ALL)
+ converted_chat_ghostsight = TRUE
+ if(CHAT_GHOSTSIGHT_NEARBYMOBS)
+ converted_chat_ghostsight = FALSE
+ set_pref(/datum/pref/player/chat/ghostsight, converted_chat_ghostsight)
+
+ // meta domain
+ set_pref(/datum/pref/player/meta/lastchangelog, S["lastchangelog"])
+ set_pref(/datum/pref/player/meta/default_slot, S["default_slot"])
+ set_pref(/datum/pref/player/meta/random_slot, S["randomslot"])
+ set_pref(/datum/pref/player/game/hotkey_mode, S["hotkeys"])
+
+ // emote panel
+ var/list/old_emotes_list = S["emote_panel"]
+ if(length(old_emotes_list))
+ var/list/disabled_emotes = global.emotes_for_emote_panel - old_emotes_list
+ set_pref(/datum/pref/player/meta/disabled_emotes_emote_panel, disabled_emotes)
+
+ // keibinds
+ var/list/old_keybinds = S["key_bindings"]
+ var/list/keyname_to_bind = list()
+ for(var/key in old_keybinds)
+ for(var/keyname in old_keybinds[key])
+ keyname_to_bind[keyname] = "[keyname_to_bind[keyname] ? "[keyname_to_bind[keyname]] " : "" ][key]"
+
+ for(var/keyname in keyname_to_bind)
+ var/pref_type = legacy_keyname_to_pref[keyname]
+ if(keyname == "None")
+ set_keybind_pref(pref_type, "")
+ else
+ set_keybind_pref(pref_type, keyname_to_bind[keyname])
/datum/preferences/proc/update_character(current_version, savefile/S)
if(current_version < 17)
@@ -118,6 +233,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
player_alt_titles -= J.title
+ if(current_version < 26)
+ for(var/role in be_role)
+ if(!CanBeRole(role))
+ be_role -= role
+
if(current_version < 27)
job_preferences = list() //It loaded null from nonexistant savefile field.
var/job_civilian_high = 0
@@ -196,7 +316,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
med_record = fix_cyrillic(med_record)
sec_record = fix_cyrillic(sec_record)
gen_record = fix_cyrillic(gen_record)
- metadata = fix_cyrillic(metadata)
home_system = fix_cyrillic(home_system)
citizenship = fix_cyrillic(citizenship)
faction = fix_cyrillic(faction)
@@ -206,7 +325,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["med_record"] << med_record
S["sec_record"] << sec_record
S["gen_record"] << gen_record
- S["OOC_Notes"] << metadata
S["home_system"] << home_system
S["citizenship"] << citizenship
S["faction"] << faction
@@ -298,49 +416,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
ResetQuirks()
break
-/// checks through keybindings for outdated unbound keys and updates them
-/datum/preferences/proc/check_keybindings()
- if(!parent)
- return
-
- // When loading from savefile key_binding can be null
- // This happens when player had savefile created before new kb system, but hotkeys was not saved
- if(!length(key_bindings))
- key_bindings = deepCopyList(global.hotkey_keybinding_list_by_key) // give them default keybinds too
-
- var/list/user_binds = list()
- for (var/key in key_bindings)
- for(var/kb_name in key_bindings[key])
- user_binds[kb_name] += list(key)
- var/list/notadded = list()
- for (var/name in global.keybindings_by_name)
- var/datum/keybinding/kb = global.keybindings_by_name[name]
- if(length(user_binds[kb.name]))
- continue // key is unbound and or bound to something
- var/addedbind = FALSE
- for(var/hotkeytobind in kb.hotkey_keys)
- if(!length(key_bindings[hotkeytobind]))
- LAZYADD(key_bindings[hotkeytobind], kb.name)
- addedbind = TRUE
- if(!addedbind)
- notadded += kb
- if(length(notadded))
- addtimer(CALLBACK(src, PROC_REF(announce_conflict), notadded), 5 SECONDS)
-
-/datum/preferences/proc/announce_conflict(list/notadded)
- to_chat(parent, "KEYBINDING CONFLICT!!!\n\
- There are new keybindings that have defaults bound to keys you already set, They will default to Unbound. You can bind them in Setup Character or Game Preferences\n\
- Or you can click here to go straight to the keybindings page")
- for(var/item in notadded)
- var/datum/keybinding/conflicted = item
- to_chat(parent, "[conflicted.category]: [conflicted.full_name] needs updating")
- LAZYADD(key_bindings["None"], conflicted.name) // set it to unbound to prevent this from opening up again in the future
-
/datum/preferences/proc/load_path(ckey, filename = "preferences.sav")
if(!ckey)
return
path = "data/player_saves/[ckey[1]]/[ckey]/[filename]"
+// loads and updates all old preferences if needed
/datum/preferences/proc/load_preferences()
if(!path)
return 0
@@ -351,120 +432,25 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return 0
S.cd = "/"
+ // convert old preferences to datumized system, if we haven't done so already
+ //if
+ convert_preferences(S)
+
+ // todo new update and examples
+
var/needs_update = savefile_needs_update(S)
if(needs_update == SAVEFILE_TOO_OLD) // fatal, can't load any data
return 0
- //General preferences
- S["ooccolor"] >> ooccolor
- S["aooccolor"] >> aooccolor
- S["lastchangelog"] >> lastchangelog
- S["UI_style"] >> UI_style
- S["UI_style_color"] >> UI_style_color
- S["UI_style_alpha"] >> UI_style_alpha
- S["clientfps"] >> clientfps
- S["default_slot"] >> default_slot
- S["chat_toggles"] >> chat_toggles
- S["toggles"] >> toggles
- S["chat_ghostsight"] >> chat_ghostsight
- S["randomslot"] >> randomslot
- S["parallax"] >> parallax
- S["ambientocclusion"] >> ambientocclusion
- S["glowlevel"] >> glowlevel
- S["lampsexposure"] >> lampsexposure
- S["lampsglare"] >> lampsglare
- S["eye_blur_effect"] >> eye_blur_effect
- S["auto_fit_viewport"] >> auto_fit_viewport
- S["lobbyanimation"] >> lobbyanimation
- S["tooltip"] >> tooltip
- S["tooltip_size"] >> tooltip_size
- S["tooltip_font"] >> tooltip_font
- S["outline_enabled"] >> outline_enabled
- S["outline_color"] >> outline_color
- S["eorg_enabled"] >> eorg_enabled
- S["show_runechat"] >> show_runechat
- S["emote_panel"] >> custom_emote_panel
-
- // Custom hotkeys
- S["key_bindings"] >> key_bindings
- check_keybindings()
- S["hotkeys"] >> hotkeys
-
- //TGUI
- S["tgui_fancy"] >> tgui_fancy
- S["tgui_lock"] >> tgui_lock
-
- //Sound preferences
- S["snd_music_vol"] >> snd_music_vol
- S["snd_ambient_vol"] >> snd_ambient_vol
- S["snd_effects_master_vol"] >> snd_effects_master_vol
- S["snd_effects_voice_announcement_vol"] >> snd_effects_voice_announcement_vol
- S["snd_effects_misc_vol"] >> snd_effects_misc_vol
- S["snd_effects_instrument_vol"] >> snd_effects_instrument_vol
- S["snd_notifications_vol"] >> snd_notifications_vol
- S["snd_admin_vol"] >> snd_admin_vol
- S["snd_jukebox_vol"] >> snd_jukebox_vol
-
- //*** FOR FUTURE UPDATES, SO YOU KNOW WHAT TO DO ***//
- //try to fix any outdated data if necessary
- if(needs_update >= 0)
- update_preferences(needs_update, S) // needs_update = savefile_version if we need an update (positive integer)
-
- //Sanitize
- ooccolor = normalize_color(sanitize_hexcolor(ooccolor, initial(ooccolor)))
- aooccolor = normalize_color(sanitize_hexcolor(aooccolor, initial(aooccolor)))
- lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
- UI_style = sanitize_inlist(UI_style, global.available_ui_styles, global.available_ui_styles[1])
- clientfps = sanitize_integer(clientfps, -1, 1000, -1)
- default_slot = sanitize_integer(default_slot, 1, GET_MAX_SAVE_SLOTS(parent), initial(default_slot))
- toggles = sanitize_integer(toggles, 0, 65535, initial(toggles))
- chat_toggles = sanitize_integer(chat_toggles, 0, 65535, initial(chat_toggles))
- chat_ghostsight = sanitize_integer(chat_ghostsight, CHAT_GHOSTSIGHT_ALL, CHAT_GHOSTSIGHT_NEARBYMOBS, CHAT_GHOSTSIGHT_ALL)
- randomslot = sanitize_integer(randomslot, 0, 1, initial(randomslot))
- 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))
- key_bindings = sanitize_keybindings(key_bindings)
- hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
- tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
- tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
- parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, PARALLAX_HIGH)
- ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
- glowlevel = sanitize_integer(glowlevel, GLOW_HIGH, GLOW_DISABLE, initial(glowlevel))
- eye_blur_effect = sanitize_integer(eye_blur_effect, 0, 1, initial(eye_blur_effect))
- lampsexposure = sanitize_integer(lampsexposure, 0, 1, initial(lampsexposure))
- lampsglare = sanitize_integer(lampsglare, 0, 1, initial(lampsglare))
- lobbyanimation = sanitize_integer(lobbyanimation, 0, 1, initial(lobbyanimation))
- auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport))
- tooltip = sanitize_integer(tooltip, 0, 1, initial(tooltip))
- tooltip_size = sanitize_integer(tooltip_size, 1, 15, initial(tooltip_size))
- outline_enabled = sanitize_integer(outline_enabled, 0, 1, initial(outline_enabled))
- outline_color = normalize_color(sanitize_hexcolor(outline_color, initial(outline_color)))
- eorg_enabled = sanitize_integer(eorg_enabled, 0, 1, initial(eorg_enabled))
- show_runechat = sanitize_integer(show_runechat, 0, 1, initial(show_runechat))
- custom_emote_panel = sanitize_emote_panel(custom_emote_panel)
-
- snd_music_vol = sanitize_integer(snd_music_vol, 0, 100, initial(snd_music_vol))
- snd_ambient_vol = sanitize_integer(snd_ambient_vol, 0, 100, initial(snd_ambient_vol))
- snd_effects_master_vol = sanitize_integer(snd_effects_master_vol, 0, 100, initial(snd_effects_master_vol))
- snd_effects_voice_announcement_vol = sanitize_integer(snd_effects_voice_announcement_vol, 0, 100, initial(snd_effects_voice_announcement_vol))
- snd_effects_misc_vol = sanitize_integer(snd_effects_misc_vol, 0, 100, initial(snd_effects_misc_vol))
- snd_effects_instrument_vol = sanitize_integer(snd_effects_instrument_vol, 0, 100, initial(snd_effects_instrument_vol))
- snd_notifications_vol = sanitize_integer(snd_notifications_vol, 0, 100, initial(snd_notifications_vol))
- snd_admin_vol = sanitize_integer(snd_admin_vol, 0, 100, initial(snd_admin_vol))
- snd_jukebox_vol = sanitize_integer(snd_jukebox_vol, 0, 100, initial(snd_jukebox_vol))
-
if(needs_update >= 0) //save the updated version
- var/old_default_slot = default_slot
for (var/slot in S.dir) //but first, update all current character slots.
if (copytext(slot, 1, 10) != "character")
continue
var/slotnum = text2num(copytext(slot, 10))
if (!slotnum)
continue
- default_slot = slotnum
- if (load_character())
- save_character()
- default_slot = old_default_slot
+ if (load_character(slotnum)) // loads and updates character
+ save_character(slotnum)
save_preferences()
return 1
@@ -479,55 +465,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["version"] << SAVEFILE_VERSION_MAX
- //general preferences
- S["ooccolor"] << ooccolor
- S["aooccolor"] << aooccolor
- S["lastchangelog"] << lastchangelog
- S["UI_style"] << UI_style
- S["UI_style_color"] << UI_style_color
- S["UI_style_alpha"] << UI_style_alpha
- S["clientfps"] << clientfps
- S["default_slot"] << default_slot
- S["toggles"] << toggles
- S["chat_toggles"] << chat_toggles
- S["chat_ghostsight"] << chat_ghostsight
- S["randomslot"] << randomslot
- S["parallax"] << parallax
- S["ambientocclusion"] << ambientocclusion
- S["glowlevel"] << glowlevel
- S["eye_blur_effect"] << eye_blur_effect
- S["lampsexposure"] << lampsexposure
- S["lampsglare"] << lampsglare
- S["lobbyanimation"] << lobbyanimation
- S["auto_fit_viewport"] << auto_fit_viewport
- S["tooltip"] << tooltip
- S["tooltip_size"] << tooltip_size
- S["tooltip_font"] << tooltip_font
- S["emote_panel"] << custom_emote_panel
-
-
- // Custom hotkeys
- S["key_bindings"] << key_bindings
- S["hotkeys"] << hotkeys
-
- S["outline_enabled"] << outline_enabled
- S["outline_color"] << outline_color
- S["eorg_enabled"] << eorg_enabled
- S["show_runechat"] << show_runechat
- //TGUI
- S["tgui_fancy"] << tgui_fancy
- S["tgui_lock"] << tgui_lock
-
- //Sound preferences
- S["snd_music_vol"] << snd_music_vol
- S["snd_ambient_vol"] << snd_ambient_vol
- S["snd_effects_master_vol"] << snd_effects_master_vol
- S["snd_effects_voice_announcement_vol"] << snd_effects_voice_announcement_vol
- S["snd_effects_misc_vol"] << snd_effects_misc_vol
- S["snd_effects_instrument_vol"] << snd_effects_instrument_vol
- S["snd_notifications_vol"] << snd_notifications_vol
- S["snd_admin_vol"] << snd_admin_vol
- S["snd_jukebox_vol"] << snd_jukebox_vol
return 1
/datum/preferences/proc/load_saved_character(dir)
@@ -541,7 +478,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return 0
//Character
- S["OOC_Notes"] >> metadata
S["real_name"] >> real_name
S["name_is_always_random"] >> be_random_name
S["gender"] >> gender
@@ -624,7 +560,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
repetitive_updates_character(needs_update, S)
//Sanitize
- metadata = sanitize_text(metadata, initial(metadata))
real_name = sanitize_name(real_name)
if(isnull(species))
@@ -721,28 +656,29 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return 0
S.cd = "/"
if(!slot)
- slot = default_slot
- slot = sanitize_integer(slot, 1, GET_MAX_SAVE_SLOTS(parent), initial(default_slot))
- if(slot != default_slot)
- default_slot = slot
- S["default_slot"] << slot
+ slot = get_pref(/datum/pref/player/meta/default_slot)
+ if(!slot)
+ CRASH("Attempt to access saves with empty slot")
S.cd = "/character[slot]"
load_saved_character(S.cd)
return 1
-/datum/preferences/proc/save_character()
+/datum/preferences/proc/save_character(slot)
if(!path)
return 0
var/savefile/S = new /savefile(path)
if(!S)
return 0
- S.cd = "/character[default_slot]"
+ if(!slot)
+ slot = get_pref(/datum/pref/player/meta/default_slot)
+ if(!slot)
+ CRASH("Attempt to access saves with empty slot")
+ S.cd = "/character[slot]"
S["version"] << SAVEFILE_VERSION_MAX // load_character will sanitize any bad data, so assume up-to-date.
//Character
- S["OOC_Notes"] << metadata
S["real_name"] << real_name
S["name_is_always_random"] << be_random_name
S["gender"] << gender
@@ -814,14 +750,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return 1
-/proc/sanitize_keybindings(value)
- var/list/base_bindings = sanitize_islist(value,list())
- for(var/key in base_bindings)
- base_bindings[key] = base_bindings[key] & global.keybindings_by_name
- if(!length(base_bindings[key]))
- base_bindings -= key
- return base_bindings
-
/proc/sanitize_emote_panel(value)
var/list/emote_panel = SANITIZE_LIST(value)
var/list/sanitized_emote_panel = list()
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index cbe84d100dea..05c3a223840d 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -1,86 +1,8 @@
//toggles
-/client/verb/toggle_ghost_ears()
- set name = "Show/Hide GhostEars"
- set category = "Preferences"
- set desc = ".Toggle Between seeing all mob speech, and only speech of nearby mobs."
- prefs.chat_toggles ^= CHAT_GHOSTEARS
- to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"].")
- prefs.save_preferences()
- feedback_add_details("admin_verb","TGE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/toggle_ghost_npc()
- set name = "Show/Hide GhostNPCsSpeech"
- set category = "Preferences"
- set desc = ".Toggle Between seeing all non-player mobs speech, and only speech of nearby non-player mobs."
- prefs.chat_toggles ^= CHAT_GHOSTNPC
- to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTNPC) ? "see all non-player mobs speech in the world" : "only see speech from nearby non-player mobs"].")
- prefs.save_preferences()
- feedback_add_details("admin_verb","TGN")
-
-/client/verb/toggle_ghost_radio()
- set name = "Enable/Disable GhostRadio"
- set category = "Preferences"
- set desc = ".Toggle between hearing all radio chatter, or only from nearby speakers."
- prefs.chat_toggles ^= CHAT_GHOSTRADIO
- to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTRADIO) ? "hear all radio chat in the world" : "only hear from nearby speakers"].")
- prefs.save_preferences()
- feedback_add_details("admin_verb","TGR")
-
-/client/proc/toggle_hear_radio()
- set name = "Show/Hide RadioChatter"
- set category = "Preferences"
- set desc = "Toggle seeing radiochatter from radios and speakers."
- if(!holder) return
- prefs.chat_toggles ^= CHAT_RADIO
- prefs.save_preferences()
- to_chat(usr, "You will [(prefs.chat_toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers")
- feedback_add_details("admin_verb","THR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/deadchat() // Deadchat toggle is usable by anyone.
- set name = "Show/Hide Deadchat"
- set category = "Preferences"
- set desc ="Toggles seeing deadchat."
- prefs.chat_toggles ^= CHAT_DEAD
- prefs.save_preferences()
-
- if(src.holder)
- to_chat(src, "You will [(prefs.chat_toggles & CHAT_DEAD) ? "now" : "no longer"] see deadchat.")
- else
- to_chat(src, "As a ghost, you will [(prefs.chat_toggles & CHAT_DEAD) ? "now" : "no longer"] see deadchat.")
-
- feedback_add_details("admin_verb","TDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/proc/toggleprayers()
- set name = "Show/Hide Prayers"
- set category = "Preferences"
- set desc = "Toggles seeing prayers."
- prefs.chat_toggles ^= CHAT_PRAYER
- prefs.save_preferences()
- to_chat(src, "You will [(prefs.chat_toggles & CHAT_PRAYER) ? "now" : "no longer"] see prayerchat.")
- feedback_add_details("admin_verb","TP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/listen_ooc()
- set name = "Show/Hide OOC"
- set category = "Preferences"
- set desc = "Toggles seeing OutOfCharacter chat."
- prefs.chat_toggles ^= CHAT_OOC
- prefs.save_preferences()
- to_chat(src, "You will [(prefs.chat_toggles & CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel.")
- feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/listen_looc()
- set name = "Show/Hide LOOC"
- set category = "Preferences"
- set desc = "Toggles seeing Local OutOfCharacter chat."
- prefs.chat_toggles ^= CHAT_LOOC
- prefs.save_preferences()
-
- to_chat(src, "You will [(prefs.chat_toggles & CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel.")
- feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/verb/toggle_be_role(role in special_roles)
set name = "Toggle SpecialRole Candidacy"
- set category = "Preferences"
+ set category = "OOC"
set desc = "Toggles which special roles you would like to be a candidate for, during events."
var/role_type = role
if(!role_type) return
@@ -94,7 +16,7 @@
/client/verb/toggle_ignored_role()
set name = "Toggle Ignore Roles"
- set category = "Preferences"
+ set category = "OOC"
set desc = "Toggles ignore questions"
var/role = input(usr, "Ignored Qustions for Roles in current Round:") as null|anything in prefs.ignore_question
@@ -104,329 +26,9 @@
to_chat(src, "You will receive requests for \"[role]\" again")
feedback_add_details("admin_verb","TBeSpecialIgnore") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/verb/change_ui()
- set name = "Change UI"
- set category = "Preferences"
- set desc = "Configure your user interface."
-
- if(!ishuman(usr))
- to_chat(usr, "This only for human")
- return
-
- var/UI_style_new = input(usr, "Select a style, we recommend White for customization") as null|anything in sortList(global.available_ui_styles)
- if(!UI_style_new)
- return
-
- var/UI_alpha_new = input(usr, "Select a new alpha(transparence) parametr for UI, between 50 and 255") as num
- if(!UI_alpha_new || !(UI_alpha_new <= 255 && UI_alpha_new >= 50))
- return
-
- var/UI_color_new = input(usr, "Choose your UI color, dark colors are not recommended!") as color|null
- if(!UI_color_new)
- return
-
- var/datum/hud/hud = usr.hud_used
-
- //update UI
- var/list/screens = hud.main + hud.adding + hud.hotkeybuttons
-
- for(var/atom/movable/screen/complex/complex as anything in hud.complex)
- screens += complex.screens
-
- var/ui_style = ui_style2icon(UI_style_new)
- var/list/icon_states = icon_states(ui_style) // so it wont break hud with dmi that has no specific icon_state.
-
- hud.ui_style = ui_style
- hud.ui_color = UI_color_new
- hud.ui_alpha = UI_alpha_new
-
- for(var/atom/movable/screen/screen as anything in screens)
- if(screen.alpha && (screen.icon_state in icon_states))
- screen.update_by_hud(hud)
-
- if(tgui_alert(usr, "Like it? Save changes?",, list("Yes", "No")) == "Yes")
- prefs.UI_style = UI_style_new
- prefs.UI_style_alpha = UI_alpha_new
- prefs.UI_style_color = UI_color_new
- prefs.save_preferences()
- to_chat(usr, "UI was saved")
- return
-
- hud.ui_style = ui_style2icon(prefs.UI_style)
- hud.ui_color = prefs.UI_style_color
- hud.ui_alpha = prefs.UI_style_alpha
-
- for(var/atom/movable/screen/screen as anything in screens)
- if(screen.alpha && (screen.icon_state in icon_states))
- screen.update_by_hud(hud)
-
-/client/verb/toggle_lobby_animation()
- set name = "Toggle Lobby Animation"
- set category = "Preferences"
- set desc = "Toggles lobby animations."
- prefs.lobbyanimation = !prefs.lobbyanimation
- prefs.save_preferences()
- if(isnewplayer(mob))
- var/mob/dead/new_player/M = mob
- M.show_titlescreen()
- if(prefs.lobbyanimation)
- to_chat(src, "You have enabled lobby animation.")
- else
- to_chat(src, "You have disabled lobby animation.")
-
-/client/verb/toggle_anim_attacks()
- set name = "Show/Hide Melee Animations"
- set category = "Preferences"
- set desc = "Toggles seeing melee attack animations."
- prefs.toggles ^= SHOW_ANIMATIONS
- prefs.save_preferences()
- to_chat(src, "You will [(prefs.toggles & SHOW_ANIMATIONS) ? "now" : "no longer"] see melee attack animations.")
- feedback_add_details("admin_verb","MAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/toggle_progress_bar()
- set name = "Show/Hide Progress Bar"
- set category = "Preferences"
- set desc = "Toggles visibility of progress bars."
- prefs.toggles ^= SHOW_PROGBAR
- prefs.save_preferences()
- to_chat(src, "You will [(prefs.toggles & SHOW_PROGBAR) ? "now" : "no longer"] see progress bars.")
- feedback_add_details("admin_verb","PRB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/set_ckey_show()
- set name = "Show/Hide Ckey"
- set desc = "Toggle between showing your Ckey in LOOC and dead chat."
- set category = "Preferences"
- prefs.chat_toggles ^= CHAT_CKEY
- prefs.save_preferences()
- to_chat(src, "You will [(prefs.chat_toggles & CHAT_CKEY) ? "now" : "no longer"] show your ckey in LOOC and deadchat.")
- feedback_add_details("admin_verb","SC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/toggle_ambient_occlusion()
- set name = "Toggle Ambient Occlusion"
- set category = "Preferences"
- set desc = "Toggle ambient occlusion."
-
- prefs.ambientocclusion = !prefs.ambientocclusion
- to_chat(src, "Ambient Occlusion: [prefs.ambientocclusion ? "Enabled" : "Disabled"].")
- prefs.save_preferences()
- update_plane_masters(/atom/movable/screen/plane_master/game_world)
- feedback_add_details("admin_verb","TAC")
-
-/client/verb/set_glow_level()
- set name = "Lighting: Glow Level"
- set category = "Preferences"
-
- var/new_setting = input(src, "Set glow level of light sources:") as null|anything in list("Disable", "Low", "Medium (Default)", "High")
- if(!new_setting)
- return
-
- switch(new_setting)
- if("Disable")
- prefs.glowlevel = GLOW_DISABLE
- if("Low")
- prefs.glowlevel = GLOW_LOW
- if("Medium (Default)")
- prefs.glowlevel = GLOW_MED
- if("High")
- prefs.glowlevel = GLOW_HIGH
-
- to_chat(src, "Glow level: [new_setting].")
- prefs.save_preferences()
- update_plane_masters(/atom/movable/screen/plane_master/lamps_selfglow)
- feedback_add_details("admin_verb","LGL")
-
-/client/verb/toggle_lamp_exposure()
- set name = "Lighting: Lamp Exposure"
- set category = "Preferences"
-
- prefs.lampsexposure = !prefs.lampsexposure
- to_chat(src, "Lamp exposure: [prefs.lampsexposure ? "Enabled" : "Disabled"].")
- prefs.save_preferences()
- update_plane_masters(/atom/movable/screen/plane_master/exposure)
- feedback_add_details("admin_verb","LEXP")
-
-/client/verb/toggle_lamps_glare()
- set name = "Lighting: Lamp Glare"
- set category = "Preferences"
-
- prefs.lampsglare = !prefs.lampsglare
- to_chat(src, "Glare: [prefs.lampsglare ? "Enabled" : "Disabled"].")
- prefs.save_preferences()
- update_plane_masters(/atom/movable/screen/plane_master/lamps_glare)
- feedback_add_details("admin_verb","GLR")
-
-/client/verb/eye_blur_effect()
- set name = "Blur effect"
- set category = "Preferences"
-
- prefs.eye_blur_effect = !prefs.eye_blur_effect
- to_chat(src, "Blur effect: [prefs.eye_blur_effect ? "Enabled" : "Old design"].")
- prefs.save_preferences()
- update_plane_masters(/atom/movable/screen/plane_master/game_world)
- feedback_add_details("admin_verb","EBE")
-
-/client/verb/set_parallax_quality()
- set name = "Set Parallax Quality"
- set category = "Preferences"
- set desc = "Set space parallax quality."
-
- var/new_setting = input(src, "Parallax quality:") as null|anything in list("Disable", "Low", "Medium", "High", "Insane")
- if(!new_setting)
- return
-
- switch(new_setting)
- if("Disable")
- prefs.parallax = PARALLAX_DISABLE
- if("Low")
- prefs.parallax = PARALLAX_LOW
- if("Medium")
- prefs.parallax = PARALLAX_MED
- if("High")
- prefs.parallax = PARALLAX_HIGH
- if("Insane")
- prefs.parallax = PARALLAX_INSANE
-
- to_chat(src, "Parallax (Fancy Space): [new_setting].")
- prefs.save_preferences()
- feedback_add_details("admin_verb","TPX")
-
- if (mob && mob.hud_used)
- mob.hud_used.update_parallax_pref()
-
-/client/verb/toggle_ghost_sight()
- set name = "Change Ghost Sight Options"
- set category = "Preferences"
- set desc = "Toggle between seeing all mob emotes, all manual-only emotes and only emotes of nearby mobs."
-
- var/new_setting = input(src, "Ghost Sight Options:") as null|anything in list("Absolutely all emotes", "All manual-only", "Only emotes of nearby mobs")
- if(!new_setting)
- return
-
- switch(new_setting)
- if("Absolutely all emotes")
- to_chat(src, "As a ghost, you will now see absolutely all emotes in the world.")
- prefs.chat_ghostsight = CHAT_GHOSTSIGHT_ALL
- if("All manual-only")
- to_chat(src, "As a ghost, you will now see all manual-only(me, *emote, etc) emotes in the world.")
- prefs.chat_ghostsight = CHAT_GHOSTSIGHT_ALLMANUAL
- if("Only emotes of nearby mobs")
- to_chat(src, "As a ghost, you will now see only see emotes from nearby mobs")
- prefs.chat_ghostsight = CHAT_GHOSTSIGHT_NEARBYMOBS
-
- prefs.save_preferences()
- feedback_add_details("admin_verb","CGSO") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/verb/toggle_fancy_tgui()
- set name = "Toggle Fancy TGUI"
- set category = "Preferences"
- set desc = "Toggle Fancy TGUI"
-
- prefs.tgui_fancy = !prefs.tgui_fancy
- prefs.save_preferences()
- feedback_add_details("admin_verb", "TFTGUI")
-
-/client/verb/toggle_tooltip()
- set name = "Tooltip: Show/Hide"
- set category = "Preferences"
- set desc = "Toggle Name of Items"
-
- prefs.tooltip = !prefs.tooltip
-
- if(prefs.tooltip)
- tooltip.set_state(TRUE)
- else
- tooltip.set_state(FALSE)
-
- prefs.save_preferences()
- to_chat(src, "Name of items [prefs.tooltip ? "enabled" : "disabled"].")
- feedback_add_details("admin_verb", "TTIP")
-
-/client/verb/change_font_tooltip()
- set name = "Tooltip: Change Font"
- set category = "Preferences"
- set desc = "Toggle Font of Names of Items"
-
- var/list/fonts = list("System", "Fixedsys", "Small Fonts", "Times New Roman", "Serif", "Verdana", "Custom Font")
-
- var/font = input(usr, "Font of Names of Items:", "Font", prefs.tooltip_font) as null|anything in fonts | prefs.tooltip_font
-
- if(font == "Custom Font")
- font = sanitize(input("Enter the font that you have on your computer:", "Font") as null|text)
-
- if(!font)
- return
-
- prefs.tooltip_font = font
-
- prefs.save_preferences()
- feedback_add_details("admin_verb", "FTIP")
-
-/client/verb/change_size_tooltip()
- set name = "Tooltip: Change Size"
- set category = "Preferences"
- set desc = "Change Size of Names of Items"
-
- prefs.tooltip_size = input(usr, "Введите размер Названий Предметов") as num
-
- tooltip.font_size = prefs.tooltip_size
- prefs.save_preferences()
- feedback_add_details("admin_verb", "LTIP")
-
-/client/verb/toggle_outline()
- set name = "Toggle Outline"
- set category = "Preferences"
- set desc = "Toggle Outline"
-
- prefs.outline_enabled = !prefs.outline_enabled
- prefs.save_preferences()
- to_chat(src, "Outline is [prefs.outline_enabled ? "enabled" : "disabled"].")
- feedback_add_details("admin_verb", "TO")
-
-/client/verb/change_outline_color()
- set name = "Change Outline Color"
- set category = "Preferences"
- set desc = "Change Outline Color"
-
- var/pickedOutlineColor = input(usr, "Choose your outline color.", "General Preference", prefs.outline_color) as color|null
- if(!pickedOutlineColor)
- return
- prefs.outline_color = pickedOutlineColor
- prefs.save_preferences()
- to_chat(src, "Outline color changed.")
- feedback_add_details("admin_verb", "COC")
-
-/client/verb/toggle_eorg()
- set name = "Toggle End of Round Deathmatch"
- set category = "Preferences"
- set desc = "At the end of the round you will be teleported to thunderdome to freely bash your fellow colleagues."
-
- prefs.eorg_enabled = !prefs.eorg_enabled
- prefs.save_preferences()
- to_chat(src, "You [prefs.eorg_enabled ? "will be" : "won't be"] teleported to Thunderdome at round end.")
- feedback_add_details("admin_verb", "ED")
-
-/client/verb/toggle_runechat()
- set name = "Toggle Runechat (Above-Head-Speech)"
- set category = "Preferences"
- prefs.show_runechat = !prefs.show_runechat
-
- to_chat(src, "Runechat is [prefs.show_runechat ? "enabled" : "disabled"].")
- feedback_add_details("admin_verb", "TRC")
-
-/client/verb/toggle_hotkeys_mode()
- set name = "Toggle Hotkeys Mode"
- set category = "Preferences"
-
- prefs.toggle_hotkeys_mode()
- if(prefs.hotkeys)
- to_chat(src, "Режим хоткеев переключен: при клике в окно игры фокус будет переключен на окно игры")
- else
- to_chat(src, "Режим хоткеев переключен: при клике в окно игры фокус останется на чате.")
- feedback_add_details("admin_verb", "thm")
-
/client/verb/edit_emote_panel()
set name = "Edit Emote Panel"
- set category = "Preferences"
+ set category = "OOC"
if(!emote_panel_editor)
emote_panel_editor = new /datum/emote_panel_editor(src)
diff --git a/code/modules/client/settings/_pref.dm b/code/modules/client/settings/_pref.dm
new file mode 100644
index 000000000000..20bdf8ed7903
--- /dev/null
+++ b/code/modules/client/settings/_pref.dm
@@ -0,0 +1,76 @@
+/datum/pref
+ // related to json name (or table in DB)
+ var/domain // player | character | keybinds | meta
+
+ // mostly for sorting prefs in settings menu
+ var/category
+
+ var/name
+
+ var/description
+
+ // current value and also default value with initial()
+ var/value
+
+ // one of PREF_TYPE_*, defines how we show preference at frontend and how we validate it
+ var/value_type = PREF_TYPE_CUSTOM // = COLOR | NUMBER | LIST | BOOLEAN | TEXT
+
+ // ambivalent value depending on value_type:
+ // PREF_TYPE_RANGE - list(min, max, step (optional), unit (optional))
+ // PREF_TYPE_SELECT - list(value, value, value) or list(value = "humanised name", value = "humanised name", value = "humanised name")
+ // PREF_TYPE_TEXT - list(max_length)
+ // not used for other types
+ var/list/value_parameters
+
+ // just so we can filter and not confuse players with preferences that they can't use
+ var/admins_only = FALSE
+ var/supporters_only = FALSE
+
+// default sanitize procedures, override it if you need something more
+/datum/pref/proc/sanitize_value(new_value, client/client)
+
+ switch(value_type)
+ if(PREF_TYPE_TEXT)
+ var/max_length = (length(value_parameters) ? value_parameters[1] : MAX_PAPER_MESSAGE_LEN)
+ . = sanitize(new_value, max_length)
+ if(PREF_TYPE_RANGE)
+ var/min = value_parameters[1]
+ var/max = value_parameters[2]
+ if(isnum(new_value) && new_value >= min && new_value <= max)
+ . = new_value
+ else
+ . = initial(value)
+ WARNING("Can't sanitize [type] new value [new_value], fallback to default.")
+ if(PREF_TYPE_SELECT)
+ if(new_value in value_parameters)
+ . = new_value
+ else
+ . = initial(value)
+ WARNING("Can't sanitize [type] new value [new_value], fallback to default.")
+ if(PREF_TYPE_HEX)
+ var/hex = sanitize_hexcolor(new_value, null)
+ if(hex)
+ . = hex
+ else
+ . = initial(value)
+ WARNING("Can't sanitize [type] new value [new_value], fallback to default.")
+ if(PREF_TYPE_BOOLEAN)
+ . = !!new_value
+ else // any custom types
+ CRASH("Not implemented sanitize for [src.type]!")
+
+ return .
+
+/datum/pref/proc/update_value(new_value, client/client)
+ var/old_value = value
+ value = sanitize_value(new_value, client)
+
+ if(old_value != value && client)
+ on_update(client, old_value)
+ return TRUE
+
+ return FALSE
+
+// override if you need to trigger any special updates after value change (reload planes, update sound volume, etc.)
+/datum/pref/proc/on_update(client/client, old_value)
+ return FALSE
diff --git a/code/modules/client/settings/keybinds/_keybinds.dm b/code/modules/client/settings/keybinds/_keybinds.dm
new file mode 100644
index 000000000000..bf6396144f92
--- /dev/null
+++ b/code/modules/client/settings/keybinds/_keybinds.dm
@@ -0,0 +1,103 @@
+// keybinds value formatted as list of binds separated by space
+// maximum 3 (KEYBINDS_MAXIMUM) binds and two spaces (or leading space) means empty bind in between
+// combinations with alt/ctrl/shift use "+"
+//
+// "+" is chosen because it should not be possible (i think) to use
+// as a key: "+" key is basically "Shift+=", and byond uses "Add" for numpad "+"
+
+/datum/pref/keybinds
+ domain = PREF_DOMAIN_KEYBINDS
+ value_type = PREF_TYPE_KEYBIND
+
+ value = ""
+
+ category = PREF_KEYBINDS_MISC
+
+ // probably there was a plan to trigger keybinds in weight order, but currently it is not used anywhere
+ // maybe we should just remove it
+ var/weight = WEIGHT_LOWEST
+
+ // legacy name for migration of old savefiles, you should not use it with new keybindings
+ var/legacy_keyname
+
+/datum/pref/keybinds/sanitize_value(new_value, client/client)
+ if(value_type != PREF_TYPE_KEYBIND)
+ return ..()
+
+ var/list/new_keybinds = splittext(new_value, " ")
+
+ if(!length(new_keybinds))
+ return ""
+
+ // skip any validation madness if it's just default
+ if(value == new_value)
+ return value
+
+ new_value = ""
+
+ // I hate this part
+ for(var/i in 1 to min(KEYBINDS_MAXIMUM, new_keybinds.len))
+ var/raw_key = new_keybinds[i]
+ var/key
+
+ var/altMod = FALSE
+ if(endswith(raw_key, "Alt"))
+ key = "Alt"
+ else if(findtext(raw_key, "Alt"))
+ altMod = TRUE
+
+ var/ctrlMod = FALSE
+ if(endswith(raw_key, "Ctrl"))
+ key = "Ctrl"
+ else if(findtext(raw_key, "Ctrl"))
+ ctrlMod = TRUE
+
+ var/shiftMod = FALSE
+ if(endswith(raw_key, "Shift"))
+ key = "Shift"
+ else if(findtext(raw_key, "Shift"))
+ shiftMod = TRUE
+
+ if(!key)
+ // old saves keybind mods don't have separator, new keybind mods separated with +
+ var/static/list/filter_list = list("Alt", "Ctrl", "Shift", "+")
+ key = replace_characters(raw_key, filter_list)
+
+ key = satinize_key(key, altMod, ctrlMod, shiftMod)
+
+ new_value += "[key] "
+
+ return trim_right(new_value)
+
+/datum/pref/keybinds/on_update(client/client, old_value)
+ if(value_type != PREF_TYPE_KEYBIND)
+ return ..()
+
+ if(!client || !client.prefs) // offline update or prefs still initializing
+ return
+
+ client.reset_held_keys()
+
+ var/list/old_keybinds = splittext(old_value, " ")
+ for(var/key in old_keybinds)
+ client.prefs.key_bindings_by_key[key] -= list(src)
+
+ var/list/new_keybinds = splittext(value, " ")
+ for(var/key in new_keybinds)
+ client.prefs.key_bindings_by_key[key] += list(src)
+
+/datum/pref/keybinds/proc/satinize_key(key, altMod, ctrlMod, shiftMod)
+ if(!length(key))
+ return ""
+ if(!(key in global.byond_valid_keys))
+ return ""
+ return "[altMod ? "Alt+" : ""][ctrlMod ? "Ctrl+" : ""][shiftMod ? "Shift+" : ""][key]"
+
+/datum/pref/keybinds/proc/down(client/user)
+ return FALSE
+
+/datum/pref/keybinds/proc/up(client/user)
+ return FALSE
+
+/datum/pref/keybinds/proc/can_use(client/user)
+ return TRUE
diff --git a/code/modules/client/settings/keybinds/admin.dm b/code/modules/client/settings/keybinds/admin.dm
new file mode 100644
index 000000000000..8acae37bc9f2
--- /dev/null
+++ b/code/modules/client/settings/keybinds/admin.dm
@@ -0,0 +1,113 @@
+/datum/pref/keybinds/admin
+ category = PREF_KEYBINDS_CLIENT
+ weight = WEIGHT_ADMIN
+
+ admins_only = TRUE
+
+/datum/pref/keybinds/admin/can_use(client/user)
+ return user.holder ? TRUE : FALSE
+
+/datum/pref/keybinds/admin/admin_say
+ name = "Admin say"
+ description = "Talk with other admins."
+ value = "F5"
+
+ legacy_keyname = "admin_say"
+
+/datum/pref/keybinds/admin/admin_say/down(client/user)
+ user.get_admin_say()
+ return TRUE
+
+/datum/pref/keybinds/admin/admin_ghost
+ name = "Aghost"
+ description = "Go ghost"
+
+ legacy_keyname = "admin_ghost"
+
+/datum/pref/keybinds/admin/admin_ghost/down(client/user)
+ user.admin_ghost()
+ return TRUE
+
+/datum/pref/keybinds/admin/player_panel_new
+ name = "Player Panel New"
+ description = "Opens up the new player panel"
+ value = "F6"
+
+ legacy_keyname = "player_panel_new"
+
+/datum/pref/keybinds/admin/player_panel_new/down(client/user)
+ user.holder.player_panel_new()
+ return TRUE
+
+/datum/pref/keybinds/admin/toggle_buildmode_self
+ name = "Toggle Buildmode Self"
+ description = "Toggles buildmode"
+ value = "F7"
+
+ legacy_keyname = "toggle_buildmode_self"
+
+/datum/pref/keybinds/admin/toggle_buildmode_self/down(client/user)
+ user.togglebuildmodeself()
+ return TRUE
+
+/datum/pref/keybinds/admin/stealthmode
+ name = "Stealth mode"
+ description = "Enters stealth mode"
+ value = "Ctrl+F8"
+
+ legacy_keyname = "stealth_mode"
+
+/datum/pref/keybinds/admin/stealthmode/down(client/user)
+ user.stealth()
+ return TRUE
+
+/datum/pref/keybinds/admin/invisimin
+ name = "Admin invisibility"
+ description = "Toggles ghost-like invisibility (Don't abuse this)"
+ value = "F8"
+
+ legacy_keyname = "invisimin"
+
+/datum/pref/keybinds/admin/invisimin/down(client/user)
+ user.invisimin()
+ return TRUE
+
+/datum/pref/keybinds/admin/deadsay
+ name = "deadsay"
+ description = "Allows you to send a message to dead chat"
+ value = "F10"
+
+ legacy_keyname = "dsay"
+
+/datum/pref/keybinds/admin/deadsay/down(client/user)
+ user.get_dead_say()
+ return TRUE
+
+/datum/pref/keybinds/admin/deadmin
+ name = "Deadmin"
+ description = "Shed your admin powers"
+
+ legacy_keyname = "deadmin"
+
+/datum/pref/keybinds/admin/deadmin/down(client/user)
+ user.deadmin_self()
+ return TRUE
+
+/datum/pref/keybinds/admin/readmin
+ name = "Readmin"
+ description = "Regain your admin powers"
+
+ legacy_keyname = "readmin"
+
+/datum/pref/keybinds/admin/readmin/down(client/user)
+ user.readmin_self()
+ return TRUE
+
+/datum/pref/keybinds/admin/toggle_combo_hud
+ name = "Toggle Combo HUD"
+
+ legacy_keyname = "toggle_combo_hud"
+
+/datum/pref/keybinds/admin/toggle_combo_hud/down(client/user)
+ user.toggle_combo_hud()
+ return TRUE
diff --git a/code/modules/client/settings/keybinds/carbon.dm b/code/modules/client/settings/keybinds/carbon.dm
new file mode 100644
index 000000000000..3b28114e3243
--- /dev/null
+++ b/code/modules/client/settings/keybinds/carbon.dm
@@ -0,0 +1,29 @@
+/datum/pref/keybinds/carbon
+ category = PREF_KEYBINDS_CARBON
+ weight = WEIGHT_MOB
+
+/datum/pref/keybinds/carbon/can_use(client/user)
+ return iscarbon(user.mob)
+
+/datum/pref/keybinds/carbon/toggle_throw_mode
+ name = "Toggle throw mode"
+ description = "Toggle throwing the current item or not."
+ value = "R Southwest" // Southwest - PAGEDOWN
+
+ legacy_keyname = "toggle_throw_mode"
+
+/datum/pref/keybinds/carbon/toggle_throw_mode/down(client/user)
+ var/mob/living/carbon/C = user.mob
+ C.toggle_throw_mode()
+ return TRUE
+
+/datum/pref/keybinds/carbon/give
+ name = "Give item"
+ description = "Give the item you're currently holding"
+
+ legacy_keyname = "Give_Item"
+
+/datum/pref/keybinds/carbon/give/down(client/user)
+ var/mob/living/carbon/C = user.mob
+ C.give()
+ return TRUE
diff --git a/code/modules/client/settings/keybinds/client.dm b/code/modules/client/settings/keybinds/client.dm
new file mode 100644
index 000000000000..5604a6afd128
--- /dev/null
+++ b/code/modules/client/settings/keybinds/client.dm
@@ -0,0 +1,35 @@
+/datum/pref/keybinds/client
+ category = PREF_KEYBINDS_CLIENT
+ weight = WEIGHT_HIGHEST
+
+/datum/pref/keybinds/client/screenshot
+ name = "Screenshot"
+ description = "Take a screenshot."
+
+ legacy_keyname = "screenshot"
+
+/datum/pref/keybinds/client/screenshot/down(client/user)
+ winset(user, null, "command=.screenshot [!user.keys_held["shift"] ? "auto" : ""]")
+ return TRUE
+
+/datum/pref/keybinds/client/minimal_hud
+ name = "Minimal HUD"
+ description = "Hide most HUD features"
+ value = "F12"
+
+ legacy_keyname = "minimal_hud"
+
+/datum/pref/keybinds/client/minimal_hud/down(client/user)
+ user.mob.button_pressed_F12()
+ return TRUE
+
+/datum/pref/keybinds/client/toggle_fullscreen
+ name = "Toggle Fullscreen"
+ description = "Toggle Fullscreen"
+ value = "F11"
+
+ legacy_keyname = "toggle_fullscreen"
+
+/datum/pref/keybinds/client/toggle_fullscreen/down(client/user)
+ user.toggle_fullscreen()
+ return TRUE
diff --git a/code/modules/client/settings/keybinds/communication.dm b/code/modules/client/settings/keybinds/communication.dm
new file mode 100644
index 000000000000..f85f3785d6c6
--- /dev/null
+++ b/code/modules/client/settings/keybinds/communication.dm
@@ -0,0 +1,65 @@
+/datum/pref/keybinds/communication
+ category = PREF_KEYBINDS_COMMUNICATION
+ weight = WEIGHT_HIGHEST
+
+/datum/pref/keybinds/communication/admin_help
+ name = "Admin Help"
+ description = "Ask an admin for help."
+ value = "F1"
+
+ legacy_keyname = "admin_help"
+
+/datum/pref/keybinds/communication/admin_help/down(client/user)
+ user.adminhelp()
+ return TRUE
+
+/datum/pref/keybinds/communication/mentor_help
+ name = "Mentor Help"
+ description = "Ask an mentors for help."
+ value = "F9"
+
+ legacy_keyname = "mentor_help"
+
+/datum/pref/keybinds/communication/mentor_help/down(client/user)
+ user.get_mentorhelp()
+ return TRUE
+
+/datum/pref/keybinds/communication/say
+ name = "IC Say"
+ value = "F3 T"
+
+ legacy_keyname = "Say"
+
+/datum/pref/keybinds/communication/say/down(client/user)
+ user.mob.say_wrapper()
+ return TRUE
+
+/datum/pref/keybinds/communication/ooc
+ name = "Out Of Character Say (OOC)"
+ value = "F2 O"
+
+ legacy_keyname = "OOC"
+
+/datum/pref/keybinds/communication/ooc/down(client/user)
+ user.ooc_wrapper()
+ return TRUE
+
+/datum/pref/keybinds/communication/looc
+ name = "Local Out Of Character Say (LOOC)"
+ value = "L"
+
+ legacy_keyname = "LOOC"
+
+/datum/pref/keybinds/communication/looc/down(client/user)
+ user.looc_wrapper()
+ return TRUE
+
+/datum/pref/keybinds/communication/me
+ name = "Custom Emote (/Me)"
+ value = "F4 M"
+
+ legacy_keyname = "Me"
+
+/datum/pref/keybinds/communication/me/down(client/user)
+ user.mob.me_wrapper()
+ return TRUE
diff --git a/code/modules/client/settings/keybinds/emotes.dm b/code/modules/client/settings/keybinds/emotes.dm
new file mode 100644
index 000000000000..a939abf0aea7
--- /dev/null
+++ b/code/modules/client/settings/keybinds/emotes.dm
@@ -0,0 +1,64 @@
+// even tho keybinds prefs domain are part compatible with other pref types, using this in mix was not planed first
+// should be ok, but need to keep it in mind while working with keybinds
+/datum/pref/keybinds/emote_select
+ category = PREF_KEYBINDS_EMOTE
+ weight = WEIGHT_MOB
+ value_type = PREF_TYPE_SELECT
+ value = "None"
+
+/datum/pref/keybinds/emote_select/New()
+ // will use all currently existing emotes keys in build as possible values
+ // prefs automatically fallback to default if value is not valid anymore
+ var/static/list/possible_values
+ if(!possible_values)
+ possible_values = list("None") + global.all_emotes_keys // all datums reference one static value instead of list per datum
+ value_parameters = possible_values
+
+/datum/pref/keybinds/emote_key
+ category = PREF_KEYBINDS_EMOTE
+ weight = WEIGHT_MOB
+
+ var/emote_pref_type
+
+/datum/pref/keybinds/emote_key/down(client/user)
+ var/emote = user.prefs.prefs_keybinds[emote_pref_type].value
+ if(emote == "None")
+ return
+ user.mob.emote(emote, intentional = TRUE, fallback_notice = TRUE)
+
+// copypaste below
+
+/datum/pref/keybinds/emote_select/num_1
+ name = "Emote 1"
+
+/datum/pref/keybinds/emote_key/num_1
+ name = "Emote key 1"
+ emote_pref_type = /datum/pref/keybinds/emote_select/num_1
+
+/datum/pref/keybinds/emote_select/num_2
+ name = "Emote 2"
+
+/datum/pref/keybinds/emote_key/num_2
+ name = "Emote key 2"
+ emote_pref_type = /datum/pref/keybinds/emote_select/num_2
+
+/datum/pref/keybinds/emote_select/num_3
+ name = "Emote 3"
+
+/datum/pref/keybinds/emote_key/num_3
+ name = "Emote key 3"
+ emote_pref_type = /datum/pref/keybinds/emote_select/num_3
+
+/datum/pref/keybinds/emote_select/num_4
+ name = "Emote 4"
+
+/datum/pref/keybinds/emote_key/num_4
+ name = "Emote key 4"
+ emote_pref_type = /datum/pref/keybinds/emote_select/num_4
+
+/datum/pref/keybinds/emote_select/num_5
+ name = "Emote 5"
+
+/datum/pref/keybinds/emote_key/num_5
+ name = "Emote key 5"
+ emote_pref_type = /datum/pref/keybinds/emote_select/num_5
diff --git a/code/modules/client/settings/keybinds/human.dm b/code/modules/client/settings/keybinds/human.dm
new file mode 100644
index 000000000000..83f670126667
--- /dev/null
+++ b/code/modules/client/settings/keybinds/human.dm
@@ -0,0 +1,53 @@
+/datum/pref/keybinds/human
+ category = PREF_KEYBINDS_HUMAN
+ weight = WEIGHT_MOB
+
+/datum/pref/keybinds/human/can_use(client/user)
+ return ishuman(user.mob)
+
+/datum/pref/keybinds/human/quick_equip
+ name = "Quick Equip"
+ description = "Quickly puts an item in the best slot available"
+ value = "E"
+
+ legacy_keyname = "quick_equip"
+
+/datum/pref/keybinds/human/quick_equip/down(client/user)
+ var/mob/living/carbon/human/H = user.mob
+ H.quick_equip()
+ return TRUE
+
+/datum/pref/keybinds/human/holster
+ name = "Holster"
+ description = "Draw or holster weapon."
+ value = "H"
+
+ legacy_keyname = "holster"
+
+/datum/pref/keybinds/human/holster/down(client/user)
+ var/mob/living/carbon/human/H = user.mob
+ H.holster_weapon()
+
+/datum/pref/keybinds/human/emote_panel
+ name = "Emote Panel"
+ description = "Shows you emote panel."
+ value = "J"
+
+ legacy_keyname = "emote_panel"
+
+/datum/pref/keybinds/human/emote_panel/down(client/user)
+ var/mob/living/carbon/human/H = user.mob
+ H.emote_panel()
+
+/datum/pref/keybinds/human/race_ability
+ name = "Race Ability"
+ description = "Activates your racial ability."
+ value = "U"
+
+ legacy_keyname = "race_ability" // or leap
+
+/datum/pref/keybinds/human/race_ability/down(client/user)
+ var/mob/living/carbon/human/H = user.mob
+ var/datum/action/A = locate(H.species.race_ability) in H.actions
+ if(A)
+ A.Trigger()
diff --git a/code/modules/client/settings/keybinds/living.dm b/code/modules/client/settings/keybinds/living.dm
new file mode 100644
index 000000000000..0c9eee898fc1
--- /dev/null
+++ b/code/modules/client/settings/keybinds/living.dm
@@ -0,0 +1,116 @@
+/datum/pref/keybinds/living
+ category = PREF_KEYBINDS_HUMAN
+ weight = WEIGHT_MOB
+
+/datum/pref/keybinds/living/can_use(client/user)
+ return isliving(user.mob)
+
+/datum/pref/keybinds/living/resist
+ name = "Resist"
+ description = "Break free of your current state. Handcuffed? On fire? Resist!"
+ value = "N"
+
+ legacy_keyname = "resist"
+
+/datum/pref/keybinds/living/resist/down(client/user)
+ var/mob/living/L = user.mob
+ L.resist()
+ return TRUE
+
+/datum/pref/keybinds/living/toggle_move_intent
+ name = "Hold to toggle move intent"
+ description = "Held down to cycle to the other move intent, release to cycle back"
+ value = "C"
+
+ legacy_keyname = "toggle_move_intent"
+
+/datum/pref/keybinds/living/toggle_move_intent/down(client/user)
+ var/mob/living/L = user.mob
+ L.set_m_intent(L.m_intent == MOVE_INTENT_WALK ? MOVE_INTENT_RUN : MOVE_INTENT_WALK)
+ return TRUE
+
+/datum/pref/keybinds/living/toggle_move_intent/up(client/user)
+ var/mob/living/L = user.mob
+ L.set_m_intent(L.m_intent == MOVE_INTENT_WALK ? MOVE_INTENT_RUN : MOVE_INTENT_WALK)
+ return TRUE
+
+/datum/pref/keybinds/living/drop_item
+ name = "Drop Item"
+ value = "Q Northwest" // Northwest - HOME
+
+ legacy_keyname = "drop_item"
+
+/datum/pref/keybinds/living/drop_item/down(client/user)
+ var/mob/living/L = user.mob
+ L.drop_item()
+ return TRUE
+
+/datum/pref/keybinds/living/crawl
+ name = "Crawl"
+ description = "You lay down/get up"
+
+ legacy_keyname = "crawl"
+
+/datum/pref/keybinds/living/crawl/down(client/user)
+ var/mob/living/L = user.mob
+ L.crawl()
+ return TRUE
+
+/datum/pref/keybinds/living/swap_hands
+ name = "Swap hands"
+ value = "X Northeast" // Northeast - PAGEUP
+
+ legacy_keyname = "swap_hands"
+
+/datum/pref/keybinds/living/swap_hands/down(client/user)
+ var/mob/living/L = user.mob
+ L.swap_hand()
+ return TRUE
+
+/datum/pref/keybinds/living/select_help_intent
+ name = "Select help intent"
+ value = "1"
+
+ legacy_keyname = "select_help_intent"
+
+/datum/pref/keybinds/living/select_help_intent/down(client/user)
+ if(issilicon(user.mob))
+ return
+ user.mob?.a_intent_change(INTENT_HELP)
+ return TRUE
+
+/datum/pref/keybinds/living/select_disarm_intent
+ name = "Select push intent"
+ value = "2"
+
+ legacy_keyname = "select_push_intent"
+
+/datum/pref/keybinds/living/select_disarm_intent/down(client/user)
+ if(issilicon(user.mob))
+ return
+ user.mob?.a_intent_change(INTENT_PUSH)
+ return TRUE
+
+/datum/pref/keybinds/living/select_grab_intent
+ name = "Select grab intent"
+ value = "3"
+
+ legacy_keyname = "select_grab_intent"
+
+/datum/pref/keybinds/living/select_grab_intent/down(client/user)
+ if(issilicon(user.mob))
+ return
+ user.mob?.a_intent_change(INTENT_GRAB)
+ return TRUE
+
+/datum/pref/keybinds/living/select_harm_intent
+ name = "Select harm intent"
+ value = "4"
+
+ legacy_keyname = "select_harm_intent"
+
+/datum/pref/keybinds/living/select_harm_intent/down(client/user)
+ if(issilicon(user.mob))
+ return
+ user.mob?.a_intent_change(INTENT_HARM)
+ return TRUE
diff --git a/code/modules/client/settings/keybinds/mob.dm b/code/modules/client/settings/keybinds/mob.dm
new file mode 100644
index 000000000000..cd8c29fb5bb1
--- /dev/null
+++ b/code/modules/client/settings/keybinds/mob.dm
@@ -0,0 +1,144 @@
+/datum/pref/keybinds/mob
+ category = PREF_KEYBINDS_HUMAN
+ weight = WEIGHT_MOB
+
+/datum/pref/keybinds/mob/stop_pulling
+ name = "Stop pulling"
+ value = "Delete"
+
+ legacy_keyname = "stop_pulling"
+
+/datum/pref/keybinds/mob/stop_pulling/down(client/user)
+ var/mob/M = user.mob
+ if(!M.pulling)
+ to_chat(user, "You are not pulling anything.")
+ else
+ M.stop_pulling()
+ return TRUE
+
+/datum/pref/keybinds/mob/cycle_intent_right
+ name = "Cycle intent right"
+ value = "G Insert"
+
+ legacy_keyname = "cycle_intent_right"
+
+/datum/pref/keybinds/mob/cycle_intent_right/down(client/user)
+ var/mob/M = user.mob
+ M.a_intent_change(INTENT_HOTKEY_RIGHT)
+ return TRUE
+
+/datum/pref/keybinds/mob/cycle_intent_left
+ name = "Cycle intent left"
+ value = "F"
+
+ legacy_keyname = "cycle_intent_left"
+
+/datum/pref/keybinds/mob/cycle_intent_left/down(client/user)
+ var/mob/M = user.mob
+ M.a_intent_change(INTENT_HOTKEY_LEFT)
+ return TRUE
+
+/datum/pref/keybinds/mob/activate_inhand
+ name = "Activate in-hand"
+ description = "Uses whatever item you have inhand"
+ value = "Z Y Southeast" // Southeast - PAGEDOWN
+
+ legacy_keyname = "activate_inhand"
+
+/datum/pref/keybinds/mob/activate_inhand/down(client/user)
+ var/mob/M = user.mob
+ M.mode()
+ return TRUE
+
+/datum/pref/keybinds/mob/target_head_cycle
+ name = "Target: Cycle head"
+ value = "Numpad8"
+
+ legacy_keyname = "target_head_cycle"
+
+/datum/pref/keybinds/mob/target_head_cycle/down(client/user)
+ user.body_toggle_head()
+ return TRUE
+
+/datum/pref/keybinds/mob/target_r_arm
+ name = "Target: right arm"
+ value = "Numpad4"
+
+ legacy_keyname = "target_r_arm"
+
+/datum/pref/keybinds/mob/target_r_arm/down(client/user)
+ user.body_r_arm()
+ return TRUE
+
+/datum/pref/keybinds/mob/target_body_chest
+ name = "Target: Body"
+ value = "Numpad5"
+
+ legacy_keyname = "target_body_chest"
+
+/datum/pref/keybinds/mob/target_body_chest/down(client/user)
+ user.body_chest()
+ return TRUE
+
+/datum/pref/keybinds/mob/target_left_arm
+ name = "Target: left arm"
+ value = "Numpad6"
+
+ legacy_keyname = "target_left_arm"
+
+/datum/pref/keybinds/mob/target_left_arm/down(client/user)
+ user.body_l_arm()
+ return TRUE
+
+/datum/pref/keybinds/mob/target_right_leg
+ name = "Target: Right leg"
+ value = "Numpad1"
+
+ legacy_keyname = "target_right_leg"
+
+/datum/pref/keybinds/mob/target_right_leg/down(client/user)
+ user.body_r_leg()
+ return TRUE
+
+/datum/pref/keybinds/mob/target_body_groin
+ name = "Target: Groin"
+ value = "Numpad2"
+
+ legacy_keyname = "target_body_groin"
+
+/datum/pref/keybinds/mob/target_body_groin/down(client/user)
+ user.body_groin()
+ return TRUE
+
+/datum/pref/keybinds/mob/target_left_leg
+ name = "Target: left leg"
+ value = "Numpad3"
+
+ legacy_keyname = "target_left_leg"
+
+/datum/pref/keybinds/mob/target_left_leg/down(client/user)
+ user.body_l_leg()
+ return TRUE
+
+/datum/pref/keybinds/mob/prevent_movement
+ name = "Block movement"
+ description = "Prevents you from moving"
+ value = "Ctrl"
+
+ legacy_keyname = "block_movement"
+
+/datum/pref/keybinds/mob/prevent_movement/down(client/user)
+ user.movement_locked = TRUE
+
+/datum/pref/keybinds/mob/prevent_movement/up(client/user)
+ user.movement_locked = FALSE
+
+/datum/pref/keybinds/mob/click_on_self
+ name = "Click On Self"
+ value = "B"
+
+ legacy_keyname = "click_on_self"
+
+/datum/pref/keybinds/mob/click_on_self/down(client/user)
+ var/mob/M = user.mob
+ M.click_on_self()
diff --git a/code/modules/client/settings/keybinds/movement.dm b/code/modules/client/settings/keybinds/movement.dm
new file mode 100644
index 000000000000..dd5e921a9e95
--- /dev/null
+++ b/code/modules/client/settings/keybinds/movement.dm
@@ -0,0 +1,35 @@
+/datum/pref/keybinds/movement
+ category = PREF_KEYBINDS_MOVEMENT
+ weight = WEIGHT_HIGHEST
+
+/datum/pref/keybinds/movement/on_update(client/client, old_value)
+ ..()
+ client?.update_movement_keybinds()
+
+/datum/pref/keybinds/movement/north
+ name = "Move North"
+ description = "Moves your character north"
+ value = "W North"
+
+ legacy_keyname = "North"
+
+/datum/pref/keybinds/movement/south
+ name = "Move South"
+ description = "Moves your character south"
+ value = "S South"
+
+ legacy_keyname = "South"
+
+/datum/pref/keybinds/movement/west
+ name = "Move West"
+ description = "Moves your character left"
+ value = "A West"
+
+ legacy_keyname = "West"
+
+/datum/pref/keybinds/movement/east
+ name = "Move East"
+ description = "Moves your character east"
+ value = "D East"
+
+ legacy_keyname = "East"
diff --git a/code/modules/client/settings/keybinds/robot.dm b/code/modules/client/settings/keybinds/robot.dm
new file mode 100644
index 000000000000..3a89b4e0f267
--- /dev/null
+++ b/code/modules/client/settings/keybinds/robot.dm
@@ -0,0 +1,67 @@
+/datum/pref/keybinds/robot
+ category = PREF_KEYBINDS_ROBOT
+ weight = WEIGHT_ROBOT
+
+/datum/pref/keybinds/robot/can_use(client/user)
+ return isrobot(user.mob)
+
+/datum/pref/keybinds/robot/moduleone
+ name = "Toggle module 1"
+ description = "Equips or unequips the first module"
+ value = "1"
+
+ legacy_keyname = "module_one"
+
+/datum/pref/keybinds/robot/moduleone/down(client/user)
+ var/mob/living/silicon/robot/R = user.mob
+ R.toggle_module(1)
+ return TRUE
+
+/datum/pref/keybinds/robot/moduletwo
+ name = "Toggle module 2"
+ description = "Equips or unequips the second module"
+ value = "2"
+
+ legacy_keyname = "module_two"
+
+/datum/pref/keybinds/robot/moduletwo/down(client/user)
+ var/mob/living/silicon/robot/R = user.mob
+ R.toggle_module(2)
+ return TRUE
+
+/datum/pref/keybinds/robot/modulethree
+ name = "Toggle module 3"
+ description = "Equips or unequips the third module"
+ value = "3"
+
+ legacy_keyname = "module_three"
+
+/datum/pref/keybinds/robot/modulethree/down(client/user)
+ var/mob/living/silicon/robot/R = user.mob
+ R.toggle_module(3)
+ return TRUE
+
+/datum/pref/keybinds/robot/intent_cycle
+ name = "Cycle intent left"
+ description = "Cycles the intent left"
+ value = "4"
+
+ legacy_keyname = "cycle_intent"
+
+/datum/pref/keybinds/robot/intent_cycle/down(client/user)
+ var/mob/living/silicon/robot/R = user.mob
+ R.a_intent_change(INTENT_HOTKEY_LEFT)
+ return TRUE
+
+/datum/pref/keybinds/robot/unequip_module
+ name = "Unequip module"
+ description = "Unequips the active module"
+ value = "Q"
+
+ legacy_keyname = "unequip_module"
+
+/datum/pref/keybinds/robot/unequip_module/down(client/user)
+ var/mob/living/silicon/robot/R = user.mob
+ if(R.module)
+ R.uneq_active()
+ return TRUE
diff --git a/code/modules/client/settings/player/_player.dm b/code/modules/client/settings/player/_player.dm
new file mode 100644
index 000000000000..87403af3d7d4
--- /dev/null
+++ b/code/modules/client/settings/player/_player.dm
@@ -0,0 +1,2 @@
+/datum/pref/player
+ domain = PREF_DOMAIN_PLAYER
diff --git a/code/modules/client/settings/player/audio.dm b/code/modules/client/settings/player/audio.dm
new file mode 100644
index 000000000000..35f5d05eff2d
--- /dev/null
+++ b/code/modules/client/settings/player/audio.dm
@@ -0,0 +1,99 @@
+// do not use these prefs dirrectly, see /code/game/sound.dm for sound methods
+
+/datum/pref/player/audio
+ category = PREF_PLAYER_AUDIO
+ value_type = PREF_TYPE_RANGE
+ value_parameters = list(0, 100)
+
+ var/volume_channel
+
+/datum/pref/player/audio/on_update(client/client, old_value)
+ client?.mob?.playsound_local(null, 'sound/weapons/saberon.ogg', volume_channel, vary = FALSE, channel = CHANNEL_VOLUMETEST)
+
+/datum/pref/player/audio/lobby
+ name = "Музыка в лобби"
+ description = "Громкость музыки в лобби игры."
+ value = 100
+
+ volume_channel = VOL_LOBBY_MUSIC
+
+/datum/pref/player/audio/lobby/on_update(client/client, old_value)
+ client?.mob?.playsound_music_update_volume(VOL_LOBBY_MUSIC, CHANNEL_MUSIC)
+ ..()
+
+/datum/pref/player/audio/ambient
+ name = "Эмбиент"
+ description = "Громкость эффектов окружающей среды - звуки станции, музыка отделов."
+ value = 100
+
+ volume_channel = VOL_AMBIENT
+
+/datum/pref/player/audio/ambient/on_update(client/client, old_value)
+ client?.mob?.playsound_music_update_volume(VOL_AMBIENT, CHANNEL_AMBIENT)
+ client?.mob?.playsound_music_update_volume(VOL_AMBIENT, CHANNEL_AMBIENT_LOOP)
+ ..()
+
+/datum/pref/player/audio/effects
+ name = "Эффекты"
+ description = "Громкость игровых эффектов"
+ value = 100
+
+ volume_channel = VOL_EFFECTS_MASTER
+
+/datum/pref/player/audio/spam_effects
+ name = "Модификатор спам-эффектов"
+ description = "Дополнительный модификатор громкости для некоторых, возможно надоедливых, игровых эффектов - теслы, эммитеры, хонк-и и некоторые другие."
+ value = 100
+
+ volume_channel = VOL_SPAM_EFFECTS
+
+/datum/pref/player/audio/voice_announcements
+ name = "Голосовые аннонсы"
+ description = "Громкость озвученных игровых аудио-аннонсов, вроде оповещений с ЦК."
+ value = 100
+
+ volume_channel = VOL_VOICE_ANNOUNCEMENTS
+
+/datum/pref/player/audio/instruments
+ name = "Музыкальные инструменты"
+ description = "Громкость музыки, проигрываемой музыкальными инструментами - пианино, гитара и т.п. Джубокс не относится к этой категории."
+ value = 100
+
+ volume_channel = VOL_MUSIC_INSTRUMENTS
+
+/datum/pref/player/audio/notifications
+ name = "Уведомления"
+ description = "Громкость различных важных уведомлений игрока - личные сообщения админов и менторов, запросы на воскрешение."
+ value = 100
+
+ volume_channel = VOL_NOTIFICATIONS
+
+/datum/pref/player/audio/admin_sounds
+ name = "Админские звуки"
+ description = "Музыка и звуки, проигрываемые администраторами."
+ value = 100
+
+ volume_channel = VOL_ADMIN_SOUNDS
+
+/datum/pref/player/audio/admin_sounds/on_update(client/client, old_value)
+ client?.mob?.playsound_music_update_volume(VOL_ADMIN_SOUNDS, CHANNEL_ADMIN)
+ ..()
+
+/datum/pref/player/audio/jukebox
+ name = "Jukebox"
+ description = "Громкость музыкального автомата."
+ value = 100
+
+ volume_channel = VOL_JUKEBOX
+
+/datum/pref/player/audio/jukebox/on_update(client/client, old_value)
+ ..()
+ var/datum/media_manager/media = client?.media
+ if(istype(media)) // will be updated in "/mob/living/Login()" if changed in lobby.
+ media.update_volume()
+
+ if(!value && old_value) // only play/stop if last change is a mute or unmute state.
+ media.stop_music()
+ else if(value && !old_value)
+ media.update_music()
+
diff --git a/code/modules/client/settings/player/chat.dm b/code/modules/client/settings/player/chat.dm
new file mode 100644
index 000000000000..f29a669971d8
--- /dev/null
+++ b/code/modules/client/settings/player/chat.dm
@@ -0,0 +1,117 @@
+/datum/pref/player/chat
+ category = PREF_PLAYER_CHAT
+
+/* ooc chat settings */
+
+/datum/pref/player/chat/show_ckey
+ name = "Показывать кей в LOOC/Ghost"
+ description = "Показать или скрыть ваш никнейм (имя Byond-аккаунта), когда вы общаетесь в LOOC и Ghost чатах."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/chat/ooccolor
+ name = "Цвет имени OOC"
+ description = "Ваш персональный цвет никнейма в OOC чате. Не даст поставить слишком темные или светлые и не читаемые цвета."
+ value_type = PREF_TYPE_HEX
+ value = OOC_COLOR_SUPPORTER
+
+ supporters_only = TRUE
+
+/datum/pref/player/chat/ooccolor/sanitize_value(new_value)
+ . = ..()
+ if(.)
+ . = color_lightness_clamp(., 20, 80) // so people don't abuse unreadable colors
+
+/datum/pref/player/chat/aooccolor
+ name = "Цвет текста OOC"
+ description = "Ваш персональный цвет в OOC чате. Не даст поставить слишком темные или светлые и не читаемые цвета."
+ value_type = PREF_TYPE_HEX
+ value = OOC_COLOR_ADMIN
+
+ admins_only = TRUE
+
+/datum/pref/player/chat/aooccolor/sanitize_value(new_value)
+ . = ..()
+ if(.)
+ . = color_lightness_clamp(., 20, 80) // so people don't abuse unreadable colors
+
+/datum/pref/player/chat/ooc
+ name = "OOC чат"
+ description = "Показывать или скрыть Out of Character чат - общий серверный не игровой чат."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/chat/looc
+ name = "LOOC чат"
+ description = "Показывать или скрыть Local OOC чат - как и общий серверный не игровой чат, но действует в пределах экрана."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/chat/dead
+ name = "Чат мертвых"
+ description = "Показывать или скрыть общий чат мертвых и обсерверов."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/* ghost chat settings */
+
+/datum/pref/player/chat/ghostears
+ name = "Призрачные уши"
+ description = "Включить слышимость всех разговоров от других мобов и персонажей в игре, когда вы призрак. Если выключено - вы будете слышать только в пределах экрана."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/chat/ghostsight
+ name = "Призрачное зрение"
+ description = "Включить видимость всех эмоутов от других мобов и персонажей в игре, когда вы призрак. Если выключено - вы будете видеть только в пределах экрана."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/chat/ghostradio
+ name = "Призрачное радио"
+ description = "Включить слышимость всех разговоров по радио на станции будучи призраком. Если выключено - вы будете слышать только те источники радио, которые в пределах экрана."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/chat/ghostantispam
+ name = "Призрачный антиспам"
+ description = "Включите, если вы хотите подавить незначительные автоматические сообщения за пределами экрана, будь то эмоуты или разговоры, если их инициатор не игрок."
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
+
+/* admin chat settings */
+
+/datum/pref/player/chat/attack_log
+ name = "Логи атак"
+ value_type = PREF_TYPE_SELECT
+ value = ATTACK_LOG_BY_CLIENT
+ value_parameters = list(
+ ATTACK_LOG_DISABLED = "Выключены",
+ ATTACK_LOG_BY_CLIENT = "Только на клиентов",
+ ATTACK_LOG_ALL = "Все"
+ )
+
+ admins_only = TRUE
+
+/datum/pref/player/chat/debug_log
+ name = "Дебаг логи"
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
+
+ admins_only = TRUE
+
+/datum/pref/player/chat/radio // how is it different with ghostradio???
+ name = "Радио чат"
+ description = "Переключает видимость радиосообщений с радио и спикеров."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+ admins_only = TRUE
+
+/datum/pref/player/chat/prayers
+ name = "Молитвы"
+ description = "Показать/скрыть молитвы игроков"
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+ admins_only = TRUE
diff --git a/code/modules/client/settings/player/display.dm b/code/modules/client/settings/player/display.dm
new file mode 100644
index 000000000000..7b614c43d229
--- /dev/null
+++ b/code/modules/client/settings/player/display.dm
@@ -0,0 +1,68 @@
+/datum/pref/player/display
+ category = PREF_PLAYER_DISPLAY
+
+/datum/pref/player/display/fps
+ name = "Кадры в секунду"
+ description = "Может сильно влиять на производительность игры. Выставьте значение меньше, если испытываете проблемы."
+ value_type = PREF_TYPE_RANGE
+ value = RECOMMENDED_FPS
+ value_parameters = list(1, 240)
+
+/datum/pref/player/display/fps/on_update(client/client, old_value)
+ if(client)
+ client.fps = value
+
+/datum/pref/player/display/fullscreen
+ name = "Полноэкранный режим"
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
+
+/datum/pref/player/display/fullscreen/on_update(client/client, old_value)
+ client?.update_fullscreen()
+
+/datum/pref/player/display/auto_fit_viewport
+ name = "Подгонять область просмотра"
+ description = "Убирает вертикальные черные полосы, автоматически подстраивая ширину игровой области под высоту."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/display/auto_fit_viewport/on_update(client/client, old_value)
+ if(value && client && !isnewplayer(client.mob)) // if new value requires to fit
+ client.fit_viewport()
+
+/datum/pref/player/display/auto_zoom
+ name = "Автомасштабирование"
+ description = "Подобрать масштаб автоматически, чтобы заполнить весь экран."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/display/auto_zoom/on_update(client/client, old_value)
+ client?.update_map_zoom()
+
+/datum/pref/player/display/zoom
+ name = "Масштабирование"
+ description = {"Масштабирование основного игрового экрана. Значения, кратные 100 (100, 200, 300, 400), дают лучшее качество спрайтов на любых методах масштабирования. В противном случае рекомендуется использовать автомасштабирование.
+Эта опция игнорируется, если включено Автомасштабирование!"}
+ value_type = PREF_TYPE_RANGE
+ value = 100
+ value_parameters = list(50, 400, 50, "%")
+
+/datum/pref/player/display/zoom/on_update(client/client, old_value)
+ client?.update_map_zoom()
+
+/datum/pref/player/display/zoom_mode
+ name = "Метод масштабирования"
+ description = {"Метод сглаживания, используемый при масштабировании спрайтов.
+
+Nearest Neighbor - даёт наибольшую четкость изображения, но при не крастных 100 масштабированиях (в т.ч. автомасштабе) может вызывать небольшие артефакты.
+
+Point Sampling - работает одинаково хорошо на любом масштабе, но делает изображение менее четким."}
+ value_type = PREF_TYPE_SELECT
+ value = SCALING_METHOD_DISTORT
+ value_parameters = list(
+ SCALING_METHOD_DISTORT = "Nearest Neighbor",
+ SCALING_METHOD_NORMAL = "Point Sampling"
+ )
+
+/datum/pref/player/display/zoom_mode/on_update(client/client, old_value)
+ client?.update_map_zoom()
diff --git a/code/modules/client/settings/player/effects.dm b/code/modules/client/settings/player/effects.dm
new file mode 100644
index 000000000000..dc737ef1aa9b
--- /dev/null
+++ b/code/modules/client/settings/player/effects.dm
@@ -0,0 +1,81 @@
+/datum/pref/player/effects
+ category = PREF_PLAYER_EFFECTS
+
+/datum/pref/player/effects/ambientocclusion
+ name = "Окружающее затенение"
+ description = "Добавляет затенение для объектов в игре, помогает придать объем изображению."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/effects/ambientocclusion/on_update(client/client, old_value)
+ client?.update_plane_masters(/atom/movable/screen/plane_master/game_world)
+
+/datum/pref/player/effects/parallax
+ name = "Качество параллакса"
+ description = "Качество анимации фонов в космосе. На высоких настройках может негативно влиять на производительность."
+ value_type = PREF_TYPE_SELECT
+ value = PARALLAX_HIGH
+ value_parameters = list(
+ PARALLAX_DISABLE = "Выключено",
+ PARALLAX_LOW = "Низкое",
+ PARALLAX_MED = "Среднее",
+ PARALLAX_HIGH = "Высокое",
+ PARALLAX_INSANE = "Безумное"
+ )
+
+/datum/pref/player/effects/parallax/on_update(client/client, old_value)
+ if(client?.mob?.hud_used)
+ client.mob.hud_used.update_parallax_pref()
+
+/datum/pref/player/effects/glowlevel // aka light sources bloom
+ name = "Уровень свечения"
+ description = "Добавляет легкий блюр источникам света. Подберите значение на свой вкус."
+ value_type = PREF_TYPE_SELECT
+ value = GLOW_MED
+ value_parameters = list(
+ GLOW_DISABLE = "Выключено",
+ GLOW_LOW = "Низкое",
+ GLOW_MED = "Среднее",
+ GLOW_HIGH = "Высокое"
+ )
+
+/datum/pref/player/effects/glowlevel/on_update(client/client, old_value)
+ client?.update_plane_masters(/atom/movable/screen/plane_master/lamps_selfglow)
+
+/datum/pref/player/effects/lampsexposure // still idk how to name it properly, spot light effect?
+ name = "Направленный свет от ламп"
+ description = "Визуально улучшает свет от ламп. Может влиять на производительность."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/effects/lampsexposure/on_update(client/client, old_value)
+ client?.update_plane_masters(/atom/movable/screen/plane_master/exposure)
+
+/datum/pref/player/effects/lampsglare
+ name = "Блик на источниках света"
+ description = "На случай, если вы фанат Джей Джей Абрамса."
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
+
+/datum/pref/player/effects/lampsglare/on_update(client/client, old_value)
+ client?.update_plane_masters(/atom/movable/screen/plane_master/lamps_glare)
+
+/datum/pref/player/effects/legacy_blur
+ name = "Старый блюр зрения"
+ description = "Использовать старый, менее затратный для производительности, эффект для повреждения/помех зрения персонажа."
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
+
+/datum/pref/player/effects/legacy_blur/on_update(client/client, old_value)
+ client?.update_plane_masters(/atom/movable/screen/plane_master/game_world)
+
+/datum/pref/player/effects/lobbyanimation
+ name = "Анимация экрана лобби"
+ description = "Включает анимированное лобби. На некоторых системах может лагать."
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
+
+/datum/pref/player/effects/lobbyanimation/on_update(client/client, old_value)
+ if(client && isnewplayer(client.mob))
+ var/mob/dead/new_player/M = client.mob
+ M.show_titlescreen()
diff --git a/code/modules/client/settings/player/game.dm b/code/modules/client/settings/player/game.dm
new file mode 100644
index 000000000000..56a7f7e5eac0
--- /dev/null
+++ b/code/modules/client/settings/player/game.dm
@@ -0,0 +1,53 @@
+/datum/pref/player/game
+ category = PREF_PLAYER_GAME
+
+// If hotkey mode is enabled, then clicking the map will automatically
+// unfocus the text bar. This removes the red color from the text bar
+// so that the visual focus indicator matches reality.
+/datum/pref/player/game/hotkey_mode
+ name = "Hotkey mode"
+ description = {"Если включено - фокус клавиатуры будет оставаться на игре. Рекомендуется, если вы используете WASD для движения и горячие клавиши.
+Если выключено - фокус будет оставаться на чате. Этот вариант больше подходит тем, кто хочет использовать стрелочки для движения и печатать на ходу."}
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/game/hotkey_mode/on_update(client/client, old_value)
+ if(value)
+ winset(client, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED]")
+ else
+ winset(client, null, "input.focus=true input.background-color=[COLOR_INPUT_DISABLED]")
+
+/datum/pref/player/game/melee_animation // todo: not only melee currently
+ name = "Анимация ближнего боя"
+ description = "Показывать или нет анимацию рукопашных атак."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/game/progressbar
+ name = "Индикатор прогресса"
+ description = "Показывать или нет анимированную шкалу для действий."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/game/endroundarena
+ name = "Арена конца раунда"
+ description = "Если включено - по окончанию раунда и до момента рестарта вы будете телепортированы на специальную гладиаторскую арену, где сможете выпустить весь накопившийся за раунд пар."
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
+
+/datum/pref/player/game/ghost_skin
+ name = "Вид призрака"
+ description = {"Вид вашего приведения, если вы умерли или зашли в раунд как обсервер.
+Выбор флаффа будет работать только в том случае, если у вас имеется залитый и одобренный флафф соответствующего типа."}
+ value_type = PREF_TYPE_SELECT
+ value = GHOST_SKIN_CHARACTER
+ value_parameters = list(
+ GHOST_SKIN_CHARACTER = "Персонаж",
+ GHOST_SKIN_GHOST = "Приведение",
+ GHOST_SKIN_FLUFF = "Флафф"
+ )
+
+/datum/pref/player/game/ghost_skin/on_update(client/client, old_value)
+ if(client && isobserver(client.mob))
+ var/mob/dead/observer/O = client.mob
+ O.update_skin()
diff --git a/code/modules/client/settings/player/meta.dm b/code/modules/client/settings/player/meta.dm
new file mode 100644
index 000000000000..f06648d2e0f6
--- /dev/null
+++ b/code/modules/client/settings/player/meta.dm
@@ -0,0 +1,53 @@
+// for special cases where we handle some prefs differently
+// these settings are hidden from client settings menu
+
+/datum/pref/player/meta
+
+/datum/pref/player/meta/lastchangelog
+ name = "lastchangelog"
+ value = ""
+ value_type = PREF_TYPE_TEXT
+
+/datum/pref/player/meta/default_slot
+ name = "default_slot"
+ value = 1
+ value_type = PREF_TYPE_CUSTOM
+
+/datum/pref/player/meta/default_slot/sanitize_value(new_value, client/client)
+ if(!client) // for automatic clientless updates in the future
+ return 1
+
+ return sanitize_integer(new_value, 1, GET_MAX_SAVE_SLOTS(client), initial(new_value))
+
+/datum/pref/player/meta/random_slot
+ name = "random_slot"
+ value = FALSE
+ value_type = PREF_TYPE_BOOLEAN
+
+// i don't want to create pref for every emote, and don't want to update prefs with any changes to emotes / emote panel
+// emotes in panel are opt-out so this is a list of disabled emotes for the emote panel, empty by default,
+// and we don't need to touch this in the future - sanitize will update it with any changes
+//
+// i don't like how this looks, but i have no better idea how to rewrite it
+/datum/pref/player/meta/disabled_emotes_emote_panel
+ name = "disabled_emotes"
+ value = ""
+ value_type = PREF_TYPE_CUSTOM
+
+/datum/pref/player/meta/disabled_emotes_emote_panel/sanitize_value(new_value, client/client)
+ var/list/new_list
+ if(islist(new_value))
+ new_list = new_value
+ else
+ new_list = params2list(new_value)
+
+ if(!length(new_list))
+ return ""
+
+ var/list/valid_emotes
+ // clean any not valid (probably removed) emotes
+ for(var/emote in new_list)
+ if(emote in global.emotes_for_emote_panel)
+ valid_emotes += emote
+
+ return list2params(valid_emotes)
diff --git a/code/modules/client/settings/player/ui.dm b/code/modules/client/settings/player/ui.dm
new file mode 100644
index 000000000000..39662141e8b1
--- /dev/null
+++ b/code/modules/client/settings/player/ui.dm
@@ -0,0 +1,96 @@
+/datum/pref/player/ui
+ category = PREF_PLAYER_UI
+
+/datum/pref/player/ui/runechat
+ name = "Runechat"
+ description = "Чат над головой персонажей."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/ui/ui_style
+ name = "Стиль UI"
+ description = "Стиль интерфейса игры."
+ value_type = PREF_TYPE_SELECT
+ value = UI_STYLE_WHITE
+ value_parameters = list(UI_STYLE_WHITE, UI_STYLE_MIDNIGHT, UI_STYLE_OLD, UI_STYLE_ORANGE)
+
+/datum/pref/player/ui/ui_style/on_update(client/client, old_value)
+ // check if current hud customizable and we can change it
+ // borgs, aliens, etc. use their own hud we should not touch
+ // todo: we can look for updates by copy_flags
+ var/datum/hud/current_style = client?.mob?.hud_used?.ui_style
+ if(current_style in global.customizable_ui_styles)
+ client.mob.hud_used.ui_style = global.available_ui_styles[value]
+ client.mob.refresh_hud()
+
+/datum/pref/player/ui/ui_style_color
+ name = "Цвет UI"
+ description = "Цвет интерфейса игры, опция лучше всего работает с белым стилем."
+ value_type = PREF_TYPE_HEX
+ value = "#ffffff"
+
+/datum/pref/player/ui/ui_style_color/on_update(client/client, old_value)
+ // same, don't color special huds
+ var/datum/hud/current_style = client?.mob?.hud_used?.ui_style
+ if(current_style in global.customizable_ui_styles)
+ client.mob.hud_used.ui_color = value
+ client.mob.refresh_hud()
+
+/datum/pref/player/ui/ui_style_opacity
+ name = "Прозрачность UI"
+ description = "Прозрачность интерфейса игры."
+ value_type = PREF_TYPE_RANGE
+ value = 0
+ value_parameters = list(0, 100)
+
+/datum/pref/player/ui/ui_style_opacity/on_update(client/client, old_value)
+ var/datum/hud/current_style = client?.mob?.hud_used?.ui_style
+ if(current_style in global.customizable_ui_styles)
+ client.mob.hud_used.ui_alpha = 255 - floor(255*value/100)
+ client.mob.refresh_hud()
+
+/datum/pref/player/ui/outline
+ name = "Подсветка предметов"
+ description = "По наведению мышью на предмет в игре, у него появится соответствующий контур."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/ui/outline_color
+ name = "Цвет подсветки"
+ value_type = PREF_TYPE_HEX
+ value = "#33ccff"
+
+/datum/pref/player/ui/tooltip
+ name = "Всплывающая подсказка"
+ description = "Подсказка в верхней части экрана, появляющаяся по наведению мышью на предмет в игре."
+ value_type = PREF_TYPE_BOOLEAN
+ value = TRUE
+
+/datum/pref/player/ui/tooltip/on_update(client/client, old_value)
+ client.tooltip.set_state(value)
+
+// i don't understand why we need it in preferences, pls don't add more fonts or styles preferences like this
+/datum/pref/player/ui/tooltip_font
+ name = "Шрифт подсказки"
+ value_type = PREF_TYPE_SELECT
+ value = FONT_SMALL_FONTS
+ value_parameters = list(
+ FONT_SYSTEM,
+ FONT_FIXEDSYS,
+ FONT_SMALL_FONTS,
+ FONT_TIMES_NEW_ROMAN,
+ FONT_SERIF,
+ FONT_VERDANA
+ )
+
+/datum/pref/player/ui/tooltip_size
+ name = "Размер подсказки"
+ value_type = PREF_TYPE_RANGE
+ value = 8
+ value_parameters = list(1, 15)
+
+/datum/pref/player/ui/tgui_lock
+ name = "TGUI только на основном мониторе"
+ description = "Блокирует перемещение окон tgui за пределы экрана."
+ value_type = PREF_TYPE_BOOLEAN
+ value = FALSE
diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm
index 651303df1e8e..f156c6abea0e 100644
--- a/code/modules/keybindings/bindings_client.dm
+++ b/code/modules/keybindings/bindings_client.dm
@@ -46,11 +46,9 @@
if(!(next_move_dir_sub & movement))
next_move_dir_add |= movement
- // Client-level keybindings are ones anyone should be able to do at any time
- // Things like taking screenshots, hitting tab, and adminhelps.
- var/AltMod = keys_held["Alt"] ? "Alt" : ""
- var/CtrlMod = keys_held["Ctrl"] ? "Ctrl" : ""
- var/ShiftMod = keys_held["Shift"] ? "Shift" : ""
+ var/AltMod = keys_held["Alt"] ? "Alt+" : ""
+ var/CtrlMod = keys_held["Ctrl"] ? "Ctrl+" : ""
+ var/ShiftMod = keys_held["Shift"] ? "Shift+" : ""
var/full_key
switch(_key)
if("Alt", "Ctrl", "Shift")
@@ -61,16 +59,16 @@
key_combos_held[_key] = full_key
else
full_key = _key
+
+ if(findtext(full_key, "+", -1))
+ full_key = copytext(full_key, 1, -1)
+
var/keycount = 0
- for(var/kb_name in prefs.key_bindings[full_key])
+ for(var/datum/pref/keybinds/kb as anything in prefs.key_bindings_by_key[full_key])
keycount++
- var/datum/keybinding/kb = global.keybindings_by_name[kb_name]
if(kb.can_use(src) && kb.down(src) && keycount >= MAX_COMMANDS_PER_KEY)
break
- holder?.key_down(full_key, src)
- mob.key_down(full_key, src)
-
/client/verb/keyUp(_key as text)
set instant = TRUE
set hidden = TRUE
@@ -92,9 +90,6 @@
// We don't do full key for release, because for mod keys you
// can hold different keys and releasing any should be handled by the key binding specifically
- for (var/kb_name in prefs.key_bindings[_key])
- var/datum/keybinding/kb = global.keybindings_by_name[kb_name]
+ for(var/datum/pref/keybinds/kb as anything in prefs.key_bindings_by_key[_key])
if(kb.can_use(src) && kb.up(src))
break
- holder?.key_up(_key, src)
- mob.key_up(_key, src)
diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm
index 19de92be004c..a0b47cf45ffe 100644
--- a/code/modules/keybindings/setup.dm
+++ b/code/modules/keybindings/setup.dm
@@ -1,9 +1,5 @@
// Set a client's focus to an object and override these procs on that object to let it handle keypresses
-/datum/proc/key_down(key, client/user) // Called when a key is pressed down initially
- return
-/datum/proc/key_up(key, client/user) // Called when a key is released
- return
/datum/proc/keyLoop(client/user) // Called once every frame
set waitfor = FALSE
return
@@ -24,23 +20,7 @@
winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED]")
- update_special_keybinds()
-
-// byond bug ID:2694120
-/client/verb/reset_macros_wrapper()
- set category = "OOC"
- set name = "Fix keybindings"
-
- reset_macros()
-
-/client/proc/reset_macros(skip_alert = FALSE)
- var/ans
- if(!skip_alert)
- ans = tgui_alert(src, "Change your keyboard language to ENG and press Ok", "Reset macros")
-
- if(skip_alert || ans == "Ok")
- set_macros()
- to_chat(src, "Keybindings were fixed") // not yet but set_macros works fast enough
+ update_movement_keybinds()
/**
* Manually clears any held keys, in case due to lag or other undefined behavior a key gets stuck.
diff --git a/code/modules/mob/dead/new_player/lobby.dm b/code/modules/mob/dead/new_player/lobby.dm
index 1a0b244ff481..2796ad4d89da 100644
--- a/code/modules/mob/dead/new_player/lobby.dm
+++ b/code/modules/mob/dead/new_player/lobby.dm
@@ -122,6 +122,7 @@ var/global/lobby_screen = "lobby"
"}
+ dat += "
"
if(config.alt_lobby_menu)
dat += {""}
@@ -136,6 +137,7 @@ var/global/lobby_screen = "lobby"
dat += {""}
dat += {""}
+ dat += {""}
dat += "
"
dat += {""}
@@ -143,7 +145,7 @@ var/global/lobby_screen = "lobby"
if(global.custom_lobby_image)
dat += {"
"}
- else if (client.prefs.lobbyanimation)
+ else if (client.prefs.get_pref(/datum/pref/player/effects/lobbyanimation))
dat += {"