mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-07-15 00:54:16 +01:00
add(colours): Ports matrix/colormate stuff from CHOMP
Original PR: https://github.com/CHOMPStation2/CHOMPStation2/pull/6159 Original Author: https://github.com/BlackMajor
This commit is contained in:
@@ -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
|
||||
@@ -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)
|
||||
var/list/RGB2 = ReadRGB(rgb2)
|
||||
|
||||
// add missing alpha if needed
|
||||
if(RGB1.len < RGB2.len) RGB1 += 255
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,665 @@
|
||||
/*
|
||||
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� 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Porting the icons_ch.dm file straight from chompstation as I do not want to risk breaking stuff.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/* Gives the result RGB of a RGB string after a matrix transformation. No alpha.
|
||||
* Input: rr, rg, rb, gr, gg, gb, br, bg, bb, cr, cg, cb
|
||||
* Output: RGB string
|
||||
*/
|
||||
/proc/RGBMatrixTransform(list/color, list/cm)
|
||||
ASSERT(cm.len >= 9)
|
||||
if(cm.len < 12) // fill in the rest
|
||||
for(var/i in 1 to (12 - cm.len))
|
||||
cm += 0
|
||||
if(!islist(color))
|
||||
color = ReadRGB(color)
|
||||
color[1] = color[1] * cm[1] + color[2] * cm[2] + color[3] * cm[3] + cm[10] * 255
|
||||
color[2] = color[1] * cm[4] + color[2] * cm[5] + color[3] * cm[6] + cm[11] * 255
|
||||
color[3] = color[1] * cm[7] + color[2] * cm[8] + color[3] * cm[9] + cm[12] * 255
|
||||
return rgb(color[1], color[2], color[3])
|
||||
@@ -183,6 +183,88 @@
|
||||
var/hex_to_work_on = copytext(hex,5,7)
|
||||
return hex2num(hex_to_work_on)
|
||||
|
||||
/**
|
||||
* 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 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 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
|
||||
|
||||
// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
/proc/heat2color(temp)
|
||||
return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp))
|
||||
@@ -211,6 +293,53 @@
|
||||
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
|
||||
)
|
||||
|
||||
// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time.
|
||||
// returns "YYYY-MM-DD" by default
|
||||
/proc/unix2date(timestamp, seperator = "-")
|
||||
|
||||
@@ -380,3 +380,88 @@
|
||||
. |= GLOB.bitfields[bitfield][flag]
|
||||
else
|
||||
return
|
||||
|
||||
/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)
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* hey, remember mutable appearance?
|
||||
*
|
||||
* only:
|
||||
* - this isn't a real object rather than a struct
|
||||
* - i'm making a cast for it so we can VV it
|
||||
* - this is also used to cast procs that operate on appearance-like things.
|
||||
*
|
||||
* Sue me, I need to debug things somehow
|
||||
*
|
||||
* DO NOT USE THESE UNLESS YOU KNOW WHAT YOU ARE DOING.
|
||||
*/
|
||||
/appearance
|
||||
var/alpha
|
||||
var/appearance_flags
|
||||
var/blend_mode
|
||||
var/color
|
||||
var/desc
|
||||
var/dir
|
||||
var/gender
|
||||
var/icon
|
||||
var/icon_state
|
||||
var/invisibility
|
||||
var/infra_luminosity
|
||||
var/list/filters
|
||||
var/layer
|
||||
var/luminosity
|
||||
var/maptext
|
||||
var/maptext_width
|
||||
var/maptext_height
|
||||
var/maptext_x
|
||||
var/maptext_y
|
||||
var/mouse_over_pointer
|
||||
var/mouse_drag_pointer
|
||||
var/mouse_drop_pointer
|
||||
var/mouse_drop_zone
|
||||
var/mouse_opacity
|
||||
var/name
|
||||
var/opacity
|
||||
var/list/overlays
|
||||
var/override
|
||||
var/pixel_x
|
||||
var/pixel_y
|
||||
var/pixel_w
|
||||
var/pixel_z
|
||||
var/plane
|
||||
var/render_source
|
||||
var/render_target
|
||||
var/suffix
|
||||
var/text
|
||||
var/transform
|
||||
var/list/underlays
|
||||
// var/vis_flags
|
||||
|
||||
//! vis_flags missing even though byond ref says it's there, fuck off why is this possible
|
||||
|
||||
GLOBAL_REAL_VAR(_appearance_var_list) = list(
|
||||
"alpha",
|
||||
"appearance_flags",
|
||||
"blend_mode",
|
||||
"color",
|
||||
"desc",
|
||||
"dir",
|
||||
"gender",
|
||||
"icon",
|
||||
"icon_state",
|
||||
"invisibility",
|
||||
"infra_luminosity",
|
||||
"filters",
|
||||
"layer",
|
||||
"luminosity",
|
||||
"maptext",
|
||||
"maptext_width",
|
||||
"maptext_height",
|
||||
"maptext_x",
|
||||
"maptext_y",
|
||||
"mouse_over_pointer",
|
||||
"mouse_drag_pointer",
|
||||
"mouse_drop_pointer",
|
||||
"mouse_drop_zone",
|
||||
"mouse_opacity",
|
||||
"name",
|
||||
"opacity",
|
||||
"overlays",
|
||||
"override",
|
||||
"pixel_x",
|
||||
"pixel_y",
|
||||
"pixel_w",
|
||||
"pixel_z",
|
||||
"plane",
|
||||
"render_source",
|
||||
"render_target",
|
||||
"suffix",
|
||||
"text",
|
||||
"transform",
|
||||
"underlays"
|
||||
// "vis_flags"
|
||||
)
|
||||
|
||||
/proc/__appearance_v_debug(appearance/A, name)
|
||||
switch(name)
|
||||
#define DEBUG_APPEARANCE_VAR(n) if(#n) return debug_variable(name, A.n, 0, null)
|
||||
DEBUG_APPEARANCE_VAR(alpha)
|
||||
DEBUG_APPEARANCE_VAR(appearance_flags)
|
||||
DEBUG_APPEARANCE_VAR(blend_mode)
|
||||
DEBUG_APPEARANCE_VAR(color)
|
||||
DEBUG_APPEARANCE_VAR(desc)
|
||||
DEBUG_APPEARANCE_VAR(dir)
|
||||
DEBUG_APPEARANCE_VAR(gender)
|
||||
DEBUG_APPEARANCE_VAR(icon)
|
||||
DEBUG_APPEARANCE_VAR(icon_state)
|
||||
DEBUG_APPEARANCE_VAR(invisibility)
|
||||
DEBUG_APPEARANCE_VAR(infra_luminosity)
|
||||
DEBUG_APPEARANCE_VAR(filters)
|
||||
DEBUG_APPEARANCE_VAR(layer)
|
||||
DEBUG_APPEARANCE_VAR(luminosity)
|
||||
DEBUG_APPEARANCE_VAR(maptext)
|
||||
DEBUG_APPEARANCE_VAR(maptext_width)
|
||||
DEBUG_APPEARANCE_VAR(maptext_height)
|
||||
DEBUG_APPEARANCE_VAR(maptext_x)
|
||||
DEBUG_APPEARANCE_VAR(maptext_y)
|
||||
DEBUG_APPEARANCE_VAR(mouse_over_pointer)
|
||||
DEBUG_APPEARANCE_VAR(mouse_drag_pointer)
|
||||
DEBUG_APPEARANCE_VAR(mouse_drop_pointer)
|
||||
DEBUG_APPEARANCE_VAR(mouse_drop_zone)
|
||||
DEBUG_APPEARANCE_VAR(mouse_opacity)
|
||||
DEBUG_APPEARANCE_VAR(name)
|
||||
DEBUG_APPEARANCE_VAR(opacity)
|
||||
DEBUG_APPEARANCE_VAR(overlays)
|
||||
DEBUG_APPEARANCE_VAR(override)
|
||||
DEBUG_APPEARANCE_VAR(pixel_x)
|
||||
DEBUG_APPEARANCE_VAR(pixel_y)
|
||||
DEBUG_APPEARANCE_VAR(pixel_w)
|
||||
DEBUG_APPEARANCE_VAR(pixel_z)
|
||||
DEBUG_APPEARANCE_VAR(plane)
|
||||
DEBUG_APPEARANCE_VAR(render_source)
|
||||
DEBUG_APPEARANCE_VAR(render_target)
|
||||
DEBUG_APPEARANCE_VAR(suffix)
|
||||
DEBUG_APPEARANCE_VAR(text)
|
||||
DEBUG_APPEARANCE_VAR(transform)
|
||||
DEBUG_APPEARANCE_VAR(underlays)
|
||||
// DEBUG_APPEARANCE_VAR(vis_flags)
|
||||
#undef DEBUG_APPEARANCE_VAR
|
||||
@@ -49,6 +49,13 @@
|
||||
var/chat_color_darkened
|
||||
/// The chat color var, without alpha.
|
||||
var/chat_color_hover
|
||||
//! 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/New(loc, ...)
|
||||
// Don't call ..() unless /datum/New() ever exists
|
||||
@@ -731,3 +738,51 @@
|
||||
// Airflow and ZAS zones now uses CanZASPass() instead of this proc.
|
||||
/atom/proc/CanPass(atom/movable/mover, turf/target)
|
||||
return !density
|
||||
|
||||
|
||||
//! ## 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
|
||||
|
||||
@@ -1,62 +1,85 @@
|
||||
// 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
|
||||
// But just in case it's cursed in some arcane horrible way
|
||||
// I'm going to leave this limit here
|
||||
#define MAX_PROCESSING 10 // Arbitrary performance insurance
|
||||
#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! Recommended to use with white items for best results."
|
||||
desc = "A machine to give your apparel a fresh new color!"
|
||||
icon = 'icons/obj/vending_vr.dmi'
|
||||
icon_state = "colormate"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
var/list/processing = list()
|
||||
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/device/radio/headset
|
||||
)
|
||||
/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(processing.len)
|
||||
else if(inserted)
|
||||
icon_state = "colormate_active"
|
||||
else
|
||||
icon_state = "colormate"
|
||||
|
||||
/obj/machinery/gear_painter/Destroy()
|
||||
for(var/atom/movable/O in processing)
|
||||
O.forceMove(drop_location())
|
||||
processing.Cut()
|
||||
if(inserted) //please i beg you do not drop nulls
|
||||
inserted.forceMove(drop_location())
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gear_painter/attackby(obj/item/W, mob/user)
|
||||
if(LAZYLEN(processing) >= MAX_PROCESSING)
|
||||
to_chat(user, "<span class='warning'>The machine is full.</span>")
|
||||
/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, W))
|
||||
if(default_deconstruction_screwdriver(user, I))
|
||||
return
|
||||
if(default_deconstruction_crowbar(user, W))
|
||||
if(default_deconstruction_crowbar(user, I))
|
||||
return
|
||||
if(default_unfasten_wrench(user, W, 40))
|
||||
if(default_unfasten_wrench(user, I, 40))
|
||||
return
|
||||
|
||||
if(is_type_in_list(W, allowed_types) && !inoperable())
|
||||
user.visible_message("<span class='notice'>[user] inserts \the [W] into the Color Mate receptable.</span>")
|
||||
user.drop_from_inventory(W)
|
||||
W.forceMove(src)
|
||||
processing |= W
|
||||
if(is_type_in_list(I, allowed_types) && !inoperable())
|
||||
user.visible_message("<span class='notice'>[user] inserts \the [I] into the Color Mate receptable.</span>")
|
||||
user.drop_from_inventory(I)
|
||||
I.forceMove(src)
|
||||
inserted = I
|
||||
SStgui.update_uis(src)
|
||||
|
||||
else
|
||||
..()
|
||||
update_icon()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gear_painter/attack_hand(mob/user)
|
||||
if(..())
|
||||
@@ -69,49 +92,209 @@
|
||||
ui = new(user, src, "ColorMate", name)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/gear_painter/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/list/data = ..()
|
||||
/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)
|
||||
|
||||
var/list/items = list()
|
||||
for(var/atom/movable/O in processing)
|
||||
items.Add("[O]")
|
||||
data["items"] = items
|
||||
/obj/machinery/gear_painter/AllowDrop()
|
||||
return FALSE
|
||||
|
||||
data["activecolor"] = activecolor
|
||||
return data
|
||||
// /obj/machinery/gear_painter/handle_atom_del(atom/movable/AM)
|
||||
// if(AM == inserted)
|
||||
// inserted = null
|
||||
// return ..()
|
||||
|
||||
/obj/machinery/gear_painter/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
add_fingerprint(usr)
|
||||
|
||||
switch(action)
|
||||
if("select")
|
||||
var/newcolor = input(usr, "Choose a color.", "", activecolor) as color|null
|
||||
if(newcolor)
|
||||
activecolor = newcolor
|
||||
. = TRUE
|
||||
|
||||
if("paint")
|
||||
for(var/atom/movable/O in processing)
|
||||
O.color = activecolor
|
||||
CHECK_TICK
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
. = TRUE
|
||||
|
||||
if("clear")
|
||||
for(var/atom/movable/O in processing)
|
||||
O.color = initial(O.color)
|
||||
CHECK_TICK
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
. = TRUE
|
||||
|
||||
if("eject")
|
||||
for(var/atom/movable/O in processing)
|
||||
O.forceMove(drop_location())
|
||||
CHECK_TICK
|
||||
processing.Cut()
|
||||
. = TRUE
|
||||
/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/tgui_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/tgui_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/tgui_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
|
||||
|
||||
@@ -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)
|
||||
@@ -40,7 +40,48 @@
|
||||
/datum/gear_tweak/color/tweak_item(var/obj/item/I, var/metadata)
|
||||
if(valid_colors && !(metadata in valid_colors))
|
||||
return
|
||||
I.color = metadata
|
||||
if(!metadata || (metadata == "#ffffff"))
|
||||
return
|
||||
if(istype(I))
|
||||
I.add_atom_colour(metadata, FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
I.color = metadata
|
||||
|
||||
GLOBAL_DATUM_INIT(gear_tweak_free_matrix_recolor, /datum/gear_tweak/matrix_recolor, new)
|
||||
|
||||
/datum/gear_tweak/matrix_recolor
|
||||
|
||||
/datum/gear_tweak/matrix_recolor/get_contents(var/metadata)
|
||||
if(islist(metadata) && length(metadata))
|
||||
return "Matrix Recolor: [english_list(metadata)]"
|
||||
return "Matrix Recolor"
|
||||
|
||||
/datum/gear_tweak/matrix_recolor/get_default()
|
||||
return null
|
||||
|
||||
/datum/gear_tweak/matrix_recolor/get_metadata(user, metadata)
|
||||
var/list/returned = color_matrix_picker(user, "Pick a color matrix for this item", "Matrix Recolor", "Ok", "Erase", "Cancel", TRUE, 10 MINUTES, islist(metadata) && metadata)
|
||||
var/list/L = returned["matrix"]
|
||||
if(returned["button"] == 3)
|
||||
return metadata
|
||||
if((returned["button"] == 2) || !islist(L) || !ISINRANGE(L.len, 9, 20))
|
||||
return list()
|
||||
var/identity = TRUE
|
||||
var/static/list/ones = list(1, 5, 9)
|
||||
for(var/i in 1 to L.len)
|
||||
if(L[i] != ((i in ones)? 1 : 0))
|
||||
identity = FALSE
|
||||
break
|
||||
return identity? list() : L
|
||||
|
||||
/datum/gear_tweak/matrix_recolor/tweak_item(obj/item/I, metadata)
|
||||
. = ..()
|
||||
if(!islist(metadata) || (length(metadata) < 12))
|
||||
return
|
||||
if(istype(I))
|
||||
I.add_atom_colour(metadata, FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
I.color = metadata
|
||||
|
||||
/*
|
||||
* Path adjustment
|
||||
|
||||
@@ -85,10 +85,12 @@
|
||||
/proc/is_admin(var/mob/user)
|
||||
return check_rights(R_ADMIN|R_EVENT, 0, user) != 0
|
||||
|
||||
|
||||
/**
|
||||
* Moved into its own file as part of port from CHOMP.
|
||||
*
|
||||
/proc/hsl2rgb(h, s, l)
|
||||
return //TODO: Implement
|
||||
|
||||
*/
|
||||
/*
|
||||
Miss Chance
|
||||
*/
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 209 B |
@@ -0,0 +1,413 @@
|
||||
import { useBackend } from '../backend';
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
NoticeBox,
|
||||
NumberInput,
|
||||
Section,
|
||||
Slider,
|
||||
Table,
|
||||
Tabs,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const ColorMate = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { activemode, temp } = data;
|
||||
const item = data.item || [];
|
||||
return (
|
||||
<Window width="980" height="720" resizable>
|
||||
<Window.Content overflow="auto">
|
||||
<Section>
|
||||
{temp ? <NoticeBox>{temp}</NoticeBox> : null}
|
||||
{Object.keys(item).length ? (
|
||||
<>
|
||||
<Table>
|
||||
<Table.Cell width="50%">
|
||||
<Section>
|
||||
<center>Item:</center>
|
||||
<img
|
||||
src={'data:image/jpeg;base64, ' + item.sprite}
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{
|
||||
'-ms-interpolation-mode': 'nearest-neighbor',
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Section>
|
||||
<center>Preview:</center>
|
||||
<img
|
||||
src={'data:image/jpeg;base64, ' + item.preview}
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{
|
||||
'-ms-interpolation-mode': 'nearest-neighbor',
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
</Table.Cell>
|
||||
</Table>
|
||||
<Tabs fluid>
|
||||
<Tabs.Tab
|
||||
key="1"
|
||||
selected={activemode === 1}
|
||||
onClick={() =>
|
||||
act('switch_modes', {
|
||||
mode: 1,
|
||||
})
|
||||
}
|
||||
>
|
||||
Tint coloring (Simple)
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="2"
|
||||
selected={activemode === 2}
|
||||
onClick={() =>
|
||||
act('switch_modes', {
|
||||
mode: 2,
|
||||
})
|
||||
}
|
||||
>
|
||||
HSV coloring (Normal)
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="3"
|
||||
selected={activemode === 3}
|
||||
onClick={() =>
|
||||
act('switch_modes', {
|
||||
mode: 3,
|
||||
})
|
||||
}
|
||||
>
|
||||
Matrix coloring (Advanced)
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
<center>Coloring: {item.name}</center>
|
||||
<Table mt={1}>
|
||||
<Table.Cell width="33%">
|
||||
<Button
|
||||
fluid
|
||||
content="Paint"
|
||||
icon="fill"
|
||||
onClick={() => act('paint')}
|
||||
/>
|
||||
<Button
|
||||
fluid
|
||||
content="Clear"
|
||||
icon="eraser"
|
||||
onClick={() => act('clear')}
|
||||
/>
|
||||
<Button
|
||||
fluid
|
||||
content="Eject"
|
||||
icon="eject"
|
||||
onClick={() => act('drop')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell width="66%">
|
||||
{activemode === 1 ? (
|
||||
<ColorMateTint />
|
||||
) : activemode === 2 ? (
|
||||
<ColorMateHSV />
|
||||
) : (
|
||||
<ColorMateMatrix />
|
||||
)}
|
||||
</Table.Cell>
|
||||
</Table>
|
||||
</>
|
||||
) : (
|
||||
<center>No item inserted.</center>
|
||||
)}
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColorMateTint = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
return (
|
||||
<Button
|
||||
fluid
|
||||
content="Select new color"
|
||||
icon="paint-brush"
|
||||
onClick={() => act('choose_color')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColorMateMatrix = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const matrixcolors = data.matrixcolors || [];
|
||||
return (
|
||||
<Table>
|
||||
<Table.Cell>
|
||||
<Table.Row>
|
||||
RR:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.rr}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 1,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
GR:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.gr}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 4,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
BR:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.br}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 7,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Table.Row>
|
||||
RG:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.rg}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 2,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
GG:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.gg}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 5,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
BG:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.bg}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 8,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Table.Row>
|
||||
RB:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.rb}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 3,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
GB:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.gb}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 6,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
BB:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.bb}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 9,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Table.Row>
|
||||
CR:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.cr}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 10,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
CG:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.cg}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 11,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
CB:{' '}
|
||||
<NumberInput
|
||||
width="50px"
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.cb}
|
||||
onChange={(e, value) =>
|
||||
act('set_matrix_color', {
|
||||
color: 12,
|
||||
value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Row>
|
||||
</Table.Cell>
|
||||
<Table.Cell width="40%">
|
||||
<Icon name="question-circle" color="blue" /> RG means red will become
|
||||
this much green.
|
||||
<br />
|
||||
<Icon name="question-circle" color="blue" /> CR means this much red will
|
||||
be added.
|
||||
</Table.Cell>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColorMateHSV = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { buildhue, buildsat, buildval } = data;
|
||||
return (
|
||||
<Table>
|
||||
<Table.Row>
|
||||
<center>Hue:</center>
|
||||
<Table.Cell width="85%">
|
||||
<Slider
|
||||
minValue={0}
|
||||
maxValue={360}
|
||||
step={1}
|
||||
value={buildhue}
|
||||
onDrag={(e, value) =>
|
||||
act('set_hue', {
|
||||
buildhue: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<center>Saturation:</center>
|
||||
<Table.Cell>
|
||||
<Slider
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={buildsat}
|
||||
onDrag={(e, value) =>
|
||||
act('set_sat', {
|
||||
buildsat: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<center>Value:</center>
|
||||
<Table.Cell>
|
||||
<Slider
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={buildval}
|
||||
onDrag={(e, value) =>
|
||||
act('set_val', {
|
||||
buildval: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Flex, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
type Data = {
|
||||
items;
|
||||
activecolor: string;
|
||||
};
|
||||
|
||||
export const ColorMate = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
|
||||
const { items, activecolor } = data;
|
||||
|
||||
let height = Math.min(270 + items.length * 15, 600);
|
||||
|
||||
return (
|
||||
<Window width={300} height={height}>
|
||||
<Window.Content>
|
||||
{(items.length && (
|
||||
<>
|
||||
<Section title="Paint">
|
||||
<Flex justify="center" align="center">
|
||||
<Flex.Item basis="50%">
|
||||
<Box
|
||||
backgroundColor={activecolor}
|
||||
width="120px"
|
||||
height="120px"
|
||||
/>
|
||||
</Flex.Item>
|
||||
<Flex.Item basis="50% ">
|
||||
<Button
|
||||
fluid
|
||||
icon="eye-dropper"
|
||||
onClick={() => act('select')}
|
||||
>
|
||||
Select Color
|
||||
</Button>
|
||||
<Button fluid icon="fill-drip" onClick={() => act('paint')}>
|
||||
Paint Items
|
||||
</Button>
|
||||
<Button fluid icon="tint-slash" onClick={() => act('clear')}>
|
||||
Remove Paintjob
|
||||
</Button>
|
||||
<Button fluid icon="eject" onClick={() => act('eject')}>
|
||||
Eject Items
|
||||
</Button>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
<Section title="Items">
|
||||
{items.map((item, i) => (
|
||||
<Box key={i}>
|
||||
#{i + 1}: {item}
|
||||
</Box>
|
||||
))}
|
||||
</Section>
|
||||
</>
|
||||
)) || (
|
||||
<Section>
|
||||
<Box color="bad">No items inserted.</Box>
|
||||
</Section>
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "code\__defines\chemistry.dm"
|
||||
#include "code\__defines\chemistry_vr.dm"
|
||||
#include "code\__defines\color.dm"
|
||||
#include "code\__defines\color_priority.dm"
|
||||
#include "code\__defines\construction.dm"
|
||||
#include "code\__defines\cooldowns.dm"
|
||||
#include "code\__defines\crafting.dm"
|
||||
@@ -59,6 +60,7 @@
|
||||
#include "code\__defines\lighting.dm"
|
||||
#include "code\__defines\lighting_vr.dm"
|
||||
#include "code\__defines\logging.dm"
|
||||
#include "code\__defines\lum.dm"
|
||||
#include "code\__defines\machinery.dm"
|
||||
#include "code\__defines\map.dm"
|
||||
#include "code\__defines\materials.dm"
|
||||
@@ -137,6 +139,7 @@
|
||||
#include "code\_helpers\global_lists.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"
|
||||
@@ -156,6 +159,7 @@
|
||||
#include "code\_helpers\unsorted_vr.dm"
|
||||
#include "code\_helpers\view.dm"
|
||||
#include "code\_helpers\visual_filters.dm"
|
||||
#include "code\_helpers\icons\flatten.dm"
|
||||
#include "code\_helpers\logging\ui.dm"
|
||||
#include "code\_helpers\sorts\__main.dm"
|
||||
#include "code\_helpers\sorts\comparators.dm"
|
||||
@@ -389,6 +393,7 @@
|
||||
#include "code\datums\helper_datums\teleport.dm"
|
||||
#include "code\datums\helper_datums\teleport_vr.dm"
|
||||
#include "code\datums\helper_datums\topic_input.dm"
|
||||
#include "code\datums\interfaces\appearance.dm"
|
||||
#include "code\datums\locations\locations.dm"
|
||||
#include "code\datums\locations\nyx.dm"
|
||||
#include "code\datums\locations\qerrvallis.dm"
|
||||
@@ -1693,6 +1698,7 @@
|
||||
#include "code\game\turfs\unsimulated\walls.dm"
|
||||
#include "code\js\byjax.dm"
|
||||
#include "code\js\menus.dm"
|
||||
#include "code\matrices\color_matrix.dm"
|
||||
#include "code\modules\admin\admin.dm"
|
||||
#include "code\modules\admin\admin_attack_log.dm"
|
||||
#include "code\modules\admin\admin_investigate.dm"
|
||||
|
||||
Reference in New Issue
Block a user