diff --git a/code/__HELPERS/matrices/color_matrix.dm b/code/__HELPERS/matrices/color_matrix.dm
index e4a4a9bb48..bcc48b45f5 100644
--- a/code/__HELPERS/matrices/color_matrix.dm
+++ b/code/__HELPERS/matrices/color_matrix.dm
@@ -138,6 +138,25 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001)
return list(cosval,sinval,0,0, -sinval,cosval,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
+/**
+ * Builds a color matrix that transforms the hue, saturation, and value, all in one operation.
+ */
+/proc/color_matrix_hsv(hue, saturation, value)
+ hue = clamp(360 - hue, 0, 360)
+
+ // This is very much a rough approximation of hueshifting. This carries some artifacting, such as negative values that simply shouldn't exist, but it does get the job done, and that's what matters.
+ var/cos_a = cos(hue) // These have to be inverted from 360, otherwise the hue's inverted
+ var/sin_a = sin(hue)
+ var/rot_x = cos_a + (1 - cos_a) / 3
+ var/rot_y = (1 - cos_a) / 3 - 0.5774 * sin_a // 0.5774 is sqrt(1/3)
+ var/rot_z = (1 - cos_a) / 3 + 0.5774 * sin_a
+
+ return list(
+ round((((1-saturation) * LUMA_R) + (rot_x * saturation)) * value, 0.01), round((((1-saturation) * LUMA_R) + (rot_y * saturation)) * value, 0.01), round((((1-saturation) * LUMA_R) + (rot_z * saturation)) * value, 0.01),
+ round((((1-saturation) * LUMA_G) + (rot_z * saturation)) * value, 0.01), round((((1-saturation) * LUMA_G) + (rot_x * saturation)) * value, 0.01), round((((1-saturation) * LUMA_G) + (rot_y * saturation)) * value, 0.01),
+ round((((1-saturation) * LUMA_B) + (rot_y * saturation)) * value, 0.01), round((((1-saturation) * LUMA_B) + (rot_z * saturation)) * value, 0.01), round((((1-saturation) * LUMA_B) + (rot_x * saturation)) * value, 0.01),
+ 0, 0, 0
+ )
//Returns a matrix addition of A with B
/proc/color_matrix_add(list/A, list/B)
diff --git a/code/game/machinery/colormate.dm b/code/game/machinery/colormate.dm
index 1528142a3b..a208fc0bd2 100644
--- a/code/game/machinery/colormate.dm
+++ b/code/game/machinery/colormate.dm
@@ -1,6 +1,10 @@
+#define COLORMATE_TINT 1
+#define COLORMATE_HSV 2
+#define COLORMATE_MATRIX 3
+
/obj/machinery/gear_painter
name = "\improper Color Mate"
- desc = "A machine to give your apparel a fresh new color! Recommended to use with white items for best results."
+ desc = "A machine to give your apparel a fresh new color!"
icon = 'icons/obj/vending.dmi'
icon_state = "colormate"
// light_mask = "colormate-light-mask"
@@ -10,7 +14,11 @@
var/atom/movable/inserted
var/activecolor = "#FFFFFF"
var/list/color_matrix_last
- var/matrix_mode = FALSE
+ var/active_mode = COLORMATE_HSV
+
+ var/build_hue = 0
+ var/build_sat = 1
+ var/build_val = 1
/// Allow holder'd mobs
var/allow_mobs = TRUE
/// Minimum lightness for normal mode
@@ -71,8 +79,8 @@
return
if(!QDELETED(H))
H.release()
+
insert_mob(victim, user)
- temp = "[victim] has been inserted."
SStgui.update_uis(src)
if(is_type_in_list(I, allowed_types) && is_operational())
@@ -85,7 +93,6 @@
inserted = I
update_icon()
- temp = "[I] has been inserted."
SStgui.update_uis(src)
else
@@ -134,7 +141,7 @@
/obj/machinery/gear_painter/ui_data(mob/user)
. = list()
- .["matrixactive"] = matrix_mode
+ .["activemode"] = active_mode
.["matrixcolors"] = list(
"rr" = color_matrix_last[1],
"rg" = color_matrix_last[2],
@@ -149,6 +156,9 @@
"cg" = color_matrix_last[11],
"cb" = color_matrix_last[12]
)
+ .["buildhue"] = build_hue
+ .["buildsat"] = build_sat
+ .["buildval"] = build_val
if(temp)
.["temp"] = temp
if(inserted)
@@ -166,7 +176,7 @@
if(inserted)
switch(action)
if("switch_modes")
- matrix_mode = text2num(params["mode"])
+ active_mode = text2num(params["mode"])
return TRUE
if("choose_color")
var/chosen_color = input(usr, "Choose a color: ", "Colormate color picking", activecolor) as color|null
@@ -174,14 +184,11 @@
activecolor = chosen_color
return TRUE
if("paint")
- if(!check_valid_color(activecolor, usr))
- return TRUE
- inserted.add_atom_colour(activecolor, FIXED_COLOUR_PRIORITY)
- playsound(src, 'sound/effects/spray3.ogg', 50, 1)
+ do_paint(usr)
temp = "Painted Successfully!"
return TRUE
if("drop")
- temp = "Ejected \the [inserted]!"
+ temp = ""
drop_item()
return TRUE
if("clear")
@@ -192,76 +199,95 @@
if("set_matrix_color")
color_matrix_last[params["color"]] = params["value"]
return TRUE
- if("matrix_paint")
- var/list/cm = rgb_construct_color_matrix(
- text2num(color_matrix_last[1]),
- text2num(color_matrix_last[2]),
- text2num(color_matrix_last[3]),
- text2num(color_matrix_last[4]),
- text2num(color_matrix_last[5]),
- text2num(color_matrix_last[6]),
- text2num(color_matrix_last[7]),
- text2num(color_matrix_last[8]),
- text2num(color_matrix_last[9]),
- text2num(color_matrix_last[10]),
- text2num(color_matrix_last[11]),
- text2num(color_matrix_last[12])
- )
- if(!check_valid_color(cm, usr))
- return TRUE
- inserted.add_atom_colour(cm, FIXED_COLOUR_PRIORITY)
- playsound(src, 'sound/effects/spray3.ogg', 50, 1)
- temp = "Matrix Painted Successfully!"
+ if("set_hue")
+ build_hue = clamp(text2num(params["buildhue"]), 0, 360)
return TRUE
+ if("set_sat")
+ build_sat = clamp(text2num(params["buildsat"]), -10, 10)
+ return TRUE
+ if("set_val")
+ build_val = clamp(text2num(params["buildval"]), -10, 10)
+ return TRUE
+
+
+/obj/machinery/gear_painter/proc/do_paint(mob/user)
+ var/color_to_use
+ switch(active_mode)
+ if(COLORMATE_TINT)
+ color_to_use = activecolor
+ if(COLORMATE_MATRIX)
+ color_to_use = rgb_construct_color_matrix(
+ text2num(color_matrix_last[1]),
+ text2num(color_matrix_last[2]),
+ text2num(color_matrix_last[3]),
+ text2num(color_matrix_last[4]),
+ text2num(color_matrix_last[5]),
+ text2num(color_matrix_last[6]),
+ text2num(color_matrix_last[7]),
+ text2num(color_matrix_last[8]),
+ text2num(color_matrix_last[9]),
+ text2num(color_matrix_last[10]),
+ text2num(color_matrix_last[11]),
+ text2num(color_matrix_last[12])
+ )
+ if(COLORMATE_HSV)
+ color_to_use = color_matrix_hsv(build_hue, build_sat, build_val)
+ color_matrix_last = color_to_use
+ if(!color_to_use || !check_valid_color(color_to_use, user))
+ to_chat(user, "Invalid color.")
+ return FALSE
+ inserted.add_atom_colour(color_to_use, FIXED_COLOUR_PRIORITY)
+ playsound(src, 'sound/effects/spray3.ogg', 50, 1)
+ return TRUE
+
/// Produces the preview image of the item, used in the UI, the way the color is not stacking is a sin.
/obj/machinery/gear_painter/proc/build_preview()
if(inserted) //sanity
- if(matrix_mode)
- var/list/cm = rgb_construct_color_matrix(
- text2num(color_matrix_last[1]),
- text2num(color_matrix_last[2]),
- text2num(color_matrix_last[3]),
- text2num(color_matrix_last[4]),
- text2num(color_matrix_last[5]),
- text2num(color_matrix_last[6]),
- text2num(color_matrix_last[7]),
- text2num(color_matrix_last[8]),
- text2num(color_matrix_last[9]),
- text2num(color_matrix_last[10]),
- text2num(color_matrix_last[11]),
- text2num(color_matrix_last[12])
- )
- if(!check_valid_color(cm, usr))
- temp = "Failed to generate preview: Invalid color!"
- return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
+ var/list/cm
+ switch(active_mode)
+ if(COLORMATE_MATRIX)
+ cm = rgb_construct_color_matrix(
+ text2num(color_matrix_last[1]),
+ text2num(color_matrix_last[2]),
+ text2num(color_matrix_last[3]),
+ text2num(color_matrix_last[4]),
+ text2num(color_matrix_last[5]),
+ text2num(color_matrix_last[6]),
+ text2num(color_matrix_last[7]),
+ text2num(color_matrix_last[8]),
+ text2num(color_matrix_last[9]),
+ text2num(color_matrix_last[10]),
+ text2num(color_matrix_last[11]),
+ text2num(color_matrix_last[12])
+ )
+ if(!check_valid_color(cm, usr))
+ return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
- var/cur_color = inserted.color
- inserted.color = null
- inserted.color = cm
- var/icon/preview = getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
- inserted.color = cur_color
+ if(COLORMATE_TINT)
+ if(!check_valid_color(activecolor, usr))
+ return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
- . = preview
+ if(COLORMATE_HSV)
+ cm = color_matrix_hsv(build_hue, build_sat, build_val)
+ color_matrix_last = cm
+ if(!check_valid_color(cm, usr))
+ return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
- else
- if(!check_valid_color(activecolor, usr))
- temp = "Failed to generate preview: Invalid color!"
- return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
+ var/cur_color = inserted.color
+ inserted.color = null
+ inserted.color = (active_mode == COLORMATE_TINT ? activecolor : cm)
+ var/icon/preview = getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
+ inserted.color = cur_color
+ temp = ""
- var/cur_color = inserted.color
- inserted.color = null
- inserted.color = activecolor
- var/icon/preview = getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
- inserted.color = cur_color
-
- . = preview
+ . = preview
/obj/machinery/gear_painter/proc/check_valid_color(list/cm, mob/user)
if(!islist(cm)) // normal
var/list/HSV = ReadHSV(RGBtoHSV(cm))
if(HSV[3] < minimum_normal_lightness)
- temp = "[cm] is far too dark (min lightness [minimum_normal_lightness]!"
+ temp = "[cm] is too dark (Minimum lightness: [minimum_normal_lightness])"
return FALSE
return TRUE
else // matrix
@@ -275,6 +301,6 @@
COLORTEST("FFFFFF", cm)
#undef COLORTEST
if(passed < minimum_matrix_tests)
- temp = "[english_list(color)] is not allowed (passed [passed] out of 4, minimum [minimum_matrix_tests], minimum lightness [minimum_matrix_lightness])."
+ temp = "Matrix is too dark. (passed [passed] out of [minimum_matrix_tests] required tests. Minimum lightness: [minimum_matrix_lightness])."
return FALSE
return TRUE
diff --git a/tgui/packages/tgui/interfaces/Colormate.js b/tgui/packages/tgui/interfaces/Colormate.js
index f376547102..02810f2943 100644
--- a/tgui/packages/tgui/interfaces/Colormate.js
+++ b/tgui/packages/tgui/interfaces/Colormate.js
@@ -1,115 +1,120 @@
import { useBackend } from '../backend';
-import { Button, Flex, Icon, NoticeBox, NumberInput, Section, Tabs } from '../components';
+import { Button, Icon, NoticeBox, NumberInput, Section, Table, Tabs, Slider } from '../components';
import { Window } from '../layouts';
export const Colormate = (props, context) => {
const { act, data } = useBackend(context);
- const { matrixactive, temp } = data;
+ const { activemode, temp } = data;
const item = data.item || [];
return (
- {temp ? (
- {temp}
- ) : (null)}
- {Object.keys(item).length ? (
- <>
-
-
- Item:
-
-
-
- Preview:
-
-
-
-
+
+ {temp ? (
+ {temp}
+ ) : (null)}
+ {Object.keys(item).length ? (
+ <>
+
+
+
+ Item:
+
+
+
+
+
+ Preview:
+
+
+
+
act('switch_modes', {
- mode: "0",
+ mode: 1,
})} >
- Regular coloring
+ Tint coloring (Simple)
act('switch_modes', {
- mode: "1",
+ mode: 2,
})} >
- Matrixed coloring
+ HSV coloring (Normal)
+
+ act('switch_modes', {
+ mode: 3,
+ })} >
+ Matrix coloring (Advanced)
- {matrixactive ? (
- <>
- Coloring: {item.name}
-
- >
- ) : (
- <>
- Coloring: {item.name}
-
- >
- )}
-
- >
- ) : (
-
+ Coloring: {item.name}
+
+
+
+
+ {activemode === 1 ? (
+
+ ) : activemode === 2 ? (
+
+ ) : (
+
+ )}
+
+
+ >
+ ) : (
No item inserted.
-
- )}
+ )}
+
);
};
-export const ColormateNoMatrix = (props, context) => {
+export const ColormateTint = (props, context) => {
const { act, data } = useBackend(context);
return (
-
-
-
- act('paint')} />
- act('clear')} />
- act('drop')} />
-
-
- act('choose_color')} />
-
-
-
+ act('choose_color')}
+ />
);
};
@@ -117,170 +122,211 @@ export const ColormateMatrix = (props, context) => {
const { act, data } = useBackend(context);
const matrixcolors = data.matrixcolors || [];
return (
-
-
-
- act('matrix_paint')} />
- act('clear')} />
- act('drop')} />
-
-
-
- RR: act('set_matrix_color', {
- color: 1,
- value,
- })} />
-
-
- GR: act('set_matrix_color', {
- color: 4,
- value,
- })} />
-
-
- BR: act('set_matrix_color', {
- color: 7,
- value,
- })} />
-
-
-
-
- RG: act('set_matrix_color', {
- color: 2,
- value,
- })} />
-
-
- GG: act('set_matrix_color', {
- color: 5,
- value,
- })} />
-
-
- BG: act('set_matrix_color', {
- color: 8,
- value,
- })} />
-
-
-
-
- RB: act('set_matrix_color', {
- color: 3,
- value,
- })} />
-
-
- GB: act('set_matrix_color', {
- color: 6,
- value,
- })} />
-
-
- BB: act('set_matrix_color', {
- color: 9,
- value,
- })} />
-
-
-
-
- CR: act('set_matrix_color', {
- color: 10,
- value,
- })} />
-
-
- CG: act('set_matrix_color', {
- color: 11,
- value,
- })} />
-
-
- CB: act('set_matrix_color', {
- color: 12,
- value,
- })} />
-
-
-
- RG means red will become this much green.
- CR means that red will have this much constrast applied to it.
-
-
-
+
+
+
+ RR: act('set_matrix_color', {
+ color: 1,
+ value,
+ })} />
+
+
+ GR: act('set_matrix_color', {
+ color: 4,
+ value,
+ })} />
+
+
+ BR: act('set_matrix_color', {
+ color: 7,
+ value,
+ })} />
+
+
+
+
+ RG: act('set_matrix_color', {
+ color: 2,
+ value,
+ })} />
+
+
+ GG: act('set_matrix_color', {
+ color: 5,
+ value,
+ })} />
+
+
+ BG: act('set_matrix_color', {
+ color: 8,
+ value,
+ })} />
+
+
+
+
+ RB: act('set_matrix_color', {
+ color: 3,
+ value,
+ })} />
+
+
+ GB: act('set_matrix_color', {
+ color: 6,
+ value,
+ })} />
+
+
+ BB: act('set_matrix_color', {
+ color: 9,
+ value,
+ })} />
+
+
+
+
+ CR: act('set_matrix_color', {
+ color: 10,
+ value,
+ })} />
+
+
+ CG: act('set_matrix_color', {
+ color: 11,
+ value,
+ })} />
+
+
+ CB: act('set_matrix_color', {
+ color: 12,
+ value,
+ })} />
+
+
+
+ RG means red will become this much green.
+ CR means this much red will be added.
+
+
+ );
+};
+
+export const ColormateHSV = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { buildhue, buildsat, buildval } = data;
+ return (
+
+
+ Hue:
+
+ act('set_hue', {
+ buildhue: value,
+ })} />
+
+
+
+ Saturation:
+
+ act('set_sat', {
+ buildsat: value,
+ })} />
+
+
+
+ Value:
+
+ act('set_val', {
+ buildval: value,
+ })} />
+
+
+
);
};