Refactors player preferences for modularity + SQLite Unit Test (#37615)

* Pref code refactor

* Empty database reference

* Unit testing SQLite

* Everything else

* Disable unit testing.

* Equivalent

* more robust unit tests
This commit is contained in:
Anthony "Shifty Rail
2025-06-07 14:54:09 +01:00
committed by GitHub
parent 3290a3a747
commit 189f77cbb7
89 changed files with 4694 additions and 2543 deletions

View File

@@ -13,6 +13,12 @@
#define TARGET_MOUTH "mouth"
#define TARGET_EYES "eyes"
#define LIMB_HEART "heart"
#define LIMB_EYES "eyes"
#define LIMB_LUNG "lungs"
#define LIMB_LIVER "liver"
#define LIMB_KIDNEYS "kidneys"
#define UNCUFF_LEGS -1
#define UNCUFF_BOTH 0
#define UNCUFF_HANDS 1

View File

@@ -16,11 +16,13 @@
#define MOB_NO_PETRIFY 64 //can't get petrified
#define MOB_NO_LAZ 128 //Can not be revived via lazarus injector
#define NO_BACKPACK 1
#define BACKPACK 2
#define SATCHEL_NORM 3
#define SATCHEL_ALT 4
#define MESSENGER_BAG 5
#define NO_BACKPACK_STRING "1"
#define BACKPACK_STRING "2"
#define SATCHEL_NORM_STRING "3"
#define SATCHEL_ALT_STRING "4"

View File

@@ -19,3 +19,45 @@
#define HEADSET_SOUND_DISABLED 0
#define HEADSET_SOUND_TRANSMIT 1
#define HEADSET_SOUND_ALL 2
// list("None", "White Briefs", "Green Briefs", "Blue Briefs", "Black Briefs", "Grey Briefs", "Mankini", "Love-Hearts Boxers", "Black Boxers", "Grey Boxers", "Stripey Boxers", "Kinky", "Freedom Boxers", "Tea Boxers", "Communist Boxers", "Cowprint Boxers", "Green Wifebeater", "White Wifebeater", "Black Wifebeater")
// Curse whoever made male/female underwear different colours
#define UNDERWEAR_MALE_NONE 1
#define UNDERWEAR_MALE_WHITE_BRIEFS 2
#define UNDERWEAR_MALE_GREEN_BRIEFS 3
#define UNDERWEAR_MALE_BLUE_BRIEFS 4
#define UNDERWEAR_MALE_BLACK_BRIEFS 5
#define UNDERWEAR_MALE_GREY_BRIEFS 6
#define UNDERWEAR_MALE_MANKINI 7
#define UNDERWEAR_MALE_LOVE_HEARTS_BOXERS 8
#define UNDERWEAR_MALE_BLACK_BOXERS 9
#define UNDERWEAR_MALE_GREY_BOXERS 10
#define UNDERWEAR_MALE_STRIPEY_BOXERS 11
#define UNDERWEAR_MALE_KINKY 12
#define UNDERWEAR_MALE_FREEDOM_BOXERS 13
#define UNDERWEAR_MALE_TEA_BOXERS 14
#define UNDERWEAR_MALE_COMMUNIST_BOXERS 15
#define UNDERWEAR_MALE_COWPRINT_BOXERS 16
#define UNDERWEAR_MALE_GREEN_WIFEBEATER 17
#define UNDERWEAR_MALE_WHITE_WIFEBEATER 18
// list("None", "White", "Green", "Blue", "Black", "Yellow", "Thong", "Baby-Blue", "Babydoll", "Red", "Pink", "Kinky", "Freedom", "Tea", "Communist", "Cowprint", "Pink Husbandbeater", "White Husbandbeater", "Black Husbandbeater")
#define UNDERWEAR_FEMALE_NONE 1
#define UNDERWEAR_FEMALE_WHITE 2
#define UNDERWEAR_FEMALE_GREEN 3
#define UNDERWEAR_FEMALE_BLUE 4
#define UNDERWEAR_FEMALE_BLACK 5
#define UNDERWEAR_FEMALE_YELLOW 6
#define UNDERWEAR_FEMALE_THONG 7
#define UNDERWEAR_FEMALE_BABY_BLUE 8
#define UNDERWEAR_FEMALE_BABYDOLL 9
#define UNDERWEAR_FEMALE_RED 10
#define UNDERWEAR_FEMALE_PINK 11
#define UNDERWEAR_FEMALE_KINKY 12
#define UNDERWEAR_FEMALE_FREEDOM 13
#define UNDERWEAR_FEMALE_TEA 14
#define UNDERWEAR_FEMALE_COMMUNIST 15
#define UNDERWEAR_FEMALE_COWPRINT 16
#define UNDERWEAR_FEMALE_PINK_HUSBANDBEATER 17
#define UNDERWEAR_FEMALE_WHITE_HUSBANDBEATER 18
#define UNDERWEAR_FEMALE_BLACK_HUSBANDBEATER 19

View File

@@ -52,7 +52,7 @@
if(send2chat)
for(var/client/C in admins)
if(C.prefs.toggles & CHAT_DEBUGLOGS)
if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEBUGLOGS)
to_chat(C, "DEBUG: [text]")
/proc/log_sql(text)

View File

@@ -13,6 +13,52 @@ var/static/list/ALL_LIMBS = list(LIMB_HEAD,LIMB_CHEST,LIMB_GROIN,
return h_style
/proc/random_hair_color()
var/red = 0
var/green = 0
var/blue = 0
var/col = pick("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", 15;"punk")
switch(col)
if("blonde")
red = 255
green = 255
blue = 0
if("black")
red = 0
green = 0
blue = 0
if("chestnut")
red = 153
green = 102
blue = 51
if("copper")
red = 255
green = 153
blue = 0
if("brown")
red = 102
green = 51
blue = 0
if("wheat")
red = 255
green = 255
blue = 153
if("old")
red = rand (100, 255)
green = red
blue = red
if("punk")
red = rand(0, 255)
green = rand(0, 255)
blue = rand(0, 255)
red = max(min(red + rand (-25, 25), 255), 0)
green = max(min(green + rand (-25, 25), 255), 0)
blue = max(min(blue + rand (-25, 25), 255), 0)
return list(red, green, blue)
/proc/GetOppositeDir(var/dir)
switch(dir)
if(NORTH)

View File

@@ -515,7 +515,7 @@
var/holding = user.get_active_hand()
var/delayfraction = round(delay/numticks)
var/image/progbar
if(user && user.client && user.client.prefs.progress_bars)
if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars))
if(!progbar)
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0")
progbar.plane = HUD_PLANE
@@ -526,7 +526,7 @@
//barbar.pixel_y = 36
//var/oldstate
for (var/i = 1 to numticks)
if(user && user.client && user.client.prefs.progress_bars && progbar)
if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars) && progbar)
//oldstate = progbar.icon_state
progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]"
user.client.images |= progbar
@@ -568,7 +568,7 @@
for(var/atom/target in targets)
initial_target_locations[target] = target.loc
if(user.client && user.client.prefs.progress_bars)
if(user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars))
for(var/target in targets)
if(!targets[target])
var/image/new_progress_bar = create_progress_bar_on(target)
@@ -672,7 +672,7 @@
var/target_location = target.loc
var/image/progbar
//var/image/barbar
if(user && user.client && user.client.prefs.progress_bars && target)
if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars) && target)
if(!progbar)
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE
@@ -680,7 +680,7 @@
progbar.layer = HUD_ABOVE_ITEM_LAYER
progbar.appearance_flags = RESET_COLOR | RESET_TRANSFORM
for (var/i = 1 to numticks)
if(user && user.client && user.client.prefs.progress_bars && target)
if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars) && target)
if(!progbar)
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = target, "icon_state" = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE
@@ -1222,12 +1222,12 @@ Game Mode config tags:
. += M.client
/client/proc/output_to_special_tab(msg, force_focus = FALSE)
if(prefs.special_popup)
if(prefs.get_pref(/datum/preference_setting/enum/special_popup))
src << output("\[[time_stamp()]] [msg]", "window1.msay_output")
if(!holder) //Force normal players to see the admin message when it gets sent to them
winset(src, "rpane.special_button", "is-checked=true")
winset(src, null, "rpanewindow.left=window1")
if(prefs.special_popup == SPECIAL_POPUP_EXCLUSIVE)
if(prefs.get_pref(/datum/preference_setting/enum/special_popup) == SPECIAL_POPUP_EXCLUSIVE)
return
to_chat(src, msg)
@@ -1352,7 +1352,7 @@ Game Mode config tags:
var/mob/M = C
if(M.client)
C = M.client
if(!istype(C) || (!C.prefs.window_flashing && !ignorepref))
if(!istype(C) || (!C.prefs.get_pref(/datum/preference_setting/toggle/window_flashing) && !ignorepref))
return
winset(C, "mainwindow", "flash=5")

View File

@@ -209,9 +209,9 @@ var/global/obj/abstract/screen/clicker/catcher = new()
ui_color = null
ui_alpha = 255
else
ui_style = ui_style2icon(mymob.client.prefs.UI_style)
ui_color = mymob.client.prefs.UI_style_color
ui_alpha = mymob.client.prefs.UI_style_alpha
ui_style = ui_style2icon(mymob.client.prefs.get_pref(/datum/preference_setting/string/UI_style))
ui_color = mymob.client.prefs.get_pref(/datum/preference_setting/string/UI_style_color)
ui_alpha = mymob.client.prefs.get_pref(/datum/preference_setting/numerical/UI_style_alpha)
if(ishuman(mymob))
human_hud(ui_style, ui_color, ui_alpha) // Pass the player the UI style chosen in preferences

View File

@@ -111,7 +111,7 @@ var/list/cult_parallax[(GRID_WIDTH**2)]
/datum/hud/proc/update_parallax()
var/client/C = mymob.client
if(C.prefs.space_parallax)
if(C.prefs.get_pref(/datum/preference_setting/toggle/space_parallax))
parallax_on_clients |= C
for(var/obj/abstract/screen/parallax/bgobj in C.parallax)
C.screen |= bgobj
@@ -168,7 +168,7 @@ var/list/cult_parallax[(GRID_WIDTH**2)]
C.screen |= C.parallax_master
C.screen |= C.parallax_spacemaster
if(C.prefs.space_dust)
if(C.prefs.get_pref(/datum/preference_setting/toggle/space_dust))
switch(starlight)
if (STARLIGHT_REDSHIFT)
C.parallax_dustmaster.color = list(
@@ -226,8 +226,8 @@ var/list/cult_parallax[(GRID_WIDTH**2)]
C.previous_turf = posobj
for(var/obj/abstract/screen/parallax/bgobj in C.parallax_movable)
var/accumulated_offset_x = bgobj.base_offset_x - round(offsetx * bgobj.parallax_speed * C.prefs.parallax_speed)
var/accumulated_offset_y = bgobj.base_offset_y - round(offsety * bgobj.parallax_speed * C.prefs.parallax_speed)
var/accumulated_offset_x = bgobj.base_offset_x - round(offsetx * bgobj.parallax_speed * C.prefs.get_pref(/datum/preference_setting/numerical/parallax_speed))
var/accumulated_offset_y = bgobj.base_offset_y - round(offsety * bgobj.parallax_speed * C.prefs.get_pref(/datum/preference_setting/numerical/parallax_speed))
//This part makes the Parallax wrap around
while(accumulated_offset_x > PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE)

View File

@@ -103,7 +103,7 @@ var/global/list/screen_alarms_locs = list(
var/obj/abstract/screen/alert/alert = mobalerts[mobalerts[i]]
if(alert.icon_state == "template")
if(!icon_pref)
icon_pref = ui_style2icon(mymob.client.prefs.UI_style)
icon_pref = ui_style2icon(mymob.client.prefs.get_pref(/datum/preference_setting/string/UI_style))
alert.icon = icon_pref
alert.screen_loc = screen_alarms_locs[i]
mymob.client.screen |= alert

View File

@@ -18,7 +18,7 @@ var/datum/subsystem/ambientsound/SSambience
if(config.no_ambience)
return
for (var/client/C in clients)
if(C && (C.prefs.toggles & SOUND_AMBIENCE))
if(C && (C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_AMBIENCE))
var/mob/new_player/NP = C.mob
if(!istype(NP))
C.handle_ambience()
@@ -59,7 +59,7 @@ Ambience system.
if(prob(initial(picked_ambience_datum.prob_fire)))
ambience_buffer = world.timeofday+initial(picked_ambience_datum.length)
last_ambient_noise = initial(picked_ambience_datum.sound)
src << sound(last_ambient_noise, 0, 0, CHANNEL_AMBIENCE, prefs.ambience_volume)
src << sound(last_ambient_noise, 0, 0, CHANNEL_AMBIENCE, prefs.get_pref(/datum/preference_setting/numerical/ambience_volume))
/client/proc/get_ambience()
var/list/personal = mob.get_personal_ambience()

View File

@@ -84,7 +84,7 @@ var/runechat_icon = null
owner.register_event(/event/destroyed, src, nameof(src::qdel_self()))
// Clip message
var/maxlen = owned_by.prefs.max_chat_length
var/maxlen = owned_by.prefs.get_pref(/datum/preference_setting/numerical/max_chat_length)
if (length_char(text) > maxlen)
text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment

View File

@@ -62,9 +62,9 @@
if(!M.client || isnewplayer(M))
continue
var/T = get_turf(user)
if(isobserver(M) && M.client && (M.client.prefs.toggles & CHAT_GHOSTSIGHT) && !(M in viewers(T)))
if(isobserver(M) && M.client && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTSIGHT) && !(M in viewers(T)))
M.show_message(formatFollow(user) + " " + msg)
if (user.client && M?.client?.prefs.mob_chat_on_map && get_dist(M, user) < M?.client.view)
if (user.client && M?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && get_dist(M, user) < M?.client.view)
M.create_chat_message(user, null, msg_runechat, "", list("italics"))
if(emote_type & EMOTE_VISIBLE)
@@ -72,13 +72,13 @@
if(!(emote_type & EMOTE_NO_RUNECHAT))
for(var/z0 in GetOpenConnectedZlevels(user))
for (var/mob/O in viewers(world.view, locate(user.x,user.y,z0)))
if (user.client && O?.client?.prefs.mob_chat_on_map && O.stat != UNCONSCIOUS && !(isinvisible(user)))
if (user.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && O.stat != UNCONSCIOUS && !(isinvisible(user)))
O.create_chat_message(user, null, msg_runechat, "", list("italics"))
else if(emote_type & EMOTE_AUDIBLE)
for(var/mob/O in get_hearers_in_view(world.view, user))
O.show_message(msg)
if(!(emote_type & EMOTE_NO_RUNECHAT))
if(user.client && O?.client?.prefs.mob_chat_on_map && O.stat != UNCONSCIOUS && !O.is_deaf())
if(user.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && O.stat != UNCONSCIOUS && !O.is_deaf())
O.create_chat_message(user, null, msg_runechat, "", list("italics"))
var/turf/T = get_turf(user)

View File

@@ -564,9 +564,9 @@ Assign your candidates in choose_candidates() instead.
if(job_master.TryAssignJob(old_AI,level,job))
break
if(!old_AI.mind.assigned_role) // still no job
if(old_AI.client.prefs.alternate_option == GET_RANDOM_JOB)
if(old_AI.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_RANDOM_JOB)
job_master.GiveRandomJob(old_AI)
else if(old_AI.client.prefs.alternate_option == BE_ASSISTANT)
else if(old_AI.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == BE_ASSISTANT)
job_master.AssignRole(old_AI, "Assistant")
if(!old_AI.mind.assigned_role)
to_chat(old_AI, "<span class='danger'>You have been returned to lobby due to your job preferences being filled.")

View File

@@ -61,9 +61,9 @@
if (man.mind.assigned_role == "MODE") // Wiz, nukies, ...
continue
if(man.client.prefs.nanotrasen_relation == "Opposed")
if(man.client.prefs.get_pref(/datum/preference_setting/enum/string/nanotrasen_relation) == "Opposed")
dudes += man
else if(man.client.prefs.nanotrasen_relation == "Skeptical" && prob(50))
else if(man.client.prefs.get_pref(/datum/preference_setting/enum/string/nanotrasen_relation) == "Skeptical" && prob(50))
dudes += man
else if( (man.mind.antag_roles[CULTIST] && prob(40)) || \

View File

@@ -71,7 +71,7 @@
antag.current << sound('sound/effects/ling_intro.ogg')
/datum/role/changeling/ForgeObjectives()
if(!antag.current.client.prefs.antag_objectives)
if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives))
AppendObjective(/datum/objective/freeform/changeling)
return
AppendObjective(/datum/objective/absorb)

View File

@@ -39,7 +39,7 @@
.=..()
/datum/role/traitor/ForgeObjectives()
if(!antag.current.client.prefs.antag_objectives)
if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives))
AppendObjective(/datum/objective/freeform/syndicate)
return
if(istype(antag.current, /mob/living/silicon))

View File

@@ -107,7 +107,7 @@
. += ..() // Who he was, his objectives...
/datum/role/vampire/ForgeObjectives()
if(!antag.current.client.prefs.antag_objectives)
if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives))
AppendObjective(/datum/objective/freeform/vampire)
return

View File

@@ -20,7 +20,7 @@
var/list/potions_bought = list()
/datum/role/wizard/ForgeObjectives()
if(!antag.current.client.prefs.antag_objectives)
if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives))
AppendObjective(/datum/objective/freeform/wizard)
return
switch(rand(1,100))

View File

@@ -115,7 +115,7 @@
/datum/intercept_text/proc/get_suspect()
var/list/dudes = list()
for(var/mob/living/carbon/human/man in player_list) if(man.client && man.client.prefs.nanotrasen_relation == "Opposed")
for(var/mob/living/carbon/human/man in player_list) if(man.client && man.client.prefs.get_pref(/datum/preference_setting/enum/string/nanotrasen_relation) == "Opposed")
dudes += man
for(var/i = 0, i < max(player_list.len/10,2), i++)
dudes += pick(player_list)

View File

@@ -326,7 +326,7 @@ We don't care about names, DNA, accounts, activity, any of that. We're just gonn
if(!player.ready)
continue
var/list/jobs = player.client.prefs.jobs
var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs)
for(var/job in jobs)
if((jobs[job] == JOB_PREF_HIGH) && GetJob(job))

View File

@@ -79,7 +79,7 @@
var/log = replacetext(n, "\n", "(new line)")//no intentionally spamming admins with 100 lines, nice try
log_say("[pda_device] notes - [U] changed the text to: [log]")
for(var/mob/dead/observer/M in player_list)
if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTPDA))
if(M.stat == DEAD && M.client && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTPDA))
M.show_message("<span class='game say'>[pda_device] notes - <span class = 'name'>[U]</span> changed the text to:</span> [log]")
else
U << browse(null, "window=pda")

View File

@@ -273,7 +273,7 @@
P_app.tnote["[msg_id]"] = "<i><b>&larr; From <a href='byond://?src=\ref[P_app];choice=Message;target=\ref[reply_to]'>[pda_device.owner]</a> ([pda_device.ownjob]):</b></i><br>[t]<br>"
msg_id++
for(var/mob/dead/observer/M in player_list)
if(!multicast_message && M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTPDA)) // src.client is so that ghosts don't have to listen to mice
if(!multicast_message && M.stat == DEAD && M.client && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTPDA)) // src.client is so that ghosts don't have to listen to mice
M.show_message("[formatFollow(U)] <span class='game say'>PDA Message - <span class='name'>\
[U.real_name][U.real_name == pda_device.owner ? "" : " (as [pda_device.owner])"]</span> -> <span class='name'>[P.owner]</span>: <span class='message'>[t]</span>\
[pda_device.photo ? " (<a href='byond://?src=\ref[P_app];choice=viewPhoto;image=\ref[pda_device.photo];skiprefresh=1;target=\ref[reply_to]'>View Photo</a>)</span>" : ""]")

View File

@@ -35,7 +35,7 @@
/obj/item/device/radio/headset/talk_into(datum/speech/speech_orig, channel=null)
if(!broadcasting)
return
if(usr?.client?.prefs.headset_sound)
if(usr?.client?.prefs.get_pref(/datum/preference_setting/numerical/headset_sound))
playsound(usr, 'sound/effects/radio_chatter.ogg', 100, 1, vary = 0)
return ..()
@@ -43,7 +43,7 @@
if(ishuman(src.loc))
var/mob/living/carbon/human/H = src.loc
if(H.ears == src)
if(H.client && (H.client.prefs.headset_sound == HEADSET_SOUND_ALL))
if(H.client && (H.client.prefs.get_pref(/datum/preference_setting/numerical/headset_sound) == HEADSET_SOUND_ALL))
playsound(H, 'sound/effects/radio_chatter.ogg', 100, 1, vary = 0)
return ..(freq, level)
return -1

View File

@@ -237,7 +237,7 @@ var/global/list/reagents_to_always_log = list(AMUTATIONTOXIN, CYANIDE, CHEFSPECI
return 0
else
delayNextpAIMove(getpAIMovementDelay())
if (user.client.prefs.stumble && ((world.time - user.last_movement) > 5) && getpAIMovementDelay() < 2)
if (user.client.prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - user.last_movement) > 5) && getpAIMovementDelay() < 2)
delayNextpAIMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier
user.last_movement=world.time
return 1

View File

@@ -72,7 +72,7 @@
if(istype(instrumentObj,/obj/item/device/instrument))
var/obj/item/device/instrument/INS = instrumentObj
INS.OnPlayed(user,M)
if(!M.client.prefs.hear_instruments)
if(!M.client.prefs.get_pref(/datum/preference_setting/toggle/hear_instruments))
continue
M.playsound_local(source, soundfile, 100, falloff = 5)
if(recording && live)

View File

@@ -169,7 +169,7 @@ var/const/SURROUND_CAP = 7
/client/proc/playtitlemusic()
if(!ticker || !ticker.login_music || config.no_lobby_music)
return
if(prefs.toggles & SOUND_LOBBY)
if(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_LOBBY)
if(istype(src))
src << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBY) // MAD JAMS

View File

@@ -21,7 +21,7 @@ var/adminbus_ooc_color
if(!msg)
return
if(!(prefs.toggles & CHAT_OOC))
if(!(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_OOC))
to_chat(src, "<span class='warning'>You have OOC muted.</span>")
return
@@ -59,10 +59,10 @@ var/adminbus_ooc_color
admin_color = global.adminbus_ooc_color
if(holder && !holder.fakekey && (holder.rights & R_ADMIN) && config.allow_admin_ooccolor)
admin_color = src.prefs.ooccolor
admin_color = src.prefs.get_pref(/datum/preference_setting/string/ooc_color)
for(var/client/C in clients)
if(!(C.prefs.toggles & CHAT_OOC) || iscluwnebanned(C.mob))
if(!(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_OOC) || iscluwnebanned(C.mob))
continue
var/display_name = src.key
@@ -111,7 +111,7 @@ var/adminbus_ooc_color
if(!msg)
return
if(!(prefs.toggles & CHAT_LOOC))
if(!(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC))
to_chat(src, "<span class='warning'>You have LOOC muted.</span>")
return
@@ -159,7 +159,7 @@ var/adminbus_ooc_color
var/mob/camera/aiEye/E = M
if(E.ai)
C = E.ai.client
if(C.prefs.toggles & CHAT_LOOC)
if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC)
var/display_name = src.key
var/is_living = isliving(src.mob) //Ghosts will show up with their ckey, living people show up with their names
if(holder)
@@ -171,7 +171,7 @@ var/adminbus_ooc_color
to_chat(C, "<font color='#6699CC'><span class='looc'><span class='prefix'>LOOC:</span> <EM>[is_living ? src.mob.name : display_name]:</EM> <span class='message'>[msg]</span></span></font>")
for(var/client/C in admins)
if(C.prefs.toggles & CHAT_LOOC)
if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC)
var/prefix = "(R)LOOC"
if (C.mob in heard)
prefix = "LOOC"
@@ -181,7 +181,7 @@ var/adminbus_ooc_color
if (C in admins)
return //already been handled
if(C.prefs.toggles & CHAT_LOOC)
if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC)
var/display_name = src.key
var/is_living = isliving(src.mob)
if(holder)

View File

@@ -16,7 +16,7 @@ var/global/floorIsLava = 0
log_adminwarn(rendered)
for(var/client/C in admins)
if(R_ADMIN & C.holder.rights)
if(C.prefs.toggles & CHAT_ATTACKLOGS)
if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_ATTACKLOGS)
var/msg = rendered
to_chat(C, msg)

View File

@@ -528,7 +528,8 @@ var/list/admin_verbs_mod = list(
return
var/new_ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color|null
if(new_ooccolor)
prefs.ooccolor = new_ooccolor
var/datum/preference_setting/ooc_color = prefs.get_pref_datum(/datum/preference_setting/string/ooc_color)
ooc_color.setting = new_ooccolor
prefs.save_preferences_sqlite(src, ckey)
feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
@@ -577,11 +578,13 @@ var/list/admin_verbs_mod = list(
if(!warn_reason)
return
holder.notes_add(warned_ckey, warn_reason)
if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:)
var/datum/preference_setting/warns = D.get_pref_datum(/datum/preference_setting/numerical/warns)
if(++warns.setting >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:)
var/bantime = AUTOBANTIME//= (++D.warnbans * AUTOBANTIME)
D.warns = 0
++D.warnbans
for(var/i = 1; i < D.warnbans; i++)
warns.setting = 0
var/datum/preference_setting/warnbans = D.get_pref_datum(/datum/preference_setting/numerical/warnbans)
++warnbans.setting
for(var/i = 1; i < warnbans.setting; i++)
bantime *= 2
ban_unban_log_save("[ckey] warned [warned_ckey] - [warn_reason], resulting in a [bantime] minute autoban.")
if(C)
@@ -595,14 +598,18 @@ var/list/admin_verbs_mod = list(
D.save_preferences_sqlite(C, C.ckey)
del(C)
else
var/warnbans = D.get_pref(/datum/preference_setting/numerical/warnbans)
if(C)
to_chat(C, "<span class='danger'><BIG>You have been formally warned by an administrator - Reason: [warn_reason].</span></BIG><br>Further warnings will result in an autoban.</font>")
message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] - [warn_reason]. They have [MAX_WARNS-D.warns] strikes remaining. And have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]")
message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] - [warn_reason]. They have [MAX_WARNS-warnbans] strikes remaining. And have been warn banned [warnbans] [warnbans == 1 ? "time" : "times"]")
else
message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC) - [warn_reason]. They have [MAX_WARNS-D.warns] strikes remaining. And have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]")
D.show_warning_next_time = 1
D.last_warned_message = warn_reason
D.warning_admin = ckey
message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC) - [warn_reason]. They have [MAX_WARNS-warnbans] strikes remaining. And have been warn banned [warnbans] [warnbans == 1 ? "time" : "times"]")
var/datum/preference_setting/show_warning_next_time = D.get_pref_datum(/datum/preference_setting/toggle/show_warning_next_time)
show_warning_next_time.setting = 1
var/datum/preference_setting/last_warned_message = D.get_pref_datum(/datum/preference_setting/string/last_warned_message)
last_warned_message.setting = warn_reason
var/datum/preference_setting/warning_admin = D.get_pref_datum(/datum/preference_setting/string/last_warned_message)
warning_admin.setting = ckey
D.save_preferences_sqlite(C, warned_ckey)
feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -627,17 +634,18 @@ var/list/admin_verbs_mod = list(
to_chat(src, "<span class='red'>Error: unwarn(): No such ckey found.</span>")
return
if(D.warns == 0)
if(D.get_pref(/datum/preference_setting/numerical/warns) == 0)
to_chat(src, "<span class='red'>Error: unwarn(): You can't unwarn someone with 0 warnings, you big dummy.</span>")
return
D.warns-=1
var/strikesleft = MAX_WARNS-D.warns
var/datum/preference_setting/warns = D.get_pref_datum(/datum/preference_setting/numerical/warns)
warns.setting-=1
var/strikesleft = MAX_WARNS-warns.setting
if(C)
to_chat(C, "<span class='red'><BIG><B>One of your warnings has been removed.</B></BIG><br>You currently have [strikesleft] strike\s left</span>")
message_admins("[key_name_admin(src)] has unwarned [key_name_admin(C)]. They have [strikesleft] strike(s) remaining, and have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]")
message_admins("[key_name_admin(src)] has unwarned [key_name_admin(C)]. They have [strikesleft] strike(s) remaining, and have been warn banned [D.get_pref(/datum/preference_setting/numerical/warnbans)] [D.get_pref(/datum/preference_setting/numerical/warnbans) == 1 ? "time" : "times"]")
else
message_admins("[key_name_admin(src)] has unwarned [warned_ckey] (DC). They have [strikesleft] strike(s) remaining, and have been warn banned [D.warnbans] [D.warnbans == 1 ? "time" : "times"]")
message_admins("[key_name_admin(src)] has unwarned [warned_ckey] (DC). They have [strikesleft] strike(s) remaining, and have been warn banned [D.get_pref(/datum/preference_setting/numerical/warnbans)] [D.get_pref(/datum/preference_setting/numerical/warnbans) == 1 ? "time" : "times"]")
D.save_preferences_sqlite(C, C.ckey)
feedback_add_details("admin_verb","UNWARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -96,7 +96,7 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","
if((R_ADMIN|R_MOD) & X.holder.rights)
if(X.is_afk())
admin_number_afk++
if(X.prefs.toggles & SOUND_ADMINHELP)
if(X.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_ADMINHELP)
X << 'sound/effects/adminhelp.ogg'
X.output_to_special_tab(msg)

View File

@@ -137,7 +137,7 @@
//play the receiving admin the adminhelp sound (if they have them enabled)
//non-admins shouldn't be able to disable this
if(C.prefs.toggles & SOUND_ADMINHELP)
if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_ADMINHELP)
C << 'sound/effects/adminhelp.ogg'
/*

View File

@@ -11,7 +11,7 @@
to_chat(src, "<span class='warning'>You cannot send DSAY messages (muted).</span>")
return
if(!(prefs.toggles & CHAT_DEAD))
if(!(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(src, "<span class='warning'>You have deadchat muted.</span>")
return
@@ -30,10 +30,10 @@
if (istype(M, /mob/new_player) || !M.client)
continue
if(M.client && M.client.holder && (M.client.prefs.toggles & CHAT_DEAD)) // show the message to admins who have deadchat toggled on
if(M.client && M.client.holder && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // show the message to admins who have deadchat toggled on
M.show_message(rendered, 2)
else if(M.client && M.stat == DEAD && (M.client) && (M.client.prefs.toggles & CHAT_DEAD)) // show the message to regular ghosts who have deadchat toggled on
else if(M.client && M.stat == DEAD && (M.client) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // show the message to regular ghosts who have deadchat toggled on
M.show_message(rendered, 2)
feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -5,6 +5,9 @@ var/list/forbidden_varedit_object_types = list(
/datum/subsystem/dbcore/, // No messing with the database.
)
/datum/proc/can_edit_var(var/edited_variable)
return TRUE
//Interface for editing a variable. It returns its new value. If edited_datum, it automatically changes the edited datum's value
//If called with just [user] argument, it allows you to create a value such as a string, a number, an empty list, a nearby object, etc...
//If called with [edited_datum] and [edited_variable], you gain the ability to get the variable's initial value.
@@ -24,6 +27,9 @@ var/list/forbidden_varedit_object_types = list(
if(!C.can_edit_var(edited_variable, edited_datum?.type))
return
if(!edited_datum.can_edit_var(edited_variable))
return
//Special case for "appearance", because appearance values can't be stored anywhere.
//It's impossible for this proc to return an appearance value, so just set it directly here
if((isimage(edited_datum) || isatom(edited_datum)) && edited_variable == "appearance")

View File

@@ -26,7 +26,7 @@
for(var/mob/M in player_list)
if(!M.client)
continue
if(M.client.prefs.toggles & SOUND_MIDI)
if(M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_MIDI)
M << uploaded_sound
feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -60,7 +60,7 @@
if((R_ADMIN|R_MOD) & C.holder.rights)
if(C.is_afk())
admin_number_afk++
if(C.prefs.toggles & CHAT_PRAYER)
if(C.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_PRAYER)
C.output_to_special_tab(msg)
C << sound

View File

@@ -88,7 +88,7 @@
if (some_hearer.ear_deaf > 0)
continue
// var/dist = get_dist(some_hearer, src)
if (!some_hearer.client.prefs.hear_instruments)
if (!some_hearer.client.prefs.get_pref(/datum/preference_setting/toggle/hear_instruments))
continue
present_listeners += some_hearer
last_updated_listeners = world.time

View File

@@ -23,6 +23,7 @@ var/bunker_up = 0 //this round
var/bunker_saved = 0 //saved to file
var/bunker_setting = 0
var/updated_stats = 0
/client
var/account_joined = ""
var/account_age
@@ -238,7 +239,7 @@ var/updated_stats = 0
holder = admin_datums[ckey]
if(holder)
if(prefs.toggles & AUTO_DEADMIN)
if(prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & AUTO_DEADMIN)
message_admins("[src] was automatically de-admined.")
deadmin()
verbs += /client/proc/readmin
@@ -322,15 +323,16 @@ var/updated_stats = 0
qdel(update_query)
qdel(query)
if (prefs && prefs.show_warning_next_time)
to_chat(src, "<span class='notice'><b>You, or another user of this ckey ([ckey]) were warned by [prefs.warning_admin].</b></span>")
to_chat(src, "<span class='notice'>The reason was: '[prefs.last_warned_message]'.</span>")
if (prefs && prefs.get_pref(/datum/preference_setting/toggle/show_warning_next_time))
to_chat(src, "<span class='notice'><b>You, or another user of this ckey ([ckey]) were warned by [prefs.get_pref(/datum/preference_setting/string/warning_admin)].</b></span>")
to_chat(src, "<span class='notice'>The reason was: '[prefs.get_pref(/datum/preference_setting/string/last_warned_message)]'.</span>")
to_chat(src, "<span class='notice'>For more information, you can ask admins in ahelps or at https://ss13.moe. </span>")
to_chat(src, "<span class='notice'><b>You can now play the game.</b></span>")
prefs.show_warning_next_time = 0
var/datum/preference_setting/show_warning_next_time = prefs.get_pref_datum(/datum/preference_setting/toggle/show_warning_next_time)
show_warning_next_time.setting = 0
prefs.save_preferences_sqlite(src, src.ckey)
if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates.
if(prefs.get_pref(/datum/preference_setting/string/changelog) != changelog_hash) //bolds the changelog button on the interface so we know there are updates.
winset_wrapper("rpane.changelog", "background-color=#eaeaea;font-style=bold")
prefs.SetChangelog(ckey,changelog_hash)
to_chat(src, "<span class='info'>Changelog has changed since your last visit.</span>")
@@ -357,7 +359,7 @@ var/updated_stats = 0
if(!tooltips)
tooltips = new /datum/tooltip(src)
fps = (prefs.fps < 0) ? RECOMMENDED_CLIENT_FPS : prefs.fps
fps = (prefs.get_pref(/datum/preference_setting/numerical/fps) < 0) ? RECOMMENDED_CLIENT_FPS : prefs.get_pref(/datum/preference_setting/numerical/fps)
// This is wrapped so that if the winset fucks up somehow, this doesn't crash the entire client login proc.
/client/proc/winset_wrapper(control_id, params)
@@ -501,7 +503,7 @@ var/updated_stats = 0
player_age = 0
if(age < MINIMUM_NON_SUS_ACCOUNT_AGE)
for(var/client/X in admins)
if(X.prefs.toggles & SOUND_ADMINHELP)
if(X.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_ADMINHELP)
X << 'sound/effects/adminhelp.ogg'
message_admins("[ckey(key)]/([src]) is a relatively new player, may consider watching them. AGE = [age] First seen = [player_age]")
log_admin(("[ckey(key)]/([src]) is a relatively new player, may consider watching them. AGE = [age] First seen = [player_age]"))
@@ -752,7 +754,7 @@ NOTE: You will only be polled about this role once per round. To change your ch
return
/client/proc/update_special_views()
if(prefs.space_parallax) //Updating parallax for clients that have parallax turned on.
if(prefs.get_pref(/datum/preference_setting/toggle/space_parallax)) //Updating parallax for clients that have parallax turned on.
if(parallax_initialized)
mob.hud_used.update_parallax_values()
@@ -803,7 +805,7 @@ NOTE: You will only be polled about this role once per round. To change your ch
ViewFilter = newimages
/client/proc/handle_hear_voice(var/mob/origin)
if(prefs.hear_voicesound)
if(prefs.get_pref(/datum/preference_setting/toggle/hear_voicesound))
if(issilicon(origin))
mob.playsound_local(get_turf(origin), get_sfx("voice-silicon"),50,1)
if(isvox(origin))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,227 @@
// -- Preference datums for easy, maintainable prefs to add --
/datum/preference_setting
var/name = "Abstract preference setting" // Actual name
var/sql_name = "" // Name in the sql query
var/sql_table = "abstract"
var/enabled = FALSE // Is it actually used in game?
var/default_setting
var/setting = null
var/saved_as_string = FALSE // This is ENTIRELY from historical reasons! Do not mess with it, or risk the pref not working!
var/datum/preferences/parent
/datum/preference_setting/New(var/datum/preferences/parent_prefs)
parent = parent_prefs
setting = default_setting
return ..()
/datum/preference_setting/Destroy()
parent = null
return ..()
// This is to prevent admins `accidentally` breaking the database.
/datum/preference_setting/can_edit_var(var/edited_variable)
switch (edited_variable)
// Block varedits related to SQL
if ("sql_table", "sql_name", "enabled", "parent", "saved_as_string")
return
else
return ..()
// Change setting to new_setting
/datum/preference_setting/proc/set_setting(var/new_setting)
new_setting = sanitize_setting(new_setting)
setting = new_setting
return setting
// Sanity checks
/datum/preference_setting/proc/sanitize_setting(var/new_setting)
return new_setting ? new_setting : default_setting // Return the default setting in case of null
// Convert the raw SQL value into something byond-readable
// For HISTORICAL REASONS (forma de technical debt), a lot of stuff is saved in the DB as a string
// so usually, this means converting whatever you have from a string into a number.
// Other examples include: the job list is loaded as a json string.
/datum/preference_setting/proc/load_sql(var/sql_value)
return sql_value
// Opposite operation.
// At the moment, this is only relevant for:
// - Player alt-titles, saved as some weird string;
// - jobs list, saved as JSON
/datum/preference_setting/proc/save_sql()
return setting
// Handles the href changes (including things like input(), randomisation, etc)
/datum/preference_setting/proc/process_link(var/task, var/mob/user, var/list/href_list) // href_list is for extra data.
switch (task)
if ("random")
randomise(user)
return TRUE
if ("input")
choose_setting(user)
return TRUE
return FALSE // Need to do something more
// These are wrapped
/datum/preference_setting/proc/randomise(var/mob/user)
/datum/preference_setting/proc/choose_setting(var/mob/user)
// -- Abstract categories
// Binary toggles
/datum/preference_setting/toggle
default_setting = TRUE
/datum/preference_setting/toggle/sanitize_setting(var/new_setting)
return sanitize_integer(new_setting, 0, 1, default_setting)
/datum/preference_setting/toggle/load_sql(var/sql_value)
if (saved_as_string)
return text2num(sql_value)
else
return sql_value
/datum/preference_setting/toggle/save_sql()
if (saved_as_string)
return num2text(setting)
else
return setting
/datum/preference_setting/toggle/choose_setting(var/mob/user)
setting = !setting
parent.ShowChoices(user)
// Numerical values
/datum/preference_setting/numerical
default_setting = 0
var/min_value = 0
var/max_value = 100
/datum/preference_setting/numerical/sanitize_setting(var/new_setting)
return sanitize_integer(new_setting, min_value, max_value, default_setting)
/datum/preference_setting/numerical/load_sql(var/sql_value)
if (saved_as_string)
return text2num(sql_value)
else
return sql_value
/datum/preference_setting/numerical/save_sql()
if (saved_as_string)
return num2text(setting)
else
return setting
/datum/preference_setting/numerical/randomise()
setting = rand(min_value, max_value)
// Floating point values
// Numerical values
/datum/preference_setting/float
default_setting = 0
var/min_value = 0
var/max_value = 1
/datum/preference_setting/float/sanitize_setting(var/new_setting)
if (isnum(new_setting))
return clamp(new_setting, min_value, max_value)
return default_setting
/datum/preference_setting/float/load_sql(var/sql_value)
if (saved_as_string)
return text2num(sql_value)
else
return sql_value
/datum/preference_setting/float/save_sql()
if (saved_as_string)
return num2text(setting)
else
return setting
// Binary flags
/datum/preference_setting/binary_flag
default_setting = 0
var/max_binary_value = (1 << 12)
var/toggles = list() // The actual flag toggles this is checking against
/datum/preference_setting/binary_flag/sanitize_setting(var/new_setting)
return sanitize_integer(new_setting, 0, max_binary_value, default_setting)
/datum/preference_setting/binary_flag/load_sql(var/sql_value)
if (saved_as_string)
return text2num(sql_value)
else
return sql_value
/datum/preference_setting/binary_flag/save_sql()
if (saved_as_string)
return num2text(setting)
else
return setting
// Strings
/datum/preference_setting/string
default_setting = ""
var/max_length = 0
/datum/preference_setting/string/sanitize_setting(var/new_setting)
return sanitize_text(new_setting, default_setting)
// "Enums"
/datum/preference_setting/enum
default_setting = null
var/allowed_values = list(null)
/datum/preference_setting/enum/sanitize_setting(var/new_setting)
if (!(new_setting in allowed_values))
return default_setting
return new_setting
/datum/preference_setting/enum/load_sql(var/sql_value)
if (saved_as_string)
return text2num(sql_value)
else
return sql_value
/datum/preference_setting/enum/save_sql()
if (saved_as_string)
return num2text(setting)
else
return setting
/datum/preference_setting/enum/string
saved_as_string = FALSE
// -- Typically of the form:
/* list(
* ROLE_ONE = JOB_PREF_HIGH,
* ROLE_TWO = JOB_PREF_LOW,
* )
*
*/
/datum/preference_setting/assoc_list_setting
default_setting = list()
var/allowed_values_for_list_items = list() // Alllowed values for the assoc list in the setting. First value = default.
/datum/preference_setting/assoc_list_setting/sanitize_setting(var/new_setting)
if (!islist(new_setting))
return default_setting
for (var/data in new_setting)
if (!(new_setting[data] in allowed_values_for_list_items))
new_setting[data] = allowed_values_for_list_items[1]
return new_setting
// -- For general lists (this is only used for alt-titles now) --
/datum/preference_setting/list_values
default_setting = list()

View File

@@ -0,0 +1,861 @@
//our character's name
/datum/preference_setting/string/real_name
name = "Character name"
sql_name = "real_name"
sql_table = "players"
enabled = TRUE
default_setting = "John D. Spessman"
/datum/preference_setting/string/real_name/sanitize_setting(var/new_setting)
return reject_bad_name(new_setting)
/datum/preference_setting/string/real_name/randomise(var/mob/user)
var/gender = parent.get_pref(/datum/preference_setting/enum/gender)
var/species = parent.get_pref(/datum/preference_setting/string/species)
setting = random_name(gender, species)
parent.ShowChoices(user)
return setting
/datum/preference_setting/string/real_name/choose_setting(var/mob/user)
var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null )
if(new_name)
setting = new_name
else
to_chat(user, "<span class='red'>Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ', some diacritics, and .</span>")
parent.ShowChoices(user)
//whether we are a random name every round
/datum/preference_setting/toggle/be_random_name
name = "Be random name"
sql_name = "random_name"
sql_table = "players"
enabled = TRUE
default_setting = FALSE
//whether we are a random name every round
/datum/preference_setting/toggle/be_random_body
name = "Be random body"
sql_name = "random_body"
sql_table = "players"
enabled = TRUE
default_setting = FALSE
//gender crticial theory [gender of character (well duh)]
/datum/preference_setting/enum/gender
name = "Gender"
sql_name = "gender"
sql_table = "players"
enabled = TRUE
default_setting = MALE // smh
allowed_values = list(MALE, FEMALE)
/datum/preference_setting/enum/gender/choose_setting(mob/user)
if(setting == MALE)
setting = FEMALE
else
setting = MALE
var/datum/preference_setting/species = parent.get_pref_datum(/datum/preference_setting/string/species)
var/datum/preference_setting/f_style = parent.get_pref_datum(/datum/preference_setting/string/f_style)
var/datum/preference_setting/h_style = parent.get_pref_datum(/datum/preference_setting/string/h_style)
f_style.setting = random_facial_hair_style(setting, species)
h_style.setting = random_hair_style(setting, species)
parent.ShowChoices(user)
/datum/preference_setting/enum/gender/sanitize_setting(var/new_setting)
return sanitize_gender(new_setting) // Historical reasons
//age of character
/datum/preference_setting/numerical/age
name = "Age"
sql_name = "age"
sql_table = "players"
enabled = TRUE
default_setting = 30 // smh
min_value = AGE_MIN
max_value = AGE_MAX
/datum/preference_setting/numerical/age/choose_setting(mob/user)
var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null
if(new_age)
setting = clamp(round(new_age), min_value, max_value)
parent.ShowChoices(user)
//underwear
// This is an integer for historical reasons (you'll read that a lot in this file)
/datum/preference_setting/numerical/underwear
name = "Underwear"
sql_name = "underwear"
sql_table = "body"
enabled = TRUE
default_setting = UNDERWEAR_MALE_NONE
min_value = UNDERWEAR_MALE_NONE
max_value = UNDERWEAR_FEMALE_BLACK_HUSBANDBEATER
/datum/preference_setting/numerical/underwear/choose_setting(var/mob/user)
var/datum/preference_setting/gender = parent.get_pref_datum(/datum/preference_setting/enum/gender)
var/list/underwear_options
if(gender.setting == MALE)
underwear_options = underwear_m
else
underwear_options = underwear_f
var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in underwear_options
if(new_underwear)
setting = underwear_options.Find(new_underwear)
parent.ShowChoices(user)
/datum/preference_setting/numerical/backbag
name = "Backbag"
sql_name = "backbag"
sql_table = "body"
enabled = TRUE
default_setting = BACKPACK
min_value = NO_BACKPACK
max_value = MESSENGER_BAG
/datum/preference_setting/numerical/backbag/choose_setting(var/mob/user)
var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in backbaglist
if(new_backbag)
setting = backbaglist.Find(new_backbag)
parent.ShowChoices(user)
/datum/preference_setting/string/h_style
name = "Hair style"
sql_name = "hair_style_name"
sql_table = "body"
enabled = TRUE
default_setting = "Bald"
/datum/preference_setting/string/h_style/sanitize_setting(var/new_setting)
return sanitize_inlist(new_setting, hair_styles_list, default_setting)
/datum/preference_setting/string/h_style/randomise()
var/datum/preference_setting/gender = parent.get_pref_datum(/datum/preference_setting/enum/gender)
var/datum/preference_setting/species = parent.get_pref_datum(/datum/preference_setting/string/species)
setting = random_hair_style(gender.setting, species.setting)
var/list/colours = random_hair_color()
var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair)
r_hair.setting = colours[1]
var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair)
g_hair.setting = colours[2]
var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair)
b_hair.setting = colours[3]
/datum/preference_setting/string/h_style/proc/input_hair_color(var/mob/user)
var/datum/preference_setting/species_setting = parent.get_pref_datum(/datum/preference_setting/string/species)
var/species = species_setting.setting
var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair)
var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair)
var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair)
switch (species)
if("Human", "Unathi", "Diona", "Mushroom")
var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference", rgb(r_hair, g_hair, b_hair)) as color|null
if(new_hair)
r_hair.setting = hex2num(copytext(new_hair, 2, 4))
g_hair.setting = hex2num(copytext(new_hair, 4, 6))
b_hair.setting = hex2num(copytext(new_hair, 6, 8))
if("Vox")
var/new_hair_vox = input(user, "Choose your character's hair color:", "Character Preference") as null|anything in list("Green", "Azure", "Brown", "Emerald", "Gray", "Light Green", "Green-Brown")
if(new_hair_vox)
r_hair = haircolordesc(new_hair_vox) // Yeah, vox uses r_hair number as an index for discrete colour values.
// Why is that? Historical reasons
to_chat(user,"<span class='notice'>Your hair will now be [new_hair_vox] in color.</span>")
if("Insectoid")
var/carapace = input(user, "Choose your character's carapace colour, color values will be adjusted to between 35 and 80:", "Character Preference", rgb(r_hair, g_hair, b_hair)) as color|null
if(carapace)
var/datum/preference_setting/numerical/r_facial = parent.get_pref_datum(/datum/preference_setting/numerical/r_facial)
var/datum/preference_setting/numerical/g_facial = parent.get_pref_datum(/datum/preference_setting/numerical/g_facial)
var/datum/preference_setting/numerical/b_facial = parent.get_pref_datum(/datum/preference_setting/numerical/b_facial)
r_facial.setting = hex2num(copytext(carapace, 2, 4))
g_facial.setting = hex2num(copytext(carapace, 4, 6))
b_facial.setting = hex2num(copytext(carapace, 6, 8))
r_facial.setting = clamp(r_hair, 0, 80)
g_facial.setting = clamp(g_hair, 0, 50)
b_facial.setting = clamp(b_hair, 0, 35)
parent.ShowChoices(user)
/datum/preference_setting/string/h_style/choose_setting(var/mob/user)
var/species = parent.get_pref(/datum/preference_setting/string/species)
var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_sprite_accessories(hair_styles_list, null, species) //gender intentionally left null so speshul snowflakes can cross-hairdress
if(new_h_style)
setting = new_h_style
parent.ShowChoices(user)
/datum/preference_setting/string/h_style/process_link(var/task, var/mob/user, var/list/href_list)
. = ..()
if (.)
return
var/species = parent.get_pref(/datum/preference_setting/string/species)
switch (task)
if ("next_hair_style")
setting = next_list_item(setting, valid_sprite_accessories(hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress
parent.ShowChoices(user)
if("previous_hair_style")
setting = previous_list_item(setting, valid_sprite_accessories(hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress
parent.ShowChoices(user)
if("input_hair_color")
input_hair_color(user)
// Those are split in 3 integer rather than a colour string for historical reasons.
/datum/preference_setting/numerical/r_hair
name = "Red comp. RGB hair"
sql_name = "hair_red" // They're also inconsistently named between DB and DM
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/r_hair/randomise()
return
/datum/preference_setting/numerical/g_hair
name = "Green comp. RGB hair"
sql_name = "hair_green"
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/b_hair/randomise()
return
/datum/preference_setting/numerical/b_hair
name = "Blue comp. RGB hair"
sql_name = "hair_blue"
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/string/f_style
name = "Facial Hair style"
sql_name = "facial_style_name" //ew
sql_table = "body"
enabled = TRUE
default_setting = "Bald"
/datum/preference_setting/string/f_style/sanitize_setting(var/new_setting)
return sanitize_inlist(new_setting, facial_hair_styles_list, default_setting)
/datum/preference_setting/string/f_style/randomise()
var/datum/preference_setting/gender = parent.get_pref_datum(/datum/preference_setting/enum/gender)
var/datum/preference_setting/species = parent.get_pref_datum(/datum/preference_setting/string/species)
setting = random_facial_hair_style(gender.setting, species.setting)
var/datum/preference_setting/numerical/r_facial = parent.get_pref_datum(/datum/preference_setting/numerical/r_facial)
var/datum/preference_setting/numerical/g_facial = parent.get_pref_datum(/datum/preference_setting/numerical/g_facial)
var/datum/preference_setting/numerical/b_facial = parent.get_pref_datum(/datum/preference_setting/numerical/b_facial)
if (prob(75))
var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair)
var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair)
var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair)
r_facial.setting = r_hair.setting
g_facial.setting = g_hair.setting
b_facial.setting = b_hair.setting
else
var/list/colours = random_hair_color()
r_facial.setting = colours[1]
g_facial.setting = colours[2]
b_facial.setting = colours[3]
/datum/preference_setting/string/f_style/proc/input_facial_hair_color(var/mob/user)
message_admins("begin input facial hair colour")
var/species = parent.get_pref(/datum/preference_setting/string/species)
var/datum/preference_setting/numerical/r_facial = parent.get_pref_datum(/datum/preference_setting/numerical/r_facial)
var/datum/preference_setting/numerical/g_facial = parent.get_pref_datum(/datum/preference_setting/numerical/g_facial)
var/datum/preference_setting/numerical/b_facial = parent.get_pref_datum(/datum/preference_setting/numerical/b_facial)
switch(species)
if("Human", "Unathi")
var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", rgb(r_facial.setting, g_facial.setting, b_facial.setting)) as color|null
if(new_facial)
r_facial.setting = hex2num(copytext(new_facial, 2, 4))
g_facial.setting = hex2num(copytext(new_facial, 4, 6))
b_facial.setting = hex2num(copytext(new_facial, 6, 8))
else
to_chat(user, "<span class='warning'>No facial hair colour setting for speices [species] yet.</span>")
parent.ShowChoices(user)
/datum/preference_setting/string/f_style/choose_setting(var/mob/user)
var/species = parent.get_pref(/datum/preference_setting/string/species)
var/gender = parent.get_pref(/datum/preference_setting/enum/gender)
var/new_f_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in valid_sprite_accessories(facial_hair_styles_list, gender, species)
if(new_f_style)
setting = new_f_style
parent.ShowChoices(user)
/datum/preference_setting/string/f_style/process_link(var/task, var/mob/user, var/list/href_list)
. = ..()
if (.)
return
var/species = parent.get_pref(/datum/preference_setting/string/species)
switch (task)
if ("next_facehair_style")
setting = next_list_item(setting, valid_sprite_accessories(facial_hair_styles_list, null, species)) //gender intentionally left null so speshul snowflakes can cross-hairdress
parent.ShowChoices(user)
if("next_facehair_style")
setting = previous_list_item(setting, valid_sprite_accessories(facial_hair_styles_list, null, species))
parent.ShowChoices(user)
if("input_facial_hair_color")
input_facial_hair_color(user)
/datum/preference_setting/numerical/r_facial
name = "Red comp. RGB facial hair"
sql_name = "facial_red"
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/r_facial/randomise()
return
/datum/preference_setting/numerical/g_facial
name = "Green comp. RGB facial hair"
sql_name = "facial_green"
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/g_facial/randomise()
return
/datum/preference_setting/numerical/b_facial
name = "Blue comp. RGB hair"
sql_name = "facial_blue"
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/b_facial/randomise()
return
// Because it's all stored as r_eyes, g_eyes, b_eyes
// We have to pick one color which acts as the `input` catcher for all eye colour
// This should probably be reworked but that'll be in a later PR.
/datum/preference_setting/numerical/r_eyes
name = "Red comp. RGB eyes"
sql_name = "eyes_red" // WHY IS YOUR SQL NAME DIFFERENT FROM YOUR REAL HANDLE???
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/r_eyes/choose_setting(var/mob/user)
var/red_value = parent.get_pref(/datum/preference_setting/numerical/r_eyes)
var/green_value = parent.get_pref(/datum/preference_setting/numerical/g_eyes)
var/blue_value = parent.get_pref(/datum/preference_setting/numerical/b_eyes)
var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", rgb(red_value, green_value, blue_value)) as color|null
if(new_eyes)
var/datum/preference_setting/numerical/g_eyes = parent.get_pref_datum(/datum/preference_setting/numerical/g_eyes)
var/datum/preference_setting/numerical/b_eyes = parent.get_pref_datum(/datum/preference_setting/numerical/b_eyes)
setting = hex2num(copytext(new_eyes, 2, 4))
g_eyes.setting = hex2num(copytext(new_eyes, 4, 6))
b_eyes.setting = hex2num(copytext(new_eyes, 6, 8))
parent.ShowChoices(user)
/datum/preference_setting/numerical/g_eyes
name = "Green comp. RGB eyes"
sql_name = "eyes_green"
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/b_eyes
name = "Blue comp. RGB eyes"
sql_name = "eyes_blue"
sql_table = "body"
enabled = TRUE
default_setting = 0
min_value = 0
max_value = 255
/datum/preference_setting/numerical/s_tone
name = "Skin tone"
sql_name = "skin_tone"
sql_table = "body"
enabled = TRUE
default_setting = -100
min_value = -185
max_value = 35
/datum/preference_setting/numerical/s_tone/choose_setting(var/mob/user)
var/datum/preference_setting/species_datum = parent.get_pref_datum(/datum/preference_setting/string/species)
var/species = species_datum.setting
switch (species)
if("Human")
var/new_s_tone = input(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference") as num|null
if(new_s_tone)
setting = 35 - clamp(new_s_tone,1,220)
to_chat(user,"You're now [skintone2racedescription(setting, species)].")
if("Vox")//Can't reference species flags here, sorry.
var/skin_c = input(user, "Choose your Vox's skin color:\n(1 = Green, 2 = Brown, 3 = Gray, 4 = Light Green, 5 = Azure, 6 = Emerald)", "Character Preference") as num|null
if(skin_c)
setting = clamp(skin_c,1,6)
to_chat(user,"You will now be [skintone2racedescription(setting,species)] in color.")
if("Grey")
var/skin_c = input(user, "Choose your Grey's skin color:\n(1 = Gray, 2 = Light, 3 = Green, 4 = Blue)", "Character Preference") as num|null
if(skin_c)
setting = clamp(skin_c,1,4)
to_chat(user,"You will now be [skintone2racedescription(setting,species)] in color.")
if("Insectoid")
var/datum/preference_setting/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair)
var/datum/preference_setting/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair)
var/datum/preference_setting/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair)
var/carapace = input(user, "Choose your character's carapace colour, color values will be adjusted to between 35 and 80:", "Character Preference", rgb(r_hair.setting, g_hair.setting, b_hair.setting)) as color|null
if(carapace)
r_hair.setting = hex2num(copytext(carapace, 2, 4))
g_hair.setting = hex2num(copytext(carapace, 4, 6))
b_hair.setting = hex2num(copytext(carapace, 6, 8))
r_hair.setting = clamp(r_hair.setting, 0, 80)
g_hair.setting = clamp(g_hair.setting, 0, 50)
b_hair.setting = clamp(b_hair.setting, 0, 35)
else
to_chat(user,"Your species doesn't have different skin tones. Yet?")
parent.ShowChoices(user)
/datum/preference_setting/numerical/s_tone/randomise()
var/species = parent.get_pref(/datum/preference_setting/string/species)
setting = random_skin_tone(species)
/datum/preference_setting/string/species
name = "Species"
sql_name = "species"
sql_table = "players"
enabled = TRUE
default_setting = "Human"
/datum/preference_setting/string/species/choose_setting(var/mob/user)
var/prev_species = setting
var/gender = parent.get_pref(/datum/preference_setting/enum/gender)
if(check_rights(R_ADMIN,0)) // `usr` my beloved :] // usr still sort of works here because this in direct line from a TOPIC() call, but still
setting = input("Please select a species", "Character Generation", null) in whitelisted_species
else
setting = input("Please select a species", "Character Generation", null) in playable_species
if(prev_species != setting)
//grab one of the valid hair styles for the newly chosen species
var/list/valid_hairstyles = valid_sprite_accessories(hair_styles_list, gender, setting, HAIRSTYLE_CANTRIP)
var/datum/preference_setting/h_style = parent.get_pref_datum(/datum/preference_setting/string/h_style)
if(valid_hairstyles.len)
h_style.setting = pick(valid_hairstyles)
else
//this shouldn't happen
h_style.setting = hair_styles_list["Bald"]
//grab one of the valid facial hair styles for the newly chosen species
var/list/valid_facialhairstyles = valid_sprite_accessories(facial_hair_styles_list, gender, setting)
var/datum/preference_setting/f_style = parent.get_pref_datum(/datum/preference_setting/string/f_style)
if(valid_facialhairstyles.len)
f_style.setting = pick(valid_facialhairstyles)
else
//this shouldn't happen
f_style.setting = facial_hair_styles_list["Shaved"]
var/datum/preference_setting/numerical/r_hair = parent.get_pref_datum(/datum/preference_setting/numerical/r_hair)
var/datum/preference_setting/numerical/g_hair = parent.get_pref_datum(/datum/preference_setting/numerical/g_hair)
var/datum/preference_setting/numerical/b_hair = parent.get_pref_datum(/datum/preference_setting/numerical/b_hair)
//reset hair colour and skin colour
r_hair.setting = 0//hex2num(copytext(new_hair, 2, 4))
g_hair.setting = 0//hex2num(copytext(new_hair, 4, 6))
b_hair.setting = 0//hex2num(copytext(new_hair, 6, 8))
var/datum/preference_setting/numerical/s_tone = parent.get_pref_datum(/datum/preference_setting/numerical/s_tone)
s_tone.setting = s_tone.default_setting
// Job list check
var/datum/preference_setting/jobs = parent.get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs)
var/list/jobs_list = jobs.setting
for(var/datum/job/job in job_master.occupations)
if(job.species_blacklist.Find(setting)) //If new species is in a job's blacklist
jobs_list -= job.title
to_chat(user, "<span class='info'>Your new species ([setting]) is blacklisted from [job.title].</span>")
if(job.species_whitelist.len) //If the job has a species whitelist
if(!job.species_whitelist.Find(setting)) //And it doesn't include our new species
if(jobs_list.Remove(job.title))
to_chat(user, "<span class='info'>Your new species ([setting]) can't be [job.title]. Your preferences have been adjusted.</span>")
parent.ShowChoices(user)
// The sanity check here is handled at Topic() level with `input()`
/datum/preference_setting/string/language
name = "language"
sql_name = "language"
sql_table = "players"
enabled = TRUE
default_setting = "None"
/datum/preference_setting/string/language/choose_setting(var/mob/user)
var/list/new_languages = list("None")
for(var/L in all_languages)
var/datum/language/lang = all_languages[L]
if(lang.flags & CAN_BE_SECONDARY_LANGUAGE)
new_languages += lang.name
setting = input("Please select a secondary language", "Character Generation", null) in new_languages
parent.ShowChoices(user)
/datum/preference_setting/string/flavor_text
name = "Flavor Text"
sql_name = "flavor_text"
sql_table = "players"
enabled = TRUE
default_setting = ""
max_length = 3000
/datum/preference_setting/string/flavor_text/choose_setting(var/mob/user)
setting = input(user,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(setting)) as message
parent.ShowChoices(user)
/datum/preference_setting/string/med_record
name = "Medical Records"
sql_name = "med_record"
sql_table = "players"
enabled = TRUE
default_setting = ""
max_length = 3000
/datum/preference_setting/string/sec_record
name = "Security Records"
sql_name = "sec_record"
sql_table = "players"
enabled = TRUE
default_setting = ""
max_length = 3000
/datum/preference_setting/string/gen_record
name = "General Records"
sql_name = "gen_record"
sql_table = "players"
enabled = TRUE
default_setting = ""
max_length = 3000
// Why is it named like this, I have no idea.
/datum/preference_setting/string/metadata
name = "OOC Notes"
sql_name = "ooc_notes"
sql_table = "players"
enabled = TRUE
default_setting = ""
max_length = MAX_MESSAGE_LEN
/datum/preference_setting/string/metadata/choose_setting(var/mob/user)
var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , setting) as message|null
if(new_metadata)
setting = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN))
/datum/preference_setting/list_values/player_alt_titles
name = "Player alt titles"
sql_name = "player_alt_titles"
sql_table = "players"
enabled = TRUE
default_setting = list()
// For some reason this is saved as a raw string like "Title":"Alt Title";"Title2":"Alt-Title2"
// So we just need to do some splittext
/datum/preference_setting/list_values/player_alt_titles/load_sql(var/sql_value)
var/list/temporary_list = list()
var/list/returned_list = list()
temporary_list.Add(splittext(sql_value, ";")) // we're getting the first part of the string for each job.
for(var/item in temporary_list) // iterating through the list
if(!findtext(item, ":"))
continue
var/delim_location = findtext(item, ":") // getting the second part of the string that will be handled for titles
var/job = copytext(item, 1, delim_location) // getting where the job is, it's in the first slot so we want to get that position.
var/title = copytext(item, delim_location + 1, 0) // getting where the job title is, it's in the second slot so we want to get that position.
returned_list[job] = title // we assign the alt_titles here to specific job titles and hope everything works.
return returned_list
/datum/preference_setting/list_values/player_alt_titles/save_sql()
var/return_string
// From Nexis, circa 2017
// The FUCK is this shit
for(var/a in setting)
return_string += "[a]:[setting[a]];"
return return_string
// The organ data is a bit strange: all these are saved as individual setting flags
// But the whole of it is read as a single list.
// It's annoying to deal with but that's just technical debt
/datum/preference_setting/enum/organ_data
name = "Organ data"
sql_name = ""
sql_table = "limbs"
enabled = FALSE
default_setting = null
allowed_values = list(null, "cyborg", "amputated")
/datum/preference_setting/enum/organ_data/limb_left_arm
name = "Left Arm"
sql_name = LIMB_LEFT_ARM
enabled = TRUE
/datum/preference_setting/enum/organ_data/limb_right_arm
name = "Right Arm"
sql_name = LIMB_RIGHT_ARM
enabled = TRUE
/datum/preference_setting/enum/organ_data/limb_left_leg
name = "Left Leg"
sql_name = LIMB_LEFT_LEG
enabled = TRUE
/datum/preference_setting/enum/organ_data/limb_right_leg
name = "Right Leg"
sql_name = LIMB_RIGHT_LEG
enabled = TRUE
/datum/preference_setting/enum/organ_data/limb_left_hand
name = "Left Hand"
sql_name = LIMB_LEFT_HAND
enabled = TRUE
/datum/preference_setting/enum/organ_data/limb_right_hand
name = "Right Hand"
sql_name = LIMB_RIGHT_HAND
enabled = TRUE
/datum/preference_setting/enum/organ_data/limb_left_foot
name = "Left Foot"
sql_name = LIMB_LEFT_FOOT
enabled = TRUE
/datum/preference_setting/enum/organ_data/limb_right_foot
name = "Right Foot"
sql_name = LIMB_RIGHT_FOOT
enabled = TRUE
/datum/preference_setting/enum/organ_data/organ
enabled = FALSE
allowed_values = list(null, "assisted", "mechanical")
/datum/preference_setting/enum/organ_data/organ/heart
name = "Heart"
sql_name = LIMB_HEART
enabled = TRUE
/datum/preference_setting/enum/organ_data/organ/eyes
name = "Eyes"
sql_name = LIMB_EYES
enabled = TRUE
/datum/preference_setting/enum/organ_data/organ/lung
name = "Lung"
sql_name = LIMB_LUNG
enabled = TRUE
/datum/preference_setting/enum/organ_data/organ/liver
name = "Liver"
sql_name = LIMB_LIVER
enabled = TRUE
/datum/preference_setting/enum/organ_data/organ/kidneys
name = "Kidneys"
sql_name = LIMB_KIDNEYS
enabled = TRUE
//Keeps track of preferrence for not getting any wanted jobs
/datum/preference_setting/enum/alternate_option
name = "Alternate Option if jobs filled"
sql_name = "alternate_option"
sql_table = "jobs"
enabled = TRUE
default_setting = RETURN_TO_LOBBY
allowed_values = list(RETURN_TO_LOBBY, GET_RANDOM_JOB, BE_ASSISTANT, GET_EMPTY_JOB)
// Stored as JSON
/datum/preference_setting/assoc_list_setting/jobs
name = "Jobs"
sql_name = "jobs"
sql_table = "jobs"
enabled = TRUE
default_setting = list()
allowed_values_for_list_items = list(JOB_PREF_NEVER, JOB_PREF_LOW, JOB_PREF_MED, JOB_PREF_HIGH)
/datum/preference_setting/assoc_list_setting/jobs/save_sql()
return json_encode(setting)
/datum/preference_setting/assoc_list_setting/jobs/load_sql(var/sql_value)
if (sql_value)
return json_decode(sql_value)
return list()
/datum/preference_setting/assoc_list_setting/jobs/process_link(var/task, var/mob/user, var/list/href_list)
var/datum/preference_setting/alternate_option = parent.get_pref(/datum/preference_setting/enum/alternate_option)
switch(task)
if("close")
user << browse(null, "window=mob_occupation")
parent.ShowChoices(user)
if("reset")
parent.ResetJobs()
if("random") // This just changes the alternate option.
if(alternate_option.setting == GET_RANDOM_JOB || alternate_option.setting == BE_ASSISTANT || alternate_option.setting == RETURN_TO_LOBBY)
alternate_option.setting += 1
else if(alternate_option == GET_EMPTY_JOB)
alternate_option.setting = 0
else
return 0
parent.SetJobsChoice(user)
if ("alt_title")
var/datum/job/job = locate(href_list["job"])
if (job)
var/choices = list(job.title) + job.alt_titles
var/choice = input("Pick a title for [job.title].", "Character Generation", parent.GetPlayerAltTitle(job)) as anything in choices | null
if(choice)
parent.SetPlayerAltTitle(job, choice)
parent.SetJobsChoice(user)
if("input")
parent.SetJob(user, href_list["text"], href_list["level"] == "1")
else // Menu
parent.SetJobsChoice(user)
return 1
/datum/preference_setting/enum/string/nanotrasen_relation
name = "Nanotrasen Relationship"
sql_name = "nanotrasen_relation"
sql_table = "players"
enabled = TRUE
default_setting = "Neutral"
allowed_values = list("Loyal", "Supportive", "Neutral", "Skeptical")
/datum/preference_setting/enum/string/nanotrasen_relation/choose_setting(var/mob/user)
var/new_relation = input(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference") as null|anything in list("Loyal", "Supportive", "Neutral", "Skeptical", )
if(new_relation)
setting = new_relation
parent.ShowChoices(user)
/datum/preference_setting/enum/bank_security
name = "Bank security"
sql_name = "bank_security"
sql_table = "players"
enabled = TRUE
default_setting = SECURITY_AUTO_LOGIN
allowed_values = list(SECURITY_AUTO_LOGIN, SECURITY_MANUAL_LOGIN, SECURITY_CARD_AND_MANUAL_LOGIN)
/datum/preference_setting/enum/bank_security/choose_setting(var/mob/user)
var/new_bank_security = input(user, BANK_SECURITY_EXPLANATION, "Character Preference") as null|anything in bank_security_text2num_associative
if(!isnull(new_bank_security))
setting = bank_security_text2num_associative[new_bank_security]
parent.ShowChoices(user)
/datum/preference_setting/numerical/wage_ratio
name = "Wage ratio"
sql_name = "wage_ratio"
sql_table = "players"
enabled = TRUE
default_setting = 50
min_value = 0
max_value = 100
/datum/preference_setting/numerical/wage_ratio/randomise()
return
/datum/preference_setting/numerical/wage_ratio/choose_setting(var/mob/user)
var/new_wage_ratio = input(user, "Input what % of wages end up in virtual wallets, from 0-100", "Character Preference",setting) as num
if(!isnull(new_wage_ratio))
new_wage_ratio = clamp(new_wage_ratio,0,100)
setting = new_wage_ratio
parent.ShowChoices(user)
/datum/preference_setting/binary_flag/disabilities
name = "Disabilities"
sql_name = "disabilities"
sql_table = "players"
enabled = TRUE
/datum/preference_setting/binary_flag/disabilities/process_link(var/task, var/mob/user, var/list/href_list)
switch(task)
if("close")
user << browse(null, "window=disabil")
parent.ShowChoices(user)
if("reset")
setting=0
parent.SetDisabilities(user)
if("input")
var/dflag=text2num(href_list["disability"])
var/species = parent.get_pref(/datum/preference_setting/string/species)
if(dflag >= 0)
if(!(dflag==DISABILITY_FLAG_FAT && species!="Human"))
setting ^= text2num(href_list["disability"]) //MAGIC
parent.SetDisabilities(user)
else
parent.SetDisabilities(user)

View File

@@ -0,0 +1,582 @@
// -- ACTUAL PREFS --
// non-prefs stuff
/datum/preference_setting/numerical/warns
name = "Warns"
sql_name = "warns"
sql_table = "client"
enabled = TRUE
default_setting = 0
/datum/preference_setting/numerical/warnbans
name = "Warn bans"
sql_name = "warnbans"
sql_table = "client"
enabled = TRUE
default_setting = 0
/datum/preference_setting/toggle/show_warning_next_time
name = "Show warning next time"
sql_name = "show_warning_next_time"
sql_table = "client"
enabled = TRUE
default_setting = 0
/datum/preference_setting/string/last_warned_message
name = "Last warned message"
sql_name = "last_warned_message"
sql_table = "client"
enabled = TRUE
default_setting = "Default warning message"
/datum/preference_setting/string/warning_admin
name = "Warning admin"
sql_name = "warning_admin"
sql_table = "client"
enabled = TRUE
default_setting = "Admin"
/datum/preference_setting/numerical/default_slot
name = "Default slot"
sql_name = "default_slot"
sql_table = "client"
enabled = TRUE
default_setting = 1
min_value = 1
max_value = MAX_SAVE_SLOTS
// game-preferences
//Saved changlog filesize to detect if there was a change
/datum/preference_setting/string/changelog
name = "Last changelog"
sql_name = "lastchangelog"
sql_table = "client"
enabled = TRUE
default_setting = ""
/datum/preference_setting/string/ooc_color
name = "OOC Color"
sql_name = "ooc_color"
sql_table = "client"
enabled = TRUE
default_setting = "#b82e00"
// For all that matters, this ISN'T in the pref UI for some reason?
/datum/preference_setting/string/ooc_color/choose_setting(var/mob/user)
var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null
if(new_ooccolor)
setting = new_ooccolor
/datum/preference_setting/string/UI_style
name = "UI Style"
sql_name = "UI_style"
sql_table = "client"
enabled = TRUE
default_setting = "Midnight"
// This is just cycling
/datum/preference_setting/string/UI_style/choose_setting(mob/user)
. = ..()
switch(setting)
if("Midnight")
setting = "Orange"
if("Orange")
setting = "old"
if("old")
setting = "White"
else
setting = "Midnight"
/datum/preference_setting/binary_flag/toggles
name = "Toggles"
sql_name = "toggles"
sql_table = "client"
enabled = TRUE
default_setting = TOGGLES_DEFAULT
// Annoying overwrite but I can't think of a more elegant way to do it.
/datum/preference_setting/binary_flag/toggles/process_link(var/task, var/mob/user, var/list/href_list)
if (task == "input")
var/toggle = text2num(href_list["toggle"])
// I can't think of any other way to check this pre-toggling. Probably overloaded but I want to replicate it, just ion case.
switch (toggle)
// Admin toggle, do not pass if we're not admin
if (SOUND_ADMINHELP, CHAT_PRAYER, CHAT_GHOSTRADIO, CHAT_ATTACKLOGS, CHAT_DEBUGLOGS, AUTO_DEADMIN)
if (!user.client.holder)
return
setting ^= toggle
// Annoying but have to do it.
switch (toggle)
if (SOUND_MIDI)
user << sound(null, repeat = 0, wait = 0, volume = 0, channel = CHANNEL_ADMINMUSIC)
if (SOUND_AMBIENCE)
if(config.no_ambience)
to_chat(user, "DEBUG: Ambience is globally disabled via server config.")
user << sound(null, repeat = 0, wait = 0, volume = 0, channel = CHANNEL_AMBIENCE)
if (SOUND_LOBBY)
if(config.no_lobby_music)
to_chat(user, "DEBUG: Lobby music is globally disabled via server config.")
if(istype(user,/mob/new_player))
user << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBY)
/datum/preference_setting/string/UI_style_color
name = "UI Style Color"
sql_name = "UI_style_color"
sql_table = "client"
enabled = TRUE
default_setting = "#ffffff"
/datum/preference_setting/string/UI_style_color/choose_setting(var/mob/user)
var/UI_style_color_new = input(user, "Choose your UI colour, dark colours are not recommended!") as color|null
if(!UI_style_color_new)
return
setting = UI_style_color_new
/datum/preference_setting/numerical/UI_style_alpha
name = "UI Style Alpha"
sql_name = "UI_style_alpha"
sql_table = "client"
enabled = TRUE
default_setting = 255
min_value = 50
max_value = 255
/datum/preference_setting/numerical/UI_style_alpha/choose_setting(var/mob/user)
var/UI_style_alpha_new = input(user, "Select a new alpha(transparency) parameter for UI, between 50 and 255") as num
if(!UI_style_alpha_new || !(UI_style_alpha_new <= max_value && UI_style_alpha_new >= min_value))
return
setting = UI_style_alpha_new
/datum/preference_setting/toggle/space_parallax
name = "Space parallax"
sql_name = "space_parallax"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/toggle/space_parallax/choose_setting(var/mob/user)
. = ..()
if(user && user.hud_used)
user.hud_used.update_parallax_existence()
/datum/preference_setting/toggle/space_dust
name = "Space dust"
sql_name = "space_dust"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/toggle/space_dust/choose_setting(var/mob/user)
. = ..()
if(user && user.hud_used)
user.hud_used.update_parallax_existence()
/datum/preference_setting/numerical/parallax_speed
name = "Parallax speed"
sql_name = "parallax_speed"
sql_table = "client"
enabled = TRUE
default_setting = 2
min_value = 0
max_value = 5
/datum/preference_setting/numerical/parallax_speed/choose_setting(var/mob/user)
var/p_speed = input(user, "Enter a number between 0 and 5 included (default=2)","Parallax Speed Preferences",setting)
setting = clamp(p_speed, min_value, max_value)
/datum/preference_setting/enum/special_popup
name = "Special popup"
sql_name = "special" // HISTORICAL REASONS :tm:
sql_table = "client"
enabled = TRUE
default_setting = SPECIAL_POPUP_USE_BOTH
allowed_values = list(SPECIAL_POPUP_DISABLED, SPECIAL_POPUP_EXCLUSIVE, SPECIAL_POPUP_USE_BOTH)
/datum/preference_setting/enum/special_popup/choose_setting(mob/user)
var/choice = input(user, "Set your special tab preferences:", "Settings") as null|anything in special_popup_text2num
if(!isnull(choice))
setting = special_popup_text2num[choice]
/datum/preference_setting/toggle/tooltips
name = "Tooltips"
sql_name = "tooltips"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/toggle/stumble
name = "Stumble"
sql_name = "stumble"
sql_table = "client"
enabled = TRUE
default_setting = FALSE
/datum/preference_setting/toggle/hear_voicesound
name = "Hear voicesound"
sql_name = "hear_voicesound"
sql_table = "client"
enabled = TRUE
default_setting = FALSE
/datum/preference_setting/numerical/volume
name = "Music volume"
sql_name = "volume"
sql_table = "client"
enabled = TRUE
default_setting = 100
min_value = 0
max_value = 100
/datum/preference_setting/numerical/volume/choose_setting(var/mob/user)
user.client.set_new_volume()
/datum/preference_setting/toggle/usewmp
name = "Use WMP"
sql_name = "usewmp"
sql_table = "client"
enabled = TRUE
default_setting = FALSE
/datum/preference_setting/toggle/usewmp/choose_setting(var/mob/user)
. = ..()
if(!user.client.media)
return
user.client.media.stop_music()
user.client.media.playerstyle = (setting ? PLAYER_OLD_HTML : PLAYER_HTML)
var/datum/preference_setting/toggles = parent.get_pref_datum(/datum/preference_setting/binary_flag/toggles)
if(toggles.setting & SOUND_STREAMING)
user.client.media.open()
user.client.media.update_music()
/datum/preference_setting/toggle/randomslot
name = "Use random character slot"
sql_name = "randomslot"
sql_table = "client"
enabled = TRUE
default_setting = FALSE
/datum/preference_setting/toggle/usenanoui
name = "Use nanoUI"
sql_name = "usenanoui"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/toggle/progress_bars
name = "Progress bars"
sql_name = "progress_bars"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
// TRAINS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/datum/preference_setting/enum/attack_animations
name = "Attack animations"
sql_name = "attack_animation"
sql_table = "client"
enabled = TRUE
default_setting = ITEM_ANIMATION
allowed_values = list(ITEM_ANIMATION, NO_ANIMATION, PERSON_ANIMATION)
/datum/preference_setting/enum/attack_animations/choose_setting(var/mob/user)
if(setting == NO_ANIMATION)
item_animation_viewers |= parent.client
setting = ITEM_ANIMATION
else if(setting == ITEM_ANIMATION)
setting = PERSON_ANIMATION
person_animation_viewers |= parent.client
item_animation_viewers -= parent.client
else if(setting == PERSON_ANIMATION)
setting = NO_ANIMATION
item_animation_viewers -= parent.client
/datum/preference_setting/toggle/pulltoggle
name = "Pull toggle"
sql_name = "pulltoggle"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/toggle/hear_instruments
name = "Hear instruments"
sql_name = "hear_instruments"
sql_table = "client"
enabled = TRUE
default_setting = FALSE
/datum/preference_setting/numerical/ambience_volume
name = "Ambience volume"
sql_name = "ambience_volume"
sql_table = "client"
enabled = TRUE
default_setting = 100
min_value = 0
max_value = 100
/datum/preference_setting/numerical/ambience_volume/choose_setting(var/mob/user)
var/new_volume = input(user, "Enter the new volume you wish to use. (0-100)","Ambience Volume Preferences", setting)
setting = clamp(new_volume, min_value, max_value)
/datum/preference_setting/numerical/credits_volume
name = "Ambience volume"
sql_name = "credits_volume"
sql_table = "client"
enabled = TRUE
default_setting = 75
min_value = 0
max_value = 100
/datum/preference_setting/numerical/credits_volume/choose_setting(var/mob/user)
var/credits_volume = input(user, "Enter the new volume you wish to use. (0-100, default is 75)","Credits/Jingle Volume", setting)
setting = clamp(credits_volume, min_value, max_value)
/datum/preference_setting/enum/credits
name = "Credits"
sql_name = "credits"
sql_table = "client"
enabled = TRUE
default_setting = CREDITS_ALWAYS
allowed_values = list(CREDITS_NEVER, CREDITS_ALWAYS, CREDITS_NO_RERUNS)
saved_as_string = FALSE
/datum/preference_setting/enum/credits/choose_setting(var/mob/user)
switch(setting)
if(CREDITS_NEVER)
setting = CREDITS_ALWAYS
if(CREDITS_ALWAYS)
setting = CREDITS_NO_RERUNS
if(CREDITS_NO_RERUNS)
setting = CREDITS_NEVER
/datum/preference_setting/enum/jingle
name = "Jingle"
sql_name = "jingle"
sql_table = "client"
enabled = TRUE
default_setting = JINGLE_CLASSIC
allowed_values = list(JINGLE_NEVER, JINGLE_CLASSIC, JINGLE_ALL)
saved_as_string = FALSE
/datum/preference_setting/enum/jingle/choose_setting(var/mob/user)
switch(setting)
if(JINGLE_NEVER)
setting = JINGLE_CLASSIC
if(JINGLE_CLASSIC)
setting = JINGLE_ALL
if(JINGLE_ALL)
setting = JINGLE_NEVER
/datum/preference_setting/numerical/headset_sound
name = "Headset sound"
sql_name = "headset_sound"
sql_table = "client"
enabled = TRUE
default_setting = HEADSET_SOUND_TRANSMIT
min_value = HEADSET_SOUND_DISABLED
max_value = HEADSET_SOUND_ALL
saved_as_string = FALSE
/datum/preference_setting/numerical/headset_sound/choose_setting(var/mob/user)
var/choice = input(user, "Set your radio headset sound preferences:", "Settings") as null|anything in headset_sound_text2num
if(!isnull(choice))
setting = headset_sound_text2num[choice]
/datum/preference_setting/toggle/window_flashing
name = "Window flashing"
sql_name = "window_flashing"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/toggle/antag_objectives
name = "Antag objectives"
sql_name = "antag_objectives"
sql_table = "client"
enabled = TRUE
default_setting = TRUE // This also has "historical reasons" but not code-related
/datum/preference_setting/toggle/typing_indicator
name = "Typing indicator"
sql_name = "typing_indicator"
sql_table = "client"
enabled = TRUE
default_setting = TRUE // This also has "historical reasons" but not code-related
// -- Runechat things
/datum/preference_setting/toggle/mob_chat_on_map
name = "Runechat on map"
sql_name = "mob_chat_on_map"
sql_table = "client"
enabled = TRUE
default_setting = TRUE // This also has "historical reasons" but not code-related
/datum/preference_setting/numerical/max_chat_length
name = "Max runechat message length"
sql_name = "max_chat_length"
sql_table = "client"
enabled = TRUE
default_setting = CHAT_MESSAGE_MAX_LENGTH
min_value = 0
max_value = CHAT_MESSAGE_MAX_LENGTH
/datum/preference_setting/numerical/max_chat_length/choose_setting(mob/user)
var/max_chat_length = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [default_setting]))", "Character Preference", setting) as null|num
setting = clamp(max_chat_length, min_value, max_value)
/datum/preference_setting/toggle/obj_chat_on_map
name = "Object runechat"
sql_name = "obj_chat_on_map"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/toggle/no_goonchat_for_obj
name = "No goonchat for object"
sql_name = "no_goonchat_for_obj"
sql_table = "client"
enabled = TRUE
default_setting = FALSE
/datum/preference_setting/toggle/tgui_fancy
name = "Fancy TGUI"
sql_name = "tgui_fancy"
sql_table = "client"
enabled = TRUE
default_setting = TRUE
/datum/preference_setting/numerical/fps
name = "FPS"
sql_name = "fps"
sql_table = "client"
enabled = TRUE
default_setting = -1
min_value = -1
max_value = 1000
saved_as_string = FALSE
/datum/preference_setting/numerical/fps/choose_setting(var/mob/user)
var/desired_fps = input(user, "Choose your desired frames per second.\n\
WARNING: BYOND versions earlier than 513.1523 might not work properly with values other than 0.\n\
Set this to -1 to use the recommended value.\n\
Set this to 0 to use the server's FPS (currently [world.fps])\n\
Values up to 1000 are allowed.", "FPS", setting) as null|num
if(isnull(desired_fps))
return
if(desired_fps < 0)
desired_fps = -1
desired_fps = sanitize_integer(desired_fps, -1, 1000, setting)
setting = desired_fps
parent.client.fps = (setting < 0) ? RECOMMENDED_CLIENT_FPS : setting
// THESE ARE UNIMPLEMENTED FROM tgui MIGRATIONS! TOFIX
/datum/preference_setting/toggle/tgui_input
name = "tgui_input"
sql_name = "tgui_input"
sql_table = "client"
enabled = FALSE
default_setting = FALSE
/datum/preference_setting/toggle/tgui_input_swapped
name = "tgui_input_swapped"
sql_name = "tgui_input_swapped"
sql_table = "client"
enabled = FALSE
default_setting = FALSE
/datum/preference_setting/toggle/tgui_input_large
name = "tgui_input_large"
sql_name = "tgui_input_large"
sql_table = "client"
enabled = FALSE
default_setting = FALSE
/datum/preference_setting/toggle/tgui_lock
name = "tgui_lock"
sql_name = "tgui_lock"
sql_table = "client"
enabled = FALSE
default_setting = FALSE
/datum/preference_setting/toggle/tgui_scale
name = "tgui_scale"
sql_name = "tgui_scale"
sql_table = "client"
enabled = FALSE
default_setting = FALSE
/datum/preference_setting/toggle/layout_prefs_used
name = "tgui_scale"
sql_name = "tgui_scale"
sql_table = "client"
enabled = FALSE
default_setting = FALSE

View File

@@ -0,0 +1,200 @@
/*
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
---------------------------------- SQLite DB workflow ----------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
"ShiftyRail", 2025
//////////////////////////////////////////////
// //
// New player Login //
// //
//////////////////////////////////////////////
1. client/New()
Called automatically by BYOND
2. /datum/preferences/New(client/C)
3. init_datums()
Creates a list of /datum/preference_setting with the default values
4. init_subsections()
Creates the subsection helpers, who handle menu generation, etc, etc.
5. try_load_preferences(Client.ckey, client.mob)
a) Executes a DB query to check if the client is registered into the database
"SELECT ckey FROM client WHERE ckey = [client.ckey]",
b)
1) If that fails:
save_preferences_sqlite(ckey, user)
Which will create and save a new entry
2) If that works
load_preferences_sqlite(ckey)
Which load the `client` preferences, such as FPS, toggles, etc.
6) If preferences have been correctly loaded
try_load_save_sqlite(Client.ckey, Client, default_slot) -- Where default_slot has been read from `client` preferences
a) Existing character slot check :
"SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?"
b)
1) If that fails:
create_character_sqlite(Client.ckey, Client, default_slot)
Which will create a new (random body) character
2) If that works:
load_character_sqlite(Client.ckey, Client, default_slot)
Which just load the character at that slot.
3) If both queries fail:
fallback_random_character(theckey, theclient)
Which generates an ad-hoc random body for that round.
If we are on this path, it is assumed that SQLite has failed.
Saving the fallback random character may overwrite an existing.
7) Play loading sound and end of flow.
//////////////////////////////////////////////
// //
// Loading a new character slot //
// //
//////////////////////////////////////////////
1) Click on the href with '?_src_=prefs;action=changeslot'
2) Since there is no `src`, this goes to Client/Topic()
3) Client/Topic() directs it to /datum/preferences/process_link()
4) Directly inside the decision tree, it calls:
try_load_slot(user.ckey, user, num) -- where `num` is the slot number passed by href
a) Exisiting character slot check:
"SELECT player_ckey FROM players WHERE player_ckey = [ckey] AND player_slot = [num]"
b)
1) If that fails:
create_character_sqlite(Client.ckey, client, num)
Which will create a new (random body) character
2) If that works:
load_character_sqlite(Client.ckey, client, num)
Which just load the character at that slot.
5) Change the default slot preference, and calls:
save_preferences_sqlite(user, ckey)
So that it is remembered for next time
6) Update the UI:
ShowChoices(user)
//////////////////////////////////////////////
// //
// Updating a preference //
// //
//////////////////////////////////////////////
1) Click on the href with '?_src_=prefs;preference=[something];task=[something]'
OR '?_src_=prefs;action=[something]'
2) Since there is no `src`, this goes to Client/Topic()
3) Client/Topic() directs it to /datum/preferences/process_link(user, list/href_list)
4) Existing preference datums are scanned for the correct `sql_name` of /datum/preference
a) If there's a match:
/datum/preference_setting/process_link(user, task, list/href_list)
-- Depending on task
- "random" = /datum/preference_setting/randomise(user)
- "input" = /datum/preference_setting/choose_setting(user)
- Special tasks : randomise hair colour, etc.
ShowChoices(user) is either called on process_link (most of the `character` prefs)
Or manually after (`client` prefs, which are mostly toggles)
b) If there isn't :
- Hardcoded tasks remaining:
* updating records
* changing role preferences (for antags)
* random body
c) Other thing : changing character preview background
5) `action` called by hrefs:
- Save slot
- Load slot
- changing preference tab
6) Still not handled the link? Coding error, throw a runtime.
//////////////////////////////////////////////
// //
// Roundstart/latejoin //
// //
//////////////////////////////////////////////
1) Roundstart:
/datum/controller/gameticker/proc/setup()
Latejoin:
/mob/new_player/AttemptLateSpawn()
They both call
2) H = mob/new_player/create_human()
This creates a client-less human mob in the famous cuck cube
3) Player is not appearance banned:
a) /datum/preferences/copy_to(H)
On the human `H`, the /datum/my_appearance is loaded with the values in `/datum/preferences`
b) If the player has `Be Random Body`, /datum/my_apperance/randomise() is called
Which randomises their appearance but respecting the `GENDER` variable in prefs.
4) Player is appearance banned:
a) /datum/preferences/randomize_appearance_for(H)
This scrambles their prefs. The code has an explicit provision for randomising gender in this case.
//////////////////////////////////////////////
// //
// Other notes //
// //
//////////////////////////////////////////////
- The DB queries for `body`, `players`, and `client` are all generated at runtime.
- They are setup to take into account changes in the code structure so you don't need to modify the procs "generating" them.
- The DB queries for `client_roles` and `jobs` are (still) hardcoded and not generated at runtime.
- Further improvements will be neded to converted those to runtime-generated code.
- However, it is less critical as those tables have much less collumns.
*/

View File

@@ -0,0 +1,602 @@
#define CHARACTER_SETUP 0
#define UI_SETUP 1
#define GENERAL_SETUP 2
#define SPECIAL_ROLES_SETUP 3
var/list/preferences_datums = list()
var/global/list/special_roles = list(
ROLE_ALIEN = 1,
BLOBOVERMIND = 1,
ROLE_BORER = 1,
CHANGELING = 1,
CULTIST = 1,
ROLE_PLANT = 1,
MALF = 1,
NUKE_OP = 1,
ROLE_PAI = 1,
ROLE_POSIBRAIN = 1,
REV = 1,
TRAITOR = 1,
CHALLENGER = 1,
VAMPIRE = 1,
VOXRAIDER = 1,
WIZARD = 1,
ROLE_STRIKE = 1,
GRINCH = 1,
NINJA = 1,
TIMEAGENT = 1,
PULSEDEMON = 1,
ROLE_MINOR = 1,
ROLE_PRISONER = 1,
ROLE_GRUE = 1,
DIVERGENTCLONE = 1,
)
/var/list/antag_roles = list(
ROLE_ALIEN = 1,
BLOBOVERMIND = 1,
CHANGELING = 1,
CULTIST = 1,
MALF = 1,
NUKE_OP = 1,
REV = 1,
TRAITOR = 1,
CHALLENGER = 1,
VAMPIRE = 1,
VOXRAIDER = 1,
WIZARD = 1,
ROLE_STRIKE = 1,
GRINCH = 1,
NINJA = 1,
TIMEAGENT = 1,
PULSEDEMON = 1,
ROLE_MINOR = 1,
ROLE_PRISONER = 1,
ROLE_GRUE = 1,
DIVERGENTCLONE = 1,
)
var/list/nonantag_roles = list(
ROLE_BORER = 1,
ROLE_PLANT = 1,
ROLE_PAI = 1,
ROLE_POSIBRAIN = 1,
)
var/list/role_wiki=list(
ROLE_ALIEN = "Xenomorph",
BLOBOVERMIND = "Blob",
ROLE_BORER = "Cortical_Borer",
CHANGELING = "Changeling",
CULTIST = "Cult",
ROLE_PLANT = "Dionaea",
MALF = "Guide_to_Malfunction",
NUKE_OP = "Nuclear_Agent",
ROLE_PAI = "Personal_AI",
ROLE_POSIBRAIN = "Guide_to_Silicon_Laws",
REV = "Revolution",
TRAITOR = "Traitor",
CHALLENGER = "Challengers",
VAMPIRE = "Vampire",
VOXRAIDER = "Vox_Raider",
WIZARD = "Wizard",
GRINCH = "Grinch",
NINJA = "Space_Ninja",
TIMEAGENT = "Time_Agent",
PULSEDEMON = "Pulse_Demon",
ROLE_MINOR = "Minor_Roles",
ROLE_PRISONER = "Minor_Roles",
ROLE_GRUE = "Grue",
DIVERGENTCLONE = "Divergent_Clone",
)
var/list/special_popup_text2num = list(
"Only use chat" = SPECIAL_POPUP_DISABLED,
"Only use special" = SPECIAL_POPUP_EXCLUSIVE,
"Use both chat and special" = SPECIAL_POPUP_USE_BOTH,
)
var/list/headset_sound_text2num = list(
"Disabled" = HEADSET_SOUND_DISABLED,
"Transmit Only" = HEADSET_SOUND_TRANSMIT,
"All" = HEADSET_SOUND_ALL,
)
var/const/MAX_SAVE_SLOTS = 16
#define POLLED_LIMIT 100
/datum/preferences
var/list/subsections
//doohickeys for savefiles
var/database/db = ("players2.sqlite")
var/path
// Which character slot
var/slot = 1
var/list/slot_names = new
var/slot_name = ""
// 0 = character settings, 1 = game preferences
var/current_tab = 0
// Last time the guy saved their prefs
var/lastPolled = 0
var/savefile_version = 0
// Alist = associative lists. This is a new 516 thing. Woo!
var/alist/preference_settings_client = alist()
var/alist/preference_settings_character = alist()
// Don't like hardcoding this but I can't find a way..
var/list/organ_data = list()
// REALLY don't like hardcoding this. Will be for another time
var/list/roles = list()
//non-preference stuff
// Shouldn't these by on client?
var/last_ip
var/last_id
var/muted
//Mob preview
var/icon/preview_icon = null
var/icon/preview_icon_front = null
var/icon/preview_icon_side = null
var/preview_background = null
var/list/background_options = list("Black", "White", "Tile")
var/client/client
var/saveloaded = 0
/datum/preferences/New(client/C)
client=C
if(istype(C))
init_datums()
init_subsections()
var/theckey = C.ckey
var/thekey = C.key
if(!IsGuestKey(thekey))
var/load_pref = try_load_preferences(theckey, C.mob)
var/default_slot = get_pref(/datum/preference_setting/numerical/default_slot)
if(load_pref)
to_chat(C, "Successfully loaded preferences.")
while(!SS_READY(SShumans))
sleep(1)
try_load_save_sqlite(theckey, C, default_slot)
return
CRASH("Could not load preferences!")
while(!SS_READY(SShumans))
sleep(1)
randomize_appearance_for(random_gender = TRUE)
var/default_slot = get_pref(/datum/preference_setting/numerical/default_slot)
var/gender = get_pref(/datum/preference_setting/enum/gender)
var/species = get_pref(/datum/preference_setting/string/species)
var/datum/preference_setting/real_name = get_pref_datum(/datum/preference_setting/string/real_name)
real_name.setting = random_name(gender, species)
save_character_sqlite(theckey, C, default_slot)
saveloaded = 1
/datum/preferences/proc/init_datums()
for (var/database_setting in typesof(/datum/preference_setting))
var/datum/preference_setting/setting_datum = database_setting
if (!initial(setting_datum.enabled))
continue
setting_datum = new database_setting(src)
if (setting_datum.sql_table == "client")
preference_settings_client[database_setting] = setting_datum
else
preference_settings_character[database_setting] = setting_datum
/datum/preferences/Destroy()
for(var/entry in subsections)
var/datum/preferences_subsection/prefs_ss = subsections[entry]
if(prefs_ss && !prefs_ss.gcDestroyed)
QDEL_NULL(prefs_ss)
for(var/key, setting in preference_settings_character)
QDEL_NULL(setting)
preference_settings_character -= key
for(var/key, setting in preference_settings_client)
QDEL_NULL(setting)
preference_settings_client -= key
..()
// Try to load a SQLite save for this character, creating it if there's nothing.
/datum/preferences/proc/try_load_save_sqlite(var/theckey, var/theclient, var/theslot)
var/attempts = 0
var/database/query/existing_player_check = new
existing_player_check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", theckey, theslot)
if(existing_player_check.Execute(db))
if(!existing_player_check.NextRow())
while(!create_character_sqlite(theckey, theclient, theslot) && attempts < 5)
sleep(15)
attempts++
if(attempts >= 5)//failsafe so people don't get locked out of the round forever
fallback_random_character(theckey, theclient)
save_character_sqlite(theckey, theclient, theslot)
else
while(!load_character_sqlite(theckey, theclient, theslot) && attempts < 5)
sleep(15)
attempts++
if(attempts >= 5)//failsafe so people don't get locked out of the round forever
fallback_random_character(theckey, theclient)
saveloaded = 1
theclient << 'sound/misc/prefsready.wav'
/datum/preferences/proc/fallback_random_character(var/theclient, var/theckey)
randomize_appearance_for(random_gender = TRUE)
var/species = get_pref(/datum/preference_setting/string/species)
var/gender = get_pref(/datum/preference_setting/enum/gender)
var/datum/preference_setting/name_setting = get_pref_datum(/datum/preference_setting/string/real_name)
name_setting.setting = random_name(gender, species)
log_debug("Player [theckey] FAILED to load save 5 times and has been randomized.")
log_admin("Player [theckey] FAILED to load save 5 times and has been randomized.")
if(theclient)
alert(theclient, "For some reason you've failed to load your save slot 5 times now, so you've been generated a random character. Don't worry, it didn't overwrite your old one. Saving it may overwrite it, so be careful.","Randomized Character", "OK")
/datum/preferences/proc/GetPlayerAltTitle(datum/job/job)
var/list/player_alt_titles = get_pref(/datum/preference_setting/list_values/player_alt_titles)
var/alt_title = player_alt_titles[job.title]
if(!alt_title || !(alt_title in job.alt_titles))
return job.title
return alt_title
/datum/preferences/proc/SetPlayerAltTitle(datum/job/job, new_title)
// remove existing entry
var/datum/preference_setting/p_alt_tiltes_set = get_pref_datum(/datum/preference_setting/list_values/player_alt_titles)
var/list/player_alt_titles = p_alt_tiltes_set.setting
if(player_alt_titles.Find(job.title))
player_alt_titles -= job.title
// add one if it's not default
if(job.title != new_title)
player_alt_titles[job.title] = new_title
/datum/preferences/proc/SetJob(mob/user, role, increase)
var/datum/job/job = job_master.GetJob(role)
var/species = get_pref(/datum/preference_setting/string/species)
if(!job)
user << browse(null, "window=mob_occupation")
ShowChoices(user)
return
if(job.species_blacklist.Find(species)) //Check if our species is in the blacklist
to_chat(user, "<span class='notice'>Your species ([species]) can't have this job!</span>")
return
if(job.species_whitelist.len) //Whitelist isn't empty - check if our species is in the whitelist
if(!job.species_whitelist.Find(species))
var/allowed_species = ""
for(var/S in job.species_whitelist)
allowed_species += "[S]"
if(job.species_whitelist.Find(S) != job.species_whitelist.len)
allowed_species += ", "
to_chat(user, "<span class='notice'>Only the following species can have this job: [allowed_species]. Your species is ([species]).</span>")
return
var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs)
var/new_value = jobs[job.title]
if(increase)
new_value += 1
if(new_value > JOB_PREF_HIGH)
new_value = JOB_PREF_NEVER
else
new_value -= 1
if(new_value < JOB_PREF_NEVER)
new_value = JOB_PREF_HIGH
// If setting a job to high,
// set any other job that is currently high to med
if(new_value == JOB_PREF_HIGH)
for(var/some_job in jobs)
if(jobs[some_job] == JOB_PREF_HIGH)
jobs[some_job] = JOB_PREF_MED
jobs[job.title] = new_value
else if(new_value == JOB_PREF_NEVER)
jobs -= job.title
else
jobs[job.title] = new_value
SetJobsChoice(user)
return 1
/datum/preferences/proc/ResetJobs()
// For reference `get_pref` would be equivalent here but I am doing this for clarity.
var/datum/preference_setting/jobs_setting = get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs)
var/list/jobs = jobs_setting.setting
jobs.Cut()
/datum/preferences/proc/process_link(mob/user, list/href_list)
if(!user)
return
var/datum/preferences_subsection/subsection = subsections[href_list["subsection"]]
if(subsection)
var/result = subsection.process_link(user, href_list)
if(result)
return result
// General soft-coding stuff.
// Relatively inelegant. Any better idea?
for (var/key, value in preference_settings_client)
var/datum/preference_setting/setting = value
if (setting.sql_name == href_list["preference"])
setting.process_link(href_list["task"], user, href_list)
ShowChoices(user)
return
for (var/key, value in preference_settings_character)
var/datum/preference_setting/setting = value
if (setting.sql_name == href_list["preference"])
setting.process_link(href_list["task"], user, href_list)
return
if(href_list["task"] == "random_body")
for (var/key, value in preference_settings_character)
var/datum/preference_setting/setting = value
if (setting.sql_name == "real_name") // Bit ugly but have to do it
continue
setting.randomise(user)
ShowChoices(user)
return
// Some unfortunate hard-coding stuff
if(href_list["preference"] == "records")
if(text2num(href_list["record"]) >= 1)
SetRecords(user)
else
user << browse(null, "window=records")
ShowChoices(user)
if(href_list["task"] == "med_record")
var/datum/preference_setting/med_record = get_pref_datum(/datum/preference_setting/string/med_record)
var/medmsg = input(usr,"Set your medical notes here.","Medical Records",html_decode(med_record.setting)) as message
if(medmsg != null)
medmsg = copytext(medmsg, 1, MAX_PAPER_MESSAGE_LEN)
medmsg = html_encode(medmsg)
med_record.setting = medmsg
SetRecords(user)
if(href_list["task"] == "sec_record")
var/datum/preference_setting/sec_record = get_pref_datum(/datum/preference_setting/string/sec_record)
var/secmsg = input(usr,"Set your security notes here.","Security Records",html_decode(sec_record.setting)) as message
if(secmsg != null)
secmsg = copytext(secmsg, 1, MAX_PAPER_MESSAGE_LEN)
secmsg = html_encode(secmsg)
sec_record.setting = secmsg
SetRecords(user)
if(href_list["task"] == "gen_record")
var/datum/preference_setting/gen_record = get_pref_datum(/datum/preference_setting/string/gen_record)
var/genmsg = input(usr,"Set your employment notes here.","Employment Records",html_decode(gen_record.setting)) as message
if(genmsg != null)
genmsg = copytext(genmsg, 1, MAX_PAPER_MESSAGE_LEN)
genmsg = html_encode(genmsg)
gen_record.setting = genmsg
SetRecords(user)
return
// Roles
if(href_list["preference"] == "set_roles")
return SetRoles(user,href_list)
if(href_list["preference"] == "next_preview_background")
preview_background = next_list_item(preview_background, background_options)
return ShowChoices(user)
if(href_list["preference"] == "previous_preview_background")
preview_background = previous_list_item(preview_background, background_options)
return ShowChoices(user)
// Special actions
if (href_list["action"])
switch (href_list["action"])
if("save")
if(world.timeofday >= (lastPolled + POLLED_LIMIT) || user.client.holder)
save_preferences_sqlite(user, user.ckey)
save_character_sqlite(user.ckey, user, slot)
lastPolled = world.timeofday
else
to_chat(user, "You need to wait [round((((lastPolled + POLLED_LIMIT) - world.timeofday) / 10))] seconds before you can save again.")
//random_character_sqlite(user, user.ckey)
if("reload")
load_preferences_sqlite(user.ckey)
load_character_sqlite(user.ckey, user, slot)
if("open_load_dialog")
if(!IsGuestKey(user.key))
open_load_dialog(user)
// DO NOT update window as it'd steal focus.
return
if("close_load_dialog")
close_load_dialog(user)
if("changeslot")
var/num = text2num(href_list["num"])
try_load_slot(user.ckey, user, num)
var/datum/preference_setting/numerical/default_slot/slot_pref = get_pref_datum(/datum/preference_setting/numerical/default_slot)
slot_pref.setting = num
slot = num
close_load_dialog(user)
ShowChoices(user)
if("tab")
if(href_list["tab"])
current_tab = text2num(href_list["tab"])
ShowChoices(user)
return
// We made it this far, means link was unprocessed
CRASH("unprocessed href for [client]; data=[json_encode(href_list)]")
/datum/preferences/proc/copy_to(mob/living/carbon/human/character, safety = 0)
var/datum/preference_setting/name_setting = get_pref_datum(/datum/preference_setting/string/real_name)
var/species = get_pref(/datum/preference_setting/string/species)
var/gender= get_pref(/datum/preference_setting/enum/gender)
if(get_pref(/datum/preference_setting/toggle/be_random_name))
name_setting.setting = random_name(gender,species)
if(config.humans_need_surnames && species == "Human")
var/firstspace = findtext(name_setting.setting, " ")
var/name_length = length(name_setting.setting)
if(!firstspace) //we need a surname
name_setting.setting += " [pick(last_names)]"
else if(firstspace == name_length)
name_setting.setting += "[pick(last_names)]"
character.real_name = name_setting.setting
character.name = character.real_name
character.flavor_text = get_pref(/datum/preference_setting/string/flavor_text)
if(character.dna)
character.dna.real_name = character.real_name
character.dna.flavor_text = character.flavor_text
character.med_record = get_pref(/datum/preference_setting/string/med_record)
character.sec_record = get_pref(/datum/preference_setting/string/sec_record)
character.gen_record = get_pref(/datum/preference_setting/string/gen_record)
character.setGender(gender)
character.age = get_pref(/datum/preference_setting/numerical/age)
character.my_appearance.r_eyes = get_pref(/datum/preference_setting/numerical/r_eyes)
character.my_appearance.g_eyes = get_pref(/datum/preference_setting/numerical/g_eyes)
character.my_appearance.b_eyes = get_pref(/datum/preference_setting/numerical/b_eyes)
character.my_appearance.r_hair = get_pref(/datum/preference_setting/numerical/r_hair)
character.my_appearance.g_hair = get_pref(/datum/preference_setting/numerical/g_hair)
character.my_appearance.b_hair = get_pref(/datum/preference_setting/numerical/b_hair)
character.my_appearance.r_facial = get_pref(/datum/preference_setting/numerical/r_facial)
character.my_appearance.g_facial = get_pref(/datum/preference_setting/numerical/g_facial)
character.my_appearance.b_facial = get_pref(/datum/preference_setting/numerical/b_facial)
character.my_appearance.s_tone = get_pref(/datum/preference_setting/numerical/s_tone)
character.my_appearance.h_style = get_pref(/datum/preference_setting/string/h_style)
character.my_appearance.f_style = get_pref(/datum/preference_setting/string/f_style)
character.dna.ResetUIFrom(character)
if(get_pref(/datum/preference_setting/toggle/be_random_body))
character.my_appearance.randomise()
// Destroy/cyborgize organs
for(var/name in organ_data)
var/datum/organ/external/O = character.organs_by_name[name]
var/datum/organ/internal/I = character.internal_organs_by_name[name]
var/status = organ_data[name]
if(status == "amputated")
O.status &= ~ORGAN_ROBOT
O.status &= ~ORGAN_PEG
O.amputated = 1
O.status |= ORGAN_DESTROYED
O.destspawn = 1
else if(status == "cyborg")
O.status &= ~ORGAN_PEG
O.status |= ORGAN_ROBOT
else if(status == "peg")
O.status &= ~ORGAN_ROBOT
O.status |= ORGAN_PEG
else if(status == "assisted")
I?.mechassist()
else if(status == "mechanical")
I?.mechanize()
else
continue
var/disabilities = get_pref(/datum/preference_setting/binary_flag/disabilities)
var/datum/species/chosen_species = all_species[species]
if( (disabilities & DISABILITY_FLAG_FAT) && (chosen_species.anatomy_flags & CAN_BE_FAT) )
character.mutations += M_FAT
if(disabilities & DISABILITY_FLAG_NEARSIGHTED)
character.disabilities|=NEARSIGHTED
if(disabilities & DISABILITY_FLAG_EPILEPTIC)
character.disabilities|=EPILEPSY
if(disabilities & DISABILITY_FLAG_EHS)
character.disabilities|=ELECTROSENSE
if(disabilities & DISABILITY_FLAG_DEAF)
character.sdisabilities|=DEAF
if(disabilities & DISABILITY_FLAG_BLIND)
character.sdisabilities|=BLIND
/*if(disabilities & DISABILITY_FLAG_COUGHING)
character.sdisabilities|=COUGHING
if(disabilities & DISABILITY_FLAG_TOURETTES)
character.sdisabilities|=TOURETTES Still working on it. - Angelite */
var/underwear = get_pref(/datum/preference_setting/numerical/underwear)
if(underwear > underwear_m.len || underwear < 1)
underwear = 0 //I'm sure this is 100% unnecessary, but I'm paranoid... sue me. //HAH NOW NO MORE MAGIC CLONING UNDIES
character.underwear = underwear
var/backbag = get_pref(/datum/preference_setting/numerical/backbag)
if(backbag > 5 || backbag < 1)
backbag = 1 //Same as above
character.backbag = backbag
//Debugging report to track down a bug, which randomly assigned the plural gender to people.
if(character.gender in list(PLURAL, NEUTER))
if(isliving(character) && !ismushroom(character)) //Ghosts and mushroom people are neuter by default
message_admins("[character] ([character.ckey]) has spawned with their gender as plural or neuter. Please notify coders.")
character.setGender(MALE)
/datum/preferences/proc/SetRoles(var/mob/user, var/list/href_list)
// We just grab the role from the POST(?) data.
for(var/role_id in special_roles)
if(role_id in href_list)
roles[role_id] = text2num(href_list[role_id])
ShowChoices(user)
return 1
/datum/preferences/proc/get_pref_datum(var/datum/preference_setting/type) as /datum/preference_setting
if (type in preference_settings_client)
var/datum/preference_setting/the_setting = preference_settings_client[type]
if (!the_setting)
stack_trace("unset client preference [type] on [client]:[client.mob.type]")
return initial(type.default_setting)
return the_setting
if (type in preference_settings_character)
var/datum/preference_setting/the_setting = preference_settings_character[type]
if (!the_setting)
stack_trace("unset character preference [type] on [client]:[client.mob.type]")
return initial(type.default_setting)
return the_setting
CRASH("invalid preference setting requested: [type] on [src.client]")
/datum/preferences/proc/get_pref(var/datum/preference_setting/type)
var/datum/preference_setting/the_setting = get_pref_datum(type)
return the_setting.setting
// limbs & organs
/datum/preferences/proc/change_pref_datum_limb(var/limb_internal_name, var/limb_internal_state)
for (var/key, setting_datum in preference_settings_character)
var/datum/preference_setting/setting_type = key
if (initial(setting_type.sql_table) != "limbs")
continue
if (initial(setting_type.sql_name) == limb_internal_name)
var/datum/preference_setting/limb = preference_settings_character[setting_type]
limb.setting = limb_internal_state
return
/client/verb/modify_preferences(page as num)
set name = "modifypreferences"
set hidden = 1
if(!prefs.saveloaded)
to_chat(src, "<span class='warning'>Your character preferences have not yet loaded.</span>")
return
switch(page)
if(1)
prefs.current_tab = GENERAL_SETUP
if(2)
prefs.current_tab = SPECIAL_ROLES_SETUP
prefs.ShowChoices(usr)

View File

@@ -0,0 +1,639 @@
/// IF YOU NEED A FIELD ADDED TO THE DATABASE, CREATE A MIGRATION SO SHIT GETS UPDATED.
/// Also update SQL/players2.sql.
/// SEE code/modules/migrations/SS13_Prefs/
/datum/preferences/proc/try_load_preferences(var/ckey, var/mob/user)
// Check client:
var/database/query/client_check = new
client_check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
if(client_check.Execute(db))
if(!client_check.NextRow())
return save_preferences_sqlite(user, ckey)
else
return load_preferences_sqlite(ckey)
else
WARNING("Error in try_load_preferences [__FILE__] ln:[__LINE__] #:[client_check.Error()] - [client_check.ErrorMsg()]")
/datum/preferences/proc/load_preferences_sqlite(var/ckey)
var/list/database_data = execute_load_pref_query(ckey)
read_database_data_client(database_data)
initialize_preferences()
if (islist(database_data))
return 1
else
return 0
/datum/preferences/proc/execute_load_pref_query(var/ckey)
var/list/preference_list_client = new
var/database/query/check = new
var/database/query/q = new
check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
if(check.Execute(db))
if(!check.NextRow())
WARNING("Empty client setting for [ckey]")
stack_trace("Empty client setting for [ckey]!")
return 0
else
WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
stack_trace("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
q.Add("SELECT * FROM client WHERE ckey = ?", ckey)
if(q.Execute(db))
while(q.NextRow())
var/list/row = q.GetRowData()
for(var/a in row)
preference_list_client[a] = row[a]
else
WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
return preference_list_client
/datum/preferences/proc/read_database_data_client(var/list/database_data)
for (var/key, value in preference_settings_client)
var/datum/preference_setting/setting_datum = value
try
setting_datum.setting = setting_datum.load_sql(database_data[setting_datum.sql_name]) // First we load...
setting_datum.setting = setting_datum.sanitize_setting(setting_datum.setting) // Then we sanitize
catch
CRASH("wrong setting loaded [key] which got [setting_datum.sql_name], wasn't in list: [json_encode(database_data)]")
/datum/preferences/proc/initialize_preferences(client_login = 0)
var/attack_animation = get_pref(/datum/preference_setting/enum/attack_animations)
if(attack_animation == PERSON_ANIMATION)
person_animation_viewers |= client
item_animation_viewers -= client
else if(attack_animation == ITEM_ANIMATION)
item_animation_viewers |= client
person_animation_viewers -= client
else
item_animation_viewers -= client
person_animation_viewers -= client
/datum/preferences/proc/save_preferences_sqlite(var/user, var/ckey)
var/database/query/check = new
var/database/query/q = new
check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
if(check.Execute(db))
if(!check.NextRow())
var/list/arguments_query = new_db_entry_query_args(ckey)
q.Add(arglist(arguments_query))
if(!q.Execute(db))
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]. sql= [arguments_query[1]]")
return 0
else
var/list/arguments_query = save_db_entry_query_args(ckey)
q.Add(arglist(arguments_query))
if(!q.Execute(db))
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]. sql= [arguments_query[1]]")
return 0
else
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
to_chat(user, "Preferences Updated.")
lastPolled = world.timeofday
return 1
/datum/preferences/proc/load_character_sqlite(var/ckey, var/user, var/slot)
var/list/preference_list = new
var/database/query/q = new
var/database/query/check = new
check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot)
if(check.Execute(db))
if(!check.NextRow())
to_chat(user, "You have no character file to load, please save one first.")
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
return 0
else
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
stack_trace("load_save_sqlite Check Error #: [check.Error()] - [check.ErrorMsg()]")
return 0
var/sql = load_character_sql()
// Ckey and slot and ?/joker parameters in the prepared query
q.Add(sql, ckey, slot)
if(q.Execute(db))
while(q.NextRow())
var/list/row = q.GetRowData()
for(var/a in row)
preference_list[a] = row[a]
else
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
stack_trace("load_save_sqlite Error [__LINE__] #: [q.Error()] - [q.ErrorMsg()]. Dump [sql]")
return 0
for (var/key, setting in preference_settings_character)
var/datum/preference_setting/the_setting = setting
the_setting.setting = the_setting.load_sql(preference_list[the_setting.sql_name])
the_setting.setting = the_setting.sanitize_setting(the_setting.setting)
// Antag roles. This is still a bit hardcoded but meeeeeh
for(var/role_id in special_roles)
roles[role_id]=0
q = new
q.Add("SELECT role, preference FROM client_roles WHERE ckey=? AND slot=?", ckey, slot)
if(q.Execute(db))
while(q.NextRow())
var/list/row = q.GetRowData()
roles[row["role"]] = text2num(row["preference"])
else
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
stack_trace("Error in load_save_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
// Unused.
//if(!skills)
// skills = list()
//if(!used_skillpoints)
// used_skillpoints= 0
if(user)
to_chat(user, "Successfully loaded [get_pref(/datum/preference_setting/string/real_name)].")
return 1
/datum/preferences/proc/save_character_sqlite(var/ckey, var/user, var/slot_chosen)
if(slot > MAX_SAVE_SLOTS)
to_chat(user, "You are limited to [MAX_SAVE_SLOTS] character slots.")
message_admins("[ckey] attempted to override character slot limit")
return 0
var/database/query/q = new
// This checks if the DB is still connected to us.
var/database/query/check = new
// General player
check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(!check.NextRow())
CRASH("Trying to save a character slot but there's no slot. Ckey = [ckey], slot_chosen = [slot_chosen]")
else
var/list/sql_arguments_update_character = update_db_entry_query_character_args(ckey, slot_chosen)
q.Add(arglist(sql_arguments_update_character))
if(!q.Execute(db))
WARNING("Error in update_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in update_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()] sql=[sql_arguments_update_character[1]]")
return 0
to_chat(user, "Updated Character")
else
WARNING("Error at character creation/update: [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error at character creation/update: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
return 0
check.Add("SELECT player_ckey FROM body WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(!check.NextRow())
CRASH("Trying to save a character slot but there's no slot")
else
var/list/sql_arguments_update_body = update_db_entry_query_body_args(ckey, slot_chosen)
q.Add(arglist(sql_arguments_update_body))
if(!q.Execute(db))
WARNING("Error in update_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in update_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Updated Body")
else
WARNING("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
// Jobs are left hardcoded for now since they not likely to be fundamentally changed
// However the same proc logic could apply here if we had some job-per-character specific sett
var/datum/preference_setting/jobs = get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs)
var/alternate_option = get_pref(/datum/preference_setting/enum/alternate_option)
check.Add("SELECT player_ckey FROM jobs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(!check.NextRow())
CRASH("Trying to save a character slot but there's no slot")
else
// 1 2
q.Add("UPDATE jobs SET alternate_option=?,jobs=? WHERE player_ckey = ? AND player_slot = ?",\
alternate_option, jobs.save_sql(jobs.setting), ckey, slot_chosen)
if(!q.Execute(db))
WARNING("Error in update_jobs_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in update_jobs_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Updated Job List")
else
WARNING("Error at jobs selection sqlite ln [__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error at jobs selection sqlite ln [__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
// Limbs
check.Add("SELECT player_ckey FROM limbs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(!check.NextRow())
CRASH("Trying to save a character slot but there's no slot")
else
for(var/key, setting in preference_settings_character)
var/datum/preference_setting/the_setting = setting
if (the_setting.sql_table != "limbs")
continue
q.Add("UPDATE limbs SET [the_setting.sql_name] = ? WHERE player_ckey = ? AND player_slot = ?", the_setting.setting, ckey, slot_chosen)
if(!q.Execute(db))
WARNING("Error in update limbs sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in update limbs sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Updated Limbs")
else
WARNING("Error at savelimbs selection from ckey sqlite character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error at limbs selection from ckey sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
for(var/role_id in roles)
if(!(roles[role_id] & ROLEPREF_SAVE))
continue
q = new
q.Add("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES (?,?,?,?)", ckey, slot, role_id, (roles[role_id] & ROLEPREF_VALMASK))
//testing("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES ('[ckey]',[slot],'[role_id]',[roles[role_id] & ROLEPREF_VALMASK])")
if(!q.Execute(db)) // This never triggers on error, for some reason.
WARNING("ClientRoleInsert: Error #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("ClientRoleInsert: Error #: [q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Successfully saved [get_pref(/datum/preference_setting/string/real_name)]")
return 1
/datum/preferences/proc/create_character_sqlite(var/ckey, var/user, var/slot_chosen)
if(slot > MAX_SAVE_SLOTS)
to_chat(user, "You are limited to [MAX_SAVE_SLOTS] character slots.")
stack_trace("[ckey] attempted to override character slot limit")
return 0
var/database/query/q = new
// This checks if the DB is still connected to us.
var/database/query/check = new
// General player
check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(check.NextRow())
CRASH("creating a character where there is already a slot! [slot_chosen]")
var/list/sql_arguments_new_character = new_db_entry_query_character_args(ckey, slot_chosen)
q.Add(arglist(sql_arguments_new_character))
if (!q.Execute(db))
WARNING("Error in create_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in create_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()] - sql:[sql_arguments_new_character[1]]")
return 0
to_chat(user, "Created Character")
else
WARNING("Error at character creation: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
stack_trace("Error at character creation: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
return 0
check.Add("SELECT player_ckey FROM body WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(check.NextRow())
CRASH("creating a body where there is already a slot!")
var/list/sql_arguments_new_body = new_db_entry_query_body_args(ckey, slot_chosen)
q.Add(arglist(sql_arguments_new_body))
if(!q.Execute(db))
WARNING("Error in create_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in create_body_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Created Body")
else
WARNING("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
stack_trace("Error at body selection from ckey: [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
// Jobs are left hardcoded for now since they not likely to be fundamentally changed
// However the same proc logic could apply here if we had some job-per-character specific sett
var/datum/preference_setting/jobs = get_pref_datum(/datum/preference_setting/assoc_list_setting/jobs)
var/alternate_option = get_pref(/datum/preference_setting/enum/alternate_option)
check.Add("SELECT player_ckey FROM jobs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(check.NextRow())
CRASH("creating a body where there is already a slot : [ckey], [slot_chosen]")
// 1 2 3 4
q.Add("INSERT INTO jobs (player_ckey,player_slot,alternate_option,jobs) \
VALUES (?, ?, ?, ?)", \
ckey, slot_chosen, alternate_option, jobs.save_sql(jobs.setting))
if(!q.Execute(db))
WARNING("Error in create_jobs_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in create_jobs_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Created Job list")
else
WARNING("Error at jobs selection sqlite ln [__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
stack_trace("Error at jobs selection sqlite ln [__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
// Limbs
check.Add("SELECT player_ckey FROM limbs WHERE player_ckey = ? AND player_slot = ?", ckey, slot_chosen)
if(check.Execute(db))
if(check.NextRow())
CRASH("creating limbs where there is already a slot!")
q.Add("INSERT INTO limbs (player_ckey, player_slot) VALUES (?,?)", ckey, slot_chosen)
if(!q.Execute(db))
WARNING("Error in insert into Limbs sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in insert into Limbs sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
for(var/key, setting in preference_settings_character)
var/datum/preference_setting/the_setting = setting
if (the_setting.sql_table != "limbs")
continue
q.Add("UPDATE limbs SET [the_setting.sql_name]=? WHERE player_ckey = ? AND player_slot = ?", the_setting.default_setting, ckey, slot_chosen)
if(!q.Execute(db))
WARNING("Error in update limbs (creation) sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in update limbs (creation) sqlite [__FILE__] ln:[__LINE__] #; [q.Error()] - [q.ErrorMsg()]")
return 0
organ_data[the_setting.sql_name] = the_setting.setting
to_chat(user, "Created Limbs")
else
WARNING("Error at save limbs [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
stack_trace("Error at save limbs [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
return 0
for(var/role_id in roles)
if(!(roles[role_id] & ROLEPREF_SAVE))
continue
q = new
q.Add("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES (?,?,?,?)", ckey, slot, role_id, (roles[role_id] & ROLEPREF_VALMASK))
//testing("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES ('[ckey]',[slot],'[role_id]',[roles[role_id] & ROLEPREF_VALMASK])")
if(!q.Execute(db)) // This never triggers on error, for some reason.
WARNING("ClientRoleInsert: Error #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("ClientRoleInsert: Error #: [q.Error()] - [q.ErrorMsg()]")
return 0
randomize_appearance_for(random_gender = TRUE)
var/gender = get_pref(/datum/preference_setting/enum/gender)
var/species = get_pref(/datum/preference_setting/string/species)
var/datum/preference_setting/name_setting = get_pref_datum(/datum/preference_setting/string/real_name)
name_setting.setting = random_name(gender, species)
to_chat(user, "Successfully created [get_pref(/datum/preference_setting/string/real_name)]")
return 1
/datum/preferences/proc/try_load_slot(var/ckey, var/user, var/num)
var/database/query/check = new
check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, num)
if(check.Execute(db))
if(!check.NextRow()) // No slot
return create_character_sqlite(ckey, user, num)
else // Has a slot
return load_character_sqlite(ckey, user, num)
else
WARNING("[__LINE__]: datum/preferences/try_load_slot has returned")
stack_trace("try_load_slot Check Error #: [check.Error()] - [check.ErrorMsg()]")
return 0
// ============================ MISC PROCS HELPER ============================
/datum/preferences/proc/SetChangelog(ckey,hash)
var/datum/preference_setting/lastchangelog = get_pref_datum(/datum/preference_setting/string/changelog)
lastchangelog.setting=hash
var/database/query/q = new
q.Add("UPDATE client SET lastchangelog=? WHERE ckey=?",lastchangelog.setting,ckey)
if(!q.Execute(db))
WARNING("Error in Setchangelog [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in SetChangelog [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
/datum/preferences/proc/random_character_sqlite(var/user, var/ckey)
var/database/query/q = new
var/list/slot_list = new
q.Add("SELECT player_slot FROM players WHERE player_ckey=?", ckey)
if(q.Execute(db))
while(q.NextRow())
slot_list.Add(q.GetColumn(1))
else
WARNING("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
stack_trace("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
return 0
var/random_slot = pick(slot_list)
save_character_sqlite(ckey, user, random_slot)
return 1
// -- DB SQL HELPERS --
// The sql text this outputs looks like this :
// "INSERT into players (player_ckey, player_slot, var1, ...)"
// And the list it returns is like:
// list(sql, ckey, slot, var1, var2, ...)
// This list is automatically converted into arguments via arglist
// ======= LOADER =======
// This loads all the character data from all SQL tables at once
// Looks like SELECT players.real_name, ..., body.r_eyes, ... FROM players INNER JOIN {joining operation on ckeys}
// WHERE players.player_ckey = ? and players.player_slot = ?
/datum/preferences/proc/load_character_sql()
var/sql = "SELECT "
for (var/database_setting in typesof(/datum/preference_setting))
var/datum/preference_setting/setting_datum = database_setting
if (!initial(setting_datum.enabled))
continue
if (initial(setting_datum.sql_table) == "client")
continue
sql += "[initial(setting_datum.sql_table)].[initial(setting_datum.sql_name)], "
sql = copytext(sql, 1, length(sql) - 1) // Get rid of the last ,
sql += " " // Empty space
// This is still relatively hard-coded but it's more or less fine for now
sql += {"
FROM
players
INNER JOIN
limbs
ON
(
players.player_ckey = limbs.player_ckey)
AND (
players.player_slot = limbs.player_slot)
INNER JOIN
jobs
ON
(
limbs.player_ckey = jobs.player_ckey)
AND (
limbs.player_slot = jobs.player_slot)
INNER JOIN
body
ON
(
jobs.player_ckey = body.player_ckey)
AND (
jobs.player_slot = body.player_slot)
WHERE
players.player_ckey = ?
AND players.player_slot = ? ;"}
return sql
// ============================ FOR CLIENT PREFS ============================
// The sql text this outputs looks like this :
// "INSERT INTO client (ckey, var1, var2, ...) VALUES (?, ?, ?, ...)"
// And the list it returns is like:
// list(sql, ckey, var1, var2, ...)
// This list is automatically converted into arguments via arglist
// -- You should not need to modify this - this automatically reads all settings and save them.
/datum/preferences/proc/new_db_entry_query_args(var/ckey)
var/list/returned_list = list()
var/sql_text = "INSERT into client (ckey"
var/sql_text_end = ") VALUES (?"
returned_list.Add(sql_text) // First item in the query is the SQL
returned_list.Add(ckey) // Second item is the ckey (first joker)
// This is a prepared queries. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list
// This MEANS that the order of the param list is pretty important!
for (var/key, setting in preference_settings_client)
var/datum/preference_setting/the_setting = setting
sql_text += ", [the_setting.sql_name]"
sql_text_end += ", ?"
returned_list.Add(the_setting.setting)
sql_text_end += ")"
returned_list[1] = "[sql_text][sql_text_end]"
return returned_list
// The sql text this outputs looks like this :
// "UPDATE client SET var1=?, var2=?, ... WHERE CKEY=?"
// And the list it returns is like:
// list(sql, var1, var2, ..., ckey)
// This list is automatically converted into arguments via arglist
/datum/preferences/proc/save_db_entry_query_args(var/ckey)
var/list/returned_list = list()
var/sql_text = "UPDATE client SET "
var/sql_text_end = " WHERE ckey = ?"
returned_list.Add(sql_text) // First item in the query is the SQL
// This is a prepared queries. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list
// This MEANS that the order of the param list is pretty important!
for (var/key, setting in preference_settings_client)
var/datum/preference_setting/the_setting = setting
sql_text += "[the_setting.sql_name]=?, "
returned_list.Add(the_setting.setting)
sql_text = copytext(sql_text, 1, length(sql_text)-1) // Remove the last ,
returned_list.Add(ckey) // This time, the ckey is the last joker
returned_list[1] = "[sql_text][sql_text_end]"
return returned_list
// ============================ FOR JOBS, ALT-TITLES, ETC ============================
// The sql text this outputs looks like this :
// "INSERT INTO players (player_ckey=?, player_slot=?, var1=?, var2=? ...) VALUES (?, ?, ?, ...)"
// And the list it returns is like:
// list(sql, ckey, slot, var1, ...)
// This list is automatically converted into arguments via arglist
/datum/preferences/proc/new_db_entry_query_character_args(var/ckey, var/slot)
// -- You should not need to modify this - this automatically reads all settings and save them.
var/list/returned_list = list()
var/sql_text = "INSERT into players (player_ckey,player_slot"
var/sql_text_end = ") VALUES (?,?"
returned_list.Add(sql_text) // First item in the query is the SQL
returned_list.Add(ckey) // Second item is the ckey (first joker)
returned_list.Add(slot) // Third item is the slot (second joker)
// This is a prepared queries. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list
// This MEANS that the order of the param list is pretty important!
for (var/key, setting in preference_settings_character)
var/datum/preference_setting/the_setting = setting
if (the_setting.sql_table != "players")
continue
sql_text += ",[the_setting.sql_name]"
sql_text_end += ",?"
var/data = the_setting.save_sql(the_setting.default_setting)
returned_list.Add(data)
sql_text_end += ")"
returned_list[1] = "[sql_text][sql_text_end]"
return returned_list
// The sql text this outputs looks like this :
// "UPDATE players SET var1=?, var2=? ... WHERE player_ckey = ? AND player_slot = ?"
// And the list it returns is like:
// list(sql, var1, var2, ..., ckey, slot)
// This list is automatically converted into arguments via arglist
/datum/preferences/proc/update_db_entry_query_character_args(var/ckey, var/slot)
// -- You should not need to modify this - this automatically reads all settings and save them.
var/list/returned_list = list()
var/sql_text = "UPDATE players SET " // White space is important
var/sql_text_end = " WHERE player_ckey = ? AND player_slot = ?"
returned_list.Add(sql_text) // First item in the query is the SQL
// This is a prepared query. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list
// This MEANS that the order of the param list is pretty important!
for (var/key, setting in preference_settings_character)
var/datum/preference_setting/the_setting = setting
if (the_setting.sql_table != "players")
continue
sql_text += "[the_setting.sql_name]=?,"
returned_list.Add(the_setting.save_sql(the_setting.setting))
sql_text = copytext(sql_text, 1, length(sql_text)) // Remove the last ,
returned_list.Add(ckey) // Second-to-last item is the ckey (second to last joker)
returned_list.Add(slot) // Last item is the slot (last joker)
returned_list[1] = "[sql_text][sql_text_end]"
return returned_list
// ============================ FOR BODIES ============================
// The sql text this outputs looks like this :
// "INSERT into body (player_ckey, player_slot, var1, ...)"
// And the list it returns is like:
// list(sql, ckey, slot, var1, var2, ...)
// This list is automatically converted into arguments via arglist
/datum/preferences/proc/new_db_entry_query_body_args(var/ckey, var/slot)
// -- You should not need to modify this - this automatically reads all settings and save them.
var/list/returned_list = list()
var/sql_text = "INSERT into body (player_ckey,player_slot"
var/sql_text_end = ") VALUES (?,?"
returned_list.Add(sql_text) // First item in the query is the SQL
returned_list.Add(ckey) // Second item is the ckey (first joker)
returned_list.Add(slot) // Third item is the slot (second joker)
// This is a prepared query. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list
// This MEANS that the order of the param list is pretty important!
for (var/key, setting in preference_settings_character)
var/datum/preference_setting/the_setting = setting
if (the_setting.sql_table != "body")
continue
sql_text += ",[the_setting.sql_name]"
sql_text_end += ",?"
returned_list.Add(the_setting.save_sql(the_setting.default_setting))
sql_text_end += ")"
returned_list[1] = "[sql_text][sql_text_end]"
return returned_list
// The sql text this outputs looks like this :
// "UPDATE body SET var1=?, var2=? ... WHERE player_ckey = ? AND player_slot = ?"
// And the list it returns is like:
// list(sql, var1, var2, ..., ckey, slot)
// This list is automatically converted into arguments via arglist
/datum/preferences/proc/update_db_entry_query_body_args(var/ckey, var/slot)
// -- You should not need to modify this - this automatically reads all settings and save them.
var/list/returned_list = list()
var/sql_text = "UPDATE body SET " // White space is important
var/sql_text_end = " WHERE player_ckey = ? AND player_slot = ?"
returned_list.Add(sql_text) // First item in the query is the SQL
// This is a prepared query. All the "?" here are jokers which will be replaced internally by BYOND with the values in the param list
// This MEANS that the order of the param list is pretty important!
for (var/key, setting in preference_settings_character)
var/datum/preference_setting/the_setting = setting
if (the_setting.sql_table != "body")
continue
sql_text += "[the_setting.sql_name]=?,"
returned_list.Add(the_setting.save_sql(the_setting.setting))
sql_text = copytext(sql_text, 1, length(sql_text)) // Remove the last ,
returned_list.Add(ckey) // Second-to-last item is the ckey (second to last joker)
returned_list.Add(slot) // Last item is the slot (last joker)
returned_list[1] = "[sql_text][sql_text_end]"
return returned_list

View File

@@ -0,0 +1,658 @@
/datum/preferences/proc/setup_character_options(var/dat, var/user)
var/race_skin_tone_desc = skintone2racedescription(get_pref(/datum/preference_setting/numerical/s_tone), get_pref(/datum/preference_setting/string/species))
dat += {"<center>
<h2>Occupation Choices</h2>
<a href='?_src_=prefs;preference=jobs;task=menu'>Set Occupation Preferences</a><br>
</center>
<h2>Identity</h2>
<table width='100%'><tr><td width='75%' valign='top'>
<a href='?_src_=prefs;preference=real_name;task=random'>Random Name</a>
<a href='?_src_=prefs;preference=random_name;task=input'>Always Random Name: [get_pref(/datum/preference_setting/toggle/be_random_name) ? "Yes" : "No"]</a><br>
<b>Name:</b> <a href='?_src_=prefs;preference=real_name;task=input'>[get_pref(/datum/preference_setting/string/real_name)]</a><BR>
<b>Gender:</b> <a href='?_src_=prefs;preference=gender;task=input'>[get_pref(/datum/preference_setting/enum/gender) == MALE ? "Male" : "Female"]</a><BR>
<b>Age:</b> <a href='?_src_=prefs;preference=age;task=input'>[get_pref(/datum/preference_setting/numerical/age)]</a>
</td><td valign='center'>
<div class='statusDisplay'style="height: 64px; width: 128px; padding:0px"><center><img src=previewicon.png class="charPreview"><img src=previewicon2.png class="charPreview"></center></div>
<b>Background </b><a href='?_src_=prefs;preference=previous_preview_background;task=input'>&lt;</a> <a href='?_src_=prefs;preference=next_preview_background;task=input'>&gt;</a><BR>
</td></tr></table>
<h2>Body</h2>
<a href='?_src_=prefs;preference=all;task=random_body'>Random Body</a>
<a href='?_src_=prefs;preference=random_body;task=input'>
Always Random Body: [get_pref(/datum/preference_setting/toggle/be_random_body) ? "Yes" : "No"]
</a><br>
<table width='100%'>
<tr>
<td width='37%' valign='top'>
<br/>
<b>Species:</b>
<a href='?_src_=prefs;preference=species;task=input'>
[get_pref(/datum/preference_setting/string/species)]
</a><br>
<b>Tertiary Language:</b>
<a href='byond://?src=\ref[user];preference=language;task=input'>
[get_pref(/datum/preference_setting/string/language)]
</a><br>
<b>Skin Tone:</b>
<a href='?_src_=prefs;preference=skin_tone;task=input'>
[get_pref(/datum/preference_setting/string/species) == "Human" ? "[-get_pref(/datum/preference_setting/numerical/s_tone) + 35]/220" : "[get_pref(/datum/preference_setting/numerical/s_tone)]"] - [race_skin_tone_desc]
</a><br><br>
</td>
<td valign='top' width='21%'>
<center>
<h3>Hair Style</h3>
<a href='?_src_=prefs;preference=hair_style_name;task=input'>
[get_pref(/datum/preference_setting/string/h_style)]
</a><br>
<a href='?_src_=prefs;preference=hair_style_name;task=previous_hair_style'>&lt;</a>
<a href='?_src_=prefs;preference=hair_style_name;task=next_hair_style'>&gt;</a><br>
<span style='border:1px solid #161616; background-color: #[num2hex(get_pref(/datum/preference_setting/numerical/r_hair), 2)][num2hex(get_pref(/datum/preference_setting/numerical/g_hair), 2)][num2hex(get_pref(/datum/preference_setting/numerical/b_hair), 2)];'>&nbsp;&nbsp;&nbsp;</span>
<a href='?_src_=prefs;preference=hair_style_name;task=input_hair_color'>Change</a><br>
</center>
</td>
<td valign='top' width='21%'>
<h3>Facial Hair Style</h3>
<center>
<a href='?_src_=prefs;preference=facial_style_name;task=input'>
[get_pref(/datum/preference_setting/string/f_style)]
</a><br>
<a href='?_src_=prefs;preference=facial_style_name;task=previous_facehair_style'>&lt;</a>
<a href='?_src_=prefs;preference=facial_style_name;task=next_facehair_style'>&gt;</a><br>
<span style='border: 1px solid #161616; background-color: #[num2hex(get_pref(/datum/preference_setting/numerical/r_facial), 2)][num2hex(get_pref(/datum/preference_setting/numerical/g_facial), 2)][num2hex(get_pref(/datum/preference_setting/numerical/b_facial), 2)];'>&nbsp;&nbsp;&nbsp;</span>
<a href='?_src_=prefs;preference=facial_style_name;task=input_facial_hair_color'>Change</a><br>
</center>
</td>
<td valign='top' width='21%'>
<h3>Eye Color</h3>
<center>
<span style='border: 1px solid #161616; background-color: #[num2hex(get_pref(/datum/preference_setting/numerical/r_eyes), 2)][num2hex(get_pref(/datum/preference_setting/numerical/g_eyes), 2)][num2hex(get_pref(/datum/preference_setting/numerical/b_eyes), 2)];'>&nbsp;&nbsp;&nbsp;</span>
<a href='?_src_=prefs;preference=eyes_red;task=input'>Change</a><br>
</center>
</td>
</tr>
</table>
<b>Handicaps:</b>
<a href='?src=\ref[user];task=input;preference=disabilities'>Set</a><br>
<b>Limbs:</b>
<a href='?_src_=prefs;subsection=limbs;task=menu'>Set</a><br>
<b>Organs:</b>
<a href='?_src_=prefs;subsection=organs;task=menu'>Set</a><br>
<b>Underwear:</b>
<a href ='?_src_=prefs;preference=underwear;task=input'>[get_pref(/datum/preference_setting/enum/gender) == MALE ? "[underwear_m[get_pref(/datum/preference_setting/numerical/underwear)]]" : "[underwear_f[get_pref(/datum/preference_setting/numerical/underwear)]]"]
</a>
<br>
<b>Backpack:</b>
<a href='?_src_=prefs;preference=backbag;task=input'>
[backbaglist[get_pref(/datum/preference_setting/numerical/backbag)]]
</a><br>
<b>Nanotrasen Relation:</b>
<a href='?_src_=prefs;preference=nanotrasen_relation;task=input'>
[get_pref(/datum/preference_setting/enum/string/nanotrasen_relation)]
</a><br>
<b>Flavor Text:</b>
<a href='?src=\ref[user];preference=flavor_text;task=input'>Set</a><br>
<b>Character records:</b>
[jobban_isbanned(user, "Records") ? "Banned" : "<a href='?src=\ref[user];preference=records;record=1'>Set</a>"]<br>
<b>Bank account security preference:</b>
<a href='?_src_=prefs;preference=bank_security;task=input'>
[bank_security_num2text(get_pref(/datum/preference_setting/enum/bank_security))]
</a><br>
<b>Percent of wages sent to ID virtual wallet:</b>
<a href='?_src_=prefs;preference=wage_ratio;task=input'>
[get_pref(/datum/preference_setting/numerical/wage_ratio)]
</a><br>
"}
return dat
/datum/preferences/proc/setup_UI(var/dat, var/user)
dat += {"<b>UI Style:</b> <a href='?_src_=prefs;preference=UI_style;task=input'><b>[get_pref(/datum/preference_setting/string/UI_style)]</b></a><br>
<b>Custom UI</b>(recommended for White UI): <span style='border:1px solid #161616; background-color: #[get_pref(/datum/preference_setting/string/UI_style_color)];'>&nbsp;&nbsp;&nbsp;</span><br>Color: <a href='?_src_=prefs;preference=UIcolor'><b>[get_pref(/datum/preference_setting/string/UI_style_color)]</b></a><br>
Alpha(transparency): <a href='?_src_=prefs;preference=UI_style_alpha;task=input'><b>[get_pref(/datum/preference_setting/numerical/UI_style_alpha)]</b></a><br>
"}
return dat
/datum/preferences/proc/setup_special(var/dat, var/mob/user)
if(user.client.holder)
dat += {"
<h1><font color=red>Admin Only Settings</font></h1>
<div id="container" style="border:1px solid #000; width:96%; padding-left:2%; padding-right:2%; overflow:auto; padding-top:5px; padding-bottom:5px;">
<div id="leftDiv" style="width:50%;height:100%;float:left;">
<b>Toggle Adminhelp Sound</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[SOUND_ADMINHELP]'><b>[get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_ADMINHELP ? "Enabled" : "Disabled"]</b></a><br>
<b>Toggle Prayers</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_PRAYER]'><b>[get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_PRAYER ? "Enabled" : "Disabled"]</b></a><br>
<b>Toggle Hear Radio</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_GHOSTRADIO]'><b>[get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTRADIO ? "Enabled" : "Disabled"]</b></a><br>
</div>
<div id="rightDiv" style="width:50%;height:100%;float:right;">
<b>Toggle Attack Logs</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_ATTACKLOGS]'><b>[get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_ATTACKLOGS ? "Enabled" : "Disabled"]</b></a><br>
<b>Toggle Debug Logs</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_DEBUGLOGS]'><b>[get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEBUGLOGS ? "Enabled" : "Disabled"]</b></a><br>
<b>De-admin on login</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[AUTO_DEADMIN]'><b>[get_pref(/datum/preference_setting/binary_flag/toggles) & AUTO_DEADMIN ? "Enabled" : "Disabled"]</b></a><br>
</div>
</div>"}
dat += {"
<h1>General Settings</h1>
<div id="container" style="border:1px solid #000; width:96; padding-left:2%; padding-right:2%; overflow:auto; padding-top:5px; padding-bottom:5px;">
<div id="leftDiv" style="width:50%;height:100%;float:left;">
<b>FPS:</b>
<a href='?_src_=prefs;preference=fps;task=input'><b>[get_pref(/datum/preference_setting/numerical/fps)]</b></a><br>
<b>Space Parallax:</b>
<a href='?_src_=prefs;preference=space_parallax;task=input'><b>[get_pref(/datum/preference_setting/toggle/space_parallax) ? "Enabled" : "Disabled"]</b></a><br>
<b>Parallax Speed:</b>
<a href='?_src_=prefs;preference=parallax_speed;task=input'><b>[get_pref(/datum/preference_setting/numerical/parallax_speed) ]</b></a><br>
<b>Space Dust:</b>
<a href='?_src_=prefs;preference=space_dust;task=input'><b>[get_pref(/datum/preference_setting/toggle/space_dust) ? "Yes" : "No"]</b></a><br>
<b>Play admin midis:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[SOUND_MIDI]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>
<b>Play lobby music:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[SOUND_LOBBY]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_LOBBY) ? "Yes" : "No"]</b></a><br>
<b>Play Ambience:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[SOUND_AMBIENCE]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_AMBIENCE) ? "Yes" : "No"]</b></a><br>
[(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_AMBIENCE)? \
"<b>Ambience Volume:</b><a href='?_src_=prefs;preference=ambience_volume;task=input'><b>[get_pref(/datum/preference_setting/numerical/ambience_volume)]</b></a><br>":""]
<b>Radio Headset Sounds:</b>
<a href='?_src_=prefs;preference=headset_sound;task=input'><b>[headset_sound_text2num[get_pref(/datum/preference_setting/numerical/headset_sound)+1]]</b></a><br>
<b>Hear streamed media:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[SOUND_STREAMING]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_STREAMING) ? "Yes" : "No"]</b></a><br>
<b>Streaming Program:</b>
<a href='?_src_=prefs;preference=usewmp;task=input'><b>[get_pref(/datum/preference_setting/toggle/usewmp) ? "WMP (compatibility)" : "VLC (requires plugin)"]</b></a><br>
<b>Streaming Volume</b>
<a href='?_src_=prefs;preference=volume;task=input'><b>[get_pref(/datum/preference_setting/numerical/volume) ]</b></a><br>
<b>Hear player voices</b>
<a href='?_src_=prefs;preference=hear_voicesound;task=input'><b>[(get_pref(/datum/preference_setting/toggle/hear_voicesound)) ? "Yes" : "No"]</b></a><br>
<b>Hear instruments</b>
<a href='?_src_=prefs;preference=hear_instruments;task=input'><b>[(get_pref(/datum/preference_setting/toggle/hear_instruments)) ? "Yes":"No"]</b></a><br>
<b>Progress Bars:</b>
<a href='?_src_=prefs;preference=progress_bars;task=input'><b>[get_pref(/datum/preference_setting/toggle/progress_bars) ? "Yes" : "No"]</b></a><br>
<b>Pause after first step:</b>
<a href='?_src_=prefs;preference=stumble;task=input'><b>[get_pref(/datum/preference_setting/toggle/stumble) ? "Yes" : "No"]</b></a><br>
<b>Pulling action:</b>
<a href='?_src_=prefs;preference=pulltoggle;task=input'><b>[get_pref(/datum/preference_setting/toggle/pulltoggle) ? "Toggle Pulling" : "Always Pull"]</b></a><br>
<b>Solo Antag Objectives:</b>
<a href='?_src_=prefs;preference=antag_objectives;task=input'><b>[get_pref(/datum/preference_setting/toggle/antag_objectives) ? "Standard" : "Freeform"]</b></a><br>
<b>Say bubbles:</b>
<a href='?_src_=prefs;preference=typing_indicator;task=input'><b>[get_pref(/datum/preference_setting/toggle/typing_indicator) ? "Active" : "Inactive"]</b></a><br>
</div>
<div id="rightDiv" style="width:50%;height:100%;float:right;">
<b>Randomized Character Slot:</b>
<a href='?_src_=prefs;preference=randomslot;task=input'><b>[get_pref(/datum/preference_setting/toggle/randomslot) ? "Yes" : "No"]</b></a><br>
<b>Show Deadchat:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_DEAD]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD) ? "On" : "Off"]</b></a><br>
<b>Ghost Hearing:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_GHOSTEARS]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS) ? "All Speech" : "Nearby Speech"]</b></a><br>
<b>Ghost Sight:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_GHOSTSIGHT]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearby Emotes"]</b></a><br>
<b>Ghost Radio:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_GHOSTRADIO]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTRADIO) ? "All Chatter" : "Nearby Speakers"]</b></a><br>
<b>Ghost PDA:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_GHOSTPDA]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTPDA) ? "All PDA Messages" : "No PDA Messages"]</b></a><br>
<b>Show OOC:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_OOC]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_OOC) ? "Enabled" : "Disabled"]</b></a><br>
<b>Show LOOC:</b>
<a href='?_src_=prefs;preference=toggles;task=input;toggle=[CHAT_LOOC]'><b>[(get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_LOOC) ? "Enabled" : "Disabled"]</b></a><br>
<b>Show Tooltips:</b>
<a href='?_src_=prefs;preference=tooltips;task=input'><b>[get_pref(/datum/preference_setting/toggle/tooltips) ? "Yes" : "No"]</b></a><br>
<b>Adminhelp Special Tab:</b>
<a href='?_src_=prefs;preference=special;task=input'><b>[special_popup_text2num[get_pref(/datum/preference_setting/enum/special_popup)+1]]</b></a><br>
<b>Attack Animations:<b>
<a href='?_src_=prefs;preference=attack_animation;task=input'><b>[get_pref(/datum/preference_setting/enum/attack_animations) ? (get_pref(/datum/preference_setting/enum/attack_animations) == ITEM_ANIMATION? "Item Anim." : "Person Anim.") : "No"]</b></a><br>
<b>Show Credits <span title='&#39;No Reruns&#39; will roll credits only if an admin customized something about this round&#39;s credits, or if a rare and exclusive episode name was selected thanks to something uncommon happening that round.'>(?):</span><b>
<a href='?_src_=prefs;preference=credits;task=input'><b>[get_pref(/datum/preference_setting/enum/credits)]</b></a><br>
<b>Server Shutdown Jingle <span title='These jingles will only play if credits don&#39;t roll for you that round. &#39;Classics&#39; will only play &#39;APC Destroyed&#39; and &#39;Banging Donk&#39;, &#39;All&#39; will play the previous plus retro videogame sounds.'>(?):</span><b>
<a href='?_src_=prefs;preference=jingle;task=input'><b>[get_pref(/datum/preference_setting/enum/jingle)]</b></a><br>
<b>Credits/Jingle Volume:</b>
<a href='?_src_=prefs;preference=credits_volume;task=input'><b>[get_pref(/datum/preference_setting/numerical/credits_volume)]</b></a><br>
<b>Window Flashing</b>
<a href='?_src_=prefs;preference=window_flashing;task=input'><b>[get_pref(/datum/preference_setting/toggle/window_flashing) ? "Yes":"No"]</b></a><br>
<b>Fancy tgui:</b>
<a href='?_src_=prefs;preference=tgui_fancy;task=input'>[get_pref(/datum/preference_setting/toggle/tgui_fancy) ? "Enabled" : "Disabled"]</a><br>
<center>Runechat prefererences</center>
<b>Chat on map for mobs:</b>
<a href='?_src_=prefs;preference=mob_chat_on_map;task=input'>[get_pref(/datum/preference_setting/toggle/mob_chat_on_map) ? "Enabled" : "Disabled"]</a><br>
<b>Chat on map for objects:</b>
<a href='?_src_=prefs;preference=obj_chat_on_map;task=input'>[get_pref(/datum/preference_setting/toggle/obj_chat_on_map) ? "Enabled" : "Disabled"]</a><br>
<b>No goonchat messages for objects:</b>
<a href='?_src_=prefs;preference=no_goonchat_for_obj;task=input'>[get_pref(/datum/preference_setting/toggle/no_goonchat_for_obj) ? "Enabled" : "Disabled"]</a><br>
<b>Runechat message char limit:</b>
<a href='?_src_=prefs;preference=max_chat_length;task=input'>[get_pref(/datum/preference_setting/numerical/max_chat_length)]</a><br>
</div>
</div>"}
if(config.allow_Metadata)
dat += "<b>OOC Notes:</b> <a href='?_src_=prefs;preference=metadata;task=input'> Edit </a><br>"
return dat
/datum/preferences/proc/getPrefLevelText(var/datum/job/job)
var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs)
switch(jobs[job.title])
if(JOB_PREF_HIGH)
return "High"
if(JOB_PREF_MED)
return "Medium"
if(JOB_PREF_LOW)
return "Low"
return "NEVER"
/datum/preferences/proc/SetJobsChoice(mob/user, limit = 16, list/splitJobs = list("Chief Engineer", "Head of Security"), widthPerColumn = 295, height = 620)
if(!job_master)
return
//limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice.
//splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice.
//width - Screen' width. Defaults to 550 to make it look nice.
//height - Screen's height. Defaults to 500 to make it look nice.
var/width = widthPerColumn
var/HTML = "<link href='./common.css' rel='stylesheet' type='text/css'><body>"
HTML += {"<script type='text/javascript'>function setJobPrefRedirect(level, rank) { window.location.href='?_src_=prefs;preference=jobs;task=input;level=' + level + ';text=' + encodeURIComponent(rank); return false; }
function mouseDown(event,rank){
return false;
}
function mouseUp(event,rank){
if(event.button == 0 || event.button == 1)
{
setJobPrefRedirect(1, rank);
return false;
}
if(event.button == 2)
{
setJobPrefRedirect(0, rank);
return false;
}
return true;
}
</script>"} //the event.button == 1 check is brought to you by legacy IE running in wine
HTML += {"<center>
<b>Choose occupation chances</b><br>
<div align='center'>Left-click to raise an occupation preference, right-click to lower it.<br><div>
<a href='?_src_=prefs;preference=jobs;task=close'>Done</a></center><br>
<table width='100%' cellpadding='1' cellspacing='0'><tr><td width='20%'>
<table width='100%' cellpadding='1' cellspacing='0'>"}
var/index = -1
//The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows.
var/datum/job/lastJob
if (!job_master)
return
for(var/datum/job/job in job_master.occupations)
index += 1
if((index >= limit) || (job.title in splitJobs))
width += widthPerColumn
if((index < limit) && (lastJob != null))
//If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with
//the last job's selection color. Creating a rather nice effect.
for(var/i = 0, i < (limit - index), i += 1)
HTML += "<tr bgcolor='[lastJob.selection_color]'><td width='60%' align='right'>&nbsp</td><td>&nbsp</td></tr>"
HTML += "</table></td><td width='20%'><table width='100%' cellpadding='1' cellspacing='0'>"
index = 0
HTML += "<tr bgcolor='[job.selection_color]'><td width='60%' align='right'>"
var/rank = job.title
lastJob = job
if(jobban_isbanned(user, rank))
HTML += "<font color=red>[rank]</font></td><td><font color=red><b> \[BANNED]</b></font></td></tr>"
continue
if(!job.player_old_enough(user.client))
var/available_in_days = job.available_in_days(user.client)
HTML += "<font color=red>[rank]</font></td><td><font color=red> \[IN [(available_in_days)] DAYS]</font></td></tr>"
continue
if((rank in command_positions) || (rank == "AI"))//Bold head jobs
if(job.alt_titles)
HTML += "<b><span class='dark'><a href=\"byond://?src=\ref[user];preference=jobs;task=alt_title;job=\ref[job]\">[GetPlayerAltTitle(job)]</a></span></b>"
else
HTML += "<b><span class='dark'>[rank]</span></b>"
else
if(job.alt_titles)
HTML += "<span class='dark'><a href=\"byond://?src=\ref[user];preference=jobs;task=alt_title;job=\ref[job]\">[GetPlayerAltTitle(job)]</a></span>"
else
HTML += "<span class='dark'>[rank]</span>"
HTML += "</td><td width='40%'>"
var/prefLevelLabel = "NEVER"
var/prefLevelColor = "red"
var/species = get_pref(/datum/preference_setting/string/species)
var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs)
if(job.species_whitelist.len && !job.species_whitelist.Find(species))
prefLevelLabel = "Unavailable"
prefLevelColor = "gray"
else if(job.species_blacklist.Find(species))
prefLevelLabel = "Unavailable"
prefLevelColor = "gray"
else
switch(jobs[job.title])
if(JOB_PREF_HIGH)
prefLevelLabel = "High"
prefLevelColor = "slateblue"
if(JOB_PREF_MED)
prefLevelLabel = "Medium"
prefLevelColor = "green"
if(JOB_PREF_LOW)
prefLevelLabel = "Low"
prefLevelColor = "orange"
HTML += "<a class='white' onmouseup='javascript:return mouseUp(event, \"[rank]\");' oncontextmenu='javascript:return mouseDown(event, \"[rank]\");'>"
HTML += "<font color=[prefLevelColor]>[prefLevelLabel]</font>"
HTML += "</a></td></tr>"
for(var/i = 1, i < (limit - index), i += 1)
HTML += "<tr bgcolor='[lastJob.selection_color]'><td width='60%' align='right'>&nbsp</td><td>&nbsp</td></tr>"
HTML += {"</td'></tr></table>
</center></table>"}
var/alternate_option = get_pref(/datum/preference_setting/enum/alternate_option)
switch(alternate_option)
if(GET_EMPTY_JOB)
HTML += "<center><br><a href='?_src_=prefs;preference=jobs;task=random'>Get unique job</a></center><br>"
if(GET_RANDOM_JOB)
HTML += "<center><br><a href='?_src_=prefs;preference=jobs;task=random'>Get random job if preferences unavailable</a></center><br>"
if(BE_ASSISTANT)
HTML += "<center><br><a href='?_src_=prefs;preference=jobs;task=random'>Be assistant if preference unavailable</a></center><br>"
if(RETURN_TO_LOBBY)
HTML += "<center><br><a href='?_src_=prefs;preference=jobs;task=random'>Return to lobby if preference unavailable</a></center><br>"
HTML += {"<center><a href='?_src_=prefs;preference=jobs;task=reset'>Reset</a></center>
</tt>"}
user << browse(null, "window=preferences")
//user << browse(HTML, "window=mob_occupation;size=[width]x[height]")
var/datum/browser/popup = new(user, "mob_occupation", "<div align='center'>Occupation Preferences</div>", width, height)
popup.set_content(HTML)
popup.open(0)
return
/datum/preferences/proc/ShowChoices(mob/user)
if(!user || !user.client)
return
update_preview_icon()
var/preview_front = fcopy_rsc(preview_icon_front)
var/preview_side = fcopy_rsc(preview_icon_side)
user << browse_rsc(preview_front, "previewicon.png")
user << browse_rsc(preview_side, "previewicon2.png")
var/dat = "<html><link href='./common.css' rel='stylesheet' type='text/css'><body>"
if(!IsGuestKey(user.key))
dat += {"<center>
Slot <b>[slot_name]</b> -
<a href=\"byond://?_src_=prefs;action=open_load_dialog\">Load slot</a> -
<a href=\"byond://?_src_=prefs;action=save\">Save slot</a> -
<a href=\"byond://?_src_=prefs;action=reload\">Reload slot</a>
</center><hr>"}
else
dat += "Please create an account to save your preferences."
dat += "<center><a href='?_src_=prefs;action=tab;tab=0' [current_tab == CHARACTER_SETUP ? "class='linkOn'" : ""]>Character Settings</a> | "
dat += "<a href='?_src_=prefs;action=tab;tab=1' [current_tab == UI_SETUP ? "class='linkOn'" : ""]>UI Settings</a> | "
dat += "<a href='?_src_=prefs;action=tab;tab=2' [current_tab == GENERAL_SETUP ? "class='linkOn'" : ""]>General Settings</a> | "
dat += "<a href='?_src_=prefs;action=tab;tab=3' [current_tab == SPECIAL_ROLES_SETUP ? "class='linkOn'" : ""]>Special Roles</a></center><br>"
if(appearance_isbanned(user))
dat += "<b>You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.</b><br>"
switch(current_tab)
if(CHARACTER_SETUP)
dat = setup_character_options(dat, user)
if(UI_SETUP)
dat = setup_UI(dat, user)
if(GENERAL_SETUP)
dat = setup_special(dat, user)
if(SPECIAL_ROLES_SETUP)
dat = configure_special_roles(dat, user)
dat += "<div style='float:none;'><br><hr><center>"
if(!IsGuestKey(user.key))
dat += {"<a href='?_src_=prefs;action=load'>Undo</a> |
<a href='?_src_=prefs;action=save'>Save Setup</a> | "}
dat += {"<a href='?_src_=prefs;action=reset_all'>Reset Setup</a>
</center></div></body></html>"}
//user << browse(HTML_SKELETON(dat), "window=preferences;size=560x580")
var/datum/browser/popup = new(user, "preferences", "<div align='center'>Character Setup</div>", 680, 720)
popup.set_content(dat)
popup.open(0)
/datum/preferences/proc/ShowDisabilityState(mob/user,flag,label)
var/species = get_pref(/datum/preference_setting/string/species)
var/disabilities = get_pref(/datum/preference_setting/binary_flag/disabilities)
if(flag==DISABILITY_FLAG_FAT && species!="Human")
return "<li><i>[species] cannot be fat.</i></li>"
return "<li><b>[label]:</b> <a href=\"?_src_=prefs;task=input;preference=disabilities;disability=[flag]\">[disabilities & flag ? "Yes" : "No"]</a></li>"
/datum/preferences/proc/SetDisabilities(mob/user)
var/HTML = "<body>"
HTML += {"<tt><center>
<b>Choose disabilities</b><ul>"}
HTML += ShowDisabilityState(user,DISABILITY_FLAG_NEARSIGHTED,"Needs Glasses")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_FAT, "Obese")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_EPILEPTIC, "Seizures")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_DEAF, "Deaf")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_BLIND, "Blind")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_MUTE, "Mute")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_VEGAN, "Vegan")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_ASTHMA, "Asthma")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_LACTOSE, "Lactose Intolerant")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_LISP, "Lisp")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_ANEMIA, "Anemia")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_EHS, "Electromagnetic Hypersensitivity")
/*HTML += ShowDisabilityState(user,DISABILITY_FLAG_COUGHING, "Coughing")
HTML += ShowDisabilityState(user,DISABILITY_FLAG_TOURETTES, "Tourettes") Still working on it! -Angelite*/
HTML += {"</ul>
<a href=\"?_src_=prefs;task=close;preference=disabilities\">\[Done\]</a>
<a href=\"?_src_=prefs;task=reset;preference=disabilities\">\[Reset\]</a>
</center></tt>"}
user << browse(null, "window=preferences")
user << browse(HTML_SKELETON(HTML), "window=disabil;size=350x300")
return
/datum/preferences/proc/SetRecords(mob/user)
var/HTML = ""
HTML += {"<tt><center>
<b>Set Character Records</b><br>
<a href=\"byond://?src=\ref[user];preference=records;task=med_record\">Medical Records</a><br>"}
var/med_record = get_pref(/datum/preference_setting/string/med_record)
if(length(med_record) <= 40)
HTML += "[med_record]"
else
HTML += "[copytext(med_record, 1, 37)]..."
HTML += "<br><br><a href=\"byond://?src=\ref[user];preference=records;task=gen_record\">Employment Records</a><br>"
var/gen_record = get_pref(/datum/preference_setting/string/gen_record)
if(length(gen_record) <= 40)
HTML += "[gen_record]"
else
HTML += "[copytext(gen_record, 1, 37)]..."
HTML += "<br><br><a href=\"byond://?src=\ref[user];preference=records;task=sec_record\">Security Records</a><br>"
var/sec_record = get_pref(/datum/preference_setting/string/sec_record)
if(length(sec_record) <= 40)
HTML += "[sec_record]<br>"
else
HTML += "[copytext(sec_record, 1, 37)]...<br>"
HTML += {"<br>
<a href=\"byond://?src=\ref[user];preference=records;records=-1\">\[Done\]</a>
</center></tt>"}
user << browse(null, "window=preferences")
user << browse(HTML_SKELETON(HTML), "window=records;size=350x300")
return
/datum/preferences/proc/open_load_dialog(mob/user)
var/database/query/q = new
var/list/name_list[MAX_SAVE_SLOTS]
message_admins("open load dialog for [user]")
q.Add("select real_name, player_slot from players where player_ckey=?", user.ckey)
if(q.Execute(db))
while(q.NextRow())
name_list[q.GetColumn(2)] = q.GetColumn(1)
else
message_admins("Error in open_load_dialog [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
warning("Error in open_load_dialog [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
var/dat = "<center><b>Select a character slot to load</b><hr>"
var/counter = 1
while(counter <= MAX_SAVE_SLOTS)
if(counter==get_pref(/datum/preference_setting/numerical/default_slot))
dat += "<a href='?_src_=prefs;action=changeslot;num=[counter];'><b>[name_list[counter]]</b></a><br>"
else
if(!name_list[counter])
dat += "<a href='?_src_=prefs;action=changeslot;num=[counter];'>Character[counter]</a><br>"
else
dat += "<a href='?_src_=prefs;action=changeslot;num=[counter];'>[name_list[counter]]</a><br>"
counter++
dat += "</center>"
var/datum/browser/browser = new(user, "saves", null, 300, 340)
browser.set_content(dat)
browser.open(use_onclose=FALSE)
/datum/preferences/proc/close_load_dialog(mob/user)
user << browse(null, "window=saves")
/datum/preferences/proc/configure_special_roles(var/dat, var/mob/user)
dat+={"
<h1>Special Role Preferences</h1>
<p>Please note that this also handles in-round polling for things like Raging Mages and Borers.</p>
<fieldset>
<legend>Legend</legend>
<dl>
<dt>Never:</dt>
<dd>Decline this role for this round and all future rounds. You will not be polled again.</dd>
<dt>No:</dt>
<dd>Default. Decline this role for this round only.</dd>
<dt>Yes:</dt>
<dd>Accept this role for this round only.</dd>
<dt>Always:</dt>
<dd>Accept this role for this round and all future rounds. You will not be polled again.</dd>
</dl>
</fieldset>
<div id="container" style="overflow:auto;">
"}
for(var/list/table_type in list(antag_roles,nonantag_roles))
dat += {"
<div id="[table_type == antag_roles ? "left" : "right"]Div" style="width:50%;float:[table_type == antag_roles ? "left" : "right"];">
<table border=\"0\" padding-left = 20px;>
<tr><th colspan='6' height = '60px' valign='bottom'><h1>[table_type == nonantag_roles ? "Non-" : ""]Antagonist Roles</h1></th></tr>
"}
if(table_type == antag_roles && isantagbanned(user))
dat += "<th colspan='6' text-align = 'center' height = '40px'><h1>You are banned from antagonist roles</h1></th>"
else
for(var/role_id in table_type)
dat += "<tr><td>[capitalize(role_id)]</td>"
if(table_type[role_id]) //if mode is available on the server
if(jobban_isbanned(user, role_id) || (role_id == "pai candidate" && jobban_isbanned(user, "pAI")) || (role_id == MALF && jobban_isbanned(user, "AI")))
dat += "<td class='bannedColumn' colspan='5'><b>\[BANNED]</b></td>"
else
var/wikiroute = role_wiki[role_id]
var/desire = get_role_desire_str(roles[role_id])
dat += {"<td>[wikiroute ? "<a HREF='?src=\ref[user];getwiki=[wikiroute]'>(Wiki)</a>" : "<s>(Wiki)</s>"]</td>
<td><a class = 'fullsize clmNeverO[desire == "Never" ? "n" : "ff"]' href='?_src_=prefs;preference=set_roles;[role_id]=[ROLEPREF_NEVER|ROLEPREF_SAVE];'>Never</a></td>
<td><a class = 'fullsize clmNoO[desire == "No" ? "n" : "ff"]' href='?_src_=prefs;preference=set_roles;[role_id]=[ROLEPREF_NO|ROLEPREF_SAVE];'>No</a></td>
<td><a class = 'fullsize clmYesO[desire == "Yes" ? "n" : "ff"]' href='?_src_=prefs;preference=set_roles;[role_id]=[ROLEPREF_YES|ROLEPREF_SAVE];'>Yes</a></td>
<td><a class = 'fullsize clmAlwaysO[desire == "Always" ? "n" : "ff"]' href='?_src_=prefs;preference=set_roles;[role_id]=[ROLEPREF_ALWAYS|ROLEPREF_SAVE];'>Always</a></td>
</tr>"}
dat += "</table></div>"
dat += "</div><br>"
return dat

View File

@@ -122,16 +122,20 @@
var/limb_internal_state = state_data["internal_name"]
prefs.organ_data[limb_internal_name] = limb_internal_state
prefs.change_pref_datum_limb(limb_internal_name, limb_internal_state)
switch(state_data["mode"])
if(LIMB_MODE_AFFECT_CHILD)
var/child_limb = configurable_limb_data["child_limb"]
if(child_limb)
prefs.organ_data[child_limb] = limb_internal_state
prefs.change_pref_datum_limb(child_limb, limb_internal_state)
if(LIMB_MODE_AFFECT_PARENT)
var/parent_limb = configurable_limb_data["parent_limb"]
if(parent_limb)
prefs.organ_data[parent_limb] = limb_internal_state
prefs.change_pref_datum_limb(parent_limb, limb_internal_state)
return TRUE

View File

@@ -62,6 +62,7 @@
var/organ_internal_state = valid_states[target_state]
prefs.organ_data[organ_internal_name] = organ_internal_state
prefs.change_pref_datum_limb(organ_internal_name, organ_internal_state)
return TRUE
/datum/preferences_subsection/organs/process_link(var/mob/user, var/list/href_list)

View File

@@ -1,551 +0,0 @@
/// IF YOU NEED A FIELD ADDED TO THE DATABASE, CREATE A MIGRATION SO SHIT GETS UPDATED.
/// Also update SQL/players2.sql.
/// SEE code/modules/migrations/SS13_Prefs/
/datum/preferences/proc/SetChangelog(ckey,hash)
lastchangelog=hash
var/database/query/q = new
q.Add("UPDATE client SET lastchangelog=? WHERE ckey=?",lastchangelog,ckey)
if(!q.Execute(db))
message_admins("Error in SetChangelog [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in Setchangelog [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
/datum/preferences/proc/load_preferences_sqlite(var/ckey)
var/list/preference_list_client = new
var/database/query/check = new
var/database/query/q = new
check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
if(check.Execute(db))
if(!check.NextRow())
message_admins("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
else
message_admins("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
q.Add("SELECT * FROM client WHERE ckey = ?", ckey)
if(q.Execute(db))
while(q.NextRow())
var/list/row = q.GetRowData()
for(var/a in row)
preference_list_client[a] = row[a]
else
message_admins("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in load_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
ooccolor = preference_list_client["ooc_color"]
lastchangelog = preference_list_client["lastchangelog"]
UI_style = preference_list_client["UI_style"]
default_slot = text2num(preference_list_client["default_slot"])
toggles = text2num(preference_list_client["toggles"])
UI_style_color = preference_list_client["UI_style_color"]
UI_style_alpha = text2num(preference_list_client["UI_style_alpha"])
warns = text2num(preference_list_client["warns"])
warnbans = text2num(preference_list_client["warnsbans"])
volume = text2num(preference_list_client["volume"])
usewmp = text2num(preference_list_client["usewmp"])
special_popup = text2num(preference_list_client["special"])
randomslot = text2num(preference_list_client["randomslot"])
usenanoui = text2num(preference_list_client["usenanoui"])
tooltips = text2num(preference_list_client["tooltips"])
progress_bars = text2num(preference_list_client["progress_bars"])
space_parallax = text2num(preference_list_client["space_parallax"])
space_dust = text2num(preference_list_client["space_dust"])
parallax_speed = text2num(preference_list_client["parallax_speed"])
stumble = text2num(preference_list_client["stumble"])
attack_animation= text2num(preference_list_client["attack_animation"])
pulltoggle = text2num(preference_list_client["pulltoggle"])
hear_voicesound = text2num(preference_list_client["hear_voicesound"])
hear_instruments = text2num(preference_list_client["hear_instruments"])
ambience_volume = text2num(preference_list_client["ambience_volume"])
headset_sound = text2num(preference_list_client["headset_sound"])
credits_volume = text2num(preference_list_client["credits_volume"])
credits = preference_list_client["credits"]
jingle = preference_list_client["jingle"]
window_flashing = text2num(preference_list_client["window_flashing"])
antag_objectives = text2num(preference_list_client["antag_objectives"])
typing_indicator = text2num(preference_list_client["typing_indicator"])
mob_chat_on_map = text2num(preference_list_client["mob_chat_on_map"])
max_chat_length = text2num(preference_list_client["max_chat_length"])
obj_chat_on_map = text2num(preference_list_client["obj_chat_on_map"])
no_goonchat_for_obj = text2num(preference_list_client["no_goonchat_for_obj"])
tgui_fancy = text2num(preference_list_client["tgui_fancy"])
show_warning_next_time = text2num(preference_list_client["show_warning_next_time"])
last_warned_message = preference_list_client["last_warned_message"]
warning_admin = preference_list_client["warning_admin"]
fps = preference_list_client["fps"]
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
UI_style = sanitize_inlist(UI_style, list("White", "Midnight","Orange","old"), initial(UI_style))
//be_special = sanitize_integer(be_special, 0, 65535, initial(be_special))
default_slot = sanitize_integer(default_slot, 1, MAX_SAVE_SLOTS, initial(default_slot))
toggles = sanitize_integer(toggles, 0, 131071, initial(toggles))
UI_style_color = sanitize_hexcolor(UI_style_color, initial(UI_style_color))
UI_style_alpha = sanitize_integer(UI_style_alpha, 0, 255, initial(UI_style_alpha))
randomslot = sanitize_integer(randomslot, 0, 1, initial(randomslot))
volume = sanitize_integer(volume, 0, 100, initial(volume))
usewmp = sanitize_integer(usewmp, 0, 1, initial(usewmp))
special_popup = sanitize_integer(special_popup, 0, 2, initial(special_popup))
usenanoui = sanitize_integer(usenanoui, 0, 1, initial(usenanoui))
progress_bars = sanitize_integer(progress_bars, 0, 1, initial(progress_bars))
space_parallax = sanitize_integer(space_parallax, 0, 1, initial(space_parallax))
space_dust = sanitize_integer(space_dust, 0, 1, initial(space_dust))
parallax_speed = sanitize_integer(parallax_speed, 0, 5, initial(parallax_speed))
stumble = sanitize_integer(stumble, 0, 1, initial(stumble))
attack_animation= sanitize_integer(attack_animation, 0, 65535, initial(attack_animation))
pulltoggle = sanitize_integer(pulltoggle, 0, 1, initial(pulltoggle))
credits = sanitize_inlist(credits, list(CREDITS_NEVER, CREDITS_ALWAYS, CREDITS_NO_RERUNS), initial(credits))
jingle = sanitize_inlist(jingle, list(JINGLE_NEVER, JINGLE_CLASSIC, JINGLE_ALL), initial(jingle))
hear_voicesound = sanitize_integer(hear_voicesound, 0, 1, initial(hear_voicesound))
hear_instruments = sanitize_integer(hear_instruments, 0, 1, initial(hear_instruments))
ambience_volume = sanitize_integer(ambience_volume, 0, 100, initial(ambience_volume))
headset_sound = sanitize_integer(headset_sound, 0, 2, initial(headset_sound))
credits_volume = sanitize_integer(credits_volume, 0, 100, initial(credits_volume))
window_flashing = sanitize_integer(window_flashing, 0, 1, initial(window_flashing))
antag_objectives = sanitize_integer(antag_objectives, 0, 1, initial(antag_objectives))
typing_indicator = sanitize_integer(typing_indicator, 0, 1, initial(typing_indicator))
mob_chat_on_map = sanitize_integer(mob_chat_on_map, 0, 1, initial(mob_chat_on_map))
max_chat_length = sanitize_integer(max_chat_length, 0, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length))
obj_chat_on_map = sanitize_integer(obj_chat_on_map, 0, 1, initial(obj_chat_on_map))
no_goonchat_for_obj = sanitize_integer(no_goonchat_for_obj, 0, 1, initial(no_goonchat_for_obj))
tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
show_warning_next_time = sanitize_integer(show_warning_next_time, 0, 1, initial(show_warning_next_time))
fps = sanitize_integer(fps, -1, 1000, initial(fps))
initialize_preferences()
return 1
/datum/preferences/proc/initialize_preferences(client_login = 0)
if(attack_animation == PERSON_ANIMATION)
person_animation_viewers |= client
item_animation_viewers -= client
else if(attack_animation == ITEM_ANIMATION)
item_animation_viewers |= client
person_animation_viewers -= client
else
item_animation_viewers -= client
person_animation_viewers -= client
/datum/preferences/proc/save_preferences_sqlite(var/user, var/ckey)
var/database/query/check = new
var/database/query/q = new
check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
if(check.Execute(db))
if(!check.NextRow())
q.Add("INSERT into client (ckey, ooc_color, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, headset_sound, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map, no_goonchat_for_obj, tgui_fancy, show_warning_next_time, last_warned_message, warning_admin, fps) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",\
ckey, ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, headset_sound, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map, no_goonchat_for_obj, tgui_fancy, show_warning_next_time, last_warned_message, warning_admin, fps)
if(!q.Execute(db))
message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
else
q.Add("UPDATE client SET ooc_color=?,lastchangelog=?,UI_style=?,default_slot=?,toggles=?,UI_style_color=?,UI_style_alpha=?,warns=?,warnbans=?,randomslot=?,volume=?,usewmp=?,special=?,usenanoui=?,tooltips=?,progress_bars=?,space_parallax=?,space_dust=?,parallax_speed=?, stumble=?, attack_animation=?, pulltoggle=?, credits=?, jingle=?, hear_voicesound=?, hear_instruments=?, ambience_volume=?, headset_sound=?, credits_volume=?, window_flashing=?, antag_objectives=? , typing_indicator=? , mob_chat_on_map=? , max_chat_length=?, obj_chat_on_map=?, no_goonchat_for_obj=?, tgui_fancy=?, show_warning_next_time=?, last_warned_message=?, warning_admin=?, fps=? WHERE ckey = ?",\
ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, headset_sound, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map,no_goonchat_for_obj, tgui_fancy, show_warning_next_time, last_warned_message, warning_admin, fps, ckey)
if(!q.Execute(db))
message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
else
message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Preferences Updated.")
lastPolled = world.timeofday
return 1
/datum/preferences/proc/load_save_sqlite(var/ckey, var/user, var/slot)
var/list/preference_list = new
var/database/query/q = new
var/database/query/check = new
check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot)
if(check.Execute(db))
if(!check.NextRow())
to_chat(user, "You have no character file to load, please save one first.")
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
return 0
else
message_admins("load_save_sqlite Check Error #: [check.Error()] - [check.ErrorMsg()]")
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
return 0
q.Add({"
SELECT
limbs.player_ckey,
limbs.player_slot,
limbs.l_arm,
limbs.r_arm,
limbs.l_leg,
limbs.r_leg,
limbs.l_foot,
limbs.r_foot,
limbs.l_hand,
limbs.r_hand,
limbs.heart,
limbs.eyes,
limbs.lungs,
limbs.liver,
limbs.kidneys,
players.player_ckey,
players.player_slot,
players.ooc_notes,
players.real_name,
players.random_name,
players.random_body,
players.gender,
players.age,
players.species,
players.language,
players.flavor_text,
players.med_record,
players.sec_record,
players.gen_record,
players.player_alt_titles,
players.disabilities,
players.nanotrasen_relation,
players.bank_security,
players.wage_ratio,
jobs.player_ckey,
jobs.player_slot,
jobs.alternate_option,
jobs.jobs,
body.player_ckey,
body.player_slot,
body.hair_red,
body.hair_green,
body.hair_blue,
body.facial_red,
body.facial_green,
body.facial_blue,
body.skin_tone,
body.hair_style_name,
body.facial_style_name,
body.eyes_red,
body.eyes_green,
body.eyes_blue,
body.underwear,
body.backbag
FROM
players
INNER JOIN
limbs
ON
(
players.player_ckey = limbs.player_ckey)
AND (
players.player_slot = limbs.player_slot)
INNER JOIN
jobs
ON
(
limbs.player_ckey = jobs.player_ckey)
AND (
limbs.player_slot = jobs.player_slot)
INNER JOIN
body
ON
(
jobs.player_ckey = body.player_ckey)
AND (
jobs.player_slot = body.player_slot)
WHERE
players.player_ckey = ?
AND players.player_slot = ? ;"}, ckey, slot)
if(q.Execute(db))
while(q.NextRow())
var/list/row = q.GetRowData()
for(var/a in row)
preference_list[a] = row[a]
else
message_admins("load_save_sqlite Error #: [q.Error()] - [q.ErrorMsg()]")
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
return 0
var/list/player_alt_list1 = new
var/list/player_alt_list2 = new()
player_alt_list1.Add(splittext(preference_list["player_alt_titles"], ";")) // we're getting the first part of the string for each job.
for(var/item in player_alt_list1) // iterating through the list
if(!findtext(item, ":"))
continue
var/delim_location = findtext(item, ":") // getting the second part of the string that will be handled for titles
var/job = copytext(item, 1, delim_location) // getting where the job is, it's in the first slot so we want to get that position.
var/title = copytext(item, delim_location + 1, 0) // getting where the job title is, it's in the second slot so we want to get that position.
player_alt_list2[job] = title // we assign the alt_titles here to specific job titles and hope everything works.
metadata = preference_list["ooc_notes"]
real_name = preference_list["real_name"]
be_random_name = text2num(preference_list["random_name"])
be_random_body = text2num(preference_list["random_body"])
gender = preference_list["gender"]
age = text2num(preference_list["age"])
species = preference_list["species"]
language = preference_list["language"]
flavor_text = preference_list["flavor_text"]
med_record = preference_list["med_record"]
sec_record = preference_list["sec_record"]
gen_record = preference_list["gen_record"]
player_alt_titles = player_alt_list2
disabilities = text2num(preference_list["disabilities"])
nanotrasen_relation = preference_list["nanotrasen_relation"]
bank_security = preference_list["bank_security"]
wage_ratio = preference_list["wage_ratio"]
r_hair = text2num(preference_list["hair_red"])
g_hair = text2num(preference_list["hair_green"])
b_hair = text2num(preference_list["hair_blue"])
h_style = preference_list["hair_style_name"]
r_facial = text2num(preference_list["facial_red"])
g_facial = text2num(preference_list["facial_green"])
b_facial = text2num(preference_list["facial_blue"])
f_style = preference_list["facial_style_name"]
r_eyes = text2num(preference_list["eyes_red"])
g_eyes = text2num(preference_list["eyes_green"])
b_eyes = text2num(preference_list["eyes_blue"])
s_tone = text2num(preference_list["skin_tone"])
underwear = text2num(preference_list["underwear"])
backbag = text2num(preference_list["backbag"])
organ_data[LIMB_LEFT_ARM] = preference_list[LIMB_LEFT_ARM]
organ_data[LIMB_RIGHT_ARM] = preference_list[LIMB_RIGHT_ARM]
organ_data[LIMB_LEFT_LEG] = preference_list[LIMB_LEFT_LEG]
organ_data[LIMB_RIGHT_LEG] = preference_list[LIMB_RIGHT_LEG]
organ_data[LIMB_LEFT_FOOT]= preference_list[LIMB_LEFT_FOOT]
organ_data[LIMB_RIGHT_FOOT]= preference_list[LIMB_RIGHT_FOOT]
organ_data[LIMB_LEFT_HAND]= preference_list[LIMB_LEFT_HAND]
organ_data[LIMB_RIGHT_HAND]= preference_list[LIMB_RIGHT_HAND]
organ_data["heart"] = preference_list["heart"]
organ_data["eyes"] = preference_list["eyes"]
organ_data["lungs"] = preference_list["lungs"]
organ_data["kidneys"]=preference_list["kidneys"]
organ_data["liver"] = preference_list["liver"]
alternate_option = text2num(preference_list["alternate_option"])
if(preference_list["jobs"] && preference_list["jobs"] != "")
jobs = json_decode(preference_list["jobs"])
else
jobs = list()
metadata = sanitize_text(metadata, initial(metadata))
real_name = reject_bad_name(real_name)
if(isnull(species))
species = "Human"
if(isnull(language))
language = "None"
if(isnull(nanotrasen_relation))
nanotrasen_relation = initial(nanotrasen_relation)
if(isnull(bank_security))
bank_security = initial(bank_security)
if(isnull(wage_ratio))
wage_ratio = initial(wage_ratio)
if(!real_name)
real_name = random_name(gender,species)
wage_ratio = clamp(wage_ratio,0,100)
be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body))
gender = sanitize_gender(gender)
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
r_hair = sanitize_integer(r_hair, 0, 255, initial(r_hair))
g_hair = sanitize_integer(g_hair, 0, 255, initial(g_hair))
b_hair = sanitize_integer(b_hair, 0, 255, initial(b_hair))
r_facial = sanitize_integer(r_facial, 0, 255, initial(r_facial))
g_facial = sanitize_integer(g_facial, 0, 255, initial(g_facial))
b_facial = sanitize_integer(b_facial, 0, 255, initial(b_facial))
s_tone = sanitize_integer(s_tone, -185, 34, initial(s_tone))
h_style = sanitize_inlist(h_style, hair_styles_list, initial(h_style))
f_style = sanitize_inlist(f_style, facial_hair_styles_list, initial(f_style))
r_eyes = sanitize_integer(r_eyes, 0, 255, initial(r_eyes))
g_eyes = sanitize_integer(g_eyes, 0, 255, initial(g_eyes))
b_eyes = sanitize_integer(b_eyes, 0, 255, initial(b_eyes))
underwear = sanitize_integer(underwear, 1, underwear_m.len, initial(underwear))
backbag = sanitize_integer(backbag, 1, backbaglist.len, initial(backbag))
//be_special = sanitize_integer(be_special, 0, 65535, initial(be_special))
alternate_option = sanitize_integer(alternate_option, 0, 2, initial(alternate_option))
for(var/role_id in special_roles)
roles[role_id]=0
q = new
q.Add("SELECT role, preference FROM client_roles WHERE ckey=? AND slot=?", ckey, slot)
if(q.Execute(db))
while(q.NextRow())
var/list/row = q.GetRowData()
roles[row["role"]] = text2num(row["preference"])
else
message_admins("Error in load_save_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("[__LINE__]: datum/preferences/load_save_sqlite has returned")
return 0
if(!skills)
skills = list()
if(!used_skillpoints)
used_skillpoints= 0
if(isnull(disabilities))
disabilities = 0
if(!player_alt_titles)
player_alt_titles = new()
if(!organ_data)
src.organ_data = list()
if(user)
to_chat(user, "Successfully loaded [real_name].")
return 1
/datum/preferences/proc/random_character_sqlite(var/user, var/ckey)
var/database/query/q = new
var/list/slot_list = new
q.Add("SELECT player_slot FROM players WHERE player_ckey=?", ckey)
if(q.Execute(db))
while(q.NextRow())
slot_list.Add(q.GetColumn(1))
else
message_admins("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in random_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
var/random_slot = pick(slot_list)
load_save_sqlite(ckey, user, random_slot)
return 1
/datum/preferences/proc/save_character_sqlite(var/ckey, var/user, var/slot)
if(slot > MAX_SAVE_SLOTS)
to_chat(user, "You are limited to 8 character slots.")
message_admins("[ckey] attempted to override character slot limit")
return 0
var/database/query/q = new
var/database/query/check = new
var/altTitles
// The FUCK is this shit
for(var/a in player_alt_titles)
altTitles += "[a]:[player_alt_titles[a]];"
check.Add("SELECT player_ckey FROM players WHERE player_ckey = ? AND player_slot = ?", ckey, slot)
if(check.Execute(db))
if(!check.NextRow())
q.Add("INSERT INTO players (player_ckey,player_slot,ooc_notes,real_name, random_name, gender, age, species, language, flavor_text, med_record, sec_record, gen_record, player_alt_titles, disabilities, nanotrasen_relation, bank_security, wage_ratio, random_body)\
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
ckey, slot, metadata, real_name, be_random_name, gender, age, species, language, flavor_text, med_record, sec_record, gen_record, altTitles, disabilities, nanotrasen_relation, bank_security, wage_ratio, be_random_body)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Created Character")
else
q.Add("UPDATE players SET ooc_notes=?,real_name=?,random_name=?, gender=?,age=?,species=?,language=?,flavor_text=?,med_record=?,sec_record=?,gen_record=?,player_alt_titles=?,disabilities=?,nanotrasen_relation=?,bank_security=?,wage_ratio=?,random_body=? WHERE player_ckey = ? AND player_slot = ?",\
metadata, real_name, be_random_name, gender, age, species, language, flavor_text, med_record, sec_record, gen_record, altTitles, disabilities, nanotrasen_relation, bank_security, wage_ratio, be_random_body, ckey, slot)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Updated Character")
else
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[check.Error()] - [check.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
check.Add("SELECT player_ckey FROM body WHERE player_ckey = ? AND player_slot = ?", ckey, slot)
if(check.Execute(db))
if(!check.NextRow())
q.Add("INSERT INTO body (player_ckey, player_slot, hair_red, hair_green, hair_blue, facial_red, facial_green, facial_blue, skin_tone, hair_style_name, facial_style_name, eyes_red, eyes_green, eyes_blue, underwear, backbag) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
ckey, slot, r_hair, g_hair, b_hair, r_facial, g_facial, b_facial, s_tone, h_style, f_style, r_eyes, g_eyes, b_eyes, underwear, backbag)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Created Body")
else
q.Add("UPDATE body SET hair_red=?, hair_green=?, hair_blue=?, facial_red=?, facial_green=?, facial_blue=?, skin_tone=?, hair_style_name=?, facial_style_name=?, eyes_red=?, eyes_green=?, eyes_blue=?, underwear=?, backbag=? WHERE player_ckey = ? AND player_slot = ?",\
r_hair, g_hair, b_hair, r_facial, g_facial, b_facial, s_tone, h_style, f_style, r_eyes, g_eyes, b_eyes, underwear, backbag, ckey, slot)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Updated Body")
else
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
check.Add("SELECT player_ckey FROM jobs WHERE player_ckey = ? AND player_slot = ?", ckey, slot)
if(check.Execute(db))
if(!check.NextRow())
// 1 2 3 4
q.Add("INSERT INTO jobs (player_ckey,player_slot,alternate_option,jobs) \
VALUES (?, ?, ?, ?)", \
ckey, slot, alternate_option,json_encode(jobs))
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Created Job list")
else
// 1 2
q.Add("UPDATE jobs SET alternate_option=?,jobs=? WHERE player_ckey = ? AND player_slot = ?",\
alternate_option, json_encode(jobs), ckey, slot)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Updated Job List")
else
message_admins("Error in save_character_sqlite ln [__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
WARNING("Error in save_character_sqlite ln [__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
check.Add("SELECT player_ckey FROM limbs WHERE player_ckey = ? AND player_slot = ?", ckey, slot)
if(check.Execute(db))
if(!check.NextRow())
q.Add("INSERT INTO limbs (player_ckey, player_slot) VALUES (?,?)", ckey, slot)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
for(var/stuff in organ_data)
q.Add("UPDATE limbs SET [stuff]=? WHERE player_ckey = ? AND player_slot = ?", organ_data[stuff], ckey, slot)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #; [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Created Limbs")
else
for(var/stuff in organ_data)
q.Add("UPDATE limbs SET [stuff] = ? WHERE player_ckey = ? AND player_slot = ?", organ_data[stuff], ckey, slot)
if(!q.Execute(db))
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
to_chat(user, "Updated Limbs")
else
message_admins("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #: [check.Error()] - [check.ErrorMsg()]")
WARNING("Error in save_character_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
return 0
for(var/role_id in roles)
if(!(roles[role_id] & ROLEPREF_SAVE))
continue
q = new
q.Add("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES (?,?,?,?)", ckey, slot, role_id, (roles[role_id] & ROLEPREF_VALMASK))
//testing("INSERT OR REPLACE INTO client_roles (ckey, slot, role, preference) VALUES ('[ckey]',[slot],'[role_id]',[roles[role_id] & ROLEPREF_VALMASK])")
if(!q.Execute(db)) // This never triggers on error, for some reason.
message_admins("ClientRoleInsert: Error #: [q.Error()] - [q.ErrorMsg()]")
WARNING("ClientRoleInsert: Error #:[q.Error()] - [q.ErrorMsg()]")
return 0
return 1

View File

@@ -160,7 +160,7 @@ var/global/datum/credits/end_credits = new
for(var/client/C in clients)
if(!C.prefs)
continue
switch(C.prefs.credits)
switch(C.prefs.get_pref(/datum/preference_setting/enum/credits))
if(CREDITS_ALWAYS)
C.credits_audio()
if(CREDITS_NO_RERUNS) //The time has come to decide. Shall we play credits audio, or preload the jingle audio instead?
@@ -171,7 +171,7 @@ var/global/datum/credits/end_credits = new
if(CREDITS_NEVER)
C.jingle_audio(preload_only = TRUE)
else
log_debug("[C] somehow had an unknown credits preference of: [C.prefs.credits]")
log_debug("[C] somehow had an unknown credits preference of: [C.prefs.get_pref(/datum/preference_setting/enum/credits)]")
/*
* on_world_reboot_end:

View File

@@ -6,49 +6,49 @@
src << output(end_credits.file, end_credits.control)
/client/proc/download_credits()
if(prefs.credits == CREDITS_NEVER)
if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NEVER)
return
if(!end_credits.finalized)
log_debug("[src] tried to download credits before the credits were ever finalized! Credits preference: [prefs.credits]")
log_debug("[src] tried to download credits before the credits were ever finalized! Credits preference: [prefs.get_pref(/datum/preference_setting/enum/credits)]")
return
src << output(list2params(end_credits.js_args), "[end_credits.control]:setupCredits")
received_credits = TRUE
/client/proc/play_downloaded_credits()
if(prefs.credits == CREDITS_NEVER)
if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NEVER)
return
if(prefs.credits == CREDITS_NO_RERUNS && end_credits.is_rerun())
if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NO_RERUNS && end_credits.is_rerun())
return
if(!received_credits)
log_debug("[src] tried to play credits without having ever received them! Credits preference: [prefs.credits]")
log_debug("[src] tried to play credits without having ever received them! Credits preference: [prefs.get_pref(/datum/preference_setting/enum/credits)]")
return
src << output("", "[end_credits.control]:startCredits") //Execute the startCredits() function in credits.html with no parameters.
/client/proc/credits_audio(var/preload_only = FALSE)
if(prefs.credits == CREDITS_NEVER)
if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NEVER)
return
if(preload_only)
src << output(list2params(list(end_credits.audio_link, FALSE)), "[end_credits.control]:setAudio")
received_roundend_audio = TRUE
else
if(prefs.credits == CREDITS_NO_RERUNS && end_credits.is_rerun())
if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NO_RERUNS && end_credits.is_rerun())
return
if(received_roundend_audio)
src << output("", "[end_credits.control]:startAudio") //Execute the playAudio() function in credits.html with no parameters.
else
src << output(list2params(list(end_credits.audio_link, TRUE)), "[end_credits.control]:setAudio")
src << output(list2params(list(prefs.credits_volume)), "[end_credits.control]:setVolume")
src << output(list2params(list(prefs.get_pref(/datum/preference_setting/numerical/credits_volume))), "[end_credits.control]:setVolume")
/client/proc/jingle_audio(var/preload_only = FALSE)
if(prefs.credits == CREDITS_ALWAYS)
if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_ALWAYS)
return
var/link = ""
switch(prefs.jingle)
switch(prefs.get_pref(/datum/preference_setting/enum/jingle))
if(JINGLE_NEVER)
return
if(JINGLE_CLASSIC)
@@ -56,19 +56,19 @@
if(JINGLE_ALL)
link = pick(end_credits.classic_roundend_jingles + end_credits.new_roundend_jingles)
if(!link)
log_debug("[src] somehow had a null jingle link! Jingle preference: [prefs.jingle]")
log_debug("[src] somehow had a null jingle link! Jingle preference: [prefs.get_pref(/datum/preference_setting/enum/jingle)]")
if(preload_only)
src << output(list2params(list(link, FALSE)), "[end_credits.control]:setAudio")
received_roundend_audio = TRUE
else
if(prefs.credits == CREDITS_NO_RERUNS && !end_credits.is_rerun())
if(prefs.get_pref(/datum/preference_setting/enum/credits) == CREDITS_NO_RERUNS && !end_credits.is_rerun())
return
if(received_roundend_audio)
src << output("", "[end_credits.control]:startAudio") //Execute the playAudio() function in credits.html with no parameters.
else
src << output(list2params(list(link, TRUE)), "[end_credits.control]:setAudio")
src << output(list2params(list(prefs.credits_volume)), "[end_credits.control]:setVolume")
src << output(list2params(list(prefs.get_pref(/datum/preference_setting/numerical/credits_volume))), "[end_credits.control]:setVolume")
/*
/client/verb/credits_debug()

View File

@@ -222,7 +222,8 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
if(flag && !player.client.desires_role(job.title))
Debug("FOC flag failed, Player: [player], Flag: [flag], ")
continue
if(player.client.prefs.jobs[job.title] == level)
var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs)
if(jobs[job.title] == level)
Debug("FOC pass, Player: [player], Level:[level]")
candidates += player
return candidates
@@ -306,7 +307,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
for(var/mob/new_player/player in player_list)
if(player.ready && player.mind && !player.mind.assigned_role)
unassigned += player
if(player.client.prefs.randomslot)
if(player.client.prefs.get_pref(/datum/preference_setting/toggle/randomslot))
player.client.prefs.random_character_sqlite(player, player.ckey)
Debug("DO, Len: [unassigned.len]")
if(unassigned.len == 0)
@@ -333,7 +334,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
// Loop through all unassigned players
for(var/mob/new_player/player in unassigned)
if(player.client.prefs.alternate_option == GET_EMPTY_JOB)
if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_EMPTY_JOB)
continue //This player doesn't want to share a job title. We need to deal with them last.
// Loop through all jobs
@@ -346,7 +347,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
// Also makes sure that they got their preference correct
for(var/mob/new_player/player in unassigned)
if(player.client.prefs.alternate_option == GET_RANDOM_JOB)
if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_RANDOM_JOB)
GiveRandomJob(player)
Debug("DO, Standard Check end")
@@ -359,7 +360,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
// For those who wanted to be assistant if their preferences were filled, here you go.
for(var/mob/new_player/player in unassigned)
if(player.client.prefs.alternate_option == BE_ASSISTANT)
if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == BE_ASSISTANT)
if(config.assistantlimit)
if(master_assistant.current_positions-FREE_ASSISTANTS_BRUT > (config.assistantratio * count)) // Not enough sec...
if(count < 5) // if theres more than 5 security on the station just let assistants join regardless, they should be able to handle the tide ; this block then doesn't get checked.
@@ -368,7 +369,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
unassigned -= player
continue
if(master_assistant.species_blacklist.len && master_assistant.species_blacklist.Find(player.client.prefs.species))
if(master_assistant.species_blacklist.len && master_assistant.species_blacklist.Find(player.client.prefs.get_pref(/datum/preference_setting/string/species)))
to_chat(player, "You have been returned to lobby because your species is blacklisted from assistant.")
player.ready = 0
unassigned -= player
@@ -410,7 +411,7 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
//Final pass - first deal with the empty job group, otherwise send any leftovers to the lobby
final_pass: //this is a loop label
for(var/mob/new_player/player in unassigned)
if(player.client.prefs.alternate_option == GET_EMPTY_JOB)
if(player.client.prefs.get_pref(/datum/preference_setting/enum/alternate_option) == GET_EMPTY_JOB)
for(var/level = 3 to 1 step -1)
for(var/datum/job/job in shuffledoccupations)
if(job.current_positions) //already someone in this job title
@@ -434,7 +435,8 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
Debug("DO player not old enough, Player: [player], Job:[job.title]")
return FALSE
// If the player wants that job on this level, then try give it to him.
if(player.client.prefs.jobs[job.title] == level)
var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs)
if(jobs[job.title] == level)
if (job.title == "Assistant" && !CheckAssistantCount(player, level))
return FALSE
// If the job isn't filled
@@ -453,8 +455,9 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
if(not_enough_sec && (count < 5))
Debug("AC1 failed, not enough sec.")
// Does he want anything else...?
var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs)
for (var/datum/job/J in occupations)
if (player.client.prefs.jobs[J.title] == level)
if (jobs[J.title] == level)
Debug("AC1 failed, but other job slots for [player]. Adding them to the list of backup assistant slots.")
assistant_second_chance[player.ckey] = level
return FALSE
@@ -475,9 +478,9 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
// Total between $200 and $500
var/balance_bank = rand(100,250)
var/balance_wallet = rand(100,250)
var/bank_pref_number = H.client.prefs.bank_security
var/bank_pref_number = H.client.prefs.get_pref(/datum/preference_setting/enum/bank_security)
var/bank_pref = bank_security_num2text(bank_pref_number)
var/pref_wage_ratio = H.client.prefs.wage_ratio
var/pref_wage_ratio = H.client.prefs.get_pref(/datum/preference_setting/numerical/wage_ratio)
if(centcomm_account_db)
var/wage = job.get_wage()
var/datum/money_account/M = create_account(H.real_name, balance_bank, null, wage_payout = wage, security_pref = bank_pref_number, ratio_pref = pref_wage_ratio)
@@ -588,7 +591,8 @@ var/global/alt_job_limit = 0 //list of alternate jobs available for new hires
if(!job.player_old_enough(player.client))
level6++
continue
switch(player.client.prefs.jobs[job.title])
var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs)
switch(jobs[job.title])
if(JOB_PREF_LOW)
level1++
if(JOB_PREF_MED)

View File

@@ -163,9 +163,9 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML
src.mob=holder
owner=src.mob.client
if(owner.prefs)
if(!isnull(owner.prefs.volume))
volume = owner.prefs.volume
if(owner.prefs.usewmp)
if(!isnull(owner.prefs.get_pref(/datum/preference_setting/numerical/volume)))
volume = owner.prefs.get_pref(/datum/preference_setting/numerical/volume)
if(owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp))
playerstyle = PLAYER_OLD_HTML
else
playerstyle = PLAYER_HTML
@@ -186,11 +186,11 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML
/datum/media_manager/proc/send_update(var/target_url)
if(!(owner.prefs))
return
if(!(owner.prefs.toggles & SOUND_STREAMING) && target_url != "")
if(!(owner.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & SOUND_STREAMING) && target_url != "")
return // Nope.
MP_DEBUG("<span class='good'>Sending update to media player ([target_url])...</span>")
var/window_playing
if(owner.prefs.usewmp)
if(owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp))
stop_music()
MP_DEBUG("<span class='good'>WMP user, no switching, going to even window.<span>")
currently_broadcasting = JUKEBOX_EVEN_PLAYER
@@ -226,7 +226,7 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML
/datum/media_manager/proc/push_music(var/targetURL,var/targetStartTime,var/targetVolume)
var/current_url
if (owner && owner.prefs.usewmp)
if (owner && owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp))
current_url = url_even
else
switch (currently_broadcasting)
@@ -266,7 +266,7 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML
var/obj/machinery/media/M = A.media_source // TODO: turn into a list, then only play the first one that's playing.
var/current_url
if (owner.prefs.usewmp) // WMP only uses the even broadcaster
if (owner.prefs.get_pref(/datum/preference_setting/toggle/usewmp)) // WMP only uses the even broadcaster
current_url = url_even
else
switch (currently_broadcasting)
@@ -311,12 +311,13 @@ var/const/PLAYER_OLD_HTML=PLAYER_HTML
/client/proc/set_new_volume()
if(!media || !istype(media))
to_chat(usr, "You have no media datum to change, if you're not in the lobby tell an admin.")
to_chat(usr, "<span class='warning'>You have no media datum to change, if you're not in the lobby tell an admin.</span>")
return
var/oldvolume = prefs.volume
var/oldvolume = prefs.get_pref(/datum/preference_setting/numerical/volume)
var/value = input("Choose your Jukebox volume.", "Jukebox volume", media.volume)
value = round(max(0, min(100, value)))
media.update_volume(value)
if(prefs && (oldvolume != value))
prefs.volume = value
var/datum/preference_setting/volume_pref = prefs.get_pref_datum(/datum/preference_setting/numerical/volume)
volume_pref.setting = value
prefs.save_preferences_sqlite(src, ckey)

View File

@@ -577,7 +577,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
sHuman.real_name = real_name
concrete_outfit.equip(sHuman, TRUE)
client?.prefs.copy_to(sHuman)
sHuman.add_language(client?.prefs.language)
sHuman.add_language(client?.prefs.get_pref(/datum/preference_setting/string/language))
sHuman.dna.UpdateSE()
sHuman.dna.UpdateUI()
sHuman.ckey = ckey

View File

@@ -41,17 +41,17 @@
if (get_dist(source_turf, src) <= get_view_range())
rendered_speech = "<B>[rendered_speech]</B>"
if (client?.prefs.mob_chat_on_map && (client.prefs.obj_chat_on_map || ismob(speech.speaker)))
if (client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && (client.prefs.get_pref(/datum/preference_setting/toggle/obj_chat_on_map) || ismob(speech.speaker)))
create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes)
else
if(client && client.prefs)
if (!speech.frequency)
if ((client.prefs.toggles & CHAT_GHOSTEARS) != CHAT_GHOSTEARS)
say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTEARS is disabled, blocking. ([client.prefs.toggles] & [CHAT_GHOSTEARS]) = [client.prefs.toggles & CHAT_GHOSTEARS]")
if ((client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS) != CHAT_GHOSTEARS)
say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTEARS is disabled, blocking. ([client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles)] & [CHAT_GHOSTEARS]) = [client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS]")
return
else
if ((client.prefs.toggles & CHAT_GHOSTRADIO) != CHAT_GHOSTRADIO)
say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTRADIO is disabled, blocking. ([client.prefs.toggles] & [CHAT_GHOSTRADIO]) = [client.prefs.toggles & CHAT_GHOSTRADIO]")
if ((client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTRADIO) != CHAT_GHOSTRADIO)
say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTRADIO is disabled, blocking. ([client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles)] & [CHAT_GHOSTRADIO]) = [client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTRADIO]")
return
to_chat(src, "[formatFollow(source)] [rendered_speech]")

View File

@@ -72,20 +72,20 @@
for(var/mob/M in dead_mob_list)
if (!M.client)
continue //skip leavers
if(isobserver(M) && M.client.prefs && (M.client.prefs.toggles & CHAT_GHOSTSIGHT) && !(M in viewers(user)))
if(isobserver(M) && M.client.prefs && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTSIGHT) && !(M in viewers(user)))
M.show_message(formatFollow(user) + " " + msg)
if(emote_type & EMOTE_VISIBLE)
user.visible_message(msg)
if(!(emote_type & EMOTE_NO_RUNECHAT))
for(var/mob/O in viewers(world.view, user))
if(O.client && O?.client?.prefs.mob_chat_on_map && get_dist(O, user) < O?.client.view)
if(O.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && get_dist(O, user) < O?.client.view)
O.create_chat_message(user, null, message, "", list("italics"))
else if(emote_type & EMOTE_AUDIBLE)
for(var/mob/O in get_hearers_in_view(world.view, user))
O.show_message(msg)
if(!(emote_type & EMOTE_NO_RUNECHAT))
if(O.client && O?.client?.prefs.mob_chat_on_map && get_dist(O, user) < O?.client.view)
if(O.client && O?.client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && get_dist(O, user) < O?.client.view)
O.create_chat_message(user, null, message, "", list("italics"))
var/location = T ? "[T.x],[T.y],[T.z]" : "nullspace"
@@ -96,7 +96,7 @@
to_chat(src, "<span class='warning'>You cannot send deadchat emotes (muted).</span>")
return
if(!(client.prefs.toggles & CHAT_DEAD))
if(!(client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(src, "<span class='warning'>You have deadchat muted.</span>")
return
@@ -117,8 +117,8 @@
if(istype(M, /mob/new_player))
continue
if(M.client && M.client.holder && (M.client.holder.rights & R_ADMIN|R_MOD) && (M.client.prefs.toggles & CHAT_DEAD)) // Show the emote to admins/mods
if(M.client && M.client.holder && (M.client.holder.rights & R_ADMIN|R_MOD) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // Show the emote to admins/mods
to_chat(M, message)
else if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_DEAD)) // Show the emote to regular ghosts with deadchat toggled on
else if(M.stat == DEAD && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) // Show the emote to regular ghosts with deadchat toggled on
M.show_message(message, 2)

View File

@@ -238,10 +238,13 @@
brainmob.dna = new()
brainmob.dna.ResetUI()
brainmob.dna.ResetSE()
if(P.be_random_name)
P.real_name = random_name(P.gender, P.species)
brainmob.name = P.real_name
brainmob.real_name = P.real_name
var/datum/preference_setting/name_pref = P.get_pref_datum(/datum/preference_setting/string/real_name)
var/species = P.get_pref(/datum/preference_setting/string/species)
var/gender = P.get_pref(/datum/preference_setting/enum/gender)
if(P.get_pref(/datum/preference_setting/toggle/be_random_name))
name_pref.setting = random_name(gender, species)
brainmob.name = name_pref.setting
brainmob.real_name = name_pref.setting
brainmob.container = src
name = "Man-Machine Interface: [brainmob.real_name]"

View File

@@ -657,7 +657,7 @@ Thanks.
if(config.allow_Metadata)
if(client)
to_chat(usr, "[src]'s Metainfo:<br>[client.prefs.metadata]")
to_chat(usr, "[src]'s Metainfo:<br>[client.prefs.get_pref(/datum/preference_setting/string/metadata)]")
else
to_chat(usr, "[src] does not have any stored infomation!")
else

View File

@@ -319,13 +319,13 @@ var/list/headset_modes = list(
rendered_message = replacetext(rendered_message, ai.real_name, "<i style='color: blue;'>[ai.real_name]</i>")
// Runechat messages
if (ismob(speech.speaker) && client?.prefs.mob_chat_on_map && stat != UNCONSCIOUS && !is_deaf())
if (ismob(speech.speaker) && client?.prefs.get_pref(/datum/preference_setting/toggle/mob_chat_on_map) && stat != UNCONSCIOUS && !is_deaf())
create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes)
else if (client?.prefs.obj_chat_on_map && stat != UNCONSCIOUS && !is_deaf())
else if (client?.prefs.get_pref(/datum/preference_setting/toggle/obj_chat_on_map) && stat != UNCONSCIOUS && !is_deaf())
create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes)
if (ismob(speech.speaker))
show_message(rendered_message, type, deaf_message, deaf_type, src)
else if (!client.prefs.no_goonchat_for_obj || length_char(speech.message) > client?.prefs.max_chat_length) // Objects : only display if no goonchat on map or if the runemessage is too small.
else if (!client.prefs.get_pref(/datum/preference_setting/toggle/no_goonchat_for_obj) || length_char(speech.message) > client?.prefs.get_pref(/datum/preference_setting/numerical/max_chat_length)) // Objects : only display if no goonchat on map or if the runemessage is too small.
show_message(rendered_message, type, deaf_message, deaf_type, src)
else if (istype(speech.speaker, /obj/item/device/assembly/speaker) || istype(speech.speaker, /obj/item/device/assembly_frame)) //Speakers will still work if no_goonchat_for_obj is set to TRUE
show_message(rendered_message, type, deaf_message, deaf_type, src)

View File

@@ -128,7 +128,7 @@
for(var/i = 0; i < max(user.sprint, initial); i += 20)
var/turf/step = get_turf(get_step(user.eyeobj, direct))
if(step)
if (user.client.prefs.stumble && ((world.time - user.last_movement) > 4))
if (user.client.prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - user.last_movement) > 4))
user.delayNextMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier
else if(istype(H) && H.advancedholo)
H.holo.dir = direct

View File

@@ -331,7 +331,7 @@ var/global/borer_unlock_types_leg = typesof(/datum/unlockable/borer/leg) - /datu
for(var/mob/M in player_list)
if(istype(M, /mob/new_player))
continue
if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS || (get_turf(src) in view(M))))
if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS || (get_turf(src) in view(M))))
var/controls = formatFollow(src,"Follow")
if(M.client.holder)
controls+= " | <A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>"

View File

@@ -22,7 +22,7 @@
for(var/mob/M in player_list)
if(istype(M, /mob/new_player))
continue
if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))
if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS))
var/controls = formatFollow(src,"Follow")
if(M.client.holder)
controls+= " | <A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>"

View File

@@ -1010,7 +1010,7 @@ Use this proc preferably at the end of an equipment loadout
// Are we trying to pull something we are already pulling?
// Then we want to either toggle pulling (stop pulling and quit), or keep pulling (just quit) if client preferences want otherwise.
if(pulling == P)
if(client && !client.prefs.pulltoggle)
if(client && !client.prefs.get_pref(/datum/preference_setting/toggle/pulltoggle))
return
else
stop_pulling()
@@ -1276,7 +1276,7 @@ Use this proc preferably at the end of an equipment loadout
)
src << browse('html/changelog.html', "window=changes;size=675x650")
if(prefs.lastchangelog != changelog_hash)
if(prefs.get_pref(/datum/preference_setting/string/changelog) != changelog_hash)
prefs.SetChangelog(ckey, changelog_hash)
winset(src, "rpane.changelog", "background-color=none;font-style=;")

View File

@@ -393,7 +393,7 @@
else
if (mob.process_confused(Dir))
return
if (prefs.stumble && ((world.time - mob.last_movement) > 5 && move_delay < 2))
if (prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - mob.last_movement) > 5 && move_delay < 2))
mob.delayNextMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier
// to_chat(src, "<span class='notice'>First Step</span>")
step(mob, Dir)

View File

@@ -43,7 +43,7 @@
spawn(0)
if(client)
//If the changelog has changed, show it to them
if(client.prefs.lastchangelog != changelog_hash)
if(client.prefs.get_pref(/datum/preference_setting/string/changelog) != changelog_hash)
// Need to send them the CSS and images :V
client.getFiles(
'html/postcardsmall.jpg',

View File

@@ -307,15 +307,19 @@
observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly.
// Has to be done here so we can get our random icon.
if(client.prefs.be_random_body)
if(client.prefs.get_pref(/datum/preference_setting/toggle/be_random_body))
client.prefs.randomize_appearance_for() // No argument means just the prefs are randomized.
client.prefs.update_preview_icon(1)
observer.icon = client.prefs.preview_icon
observer.alpha = 127
if(client.prefs.be_random_name)
client.prefs.real_name = random_name(client.prefs.gender,client.prefs.species)
observer.real_name = client.prefs.real_name
var/datum/preference_setting/name_pref = client.prefs.get_pref_datum(/datum/preference_setting/string/real_name)
if(client.prefs.get_pref(/datum/preference_setting/toggle/be_random_name))
var/gender = client.prefs.get_pref(/datum/preference_setting/enum/gender)
var/species = client.prefs.get_pref(/datum/preference_setting/string/species)
name_pref.setting = random_name(gender,species)
observer.real_name = name_pref.setting
observer.name = observer.real_name
if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed.
observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing!
@@ -346,12 +350,12 @@
return 0
var/datum/job/job = job_master.GetJob(rank)
if(job.species_whitelist.len)
if(!job.species_whitelist.Find(client.prefs.species))
to_chat(src, alert("[rank] is not available for [client.prefs.species]."))
if(!job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))
to_chat(src, alert("[rank] is not available for [client.prefs.get_pref(/datum/preference_setting/string/species)]."))
return 0
if(job.species_blacklist.len)
if(job.species_blacklist.Find(client.prefs.species))
to_chat(src, alert("[rank] is not available for [client.prefs.species]."))
if(job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species)))
to_chat(src, alert("[rank] is not available for [client.prefs.get_pref(/datum/preference_setting/string/species)]."))
return 0
job_master.AssignRole(src, rank, 1)
@@ -361,7 +365,7 @@
job = job_master.GetJob(rank)
var/mob/living/carbon/human/character = create_human(client.prefs) //creates the human and transfers vars and mind
if(character.client.prefs.randomslot)
if(character.client.prefs.get_pref(/datum/preference_setting/toggle/randomslot))
character.client.prefs.random_character_sqlite(character, character.ckey)
var/atom/movable/what_to_move = character.locked_to || character
@@ -566,7 +570,7 @@
if(highprior.len > 0)
dat += "<tr><th class='reqhead' colspan=3>High Priority Jobs</th></tr>"
for(var/datum/job/job in highprior)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
@@ -578,7 +582,7 @@
if(heads.len > 0)
dat += "<tr><th colspan=3>Heads</th></tr>"
for(var/datum/job/job in heads)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
if(job.department_prioritized)
@@ -590,7 +594,7 @@
if(sec.len > 0)
dat += "<tr><th colspan=3>Security</th></tr>"
for(var/datum/job/job in sec)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
@@ -600,7 +604,7 @@
if(eng.len > 0)
dat += "<tr><th colspan=3>Engineering</th></tr>"
for(var/datum/job/job in eng)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
@@ -610,7 +614,7 @@
if(med.len > 0)
dat += "<tr><th colspan=3>Medical</th></tr>"
for(var/datum/job/job in med)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
@@ -620,7 +624,7 @@
if(sci.len > 0)
dat += "<tr><th colspan=3>Science</th></tr>"
for(var/datum/job/job in sci)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
@@ -630,7 +634,7 @@
if(cgo.len > 0)
dat += "<tr><th colspan=3>Cargo</th></tr>"
for(var/datum/job/job in cgo)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
if(job.department_prioritized)
@@ -642,7 +646,7 @@
if(civ.len > 0)
dat += "<tr><th colspan=3>Civilian</th></tr>"
for(var/datum/job/job in civ)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
@@ -653,7 +657,7 @@
if(misc.len > 0)
dat += "<tr><th colspan=3>Miscellaneous</th></tr>"
for(var/datum/job/job in misc)
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.species)) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.species)))
if((job.species_whitelist.len && !job.species_whitelist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))) || (job.species_blacklist.len && job.species_blacklist.Find(client.prefs.get_pref(/datum/preference_setting/string/species))))
dat += "<tr class='striked'><td><s>[job.title]</s></td><td><s>[job.current_positions]</s></td><td><s>[highprior[job]]</s></td></tr>"
continue
@@ -673,24 +677,30 @@
var/datum/species/chosen_species
var/late_join = ticker.current_state == GAME_STATE_PLAYING ? TRUE : FALSE
if(prefs.species)
chosen_species = all_species[prefs.species]
var/species = prefs.get_pref(/datum/preference_setting/string/species)
var/language = prefs.get_pref(/datum/preference_setting/string/language)
var/datum/preference_setting/name_pref = prefs.get_pref_datum(/datum/preference_setting/string/real_name)
if(species)
chosen_species = all_species[species]
if(chosen_species && (check_rights(R_ADMIN, 0) || chosen_species.flags & PLAYABLE || chosen_species.conditional_playable()))
new_character.set_species(prefs.species)
new_character.set_species(species)
else if(chosen_species.fallback())
to_chat(usr, "Your preferences had a non-playable species, so you were reverted to [chosen_species.fallback()] (default for [chosen_species]).")
new_character.set_species(chosen_species.fallback())
var/datum/language/chosen_language
if(prefs.language)
chosen_language = all_languages["[prefs.language]"]
// This is a bit backwards, but: we check if language exists, and then add it as a string.
if(language)
chosen_language = all_languages[language]
if(chosen_language)
new_character.add_language("[prefs.language]")
new_character.add_language(language)
if(ticker.random_players || appearance_isbanned(src)) //disabling ident bans for now
var/datum/preference_setting/flavor_text = prefs.get_pref_datum(/datum/preference_setting/string/flavor_text)
new_character.setGender(pick(MALE, FEMALE))
prefs.real_name = random_name(new_character.gender, new_character.species.name)
name_pref.setting = random_name(new_character.gender, new_character.species.name)
prefs.randomize_appearance_for(new_character)
prefs.flavor_text = ""
flavor_text.setting = ""
else
prefs.copy_to(new_character)
@@ -701,43 +711,45 @@
mind.active = 0 // we wish to transfer the key manually
mind.transfer_to(new_character) // won't transfer key since the mind is not active
new_character.name = prefs.real_name
new_character.name = name_pref.setting
new_character.dna.ready_dna(new_character)
if(new_character.mind)
new_character.mind.store_memory("<b>Your blood type is:</b> [new_character.dna.b_type]<br>", category=MIND_MEMORY_GENERAL, forced=TRUE)
if(prefs.disabilities & DISABILITY_FLAG_NEARSIGHTED)
var/disabilities = prefs.get_pref(/datum/preference_setting/binary_flag/disabilities)
if(disabilities & DISABILITY_FLAG_NEARSIGHTED)
new_character.dna.SetSEState(GLASSESBLOCK,1,1)
new_character.disabilities |= NEARSIGHTED
if(prefs.disabilities & DISABILITY_FLAG_VEGAN)
if(disabilities & DISABILITY_FLAG_VEGAN)
new_character.dna.SetSEState(VEGANBLOCK, 1, 1)
if(prefs.disabilities & DISABILITY_FLAG_ASTHMA)
if(disabilities & DISABILITY_FLAG_ASTHMA)
new_character.dna.SetSEState(ASTHMABLOCK, 1, 1)
chosen_species = all_species[prefs.species]
if( (prefs.disabilities & DISABILITY_FLAG_FAT) && (chosen_species.anatomy_flags & CAN_BE_FAT) )
chosen_species = all_species[species]
if( (disabilities & DISABILITY_FLAG_FAT) && (chosen_species.anatomy_flags & CAN_BE_FAT) )
new_character.mutations += M_FAT
new_character.overeatduration = 600
if(prefs.disabilities & DISABILITY_FLAG_EPILEPTIC)
if(disabilities & DISABILITY_FLAG_EPILEPTIC)
new_character.dna.SetSEState(EPILEPSYBLOCK,1,1)
new_character.disabilities |= EPILEPSY
if(prefs.disabilities & DISABILITY_FLAG_DEAF)
if(disabilities & DISABILITY_FLAG_DEAF)
new_character.dna.SetSEState(DEAFBLOCK,1,1)
new_character.sdisabilities |= DEAF
if(prefs.disabilities & DISABILITY_FLAG_MUTE)
if(disabilities & DISABILITY_FLAG_MUTE)
new_character.dna.SetSEState(MUTEBLOCK,1,1)
new_character.sdisabilities |= MUTE
if(prefs.disabilities & DISABILITY_FLAG_LISP)
if(disabilities & DISABILITY_FLAG_LISP)
new_character.dna.SetSEState(LISPBLOCK, 1, 1)
if(prefs.disabilities & DISABILITY_FLAG_ANEMIA)
if(disabilities & DISABILITY_FLAG_ANEMIA)
new_character.dna.SetSEState(ANEMIABLOCK, 1, 1)
new_character.dna.UpdateSE()
@@ -778,6 +790,7 @@
break //Only autoconvert them once, and only if they aren't leading their own faith.
if(late_join)
message_admins("latejoin, key transfer.")
new_character.key = key
return new_character

View File

@@ -1,136 +1,43 @@
/datum/preferences
//The mob should have a gender you want before running this proc. Will run fine without H
/datum/preferences/proc/randomize_appearance_for(var/mob/living/carbon/human/H)
// I mean ideally we should want to use appearance_datums... ?
/datum/preferences/proc/randomize_appearance_for(var/mob/living/carbon/human/H, var/random_gender = FALSE)
var/datum/preference_setting/gender_pref = get_pref_datum(/datum/preference_setting/enum/gender)
if(H)
if(H.gender == MALE)
gender = MALE
else
gender = FEMALE
s_tone = random_skin_tone(species)
h_style = random_hair_style(gender, species)
f_style = random_facial_hair_style(gender, species)
randomize_hair_color("hair")
randomize_hair_color("facial")
randomize_eyes_color()
underwear = rand(1,underwear_m.len)
backbag = 2
age = rand(AGE_MIN,AGE_MAX)
gender_pref.setting = H.gender
else if (random_gender)
gender_pref.setting = pick(MALE, FEMALE)
var/datum/preference_setting/s_tone = get_pref_datum(/datum/preference_setting/numerical/s_tone)
var/datum/preference_setting/h_style = get_pref_datum(/datum/preference_setting/string/h_style)
var/datum/preference_setting/f_style = get_pref_datum(/datum/preference_setting/string/f_style)
s_tone.randomise()
h_style.randomise() // This also handles colours
f_style.randomise()
var/datum/preference_setting/r_eyes = get_pref_datum(/datum/preference_setting/numerical/r_eyes)
var/datum/preference_setting/g_eyes = get_pref_datum(/datum/preference_setting/numerical/g_eyes)
var/datum/preference_setting/b_eyes = get_pref_datum(/datum/preference_setting/numerical/b_eyes)
r_eyes.randomise()
g_eyes.randomise()
b_eyes.randomise()
var/datum/preference_setting/underwear = get_pref_datum(/datum/preference_setting/numerical/underwear)
underwear.setting = rand(1, underwear_m.len) // Bug-for-bug compatibility, the last underwear_f values aren't available here
var/datum/preference_setting/backbag = get_pref_datum(/datum/preference_setting/numerical/backbag)
backbag.setting = BACKPACK
var/datum/preference_setting/age = get_pref_datum(/datum/preference_setting/numerical/age)
age.randomise()
if(H)
copy_to(H,1)
/datum/preferences/proc/randomize_hair_color(var/target = "hair")
if(species == "Vox")
r_hair = rand(1,7)
if(prob (75) && target == "facial") // Chance to inherit hair color
r_facial = r_hair
g_facial = g_hair
b_facial = b_hair
return
var/red
var/green
var/blue
var/col = pick("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", 15;"punk")
switch(col)
if("blonde")
red = 255
green = 255
blue = 0
if("black")
red = 0
green = 0
blue = 0
if("chestnut")
red = 153
green = 102
blue = 51
if("copper")
red = 255
green = 153
blue = 0
if("brown")
red = 102
green = 51
blue = 0
if("wheat")
red = 255
green = 255
blue = 153
if("old")
red = rand (100, 255)
green = red
blue = red
if("punk")
red = rand(0, 255)
green = rand(0, 255)
blue = rand(0, 255)
red = max(min(red + rand (-25, 25), 255), 0)
green = max(min(green + rand (-25, 25), 255), 0)
blue = max(min(blue + rand (-25, 25), 255), 0)
switch(target)
if("hair")
r_hair = red
g_hair = green
b_hair = blue
if("facial")
r_facial = red
g_facial = green
b_facial = blue
/datum/preferences/proc/randomize_eyes_color()
var/red
var/green
var/blue
var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino")
switch(col)
if("black")
red = 0
green = 0
blue = 0
if("grey")
red = rand (100, 200)
green = red
blue = red
if("brown")
red = 102
green = 51
blue = 0
if("chestnut")
red = 153
green = 102
blue = 0
if("blue")
red = 51
green = 102
blue = 204
if("lightblue")
red = 102
green = 204
blue = 255
if("green")
red = 0
green = 102
blue = 0
if("albino")
red = rand (200, 255)
green = rand (0, 150)
blue = rand (0, 150)
red = max(min(red + rand (-25, 25), 255), 0)
green = max(min(green + rand (-25, 25), 255), 0)
blue = max(min(blue + rand (-25, 25), 255), 0)
r_eyes = red
g_eyes = green
b_eyes = blue
/datum/preferences/proc/blend_backpack(var/icon/clothes_s,var/backbag,var/satchel,var/backpack="backpack",var/messenger_bag)
// I'm not even going to pretend what these fucking magic values mean
switch(backbag)
if(2)
clothes_s.Blend(new /icon('icons/mob/back.dmi', backpack), ICON_OVERLAY)
@@ -164,11 +71,32 @@
preview_icon = null
var/g = "m"
if(gender == FEMALE)
if(get_pref(/datum/preference_setting/enum/gender) == FEMALE)
g = "f"
var/icon/icobase
var/species = get_pref(/datum/preference_setting/string/species)
var/datum/species/current_species = all_species[species]
var/s_tone = get_pref(/datum/preference_setting/numerical/s_tone)
var/r_hair = get_pref(/datum/preference_setting/numerical/r_hair)
var/g_hair = get_pref(/datum/preference_setting/numerical/g_hair)
var/b_hair = get_pref(/datum/preference_setting/numerical/b_hair)
var/r_eyes = get_pref(/datum/preference_setting/numerical/r_eyes)
var/g_eyes = get_pref(/datum/preference_setting/numerical/b_eyes)
var/b_eyes = get_pref(/datum/preference_setting/numerical/g_eyes)
var/r_facial = get_pref(/datum/preference_setting/numerical/r_facial)
var/g_facial = get_pref(/datum/preference_setting/numerical/g_facial)
var/b_facial = get_pref(/datum/preference_setting/numerical/b_facial)
var/list/jobs = get_pref(/datum/preference_setting/assoc_list_setting/jobs)
var/h_style = get_pref(/datum/preference_setting/string/h_style)
var/f_style = get_pref(/datum/preference_setting/string/f_style)
var/backbag = get_pref(/datum/preference_setting/numerical/backbag)
var/disabilites = get_pref(/datum/preference_setting/binary_flag/disabilities)
//icon based species color
if(current_species)
@@ -202,7 +130,7 @@
icobase = 'icons/mob/human_races/r_human.dmi'
var/fat=""
if(disabilities&DISABILITY_FLAG_FAT && current_species.anatomy_flags & CAN_BE_FAT)
if(disabilites&DISABILITY_FLAG_FAT && current_species.anatomy_flags & CAN_BE_FAT)
fat="_fat"
preview_icon = new /icon(icobase, "torso_[g][fat]")
@@ -296,13 +224,13 @@
// UNIFORM DMI
if(current_species)
uniform_dmi=current_species.uniform_icons
if(disabilities&DISABILITY_FLAG_FAT && current_species.fat_uniform_icons)
if(disabilites&DISABILITY_FLAG_FAT && current_species.fat_uniform_icons)
uniform_dmi=current_species.fat_uniform_icons
// SUIT DMI
if(current_species)
suit_dmi=current_species.wear_suit_icons
if(disabilities&DISABILITY_FLAG_FAT && current_species.fat_wear_suit_icons)
if(disabilites&DISABILITY_FLAG_FAT && current_species.fat_wear_suit_icons)
suit_dmi=current_species.fat_wear_suit_icons
@@ -566,7 +494,7 @@
var/drink_icon_state = pick("coffee", "cafe_latte", "beer", "alebottle", "gargleblasterglass", "energy_drink", "sangria", "gintonicglass")
clothes_s.Blend(new /icon(drink_icon, drink_icon_state), ICON_OVERLAY)
if(disabilities & NEARSIGHTED)
if(disabilites & NEARSIGHTED)
preview_icon.Blend(new /icon('icons/mob/eyes.dmi', "glasses"), ICON_OVERLAY)
preview_icon.Blend(eyes_s, ICON_OVERLAY)

View File

@@ -55,7 +55,7 @@ var/list/global_deadchat_listeners = list()
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
if(client && !(client.prefs.toggles & CHAT_DEAD))
if(client && !(client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(usr, "<span class='danger'>You have deadchat muted.</span>")
return

View File

@@ -17,7 +17,7 @@ var/atom/movable/typing_indicator/typing_indicator
return ..()
/mob/proc/create_typing_indicator()
if(client && !stat && client.prefs.typing_indicator && src.is_visible() && isturf(src.loc))
if(client && !stat && client.prefs.get_pref(/datum/preference_setting/toggle/typing_indicator) && src.is_visible() && isturf(src.loc))
vis_contents |= typing_indicator
/mob/proc/remove_typing_indicator()

View File

@@ -54,7 +54,7 @@
if(target_move.return_network(target_move) != return_network(src))
user.remove_ventcrawl()
user.add_ventcrawl(target_move)
if (user.client.prefs.stumble && ((world.time - user.last_movement) > 5))
if (user.client.prefs.get_pref(/datum/preference_setting/toggle/stumble) && ((world.time - user.last_movement) > 5))
user.delayNextMove(3) //if set, delays the second step when a mob starts moving to attempt to make precise high ping movement easier
user.forceMove(target_move)
user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement

View File

@@ -80,7 +80,7 @@
..()
/obj/item/weapon/paper/merchant/proc/apply_text(mob/living/carbon/human/merchant)
identity = merchant.client.prefs.real_name
identity = merchant.client.prefs.get_pref(/datum/preference_setting/string/real_name)
icon = 'icons/obj/items.dmi'
icon_state = "permit"
name = "Merchant's Licence - [identity]"
@@ -105,7 +105,7 @@
display_y = 700
/obj/item/weapon/paper/merchant/report/apply_text(mob/living/carbon/human/merchant)
identity = merchant.client.prefs.real_name
identity = merchant.client.prefs.get_pref(/datum/preference_setting/string/real_name)
name = "Licensed Merchant Report - [identity]"
info = {"<html><style>
body {color: #000000; background: #ccffff;}

View File

@@ -802,11 +802,11 @@ var/list/obj/machinery/singularity/white_hole_candidates
for(var/mob/M in player_list)
if(istype(M, /mob/new_player) || !M.client)
continue
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.toggles & CHAT_DEAD))
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.toggles & CHAT_DEAD))
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.toggles & CHAT_DEAD))
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
var/mob/living/carbon/brain/B = M
if(B.brain_dead_chat())
to_chat(M, message)
@@ -888,11 +888,11 @@ var/list/obj/machinery/singularity/white_hole_candidates
for(var/mob/M in player_list)
if(istype(M, /mob/new_player) || !M.client)
continue
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.toggles & CHAT_DEAD)) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
to_chat(M, message)
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.toggles & CHAT_DEAD))
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.toggles & CHAT_DEAD))
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
var/mob/living/carbon/brain/B = M
if(B.brain_dead_chat())
to_chat(M, message)
@@ -901,11 +901,11 @@ var/list/obj/machinery/singularity/white_hole_candidates
for(var/mob/M in player_list)
if(istype(M, /mob/new_player) || !M.client)
continue
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.toggles & CHAT_DEAD)) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
if(M.client && M.client.holder && M.client.holder.rights & R_ADMIN && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
to_chat(M, message)
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.toggles & CHAT_DEAD))
else if(M.client && M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
to_chat(M, message)
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.toggles & CHAT_DEAD))
else if(M.client && istype(M,/mob/living/carbon/brain) && (M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD))
var/mob/living/carbon/brain/B = M
if(B.brain_dead_chat())
to_chat(M, message)

View File

@@ -103,7 +103,7 @@
continue //skip monkeys and leavers
if (istype(M, /mob/new_player))
continue
if(M.stat == 2 && M.client.prefs.toggles & CHAT_GHOSTEARS)
if(M.stat == 2 && M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS)
listening|=M
for(var/mob/M in listening)

View File

@@ -685,7 +685,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
var/Location = user.loc
var/image/progbar
if(user && user.client && user.client.prefs.progress_bars)
if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars))
if(!progbar)
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = user, "icon_state" = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE
@@ -694,7 +694,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
progbar.appearance_flags = RESET_COLOR
for (var/i = 1 to numticks)
if(user && user.client && user.client.prefs.progress_bars)
if(user && user.client && user.client.prefs.get_pref(/datum/preference_setting/toggle/progress_bars))
if(!progbar)
progbar = image("icon" = 'icons/effects/doafter_icon.dmi', "loc" = user, "icon_state" = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE

View File

@@ -60,7 +60,7 @@ var/list/all_GPS_list = list()
SStgui.update_uis(src)
/obj/item/device/gps/attack_self(mob/user)
if(user.client.prefs.tgui_fancy)
if(user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_fancy))
tgui_interact(user)
else
ui_interact(user)

View File

@@ -99,7 +99,7 @@
// if(action == "change_ui_state")
// var/mob/living/user = ui.user
// //write_preferences will make sure it's valid for href exploits.
// user.client.prefs.layout_prefs_used = params["new_state"]
// user.client.prefs.get_pref(/datum/preference_setting/toggle/layout_prefs_used) = params["new_state"]
/**
* public

View File

@@ -56,7 +56,7 @@
*/
/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
log_tgui(user,
"new [interface] fancy [user?.client?.prefs.tgui_fancy]",
"new [interface] fancy [user?.client?.prefs.get_pref(/datum/preference_setting/toggle/tgui_fancy)]",
src_object = src_object)
src.user = user
src.src_object = src_object
@@ -98,7 +98,7 @@
if(!window.is_ready())
window.initialize(
strict_mode = TRUE,
fancy = user.client.prefs.tgui_fancy,
fancy = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_fancy),
assets = list(
get_tg_asset_datum(/datum/tg_asset/simple/tgui),
),
@@ -248,15 +248,15 @@
"status" = status,
"interface" = list(
"name" = interface,
"layout" = user.client.prefs.layout_prefs_used,
"layout" = user.client.prefs.get_pref(/datum/preference_setting/toggle/layout_prefs_used),
),
"refreshing" = refreshing,
"window" = list(
"key" = window_key,
"size" = window_size,
"fancy" = user.client.prefs.tgui_fancy,
"locked" = user.client.prefs.tgui_lock,
"scale" = user.client.prefs.tgui_scale,
"fancy" = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_fancy),
"locked" = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_lock),
"scale" = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_scale),
),
"client" = list(
"ckey" = user.client.ckey,

View File

@@ -28,7 +28,7 @@
log_tgui(user, "Error: TGUI Alert initiated with too many buttons. Use a list.", "TguiAlert")
return tgui_input_list(user, message, title, buttons, timeout, autofocus)
// Client does NOT have tgui_input on: Returns regular input
if(!user.client.prefs.tgui_input)
if(!user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input))
if(length(buttons) == 2)
return alert(user, message, title, buttons[1], buttons[2])
if(length(buttons) == 3)
@@ -110,8 +110,8 @@
data["autofocus"] = autofocus
data["buttons"] = buttons
data["message"] = message
data["large_buttons"] = user.client.prefs.tgui_input_large
data["swapped_buttons"] = user.client.prefs.tgui_input_swapped
data["large_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_large)
data["swapped_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_swapped)
data["title"] = title
return data

View File

@@ -25,7 +25,7 @@
if(isnull(user.client))
return null
if(!user.client.prefs.tgui_input)
if(!user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input))
return input(user, message, title) as null|anything in items
var/datum/tgui_checkbox_input/input = new(user, message, title, items, min_checked, max_checked, timeout, ui_state)
input.ui_interact(user)
@@ -108,9 +108,9 @@
data["items"] = items
data["min_checked"] = min_checked
data["max_checked"] = max_checked
data["large_buttons"] = user.client.prefs.tgui_input_large
data["large_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_large)
data["message"] = message
data["swapped_buttons"] = user.client.prefs.tgui_input_swapped
data["swapped_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_swapped)
data["title"] = title
return data

View File

@@ -22,7 +22,7 @@
return null
// Client does NOT have tgui_input on: Returns regular input
if(!user.client.prefs.tgui_input)
if(!user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input))
var/input_key = input(user, message, title + "(Modifiers are TGUI only, sorry!)", default) as null|text
return input_key[1]
var/datum/tgui_input_keycombo/key_input = new(user, message, title, default, timeout, ui_state)
@@ -96,9 +96,9 @@
/datum/tgui_input_keycombo/ui_static_data(mob/user)
var/list/data = list()
data["init_value"] = default // Default is a reserved keyword
data["large_buttons"] = user.client.prefs.tgui_input_large
data["large_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_large)
data["message"] = message
data["swapped_buttons"] = user.client.prefs.tgui_input_swapped
data["swapped_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_swapped)
data["title"] = title
return data

View File

@@ -26,7 +26,7 @@
return null
/// Client does NOT have tgui_input on: Returns regular input
if(!user.client.prefs.tgui_input)
if(!user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input))
return input(user, message, title, default) as null|anything in items
var/datum/tgui_list_input/input = new(user, message, title, items, default, timeout, ui_state)
if(input.invalid)
@@ -127,9 +127,9 @@
var/list/data = list()
data["init_value"] = default || items[1]
data["items"] = items
data["large_buttons"] = user.client.prefs.tgui_input_large
data["large_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_large)
data["message"] = message
data["swapped_buttons"] = user.client.prefs.tgui_input_swapped
data["swapped_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_swapped)
data["title"] = title
return data

View File

@@ -29,7 +29,7 @@
return null
// Client does NOT have tgui_input on: Returns regular input
if(!user.client.prefs.tgui_input)
if(!user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input))
var/input_number = input(user, message, title, default) as null|num
return clamp(round_value ? round(input_number) : input_number, min_value, max_value)
var/datum/tgui_input_number/number_input = new(user, message, title, default, max_value, min_value, timeout, round_value, ui_state)
@@ -122,11 +122,11 @@
/datum/tgui_input_number/ui_static_data(mob/user)
var/list/data = list()
data["init_value"] = default // Default is a reserved keyword
data["large_buttons"] = user.client.prefs.tgui_input_large
data["large_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_large)
data["max_value"] = max_value
data["message"] = message
data["min_value"] = min_value
data["swapped_buttons"] = user.client.prefs.tgui_input_swapped
data["swapped_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_swapped)
data["title"] = title
data["round_value"] = round_value
return data

View File

@@ -29,7 +29,7 @@
return null
// Client does NOT have tgui_input on: Returns regular input
if(!user.client.prefs.tgui_input)
if(!user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input))
if(encode)
if(multiline)
return stripped_multiline_input(user, message, title, default, PREVENT_CHARACTER_TRIM_LOSS(max_length))
@@ -119,12 +119,12 @@
/datum/tgui_input_text/ui_static_data(mob/user)
var/list/data = list()
data["large_buttons"] = user.client.prefs.tgui_input_large
data["large_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_large)
data["max_length"] = max_length
data["message"] = message
data["multiline"] = multiline
data["placeholder"] = default // Default is a reserved keyword
data["swapped_buttons"] = user.client.prefs.tgui_input_swapped
data["swapped_buttons"] = user.client.prefs.get_pref(/datum/preference_setting/toggle/tgui_input_swapped)
data["title"] = title
return data

View File

@@ -88,9 +88,9 @@ Notes:
//Includes sanity.checks
/proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null,title = "",content = "",theme = "")
if(istype(user))
if(user.client && user.client.tooltips && user.client.prefs.tooltips)
if(!theme && user.client.prefs && user.client.prefs.UI_style)
theme = lowertext(user.client.prefs.UI_style)
if(user.client && user.client.tooltips && user.client.prefs.get_pref(/datum/preference_setting/toggle/tooltips))
if(!theme && user.client.prefs && user.client.prefs.get_pref(/datum/preference_setting/string/UI_style))
theme = lowertext(user.client.prefs.get_pref(/datum/preference_setting/string/UI_style))
if(!theme)
theme = "default"
user.client.tooltips.show(tip_src, params,title,content,theme)

View File

@@ -22,4 +22,5 @@
#include "supermatter_airflow.dm"
#include "languages.dm"
#include "hour_calculations.dm"
#include "sqlite_prefs.dm"
#endif

View File

@@ -0,0 +1,162 @@
#include "sqlite_unit_testing_procs.dm"
// -- Targets the empty file
/datum/preferences/unit_testing
// We only target the baseline DB so that we cant EVER even accidentally touch player prefs
db = ("players2_empty.sqlite")
// -- Only inits its datums
/datum/preferences/unit_testing/New()
// This will automatically put players2_emtpy to the latest schema
migration_controller_sqlite = new ("players2_empty.sqlite", "players2_empty.sqlite")
init_datums()
/datum/preferences/unit_testing/proc/clear_db()
var/database/query/clear_db = new
clear_db.Add("DELETE FROM client;")
ASSERT(clear_db.Execute(db))
clear_db.Add("DELETE FROM players;")
ASSERT(clear_db.Execute(db))
clear_db.Add("DELETE FROM body;")
ASSERT(clear_db.Execute(db))
clear_db.Add("DELETE FROM limbs;")
ASSERT(clear_db.Execute(db))
clear_db.Add("DELETE FROM client_roles;")
ASSERT(clear_db.Execute(db))
clear_db.Add("DELETE FROM jobs;")
ASSERT(clear_db.Execute(db))
/datum/preferences/unit_testing/Del()
world.log << "Clearing dummy database..."
clear_db()
return ..()
/datum/unit_test/sqlite/start()
var/datum/preferences/unit_testing/test_prefs = new
test_prefs.clear_db()
var/dummy_ckey = "this_ckey_does_not_exist" // this dummy ckey contains _, so we won't ever accidentally collide with an existing ckey
var/DEFAULT_SLOT = 1
// TEST 1: loading a pref for a new client
var/load_pref = test_prefs.try_load_preferences(dummy_ckey, null) // Here, `null` simply means there is no mob to recieve confirmation messages
ASSERT(load_pref)
// TEST 2: creating a new save
test_prefs.try_load_save_sqlite(dummy_ckey, null, DEFAULT_SLOT)
// TEST 3: client table is not empty
var/database/query/check_existing = new
check_existing.Add("SELECT ckey FROM client WHERE ckey = ?", dummy_ckey)
check_existing.Execute(test_prefs.db)
ASSERT(check_existing.NextRow()) // there should be at least one row.
// TEST 4: loading a character works
test_prefs.load_character_sqlite(dummy_ckey, null, DEFAULT_SLOT)
// TEST 5: Checking vars:
for (var/setting in typesof(/datum/preference_setting))
var/datum/preference_setting/setting_type = setting
if (!initial(setting_type.enabled))
continue
// Cannot check for default values since body is randomised at first entry.
var/datum/preference_setting/test = test_prefs.get_pref_datum(setting)
ASSERT(test.enabled)
// TEST 6: Checking if vars are equal to what is on the database
// For client
var/list/database_data = test_prefs.execute_load_pref_query(dummy_ckey)
for (var/setting in typesof(/datum/preference_setting))
var/datum/preference_setting/setting_type = setting
if (!initial(setting_type.enabled))
continue
if (initial(setting_type.sql_table) != "client")
continue
var/datum/preference_setting/the_setting = test_prefs.get_pref_datum(setting_type)
var/db_value = the_setting.load_sql(database_data[the_setting.sql_name])
var/actual_value = the_setting.setting
if(!(actual_value ~= db_value))
stack_trace("equal values test failed. actual_value = [actual_value], db_value = [db_value]. Setting type = [setting]")
// For body
var/sql = test_prefs.load_character_sql()
var/database/query/check_body = new
var/list/preference_list = list()
// Ckey and slot and ?/joker parameters in the prepared query
check_body.Add(sql, dummy_ckey, DEFAULT_SLOT)
if(check_body.Execute(test_prefs.db))
while(check_body.NextRow())
var/list/row = check_body.GetRowData()
for(var/a in row)
preference_list[a] = row[a]
for (var/setting in typesof(/datum/preference_setting))
var/datum/preference_setting/setting_type = setting
if (!initial(setting_type.enabled))
continue
if (initial(setting_type.sql_table) == "client")
continue
var/datum/preference_setting/the_setting = test_prefs.get_pref_datum(setting_type)
var/db_value = the_setting.load_sql(preference_list[the_setting.sql_name])
var/actual_value = the_setting.setting
if(!(actual_value ~= db_value))
stack_trace("equal values test failed. actual_value = [islist(actual_value) ? json_encode(actual_value) : actual_value], db_value = [islist(db_value) ? json_encode(db_value) : db_value]. Setting type = [setting]")
// 7. Change some vars (will be checked later)
for (var/type, setting in test_prefs.preference_settings_client)
var/datum/preference_setting/the_setting = setting
the_setting.simulate_setting_change()
for (var/type, setting in test_prefs.preference_settings_character)
var/datum/preference_setting/the_setting = setting
the_setting.simulate_setting_change()
test_prefs.save_character_sqlite(dummy_ckey, null, DEFAULT_SLOT)
test_prefs.save_preferences_sqlite(null, dummy_ckey)
// 8. Creating a new slot on an unoccupied slot
var/NEW_SLOT = 2
var/load_new_slot_result = test_prefs.try_load_slot(dummy_ckey, null, NEW_SLOT)
ASSERT(load_new_slot_result)
// Simulate default slot change
var/datum/preference_setting/slot_pref = test_prefs.get_pref_datum(/datum/preference_setting/numerical/default_slot)
slot_pref.setting = NEW_SLOT
test_prefs.slot = NEW_SLOT
test_prefs.save_preferences_sqlite(null, dummy_ckey)
// 9. Has the defaultslot being changed?
var/database/query/check_slot = new
var/list/data_default_slot = list()
check_slot.Add("SELECT [slot_pref.sql_name] FROM [slot_pref.sql_table] WHERE ckey = ?", dummy_ckey)
ASSERT(check_slot.Execute(test_prefs.db))
while(check_slot.NextRow())
var/list/row = check_slot.GetRowData()
for(var/a in row)
data_default_slot[a] = row[a]
var/def_slot_db_result = slot_pref.load_sql(data_default_slot[slot_pref.sql_name])
assert_eq(slot_pref.setting, def_slot_db_result)
// 10. Load back initial slot
var/load_initial_slot_result = test_prefs.try_load_slot(dummy_ckey, null, DEFAULT_SLOT)
ASSERT(load_initial_slot_result)
// 11. Check if we get our vars back
for (var/type, setting in test_prefs.preference_settings_client)
var/datum/preference_setting/the_setting = setting
the_setting.check_setting_change()
for (var/type, setting in test_prefs.preference_settings_character)
var/datum/preference_setting/the_setting = setting
the_setting.check_setting_change()
del test_prefs // Explicitly clears out the DB

View File

@@ -0,0 +1,319 @@
// -- Automated change of value --
/datum/preference_setting/proc/simulate_setting_change()
/datum/preference_setting/proc/check_setting_change()
/// Toggles : simple enough.
/datum/preference_setting/toggle/simulate_setting_change()
setting = !default_setting
/datum/preference_setting/toggle/check_setting_change()
ASSERT(setting == !default_setting)
/datum/preference_setting/string/real_name/simulate_setting_change()
setting = "Jeanne D. Spesswoman"
/datum/preference_setting/string/real_name/check_setting_change()
ASSERT(setting == "Jeanne D. Spesswoman")
/datum/preference_setting/enum/gender/simulate_setting_change()
setting = FEMALE
/datum/preference_setting/enum/gender/check_setting_change()
ASSERT(setting == FEMALE)
/datum/preference_setting/numerical/age/simulate_setting_change()
setting = 45
/datum/preference_setting/numerical/age/check_setting_change()
ASSERT(setting == 45)
// -- Automated change of value --
/datum/preference_setting/numerical/underwear/simulate_setting_change()
setting = UNDERWEAR_FEMALE_BLACK_HUSBANDBEATER
/datum/preference_setting/numerical/underwear/check_setting_change()
ASSERT(setting == UNDERWEAR_FEMALE_BLACK_HUSBANDBEATER)
// -- Automated change of value --
/datum/preference_setting/numerical/backbag/simulate_setting_change()
setting = MESSENGER_BAG
/datum/preference_setting/numerical/backbag/check_setting_change()
ASSERT(setting == MESSENGER_BAG)
// -- Automated change of value --
/datum/preference_setting/string/h_style/simulate_setting_change()
setting = "Mohawk"
/datum/preference_setting/string/h_style/check_setting_change()
ASSERT(setting == "Mohawk")
// -- Automated change of value --
/datum/preference_setting/numerical/r_hair/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/r_hair/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/g_hair/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/g_hair/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/b_hair/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/b_hair/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/string/f_style/simulate_setting_change()
setting = "Full Beard"
/datum/preference_setting/string/f_style/check_setting_change()
ASSERT(setting == "Full Beard")
// -- Automated change of value --
/datum/preference_setting/numerical/r_facial/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/r_facial/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/g_facial/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/g_facial/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/b_facial/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/b_facial/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/r_eyes/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/r_eyes/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/g_eyes/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/g_eyes/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/b_eyes/simulate_setting_change()
setting = 255
/datum/preference_setting/numerical/b_eyes/check_setting_change()
ASSERT(setting == 255)
// -- Automated change of value --
/datum/preference_setting/numerical/s_tone/simulate_setting_change()
setting = 35
/datum/preference_setting/numerical/s_tone/check_setting_change()
ASSERT(setting == 35)
// -- Automated change of value --
/datum/preference_setting/string/language/simulate_setting_change()
setting = "Spanish"
/datum/preference_setting/string/language/check_setting_change()
ASSERT(setting == "Spanish")
// -- Automated change of value --
/datum/preference_setting/string/flavor_text/simulate_setting_change()
setting = "An enigmatic figure with a scar over one eye."
/datum/preference_setting/string/flavor_text/check_setting_change()
ASSERT(setting == "An enigmatic figure with a scar over one eye.")
// -- Automated change of value --
/datum/preference_setting/string/med_record/simulate_setting_change()
setting = "No known allergies. Former boxer."
/datum/preference_setting/string/med_record/check_setting_change()
ASSERT(setting == "No known allergies. Former boxer.")
// -- Automated change of value --
/datum/preference_setting/string/sec_record/simulate_setting_change()
setting = "Has prior infractions for insubordination."
/datum/preference_setting/string/sec_record/check_setting_change()
ASSERT(setting == "Has prior infractions for insubordination.")
// -- Automated change of value --
/datum/preference_setting/string/gen_record/simulate_setting_change()
setting = "Ex-Nanotrasen researcher."
/datum/preference_setting/string/gen_record/check_setting_change()
ASSERT(setting == "Ex-Nanotrasen researcher.")
// -- Automated change of value --
/datum/preference_setting/string/metadata/simulate_setting_change()
setting = "Prefers quiet RP sessions."
/datum/preference_setting/string/metadata/check_setting_change()
ASSERT(setting == "Prefers quiet RP sessions.")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_left_arm/simulate_setting_change()
setting = "amputated"
/datum/preference_setting/enum/organ_data/limb_left_arm/check_setting_change()
ASSERT(setting == "amputated")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_right_arm/simulate_setting_change()
setting = "cyborg"
/datum/preference_setting/enum/organ_data/limb_right_arm/check_setting_change()
ASSERT(setting == "cyborg")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_left_leg/simulate_setting_change()
setting = "amputated"
/datum/preference_setting/enum/organ_data/limb_left_leg/check_setting_change()
ASSERT(setting == "amputated")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_right_leg/simulate_setting_change()
setting = "cyborg"
/datum/preference_setting/enum/organ_data/limb_right_leg/check_setting_change()
ASSERT(setting == "cyborg")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_left_hand/simulate_setting_change()
setting = "amputated"
/datum/preference_setting/enum/organ_data/limb_left_hand/check_setting_change()
ASSERT(setting == "amputated")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_right_hand/simulate_setting_change()
setting = "cyborg"
/datum/preference_setting/enum/organ_data/limb_right_hand/check_setting_change()
ASSERT(setting == "cyborg")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_left_foot/simulate_setting_change()
setting = "cyborg"
/datum/preference_setting/enum/organ_data/limb_left_foot/check_setting_change()
ASSERT(setting == "cyborg")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/limb_right_foot/simulate_setting_change()
setting = "amputated"
/datum/preference_setting/enum/organ_data/limb_right_foot/check_setting_change()
ASSERT(setting == "amputated")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/organ/heart/simulate_setting_change()
setting = "mechanical"
/datum/preference_setting/enum/organ_data/organ/heart/check_setting_change()
ASSERT(setting == "mechanical")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/organ/eyes/simulate_setting_change()
setting = "assisted"
/datum/preference_setting/enum/organ_data/organ/eyes/check_setting_change()
ASSERT(setting == "assisted")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/organ/lung/simulate_setting_change()
setting = "mechanical"
/datum/preference_setting/enum/organ_data/organ/lung/check_setting_change()
ASSERT(setting == "mechanical")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/organ/liver/simulate_setting_change()
setting = "mechanical"
/datum/preference_setting/enum/organ_data/organ/liver/check_setting_change()
ASSERT(setting == "mechanical")
// -- Automated change of value --
/datum/preference_setting/enum/organ_data/organ/kidneys/simulate_setting_change()
setting = "mechanical"
/datum/preference_setting/enum/organ_data/organ/kidneys/check_setting_change()
ASSERT(setting == "mechanical")
// -- Automated change of value --
/datum/preference_setting/enum/alternate_option/simulate_setting_change()
setting = BE_ASSISTANT
/datum/preference_setting/enum/alternate_option/check_setting_change()
ASSERT(setting == BE_ASSISTANT)
// -- Automated change of value --
/datum/preference_setting/enum/string/nanotrasen_relation/simulate_setting_change()
setting = "Loyal"
/datum/preference_setting/enum/string/nanotrasen_relation/check_setting_change()
ASSERT(setting == "Loyal")
// -- Automated change of value --
/datum/preference_setting/enum/bank_security/simulate_setting_change()
setting = SECURITY_CARD_AND_MANUAL_LOGIN
/datum/preference_setting/enum/bank_security/check_setting_change()
ASSERT(setting == SECURITY_CARD_AND_MANUAL_LOGIN)
// -- Automated change of value --
/datum/preference_setting/numerical/wage_ratio/simulate_setting_change()
setting = 100
/datum/preference_setting/numerical/wage_ratio/check_setting_change()
ASSERT(setting == 100)
// -- Automated change of value --
/datum/preference_setting/binary_flag/disabilities/simulate_setting_change()
setting = 1
/datum/preference_setting/binary_flag/disabilities/check_setting_change()
ASSERT(setting == 1)
/// --- Jobs
var/list/jobs_example = list(
"Research Director" = JOB_PREF_HIGH,
)
/datum/preference_setting/assoc_list_setting/jobs/simulate_setting_change()
setting = jobs_example
/datum/preference_setting/assoc_list_setting/jobs/check_setting_change()
ASSERT(setting ~= jobs_example)
var/list/alt_title_example = list(
"Station Engineer" = "Engine Technician",
)
/datum/preference_setting/list_values/player_alt_titles/simulate_setting_change()
setting = alt_title_example
/datum/preference_setting/list_values/player_alt_titles/check_setting_change()
ASSERT(setting ~= alt_title_example)

View File

@@ -345,7 +345,7 @@ For the main html chat area
if(istype(M, /mob/new_player))
continue
else if(M.client.prefs.toggles & CHAT_DEAD)
else if(M.client.prefs.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_DEAD)
if(M.client.holder && M.client.holder.rights & R_ADMIN) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
. += M
else if(M.stat == DEAD && !istype(M, /mob/dead/observer/deafmute))

Binary file not shown.

View File

@@ -1491,10 +1491,15 @@
#include "code\modules\client\client_procs.dm"
#include "code\modules\client\darkmode.dm"
#include "code\modules\client\global cache.dm"
#include "code\modules\client\preferences.dm"
#include "code\modules\client\preferences_savefile.dm"
#include "code\modules\client\secret_hwaccel_check.dm"
#include "code\modules\client\edge_sliding\move_loop.dm"
#include "code\modules\client\preferences\pref_datums.dm"
#include "code\modules\client\preferences\pref_datums_character.dm"
#include "code\modules\client\preferences\pref_datums_client.dm"
#include "code\modules\client\preferences\pref_db_workflow.dm"
#include "code\modules\client\preferences\preferences.dm"
#include "code\modules\client\preferences\preferences_savefile.dm"
#include "code\modules\client\preferences\preferences_ui.dm"
#include "code\modules\client\preferences\subsections.dm"
#include "code\modules\client\preferences\subsections\limbs.dm"
#include "code\modules\client\preferences\subsections\organs.dm"