diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index c4e53366e..9b5ac9395 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -86,6 +86,13 @@
var/datum/emote/E = new path()
E.emote_list[E.key] = E
+
+
+ // Hair Gradients - Initialise all /datum/sprite_accessory/hair_gradient into an list indexed by gradient-style name
+ for(var/path in subtypesof(/datum/sprite_accessory/hair_gradient))
+ var/datum/sprite_accessory/hair_gradient/H = new path()
+ GLOB.hair_gradients_list[H.name] = H
+
init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
//creates every subtype of prototype (excluding prototype) and adds it to list L.
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 48ab3a888..7e272b884 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -6,6 +6,7 @@ GLOBAL_LIST_EMPTY(hair_styles_female_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_list) //stores /datum/sprite_accessory/facial_hair indexed by name
GLOBAL_LIST_EMPTY(facial_hair_styles_male_list) //stores only hair names
GLOBAL_LIST_EMPTY(facial_hair_styles_female_list) //stores only hair names
+GLOBAL_LIST_EMPTY(hair_gradients_list) //stores /datum/sprite_accessory/hair_gradient indexed by name
//Underwear
GLOBAL_LIST_EMPTY(underwear_list) //stores /datum/sprite_accessory/underwear/bottom indexed by name
GLOBAL_LIST_EMPTY(underwear_m) //stores only underwear name
diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm
index 18b2b4b6f..47c5ba9aa 100644
--- a/code/datums/traits/neutral.dm
+++ b/code/datums/traits/neutral.dm
@@ -152,3 +152,20 @@
value = 0
category = CATEGORY_SEXUAL //Any better place to put it? Doesn't really affect gameplay
medical_record_text = "Patient cares little with or dislikes being touched."
+
+/datum/quirk/colorist
+ name = "Colorist"
+ desc = "You like carrying around a hair dye spray to quickly apply color patterns to your hair."
+ value = 0
+ medical_record_text = "Patient enjoys dyeing their hair with pretty colors."
+
+/datum/quirk/colorist/on_spawn()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/dyespray/spraycan = new(get_turf(quirk_holder))
+ H.equip_to_slot(spraycan, SLOT_IN_BACKPACK)
+ H.regenerate_icons()
+
+/datum/quirk/colorist/post_add()
+ var/mob/living/carbon/human/H = quirk_holder
+ SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
+ to_chat(quirk_holder, "You brought some extra dye with you! It's in your bag if you forgot.")
\ No newline at end of file
diff --git a/code/game/objects/items/dyekit.dm b/code/game/objects/items/dyekit.dm
new file mode 100644
index 000000000..8d4d90e96
--- /dev/null
+++ b/code/game/objects/items/dyekit.dm
@@ -0,0 +1,40 @@
+/obj/item/dyespray
+ name = "hair dye spray"
+ desc = "A spray to dye your hair any gradients you'd like."
+ icon = 'icons/obj/dyespray.dmi'
+ icon_state = "dyespray"
+
+/obj/item/dyespray/attack_self(mob/user)
+ dye(user)
+
+/obj/item/dyespray/pre_attack(atom/target, mob/living/user, params)
+ dye(target)
+ return ..()
+
+/**
+ * Applies a gradient and a gradient color to a mob.
+ *
+ * Arguments:
+ * * target - The mob who we will apply the gradient and gradient color to.
+ */
+
+/obj/item/dyespray/proc/dye(mob/target)
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/human_target = target
+
+ var/new_grad_style = input(usr, "Choose a color pattern:", "Character Preference") as null|anything in GLOB.hair_gradients_list
+ if(!new_grad_style)
+ return
+
+ var/new_grad_color = input(usr, "Choose a secondary hair color:", "Character Preference","#"+human_target.grad_color) as color|null
+ if(!new_grad_color)
+ return
+
+ human_target.grad_style = new_grad_style
+ human_target.grad_color = sanitize_hexcolor(new_grad_color)
+ to_chat(human_target, "You start applying the hair dye...")
+ if(!do_after(usr, 30, target = human_target))
+ return
+ playsound(src, 'sound/effects/spray.ogg', 5, TRUE, 5)
+ human_target.update_hair()
\ No newline at end of file
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index ac70ef39f..888bd00e3 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -96,6 +96,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/eye_color = "000" //Eye color
var/wing_color = "fff" //Wing color
+ var/grad_style //Hair gradient style
+ var/grad_color = "FFFFFF" //Hair gradient color
+
//HS13
var/body_size = 100 //Body Size in percent
var/can_get_preg = 0 //if they can get preggers
@@ -506,6 +509,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "< >
"
dat += " Change
"
+
+ dat += "
Hair Gradient
"
+
+ dat += "[grad_style]"
+ dat += "< >
"
+ dat += " Change
"
+
+
dat += ""
//Mutant stuff
var/mutant_category = 0
@@ -1846,6 +1857,24 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("previous_facehair_style")
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_list)
+ if("grad_color")
+ var/new_grad_color = input(user, "Choose your character's gradient colour:", "Character Preference","#"+grad_color) as color|null
+ if(new_grad_color)
+ grad_color = sanitize_hexcolor(new_grad_color, 6)
+
+ if("grad_style")
+ var/new_grad_style
+ new_grad_style = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list
+ if(new_grad_style)
+ grad_style = new_grad_style
+
+ if("next_grad_style")
+ grad_style = next_list_item(grad_style, GLOB.hair_gradients_list)
+
+ if("previous_grad_style")
+ grad_style = previous_list_item(grad_style, GLOB.hair_gradients_list)
+
+
if("cycle_bg")
bgstate = next_list_item(bgstate, bgstate_options)
@@ -2801,6 +2830,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.skin_tone = skin_tone
character.hair_style = hair_style
character.facial_hair_style = facial_hair_style
+
+ character.grad_style = grad_style
+ character.grad_color = grad_color
character.underwear = underwear
character.saved_underwear = underwear
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 174cc9750..60bbc7c1b 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -327,6 +327,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["skin_tone"] >> skin_tone
S["hair_style_name"] >> hair_style
S["facial_style_name"] >> facial_hair_style
+ S["grad_style"] >> grad_style
+ S["grad_color"] >> grad_color
S["underwear"] >> underwear
S["undie_color"] >> undie_color
S["undershirt"] >> undershirt
@@ -517,6 +519,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
hair_color = sanitize_hexcolor(hair_color, 3, 0)
facial_hair_color = sanitize_hexcolor(facial_hair_color, 3, 0)
+ grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list)
+ grad_color = sanitize_hexcolor(grad_color, 6, FALSE)
eye_color = sanitize_hexcolor(eye_color, 3, 0)
skin_tone = sanitize_inlist(skin_tone, GLOB.skin_tones)
wing_color = sanitize_hexcolor(wing_color, 3, FALSE, "#FFFFFF")
@@ -593,6 +597,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["skin_tone"] , skin_tone)
WRITE_FILE(S["hair_style_name"] , hair_style)
WRITE_FILE(S["facial_style_name"] , facial_hair_style)
+ WRITE_FILE(S["grad_style"] , grad_style)
+ WRITE_FILE(S["grad_color"] , grad_color)
WRITE_FILE(S["underwear"] , underwear)
WRITE_FILE(S["body_size"] , body_size)
WRITE_FILE(S["undie_color"] , undie_color)
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
index 769ff0d69..63b845ed8 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm
@@ -484,3 +484,58 @@
/datum/sprite_accessory/hair/curtains
name = "(Hyper) Curtains"
icon_state = "hair_curtains"
+
+/datum/sprite_accessory/hair_gradient
+ icon = 'icons/mob/hair_gradients.dmi'
+
+/datum/sprite_accessory/hair_gradient/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/hair_gradient/fadeup
+ name = "Fade Up"
+ icon_state = "fadeup"
+
+/datum/sprite_accessory/hair_gradient/fadedown
+ name = "Fade Down"
+ icon_state = "fadedown"
+
+/datum/sprite_accessory/hair_gradient/vertical_split
+ name = "Vertical Split"
+ icon_state = "vsplit"
+
+/datum/sprite_accessory/hair_gradient/_split
+ name = "Horizontal Split"
+ icon_state = "bottomflat"
+
+/datum/sprite_accessory/hair_gradient/reflected
+ name = "Reflected"
+ icon_state = "reflected_high"
+
+/datum/sprite_accessory/hair_gradient/reflected_inverse
+ name = "Reflected Inverse"
+ icon_state = "reflected_inverse_high"
+
+/datum/sprite_accessory/hair_gradient/wavy
+ name = "Wavy"
+ icon_state = "wavy"
+
+/datum/sprite_accessory/hair_gradient/long_fade_up
+ name = "Long Fade Up"
+ icon_state = "long_fade_up"
+
+/datum/sprite_accessory/hair_gradient/long_fade_down
+ name = "Long Fade Down"
+ icon_state = "long_fade_down"
+
+/datum/sprite_accessory/hair_gradient/short_fade_up
+ name = "Short Fade Up"
+ icon_state = "short_fade_up"
+
+/datum/sprite_accessory/hair_gradient/short_fade_down
+ name = "Short Fade Down"
+ icon_state = "short_fade_down"
+
+/datum/sprite_accessory/hair_gradient/wavy_spike
+ name = "Spiked Wavy"
+ icon_state = "wavy_spiked"
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 161725d67..486e6583c 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -10,6 +10,13 @@
var/hair_color = "000"
var/hair_style = "Bald"
+
+ ///Colour used for the hair gradient.
+ var/grad_color = "000"
+ ///Style used for the hair gradient.
+ var/grad_style
+
+
//Facial hair colour and style
var/facial_hair_color = "000"
var/facial_hair_style = "Shaved"
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 7bef0efac..2fd92dcba 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -16,6 +16,11 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/hair_alpha = 255 // the alpha used by the hair. 255 is completely solid, 0 is transparent.
var/wing_color
+ ///The gradient style used for the mob's hair.
+ var/grad_style
+ ///The gradient color used to color the gradient.
+ var/grad_color
+
var/use_skintones = 0 // does it use skintones or not? (spoiler alert this is only used by humans)
var/exotic_blood = "" // If your race wants to bleed something other than bog standard blood, change this to reagent id.
var/exotic_bloodtype = "" //If your race uses a non standard bloodtype (A+, O-, AB-, etc)
@@ -426,6 +431,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(!hair_hidden || dynamic_hair_suffix)
var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
+ var/mutable_appearance/gradient_overlay = mutable_appearance(layer = -HAIR_LAYER)
if(!hair_hidden && !H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
if(!(NOBLOOD in species_traits))
hair_overlay.icon = 'icons/mob/human_face.dmi'
@@ -461,6 +467,20 @@ GLOBAL_LIST_EMPTY(roundstart_races)
hair_overlay.color = "#" + hair_color
else
hair_overlay.color = "#" + H.hair_color
+
+
+ //Gradients
+ grad_style = H.grad_style
+ grad_color = H.grad_color
+ if(grad_style)
+ var/datum/sprite_accessory/gradient = GLOB.hair_gradients_list[grad_style]
+ var/icon/temp = icon(gradient.icon, gradient.icon_state)
+ var/icon/temp_hair = icon(hair_file, hair_state)
+ temp.Blend(temp_hair, ICON_ADD)
+ gradient_overlay.icon = temp
+ gradient_overlay.color = "#" + grad_color
+
+
else
hair_overlay.color = forced_colour
hair_overlay.alpha = hair_alpha
@@ -469,6 +489,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
hair_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FACE][2]
if(hair_overlay.icon)
standing += hair_overlay
+ standing += gradient_overlay
if(standing.len)
H.overlays_standing[HAIR_LAYER] = standing
diff --git a/code/modules/vending/games.dm b/code/modules/vending/games.dm
index 8e9851398..699c9845b 100644
--- a/code/modules/vending/games.dm
+++ b/code/modules/vending/games.dm
@@ -7,6 +7,7 @@
/obj/item/storage/pill_bottle/dice = 10,
/obj/item/toy/cards/deck/cas = 3,
/obj/item/toy/cards/deck/cas/black = 3,
+ /obj/item/dyespray=3,
/obj/item/toy/cards/deck/unum = 3)
contraband = list(/obj/item/dice/fudge = 9)
refill_canister = /obj/item/vending_refill/games
diff --git a/icons/mob/hair_gradients.dmi b/icons/mob/hair_gradients.dmi
new file mode 100644
index 000000000..ceb3b52ea
Binary files /dev/null and b/icons/mob/hair_gradients.dmi differ
diff --git a/icons/obj/dyespray.dmi b/icons/obj/dyespray.dmi
new file mode 100644
index 000000000..eb0560367
Binary files /dev/null and b/icons/obj/dyespray.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 2b2d84149..9126df2e6 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -868,6 +868,7 @@
#include "code\game\objects\items\dice.dm"
#include "code\game\objects\items\dna_injector.dm"
#include "code\game\objects\items\documents.dm"
+#include "code\game\objects\items\dyekit.dm"
#include "code\game\objects\items\eightball.dm"
#include "code\game\objects\items\extinguisher.dm"
#include "code\game\objects\items\flamethrower.dm"