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:
DeityLink
2023-12-11 00:53:03 +01:00
committed by GitHub
parent 3ec956fbca
commit 0f9180bf19
22 changed files with 659 additions and 101 deletions

View File

@@ -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));

View File

@@ -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

View File

@@ -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;