diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index ba292ab65f6..395a1ee3b28 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -114,3 +114,11 @@
#define _2FA_ENABLED_IP "ENABLED_IP"
/// Client will be prompted for 2FA always
#define _2FA_ENABLED_ALWAYS "ENABLED_ALWAYS"
+
+
+#define MAX_SAVE_SLOTS 30 // Save slots for regular players
+#define MAX_SAVE_SLOTS_MEMBER 30 // Save slots for BYOND members
+
+#define TAB_CHAR 0
+#define TAB_GAME 1
+#define TAB_GEAR 2
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 612f7b5abef..402ba2c7e3b 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -51,3 +51,6 @@
return A.plane - B.plane
else
return A.layer - B.layer
+
+/proc/cmp_login_processor_priority(datum/client_login_processor/A, datum/client_login_processor/B)
+ return A.priority - B.priority
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 282ad53cd06..058aecc2819 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -128,6 +128,13 @@
continue
GLOB.world_topic_handlers[wth.topic_key] = topic_handler_type
+ // Setup client login processors.
+ for(var/processor_type in subtypesof(/datum/client_login_processor))
+ var/datum/client_login_processor/CLP = new processor_type()
+ GLOB.client_login_processors.Add(CLP)
+ // Sort them by priority, lowest first
+ sortTim(GLOB.client_login_processors, /proc/cmp_login_processor_priority)
+
/* // Uncomment to debug chemical reaction list.
/client/verb/debug_chemical_list()
diff --git a/code/_globalvars/lists/misc.dm b/code/_globalvars/lists/misc.dm
index 5464ed64bb4..c5afa64cfba 100644
--- a/code/_globalvars/lists/misc.dm
+++ b/code/_globalvars/lists/misc.dm
@@ -56,3 +56,5 @@ GLOBAL_LIST_INIT(cooking_ingredients, list(RECIPE_MICROWAVE = list(), RECIPE_OVE
GLOBAL_LIST_INIT(cooking_reagents, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list()))
#define EGG_LAYING_MESSAGES list("lays an egg.", "squats down and croons.", "begins making a huge racket.", "begins clucking raucously.")
+
+GLOBAL_LIST_EMPTY(client_login_processors)
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index f7660f4d75a..04e1c5fb5df 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -65,7 +65,7 @@ SUBSYSTEM_DEF(jobs)
return type_occupations[jobtype]
/datum/controller/subsystem/jobs/proc/GetPlayerAltTitle(mob/new_player/player, rank)
- return player.client.prefs.GetPlayerAltTitle(GetJob(rank))
+ return player.client.prefs.active_character.GetPlayerAltTitle(GetJob(rank))
/datum/controller/subsystem/jobs/proc/AssignRole(mob/new_player/player, rank, latejoin = 0)
Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]")
@@ -121,7 +121,7 @@ SUBSYSTEM_DEF(jobs)
Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
var/list/candidates = list()
for(var/mob/new_player/player in unassigned)
- Debug(" - Player: [player] Banned: [jobban_isbanned(player, job.title)] Old Enough: [!job.player_old_enough(player.client)] AvInPlaytime: [job.available_in_playtime(player.client)] Flag && Be Special: [flag] && [player.client.prefs.be_special] Job Department: [player.client.prefs.GetJobDepartment(job, level)] Job Flag: [job.flag] Job Department Flag = [job.department_flag]")
+ Debug(" - Player: [player] Banned: [jobban_isbanned(player, job.title)] Old Enough: [!job.player_old_enough(player.client)] AvInPlaytime: [job.available_in_playtime(player.client)] Flag && Be Special: [flag] && [player.client.prefs.be_special] Job Department: [player.client.prefs.active_character.GetJobDepartment(job, level)] Job Flag: [job.flag] Job Department Flag = [job.department_flag]")
if(jobban_isbanned(player, job.title))
Debug("FOC isbanned failed, Player: [player]")
continue
@@ -140,7 +140,7 @@ SUBSYSTEM_DEF(jobs)
if(player.mind && (job.title in player.mind.restricted_roles))
Debug("FOC incompatbile with antagonist role, Player: [player]")
continue
- if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
+ if(player.client.prefs.active_character.GetJobDepartment(job, level) & job.flag)
Debug("FOC pass, Player: [player], Level:[level]")
candidates += player
return candidates
@@ -367,12 +367,12 @@ SUBSYSTEM_DEF(jobs)
continue
// If the player wants that job on this level, then try give it to him.
- if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
+ if(player.client.prefs.active_character.GetJobDepartment(job, level) & job.flag)
// If the job isn't filled
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
Debug("DO pass, Player: [player], Level:[level], Job:[job.title]")
- Debug(" - Job Flag: [job.flag] Job Department: [player.client.prefs.GetJobDepartment(job, level)] Job Current Pos: [job.current_positions] Job Spawn Positions = [job.spawn_positions]")
+ Debug(" - Job Flag: [job.flag] Job Department: [player.client.prefs.active_character.GetJobDepartment(job, level)] Job Current Pos: [job.current_positions] Job Spawn Positions = [job.spawn_positions]")
AssignRole(player, job.title)
unassigned -= player
break
@@ -380,7 +380,7 @@ SUBSYSTEM_DEF(jobs)
// Hand out random jobs to the people who didn't get any in the last check
// 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.active_character.alternate_option == GET_RANDOM_JOB)
GiveRandomJob(player)
Debug("DO, Standard Check end")
@@ -390,7 +390,7 @@ SUBSYSTEM_DEF(jobs)
// Antags, who have to get in, come first
for(var/mob/new_player/player in unassigned)
if(player.mind.special_role)
- if(player.client.prefs.alternate_option != BE_ASSISTANT)
+ if(player.client.prefs.active_character.alternate_option != BE_ASSISTANT)
GiveRandomJob(player)
if(player in unassigned)
AssignRole(player, "Civilian")
@@ -399,10 +399,10 @@ SUBSYSTEM_DEF(jobs)
// Then we assign what we can to everyone else.
for(var/mob/new_player/player in unassigned)
- if(player.client.prefs.alternate_option == BE_ASSISTANT)
+ if(player.client.prefs.active_character.alternate_option == BE_ASSISTANT)
Debug("AC2 Assistant located, Player: [player]")
AssignRole(player, "Civilian")
- else if(player.client.prefs.alternate_option == RETURN_TO_LOBBY)
+ else if(player.client.prefs.active_character.alternate_option == RETURN_TO_LOBBY)
player.ready = 0
unassigned -= player
@@ -564,11 +564,11 @@ SUBSYSTEM_DEF(jobs)
if(job.barred_by_disability(player.client))
disabled++
continue
- if(player.client.prefs.GetJobDepartment(job, 1) & job.flag)
+ if(player.client.prefs.active_character.GetJobDepartment(job, 1) & job.flag)
high++
- else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag)
+ else if(player.client.prefs.active_character.GetJobDepartment(job, 2) & job.flag)
medium++
- else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag)
+ else if(player.client.prefs.active_character.GetJobDepartment(job, 3) & job.flag)
low++
else never++ //not selected
diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm
index f315d79eb99..38dfbfab5d4 100644
--- a/code/game/gamemodes/changeling/traitor_chan.dm
+++ b/code/game/gamemodes/changeling/traitor_chan.dm
@@ -23,7 +23,7 @@
secondary_enemies = CEILING((secondary_enemies_scaling * num_players()), 1)
for(var/mob/new_player/player in GLOB.player_list)
- if((player.mind in possible_changelings) && (player.client.prefs.species in secondary_protected_species))
+ if((player.mind in possible_changelings) && (player.client.prefs.active_character.species in secondary_protected_species))
possible_changelings -= player.mind
if(possible_changelings.len > 0)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 4e5372ffc2d..08f0a352d4a 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -248,7 +248,7 @@
// Get a list of all the people who want to be the antagonist for this round, except those with incompatible species
for(var/mob/new_player/player in players)
if(!player.client.skip_antag)
- if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species))
+ if((role in player.client.prefs.be_special) && !(player.client.prefs.active_character.species in protected_species))
player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them."
candidates += player.mind
players -= player
diff --git a/code/game/gamemodes/intercept_report.dm b/code/game/gamemodes/intercept_report.dm
index aaf5dd1eea7..7b3fd4d52fc 100644
--- a/code/game/gamemodes/intercept_report.dm
+++ b/code/game/gamemodes/intercept_report.dm
@@ -110,7 +110,7 @@
/datum/intercept_text/proc/get_suspect()
var/list/dudes = list()
for(var/mob/living/carbon/human/man in GLOB.player_list)
- if(man.client && man.client.prefs.nanotrasen_relation == "Opposed")
+ if(man.client && man.client.prefs.active_character.nanotrasen_relation == "Opposed")
//don't include suspects who can't possibly be the antag based on their job (no suspecting the captain of being a damned dirty tator)
if(man.mind && man.mind.assigned_role)
if((man.mind.assigned_role in SSticker.mode.protected_jobs) || (man.mind.assigned_role in SSticker.mode.restricted_jobs))
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 1ace6f6a836..6aef09163d3 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -279,14 +279,6 @@
synd_mob.equip_to_slot_or_del(U, slot_in_backpack)
if(synd_mob.dna.species)
-
- /*
- Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave
- synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops.
- var/datum/preferences/pref = new()
- A.randomize_appearance_for(synd_mob)
- */
-
var/race = synd_mob.dna.species.name
switch(race)
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index b7ff2d7992e..7e48eae4c51 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -486,7 +486,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
if(SSticker.current_state == GAME_STATE_SETTING_UP)
for(var/mob/new_player/P in GLOB.player_list)
if(P.client && P.ready && P.mind != owner)
- if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution.
+ if(P.client.prefs && (P.client.prefs.active_character.species == "Machine")) // Special check for species that can't be absorbed. No better solution.
continue
n_p++
else if(SSticker.current_state == GAME_STATE_PLAYING)
diff --git a/code/game/gamemodes/vampire/traitor_vamp.dm b/code/game/gamemodes/vampire/traitor_vamp.dm
index 9ad66fdb976..b55d6c01c45 100644
--- a/code/game/gamemodes/vampire/traitor_vamp.dm
+++ b/code/game/gamemodes/vampire/traitor_vamp.dm
@@ -24,7 +24,7 @@
secondary_enemies = CEILING((secondary_enemies_scaling * num_players()), 1)
for(var/mob/new_player/player in GLOB.player_list)
- if((player.mind in possible_vampires) && (player.client.prefs.species in secondary_protected_species))
+ if((player.mind in possible_vampires) && (player.client.prefs.active_character.species in secondary_protected_species))
possible_vampires -= player.mind
if(possible_vampires.len > 0)
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index 259de282382..ddace2bf4ed 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -347,9 +347,9 @@ GLOBAL_LIST_EMPTY(multiverse)
/obj/item/multisword/proc/spawn_copy(client/C, turf/T, mob/user)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
if(duplicate_self)
- user.client.prefs.copy_to(M)
+ user.client.prefs.active_character.copy_to(M)
else
- C.prefs.copy_to(M)
+ C.prefs.active_character.copy_to(M)
M.key = C.key
M.mind.name = user.real_name
to_chat(M, "You are an alternate version of [user.real_name] from another universe! Help [user.p_them()] accomplish [user.p_their()] goals at all costs.")
diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm
index d50a6200764..88cd471b372 100644
--- a/code/game/gamemodes/wizard/raginmages.dm
+++ b/code/game/gamemodes/wizard/raginmages.dm
@@ -147,7 +147,7 @@
if(!G || !G.key)
return // Let's not steal someone's soul here
var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))
- G.client.prefs.copy_to(new_character)
+ G.client.prefs.active_character.copy_to(new_character)
new_character.key = G.key
return new_character
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index c39e1ff555f..881ba5893ec 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -123,7 +123,7 @@
var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY)
for(var/i = 1, i < prohibited_disabilities.len, i++)
var/this_disability = prohibited_disabilities[i]
- if(C.prefs.disabilities & this_disability)
+ if(C.prefs.active_character.disabilities & this_disability)
return 1
return 0
@@ -173,8 +173,8 @@
if(box && H.dna.species.speciesbox)
box = H.dna.species.speciesbox
- if(allow_loadout && H.client && (H.client.prefs.loadout_gear && H.client.prefs.loadout_gear.len))
- for(var/gear in H.client.prefs.loadout_gear)
+ if(allow_loadout && H.client && (H.client.prefs.active_character.loadout_gear && length(H.client.prefs.active_character.loadout_gear)))
+ for(var/gear in H.client.prefs.active_character.loadout_gear)
var/datum/gear/G = GLOB.gear_datums[gear]
if(G)
var/permitted = FALSE
@@ -212,7 +212,7 @@
if(gear_leftovers.len)
for(var/datum/gear/G in gear_leftovers)
- var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.loadout_gear[G.display_name]))
+ var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.active_character.loadout_gear[G.display_name]))
if(istype(placed_in))
if(isturf(placed_in))
to_chat(H, "Placing [G.display_name] on [placed_in]!")
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index e40d05da102..c0346f575db 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -130,7 +130,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
if(GLOB.configuration.url.forum_playerinfo_url)
body += "WebInfo | "
if(M.client)
- if(check_watchlist(M.client.ckey))
+ if(M.client.watchlisted)
body += "Remove from Watchlist | "
body += "Edit Watchlist Reason "
else
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index 5d8196a8d88..8cbd56311dc 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -95,6 +95,24 @@ you will have to do something like if(client.holder.rights & R_ADMIN) yourself.
to_chat(user, "Error: You are not an admin.")
return 0
+// Basically the above proc but checks at a /client level
+/proc/check_rights_client(rights_required, show_msg = TRUE, client/C)
+ if(C)
+ if(rights_required)
+ if(C.holder)
+ if(rights_required & C.holder.rights)
+ return TRUE
+ else
+ if(show_msg)
+ to_chat(C, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].")
+ else
+ if(C.holder)
+ return TRUE
+ else
+ if(show_msg)
+ to_chat(C, "Error: You are not an admin.")
+ return FALSE
+
//probably a bit iffy - will hopefully figure out a better solution
/proc/check_if_greater_rights_than(client/other)
if(usr && usr.client)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index f04c4733c6f..ce4ccb9723c 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1363,7 +1363,7 @@
to_chat(usr, "[M] doesn't seem to have an active client.")
return
- if(M.flavor_text == "" && M.client.prefs.flavor_text == "")
+ if(M.flavor_text == "" && M.client.prefs.active_character.flavor_text == "")
to_chat(usr, "[M] has no flavor text set.")
return
@@ -1377,8 +1377,8 @@
M.flavor_text = ""
// Clear and save the DB character's flavor text
- M.client.prefs.flavor_text = ""
- M.client.prefs.save_character(M.client)
+ M.client.prefs.active_character.flavor_text = ""
+ M.client.prefs.active_character.save(M.client)
else if(href_list["userandomname"])
if(!check_rights(R_ADMIN))
@@ -1401,12 +1401,12 @@
message_admins("[key_name_admin(usr)] has forced [key_name_admin(M)] to use a random name.")
// Update the mob's name with a random one straight away
- var/random_name = random_name(M.client.prefs.gender, M.client.prefs.species)
+ var/random_name = random_name(M.client.prefs.active_character.gender, M.client.prefs.active_character.species)
M.rename_character(M.real_name, random_name)
// Save that random name for next rounds
- M.client.prefs.real_name = random_name
- M.client.prefs.save_character(M.client)
+ M.client.prefs.active_character.real_name = random_name
+ M.client.prefs.active_character.save(M.client)
else if(href_list["asays"])
if(!check_rights(R_ADMIN))
diff --git a/code/modules/admin/verbs/gimmick_team.dm b/code/modules/admin/verbs/gimmick_team.dm
index 884569c96fa..9ce5b2be17b 100644
--- a/code/modules/admin/verbs/gimmick_team.dm
+++ b/code/modules/admin/verbs/gimmick_team.dm
@@ -63,9 +63,10 @@
for(var/mob/thisplayer in players_to_spawn)
var/mob/living/carbon/human/H = new /mob/living/carbon/human(T)
H.name = random_name(pick(MALE,FEMALE))
- var/datum/preferences/A = new() //Randomize appearance
- A.real_name = H.name
- A.copy_to(H)
+ var/datum/character_save/S = new() //Randomize appearance
+ S.randomise()
+ S.real_name = H.name
+ S.copy_to(H)
H.dna.ready_dna(H)
H.mind_initialize()
diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
index 1199a8e5ea5..669209d86cc 100644
--- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm
+++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
@@ -124,9 +124,10 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0)
var/syndicate_infiltrator_name = random_name(pick(MALE,FEMALE))
- var/datum/preferences/A = new() //Randomize appearance
- A.real_name = syndicate_infiltrator_name
- A.copy_to(new_syndicate_infiltrator)
+ var/datum/character_save/S = new() //Randomize appearance
+ S.randomise()
+ S.real_name = syndicate_infiltrator_name
+ S.copy_to(new_syndicate_infiltrator)
new_syndicate_infiltrator.dna.ready_dna(new_syndicate_infiltrator)
//Creates mind stuff.
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index d92fc9ad235..52c95e63c63 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -32,7 +32,7 @@
if(M.stat || !M.mind || M.mind.special_role || M.mind.offstation_role)
return FALSE
if(temp)
- if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.species in temp.protected_species))
+ if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.active_character.species in temp.protected_species))
return FALSE
if(role) // Don't even bother evaluating if there's no role
if(player_old_enough_antag(M.client,role) && (role in M.client.prefs.be_special) && !M.client.skip_antag && (!jobban_isbanned(M, role)))
@@ -374,8 +374,8 @@
//First we spawn a dude.
var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned.
- var/datum/preferences/A = new(G_found.client)
- A.copy_to(new_character)
+ // Then clone stuff over
+ G_found.client.prefs.active_character.copy_to(new_character)
new_character.dna.ready_dna(new_character)
new_character.key = G_found.key
@@ -388,13 +388,14 @@
var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major")
var/syndicate_commando_name = pick(GLOB.last_names)
- var/datum/preferences/A = new()//Randomize appearance for the commando.
+ var/datum/character_save/S = new()//Randomize appearance for the commando.
+ S.randomise()
if(syndicate_leader_selected)
- A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
- A.age = rand(35,45)
+ S.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
+ S.age = rand(35,45)
else
- A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
- A.copy_to(new_syndicate_commando)
+ S.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
+ S.copy_to(new_syndicate_commando)
new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA.
@@ -576,7 +577,8 @@
if(candidates.len)
var/teamOneMembers = 5
var/teamTwoMembers = 5
- var/datum/preferences/A = new()
+ var/datum/character_save/S = new()
+ S.randomise()
for(var/thing in GLOB.landmarks_list)
var/obj/effect/landmark/L = thing
if(L.name == "tdome1")
@@ -585,7 +587,7 @@
var/mob/living/carbon/human/newMember = new(L.loc)
- A.copy_to(newMember)
+ S.copy_to(newMember)
newMember.dna.ready_dna(newMember)
@@ -607,7 +609,7 @@
var/mob/living/carbon/human/newMember = new(L.loc)
- A.copy_to(newMember)
+ S.copy_to(newMember)
newMember.dna.ready_dna(newMember)
diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm
index 32ec86541ab..3a3eb7d7e6a 100644
--- a/code/modules/admin/verbs/onlyone.dm
+++ b/code/modules/admin/verbs/onlyone.dm
@@ -11,8 +11,9 @@
continue
if(is_type_in_list(H.dna.species, incompatible_species))
H.set_species(/datum/species/human)
- var/datum/preferences/A = new() // Randomize appearance
- A.copy_to(H)
+ var/datum/character_save/S = new() // Randomize appearance
+ S.randomise()
+ S.copy_to(H)
SSticker.mode.traitors += H.mind
H.mind.special_role = SPECIAL_ROLE_TRAITOR
diff --git a/code/modules/admin/verbs/onlyoneteam.dm b/code/modules/admin/verbs/onlyoneteam.dm
index a822367b68c..8589e016bc1 100644
--- a/code/modules/admin/verbs/onlyoneteam.dm
+++ b/code/modules/admin/verbs/onlyoneteam.dm
@@ -12,8 +12,9 @@
continue
if(is_type_in_list(H.dna.species, incompatible_species))
H.set_species(/datum/species/human)
- var/datum/preferences/A = new() // Randomize appearance
- A.copy_to(H)
+ var/datum/character_save/S = new() // Randomize appearance
+ S.randomise()
+ S.copy_to(H)
for(var/obj/item/I in H)
if(istype(I, /obj/item/implant))
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 53615c3d2f7..2a9c4e9e5a4 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -396,10 +396,12 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.age = record_found.fields["age"]
new_character.dna.blood_type = record_found.fields["blood_type"]
else
+ // We make a random character
new_character.change_gender(pick(MALE,FEMALE))
- var/datum/preferences/A = new()
- A.real_name = G_found.real_name
- A.copy_to(new_character)
+ var/datum/character_save/S = new()
+ S.randomise()
+ S.real_name = G_found.real_name
+ S.copy_to(new_character)
if(!new_character.real_name)
new_character.real_name = random_name(new_character.gender)
diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm
index 55895e5b282..b445314fbf1 100644
--- a/code/modules/admin/verbs/striketeam.dm
+++ b/code/modules/admin/verbs/striketeam.dm
@@ -138,13 +138,14 @@ GLOBAL_VAR_INIT(sent_strike_team, 0)
var/commando_leader_rank = pick("Lieutenant", "Captain", "Major")
var/commando_name = pick(GLOB.commando_names)
- var/datum/preferences/A = new()//Randomize appearance for the commando.
+ var/datum/character_save/S = new()//Randomize appearance for the commando.
+ S.randomise()
if(is_leader)
- A.age = rand(35,45)
- A.real_name = "[commando_leader_rank] [commando_name]"
+ S.age = rand(35,45)
+ S.real_name = "[commando_leader_rank] [commando_name]"
else
- A.real_name = "[commando_name]"
- A.copy_to(new_commando)
+ S.real_name = "[commando_name]"
+ S.copy_to(new_commando)
new_commando.dna.ready_dna(new_commando)//Creates DNA.
diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm
index 0c359e532b6..37d5214ef34 100644
--- a/code/modules/admin/verbs/striketeam_syndicate.dm
+++ b/code/modules/admin/verbs/striketeam_syndicate.dm
@@ -104,13 +104,14 @@ GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0)
var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major")
var/syndicate_commando_name = pick(GLOB.last_names)
- var/datum/preferences/A = new()//Randomize appearance for the commando.
+ var/datum/character_save/S = new()//Randomize appearance for the commando.
+ S.randomise()
if(is_leader)
- A.age = rand(35,45)
- A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
+ S.age = rand(35,45)
+ S.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
else
- A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
- A.copy_to(new_syndicate_commando)
+ S.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
+ S.copy_to(new_syndicate_commando)
new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA.
diff --git a/code/modules/admin/watchlist.dm b/code/modules/admin/watchlist.dm
index 89288bd13ab..b6a7c14280c 100644
--- a/code/modules/admin/watchlist.dm
+++ b/code/modules/admin/watchlist.dm
@@ -17,7 +17,19 @@
return
else
target_ckey = new_ckey
- if(check_watchlist(target_ckey))
+
+ var/already_watched = FALSE
+ var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM watch WHERE ckey=:target_ckey", list(
+ "target_ckey" = target_ckey
+ ))
+ if(!query_watch.warn_execute())
+ qdel(query_watch)
+ return
+ if(query_watch.NextRow())
+ already_watched = TRUE
+ qdel(query_watch)
+
+ if(already_watched)
to_chat(usr, "[target_ckey] is already on the watchlist.")
return
var/reason = input(usr,"Please state the reason","Reason") as message|null
@@ -132,18 +144,3 @@
output += "
[reason]
"
usr << browse(output, "window=watchwin;size=900x500")
qdel(query_watchlist)
-
-/proc/check_watchlist(target_ckey)
- var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM watch WHERE ckey=:target_ckey", list(
- "target_ckey" = target_ckey
- ))
- if(!query_watch.warn_execute())
- qdel(query_watch)
- return
- if(query_watch.NextRow())
- var/entry = query_watch.item[1]
- qdel(query_watch)
- return entry
- else
- qdel(query_watch)
- return 0
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 91b0be52338..26cc21ab192 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -268,8 +268,8 @@
show_update_prompt = TRUE
else if(byond_version == SUGGESTED_CLIENT_VERSION && byond_build < SUGGESTED_CLIENT_BUILD)
show_update_prompt = TRUE
- // Actually sent to client much later, so it appears after MOTD.
+ // Actually sent to client much later, so it appears after MOTD.
to_chat(src, "If the title screen is black, resources are still downloading. Please be patient until the title screen appears.")
GLOB.directory[ckey] = src
@@ -278,25 +278,54 @@
if(GLOB.configuration.admin.enable_localhost_autoadmin)
if(is_connecting_from_localhost())
new /datum/admins("!LOCALHOST!", R_HOST, ckey) // Makes localhost rank
+
holder = GLOB.admin_datums[ckey]
if(holder)
GLOB.admins += src
holder.owner = src
- //preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
- prefs = GLOB.preferences_datums[ckey]
- if(!prefs)
- prefs = new /datum/preferences(src)
- GLOB.preferences_datums[ckey] = prefs
+ log_client_to_db(tdata) // Make sure our client exists in the DB
+
+ // This is where stuff happens on the client
+ if(SSdbcore.IsConnected())
+ // Load in all our client data from the DB
+ var/list/datum/db_query/login_queries = list() // List of queries to run for login processing
+
+ for(var/datum/client_login_processor/CLP in GLOB.client_login_processors)
+ login_queries[CLP.type] = CLP.get_query(src)
+
+ SSdbcore.MassExecute(login_queries, TRUE, FALSE, TRUE, FALSE) // Warn, no qdel, assoc, no log
+
+ // Now do fancy things with the results
+ for(var/datum/client_login_processor/CLP in GLOB.client_login_processors)
+ CLP.process_result(login_queries[CLP.type], src)
+
+ QDEL_LIST_ASSOC_VAL(login_queries) // Clear out the used queries
else
- prefs.parent = src
+ // Set vars here that need to be set if the DB is offline
+
+ // Give blank prefs
+ prefs = new /datum/preferences(src)
+ prefs.character_saves = list()
+
+ // Random character
+ prefs.character_saves.Add(new /datum/character_save)
+ prefs.active_character = prefs.character_saves[1]
+
+ // ToS accepted
+ tos_consent = TRUE
+
+
prefs.last_ip = address //these are gonna be used for banning
prefs.last_id = computer_id //these are gonna be used for banning
if(world.byond_version >= 511 && byond_version >= 511 && prefs.clientfps)
fps = prefs.clientfps
- // Check if the client has or has not accepted TOS
- check_tos_consent()
+ // Log alts
+ if(length(related_accounts_ip))
+ log_admin("[key_name(src)] Alts by IP: [jointext(related_accounts_ip, " ")]")
+ if(length(related_accounts_cid))
+ log_admin("[key_name(src)] Alts by CID: [jointext(related_accounts_cid, " ")]")
// This has to go here to avoid issues
// If you sleep past this point, you will get SSinput errors as well as goonchat errors
@@ -320,7 +349,6 @@
winset(src, null, "command=\".configure graphics-hwmode off\"")
winset(src, null, "command=\".configure graphics-hwmode on\"")
- log_client_to_db(tdata)
. = ..() //calls mob.Login()
@@ -332,7 +360,6 @@
if(SSinput.initialized)
set_macros()
- donator_check()
check_ip_intel()
send_resources()
@@ -394,8 +421,6 @@
if(world.TgsAvailable() && length(GLOB.revision_info.testmerges))
to_chat(src, GLOB.revision_info.get_testmerge_chatmessage(TRUE))
- INVOKE_ASYNC(src, .proc/cid_count_check)
-
/client/proc/is_connecting_from_localhost()
var/localhost_addresses = list("127.0.0.1", "::1") // Adresses
@@ -428,52 +453,18 @@
return QDEL_HINT_HARDDEL_NOW
-/client/proc/donator_check()
- set waitfor = FALSE // This needs to run async because any sleep() inside /client/New() breaks stuff badly
- if(IsGuestKey(key))
- return
-
- if(!SSdbcore.IsConnected())
- return
-
- if(check_rights(R_ADMIN, 0, mob)) // Yes, the mob is required, regardless of other examples in this file, it won't work otherwise
- donator_level = DONATOR_LEVEL_MAX
- donor_loadout_points()
- return
-
- //Donator stuff.
- var/datum/db_query/query_donor_select = SSdbcore.NewQuery("SELECT ckey, tier, active FROM donators WHERE ckey=:ckey", list(
- "ckey" = ckey
- ))
-
- if(!query_donor_select.warn_execute())
- qdel(query_donor_select)
- return
-
- while(query_donor_select.NextRow())
- if(!text2num(query_donor_select.item[3]))
- // Inactive donator.
- donator_level = 0
- qdel(query_donor_select)
- return
- donator_level = text2num(query_donor_select.item[2])
- donor_loadout_points()
- break
- qdel(query_donor_select)
-
/client/proc/donor_loadout_points()
if(donator_level > 0 && prefs)
prefs.max_gear_slots = GLOB.configuration.general.base_loadout_points + 5
/client/proc/log_client_to_db(connectiontopic)
- set waitfor = FALSE // This needs to run async because any sleep() inside /client/New() breaks stuff badly
if(IsGuestKey(key))
return
if(!SSdbcore.IsConnected())
return
- var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM player WHERE ckey=:ckey", list(
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, datediff(Now(), firstseen) as age FROM player WHERE ckey=:ckey", list(
"ckey" = ckey
))
if(!query.warn_execute())
@@ -488,67 +479,29 @@
break
qdel(query)
- var/datum/db_query/query_ip = SSdbcore.NewQuery("SELECT ckey FROM player WHERE ip=:address", list(
- "address" = address
- ))
- if(!query_ip.warn_execute())
- qdel(query_ip)
- return
- related_accounts_ip = list()
- while(query_ip.NextRow())
- if(ckey != query_ip.item[1])
- related_accounts_ip.Add("[query_ip.item[1]]")
-
- qdel(query_ip)
-
- var/datum/db_query/query_cid = SSdbcore.NewQuery("SELECT ckey FROM player WHERE computerid=:cid", list(
- "cid" = computer_id
- ))
- if(!query_cid.warn_execute())
- qdel(query_cid)
- return
-
- related_accounts_cid = list()
- while(query_cid.NextRow())
- if(ckey != query_cid.item[1])
- related_accounts_cid.Add("[query_cid.item[1]]")
-
- qdel(query_cid)
var/admin_rank = "Player"
+ // Admins don't get slammed by this, I guess
if(holder)
admin_rank = holder.rank
- // Admins don't get slammed by this, I guess
else
if(check_randomizer(connectiontopic))
return
- //Log all the alts
- if(related_accounts_cid.len)
- log_admin("[key_name(src)] alts:[jointext(related_accounts_cid, " - ")]")
-
-
- var/watchreason = check_watchlist(ckey)
- if(watchreason)
- message_admins("Notice: [key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]")
- SSdiscord.send2discord_simple_noadmins("**\[Watchlist]** [key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]")
- watchlisted = TRUE
-
-
- //Just the standard check to see if it's actually a number
if(sql_id)
+ //Just the standard check to see if it's actually a number
if(istext(sql_id))
sql_id = text2num(sql_id)
if(!isnum(sql_id))
- return
+ return // Return here because if we somehow didnt pull a number from an INT column, EVERYTHING is breaking
- if(sql_id)
var/client_address = address
if(!client_address) // Localhost can sometimes have no address set
client_address = "127.0.0.1"
+
//Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables
- var/datum/db_query/query_update = SSdbcore.NewQuery("UPDATE player SET lastseen = Now(), ip=:sql_ip, computerid=:sql_cid, lastadminrank=:sql_ar WHERE id=:sql_id", list(
+ var/datum/db_query/query_update = SSdbcore.NewQuery("UPDATE player SET lastseen=NOW(), ip=:sql_ip, computerid=:sql_cid, lastadminrank=:sql_ar WHERE id=:sql_id", list(
"sql_ip" = client_address,
"sql_cid" = computer_id,
"sql_ar" = admin_rank,
@@ -558,6 +511,7 @@
if(!query_update.warn_execute())
qdel(query_update)
return
+
qdel(query_update)
// After the regular update
INVOKE_ASYNC(src, /client/.proc/get_byond_account_date, FALSE) // Async to avoid other procs in the client chain being delayed by a web request
@@ -573,7 +527,7 @@
qdel(query_insert)
return
qdel(query_insert)
- // This is their first connection instance, so TRUE here to nofiy admins
+ // This is their first connection instance, so TRUE here to notify admins
// This needs to happen here to ensure they actually have a row to update
INVOKE_ASYNC(src, /client/.proc/get_byond_account_date, TRUE) // Async to avoid other procs in the client chain being delayed by a web request
@@ -1059,7 +1013,7 @@
qdel(query_date)
- // They have a date, lets bail
+ // They have a date already, lets bail
if(byondacc_date)
return
@@ -1100,120 +1054,6 @@
/client/proc/show_update_notice()
to_chat(src, "Your BYOND client (v: [byond_version].[byond_build]) is out of date. This can cause glitches. We highly suggest you download the latest client from byond.com before playing. You can also update via the BYOND launcher application.")
-/**
- * Checks if the client has accepted TOS
- *
- * Runs some checks against vars and the DB to see if the client has accepted TOS.
- * Returns TRUE or FALSE if they have or have not
- */
-/client/proc/check_tos_consent()
- // If there is no TOS, auto accept
- if(!GLOB.join_tos)
- tos_consent = TRUE
- return TRUE
-
- // If theres no DB, assume yes
- if(!SSdbcore.IsConnected())
- tos_consent = TRUE
- return TRUE
-
- var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM privacy WHERE ckey=:ckey AND consent=1", list(
- "ckey" = ckey
- ))
- if(!query.warn_execute())
- qdel(query)
- // If our query failed, just assume yes
- tos_consent = TRUE
- return TRUE
-
- // If we returned a row, they accepted
- while(query.NextRow())
- qdel(query)
- tos_consent = TRUE
- return TRUE
-
- qdel(query)
- // If we are here, they have not accepted, and need to read it
- return FALSE
-
-/**
- * Checks if the client has more than a configured amount of CIDs tied to them in the past
- */
-/client/proc/cid_count_check()
- // If the config is 0, disable this
- if(GLOB.configuration.general.max_client_cid_history == 0)
- return
-
- // If we have no DB, dont even bother
- if(!SSdbcore.IsConnected())
- return
-
- // Now query how many cids they have
- var/datum/db_query/query_cidcheck = SSdbcore.NewQuery("SELECT COUNT(DISTINCT computerID) FROM connection_log WHERE ckey=:ckey", list(
- "ckey" = ckey
- ))
- if(!query_cidcheck.warn_execute())
- qdel(query_cidcheck)
- return
-
- var/cidcount = 0
- if(query_cidcheck.NextRow())
- cidcount = query_cidcheck.item[1]
- qdel(query_cidcheck)
-
- if(cidcount > GLOB.configuration.general.max_client_cid_history)
- // Check their notes for CID tracking in the past
- var/has_note = FALSE
- var/note_text = ""
- var/datum/db_query/query_find_track_note = SSdbcore.NewQuery("SELECT notetext FROM notes WHERE ckey=:ckey AND adminckey=:ackey", list(
- "ckey" = ckey,
- "ackey" = CIDTRACKING_PSUEDO_CKEY
- ))
- if(!query_find_track_note.warn_execute())
- qdel(query_find_track_note)
- return
- if(query_find_track_note.NextRow())
- note_text = query_find_track_note.item[1] // Grab existing note text
- has_note = TRUE
- qdel(query_find_track_note)
-
-
- if(has_note) // They have a note. Update it.
- var/new_text = "Connected on the date of this note with unique CID #[cidcount]"
- // Only update the note if the text is different. Otherwise it bumps the timestamp when it shouldnt
- if(note_text != new_text)
- var/datum/db_query/query_update_track_note = SSdbcore.NewQuery("UPDATE notes SET notetext=:notetext, timestamp=NOW(), round_id=:rid WHERE ckey=:ckey AND adminckey=:ackey", list(
- "notetext" = new_text,
- "ckey" = ckey,
- "ackey" = CIDTRACKING_PSUEDO_CKEY,
- "rid" = GLOB.round_id
- ))
- if(!query_update_track_note.warn_execute())
- qdel(query_update_track_note)
- return
- qdel(query_update_track_note)
-
- else // They dont have a note. Make one.
- // NOT logged because its automatic and will spam logs otherwise
- // Also right checking must be disabled because its a psuedockey, not a real one
- add_note(ckey, "Connected on the date of this note with unique CID #[cidcount]", adminckey = CIDTRACKING_PSUEDO_CKEY, logged = FALSE, checkrights = FALSE, automated = TRUE)
-
- var/show_warning = TRUE
- // Check if they have a note that matches the warning suppressor
- var/datum/db_query/query_find_note = SSdbcore.NewQuery("SELECT id FROM notes WHERE ckey=:ckey AND notetext=:notetext", list(
- "ckey" = ckey,
- "notetext" = CIDWARNING_SUPPRESSED_NOTETEXT
- ))
- if(!query_find_note.warn_execute())
- qdel(query_find_note)
- return
- if(query_find_note.NextRow())
- show_warning = FALSE
- qdel(query_find_note)
-
- if(show_warning)
- message_admins("[ckey] has just connected and has a history of [cidcount] different CIDs. (WebInfo) (Suppress Warning)")
-
/client/proc/update_ambience_pref()
if(prefs.sound & SOUND_AMBIENCE)
if(SSambience.ambience_listening_clients[src] > world.time)
diff --git a/code/modules/client/login_processing/10-load_preferences.dm b/code/modules/client/login_processing/10-load_preferences.dm
new file mode 100644
index 00000000000..8ad85148e46
--- /dev/null
+++ b/code/modules/client/login_processing/10-load_preferences.dm
@@ -0,0 +1,36 @@
+/datum/client_login_processor/load_preferences
+ priority = 10
+
+/datum/client_login_processor/load_preferences/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
+ ooccolor,
+ UI_style,
+ UI_style_color,
+ UI_style_alpha,
+ be_role,
+ default_slot,
+ toggles,
+ toggles_2,
+ sound,
+ volume_mixer,
+ lastchangelog,
+ exp,
+ clientfps,
+ atklog,
+ fuid,
+ parallax,
+ 2fa_status
+ FROM player
+ WHERE ckey=:ckey"}, list(
+ "ckey" = C.ckey
+ ))
+
+ return query
+
+/datum/client_login_processor/load_preferences/process_result(datum/db_query/Q, client/C)
+ C.prefs = GLOB.preferences_datums[C.ckey]
+ if(!C.prefs)
+ C.prefs = new /datum/preferences(C, Q)
+ GLOB.preferences_datums[C.ckey] = C.prefs
+ else
+ C.prefs.parent = C
diff --git a/code/modules/client/login_processing/20-load_characters.dm b/code/modules/client/login_processing/20-load_characters.dm
new file mode 100644
index 00000000000..10eb0035f66
--- /dev/null
+++ b/code/modules/client/login_processing/20-load_characters.dm
@@ -0,0 +1,106 @@
+// AA TODO:
+// - split character out of the prefs datum
+// - make this load **ALL CHARACTERS**
+// - make a post_load() op for stuff
+/datum/client_login_processor/load_characters
+ priority = 20
+
+/datum/client_login_processor/load_characters/get_query(client/C)
+ // Get all their characters
+ var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
+ OOC_Notes,
+ real_name,
+ name_is_always_random,
+ gender,
+ age,
+ species,
+ language,
+ hair_colour,
+ secondary_hair_colour,
+ facial_hair_colour,
+ secondary_facial_hair_colour,
+ skin_tone,
+ skin_colour,
+ marking_colours,
+ head_accessory_colour,
+ hair_style_name,
+ facial_style_name,
+ marking_styles,
+ head_accessory_style_name,
+ alt_head_name,
+ eye_colour,
+ underwear,
+ undershirt,
+ backbag,
+ b_type,
+ alternate_option,
+ job_support_high,
+ job_support_med,
+ job_support_low,
+ job_medsci_high,
+ job_medsci_med,
+ job_medsci_low,
+ job_engsec_high,
+ job_engsec_med,
+ job_engsec_low,
+ job_karma_high,
+ job_karma_med,
+ job_karma_low,
+ flavor_text,
+ med_record,
+ sec_record,
+ gen_record,
+ disabilities,
+ player_alt_titles,
+ organ_data,
+ rlimb_data,
+ nanotrasen_relation,
+ speciesprefs,
+ socks,
+ body_accessory,
+ gear,
+ autohiss,
+ slot
+ FROM characters WHERE ckey=:ckey"}, list(
+ "ckey" = C.ckey
+ ))
+
+ return query
+
+/datum/client_login_processor/load_characters/process_result(datum/db_query/Q, client/C)
+ // If we already loaded their characters, dont do it all again
+ if(C.prefs.characters_loaded)
+ return
+ // Step one, initialize their list of characters
+ C.prefs.character_saves = list()
+ C.prefs.character_saves.len = C.prefs.max_save_slots // Fill the list with empty indexes
+ // Fill it with blank characters first
+ for(var/i in 1 to C.prefs.max_save_slots)
+ var/datum/character_save/CS = new()
+ CS.slot_number = i
+ C.prefs.character_saves[i] = CS
+
+ // Did we load at all
+ var/character_loaded = FALSE
+
+ while(Q.NextRow())
+ character_loaded = TRUE
+ var/datum/character_save/CS = C.prefs.character_saves[Q.item[53]] // Get the slot referenced by this query
+ CS.load(Q) // Let the save handle the query processing
+ CS.valid_save = TRUE
+
+ if(character_loaded)
+ // They have a character, set their active as their default slot
+ C.prefs.active_character = C.prefs.character_saves[C.prefs.default_slot]
+ else
+ // If we are here, they dont have a character. Lets make them a random one
+ var/datum/character_save/CS = C.prefs.character_saves[1] // Get slot 1
+ CS.randomise()
+ CS.real_name = random_name(CS.gender) // Pick a name
+ CS.valid_save = TRUE
+ CS.save()
+ C.prefs.active_character = C.prefs.character_saves[1] // Set slot 1 as their active
+ C.prefs.default_slot = 1
+ C.prefs.save_preferences(C)
+
+ C.prefs.characters_loaded = TRUE // Avoid this happening again
diff --git a/code/modules/client/login_processing/30-tos_consent.dm b/code/modules/client/login_processing/30-tos_consent.dm
new file mode 100644
index 00000000000..452a638cc02
--- /dev/null
+++ b/code/modules/client/login_processing/30-tos_consent.dm
@@ -0,0 +1,24 @@
+/datum/client_login_processor/tos_consent
+ priority = 30
+
+/datum/client_login_processor/tos_consent/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM privacy WHERE ckey=:ckey AND consent=1", list(
+ "ckey" = C.ckey
+ ))
+ return query
+
+/datum/client_login_processor/tos_consent/process_result(datum/db_query/Q, client/C)
+ // If there is no TOS, auto accept
+ if(!GLOB.join_tos)
+ C.tos_consent = TRUE
+ return
+
+ // If our query failed, just assume yes
+ if(Q.last_error)
+ C.tos_consent = TRUE
+ return
+
+ // If we returned a row, they accepted
+ while(Q.NextRow())
+ C.tos_consent = TRUE
+
diff --git a/code/modules/client/login_processing/35-donator_check.dm b/code/modules/client/login_processing/35-donator_check.dm
new file mode 100644
index 00000000000..fabcb6bdcdf
--- /dev/null
+++ b/code/modules/client/login_processing/35-donator_check.dm
@@ -0,0 +1,27 @@
+/datum/client_login_processor/donator_check
+ priority = 35
+
+/datum/client_login_processor/donator_check/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey, tier, active FROM donators WHERE ckey=:ckey", list(
+ "ckey" = C.ckey
+ ))
+ return query
+
+/datum/client_login_processor/donator_check/process_result(datum/db_query/Q, client/C)
+ if(IsGuestKey(C.key))
+ return
+
+ // Admins get all donor perks
+ if(check_rights_client(R_ADMIN, FALSE, C))
+ C.donator_level = DONATOR_LEVEL_MAX
+ C.donor_loadout_points()
+ return
+
+ while(Q.NextRow())
+ if(!text2num(Q.item[3]))
+ // Inactive donator.
+ C.donator_level = 0
+ return
+
+ C.donator_level = text2num(Q.item[2])
+ C.donor_loadout_points()
diff --git a/code/modules/client/login_processing/36-watchlist.dm b/code/modules/client/login_processing/36-watchlist.dm
new file mode 100644
index 00000000000..7bf43ae138d
--- /dev/null
+++ b/code/modules/client/login_processing/36-watchlist.dm
@@ -0,0 +1,15 @@
+/datum/client_login_processor/watchlist
+ priority = 36
+
+/datum/client_login_processor/watchlist/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT reason FROM watch WHERE ckey=:target_ckey", list(
+ "target_ckey" = C.ckey
+ ))
+ return query
+
+/datum/client_login_processor/watchlist/process_result(datum/db_query/Q, client/C)
+ if(Q.NextRow())
+ var/watchreason = Q.item[1]
+ message_admins("Notice: [key_name_admin(C)] is on the watchlist and has just connected - Reason: [watchreason]")
+ SSdiscord.send2discord_simple_noadmins("**\[Watchlist]** [key_name(C)] is on the watchlist and has just connected - Reason: [watchreason]")
+ C.watchlisted = TRUE
diff --git a/code/modules/client/login_processing/37-alts_ip.dm b/code/modules/client/login_processing/37-alts_ip.dm
new file mode 100644
index 00000000000..edf47b0f458
--- /dev/null
+++ b/code/modules/client/login_processing/37-alts_ip.dm
@@ -0,0 +1,14 @@
+/datum/client_login_processor/alts_ip
+ priority = 37
+
+/datum/client_login_processor/alts_ip/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM player WHERE ip=:address", list(
+ "address" = C.address
+ ))
+ return query
+
+/datum/client_login_processor/alts_ip/process_result(datum/db_query/Q, client/C)
+ while(Q.NextRow())
+ if("[Q.item[1]]" == C.ckey)
+ continue
+ C.related_accounts_ip.Add("[Q.item[1]]")
diff --git a/code/modules/client/login_processing/38-alts_cid.dm b/code/modules/client/login_processing/38-alts_cid.dm
new file mode 100644
index 00000000000..541204b529e
--- /dev/null
+++ b/code/modules/client/login_processing/38-alts_cid.dm
@@ -0,0 +1,14 @@
+/datum/client_login_processor/alts_cid
+ priority = 38
+
+/datum/client_login_processor/alts_cid/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM player WHERE computerid=:cid", list(
+ "cid" = C.computer_id
+ ))
+ return query
+
+/datum/client_login_processor/alts_cid/process_result(datum/db_query/Q, client/C)
+ while(Q.NextRow())
+ if("[Q.item[1]]" == C.ckey)
+ continue
+ C.related_accounts_cid.Add("[Q.item[1]]")
diff --git a/code/modules/client/login_processing/39-cid_count.dm b/code/modules/client/login_processing/39-cid_count.dm
new file mode 100644
index 00000000000..c0e8248e892
--- /dev/null
+++ b/code/modules/client/login_processing/39-cid_count.dm
@@ -0,0 +1,71 @@
+/datum/client_login_processor/cid_count
+ priority = 39
+
+/datum/client_login_processor/cid_count/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT COUNT(DISTINCT computerID) FROM connection_log WHERE ckey=:ckey", list(
+ "ckey" = C.ckey
+ ))
+ return query
+
+/datum/client_login_processor/cid_count/process_result(datum/db_query/Q, client/C)
+ // If the config is 0, disable this
+ if(GLOB.configuration.general.max_client_cid_history == 0)
+ return
+
+ // Now query how many cids they have
+ var/cidcount = 0
+ if(Q.NextRow())
+ cidcount = Q.item[1]
+
+ if(cidcount > GLOB.configuration.general.max_client_cid_history)
+ // Check their notes for CID tracking in the past
+ var/has_note = FALSE
+ var/note_text = ""
+ var/datum/db_query/query_find_track_note = SSdbcore.NewQuery("SELECT notetext FROM notes WHERE ckey=:ckey AND adminckey=:ackey", list(
+ "ckey" = C.ckey,
+ "ackey" = CIDTRACKING_PSUEDO_CKEY
+ ))
+ if(!query_find_track_note.warn_execute())
+ qdel(query_find_track_note)
+ return
+ if(query_find_track_note.NextRow())
+ note_text = query_find_track_note.item[1] // Grab existing note text
+ has_note = TRUE
+ qdel(query_find_track_note)
+
+
+ if(has_note) // They have a note. Update it.
+ var/new_text = "Connected on the date of this note with unique CID #[cidcount]"
+ // Only update the note if the text is different. Otherwise it bumps the timestamp when it shouldnt
+ if(note_text != new_text)
+ var/datum/db_query/query_update_track_note = SSdbcore.NewQuery("UPDATE notes SET notetext=:notetext, timestamp=NOW(), round_id=:rid WHERE ckey=:ckey AND adminckey=:ackey", list(
+ "notetext" = new_text,
+ "ckey" = C.ckey,
+ "ackey" = CIDTRACKING_PSUEDO_CKEY,
+ "rid" = GLOB.round_id
+ ))
+ if(!query_update_track_note.warn_execute())
+ qdel(query_update_track_note)
+ return
+ qdel(query_update_track_note)
+
+ else // They dont have a note. Make one.
+ // NOT logged because its automatic and will spam logs otherwise
+ // Also right checking must be disabled because its a psuedockey, not a real one
+ add_note(C.ckey, "Connected on the date of this note with unique CID #[cidcount]", adminckey = CIDTRACKING_PSUEDO_CKEY, logged = FALSE, checkrights = FALSE, automated = TRUE)
+
+ var/show_warning = TRUE
+ // Check if they have a note that matches the warning suppressor
+ var/datum/db_query/query_find_note = SSdbcore.NewQuery("SELECT id FROM notes WHERE ckey=:ckey AND notetext=:notetext", list(
+ "ckey" = C.ckey,
+ "notetext" = CIDWARNING_SUPPRESSED_NOTETEXT
+ ))
+ if(!query_find_note.warn_execute())
+ qdel(query_find_note)
+ return
+ if(query_find_note.NextRow())
+ show_warning = FALSE
+ qdel(query_find_note)
+
+ if(show_warning)
+ message_admins("[C.ckey] has just connected and has a history of [cidcount] different CIDs. (WebInfo) (Suppress Warning)")
diff --git a/code/modules/client/login_processing/__client_login_processor.dm b/code/modules/client/login_processing/__client_login_processor.dm
new file mode 100644
index 00000000000..242e797f680
--- /dev/null
+++ b/code/modules/client/login_processing/__client_login_processor.dm
@@ -0,0 +1,51 @@
+/**
+ * # Client Login Processor Framework
+ *
+ * The holder class for all client data processing
+ *
+ * This framework is designed for loading in client data from the database.
+ * Login processors have their own queries, which will be put into one async batch and
+ * executed at the same time, to reduce the time it takes for a client to login.
+ * Login processors can also be given priorities to have things fire in specific orders
+ * EG: Load their preferences before their job bans, etc etc
+ *
+ * When creating these, please name the files with the priority at the start, and the typepath after
+ * EG: 10-load_preferences.dm
+ * This makes it easier to track stuff down -AA07
+ *
+ * Also if you have used other languages before with "interface" types (Java, C# (Microsoft Java), etc),
+ * treat this class as one of those. [get_query(client/C)] and [process_result(datum/db_query/Q, client/C)] MUST be overriden
+ */
+/datum/client_login_processor
+ /// The login priority. A lower priority will fire first
+ var/priority = 0
+
+/**
+ * Query Getter
+ *
+ * Gets the DB query for this login processor
+ *
+ * Takes the client as an arg instead of just the ckey incase we need more data (IP, CID, etc).
+ * Returns a DB query datum.
+ *
+ * Arguments:
+ * * C - The client to use to generate the query
+ */
+/datum/client_login_processor/proc/get_query(client/C)
+ RETURN_TYPE(/datum/db_query)
+ CRASH("get_query() not overriden for [type]!")
+
+
+
+/**
+ * Result Processor
+ *
+ * Takes the (now executed) query and the client and parses the required data out
+ * Note: This can be a no-op if you want to just update the DB, just return on the override
+ *
+ * Arguments:
+ * * Q - The DB query to process data from
+ * * C - The client to store stuff on
+ */
+/datum/client_login_processor/proc/process_result(datum/db_query/Q, client/C)
+ CRASH("process_result() not overriden for [type]!")
diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/client/preference/character.dm
similarity index 50%
rename from code/modules/mob/new_player/preferences_setup.dm
rename to code/modules/client/preference/character.dm
index 3fe022269da..7ab967ed7e0 100644
--- a/code/modules/mob/new_player/preferences_setup.dm
+++ b/code/modules/client/preference/character.dm
@@ -1,6 +1,528 @@
-/datum/preferences
- //The mob should have a gender you want before running this proc. Will run fine without H
-/datum/preferences/proc/random_character(gender_override)
+// PSA To anyone who opens this:
+// Good fucking luck. You will need this: https://www.youtube.com/watch?v=W9GaIbECisQ
+
+
+/datum/character_save
+ var/real_name //our character's name
+ var/be_random_name = 0 //whether we are a random name every round
+ var/gender = MALE //gender of character (well duh)
+ var/age = 30 //age of character
+ var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2).
+ var/b_type = "A+" //blood type (not-chooseable)
+ var/underwear = "Nude" //underwear type
+ var/undershirt = "Nude" //undershirt type
+ var/socks = "Nude" //socks type
+ var/backbag = GBACKPACK //backpack type
+ var/ha_style = "None" //Head accessory style
+ var/hacc_colour = "#000000" //Head accessory colour
+ var/list/m_styles = list(
+ "head" = "None",
+ "body" = "None",
+ "tail" = "None"
+ ) //Marking styles.
+ var/list/m_colours = list(
+ "head" = "#000000",
+ "body" = "#000000",
+ "tail" = "#000000"
+ ) //Marking colours.
+ var/h_style = "Bald" //Hair type
+ var/h_colour = "#000000" //Hair color
+ var/h_sec_colour = "#000000" //Secondary hair color
+ var/f_style = "Shaved" //Facial hair type
+ var/f_colour = "#000000" //Facial hair color
+ var/f_sec_colour = "#000000" //Secondary facial hair color
+ var/s_tone = 0 //Skin tone
+ var/s_colour = "#000000" //Skin color
+ var/e_colour = "#000000" //Eye color
+ var/alt_head = "None" //Alt head style.
+ var/species = "Human"
+ var/language = "None" //Secondary language
+ var/autohiss_mode = AUTOHISS_OFF //Species autohiss level. OFF, BASIC, FULL.
+
+ var/body_accessory = null
+
+ var/speciesprefs = 0//I hate having to do this, I really do (Using this for oldvox code, making names universal I guess
+
+ //Mob preview
+ var/icon/preview_icon = null
+ var/icon/preview_icon_front = null
+ var/icon/preview_icon_side = null
+
+ //Jobs, uses bitflags
+ var/job_support_high = 0
+ var/job_support_med = 0
+ var/job_support_low = 0
+
+ var/job_medsci_high = 0
+ var/job_medsci_med = 0
+ var/job_medsci_low = 0
+
+ var/job_engsec_high = 0
+ var/job_engsec_med = 0
+ var/job_engsec_low = 0
+
+ var/job_karma_high = 0
+ var/job_karma_med = 0
+ var/job_karma_low = 0
+
+ //Keeps track of preferrence for not getting any wanted jobs
+ var/alternate_option = 2
+
+ // maps each organ to either null(intact), "cyborg" or "amputated"
+ // will probably not be able to do this for head and torso ;)
+ var/list/organ_data = list()
+ var/list/rlimb_data = list()
+
+ var/list/player_alt_titles = new() // the default name of a job like "Medical Doctor"
+ var/flavor_text = ""
+ var/med_record = ""
+ var/sec_record = ""
+ var/gen_record = ""
+ var/disabilities = 0
+
+ var/nanotrasen_relation = "Neutral"
+
+ // OOC Metadata:
+ var/metadata = ""
+
+ //Gear stuff
+ var/list/loadout_gear = list()
+
+ /// Is this character from the DB?
+ var/from_db = FALSE
+ /// Is this character valid to be picked? This is necessary to avoid someone getting a bald human called "Character 30"
+ var/valid_save = FALSE
+ /// Character slot number, used for saves and stuff.
+ var/slot_number = 0
+
+/datum/character_save/proc/save(client/C)
+ var/organlist
+ var/rlimblist
+ var/playertitlelist
+ var/gearlist
+
+ var/markingcolourslist = list2params(m_colours)
+ var/markingstyleslist = list2params(m_styles)
+ if(!isemptylist(organ_data))
+ organlist = list2params(organ_data)
+ if(!isemptylist(rlimb_data))
+ rlimblist = list2params(rlimb_data)
+ if(!isemptylist(player_alt_titles))
+ playertitlelist = list2params(player_alt_titles)
+ if(!isemptylist(loadout_gear))
+ gearlist = list2params(loadout_gear)
+
+ var/datum/db_query/firstquery = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey ORDER BY slot", list(
+ "ckey" = C.ckey
+ ))
+ if(!firstquery.warn_execute())
+ qdel(firstquery)
+ return
+ while(firstquery.NextRow())
+ if(text2num(firstquery.item[1]) == slot_number) // Check if the character exists
+ var/datum/db_query/query = SSdbcore.NewQuery({"UPDATE characters
+ SET
+ OOC_Notes=:metadata,
+ real_name=:real_name,
+ name_is_always_random=:be_random_name,
+ gender=:gender,
+ age=:age,
+ species=:species,
+ language=:language,
+ hair_colour=:h_colour,
+ secondary_hair_colour=:h_sec_colour,
+ facial_hair_colour=:f_colour,
+ secondary_facial_hair_colour=:f_sec_colour,
+ skin_tone=:s_tone,
+ skin_colour=:s_colour,
+ marking_colours=:markingcolourslist,
+ head_accessory_colour=:hacc_colour,
+ hair_style_name=:h_style,
+ facial_style_name=:f_style,
+ marking_styles=:markingstyleslist,
+ head_accessory_style_name=:ha_style,
+ alt_head_name=:alt_head,
+ eye_colour=:e_colour,
+ underwear=:underwear,
+ undershirt=:undershirt,
+ backbag=:backbag,
+ b_type=:b_type,
+ alternate_option=:alternate_option,
+ job_support_high=:job_support_high,
+ job_support_med=:job_support_med,
+ job_support_low=:job_support_low,
+ job_medsci_high=:job_medsci_high,
+ job_medsci_med=:job_medsci_med,
+ job_medsci_low=:job_medsci_low,
+ job_engsec_high=:job_engsec_high,
+ job_engsec_med=:job_engsec_med,
+ job_engsec_low=:job_engsec_low,
+ job_karma_high=:job_karma_high,
+ job_karma_med=:job_karma_med,
+ job_karma_low=:job_karma_low,
+ flavor_text=:flavor_text,
+ med_record=:med_record,
+ sec_record=:sec_record,
+ gen_record=:gen_record,
+ player_alt_titles=:playertitlelist,
+ disabilities=:disabilities,
+ organ_data=:organlist,
+ rlimb_data=:rlimblist,
+ nanotrasen_relation=:nanotrasen_relation,
+ speciesprefs=:speciesprefs,
+ socks=:socks,
+ body_accessory=:body_accessory,
+ gear=:gearlist,
+ autohiss=:autohiss_mode
+ WHERE ckey=:ckey
+ AND slot=:slot"}, list(
+ // OH GOD SO MANY PARAMETERS
+ "metadata" = metadata,
+ "real_name" = real_name,
+ "be_random_name" = be_random_name,
+ "gender" = gender,
+ "age" = age,
+ "species" = species,
+ "language" = language,
+ "h_colour" = h_colour,
+ "h_sec_colour" = h_sec_colour,
+ "f_colour" = f_colour,
+ "f_sec_colour" = f_sec_colour,
+ "s_tone" = s_tone,
+ "s_colour" = s_colour,
+ "markingcolourslist" = markingcolourslist,
+ "hacc_colour" = hacc_colour,
+ "h_style" = h_style,
+ "f_style" = f_style,
+ "markingstyleslist" = markingstyleslist,
+ "ha_style" = ha_style,
+ "alt_head" = (alt_head ? alt_head : ""), // This it intentional. It wont work without it!
+ "e_colour" = e_colour,
+ "underwear" = underwear,
+ "undershirt" = undershirt,
+ "backbag" = backbag,
+ "b_type" = b_type,
+ "alternate_option" = alternate_option,
+ "job_support_high" = job_support_high,
+ "job_support_med" = job_support_med,
+ "job_support_low" = job_support_low,
+ "job_medsci_high" = job_medsci_high,
+ "job_medsci_med" = job_medsci_med,
+ "job_medsci_low" = job_medsci_low,
+ "job_engsec_high" = job_engsec_high,
+ "job_engsec_med" = job_engsec_med,
+ "job_engsec_low" = job_engsec_low,
+ "job_karma_high" = job_karma_high,
+ "job_karma_med" = job_karma_med,
+ "job_karma_low" = job_karma_low,
+ "flavor_text" = flavor_text,
+ "med_record" = med_record,
+ "sec_record" = sec_record,
+ "gen_record" = gen_record,
+ "playertitlelist" = (playertitlelist ? playertitlelist : ""), // This it intentnional. It wont work without it!
+ "disabilities" = disabilities,
+ "organlist" = (organlist ? organlist : ""),
+ "rlimblist" = (rlimblist ? rlimblist : ""),
+ "nanotrasen_relation" = nanotrasen_relation,
+ "speciesprefs" = speciesprefs,
+ "socks" = socks,
+ "body_accessory" = (body_accessory ? body_accessory : ""),
+ "gearlist" = (gearlist ? gearlist : ""),
+ "autohiss_mode" = autohiss_mode,
+ "ckey" = C.ckey,
+ "slot" = slot_number
+ ))
+
+ if(!query.warn_execute())
+ qdel(firstquery)
+ qdel(query)
+ return
+ qdel(firstquery)
+ qdel(query)
+ return 1
+
+ qdel(firstquery)
+
+ var/datum/db_query/query = SSdbcore.NewQuery({"
+ INSERT INTO characters (ckey, slot, OOC_Notes, real_name, name_is_always_random, gender,
+ age, species, language,
+ hair_colour, secondary_hair_colour,
+ facial_hair_colour, secondary_facial_hair_colour,
+ skin_tone, skin_colour,
+ marking_colours,
+ head_accessory_colour,
+ hair_style_name,
+ facial_style_name,
+ marking_styles,
+ head_accessory_style_name,
+ alt_head_name,
+ eye_colour,
+ underwear, undershirt,
+ backbag, b_type, alternate_option,
+ job_support_high, job_support_med, job_support_low,
+ job_medsci_high, job_medsci_med, job_medsci_low,
+ job_engsec_high, job_engsec_med, job_engsec_low,
+ job_karma_high, job_karma_med, job_karma_low,
+ flavor_text,
+ med_record,
+ sec_record,
+ gen_record,
+ player_alt_titles,
+ disabilities, organ_data, rlimb_data, nanotrasen_relation, speciesprefs,
+ socks, body_accessory, gear, autohiss)
+ VALUES
+ (:ckey, :slot, :metadata, :name, :be_random_name, :gender,
+ :age, :species, :language,
+ :h_colour, :h_sec_colour,
+ :f_colour, :f_sec_colour,
+ :s_tone, :s_colour,
+ :markingcolourslist,
+ :hacc_colour,
+ :h_style,
+ :f_style,
+ :markingstyleslist,
+ :ha_style,
+ :alt_head,
+ :e_colour,
+ :underwear, :undershirt,
+ :backbag, :b_type, :alternate_option,
+ :job_support_high, :job_support_med, :job_support_low,
+ :job_medsci_high, :job_medsci_med, :job_medsci_low,
+ :job_engsec_high, :job_engsec_med, :job_engsec_low,
+ :job_karma_high, :job_karma_med, :job_karma_low,
+ :flavor_text,
+ :med_record,
+ :sec_record,
+ :gen_record,
+ :playertitlelist,
+ :disabilities, :organlist, :rlimblist, :nanotrasen_relation, :speciesprefs,
+ :socks, :body_accessory, :gearlist, :autohiss_mode)
+ "}, list(
+ // This has too many params for anyone to look at this without going insae
+ "ckey" = C.ckey,
+ "slot" = slot_number,
+ "metadata" = metadata,
+ "name" = real_name,
+ "be_random_name" = be_random_name,
+ "gender" = gender,
+ "age" = age,
+ "species" = species,
+ "language" = language,
+ "h_colour" = h_colour,
+ "h_sec_colour" = h_sec_colour,
+ "f_colour" = f_colour,
+ "f_sec_colour" = f_sec_colour,
+ "s_tone" = s_tone,
+ "s_colour" = s_colour,
+ "markingcolourslist" = markingcolourslist,
+ "hacc_colour" = hacc_colour,
+ "h_style" = h_style,
+ "f_style" = f_style,
+ "markingstyleslist" = markingstyleslist,
+ "ha_style" = ha_style,
+ "alt_head" = (alt_head ? alt_head : "None"), // bane of my fucking life
+ "e_colour" = e_colour,
+ "underwear" = underwear,
+ "undershirt" = undershirt,
+ "backbag" = backbag,
+ "b_type" = b_type,
+ "alternate_option" = alternate_option,
+ "job_support_high" = job_support_high,
+ "job_support_med" = job_support_med,
+ "job_support_low" = job_support_low,
+ "job_medsci_high" = job_medsci_high,
+ "job_medsci_med" = job_medsci_med,
+ "job_medsci_low" = job_medsci_low,
+ "job_engsec_high" = job_engsec_high,
+ "job_engsec_med" = job_engsec_med,
+ "job_engsec_low" = job_engsec_low,
+ "job_karma_high" = job_karma_high,
+ "job_karma_med" = job_karma_med,
+ "job_karma_low" = job_karma_low,
+ "flavor_text" = flavor_text,
+ "med_record" = med_record,
+ "sec_record" = sec_record,
+ "gen_record" = gen_record,
+ "playertitlelist" = (playertitlelist ? playertitlelist : ""), // This it intentnional. It wont work without it!
+ "disabilities" = disabilities,
+ "organlist" = (organlist ? organlist : ""),
+ "rlimblist" = (rlimblist ? rlimblist : ""),
+ "nanotrasen_relation" = nanotrasen_relation,
+ "speciesprefs" = speciesprefs,
+ "socks" = socks,
+ "body_accessory" = (body_accessory ? body_accessory : ""),
+ "gearlist" = (gearlist ? gearlist : ""),
+ "autohiss_mode" = autohiss_mode
+ ))
+
+ if(!query.warn_execute())
+ qdel(query)
+ return
+
+ qdel(query)
+ from_db = TRUE
+ return 1
+
+
+/datum/character_save/proc/load(datum/db_query/query)
+ //Character
+ metadata = query.item[1]
+ real_name = query.item[2]
+ be_random_name = text2num(query.item[3])
+ gender = query.item[4]
+ age = text2num(query.item[5])
+ species = query.item[6]
+ language = query.item[7]
+
+ h_colour = query.item[8]
+ h_sec_colour = query.item[9]
+ f_colour = query.item[10]
+ f_sec_colour = query.item[11]
+ s_tone = text2num(query.item[12])
+ s_colour = query.item[13]
+ m_colours = params2list(query.item[14])
+ hacc_colour = query.item[15]
+ h_style = query.item[16]
+ f_style = query.item[17]
+ m_styles = params2list(query.item[18])
+ ha_style = query.item[19]
+ alt_head = query.item[20]
+ e_colour = query.item[21]
+ underwear = query.item[22]
+ undershirt = query.item[23]
+ backbag = query.item[24]
+ b_type = query.item[25]
+
+
+ //Jobs
+ alternate_option = text2num(query.item[26])
+ job_support_high = text2num(query.item[27])
+ job_support_med = text2num(query.item[28])
+ job_support_low = text2num(query.item[29])
+ job_medsci_high = text2num(query.item[30])
+ job_medsci_med = text2num(query.item[31])
+ job_medsci_low = text2num(query.item[32])
+ job_engsec_high = text2num(query.item[33])
+ job_engsec_med = text2num(query.item[34])
+ job_engsec_low = text2num(query.item[35])
+ job_karma_high = text2num(query.item[36])
+ job_karma_med = text2num(query.item[37])
+ job_karma_low = text2num(query.item[38])
+
+ //Miscellaneous
+ flavor_text = query.item[39]
+ med_record = query.item[40]
+ sec_record = query.item[41]
+ gen_record = query.item[42]
+ // Apparently, the preceding vars weren't always encoded properly...
+ if(findtext(flavor_text, "<")) // ... so let's clumsily check for tags!
+ flavor_text = html_encode(flavor_text)
+ if(findtext(med_record, "<"))
+ med_record = html_encode(med_record)
+ if(findtext(sec_record, "<"))
+ sec_record = html_encode(sec_record)
+ if(findtext(gen_record, "<"))
+ gen_record = html_encode(gen_record)
+ disabilities = text2num(query.item[43])
+ player_alt_titles = params2list(query.item[44])
+ organ_data = params2list(query.item[45])
+ rlimb_data = params2list(query.item[46])
+ nanotrasen_relation = query.item[47]
+ speciesprefs = text2num(query.item[48])
+
+ //socks
+ socks = query.item[49]
+ body_accessory = query.item[50]
+ loadout_gear = params2list(query.item[51])
+ autohiss_mode = text2num(query.item[52])
+
+ //Sanitize
+ var/datum/species/SP = GLOB.all_species[species]
+ metadata = sanitize_text(metadata, initial(metadata))
+ real_name = reject_bad_name(real_name, 1)
+
+ if(isnull(species))
+ species = "Human"
+
+ if(isnull(language))
+ language = "None"
+
+ if(isnull(nanotrasen_relation))
+ nanotrasen_relation = initial(nanotrasen_relation)
+
+ if(isnull(speciesprefs))
+ speciesprefs = initial(speciesprefs)
+
+ if(!real_name)
+ real_name = random_name(gender,species)
+
+ be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
+ gender = sanitize_gender(gender, FALSE, !SP.has_gender)
+ age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
+ h_colour = sanitize_hexcolor(h_colour)
+ h_sec_colour = sanitize_hexcolor(h_sec_colour)
+ f_colour = sanitize_hexcolor(f_colour)
+ f_sec_colour = sanitize_hexcolor(f_sec_colour)
+ s_tone = sanitize_integer(s_tone, -185, 34, initial(s_tone))
+ s_colour = sanitize_hexcolor(s_colour)
+
+ for(var/marking_location in m_colours)
+ m_colours[marking_location] = sanitize_hexcolor(m_colours[marking_location], DEFAULT_MARKING_COLOURS[marking_location])
+
+ hacc_colour = sanitize_hexcolor(hacc_colour)
+ h_style = sanitize_inlist(h_style, GLOB.hair_styles_public_list, initial(h_style))
+ f_style = sanitize_inlist(f_style, GLOB.facial_hair_styles_list, initial(f_style))
+
+ for(var/marking_location in m_styles)
+ m_styles[marking_location] = sanitize_inlist(m_styles[marking_location], GLOB.marking_styles_list, DEFAULT_MARKING_STYLES[marking_location])
+
+ ha_style = sanitize_inlist(ha_style, GLOB.head_accessory_styles_list, initial(ha_style))
+ alt_head = sanitize_inlist(alt_head, GLOB.alt_heads_list, initial(alt_head))
+ e_colour = sanitize_hexcolor(e_colour)
+ underwear = sanitize_text(underwear, initial(underwear))
+ undershirt = sanitize_text(undershirt, initial(undershirt))
+ backbag = sanitize_text(backbag, initial(backbag))
+ b_type = sanitize_text(b_type, initial(b_type))
+ autohiss_mode = sanitize_integer(autohiss_mode, 0, 2, initial(autohiss_mode))
+
+ alternate_option = sanitize_integer(alternate_option, 0, 2, initial(alternate_option))
+ job_support_high = sanitize_integer(job_support_high, 0, 65535, initial(job_support_high))
+ job_support_med = sanitize_integer(job_support_med, 0, 65535, initial(job_support_med))
+ job_support_low = sanitize_integer(job_support_low, 0, 65535, initial(job_support_low))
+ job_medsci_high = sanitize_integer(job_medsci_high, 0, 65535, initial(job_medsci_high))
+ job_medsci_med = sanitize_integer(job_medsci_med, 0, 65535, initial(job_medsci_med))
+ job_medsci_low = sanitize_integer(job_medsci_low, 0, 65535, initial(job_medsci_low))
+ job_engsec_high = sanitize_integer(job_engsec_high, 0, 65535, initial(job_engsec_high))
+ job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med))
+ job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low))
+ job_karma_high = sanitize_integer(job_karma_high, 0, 65535, initial(job_karma_high))
+ job_karma_med = sanitize_integer(job_karma_med, 0, 65535, initial(job_karma_med))
+ job_karma_low = sanitize_integer(job_karma_low, 0, 65535, initial(job_karma_low))
+ disabilities = sanitize_integer(disabilities, 0, 65535, initial(disabilities))
+
+ socks = sanitize_text(socks, initial(socks))
+ body_accessory = sanitize_text(body_accessory, initial(body_accessory))
+
+ if(!player_alt_titles)
+ player_alt_titles = new()
+ if(!organ_data)
+ src.organ_data = list()
+ if(!rlimb_data)
+ src.rlimb_data = list()
+ if(!loadout_gear)
+ loadout_gear = list()
+
+ // Check if the current body accessory exists
+ if(!GLOB.body_accessory_by_name[body_accessory])
+ body_accessory = null
+
+ from_db = TRUE
+ valid_save = TRUE
+
+ return TRUE
+
+/datum/character_save/proc/randomise(gender_override)
+ b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
var/datum/species/S = GLOB.all_species[species]
if(!istype(S)) //The species was invalid. Set the species to the default, fetch the datum for that species and generate a random character.
species = initial(species)
@@ -48,7 +570,7 @@
age = rand(AGE_MIN, AGE_MAX)
-/datum/preferences/proc/randomize_hair_color(target = "hair")
+/datum/character_save/proc/randomize_hair_color(target = "hair")
if(prob (75) && target == "facial") // Chance to inherit hair color
f_colour = h_colour
return
@@ -102,7 +624,7 @@
if("facial")
f_colour = rgb(red, green, blue)
-/datum/preferences/proc/randomize_eyes_color()
+/datum/character_save/proc/randomize_eyes_color()
var/red
var/green
var/blue
@@ -148,7 +670,7 @@
e_colour = rgb(red, green, blue)
-/datum/preferences/proc/randomize_skin_color(pass_on)
+/datum/character_save/proc/randomize_skin_color(pass_on)
var/red
var/green
var/blue
@@ -197,7 +719,7 @@
else
s_colour = rgb(red, green, blue)
-/datum/preferences/proc/blend_backpack(icon/clothes_s, backbag, satchel, backpack="backpack")
+/datum/character_save/proc/blend_backpack(icon/clothes_s, backbag, satchel, backpack="backpack")
switch(backbag)
if(2)
clothes_s.Blend(new /icon('icons/mob/back.dmi', backpack), ICON_OVERLAY)
@@ -207,7 +729,7 @@
clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY)
return clothes_s
-/datum/preferences/proc/update_preview_icon(for_observer=0) //seriously. This is horrendous.
+/datum/character_save/proc/update_preview_icon(for_observer=0) //seriously. This is horrendous.
qdel(preview_icon_front)
qdel(preview_icon_side)
qdel(preview_icon)
@@ -950,3 +1472,654 @@
qdel(undershirt_s)
qdel(socks_s)
qdel(clothes_s)
+
+
+
+/datum/character_save/proc/get_gear_metadata(datum/gear/G)
+ . = loadout_gear[G.display_name]
+ if(!.)
+ . = list()
+ loadout_gear[G.display_name] = .
+
+/datum/character_save/proc/get_tweak_metadata(datum/gear/G, datum/gear_tweak/tweak)
+ var/list/metadata = get_gear_metadata(G)
+ . = metadata["[tweak]"]
+ if(!.)
+ . = tweak.get_default()
+ metadata["[tweak]"] = .
+
+/datum/character_save/proc/set_tweak_metadata(datum/gear/G, datum/gear_tweak/tweak, new_metadata)
+ var/list/metadata = get_gear_metadata(G)
+ metadata["[tweak]"] = new_metadata
+
+
+/datum/character_save/proc/SetJobPreferenceLevel(datum/job/job, level)
+ if(!job)
+ return 0
+
+ if(level == 1) // to high
+ // remove any other job(s) set to high
+ job_support_med |= job_support_high
+ job_engsec_med |= job_engsec_high
+ job_medsci_med |= job_medsci_high
+ job_karma_med |= job_karma_high
+ job_support_high = 0
+ job_engsec_high = 0
+ job_medsci_high = 0
+ job_karma_high = 0
+
+ if(job.department_flag == JOBCAT_SUPPORT)
+ job_support_low &= ~job.flag
+ job_support_med &= ~job.flag
+ job_support_high &= ~job.flag
+
+ switch(level)
+ if(1)
+ job_support_high |= job.flag
+ if(2)
+ job_support_med |= job.flag
+ if(3)
+ job_support_low |= job.flag
+
+ return 1
+ else if(job.department_flag == JOBCAT_ENGSEC)
+ job_engsec_low &= ~job.flag
+ job_engsec_med &= ~job.flag
+ job_engsec_high &= ~job.flag
+
+ switch(level)
+ if(1)
+ job_engsec_high |= job.flag
+ if(2)
+ job_engsec_med |= job.flag
+ if(3)
+ job_engsec_low |= job.flag
+
+ return 1
+ else if(job.department_flag == JOBCAT_MEDSCI)
+ job_medsci_low &= ~job.flag
+ job_medsci_med &= ~job.flag
+ job_medsci_high &= ~job.flag
+
+ switch(level)
+ if(1)
+ job_medsci_high |= job.flag
+ if(2)
+ job_medsci_med |= job.flag
+ if(3)
+ job_medsci_low |= job.flag
+
+ return 1
+ else if(job.department_flag == JOBCAT_KARMA)
+ job_karma_low &= ~job.flag
+ job_karma_med &= ~job.flag
+ job_karma_high &= ~job.flag
+
+ switch(level)
+ if(1)
+ job_karma_high |= job.flag
+ if(2)
+ job_karma_med |= job.flag
+ if(3)
+ job_karma_low |= job.flag
+
+ return 1
+
+ return 0
+
+/datum/character_save/proc/ShowDisabilityState(mob/user, flag, label)
+ return "[label]: [disabilities & flag ? "Yes" : "No"]"
+
+/datum/character_save/proc/SetDisabilities(mob/user)
+ var/datum/species/S = GLOB.all_species[species]
+ var/HTML = ""
+ HTML += ""
+
+ if(CAN_WINGDINGS in S.species_traits)
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_WINGDINGS, "Speak in Wingdings")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_NEARSIGHTED, "Nearsighted")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_COLOURBLIND, "Colourblind")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_BLIND, "Blind")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_DEAF, "Deaf")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_MUTE, "Mute")
+ if(!(TRAIT_NOFAT in S.inherent_traits))
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_FAT, "Obese")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_NERVOUS, "Stutter")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_SWEDISH, "Swedish accent")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_CHAV, "Chav accent")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_LISP, "Lisp")
+ HTML += ShowDisabilityState(user, DISABILITY_FLAG_DIZZY, "Dizziness")
+
+
+ HTML += {"
+ \[Done\]
+ \[Reset\]
+ "}
+
+ var/datum/browser/popup = new(user, "disabil", "Choose Disabilities
", 350, 380)
+ popup.set_content(HTML)
+ popup.open(0)
+
+/datum/character_save/proc/SetRecords(mob/user)
+ var/HTML = ""
+ HTML += ""
+
+ HTML += "Medical Records
"
+
+ if(length(med_record) <= 40)
+ HTML += "[med_record]"
+ else
+ HTML += "[copytext(med_record, 1, 37)]..."
+
+ HTML += "
Employment Records
"
+
+ if(length(gen_record) <= 40)
+ HTML += "[gen_record]"
+ else
+ HTML += "[copytext(gen_record, 1, 37)]..."
+
+ HTML += "
Security Records
"
+
+ if(length(sec_record) <= 40)
+ HTML += "[sec_record]
"
+ else
+ HTML += "[copytext(sec_record, 1, 37)]...
"
+
+ HTML += "\[Done\]"
+ HTML += ""
+
+ var/datum/browser/popup = new(user, "records", "Character Records
", 350, 300)
+ popup.set_content(HTML)
+ popup.open(0)
+
+/datum/character_save/proc/GetPlayerAltTitle(datum/job/job)
+ return player_alt_titles.Find(job.title) > 0 \
+ ? player_alt_titles[job.title] \
+ : job.title
+
+/datum/character_save/proc/SetPlayerAltTitle(datum/job/job, new_title)
+ // remove existing entry
+ if(player_alt_titles.Find(job.title))
+ player_alt_titles -= job.title
+ // add one if it's not default
+ if(job.title != new_title)
+ player_alt_titles[job.title] = new_title
+
+/datum/character_save/proc/ResetJobs()
+ job_support_high = 0
+ job_support_med = 0
+ job_support_low = 0
+
+ job_medsci_high = 0
+ job_medsci_med = 0
+ job_medsci_low = 0
+
+ job_engsec_high = 0
+ job_engsec_med = 0
+ job_engsec_low = 0
+
+ job_karma_high = 0
+ job_karma_med = 0
+ job_karma_low = 0
+
+
+/datum/character_save/proc/GetJobDepartment(datum/job/job, level)
+ if(!job || !level) return 0
+ switch(job.department_flag)
+ if(JOBCAT_SUPPORT)
+ switch(level)
+ if(1)
+ return job_support_high
+ if(2)
+ return job_support_med
+ if(3)
+ return job_support_low
+ if(JOBCAT_MEDSCI)
+ switch(level)
+ if(1)
+ return job_medsci_high
+ if(2)
+ return job_medsci_med
+ if(3)
+ return job_medsci_low
+ if(JOBCAT_ENGSEC)
+ switch(level)
+ if(1)
+ return job_engsec_high
+ if(2)
+ return job_engsec_med
+ if(3)
+ return job_engsec_low
+ if(JOBCAT_KARMA)
+ switch(level)
+ if(1)
+ return job_karma_high
+ if(2)
+ return job_karma_med
+ if(3)
+ return job_karma_low
+ return 0
+
+/datum/character_save/proc/SetJobDepartment(datum/job/job, level)
+ if(!job || !level) return 0
+ switch(level)
+ if(1)//Only one of these should ever be active at once so clear them all here
+ job_support_high = 0
+ job_medsci_high = 0
+ job_engsec_high = 0
+ job_karma_high = 0
+ return 1
+ if(2)//Set current highs to med, then reset them
+ job_support_med |= job_support_high
+ job_medsci_med |= job_medsci_high
+ job_engsec_med |= job_engsec_high
+ job_karma_med |= job_karma_high
+ job_support_high = 0
+ job_medsci_high = 0
+ job_engsec_high = 0
+ job_karma_high = 0
+
+ switch(job.department_flag)
+ if(JOBCAT_SUPPORT)
+ switch(level)
+ if(2)
+ job_support_high = job.flag
+ job_support_med &= ~job.flag
+ if(3)
+ job_support_med |= job.flag
+ job_support_low &= ~job.flag
+ else
+ job_support_low |= job.flag
+ if(JOBCAT_MEDSCI)
+ switch(level)
+ if(2)
+ job_medsci_high = job.flag
+ job_medsci_med &= ~job.flag
+ if(3)
+ job_medsci_med |= job.flag
+ job_medsci_low &= ~job.flag
+ else
+ job_medsci_low |= job.flag
+ if(JOBCAT_ENGSEC)
+ switch(level)
+ if(2)
+ job_engsec_high = job.flag
+ job_engsec_med &= ~job.flag
+ if(3)
+ job_engsec_med |= job.flag
+ job_engsec_low &= ~job.flag
+ else
+ job_engsec_low |= job.flag
+ if(JOBCAT_KARMA)
+ switch(level)
+ if(2)
+ job_karma_high = job.flag
+ job_karma_med &= ~job.flag
+ if(3)
+ job_karma_med |= job.flag
+ job_karma_low &= ~job.flag
+ else
+ job_karma_low |= job.flag
+ return 1
+
+/datum/character_save/proc/copy_to(mob/living/carbon/human/character)
+ var/datum/species/S = GLOB.all_species[species]
+ character.set_species(S.type) // Yell at me if this causes everything to melt
+ if(be_random_name)
+ real_name = random_name(gender,species)
+
+ character.add_language(language)
+
+
+ character.real_name = real_name
+ character.dna.real_name = real_name
+ character.name = character.real_name
+
+ character.flavor_text = flavor_text
+ character.med_record = med_record
+ character.sec_record = sec_record
+ character.gen_record = gen_record
+
+ character.change_gender(gender)
+ character.age = age
+
+ //Head-specific
+ var/obj/item/organ/external/head/H = character.get_organ("head")
+
+ H.hair_colour = h_colour
+
+ H.sec_hair_colour = h_sec_colour
+
+ H.facial_colour = f_colour
+
+ H.sec_facial_colour = f_sec_colour
+
+ H.h_style = h_style
+ H.f_style = f_style
+
+ H.alt_head = alt_head
+ //End of head-specific.
+
+ character.skin_colour = s_colour
+
+ character.s_tone = s_tone
+
+ // Destroy/cyborgize organs
+ for(var/name in organ_data)
+
+ var/status = organ_data[name]
+ var/obj/item/organ/external/O = character.bodyparts_by_name[name]
+ if(O)
+ if(status == "amputated")
+ qdel(O.remove(character))
+
+ else if(status == "cyborg")
+ if(rlimb_data[name])
+ O.robotize(rlimb_data[name], convert_all = 0)
+ else
+ O.robotize()
+ else
+ var/obj/item/organ/internal/I = character.get_int_organ_tag(name)
+ if(I)
+ if(status == "cybernetic")
+ I.robotize()
+
+ character.dna.blood_type = b_type
+
+ // Wheelchair necessary?
+ var/obj/item/organ/external/l_foot = character.get_organ("l_foot")
+ var/obj/item/organ/external/r_foot = character.get_organ("r_foot")
+ if(!l_foot && !r_foot)
+ var/obj/structure/chair/wheelchair/W = new /obj/structure/chair/wheelchair(character.loc)
+ W.buckle_mob(character, TRUE)
+
+ character.underwear = underwear
+ character.undershirt = undershirt
+ character.socks = socks
+
+ if(character.dna.species.bodyflags & HAS_HEAD_ACCESSORY)
+ H.headacc_colour = hacc_colour
+ H.ha_style = ha_style
+ if(character.dna.species.bodyflags & HAS_MARKINGS)
+ character.m_colours = m_colours
+ character.m_styles = m_styles
+
+ if(body_accessory)
+ character.body_accessory = GLOB.body_accessory_by_name["[body_accessory]"]
+
+ character.backbag = backbag
+
+ //Debugging report to track down a bug, which randomly assigned the plural gender to people.
+ if(character.dna.species.has_gender && (character.gender in list(PLURAL, NEUTER)))
+ if(isliving(src)) //Ghosts get neuter by default
+ message_admins("[key_name_admin(character)] has spawned with their gender as plural or neuter. Please notify coders.")
+ character.change_gender(MALE)
+
+ character.change_eye_color(e_colour)
+ character.original_eye_color = e_colour
+
+ if(disabilities & DISABILITY_FLAG_FAT)
+ character.dna.SetSEState(GLOB.fatblock, TRUE, TRUE)
+ character.overeatduration = 600
+ character.dna.default_blocks.Add(GLOB.fatblock)
+
+ if(disabilities & DISABILITY_FLAG_NEARSIGHTED)
+ character.dna.SetSEState(GLOB.glassesblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.glassesblock)
+
+ if(disabilities & DISABILITY_FLAG_BLIND)
+ character.dna.SetSEState(GLOB.blindblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.blindblock)
+
+ if(disabilities & DISABILITY_FLAG_DEAF)
+ character.dna.SetSEState(GLOB.deafblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.deafblock)
+
+ if(disabilities & DISABILITY_FLAG_COLOURBLIND)
+ character.dna.SetSEState(GLOB.colourblindblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.colourblindblock)
+
+ if(disabilities & DISABILITY_FLAG_MUTE)
+ character.dna.SetSEState(GLOB.muteblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.muteblock)
+
+ if(disabilities & DISABILITY_FLAG_NERVOUS)
+ character.dna.SetSEState(GLOB.nervousblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.nervousblock)
+
+ if(disabilities & DISABILITY_FLAG_SWEDISH)
+ character.dna.SetSEState(GLOB.swedeblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.swedeblock)
+
+ if(disabilities & DISABILITY_FLAG_CHAV)
+ character.dna.SetSEState(GLOB.chavblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.chavblock)
+
+ if(disabilities & DISABILITY_FLAG_LISP)
+ character.dna.SetSEState(GLOB.lispblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.lispblock)
+
+ if(disabilities & DISABILITY_FLAG_DIZZY)
+ character.dna.SetSEState(GLOB.dizzyblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.dizzyblock)
+
+ if(disabilities & DISABILITY_FLAG_WINGDINGS && (CAN_WINGDINGS in character.dna.species.species_traits))
+ character.dna.SetSEState(GLOB.wingdingsblock, TRUE, TRUE)
+ character.dna.default_blocks.Add(GLOB.wingdingsblock)
+
+ character.dna.species.handle_dna(character)
+
+ if(character.dna.dirtySE)
+ character.dna.UpdateSE()
+ domutcheck(character, MUTCHK_FORCED) //'Activates' all the above disabilities.
+
+ character.dna.ready_dna(character, flatten_SE = 0)
+ character.sync_organ_dna(assimilate=1)
+ character.UpdateAppearance()
+
+ // Do the initial caching of the player's body icons.
+ character.force_update_limbs()
+ character.update_eyes()
+ character.regenerate_icons()
+
+//Check if the user has ANY job selected.
+/datum/character_save/proc/check_any_job()
+ return(job_support_high || job_support_med || job_support_low || job_medsci_high || job_medsci_med || job_medsci_low || job_engsec_high || job_engsec_med || job_engsec_low || job_karma_high || job_karma_med || job_karma_low)
+
+
+/datum/character_save/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Head of Security", "Bartender"), widthPerColumn = 400, height = 700)
+ if(!SSjobs)
+ 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.
+ //widthPerColumn - Screen's width for every column.
+ //height - Screen's height.
+ var/width = widthPerColumn
+
+
+ var/list/html = list()
+ html += ""
+ if(!length(SSjobs.occupations))
+ html += "The Jobs subsystem is not yet finished creating jobs, please try again later"
+ html += "Done
" // Easier to press up here.
+ else
+ html += ""
+ html += "Choose occupation chances
Unavailable occupations are crossed out.
"
+ html += "Save
" // Easier to press up here.
+ html += "Left-click to raise an occupation preference, right-click to lower it.
"
+ html += ""
+ html += "" // Table within a table for alignment, also allows you to easily add more colomns.
+ html += ""
+ 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(!SSjobs)
+ return
+ for(var/J in SSjobs.occupations)
+ var/datum/job/job = J
+
+ if(job.admin_only)
+ continue
+
+ if(job.hidden_from_job_prefs)
+ continue
+
+ index += 1
+ if((index >= limit) || (job.title in splitJobs))
+ if((index < limit) && (lastJob != null))
+ // Dynamic window width
+ width += widthPerColumn
+ //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 in 1 to limit - index)
+ html += "|   |   | "
+ html += " | "
+ index = 0
+
+ html += ""
+ var/rank
+ if(job.alt_titles)
+ rank = "[GetPlayerAltTitle(job)]"
+ else
+ rank = job.title
+ lastJob = job
+ if(!is_job_whitelisted(user, job.title))
+ html += "[rank] | \[KARMA] | "
+ continue
+ if(jobban_isbanned(user, job.title))
+ html += "[rank] \[BANNED] | "
+ continue
+ var/available_in_playtime = job.available_in_playtime(user.client)
+ if(available_in_playtime)
+ html += "[rank] \[" + get_exp_format(available_in_playtime) + " as " + job.get_exp_req_type() + "\] | "
+ continue
+ if(job.barred_by_disability(user.client))
+ html += "[rank] \[DISABILITY\] | "
+ continue
+ if(!job.player_old_enough(user.client))
+ var/available_in_days = job.available_in_days(user.client)
+ html += "[rank] \[IN [(available_in_days)] DAYS] | "
+ continue
+ if((job_support_low & JOB_CIVILIAN) && (job.title != "Civilian"))
+ html += "[rank] | "
+ continue
+ if((job.title in GLOB.command_positions) || (job.title == "AI"))//Bold head jobs
+ html += "[rank]"
+ else
+ html += "[rank]"
+
+ html += ""
+
+ var/prefLevelLabel = "ERROR"
+ var/prefLevelColor = "pink"
+ var/prefUpperLevel = -1 // level to assign on left click
+ var/prefLowerLevel = -1 // level to assign on right click
+
+ if(GetJobDepartment(job, 1) & job.flag)
+ prefLevelLabel = "High"
+ prefLevelColor = "slateblue"
+ prefUpperLevel = 4
+ prefLowerLevel = 2
+ else if(GetJobDepartment(job, 2) & job.flag)
+ prefLevelLabel = "Medium"
+ prefLevelColor = "green"
+ prefUpperLevel = 1
+ prefLowerLevel = 3
+ else if(GetJobDepartment(job, 3) & job.flag)
+ prefLevelLabel = "Low"
+ prefLevelColor = "orange"
+ prefUpperLevel = 2
+ prefLowerLevel = 4
+ else
+ prefLevelLabel = "NEVER"
+ prefLevelColor = "red"
+ prefUpperLevel = 3
+ prefLowerLevel = 1
+
+
+ html += ""
+
+ // HTML += ""
+
+ if(job.title == "Civilian")//Civilian is special
+ if(job_support_low & JOB_CIVILIAN)
+ html += " Yes"
+ else
+ html += " No"
+ html += " | "
+ continue
+ /*
+ if(GetJobDepartment(job, 1) & job.flag)
+ HTML += " \[High]"
+ else if(GetJobDepartment(job, 2) & job.flag)
+ HTML += " \[Medium]"
+ else if(GetJobDepartment(job, 3) & job.flag)
+ HTML += " \[Low]"
+ else
+ HTML += " \[NEVER]"
+ */
+ html += "[prefLevelLabel]"
+
+ html += ""
+
+ for(var/i in 1 to limit - index) // Finish the column so it is even
+ html += "|   |   | "
+
+ html += " "
+ html += " |
"
+
+ switch(alternate_option)
+ if(GET_RANDOM_JOB)
+ html += "
Get random job if preferences unavailable
"
+ if(BE_ASSISTANT)
+ html += "
Be a civilian if preferences unavailable
"
+ if(RETURN_TO_LOBBY)
+ html += "
Return to lobby if preferences unavailable
"
+
+ html += "Reset"
+ html += "
Learn About Job Selection"
+ html += ""
+
+ user << browse(null, "window=preferences")
+// user << browse(HTML, "window=mob_occupation;size=[width]x[height]")
+ var/datum/browser/popup = new(user, "mob_occupation", "Occupation Preferences
", width, height)
+ popup.set_window_options("can_close=0")
+ var/html_string = html.Join()
+ popup.set_content(html_string)
+ popup.open(0)
+ return
+
+
+/datum/character_save/proc/clear_character_slot(client/C)
+ . = FALSE
+ // Is there a character in that slot?
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey AND slot=:slot", list(
+ "ckey" = C.ckey,
+ "slot" = slot_number
+ ))
+
+ if(!query.warn_execute())
+ qdel(query)
+ return
+
+ if(!query.NextRow())
+ qdel(query)
+ return
+
+ qdel(query)
+
+ var/datum/db_query/delete_query = SSdbcore.NewQuery("DELETE FROM characters WHERE ckey=:ckey AND slot=:slot", list(
+ "ckey" = C.ckey,
+ "slot" = slot_number
+ ))
+
+ if(!delete_query.warn_execute())
+ qdel(delete_query)
+ return
+
+ qdel(delete_query)
+
+ from_db = FALSE
+ return TRUE
diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm
new file mode 100644
index 00000000000..dea5d8dee2a
--- /dev/null
+++ b/code/modules/client/preference/link_processing.dm
@@ -0,0 +1,1044 @@
+
+/datum/preferences/proc/process_link(mob/user, list/href_list)
+ if(!user) return
+
+ var/datum/species/S = GLOB.all_species[active_character.species]
+ if(href_list["preference"] == "job")
+ switch(href_list["task"])
+ if("close")
+ user << browse(null, "window=mob_occupation")
+ ShowChoices(user)
+ if("reset")
+ active_character.ResetJobs()
+ active_character.SetChoices(user)
+ if("learnaboutselection")
+ if(GLOB.configuration.url.wiki_url)
+ if(alert("Would you like to open the Job selection info in your browser?", "Open Job Selection", "Yes", "No") == "Yes")
+ user << link("[GLOB.configuration.url.wiki_url]/index.php/Job_Selection_and_Assignment")
+ else
+ to_chat(user, "The Wiki URL is not set in the server configuration.")
+ if("random")
+ if(active_character.alternate_option == GET_RANDOM_JOB || active_character.alternate_option == BE_ASSISTANT)
+ active_character.alternate_option += 1
+ else if(active_character.alternate_option == RETURN_TO_LOBBY)
+ active_character.alternate_option = 0
+ else
+ return 0
+ active_character.SetChoices(user)
+ if("alt_title")
+ var/datum/job/job = locate(href_list["job"])
+ if(job)
+ var/choices = list(job.title) + job.alt_titles
+ var/choice = input("Pick a title for [job.title].", "Character Generation", active_character.GetPlayerAltTitle(job)) as anything in choices | null
+ if(choice)
+ active_character.SetPlayerAltTitle(job, choice)
+ active_character.SetChoices(user)
+ if("input")
+ SetJob(user, href_list["text"])
+ if("setJobLevel")
+ UpdateJobPreference(user, href_list["text"], text2num(href_list["level"]))
+ else
+ active_character.SetChoices(user)
+ return 1
+ else if(href_list["preference"] == "disabilities")
+
+ switch(href_list["task"])
+ if("close")
+ user << browse(null, "window=disabil")
+ ShowChoices(user)
+ if("reset")
+ active_character.disabilities = 0
+ active_character.SetDisabilities(user)
+ if("input")
+ var/dflag=text2num(href_list["disability"])
+ if(dflag >= 0) // Toggle it.
+ active_character.disabilities ^= text2num(href_list["disability"]) //MAGIC
+ active_character.SetDisabilities(user)
+ else
+ active_character.SetDisabilities(user)
+ return 1
+
+ else if(href_list["preference"] == "records")
+ if(text2num(href_list["record"]) >= 1)
+ active_character.SetRecords(user)
+ return
+ else
+ user << browse(null, "window=records")
+ if(href_list["task"] == "med_record")
+ var/medmsg = input(usr,"Set your medical notes here.","Medical Records",html_decode(active_character.med_record)) as message
+
+ if(medmsg != null)
+ medmsg = copytext(medmsg, 1, MAX_PAPER_MESSAGE_LEN)
+ medmsg = html_encode(medmsg)
+
+ active_character.med_record = medmsg
+ active_character.SetRecords(user)
+
+ if(href_list["task"] == "sec_record")
+ var/secmsg = input(usr,"Set your security notes here.","Security Records",html_decode(active_character.sec_record)) as message
+
+ if(secmsg != null)
+ secmsg = copytext(secmsg, 1, MAX_PAPER_MESSAGE_LEN)
+ secmsg = html_encode(secmsg)
+
+ active_character.sec_record = secmsg
+ active_character.SetRecords(user)
+ if(href_list["task"] == "gen_record")
+ var/genmsg = input(usr,"Set your employment notes here.","Employment Records",html_decode(active_character.gen_record)) as message
+
+ if(genmsg != null)
+ genmsg = copytext(genmsg, 1, MAX_PAPER_MESSAGE_LEN)
+ genmsg = html_encode(genmsg)
+
+ active_character.gen_record = genmsg
+ active_character.SetRecords(user)
+
+ if(href_list["preference"] == "gear")
+ if(href_list["toggle_gear"])
+ var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]]
+ if(TG.display_name in active_character.loadout_gear)
+ active_character.loadout_gear -= TG.display_name
+ else
+ if(TG.donator_tier && user.client.donator_level < TG.donator_tier)
+ to_chat(user, "That gear is only available at a higher donation tier than you are on.")
+ return
+ var/total_cost = 0
+ var/list/type_blacklist = list()
+ for(var/gear_name in active_character.loadout_gear)
+ var/datum/gear/G = GLOB.gear_datums[gear_name]
+ if(istype(G))
+ if(!G.subtype_cost_overlap)
+ if(G.subtype_path in type_blacklist)
+ continue
+ type_blacklist += G.subtype_path
+ total_cost += G.cost
+
+ if((total_cost + TG.cost) <= max_gear_slots)
+ active_character.loadout_gear += TG.display_name
+
+ else if(href_list["gear"] && href_list["tweak"])
+ var/datum/gear/gear = GLOB.gear_datums[href_list["gear"]]
+ var/datum/gear_tweak/tweak = locate(href_list["tweak"])
+ if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks))
+ return
+ var/metadata = tweak.get_metadata(user, active_character.get_tweak_metadata(gear, tweak))
+ if(!metadata)
+ return
+ active_character.set_tweak_metadata(gear, tweak, metadata)
+ else if(href_list["select_category"])
+ gear_tab = href_list["select_category"]
+ else if(href_list["clear_loadout"])
+ active_character.loadout_gear.Cut()
+
+ ShowChoices(user)
+ return
+
+ switch(href_list["task"])
+ if("random")
+ var/datum/robolimb/robohead
+ if(S.bodyflags & ALL_RPARTS)
+ var/head_model = "[!active_character.rlimb_data["head"] ? "Morpheus Cyberkinetics" : active_character.rlimb_data["head"]]"
+ robohead = GLOB.all_robolimbs[head_model]
+ switch(href_list["preference"])
+ if("name")
+ active_character.real_name = random_name(active_character.gender, active_character.species)
+ if(isnewplayer(user))
+ var/mob/new_player/N = user
+ N.new_player_panel_proc()
+ if("age")
+ active_character.age = rand(AGE_MIN, AGE_MAX)
+ if("hair")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
+ active_character.h_colour = rand_hex_color()
+ if("secondary_hair")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
+ active_character.h_sec_colour = rand_hex_color()
+ if("h_style")
+ active_character.h_style = random_hair_style(active_character.gender, active_character.species, robohead)
+ if("facial")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
+ active_character.f_colour = rand_hex_color()
+ if("secondary_facial")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
+ active_character.f_sec_colour = rand_hex_color()
+ if("f_style")
+ active_character.f_style = random_facial_hair_style(active_character.gender, active_character.species, robohead)
+ if("headaccessory")
+ if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
+ active_character.hacc_colour = rand_hex_color()
+ if("ha_style")
+ if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
+ active_character.ha_style = random_head_accessory(active_character.species)
+ if("m_style_head")
+ if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
+ active_character.m_styles["head"] = random_marking_style("head", active_character.species, robohead, null, active_character.alt_head)
+ if("m_head_colour")
+ if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
+ active_character.m_colours["head"] = rand_hex_color()
+ if("m_style_body")
+ if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings.
+ active_character.m_styles["body"] = random_marking_style("body", active_character.species)
+ if("m_body_colour")
+ if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings.
+ active_character.m_colours["body"] = rand_hex_color()
+ if("m_style_tail")
+ if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
+ active_character.m_styles["tail"] = random_marking_style("tail", active_character.species, null, active_character.body_accessory)
+ if("m_tail_colour")
+ if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
+ active_character.m_colours["tail"] = rand_hex_color()
+ if("underwear")
+ active_character.underwear = random_underwear(active_character.gender, active_character.species)
+ ShowChoices(user)
+ if("undershirt")
+ active_character.undershirt = random_undershirt(active_character.gender, active_character.species)
+ ShowChoices(user)
+ if("socks")
+ active_character.socks = random_socks(active_character.gender, active_character.species)
+ ShowChoices(user)
+ if("eyes")
+ active_character.e_colour = rand_hex_color()
+ if("s_tone")
+ if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
+ active_character.s_tone = random_skin_tone()
+ if("s_color")
+ if(S.bodyflags & HAS_SKIN_COLOR)
+ active_character.s_colour = rand_hex_color()
+ if("bag")
+ active_character.backbag = pick(GLOB.backbaglist)
+ if("all")
+ active_character.randomise()
+ if("input")
+ switch(href_list["preference"])
+ if("name")
+ var/raw_name = clean_input("Choose your character's name:", "Character Preference", , user)
+ if(!isnull(raw_name)) // Check to ensure that the user entered text (rather than cancel.)
+ var/new_name = reject_bad_name(raw_name, 1)
+ if(new_name)
+ active_character.real_name = new_name
+ if(isnewplayer(user))
+ var/mob/new_player/N = user
+ N.new_player_panel_proc()
+ else
+ to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .")
+
+ if("age")
+ var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null
+ if(new_age)
+ active_character.age = max(min(round(text2num(new_age)), AGE_MAX),AGE_MIN)
+ if("species")
+ var/list/new_species = list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin")
+ var/prev_species = active_character.species
+
+ for(var/_species in GLOB.whitelisted_species)
+ if(is_alien_whitelisted(user, _species))
+ new_species += _species
+
+ active_character.species = input("Please select a species", "Character Generation", null) in sortTim(new_species, /proc/cmp_text_asc)
+ var/datum/species/NS = GLOB.all_species[active_character.species]
+ if(!istype(NS)) //The species was invalid. Notify the user and fail out.
+ active_character.species = prev_species
+ to_chat(user, "Invalid species, please pick something else.")
+ return
+ if(prev_species != active_character.species)
+ if(NS.has_gender && active_character.gender == PLURAL)
+ active_character.gender = pick(MALE,FEMALE)
+ var/datum/robolimb/robohead
+ if(NS.bodyflags & ALL_RPARTS)
+ var/head_model = "[!active_character.rlimb_data["head"] ? "Morpheus Cyberkinetics" : active_character.rlimb_data["head"]]"
+ robohead = GLOB.all_robolimbs[head_model]
+ //grab one of the valid hair styles for the newly chosen species
+ active_character.h_style = random_hair_style(active_character.gender, active_character.species, robohead)
+
+ //grab one of the valid facial hair styles for the newly chosen species
+ active_character.f_style = random_facial_hair_style(active_character.gender, active_character.species, robohead)
+
+ if(NS.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
+ active_character.ha_style = random_head_accessory(active_character.species)
+ else
+ active_character.ha_style = "None" // No Vulp ears on Unathi
+ active_character.hacc_colour = rand_hex_color()
+
+ if(NS.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
+ active_character.m_styles["head"] = random_marking_style("head", active_character.species, robohead, null, active_character.alt_head)
+ else
+ active_character.m_styles["head"] = "None"
+ active_character.m_colours["head"] = "#000000"
+
+ if(NS.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
+ active_character.m_styles["body"] = random_marking_style("body", active_character.species)
+ else
+ active_character.m_styles["body"] = "None"
+ active_character.m_colours["body"] = "#000000"
+
+ if(NS.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
+ active_character.m_styles["tail"] = random_marking_style("tail", active_character.species, null, active_character.body_accessory)
+ else
+ active_character.m_styles["tail"] = "None"
+ active_character.m_colours["tail"] = "#000000"
+
+ // Don't wear another species' underwear!
+ var/datum/sprite_accessory/SA = GLOB.underwear_list[active_character.underwear]
+ if(!SA || !(active_character.species in SA.species_allowed))
+ active_character.underwear = random_underwear(active_character.gender, active_character.species)
+
+ SA = GLOB.undershirt_list[active_character.undershirt]
+ if(!SA || !(active_character.species in SA.species_allowed))
+ active_character.undershirt = random_undershirt(active_character.gender, active_character.species)
+
+ SA = GLOB.socks_list[active_character.socks]
+ if(!SA || !(active_character.species in SA.species_allowed))
+ active_character.socks = random_socks(active_character.gender, active_character.species)
+
+ //reset skin tone and colour
+ if(NS.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
+ random_skin_tone(active_character.species)
+ else
+ active_character.s_tone = 0
+
+ if(!(NS.bodyflags & HAS_SKIN_COLOR))
+ active_character.s_colour = "#000000"
+
+ active_character.alt_head = "None" //No alt heads on species that don't have them.
+ active_character.speciesprefs = 0 //My Vox tank shouldn't change how my future Grey talks.
+
+ active_character.body_accessory = null //no vulptail on humans damnit
+
+ //Reset prosthetics.
+ active_character.organ_data = list()
+ active_character.rlimb_data = list()
+
+ if(!(NS.autohiss_basic_map))
+ active_character.autohiss_mode = AUTOHISS_OFF
+ if("speciesprefs")
+ active_character.speciesprefs = !active_character.speciesprefs //Starts 0, so if someone clicks the button up top there, this won't be 0 anymore. If they click it again, it'll go back to 0.
+ if("language")
+// var/languages_available
+ var/list/new_languages = list("None")
+ for(var/L in GLOB.all_languages)
+ var/datum/language/lang = GLOB.all_languages[L]
+ if(!(lang.flags & RESTRICTED))
+ new_languages += lang.name
+
+ active_character.language = input("Please select a secondary language", "Character Generation", null) in sortTim(new_languages, /proc/cmp_text_asc)
+
+ if("autohiss_mode")
+ if(S.autohiss_basic_map)
+ var/list/autohiss_choice = list("Off" = AUTOHISS_OFF, "Basic" = AUTOHISS_BASIC, "Full" = AUTOHISS_FULL)
+ var/new_autohiss_pref = input(user, "Choose your character's auto-accent level:", "Character Preference") as null|anything in autohiss_choice
+ if(new_autohiss_pref)
+ active_character.autohiss_mode = autohiss_choice[new_autohiss_pref]
+
+ if("metadata")
+ var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , active_character.metadata) as message|null
+ if(new_metadata)
+ active_character.metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN))
+
+ if("b_type")
+ var/new_b_type = input(user, "Choose your character's blood-type:", "Character Preference") as null|anything in list( "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-" )
+ if(new_b_type)
+ active_character.b_type = new_b_type
+
+ if("hair")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox")) //Species that have hair. (No HAS_HAIR flag)
+ var/input = "Choose your character's hair colour:"
+ var/new_hair = input(user, input, "Character Preference", active_character.h_colour) as color|null
+ if(new_hair)
+ active_character.h_colour = new_hair
+
+ if("secondary_hair")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox"))
+ var/datum/sprite_accessory/hair_style = GLOB.hair_styles_public_list[active_character.h_style]
+ if(hair_style.secondary_theme && !hair_style.no_sec_colour)
+ var/new_hair = input(user, "Choose your character's secondary hair colour:", "Character Preference", active_character.h_sec_colour) as color|null
+ if(new_hair)
+ active_character.h_sec_colour = new_hair
+
+ if("h_style")
+ var/list/valid_hairstyles = list()
+ for(var/hairstyle in GLOB.hair_styles_public_list)
+ var/datum/sprite_accessory/SA = GLOB.hair_styles_public_list[hairstyle]
+
+ if(hairstyle == "Bald") //Just in case.
+ valid_hairstyles += hairstyle
+ continue
+ if(S.bodyflags & ALL_RPARTS) //Species that can use prosthetic heads.
+ var/head_model
+ if(!active_character.rlimb_data["head"]) //Handle situations where the head is default.
+ head_model = "Morpheus Cyberkinetics"
+ else
+ head_model = active_character.rlimb_data["head"]
+ var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model]
+ if((active_character.species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
+ valid_hairstyles += hairstyle //Give them their hairstyles if they do.
+ else
+ if(!robohead.is_monitor && ("Human" in SA.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it.
+ But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */
+ valid_hairstyles += hairstyle
+ else //If the user is not a species who can have robotic heads, use the default handling.
+ if(active_character.species in SA.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list.
+ valid_hairstyles += hairstyle
+
+ sortTim(valid_hairstyles, /proc/cmp_text_asc) //this alphabetizes the list
+ var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_hairstyles
+ if(new_h_style)
+ active_character.h_style = new_h_style
+
+ if("headaccessory")
+ if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories.
+ var/input = "Choose the colour of your your character's head accessory:"
+ var/new_head_accessory = input(user, input, "Character Preference", active_character.hacc_colour) as color|null
+ if(new_head_accessory)
+ active_character.hacc_colour = new_head_accessory
+
+ if("ha_style")
+ if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories.
+ var/list/valid_head_accessory_styles = list()
+ for(var/head_accessory_style in GLOB.head_accessory_styles_list)
+ var/datum/sprite_accessory/H = GLOB.head_accessory_styles_list[head_accessory_style]
+ if(!(active_character.species in H.species_allowed))
+ continue
+
+ valid_head_accessory_styles += head_accessory_style
+
+ sortTim(valid_head_accessory_styles, /proc/cmp_text_asc)
+ var/new_head_accessory_style = input(user, "Choose the style of your character's head accessory:", "Character Preference") as null|anything in valid_head_accessory_styles
+ if(new_head_accessory_style)
+ active_character.ha_style = new_head_accessory_style
+
+ if("alt_head")
+ if(active_character.organ_data["head"] == "cyborg")
+ return
+ if(S.bodyflags & HAS_ALT_HEADS) //Species with alt heads.
+ var/list/valid_alt_heads = list()
+ valid_alt_heads["None"] = GLOB.alt_heads_list["None"] //The only null entry should be the "None" option
+ for(var/alternate_head in GLOB.alt_heads_list)
+ var/datum/sprite_accessory/alt_heads/head = GLOB.alt_heads_list[alternate_head]
+ if(!(active_character.species in head.species_allowed))
+ continue
+
+ valid_alt_heads += alternate_head
+
+ var/new_alt_head = input(user, "Choose your character's alternate head style:", "Character Preference") as null|anything in valid_alt_heads
+ if(new_alt_head)
+ active_character.alt_head = new_alt_head
+ if(active_character.m_styles["head"])
+ var/head_marking = active_character.m_styles["head"]
+ var/datum/sprite_accessory/body_markings/head/head_marking_style = GLOB.marking_styles_list[head_marking]
+ if(!head_marking_style.heads_allowed || (!("All" in head_marking_style.heads_allowed) && !(active_character.alt_head in head_marking_style.heads_allowed)))
+ active_character.m_styles["head"] = "None"
+
+ if("m_style_head")
+ if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
+ var/list/valid_markings = list()
+ valid_markings["None"] = GLOB.marking_styles_list["None"]
+ for(var/markingstyle in GLOB.marking_styles_list)
+ var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[markingstyle]
+ if(!(active_character.species in M.species_allowed))
+ continue
+ if(M.marking_location != "head")
+ continue
+ if(active_character.alt_head && active_character.alt_head != "None")
+ if(!("All" in M.heads_allowed) && !(active_character.alt_head in M.heads_allowed))
+ continue
+ else
+ if(M.heads_allowed && !("All" in M.heads_allowed))
+ continue
+
+ if(S.bodyflags & ALL_RPARTS) //Species that can use prosthetic heads.
+ var/head_model
+ if(!active_character.rlimb_data["head"]) //Handle situations where the head is default.
+ head_model = "Morpheus Cyberkinetics"
+ else
+ head_model = active_character.rlimb_data["head"]
+ var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model]
+ if(robohead.is_monitor && M.name != "None") //If the character can have prosthetic heads and they have the default Morpheus head (or another monitor-head), no optic markings.
+ continue
+ else if(!robohead.is_monitor && M.name != "None") //Otherwise, if they DON'T have the default head and the head's not a monitor but the head's not in the style's list of allowed models, skip.
+ if(!(robohead.company in M.models_allowed))
+ continue
+
+ valid_markings += markingstyle
+ sortTim(valid_markings, /proc/cmp_text_asc)
+ var/new_marking_style = input(user, "Choose the style of your character's head markings:", "Character Preference", active_character.m_styles["head"]) as null|anything in valid_markings
+ if(new_marking_style)
+ active_character.m_styles["head"] = new_marking_style
+
+ if("m_head_colour")
+ if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
+ var/input = "Choose the colour of your your character's head markings:"
+ var/new_markings = input(user, input, "Character Preference", active_character.m_colours["head"]) as color|null
+ if(new_markings)
+ active_character.m_colours["head"] = new_markings
+
+ if("m_style_body")
+ if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
+ var/list/valid_markings = list()
+ valid_markings["None"] = GLOB.marking_styles_list["None"]
+ for(var/markingstyle in GLOB.marking_styles_list)
+ var/datum/sprite_accessory/M = GLOB.marking_styles_list[markingstyle]
+ if(!(active_character.species in M.species_allowed))
+ continue
+ if(M.marking_location != "body")
+ continue
+
+ valid_markings += markingstyle
+ sortTim(valid_markings, /proc/cmp_text_asc)
+ var/new_marking_style = input(user, "Choose the style of your character's body markings:", "Character Preference", active_character.m_styles["body"]) as null|anything in valid_markings
+ if(new_marking_style)
+ active_character.m_styles["body"] = new_marking_style
+
+ if("m_body_colour")
+ if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
+ var/input = "Choose the colour of your your character's body markings:"
+ var/new_markings = input(user, input, "Character Preference", active_character.m_colours["body"]) as color|null
+ if(new_markings)
+ active_character.m_colours["body"] = new_markings
+
+ if("m_style_tail")
+ if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
+ var/list/valid_markings = list()
+ valid_markings["None"] = GLOB.marking_styles_list["None"]
+ for(var/markingstyle in GLOB.marking_styles_list)
+ var/datum/sprite_accessory/body_markings/tail/M = GLOB.marking_styles_list[markingstyle]
+ if(M.marking_location != "tail")
+ continue
+ if(!(active_character.species in M.species_allowed))
+ continue
+ if(!active_character.body_accessory)
+ if(M.tails_allowed)
+ continue
+ else
+ if(!M.tails_allowed || !(active_character.body_accessory in M.tails_allowed))
+ continue
+
+ valid_markings += markingstyle
+ sortTim(valid_markings, /proc/cmp_text_asc)
+ var/new_marking_style = input(user, "Choose the style of your character's tail markings:", "Character Preference", active_character.m_styles["tail"]) as null|anything in valid_markings
+ if(new_marking_style)
+ active_character.m_styles["tail"] = new_marking_style
+
+ if("m_tail_colour")
+ if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
+ var/input = "Choose the colour of your your character's tail markings:"
+ var/new_markings = input(user, input, "Character Preference", active_character.m_colours["tail"]) as color|null
+ if(new_markings)
+ active_character.m_colours["tail"] = new_markings
+
+ if("body_accessory")
+ var/list/possible_body_accessories = list()
+ if(check_rights(R_ADMIN, 1, user))
+ possible_body_accessories = GLOB.body_accessory_by_name.Copy()
+ else
+ for(var/B in GLOB.body_accessory_by_name)
+ var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[B]
+ if(!istype(accessory))
+ possible_body_accessories += "None" //the only null entry should be the "None" option
+ continue
+ if(active_character.species in accessory.allowed_species)
+ possible_body_accessories += B
+ sortTim(possible_body_accessories, /proc/cmp_text_asc)
+ var/new_body_accessory = input(user, "Choose your body accessory:", "Character Preference") as null|anything in possible_body_accessories
+ if(new_body_accessory)
+ active_character.m_styles["tail"] = "None"
+ active_character.body_accessory = (new_body_accessory == "None") ? null : new_body_accessory
+
+ if("facial")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox")) //Species that have facial hair. (No HAS_HAIR_FACIAL flag)
+ var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", active_character.f_colour) as color|null
+ if(new_facial)
+ active_character.f_colour = new_facial
+
+ if("secondary_facial")
+ if(active_character.species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox"))
+ var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[active_character.f_style]
+ if(facial_hair_style.secondary_theme && !facial_hair_style.no_sec_colour)
+ var/new_facial = input(user, "Choose your character's secondary facial-hair colour:", "Character Preference", active_character.f_sec_colour) as color|null
+ if(new_facial)
+ active_character.f_sec_colour = new_facial
+
+ if("f_style")
+ var/list/valid_facial_hairstyles = list()
+ for(var/facialhairstyle in GLOB.facial_hair_styles_list)
+ var/datum/sprite_accessory/SA = GLOB.facial_hair_styles_list[facialhairstyle]
+
+ if(facialhairstyle == "Shaved") //Just in case.
+ valid_facial_hairstyles += facialhairstyle
+ continue
+ if(active_character.gender == MALE && SA.gender == FEMALE)
+ continue
+ if(active_character.gender == FEMALE && SA.gender == MALE)
+ continue
+ if(S.bodyflags & ALL_RPARTS) //Species that can use prosthetic heads.
+ var/head_model
+ if(!active_character.rlimb_data["head"]) //Handle situations where the head is default.
+ head_model = "Morpheus Cyberkinetics"
+ else
+ head_model = active_character.rlimb_data["head"]
+ var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model]
+ if((active_character.species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
+ valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do.
+ else
+ if(!robohead.is_monitor && ("Human" in SA.species_allowed)) /*If the facial hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it.
+ But if the user has a robotic humanoid head and the facial hairstyle can fit humans, let them use it as a wig. */
+ valid_facial_hairstyles += facialhairstyle
+ else //If the user is not a species who can have robotic heads, use the default handling.
+ if(active_character.species in SA.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list.
+ valid_facial_hairstyles += facialhairstyle
+ sortTim(valid_facial_hairstyles, /proc/cmp_text_asc)
+ var/new_f_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in valid_facial_hairstyles
+ if(new_f_style)
+ active_character.f_style = new_f_style
+
+ if("underwear")
+ var/list/valid_underwear = list()
+ for(var/underwear in GLOB.underwear_list)
+ var/datum/sprite_accessory/SA = GLOB.underwear_list[underwear]
+ if(active_character.gender == MALE && SA.gender == FEMALE)
+ continue
+ if(active_character.gender == FEMALE && SA.gender == MALE)
+ continue
+ if(!(active_character.species in SA.species_allowed))
+ continue
+ valid_underwear[underwear] = GLOB.underwear_list[underwear]
+ sortTim(valid_underwear, /proc/cmp_text_asc)
+ var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in valid_underwear
+ ShowChoices(user)
+ if(new_underwear)
+ active_character.underwear = new_underwear
+ if("undershirt")
+ var/list/valid_undershirts = list()
+ for(var/undershirt in GLOB.undershirt_list)
+ var/datum/sprite_accessory/SA = GLOB.undershirt_list[undershirt]
+ if(active_character.gender == MALE && SA.gender == FEMALE)
+ continue
+ if(active_character.gender == FEMALE && SA.gender == MALE)
+ continue
+ if(!(active_character.species in SA.species_allowed))
+ continue
+ valid_undershirts[undershirt] = GLOB.undershirt_list[undershirt]
+ sortTim(valid_undershirts, /proc/cmp_text_asc)
+ var/new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in valid_undershirts
+ ShowChoices(user)
+ if(new_undershirt)
+ active_character.undershirt = new_undershirt
+
+ if("socks")
+ var/list/valid_sockstyles = list()
+ for(var/sockstyle in GLOB.socks_list)
+ var/datum/sprite_accessory/SA = GLOB.socks_list[sockstyle]
+ if(active_character.gender == MALE && SA.gender == FEMALE)
+ continue
+ if(active_character.gender == FEMALE && SA.gender == MALE)
+ continue
+ if(!(active_character.species in SA.species_allowed))
+ continue
+ valid_sockstyles[sockstyle] = GLOB.socks_list[sockstyle]
+ sortTim(valid_sockstyles, /proc/cmp_text_asc)
+ var/new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in valid_sockstyles
+ ShowChoices(user)
+ if(new_socks)
+ active_character.socks = new_socks
+
+ if("eyes")
+ var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", active_character.e_colour) as color|null
+ if(new_eyes)
+ active_character.e_colour = new_eyes
+
+ if("s_tone")
+ if(S.bodyflags & HAS_SKIN_TONE)
+ 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)
+ active_character.s_tone = 35 - max(min(round(new_s_tone), 220), 1)
+ else if(S.bodyflags & HAS_ICON_SKIN_TONE)
+ var/const/MAX_LINE_ENTRIES = 4
+ var/prompt = "Choose your character's skin tone: 1-[S.icon_skin_tones.len]\n("
+ for(var/i = 1 to S.icon_skin_tones.len)
+ if(i > MAX_LINE_ENTRIES && !((i - 1) % MAX_LINE_ENTRIES))
+ prompt += "\n"
+ prompt += "[i] = [S.icon_skin_tones[i]]"
+ if(i != S.icon_skin_tones.len)
+ prompt += ", "
+ prompt += ")"
+ var/skin_c = input(user, prompt, "Character Preference") as num|null
+ if(isnum(skin_c))
+ active_character.s_tone = max(min(round(skin_c), S.icon_skin_tones.len), 1)
+
+ if("skin")
+ if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[active_character.species] || check_rights(R_ADMIN, 0, user))
+ var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", active_character.s_colour) as color|null
+ if(new_skin)
+ active_character.s_colour = new_skin
+
+ if("ooccolor")
+ var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference", ooccolor) as color|null
+ if(new_ooccolor)
+ ooccolor = new_ooccolor
+
+ if("bag")
+ var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist
+ if(new_backbag)
+ active_character.backbag = new_backbag
+
+ if("nt_relation")
+ var/new_relation = input(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference") as null|anything in list("Loyal", "Supportive", "Neutral", "Skeptical", "Opposed")
+ if(new_relation)
+ active_character.nanotrasen_relation = new_relation
+
+ if("flavor_text")
+ var/msg = input(usr,"Set the flavor text in your 'examine' verb. The flavor text should be a physical descriptor of your character at a glance. SFW Drawn Art of your character is acceptable.","Flavor Text",html_decode(active_character.flavor_text)) as message
+
+ if(msg != null)
+ msg = copytext(msg, 1, MAX_MESSAGE_LEN)
+ msg = html_encode(msg)
+
+ active_character.flavor_text = msg
+
+ if("limbs")
+ var/valid_limbs = list("Left Leg", "Right Leg", "Left Arm", "Right Arm", "Left Foot", "Right Foot", "Left Hand", "Right Hand")
+ if(S.bodyflags & ALL_RPARTS)
+ valid_limbs = list("Torso", "Lower Body", "Head", "Left Leg", "Right Leg", "Left Arm", "Right Arm", "Left Foot", "Right Foot", "Left Hand", "Right Hand")
+ var/limb_name = input(user, "Which limb do you want to change?") as null|anything in valid_limbs
+ if(!limb_name) return
+
+ var/limb = null
+ var/second_limb = null // if you try to change the arm, the hand should also change
+ var/third_limb = null // if you try to unchange the hand, the arm should also change
+ var/valid_limb_states = list("Normal", "Amputated", "Prosthesis")
+ var/no_amputate = 0
+
+ switch(limb_name)
+ if("Torso")
+ limb = "chest"
+ second_limb = "groin"
+ no_amputate = 1
+ if("Lower Body")
+ limb = "groin"
+ no_amputate = 1
+ if("Head")
+ limb = "head"
+ no_amputate = 1
+ if("Left Leg")
+ limb = "l_leg"
+ second_limb = "l_foot"
+ if("Right Leg")
+ limb = "r_leg"
+ second_limb = "r_foot"
+ if("Left Arm")
+ limb = "l_arm"
+ second_limb = "l_hand"
+ if("Right Arm")
+ limb = "r_arm"
+ second_limb = "r_hand"
+ if("Left Foot")
+ limb = "l_foot"
+ if(!(S.bodyflags & ALL_RPARTS))
+ third_limb = "l_leg"
+ if("Right Foot")
+ limb = "r_foot"
+ if(!(S.bodyflags & ALL_RPARTS))
+ third_limb = "r_leg"
+ if("Left Hand")
+ limb = "l_hand"
+ if(!(S.bodyflags & ALL_RPARTS))
+ third_limb = "l_arm"
+ if("Right Hand")
+ limb = "r_hand"
+ if(!(S.bodyflags & ALL_RPARTS))
+ third_limb = "r_arm"
+
+ var/new_state = input(user, "What state do you wish the limb to be in?") as null|anything in valid_limb_states
+ if(!new_state) return
+
+ switch(new_state)
+ if("Normal")
+ if(limb == "head")
+ active_character.m_styles["head"] = "None"
+ active_character.h_style = GLOB.hair_styles_public_list["Bald"]
+ active_character.f_style = GLOB.facial_hair_styles_list["Shaved"]
+ active_character.organ_data[limb] = null
+ active_character.rlimb_data[limb] = null
+ if(third_limb)
+ active_character.organ_data[third_limb] = null
+ active_character.rlimb_data[third_limb] = null
+ if("Amputated")
+ if(!no_amputate)
+ active_character.organ_data[limb] = "amputated"
+ active_character.rlimb_data[limb] = null
+ if(second_limb)
+ active_character.organ_data[second_limb] = "amputated"
+ active_character.rlimb_data[second_limb] = null
+ if("Prosthesis")
+ var/choice
+ var/subchoice
+ var/datum/robolimb/R = new()
+ var/in_model
+ var/robolimb_companies = list()
+ for(var/limb_type in typesof(/datum/robolimb)) //This loop populates a list of companies that offer the limb the user selected previously as one of their cybernetic products.
+ R = new limb_type()
+ if(!R.unavailable_at_chargen && (limb in R.parts) && R.has_subtypes) //Ensures users can only choose companies that offer the parts they want, that singular models get added to the list as well companies that offer more than one model, and...
+ robolimb_companies[R.company] = R //List only main brands that have the parts we're looking for.
+ R = new() //Re-initialize R.
+
+ choice = input(user, "Which manufacturer do you wish to use for this limb?") as null|anything in robolimb_companies //Choose from a list of companies that offer the part the user wants.
+ if(!choice)
+ return
+ R.company = choice
+ R = GLOB.all_robolimbs[R.company]
+ if(R.has_subtypes == 1) //If the company the user selected provides more than just one base model, lets handle it.
+ var/list/robolimb_models = list()
+ for(var/limb_type in typesof(R)) //Handling the different models of parts that manufacturers can provide.
+ var/datum/robolimb/L = new limb_type()
+ if(limb in L.parts) //Make sure that only models that provide the parts the user needs populate the list.
+ robolimb_models[L.company] = L
+ if(robolimb_models.len == 1) //If there's only one model available in the list, autoselect it to avoid having to bother the user with a dialog that provides only one option.
+ subchoice = L.company //If there ends up being more than one model populating the list, subchoice will be overwritten later anyway, so this isn't a problem.
+ if(second_limb in L.parts) //If the child limb of the limb the user selected is also present in the model's parts list, state it's been found so the second limb can be set later.
+ in_model = 1
+ if(robolimb_models.len > 1) //If there's more than one model in the list that can provide the part the user wants, let them choose.
+ subchoice = input(user, "Which model of [choice] [limb_name] do you wish to use?") as null|anything in robolimb_models
+ if(subchoice)
+ choice = subchoice
+ if(limb in list("head", "chest", "groin"))
+ if(!(S.bodyflags & ALL_RPARTS))
+ return
+ if(limb == "head")
+ active_character.ha_style = "None"
+ active_character.alt_head = null
+ active_character.h_style = GLOB.hair_styles_public_list["Bald"]
+ active_character.f_style = GLOB.facial_hair_styles_list["Shaved"]
+ active_character.m_styles["head"] = "None"
+ active_character.rlimb_data[limb] = choice
+ active_character.organ_data[limb] = "cyborg"
+ if(second_limb)
+ if(subchoice)
+ if(in_model)
+ active_character.rlimb_data[second_limb] = choice
+ active_character.organ_data[second_limb] = "cyborg"
+ else
+ active_character.rlimb_data[second_limb] = choice
+ active_character.organ_data[second_limb] = "cyborg"
+ if("organs")
+ var/organ_name = input(user, "Which internal function do you want to change?") as null|anything in list("Eyes", "Ears", "Heart", "Lungs", "Liver", "Kidneys")
+ if(!organ_name)
+ return
+
+ var/organ = null
+ switch(organ_name)
+ if("Eyes")
+ organ = "eyes"
+ if("Ears")
+ organ = "ears"
+ if("Heart")
+ organ = "heart"
+ if("Lungs")
+ organ = "lungs"
+ if("Liver")
+ organ = "liver"
+ if("Kidneys")
+ organ = "kidneys"
+
+ var/new_state = input(user, "What state do you wish the organ to be in?") as null|anything in list("Normal", "Cybernetic")
+ if(!new_state) return
+
+ switch(new_state)
+ if("Normal")
+ active_character.organ_data[organ] = null
+ if("Cybernetic")
+ active_character.organ_data[organ] = "cybernetic"
+
+ if("clientfps")
+ var/version_message
+ if(user.client && user.client.byond_version < 511)
+ version_message = "\nYou need to be using byond version 511 or later to take advantage of this feature, your version of [user.client.byond_version] is too low"
+ if(world.byond_version < 511)
+ version_message += "\nThis server does not currently support client side fps. You can set now for when it does."
+ var/desiredfps = input(user, "Choose your desired fps.[version_message]\n(0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num
+ if(!isnull(desiredfps))
+ clientfps = desiredfps
+ if(world.byond_version >= 511 && user.client && user.client.byond_version >= 511)
+ parent.fps = clientfps
+
+ else
+ switch(href_list["preference"])
+ if("publicity")
+ if(unlock_content)
+ toggles ^= PREFTOGGLE_MEMBER_PUBLIC
+
+ if("donor_public")
+ if(user.client.donator_level > 0)
+ toggles ^= PREFTOGGLE_DONATOR_PUBLIC
+
+ if("gender")
+ if(!S.has_gender)
+ var/newgender = input(user, "Choose Gender:") as null|anything in list("Male", "Female", "Genderless")
+ switch(newgender)
+ if("Male")
+ active_character.gender = MALE
+ if("Female")
+ active_character.gender = FEMALE
+ if("Genderless")
+ active_character.gender = PLURAL
+ else
+ if(active_character.gender == MALE)
+ active_character.gender = FEMALE
+ else
+ active_character.gender = MALE
+ active_character.underwear = random_underwear(active_character.gender)
+
+ if("hear_adminhelps")
+ sound ^= SOUND_ADMINHELP
+ if("ui")
+ switch(UI_style)
+ if("Midnight")
+ UI_style = "Plasmafire"
+ if("Plasmafire")
+ UI_style = "Retro"
+ if("Retro")
+ UI_style = "Slimecore"
+ if("Slimecore")
+ UI_style = "Operative"
+ if("Operative")
+ UI_style = "White"
+ else
+ UI_style = "Midnight"
+
+ if(ishuman(usr)) //mid-round preference changes, for aesthetics
+ var/mob/living/carbon/human/H = usr
+ H.remake_hud()
+
+ if("tgui")
+ toggles2 ^= PREFTOGGLE_2_FANCYUI
+
+ if("ghost_att_anim")
+ toggles2 ^= PREFTOGGLE_2_ITEMATTACK
+
+ if("winflash")
+ toggles2 ^= PREFTOGGLE_2_WINDOWFLASHING
+
+ if("afk_watch")
+ if(!(toggles2 & PREFTOGGLE_2_AFKWATCH))
+ to_chat(user, "You will now get put into cryo dorms after [GLOB.configuration.afk.auto_cryo_minutes] minutes. \
+ Then after [GLOB.configuration.afk.auto_despawn_minutes] minutes you will be fully despawned. You will receive a visual and auditory warning before you will be put into cryodorms.")
+ else
+ to_chat(user, "Automatic cryoing turned off.")
+ toggles2 ^= PREFTOGGLE_2_AFKWATCH
+
+ if("UIcolor")
+ var/UI_style_color_new = input(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) as color|null
+ if(!UI_style_color_new) return
+ UI_style_color = UI_style_color_new
+
+ if(ishuman(usr)) //mid-round preference changes, for aesthetics
+ var/mob/living/carbon/human/H = usr
+ H.remake_hud()
+
+ if("UIalpha")
+ var/UI_style_alpha_new = input(user, "Select a new alpha(transparence) parameter for UI, between 50 and 255", UI_style_alpha) as num
+ if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50))
+ return
+ UI_style_alpha = UI_style_alpha_new
+
+ if(ishuman(usr)) //mid-round preference changes, for aesthetics
+ var/mob/living/carbon/human/H = usr
+ H.remake_hud()
+
+ if("be_special")
+ var/r = href_list["role"]
+ if(r in GLOB.special_roles)
+ be_special ^= r
+
+ if("name")
+ active_character.be_random_name = !active_character.be_random_name
+
+ if("randomslot")
+ toggles2 ^= PREFTOGGLE_2_RANDOMSLOT
+ if(isnewplayer(usr))
+ var/mob/new_player/N = usr
+ N.new_player_panel_proc()
+
+ if("hear_midis")
+ sound ^= SOUND_MIDI
+
+ if("lobby_music")
+ sound ^= SOUND_LOBBY
+ if((sound & SOUND_LOBBY) && user.client)
+ user.client.playtitlemusic()
+ else
+ user.stop_sound_channel(CHANNEL_LOBBYMUSIC)
+
+ if("ghost_ears")
+ toggles ^= PREFTOGGLE_CHAT_GHOSTEARS
+
+ if("ghost_sight")
+ toggles ^= PREFTOGGLE_CHAT_GHOSTSIGHT
+
+ if("ghost_radio")
+ toggles ^= PREFTOGGLE_CHAT_GHOSTRADIO
+
+ if("ghost_pda")
+ toggles ^= PREFTOGGLE_CHAT_GHOSTPDA
+
+ if("ghost_anonsay")
+ toggles2 ^= PREFTOGGLE_2_ANONDCHAT
+
+ if("save")
+ save_preferences(user)
+ active_character.save(user)
+
+ if("clear")
+ if(!active_character.from_db || active_character.real_name != input("This will clear the current slot permanently. Please enter the character's full name to confirm."))
+ return FALSE
+ active_character.clear_character_slot(user)
+ // Gimmie a freshie
+ var/datum/character_save/CS = new()
+ character_saves[active_character.slot_number] = CS
+ active_character = CS
+
+ if("open_load_dialog")
+ if(!IsGuestKey(user.key))
+ open_load_dialog(user)
+ return 1
+
+ if("close_load_dialog")
+ close_load_dialog(user)
+
+ if("changeslot")
+ active_character = character_saves[text2num(href_list["num"])]
+ default_slot = text2num(href_list["num"])
+ close_load_dialog(user)
+ if(isnewplayer(user))
+ var/mob/new_player/N = user
+ N.new_player_panel_proc()
+
+ if("tab")
+ if(href_list["tab"])
+ current_tab = text2num(href_list["tab"])
+
+
+ if("ambientocclusion")
+ toggles ^= PREFTOGGLE_AMBIENT_OCCLUSION
+ if(length(parent?.screen))
+ var/obj/screen/plane_master/game_world/PM = locate(/obj/screen/plane_master/game_world) in parent.screen
+ PM.backdrop(parent.mob)
+
+ if("parallax")
+ var/parallax_styles = list(
+ "Off" = PARALLAX_DISABLE,
+ "Low" = PARALLAX_LOW,
+ "Medium" = PARALLAX_MED,
+ "High" = PARALLAX_HIGH,
+ "Insane" = PARALLAX_INSANE
+ )
+ parallax = parallax_styles[input(user, "Pick a parallax style", "Parallax Style") as null|anything in parallax_styles]
+ if(parent && parent.mob && parent.mob.hud_used)
+ parent.mob.hud_used.update_parallax_pref()
+
+ if("edit_2fa")
+ // Do this async so we arent holding up a topic() call
+ INVOKE_ASYNC(user.client, /client.proc/edit_2fa)
+ return // We return here to avoid focus being lost
+
+
+ ShowChoices(user)
+ return 1
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 1979be243fa..befa6f05c8e 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -54,19 +54,10 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
else
return max(0, days - C.player_age)
-#define MAX_SAVE_SLOTS 30 // Save slots for regular players
-#define MAX_SAVE_SLOTS_MEMBER 30 // Save slots for BYOND members
-
-#define TAB_CHAR 0
-#define TAB_GAME 1
-#define TAB_GEAR 2
-
/datum/preferences
var/client/parent
//doohickeys for savefiles
-// var/path
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
-// var/savefile_version = 0
var/max_save_slots = MAX_SAVE_SLOTS
var/max_gear_slots = 0
@@ -91,98 +82,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
var/atklog = ATKLOG_ALL
var/fuid // forum userid
- //character preferences
- var/real_name //our character's name
- var/be_random_name = 0 //whether we are a random name every round
- var/gender = MALE //gender of character (well duh)
- var/age = 30 //age of character
- var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2).
- var/b_type = "A+" //blood type (not-chooseable)
- var/underwear = "Nude" //underwear type
- var/undershirt = "Nude" //undershirt type
- var/socks = "Nude" //socks type
- var/backbag = GBACKPACK //backpack type
- var/ha_style = "None" //Head accessory style
- var/hacc_colour = "#000000" //Head accessory colour
- var/list/m_styles = list(
- "head" = "None",
- "body" = "None",
- "tail" = "None"
- ) //Marking styles.
- var/list/m_colours = list(
- "head" = "#000000",
- "body" = "#000000",
- "tail" = "#000000"
- ) //Marking colours.
- var/h_style = "Bald" //Hair type
- var/h_colour = "#000000" //Hair color
- var/h_sec_colour = "#000000" //Secondary hair color
- var/f_style = "Shaved" //Facial hair type
- var/f_colour = "#000000" //Facial hair color
- var/f_sec_colour = "#000000" //Secondary facial hair color
- var/s_tone = 0 //Skin tone
- var/s_colour = "#000000" //Skin color
- var/e_colour = "#000000" //Eye color
- var/alt_head = "None" //Alt head style.
- var/species = "Human"
- var/language = "None" //Secondary language
- var/autohiss_mode = AUTOHISS_OFF //Species autohiss level. OFF, BASIC, FULL.
-
- var/body_accessory = null
-
- var/speciesprefs = 0//I hate having to do this, I really do (Using this for oldvox code, making names universal I guess
-
- //Mob preview
- var/icon/preview_icon = null
- var/icon/preview_icon_front = null
- var/icon/preview_icon_side = null
-
- //Jobs, uses bitflags
- var/job_support_high = 0
- var/job_support_med = 0
- var/job_support_low = 0
-
- var/job_medsci_high = 0
- var/job_medsci_med = 0
- var/job_medsci_low = 0
-
- var/job_engsec_high = 0
- var/job_engsec_med = 0
- var/job_engsec_low = 0
-
- var/job_karma_high = 0
- var/job_karma_med = 0
- var/job_karma_low = 0
-
- //Keeps track of preferrence for not getting any wanted jobs
- var/alternate_option = 2
-
- // maps each organ to either null(intact), "cyborg" or "amputated"
- // will probably not be able to do this for head and torso ;)
- var/list/organ_data = list()
- var/list/rlimb_data = list()
-
- var/list/player_alt_titles = new() // the default name of a job like "Medical Doctor"
-// var/accent = "en-us"
-// var/voice = "m1"
-// var/pitch = 50
-// var/talkspeed = 175
- var/flavor_text = ""
- var/med_record = ""
- var/sec_record = ""
- var/gen_record = ""
- var/disabilities = 0
-
- var/nanotrasen_relation = "Neutral"
-
- // 0 = character settings, 1 = game preferences
- var/current_tab = TAB_CHAR
-
- // OOC Metadata:
- var/metadata = ""
- var/slot_name = ""
- var/saved = FALSE // Indicates whether the character comes from the database or not
-
/// Volume mixer, indexed by channel as TEXT (numerical indexes will not work). Volume goes from 0 to 100.
var/list/volume_mixer = list(
"1024" = 100, // CHANNEL_LOBBYMUSIC
@@ -200,39 +99,41 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
// BYOND membership
var/unlock_content = 0
- //Gear stuff
- var/list/loadout_gear = list()
var/gear_tab = "General"
// Parallax
var/parallax = PARALLAX_HIGH
/// 2FA status
var/_2fa_status = _2FA_DISABLED
- /// Do we want to force our runechat colour to be white?
- var/force_white_runechat = FALSE
+ /// Did we load successfully?
+ var/successful_load = FALSE
+ /// Have we loaded characters already?
+ var/characters_loaded = FALSE
-/datum/preferences/New(client/C)
+ // 0 = character settings, 1 = game preferences, 2 = loadout
+ var/current_tab = TAB_CHAR
+
+ /// List of all character saves we have. This is indexed based on the slot number
+ var/list/datum/character_save/character_saves = list()
+ /// The current active character
+ var/datum/character_save/active_character
+
+/datum/preferences/New(client/C, datum/db_query/Q) // Process our query
parent = C
- b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
max_gear_slots = GLOB.configuration.general.base_loadout_points
- var/loaded_preferences_successfully = FALSE
+
+ if(!SSdbcore.IsConnected())
+ return // Bail
+
if(istype(C))
if(!IsGuestKey(C.key))
unlock_content = C.IsByondMember()
if(unlock_content)
max_save_slots = MAX_SAVE_SLOTS_MEMBER
- loaded_preferences_successfully = load_preferences(C) // Do not call this with no client/C, it generates a runtime / SQL error
- if(loaded_preferences_successfully)
- if(load_character(C))
- return
- //we couldn't load character data so just randomize the character appearance + name
- random_character() //let's create a random character then - rather than a fat, bald and naked man.
- real_name = random_name(gender)
- if(istype(C))
- if(!loaded_preferences_successfully)
- save_preferences(C) // Do not call this with no client/C, it generates a runtime / SQL error
- save_character(C) // Do not call this with no client/C, it generates a runtime / SQL error
+ successful_load = load_preferences(Q)
+ if(!successful_load)
+ to_chat(C, "Your preferences failed to load. Please inform the server host immediately.")
/datum/preferences/proc/color_square(colour)
return "___"
@@ -241,9 +142,9 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
/datum/preferences/proc/ShowChoices(mob/user)
if(!user || !user.client)
return
- update_preview_icon()
- user << browse_rsc(preview_icon_front, "previewicon.png")
- user << browse_rsc(preview_icon_side, "previewicon2.png")
+ active_character.update_preview_icon()
+ user << browse_rsc(active_character.preview_icon_front, "previewicon.png")
+ user << browse_rsc(active_character.preview_icon_side, "previewicon2.png")
var/dat = ""
dat += ""
@@ -255,26 +156,25 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
switch(current_tab)
if(TAB_CHAR) // Character Settings
- var/datum/species/S = GLOB.all_species[species]
+ var/datum/species/S = GLOB.all_species[active_character.species]
if(!istype(S)) //The species was invalid. Set the species to the default, fetch the datum for that species and generate a random character.
- species = initial(species)
- S = GLOB.all_species[species]
- random_character()
+ active_character.species = initial(active_character.species)
+ S = GLOB.all_species[active_character.species]
+ active_character.randomise()
dat += "

"
dat += ""
dat += ""
@@ -283,81 +183,83 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "You are banned from using custom names and appearances. \
You can continue to adjust your characters, but you will be randomised once you join the game.\
"
- dat += "Gender: [gender == MALE ? "Male" : (gender == FEMALE ? "Female" : "Genderless")]"
+ dat += "Gender: [active_character.gender == MALE ? "Male" : (active_character.gender == FEMALE ? "Female" : "Genderless")]"
dat += " "
- dat += "Age: [age] "
+ dat += "Age: [active_character.age] "
dat += "Body: (®) "
- dat += "Species: [species] "
- if(species == "Vox")
- dat += "N2 Tank: [speciesprefs ? "Large N2 Tank" : "Specialized N2 Tank"] "
- if(species == "Grey")
+ dat += "Species: [active_character.species] "
+ if(active_character.species == "Vox") // Purge these bastards
+ dat += "N2 Tank: [active_character.speciesprefs ? "Large N2 Tank" : "Specialized N2 Tank"] "
+ if(active_character.species == "Grey")
dat += "Wingdings: Set in disabilities "
- dat += "Voice Translator: [speciesprefs ? "Yes" : "No"] "
- dat += "Secondary Language: [language] "
+ dat += "Voice Translator: [active_character.speciesprefs ? "Yes" : "No"] "
+ dat += "Secondary Language: [active_character.language] "
if(S.autohiss_basic_map)
- dat += "Auto-accent: [autohiss_mode == AUTOHISS_FULL ? "Full" : (autohiss_mode == AUTOHISS_BASIC ? "Basic" : "Off")] "
- dat += "Blood Type: [b_type] "
+ dat += "Auto-accent: [active_character.autohiss_mode == AUTOHISS_FULL ? "Full" : (active_character.autohiss_mode == AUTOHISS_BASIC ? "Basic" : "Off")] "
+ dat += "Blood Type: [active_character.b_type] "
if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
- dat += "Skin Tone: [S.bodyflags & HAS_ICON_SKIN_TONE ? "[s_tone]" : "[-s_tone + 35]/220"] "
+ dat += "Skin Tone: [S.bodyflags & HAS_ICON_SKIN_TONE ? "[active_character.s_tone]" : "[-active_character.s_tone + 35]/220"] "
dat += "Disabilities: \[Set\] "
- dat += "Nanotrasen Relation: [nanotrasen_relation] "
+ dat += "Nanotrasen Relation: [active_character.nanotrasen_relation] "
dat += "Set Flavor Text "
- if(length(flavor_text) <= 40)
- if(!length(flavor_text)) dat += "\[...\] "
- else dat += "[flavor_text] "
- else dat += "[TextPreview(flavor_text)]... "
+ if(length(active_character.flavor_text) <= 40)
+ if(!length(active_character.flavor_text))
+ dat += "\[...\] "
+ else
+ dat += "[active_character.flavor_text] "
+ else dat += "[TextPreview(active_character.flavor_text)]... "
dat += "Hair & Accessories"
if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
var/headaccessoryname = "Head Accessory: "
- if(species == "Unathi")
+ if(active_character.species == "Unathi")
headaccessoryname = "Horns: "
dat += "[headaccessoryname]"
- dat += "[ha_style] "
- dat += "Color [color_square(hacc_colour)] "
+ dat += "[active_character.ha_style] "
+ dat += "Color [color_square(active_character.hacc_colour)] "
if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
dat += "Head Markings: "
- dat += "[m_styles["head"]]"
- dat += "Color [color_square(m_colours["head"])] "
+ dat += "[active_character.m_styles["head"]]"
+ dat += "Color [color_square(active_character.m_colours["head"])] "
if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
dat += "Body Markings: "
- dat += "[m_styles["body"]]"
- dat += "Color [color_square(m_colours["body"])] "
+ dat += "[active_character.m_styles["body"]]"
+ dat += "Color [color_square(active_character.m_colours["body"])] "
if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
dat += "Tail Markings: "
- dat += "[m_styles["tail"]]"
- dat += "Color [color_square(m_colours["tail"])] "
+ dat += "[active_character.m_styles["tail"]]"
+ dat += "Color [color_square(active_character.m_colours["tail"])] "
dat += "Hair: "
- dat += "[h_style]"
- dat += "Color [color_square(h_colour)]"
- var/datum/sprite_accessory/temp_hair_style = GLOB.hair_styles_public_list[h_style]
+ dat += "[active_character.h_style]"
+ dat += "Color [color_square(active_character.h_colour)]"
+ var/datum/sprite_accessory/temp_hair_style = GLOB.hair_styles_public_list[active_character.h_style]
if(temp_hair_style && temp_hair_style.secondary_theme && !temp_hair_style.no_sec_colour)
- dat += " Color #2 [color_square(h_sec_colour)]"
+ dat += " Color #2 [color_square(active_character.h_sec_colour)]"
dat += " "
dat += "Facial Hair: "
- dat += "[f_style ? "[f_style]" : "Shaved"]"
- dat += "Color [color_square(f_colour)]"
- var/datum/sprite_accessory/temp_facial_hair_style = GLOB.facial_hair_styles_list[f_style]
+ dat += "[active_character.f_style ? "[active_character.f_style]" : "Shaved"]"
+ dat += "Color [color_square(active_character.f_colour)]"
+ var/datum/sprite_accessory/temp_facial_hair_style = GLOB.facial_hair_styles_list[active_character.f_style]
if(temp_facial_hair_style && temp_facial_hair_style.secondary_theme && !temp_facial_hair_style.no_sec_colour)
- dat += " Color #2 [color_square(f_sec_colour)]"
+ dat += " Color #2 [color_square(active_character.f_sec_colour)]"
dat += " "
if(!(S.bodyflags & ALL_RPARTS))
dat += "Eyes: "
- dat += "Color [color_square(e_colour)] "
+ dat += "Color [color_square(active_character.e_colour)] "
- if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins
+ if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[active_character.species] || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins
dat += "Body Color: "
- dat += "Color [color_square(s_colour)] "
+ dat += "Color [color_square(active_character.s_colour)] "
- if(GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user))
+ if(GLOB.body_accessory_by_species[active_character.species] || check_rights(R_ADMIN, 0, user))
dat += "Body Accessory: "
- dat += "[body_accessory ? "[body_accessory]" : "None"] "
+ dat += "[active_character.body_accessory ? "[active_character.body_accessory]" : "None"] "
dat += " | "
dat += "Occupation Choices"
@@ -370,15 +272,15 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "Limbs"
if(S.bodyflags & HAS_ALT_HEADS) //Species with alt heads.
dat += "Alternate Head: "
- dat += "[alt_head] "
+ dat += "[active_character.alt_head] "
dat += "Limbs and Parts: Adjust "
- if(species != "Slime People" && species != "Machine")
+ if(active_character.species != "Slime People" && active_character.species != "Machine")
dat += "Internal Organs: Adjust "
//display limbs below
var/ind = 0
- for(var/name in organ_data)
- var/status = organ_data[name]
+ for(var/name in active_character.organ_data)
+ var/status = active_character.organ_data[name]
var/organ_name = null
switch(name)
if("chest")
@@ -423,8 +325,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
switch(status)
if("cyborg")
var/datum/robolimb/R
- if(rlimb_data[name] && GLOB.all_robolimbs[rlimb_data[name]])
- R = GLOB.all_robolimbs[rlimb_data[name]]
+ if(active_character.rlimb_data[name] && GLOB.all_robolimbs[active_character.rlimb_data[name]])
+ R = GLOB.all_robolimbs[active_character.rlimb_data[name]]
else
R = GLOB.basic_robolimb
dat += "\t[R.company] [organ_name] prosthesis"
@@ -437,12 +339,12 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "Clothing"
if(S.clothing_flags & HAS_UNDERWEAR)
- dat += "Underwear: [underwear] "
+ dat += "Underwear: [active_character.underwear] "
if(S.clothing_flags & HAS_UNDERSHIRT)
- dat += "Undershirt: [undershirt] "
+ dat += "Undershirt: [active_character.undershirt] "
if(S.clothing_flags & HAS_SOCKS)
- dat += "Socks: [socks] "
- dat += "Backpack Type: [backbag] "
+ dat += "Socks: [active_character.socks] "
+ dat += "Backpack Type: [active_character.backbag] "
dat += " |
"
@@ -518,9 +420,9 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
if(TAB_GEAR)
var/total_cost = 0
var/list/type_blacklist = list()
- if(loadout_gear && loadout_gear.len)
- for(var/i = 1, i <= loadout_gear.len, i++)
- var/datum/gear/G = GLOB.gear_datums[loadout_gear[i]]
+ if(active_character.loadout_gear && length(active_character.loadout_gear))
+ for(var/i = 1, i <= length(active_character.loadout_gear), i++)
+ var/datum/gear/G = GLOB.gear_datums[active_character.loadout_gear[i]]
if(G)
if(!G.subtype_cost_overlap)
if(G.subtype_path in type_blacklist)
@@ -553,7 +455,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "
|
"
for(var/gear_name in LC.gear)
var/datum/gear/G = LC.gear[gear_name]
- var/ticked = (G.display_name in loadout_gear)
+ var/ticked = (G.display_name in active_character.loadout_gear)
if(G.donator_tier > user.client.donator_level)
dat += "| [G.display_name] | "
else
@@ -568,7 +470,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
if(ticked)
. += "
| "
for(var/datum/gear_tweak/tweak in G.gear_tweaks)
- . += " [tweak.get_contents(get_tweak_metadata(G, tweak))]"
+ . += " [tweak.get_contents(active_character.get_tweak_metadata(G, tweak))]"
. += " |
"
dat += ""
@@ -584,262 +486,30 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
popup.set_content(dat)
popup.open(0)
+/datum/preferences/proc/open_load_dialog(mob/user)
+ var/dat = ""
+ dat += ""
+ dat += "Select a character slot to load
"
+ var/name
-/datum/preferences/proc/get_gear_metadata(datum/gear/G)
- . = loadout_gear[G.display_name]
- if(!.)
- . = list()
- loadout_gear[G.display_name] = .
+ for(var/i in 1 to length(character_saves))
+ var/datum/character_save/CS = character_saves[i]
+ name = CS.real_name || "Character [i]"
+ if(i == default_slot)
+ name = "[name]"
+ dat += "[name]
"
-/datum/preferences/proc/get_tweak_metadata(datum/gear/G, datum/gear_tweak/tweak)
- var/list/metadata = get_gear_metadata(G)
- . = metadata["[tweak]"]
- if(!.)
- . = tweak.get_default()
- metadata["[tweak]"] = .
+ dat += "
"
+ dat += "Close
"
+ dat += ""
-/datum/preferences/proc/set_tweak_metadata(datum/gear/G, datum/gear_tweak/tweak, new_metadata)
- var/list/metadata = get_gear_metadata(G)
- metadata["[tweak]"] = new_metadata
-
-
-/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Head of Security", "Bartender"), widthPerColumn = 400, height = 700)
- if(!SSjobs)
- 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.
- //widthPerColumn - Screen's width for every column.
- //height - Screen's height.
- var/width = widthPerColumn
-
-
- var/list/html = list()
- html += ""
- if(!length(SSjobs.occupations))
- html += "The Jobs subsystem is not yet finished creating jobs, please try again later"
- html += "Done
" // Easier to press up here.
- else
- html += ""
- html += "Choose occupation chances
Unavailable occupations are crossed out.
"
- html += "Save
" // Easier to press up here.
- html += "Left-click to raise an occupation preference, right-click to lower it.
"
- html += ""
- html += "" // Table within a table for alignment, also allows you to easily add more colomns.
- html += ""
- 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(!SSjobs)
- return
- for(var/J in SSjobs.occupations)
- var/datum/job/job = J
-
- if(job.admin_only)
- continue
-
- if(job.hidden_from_job_prefs)
- continue
-
- index += 1
- if((index >= limit) || (job.title in splitJobs))
- if((index < limit) && (lastJob != null))
- // Dynamic window width
- width += widthPerColumn
- //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 in 1 to limit - index)
- html += "|   |   | "
- html += " | "
- index = 0
-
- html += ""
- var/rank
- if(job.alt_titles)
- rank = "[GetPlayerAltTitle(job)]"
- else
- rank = job.title
- lastJob = job
- if(!is_job_whitelisted(user, job.title))
- html += "[rank] | \[KARMA] | "
- continue
- if(jobban_isbanned(user, job.title))
- html += "[rank] \[BANNED] | "
- continue
- var/available_in_playtime = job.available_in_playtime(user.client)
- if(available_in_playtime)
- html += "[rank] \[" + get_exp_format(available_in_playtime) + " as " + job.get_exp_req_type() + "\] | "
- continue
- if(job.barred_by_disability(user.client))
- html += "[rank] \[DISABILITY\] | "
- continue
- if(!job.player_old_enough(user.client))
- var/available_in_days = job.available_in_days(user.client)
- html += "[rank] \[IN [(available_in_days)] DAYS] | "
- continue
- if((job_support_low & JOB_CIVILIAN) && (job.title != "Civilian"))
- html += "[rank] | "
- continue
- if((job.title in GLOB.command_positions) || (job.title == "AI"))//Bold head jobs
- html += "[rank]"
- else
- html += "[rank]"
-
- html += ""
-
- var/prefLevelLabel = "ERROR"
- var/prefLevelColor = "pink"
- var/prefUpperLevel = -1 // level to assign on left click
- var/prefLowerLevel = -1 // level to assign on right click
-
- if(GetJobDepartment(job, 1) & job.flag)
- prefLevelLabel = "High"
- prefLevelColor = "slateblue"
- prefUpperLevel = 4
- prefLowerLevel = 2
- else if(GetJobDepartment(job, 2) & job.flag)
- prefLevelLabel = "Medium"
- prefLevelColor = "green"
- prefUpperLevel = 1
- prefLowerLevel = 3
- else if(GetJobDepartment(job, 3) & job.flag)
- prefLevelLabel = "Low"
- prefLevelColor = "orange"
- prefUpperLevel = 2
- prefLowerLevel = 4
- else
- prefLevelLabel = "NEVER"
- prefLevelColor = "red"
- prefUpperLevel = 3
- prefLowerLevel = 1
-
-
- html += ""
-
- // HTML += ""
-
- if(job.title == "Civilian")//Civilian is special
- if(job_support_low & JOB_CIVILIAN)
- html += " Yes"
- else
- html += " No"
- html += " | "
- continue
- /*
- if(GetJobDepartment(job, 1) & job.flag)
- HTML += " \[High]"
- else if(GetJobDepartment(job, 2) & job.flag)
- HTML += " \[Medium]"
- else if(GetJobDepartment(job, 3) & job.flag)
- HTML += " \[Low]"
- else
- HTML += " \[NEVER]"
- */
- html += "[prefLevelLabel]"
-
- html += ""
-
- for(var/i in 1 to limit - index) // Finish the column so it is even
- html += "|   |   | "
-
- html += " "
- html += " |
"
-
- switch(alternate_option)
- if(GET_RANDOM_JOB)
- html += "
Get random job if preferences unavailable
"
- if(BE_ASSISTANT)
- html += "
Be a civilian if preferences unavailable
"
- if(RETURN_TO_LOBBY)
- html += "
Return to lobby if preferences unavailable
"
-
- html += "Reset"
- html += "
Learn About Job Selection"
- html += ""
-
- user << browse(null, "window=preferences")
-// user << browse(HTML, "window=mob_occupation;size=[width]x[height]")
- var/datum/browser/popup = new(user, "mob_occupation", "Occupation Preferences
", width, height)
- popup.set_window_options("can_close=0")
- var/html_string = html.Join()
- popup.set_content(html_string)
+ var/datum/browser/popup = new(user, "saves", "Character Saves
", 300, 390)
+ popup.set_content(dat)
popup.open(0)
- return
-/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level)
- if(!job)
- return 0
+/datum/preferences/proc/close_load_dialog(mob/user)
+ user << browse(null, "window=saves")
- if(level == 1) // to high
- // remove any other job(s) set to high
- job_support_med |= job_support_high
- job_engsec_med |= job_engsec_high
- job_medsci_med |= job_medsci_high
- job_karma_med |= job_karma_high
- job_support_high = 0
- job_engsec_high = 0
- job_medsci_high = 0
- job_karma_high = 0
-
- if(job.department_flag == JOBCAT_SUPPORT)
- job_support_low &= ~job.flag
- job_support_med &= ~job.flag
- job_support_high &= ~job.flag
-
- switch(level)
- if(1)
- job_support_high |= job.flag
- if(2)
- job_support_med |= job.flag
- if(3)
- job_support_low |= job.flag
-
- return 1
- else if(job.department_flag == JOBCAT_ENGSEC)
- job_engsec_low &= ~job.flag
- job_engsec_med &= ~job.flag
- job_engsec_high &= ~job.flag
-
- switch(level)
- if(1)
- job_engsec_high |= job.flag
- if(2)
- job_engsec_med |= job.flag
- if(3)
- job_engsec_low |= job.flag
-
- return 1
- else if(job.department_flag == JOBCAT_MEDSCI)
- job_medsci_low &= ~job.flag
- job_medsci_med &= ~job.flag
- job_medsci_high &= ~job.flag
-
- switch(level)
- if(1)
- job_medsci_high |= job.flag
- if(2)
- job_medsci_med |= job.flag
- if(3)
- job_medsci_low |= job.flag
-
- return 1
- else if(job.department_flag == JOBCAT_KARMA)
- job_karma_low &= ~job.flag
- job_karma_med &= ~job.flag
- job_karma_high &= ~job.flag
-
- switch(level)
- if(1)
- job_karma_high |= job.flag
- if(2)
- job_karma_med |= job.flag
- if(3)
- job_karma_low |= job.flag
-
- return 1
-
- return 0
/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl)
var/datum/job/job = SSjobs.GetJob(role)
@@ -855,96 +525,19 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
return
if(role == "Civilian")
- if(job_support_low & job.flag)
- job_support_low &= ~job.flag
+ if(active_character.job_support_low & job.flag)
+ active_character.job_support_low &= ~job.flag
else
- job_support_low |= job.flag
- SetChoices(user)
+ active_character.job_support_low |= job.flag
+ active_character.SetChoices(user)
return 1
- SetJobPreferenceLevel(job, desiredLvl)
- SetChoices(user)
+ active_character.SetJobPreferenceLevel(job, desiredLvl)
+ active_character.SetChoices(user)
return 1
-/datum/preferences/proc/ShowDisabilityState(mob/user, flag, label)
- return "[label]: [disabilities & flag ? "Yes" : "No"]"
-
-/datum/preferences/proc/SetDisabilities(mob/user)
- var/datum/species/S = GLOB.all_species[species]
- var/HTML = ""
- HTML += ""
-
- if(CAN_WINGDINGS in S.species_traits)
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_WINGDINGS, "Speak in Wingdings")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_NEARSIGHTED, "Nearsighted")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_COLOURBLIND, "Colourblind")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_BLIND, "Blind")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_DEAF, "Deaf")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_MUTE, "Mute")
- if(!(TRAIT_NOFAT in S.inherent_traits))
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_FAT, "Obese")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_NERVOUS, "Stutter")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_SWEDISH, "Swedish accent")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_CHAV, "Chav accent")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_LISP, "Lisp")
- HTML += ShowDisabilityState(user, DISABILITY_FLAG_DIZZY, "Dizziness")
-
-
- HTML += {"
- \[Done\]
- \[Reset\]
- "}
-
- var/datum/browser/popup = new(user, "disabil", "Choose Disabilities
", 350, 380)
- popup.set_content(HTML)
- popup.open(0)
-
-/datum/preferences/proc/SetRecords(mob/user)
- var/HTML = ""
- HTML += ""
-
- HTML += "Medical Records
"
-
- if(length(med_record) <= 40)
- HTML += "[med_record]"
- else
- HTML += "[copytext(med_record, 1, 37)]..."
-
- HTML += "
Employment Records
"
-
- if(length(gen_record) <= 40)
- HTML += "[gen_record]"
- else
- HTML += "[copytext(gen_record, 1, 37)]..."
-
- HTML += "
Security Records
"
-
- if(length(sec_record) <= 40)
- HTML += "[sec_record]
"
- else
- HTML += "[copytext(sec_record, 1, 37)]...
"
-
- HTML += "\[Done\]"
- HTML += ""
-
- var/datum/browser/popup = new(user, "records", "Character Records
", 350, 300)
- popup.set_content(HTML)
- popup.open(0)
-
-/datum/preferences/proc/GetPlayerAltTitle(datum/job/job)
- return player_alt_titles.Find(job.title) > 0 \
- ? player_alt_titles[job.title] \
- : job.title
-
-/datum/preferences/proc/SetPlayerAltTitle(datum/job/job, new_title)
- // remove existing entry
- if(player_alt_titles.Find(job.title))
- player_alt_titles -= job.title
- // add one if it's not default
- if(job.title != new_title)
- player_alt_titles[job.title] = new_title
-
+// This is scoped here instead of /datum/character_save just because of ShowChoices()
/datum/preferences/proc/SetJob(mob/user, role)
var/datum/job/job = SSjobs.GetJob(role)
if(!job)
@@ -953,1387 +546,21 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
return
if(role == "Civilian")
- if(job_support_low & job.flag)
- job_support_low &= ~job.flag
+ if(active_character.job_support_low & job.flag)
+ active_character.job_support_low &= ~job.flag
else
- job_support_low |= job.flag
- SetChoices(user)
+ active_character.job_support_low |= job.flag
+ active_character.SetChoices(user)
return 1
- if(GetJobDepartment(job, 1) & job.flag)
- SetJobDepartment(job, 1)
- else if(GetJobDepartment(job, 2) & job.flag)
- SetJobDepartment(job, 2)
- else if(GetJobDepartment(job, 3) & job.flag)
- SetJobDepartment(job, 3)
+ if(active_character.GetJobDepartment(job, 1) & job.flag)
+ active_character.SetJobDepartment(job, 1)
+ else if(active_character.GetJobDepartment(job, 2) & job.flag)
+ active_character.SetJobDepartment(job, 2)
+ else if(active_character.GetJobDepartment(job, 3) & job.flag)
+ active_character.SetJobDepartment(job, 3)
else//job = Never
- SetJobDepartment(job, 4)
+ active_character.SetJobDepartment(job, 4)
- SetChoices(user)
+ active_character.SetChoices(user)
return 1
-
-/datum/preferences/proc/ResetJobs()
- job_support_high = 0
- job_support_med = 0
- job_support_low = 0
-
- job_medsci_high = 0
- job_medsci_med = 0
- job_medsci_low = 0
-
- job_engsec_high = 0
- job_engsec_med = 0
- job_engsec_low = 0
-
- job_karma_high = 0
- job_karma_med = 0
- job_karma_low = 0
-
-
-/datum/preferences/proc/GetJobDepartment(datum/job/job, level)
- if(!job || !level) return 0
- switch(job.department_flag)
- if(JOBCAT_SUPPORT)
- switch(level)
- if(1)
- return job_support_high
- if(2)
- return job_support_med
- if(3)
- return job_support_low
- if(JOBCAT_MEDSCI)
- switch(level)
- if(1)
- return job_medsci_high
- if(2)
- return job_medsci_med
- if(3)
- return job_medsci_low
- if(JOBCAT_ENGSEC)
- switch(level)
- if(1)
- return job_engsec_high
- if(2)
- return job_engsec_med
- if(3)
- return job_engsec_low
- if(JOBCAT_KARMA)
- switch(level)
- if(1)
- return job_karma_high
- if(2)
- return job_karma_med
- if(3)
- return job_karma_low
- return 0
-
-/datum/preferences/proc/SetJobDepartment(datum/job/job, level)
- if(!job || !level) return 0
- switch(level)
- if(1)//Only one of these should ever be active at once so clear them all here
- job_support_high = 0
- job_medsci_high = 0
- job_engsec_high = 0
- job_karma_high = 0
- return 1
- if(2)//Set current highs to med, then reset them
- job_support_med |= job_support_high
- job_medsci_med |= job_medsci_high
- job_engsec_med |= job_engsec_high
- job_karma_med |= job_karma_high
- job_support_high = 0
- job_medsci_high = 0
- job_engsec_high = 0
- job_karma_high = 0
-
- switch(job.department_flag)
- if(JOBCAT_SUPPORT)
- switch(level)
- if(2)
- job_support_high = job.flag
- job_support_med &= ~job.flag
- if(3)
- job_support_med |= job.flag
- job_support_low &= ~job.flag
- else
- job_support_low |= job.flag
- if(JOBCAT_MEDSCI)
- switch(level)
- if(2)
- job_medsci_high = job.flag
- job_medsci_med &= ~job.flag
- if(3)
- job_medsci_med |= job.flag
- job_medsci_low &= ~job.flag
- else
- job_medsci_low |= job.flag
- if(JOBCAT_ENGSEC)
- switch(level)
- if(2)
- job_engsec_high = job.flag
- job_engsec_med &= ~job.flag
- if(3)
- job_engsec_med |= job.flag
- job_engsec_low &= ~job.flag
- else
- job_engsec_low |= job.flag
- if(JOBCAT_KARMA)
- switch(level)
- if(2)
- job_karma_high = job.flag
- job_karma_med &= ~job.flag
- if(3)
- job_karma_med |= job.flag
- job_karma_low &= ~job.flag
- else
- job_karma_low |= job.flag
- return 1
-
-/datum/preferences/proc/process_link(mob/user, list/href_list)
- if(!user) return
-
- var/datum/species/S = GLOB.all_species[species]
- if(href_list["preference"] == "job")
- switch(href_list["task"])
- if("close")
- user << browse(null, "window=mob_occupation")
- ShowChoices(user)
- if("reset")
- ResetJobs()
- SetChoices(user)
- if("learnaboutselection")
- if(GLOB.configuration.url.wiki_url)
- if(alert("Would you like to open the Job selection info in your browser?", "Open Job Selection", "Yes", "No") == "Yes")
- user << link("[GLOB.configuration.url.wiki_url]/index.php/Job_Selection_and_Assignment")
- else
- to_chat(user, "The Wiki URL is not set in the server configuration.")
- if("random")
- if(alternate_option == GET_RANDOM_JOB || alternate_option == BE_ASSISTANT)
- alternate_option += 1
- else if(alternate_option == RETURN_TO_LOBBY)
- alternate_option = 0
- else
- return 0
- SetChoices(user)
- if("alt_title")
- var/datum/job/job = locate(href_list["job"])
- if(job)
- var/choices = list(job.title) + job.alt_titles
- var/choice = input("Pick a title for [job.title].", "Character Generation", GetPlayerAltTitle(job)) as anything in choices | null
- if(choice)
- SetPlayerAltTitle(job, choice)
- SetChoices(user)
- if("input")
- SetJob(user, href_list["text"])
- if("setJobLevel")
- UpdateJobPreference(user, href_list["text"], text2num(href_list["level"]))
- else
- SetChoices(user)
- return 1
- else if(href_list["preference"] == "disabilities")
-
- switch(href_list["task"])
- if("close")
- user << browse(null, "window=disabil")
- ShowChoices(user)
- if("reset")
- disabilities=0
- SetDisabilities(user)
- if("input")
- var/dflag=text2num(href_list["disability"])
- if(dflag >= 0) // Toggle it.
- disabilities ^= text2num(href_list["disability"]) //MAGIC
- SetDisabilities(user)
- else
- SetDisabilities(user)
- return 1
-
- else if(href_list["preference"] == "records")
- if(text2num(href_list["record"]) >= 1)
- SetRecords(user)
- return
- else
- user << browse(null, "window=records")
- if(href_list["task"] == "med_record")
- var/medmsg = input(usr,"Set your medical notes here.","Medical Records",html_decode(med_record)) as message
-
- if(medmsg != null)
- medmsg = copytext(medmsg, 1, MAX_PAPER_MESSAGE_LEN)
- medmsg = html_encode(medmsg)
-
- med_record = medmsg
- SetRecords(user)
-
- if(href_list["task"] == "sec_record")
- var/secmsg = input(usr,"Set your security notes here.","Security Records",html_decode(sec_record)) as message
-
- if(secmsg != null)
- secmsg = copytext(secmsg, 1, MAX_PAPER_MESSAGE_LEN)
- secmsg = html_encode(secmsg)
-
- sec_record = secmsg
- SetRecords(user)
- if(href_list["task"] == "gen_record")
- var/genmsg = input(usr,"Set your employment notes here.","Employment Records",html_decode(gen_record)) as message
-
- if(genmsg != null)
- genmsg = copytext(genmsg, 1, MAX_PAPER_MESSAGE_LEN)
- genmsg = html_encode(genmsg)
-
- gen_record = genmsg
- SetRecords(user)
-
- if(href_list["preference"] == "gear")
- if(href_list["toggle_gear"])
- var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]]
- if(TG.display_name in loadout_gear)
- loadout_gear -= TG.display_name
- else
- if(TG.donator_tier && user.client.donator_level < TG.donator_tier)
- to_chat(user, "That gear is only available at a higher donation tier than you are on.")
- return
- var/total_cost = 0
- var/list/type_blacklist = list()
- for(var/gear_name in loadout_gear)
- var/datum/gear/G = GLOB.gear_datums[gear_name]
- if(istype(G))
- if(!G.subtype_cost_overlap)
- if(G.subtype_path in type_blacklist)
- continue
- type_blacklist += G.subtype_path
- total_cost += G.cost
-
- if((total_cost + TG.cost) <= max_gear_slots)
- loadout_gear += TG.display_name
-
- else if(href_list["gear"] && href_list["tweak"])
- var/datum/gear/gear = GLOB.gear_datums[href_list["gear"]]
- var/datum/gear_tweak/tweak = locate(href_list["tweak"])
- if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks))
- return
- var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak))
- if(!metadata)
- return
- set_tweak_metadata(gear, tweak, metadata)
- else if(href_list["select_category"])
- gear_tab = href_list["select_category"]
- else if(href_list["clear_loadout"])
- loadout_gear.Cut()
-
- ShowChoices(user)
- return
-
- switch(href_list["task"])
- if("random")
- var/datum/robolimb/robohead
- if(S.bodyflags & ALL_RPARTS)
- var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]"
- robohead = GLOB.all_robolimbs[head_model]
- switch(href_list["preference"])
- if("name")
- real_name = random_name(gender,species)
- if(isnewplayer(user))
- var/mob/new_player/N = user
- N.new_player_panel_proc()
- if("age")
- age = rand(AGE_MIN, AGE_MAX)
- if("hair")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
- h_colour = rand_hex_color()
- if("secondary_hair")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
- h_sec_colour = rand_hex_color()
- if("h_style")
- h_style = random_hair_style(gender, species, robohead)
- if("facial")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
- f_colour = rand_hex_color()
- if("secondary_facial")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox"))
- f_sec_colour = rand_hex_color()
- if("f_style")
- f_style = random_facial_hair_style(gender, species, robohead)
- if("headaccessory")
- if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
- hacc_colour = rand_hex_color()
- if("ha_style")
- if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
- ha_style = random_head_accessory(species)
- if("m_style_head")
- if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
- m_styles["head"] = random_marking_style("head", species, robohead, null, alt_head)
- if("m_head_colour")
- if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
- m_colours["head"] = rand_hex_color()
- if("m_style_body")
- if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings.
- m_styles["body"] = random_marking_style("body", species)
- if("m_body_colour")
- if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings.
- m_colours["body"] = rand_hex_color()
- if("m_style_tail")
- if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
- m_styles["tail"] = random_marking_style("tail", species, null, body_accessory)
- if("m_tail_colour")
- if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
- m_colours["tail"] = rand_hex_color()
- if("underwear")
- underwear = random_underwear(gender, species)
- ShowChoices(user)
- if("undershirt")
- undershirt = random_undershirt(gender, species)
- ShowChoices(user)
- if("socks")
- socks = random_socks(gender, species)
- ShowChoices(user)
- if("eyes")
- e_colour = rand_hex_color()
- if("s_tone")
- if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
- s_tone = random_skin_tone()
- if("s_color")
- if(S.bodyflags & HAS_SKIN_COLOR)
- s_colour = rand_hex_color()
- if("bag")
- backbag = pick(GLOB.backbaglist)
- /*if("skin_style")
- h_style = random_skin_style(gender)*/
- if("all")
- random_character()
- if("input")
- switch(href_list["preference"])
- if("name")
- var/raw_name = clean_input("Choose your character's name:", "Character Preference", , user)
- if(!isnull(raw_name)) // Check to ensure that the user entered text (rather than cancel.)
- var/new_name = reject_bad_name(raw_name, 1)
- if(new_name)
- real_name = new_name
- if(isnewplayer(user))
- var/mob/new_player/N = user
- N.new_player_panel_proc()
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .")
-
- if("age")
- var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null
- if(new_age)
- age = max(min(round(text2num(new_age)), AGE_MAX),AGE_MIN)
- if("species")
- var/list/new_species = list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin")
- var/prev_species = species
-
- for(var/species in GLOB.whitelisted_species)
- if(is_alien_whitelisted(user, species))
- new_species += species
-
- species = input("Please select a species", "Character Generation", null) in sortTim(new_species, /proc/cmp_text_asc)
- var/datum/species/NS = GLOB.all_species[species]
- if(!istype(NS)) //The species was invalid. Notify the user and fail out.
- species = prev_species
- to_chat(user, "Invalid species, please pick something else.")
- return
- if(prev_species != species)
- if(NS.has_gender && gender == PLURAL)
- gender = pick(MALE,FEMALE)
- var/datum/robolimb/robohead
- if(NS.bodyflags & ALL_RPARTS)
- var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]"
- robohead = GLOB.all_robolimbs[head_model]
- //grab one of the valid hair styles for the newly chosen species
- h_style = random_hair_style(gender, species, robohead)
-
- //grab one of the valid facial hair styles for the newly chosen species
- f_style = random_facial_hair_style(gender, species, robohead)
-
- if(NS.bodyflags & HAS_HEAD_ACCESSORY) //Species that have head accessories.
- ha_style = random_head_accessory(species)
- else
- ha_style = "None" // No Vulp ears on Unathi
- hacc_colour = rand_hex_color()
-
- if(NS.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
- m_styles["head"] = random_marking_style("head", species, robohead, null, alt_head)
- else
- m_styles["head"] = "None"
- m_colours["head"] = "#000000"
-
- if(NS.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
- m_styles["body"] = random_marking_style("body", species)
- else
- m_styles["body"] = "None"
- m_colours["body"] = "#000000"
-
- if(NS.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
- m_styles["tail"] = random_marking_style("tail", species, null, body_accessory)
- else
- m_styles["tail"] = "None"
- m_colours["tail"] = "#000000"
-
- // Don't wear another species' underwear!
- var/datum/sprite_accessory/SA = GLOB.underwear_list[underwear]
- if(!SA || !(species in SA.species_allowed))
- underwear = random_underwear(gender, species)
-
- SA = GLOB.undershirt_list[undershirt]
- if(!SA || !(species in SA.species_allowed))
- undershirt = random_undershirt(gender, species)
-
- SA = GLOB.socks_list[socks]
- if(!SA || !(species in SA.species_allowed))
- socks = random_socks(gender, species)
-
- //reset skin tone and colour
- if(NS.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
- random_skin_tone(species)
- else
- s_tone = 0
-
- if(!(NS.bodyflags & HAS_SKIN_COLOR))
- s_colour = "#000000"
-
- alt_head = "None" //No alt heads on species that don't have them.
- speciesprefs = 0 //My Vox tank shouldn't change how my future Grey talks.
-
- body_accessory = null //no vulptail on humans damnit
-
- //Reset prosthetics.
- organ_data = list()
- rlimb_data = list()
-
- if(!(NS.autohiss_basic_map))
- autohiss_mode = AUTOHISS_OFF
- if("speciesprefs")
- speciesprefs = !speciesprefs //Starts 0, so if someone clicks the button up top there, this won't be 0 anymore. If they click it again, it'll go back to 0.
- if("language")
-// var/languages_available
- var/list/new_languages = list("None")
- for(var/L in GLOB.all_languages)
- var/datum/language/lang = GLOB.all_languages[L]
- if(!(lang.flags & RESTRICTED))
- new_languages += lang.name
-
- language = input("Please select a secondary language", "Character Generation", null) in sortTim(new_languages, /proc/cmp_text_asc)
-
- if("autohiss_mode")
- if(S.autohiss_basic_map)
- var/list/autohiss_choice = list("Off" = AUTOHISS_OFF, "Basic" = AUTOHISS_BASIC, "Full" = AUTOHISS_FULL)
- var/new_autohiss_pref = input(user, "Choose your character's auto-accent level:", "Character Preference") as null|anything in autohiss_choice
- if(new_autohiss_pref)
- autohiss_mode = autohiss_choice[new_autohiss_pref]
-
- if("metadata")
- var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null
- if(new_metadata)
- metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN))
-
- if("b_type")
- var/new_b_type = input(user, "Choose your character's blood-type:", "Character Preference") as null|anything in list( "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-" )
- if(new_b_type)
- b_type = new_b_type
-
- if("hair")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox")) //Species that have hair. (No HAS_HAIR flag)
- var/input = "Choose your character's hair colour:"
- var/new_hair = input(user, input, "Character Preference", h_colour) as color|null
- if(new_hair)
- h_colour = new_hair
-
- if("secondary_hair")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox"))
- var/datum/sprite_accessory/hair_style = GLOB.hair_styles_public_list[h_style]
- if(hair_style.secondary_theme && !hair_style.no_sec_colour)
- var/new_hair = input(user, "Choose your character's secondary hair colour:", "Character Preference", h_sec_colour) as color|null
- if(new_hair)
- h_sec_colour = new_hair
-
- if("h_style")
- var/list/valid_hairstyles = list()
- for(var/hairstyle in GLOB.hair_styles_public_list)
- var/datum/sprite_accessory/SA = GLOB.hair_styles_public_list[hairstyle]
-
- if(hairstyle == "Bald") //Just in case.
- valid_hairstyles += hairstyle
- continue
- if(S.bodyflags & ALL_RPARTS) //Species that can use prosthetic heads.
- var/head_model
- if(!rlimb_data["head"]) //Handle situations where the head is default.
- head_model = "Morpheus Cyberkinetics"
- else
- head_model = rlimb_data["head"]
- var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model]
- if((species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
- valid_hairstyles += hairstyle //Give them their hairstyles if they do.
- else
- if(!robohead.is_monitor && ("Human" in SA.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it.
- But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */
- valid_hairstyles += hairstyle
- else //If the user is not a species who can have robotic heads, use the default handling.
- if(species in SA.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list.
- valid_hairstyles += hairstyle
-
- sortTim(valid_hairstyles, /proc/cmp_text_asc) //this alphabetizes the list
- var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_hairstyles
- if(new_h_style)
- h_style = new_h_style
-
- if("headaccessory")
- if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories.
- var/input = "Choose the colour of your your character's head accessory:"
- var/new_head_accessory = input(user, input, "Character Preference", hacc_colour) as color|null
- if(new_head_accessory)
- hacc_colour = new_head_accessory
-
- if("ha_style")
- if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories.
- var/list/valid_head_accessory_styles = list()
- for(var/head_accessory_style in GLOB.head_accessory_styles_list)
- var/datum/sprite_accessory/H = GLOB.head_accessory_styles_list[head_accessory_style]
- if(!(species in H.species_allowed))
- continue
-
- valid_head_accessory_styles += head_accessory_style
-
- sortTim(valid_head_accessory_styles, /proc/cmp_text_asc)
- var/new_head_accessory_style = input(user, "Choose the style of your character's head accessory:", "Character Preference") as null|anything in valid_head_accessory_styles
- if(new_head_accessory_style)
- ha_style = new_head_accessory_style
-
- if("alt_head")
- if(organ_data["head"] == "cyborg")
- return
- if(S.bodyflags & HAS_ALT_HEADS) //Species with alt heads.
- var/list/valid_alt_heads = list()
- valid_alt_heads["None"] = GLOB.alt_heads_list["None"] //The only null entry should be the "None" option
- for(var/alternate_head in GLOB.alt_heads_list)
- var/datum/sprite_accessory/alt_heads/head = GLOB.alt_heads_list[alternate_head]
- if(!(species in head.species_allowed))
- continue
-
- valid_alt_heads += alternate_head
-
- var/new_alt_head = input(user, "Choose your character's alternate head style:", "Character Preference") as null|anything in valid_alt_heads
- if(new_alt_head)
- alt_head = new_alt_head
- if(m_styles["head"])
- var/head_marking = m_styles["head"]
- var/datum/sprite_accessory/body_markings/head/head_marking_style = GLOB.marking_styles_list[head_marking]
- if(!head_marking_style.heads_allowed || (!("All" in head_marking_style.heads_allowed) && !(alt_head in head_marking_style.heads_allowed)))
- m_styles["head"] = "None"
-
- if("m_style_head")
- if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
- var/list/valid_markings = list()
- valid_markings["None"] = GLOB.marking_styles_list["None"]
- for(var/markingstyle in GLOB.marking_styles_list)
- var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[markingstyle]
- if(!(species in M.species_allowed))
- continue
- if(M.marking_location != "head")
- continue
- if(alt_head && alt_head != "None")
- if(!("All" in M.heads_allowed) && !(alt_head in M.heads_allowed))
- continue
- else
- if(M.heads_allowed && !("All" in M.heads_allowed))
- continue
-
- if(S.bodyflags & ALL_RPARTS) //Species that can use prosthetic heads.
- var/head_model
- if(!rlimb_data["head"]) //Handle situations where the head is default.
- head_model = "Morpheus Cyberkinetics"
- else
- head_model = rlimb_data["head"]
- var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model]
- if(robohead.is_monitor && M.name != "None") //If the character can have prosthetic heads and they have the default Morpheus head (or another monitor-head), no optic markings.
- continue
- else if(!robohead.is_monitor && M.name != "None") //Otherwise, if they DON'T have the default head and the head's not a monitor but the head's not in the style's list of allowed models, skip.
- if(!(robohead.company in M.models_allowed))
- continue
-
- valid_markings += markingstyle
- sortTim(valid_markings, /proc/cmp_text_asc)
- var/new_marking_style = input(user, "Choose the style of your character's head markings:", "Character Preference", m_styles["head"]) as null|anything in valid_markings
- if(new_marking_style)
- m_styles["head"] = new_marking_style
-
- if("m_head_colour")
- if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings.
- var/input = "Choose the colour of your your character's head markings:"
- var/new_markings = input(user, input, "Character Preference", m_colours["head"]) as color|null
- if(new_markings)
- m_colours["head"] = new_markings
-
- if("m_style_body")
- if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
- var/list/valid_markings = list()
- valid_markings["None"] = GLOB.marking_styles_list["None"]
- for(var/markingstyle in GLOB.marking_styles_list)
- var/datum/sprite_accessory/M = GLOB.marking_styles_list[markingstyle]
- if(!(species in M.species_allowed))
- continue
- if(M.marking_location != "body")
- continue
-
- valid_markings += markingstyle
- sortTim(valid_markings, /proc/cmp_text_asc)
- var/new_marking_style = input(user, "Choose the style of your character's body markings:", "Character Preference", m_styles["body"]) as null|anything in valid_markings
- if(new_marking_style)
- m_styles["body"] = new_marking_style
-
- if("m_body_colour")
- if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos.
- var/input = "Choose the colour of your your character's body markings:"
- var/new_markings = input(user, input, "Character Preference", m_colours["body"]) as color|null
- if(new_markings)
- m_colours["body"] = new_markings
-
- if("m_style_tail")
- if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
- var/list/valid_markings = list()
- valid_markings["None"] = GLOB.marking_styles_list["None"]
- for(var/markingstyle in GLOB.marking_styles_list)
- var/datum/sprite_accessory/body_markings/tail/M = GLOB.marking_styles_list[markingstyle]
- if(M.marking_location != "tail")
- continue
- if(!(species in M.species_allowed))
- continue
- if(!body_accessory)
- if(M.tails_allowed)
- continue
- else
- if(!M.tails_allowed || !(body_accessory in M.tails_allowed))
- continue
-
- valid_markings += markingstyle
- sortTim(valid_markings, /proc/cmp_text_asc)
- var/new_marking_style = input(user, "Choose the style of your character's tail markings:", "Character Preference", m_styles["tail"]) as null|anything in valid_markings
- if(new_marking_style)
- m_styles["tail"] = new_marking_style
-
- if("m_tail_colour")
- if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings.
- var/input = "Choose the colour of your your character's tail markings:"
- var/new_markings = input(user, input, "Character Preference", m_colours["tail"]) as color|null
- if(new_markings)
- m_colours["tail"] = new_markings
-
- if("body_accessory")
- var/list/possible_body_accessories = list()
- if(check_rights(R_ADMIN, 1, user))
- possible_body_accessories = GLOB.body_accessory_by_name.Copy()
- else
- for(var/B in GLOB.body_accessory_by_name)
- var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[B]
- if(!istype(accessory))
- possible_body_accessories += "None" //the only null entry should be the "None" option
- continue
- if(species in accessory.allowed_species)
- possible_body_accessories += B
- sortTim(possible_body_accessories, /proc/cmp_text_asc)
- var/new_body_accessory = input(user, "Choose your body accessory:", "Character Preference") as null|anything in possible_body_accessories
- if(new_body_accessory)
- m_styles["tail"] = "None"
- body_accessory = (new_body_accessory == "None") ? null : new_body_accessory
-
- if("facial")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox")) //Species that have facial hair. (No HAS_HAIR_FACIAL flag)
- var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", f_colour) as color|null
- if(new_facial)
- f_colour = new_facial
-
- if("secondary_facial")
- if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Vulpkanin", "Vox"))
- var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style]
- if(facial_hair_style.secondary_theme && !facial_hair_style.no_sec_colour)
- var/new_facial = input(user, "Choose your character's secondary facial-hair colour:", "Character Preference", f_sec_colour) as color|null
- if(new_facial)
- f_sec_colour = new_facial
-
- if("f_style")
- var/list/valid_facial_hairstyles = list()
- for(var/facialhairstyle in GLOB.facial_hair_styles_list)
- var/datum/sprite_accessory/SA = GLOB.facial_hair_styles_list[facialhairstyle]
-
- if(facialhairstyle == "Shaved") //Just in case.
- valid_facial_hairstyles += facialhairstyle
- continue
- if(gender == MALE && SA.gender == FEMALE)
- continue
- if(gender == FEMALE && SA.gender == MALE)
- continue
- if(S.bodyflags & ALL_RPARTS) //Species that can use prosthetic heads.
- var/head_model
- if(!rlimb_data["head"]) //Handle situations where the head is default.
- head_model = "Morpheus Cyberkinetics"
- else
- head_model = rlimb_data["head"]
- var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model]
- if((species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list.
- valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do.
- else
- if(!robohead.is_monitor && ("Human" in SA.species_allowed)) /*If the facial hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it.
- But if the user has a robotic humanoid head and the facial hairstyle can fit humans, let them use it as a wig. */
- valid_facial_hairstyles += facialhairstyle
- else //If the user is not a species who can have robotic heads, use the default handling.
- if(species in SA.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list.
- valid_facial_hairstyles += facialhairstyle
- sortTim(valid_facial_hairstyles, /proc/cmp_text_asc)
- var/new_f_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in valid_facial_hairstyles
- if(new_f_style)
- f_style = new_f_style
-
- if("underwear")
- var/list/valid_underwear = list()
- for(var/underwear in GLOB.underwear_list)
- var/datum/sprite_accessory/SA = GLOB.underwear_list[underwear]
- if(gender == MALE && SA.gender == FEMALE)
- continue
- if(gender == FEMALE && SA.gender == MALE)
- continue
- if(!(species in SA.species_allowed))
- continue
- valid_underwear[underwear] = GLOB.underwear_list[underwear]
- sortTim(valid_underwear, /proc/cmp_text_asc)
- var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in valid_underwear
- ShowChoices(user)
- if(new_underwear)
- underwear = new_underwear
- if("undershirt")
- var/list/valid_undershirts = list()
- for(var/undershirt in GLOB.undershirt_list)
- var/datum/sprite_accessory/SA = GLOB.undershirt_list[undershirt]
- if(gender == MALE && SA.gender == FEMALE)
- continue
- if(gender == FEMALE && SA.gender == MALE)
- continue
- if(!(species in SA.species_allowed))
- continue
- valid_undershirts[undershirt] = GLOB.undershirt_list[undershirt]
- sortTim(valid_undershirts, /proc/cmp_text_asc)
- var/new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in valid_undershirts
- ShowChoices(user)
- if(new_undershirt)
- undershirt = new_undershirt
-
- if("socks")
- var/list/valid_sockstyles = list()
- for(var/sockstyle in GLOB.socks_list)
- var/datum/sprite_accessory/SA = GLOB.socks_list[sockstyle]
- if(gender == MALE && SA.gender == FEMALE)
- continue
- if(gender == FEMALE && SA.gender == MALE)
- continue
- if(!(species in SA.species_allowed))
- continue
- valid_sockstyles[sockstyle] = GLOB.socks_list[sockstyle]
- sortTim(valid_sockstyles, /proc/cmp_text_asc)
- var/new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in valid_sockstyles
- ShowChoices(user)
- if(new_socks)
- socks = new_socks
-
- if("eyes")
- var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", e_colour) as color|null
- if(new_eyes)
- e_colour = new_eyes
-
- if("s_tone")
- if(S.bodyflags & HAS_SKIN_TONE)
- var/new_s_tone = input(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference") as num|null
- if(new_s_tone)
- s_tone = 35 - max(min(round(new_s_tone), 220), 1)
- else if(S.bodyflags & HAS_ICON_SKIN_TONE)
- var/const/MAX_LINE_ENTRIES = 4
- var/prompt = "Choose your character's skin tone: 1-[S.icon_skin_tones.len]\n("
- for(var/i = 1 to S.icon_skin_tones.len)
- if(i > MAX_LINE_ENTRIES && !((i - 1) % MAX_LINE_ENTRIES))
- prompt += "\n"
- prompt += "[i] = [S.icon_skin_tones[i]]"
- if(i != S.icon_skin_tones.len)
- prompt += ", "
- prompt += ")"
- var/skin_c = input(user, prompt, "Character Preference") as num|null
- if(isnum(skin_c))
- s_tone = max(min(round(skin_c), S.icon_skin_tones.len), 1)
-
- if("skin")
- if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user))
- var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", s_colour) as color|null
- if(new_skin)
- s_colour = new_skin
-
- if("ooccolor")
- var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference", ooccolor) as color|null
- if(new_ooccolor)
- ooccolor = new_ooccolor
-
- if("bag")
- var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist
- if(new_backbag)
- backbag = new_backbag
-
- if("nt_relation")
- var/new_relation = input(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference") as null|anything in list("Loyal", "Supportive", "Neutral", "Skeptical", "Opposed")
- if(new_relation)
- nanotrasen_relation = new_relation
-
- if("flavor_text")
- var/msg = input(usr,"Set the flavor text in your 'examine' verb. The flavor text should be a physical descriptor of your character at a glance. SFW Drawn Art of your character is acceptable.","Flavor Text",html_decode(flavor_text)) as message
-
- if(msg != null)
- msg = copytext(msg, 1, MAX_MESSAGE_LEN)
- msg = html_encode(msg)
-
- flavor_text = msg
-
- if("limbs")
- var/valid_limbs = list("Left Leg", "Right Leg", "Left Arm", "Right Arm", "Left Foot", "Right Foot", "Left Hand", "Right Hand")
- if(S.bodyflags & ALL_RPARTS)
- valid_limbs = list("Torso", "Lower Body", "Head", "Left Leg", "Right Leg", "Left Arm", "Right Arm", "Left Foot", "Right Foot", "Left Hand", "Right Hand")
- var/limb_name = input(user, "Which limb do you want to change?") as null|anything in valid_limbs
- if(!limb_name) return
-
- var/limb = null
- var/second_limb = null // if you try to change the arm, the hand should also change
- var/third_limb = null // if you try to unchange the hand, the arm should also change
- var/valid_limb_states = list("Normal", "Amputated", "Prosthesis")
- var/no_amputate = 0
-
- switch(limb_name)
- if("Torso")
- limb = "chest"
- second_limb = "groin"
- no_amputate = 1
- if("Lower Body")
- limb = "groin"
- no_amputate = 1
- if("Head")
- limb = "head"
- no_amputate = 1
- if("Left Leg")
- limb = "l_leg"
- second_limb = "l_foot"
- if("Right Leg")
- limb = "r_leg"
- second_limb = "r_foot"
- if("Left Arm")
- limb = "l_arm"
- second_limb = "l_hand"
- if("Right Arm")
- limb = "r_arm"
- second_limb = "r_hand"
- if("Left Foot")
- limb = "l_foot"
- if(!(S.bodyflags & ALL_RPARTS))
- third_limb = "l_leg"
- if("Right Foot")
- limb = "r_foot"
- if(!(S.bodyflags & ALL_RPARTS))
- third_limb = "r_leg"
- if("Left Hand")
- limb = "l_hand"
- if(!(S.bodyflags & ALL_RPARTS))
- third_limb = "l_arm"
- if("Right Hand")
- limb = "r_hand"
- if(!(S.bodyflags & ALL_RPARTS))
- third_limb = "r_arm"
-
- var/new_state = input(user, "What state do you wish the limb to be in?") as null|anything in valid_limb_states
- if(!new_state) return
-
- switch(new_state)
- if("Normal")
- if(limb == "head")
- m_styles["head"] = "None"
- h_style = GLOB.hair_styles_public_list["Bald"]
- f_style = GLOB.facial_hair_styles_list["Shaved"]
- organ_data[limb] = null
- rlimb_data[limb] = null
- if(third_limb)
- organ_data[third_limb] = null
- rlimb_data[third_limb] = null
- if("Amputated")
- if(!no_amputate)
- organ_data[limb] = "amputated"
- rlimb_data[limb] = null
- if(second_limb)
- organ_data[second_limb] = "amputated"
- rlimb_data[second_limb] = null
- if("Prosthesis")
- var/choice
- var/subchoice
- var/datum/robolimb/R = new()
- var/in_model
- var/robolimb_companies = list()
- for(var/limb_type in typesof(/datum/robolimb)) //This loop populates a list of companies that offer the limb the user selected previously as one of their cybernetic products.
- R = new limb_type()
- if(!R.unavailable_at_chargen && (limb in R.parts) && R.has_subtypes) //Ensures users can only choose companies that offer the parts they want, that singular models get added to the list as well companies that offer more than one model, and...
- robolimb_companies[R.company] = R //List only main brands that have the parts we're looking for.
- R = new() //Re-initialize R.
-
- choice = input(user, "Which manufacturer do you wish to use for this limb?") as null|anything in robolimb_companies //Choose from a list of companies that offer the part the user wants.
- if(!choice)
- return
- R.company = choice
- R = GLOB.all_robolimbs[R.company]
- if(R.has_subtypes == 1) //If the company the user selected provides more than just one base model, lets handle it.
- var/list/robolimb_models = list()
- for(var/limb_type in typesof(R)) //Handling the different models of parts that manufacturers can provide.
- var/datum/robolimb/L = new limb_type()
- if(limb in L.parts) //Make sure that only models that provide the parts the user needs populate the list.
- robolimb_models[L.company] = L
- if(robolimb_models.len == 1) //If there's only one model available in the list, autoselect it to avoid having to bother the user with a dialog that provides only one option.
- subchoice = L.company //If there ends up being more than one model populating the list, subchoice will be overwritten later anyway, so this isn't a problem.
- if(second_limb in L.parts) //If the child limb of the limb the user selected is also present in the model's parts list, state it's been found so the second limb can be set later.
- in_model = 1
- if(robolimb_models.len > 1) //If there's more than one model in the list that can provide the part the user wants, let them choose.
- subchoice = input(user, "Which model of [choice] [limb_name] do you wish to use?") as null|anything in robolimb_models
- if(subchoice)
- choice = subchoice
- if(limb in list("head", "chest", "groin"))
- if(!(S.bodyflags & ALL_RPARTS))
- return
- if(limb == "head")
- ha_style = "None"
- alt_head = null
- h_style = GLOB.hair_styles_public_list["Bald"]
- f_style = GLOB.facial_hair_styles_list["Shaved"]
- m_styles["head"] = "None"
- rlimb_data[limb] = choice
- organ_data[limb] = "cyborg"
- if(second_limb)
- if(subchoice)
- if(in_model)
- rlimb_data[second_limb] = choice
- organ_data[second_limb] = "cyborg"
- else
- rlimb_data[second_limb] = choice
- organ_data[second_limb] = "cyborg"
- if("organs")
- var/organ_name = input(user, "Which internal function do you want to change?") as null|anything in list("Eyes", "Ears", "Heart", "Lungs", "Liver", "Kidneys")
- if(!organ_name)
- return
-
- var/organ = null
- switch(organ_name)
- if("Eyes")
- organ = "eyes"
- if("Ears")
- organ = "ears"
- if("Heart")
- organ = "heart"
- if("Lungs")
- organ = "lungs"
- if("Liver")
- organ = "liver"
- if("Kidneys")
- organ = "kidneys"
-
- var/new_state = input(user, "What state do you wish the organ to be in?") as null|anything in list("Normal", "Cybernetic")
- if(!new_state) return
-
- switch(new_state)
- if("Normal")
- organ_data[organ] = null
- if("Cybernetic")
- organ_data[organ] = "cybernetic"
-
- if("clientfps")
- var/version_message
- if(user.client && user.client.byond_version < 511)
- version_message = "\nYou need to be using byond version 511 or later to take advantage of this feature, your version of [user.client.byond_version] is too low"
- if(world.byond_version < 511)
- version_message += "\nThis server does not currently support client side fps. You can set now for when it does."
- var/desiredfps = input(user, "Choose your desired fps.[version_message]\n(0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num
- if(!isnull(desiredfps))
- clientfps = desiredfps
- if(world.byond_version >= 511 && user.client && user.client.byond_version >= 511)
- parent.fps = clientfps
-
- else
- switch(href_list["preference"])
- if("publicity")
- if(unlock_content)
- toggles ^= PREFTOGGLE_MEMBER_PUBLIC
-
- if("donor_public")
- if(user.client.donator_level > 0)
- toggles ^= PREFTOGGLE_DONATOR_PUBLIC
-
- if("gender")
- if(!S.has_gender)
- var/newgender = input(user, "Choose Gender:") as null|anything in list("Male", "Female", "Genderless")
- switch(newgender)
- if("Male")
- gender = MALE
- if("Female")
- gender = FEMALE
- if("Genderless")
- gender = PLURAL
- else
- if(gender == MALE)
- gender = FEMALE
- else
- gender = MALE
- underwear = random_underwear(gender)
-
- if("hear_adminhelps")
- sound ^= SOUND_ADMINHELP
- if("ui")
- switch(UI_style)
- if("Midnight")
- UI_style = "Plasmafire"
- if("Plasmafire")
- UI_style = "Retro"
- if("Retro")
- UI_style = "Slimecore"
- if("Slimecore")
- UI_style = "Operative"
- if("Operative")
- UI_style = "White"
- else
- UI_style = "Midnight"
-
- if(ishuman(usr)) //mid-round preference changes, for aesthetics
- var/mob/living/carbon/human/H = usr
- H.remake_hud()
-
- if("tgui")
- toggles2 ^= PREFTOGGLE_2_FANCYUI
-
- if("ghost_att_anim")
- toggles2 ^= PREFTOGGLE_2_ITEMATTACK
-
- if("winflash")
- toggles2 ^= PREFTOGGLE_2_WINDOWFLASHING
-
- if("afk_watch")
- if(!(toggles2 & PREFTOGGLE_2_AFKWATCH))
- to_chat(user, "You will now get put into cryo dorms after [GLOB.configuration.afk.auto_cryo_minutes] minutes. \
- Then after [GLOB.configuration.afk.auto_despawn_minutes] minutes you will be fully despawned. You will receive a visual and auditory warning before you will be put into cryodorms.")
- else
- to_chat(user, "Automatic cryoing turned off.")
- toggles2 ^= PREFTOGGLE_2_AFKWATCH
-
- if("UIcolor")
- var/UI_style_color_new = input(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) as color|null
- if(!UI_style_color_new) return
- UI_style_color = UI_style_color_new
-
- if(ishuman(usr)) //mid-round preference changes, for aesthetics
- var/mob/living/carbon/human/H = usr
- H.remake_hud()
-
- if("UIalpha")
- var/UI_style_alpha_new = input(user, "Select a new alpha(transparence) parameter for UI, between 50 and 255", UI_style_alpha) as num
- if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50))
- return
- UI_style_alpha = UI_style_alpha_new
-
- if(ishuman(usr)) //mid-round preference changes, for aesthetics
- var/mob/living/carbon/human/H = usr
- H.remake_hud()
-
- if("be_special")
- var/r = href_list["role"]
- if(r in GLOB.special_roles)
- be_special ^= r
-
- if("name")
- be_random_name = !be_random_name
-
- if("randomslot")
- toggles2 ^= PREFTOGGLE_2_RANDOMSLOT
- if(isnewplayer(usr))
- var/mob/new_player/N = usr
- N.new_player_panel_proc()
-
- if("hear_midis")
- sound ^= SOUND_MIDI
-
- if("lobby_music")
- sound ^= SOUND_LOBBY
- if((sound & SOUND_LOBBY) && user.client)
- user.client.playtitlemusic()
- else
- user.stop_sound_channel(CHANNEL_LOBBYMUSIC)
-
- if("ghost_ears")
- toggles ^= PREFTOGGLE_CHAT_GHOSTEARS
-
- if("ghost_sight")
- toggles ^= PREFTOGGLE_CHAT_GHOSTSIGHT
-
- if("ghost_radio")
- toggles ^= PREFTOGGLE_CHAT_GHOSTRADIO
-
- if("ghost_pda")
- toggles ^= PREFTOGGLE_CHAT_GHOSTPDA
-
- if("ghost_anonsay")
- toggles2 ^= PREFTOGGLE_2_ANONDCHAT
-
- if("save")
- save_preferences(user)
- save_character(user)
-
- if("reload")
- load_preferences(user)
- load_character(user)
-
- if("clear")
- if(!saved || real_name != input("This will clear the current slot permanently. Please enter the character's full name to confirm."))
- return FALSE
- clear_character_slot(user)
-
- if("open_load_dialog")
- if(!IsGuestKey(user.key))
- open_load_dialog(user)
- return 1
-
- if("close_load_dialog")
- close_load_dialog(user)
-
- if("changeslot")
- if(!load_character(user,text2num(href_list["num"])))
- random_character()
- real_name = random_name(gender)
- save_character(user)
- close_load_dialog(user)
- if(isnewplayer(user))
- var/mob/new_player/N = user
- N.new_player_panel_proc()
-
- if("tab")
- if(href_list["tab"])
- current_tab = text2num(href_list["tab"])
-
-
- if("ambientocclusion")
- toggles ^= PREFTOGGLE_AMBIENT_OCCLUSION
- if(length(parent?.screen))
- var/obj/screen/plane_master/game_world/PM = locate(/obj/screen/plane_master/game_world) in parent.screen
- PM.backdrop(parent.mob)
-
- if("parallax")
- var/parallax_styles = list(
- "Off" = PARALLAX_DISABLE,
- "Low" = PARALLAX_LOW,
- "Medium" = PARALLAX_MED,
- "High" = PARALLAX_HIGH,
- "Insane" = PARALLAX_INSANE
- )
- parallax = parallax_styles[input(user, "Pick a parallax style", "Parallax Style") as null|anything in parallax_styles]
- if(parent && parent.mob && parent.mob.hud_used)
- parent.mob.hud_used.update_parallax_pref()
-
- if("edit_2fa")
- // Do this async so we arent holding up a topic() call
- INVOKE_ASYNC(user.client, /client.proc/edit_2fa)
- return // We return here to avoid focus being lost
-
-
- ShowChoices(user)
- return 1
-
-/datum/preferences/proc/copy_to(mob/living/carbon/human/character)
- var/datum/species/S = GLOB.all_species[species]
- character.set_species(S.type) // Yell at me if this causes everything to melt
- if(be_random_name)
- real_name = random_name(gender,species)
-
- character.add_language(language)
-
-
- character.real_name = real_name
- character.dna.real_name = real_name
- character.name = character.real_name
-
- character.flavor_text = flavor_text
- character.med_record = med_record
- character.sec_record = sec_record
- character.gen_record = gen_record
-
- character.change_gender(gender)
- character.age = age
-
- //Head-specific
- var/obj/item/organ/external/head/H = character.get_organ("head")
-
- H.hair_colour = h_colour
-
- H.sec_hair_colour = h_sec_colour
-
- H.facial_colour = f_colour
-
- H.sec_facial_colour = f_sec_colour
-
- H.h_style = h_style
- H.f_style = f_style
-
- H.alt_head = alt_head
- //End of head-specific.
-
- character.skin_colour = s_colour
-
- character.s_tone = s_tone
-
- // Destroy/cyborgize organs
- for(var/name in organ_data)
-
- var/status = organ_data[name]
- var/obj/item/organ/external/O = character.bodyparts_by_name[name]
- if(O)
- if(status == "amputated")
- qdel(O.remove(character))
-
- else if(status == "cyborg")
- if(rlimb_data[name])
- O.robotize(rlimb_data[name], convert_all = 0)
- else
- O.robotize()
- else
- var/obj/item/organ/internal/I = character.get_int_organ_tag(name)
- if(I)
- if(status == "cybernetic")
- I.robotize()
-
- character.dna.blood_type = b_type
-
- // Wheelchair necessary?
- var/obj/item/organ/external/l_foot = character.get_organ("l_foot")
- var/obj/item/organ/external/r_foot = character.get_organ("r_foot")
- if(!l_foot && !r_foot)
- var/obj/structure/chair/wheelchair/W = new /obj/structure/chair/wheelchair(character.loc)
- W.buckle_mob(character, TRUE)
-
- character.underwear = underwear
- character.undershirt = undershirt
- character.socks = socks
-
- if(character.dna.species.bodyflags & HAS_HEAD_ACCESSORY)
- H.headacc_colour = hacc_colour
- H.ha_style = ha_style
- if(character.dna.species.bodyflags & HAS_MARKINGS)
- character.m_colours = m_colours
- character.m_styles = m_styles
-
- if(body_accessory)
- character.body_accessory = GLOB.body_accessory_by_name["[body_accessory]"]
-
- character.backbag = backbag
-
- //Debugging report to track down a bug, which randomly assigned the plural gender to people.
- if(character.dna.species.has_gender && (character.gender in list(PLURAL, NEUTER)))
- if(isliving(src)) //Ghosts get neuter by default
- message_admins("[key_name_admin(character)] has spawned with their gender as plural or neuter. Please notify coders.")
- character.change_gender(MALE)
-
- character.change_eye_color(e_colour)
- character.original_eye_color = e_colour
-
- if(disabilities & DISABILITY_FLAG_FAT)
- character.dna.SetSEState(GLOB.fatblock, TRUE, TRUE)
- character.overeatduration = 600
- character.dna.default_blocks.Add(GLOB.fatblock)
-
- if(disabilities & DISABILITY_FLAG_NEARSIGHTED)
- character.dna.SetSEState(GLOB.glassesblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.glassesblock)
-
- if(disabilities & DISABILITY_FLAG_BLIND)
- character.dna.SetSEState(GLOB.blindblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.blindblock)
-
- if(disabilities & DISABILITY_FLAG_DEAF)
- character.dna.SetSEState(GLOB.deafblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.deafblock)
-
- if(disabilities & DISABILITY_FLAG_COLOURBLIND)
- character.dna.SetSEState(GLOB.colourblindblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.colourblindblock)
-
- if(disabilities & DISABILITY_FLAG_MUTE)
- character.dna.SetSEState(GLOB.muteblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.muteblock)
-
- if(disabilities & DISABILITY_FLAG_NERVOUS)
- character.dna.SetSEState(GLOB.nervousblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.nervousblock)
-
- if(disabilities & DISABILITY_FLAG_SWEDISH)
- character.dna.SetSEState(GLOB.swedeblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.swedeblock)
-
- if(disabilities & DISABILITY_FLAG_CHAV)
- character.dna.SetSEState(GLOB.chavblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.chavblock)
-
- if(disabilities & DISABILITY_FLAG_LISP)
- character.dna.SetSEState(GLOB.lispblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.lispblock)
-
- if(disabilities & DISABILITY_FLAG_DIZZY)
- character.dna.SetSEState(GLOB.dizzyblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.dizzyblock)
-
- if(disabilities & DISABILITY_FLAG_WINGDINGS && (CAN_WINGDINGS in character.dna.species.species_traits))
- character.dna.SetSEState(GLOB.wingdingsblock, TRUE, TRUE)
- character.dna.default_blocks.Add(GLOB.wingdingsblock)
-
- character.dna.species.handle_dna(character)
-
- if(character.dna.dirtySE)
- character.dna.UpdateSE()
- domutcheck(character, MUTCHK_FORCED) //'Activates' all the above disabilities.
-
- character.dna.ready_dna(character, flatten_SE = 0)
- character.sync_organ_dna(assimilate=1)
- character.UpdateAppearance()
-
- // Do the initial caching of the player's body icons.
- character.force_update_limbs()
- character.update_eyes()
- character.regenerate_icons()
-
-/datum/preferences/proc/open_load_dialog(mob/user)
-
- var/datum/db_query/query = SSdbcore.NewQuery("SELECT slot, real_name FROM characters WHERE ckey=:ckey ORDER BY slot", list(
- "ckey" = user.ckey
- ))
- var/list/slotnames[max_save_slots]
-
- if(!query.warn_execute())
- qdel(query)
- return
-
- while(query.NextRow())
- slotnames[text2num(query.item[1])] = query.item[2]
- qdel(query)
-
- var/dat = ""
- dat += ""
- dat += "Select a character slot to load
"
- var/name
-
- for(var/i in 1 to max_save_slots)
- name = slotnames[i] || "Character [i]"
- if(i == default_slot)
- name = "[name]"
- dat += "[name]
"
-
- dat += "
"
- dat += "Close
"
- dat += ""
-// user << browse(dat, "window=saves;size=300x390")
- var/datum/browser/popup = new(user, "saves", "Character Saves
", 300, 390)
- popup.set_content(dat)
- popup.open(0)
-
-/datum/preferences/proc/close_load_dialog(mob/user)
- user << browse(null, "window=saves")
-
-//Check if the user has ANY job selected.
-/datum/preferences/proc/check_any_job()
- return(job_support_high || job_support_med || job_support_low || job_medsci_high || job_medsci_med || job_medsci_low || job_engsec_high || job_engsec_med || job_engsec_low || job_karma_high || job_karma_med || job_karma_low)
diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm
index 77f11729293..f46fa036a48 100644
--- a/code/modules/client/preference/preferences_mysql.dm
+++ b/code/modules/client/preference/preferences_mysql.dm
@@ -1,32 +1,6 @@
-/datum/preferences/proc/load_preferences(client/C)
-
- var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
- ooccolor,
- UI_style,
- UI_style_color,
- UI_style_alpha,
- be_role,
- default_slot,
- toggles,
- toggles_2,
- sound,
- volume_mixer,
- lastchangelog,
- exp,
- clientfps,
- atklog,
- fuid,
- parallax,
- 2fa_status
- FROM player
- WHERE ckey=:ckey"}, list(
- "ckey" = C.ckey
- ))
-
- if(!query.warn_execute())
- qdel(query)
- return
-
+/datum/preferences/proc/load_preferences(datum/db_query/query)
+ // Looking for the query?
+ // Check ../login_processing/10-load_preferences.dm
//general preferences
while(query.NextRow())
@@ -48,8 +22,6 @@
parallax = text2num(query.item[16])
_2fa_status = query.item[17]
- qdel(query)
-
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
UI_style = sanitize_inlist(UI_style, list("White", "Midnight", "Plasmafire", "Retro", "Slimecore", "Operative"), initial(UI_style))
@@ -65,7 +37,7 @@
atklog = sanitize_integer(atklog, 0, 100, initial(atklog))
fuid = sanitize_integer(fuid, 0, 10000000, initial(fuid))
parallax = sanitize_integer(parallax, 0, 16, initial(parallax))
- return 1
+ return TRUE
/datum/preferences/proc/save_preferences(client/C)
@@ -126,545 +98,24 @@
qdel(query)
return 1
-/datum/preferences/proc/load_character(client/C,slot)
- saved = FALSE
-
- if(!slot) slot = default_slot
- slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
- if(slot != default_slot)
- default_slot = slot
- var/datum/db_query/firstquery = SSdbcore.NewQuery("UPDATE player SET default_slot=:slot WHERE ckey=:ckey", list(
- "slot" = slot,
- "ckey" = C.ckey
- ))
- if(!firstquery.warn_execute(async = FALSE)) // Dont make this async. It makes roundstart slow.
- qdel(firstquery)
- return
- qdel(firstquery)
-
- // Let's not have this explode if you sneeze on the DB
- var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
- OOC_Notes,
- real_name,
- name_is_always_random,
- gender,
- age,
- species,
- language,
- hair_colour,
- secondary_hair_colour,
- facial_hair_colour,
- secondary_facial_hair_colour,
- skin_tone,
- skin_colour,
- marking_colours,
- head_accessory_colour,
- hair_style_name,
- facial_style_name,
- marking_styles,
- head_accessory_style_name,
- alt_head_name,
- eye_colour,
- underwear,
- undershirt,
- backbag,
- b_type,
- alternate_option,
- job_support_high,
- job_support_med,
- job_support_low,
- job_medsci_high,
- job_medsci_med,
- job_medsci_low,
- job_engsec_high,
- job_engsec_med,
- job_engsec_low,
- job_karma_high,
- job_karma_med,
- job_karma_low,
- flavor_text,
- med_record,
- sec_record,
- gen_record,
- disabilities,
- player_alt_titles,
- organ_data,
- rlimb_data,
- nanotrasen_relation,
- speciesprefs,
- socks,
- body_accessory,
- gear,
- autohiss
- FROM characters WHERE ckey=:ckey AND slot=:slot"}, list(
- "ckey" = C.ckey,
- "slot" = slot
- ))
- if(!query.warn_execute(async = FALSE)) // Dont make this async. It makes roundstart slow.
- qdel(query)
- return
-
- while(query.NextRow())
- //Character
- metadata = query.item[1]
- real_name = query.item[2]
- be_random_name = text2num(query.item[3])
- gender = query.item[4]
- age = text2num(query.item[5])
- species = query.item[6]
- language = query.item[7]
-
- h_colour = query.item[8]
- h_sec_colour = query.item[9]
- f_colour = query.item[10]
- f_sec_colour = query.item[11]
- s_tone = text2num(query.item[12])
- s_colour = query.item[13]
- m_colours = params2list(query.item[14])
- hacc_colour = query.item[15]
- h_style = query.item[16]
- f_style = query.item[17]
- m_styles = params2list(query.item[18])
- ha_style = query.item[19]
- alt_head = query.item[20]
- e_colour = query.item[21]
- underwear = query.item[22]
- undershirt = query.item[23]
- backbag = query.item[24]
- b_type = query.item[25]
-
-
- //Jobs
- alternate_option = text2num(query.item[26])
- job_support_high = text2num(query.item[27])
- job_support_med = text2num(query.item[28])
- job_support_low = text2num(query.item[29])
- job_medsci_high = text2num(query.item[30])
- job_medsci_med = text2num(query.item[31])
- job_medsci_low = text2num(query.item[32])
- job_engsec_high = text2num(query.item[33])
- job_engsec_med = text2num(query.item[34])
- job_engsec_low = text2num(query.item[35])
- job_karma_high = text2num(query.item[36])
- job_karma_med = text2num(query.item[37])
- job_karma_low = text2num(query.item[38])
-
- //Miscellaneous
- flavor_text = query.item[39]
- med_record = query.item[40]
- sec_record = query.item[41]
- gen_record = query.item[42]
- // Apparently, the preceding vars weren't always encoded properly...
- if(findtext(flavor_text, "<")) // ... so let's clumsily check for tags!
- flavor_text = html_encode(flavor_text)
- if(findtext(med_record, "<"))
- med_record = html_encode(med_record)
- if(findtext(sec_record, "<"))
- sec_record = html_encode(sec_record)
- if(findtext(gen_record, "<"))
- gen_record = html_encode(gen_record)
- disabilities = text2num(query.item[43])
- player_alt_titles = params2list(query.item[44])
- organ_data = params2list(query.item[45])
- rlimb_data = params2list(query.item[46])
- nanotrasen_relation = query.item[47]
- speciesprefs = text2num(query.item[48])
-
- //socks
- socks = query.item[49]
- body_accessory = query.item[50]
- loadout_gear = params2list(query.item[51])
- autohiss_mode = text2num(query.item[52])
-
- saved = TRUE
-
- qdel(query)
- //Sanitize
- var/datum/species/SP = GLOB.all_species[species]
- metadata = sanitize_text(metadata, initial(metadata))
- real_name = reject_bad_name(real_name, 1)
- if(isnull(species)) species = "Human"
- if(isnull(language)) language = "None"
- if(isnull(nanotrasen_relation)) nanotrasen_relation = initial(nanotrasen_relation)
- if(isnull(speciesprefs)) speciesprefs = initial(speciesprefs)
- if(!real_name) real_name = random_name(gender,species)
- be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
- gender = sanitize_gender(gender, FALSE, !SP.has_gender)
- age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
- h_colour = sanitize_hexcolor(h_colour)
- h_sec_colour = sanitize_hexcolor(h_sec_colour)
- f_colour = sanitize_hexcolor(f_colour)
- f_sec_colour = sanitize_hexcolor(f_sec_colour)
- s_tone = sanitize_integer(s_tone, -185, 34, initial(s_tone))
- s_colour = sanitize_hexcolor(s_colour)
- for(var/marking_location in m_colours)
- m_colours[marking_location] = sanitize_hexcolor(m_colours[marking_location], DEFAULT_MARKING_COLOURS[marking_location])
- hacc_colour = sanitize_hexcolor(hacc_colour)
- h_style = sanitize_inlist(h_style, GLOB.hair_styles_public_list, initial(h_style))
- f_style = sanitize_inlist(f_style, GLOB.facial_hair_styles_list, initial(f_style))
- for(var/marking_location in m_styles)
- m_styles[marking_location] = sanitize_inlist(m_styles[marking_location], GLOB.marking_styles_list, DEFAULT_MARKING_STYLES[marking_location])
- ha_style = sanitize_inlist(ha_style, GLOB.head_accessory_styles_list, initial(ha_style))
- alt_head = sanitize_inlist(alt_head, GLOB.alt_heads_list, initial(alt_head))
- e_colour = sanitize_hexcolor(e_colour)
- underwear = sanitize_text(underwear, initial(underwear))
- undershirt = sanitize_text(undershirt, initial(undershirt))
- backbag = sanitize_text(backbag, initial(backbag))
- b_type = sanitize_text(b_type, initial(b_type))
- autohiss_mode = sanitize_integer(autohiss_mode, 0, 2, initial(autohiss_mode))
-
- alternate_option = sanitize_integer(alternate_option, 0, 2, initial(alternate_option))
- job_support_high = sanitize_integer(job_support_high, 0, 65535, initial(job_support_high))
- job_support_med = sanitize_integer(job_support_med, 0, 65535, initial(job_support_med))
- job_support_low = sanitize_integer(job_support_low, 0, 65535, initial(job_support_low))
- job_medsci_high = sanitize_integer(job_medsci_high, 0, 65535, initial(job_medsci_high))
- job_medsci_med = sanitize_integer(job_medsci_med, 0, 65535, initial(job_medsci_med))
- job_medsci_low = sanitize_integer(job_medsci_low, 0, 65535, initial(job_medsci_low))
- job_engsec_high = sanitize_integer(job_engsec_high, 0, 65535, initial(job_engsec_high))
- job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med))
- job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low))
- job_karma_high = sanitize_integer(job_karma_high, 0, 65535, initial(job_karma_high))
- job_karma_med = sanitize_integer(job_karma_med, 0, 65535, initial(job_karma_med))
- job_karma_low = sanitize_integer(job_karma_low, 0, 65535, initial(job_karma_low))
- disabilities = sanitize_integer(disabilities, 0, 65535, initial(disabilities))
-
- socks = sanitize_text(socks, initial(socks))
- body_accessory = sanitize_text(body_accessory, initial(body_accessory))
-
-// if(isnull(disabilities)) disabilities = 0
- if(!player_alt_titles) player_alt_titles = new()
- if(!organ_data) src.organ_data = list()
- if(!rlimb_data) src.rlimb_data = list()
- if(!loadout_gear) loadout_gear = list()
-
- // Check if the current body accessory exists
- if(!GLOB.body_accessory_by_name[body_accessory])
- body_accessory = null
-
- return 1
-
-/datum/preferences/proc/save_character(client/C)
- var/organlist
- var/rlimblist
- var/playertitlelist
- var/gearlist
-
- var/markingcolourslist = list2params(m_colours)
- var/markingstyleslist = list2params(m_styles)
- if(!isemptylist(organ_data))
- organlist = list2params(organ_data)
- if(!isemptylist(rlimb_data))
- rlimblist = list2params(rlimb_data)
- if(!isemptylist(player_alt_titles))
- playertitlelist = list2params(player_alt_titles)
- if(!isemptylist(loadout_gear))
- gearlist = list2params(loadout_gear)
-
- var/datum/db_query/firstquery = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey ORDER BY slot", list(
- "ckey" = C.ckey
- ))
- if(!firstquery.warn_execute())
- qdel(firstquery)
- return
- while(firstquery.NextRow())
- if(text2num(firstquery.item[1]) == default_slot)
- var/datum/db_query/query = SSdbcore.NewQuery({"UPDATE characters
- SET
- OOC_Notes=:metadata,
- real_name=:real_name,
- name_is_always_random=:be_random_name,
- gender=:gender,
- age=:age,
- species=:species,
- language=:language,
- hair_colour=:h_colour,
- secondary_hair_colour=:h_sec_colour,
- facial_hair_colour=:f_colour,
- secondary_facial_hair_colour=:f_sec_colour,
- skin_tone=:s_tone,
- skin_colour=:s_colour,
- marking_colours=:markingcolourslist,
- head_accessory_colour=:hacc_colour,
- hair_style_name=:h_style,
- facial_style_name=:f_style,
- marking_styles=:markingstyleslist,
- head_accessory_style_name=:ha_style,
- alt_head_name=:alt_head,
- eye_colour=:e_colour,
- underwear=:underwear,
- undershirt=:undershirt,
- backbag=:backbag,
- b_type=:b_type,
- alternate_option=:alternate_option,
- job_support_high=:job_support_high,
- job_support_med=:job_support_med,
- job_support_low=:job_support_low,
- job_medsci_high=:job_medsci_high,
- job_medsci_med=:job_medsci_med,
- job_medsci_low=:job_medsci_low,
- job_engsec_high=:job_engsec_high,
- job_engsec_med=:job_engsec_med,
- job_engsec_low=:job_engsec_low,
- job_karma_high=:job_karma_high,
- job_karma_med=:job_karma_med,
- job_karma_low=:job_karma_low,
- flavor_text=:flavor_text,
- med_record=:med_record,
- sec_record=:sec_record,
- gen_record=:gen_record,
- player_alt_titles=:playertitlelist,
- disabilities=:disabilities,
- organ_data=:organlist,
- rlimb_data=:rlimblist,
- nanotrasen_relation=:nanotrasen_relation,
- speciesprefs=:speciesprefs,
- socks=:socks,
- body_accessory=:body_accessory,
- gear=:gearlist,
- autohiss=:autohiss_mode
- WHERE ckey=:ckey
- AND slot=:slot"}, list(
- // OH GOD SO MANY PARAMETERS
- "metadata" = metadata,
- "real_name" = real_name,
- "be_random_name" = be_random_name,
- "gender" = gender,
- "age" = age,
- "species" = species,
- "language" = language,
- "h_colour" = h_colour,
- "h_sec_colour" = h_sec_colour,
- "f_colour" = f_colour,
- "f_sec_colour" = f_sec_colour,
- "s_tone" = s_tone,
- "s_colour" = s_colour,
- "markingcolourslist" = markingcolourslist,
- "hacc_colour" = hacc_colour,
- "h_style" = h_style,
- "f_style" = f_style,
- "markingstyleslist" = markingstyleslist,
- "ha_style" = ha_style,
- "alt_head" = (alt_head ? alt_head : ""), // This it intentional. It wont work without it!
- "e_colour" = e_colour,
- "underwear" = underwear,
- "undershirt" = undershirt,
- "backbag" = backbag,
- "b_type" = b_type,
- "alternate_option" = alternate_option,
- "job_support_high" = job_support_high,
- "job_support_med" = job_support_med,
- "job_support_low" = job_support_low,
- "job_medsci_high" = job_medsci_high,
- "job_medsci_med" = job_medsci_med,
- "job_medsci_low" = job_medsci_low,
- "job_engsec_high" = job_engsec_high,
- "job_engsec_med" = job_engsec_med,
- "job_engsec_low" = job_engsec_low,
- "job_karma_high" = job_karma_high,
- "job_karma_med" = job_karma_med,
- "job_karma_low" = job_karma_low,
- "flavor_text" = flavor_text,
- "med_record" = med_record,
- "sec_record" = sec_record,
- "gen_record" = gen_record,
- "playertitlelist" = (playertitlelist ? playertitlelist : ""), // This it intentnional. It wont work without it!
- "disabilities" = disabilities,
- "organlist" = (organlist ? organlist : ""),
- "rlimblist" = (rlimblist ? rlimblist : ""),
- "nanotrasen_relation" = nanotrasen_relation,
- "speciesprefs" = speciesprefs,
- "socks" = socks,
- "body_accessory" = (body_accessory ? body_accessory : ""),
- "gearlist" = (gearlist ? gearlist : ""),
- "autohiss_mode" = autohiss_mode,
- "ckey" = C.ckey,
- "slot" = default_slot
- )
- )
-
- if(!query.warn_execute())
- qdel(firstquery)
- qdel(query)
- return
- qdel(firstquery)
- qdel(query)
- return 1
-
- qdel(firstquery)
-
- var/datum/db_query/query = SSdbcore.NewQuery({"
- INSERT INTO characters (ckey, slot, OOC_Notes, real_name, name_is_always_random, gender,
- age, species, language,
- hair_colour, secondary_hair_colour,
- facial_hair_colour, secondary_facial_hair_colour,
- skin_tone, skin_colour,
- marking_colours,
- head_accessory_colour,
- hair_style_name,
- facial_style_name,
- marking_styles,
- head_accessory_style_name,
- alt_head_name,
- eye_colour,
- underwear, undershirt,
- backbag, b_type, alternate_option,
- job_support_high, job_support_med, job_support_low,
- job_medsci_high, job_medsci_med, job_medsci_low,
- job_engsec_high, job_engsec_med, job_engsec_low,
- job_karma_high, job_karma_med, job_karma_low,
- flavor_text,
- med_record,
- sec_record,
- gen_record,
- player_alt_titles,
- disabilities, organ_data, rlimb_data, nanotrasen_relation, speciesprefs,
- socks, body_accessory, gear, autohiss)
-
- VALUES
- (:ckey, :slot, :metadata, :name, :be_random_name, :gender,
- :age, :species, :language,
- :h_colour, :h_sec_colour,
- :f_colour, :f_sec_colour,
- :s_tone, :s_colour,
- :markingcolourslist,
- :hacc_colour,
- :h_style,
- :f_style,
- :markingstyleslist,
- :ha_style,
- :alt_head,
- :e_colour,
- :underwear, :undershirt,
- :backbag, :b_type, :alternate_option,
- :job_support_high, :job_support_med, :job_support_low,
- :job_medsci_high, :job_medsci_med, :job_medsci_low,
- :job_engsec_high, :job_engsec_med, :job_engsec_low,
- :job_karma_high, :job_karma_med, :job_karma_low,
- :flavor_text,
- :med_record,
- :sec_record,
- :gen_record,
- :playertitlelist,
- :disabilities, :organlist, :rlimblist, :nanotrasen_relation, :speciesprefs,
- :socks, :body_accessory, :gearlist, :autohiss_mode)
-
- "}, list(
- // This has too many params for anyone to look at this without going insae
- "ckey" = C.ckey,
- "slot" = default_slot,
- "metadata" = metadata,
- "name" = real_name,
- "be_random_name" = be_random_name,
- "gender" = gender,
- "age" = age,
- "species" = species,
- "language" = language,
- "h_colour" = h_colour,
- "h_sec_colour" = h_sec_colour,
- "f_colour" = f_colour,
- "f_sec_colour" = f_sec_colour,
- "s_tone" = s_tone,
- "s_colour" = s_colour,
- "markingcolourslist" = markingcolourslist,
- "hacc_colour" = hacc_colour,
- "h_style" = h_style,
- "f_style" = f_style,
- "markingstyleslist" = markingstyleslist,
- "ha_style" = ha_style,
- "alt_head" = alt_head,
- "e_colour" = e_colour,
- "underwear" = underwear,
- "undershirt" = undershirt,
- "backbag" = backbag,
- "b_type" = b_type,
- "alternate_option" = alternate_option,
- "job_support_high" = job_support_high,
- "job_support_med" = job_support_med,
- "job_support_low" = job_support_low,
- "job_medsci_high" = job_medsci_high,
- "job_medsci_med" = job_medsci_med,
- "job_medsci_low" = job_medsci_low,
- "job_engsec_high" = job_engsec_high,
- "job_engsec_med" = job_engsec_med,
- "job_engsec_low" = job_engsec_low,
- "job_karma_high" = job_karma_high,
- "job_karma_med" = job_karma_med,
- "job_karma_low" = job_karma_low,
- "flavor_text" = flavor_text,
- "med_record" = med_record,
- "sec_record" = sec_record,
- "gen_record" = gen_record,
- "playertitlelist" = (playertitlelist ? playertitlelist : ""), // This it intentnional. It wont work without it!
- "disabilities" = disabilities,
- "organlist" = (organlist ? organlist : ""),
- "rlimblist" = (rlimblist ? rlimblist : ""),
- "nanotrasen_relation" = nanotrasen_relation,
- "speciesprefs" = speciesprefs,
- "socks" = socks,
- "body_accessory" = (body_accessory ? body_accessory : ""),
- "gearlist" = (gearlist ? gearlist : ""),
- "autohiss_mode" = autohiss_mode
- ))
-
- if(!query.warn_execute())
- qdel(query)
- return
-
- qdel(query)
- saved = TRUE
- return 1
/datum/preferences/proc/load_random_character_slot(client/C)
- var/datum/db_query/query = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey ORDER BY slot", list(
- "ckey" = C.ckey
- ))
- var/list/saves = list()
+ var/list/datum/character_save/valid_slots = list()
+ for(var/datum/character_save/CS in character_saves)
+ if(CS.valid_save)
+ valid_slots.Add(CS)
- if(!query.warn_execute(async = FALSE)) // Dont async this. Youll make roundstart slow.
- qdel(query)
+ if(!length(valid_slots))
+ // They have no valid saves. Lets just randomise #1
+ var/datum/character_save/CS = C.prefs.character_saves[1] // Get slot 1
+ CS.randomise()
+ CS.real_name = random_name(CS.gender) // Pick a name
+ C.prefs.active_character = C.prefs.character_saves[1] // Set slot 1 as their active
return
- while(query.NextRow())
- saves += text2num(query.item[1])
- qdel(query)
-
- if(!saves.len)
- load_character(C)
- return 0
- load_character(C,pick(saves))
- return 1
-
-/datum/preferences/proc/clear_character_slot(client/C)
- . = FALSE
- // Is there a character in that slot?
- var/datum/db_query/query = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey AND slot=:slot", list(
- "ckey" = C.ckey,
- "slot" = default_slot
- ))
-
- if(!query.warn_execute())
- qdel(query)
- return
-
- if(!query.NextRow())
- qdel(query)
- return
-
- qdel(query)
-
- var/datum/db_query/delete_query = SSdbcore.NewQuery("DELETE FROM characters WHERE ckey=:ckey AND slot=:slot", list(
- "ckey" = C.ckey,
- "slot" = default_slot
- ))
-
- if(!delete_query.warn_execute())
- qdel(delete_query)
- return
-
- qdel(delete_query)
-
- saved = FALSE
- return TRUE
+ var/datum/character_save/CS = pick(valid_slots)
+ C.prefs.active_character = CS
+ return
/**
* Saves [/datum/preferences/proc/volume_mixer] for the current client.
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 95fc060fc0c..95288b458bf 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -715,7 +715,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return
var/mob/living/carbon/human/new_char = new(get_turf(src))
- client.prefs.copy_to(new_char)
+ client.prefs.active_character.copy_to(new_char)
if(mind)
mind.active = TRUE
mind.transfer_to(new_char)
diff --git a/code/modules/mob/living/autohiss.dm b/code/modules/mob/living/autohiss.dm
index 78c12148ee3..0535b3f172b 100644
--- a/code/modules/mob/living/autohiss.dm
+++ b/code/modules/mob/living/autohiss.dm
@@ -2,17 +2,17 @@
return message // no autohiss at this level
/mob/living/carbon/human/handle_autohiss(message, datum/language/L)
- if(!client || client.prefs.autohiss_mode == AUTOHISS_OFF) // no need to process if there's no client or they have autohiss off
+ if(!client || client.prefs.active_character.autohiss_mode == AUTOHISS_OFF) // no need to process if there's no client or they have autohiss off
return message
- return dna.species.handle_autohiss(message, L, client.prefs.autohiss_mode)
+ return dna.species.handle_autohiss(message, L, client.prefs.active_character.autohiss_mode)
/client/verb/toggle_autohiss()
set name = "Toggle Auto-Accent"
set desc = "Toggle automatic accents for your species"
set category = "OOC"
- prefs.autohiss_mode = (prefs.autohiss_mode + 1) % AUTOHISS_NUM
- switch(prefs.autohiss_mode)
+ prefs.active_character.autohiss_mode = (prefs.active_character.autohiss_mode + 1) % AUTOHISS_NUM
+ switch(prefs.active_character.autohiss_mode)
if(AUTOHISS_OFF)
to_chat(src, "Auto-hiss is now OFF.")
if(AUTOHISS_BASIC)
@@ -20,7 +20,7 @@
if(AUTOHISS_FULL)
to_chat(src, "Auto-hiss is now FULL.")
else
- prefs.autohiss_mode = AUTOHISS_OFF
+ prefs.active_character.autohiss_mode = AUTOHISS_OFF
to_chat(src, "Auto-hiss is now OFF.")
/datum/species
@@ -61,7 +61,7 @@
/datum/species/drask
autohiss_basic_map = list(
"o" = list ("oo", "ooo"),
- "u" = list ("uu", "uuu")
+ "u" = list ("uu", "uuu")
)
autohiss_extra_map = list(
"m" = list ("mm", "mmm")
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 9c8635afa9e..4fc523a5466 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1950,7 +1950,7 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
if(GLOB.configuration.general.allow_character_metadata)
if(client)
- to_chat(usr, "[src]'s Metainfo:
[sanitize(client.prefs.metadata)]")
+ to_chat(usr, "[src]'s Metainfo:
[sanitize(client.prefs.active_character.metadata)]")
else
to_chat(usr, "[src] does not have any stored infomation!")
else
diff --git a/code/modules/mob/living/carbon/human/species/grey.dm b/code/modules/mob/living/carbon/human/species/grey.dm
index 915d0072434..6959a08e38f 100644
--- a/code/modules/mob/living/carbon/human/species/grey.dm
+++ b/code/modules/mob/living/carbon/human/species/grey.dm
@@ -67,7 +67,7 @@
to_chat(H, "The water stings[volume < 10 ? " you, but isn't concentrated enough to harm you" : null]!")
/datum/species/grey/after_equip_job(datum/job/J, mob/living/carbon/human/H)
- var/translator_pref = H.client.prefs.speciesprefs
+ var/translator_pref = H.client.prefs.active_character.speciesprefs
if(translator_pref || ((ismindshielded(H) || J.is_command || J.supervisors == "the captain") && HAS_TRAIT(H, TRAIT_WINGDINGS)))
if(J.title == "Mime")
return
diff --git a/code/modules/mob/living/carbon/human/species/vox.dm b/code/modules/mob/living/carbon/human/species/vox.dm
index f4ccced8fd8..7fc3640ac3d 100644
--- a/code/modules/mob/living/carbon/human/species/vox.dm
+++ b/code/modules/mob/living/carbon/human/species/vox.dm
@@ -81,7 +81,7 @@
H.unEquip(H.wear_mask)
H.equip_or_collect(new /obj/item/clothing/mask/breath/vox(H), slot_wear_mask)
- var/tank_pref = H.client && H.client.prefs ? H.client.prefs.speciesprefs : null
+ var/tank_pref = H.client && H.client.prefs ? H.client.prefs.active_character.speciesprefs : null
var/obj/item/tank/internal_tank
if(tank_pref)//Diseasel, here you go
internal_tank = new /obj/item/tank/internals/nitrogen(H)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 7e1c22aa237..161722fa7bd 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -668,13 +668,13 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM
/mob/proc/has_valid_preferences()
if(!client)
return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe.
- if(client.prefs.alternate_option != RETURN_TO_LOBBY)
+ if(client.prefs.active_character.alternate_option != RETURN_TO_LOBBY)
return TRUE
// If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so.
var/has_antags = FALSE
if(client.prefs.be_special.len > 0)
has_antags = TRUE
- if(!client.prefs.check_any_job())
+ if(!client.prefs.active_character.check_any_job())
to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.")
if(has_antags)
log_admin("[src.ckey] just got booted back to lobby with no jobs, but antags enabled.")
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 30a26a7af6e..14dae892908 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -42,7 +42,7 @@
/mob/new_player/proc/new_player_panel_proc()
- var/real_name = client.prefs.real_name
+ var/real_name = client.prefs.active_character.real_name
if(client.prefs.toggles2 & PREFTOGGLE_2_RANDOMSLOT)
real_name = "Random Character Slot"
var/output = "Setup Character
[real_name]
"
@@ -184,13 +184,13 @@
to_chat(src, "Now teleporting.")
observer.forceMove(O.loc)
observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly.
- client.prefs.update_preview_icon(1)
- observer.icon = client.prefs.preview_icon
+ client.prefs.active_character.update_preview_icon(1)
+ observer.icon = client.prefs.active_character.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
+ if(client.prefs.active_character.be_random_name)
+ client.prefs.active_character.real_name = random_name(client.prefs.active_character.gender,client.prefs.active_character.species)
+ observer.real_name = client.prefs.active_character.real_name
observer.name = observer.real_name
observer.key = key
QDEL_NULL(mind)
@@ -212,10 +212,10 @@
if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING)
to_chat(usr, "The round is either not ready, or has already finished...")
return
- if(client.prefs.species in GLOB.whitelisted_species)
+ if(client.prefs.active_character.species in GLOB.whitelisted_species)
- if(!is_alien_whitelisted(src, client.prefs.species))
- to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species]."))
+ if(!is_alien_whitelisted(src, client.prefs.active_character.species))
+ to_chat(src, alert("You are currently not whitelisted to play [client.prefs.active_character.species]."))
return FALSE
LateChoices()
@@ -232,12 +232,12 @@
if(client.prefs.toggles2 & PREFTOGGLE_2_RANDOMSLOT)
client.prefs.load_random_character_slot(client)
- if(client.prefs.species in GLOB.whitelisted_species)
- if(!is_alien_whitelisted(src, client.prefs.species))
- to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species]."))
+ if(client.prefs.active_character.species in GLOB.whitelisted_species)
+ if(!is_alien_whitelisted(src, client.prefs.active_character.species))
+ to_chat(src, alert("You are currently not whitelisted to play [client.prefs.active_character.species]."))
return FALSE
- AttemptLateSpawn(href_list["SelectedJob"],client.prefs.spawnpoint)
+ AttemptLateSpawn(href_list["SelectedJob"],client.prefs.active_character.spawnpoint)
return
if(!ready && href_list["preference"])
@@ -543,9 +543,9 @@
new_character.lastarea = get_area(loc)
if(SSticker.random_players || appearance_isbanned(new_character))
- client.prefs.random_character()
- client.prefs.real_name = random_name(client.prefs.gender)
- client.prefs.copy_to(new_character)
+ client.prefs.active_character.randomise()
+ client.prefs.active_character.real_name = random_name(client.prefs.active_character.gender)
+ client.prefs.active_character.copy_to(new_character)
stop_sound_channel(CHANNEL_LOBBYMUSIC)
@@ -569,19 +569,19 @@
// This is to check that the player only has preferences set that they're supposed to
/mob/new_player/proc/check_prefs_are_sane()
var/datum/species/chosen_species
- if(client.prefs.species)
- chosen_species = GLOB.all_species[client.prefs.species]
+ if(client.prefs.active_character.species)
+ chosen_species = GLOB.all_species[client.prefs.active_character.species]
if(!(chosen_species && (is_species_whitelisted(chosen_species) || has_admin_rights())))
// Have to recheck admin due to no usr at roundstart. Latejoins are fine though.
- log_runtime(EXCEPTION("[src] had species [client.prefs.species], though they weren't supposed to. Setting to Human."), src)
- client.prefs.species = "Human"
+ log_runtime(EXCEPTION("[src] had species [client.prefs.active_character.species], though they weren't supposed to. Setting to Human."), src)
+ client.prefs.active_character.species = "Human"
var/datum/language/chosen_language
- if(client.prefs.language)
- chosen_language = GLOB.all_languages[client.prefs.language]
- if((chosen_language == null && client.prefs.language != "None") || (chosen_language && chosen_language.flags & RESTRICTED))
- log_runtime(EXCEPTION("[src] had language [client.prefs.language], though they weren't supposed to. Setting to None."), src)
- client.prefs.language = "None"
+ if(client.prefs.active_character.language)
+ chosen_language = GLOB.all_languages[client.prefs.active_character.language]
+ if((chosen_language == null && client.prefs.active_character.language != "None") || (chosen_language && chosen_language.flags & RESTRICTED))
+ log_runtime(EXCEPTION("[src] had language [client.prefs.active_character.language], though they weren't supposed to. Setting to None."), src)
+ client.prefs.active_character.language = "None"
/mob/new_player/proc/ViewManifest()
GLOB.generic_crew_manifest.ui_interact(usr, state = GLOB.always_state)
@@ -607,7 +607,7 @@
/mob/new_player/get_gender()
if(!client || !client.prefs) ..()
- return client.prefs.gender
+ return client.prefs.active_character.gender
/mob/new_player/is_ready()
return ready && ..()
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 512f9188606..5e93e004b0b 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -267,9 +267,10 @@
if("human")
new_mob = new /mob/living/carbon/human(M.loc)
var/mob/living/carbon/human/H = new_mob
- var/datum/preferences/A = new() //Randomize appearance for the human
- A.species = get_random_species(TRUE)
- A.copy_to(new_mob)
+ var/datum/character_save/S = new() //Randomize appearance for the human
+ S.species = get_random_species(TRUE)
+ S.randomise()
+ S.copy_to(new_mob)
randomize = H.dna.species.name
else
return
diff --git a/paradise.dme b/paradise.dme
index f3ccd51b0b2..d2032cff916 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1368,6 +1368,17 @@
#include "code\modules\client\client_procs.dm"
#include "code\modules\client\message.dm"
#include "code\modules\client\view.dm"
+#include "code\modules\client\login_processing\10-load_preferences.dm"
+#include "code\modules\client\login_processing\20-load_characters.dm"
+#include "code\modules\client\login_processing\30-tos_consent.dm"
+#include "code\modules\client\login_processing\35-donator_check.dm"
+#include "code\modules\client\login_processing\36-watchlist.dm"
+#include "code\modules\client\login_processing\37-alts_ip.dm"
+#include "code\modules\client\login_processing\38-alts_cid.dm"
+#include "code\modules\client\login_processing\39-cid_count.dm"
+#include "code\modules\client\login_processing\__client_login_processor.dm"
+#include "code\modules\client\preference\character.dm"
+#include "code\modules\client\preference\link_processing.dm"
#include "code\modules\client\preference\preferences.dm"
#include "code\modules\client\preference\preferences_mysql.dm"
#include "code\modules\client\preference\preferences_spawnpoints.dm"
@@ -2082,7 +2093,6 @@
#include "code\modules\mob\new_player\login.dm"
#include "code\modules\mob\new_player\logout.dm"
#include "code\modules\mob\new_player\new_player.dm"
-#include "code\modules\mob\new_player\preferences_setup.dm"
#include "code\modules\mob\new_player\sprite_accessories\sprite_accessories.dm"
#include "code\modules\mob\new_player\sprite_accessories\diona\diona_hair.dm"
#include "code\modules\mob\new_player\sprite_accessories\drask\drask_body_markings.dm"