diff --git a/SQL/migrate/V020__disabilities.sql b/SQL/migrate/V020__disabilities.sql
new file mode 100644
index 00000000000..4da7f7cc61f
--- /dev/null
+++ b/SQL/migrate/V020__disabilities.sql
@@ -0,0 +1,10 @@
+--
+-- Adds different disabilities into the loadout
+-- Implemented in #4485
+--
+ALTER TABLE `ss13_characters`
+ CHANGE COLUMN `disabilities` `disabilities` TEXT NULL DEFAULT NULL AFTER `alternate_titles`;
+
+UPDATE `ss13_characters` SET `disabilities` = NULL WHERE `disabilities` = 0;
+
+UPDATE `ss13_characters` SET `disabilities` = "[\"Nearsightedness\"]" WHERE `disabilities` = 1;
diff --git a/aurorastation.dme b/aurorastation.dme
index 09492c8f802..505bd33fd65 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -1141,6 +1141,7 @@
#include "code\modules\client\asset_cache.dm"
#include "code\modules\client\client defines.dm"
#include "code\modules\client\client procs.dm"
+#include "code\modules\client\client_color.dm"
#include "code\modules\client\movement.dm"
#include "code\modules\client\preferences.dm"
#include "code\modules\client\preferences_ambience.dm"
@@ -1528,6 +1529,7 @@
#include "code\modules\mob\abstract\freelook\mask\cultnet.dm"
#include "code\modules\mob\abstract\freelook\mask\eye.dm"
#include "code\modules\mob\abstract\freelook\mask\update_triggers.dm"
+#include "code\modules\mob\abstract\new_player\character_traits.dm"
#include "code\modules\mob\abstract\new_player\login.dm"
#include "code\modules\mob\abstract\new_player\logout.dm"
#include "code\modules\mob\abstract\new_player\new_player.dm"
diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index 25c41004cde..9e507688d49 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -55,6 +55,7 @@ var/global/list/facial_hair_styles_male_list = list()
var/global/list/facial_hair_styles_female_list = list()
var/global/list/skin_styles_female_list = list() //unused
var/global/list/body_marking_styles_list = list()
+var/global/list/chargen_disabilities_list = list()
//Underwear
var/global/list/underwear_m = list("White" = "m1", "Grey" = "m2", "Green" = "m3", "Blue" = "m4", "Black" = "m5", "Mankini" = "m6", "Boxers" = "boxers", "Green and blue boxers" = "boxers_green_and_blue","Loveheart boxers" = "boxers_loveheart","None") //Curse whoever made male/female underwear diffrent colours
var/global/list/underwear_f = list("Red" = "f1", "White" = "f2", "Yellow" = "f3", "Blue" = "f4", "Black" = "f5", "Thong" = "f6", "Black Sports" = "f7","White Sports" = "f8","None")
@@ -169,6 +170,14 @@ var/global/list/cloaking_devices = list()
sortTim(body_marking_styles_list, /proc/cmp_text_asc)
+ //Disability datums
+ paths = subtypesof(/datum/character_disabilities)
+ for(var/path in paths)
+ var/datum/character_disabilities/T = new path()
+ chargen_disabilities_list[T.name] = T
+
+ sortTim(chargen_disabilities_list, /proc/cmp_text_asc)
+
//Surgery Steps - Initialize all /datum/surgery_step into a list
paths = subtypesof(/datum/surgery_step)
for(var/T in paths)
diff --git a/code/_helpers/sorting/cmp.dm b/code/_helpers/sorting/cmp.dm
index a921d158a90..b0fc68db9e4 100644
--- a/code/_helpers/sorting/cmp.dm
+++ b/code/_helpers/sorting/cmp.dm
@@ -89,3 +89,6 @@ var/cmp_field = "name"
/proc/cmp_planelayer(atom/A, atom/B)
return (B.plane - A.plane) || (B.layer - A.layer)
+
+/proc/cmp_clientcolor_priority(datum/client_color/A, datum/client_color/B)
+ return B.priority - A.priority
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 83b47c23bb3..13141bb03c4 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -12,6 +12,7 @@
layer = 20.0
unacidable = 1
var/obj/master = null //A reference to the object in the slot. Grabs or items, generally.
+ appearance_flags = NO_CLIENT_COLOR
/obj/screen/Destroy(force = FALSE)
master = null
diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm
index 9be0a713baa..ec4839a60cd 100644
--- a/code/datums/brain_damage/mild.dm
+++ b/code/datums/brain_damage/mild.dm
@@ -226,16 +226,49 @@
lose_text = "Your vision returns."
/datum/brain_trauma/mild/nearsightedness/on_gain()
- owner.disabilities |= BLIND
+ owner.disabilities |= NEARSIGHTED
..()
//no fiddling with genetics to get out of this one
/datum/brain_trauma/mild/nearsightedness/on_life()
- if(!(owner.disabilities & BLIND))
+ if(!(owner.disabilities & NEARSIGHTED))
on_gain()
..()
/datum/brain_trauma/mild/nearsightedness/on_lose()
- if(owner.disabilities & BLIND)
- owner.disabilities &= ~BLIND
+ if(owner.disabilities & NEARSIGHTED)
+ owner.disabilities &= ~NEARSIGHTED
+ ..()
+
+/datum/brain_trauma/mild/colorblind
+ name = "Partial Colorblindedness"
+ desc = "Patient's brain is loosely connected to ocular cones."
+ scan_desc = "minor damage to the brain's occipital lobe"
+ gain_text = "Your perception of color distorts!"
+ lose_text = "Your vision returns."
+ var/colorblindedness
+
+/datum/brain_trauma/mild/colorblind/on_gain()
+ colorblindedness = pick("deuteranopia", "protanopia", "tritanopia")
+ switch(colorblindedness)
+ if("deuteranopia")
+ owner.add_client_color(/datum/client_color/deuteranopia)
+ if("protanopia")
+ owner.add_client_color(/datum/client_color/protanopia)
+ if("tritanopia")
+ owner.add_client_color(/datum/client_color/tritanopia)
+ ..()
+
+/datum/brain_trauma/mild/colorblind/on_life()
+ if(owner.client && !owner.client.color)
+ on_gain()
+
+/datum/brain_trauma/mild/colorblind/on_lose()
+ switch(colorblindedness)
+ if("deuteranopia")
+ owner.remove_client_color(/datum/client_color/deuteranopia)
+ if("protanopia")
+ owner.remove_client_color(/datum/client_color/protanopia)
+ if("tritanopia")
+ owner.remove_client_color(/datum/client_color/tritanopia)
..()
\ No newline at end of file
diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm
index 91f1eeb2836..9bcb985dd2b 100644
--- a/code/datums/brain_damage/severe.dm
+++ b/code/datums/brain_damage/severe.dm
@@ -218,4 +218,23 @@
/datum/brain_trauma/severe/pacifism/on_lose()
owner.disabilities &= ~PACIFIST
+ ..()
+
+/datum/brain_trauma/severe/total_colorblind
+ name = "Total Colorblindedness"
+ desc = "Patient's brain is loosely connected to ocular cones."
+ scan_desc = "minor damage to the brain's occipital lobe"
+ gain_text = "Your perception of color vanishes!"
+ lose_text = "Your vision returns."
+
+/datum/brain_trauma/severe/total_colorblind/on_gain()
+ owner.add_client_color(/datum/client_color/monochrome)
+ ..()
+
+/datum/brain_trauma/severe/total_colorblind/on_life()
+ if(owner.client && !owner.client.color)
+ on_gain()
+
+/datum/brain_trauma/severe/total_colorblind/on_lose()
+ owner.remove_client_color(/datum/client_color/monochrome)
..()
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 6699c93a6a5..e2d19a39c5c 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -2,6 +2,7 @@
density = 1
anchored = 1
icon = 'icons/obj/cult.dmi'
+ appearance_flags = NO_CLIENT_COLOR
/obj/structure/cult/cultify()
return
@@ -623,6 +624,7 @@
/mob/living/simple_animal/hostile/creature/cult,
/mob/living/simple_animal/hostile/faithless/cult
)
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/gateway/active/cult/cultify()
return
diff --git a/code/game/objects/effects/projectile/projectile_impact.dm b/code/game/objects/effects/projectile/projectile_impact.dm
index 31afa9b8202..a8ef339f80f 100644
--- a/code/game/objects/effects/projectile/projectile_impact.dm
+++ b/code/game/objects/effects/projectile/projectile_impact.dm
@@ -47,10 +47,12 @@
name = "arcane blast"
icon_state = "impact_cult"
light_color = LIGHT_COLOR_VIOLET
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/projectile/impact/cult/heavy
icon_state = "impact_hcult"
light_power = 3
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/projectile/impact/solar
name = "solar eruption"
diff --git a/code/game/objects/effects/projectile/projectile_muzzle.dm b/code/game/objects/effects/projectile/projectile_muzzle.dm
index 307ee3eeef4..e111ee72e3b 100644
--- a/code/game/objects/effects/projectile/projectile_muzzle.dm
+++ b/code/game/objects/effects/projectile/projectile_muzzle.dm
@@ -39,10 +39,12 @@
name = "arcane flash"
icon_state = "muzzle_cult"
light_color = LIGHT_COLOR_VIOLET
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/projectile/muzzle/cult/heavy
icon_state = "muzzle_hcult"
light_power = 3
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/projectile/muzzle/solar
icon_state = "muzzle_solar"
diff --git a/code/game/objects/effects/projectile/projectile_tracer.dm b/code/game/objects/effects/projectile/projectile_tracer.dm
index 2a202b76471..edac3dc70c3 100644
--- a/code/game/objects/effects/projectile/projectile_tracer.dm
+++ b/code/game/objects/effects/projectile/projectile_tracer.dm
@@ -57,11 +57,13 @@
name = "arcane beam"
icon_state = "cult"
light_color = LIGHT_COLOR_VIOLET
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/projectile/tracer/cult/heavy
name = "heavy arcane beam"
icon_state = "hcult"
light_power = 3
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/projectile/tracer/solar
name = "solar energy"
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 66184d42ba4..3650401dc25 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -265,6 +265,7 @@
icon_state= "cultgirder"
health = 250
cover = 70
+ appearance_flags = NO_CLIENT_COLOR
/obj/structure/girder/cult/dismantle()
new /obj/effect/decal/remains/human(get_turf(src))
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 33b414336da..75b85f4995d 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -241,6 +241,7 @@
desc = "A matrice built out of an unknown material, with some sort of force field blocking air around it"
icon_state = "grillecult"
health = 40 //Make it strong enough to avoid people breaking in too easily
+ appearance_flags = NO_CLIENT_COLOR
/obj/structure/grille/cult/CanPass(atom/movable/mover, turf/target, height = 1.5, air_group = 0)
if(air_group)
diff --git a/code/game/turfs/flooring/flooring_premade.dm b/code/game/turfs/flooring/flooring_premade.dm
index 92056351f57..4e597caeb90 100644
--- a/code/game/turfs/flooring/flooring_premade.dm
+++ b/code/game/turfs/flooring/flooring_premade.dm
@@ -91,6 +91,7 @@
icon = 'icons/turf/flooring/cult.dmi'
icon_state = "cult"
initial_flooring = /decl/flooring/reinforced/cult
+ appearance_flags = NO_CLIENT_COLOR
/turf/simulated/floor/cult/cultify()
return
diff --git a/code/game/turfs/simulated/wall_types.dm b/code/game/turfs/simulated/wall_types.dm
index add91920f1e..2a6a04ca115 100644
--- a/code/game/turfs/simulated/wall_types.dm
+++ b/code/game/turfs/simulated/wall_types.dm
@@ -7,10 +7,11 @@
/turf/simulated/wall/cult
icon_state = "cult"
desc = "Hideous images dance beneath the surface."
+ appearance_flags = NO_CLIENT_COLOR
/turf/simulated/wall/cult/Initialize(mapload)
. = ..(mapload,"cult")
- desc = "Hideous images dance beneath the surface."
+ desc = "Hideous images dance beneath the surface."
/turf/simulated/wall/cult_reinforced/Initialize(mapload)
. = ..(mapload,"cult","cult2")
@@ -24,6 +25,7 @@
smooth = SMOOTH_TRUE
smoothing_hints = SMOOTHHINT_TARGETS_NOT_UNIQUE | SMOOTHHINT_ONLY_MATCH_TURF
icon_state = "cult"
+ appearance_flags = NO_CLIENT_COLOR
/turf/simulated/wall/rusty
icon_state = "arust"
diff --git a/code/modules/client/client_color.dm b/code/modules/client/client_color.dm
new file mode 100644
index 00000000000..575641ed3b6
--- /dev/null
+++ b/code/modules/client/client_color.dm
@@ -0,0 +1,71 @@
+/datum/client_color
+ var/client_color = "" //Any client.color-valid value
+ var/priority = 1 //Since only one client.color can be rendered on screen, we take the one with the highest priority value:
+ //eg: "Bloody screen" > "goggles color" as the former is much more important
+
+
+/mob
+ var/list/client_colors = list()
+
+
+
+/*
+ Adds an instance of color_type to the mob's client_colors list
+ color_type - a typepath (subtyped from /datum/client_color)
+*/
+/mob/proc/add_client_color(color_type)
+ if(!ispath(/datum/client_color))
+ return
+
+ var/datum/client_color/CC = new color_type()
+ client_colors |= CC
+ sortTim(client_colors, /proc/cmp_clientcolor_priority)
+ update_client_color()
+
+
+/*
+ Removes an instance of color_type from the mob's client_colors list
+ color_type - a typepath (subtyped from /datum/client_color)
+*/
+/mob/proc/remove_client_color(color_type)
+ if(!ispath(/datum/client_color))
+ return
+
+ for(var/cc in client_colors)
+ var/datum/client_color/CC = cc
+ if(CC.type == color_type)
+ client_colors -= CC
+ qdel(CC)
+ break
+ update_client_color()
+
+
+/*
+ Resets the mob's client.color to null, and then sets it to the highest priority
+ client_color datum, if one exists
+*/
+/mob/proc/update_client_color()
+ if(!client)
+ return
+ client.color = ""
+ if(!client_colors.len)
+ return
+ var/datum/client_color/CC = client_colors[1]
+ if(CC)
+ animate(client, color = CC.client_color)
+
+/datum/client_color/monochrome
+ client_color = list(0.33,0.33,0.33, 0.33,0.33,0.33, 0.33,0.33,0.33)
+ priority = INFINITY
+
+/datum/client_color/deuteranopia
+ client_color = list(0.47,0.38,0.15, 0.54,0.31,0.15, 0,0.3,0.7)
+ priority = 100
+
+/datum/client_color/protanopia
+ client_color = list(0.51,0.4,0.12, 0.49,0.41,0.12, 0,0.2,0.76)
+ priority = 100
+
+/datum/client_color/tritanopia
+ client_color = list(0.95,0.07,0, 0,0.44,0.52, 0.05,0.49,0.48)
+ priority = 100
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index b07361929ca..9def5770be4 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -103,7 +103,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
"facial_style" = pref.f_style,
"eyes_colour" = rgb(pref.r_eyes, pref.g_eyes, pref.b_eyes),
"b_type" = pref.b_type,
- "disabilities" = pref.disabilities,
+ "disabilities" = json_encode(pref.disabilities),
"organs_data" = list2params(pref.organ_data),
"organs_robotic"= list2params(pref.rlimb_data),
"body_markings" = json_encode(pref.body_markings),
@@ -135,8 +135,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
pref.g_eyes = GetGreenPart(pref.eyes_colour)
pref.b_eyes = GetBluePart(pref.eyes_colour)
- pref.disabilities = text2num(pref.disabilities)
-
if (istext(pref.organ_data))
pref.organ_data = params2list(pref.organ_data)
if (istext(pref.rlimb_data))
@@ -148,6 +146,13 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
catch (var/exception/e)
log_debug("BODY MARKINGS: Caught [e]. Initial value: [before]")
pref.body_markings = list()
+ if (istext(pref.disabilities))
+ var/before = pref.disabilities
+ try
+ pref.disabilities = json_decode(pref.disabilities)
+ catch (var/exception/e)
+ log_debug("DISABILITIES: Caught [e]. Initial value: [before]")
+ pref.disabilities = list()
pref.r_hair = sanitize_integer(pref.r_hair, 0, 255, initial(pref.r_hair))
pref.g_hair = sanitize_integer(pref.g_hair, 0, 255, initial(pref.g_hair))
@@ -166,13 +171,14 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
pref.b_eyes = sanitize_integer(pref.b_eyes, 0, 255, initial(pref.b_eyes))
pref.b_type = sanitize_text(pref.b_type, initial(pref.b_type))
- pref.disabilities = sanitize_integer(pref.disabilities, 0, 65535, initial(pref.disabilities))
if (!pref.organ_data || !islist(pref.organ_data))
pref.organ_data = list()
if (!pref.rlimb_data || !islist(pref.rlimb_data))
pref.rlimb_data = list()
if (!pref.body_markings || !islist(pref.body_markings))
pref.body_markings = list()
+ if (!pref.disabilities || !islist(pref.disabilities))
+ pref.disabilities = list()
/datum/category_item/player_setup_item/general/body/content(var/mob/user)
var/list/out = list()
@@ -189,7 +195,9 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
out += "Blood Type: [pref.b_type]
"
if(has_flag(mob_species, HAS_SKIN_TONE))
out += "Skin Tone: [-pref.s_tone + 35]/220
"
- out += "Needs Glasses: [pref.disabilities & NEARSIGHTED ? "Yes" : "No"]
"
+ out += "Disabilities: Adjust
"
+ for(var/M in pref.disabilities)
+ out += " [M] -
"
if(!(has_flag(mob_species, HAS_FBP)))
out += "Limbs: Adjust
"
out += "Internal Organs: Adjust
"
@@ -622,9 +630,17 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
return TOPIC_REFRESH
- else if(href_list["disabilities"])
- var/disability_flag = text2num(href_list["disabilities"])
- pref.disabilities ^= disability_flag
+ else if(href_list["trait_add"])
+ var/list/available_disabilities = pref.disabilities ^ chargen_disabilities_list
+
+ var/new_trait = input(user, "Choose a disability:", "Character Preference") as null|anything in available_disabilities
+ if(new_trait && CanUseTopic(user))
+ pref.disabilities += new_trait
+ return TOPIC_REFRESH
+
+ else if(href_list["trait_remove"])
+ var/M = href_list["trait_remove"]
+ pref.disabilities -= M
return TOPIC_REFRESH
else if(href_list["toggle_clothing"])
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 39b821d805d..aa9b19c2fd7 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -116,7 +116,7 @@ datum/preferences
var/exploit_record = ""
var/ccia_record = ""
var/list/ccia_actions = list()
- var/disabilities = 0
+ var/list/disabilities = list()
var/nanotrasen_relation = "Neutral"
@@ -369,6 +369,8 @@ datum/preferences
// Destroy/cyborgize organs & setup body markings
character.sync_organ_prefs_to_mob(src)
+ character.sync_trait_prefs_to_mob(src)
+
character.underwear = underwear
character.undershirt = undershirt
@@ -535,7 +537,7 @@ datum/preferences
flavour_texts_robot = list()
ccia_actions = list()
- disabilities = 0
+ disabilities = list()
nanotrasen_relation = "Neutral"
diff --git a/code/modules/mob/abstract/new_player/character_traits.dm b/code/modules/mob/abstract/new_player/character_traits.dm
new file mode 100644
index 00000000000..400ff187034
--- /dev/null
+++ b/code/modules/mob/abstract/new_player/character_traits.dm
@@ -0,0 +1,57 @@
+//Character trait system. Currently geared only for disabilities but a refactor to make it trait neutral would be trivial.
+
+/datum/character_disabilities
+ var/name = "Enigma"
+ var/desc = "This trait was not meant to be seen by mortal minds."
+
+/datum/character_disabilities/proc/apply_self(var/mob/living/carbon/human/H)
+ return
+
+/datum/character_disabilities/nearsighted
+ name = "Nearsightedness"
+ desc = "Without prescription glasses your vision is impaired."
+
+/datum/character_disabilities/nearsighted/apply_self(var/mob/living/carbon/human/H)
+ H.disabilities |= NEARSIGHTED
+
+/datum/character_disabilities/nervous
+ name = "Nervousness"
+ desc = "You are prone to stuttering and bouts of anxiety."
+
+/datum/character_disabilities/nervous/apply_self(var/mob/living/carbon/human/H)
+ H.disabilities |= NERVOUS
+
+/datum/character_disabilities/deuteranomaly
+ name = "Deuteranopia"
+ desc = "You have difficulty perceiving green."
+
+/datum/character_disabilities/deuteranomaly/apply_self(var/mob/living/carbon/human/H)
+ H.add_client_color(/datum/client_color/deuteranopia)
+
+/datum/character_disabilities/protanopia
+ name = "Protanopia"
+ desc = "You have difficulty perceiving red."
+
+/datum/character_disabilities/protanopia/apply_self(var/mob/living/carbon/human/H)
+ H.add_client_color(/datum/client_color/protanopia)
+
+/datum/character_disabilities/tritanopia
+ name = "Tritanopia"
+ desc = "You have difficulty perceiving green and yellow."
+
+/datum/character_disabilities/tritanopia/apply_self(var/mob/living/carbon/human/H)
+ H.add_client_color(/datum/client_color/tritanopia)
+
+/datum/character_disabilities/mute
+ name = "Muteness"
+ desc = "You are unable to form coherent speech."
+
+/datum/character_disabilities/mute/apply_self(var/mob/living/carbon/human/H)
+ H.sdisabilities |= MUTE
+
+/datum/character_disabilities/deaf
+ name = "Deafness"
+ desc = "You are unable to percieve sound."
+
+/datum/character_disabilities/deaf/apply_self(var/mob/living/carbon/human/H)
+ H.sdisabilities |= DEAF
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 5da3de9ab3e..44d46625c03 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -135,6 +135,12 @@
LAZYINITLIST(O.temporary_markings)
O.temporary_markings[M] = attr
+/mob/living/carbon/human/proc/sync_trait_prefs_to_mob(datum/preferences/prefs)
+ var/list/traits = prefs.disabilities
+ for(var/M in traits)
+ var/datum/character_disabilities/trait = chargen_disabilities_list[M]
+ trait.apply_self(src)
+
// Helper proc that grabs whatever organ this humantype uses to see.
// Usually eyes, but can be something else.
// If `no_synthetic` is TRUE, returns null for mobs that are mechanical, or for mechanical eyes.
diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm
index 41af6a1b3b8..97805cf45b3 100644
--- a/code/modules/mob/living/simple_animal/constructs/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs/constructs.dm
@@ -41,6 +41,7 @@
var/can_repair = 0
var/health_prefix = ""
+ appearance_flags = NO_CLIENT_COLOR
/mob/living/simple_animal/construct/cultify()
diff --git a/code/modules/mob/living/simple_animal/constructs/soulstone.dm b/code/modules/mob/living/simple_animal/constructs/soulstone.dm
index 4102df60dfc..03ee0f84fbb 100644
--- a/code/modules/mob/living/simple_animal/constructs/soulstone.dm
+++ b/code/modules/mob/living/simple_animal/constructs/soulstone.dm
@@ -11,6 +11,7 @@
slot_flags = SLOT_BELT
origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4)
var/imprinted = "empty"
+ appearance_flags = NO_CLIENT_COLOR
//////////////////////////////Capturing////////////////////////////////////////////////////////
@@ -88,6 +89,7 @@
return
/obj/structure/constructshell/cult
+ appearance_flags = NO_CLIENT_COLOR
icon_state = "construct-cult"
desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient."
diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm
index c03df190ed3..b98693a7abc 100644
--- a/code/modules/mob/living/simple_animal/hostile/bat.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bat.dm
@@ -74,6 +74,7 @@
faction = "cult"
supernatural = 1
tameable = FALSE
+ appearance_flags = NO_CLIENT_COLOR
/mob/living/simple_animal/hostile/scarybat/cult/cultify()
return
diff --git a/code/modules/mob/living/simple_animal/hostile/creature.dm b/code/modules/mob/living/simple_animal/hostile/creature.dm
index e921175e1e9..3fbae60ee89 100644
--- a/code/modules/mob/living/simple_animal/hostile/creature.dm
+++ b/code/modules/mob/living/simple_animal/hostile/creature.dm
@@ -32,6 +32,7 @@
minbodytemp = 0
supernatural = 1
+ appearance_flags = NO_CLIENT_COLOR
/mob/living/simple_animal/hostile/creature/cult/cultify()
return
diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm
index 491975af377..b364af8b9a3 100644
--- a/code/modules/mob/living/simple_animal/hostile/faithless.dm
+++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm
@@ -55,6 +55,7 @@
/mob/living/simple_animal/hostile/faithless/cult
faction = "cult"
+ appearance_flags = NO_CLIENT_COLOR
/mob/living/simple_animal/hostile/faithless/cult/cultify()
return
diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm
index e8f9d02acb5..1c2bc9e591e 100644
--- a/code/modules/mob/living/simple_animal/shade.dm
+++ b/code/modules/mob/living/simple_animal/shade.dm
@@ -28,6 +28,7 @@
faction = "cult"
status_flags = CANPUSH
hunger_enabled = 0
+ appearance_flags = NO_CLIENT_COLOR
/mob/living/simple_animal/shade/cultify()
return
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 432c32541fb..2a8574bb39b 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -50,3 +50,5 @@
//set macro to normal incase it was overriden (like cyborg currently does)
winset(src, null, "mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true input.background-color=#D3B5B5")
MOB_STOP_THINKING(src)
+
+ update_client_color()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index f576d1ccac7..6ce7d2e14a9 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -16,6 +16,9 @@
mind.handle_mob_deletion(src)
for(var/infection in viruses)
qdel(infection)
+ for(var/cc in client_colors)
+ qdel(cc)
+ client_colors = null
viruses.Cut()
//Added this to prevent nonliving mobs from ghostising
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 1caf2aff838..a66615f37e4 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -10,6 +10,7 @@
layer = 6
light_power = -100 //eats all light
unacidable = 1 //Don't comment this out.
+ appearance_flags = NO_CLIENT_COLOR
var/current_size = 1
var/allowed_size = 1
diff --git a/code/modules/projectiles/targeting/targeting_overlay.dm b/code/modules/projectiles/targeting/targeting_overlay.dm
index cc23bcec696..33b09cb746c 100644
--- a/code/modules/projectiles/targeting/targeting_overlay.dm
+++ b/code/modules/projectiles/targeting/targeting_overlay.dm
@@ -7,6 +7,7 @@
density = 0
opacity = 0
layer = FLY_LAYER
+ appearance_flags = NO_CLIENT_COLOR
simulated = 0
mouse_opacity = 0
diff --git a/code/modules/spells/aoe_turf/conjure/construct.dm b/code/modules/spells/aoe_turf/conjure/construct.dm
index 3885e16ad8a..c19469b5f97 100644
--- a/code/modules/spells/aoe_turf/conjure/construct.dm
+++ b/code/modules/spells/aoe_turf/conjure/construct.dm
@@ -132,6 +132,7 @@
icon_state = "m_shield_cult"
light_color = "#B40000"
light_range = 2
+ appearance_flags = NO_CLIENT_COLOR
/obj/effect/forcefield/cult/cultify()
return