diff --git a/code/datums/colormate.dm b/code/datums/colormate.dm
index c82f09a11c..cfa5a118b2 100644
--- a/code/datums/colormate.dm
+++ b/code/datums/colormate.dm
@@ -83,7 +83,7 @@
active_mode = text2num(params["mode"])
return TRUE
if("choose_color")
- var/chosen_color = input(inserted, "Choose a color: ", "ColorMate colour picking", activecolor) as color|null
+ var/chosen_color = tgui_color_picker(inserted, "Choose a color: ", "ColorMate colour picking", activecolor)
if(chosen_color)
activecolor = chosen_color
return TRUE
diff --git a/code/game/machinery/painter_vr.dm b/code/game/machinery/painter_vr.dm
index aaf4d632cf..9c8377e248 100644
--- a/code/game/machinery/painter_vr.dm
+++ b/code/game/machinery/painter_vr.dm
@@ -168,7 +168,7 @@
active_mode = text2num(params["mode"])
return TRUE
if("choose_color")
- var/chosen_color = input(ui.user, "Choose a color: ", "ColorMate colour picking", activecolor) as color|null
+ var/chosen_color = tgui_color_picker(ui.user, "Choose a color: ", "ColorMate colour picking", activecolor)
if(chosen_color)
activecolor = chosen_color
return TRUE
diff --git a/code/game/machinery/petrification.dm b/code/game/machinery/petrification.dm
index 5b061e8006..e7d8a9d51e 100644
--- a/code/game/machinery/petrification.dm
+++ b/code/game/machinery/petrification.dm
@@ -171,7 +171,7 @@
return
switch(option)
if("tint")
- var/new_color = input(user, "Choose the color for the [identifier] to be:", "Statue color", tint) as color|null
+ var/new_color = tgui_color_picker(user, "Choose the color for the [identifier] to be:", "Statue color", tint)
if (new_color)
tint = new_color
if("material","identifier","adjective")
diff --git a/code/game/mecha/combat/fighter.dm b/code/game/mecha/combat/fighter.dm
index bdb1c7c96a..8fb6196656 100644
--- a/code/game/mecha/combat/fighter.dm
+++ b/code/game/mecha/combat/fighter.dm
@@ -294,7 +294,7 @@
if(istype(W,/obj/item/multitool) && state == 1)
var/new_paint_location = tgui_input_list(user, "Please select a target zone.", "Paint Zone", list("Fore Stripe", "Aft Stripe", "CANCEL"))
if(new_paint_location && new_paint_location != "CANCEL")
- var/new_paint_color = input(user, "Please select a paint color.", "Paint Color", null) as color|null
+ var/new_paint_color = tgui_color_picker(user, "Please select a paint color.", "Paint Color", null)
if(new_paint_color)
switch(new_paint_location)
if("Fore Stripe")
diff --git a/code/game/mecha/space/shuttle.dm b/code/game/mecha/space/shuttle.dm
index b7dd4f35f3..a337d337dd 100644
--- a/code/game/mecha/space/shuttle.dm
+++ b/code/game/mecha/space/shuttle.dm
@@ -70,7 +70,7 @@
if(istype(W,/obj/item/multitool) && state == 1)
var/new_paint_location = tgui_input_list(user, "Please select a target zone.", "Paint Zone", list("Central", "Engine", "Base", "Front", "CANCEL"))
if(new_paint_location && new_paint_location != "CANCEL")
- var/new_paint_color = input(user, "Please select a paint color.", "Paint Color", null) as color|null
+ var/new_paint_color = tgui_color_picker(user, "Please select a paint color.", "Paint Color", null)
if(new_paint_color)
switch(new_paint_location)
if("Central")
diff --git a/code/game/objects/items/devices/floor_painter.dm b/code/game/objects/items/devices/floor_painter.dm
index ae9b0a5ed5..644523f8cf 100644
--- a/code/game/objects/items/devices/floor_painter.dm
+++ b/code/game/objects/items/devices/floor_painter.dm
@@ -129,7 +129,7 @@
if(usr.incapacitated())
return
- var/new_colour = input(usr, "Choose a colour.", name, paint_colour) as color|null
+ var/new_colour = tgui_color_picker(usr, "Choose a colour.", name, paint_colour)
if(new_colour && new_colour != paint_colour)
paint_colour = new_colour
to_chat(usr, span_notice("You set \the [src] to paint with a new colour."))
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index 578279ae0a..4f1b136df4 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -318,7 +318,7 @@ GLOBAL_LIST_EMPTY(GPS_list)
if(href_list["track_color"])
var/obj/item/gps/gps = locate(href_list["track_color"])
if(istype(gps) && !QDELETED(gps))
- var/new_colour = input(usr, "Enter a new tracking color.", "GPS Waypoint Color") as color|null
+ var/new_colour = tgui_color_picker(usr, "Enter a new tracking color.", "GPS Waypoint Color")
if(new_colour && istype(gps) && !QDELETED(gps) && holder == usr && !usr.incapacitated())
to_chat(usr, span_notice("You adjust the colour \the [src] is using to highlight [gps.gps_tag]."))
LAZYSET(tracking_devices, href_list["track_color"], new_colour)
diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index 9a3329b636..266c0c5d89 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -138,7 +138,7 @@
return
*/
to_chat(user, "It has [uses] lights remaining.")
- var/new_color = input(user, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null
+ var/new_color = tgui_color_picker(user, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color)
if(new_color)
selected_color = new_color
to_chat(user, "The light color has been changed.")
diff --git a/code/game/objects/items/devices/personal_shield_generator_vr.dm b/code/game/objects/items/devices/personal_shield_generator_vr.dm
index 76cd026389..714ada7d3a 100644
--- a/code/game/objects/items/devices/personal_shield_generator_vr.dm
+++ b/code/game/objects/items/devices/personal_shield_generator_vr.dm
@@ -208,7 +208,7 @@
to_chat(user, span_notice("You remove the cell from \the [src]."))
update_icon()
else if(istype(W,/obj/item/multitool))
- var/new_color = input(usr, "Choose a color to set the shield to!", "", effect_color) as color|null
+ var/new_color = tgui_color_picker(usr, "Choose a color to set the shield to!", "", effect_color)
if(new_color)
effect_color = new_color
else
diff --git a/code/game/objects/items/toys/toys.dm b/code/game/objects/items/toys/toys.dm
index 71c96d7b1c..3b19932f3e 100644
--- a/code/game/objects/items/toys/toys.dm
+++ b/code/game/objects/items/toys/toys.dm
@@ -198,7 +198,7 @@
return
if(tgui_alert(user, "Are you sure you want to recolor your blade?", "Confirm Recolor", list("Yes", "No")) == "Yes")
- var/energy_color_input = input(user,"","Choose Energy Color",lcolor) as color|null
+ var/energy_color_input = tgui_color_picker(user,"","Choose Energy Color",lcolor)
if(energy_color_input)
lcolor = sanitize_hexcolor(energy_color_input)
update_icon()
diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm
index 62ebd2ddcb..66c9bb03d9 100644
--- a/code/game/objects/items/weapons/grenades/smokebomb.dm
+++ b/code/game/objects/items/weapons/grenades/smokebomb.dm
@@ -34,6 +34,6 @@
/obj/item/grenade/smokebomb/attackby(obj/item/I as obj, mob/user as mob)
if(istype(I,/obj/item/multitool))
- var/new_smoke_color = input(user, "Choose a color for the smoke:", "Smoke Color", smoke_color) as color|null
+ var/new_smoke_color = tgui_color_picker(user, "Choose a color for the smoke:", "Smoke Color", smoke_color)
if(new_smoke_color)
smoke_color = new_smoke_color
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index 7997c6870c..d2e501036b 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -188,10 +188,12 @@
return
if(tgui_alert(user, "Are you sure you want to recolor your blade?", "Confirm Recolor", list("Yes", "No")) == "Yes")
- var/energy_color_input = input(user,"","Choose Energy Color",lcolor) as color|null
+ var/energy_color_input = tgui_color_picker(user,"","Choose Energy Color",lcolor)
if(energy_color_input)
lcolor = sanitize_hexcolor(energy_color_input)
update_icon()
+ if(active)
+ set_light(lrange, lpower, lcolor)
/obj/item/melee/energy/examine(mob/user)
. = ..()
diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm
index 1055edef2c..a24869731c 100644
--- a/code/game/objects/items/weapons/shields.dm
+++ b/code/game/objects/items/weapons/shields.dm
@@ -209,7 +209,7 @@
to_chat(user, span_warning("You can't do that right now!"))
return
if(tgui_alert(user, "Are you sure you want to recolor your shield?", "Confirm Recolor", list("Yes", "No")) == "Yes")
- var/energy_color_input = input(user,"","Choose Energy Color",lcolor) as color|null
+ var/energy_color_input = tgui_color_picker(user,"","Choose Energy Color",lcolor)
if(energy_color_input)
lcolor = sanitize_hexcolor(energy_color_input)
update_icon()
diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm
index f0ddba449d..8fe4e29d6f 100644
--- a/code/game/objects/items/weapons/storage/wallets.dm
+++ b/code/game/objects/items/weapons/storage/wallets.dm
@@ -120,7 +120,7 @@
if(usr.stat || usr.restrained() || usr.incapacitated())
return
- var/new_color = input(usr, "Pick a new color", "Wallet Color", color) as color|null
+ var/new_color = tgui_color_picker(usr, "Pick a new color", "Wallet Color", color)
if(new_color && (new_color != color))
color = new_color
diff --git a/code/modules/client/preference_setup/general/02_language.dm b/code/modules/client/preference_setup/general/02_language.dm
index d796ba9ca2..2b1af1b02e 100644
--- a/code/modules/client/preference_setup/general/02_language.dm
+++ b/code/modules/client/preference_setup/general/02_language.dm
@@ -199,7 +199,7 @@
// VOREStation Add End
else if(href_list["pref_runechat_color"])
- var/new_runechat_color = input(user, "Choose your character's runechat colour (#000000 for random):", "Character Preference", pref.runechat_color) as color|null
+ var/new_runechat_color = tgui_color_picker(user, "Choose your character's runechat colour (#000000 for random):", "Character Preference", pref.runechat_color)
if(new_runechat_color && CanUseTopic(user))
pref.runechat_color = new_runechat_color
// whenever we change this, we update our mob
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index 18ad76141e..66482747c8 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -593,7 +593,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["hair_color"])
if(!has_flag(mob_species, HAS_HAIR_COLOR))
return TOPIC_NOACTION
- var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/hair_color)) as color|null
+ var/new_hair = tgui_color_picker(user, "Choose your character's hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/hair_color))
if(new_hair && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/color/human/hair_color, new_hair)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -601,7 +601,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["grad_color"])
if(!has_flag(mob_species, HAS_HAIR_COLOR))
return TOPIC_NOACTION
- var/new_grad = input(user, "Choose your character's secondary hair color:", "Character Preference", pref.read_preference(/datum/preference/color/human/grad_color)) as color|null
+ var/new_grad = tgui_color_picker(user, "Choose your character's secondary hair color:", "Character Preference", pref.read_preference(/datum/preference/color/human/grad_color))
if(new_grad && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/color/human/grad_color, new_grad)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -647,7 +647,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["facial_color"])
if(!has_flag(mob_species, HAS_HAIR_COLOR))
return TOPIC_NOACTION
- var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/facial_color)) as color|null
+ var/new_facial = tgui_color_picker(user, "Choose your character's facial-hair colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/facial_color))
if(new_facial && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/color/human/facial_color, new_facial)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -660,7 +660,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["eye_color"])
if(!has_flag(mob_species, HAS_EYE_COLOR))
return TOPIC_NOACTION
- var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/eyes_color)) as color|null
+ var/new_eyes = tgui_color_picker(user, "Choose your character's eye colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/eyes_color))
if(new_eyes && has_flag(mob_species, HAS_EYE_COLOR) && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/color/human/eyes_color, new_eyes)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -676,7 +676,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["skin_color"])
if(!has_flag(mob_species, HAS_SKIN_COLOR))
return TOPIC_NOACTION
- var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/skin_color)) as color|null
+ var/new_skin = tgui_color_picker(user, "Choose your character's skin colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/skin_color))
if(new_skin && has_flag(mob_species, HAS_SKIN_COLOR) && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/color/human/skin_color, new_skin)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -769,7 +769,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
if (isnull(pref.body_markings[M]["color"]))
if (tgui_alert(user, "You currently have customized marking colors. This will reset each bodypart's color. Are you sure you want to continue?","Reset Bodypart Colors",list("Yes","No")) != "Yes")
return TOPIC_NOACTION
- var/mark_color = input(user, "Choose the [M] color: ", "Character Preference", pref.body_markings[M]["color"]) as color|null
+ var/mark_color = tgui_color_picker(user, "Choose the [M] color: ", "Character Preference", pref.body_markings[M]["color"])
if(mark_color && CanUseTopic(user))
pref.body_markings[M] = pref.mass_edit_marking_list(M,FALSE,TRUE,pref.body_markings[M],color="[mark_color]")
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -794,7 +794,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
if (pref.body_markings.Find(marking) == 0)
winshow(user, "prefs_markings_subwindow", FALSE)
return TOPIC_NOACTION
- var/mark_color = input(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking]["color"]) as color|null
+ var/mark_color = tgui_color_picker(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking]["color"])
if(mark_color && CanUseTopic(user))
pref.body_markings[marking] = pref.mass_edit_marking_list(marking,FALSE,TRUE,pref.body_markings[marking],color="[mark_color]")
markings_subwindow(user, marking)
@@ -807,7 +807,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
return TOPIC_NOACTION
var/zone = href_list["zone"]
pref.body_markings[marking]["color"] = null //turn off the color button outside the submenu
- var/mark_color = input(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking][zone]["color"]) as color|null
+ var/mark_color = tgui_color_picker(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking][zone]["color"])
if(mark_color && CanUseTopic(user))
pref.body_markings[marking][zone]["color"] = "[mark_color]"
markings_subwindow(user, marking)
@@ -1036,7 +1036,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["synth2_color"])
- var/new_color = input(user, "Choose your character's synth colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/synth_color)) as color|null
+ var/new_color = tgui_color_picker(user, "Choose your character's synth colour: ", "Character Preference", pref.read_preference(/datum/preference/color/human/synth_color))
if(new_color && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/color/human/synth_color, new_color)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -1057,22 +1057,22 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["ear_color"])
- var/new_earc = input(user, "Choose your character's ear colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/ears_color1)) as color|null
+ var/new_earc = tgui_color_picker(user, "Choose your character's ear colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/ears_color1))
if(new_earc)
pref.update_preference_by_type(/datum/preference/color/human/ears_color1, new_earc)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["ear_color2"])
- var/new_earc2 = input(user, "Choose your character's ear colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/ears_color2)) as color|null
+ var/new_earc2 = tgui_color_picker(user, "Choose your character's ear colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/ears_color2))
if(new_earc2)
pref.update_preference_by_type(/datum/preference/color/human/ears_color2, new_earc2)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["ear_color3"])
- var/new_earc3 = input(user, "Choose your character's tertiary ear colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/ears_color3)) as color|null
+ var/new_earc3 = tgui_color_picker(user, "Choose your character's tertiary ear colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/ears_color3))
if(new_earc3)
pref.update_preference_by_type(/datum/preference/color/human/ears_color3, new_earc3)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -1089,12 +1089,12 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
if(channel > GLOB.fancy_sprite_accessory_color_channel_names.len)
return TOPIC_NOACTION
// this would say 'secondary ears' but you'd get 'choose your character's primary secondary ear colour' which sounds silly
- var/new_color = input(
+ var/new_color = tgui_color_picker(
user,
"Choose your character's [lowertext(GLOB.fancy_sprite_accessory_color_channel_names[channel])] ear colour:",
"Secondary Ear Coloration",
LAZYACCESS(pref.ear_secondary_colors, channel) || "#ffffff",
- ) as color | null
+ )
if(!new_color)
return TOPIC_NOACTION
// ensures color channel list is at least that long
@@ -1110,22 +1110,22 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["tail_color"])
- var/new_tailc = input(user, "Choose your character's tail/taur colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/tail_color1)) as color|null
+ var/new_tailc = tgui_color_picker(user, "Choose your character's tail/taur colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/tail_color1))
if(new_tailc)
pref.update_preference_by_type(/datum/preference/color/human/tail_color1, new_tailc)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["tail_color2"])
- var/new_tailc2 = input(user, "Choose your character's secondary tail/taur colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/tail_color2)) as color|null
+ var/new_tailc2 = tgui_color_picker(user, "Choose your character's secondary tail/taur colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/tail_color2))
if(new_tailc2)
pref.update_preference_by_type(/datum/preference/color/human/tail_color2, new_tailc2)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["tail_color3"])
- var/new_tailc3 = input(user, "Choose your character's tertiary tail/taur colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/tail_color3)) as color|null
+ var/new_tailc3 = tgui_color_picker(user, "Choose your character's tertiary tail/taur colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/tail_color3))
if(new_tailc3)
pref.update_preference_by_type(/datum/preference/color/human/tail_color3, new_tailc3)
return TOPIC_REFRESH_UPDATE_PREVIEW
@@ -1138,22 +1138,22 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["wing_color"])
- var/new_wingc = input(user, "Choose your character's wing colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/wing_color1)) as color|null
+ var/new_wingc = tgui_color_picker(user, "Choose your character's wing colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/wing_color1))
if(new_wingc)
pref.update_preference_by_type(/datum/preference/color/human/wing_color1, new_wingc)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["wing_color2"])
- var/new_wingc = input(user, "Choose your character's secondary wing colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/wing_color2)) as color|null
+ var/new_wingc = tgui_color_picker(user, "Choose your character's secondary wing colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/wing_color2))
if(new_wingc)
pref.update_preference_by_type(/datum/preference/color/human/wing_color2, new_wingc)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["wing_color3"])
- var/new_wingc = input(user, "Choose your character's tertiary wing colour:", "Character Preference",
- pref.read_preference(/datum/preference/color/human/wing_color3)) as color|null
+ var/new_wingc = tgui_color_picker(user, "Choose your character's tertiary wing colour:", "Character Preference",
+ pref.read_preference(/datum/preference/color/human/wing_color3))
if(new_wingc)
pref.update_preference_by_type(/datum/preference/color/human/wing_color3, new_wingc)
return TOPIC_REFRESH_UPDATE_PREVIEW
diff --git a/code/modules/client/preference_setup/loadout/gear_tweaks.dm b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
index 5948152129..38c0397f41 100644
--- a/code/modules/client/preference_setup/loadout/gear_tweaks.dm
+++ b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
@@ -35,7 +35,7 @@
/datum/gear_tweak/color/get_metadata(var/user, var/metadata, var/title = "Character Preference")
if(valid_colors)
return tgui_input_list(user, "Choose a color.", title, valid_colors, metadata)
- return input(user, "Choose a color.", title, metadata) as color|null
+ return tgui_color_picker(user, "Choose a color.", title, metadata)
/datum/gear_tweak/color/tweak_item(var/obj/item/I, var/metadata)
if(valid_colors && !(metadata in valid_colors))
@@ -614,4 +614,4 @@ var/datum/gear_tweak/custom_desc/gear_tweak_free_desc = new()
else
C.initialize_tag(metadata)
-#undef LOADOUT_BAN_STRING
\ No newline at end of file
+#undef LOADOUT_BAN_STRING
diff --git a/code/modules/client/preference_setup/vore/07_traits.dm b/code/modules/client/preference_setup/vore/07_traits.dm
index 92909532de..6b803a896c 100644
--- a/code/modules/client/preference_setup/vore/07_traits.dm
+++ b/code/modules/client/preference_setup/vore/07_traits.dm
@@ -101,7 +101,7 @@ var/global/list/valid_bloodreagents = list("default",REAGENT_ID_IRON,REAGENT_ID_
if (1) //TRAIT_PREF_TYPE_BOOLEAN
trait_prefs[preference] = !trait_prefs[preference]
if (2) //TRAIT_PREF_TYPE_COLOR
- var/new_color = input(user, "Choose the color for this trait preference:", "Trait Preference", trait_prefs[preference]) as color|null
+ var/new_color = tgui_color_picker(user, "Choose the color for this trait preference:", "Trait Preference", trait_prefs[preference])
if (new_color)
trait_prefs[preference] = new_color
if (3) //TRAIT_PREF_TYPE_STRING
diff --git a/code/modules/client/preferences/types/game/ui.dm b/code/modules/client/preferences/types/game/ui.dm
index 1d770dc3e7..3f110e0680 100644
--- a/code/modules/client/preferences/types/game/ui.dm
+++ b/code/modules/client/preferences/types/game/ui.dm
@@ -89,3 +89,12 @@
/datum/preference/numeric/tgui_say_height/apply_to_client(client/client, value)
client.tgui_say?.load()
+
+/datum/preference/text/preset_colors
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_identifier = PREFERENCE_PLAYER
+ savefile_key = "preset_colors"
+ maximum_value_length = 160
+
+/datum/preference/text/preset_colors/apply_to_client(client/client, value)
+ return
diff --git a/code/modules/client/preferences_tgui.dm b/code/modules/client/preferences_tgui.dm
index 068580432c..62c5cb7b03 100644
--- a/code/modules/client/preferences_tgui.dm
+++ b/code/modules/client/preferences_tgui.dm
@@ -92,12 +92,12 @@
var/default_value = read_preference(requested_preference.type)
// Yielding
- var/new_color = input(
+ var/new_color = tgui_color_picker(
usr,
"Select new color",
null,
default_value || COLOR_WHITE,
- ) as color | null
+ )
if(!new_color)
return FALSE
diff --git a/code/modules/client/ui_style.dm b/code/modules/client/ui_style.dm
index 479d59d181..3baa865c5e 100644
--- a/code/modules/client/ui_style.dm
+++ b/code/modules/client/ui_style.dm
@@ -51,7 +51,7 @@ var/global/list/all_tooltip_styles = list(
var/UI_style_alpha_new = tgui_input_number(usr, "Select a new alpha (transparency) parameter for your UI, between 50 and 255", null, null, 255, 50)
if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) return
- var/UI_style_color_new = input(usr, "Choose your UI color. Dark colors are not recommended!") as color|null
+ var/UI_style_color_new = tgui_color_picker(usr, "Choose your UI color. Dark colors are not recommended!")
if(!UI_style_color_new) return
//update UI
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 410b3606a7..909fd1a452 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -177,7 +177,7 @@
if(usr.stat || usr.restrained() || usr.incapacitated())
return
- var/new_color = input(usr, "Pick a new color", "Color", color) as color|null
+ var/new_color = tgui_color_picker(usr, "Pick a new color", "Color", color)
if(new_color && (new_color != color))
color = new_color
diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm
index 7d0a079979..d385387396 100644
--- a/code/modules/integrated_electronics/core/detailer.dm
+++ b/code/modules/integrated_electronics/core/detailer.dm
@@ -69,8 +69,8 @@
// Just uncomment this and get rid of all of the TGUI bullshit lol
// if(!in_range(user, src))
// return
- // var/new_color = input(user, "Pick a color", "Color Selection", detail_color) as color|null
+ // var/new_color = tgui_color_picker(user, "Pick a color", "Color Selection", detail_color)
// if(!new_color)
// return
// detail_color = new_color
- // update_icon()
\ No newline at end of file
+ // update_icon()
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
index b7c73a5482..019db4a31a 100644
--- a/code/modules/integrated_electronics/subtypes/input.dm
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -92,7 +92,7 @@
power_draw_per_use = 4
/obj/item/integrated_circuit/input/colorpad/ask_for_input(mob/user)
- var/new_color = input(user, "Enter a color, please.", "Color pad", get_pin_data(IC_OUTPUT, 1)) as color|null
+ var/new_color = tgui_color_picker(user, "Enter a color, please.", "Color pad", get_pin_data(IC_OUTPUT, 1))
if(new_color && CanInteract(user, GLOB.tgui_physical_state))
set_pin_data(IC_OUTPUT, 1, new_color)
push_data()
diff --git a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm
index 35548d6288..def65e1ffd 100644
--- a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm
+++ b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm
@@ -83,7 +83,7 @@
for(var/channel in 1 to ear_secondary_style.get_color_channel_count())
var/channel_name = GLOB.fancy_sprite_accessory_color_channel_names[channel]
var/default = LAZYACCESS(ear_secondary_colors, channel) || "#ffffff"
- var/new_color = input(usr, "Pick [channel_name]", "Ear Color ([channel_name])", default) as color | null
+ var/new_color = tgui_color_picker(usr, "Pick [channel_name]", "Ear Color ([channel_name])", default)
new_colors += new_color || default
update_hair()
diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
index 45ca875563..485b16b95c 100644
--- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
+++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
@@ -1274,7 +1274,7 @@
return
if(choice == "Color") //Easy way to set color so we don't bloat up the menu with even more buttons.
- var/new_color = input(src, "Choose a color to set your appendage to!", "", appendage_color) as color|null
+ var/new_color = tgui_color_picker(src, "Choose a color to set your appendage to!", "", appendage_color)
if(new_color)
appendage_color = new_color
if(choice == "Functionality") //Easy way to set color so we don't bloat up the menu with even more buttons.
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index ba600401aa..491876c859 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -607,7 +607,7 @@ var/list/ai_verbs_default = list(
switch(choice)
if("Color")
- input = input("Choose a color:", "Hologram Color", holo_color) as color|null
+ input = tgui_color_picker("Choose a color:", "Hologram Color", holo_color)
if(input)
holo_color = input
diff --git a/code/modules/mob/living/silicon/pai/pai_vr.dm b/code/modules/mob/living/silicon/pai/pai_vr.dm
index 67c4716218..69f5839a15 100644
--- a/code/modules/mob/living/silicon/pai/pai_vr.dm
+++ b/code/modules/mob/living/silicon/pai/pai_vr.dm
@@ -236,7 +236,7 @@
else
to_chat(src, span_warning("Your selected chassis eye color can not be modified. The color you pick will only apply to supporting chassis and your card screen."))
- var/new_eye_color = input(src, "Choose your character's eye color:", "Eye Color") as color|null
+ var/new_eye_color = tgui_color_picker(src, "Choose your character's eye color:", "Eye Color")
if(new_eye_color)
eye_color = new_eye_color
update_icon()
diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
index 091e655324..1cafcf860f 100644
--- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
+++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
@@ -254,7 +254,7 @@
if(!choice)
return
if(choice == "Color")
- var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null
+ var/new_color = tgui_color_picker(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color)
if(new_color)
selected_color = new_color
to_chat(user, span_filter_notice("The light color has been changed."))
diff --git a/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm
index ebe112a4e8..fca4a2d3f1 100644
--- a/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm
+++ b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm
@@ -28,7 +28,7 @@
set category = "Abilities.Silicon"
set src in usr
- var/new_pupil_color = input(usr, "Select a pupil colour.", "Pupil Colour Selection") as color|null
+ var/new_pupil_color = tgui_color_picker(usr, "Select a pupil colour.", "Pupil Colour Selection")
if(usr.incapacitated() || QDELETED(usr) || QDELETED(src) || loc != usr)
return
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm
index f88ff654a8..4de0146444 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm
@@ -291,7 +291,7 @@
if(picked_color)
to_chat(src, span_notice("You have already picked a color! If you picked the wrong color, ask an admin to change your picked_color variable to 0."))
return
- var/newcolor = input(usr, "Choose a color.", "", color) as color|null
+ var/newcolor = tgui_color_picker(usr, "Choose a color.", "", color)
if(newcolor)
color = newcolor
picked_color = TRUE
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm b/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm
index 4ba69741eb..97d79e4290 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm
@@ -215,7 +215,7 @@
if(picked_color)
to_chat(src, span_notice("You have already picked a color! If you picked the wrong color, ask an admin to change your picked_color variable to 0."))
return
- var/newcolor = input(usr, "Choose a color.", "", color) as color|null
+ var/newcolor = tgui_color_picker(usr, "Choose a color.", "", color)
if(newcolor)
color = newcolor
picked_color = TRUE
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm
index ec0508f787..3db9acc2cb 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm
@@ -249,7 +249,7 @@
set name = "Pick Color"
set category = "Abilities.Settings"
set desc = "You can set your color!"
- var/newcolor = input(usr, "Choose a color.", "", color) as color|null
+ var/newcolor = tgui_color_picker(usr, "Choose a color.", "", color)
if(newcolor)
color = newcolor
chosen_color = newcolor
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/sect_drone.dm b/code/modules/mob/living/simple_mob/subtypes/vore/sect_drone.dm
index 27e6d0e92a..e5b0b92f3b 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/sect_drone.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/sect_drone.dm
@@ -95,7 +95,7 @@
set desc = "Customize your eyes and abdomen glow color."
set category = "Abilities.Sect Drone"
- var/new_color = input(src, "Please select color.", "Glow Color", custom_eye_color) as color|null
+ var/new_color = tgui_color_picker(src, "Please select color.", "Glow Color", custom_eye_color)
if(new_color)
custom_eye_color = new_color
remove_eyes()
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/sect_queen.dm b/code/modules/mob/living/simple_mob/subtypes/vore/sect_queen.dm
index 76ab3b915f..2279b31f66 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/sect_queen.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/sect_queen.dm
@@ -96,7 +96,7 @@
set desc = "Customize your eyes and abdomen glow color."
set category = "Abilities.Sect Queen"
- var/new_color = input(src, "Please select color.", "Glow Color", custom_eye_color) as color|null
+ var/new_color = tgui_color_picker(src, "Please select color.", "Glow Color", custom_eye_color)
if(new_color)
custom_eye_color = new_color
remove_eyes()
diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm
index 0473457bdc..e030e0d7bb 100644
--- a/code/modules/overmap/ships/computers/helm.dm
+++ b/code/modules/overmap/ships/computers/helm.dm
@@ -113,8 +113,8 @@ GLOBAL_LIST_EMPTY(all_waypoints)
var/turf/T = get_turf(linked)
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
-
- data["mapRef"] = linked?.map_name
+ if(linked)
+ data["mapRef"] = linked.map_name
data["sector"] = current_sector ? current_sector.name : "Deep Space"
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
data["landed"] = linked.get_landed_info()
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index fc66eb453c..8b4b1266b7 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -1114,7 +1114,7 @@ var/global/list/light_type_cache = list()
brightness_power = new_power
if("Normal Color")
- var/new_color = input(usr, "Choose a color to set the light to!", "", brightness_color) as color|null
+ var/new_color = tgui_color_picker(usr, "Choose a color to set the light to!", "", brightness_color)
if(new_color)
brightness_color = new_color
@@ -1129,7 +1129,7 @@ var/global/list/light_type_cache = list()
nightshift_power = new_power
if("Nightshift Color")
- var/new_color = input(usr, "Choose a color to set the light to!", "", nightshift_color) as color|null
+ var/new_color = tgui_color_picker(usr, "Choose a color to set the light to!", "", nightshift_color)
if(new_color)
nightshift_color = new_color
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm
index e963004064..52203deb1c 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm
@@ -734,7 +734,7 @@
desc = "Causes kinetic accelerator bolts to have an adjustable-colored tracer trail and explosion. Use in-hand to change color."
/obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user)
- bolt_color = input(user,"","Choose Color",bolt_color) as color|null
+ bolt_color = tgui_color_picker(user,"","Choose Color",bolt_color)
#undef KA_ENVIRO_TYPE_COLD
#undef KA_ENVIRO_TYPE_HOT
diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm
index 3ffd787f42..fad47cc4a8 100644
--- a/code/modules/tgui/modules/appearance_changer.dm
+++ b/code/modules/tgui/modules/appearance_changer.dm
@@ -116,7 +116,7 @@
return owner.change_skin_tone(new_s_tone)
if("skin_color")
if(can_change_skin_color(owner))
- var/new_skin = input(ui.user, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as color|null
+ var/new_skin = tgui_color_picker(ui.user, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin))
if(new_skin && can_still_topic(owner, state))
var/r_skin = hex2num(copytext(new_skin, 2, 4))
var/g_skin = hex2num(copytext(new_skin, 4, 6))
@@ -133,7 +133,7 @@
return 1
if("hair_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair))
if(new_hair && can_still_topic(owner, state))
var/r_hair = hex2num(copytext(new_hair, 2, 4))
var/g_hair = hex2num(copytext(new_hair, 4, 6))
@@ -150,7 +150,7 @@
return 1
if("facial_hair_color")
if(can_change(owner, APPEARANCE_FACIAL_HAIR_COLOR))
- var/new_facial = input(ui.user, "Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial)) as color|null
+ var/new_facial = tgui_color_picker(ui.user, "Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial))
if(new_facial && can_still_topic(owner, state))
var/r_facial = hex2num(copytext(new_facial, 2, 4))
var/g_facial = hex2num(copytext(new_facial, 4, 6))
@@ -161,7 +161,7 @@
return 1
if("eye_color")
if(can_change(owner, APPEARANCE_EYE_COLOR))
- var/new_eyes = input(ui.user, "Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) as color|null
+ var/new_eyes = tgui_color_picker(ui.user, "Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes))
if(new_eyes && can_still_topic(owner, state))
var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
@@ -201,7 +201,7 @@
return TRUE
if("ears_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select ear color.", "Ear Color", rgb(owner.r_ears, owner.g_ears, owner.b_ears)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select ear color.", "Ear Color", rgb(owner.r_ears, owner.g_ears, owner.b_ears))
if(new_hair && can_still_topic(owner, state))
owner.r_ears = hex2num(copytext(new_hair, 2, 4))
owner.g_ears = hex2num(copytext(new_hair, 4, 6))
@@ -212,7 +212,7 @@
return 1
if("ears2_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select secondary ear color.", "2nd Ear Color", rgb(owner.r_ears2, owner.g_ears2, owner.b_ears2)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select secondary ear color.", "2nd Ear Color", rgb(owner.r_ears2, owner.g_ears2, owner.b_ears2))
if(new_hair && can_still_topic(owner, state))
owner.r_ears2 = hex2num(copytext(new_hair, 2, 4))
owner.g_ears2 = hex2num(copytext(new_hair, 4, 6))
@@ -227,7 +227,7 @@
if(channel > length(owner.ear_secondary_colors))
return TRUE
var/existing = LAZYACCESS(owner.ear_secondary_colors, channel) || "#ffffff"
- var/new_color = input(ui.user, "Please select ear color.", "2nd Ear Color", existing) as color|null
+ var/new_color = tgui_color_picker(ui.user, "Please select ear color.", "2nd Ear Color", existing)
if(new_color && can_still_topic(owner, state))
owner.ear_secondary_colors[channel] = new_color
update_dna(owner)
@@ -248,7 +248,7 @@
return TRUE
if("tail_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select tail color.", "Tail Color", rgb(owner.r_tail, owner.g_tail, owner.b_tail)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select tail color.", "Tail Color", rgb(owner.r_tail, owner.g_tail, owner.b_tail))
if(new_hair && can_still_topic(owner, state))
owner.r_tail = hex2num(copytext(new_hair, 2, 4))
owner.g_tail = hex2num(copytext(new_hair, 4, 6))
@@ -259,7 +259,7 @@
return 1
if("tail2_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select secondary tail color.", "2nd Tail Color", rgb(owner.r_tail2, owner.g_tail2, owner.b_tail2)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select secondary tail color.", "2nd Tail Color", rgb(owner.r_tail2, owner.g_tail2, owner.b_tail2))
if(new_hair && can_still_topic(owner, state))
owner.r_tail2 = hex2num(copytext(new_hair, 2, 4))
owner.g_tail2 = hex2num(copytext(new_hair, 4, 6))
@@ -270,7 +270,7 @@
return 1
if("tail3_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select secondary tail color.", "3rd Tail Color", rgb(owner.r_tail3, owner.g_tail3, owner.b_tail3)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select secondary tail color.", "3rd Tail Color", rgb(owner.r_tail3, owner.g_tail3, owner.b_tail3))
if(new_hair && can_still_topic(owner, state))
owner.r_tail3 = hex2num(copytext(new_hair, 2, 4))
owner.g_tail3 = hex2num(copytext(new_hair, 4, 6))
@@ -293,7 +293,7 @@
return TRUE
if("wing_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select wing color.", "Wing Color", rgb(owner.r_wing, owner.g_wing, owner.b_wing)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select wing color.", "Wing Color", rgb(owner.r_wing, owner.g_wing, owner.b_wing))
if(new_hair && can_still_topic(owner, state))
owner.r_wing = hex2num(copytext(new_hair, 2, 4))
owner.g_wing = hex2num(copytext(new_hair, 4, 6))
@@ -304,7 +304,7 @@
return 1
if("wing2_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select secondary wing color.", "2nd Wing Color", rgb(owner.r_wing2, owner.g_wing2, owner.b_wing2)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select secondary wing color.", "2nd Wing Color", rgb(owner.r_wing2, owner.g_wing2, owner.b_wing2))
if(new_hair && can_still_topic(owner, state))
owner.r_wing2 = hex2num(copytext(new_hair, 2, 4))
owner.g_wing2 = hex2num(copytext(new_hair, 4, 6))
@@ -315,7 +315,7 @@
return 1
if("wing3_color")
if(can_change(owner, APPEARANCE_HAIR_COLOR))
- var/new_hair = input(ui.user, "Please select secondary wing color.", "3rd Wing Color", rgb(owner.r_wing3, owner.g_wing3, owner.b_wing3)) as color|null
+ var/new_hair = tgui_color_picker(ui.user, "Please select secondary wing color.", "3rd Wing Color", rgb(owner.r_wing3, owner.g_wing3, owner.b_wing3))
if(new_hair && can_still_topic(owner, state))
owner.r_wing3 = hex2num(copytext(new_hair, 2, 4))
owner.g_wing3 = hex2num(copytext(new_hair, 4, 6))
@@ -353,7 +353,7 @@
return TRUE
if (4) //color
var/current = markings[name_marking] ? markings[name_marking] : "#000000"
- var/marking_color = input(ui.user, "Please select marking color", "Marking color", current) as color|null
+ var/marking_color = tgui_color_picker(ui.user, "Please select marking color", "Marking color", current)
if(marking_color && can_still_topic(owner, state))
var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking]
if (owner.change_marking_color(mark_datum, marking_color))
diff --git a/code/modules/tgui/modules/overmap.dm b/code/modules/tgui/modules/overmap.dm
index 6b7e31a5ab..f26eef3ab5 100644
--- a/code/modules/tgui/modules/overmap.dm
+++ b/code/modules/tgui/modules/overmap.dm
@@ -33,7 +33,8 @@
/datum/tgui_module/ship/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
- data["mapRef"] = linked?.map_name
+ if(linked)
+ data["mapRef"] = linked.map_name
return data
/datum/tgui_module/ship/tgui_status(mob/user)
diff --git a/code/modules/tgui_input/color.dm b/code/modules/tgui_input/color.dm
new file mode 100644
index 0000000000..30865c412c
--- /dev/null
+++ b/code/modules/tgui_input/color.dm
@@ -0,0 +1,144 @@
+/**
+ * Creates a TGUI color picker window and returns the user's response.
+ *
+ * This proc should be used to create a color picker that the caller will wait for a response from.
+ * Arguments:
+ * * user - The user to show the picker to.
+ * * title - The of the picker modal, shown on the top of the TGUI window.
+ * * timeout - The timeout of the picker, after which the modal will close and qdel itself. Set to zero for no timeout.
+ * * autofocus - The bool that controls if this picker should grab window focus.
+ */
+/proc/tgui_color_picker(mob/user, message, title, default = "#000000", timeout = 0, autofocus = TRUE)
+ if (!user)
+ user = usr
+ if (!istype(user))
+ if (istype(user, /client))
+ var/client/client = user
+ user = client.mob
+ else
+ return
+ // Client does NOT have tgui_input on: Returns regular input
+ if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_mode) || user.client.byond_version < 516) // Todo, remove once virgo is on 516, but currently the TGUI interface is already programmed for edge
+ return input(user, message, title, default) as color|null
+ var/datum/tgui_color_picker/picker = new(user, message, title, default, timeout, autofocus)
+ picker.tgui_interact(user)
+ picker.wait()
+ if (picker)
+ . = picker.choice
+ qdel(picker)
+
+/**
+ * # tgui_color_picker
+ *
+ * Datum used for instantiating and using a TGUI-controlled color picker.
+ */
+/datum/tgui_color_picker
+ /// The title of the TGUI window
+ var/title
+ /// The message to show the user
+ var/message
+ /// The default choice, used if there is an existing value
+ var/default
+ /// The color the user selected, null if no selection has been made
+ var/choice
+ /// The time at which the tgui_color_picker was created, for displaying timeout progress.
+ var/start_time
+ /// The lifespan of the tgui_color_picker, after which the window will close and delete itself.
+ var/timeout
+ /// The bool that controls if this modal should grab window focus
+ var/autofocus
+ /// Boolean field describing if the tgui_color_picker was closed by the user.
+ var/closed
+
+/datum/tgui_color_picker/New(mob/user, message, title, default, timeout, autofocus)
+ src.autofocus = autofocus
+ src.title = title
+ src.default = default
+ src.message = message
+ if (timeout)
+ src.timeout = timeout
+ start_time = world.time
+ QDEL_IN(src, timeout)
+
+/datum/tgui_color_picker/Destroy(force)
+ SStgui.close_uis(src)
+ . = ..()
+
+/**
+ * Waits for a user's response to the tgui_color_picker's prompt before returning. Returns early if
+ * the window was closed by the user.
+ */
+/datum/tgui_color_picker/proc/wait()
+ while (!choice && !closed && !QDELETED(src))
+ stoplag(1)
+
+/datum/tgui_color_picker/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ColorPickerModal")
+ ui.open()
+ ui.set_autoupdate(timeout > 0)
+
+/datum/tgui_color_picker/tgui_close(mob/user)
+ . = ..()
+ closed = TRUE
+
+/datum/tgui_color_picker/tgui_state(mob/user)
+ return GLOB.tgui_always_state
+
+/datum/tgui_color_picker/tgui_static_data(mob/user)
+ . = list()
+ .["autofocus"] = autofocus
+ .["large_buttons"] = !user.client?.prefs || user.client.prefs.read_preference(/datum/preference/toggle/tgui_large_buttons)
+ .["swapped_buttons"] = !user.client?.prefs || user.client.prefs.read_preference(/datum/preference/toggle/tgui_swapped_buttons)
+ .["title"] = title
+ .["default_color"] = default
+ .["message"] = message
+
+/datum/tgui_color_picker/tgui_data(mob/user)
+ . = list()
+ if(timeout)
+ .["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
+ .["presets"] = user.read_preference(/datum/preference/text/preset_colors)
+
+/datum/tgui_color_picker/tgui_act(action, list/params, datum/tgui/ui)
+ . = ..()
+ if (.)
+ return
+ switch(action)
+ if("submit")
+ var/raw_data = lowertext(params["entry"])
+ var/hex = sanitize_hexcolor(raw_data)
+ if (!hex)
+ return
+ set_choice(hex)
+ closed = TRUE
+ SStgui.close_uis(src)
+ return TRUE
+ if("cancel")
+ closed = TRUE
+ SStgui.close_uis(src)
+ return TRUE
+ if("null")
+ set_choice(null)
+ SStgui.close_uis(src)
+ return TRUE
+ if("preset")
+ if(ui.user)
+ var/raw_data = lowertext(params["color"])
+ var/index = lowertext(params["index"])
+ var/colors = ui.user.read_preference(/datum/preference/text/preset_colors)
+ var/list/entries = splittext(colors, ";")
+ while(LAZYLEN(entries) < 20)
+ entries += "#FFFFFF"
+ if(LAZYLEN(entries) > 20)
+ entries.Cut(21)
+ var/hex = sanitize_hexcolor(raw_data)
+ if (!hex || !isnum(index) || entries[index] == hex)
+ return
+ entries[index] = hex
+ ui.user.write_preference_directly(/datum/preference/text/preset_colors, entries.Join(";"))
+ return TRUE
+
+/datum/tgui_color_picker/proc/set_choice(choice)
+ src.choice = choice
diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm
index 0ced8dc838..e61dd1a065 100644
--- a/code/modules/vehicles/bike.dm
+++ b/code/modules/vehicles/bike.dm
@@ -46,9 +46,9 @@
paint_color = rgb(rand(1,255),rand(1,255),rand(1,255))
. = ..()
-/obj/vehicle/bike/attackby(obj/item/W as obj, mob/user as mob)
+/obj/vehicle/bike/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/multitool) && open)
- var/new_paint = input(usr, "Please select paint color.", "Paint Color", paint_color) as color|null
+ var/new_paint = tgui_color_picker(user, "Please select paint color.", "Paint Color", paint_color)
if(new_paint)
paint_color = new_paint
update_icon()
diff --git a/code/modules/vehicles/quad.dm b/code/modules/vehicles/quad.dm
index 4090cca797..2ba4bfca53 100644
--- a/code/modules/vehicles/quad.dm
+++ b/code/modules/vehicles/quad.dm
@@ -88,9 +88,9 @@
if(8)
pixel_y = 0
-/obj/vehicle/train/engine/quadbike/attackby(obj/item/W as obj, mob/user as mob)
+/obj/vehicle/train/engine/quadbike/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/multitool) && open)
- var/new_paint = input(usr, "Please select paint color.", "Paint Color", paint_color) as color|null
+ var/new_paint = tgui_color_picker(user, "Please select paint color.", "Paint Color", paint_color)
if(new_paint)
paint_color = new_paint
update_icon()
@@ -276,9 +276,9 @@
Bodypaint.color = paint_color
add_overlay(Bodypaint)
-/obj/vehicle/train/trolley/trailer/attackby(obj/item/W as obj, mob/user as mob)
+/obj/vehicle/train/trolley/trailer/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/multitool) && open)
- var/new_paint = input(usr, "Please select paint color.", "Paint Color", paint_color) as color|null
+ var/new_paint = tgui_color_picker(user, "Please select paint color.", "Paint Color", paint_color)
if(new_paint)
paint_color = new_paint
update_icon()
diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm
index 04ea19e846..46af90ad28 100644
--- a/code/modules/vore/eating/vorepanel_vr.dm
+++ b/code/modules/vore/eating/vorepanel_vr.dm
@@ -676,7 +676,7 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
if("set_vs_color")
var/belly_choice = tgui_input_list(ui.user, "Which vore sprite are you going to edit the color of?", "Vore Sprite Color", host.vore_icon_bellies)
if(belly_choice)
- var/newcolor = input(ui.user, "Choose a color.", "", host.vore_sprite_color[belly_choice]) as color|null
+ var/newcolor = tgui_color_picker(ui.user, "Choose a color.", "", host.vore_sprite_color[belly_choice])
if(newcolor)
host.vore_sprite_color[belly_choice] = newcolor
var/multiply = tgui_input_list(ui.user, "Set the color to be applied multiplicatively or additively? Currently in [host.vore_sprite_multiply[belly_choice] ? "Multiply" : "Add"]", "Vore Sprite Color", list("Multiply", "Add"))
@@ -1724,17 +1724,17 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
host.vore_selected.clear_preview(host) //Clears the stomach overlay. This is a failsafe but shouldn't occur.
. = TRUE
if("b_fullscreen_color")
- var/newcolor = input(user, "Choose a color.", "", host.vore_selected.belly_fullscreen_color) as color|null
+ var/newcolor = tgui_color_picker(user, "Choose a color.", "", host.vore_selected.belly_fullscreen_color)
if(newcolor)
host.vore_selected.belly_fullscreen_color = newcolor
. = TRUE
if("b_fullscreen_color_secondary")
- var/newcolor = input(user, "Choose a color.", "", host.vore_selected.belly_fullscreen_color_secondary) as color|null
+ var/newcolor = tgui_color_picker(user, "Choose a color.", "", host.vore_selected.belly_fullscreen_color_secondary)
if(newcolor)
host.vore_selected.belly_fullscreen_color_secondary = newcolor
. = TRUE
if("b_fullscreen_color_trinary")
- var/newcolor = input(user, "Choose a color.", "", host.vore_selected.belly_fullscreen_color_trinary) as color|null
+ var/newcolor = tgui_color_picker(user, "Choose a color.", "", host.vore_selected.belly_fullscreen_color_trinary)
if(newcolor)
host.vore_selected.belly_fullscreen_color_trinary = newcolor
. = TRUE
@@ -1867,7 +1867,7 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
host.update_fullness()
host.updateVRPanel()
if("b_undergarment_color")
- var/newcolor = input(user, "Choose a color.", "", host.vore_selected.undergarment_color) as color|null
+ var/newcolor = tgui_color_picker(user, "Choose a color.", "", host.vore_selected.undergarment_color)
if(newcolor)
host.vore_selected.undergarment_color = newcolor
host.update_fullness()
@@ -1880,17 +1880,17 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
host.vore_selected.tail_to_change_to = tail_choice
. = TRUE
if("b_tail_color")
- var/newcolor = input(user, "Choose tail color.", "", host.vore_selected.tail_colouration) as color|null
+ var/newcolor = tgui_color_picker(user, "Choose tail color.", "", host.vore_selected.tail_colouration)
if(newcolor)
host.vore_selected.tail_colouration = newcolor
. = TRUE
if("b_tail_color2")
- var/newcolor = input(user, "Choose tail secondary color.", "", host.vore_selected.tail_extra_overlay) as color|null
+ var/newcolor = tgui_color_picker(user, "Choose tail secondary color.", "", host.vore_selected.tail_extra_overlay)
if(newcolor)
host.vore_selected.tail_extra_overlay = newcolor
. = TRUE
if("b_tail_color3")
- var/newcolor = input(user, "Choose tail tertiary color.", "", host.vore_selected.tail_extra_overlay2) as color|null
+ var/newcolor = tgui_color_picker(user, "Choose tail tertiary color.", "", host.vore_selected.tail_extra_overlay2)
if(newcolor)
host.vore_selected.tail_extra_overlay2 = newcolor
. = TRUE
diff --git a/code/modules/vore/smoleworld/smoleworld_vr.dm b/code/modules/vore/smoleworld/smoleworld_vr.dm
index 7e43c79a21..06aa6b2267 100644
--- a/code/modules/vore/smoleworld/smoleworld_vr.dm
+++ b/code/modules/vore/smoleworld/smoleworld_vr.dm
@@ -147,7 +147,7 @@
set src in oview(1)
if(ismouse(usr) || (isobserver(usr) && !CONFIG_GET(flag/ghost_interaction)))
return
- var/new_color = input(usr, "Please select color.", "Paint Color", color) as color|null
+ var/new_color = tgui_color_picker(usr, "Please select color.", "Paint Color", color)
color = new_color
return
@@ -307,7 +307,7 @@
set src in oview(1)
if(ismouse(usr) || (isobserver(usr) && !CONFIG_GET(flag/ghost_interaction)))
return
- var/new_color = input(usr, "Please select color.", "Paint Color", color) as color|null
+ var/new_color = tgui_color_picker(usr, "Please select color.", "Paint Color", color)
color = new_color
return
diff --git a/tgui/packages/common/colorpicker.tsx b/tgui/packages/common/colorpicker.tsx
new file mode 100644
index 0000000000..494ab3ea7f
--- /dev/null
+++ b/tgui/packages/common/colorpicker.tsx
@@ -0,0 +1,415 @@
+/*
+ * MIT License
+ * https://github.com/omgovich/react-colorful/
+ *
+ * Copyright (c) 2020 Vlad Shilov
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+const round = (
+ number: number,
+ digits = 0,
+ base = Math.pow(10, digits),
+): number => {
+ return Math.round(base * number) / base;
+};
+export interface RgbColor {
+ r: number;
+ g: number;
+ b: number;
+}
+export interface RgbaColor extends RgbColor {
+ a: number;
+}
+export interface HslColor {
+ h: number;
+ s: number;
+ l: number;
+}
+export interface HslaColor extends HslColor {
+ a: number;
+}
+export interface HsvColor {
+ h: number;
+ s: number;
+ v: number;
+}
+export interface HsvaColor extends HsvColor {
+ a: number;
+}
+export type ObjectColor =
+ | RgbColor
+ | HslColor
+ | HsvColor
+ | RgbaColor
+ | HslaColor
+ | HsvaColor;
+export type AnyColor = string | ObjectColor;
+/**
+ * Valid CSS units.
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/angle
+ */
+const angleUnits: Record = {
+ grad: 360 / 400,
+ turn: 360,
+ rad: 360 / (Math.PI * 2),
+};
+export const hexToHsva = (hex: string): HsvaColor => rgbaToHsva(hexToRgba(hex));
+export const hexToRgba = (hex: string): RgbaColor => {
+ if (hex[0] === '#') hex = hex.substring(1);
+ if (hex.length < 6) {
+ return {
+ r: parseInt(hex[0] + hex[0], 16),
+ g: parseInt(hex[1] + hex[1], 16),
+ b: parseInt(hex[2] + hex[2], 16),
+ a: hex.length === 4 ? round(parseInt(hex[3] + hex[3], 16) / 255, 2) : 1,
+ };
+ }
+ return {
+ r: parseInt(hex.substring(0, 2), 16),
+ g: parseInt(hex.substring(2, 4), 16),
+ b: parseInt(hex.substring(4, 6), 16),
+ a: hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 2) : 1,
+ };
+};
+export const parseHue = (value: string, unit = 'deg'): number => {
+ return Number(value) * (angleUnits[unit] || 1);
+};
+export const hslaStringToHsva = (hslString: string): HsvaColor => {
+ const matcher =
+ /hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i;
+ const match = matcher.exec(hslString);
+ if (!match) return { h: 0, s: 0, v: 0, a: 1 };
+ return hslaToHsva({
+ h: parseHue(match[1], match[2]),
+ s: Number(match[3]),
+ l: Number(match[4]),
+ a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1),
+ });
+};
+export const hslStringToHsva = hslaStringToHsva;
+export const hslaToHsva = ({ h, s, l, a }: HslaColor): HsvaColor => {
+ s *= (l < 50 ? l : 100 - l) / 100;
+ return {
+ h: h,
+ s: s > 0 ? ((2 * s) / (l + s)) * 100 : 0,
+ v: l + s,
+ a,
+ };
+};
+export const hsvaToHex = (hsva: HsvaColor): string =>
+ rgbaToHex(hsvaToRgba(hsva));
+export const hsvaToHsla = ({ h, s, v, a }: HsvaColor): HslaColor => {
+ const hh = ((200 - s) * v) / 100;
+ return {
+ h: round(h),
+ s: round(
+ hh > 0 && hh < 200
+ ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100
+ : 0,
+ ),
+ l: round(hh / 2),
+ a: round(a, 2),
+ };
+};
+export const hsvaToHslString = (hsva: HsvaColor): string => {
+ const { h, s, l } = hsvaToHsla(hsva);
+ return `hsl(${h}, ${s}%, ${l}%)`;
+};
+export const hsvaToHsvString = (hsva: HsvaColor): string => {
+ const { h, s, v } = roundHsva(hsva);
+ return `hsv(${h}, ${s}%, ${v}%)`;
+};
+export const hsvaToHsvaString = (hsva: HsvaColor): string => {
+ const { h, s, v, a } = roundHsva(hsva);
+ return `hsva(${h}, ${s}%, ${v}%, ${a})`;
+};
+export const hsvaToHslaString = (hsva: HsvaColor): string => {
+ const { h, s, l, a } = hsvaToHsla(hsva);
+ return `hsla(${h}, ${s}%, ${l}%, ${a})`;
+};
+export const hsvaToRgba = ({ h, s, v, a }: HsvaColor): RgbaColor => {
+ h = (h / 360) * 6;
+ s = s / 100;
+ v = v / 100;
+ const hh = Math.floor(h),
+ b = v * (1 - s),
+ c = v * (1 - (h - hh) * s),
+ d = v * (1 - (1 - h + hh) * s),
+ module = hh % 6;
+ return {
+ r: [v, c, b, b, d, v][module] * 255,
+ g: [d, v, v, c, b, b][module] * 255,
+ b: [b, b, d, v, v, c][module] * 255,
+ a: round(a, 2),
+ };
+};
+export const hsvaToRgbString = (hsva: HsvaColor): string => {
+ const { r, g, b } = hsvaToRgba(hsva);
+ return `rgb(${round(r)}, ${round(g)}, ${round(b)})`;
+};
+export const hsvaToRgbaString = (hsva: HsvaColor): string => {
+ const { r, g, b, a } = hsvaToRgba(hsva);
+ return `rgba(${round(r)}, ${round(g)}, ${round(b)}, ${round(a, 2)})`;
+};
+export const hsvaStringToHsva = (hsvString: string): HsvaColor => {
+ const matcher =
+ /hsva?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i;
+ const match = matcher.exec(hsvString);
+ if (!match) return { h: 0, s: 0, v: 0, a: 1 };
+ return roundHsva({
+ h: parseHue(match[1], match[2]),
+ s: Number(match[3]),
+ v: Number(match[4]),
+ a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1),
+ });
+};
+export const hsvStringToHsva = hsvaStringToHsva;
+export const rgbaStringToHsva = (rgbaString: string): HsvaColor => {
+ const matcher =
+ /rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i;
+ const match = matcher.exec(rgbaString);
+ if (!match) return { h: 0, s: 0, v: 0, a: 1 };
+ return rgbaToHsva({
+ r: Number(match[1]) / (match[2] ? 100 / 255 : 1),
+ g: Number(match[3]) / (match[4] ? 100 / 255 : 1),
+ b: Number(match[5]) / (match[6] ? 100 / 255 : 1),
+ a: match[7] === undefined ? 1 : Number(match[7]) / (match[8] ? 100 : 1),
+ });
+};
+export const rgbStringToHsva = rgbaStringToHsva;
+const format = (number: number) => {
+ const hex = number.toString(16);
+ return hex.length < 2 ? '0' + hex : hex;
+};
+export const rgbaToHex = ({ r, g, b, a }: RgbaColor): string => {
+ const alphaHex = a < 1 ? format(round(a * 255)) : '';
+ return (
+ '#' + format(round(r)) + format(round(g)) + format(round(b)) + alphaHex
+ );
+};
+export const rgbaToHsva = ({ r, g, b, a }: RgbaColor): HsvaColor => {
+ const max = Math.max(r, g, b);
+ const delta = max - Math.min(r, g, b);
+ // prettier-ignore
+ const hh = delta
+ ? max === r
+ ? (g - b) / delta
+ : max === g
+ ? 2 + (b - r) / delta
+ : 4 + (r - g) / delta
+ : 0;
+ return {
+ h: 60 * (hh < 0 ? hh + 6 : hh),
+ s: max ? (delta / max) * 100 : 0,
+ v: (max / 255) * 100,
+ a,
+ };
+};
+export const roundHsva = (hsva: HsvaColor): HsvaColor => ({
+ h: round(hsva.h),
+ s: round(hsva.s),
+ v: round(hsva.v),
+ a: round(hsva.a, 2),
+});
+export const rgbaToRgb = ({ r, g, b }: RgbaColor): RgbColor => ({ r, g, b });
+export const hslaToHsl = ({ h, s, l }: HslaColor): HslColor => ({ h, s, l });
+export const hsvaToHsv = (hsva: HsvaColor): HsvColor => {
+ const { h, s, v } = roundHsva(hsva);
+ return { h, s, v };
+};
+const hexMatcher = /^#?([0-9A-F]{3,8})$/i;
+export const validHex = (value: string, alpha?: boolean): boolean => {
+ const match = hexMatcher.exec(value);
+ const length = match ? match[1].length : 0;
+ return (
+ length === 3 || // '#rgb' format
+ length === 6 || // '#rrggbb' format
+ (!!alpha && length === 4) || // '#rgba' format
+ (!!alpha && length === 8) // '#rrggbbaa' format
+ );
+};
+// Source for the following luminance and contrast calculation code: https://blog.cristiana.tech/calculating-color-contrast-in-typescript-using-web-content-accessibility-guidelines-wcag
+export const luminance = (rgb: RgbColor): number => {
+ const [r, g, b] = [rgb.r, rgb.g, rgb.b].map((v) => {
+ v /= 255;
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
+ });
+ return r * 0.2126 + g * 0.7152 + b * 0.0722;
+};
+export const contrast = (
+ foreground: RgbColor,
+ background: RgbColor,
+): number => {
+ const foreground_luminance = luminance(foreground);
+ const background_luminance = luminance(background);
+ return background_luminance < foreground_luminance
+ ? (background_luminance + 0.05) / (foreground_luminance + 0.05)
+ : (foreground_luminance + 0.05) / (background_luminance + 0.05);
+};
+
+export const colorList = [
+ ['003366', '336699', '3366CC', '003399', '000099', '0000CC', '000066'],
+ [
+ '006666',
+ '006699',
+ '0099CC',
+ '0066CC',
+ '0033CC',
+ '0000FF',
+ '3333FF',
+ '333399',
+ ],
+ [
+ '008080',
+ '009999',
+ '33CCCC',
+ '00CCFF',
+ '0099FF',
+ '0066FF',
+ '3366FF',
+ '3333CC',
+ '666699',
+ ],
+ [
+ '339966',
+ '00CC99',
+ '00FFCC',
+ '00FFFF',
+ '33CCFF',
+ '3399FF',
+ '6699FF',
+ '6666FF',
+ '6600FF',
+ '6600CC',
+ ],
+ [
+ '339933',
+ '00CC66',
+ '00FF99',
+ '66FFCC',
+ '66FFFF',
+ '66CCFF',
+ '99CCFF',
+ '9999FF',
+ '9966FF',
+ '9933FF',
+ '9900FF',
+ ],
+ [
+ '006600',
+ '00CC00',
+ '00FF00',
+ '66FF99',
+ '99FFCC',
+ 'CCFFFF',
+ 'CCECFF',
+ 'CCCCFF',
+ 'CC99FF',
+ 'CC66FF',
+ 'CC00FF',
+ '9900CC',
+ ],
+ [
+ '003300',
+ '008000',
+ '33CC33',
+ '66FF66',
+ '99FF99',
+ 'CCFFCC',
+ 'FFFFFF',
+ 'FFCCFF',
+ 'FF99FF',
+ 'FF66FF',
+ 'FF00FF',
+ 'CC00CC',
+ '660066',
+ ],
+ [
+ '336600',
+ '009900',
+ '66FF33',
+ '99FF66',
+ 'CCFF99',
+ 'FFFFCC',
+ 'FFCCCC',
+ 'FF99CC',
+ 'FF66CC',
+ 'FF33CC',
+ 'CC0099',
+ '800080',
+ ],
+ [
+ '333300',
+ '669900',
+ '99FF33',
+ 'CCFF66',
+ 'FFFF99',
+ 'FFCC99',
+ 'FF9999',
+ 'FF6699',
+ 'FF3399',
+ 'CC3399',
+ '990099',
+ ],
+ [
+ '666633',
+ '99CC00',
+ 'CCFF33',
+ 'FFFF66',
+ 'FFCC66',
+ 'FF9966',
+ 'FF7C80',
+ 'FF0066',
+ 'D60093',
+ '993366',
+ ],
+ [
+ '808000',
+ 'CCCC00',
+ 'FFFF00',
+ 'FFCC00',
+ 'FF9933',
+ 'FF6600',
+ 'FF5050',
+ 'CC0066',
+ '660033',
+ ],
+ [
+ '996633',
+ 'CC9900',
+ 'FF9900',
+ 'CC6600',
+ 'FF3300',
+ 'FF0000',
+ 'CC0000',
+ '990033',
+ ],
+ ['663300', '996600', 'CC3300', '993300', '990000', '800000', 'A50021'],
+ [
+ 'F8F8F8',
+ 'DDDDDD',
+ 'B2B2B2',
+ '808080',
+ '5F5F5F',
+ '333333',
+ '1C1C1C',
+ '080808',
+ ],
+ [
+ 'FFFFFF',
+ 'EAEAEA',
+ 'C0C0C0',
+ '969696',
+ '777777',
+ '4D4D4D',
+ '292929',
+ '111111',
+ '000000',
+ ],
+];
diff --git a/tgui/packages/tgui/components/Interactive.tsx b/tgui/packages/tgui/components/Interactive.tsx
new file mode 100644
index 0000000000..9281ab0dbe
--- /dev/null
+++ b/tgui/packages/tgui/components/Interactive.tsx
@@ -0,0 +1,143 @@
+/**
+ * MIT License
+ * https://github.com/omgovich/react-colorful/
+ *
+ * Copyright (c) 2020 Vlad Shilov
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import { KEY } from 'common/keys';
+import { clamp } from 'common/math';
+import React, { Component, createRef, ReactNode, RefObject } from 'react';
+
+export interface Interaction {
+ left: number;
+ top: number;
+}
+
+// Finds the proper window object to fix iframe embedding issues
+const getParentWindow = (node?: HTMLDivElement | null): Window => {
+ return (node && node.ownerDocument.defaultView) || window;
+};
+
+// Returns a relative position of the pointer inside the node's bounding box
+const getRelativePosition = (
+ node: HTMLDivElement,
+ event: MouseEvent,
+): Interaction => {
+ const rect = node.getBoundingClientRect();
+ const parentWindow = getParentWindow(node);
+
+ const offsetX = event.pageX - (rect.left + parentWindow.scrollX);
+ const offsetY = event.pageY - (rect.top + parentWindow.scrollY);
+
+ return {
+ left: clamp(offsetX / rect.width, 0, 1),
+ top: clamp(offsetY / rect.height, 0, 1),
+ };
+};
+
+export interface InteractiveProps {
+ onMove: (interaction: Interaction) => void;
+ onKey: (offset: Interaction) => void;
+ children: ReactNode;
+ style?: React.CSSProperties;
+}
+
+export class Interactive extends Component {
+ containerRef: RefObject;
+
+ constructor(props: InteractiveProps) {
+ super(props);
+ this.containerRef = createRef();
+ }
+
+ handleMoveStart = (event: React.MouseEvent) => {
+ const el = this.containerRef?.current;
+ if (!el) return;
+
+ // Prevent text selection
+ event.preventDefault();
+ el.focus();
+ this.props.onMove(getRelativePosition(el, event.nativeEvent));
+ this.toggleDocumentEvents(true);
+ };
+
+ handleMove = (event: MouseEvent) => {
+ event.preventDefault();
+
+ const isDown = event.buttons > 0;
+
+ if (isDown && this.containerRef?.current) {
+ this.props.onMove(getRelativePosition(this.containerRef.current, event));
+ } else {
+ this.toggleDocumentEvents(false);
+ }
+ };
+
+ handleMoveEnd = () => {
+ this.toggleDocumentEvents(false);
+ };
+
+ handleKeyDown = (event: React.KeyboardEvent) => {
+ const pressedKey = event.key;
+
+ if (
+ pressedKey !== KEY.Left &&
+ pressedKey !== KEY.Right &&
+ pressedKey !== KEY.Up &&
+ pressedKey !== KEY.Down
+ ) {
+ return;
+ }
+ event.preventDefault();
+ this.props.onKey({
+ left:
+ pressedKey === KEY.Right ? 0.05 : pressedKey === KEY.Left ? -0.05 : 0,
+ top: pressedKey === KEY.Down ? 0.05 : pressedKey === KEY.Up ? -0.05 : 0,
+ });
+ };
+
+ toggleDocumentEvents(state?: boolean) {
+ const el = this.containerRef?.current;
+ const parentWindow = getParentWindow(el);
+
+ const toggleEvent = state
+ ? parentWindow.addEventListener
+ : parentWindow.removeEventListener;
+ toggleEvent('mousemove', this.handleMove);
+ toggleEvent('mouseup', this.handleMoveEnd);
+ }
+
+ componentDidMount() {
+ this.toggleDocumentEvents(true);
+ }
+
+ componentWillUnmount() {
+ this.toggleDocumentEvents(false);
+ }
+
+ render() {
+ const { style, children, ...rest } = this.props;
+ return (
+
+ {children}
+
+ );
+ }
+}
diff --git a/tgui/packages/tgui/components/Pointer.tsx b/tgui/packages/tgui/components/Pointer.tsx
new file mode 100644
index 0000000000..1e52cec455
--- /dev/null
+++ b/tgui/packages/tgui/components/Pointer.tsx
@@ -0,0 +1,46 @@
+/**
+ * MIT License
+ * https://github.com/omgovich/react-colorful/
+ *
+ * Copyright (c) 2020 Vlad Shilov
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import { classes } from 'common/react';
+import React from 'react';
+
+interface PointerProps {
+ className?: string;
+ top?: number;
+ left: number;
+ color: string;
+}
+
+export const Pointer: React.FC = ({
+ className,
+ color,
+ left,
+ top = 0.5,
+}) => {
+ const nodeClassName = classes(['react-colorful__pointer', className]);
+
+ const style = {
+ top: `${top * 100}%`,
+ left: `${left * 100}%`,
+ };
+
+ return (
+