mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
The Paint & Linen Update [Splinter 4]: Painting Brush, Palette, Canvas, and Paintings changes (#35530)
* Soft Reset * fix issues * radium fluff * :agony:
This commit is contained in:
@@ -365,15 +365,16 @@ function initCanvas(paintInitData, canvasInitData) {
|
||||
|
||||
var paletteButtonPanel = document.getElementById("palette_buttons");
|
||||
var palette = canvasInitData.palette;
|
||||
var nano_palette = canvasInitData.nano_palette;
|
||||
while (paletteButtonPanel.childElementCount > 0) {
|
||||
paletteButtonPanel.removeChild(paletteButtonPanel.firstChild);
|
||||
}
|
||||
|
||||
for (color in palette) {
|
||||
paletteButtonPanel.innerHTML +=
|
||||
'<div class="paletteColor" onclick="setColor(\'' + palette[color] + '\');" style="background-image:' + generateColorPaletteBackgroundStyle(palette[color]) + '; background-image:' + generateColorPaletteBackgroundStyle(palette[color], true) + '"></div>\n';
|
||||
'<div class="paletteColor" onclick="setColor(\'' + palette[color] + '\',\'' + nano_palette[color] + '\');" style="background-image:' + generateColorPaletteBackgroundStyle(palette[color]) + '; background-image:' + generateColorPaletteBackgroundStyle(palette[color], true) + '; border:3px solid ' + nano_palette[color] + '"></div>\n';
|
||||
}
|
||||
setColor(palette[0]);
|
||||
setColor(palette[0],nano_palette[0]);
|
||||
|
||||
//no errors initializing canvas stuff thus far, hide the error message
|
||||
document.getElementById("canvas-error").style.display = "none";
|
||||
@@ -397,9 +398,24 @@ function generateColorPaletteBackgroundStyle (color, ieMode) {
|
||||
}
|
||||
}
|
||||
|
||||
function setColor(color){
|
||||
function setColor(color,nano){
|
||||
setPaintColor(color);
|
||||
switch(nano) {
|
||||
case "#161616":
|
||||
setNanoPaint(0);
|
||||
break;
|
||||
case "#999999":
|
||||
setNanoPaint(1);
|
||||
break;
|
||||
default:
|
||||
setNanoPaint(2);
|
||||
}
|
||||
updateSelectedColorDisplay(color, getOpacity())
|
||||
|
||||
//telling the game that we're changing color so that the player's painting brush actually changes its current color.
|
||||
var content = "newcolor=" + encodeURIComponent(color) + ";";
|
||||
content += "nanopaint=" + encodeURIComponent(nanopaint);
|
||||
HREFmultipartHandler(src, content);
|
||||
}
|
||||
|
||||
function updateSelectedColorDisplay (color, alpha) {
|
||||
@@ -443,6 +459,7 @@ const MAX_DESCRIPTION_LENGTH = 1024;
|
||||
|
||||
function submitData() {
|
||||
var content = "bitmap=" + encodeURIComponent(bitmap) + ";";
|
||||
content += "nanomap=" + encodeURIComponent(nanomap) + ";";
|
||||
content += "author=" + encodeURIComponent(document.getElementById("author").value.slice(0, MAX_AUTHOR_LENGTH)) + ";";
|
||||
content += "title=" + encodeURIComponent(document.getElementById("title").value.slice(0, MAX_TITLE_LENGTH)) + ";";
|
||||
content += "description=" + encodeURIComponent(document.getElementById("description").value.slice(0, MAX_DESCRIPTION_LENGTH));
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
var/min_strength = 0
|
||||
var/max_strength = 1
|
||||
var/list/palette = list() // List of colors that will be made available while painting
|
||||
var/list/nano_palette = list()
|
||||
var/base_color
|
||||
var/nano_paint = FALSE
|
||||
var/polarized = FALSE
|
||||
var/list/components = list()
|
||||
|
||||
/datum/painting_utensil/New(mob/user, obj/item/held_item)
|
||||
if (!user) // Special case
|
||||
@@ -27,10 +31,13 @@
|
||||
max_strength = PENCIL_STRENGTH_MAX
|
||||
min_strength = PENCIL_STRENGTH_MIN
|
||||
palette += p.colour_rgb
|
||||
nano_palette += "#161616"
|
||||
base_color = p.colour_rgb
|
||||
components = list("pen ink")
|
||||
|
||||
// Painting with a crayon
|
||||
if (istype(held_item, /obj/item/toy/crayon))
|
||||
components = list("crayon")
|
||||
for (var/obj/item/weapon/storage/fancy/crayons/box in user.held_items)
|
||||
for (var/crayon in box)
|
||||
var/obj/item/toy/crayon/c = crayon
|
||||
@@ -44,25 +51,39 @@
|
||||
max_strength = PENCIL_STRENGTH_MAX
|
||||
min_strength = PENCIL_STRENGTH_MIN
|
||||
palette += c.mainColour
|
||||
nano_palette += "#161616"
|
||||
palette += c.shadeColour
|
||||
nano_palette += "#161616"
|
||||
base_color = c.mainColour
|
||||
|
||||
// Painting with hair dye sprays
|
||||
if (istype(held_item, /obj/item/weapon/hair_dye))
|
||||
components = list("hair dye")
|
||||
var/obj/item/weapon/hair_dye/h = held_item
|
||||
max_strength = PENCIL_STRENGTH_MAX
|
||||
min_strength = PENCIL_STRENGTH_MIN
|
||||
palette += rgb(h.color_r, h.color_g, h.color_b)
|
||||
nano_palette += "#161616"
|
||||
base_color = rgb(h.color_r, h.color_g, h.color_b)
|
||||
|
||||
// Painting with a brush
|
||||
if (istype(held_item, /obj/item/weapon/painting_brush))
|
||||
if (istype(held_item, /obj/item/painting_brush))
|
||||
// If holding a palette (item) add it's colors to the brush's list
|
||||
for (var/obj/item/weapon/palette/pal in user.held_items)
|
||||
|
||||
var/obj/item/painting_brush/b = held_item
|
||||
components = b.component.Copy()
|
||||
|
||||
for (var/obj/item/palette/pal in user.held_items)
|
||||
for (var/c in pal.stored_colours)
|
||||
palette += pal.stored_colours[c]
|
||||
switch(pal.nanopaint_indexes[c])
|
||||
if (PAINTLIGHT_NONE)
|
||||
nano_palette += "#161616"
|
||||
if (PAINTLIGHT_LIMITED)
|
||||
nano_palette += "#999999"
|
||||
if (PAINTLIGHT_FULL)
|
||||
nano_palette += "#FFFFFF"
|
||||
|
||||
var/obj/item/weapon/painting_brush/b = held_item
|
||||
if (b.paint_color)
|
||||
max_strength = BRUSH_STRENGTH_MAX
|
||||
min_strength = BRUSH_STRENGTH_MIN
|
||||
@@ -70,7 +91,26 @@
|
||||
// so make sure we're not adding it again to the list
|
||||
if (!(b.paint_color in palette))
|
||||
palette += b.paint_color
|
||||
switch(b.nano_paint)
|
||||
if (PAINTLIGHT_NONE)
|
||||
nano_palette += "#161616"
|
||||
if (PAINTLIGHT_LIMITED)
|
||||
nano_palette += "#999999"
|
||||
if (PAINTLIGHT_FULL)
|
||||
nano_palette += "#FFFFFF"
|
||||
base_color = b.paint_color
|
||||
nano_paint = b.nano_paint
|
||||
|
||||
// Wearing polarized glasses
|
||||
polarized = FALSE
|
||||
if (ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if (istype(H.glasses, /obj/item/clothing/glasses/sunglasses/polarized))
|
||||
polarized = TRUE
|
||||
else if (ismonkey(user))
|
||||
var/mob/living/carbon/monkey/M = user
|
||||
if (istype(M.glasses, /obj/item/clothing/glasses/sunglasses/polarized))
|
||||
polarized = TRUE
|
||||
|
||||
// Normalize palette colors
|
||||
for (var/i = 1; i < palette.len; i++)
|
||||
@@ -89,7 +129,10 @@
|
||||
dupe.max_strength = src.max_strength
|
||||
dupe.min_strength = src.min_strength
|
||||
dupe.palette = src.palette
|
||||
dupe.nano_palette = src.nano_palette
|
||||
dupe.base_color = src.base_color
|
||||
dupe.nano_paint = src.nano_paint
|
||||
dupe.components = src.components.Copy()
|
||||
dupe.tag = "\ref[dupe]"
|
||||
return dupe
|
||||
|
||||
@@ -110,6 +153,10 @@
|
||||
var/bitmap_width = 14
|
||||
var/bitmap_height = 14
|
||||
|
||||
// Secondary luminous bitmap for when working with nano-paint
|
||||
var/list/nanomap = list()
|
||||
var/has_nano_paint = FALSE
|
||||
|
||||
// Color that shows up on creation or after cleaning
|
||||
var/base_color = "#ffffff"
|
||||
|
||||
@@ -128,6 +175,8 @@
|
||||
|
||||
var/copy = 0
|
||||
|
||||
var/list/components = list()
|
||||
|
||||
/datum/custom_painting/New(parent, bitmap_width, bitmap_height, offset_x=0, offset_y=0, base_color=src.base_color)
|
||||
src.parent = parent
|
||||
src.bitmap_width = bitmap_width
|
||||
@@ -158,18 +207,55 @@
|
||||
copy.title = title
|
||||
copy.description = description
|
||||
copy.bitmap = bitmap.Copy()
|
||||
copy.nanomap = nanomap.Copy()
|
||||
copy.components = components.Copy()
|
||||
return copy
|
||||
|
||||
/datum/custom_painting/proc/set_parent(parent)
|
||||
src.parent = parent
|
||||
mp_handler.set_parent(parent)
|
||||
|
||||
/datum/custom_painting/proc/bucket_fill(var/color)
|
||||
/datum/custom_painting/proc/smear(var/amount=50, var/strength=1)
|
||||
var/list/list_of_numbers = list()
|
||||
var/list/pixels_to_shift = list()
|
||||
|
||||
for (var/i = 1 to bitmap.len)
|
||||
list_of_numbers += "[i]"
|
||||
for (var/i = 1 to amount)
|
||||
var/num = pick(list_of_numbers)
|
||||
var/actual_num = text2num(num)
|
||||
list_of_numbers -= num
|
||||
pixels_to_shift[num] = list(bitmap[actual_num], nanomap[actual_num])//saving pixel data and location before smearing
|
||||
|
||||
for (var/index in pixels_to_shift)
|
||||
var/list/colors_to_move = pixels_to_shift[index]
|
||||
var/actual_index = text2num(index)
|
||||
var/new_index = actual_index
|
||||
for (var/i = 1 to strength)
|
||||
new_index = actual_index + bitmap_width
|
||||
if (new_index > bitmap.len)
|
||||
new_index -= bitmap.len
|
||||
colors_to_move = list(base_color, "#000000")
|
||||
bitmap[new_index] = colors_to_move[1]
|
||||
nanomap[new_index] = colors_to_move[2]
|
||||
|
||||
/datum/custom_painting/proc/bucket_fill(var/color,var/nanopaint=PAINTLIGHT_NONE)
|
||||
bitmap = list()
|
||||
for (var/i = 0, i < bitmap_height * bitmap_width, i++)
|
||||
bitmap += color
|
||||
nanomap = list()
|
||||
if (nanopaint != PAINTLIGHT_NONE)
|
||||
for (var/i = 0, i < bitmap_height * bitmap_width, i++)
|
||||
bitmap += color
|
||||
if (uppertext(color) == "#FFFFFF")
|
||||
color = "#FEFEFE"//workaround because white nano paint doesn't glow up for some reason
|
||||
nanomap += color
|
||||
else
|
||||
for (var/i = 0, i < bitmap_height * bitmap_width, i++)
|
||||
bitmap += color
|
||||
nanomap += "#000000"
|
||||
|
||||
|
||||
/datum/custom_painting/proc/blank_contents()
|
||||
components = list()
|
||||
bucket_fill(base_color)
|
||||
|
||||
/datum/custom_painting/proc/is_blank()
|
||||
@@ -179,6 +265,9 @@
|
||||
for (var/b in bitmap)
|
||||
if (b != base_color)
|
||||
return FALSE
|
||||
for (var/b in nanomap)
|
||||
if (b != "#000000")
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
@@ -211,13 +300,17 @@
|
||||
"width" = bitmap_width,
|
||||
"height" = bitmap_height,
|
||||
"bitmap" = bitmap,
|
||||
"nanomap" = nanomap,
|
||||
"nanopaint" = p.nano_paint,
|
||||
"polarized" = p.polarized,
|
||||
"minPaintStrength" = p.min_strength,
|
||||
"maxPaintStrength" = p.max_strength
|
||||
"maxPaintStrength" = p.max_strength,
|
||||
))
|
||||
|
||||
var/canvas_init_inputs = json_encode(list(
|
||||
"src" = "\ref[parent]",
|
||||
"palette" = p.palette, //list("#000000", "#ffffff", "#ff0000", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff"),
|
||||
"nano_palette" = p.nano_palette,
|
||||
"title" = title,
|
||||
"author" = author,
|
||||
"description" = description
|
||||
@@ -244,6 +337,26 @@
|
||||
mp_handler.Topic(href, href_list)
|
||||
return
|
||||
|
||||
// Change painting brush color
|
||||
else if (href_list["newcolor"])
|
||||
var/mob/user = usr
|
||||
var/obj/item/held_item = user.get_active_hand()
|
||||
if (istype(held_item,/obj/item/painting_brush))
|
||||
var/obj/item/painting_brush/PB = held_item
|
||||
PB.paint_color = href_list["newcolor"]
|
||||
PB.nano_paint = text2num(href_list["nanopaint"])
|
||||
for (var/obj/item/palette/pal in user.held_items)
|
||||
for (var/c in pal.stored_colours)
|
||||
var/found = FALSE
|
||||
if (pal.stored_colours[c] == PB.paint_color)
|
||||
var/list/pal_comp = pal.components[c]
|
||||
PB.component = pal_comp.Copy()
|
||||
found = TRUE
|
||||
break
|
||||
if (!found)
|
||||
PB.component = PB.component_alt.Copy()//using the original brush color that isn't on the palette
|
||||
PB.update_icon()
|
||||
|
||||
// Save changes
|
||||
else
|
||||
// Make sure the player can actually paint
|
||||
@@ -258,10 +371,15 @@
|
||||
if (!do_after(usr, parent, 30))
|
||||
return
|
||||
|
||||
components |= pu.components//This won't get us all the components if the player changed c
|
||||
|
||||
//Save and sanitize bitmap
|
||||
bitmap = splittext(url_decode(href_list["bitmap"]), ",")
|
||||
for (var/i = 1; i <= bitmap.len; i++)
|
||||
bitmap[i] = sanitize_hexcolor(bitmap[i])
|
||||
nanomap = splittext(url_decode(href_list["nanomap"]), ",")
|
||||
for (var/i = 1; i <= nanomap.len; i++)
|
||||
nanomap[i] = sanitize_hexcolor(nanomap[i])
|
||||
|
||||
//Save and sanitize author, title and description
|
||||
author = copytext(sanitize(url_decode(href_list["author"])), 1, MAX_NAME_LEN)
|
||||
@@ -274,6 +392,8 @@
|
||||
var/turf/simulated/floor/F = parent
|
||||
F.render_advanced_graffiti(src, usr)
|
||||
|
||||
playsound(usr.loc, get_sfx("mop"), 10, 1)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/custom_painting/proc/render_on(icon/ico, offset_x = src.offset_x, offset_y = src.offset_y)
|
||||
@@ -291,6 +411,57 @@
|
||||
|
||||
return ico
|
||||
|
||||
/datum/custom_painting/proc/render_nanomap(icon/ico, offset_x = src.offset_x, offset_y = src.offset_y)
|
||||
var/x
|
||||
var/y
|
||||
has_nano_paint = FALSE
|
||||
for (var/pixel = 0; pixel < nanomap.len; pixel++)
|
||||
x = pixel % bitmap_width
|
||||
y = (pixel - x)/bitmap_width
|
||||
|
||||
//for DrawBox, (x:1,y:1) is the lower left corner. On bitmap, (x:0,y:0) is the upper left
|
||||
x = 1 + offset_x + x
|
||||
y = offset_y + bitmap_height - y
|
||||
|
||||
ico.DrawBox(nanomap[pixel + 1], x, y)
|
||||
|
||||
if (nanomap[pixel + 1] != "#000000")
|
||||
has_nano_paint = TRUE
|
||||
|
||||
return image(ico)
|
||||
|
||||
/datum/custom_painting/proc/get_components()
|
||||
var/mats = ""
|
||||
|
||||
var/is_drawing = TRUE
|
||||
var/list/drawing_implements = list(
|
||||
"crayon",
|
||||
"pen ink",
|
||||
)
|
||||
if (components.len > 0)
|
||||
var/mat = components[1]
|
||||
mats = "\a [mat]"
|
||||
if (!(mat in drawing_implements))
|
||||
is_drawing = FALSE
|
||||
|
||||
for (var/i = 2 to components.len)
|
||||
if (i == components.len)
|
||||
mats += " and "
|
||||
else
|
||||
mats += ", "
|
||||
|
||||
mat = components[i]
|
||||
if (!(mat in drawing_implements))
|
||||
is_drawing = FALSE
|
||||
mats += mat
|
||||
|
||||
if (is_drawing)
|
||||
mats += " drawing"
|
||||
else
|
||||
mats += " painting"
|
||||
|
||||
return mats
|
||||
|
||||
// -- export/import stuff
|
||||
// -- don't we have a serializer for this? :thinking:
|
||||
|
||||
@@ -301,7 +472,9 @@
|
||||
painting.offset_x,
|
||||
painting.offset_y,
|
||||
painting.base_color,
|
||||
painting.bitmap
|
||||
painting.bitmap,
|
||||
painting.nanomap,
|
||||
painting.components,
|
||||
)
|
||||
return json_encode(L)
|
||||
|
||||
@@ -310,6 +483,11 @@
|
||||
var/datum/custom_painting/painting = new(null, L[1], L[2], L[3], L[4], L[5]) // no parents
|
||||
var/list/bitmap_to_copy = L[6]
|
||||
painting.bitmap = bitmap_to_copy.Copy()
|
||||
if (L.len > 6)//post Paint & Linen update
|
||||
var/list/nanomap_to_copy = L[7]
|
||||
painting.nanomap = nanomap_to_copy.Copy()
|
||||
var/list/components_to_copy = L[8]
|
||||
painting.components = components_to_copy.Copy()
|
||||
painting.title = title
|
||||
painting.author = author
|
||||
painting.description = description
|
||||
|
||||
@@ -71,6 +71,10 @@ var ctx;
|
||||
var width;
|
||||
var height;
|
||||
var bitmap;
|
||||
var nanomap;
|
||||
|
||||
var nanopaint = 0;
|
||||
var polarized = false;
|
||||
|
||||
//Keep track of how scaled up the canvas is vs the actual bitmap
|
||||
var scaleX = 20;
|
||||
@@ -108,6 +112,9 @@ function initPaint(initData) {
|
||||
canvas.width = width * scaleX;
|
||||
canvas.height = height * scaleY;
|
||||
bitmap = initData.bitmap;
|
||||
nanomap = initData.nanomap;
|
||||
nanopaint = initData.nanopaint;
|
||||
polarized = initData.polarized;
|
||||
|
||||
minPaintStrength = initData.minPaintStrength;
|
||||
maxPaintStrength = initData.maxPaintStrength;
|
||||
@@ -117,6 +124,7 @@ function initPaint(initData) {
|
||||
if (bitmap.length != width * height) {
|
||||
while (bitmap.length < width * height) {
|
||||
bitmap.push("#ffffff");
|
||||
nanomap.push("#000000");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +144,10 @@ function setPaintColor(color) {
|
||||
paint_color = color;
|
||||
}
|
||||
|
||||
function setNanoPaint(nano) {
|
||||
nanopaint = nano;
|
||||
}
|
||||
|
||||
function getPaintColor() {
|
||||
return paint_color;
|
||||
}
|
||||
@@ -180,7 +192,15 @@ function hexToRgba(hex) {
|
||||
* hex string (eg: #AA88FFAA).
|
||||
* If the alpha component is FF, it will be omitted on the hex
|
||||
*/
|
||||
function rgbaToHex(rgba) {
|
||||
function rgbaToHex(rgba_raw) {
|
||||
|
||||
let rgba = {
|
||||
r: rgba_raw.r,
|
||||
g: rgba_raw.g,
|
||||
b: rgba_raw.b,
|
||||
a: rgba_raw.a,
|
||||
}
|
||||
|
||||
for (k in rgba) {
|
||||
//Convert to hex value
|
||||
rgba[k] = Math.round(rgba[k]).toString(16);
|
||||
@@ -208,16 +228,36 @@ var blendFunction = colorRybBlend;
|
||||
function pixelDraw(x, y, rgba, alpha) {
|
||||
//Figure out the pixel index off the x and y
|
||||
let pixel = y * width + x;
|
||||
let painthex = rgba
|
||||
|
||||
//Convert to numeric values
|
||||
rgba = hexToRgba(rgba);
|
||||
let orgba = hexToRgba(bitmap[pixel]);
|
||||
|
||||
//In case we're using nano paint, let's keep track of the colour we're using
|
||||
let paint = rgba;
|
||||
let nanorba = hexToRgba(nanomap[pixel]);
|
||||
|
||||
//Mix both color values
|
||||
rgba = blendFunction(rgba, orgba, alpha);
|
||||
if (nanopaint > 1)//nano paint is additive. radium blends normally.
|
||||
rgba = colorAdditiveBlend(rgba, orgba, alpha);
|
||||
else
|
||||
rgba = blendFunction(rgba, orgba, alpha);
|
||||
|
||||
//Save result into bitmap
|
||||
bitmap[pixel] = rgbaToHex(rgba);
|
||||
|
||||
//If we're painting with nano paint, we draw on the nanomap as well
|
||||
if (nanopaint > 0) {
|
||||
paint = colorAdditiveBlend(paint, nanorba, alpha);//Nano-Paint is additive
|
||||
if ((painthex == "#FFFFFF") || (painthex == "#ffffff"))
|
||||
nanomap[pixel] = "#FEFEFE";//workaround because Byond
|
||||
else
|
||||
nanomap[pixel] = rgbaToHex(paint);
|
||||
} else {
|
||||
//Otherwise, we erase the nanomap (by drawing on it in black)
|
||||
nanomap[pixel] = rgbaToHex(blendFunction(hexToRgba("#000000"), hexToRgba(nanomap[pixel]), alpha));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,6 +280,17 @@ function colorMultiplyBlend(c1, c2, alpha) {
|
||||
return colorAlphaBlend(result, c2, alpha);
|
||||
}
|
||||
|
||||
//c1 is the new color, c2 is the canvas
|
||||
function colorAdditiveBlend(c1, c2, alpha) {
|
||||
var result = {};
|
||||
for (k in c1)
|
||||
if (c2[k] >= c1[k])
|
||||
result[k] = c2[k]
|
||||
else
|
||||
result[k] = Math.round(c2[k] + (c1[k] - c2[k]) * alpha);
|
||||
return result;
|
||||
}
|
||||
|
||||
function colorScreenBlend(c1, c2, alpha) {
|
||||
var result = {};
|
||||
for (k in c1)
|
||||
@@ -471,7 +522,11 @@ function display_bitmap() {
|
||||
var pixel = (y * width + x);
|
||||
|
||||
//Grab the pixel's color
|
||||
var color = hexToRgba(bitmap[pixel]);
|
||||
var color;
|
||||
if (polarized)
|
||||
color = hexToRgba(nanomap[pixel]);
|
||||
else
|
||||
color = hexToRgba(bitmap[pixel]);
|
||||
var alpha = color.a/255.0;
|
||||
ctx.globalAlpha = alpha;
|
||||
color.a = 255;
|
||||
|
||||
Reference in New Issue
Block a user