mirror of
				https://github.com/LDDestroier/CC/
				synced 2025-10-31 07:22:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			842 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			842 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| --[[
 | |
| 
 | |
|  8888888b.     d8888 8888888 888b    888  .d8888b.
 | |
|  888   Y88b   d88888   888   8888b   888 d88P  Y88b
 | |
|  888    888  d88P888   888   88888b  888        888
 | |
|  888   d88P d88P 888   888   888Y88b 888      .d88P
 | |
|  8888888P' d88P  888   888   888 Y88b888  .od888P"
 | |
|  888      d88P   888   888   888  Y88888 d88P"
 | |
|  888     d8888888888   888   888   Y8888 888"
 | |
|  888    d88P     888 8888888 888    Y888 888888888
 | |
| 
 | |
| Download with:
 | |
| 	wget https://github.com/LDDestroier/CC/raw/master/pain2.lua
 | |
| 
 | |
| To-do:
 | |
| 	* Add more tools, such as Fill or Color Picker.
 | |
| 	* Add an actual menu.
 | |
| 	* Add a help screen, and don't make it as bland-looking as PAIN 1's.
 | |
| 	* Add support for every possible image format under the sun.
 | |
| 	* Add the ability to add/remove layers.
 | |
| 
 | |
| --]]
 | |
| 
 | |
| local pain = {
 | |
| 	running = true,	-- if true, will run. otherwise, quit
 | |
| 	layer = 1,		-- current layer selected
 | |
| 	image = {},		-- table of 2D canvases
 | |
| 	manip = {},		-- basic canvas manipulation functions
 | |
| 	timers = {},	-- built-in timer system
 | |
| 	windows = {},	-- various windows drawn to the screen
 | |
| }
 | |
| 
 | |
| keys.ctrl = 256
 | |
| keys.alt = 257
 | |
| keys.shift = 258
 | |
| 
 | |
| local keysDown = {}
 | |
| local miceDown = {}
 | |
| 
 | |
| pain.color = {
 | |
| 	char = " ",
 | |
| 	text = "f",
 | |
| 	back = "0"
 | |
| }
 | |
| 
 | |
| pain.controlHoldCheck = {}	-- used to check if an input has just been used or not
 | |
| pain.control = {
 | |
| 	quit = {
 | |
| 		key = keys.q,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {
 | |
| 			[keys.leftCtrl] = true
 | |
| 		},
 | |
| 	},
 | |
| 	scrollUp = {
 | |
| 		key = keys.up,
 | |
| 		holdDown = true,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	scrollDown = {
 | |
| 		key = keys.down,
 | |
| 		holdDown = true,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	scrollLeft = {
 | |
| 		key = keys.left,
 | |
| 		holdDown = true,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	scrollRight = {
 | |
| 		key = keys.right,
 | |
| 		holdDown = true,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	singleScroll = {
 | |
| 		key = keys.tab,
 | |
| 		holdDown = true,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	resetScroll = {
 | |
| 		key = keys.a,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	cancelTool = {
 | |
| 		key = keys.space,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	nextTextColor = {
 | |
| 		key = keys.rightBracket,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {
 | |
| 			[keys.shift] = true
 | |
| 		},
 | |
| 	},
 | |
| 	prevTextColor = {
 | |
| 		key = keys.leftBracket,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {
 | |
| 			[keys.shift] = true
 | |
| 		},
 | |
| 	},
 | |
| 	nextBackColor = {
 | |
| 		key = keys.rightBracket,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	prevBackColor = {
 | |
| 		key = keys.leftBracket,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {},
 | |
| 	},
 | |
| 	shiftDotsRight = {
 | |
| 		key = keys.right,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {
 | |
| 			[keys.shift] = true
 | |
| 		}
 | |
| 	},
 | |
| 	shiftDotsLeft = {
 | |
| 		key = keys.left,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {
 | |
| 			[keys.shift] = true
 | |
| 		}
 | |
| 	},
 | |
| 	shiftDotsUp = {
 | |
| 		key = keys.up,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {
 | |
| 			[keys.shift] = true
 | |
| 		}
 | |
| 	},
 | |
| 	shiftDotsDown = {
 | |
| 		key = keys.down,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {
 | |
| 			[keys.shift] = true
 | |
| 		}
 | |
| 	},
 | |
| 	toggleLayerMenu = {
 | |
| 		key = keys.l,
 | |
| 		holdDown = false,
 | |
| 		modifiers = {}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| local checkControl = function(name, forceHoldDown)
 | |
| 	local modlist = {
 | |
| 		keys.ctrl,
 | |
| 		keys.shift,
 | |
| 		keys.alt,
 | |
| 	}
 | |
| 	for i = 1, #modlist do
 | |
| 		if pain.control[name].modifiers[modlist[i]] then
 | |
| 			if not keysDown[modlist[i]] then
 | |
| 				return false
 | |
| 			end
 | |
| 		else
 | |
| 			if keysDown[modlist[i]] then
 | |
| 				return false
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 	if pain.control[name].key then
 | |
| 		if keysDown[pain.control[name].key] then
 | |
| 			local holdDown = pain.control[name].holdDown
 | |
| 			if forceHoldDown ~= nil then
 | |
| 				holdDown = forceHoldDown
 | |
| 			end
 | |
| 			if holdDown then
 | |
| 				return true
 | |
| 			else
 | |
| 				if not pain.controlHoldCheck[name] then
 | |
| 					pain.controlHoldCheck[name] = true
 | |
| 					return true
 | |
| 				end
 | |
| 			end
 | |
| 		else
 | |
| 			pain.controlHoldCheck[name] = false
 | |
| 			return false
 | |
| 		end
 | |
| 	end
 | |
| end
 | |
| 
 | |
| -- stores the native color palettes, in case the current iteration of ComputerCraft doesn't come with term.nativePaletteColor
 | |
| -- if you're using ATOM, feel free to minimize this whole table
 | |
| pain.nativePalette = {
 | |
| 	[ 1 ] = {
 | |
| 		0.94117647409439,
 | |
| 		0.94117647409439,
 | |
| 		0.94117647409439,
 | |
| 	},
 | |
| 	[ 2 ] = {
 | |
| 		0.94901961088181,
 | |
| 		0.69803923368454,
 | |
| 		0.20000000298023,
 | |
| 	},
 | |
| 	[ 4 ] = {
 | |
| 		0.89803922176361,
 | |
| 		0.49803921580315,
 | |
| 		0.84705883264542,
 | |
| 	},
 | |
| 	[ 8 ] = {
 | |
| 		0.60000002384186,
 | |
| 		0.69803923368454,
 | |
| 		0.94901961088181,
 | |
| 	},
 | |
| 	[ 16 ] = {
 | |
| 		0.87058824300766,
 | |
| 		0.87058824300766,
 | |
| 		0.42352941632271,
 | |
| 	},
 | |
| 	[ 32 ] = {
 | |
| 		0.49803921580315,
 | |
| 		0.80000001192093,
 | |
| 		0.098039217293262,
 | |
| 	},
 | |
| 	[ 64 ] = {
 | |
| 		0.94901961088181,
 | |
| 		0.69803923368454,
 | |
| 		0.80000001192093,
 | |
| 	},
 | |
| 	[ 128 ] = {
 | |
| 		0.29803922772408,
 | |
| 		0.29803922772408,
 | |
| 		0.29803922772408,
 | |
| 	},
 | |
| 	[ 256 ] = {
 | |
| 		0.60000002384186,
 | |
| 		0.60000002384186,
 | |
| 		0.60000002384186,
 | |
| 	},
 | |
| 	[ 512 ] = {
 | |
| 		0.29803922772408,
 | |
| 		0.60000002384186,
 | |
| 		0.69803923368454,
 | |
| 	},
 | |
| 	[ 1024 ] = {
 | |
| 		0.69803923368454,
 | |
| 		0.40000000596046,
 | |
| 		0.89803922176361,
 | |
| 	},
 | |
| 	[ 2048 ] = {
 | |
| 		0.20000000298023,
 | |
| 		0.40000000596046,
 | |
| 		0.80000001192093,
 | |
| 	},
 | |
| 	[ 4096 ] = {
 | |
| 		0.49803921580315,
 | |
| 		0.40000000596046,
 | |
| 		0.29803922772408,
 | |
| 	},
 | |
| 	[ 8192 ] = {
 | |
| 		0.34117648005486,
 | |
| 		0.65098041296005,
 | |
| 		0.30588236451149,
 | |
| 	},
 | |
| 	[ 16384 ] = {
 | |
| 		0.80000001192093,
 | |
| 		0.29803922772408,
 | |
| 		0.29803922772408,
 | |
| 	},
 | |
| 	[ 32768 ] = {
 | |
| 		0.066666670143604,
 | |
| 		0.066666670143604,
 | |
| 		0.066666670143604,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| local hexColors = "0123456789abcdef"
 | |
| 
 | |
| -- load Windon't API
 | |
| -- if you're using ATOM, feel free to minimize this whole function
 | |
| local windont = require "windont"
 | |
| 
 | |
| windont.default.alwaysRender = false
 | |
| 
 | |
| local scr_x, scr_y = term.getSize()
 | |
| 
 | |
| pain.windows.toolPreview 	= windont.newWindow(1,          1, scr_x, scr_y, {textColor = "-", backColor = "-"})
 | |
| pain.windows.mainMenu 		= windont.newWindow(1,          1, scr_x, scr_y, {textColor = "-", backColor = "-"})
 | |
| pain.windows.layerMenu 		= windont.newWindow(scr_x - 20, 1, 20,    scr_y, {textColor = "-", backColor = "-"})
 | |
| pain.windows.smallPreview 	= windont.newWindow(1,          1, scr_x, scr_y, {textColor = "-", backColor = "-"})
 | |
| pain.windows.grid 			= windont.newWindow(1,          1, scr_x, scr_y, {textColor = "-", backColor = "-"})
 | |
| 
 | |
| local function tableCopy(tbl)
 | |
| 	local output = {}
 | |
| 	for k, v in next, tbl do
 | |
| 		output[k] = type(v) == "table" and tableCopy(v) or v
 | |
| 	end
 | |
| 	return output
 | |
| end
 | |
| 
 | |
| pain.startTimer = function(name, duration)
 | |
| 	if type(duration) ~= "number" then
 | |
| 		error("duration must be number")
 | |
| 	elseif type(name) ~= "string" then
 | |
| 		error("name must be string")
 | |
| 	else
 | |
| 		pain.timers[name] = duration
 | |
| 	end
 | |
| end
 | |
| 
 | |
| pain.cancelTimer = function(name)
 | |
| 	if type(name) ~= "string" then
 | |
| 		error("name must be string")
 | |
| 	else
 | |
| 		pain.timers[name] = nil
 | |
| 	end
 | |
| end
 | |
| 
 | |
| pain.tickTimers = function()
 | |
| 	local done = {}
 | |
| 	for k,v in next, pain.timers do
 | |
| 		pain.timers[k] = v - 1
 | |
| 		if pain.timers[k] <= 0 then
 | |
| 			done[k] = true
 | |
| 		end
 | |
| 	end
 | |
| 	for k,v in next, done do
 | |
| 		pain.timers[k] = nil
 | |
| 	end
 | |
| 	return done
 | |
| end
 | |
| 
 | |
| -- a 'canvas' refers to a single layer only
 | |
| -- canvases are also windon't objects, like terminals
 | |
| 
 | |
| 
 | |
| -- stolen from the paintutils API...nwehehehe
 | |
| local getDotsInLine = function( startX, startY, endX, endY )
 | |
| 	local out = {}
 | |
| 	startX = math.floor(startX)
 | |
| 	startY = math.floor(startY)
 | |
| 	endX = math.floor(endX)
 | |
| 	endY = math.floor(endY)
 | |
| 	if startX == endX and startY == endY then
 | |
| 		out = {{startX, startY}}
 | |
| 		return out
 | |
| 	end
 | |
|     local minX = math.min( startX, endX )
 | |
| 	if minX == startX then
 | |
| 		minY = startY
 | |
| 		maxX = endX
 | |
| 		maxY = endY
 | |
| 	else
 | |
| 		minY = endY
 | |
| 		maxX = startX
 | |
| 		maxY = startY
 | |
| 	end
 | |
| 	local xDiff = maxX - minX
 | |
| 	local yDiff = maxY - minY
 | |
| 	if xDiff > math.abs(yDiff) then
 | |
|         local y = minY
 | |
|         local dy = yDiff / xDiff
 | |
|         for x=minX,maxX do
 | |
|             out[#out+1] = {x, math.floor(y+0.5)}
 | |
|             y = y + dy
 | |
|         end
 | |
|     else
 | |
|         local x = minX
 | |
|         local dx = xDiff / yDiff
 | |
|         if maxY >= minY then
 | |
|             for y=minY,maxY do
 | |
|                 out[#out+1] = {math.floor(x+0.5), y}
 | |
|                 x = x + dx
 | |
|             end
 | |
|         else
 | |
|             for y=minY,maxY,-1 do
 | |
|                 out[#out+1] = {math.floor(x+0.5), y}
 | |
|                 x = x - dx
 | |
|             end
 | |
|         end
 | |
|     end
 | |
|     return out
 | |
| end
 | |
| 
 | |
| pain.manip.touchDot = function(canvas, x, y)
 | |
| 	for c = 1, 3 do
 | |
| 		canvas.meta.buffer[c][y] = canvas.meta.buffer[c][y] or {}
 | |
| 		for xx = 1, x do
 | |
| 			canvas.meta.buffer[c][y][xx] = canvas.meta.buffer[c][y][xx] or "-"
 | |
| 		end
 | |
| 	end
 | |
| 	return true
 | |
| end
 | |
| 
 | |
| pain.manip.setDot = function(canvas, x, y, char, text, back)
 | |
| 	if pain.manip.touchDot(canvas, x, y) then
 | |
| 		canvas.meta.buffer[1][y][x] = char
 | |
| 		canvas.meta.buffer[2][y][x] = text
 | |
| 		canvas.meta.buffer[3][y][x] = back
 | |
| 	end
 | |
| end
 | |
| 
 | |
| pain.manip.setDotLine = function(canvas, x1, y1, x2, y2, char, text, back)
 | |
| 	local dots = getDotsInLine(x1, y1, x2, y2)
 | |
| 	for i = 1, #dots do
 | |
| 		pain.manip.setDot(canvas, dots[i][1], dots[i][2], char, text, back)
 | |
| 	end
 | |
| end
 | |
| 
 | |
| pain.manip.changePainColor = function(mode, amount, doLoop)
 | |
| 	local cNum = hexColors:find(pain.color[mode])
 | |
| 	local sNum
 | |
| 	if doLoop then
 | |
| 		sNum = ((cNum + amount - 1) % 16) + 1
 | |
| 	else
 | |
| 		sNum = math.min(math.max(cNum + amount, 1), 16)
 | |
| 	end
 | |
| 	pain.color[mode] = hexColors:sub(sNum, sNum)
 | |
| end
 | |
| 
 | |
| pain.manip.shiftDots = function(canvas, xDist, yDist)
 | |
| 	local output = {{}, {}, {}}
 | |
| 	for c = 1, 3 do
 | |
| 		for y,vy in next, canvas.meta.buffer[c] do
 | |
| 			output[c][y + yDist] = {}
 | |
| 			for x,vx in next, vy do
 | |
| 				output[c][y + yDist][x + xDist] = vx
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 	canvas.meta.buffer = output
 | |
| end
 | |
| 
 | |
| local whitespace = {
 | |
| 	["\009"] = true,
 | |
| 	["\010"] = true,
 | |
| 	["\013"] = true,
 | |
| 	["\032"] = true,
 | |
| 	["\128"] = true
 | |
| }
 | |
| 
 | |
| -- checks if a char/text/back combination should be considered "transparent"
 | |
| pain.checkTransparent = function(char, text, back)
 | |
| 	if whitespace[char] then
 | |
| 		return (not back) or (back == "-")
 | |
| 	else
 | |
| 		return ((not back) or (back == "-")) and ((not text) or (text == "-") )
 | |
| 	end
 | |
| end
 | |
| 
 | |
| -- checks if a certain x,y position on the canvas exists
 | |
| pain.checkDot = function(canvas, x, y)
 | |
| 	if paint.manip.touchDot(canvas, x, y) then
 | |
| 		if canvas[1][y][x] then
 | |
| 			return canvas[1][y][x], canvas[2][y][x], canvas[3][y][x]
 | |
| 		end
 | |
| 	end
 | |
| end
 | |
| 
 | |
| local tools = {}
 | |
| tools.pencil = {
 | |
| 	run = function(canvas, initEvent, toolInfo)
 | |
| 		local mx, my, evt = initEvent[3], initEvent[4]
 | |
| 		local oldX, oldY
 | |
| 		local mode = initEvent[2]	-- 1 = draw, 2 = erase
 | |
| 		if keysDown[keys.shift] then
 | |
| 			return tools.line.run(canvas, initEvent, toolInfo)
 | |
| 		else
 | |
| 			local setDot = function()
 | |
| 				pain.manip.setDotLine(
 | |
| 					canvas,
 | |
| 					oldX or (mx - (canvas.meta.x - 1)),
 | |
| 					oldY or (my - (canvas.meta.y - 1)),
 | |
| 					mx - (canvas.meta.x - 1),
 | |
| 					my - (canvas.meta.y - 1),
 | |
| 					mode == 1 and pain.color.char or nil, -- " ",
 | |
| 					mode == 1 and pain.color.text or nil, -- "-",
 | |
| 					mode == 1 and pain.color.back or nil -- "-"
 | |
| 				)
 | |
| 			end
 | |
| 			while miceDown[mode] do
 | |
| 				evt = {os.pullEvent()}
 | |
| 				if evt[1] == "mouse_click" or evt[1] == "mouse_drag" then
 | |
| 					oldX, oldY = mx - (canvas.meta.x - 1), my - (canvas.meta.y - 1)
 | |
| 					mx, my = evt[3], evt[4]
 | |
| 					setDot()
 | |
| 				elseif evt[1] == "refresh" then
 | |
| 					oldX, oldY = mx - (canvas.meta.x - 1), my - (canvas.meta.y - 1)
 | |
| 					setDot()
 | |
| 				end
 | |
| 			end
 | |
| 		end
 | |
| 	end,
 | |
| 	options = {}
 | |
| }
 | |
| 
 | |
| tools.line = {
 | |
| 	run = function(canvas, initEvent, toolInfo)
 | |
| 		local mx, my, evt = initEvent[3], initEvent[4]
 | |
| 		local initX, initY
 | |
| 		local oldX, oldY
 | |
| 		local mode = initEvent[2]	-- 1 = draw, 2 = erase
 | |
| 		local setDot = function(sCanvas)
 | |
| 			if initX and initY then
 | |
| 				pain.manip.setDotLine(
 | |
| 					sCanvas,
 | |
| 					initX,
 | |
| 					initY,
 | |
| 					mx - (canvas.meta.x - 1),
 | |
| 					my - (canvas.meta.y - 1),
 | |
| 					mode == 1 and pain.color.char or nil, --" ",
 | |
| 					mode == 1 and pain.color.text or nil, -- "-",
 | |
| 					mode == 1 and pain.color.back or nil -- "-"
 | |
| 				)
 | |
| 			end
 | |
| 		end
 | |
| 		toolInfo.showToolPreview = true
 | |
| 		while miceDown[mode] do
 | |
| 			evt = {os.pullEvent()}
 | |
| 			if evt[1] == "mouse_click" or evt[1] == "mouse_drag" then
 | |
| 				oldX, oldY = mx - (canvas.meta.x - 1), my - (canvas.meta.y - 1)
 | |
| 				mx, my = evt[3], evt[4]
 | |
| 				if not (initX and initY) then
 | |
| 					initX = mx - (canvas.meta.x - 1)
 | |
| 					initY = my - (canvas.meta.y - 1)
 | |
| 				end
 | |
| 				setDot(pain.windows.toolPreview)
 | |
| 			elseif evt[1] == "mouse_up" then
 | |
| 				setDot(canvas)
 | |
| 			elseif evt[1] == "refresh" then
 | |
| 				oldX, oldY = mx - (canvas.meta.x - 1), my - (canvas.meta.y - 1)
 | |
| 				setDot(pain.windows.toolPreview)
 | |
| 			end
 | |
| 		end
 | |
| 	end,
 | |
| 	options = {}
 | |
| }
 | |
| 
 | |
| local genPalette = function()
 | |
| 	local palette = {}
 | |
| 	for i = 0, 15 do
 | |
| 		palette[2^i] = pain.nativePalettes[2^i]
 | |
| 	end
 | |
| 	return palette
 | |
| end
 | |
| 
 | |
| local newCanvas = function()
 | |
| 	local canvas = windont.newWindow(1, 1, 1, 1, {textColor = "-", backColor = "-"})
 | |
| 	canvas.meta.x = 1
 | |
| 	canvas.meta.y = 1
 | |
| 	return canvas
 | |
| end
 | |
| 
 | |
| local getGridFromPos = function(x, y, scrollX, scrollY)
 | |
| 	local grid
 | |
| 	if (x >= 0 and y >= 0) then
 | |
| 		grid = {
 | |
| 			"$$..%%..%%..%%..",
 | |
| 			"$$..%%..%%..%%..",
 | |
| 			"$$..%%..%%..%%..",
 | |
| 			"..$$..%%..%%..$$",
 | |
| 			"..$$..%%..%%..$$",
 | |
| 			"..$$..%%..%%..$$",
 | |
| 			"%%..$$..%%..$$..",
 | |
| 			"%%..$$..%%..$$..",
 | |
| 			"%%..$$..%%..$$..",
 | |
| 			"..%%..$$..$$..%%",
 | |
| 			"..%%..$$..$$..%%",
 | |
| 			"..%%..$$..$$..%%",
 | |
| 			"%%..%%..$$..%%..",
 | |
| 			"%%..%%..$$..%%..",
 | |
| 			"%%..%%..$$..%%..",
 | |
| 			"..%%..$$..$$..%%",
 | |
| 			"..%%..$$..$$..%%",
 | |
| 			"..%%..$$..$$..%%",
 | |
| 			"%%..$$..%%..$$..",
 | |
| 			"%%..$$..%%..$$..",
 | |
| 			"%%..$$..%%..$$..",
 | |
| 			"..$$..%%..%%..$$",
 | |
| 			"..$$..%%..%%..$$",
 | |
| 			"..$$..%%..%%..$$",
 | |
| 		}
 | |
| 	else
 | |
| 		if (x < 0 and y >= 0) then
 | |
| 			-- too far to the left, but not too far up
 | |
| 			grid = {
 | |
| 				"GO#RIGHT#",
 | |
| 				"#---\16####",
 | |
| 				"##---\16###",
 | |
| 				"###---\16##",
 | |
| 				"####---\16#",
 | |
| 				"###---\16##",
 | |
| 				"##---\16###",
 | |
| 				"#---\16####",
 | |
| 			}
 | |
| 		elseif (x >= 0 and y < 0) then
 | |
| 			-- too far up, but not too far to the left
 | |
| 			grid = {
 | |
| 				"#GO##DOWN#",
 | |
| 				"#|#######|",
 | |
| 				"#||#####||",
 | |
| 				"#\31||###||\31",
 | |
| 				"##\31||#||\31#",
 | |
| 				"###\31|||\31##",
 | |
| 				"####\31|\31###",
 | |
| 				"#####\31####",
 | |
| 				"##########",
 | |
| 			}
 | |
| 		else
 | |
| 			grid = {
 | |
| 				"\\##\\",
 | |
| 				"\\\\##",
 | |
| 				"#\\\\#",
 | |
| 				"##\\\\",
 | |
| 			}
 | |
| 		end
 | |
| 	end
 | |
| 	local xx = (x % #grid[1]) + 1
 | |
| 	return grid[(y % #grid) + 1]:sub(xx, xx), "7", "f"
 | |
| end
 | |
| 
 | |
| local drawGrid = function(canvas)
 | |
| 	local xx
 | |
| 	for y = 1, pain.windows.grid.meta.height do
 | |
| 		for x = 1, pain.windows.grid.meta.width do
 | |
| 			pain.windows.grid.meta.buffer[1][y][x], pain.windows.grid.meta.buffer[2][y][x], pain.windows.grid.meta.buffer[3][y][x] = getGridFromPos(x - canvas.meta.x, y - canvas.meta.y)
 | |
| 		end
 | |
| 	end
 | |
| end
 | |
| 
 | |
| local copyCanvasBuffer = function(buffer, x1, y1, x2, y2)
 | |
| 	local output = {{}, {}, {}}
 | |
| 	for c = 1, 3 do
 | |
| 		for y = y1, y2 do
 | |
| 			output[c][y] = {}
 | |
| 			if buffer[c][y] then
 | |
| 				for x = x1, x2 do
 | |
| 					output[c][y][x] = buffer[c][y][x]
 | |
| 				end
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 	return output
 | |
| end
 | |
| 
 | |
| local main = function()
 | |
| 	local render = function(canvasList)
 | |
| 		drawGrid(canvasList[1])
 | |
| 		local rList = {
 | |
| --			pain.windows.mainMenu,
 | |
| --			pain.windows.layerMenu,
 | |
| --			pain.windows.smallPreview,
 | |
| 			pain.windows.toolPreview,
 | |
| 		}
 | |
| 		for i = 1, #canvasList do
 | |
| 			rList[#rList + 1] = canvasList[i]
 | |
| 		end
 | |
| 		rList[#rList + 1] = pain.windows.grid
 | |
| 		windont.render(
 | |
| 			{baseTerm = term.current()},
 | |
| 			table.unpack(rList)
 | |
| 		)
 | |
| 	end
 | |
| 	local canvas, evt
 | |
| 	local tCompleted = {}
 | |
| 	local mainTimer = os.startTimer(0.05)
 | |
| 	local resumeTimer = os.startTimer(0.05)
 | |
| 
 | |
| 	pain.startTimer("render", 0.05)
 | |
| 
 | |
| 	-- initialize first layer
 | |
| 	pain.image[1] = newCanvas()
 | |
| 
 | |
| 	local cTool = {
 | |
| 		name = "pencil",
 | |
| 		lastEvent = nil,
 | |
| 		active = false,
 | |
| 		coroutine = nil,
 | |
| 		doRender = false,			-- if true after resuming the coroutine, renders directly after resuming
 | |
| 		showToolPreview = false		-- if true, will render the tool preview INSTEAD of the current canvas
 | |
| 	}
 | |
| 
 | |
| 	local resume = function(newEvent)
 | |
| 		if cTool.coroutine then
 | |
| 			if (cTool.lastEvent == (newEvent or evt[1])) or (not cTool.lastEvent) then
 | |
| 				cTool.doQuickResume = false
 | |
| 				if cTool.showToolPreview then
 | |
| 					pain.windows.toolPreview.meta.buffer = copyCanvasBuffer(
 | |
| 						canvas.meta.buffer,
 | |
| 						-canvas.meta.x,
 | |
| 						-canvas.meta.y,
 | |
| 						-canvas.meta.x + scr_x + 1,
 | |
| 						-canvas.meta.y + scr_y + 1
 | |
| 					)
 | |
| 					pain.windows.toolPreview.meta.x = canvas.meta.x
 | |
| 					pain.windows.toolPreview.meta.y = canvas.meta.y
 | |
| 					pain.windows.toolPreview.meta.width = canvas.meta.width
 | |
| 					pain.windows.toolPreview.meta.height = canvas.meta.height
 | |
| 				end
 | |
| 				cTool.active, cTool.lastEvent = coroutine.resume(cTool.coroutine, table.unpack(newEvent or evt))
 | |
| 			end
 | |
| 			if checkControl("cancelTool") then
 | |
| 				cTool.active = false
 | |
| 			end
 | |
| 			if (not cTool.active) or coroutine.status(cTool.coroutine) == "dead" then
 | |
| 				cTool.active = false
 | |
| 			end
 | |
| 			if not cTool.active then
 | |
| 				if type(cTool.lastEvent) == "string" then
 | |
| 					if cTool.lastEvent:sub(1,4) == "ERR:" then
 | |
| 						error(cTool.lastEvent:sub(5))
 | |
| 					end
 | |
| 				end
 | |
| 				cTool.coroutine = nil
 | |
| 				cTool.lastEvent = nil
 | |
| 				cTool.showToolPreview = false
 | |
| 				pain.windows.toolPreview.clear()
 | |
| 			end
 | |
| 			if cTool.doRender then
 | |
| 				render({canvas})
 | |
| 				cTool.doRender = false
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	while pain.running do
 | |
| 
 | |
| 		evt = {os.pullEvent()}
 | |
| 
 | |
| 
 | |
| 		if evt[1] == "timer" and evt[2] == mainTimer then
 | |
| 			mainTimer = os.startTimer(0.05)
 | |
| 			tCompleted = pain.tickTimers()		-- get list of completed pain timers
 | |
| 			canvas = pain.image[pain.layer]		-- 'canvas' is a term object, you smarmy cunt
 | |
| 			for k,v in next, keysDown do keysDown[k] = v + 1 end
 | |
| 
 | |
| 			local singleScroll = checkControl("singleScroll")
 | |
| 
 | |
| 			if checkControl("quit") then	-- why did I call myself a cunt
 | |
| 				pain.running = false
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("scrollRight", not singleScroll) then
 | |
| 				canvas.meta.x = canvas.meta.x - 1
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("scrollLeft", not singleScroll) then
 | |
| 				canvas.meta.x = canvas.meta.x + 1
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("scrollDown", not singleScroll) then
 | |
| 				canvas.meta.y = canvas.meta.y - 1
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("scrollUp", not singleScroll) then
 | |
| 				canvas.meta.y = canvas.meta.y + 1
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("shiftDotsRight") then
 | |
| 				pain.manip.shiftDots(canvas, 1, 0)
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("shiftDotsLeft") then
 | |
| 				pain.manip.shiftDots(canvas, -1, 0)
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("shiftDotsUp") then
 | |
| 				pain.manip.shiftDots(canvas, 0, -1)
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("shiftDotsDown") then
 | |
| 				pain.manip.shiftDots(canvas, 0, 1)
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("resetScroll") then
 | |
| 				canvas.meta.x = 1
 | |
| 				canvas.meta.y = 1
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("nextTextColor") then
 | |
| 				pain.manip.changePainColor("text", 1, false)
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("nextBackColor") then
 | |
| 				pain.manip.changePainColor("back", 1, false)
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("prevTextColor") then
 | |
| 				pain.manip.changePainColor("text", -1, false)
 | |
| 			end
 | |
| 
 | |
| 			if checkControl("prevBackColor") then
 | |
| 				pain.manip.changePainColor("back", -1, false)
 | |
| 			end
 | |
| 
 | |
| 			resume({"refresh"})
 | |
| 
 | |
| 			if tCompleted.render then
 | |
| 				pain.startTimer("render", 0.05)
 | |
| 				render({cTool.showToolPreview and pain.windows.toolPreview or canvas})
 | |
| 			end
 | |
| 
 | |
| 		else
 | |
| 
 | |
| 			if evt[1] == "term_resize" then
 | |
| 				scr_x, scr_y = term.getSize()
 | |
| 			elseif evt[1] == "key" or evt[1] == "key_up" then
 | |
| 				if evt[1] == "key" then
 | |
| 					if not evt[3] then
 | |
| 						keysDown[evt[2]] = 0
 | |
| 					end
 | |
| 				elseif evt[1] == "key_up" then
 | |
| 					keysDown[evt[2]] = nil
 | |
| 				end
 | |
| 				keysDown[keys.ctrl] = keysDown[keys.leftCtrl] or keysDown[keys.rightCtrl]
 | |
| 				keysDown[keys.shift] = keysDown[keys.leftShift] or keysDown[keys.rightShift]
 | |
| 				keysDown[keys.alt] = keysDown[keys.leftAlt] or keysDown[keys.rightAlt]
 | |
| 			elseif evt[1] == "mouse_up" then
 | |
| 				miceDown[evt[2]] = nil
 | |
| 			elseif (evt[1] == "mouse_click" or evt[1] == "mouse_drag") then
 | |
| 				miceDown[evt[2]] = {evt[3], evt[4]}
 | |
| 				if evt[1] == "mouse_click" then
 | |
| 					if not cTool.active then
 | |
| 						cTool.coroutine = coroutine.create(function(...)
 | |
| 							local result, message = pcall(tools[cTool.name].run, ...)
 | |
| 							if not result then
 | |
| 								error("ERR:" .. message, 2)
 | |
| 							end
 | |
| 						end)
 | |
| 						cTool.active = coroutine.resume(cTool.coroutine, canvas, evt, cTool)
 | |
| 					end
 | |
| 				end
 | |
| 			end
 | |
| 
 | |
| 			resume()
 | |
| 
 | |
| 		end
 | |
| 
 | |
| 	end
 | |
| 
 | |
| 	term.setCursorPos(1, scr_y)
 | |
| 	term.clearLine()
 | |
| 
 | |
| end
 | |
| 
 | |
| main()
 | 
