mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
yeah
This commit is contained in:
4
code/__defines/lum_ch.dm
Normal file
4
code/__defines/lum_ch.dm
Normal file
@@ -0,0 +1,4 @@
|
||||
//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1.
|
||||
#define LUMA_R 0.213
|
||||
#define LUMA_G 0.715
|
||||
#define LUMA_B 0.072
|
||||
@@ -80,8 +80,8 @@
|
||||
Blend(M, ICON_ADD)
|
||||
|
||||
/proc/BlendRGB(rgb1, rgb2, amount)
|
||||
var/list/RGB1 = rgb2num(rgb1)
|
||||
var/list/RGB2 = rgb2num(rgb2)
|
||||
var/list/RGB1 = ReadRGB(rgb1) //CHOMPEdit - Better rgb blend
|
||||
var/list/RGB2 = ReadRGB(rgb2)
|
||||
|
||||
// add missing alpha if needed
|
||||
if(RGB1.len < RGB2.len) RGB1 += 255
|
||||
|
||||
645
code/_helpers/icons_ch.dm
Normal file
645
code/_helpers/icons_ch.dm
Normal file
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
IconProcs README
|
||||
|
||||
A BYOND library for manipulating icons and colors
|
||||
|
||||
by Lummox JR
|
||||
|
||||
version 1.0
|
||||
|
||||
The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation
|
||||
routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners.
|
||||
|
||||
CHANGING ICONS
|
||||
|
||||
Several new procs have been added to the /icon datum to simplify working with icons. To use them,
|
||||
remember you first need to setup an /icon var like so:
|
||||
|
||||
GLOBAL_DATUM_INIT(my_icon, /icon, new('iconfile.dmi'))
|
||||
|
||||
icon/ChangeOpacity(amount = 1)
|
||||
A very common operation in DM is to try to make an icon more or less transparent. Making an icon more
|
||||
transparent is usually much easier than making it less so, however. This proc basically is a frontend
|
||||
for MapColors() which can change opacity any way you like, in much the same way that SetIntensity()
|
||||
can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half.
|
||||
If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque.
|
||||
icon/GrayScale()
|
||||
Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact.
|
||||
icon/ColorTone(tone)
|
||||
Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an
|
||||
RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect.
|
||||
See also the global ColorTone() proc.
|
||||
icon/MinColors(icon)
|
||||
The icon is blended with a second icon where the minimum of each RGB pixel is the result.
|
||||
Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon.
|
||||
icon/MaxColors(icon)
|
||||
The icon is blended with a second icon where the maximum of each RGB pixel is the result.
|
||||
Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon.
|
||||
icon/Opaque(background = "#000000")
|
||||
All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify.
|
||||
icon/BecomeAlphaMask()
|
||||
You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc.
|
||||
The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white.
|
||||
icon/AddAlphaMask(mask)
|
||||
The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque,
|
||||
the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent.
|
||||
Where the mask is translucent, the current icon becomes more transparent.
|
||||
icon/UseAlphaMask(mask, mode)
|
||||
Sometimes you may want to take the alpha values from one icon and use them on a different icon.
|
||||
This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change
|
||||
so it has the same colors as before but uses the mask for opacity.
|
||||
|
||||
COLOR MANAGEMENT AND HSV
|
||||
|
||||
RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value.
|
||||
|
||||
* The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to
|
||||
cyan to blue to magenta and back to red.
|
||||
* The saturation of a color is how much color is in it. A color with low saturation will be more gray,
|
||||
and with no saturation at all it is a shade of gray.
|
||||
* The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark,
|
||||
and no value at all is black.
|
||||
|
||||
Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three
|
||||
hex digits because it ranges from 0 to 0x5FF.
|
||||
|
||||
* 0 to 0xFF - red to yellow
|
||||
* 0x100 to 0x1FF - yellow to green
|
||||
* 0x200 to 0x2FF - green to cyan
|
||||
* 0x300 to 0x3FF - cyan to blue
|
||||
* 0x400 to 0x4FF - blue to magenta
|
||||
* 0x500 to 0x5FF - magenta to red
|
||||
|
||||
Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible),
|
||||
value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff".
|
||||
|
||||
More than one HSV color can match the same RGB color.
|
||||
|
||||
Here are some procs you can use for color management:
|
||||
|
||||
ReadRGB(rgb)
|
||||
Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used
|
||||
that includes alpha, the list will have a fourth item for the alpha value.
|
||||
hsv(hue, sat, val, apha)
|
||||
Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa"
|
||||
format. Alpha is not included in the result if null.
|
||||
ReadHSV(rgb)
|
||||
Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that
|
||||
includes alpha, the list will have a fourth item for the alpha value.
|
||||
RGBtoHSV(rgb)
|
||||
Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff".
|
||||
HSVtoRGB(hsv)
|
||||
Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa".
|
||||
BlendRGB(rgb1, rgb2, amount)
|
||||
Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result;
|
||||
if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
|
||||
The returned value is an RGB or RGBA color.
|
||||
BlendHSV(hsv1, hsv2, amount)
|
||||
Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB
|
||||
blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1,
|
||||
the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
|
||||
The returned value is an HSV or HSVA color.
|
||||
BlendRGBasHSV(rgb1, rgb2, amount)
|
||||
Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form.
|
||||
HueToAngle(hue)
|
||||
Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue.
|
||||
AngleToHue(hue)
|
||||
Converts an angle to a hue in the valid range.
|
||||
RotateHue(hsv, angle)
|
||||
Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360.
|
||||
(Rotating red by 60<36> produces yellow.) The result is another HSV or HSVA color with the same saturation and value
|
||||
as the original, but a different hue.
|
||||
GrayScale(rgb)
|
||||
Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string.
|
||||
ColorTone(rgb, tone)
|
||||
Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of
|
||||
using strict shades of gray. The tone value is an RGB color; any alpha value is ignored.
|
||||
*/
|
||||
|
||||
/*
|
||||
Get Flat Icon DEMO by DarkCampainger
|
||||
|
||||
This is a test for the get flat icon proc, modified approprietly for icons and their states.
|
||||
Probably not a good idea to run this unless you want to see how the proc works in detail.
|
||||
mob
|
||||
icon = 'old_or_unused.dmi'
|
||||
icon_state = "green"
|
||||
|
||||
Login()
|
||||
// Testing image underlays
|
||||
underlays += image(icon='old_or_unused.dmi',icon_state="red")
|
||||
underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32)
|
||||
underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32)
|
||||
|
||||
// Testing image overlays
|
||||
add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32))
|
||||
add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32))
|
||||
add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32))
|
||||
|
||||
// Testing icon file overlays (defaults to mob's state)
|
||||
add_overlay('_flat_demoIcons2.dmi')
|
||||
|
||||
// Testing icon_state overlays (defaults to mob's icon)
|
||||
add_overlay("white")
|
||||
|
||||
// Testing dynamic icon overlays
|
||||
var/icon/I = icon('old_or_unused.dmi', icon_state="aqua")
|
||||
I.Shift(NORTH,16,1)
|
||||
add_overlay(I)
|
||||
|
||||
// Testing dynamic image overlays
|
||||
I=image(icon=I,pixel_x = -32, pixel_y = 32)
|
||||
add_overlay(I)
|
||||
|
||||
// Testing object types (and layers)
|
||||
add_overlay(/obj/effect/overlayTest)
|
||||
|
||||
loc = locate (10,10,1)
|
||||
verb
|
||||
Browse_Icon()
|
||||
set name = "1. Browse Icon"
|
||||
// Give it a name for the cache
|
||||
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
||||
// Send the icon to src's local cache
|
||||
src<<browse_rsc(get_flat_icon(src), iconName)
|
||||
// Display the icon in their browser
|
||||
src<<browse("<body bgcolor='#000000'><p><img src='[iconName]'></p></body>")
|
||||
|
||||
Output_Icon()
|
||||
set name = "2. Output Icon"
|
||||
to_chat(src, "Icon is: [icon2base64html(get_flat_icon(src))]")
|
||||
|
||||
Label_Icon()
|
||||
set name = "3. Label Icon"
|
||||
// Give it a name for the cache
|
||||
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
||||
// Copy the file to the rsc manually
|
||||
var/icon/I = fcopy_rsc(get_flat_icon(src))
|
||||
// Send the icon to src's local cache
|
||||
src<<browse_rsc(I, iconName)
|
||||
// Update the label to show it
|
||||
winset(src,"imageLabel","image='[REF(I)]'");
|
||||
|
||||
Add_Overlay()
|
||||
set name = "4. Add Overlay"
|
||||
add_overlay(image(icon='old_or_unused.dmi',icon_state="yellow",pixel_x = rand(-64,32), pixel_y = rand(-64,32))
|
||||
|
||||
Stress_Test()
|
||||
set name = "5. Stress Test"
|
||||
for(var/i = 0 to 1000)
|
||||
// The third parameter forces it to generate a new one, even if it's already cached
|
||||
get_flat_icon(src,0,2)
|
||||
if(prob(5))
|
||||
Add_Overlay()
|
||||
Browse_Icon()
|
||||
|
||||
Cache_Test()
|
||||
set name = "6. Cache Test"
|
||||
for(var/i = 0 to 1000)
|
||||
get_flat_icon(src)
|
||||
Browse_Icon()
|
||||
|
||||
/obj/effect/overlayTest
|
||||
icon = 'old_or_unused.dmi'
|
||||
icon_state = "blue"
|
||||
pixel_x = -24
|
||||
pixel_y = 24
|
||||
layer = TURF_LAYER // Should appear below the rest of the overlays
|
||||
|
||||
world
|
||||
view = "7x7"
|
||||
maxx = 20
|
||||
maxy = 20
|
||||
maxz = 1
|
||||
*/
|
||||
|
||||
/*
|
||||
HSV format is represented as "#hhhssvv" or "#hhhssvvaa"
|
||||
|
||||
Hue ranges from 0 to 0x5ff (1535)
|
||||
|
||||
0x000 = red
|
||||
0x100 = yellow
|
||||
0x200 = green
|
||||
0x300 = cyan
|
||||
0x400 = blue
|
||||
0x500 = magenta
|
||||
|
||||
Saturation is from 0 to 0xff (255)
|
||||
|
||||
More saturation = more color
|
||||
Less saturation = more gray
|
||||
|
||||
Value ranges from 0 to 0xff (255)
|
||||
|
||||
Higher value means brighter color
|
||||
*/
|
||||
|
||||
/**
|
||||
* reads RGB or RGBA values to list
|
||||
* @return list(r, g, b) or list(r, g, b, a), values 0 to 255.
|
||||
*/
|
||||
/proc/ReadRGB(rgb)
|
||||
if(!rgb)
|
||||
return
|
||||
|
||||
// interpret the HSV or HSVA value
|
||||
var/i=1,start=1
|
||||
if(text2ascii(rgb) == 35) ++start // skip opening #
|
||||
var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha
|
||||
var/digits=0
|
||||
for(i=start, i<=length(rgb), ++i)
|
||||
ch = text2ascii(rgb, i)
|
||||
if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102)
|
||||
break
|
||||
++digits
|
||||
if(digits == 8)
|
||||
break
|
||||
|
||||
var/single = digits < 6
|
||||
if(digits != 3 && digits != 4 && digits != 6 && digits != 8)
|
||||
return
|
||||
if(digits == 4 || digits == 8)
|
||||
usealpha = 1
|
||||
for(i=start, digits>0, ++i)
|
||||
ch = text2ascii(rgb, i)
|
||||
if(ch >= 48 && ch <= 57)
|
||||
ch -= 48
|
||||
else if(ch >= 65 && ch <= 70)
|
||||
ch -= 55
|
||||
else if(ch >= 97 && ch <= 102)
|
||||
ch -= 87
|
||||
else
|
||||
break
|
||||
--digits
|
||||
switch(which)
|
||||
if(0)
|
||||
r = (r << 4) | ch
|
||||
if(single)
|
||||
r |= r << 4
|
||||
++which
|
||||
else if(!(digits & 1))
|
||||
++which
|
||||
if(1)
|
||||
g = (g << 4) | ch
|
||||
if(single)
|
||||
g |= g << 4
|
||||
++which
|
||||
else if(!(digits & 1))
|
||||
++which
|
||||
if(2)
|
||||
b = (b << 4) | ch
|
||||
if(single)
|
||||
b |= b << 4
|
||||
++which
|
||||
else if(!(digits & 1))
|
||||
++which
|
||||
if(3)
|
||||
alpha = (alpha << 4) | ch
|
||||
if(single)
|
||||
alpha |= alpha << 4
|
||||
|
||||
. = list(r, g, b)
|
||||
if(usealpha)
|
||||
. += alpha
|
||||
|
||||
/proc/ReadHSV(hsv)
|
||||
if(!hsv)
|
||||
return
|
||||
|
||||
// interpret the HSV or HSVA value
|
||||
var/i=1,start=1
|
||||
if(text2ascii(hsv) == 35)
|
||||
++start // skip opening #
|
||||
var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha
|
||||
var/digits=0
|
||||
for(i=start, i<=length(hsv), ++i)
|
||||
ch = text2ascii(hsv, i)
|
||||
if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102)
|
||||
break
|
||||
++digits
|
||||
if(digits == 9)
|
||||
break
|
||||
if(digits > 7)
|
||||
usealpha = 1
|
||||
if(digits <= 4)
|
||||
++which
|
||||
if(digits <= 2)
|
||||
++which
|
||||
for(i=start, digits>0, ++i)
|
||||
ch = text2ascii(hsv, i)
|
||||
if(ch >= 48 && ch <= 57)
|
||||
ch -= 48
|
||||
else if(ch >= 65 && ch <= 70)
|
||||
ch -= 55
|
||||
else if(ch >= 97 && ch <= 102)
|
||||
ch -= 87
|
||||
else
|
||||
break
|
||||
--digits
|
||||
switch(which)
|
||||
if(0)
|
||||
hue = (hue << 4) | ch
|
||||
if(digits == (usealpha ? 6 : 4))
|
||||
++which
|
||||
if(1)
|
||||
sat = (sat << 4) | ch
|
||||
if(digits == (usealpha ? 4 : 2))
|
||||
++which
|
||||
if(2)
|
||||
val = (val << 4) | ch
|
||||
if(digits == (usealpha ? 2 : 0))
|
||||
++which
|
||||
if(3)
|
||||
alpha = (alpha << 4) | ch
|
||||
|
||||
. = list(hue, sat, val)
|
||||
if(usealpha)
|
||||
. += alpha
|
||||
|
||||
/proc/HSVtoRGB(hsv)
|
||||
if(!hsv)
|
||||
return "#000000"
|
||||
var/list/HSV = ReadHSV(hsv)
|
||||
if(!HSV)
|
||||
return "#000000"
|
||||
|
||||
var/hue = HSV[1]
|
||||
var/sat = HSV[2]
|
||||
var/val = HSV[3]
|
||||
|
||||
// Compress hue into easier-to-manage range
|
||||
hue -= hue >> 8
|
||||
if(hue >= 0x5fa)
|
||||
hue -= 0x5fa
|
||||
|
||||
var/hi,mid,lo,r,g,b
|
||||
hi = val
|
||||
lo = round((255 - sat) * val / 255, 1)
|
||||
mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1)
|
||||
if(hue >= 765)
|
||||
if(hue >= 1275) {r=hi; g=lo; b=mid}
|
||||
else if(hue >= 1020) {r=mid; g=lo; b=hi }
|
||||
else {r=lo; g=mid; b=hi }
|
||||
else
|
||||
if(hue >= 510) {r=lo; g=hi; b=mid}
|
||||
else if(hue >= 255) {r=mid; g=hi; b=lo }
|
||||
else {r=hi; g=mid; b=lo }
|
||||
|
||||
return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b)
|
||||
|
||||
/proc/RGBtoHSV(rgb)
|
||||
if(!rgb)
|
||||
return "#0000000"
|
||||
var/list/RGB = ReadRGB(rgb)
|
||||
if(!RGB)
|
||||
return "#0000000"
|
||||
|
||||
var/r = RGB[1]
|
||||
var/g = RGB[2]
|
||||
var/b = RGB[3]
|
||||
var/hi = max(r,g,b)
|
||||
var/lo = min(r,g,b)
|
||||
|
||||
var/val = hi
|
||||
var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0
|
||||
var/hue = 0
|
||||
|
||||
if(sat)
|
||||
var/dir
|
||||
var/mid
|
||||
if(hi == r)
|
||||
if(lo == b) {hue=0; dir=1; mid=g}
|
||||
else {hue=1535; dir=-1; mid=b}
|
||||
else if(hi == g)
|
||||
if(lo == r) {hue=512; dir=1; mid=b}
|
||||
else {hue=511; dir=-1; mid=r}
|
||||
else if(hi == b)
|
||||
if(lo == g) {hue=1024; dir=1; mid=r}
|
||||
else {hue=1023; dir=-1; mid=g}
|
||||
hue += dir * round((mid-lo) * 255 / (hi-lo), 1)
|
||||
|
||||
return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null))
|
||||
|
||||
/proc/hsv(hue, sat, val, alpha)
|
||||
if(hue < 0 || hue >= 1536)
|
||||
hue %= 1536
|
||||
if(hue < 0)
|
||||
hue += 1536
|
||||
if((hue & 0xFF) == 0xFF)
|
||||
++hue
|
||||
if(hue >= 1536)
|
||||
hue = 0
|
||||
if(sat < 0)
|
||||
sat = 0
|
||||
if(sat > 255)
|
||||
sat = 255
|
||||
if(val < 0)
|
||||
val = 0
|
||||
if(val > 255)
|
||||
val = 255
|
||||
. = "#"
|
||||
. += TO_HEX_DIGIT(hue >> 8)
|
||||
. += TO_HEX_DIGIT(hue >> 4)
|
||||
. += TO_HEX_DIGIT(hue)
|
||||
. += TO_HEX_DIGIT(sat >> 4)
|
||||
. += TO_HEX_DIGIT(sat)
|
||||
. += TO_HEX_DIGIT(val >> 4)
|
||||
. += TO_HEX_DIGIT(val)
|
||||
if(!isnull(alpha))
|
||||
if(alpha < 0)
|
||||
alpha = 0
|
||||
if(alpha > 255)
|
||||
alpha = 255
|
||||
. += TO_HEX_DIGIT(alpha >> 4)
|
||||
. += TO_HEX_DIGIT(alpha)
|
||||
|
||||
/*
|
||||
Smooth blend between HSV colors
|
||||
|
||||
amount=0 is the first color
|
||||
amount=1 is the second color
|
||||
amount=0.5 is directly between the two colors
|
||||
|
||||
amount<0 or amount>1 are allowed
|
||||
*/
|
||||
/proc/BlendHSV(hsv1, hsv2, amount)
|
||||
var/list/HSV1 = ReadHSV(hsv1)
|
||||
var/list/HSV2 = ReadHSV(hsv2)
|
||||
|
||||
// add missing alpha if needed
|
||||
if(HSV1.len < HSV2.len)
|
||||
HSV1 += 255
|
||||
else if(HSV2.len < HSV1.len)
|
||||
HSV2 += 255
|
||||
var/usealpha = HSV1.len > 3
|
||||
|
||||
// normalize hsv values in case anything is screwy
|
||||
if(HSV1[1] > 1536)
|
||||
HSV1[1] %= 1536
|
||||
if(HSV2[1] > 1536)
|
||||
HSV2[1] %= 1536
|
||||
if(HSV1[1] < 0)
|
||||
HSV1[1] += 1536
|
||||
if(HSV2[1] < 0)
|
||||
HSV2[1] += 1536
|
||||
if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0}
|
||||
if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0}
|
||||
|
||||
// no value for one color means don't change saturation
|
||||
if(!HSV1[3])
|
||||
HSV1[2] = HSV2[2]
|
||||
if(!HSV2[3])
|
||||
HSV2[2] = HSV1[2]
|
||||
// no saturation for one color means don't change hues
|
||||
if(!HSV1[2])
|
||||
HSV1[1] = HSV2[1]
|
||||
if(!HSV2[2])
|
||||
HSV2[1] = HSV1[1]
|
||||
|
||||
// Compress hues into easier-to-manage range
|
||||
HSV1[1] -= HSV1[1] >> 8
|
||||
HSV2[1] -= HSV2[1] >> 8
|
||||
|
||||
var/hue_diff = HSV2[1] - HSV1[1]
|
||||
if(hue_diff > 765)
|
||||
hue_diff -= 1530
|
||||
else if(hue_diff <= -765)
|
||||
hue_diff += 1530
|
||||
|
||||
var/hue = round(HSV1[1] + hue_diff * amount, 1)
|
||||
var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1)
|
||||
var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1)
|
||||
var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null
|
||||
|
||||
// normalize hue
|
||||
if(hue < 0 || hue >= 1530)
|
||||
hue %= 1530
|
||||
if(hue < 0)
|
||||
hue += 1530
|
||||
// decompress hue
|
||||
hue += round(hue / 255)
|
||||
|
||||
return hsv(hue, sat, val, alpha)
|
||||
|
||||
/proc/BlendRGBasHSV(rgb1, rgb2, amount)
|
||||
return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount)
|
||||
|
||||
/proc/HueToAngle(hue)
|
||||
// normalize hsv in case anything is screwy
|
||||
if(hue < 0 || hue >= 1536)
|
||||
hue %= 1536
|
||||
if(hue < 0)
|
||||
hue += 1536
|
||||
// Compress hue into easier-to-manage range
|
||||
hue -= hue >> 8
|
||||
return hue / (1530/360)
|
||||
|
||||
/proc/AngleToHue(angle)
|
||||
// normalize hsv in case anything is screwy
|
||||
if(angle < 0 || angle >= 360)
|
||||
angle -= 360 * round(angle / 360)
|
||||
var/hue = angle * (1530/360)
|
||||
// Decompress hue
|
||||
hue += round(hue / 255)
|
||||
return hue
|
||||
|
||||
|
||||
// positive angle rotates forward through red->green->blue
|
||||
/proc/RotateHue(hsv, angle)
|
||||
var/list/HSV = ReadHSV(hsv)
|
||||
|
||||
// normalize hsv in case anything is screwy
|
||||
if(HSV[1] >= 1536)
|
||||
HSV[1] %= 1536
|
||||
if(HSV[1] < 0)
|
||||
HSV[1] += 1536
|
||||
|
||||
// Compress hue into easier-to-manage range
|
||||
HSV[1] -= HSV[1] >> 8
|
||||
|
||||
if(angle < 0 || angle >= 360)
|
||||
angle -= 360 * round(angle / 360)
|
||||
HSV[1] = round(HSV[1] + angle * (1530/360), 1)
|
||||
|
||||
// normalize hue
|
||||
if(HSV[1] < 0 || HSV[1] >= 1530)
|
||||
HSV[1] %= 1530
|
||||
if(HSV[1] < 0)
|
||||
HSV[1] += 1530
|
||||
// decompress hue
|
||||
HSV[1] += round(HSV[1] / 255)
|
||||
|
||||
return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null))
|
||||
|
||||
// Convert an rgb color to grayscale
|
||||
/proc/GrayScale(rgb)
|
||||
var/list/RGB = ReadRGB(rgb)
|
||||
var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11
|
||||
return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray)
|
||||
|
||||
// Change grayscale color to black->tone->white range
|
||||
/proc/ColorTone(rgb, tone)
|
||||
var/list/RGB = ReadRGB(rgb)
|
||||
var/list/TONE = ReadRGB(tone)
|
||||
|
||||
var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11
|
||||
var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11
|
||||
|
||||
if(gray <= tone_gray)
|
||||
return BlendRGB("#000000", tone, gray/(tone_gray || 1))
|
||||
else
|
||||
return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1))
|
||||
|
||||
|
||||
//Used in the OLD chem colour mixing algorithm
|
||||
/proc/GetColors(hex)
|
||||
hex = uppertext(hex)
|
||||
// No alpha set? Default to full alpha.
|
||||
if(length(hex) == 7)
|
||||
hex += "FF"
|
||||
var/hi1 = text2ascii(hex, 2) // R
|
||||
var/lo1 = text2ascii(hex, 3) // R
|
||||
var/hi2 = text2ascii(hex, 4) // G
|
||||
var/lo2 = text2ascii(hex, 5) // G
|
||||
var/hi3 = text2ascii(hex, 6) // B
|
||||
var/lo3 = text2ascii(hex, 7) // B
|
||||
var/hi4 = text2ascii(hex, 8) // A
|
||||
var/lo4 = text2ascii(hex, 9) // A
|
||||
return list(((hi1>= 65 ? hi1-55 : hi1-48)<<4) | (lo1 >= 65 ? lo1-55 : lo1-48),
|
||||
((hi2 >= 65 ? hi2-55 : hi2-48)<<4) | (lo2 >= 65 ? lo2-55 : lo2-48),
|
||||
((hi3 >= 65 ? hi3-55 : hi3-48)<<4) | (lo3 >= 65 ? lo3-55 : lo3-48),
|
||||
((hi4 >= 65 ? hi4-55 : hi4-48)<<4) | (lo4 >= 65 ? lo4-55 : lo4-48))
|
||||
|
||||
//Interface for using DrawBox() to draw 1 pixel on a coordinate.
|
||||
//Returns the same icon specifed in the argument, but with the pixel drawn
|
||||
/proc/DrawPixel(icon/I,colour,drawX,drawY)
|
||||
if(!I)
|
||||
return 0
|
||||
|
||||
var/Iwidth = I.Width()
|
||||
var/Iheight = I.Height()
|
||||
|
||||
if(drawX > Iwidth || drawX <= 0)
|
||||
return 0
|
||||
if(drawY > Iheight || drawY <= 0)
|
||||
return 0
|
||||
|
||||
I.DrawBox(colour,drawX, drawY)
|
||||
return I
|
||||
|
||||
|
||||
//Interface for easy drawing of one pixel on an atom.
|
||||
/atom/proc/DrawPixelOn(colour, drawX, drawY)
|
||||
var/icon/I = new(icon)
|
||||
var/icon/J = DrawPixel(I, colour, drawX, drawY)
|
||||
if(J) //Only set the icon if it succeeded, the icon without the pixel is 1000x better than a black square.
|
||||
icon = J
|
||||
return J
|
||||
return 0
|
||||
|
||||
//Hook, override to run code on- wait this is images
|
||||
//Images have dir without being an atom, so they get their own definition.
|
||||
//Lame.
|
||||
/image/proc/setDir(newdir)
|
||||
dir = newdir
|
||||
@@ -1,3 +1,5 @@
|
||||
/* CHOMPStation edit - File overidden. See modular_chomp/code/game/machinery/colormate.dm
|
||||
|
||||
// I'm honestly pretty sure that short of stuffing five million things into this
|
||||
// there's absolutely no way it could ever have any performance impact
|
||||
// Given that all it does is set the color var
|
||||
@@ -115,3 +117,4 @@
|
||||
. = TRUE
|
||||
|
||||
update_icon()
|
||||
/*
|
||||
@@ -85,10 +85,10 @@
|
||||
/proc/is_admin(var/mob/user)
|
||||
return check_rights(R_ADMIN|R_EVENT, 0, user) != 0
|
||||
|
||||
|
||||
/* CHOMPEdit - See modular_chomp/code/_HELPERS/type2type/color.dm
|
||||
/proc/hsl2rgb(h, s, l)
|
||||
return //TODO: Implement
|
||||
|
||||
*/
|
||||
/*
|
||||
Miss Chance
|
||||
*/
|
||||
|
||||
11
modular_chomp/code/_DEFINES/color_priority.dm
Normal file
11
modular_chomp/code/_DEFINES/color_priority.dm
Normal file
@@ -0,0 +1,11 @@
|
||||
//different types of atom colorations
|
||||
///only used by rare effects like greentext coloring mobs and when admins varedit color
|
||||
#define ADMIN_COLOUR_PRIORITY 1
|
||||
///e.g. purple effect of the revenant on a mob, black effect when mob electrocuted
|
||||
#define TEMPORARY_COLOUR_PRIORITY 2
|
||||
///color splashed onto an atom (e.g. paint on turf)
|
||||
#define WASHABLE_COLOUR_PRIORITY 3
|
||||
///color inherent to the atom (e.g. blob color)
|
||||
#define FIXED_COLOUR_PRIORITY 4
|
||||
///how many priority levels there are.
|
||||
#define COLOUR_PRIORITY_AMOUNT 4
|
||||
268
modular_chomp/code/_HELPERS/icons/flatten.dm
Normal file
268
modular_chomp/code/_HELPERS/icons/flatten.dm
Normal file
@@ -0,0 +1,268 @@
|
||||
//? File contains functions to generate flat /icon's from things.
|
||||
//? This is obviously expensive. Very, very expensive.
|
||||
//? new get_flat_icon is faster, but, really, don't use these unless you need to.
|
||||
//? Chances are unless you are:
|
||||
//? - sending to html/browser (for non character preview purposes)
|
||||
//? - taking photos
|
||||
//? - doing complex icon operations that can't be done with filters/overlays
|
||||
//? you probably don't need to use these.
|
||||
|
||||
/**
|
||||
* Generates an icon with all 4 directions of something.
|
||||
*
|
||||
* @params
|
||||
* - A - appearancelike object.
|
||||
* - no_anim - flatten out animations
|
||||
*/
|
||||
/proc/get_compound_icon(atom/A, no_anim)
|
||||
var/mutable_appearance/N = new
|
||||
N.appearance = A
|
||||
N.dir = NORTH
|
||||
var/icon/north = get_flat_icon(N, NORTH, no_anim = no_anim)
|
||||
N.dir = SOUTH
|
||||
var/icon/south = get_flat_icon(N, SOUTH, no_anim = no_anim)
|
||||
N.dir = EAST
|
||||
var/icon/east = get_flat_icon(N, EAST, no_anim = no_anim)
|
||||
N.dir = WEST
|
||||
var/icon/west = get_flat_icon(N, WEST, no_anim = no_anim)
|
||||
qdel(N)
|
||||
//Starts with a blank icon because of byond bugs.
|
||||
var/icon/full = icon('icons/system/blank_32x32.dmi', "")
|
||||
full.Insert(north, dir = NORTH)
|
||||
full.Insert(south, dir = SOUTH)
|
||||
full.Insert(east, dir = EAST)
|
||||
full.Insert(west, dir = WEST)
|
||||
qdel(north)
|
||||
qdel(south)
|
||||
qdel(east)
|
||||
qdel(west)
|
||||
return full
|
||||
|
||||
/proc/get_flat_icon(appearance/appearancelike, dir, no_anim)
|
||||
if(!dir && isloc(appearancelike))
|
||||
dir = appearancelike.dir
|
||||
return _get_flat_icon(appearancelike, dir, no_anim, null, TRUE)
|
||||
|
||||
/proc/_get_flat_icon(image/A, defdir, no_anim, deficon, start)
|
||||
// start with blank image
|
||||
var/static/icon/template = icon('icons/system/blank_32x32.dmi', "")
|
||||
|
||||
#define BLANK icon(template)
|
||||
|
||||
#define INDEX_X_LOW 1
|
||||
#define INDEX_X_HIGH 2
|
||||
#define INDEX_Y_LOW 3
|
||||
#define INDEX_Y_HIGH 4
|
||||
|
||||
#define flatX1 flat_size[INDEX_X_LOW]
|
||||
#define flatX2 flat_size[INDEX_X_HIGH]
|
||||
#define flatY1 flat_size[INDEX_Y_LOW]
|
||||
#define flatY2 flat_size[INDEX_Y_HIGH]
|
||||
#define addX1 add_size[INDEX_X_LOW]
|
||||
#define addX2 add_size[INDEX_X_HIGH]
|
||||
#define addY1 add_size[INDEX_Y_LOW]
|
||||
#define addY2 add_size[INDEX_Y_HIGH]
|
||||
|
||||
// invis? skip.
|
||||
if(!A || A.alpha <= 0)
|
||||
return BLANK
|
||||
|
||||
// detect if state exists
|
||||
var/icon/icon = A.icon || deficon
|
||||
var/state = A.icon_state
|
||||
var/none = !icon
|
||||
if(!none)
|
||||
var/list/states = icon_states(icon)
|
||||
if(!(state in states))
|
||||
if(!("" in states))
|
||||
none = TRUE
|
||||
else
|
||||
state = ""
|
||||
|
||||
// determine if there's directionals
|
||||
// propagate forced direcitons down if and only if A has a direction
|
||||
// todo: this results in a mismatch if someone is facing east but their overlays are facing south.
|
||||
var/dir
|
||||
if(start || !A.dir)
|
||||
dir = defdir
|
||||
else
|
||||
dir = A.dir
|
||||
var/ourdir = dir
|
||||
if(!none && ourdir != SOUTH)
|
||||
if(length(icon_states(icon(icon, state, NORTH))))
|
||||
else if(length(icon_states(icon(icon, state, EAST))))
|
||||
else if(length(icon_states(icon(icon, state, WEST))))
|
||||
else
|
||||
ourdir = SOUTH
|
||||
|
||||
// start generating
|
||||
if(!A.overlays.len && !A.underlays.len)
|
||||
// we don't even have ourselves!
|
||||
if(none)
|
||||
return BLANK
|
||||
// no overlays/underlays, we're done, just mix in ourselves
|
||||
var/icon/self_icon = icon(icon(icon, state, ourdir), "", SOUTH, no_anim? 1 : null)
|
||||
if(A.alpha < 255)
|
||||
self_icon.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
|
||||
if(A.color)
|
||||
if(islist(A.color))
|
||||
self_icon.MapColors(arglist(A.color))
|
||||
else
|
||||
self_icon.Blend(A.color, ICON_MULTIPLY)
|
||||
return self_icon
|
||||
|
||||
// safety/performance check
|
||||
if((A.overlays.len + A.underlays.len) > 80)
|
||||
// we use fucking insertion check
|
||||
// > 80 = death.
|
||||
CRASH("get_flat_icon tried to process more than 80 layers")
|
||||
|
||||
// otherwise, we have to blend in all overlays/underlays.
|
||||
var/icon/flat = BLANK
|
||||
var/list/appearance/gathered = list()
|
||||
var/appearance/copying
|
||||
var/appearance/comparing
|
||||
var/i
|
||||
var/appearance/self
|
||||
var/current_layer
|
||||
|
||||
if(!none)
|
||||
// add the atom itself
|
||||
self = image(icon = icon, icon_state = state, layer = A.layer, dir = ourdir)
|
||||
self.color = A.color
|
||||
self.alpha = A.alpha
|
||||
self.blend_mode = A.blend_mode
|
||||
gathered[self] = A.layer
|
||||
|
||||
// gather
|
||||
for(copying as anything in A.overlays)
|
||||
// todo: better handling
|
||||
if(copying.plane != FLOAT_PLANE && copying.plane != A.plane)
|
||||
// we don't care probably HUD or something lol
|
||||
continue
|
||||
current_layer = copying.layer
|
||||
// if it's float layer, shove it right above atom.
|
||||
if(current_layer < 0)
|
||||
if(current_layer < -1000)
|
||||
CRASH("who the hell is using -1000 or below on float layers?")
|
||||
current_layer = A.layer + (1000 + current_layer) / 1000
|
||||
// else, add 1 so it doesn't potentially collide on float
|
||||
else
|
||||
++current_layer
|
||||
|
||||
// inject with insertion sort
|
||||
for(i in 1 to gathered.len)
|
||||
comparing = gathered[i]
|
||||
if(current_layer < gathered[comparing])
|
||||
gathered.Insert(i, copying)
|
||||
// associate
|
||||
gathered[copying] = current_layer
|
||||
|
||||
for(copying as anything in A.underlays)
|
||||
// todo: better handling
|
||||
if(copying.plane != FLOAT_PLANE && copying.plane != A.plane)
|
||||
// we don't care probably HUD or something lol
|
||||
continue
|
||||
current_layer = copying.layer
|
||||
// if it's float layer, shove it right below atom.
|
||||
if(current_layer < 0)
|
||||
if(current_layer < -1000)
|
||||
CRASH("who the hell is using -1000 or below on float layers?")
|
||||
current_layer = A.layer - (1000 + current_layer) / 1000
|
||||
// else, subtract 1 so it doesn't potentially collide on float
|
||||
else
|
||||
--current_layer
|
||||
|
||||
// inject with insertion sort
|
||||
for(i in 1 to gathered.len)
|
||||
comparing = gathered[i]
|
||||
if(current_layer < gathered[comparing])
|
||||
gathered.Insert(i, copying)
|
||||
// associate
|
||||
gathered[copying] = current_layer
|
||||
|
||||
// adding icon we're mixing in
|
||||
var/icon/adding
|
||||
// current dimensions
|
||||
var/list/flat_size = list(1, flat.Width(), 1, flat.Height())
|
||||
// adding dimensions
|
||||
var/list/add_size[4]
|
||||
// blend mode
|
||||
var/blend_mode
|
||||
|
||||
// blend in layers
|
||||
for(copying as anything in gathered)
|
||||
// if invis, skip
|
||||
if(copying.alpha == 0)
|
||||
continue
|
||||
|
||||
// detect if it's literally ourselves
|
||||
if(copying == self)
|
||||
// blend in normally (no sense doing otherwise unless we're on map)
|
||||
// we can't assume we're on map.
|
||||
blend_mode = BLEND_OVERLAY
|
||||
adding = icon(icon, state, ourdir)
|
||||
else
|
||||
// use full get_flat_icon
|
||||
blend_mode = copying.blend_mode
|
||||
adding = _get_flat_icon(copying, defdir, no_anim, icon)
|
||||
|
||||
// if we got nothing, skip
|
||||
if(!adding)
|
||||
continue
|
||||
|
||||
// detect adding size, taking into account copying overlay's pixel offsets
|
||||
add_size[INDEX_X_LOW] = min(flatX1, copying.pixel_x + 1)
|
||||
add_size[INDEX_X_HIGH] = max(flatX2, copying.pixel_x + adding.Width())
|
||||
add_size[INDEX_Y_LOW] = min(flatY1, copying.pixel_y + 1)
|
||||
add_size[INDEX_Y_HIGH] = max(flatY2, copying.pixel_y + adding.Height())
|
||||
|
||||
// resize flat to fit if necessary
|
||||
if(flat_size ~! add_size)
|
||||
flat.Crop(
|
||||
addX1 - flatX1 + 1,
|
||||
addY1 - flatY1 + 1,
|
||||
addX2 - flatX1 + 1,
|
||||
addY2 - flatY1 + 1
|
||||
)
|
||||
flat_size = add_size.Copy()
|
||||
|
||||
// blend the overlay/underlay in
|
||||
flat.Blend(adding, blendMode2iconMode(blend_mode), copying.pixel_x + 2 - flatX1, copying.pixel_y + 2 - flatY1)
|
||||
|
||||
// apply colors
|
||||
if(A.color)
|
||||
if(islist(A.color))
|
||||
flat.MapColors(arglist(A.color))
|
||||
else
|
||||
flat.Blend(A.color, ICON_MULTIPLY)
|
||||
|
||||
// apply alpha
|
||||
if(A.alpha < 255)
|
||||
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
|
||||
|
||||
// finalize
|
||||
if(no_anim)
|
||||
// clean up frames
|
||||
var/icon/cleaned = icon()
|
||||
cleaned.Insert(flat, "", SOUTH, 1, 0)
|
||||
return cleaned
|
||||
else
|
||||
// just return flat as SOUTH
|
||||
return icon(flat, "", SOUTH)
|
||||
|
||||
#undef flatX1
|
||||
#undef flatX2
|
||||
#undef flatY1
|
||||
#undef flatY2
|
||||
#undef addX1
|
||||
#undef addX2
|
||||
#undef addY1
|
||||
#undef addY2
|
||||
|
||||
#undef INDEX_X_LOW
|
||||
#undef INDEX_X_HIGH
|
||||
#undef INDEX_Y_LOW
|
||||
#undef INDEX_Y_HIGH
|
||||
|
||||
#undef BLANK
|
||||
172
modular_chomp/code/_HELPERS/type2type/color.dm
Normal file
172
modular_chomp/code/_HELPERS/type2type/color.dm
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Convert RBG to HSL
|
||||
*/
|
||||
/proc/rgb2hsl(red, green, blue)
|
||||
red /= 255
|
||||
green /= 255
|
||||
blue /= 255
|
||||
|
||||
var/max = max(red, green, blue)
|
||||
var/min = min(red, green, blue)
|
||||
var/range = max - min
|
||||
|
||||
var/hue = 0
|
||||
var/saturation = 0
|
||||
var/lightness = 0
|
||||
|
||||
lightness = (max + min) / 2
|
||||
if(range != 0)
|
||||
if(lightness < 0.5)
|
||||
saturation = range / (max + min)
|
||||
else
|
||||
saturation = range / (2 - max - min)
|
||||
|
||||
var/dred = ((max - red) / (6 * max)) + 0.5
|
||||
var/dgreen = ((max - green) / (6 * max)) + 0.5
|
||||
var/dblue = ((max - blue) / (6 * max)) + 0.5
|
||||
|
||||
if(max == red)
|
||||
hue = dblue - dgreen
|
||||
else if(max == green)
|
||||
hue = dred - dblue + (1 / 3)
|
||||
else
|
||||
hue = dgreen - dred + (2 / 3)
|
||||
if(hue < 0)
|
||||
hue++
|
||||
else if(hue > 1)
|
||||
hue--
|
||||
|
||||
return list(hue, saturation, lightness)
|
||||
/**
|
||||
* Convert HSL to RGB
|
||||
*/
|
||||
/proc/hsl2rgb(hue, saturation, lightness)
|
||||
var/red
|
||||
var/green
|
||||
var/blue
|
||||
|
||||
if(saturation == 0)
|
||||
red = lightness * 255
|
||||
green = red
|
||||
blue = red
|
||||
else
|
||||
var/a;var/b;
|
||||
if(lightness < 0.5)
|
||||
b = lightness * (1 + saturation)
|
||||
else
|
||||
b = (lightness + saturation) - (saturation * lightness)
|
||||
a = 2 * lightness - b
|
||||
|
||||
red = round(255 * hue2rgb(a, b, hue + (1/3)), 1)
|
||||
green = round(255 * hue2rgb(a, b, hue), 1)
|
||||
blue = round(255 * hue2rgb(a, b, hue - (1/3)), 1)
|
||||
|
||||
return list(red, green, blue)
|
||||
|
||||
/**
|
||||
* Convert hue to RGB
|
||||
*/
|
||||
/proc/hue2rgb(a, b, hue)
|
||||
if(hue < 0)
|
||||
hue++
|
||||
else if(hue > 1)
|
||||
hue--
|
||||
if(6*hue < 1)
|
||||
return (a + (b - a) * 6 * hue)
|
||||
if(2*hue < 1)
|
||||
return b
|
||||
if(3*hue < 2)
|
||||
return (a + (b - a) * ((2 / 3) - hue) * 6)
|
||||
return a
|
||||
|
||||
/**
|
||||
* Convert Kelvin to RGB
|
||||
*
|
||||
* Adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
*/
|
||||
/proc/heat2colour(temp)
|
||||
return rgb(
|
||||
heat2colour_r(temp),
|
||||
heat2colour_g(temp),
|
||||
heat2colour_b(temp),
|
||||
)
|
||||
|
||||
/**
|
||||
* Convert Kelvin for the Red channel
|
||||
*/
|
||||
/proc/heat2colour_r(temp)
|
||||
temp /= 100
|
||||
if(temp <= 66)
|
||||
. = 255
|
||||
else
|
||||
. = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592))
|
||||
|
||||
/**
|
||||
* Convert Kelvin for the Green channel
|
||||
*/
|
||||
/proc/heat2colour_g(temp)
|
||||
temp /= 100
|
||||
if(temp <= 66)
|
||||
. = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661))
|
||||
else
|
||||
. = max(0, min(255, 288.1221685293 * ((temp - 60) ** -0.075148492)))
|
||||
|
||||
/**
|
||||
* Convert Kelvin for the Blue channel
|
||||
*/
|
||||
/proc/heat2colour_b(temp)
|
||||
temp /= 100
|
||||
if(temp >= 66)
|
||||
. = 255
|
||||
else
|
||||
if(temp <= 16)
|
||||
. = 0
|
||||
else
|
||||
. = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307))
|
||||
|
||||
/**
|
||||
* Assumes format #RRGGBB #rrggbb
|
||||
*/
|
||||
/proc/color_hex2num(A)
|
||||
if(!A || length(A) != length_char(A))
|
||||
return 0
|
||||
var/R = hex2num(copytext(A, 2, 4))
|
||||
var/G = hex2num(copytext(A, 4, 6))
|
||||
var/B = hex2num(copytext(A, 6, 8))
|
||||
return R+G+B
|
||||
|
||||
/**
|
||||
*! Word of warning:
|
||||
* Using a matrix like this as a color value will simplify it back to a string after being set.
|
||||
*/
|
||||
/proc/color_hex2color_matrix(string)
|
||||
var/length = length(string)
|
||||
if((length != 7 && length != 9) || length != length_char(string))
|
||||
return color_matrix_identity()
|
||||
var/r = hex2num(copytext(string, 2, 4)) / 255
|
||||
var/g = hex2num(copytext(string, 4, 6)) / 255
|
||||
var/b = hex2num(copytext(string, 6, 8)) / 255
|
||||
var/a = 1
|
||||
if(length == 9)
|
||||
a = hex2num(copytext(string, 8, 10)) / 255
|
||||
if(!isnum(r) || !isnum(g) || !isnum(b) || !isnum(a))
|
||||
return color_matrix_identity()
|
||||
return list(
|
||||
r,0,0,0,0,
|
||||
g,0,0,0,0,
|
||||
b,0,0,0,0,
|
||||
a,0,0,0,0,
|
||||
)
|
||||
|
||||
/**
|
||||
* Will drop all values not on the diagonal.
|
||||
*/
|
||||
/proc/color_matrix2color_hex(list/the_matrix)
|
||||
if(!istype(the_matrix) || the_matrix.len != 20)
|
||||
return "#ffffffff"
|
||||
return rgb(
|
||||
the_matrix[1] * 255, // R
|
||||
the_matrix[6] * 255, // G
|
||||
the_matrix[11] * 255, // B
|
||||
the_matrix[16] * 255, // A
|
||||
)
|
||||
84
modular_chomp/code/datums/browser/color_matrix_picker.dm
Normal file
84
modular_chomp/code/datums/browser/color_matrix_picker.dm
Normal file
@@ -0,0 +1,84 @@
|
||||
/datum/browser/modal/color_matrix_picker
|
||||
var/color_matrix
|
||||
|
||||
/datum/browser/modal/color_matrix_picker/New(mob/user, message, title, button1 = "Ok", button2, button3, stealfocus = TRUE, timeout = 0, list/values)
|
||||
if(!user)
|
||||
return
|
||||
if(!values)
|
||||
values = list(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
if(values.len < 12)
|
||||
values.len = 12
|
||||
var/list/output = list()
|
||||
output += "<form><input type='hidden' name='src' value='[REF(src)]'>"
|
||||
output += "[message]"
|
||||
#define MATRIX_FIELD(field, default) "<b><label for='[##field]'>[##field]</label></b> <input type='number' step='0.001' name='[field]' value='[default]'>"
|
||||
output += "<br><br>"
|
||||
output += MATRIX_FIELD("rr", values[1])
|
||||
output += MATRIX_FIELD("gr", values[4])
|
||||
output += MATRIX_FIELD("br", values[7])
|
||||
output += "<br><br>"
|
||||
output += MATRIX_FIELD("rg", values[2])
|
||||
output += MATRIX_FIELD("gg", values[5])
|
||||
output += MATRIX_FIELD("bg", values[8])
|
||||
output += "<br><br>"
|
||||
output += MATRIX_FIELD("rb", values[3])
|
||||
output += MATRIX_FIELD("gb", values[6])
|
||||
output += MATRIX_FIELD("bb", values[9])
|
||||
output += "<br><br>"
|
||||
output += MATRIX_FIELD("cr", values[10])
|
||||
output += MATRIX_FIELD("cg", values[11])
|
||||
output += MATRIX_FIELD("cb", values[12])
|
||||
output += "<br><br>"
|
||||
#undef MATRIX_FIELD
|
||||
|
||||
output += {"</ul><div style="text-align:center">
|
||||
<button type="submit" name="button" value="1" style="font-size:large;float:[( button2 ? "left" : "right" )]">[button1]</button>"}
|
||||
|
||||
if (button2)
|
||||
output += {"<button type="submit" name="button" value="2" style="font-size:large;[( button3 ? "" : "float:right" )]">[button2]</button>"}
|
||||
|
||||
if (button3)
|
||||
output += {"<button type="submit" name="button" value="3" style="font-size:large;float:right">[button3]</button>"}
|
||||
output += {"</form></div>"}
|
||||
|
||||
..(user, ckey("[user]-[message]-[title]-[world.time]-[rand(1,10000)]"), title, 800, 400, src, stealfocus, timeout)
|
||||
set_content(output.Join(""))
|
||||
|
||||
/datum/browser/modal/color_matrix_picker/Topic(href, list/href_list)
|
||||
if(href_list["close"] || !user)
|
||||
opentime = 0
|
||||
return
|
||||
if(href_list["button"])
|
||||
var/button = text2num(href_list["button"])
|
||||
if(ISINRANGE(button, 1, 3))
|
||||
selectedbutton = button
|
||||
var/list/cm = rgb_construct_color_matrix(
|
||||
text2num(href_list["rr"]),
|
||||
text2num(href_list["rg"]),
|
||||
text2num(href_list["rb"]),
|
||||
text2num(href_list["gr"]),
|
||||
text2num(href_list["gg"]),
|
||||
text2num(href_list["gb"]),
|
||||
text2num(href_list["br"]),
|
||||
text2num(href_list["bg"]),
|
||||
text2num(href_list["bb"]),
|
||||
text2num(href_list["cr"]),
|
||||
text2num(href_list["cg"]),
|
||||
text2num(href_list["cb"])
|
||||
)
|
||||
if(cm)
|
||||
color_matrix = cm
|
||||
opentime = 0
|
||||
close()
|
||||
|
||||
/proc/color_matrix_picker(mob/user, message, title, button1 = "Ok", button2, button3, stealfocus, timeout = 10 MINUTES, list/values)
|
||||
if(!istype(user))
|
||||
if(istype(user, /client))
|
||||
var/client/C = user
|
||||
user = C.mob
|
||||
else
|
||||
return
|
||||
var/datum/browser/modal/color_matrix_picker/B = new(user, message, title, button1, button2, button3, stealfocus, timeout, values)
|
||||
B.open()
|
||||
B.wait()
|
||||
return list("button" = B.selectedbutton, "matrix" = B.color_matrix)
|
||||
55
modular_chomp/code/game/atoms/atoms.dm
Normal file
55
modular_chomp/code/game/atoms/atoms.dm
Normal file
@@ -0,0 +1,55 @@
|
||||
/atom
|
||||
//! Colors
|
||||
/**
|
||||
* used to store the different colors on an atom
|
||||
*
|
||||
* its inherent color, the colored paint applied on it, special color effect etc...
|
||||
*/
|
||||
var/list/atom_colours
|
||||
|
||||
//! ## Atom Colour Priority System
|
||||
/**
|
||||
* A System that gives finer control over which atom colour to colour the atom with.
|
||||
* The "highest priority" one is always displayed as opposed to the default of
|
||||
* "whichever was set last is displayed"
|
||||
*/
|
||||
|
||||
/// Adds an instance of colour_type to the atom's atom_colours list
|
||||
/atom/proc/add_atom_colour(coloration, colour_priority)
|
||||
if(!atom_colours || !atom_colours.len)
|
||||
atom_colours = list()
|
||||
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
|
||||
if(!coloration)
|
||||
return
|
||||
if(colour_priority > atom_colours.len)
|
||||
return
|
||||
atom_colours[colour_priority] = coloration
|
||||
update_atom_colour()
|
||||
|
||||
/// Removes an instance of colour_type from the atom's atom_colours list
|
||||
/atom/proc/remove_atom_colour(colour_priority, coloration)
|
||||
if(!atom_colours)
|
||||
atom_colours = list()
|
||||
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
|
||||
if(colour_priority > atom_colours.len)
|
||||
return
|
||||
if(coloration && atom_colours[colour_priority] != coloration)
|
||||
return //if we don't have the expected color (for a specific priority) to remove, do nothing
|
||||
atom_colours[colour_priority] = null
|
||||
update_atom_colour()
|
||||
|
||||
/// Resets the atom's color to null, and then sets it to the highest priority colour available
|
||||
/atom/proc/update_atom_colour()
|
||||
if(!atom_colours)
|
||||
atom_colours = list()
|
||||
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
|
||||
color = null
|
||||
for(var/C in atom_colours)
|
||||
if(islist(C))
|
||||
var/list/L = C
|
||||
if(L.len)
|
||||
color = L
|
||||
return
|
||||
else if(C)
|
||||
color = C
|
||||
return
|
||||
304
modular_chomp/code/game/machinery/colormate.dm
Normal file
304
modular_chomp/code/game/machinery/colormate.dm
Normal file
@@ -0,0 +1,304 @@
|
||||
#define COLORMATE_TINT 1
|
||||
#define COLORMATE_HSV 2
|
||||
#define COLORMATE_MATRIX 3
|
||||
|
||||
/obj/machinery/gear_painter
|
||||
name = "Color Mate"
|
||||
desc = "A machine to give your apparel a fresh new color!"
|
||||
icon = 'icons/obj/vending.dmi'
|
||||
icon_state = "colormate"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
var/atom/movable/inserted
|
||||
var/activecolor = "#FFFFFF"
|
||||
var/list/color_matrix_last
|
||||
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
|
||||
var/minimum_normal_lightness = 50
|
||||
/// Minimum lightness for matrix mode, tested using 4 test colors of full red, green, blue, white.
|
||||
var/minimum_matrix_lightness = 75
|
||||
/// Minimum matrix tests that must pass for something to be considered a valid color (see above)
|
||||
var/minimum_matrix_tests = 2
|
||||
/// Temporary messages
|
||||
var/temp
|
||||
|
||||
var/list/allowed_types = list(
|
||||
/obj/item/clothing,
|
||||
/obj/item/weapon/storage/backpack,
|
||||
/obj/item/weapon/storage/belt,
|
||||
/obj/item/toy
|
||||
)
|
||||
|
||||
/obj/machinery/gear_painter/Initialize(mapload)
|
||||
. = ..()
|
||||
color_matrix_last = list(
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1,
|
||||
0, 0, 0,
|
||||
)
|
||||
|
||||
/obj/machinery/gear_painter/update_icon()
|
||||
if(panel_open)
|
||||
icon_state = "colormate_open"
|
||||
else if(inoperable())
|
||||
icon_state = "colormate_off"
|
||||
else if(inserted)
|
||||
icon_state = "colormate_active"
|
||||
else
|
||||
icon_state = "colormate"
|
||||
|
||||
/obj/machinery/gear_painter/Destroy()
|
||||
if(inserted) //please i beg you do not drop nulls
|
||||
inserted.forceMove(drop_location())
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gear_painter/attackby(obj/item/I, mob/living/user)
|
||||
if(inserted)
|
||||
to_chat(user, SPAN_WARNING("The machine is already loaded."))
|
||||
return
|
||||
if(default_deconstruction_screwdriver(user, I))
|
||||
return
|
||||
if(default_deconstruction_crowbar(user, I))
|
||||
return
|
||||
if(default_unfasten_wrench(user, I, 40))
|
||||
return
|
||||
|
||||
if(allow_mobs && istype(I, /obj/item/weapon/holder))
|
||||
var/obj/item/holder/H = I
|
||||
var/mob/victim = H.held_mob
|
||||
if(!user.attempt_insert_item_for_installation(I, src))
|
||||
return
|
||||
if(!QDELETED(H))
|
||||
H.drop_items()
|
||||
|
||||
insert_mob(victim, user)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
if(is_type_in_list(I, allowed_types) && !inoperable())
|
||||
if(!user.attempt_insert_item_for_installation(I, src))
|
||||
return
|
||||
if(QDELETED(I))
|
||||
return
|
||||
user.visible_message(SPAN_NOTICE("[user] inserts [I] into [src]'s receptable."))
|
||||
|
||||
inserted = I
|
||||
update_icon()
|
||||
SStgui.update_uis(src)
|
||||
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gear_painter/proc/insert_mob(mob/victim, mob/user)
|
||||
if(inserted)
|
||||
return
|
||||
if(user)
|
||||
visible_message(SPAN_WARNING("[user] stuffs [victim] into [src]!"))
|
||||
inserted = victim
|
||||
inserted.forceMove(src)
|
||||
|
||||
/obj/machinery/gear_painter/AllowDrop()
|
||||
return FALSE
|
||||
|
||||
// /obj/machinery/gear_painter/handle_atom_del(atom/movable/AM)
|
||||
// if(AM == inserted)
|
||||
// inserted = null
|
||||
// return ..()
|
||||
|
||||
/obj/machinery/gear_painter/AltClick(mob/user)
|
||||
. = ..()
|
||||
drop_item()
|
||||
|
||||
/obj/machinery/gear_painter/proc/drop_item()
|
||||
if(!oview(1,src))
|
||||
return
|
||||
if(!inserted)
|
||||
return
|
||||
to_chat(usr, SPAN_NOTICE("You remove [inserted] from [src]"))
|
||||
inserted.forceMove(drop_location())
|
||||
var/mob/living/user = usr
|
||||
if(istype(user))
|
||||
user.put_in_hands(inserted)
|
||||
inserted = null
|
||||
update_icon()
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/machinery/gear_painter/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ColorMate", src.name)
|
||||
ui.set_autoupdate(FALSE) //This might be a bit intensive, better to not update it every few ticks
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/gear_painter/ui_data(mob/user)
|
||||
. = list()
|
||||
.["activemode"] = active_mode
|
||||
.["matrixcolors"] = list(
|
||||
"rr" = color_matrix_last[1],
|
||||
"rg" = color_matrix_last[2],
|
||||
"rb" = color_matrix_last[3],
|
||||
"gr" = color_matrix_last[4],
|
||||
"gg" = color_matrix_last[5],
|
||||
"gb" = color_matrix_last[6],
|
||||
"br" = color_matrix_last[7],
|
||||
"bg" = color_matrix_last[8],
|
||||
"bb" = color_matrix_last[9],
|
||||
"cr" = color_matrix_last[10],
|
||||
"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)
|
||||
.["item"] = list()
|
||||
.["item"]["name"] = inserted.name
|
||||
.["item"]["sprite"] = icon2base64(get_flat_icon(inserted,dir=SOUTH,no_anim=TRUE))
|
||||
.["item"]["preview"] = icon2base64(build_preview())
|
||||
else
|
||||
.["item"] = null
|
||||
|
||||
/obj/machinery/gear_painter/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(inserted)
|
||||
switch(action)
|
||||
if("switch_modes")
|
||||
active_mode = text2num(params["mode"])
|
||||
return TRUE
|
||||
if("choose_color")
|
||||
var/chosen_color = input(usr, "Choose a color: ", "ColorMate colour picking", activecolor) as color|null
|
||||
if(chosen_color)
|
||||
activecolor = chosen_color
|
||||
return TRUE
|
||||
if("paint")
|
||||
do_paint(usr)
|
||||
temp = "Painted Successfully!"
|
||||
return TRUE
|
||||
if("drop")
|
||||
temp = ""
|
||||
drop_item()
|
||||
return TRUE
|
||||
if("clear")
|
||||
inserted.remove_atom_colour(FIXED_COLOUR_PRIORITY)
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
temp = "Cleared Successfully!"
|
||||
return TRUE
|
||||
if("set_matrix_color")
|
||||
color_matrix_last[params["color"]] = params["value"]
|
||||
return TRUE
|
||||
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, SPAN_NOTICE("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
|
||||
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 get_flat_icon(inserted, dir=SOUTH, no_anim=TRUE)
|
||||
|
||||
if(COLORMATE_TINT)
|
||||
if(!check_valid_color(activecolor, usr))
|
||||
return get_flat_icon(inserted, dir=SOUTH, no_anim=TRUE)
|
||||
|
||||
if(COLORMATE_HSV)
|
||||
cm = color_matrix_hsv(build_hue, build_sat, build_val)
|
||||
color_matrix_last = cm
|
||||
if(!check_valid_color(cm, usr))
|
||||
return get_flat_icon(inserted, dir=SOUTH, no_anim=TRUE)
|
||||
|
||||
var/cur_color = inserted.color
|
||||
inserted.color = null
|
||||
inserted.color = (active_mode == COLORMATE_TINT ? activecolor : cm)
|
||||
var/icon/preview = get_flat_icon(inserted, dir=SOUTH, no_anim=TRUE)
|
||||
inserted.color = cur_color
|
||||
temp = ""
|
||||
|
||||
. = 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 too dark (Minimum lightness: [minimum_normal_lightness])"
|
||||
return FALSE
|
||||
return TRUE
|
||||
else // matrix
|
||||
// We test using full red, green, blue, and white
|
||||
// A predefined number of them must pass to be considered valid
|
||||
var/passed = 0
|
||||
#define COLORTEST(thestring, thematrix) passed += (ReadHSV(RGBtoHSV(RGBMatrixTransform(thestring, thematrix)))[3] >= minimum_matrix_lightness)
|
||||
COLORTEST("FF0000", cm)
|
||||
COLORTEST("00FF00", cm)
|
||||
COLORTEST("0000FF", cm)
|
||||
COLORTEST("FFFFFF", cm)
|
||||
#undef COLORTEST
|
||||
if(passed < minimum_matrix_tests)
|
||||
temp = "Matrix is too dark. (passed [passed] out of [minimum_matrix_tests] required tests. Minimum lightness: [minimum_matrix_lightness])."
|
||||
return FALSE
|
||||
return TRUE
|
||||
240
modular_chomp/code/matrices/color_matrix.dm
Normal file
240
modular_chomp/code/matrices/color_matrix.dm
Normal file
@@ -0,0 +1,240 @@
|
||||
/////////////////////
|
||||
// COLOUR MATRICES //
|
||||
/////////////////////
|
||||
|
||||
/* Documenting a couple of potentially useful color matrices here to inspire ideas.
|
||||
// Greyscale - indentical to saturation @ 0
|
||||
list(LUMA_R,LUMA_R,LUMA_R,0, LUMA_G,LUMA_G,LUMA_G,0, LUMA_B,LUMA_B,LUMA_B,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
// Color inversion
|
||||
list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0)
|
||||
|
||||
// Sepiatone
|
||||
list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0)
|
||||
*/
|
||||
|
||||
/// Does nothing.
|
||||
/proc/color_matrix_identity()
|
||||
return list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/**
|
||||
* Adds/subtracts overall lightness.
|
||||
* 0 is identity, 1 makes everything white, -1 makes everything black.
|
||||
*/
|
||||
/proc/color_matrix_lightness(power)
|
||||
return list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, power,power,power,0)
|
||||
|
||||
/**
|
||||
* Changes distance hues have from grey while maintaining the overall lightness. Greys are unaffected.
|
||||
* 1 is identity, 0 is greyscale, >1 oversaturates colors.
|
||||
*/
|
||||
/proc/color_matrix_saturation(value)
|
||||
var/inv = 1 - value
|
||||
var/R = round(LUMA_R * inv, 0.001)
|
||||
var/G = round(LUMA_G * inv, 0.001)
|
||||
var/B = round(LUMA_B * inv, 0.001)
|
||||
|
||||
return list(R + value,R,R,0, G,G + value,G,0, B,B,B + value,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/**
|
||||
* Exxagerates or removes colors.
|
||||
*/
|
||||
/proc/color_matrix_saturation_percent(percent)
|
||||
if(percent == 0)
|
||||
return color_matrix_identity()
|
||||
percent = clamp(percent, -100, 100)
|
||||
if(percent > 0)
|
||||
percent *= 3
|
||||
var/x = 1 + percent / 100
|
||||
var/inv = 1 - x
|
||||
var/R = LUMA_R * inv
|
||||
var/G = LUMA_G * inv
|
||||
var/B = LUMA_B * inv
|
||||
|
||||
return list(R + x,R,R, G,G + x,G, B,B,B + x)
|
||||
|
||||
/**
|
||||
* Greyscale matrix.
|
||||
*/
|
||||
/proc/color_matrix_greyscale()
|
||||
return list(LUMA_R, LUMA_R, LUMA_R, LUMA_G, LUMA_G, LUMA_G, LUMA_B, LUMA_B, LUMA_B)
|
||||
|
||||
/**
|
||||
* Changes distance colors have from rgb(127,127,127) grey.
|
||||
* 1 is identity. 0 makes everything grey >1 blows out colors and greys.
|
||||
*/
|
||||
/proc/color_matrix_contrast(value)
|
||||
var/add = (1 - value) / 2
|
||||
return list(value,0,0,0, 0,value,0,0, 0,0,value,0, 0,0,0,1, add,add,add,0)
|
||||
|
||||
/**
|
||||
* Exxagerates or removes brightness.
|
||||
*/
|
||||
/proc/color_matrix_contrast_percent(percent)
|
||||
var/static/list/delta_index = list(
|
||||
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
|
||||
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
|
||||
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
|
||||
0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
|
||||
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
|
||||
1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
|
||||
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
|
||||
2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
|
||||
4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
|
||||
7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
|
||||
10.0)
|
||||
percent = clamp(percent, -100, 100)
|
||||
if(percent == 0)
|
||||
return color_matrix_identity()
|
||||
|
||||
var/x = 0
|
||||
if (percent < 0)
|
||||
x = 127 + percent / 100 * 127;
|
||||
else
|
||||
x = percent % 1
|
||||
if(x == 0)
|
||||
x = delta_index[percent]
|
||||
else
|
||||
x = delta_index[percent] * (1-x) + delta_index[percent+1] * x//use linear interpolation for more granularity.
|
||||
x = x * 127 + 127
|
||||
|
||||
var/mult = x / 127
|
||||
var/add = 0.5 * (127-x) / 255
|
||||
return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add)
|
||||
|
||||
/**
|
||||
* Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting greys.
|
||||
* 0 is identity, 120 moves reds to greens, 240 moves reds to blues.
|
||||
*/
|
||||
//
|
||||
//
|
||||
/proc/color_matrix_rotate_hue(angle)
|
||||
var/sin = sin(angle)
|
||||
var/cos = cos(angle)
|
||||
var/cos_inv_third = 0.333*(1-cos)
|
||||
var/sqrt3_sin = sqrt(3)*sin
|
||||
return list(
|
||||
round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), 0,
|
||||
round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), 0,
|
||||
round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), 0,
|
||||
0,0,0,1,
|
||||
0,0,0,0,
|
||||
)
|
||||
|
||||
/**
|
||||
* Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites.
|
||||
* TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone)
|
||||
*/
|
||||
/proc/color_matrix_rotation(angle)
|
||||
if(angle == 0)
|
||||
return color_matrix_identity()
|
||||
angle = clamp(angle, -180, 180)
|
||||
var/cos = cos(angle)
|
||||
var/sin = sin(angle)
|
||||
|
||||
var/constA = 0.143
|
||||
var/constB = 0.140
|
||||
var/constC = -0.283
|
||||
return list(
|
||||
LUMA_R + cos * (1-LUMA_R) + sin * -LUMA_R, LUMA_R + cos * -LUMA_R + sin * constA, LUMA_R + cos * -LUMA_R + sin * -(1-LUMA_R),
|
||||
LUMA_G + cos * -LUMA_G + sin * -LUMA_G, LUMA_G + cos * (1-LUMA_G) + sin * constB, LUMA_G + cos * -LUMA_G + sin * LUMA_G,
|
||||
LUMA_B + cos * -LUMA_B + sin * (1-LUMA_B), LUMA_B + cos * -LUMA_B + sin * constC, LUMA_B + cos * (1-LUMA_B) + sin * LUMA_B
|
||||
)
|
||||
|
||||
/**
|
||||
* These next three rotate values about one axis only.
|
||||
* x is the red axis, y is the green axis, z is the blue axis.
|
||||
*/
|
||||
/proc/color_matrix_rotate_x(angle)
|
||||
var/sinval = round(sin(angle), 0.001)
|
||||
var/cosval = round(cos(angle), 0.001)
|
||||
return list(1,0,0,0, 0,cosval,sinval,0, 0,-sinval,cosval,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/proc/color_matrix_rotate_y(angle)
|
||||
var/sinval = round(sin(angle), 0.001)
|
||||
var/cosval = round(cos(angle), 0.001)
|
||||
return list(cosval,0,-sinval,0, 0,1,0,0, sinval,0,cosval,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/proc/color_matrix_rotate_z(angle)
|
||||
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)
|
||||
if(!istype(A) || !istype(B))
|
||||
return color_matrix_identity()
|
||||
if(A.len != 20 || B.len != 20)
|
||||
return color_matrix_identity()
|
||||
var/list/output = list()
|
||||
output.len = 20
|
||||
for(var/value in 1 to 20)
|
||||
output[value] = A[value] + B[value]
|
||||
return output
|
||||
|
||||
/**
|
||||
* Returns a matrix multiplication of A with B.
|
||||
*/
|
||||
/proc/color_matrix_multiply(list/A, list/B)
|
||||
if(!istype(A) || !istype(B))
|
||||
return color_matrix_identity()
|
||||
if(A.len != 20 || B.len != 20)
|
||||
return color_matrix_identity()
|
||||
var/list/output = list()
|
||||
output.len = 20
|
||||
var/x = 1
|
||||
var/y = 1
|
||||
var/offset = 0
|
||||
for(y in 1 to 5)
|
||||
offset = (y-1)*4
|
||||
for(x in 1 to 4)
|
||||
output[offset+x] = round(A[offset+1]*B[x] + A[offset+2]*B[x+4] + A[offset+3]*B[x+8] + A[offset+4]*B[x+12]+(y==5?B[x+16]:0), 0.001)
|
||||
return output
|
||||
|
||||
/**
|
||||
* Assembles a color matrix, defaulting to identity.
|
||||
*/
|
||||
/proc/rgb_construct_color_matrix(rr = 1, rg, rb, gr, gg = 1, gb, br, bg, bb = 1, cr, cg, cb)
|
||||
return list(rr, rg, rb, gr, gg, gb, br, bg, bb, cr, cg, cb)
|
||||
|
||||
/**
|
||||
* Assembles a color matrix, defaulting to identity.
|
||||
*/
|
||||
/proc/rgba_construct_color_matrix(rr = 1, rg, rb, ra, gr, gg = 1, gb, ga, br, bg, bb = 1, ba, ar, ag, ab, aa = 1, cr, cg, cb, ca)
|
||||
return list(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, cr, cg, cb, ca)
|
||||
|
||||
/**
|
||||
* Constructs a colored greyscale matrix.
|
||||
* WARNING: Bad math up ahead.
|
||||
*/
|
||||
/proc/rgba_auto_greyscale_matrix(rgba_string)
|
||||
// process rgb(a)
|
||||
var/list/L1 = ReadRGB(rgba_string)
|
||||
ASSERT(L1.len)
|
||||
if(L1.len == 3)
|
||||
return rgba_construct_color_matrix(0.39, 0.39, 0.39, 0, 0.5, 0.5, 0.5, 0, 0.11, 0.11, 0.11, 0, 0, 0, 0, 1, max(-0.5, (L1[1] - 255) / 255), max(-0.5, (L1[2] - 255) / 255), max(-0.5, (L1[3] - 255) / 255), 0)
|
||||
else
|
||||
// alpha
|
||||
return rgba_construct_color_matrix(0.39, 0.39, 0.39, 0, 0.5, 0.5, 0.5, 0, 0.11, 0.11, 0.11, 0, 0, 0, 0, 0, max(-0.5, (L1[1] - 255) / 255), max(-0.5, (L1[2] - 255) / 255), max(-0.5, (L1[3] - 255) / 255), L1[4] / 255)
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "code\__defines\items_clothing.dm"
|
||||
#include "code\__defines\lighting.dm"
|
||||
#include "code\__defines\lighting_vr.dm"
|
||||
#include "code\__defines\lum_ch.dm"
|
||||
#include "code\__defines\machinery.dm"
|
||||
#include "code\__defines\map.dm"
|
||||
#include "code\__defines\materials.dm"
|
||||
@@ -139,6 +140,7 @@
|
||||
#include "code\_helpers\global_lists_ch.dm"
|
||||
#include "code\_helpers\global_lists_vr.dm"
|
||||
#include "code\_helpers\icons.dm"
|
||||
#include "code\_helpers\icons_ch.dm"
|
||||
#include "code\_helpers\icons_vr.dm"
|
||||
#include "code\_helpers\lighting.dm"
|
||||
#include "code\_helpers\logging.dm"
|
||||
@@ -899,7 +901,6 @@
|
||||
#include "code\game\machinery\OpTable.dm"
|
||||
#include "code\game\machinery\overview.dm"
|
||||
#include "code\game\machinery\oxygen_pump.dm"
|
||||
#include "code\game\machinery\painter_vr.dm"
|
||||
#include "code\game\machinery\partslathe_vr.dm"
|
||||
#include "code\game\machinery\pda_multicaster.dm"
|
||||
#include "code\game\machinery\pointdefense.dm"
|
||||
@@ -4521,19 +4522,25 @@
|
||||
#include "maps\~map_system\maps.dm"
|
||||
#include "modular_chomp\code\coalesce_ch.dm"
|
||||
#include "modular_chomp\code\global.dm"
|
||||
#include "modular_chomp\code\_DEFINES\color_priority.dm"
|
||||
#include "modular_chomp\code\_HELPERS\icons\flatten.dm"
|
||||
#include "modular_chomp\code\_HELPERS\type2type\color.dm"
|
||||
#include "modular_chomp\code\ATMOSPHERICS\atmospherics.dm"
|
||||
#include "modular_chomp\code\datums\autolathe\arms.dm"
|
||||
#include "modular_chomp\code\datums\autolathe\engineering_ch.dm"
|
||||
#include "modular_chomp\code\datums\autolathe\general_ch.dm"
|
||||
#include "modular_chomp\code\datums\browser\color_matrix_picker.dm"
|
||||
#include "modular_chomp\code\datums\components\gargoyle.dm"
|
||||
#include "modular_chomp\code\datums\outfits\jobs\noncrew.dm"
|
||||
#include "modular_chomp\code\datums\supplypacks\medical.dm"
|
||||
#include "modular_chomp\code\game\atoms\atoms.dm"
|
||||
#include "modular_chomp\code\game\dna\dna2.dm"
|
||||
#include "modular_chomp\code\game\jobs\job\department.dm"
|
||||
#include "modular_chomp\code\game\jobs\job\noncrew.dm"
|
||||
#include "modular_chomp\code\game\jobs\job\silicon.dm"
|
||||
#include "modular_chomp\code\game\machinery\airconditioner_ch.dm"
|
||||
#include "modular_chomp\code\game\machinery\autolathe_armory.dm"
|
||||
#include "modular_chomp\code\game\machinery\colormate.dm"
|
||||
#include "modular_chomp\code\game\objects\items\clockwork\ratvarian_spear.dm"
|
||||
#include "modular_chomp\code\game\objects\structures\desert_planet_structures.dm"
|
||||
#include "modular_chomp\code\game\objects\structures\gargoyle.dm"
|
||||
@@ -4541,6 +4548,7 @@
|
||||
#include "modular_chomp\code\game\objects\structures\watercloset_ch.dm"
|
||||
#include "modular_chomp\code\game\objects\structures\crate_lockers\largecrate.dm"
|
||||
#include "modular_chomp\code\game\turfs\simulated\outdoors\desert_planet.dm"
|
||||
#include "modular_chomp\code\matrices\color_matrix.dm"
|
||||
#include "modular_chomp\code\modules\admin\functions\modify_traits.dm"
|
||||
#include "modular_chomp\code\modules\artifice\deadringer.dm"
|
||||
#include "modular_chomp\code\modules\client\preferences.dm"
|
||||
|
||||
Reference in New Issue
Block a user