1
0
mirror of https://github.com/LDDestroier/CC/ synced 2024-06-27 07:33:18 +00:00
ldd-CC/pain2.lua
2020-04-14 01:28:31 -04:00

687 lines
15 KiB
Lua

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 = {},
},
resetScroll = {
key = keys.a,
holdDown = false,
modifiers = {},
},
cancelTool = {
key = keys.space,
holdDown = false,
modifiers = {},
}
}
local checkControl = function(name)
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
if pain.control[name].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,
}
}
-- 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.menu = windont.newWindow(1, 1, scr_x, 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 pairs(pain.timers) do
pain.timers[k] = v - 1
if pain.timers[k] <= 0 then
done[k] = true
end
end
for k,v in pairs(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)
if false then
if (x > canvas.meta.width or y > canvas.meta.height) and (x >= 1 and y >= 1) then
canvas.meta.width = x
canvas.meta.height = y
canvas.meta.buffer = canvas.meta.newBuffer(
x,
y,
" ",
"-",
"-",
canvas.meta.buffer
)
end
return true
else
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
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
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
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 " ",
mode == 1 and pain.color.text or "-",
mode == 1 and pain.color.back or "-"
)
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,
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 " ",
mode == 1 and pain.color.text or "-",
mode == 1 and pain.color.back or "-"
)
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 makeMenu = function()
end
local main = function()
local render = function(canvasList)
drawGrid(canvasList[1])
local rList = {
pain.windows.menu,
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 = "line",
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 isToolGood = false
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 = tableCopy(canvas.meta.buffer)
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 pairs(keysDown) do keysDown[k] = v + 1 end
if checkControl("quit") then -- why did I call myself a cunt
pain.running = false
end
if checkControl("scrollRight") then
canvas.meta.x = canvas.meta.x - 1
end
if checkControl("scrollLeft") then
canvas.meta.x = canvas.meta.x + 1
end
if checkControl("scrollDown") then
canvas.meta.y = canvas.meta.y - 1
end
if checkControl("scrollUp") then
canvas.meta.y = canvas.meta.y + 1
end
if checkControl("resetScroll") then
canvas.meta.x = 1
canvas.meta.y = 1
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" then
if not evt[3] then
keysDown[evt[2]] = 0
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]
end
elseif evt[1] == "mouse_up" then
miceDown[evt[2]] = nil
elseif evt[1] == "key_up" then
keysDown[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()