Last active
May 13, 2018 23:38
-
-
Save incinirate/54bfe3f7d20823fcc6a730c3f0bd9feb to your computer and use it in GitHub Desktop.
Don't read me, I'm a squashed and bundled nightmare
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- vim: syntax=lua | |
-- luacheck: globals loadRemote getRemote fs loadstring peripheral | |
-- Load required libs / files | |
local surface = (function() | |
local surface = { } do | |
--[[ | |
Surface 2 | |
The MIT License (MIT) | |
Copyright (c) 2017 CrazedProgrammer | |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and | |
associated documentation files (the "Software"), to deal in the Software without restriction, | |
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do | |
so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all copies or | |
substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | |
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
]] | |
local surf = { } | |
surface.surf = surf | |
local table_concat, math_floor, math_atan2 = table.concat, math.floor, math.atan2 | |
local _cc_color_to_hex, _cc_hex_to_color = { }, { } | |
for i = 0, 15 do | |
_cc_color_to_hex[2 ^ i] = string.format("%01x", i) | |
_cc_hex_to_color[string.format("%01x", i)] = 2 ^ i | |
end | |
local _chars = { } | |
for i = 0, 255 do | |
_chars[i] = string.char(i) | |
end | |
local _numstr = { } | |
for i = 0, 1023 do | |
_numstr[i] = tostring(i) | |
end | |
local _eprc, _esin, _ecos = 20, { }, { } | |
for i = 0, _eprc - 1 do | |
_esin[i + 1] = (1 - math.sin(i / _eprc * math.pi * 2)) / 2 | |
_ecos[i + 1] = (1 + math.cos(i / _eprc * math.pi * 2)) / 2 | |
end | |
local _steps, _palette, _rgbpal, _palr, _palg, _palb = 16 | |
local function calcStack(stack, width, height) | |
local ox, oy, cx, cy, cwidth, cheight = 0, 0, 0, 0, width, height | |
for i = 1, #stack do | |
ox = ox + stack[i].ox | |
oy = oy + stack[i].oy | |
cx = cx + stack[i].x | |
cy = cy + stack[i].y | |
cwidth = stack[i].width | |
cheight = stack[i].height | |
end | |
return ox, oy, cx, cy, cwidth, cheight | |
end | |
local function clipRect(x, y, width, height, cx, cy, cwidth, cheight) | |
if x < cx then | |
width = width + x - cx | |
x = cx | |
end | |
if y < cy then | |
height = height + y - cy | |
y = cy | |
end | |
if x + width > cx + cwidth then | |
width = cwidth + cx - x | |
end | |
if y + height > cy + cheight then | |
height = cheight + cy - y | |
end | |
return x, y, width, height | |
end | |
function surface.create(width, height, b, t, c) | |
local surface = setmetatable({ }, {__index = surface.surf}) | |
surface.width = width | |
surface.height = height | |
surface.buffer = { } | |
surface.overwrite = false | |
surface.stack = { } | |
surface.ox, surface.oy, surface.cx, surface.cy, surface.cwidth, surface.cheight = calcStack(surface.stack, width, height) | |
-- force array indeces instead of hashed indices | |
local buffer = surface.buffer | |
for i = 1, width * height * 3, 3 do | |
buffer[i] = b or false | |
buffer[i + 1] = t or false | |
buffer[i + 2] = c or false | |
end | |
buffer[width * height * 3 + 1] = false | |
if not b then | |
for i = 1, width * height * 3, 3 do | |
buffer[i] = b | |
end | |
end | |
if not t then | |
for i = 2, width * height * 3, 3 do | |
buffer[i] = t | |
end | |
end | |
if not c then | |
for i = 3, width * height * 3, 3 do | |
buffer[i] = c | |
end | |
end | |
return surface | |
end | |
function surface.getPlatformOutput(output) | |
output = output or (term or gpu or (love and love.graphics) or io) | |
if output.blit and output.setCursorPos then | |
return "cc", output, output.getSize() | |
elseif output.write and output.setCursorPos and output.setTextColor and output.setBackgroundColor then | |
return "cc-old", output, output.getSize() | |
elseif output.blitPixels then | |
return "riko-4", output, 320, 200 | |
elseif output.points and output.setColor then | |
return "love2d", output, output.getWidth(), output.getHeight() | |
elseif output.drawPixel then | |
return "redirection", output, 64, 64 | |
elseif output.setForeground and output.setBackground and output.set then | |
return "oc", output, output.getResolution() | |
elseif output.write then | |
return "ansi", output, (os.getenv and (os.getenv("COLUMNS"))) or 80, (os.getenv and (os.getenv("LINES"))) or 43 | |
else | |
error("unsupported platform/output object") | |
end | |
end | |
function surf:output(output, x, y, sx, sy, swidth, sheight) | |
local platform, output, owidth, oheight = surface.getPlatformOutput(output) | |
x = x or 0 | |
y = y or 0 | |
sx = sx or 0 | |
sy = sy or 0 | |
swidth = swidth or self.width | |
sheight = sheight or self.height | |
sx, sy, swidth, sheight = clipRect(sx, sy, swidth, sheight, 0, 0, self.width, self.height) | |
local buffer = self.buffer | |
local bwidth = self.width | |
local xoffset, yoffset, idx | |
if platform == "cc" then | |
-- CC | |
local str, text, back = { }, { }, { } | |
for j = 0, sheight - 1 do | |
yoffset = (j + sy) * bwidth + sx | |
for i = 0, swidth - 1 do | |
xoffset = (yoffset + i) * 3 | |
idx = i + 1 | |
str[idx] = buffer[xoffset + 3] or " " | |
text[idx] = _cc_color_to_hex[buffer[xoffset + 2] or 1] | |
back[idx] = _cc_color_to_hex[buffer[xoffset + 1] or 32768] | |
end | |
output.setCursorPos(x + 1, y + j + 1) | |
output.blit(table_concat(str), table_concat(text), table_concat(back)) | |
end | |
elseif platform == "cc-old" then | |
-- CC pre-1.76 | |
local str, b, t, pb, pt = { } | |
for j = 0, sheight - 1 do | |
output.setCursorPos(x + 1, y + j + 1) | |
yoffset = (j + sy) * bwidth + sx | |
for i = 0, swidth - 1 do | |
xoffset = (yoffset + i) * 3 | |
pb = buffer[xoffset + 1] or 32768 | |
pt = buffer[xoffset + 2] or 1 | |
if pb ~= b then | |
if #str ~= 0 then | |
output.write(table_concat(str)) | |
str = { } | |
end | |
b = pb | |
output.setBackgroundColor(b) | |
end | |
if pt ~= t then | |
if #str ~= 0 then | |
output.write(table_concat(str)) | |
str = { } | |
end | |
t = pt | |
output.setTextColor(t) | |
end | |
str[#str + 1] = buffer[xoffset + 3] or " " | |
end | |
output.write(table_concat(str)) | |
str = { } | |
end | |
elseif platform == "riko-4" then | |
-- Riko 4 | |
local pixels = { } | |
for j = 0, sheight - 1 do | |
yoffset = (j + sy) * bwidth + sx | |
for i = 0, swidth - 1 do | |
pixels[j * swidth + i + 1] = buffer[(yoffset + i) * 3 + 1] or 0 | |
end | |
end | |
output.blitPixels(x, y, swidth, sheight, pixels) | |
elseif platform == "love2d" then | |
-- Love2D | |
local pos, r, g, b, pr, pg, pb = { } | |
for j = 0, sheight - 1 do | |
yoffset = (j + sy) * bwidth + sx | |
for i = 0, swidth - 1 do | |
xoffset = (yoffset + i) * 3 | |
pr = buffer[xoffset + 1] | |
pg = buffer[xoffset + 2] | |
pb = buffer[xoffset + 3] | |
if pr ~= r or pg ~= g or pb ~= b then | |
if #pos ~= 0 then | |
output.setColor((r or 0) * 255, (g or 0) * 255, (b or 0) * 255, (r or g or b) and 255 or 0) | |
output.points(pos) | |
end | |
r, g, b = pr, pg, pb | |
pos = { } | |
end | |
pos[#pos + 1] = i + x + 1 | |
pos[#pos + 1] = j + y + 1 | |
end | |
end | |
output.setColor((r or 0) * 255, (g or 0) * 255, (b or 0) * 255, (r or g or b) and 255 or 0) | |
output.points(pos) | |
elseif platform == "redirection" then | |
-- Redirection arcade (gpu) | |
-- todo: add image:write support for extra performance | |
local px = output.drawPixel | |
for j = 0, sheight - 1 do | |
for i = 0, swidth - 1 do | |
px(x + i, y + j, buffer[((j + sy) * bwidth + (i + sx)) * 3 + 1] or 0) | |
end | |
end | |
elseif platform == "oc" then | |
-- OpenComputers | |
local str, lx, b, t, pb, pt = { } | |
for j = 0, sheight - 1 do | |
lx = x | |
yoffset = (j + sy) * bwidth + sx | |
for i = 0, swidth - 1 do | |
xoffset = (yoffset + i) * 3 | |
pb = buffer[xoffset + 1] or 0x000000 | |
pt = buffer[xoffset + 2] or 0xFFFFFF | |
if pb ~= b then | |
if #str ~= 0 then | |
output.set(lx + 1, j + y + 1, table_concat(str)) | |
lx = i + x | |
str = { } | |
end | |
b = pb | |
output.setBackground(b) | |
end | |
if pt ~= t then | |
if #str ~= 0 then | |
output.set(lx + 1, j + y + 1, table_concat(str)) | |
lx = i + x | |
str = { } | |
end | |
t = pt | |
output.setForeground(t) | |
end | |
str[#str + 1] = buffer[xoffset + 3] or " " | |
end | |
output.set(lx + 1, j + y + 1, table_concat(str)) | |
str = { } | |
end | |
elseif platform == "ansi" then | |
-- ANSI terminal | |
local str, b, t, pb, pt = { } | |
for j = 0, sheight - 1 do | |
str[#str + 1] = "\x1b[".._numstr[y + j + 1]..";".._numstr[x + 1].."H" | |
yoffset = (j + sy) * bwidth + sx | |
for i = 0, swidth - 1 do | |
xoffset = (yoffset + i) * 3 | |
pb = buffer[xoffset + 1] or 0 | |
pt = buffer[xoffset + 2] or 7 | |
if pb ~= b then | |
b = pb | |
if b < 8 then | |
str[#str + 1] = "\x1b[".._numstr[40 + b].."m" | |
elseif b < 16 then | |
str[#str + 1] = "\x1b[".._numstr[92 + b].."m" | |
elseif b < 232 then | |
str[#str + 1] = "\x1b[48;2;".._numstr[math_floor((b - 16) / 36 * 85 / 2)]..";".._numstr[math_floor((b - 16) / 6 % 6 * 85 / 2)]..";".._numstr[math_floor((b - 16) % 6 * 85 / 2)].."m" | |
else | |
local gr = _numstr[b * 10 - 2312] | |
str[#str + 1] = "\x1b[48;2;"..gr..";"..gr..";"..gr.."m" | |
end | |
end | |
if pt ~= t then | |
t = pt | |
if t < 8 then | |
str[#str + 1] = "\x1b[".._numstr[30 + t].."m" | |
elseif t < 16 then | |
str[#str + 1] = "\x1b[".._numstr[82 + t].."m" | |
elseif t < 232 then | |
str[#str + 1] = "\x1b[38;2;".._numstr[math_floor((t - 16) / 36 * 85 / 2)]..";".._numstr[math_floor((t - 16) / 6 % 6 * 85 / 2)]..";".._numstr[math_floor((t - 16) % 6 * 85 / 2)].."m" | |
else | |
local gr = _numstr[t * 10 - 2312] | |
str[#str + 1] = "\x1b[38;2;"..gr..";"..gr..";"..gr.."m" | |
end | |
end | |
str[#str + 1] = buffer[xoffset + 3] or " " | |
end | |
end | |
output.write(table_concat(str)) | |
end | |
end | |
function surf:push(x, y, width, height, nooffset) | |
x, y = x + self.ox, y + self.oy | |
local ox, oy = nooffset and self.ox or x, nooffset and self.oy or y | |
x, y, width, height = clipRect(x, y, width, height, self.cx, self.cy, self.cwidth, self.cheight) | |
self.stack[#self.stack + 1] = {ox = ox - self.ox, oy = oy - self.oy, x = x - self.cx, y = y - self.cy, width = width, height = height} | |
self.ox, self.oy, self.cx, self.cy, self.cwidth, self.cheight = calcStack(self.stack, self.width, self.height) | |
end | |
function surf:pop() | |
if #self.stack == 0 then | |
error("no stencil to pop") | |
end | |
self.stack[#self.stack] = nil | |
self.ox, self.oy, self.cx, self.cy, self.cwidth, self.cheight = calcStack(self.stack, self.width, self.height) | |
end | |
function surf:copy() | |
local surface = setmetatable({ }, {__index = surface.surf}) | |
for k, v in pairs(self) do | |
surface[k] = v | |
end | |
surface.buffer = { } | |
for i = 1, self.width * self.height * 3 + 1 do | |
surface.buffer[i] = false | |
end | |
for i = 1, self.width * self.height * 3 do | |
surface.buffer[i] = self.buffer[i] | |
end | |
surface.stack = { } | |
for i = 1, #self.stack do | |
surface.stack[i] = self.stack[i] | |
end | |
return surface | |
end | |
function surf:clear(b, t, c) | |
local xoffset, yoffset | |
for j = 0, self.cheight - 1 do | |
yoffset = (j + self.cy) * self.width + self.cx | |
for i = 0, self.cwidth - 1 do | |
xoffset = (yoffset + i) * 3 | |
self.buffer[xoffset + 1] = b | |
self.buffer[xoffset + 2] = t | |
self.buffer[xoffset + 3] = c | |
end | |
end | |
end | |
function surf:drawPixel(x, y, b, t, c) | |
x, y = x + self.ox, y + self.oy | |
local idx | |
if x >= self.cx and x < self.cx + self.cwidth and y >= self.cy and y < self.cy + self.cheight then | |
idx = (y * self.width + x) * 3 | |
if b or self.overwrite then | |
self.buffer[idx + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[idx + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[idx + 3] = c | |
end | |
end | |
end | |
function surf:drawString(str, x, y, b, t) | |
x, y = x + self.ox, y + self.oy | |
local sx = x | |
local insidey = y >= self.cy and y < self.cy + self.cheight | |
local idx | |
local lowerxlim = self.cx | |
local upperxlim = self.cx + self.cwidth | |
local writeb = b or self.overwrite | |
local writet = t or self.overwrite | |
for i = 1, #str do | |
local c = str:sub(i, i) | |
if c == "\n" then | |
x = sx | |
y = y + 1 | |
if insidey then | |
if y >= self.cy + self.cheight then | |
return | |
end | |
else | |
insidey = y >= self.cy | |
end | |
else | |
idx = (y * self.width + x) * 3 | |
if x >= lowerxlim and x < upperxlim and insidey then | |
if writeb then | |
self.buffer[idx + 1] = b | |
end | |
if writet then | |
self.buffer[idx + 2] = t | |
end | |
self.buffer[idx + 3] = c | |
end | |
x = x + 1 | |
end | |
end | |
end | |
-- You can remove any of these components | |
function surface.load(strpath, isstr) | |
local data = strpath | |
if not isstr then | |
local handle = io.open(strpath, "rb") | |
if not handle then return end | |
local chars = { } | |
local byte = handle:read(1) | |
if type(byte) == "number" then -- cc doesn't conform to standards | |
while byte do | |
chars[#chars + 1] = _chars[byte] | |
byte = handle:read(1) | |
end | |
else | |
while byte do | |
chars[#chars + 1] = byte | |
byte = handle:read(1) | |
end | |
end | |
handle:close() | |
data = table_concat(chars) | |
end | |
if data:sub(1, 3) == "RIF" then | |
-- Riko 4 image format | |
local width, height = data:byte(4) * 256 + data:byte(5), data:byte(6) * 256 + data:byte(7) | |
local surf = surface.create(width, height) | |
local buffer = surf.buffer | |
local upper, byte = 8, false | |
local byte = data:byte(index) | |
for j = 0, height - 1 do | |
for i = 0, height - 1 do | |
if not upper then | |
buffer[(j * width + i) * 3 + 1] = math_floor(byte / 16) | |
else | |
buffer[(j * width + i) * 3 + 1] = byte % 16 | |
index = index + 1 | |
data = data:byte(index) | |
end | |
upper = not upper | |
end | |
end | |
return surf | |
elseif data:sub(1, 2) == "BM" then | |
-- BMP format | |
local width = data:byte(0x13) + data:byte(0x14) * 256 | |
local height = data:byte(0x17) + data:byte(0x18) * 256 | |
if data:byte(0xF) ~= 0x28 or data:byte(0x1B) ~= 1 or data:byte(0x1D) ~= 0x18 then | |
error("unsupported bmp format, only uncompressed 24-bit rgb is supported.") | |
end | |
local offset, linesize = 0x36, math.ceil((width * 3) / 4) * 4 | |
local surf = surface.create(width, height) | |
local buffer = surf.buffer | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
buffer[(j * width + i) * 3 + 1] = data:byte((height - j - 1) * linesize + i * 3 + offset + 3) / 255 | |
buffer[(j * width + i) * 3 + 2] = data:byte((height - j - 1) * linesize + i * 3 + offset + 2) / 255 | |
buffer[(j * width + i) * 3 + 3] = data:byte((height - j - 1) * linesize + i * 3 + offset + 1) / 255 | |
end | |
end | |
return surf | |
elseif data:find("\30") then | |
-- NFT format | |
local width, height, lwidth = 0, 1, 0 | |
for i = 1, #data do | |
if data:byte(i) == 10 then -- newline | |
height = height + 1 | |
if lwidth > width then | |
width = lwidth | |
end | |
lwidth = 0 | |
elseif data:byte(i) == 30 or data:byte(i) == 31 then -- color control | |
lwidth = lwidth - 1 | |
elseif data:byte(i) ~= 13 then -- not carriage return | |
lwidth = lwidth + 1 | |
end | |
end | |
if data:byte(#data) == 10 then | |
height = height - 1 | |
end | |
local surf = surface.create(width, height) | |
local buffer = surf.buffer | |
local index, x, y, b, t = 1, 0, 0 | |
while index <= #data do | |
if data:byte(index) == 10 then | |
x, y = 0, y + 1 | |
elseif data:byte(index) == 30 then | |
index = index + 1 | |
b = _cc_hex_to_color[data:sub(index, index)] | |
elseif data:byte(index) == 31 then | |
index = index + 1 | |
t = _cc_hex_to_color[data:sub(index, index)] | |
elseif data:byte(index) ~= 13 then | |
buffer[(y * width + x) * 3 + 1] = b | |
buffer[(y * width + x) * 3 + 2] = t | |
if b or t then | |
buffer[(y * width + x) * 3 + 3] = data:sub(index, index) | |
elseif data:sub(index, index) ~= " " then | |
buffer[(y * width + x) * 3 + 3] = data:sub(index, index) | |
end | |
x = x + 1 | |
end | |
index = index + 1 | |
end | |
return surf | |
else | |
-- NFP format | |
local width, height, lwidth = 0, 1, 0 | |
for i = 1, #data do | |
if data:byte(i) == 10 then -- newline | |
height = height + 1 | |
if lwidth > width then | |
width = lwidth | |
end | |
lwidth = 0 | |
elseif data:byte(i) ~= 13 then -- not carriage return | |
lwidth = lwidth + 1 | |
end | |
end | |
if data:byte(#data) == 10 then | |
height = height - 1 | |
end | |
local surf = surface.create(width, height) | |
local buffer = surf.buffer | |
local x, y = 0, 0 | |
for i = 1, #data do | |
if data:byte(i) == 10 then | |
x, y = 0, y + 1 | |
elseif data:byte(i) ~= 13 then | |
buffer[(y * width + x) * 3 + 1] = _cc_hex_to_color[data:sub(i, i)] | |
x = x + 1 | |
end | |
end | |
return surf | |
end | |
end | |
function surf:save(file, format) | |
format = format or "nfp" | |
local data = { } | |
if format == "nfp" then | |
for j = 0, self.height - 1 do | |
for i = 0, self.width - 1 do | |
data[#data + 1] = _cc_color_to_hex[self.buffer[(j * self.width + i) * 3 + 1]] or " " | |
end | |
data[#data + 1] = "\n" | |
end | |
elseif format == "nft" then | |
for j = 0, self.height - 1 do | |
local b, t, pb, pt | |
for i = 0, self.width - 1 do | |
pb = self.buffer[(j * self.width + i) * 3 + 1] | |
pt = self.buffer[(j * self.width + i) * 3 + 2] | |
if pb ~= b then | |
data[#data + 1] = "\30"..(_cc_color_to_hex[pb] or " ") | |
b = pb | |
end | |
if pt ~= t then | |
data[#data + 1] = "\31"..(_cc_color_to_hex[pt] or " ") | |
t = pt | |
end | |
data[#data + 1] = self.buffer[(j * self.width + i) * 3 + 3] or " " | |
end | |
data[#data + 1] = "\n" | |
end | |
elseif format == "rif" then | |
data[1] = "RIF" | |
data[2] = string.char(math_floor(self.width / 256), self.width % 256) | |
data[3] = string.char(math_floor(self.height / 256), self.height % 256) | |
local byte, upper, c = 0, false | |
for j = 0, self.width - 1 do | |
for i = 0, self.height - 1 do | |
c = self.buffer[(j * self.width + i) * 3 + 1] or 0 | |
if not upper then | |
byte = c * 16 | |
else | |
byte = byte + c | |
data[#data + 1] = string.char(byte) | |
end | |
upper = not upper | |
end | |
end | |
if upper then | |
data[#data + 1] = string.char(byte) | |
end | |
elseif format == "bmp" then | |
data[1] = "BM" | |
data[2] = string.char(0, 0, 0, 0) -- file size, change later | |
data[3] = string.char(0, 0, 0, 0, 0x36, 0, 0, 0, 0x28, 0, 0, 0) | |
data[4] = string.char(self.width % 256, math_floor(self.width / 256), 0, 0) | |
data[5] = string.char(self.height % 256, math_floor(self.height / 256), 0, 0) | |
data[6] = string.char(1, 0, 0x18, 0, 0, 0, 0, 0) | |
data[7] = string.char(0, 0, 0, 0) -- pixel data size, change later | |
data[8] = string.char(0x13, 0x0B, 0, 0, 0x13, 0x0B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | |
local padchars = math.ceil((self.width * 3) / 4) * 4 - self.width * 3 | |
for j = self.height - 1, 0, -1 do | |
for i = 0, self.width - 1 do | |
data[#data + 1] = string.char((self.buffer[(j * self.width + i) * 3 + 1] or 0) * 255) | |
data[#data + 1] = string.char((self.buffer[(j * self.width + i) * 3 + 2] or 0) * 255) | |
data[#data + 1] = string.char((self.buffer[(j * self.width + i) * 3 + 3] or 0) * 255) | |
end | |
data[#data + 1] = ("\0"):rep(padchars) | |
end | |
local size = #table_concat(data) | |
data[2] = string.char(size % 256, math_floor(size / 256) % 256, math_floor(size / 65536), 0) | |
size = size - 54 | |
data[7] = string.char(size % 256, math_floor(size / 256) % 256, math_floor(size / 65536), 0) | |
else | |
error("format not supported") | |
end | |
data = table_concat(data) | |
if file then | |
local handle = io.open(file, "wb") | |
for i = 1, #data do | |
handle:write(data:byte(i)) | |
end | |
handle:close() | |
end | |
return data | |
end | |
function surf:drawLine(x1, y1, x2, y2, b, t, c) | |
if x1 == x2 then | |
x1, y1, x2, y2 = x1 + self.ox, y1 + self.oy, x2 + self.ox, y2 + self.oy | |
if x1 < self.cx or x1 >= self.cx + self.cwidth then return end | |
if y2 < y1 then | |
local temp = y1 | |
y1 = y2 | |
y2 = temp | |
end | |
if y1 < self.cy then y1 = self.cy end | |
if y2 >= self.cy + self.cheight then y2 = self.cy + self.cheight - 1 end | |
if b or self.overwrite then | |
for j = y1, y2 do | |
self.buffer[(j * self.width + x1) * 3 + 1] = b | |
end | |
end | |
if t or self.overwrite then | |
for j = y1, y2 do | |
self.buffer[(j * self.width + x1) * 3 + 2] = t | |
end | |
end | |
if c or self.overwrite then | |
for j = y1, y2 do | |
self.buffer[(j * self.width + x1) * 3 + 3] = c | |
end | |
end | |
elseif y1 == y2 then | |
x1, y1, x2, y2 = x1 + self.ox, y1 + self.oy, x2 + self.ox, y2 + self.oy | |
if y1 < self.cy or y1 >= self.cy + self.cheight then return end | |
if x2 < x1 then | |
local temp = x1 | |
x1 = x2 | |
x2 = temp | |
end | |
if x1 < self.cx then x1 = self.cx end | |
if x2 >= self.cx + self.cwidth then x2 = self.cx + self.cwidth - 1 end | |
if b or self.overwrite then | |
for i = x1, x2 do | |
self.buffer[(y1 * self.width + i) * 3 + 1] = b | |
end | |
end | |
if t or self.overwrite then | |
for i = x1, x2 do | |
self.buffer[(y1 * self.width + i) * 3 + 2] = t | |
end | |
end | |
if c or self.overwrite then | |
for i = x1, x2 do | |
self.buffer[(y1 * self.width + i) * 3 + 3] = c | |
end | |
end | |
else | |
local delta_x = x2 - x1 | |
local ix = delta_x > 0 and 1 or -1 | |
delta_x = 2 * math.abs(delta_x) | |
local delta_y = y2 - y1 | |
local iy = delta_y > 0 and 1 or -1 | |
delta_y = 2 * math.abs(delta_y) | |
self:drawPixel(x1, y1, b, t, c) | |
if delta_x >= delta_y then | |
local error = delta_y - delta_x / 2 | |
while x1 ~= x2 do | |
if (error >= 0) and ((error ~= 0) or (ix > 0)) then | |
error = error - delta_x | |
y1 = y1 + iy | |
end | |
error = error + delta_y | |
x1 = x1 + ix | |
self:drawPixel(x1, y1, b, t, c) | |
end | |
else | |
local error = delta_x - delta_y / 2 | |
while y1 ~= y2 do | |
if (error >= 0) and ((error ~= 0) or (iy > 0)) then | |
error = error - delta_y | |
x1 = x1 + ix | |
end | |
error = error + delta_x | |
y1 = y1 + iy | |
self:drawPixel(x1, y1, b, t, c) | |
end | |
end | |
end | |
end | |
function surf:drawRect(x, y, width, height, b, t, c) | |
self:drawLine(x, y, x + width - 1, y, b, t, c) | |
self:drawLine(x, y, x, y + height - 1, b, t, c) | |
self:drawLine(x + width - 1, y, x + width - 1, y + height - 1, b, t, c) | |
self:drawLine(x, y + height - 1, x + width - 1, y + height - 1, b, t, c) | |
end | |
function surf:fillRect(x, y, width, height, b, t, c) | |
x, y, width, height = clipRect(x + self.ox, y + self.oy, width, height, self.cx, self.cy, self.cwidth, self.cheight) | |
if b or self.overwrite then | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
self.buffer[((j + y) * self.width + i + x) * 3 + 1] = b | |
end | |
end | |
end | |
if t or self.overwrite then | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
self.buffer[((j + y) * self.width + i + x) * 3 + 2] = t | |
end | |
end | |
end | |
if c or self.overwrite then | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
self.buffer[((j + y) * self.width + i + x) * 3 + 3] = c | |
end | |
end | |
end | |
end | |
function surf:drawTriangle(x1, y1, x2, y2, x3, y3, b, t, c) | |
self:drawLine(x1, y1, x2, y2, b, t, c) | |
self:drawLine(x2, y2, x3, y3, b, t, c) | |
self:drawLine(x3, y3, x1, y1, b, t, c) | |
end | |
function surf:fillTriangle(x1, y1, x2, y2, x3, y3, b, t, c) | |
if y1 > y2 then | |
local tempx, tempy = x1, y1 | |
x1, y1 = x2, y2 | |
x2, y2 = tempx, tempy | |
end | |
if y1 > y3 then | |
local tempx, tempy = x1, y1 | |
x1, y1 = x3, y3 | |
x3, y3 = tempx, tempy | |
end | |
if y2 > y3 then | |
local tempx, tempy = x2, y2 | |
x2, y2 = x3, y3 | |
x3, y3 = tempx, tempy | |
end | |
if y1 == y2 and x1 > x2 then | |
local temp = x1 | |
x1 = x2 | |
x2 = temp | |
end | |
if y2 == y3 and x2 > x3 then | |
local temp = x2 | |
x2 = x3 | |
x3 = temp | |
end | |
local x4, y4 | |
if x1 <= x2 then | |
x4 = x1 + (y2 - y1) / (y3 - y1) * (x3 - x1) | |
y4 = y2 | |
local tempx, tempy = x2, y2 | |
x2, y2 = x4, y4 | |
x4, y4 = tempx, tempy | |
else | |
x4 = x1 + (y2 - y1) / (y3 - y1) * (x3 - x1) | |
y4 = y2 | |
end | |
local finvslope1 = (x2 - x1) / (y2 - y1) | |
local finvslope2 = (x4 - x1) / (y4 - y1) | |
local linvslope1 = (x3 - x2) / (y3 - y2) | |
local linvslope2 = (x3 - x4) / (y3 - y4) | |
local xstart, xend, dxstart, dxend | |
for y = math.ceil(y1 + 0.5) - 0.5, math.floor(y3 - 0.5) + 0.5, 1 do | |
if y <= y2 then -- first half | |
xstart = x1 + finvslope1 * (y - y1) | |
xend = x1 + finvslope2 * (y - y1) | |
else -- second half | |
xstart = x3 - linvslope1 * (y3 - y) | |
xend = x3 - linvslope2 * (y3 - y) | |
end | |
dxstart, dxend = math.ceil(xstart - 0.5), math.floor(xend - 0.5) | |
if dxstart <= dxend then | |
self:drawLine(dxstart, y - 0.5, dxend, y - 0.5, b, t, c) | |
end | |
end | |
end | |
function surf:drawEllipse(x, y, width, height, b, t, c) | |
for i = 0, _eprc - 1 do | |
self:drawLine(math_floor(x + _ecos[i + 1] * (width - 1) + 0.5), math_floor(y + _esin[i + 1] * (height - 1) + 0.5), math_floor(x + _ecos[(i + 1) % _eprc + 1] * (width - 1) + 0.5), math_floor(y + _esin[(i + 1) % _eprc + 1] * (height - 1) + 0.5), b, t, c) | |
end | |
end | |
function surf:fillEllipse(x, y, width, height, b, t, c) | |
x, y = x + self.ox, y + self.oy | |
local sx, sy | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
sx, sy = i + x, j + y | |
if ((i + 0.5) / width * 2 - 1) ^ 2 + ((j + 0.5) / height * 2 - 1) ^ 2 <= 1 and sx >= self.cx and sx < self.cx + self.cwidth and sy >= self.cy and sy < self.cy + self.cheight then | |
if b or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 3] = c | |
end | |
end | |
end | |
end | |
end | |
function surf:drawArc(x, y, width, height, fromangle, toangle, b, t, c) | |
if fromangle > toangle then | |
local temp = fromangle | |
fromangle = toangle | |
temp = toangle | |
end | |
fromangle = math_floor(fromangle / math.pi / 2 * _eprc + 0.5) | |
toangle = math_floor(toangle / math.pi / 2 * _eprc + 0.5) - 1 | |
for j = fromangle, toangle do | |
local i = j % _eprc | |
self:drawLine(math_floor(x + _ecos[i + 1] * (width - 1) + 0.5), math_floor(y + _esin[i + 1] * (height - 1) + 0.5), math_floor(x + _ecos[(i + 1) % _eprc + 1] * (width - 1) + 0.5), math_floor(y + _esin[(i + 1) % _eprc + 1] * (height - 1) + 0.5), b, t, c) | |
end | |
end | |
function surf:fillArc(x, y, width, height, fromangle, toangle, b, t, c) | |
x, y = x + self.ox, y + self.oy | |
if fromangle > toangle then | |
local temp = fromangle | |
fromangle = toangle | |
temp = toangle | |
end | |
local diff = toangle - fromangle | |
fromangle = fromangle % (math.pi * 2) | |
local fx, fy, sx, sy, dir | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
fx, fy = (i + 0.5) / width * 2 - 1, (j + 0.5) / height * 2 - 1 | |
sx, sy = i + x, j + y | |
dir = math_atan2(-fy, fx) % (math.pi * 2) | |
if fx ^ 2 + fy ^ 2 <= 1 and ((dir >= fromangle and dir - fromangle <= diff) or (dir <= (fromangle + diff) % (math.pi * 2))) and sx >= self.cx and sx < self.cx + self.cwidth and sy >= self.cy and sy < self.cy + self.cheight then | |
if b or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 3] = c | |
end | |
end | |
end | |
end | |
end | |
function surf:drawSurface(surf2, x, y, width, height, sx, sy, swidth, sheight) | |
x, y, width, height, sx, sy, swidth, sheight = x + self.ox, y + self.oy, width or surf2.width, height or surf2.height, sx or 0, sy or 0, swidth or surf2.width, sheight or surf2.height | |
if width == swidth and height == sheight then | |
local nx, ny | |
nx, ny, width, height = clipRect(x, y, width, height, self.cx, self.cy, self.cwidth, self.cheight) | |
swidth, sheight = width, height | |
if nx > x then | |
sx = sx + nx - x | |
x = nx | |
end | |
if ny > y then | |
sy = sy + ny - y | |
y = ny | |
end | |
nx, ny, swidth, sheight = clipRect(sx, sy, swidth, sheight, 0, 0, surf2.width, surf2.height) | |
width, height = swidth, sheight | |
if nx > sx then | |
x = x + nx - sx | |
sx = nx | |
end | |
if ny > sy then | |
y = y + ny - sy | |
sy = ny | |
end | |
local b, t, c | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
b = surf2.buffer[((j + sy) * surf2.width + i + sx) * 3 + 1] | |
t = surf2.buffer[((j + sy) * surf2.width + i + sx) * 3 + 2] | |
c = surf2.buffer[((j + sy) * surf2.width + i + sx) * 3 + 3] | |
if b or self.overwrite then | |
self.buffer[((j + y) * self.width + i + x) * 3 + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[((j + y) * self.width + i + x) * 3 + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[((j + y) * self.width + i + x) * 3 + 3] = c | |
end | |
end | |
end | |
else | |
local hmirror, vmirror = false, false | |
if width < 0 then | |
hmirror = true | |
x = x + width | |
end | |
if height < 0 then | |
vmirror = true | |
y = y + height | |
end | |
if swidth < 0 then | |
hmirror = not hmirror | |
sx = sx + swidth | |
end | |
if sheight < 0 then | |
vmirror = not vmirror | |
sy = sy + sheight | |
end | |
width, height, swidth, sheight = math.abs(width), math.abs(height), math.abs(swidth), math.abs(sheight) | |
local xscale, yscale, px, py, ssx, ssy, b, t, c = swidth / width, sheight / height | |
for j = 0, height - 1 do | |
for i = 0, width - 1 do | |
px, py = math_floor((i + 0.5) * xscale), math_floor((j + 0.5) * yscale) | |
if hmirror then | |
ssx = x + width - i - 1 | |
else | |
ssx = i + x | |
end | |
if vmirror then | |
ssy = y + height - j - 1 | |
else | |
ssy = j + y | |
end | |
if ssx >= self.cx and ssx < self.cx + self.cwidth and ssy >= self.cy and ssy < self.cy + self.cheight and px >= 0 and px < surf2.width and py >= 0 and py < surf2.height then | |
b = surf2.buffer[(py * surf2.width + px) * 3 + 1] | |
t = surf2.buffer[(py * surf2.width + px) * 3 + 2] | |
c = surf2.buffer[(py * surf2.width + px) * 3 + 3] | |
if b or self.overwrite then | |
self.buffer[(ssy * self.width + ssx) * 3 + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[(ssy * self.width + ssx) * 3 + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[(ssy * self.width + ssx) * 3 + 3] = c | |
end | |
end | |
end | |
end | |
end | |
end | |
function surf:drawSurfaceRotated(surf2, x, y, ox, oy, angle) | |
local sin, cos, sx, sy, px, py = math.sin(angle), math.cos(angle) | |
for j = math.floor(-surf2.height * 0.75), math.ceil(surf2.height * 0.75) do | |
for i = math.floor(-surf2.width * 0.75), math.ceil(surf2.width * 0.75) do | |
sx, sy, px, py = x + i, y + j, math_floor(cos * (i + 0.5) - sin * (j + 0.5) + ox), math_floor(sin * (i + 0.5) + cos * (j + 0.5) + oy) | |
if sx >= self.cx and sx < self.cx + self.cwidth and sy >= self.cy and sy < self.cy + self.cheight and px >= 0 and px < surf2.width and py >= 0 and py < surf2.height then | |
b = surf2.buffer[(py * surf2.width + px) * 3 + 1] | |
t = surf2.buffer[(py * surf2.width + px) * 3 + 2] | |
c = surf2.buffer[(py * surf2.width + px) * 3 + 3] | |
if b or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[(sy * self.width + sx) * 3 + 3] = c | |
end | |
end | |
end | |
end | |
end | |
function surf:drawSurfacesInterlaced(surfs, x, y, step) | |
x, y, step = x + self.ox, y + self.oy, step or 0 | |
local width, height = surfs[1].width, surfs[1].height | |
for i = 2, #surfs do | |
if surfs[i].width ~= width or surfs[i].height ~= height then | |
error("surfaces should be the same size") | |
end | |
end | |
local sx, sy, swidth, sheight, index, b, t, c = clipRect(x, y, width, height, self.cx, self.cy, self.cwidth, self.cheight) | |
for j = sy, sy + sheight - 1 do | |
for i = sx, sx + swidth - 1 do | |
index = (i + j + step) % #surfs + 1 | |
b = surfs[index].buffer[((j - sy) * surfs[index].width + i - sx) * 3 + 1] | |
t = surfs[index].buffer[((j - sy) * surfs[index].width + i - sx) * 3 + 2] | |
c = surfs[index].buffer[((j - sy) * surfs[index].width + i - sx) * 3 + 3] | |
if b or self.overwrite then | |
self.buffer[(j * self.width + i) * 3 + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[(j * self.width + i) * 3 + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[(j * self.width + i) * 3 + 3] = c | |
end | |
end | |
end | |
end | |
function surf:drawSurfaceSmall(surf2, x, y) | |
x, y = x + self.ox, y + self.oy | |
if surf2.width % 2 ~= 0 or surf2.height % 3 ~= 0 then | |
error("surface width must be a multiple of 2 and surface height a multiple of 3") | |
end | |
local sub, char, c1, c2, c3, c4, c5, c6 = 32768 | |
for j = 0, surf2.height / 3 - 1 do | |
for i = 0, surf2.width / 2 - 1 do | |
if i + x >= self.cx and i + x < self.cx + self.cwidth and j + y >= self.cy and j + y < self.cy + self.cheight then | |
char, c1, c2, c3, c4, c5, c6 = 0, | |
surf2.buffer[((j * 3) * surf2.width + i * 2) * 3 + 1], | |
surf2.buffer[((j * 3) * surf2.width + i * 2 + 1) * 3 + 1], | |
surf2.buffer[((j * 3 + 1) * surf2.width + i * 2) * 3 + 1], | |
surf2.buffer[((j * 3 + 1) * surf2.width + i * 2 + 1) * 3 + 1], | |
surf2.buffer[((j * 3 + 2) * surf2.width + i * 2) * 3 + 1], | |
surf2.buffer[((j * 3 + 2) * surf2.width + i * 2 + 1) * 3 + 1] | |
if c1 ~= c6 then | |
sub = c1 | |
char = 1 | |
end | |
if c2 ~= c6 then | |
sub = c2 | |
char = char + 2 | |
end | |
if c3 ~= c6 then | |
sub = c3 | |
char = char + 4 | |
end | |
if c4 ~= c6 then | |
sub = c4 | |
char = char + 8 | |
end | |
if c5 ~= c6 then | |
sub = c5 | |
char = char + 16 | |
end | |
self.buffer[((j + y) * self.width + i + x) * 3 + 1] = c6 | |
self.buffer[((j + y) * self.width + i + x) * 3 + 2] = sub | |
self.buffer[((j + y) * self.width + i + x) * 3 + 3] = _chars[128 + char] | |
end | |
end | |
end | |
end | |
function surf:flip(horizontal, vertical) | |
local ox, oy, nx, ny, tb, tt, tc | |
if horizontal then | |
for i = 0, math.ceil(self.cwidth / 2) - 1 do | |
for j = 0, self.cheight - 1 do | |
ox, oy, nx, ny = i + self.cx, j + self.cy, self.cx + self.cwidth - i - 1, j + self.cy | |
tb = self.buffer[(oy * self.width + ox) * 3 + 1] | |
tt = self.buffer[(oy * self.width + ox) * 3 + 2] | |
tc = self.buffer[(oy * self.width + ox) * 3 + 3] | |
self.buffer[(oy * self.width + ox) * 3 + 1] = self.buffer[(ny * self.width + nx) * 3 + 1] | |
self.buffer[(oy * self.width + ox) * 3 + 2] = self.buffer[(ny * self.width + nx) * 3 + 2] | |
self.buffer[(oy * self.width + ox) * 3 + 3] = self.buffer[(ny * self.width + nx) * 3 + 3] | |
self.buffer[(ny * self.width + nx) * 3 + 1] = tb | |
self.buffer[(ny * self.width + nx) * 3 + 2] = tt | |
self.buffer[(ny * self.width + nx) * 3 + 3] = tc | |
end | |
end | |
end | |
if vertical then | |
for j = 0, math.ceil(self.cheight / 2) - 1 do | |
for i = 0, self.cwidth - 1 do | |
ox, oy, nx, ny = i + self.cx, j + self.cy, i + self.cx, self.cy + self.cheight - j - 1 | |
tb = self.buffer[(oy * self.width + ox) * 3 + 1] | |
tt = self.buffer[(oy * self.width + ox) * 3 + 2] | |
tc = self.buffer[(oy * self.width + ox) * 3 + 3] | |
self.buffer[(oy * self.width + ox) * 3 + 1] = self.buffer[(ny * self.width + nx) * 3 + 1] | |
self.buffer[(oy * self.width + ox) * 3 + 2] = self.buffer[(ny * self.width + nx) * 3 + 2] | |
self.buffer[(oy * self.width + ox) * 3 + 3] = self.buffer[(ny * self.width + nx) * 3 + 3] | |
self.buffer[(ny * self.width + nx) * 3 + 1] = tb | |
self.buffer[(ny * self.width + nx) * 3 + 2] = tt | |
self.buffer[(ny * self.width + nx) * 3 + 3] = tc | |
end | |
end | |
end | |
end | |
function surf:shift(x, y, b, t, c) | |
local hdir, vdir = x < 0, y < 0 | |
local xstart, xend = self.cx, self.cx + self.cwidth - 1 | |
local ystart, yend = self.cy, self.cy + self.cheight - 1 | |
local nx, ny | |
for j = vdir and ystart or yend, vdir and yend or ystart, vdir and 1 or -1 do | |
for i = hdir and xstart or xend, hdir and xend or xstart, hdir and 1 or -1 do | |
nx, ny = i - x, j - y | |
if nx >= 0 and nx < self.width and ny >= 0 and ny < self.height then | |
self.buffer[(j * self.width + i) * 3 + 1] = self.buffer[(ny * self.width + nx) * 3 + 1] | |
self.buffer[(j * self.width + i) * 3 + 2] = self.buffer[(ny * self.width + nx) * 3 + 2] | |
self.buffer[(j * self.width + i) * 3 + 3] = self.buffer[(ny * self.width + nx) * 3 + 3] | |
else | |
self.buffer[(j * self.width + i) * 3 + 1] = b | |
self.buffer[(j * self.width + i) * 3 + 2] = t | |
self.buffer[(j * self.width + i) * 3 + 3] = c | |
end | |
end | |
end | |
end | |
function surf:map(colors) | |
local c | |
for j = self.cy, self.cy + self.cheight - 1 do | |
for i = self.cx, self.cx + self.cwidth - 1 do | |
c = colors[self.buffer[(j * self.width + i) * 3 + 1]] | |
if c or self.overwrite then | |
self.buffer[(j * self.width + i) * 3 + 1] = c | |
end | |
end | |
end | |
end | |
surface.palette = { } | |
surface.palette.cc = {[1]="F0F0F0",[2]="F2B233",[4]="E57FD8",[8]="99B2F2",[16]="DEDE6C",[32]="7FCC19",[64]="F2B2CC",[128]="4C4C4C",[256]="999999",[512]="4C99B2",[1024]="B266E5",[2048]="3366CC",[4096]="7F664C",[8192]="57A64E",[16384]="CC4C4C",[32768]="191919"} | |
surface.palette.riko4 = {"181818","1D2B52","7E2553","008651","AB5136","5F564F","7D7F82","FF004C","FFA300","FFF023","00E755","29ADFF","82769C","FF77A9","FECCA9","ECECEC"} | |
surface.palette.redirection = {[0]="040404",[1]="FFFFFF"} | |
local function setPalette(palette) | |
if palette == _palette then return end | |
_palette = palette | |
_rgbpal, _palr, _palg, _palb = { }, { }, { }, { } | |
local indices = { } | |
for k, v in pairs(_palette) do | |
if type(v) == "string" then | |
_palr[k] = tonumber(v:sub(1, 2), 16) / 255 | |
_palg[k] = tonumber(v:sub(3, 4), 16) / 255 | |
_palb[k] = tonumber(v:sub(5, 6), 16) / 255 | |
elseif type(v) == "number" then | |
_palr[k] = math.floor(v / 65536) / 255 | |
_palg[k] = (math.floor(v / 256) % 256) / 255 | |
_palb[k] = (v % 256) / 255 | |
end | |
indices[#indices + 1] = k | |
end | |
local pr, pg, pb, dist, d, id | |
for i = 0, _steps - 1 do | |
for j = 0, _steps - 1 do | |
for k = 0, _steps - 1 do | |
pr = (i + 0.5) / _steps | |
pg = (j + 0.5) / _steps | |
pb = (k + 0.5) / _steps | |
dist = 1e10 | |
for l = 1, #indices do | |
d = (pr - _palr[indices[l]]) ^ 2 + (pg - _palg[indices[l]]) ^ 2 + (pb - _palb[indices[l]]) ^ 2 | |
if d < dist then | |
dist = d | |
id = l | |
end | |
end | |
_rgbpal[i * _steps * _steps + j * _steps + k + 1] = indices[id] | |
end | |
end | |
end | |
end | |
function surf:toRGB(palette) | |
setPalette(palette) | |
local c | |
for j = 0, self.height - 1 do | |
for i = 0, self.width - 1 do | |
c = self.buffer[(j * self.width + i) * 3 + 1] | |
self.buffer[(j * self.width + i) * 3 + 1] = _palr[c] | |
self.buffer[(j * self.width + i) * 3 + 2] = _palg[c] | |
self.buffer[(j * self.width + i) * 3 + 3] = _palb[c] | |
end | |
end | |
end | |
function surf:toPalette(palette, dither) | |
setPalette(palette) | |
local scale, r, g, b, nr, ng, nb, c, dr, dg, db = _steps - 1 | |
for j = 0, self.height - 1 do | |
for i = 0, self.width - 1 do | |
r = self.buffer[(j * self.width + i) * 3 + 1] | |
g = self.buffer[(j * self.width + i) * 3 + 2] | |
b = self.buffer[(j * self.width + i) * 3 + 3] | |
r = (r > 1) and 1 or r | |
r = (r < 0) and 0 or r | |
g = (g > 1) and 1 or g | |
g = (g < 0) and 0 or g | |
b = (b > 1) and 1 or b | |
b = (b < 0) and 0 or b | |
nr = (r == 1) and scale or math_floor(r * _steps) | |
ng = (g == 1) and scale or math_floor(g * _steps) | |
nb = (b == 1) and scale or math_floor(b * _steps) | |
c = _rgbpal[nr * _steps * _steps + ng * _steps + nb + 1] | |
if dither then | |
dr = (r - _palr[c]) / 16 | |
dg = (g - _palg[c]) / 16 | |
db = (b - _palb[c]) / 16 | |
if i < self.width - 1 then | |
self.buffer[(j * self.width + i + 1) * 3 + 1] = self.buffer[(j * self.width + i + 1) * 3 + 1] + dr * 7 | |
self.buffer[(j * self.width + i + 1) * 3 + 2] = self.buffer[(j * self.width + i + 1) * 3 + 2] + dg * 7 | |
self.buffer[(j * self.width + i + 1) * 3 + 3] = self.buffer[(j * self.width + i + 1) * 3 + 3] + db * 7 | |
end | |
if j < self.height - 1 then | |
if i > 0 then | |
self.buffer[((j + 1) * self.width + i - 1) * 3 + 1] = self.buffer[((j + 1) * self.width + i - 1) * 3 + 1] + dr * 3 | |
self.buffer[((j + 1) * self.width + i - 1) * 3 + 2] = self.buffer[((j + 1) * self.width + i - 1) * 3 + 2] + dg * 3 | |
self.buffer[((j + 1) * self.width + i - 1) * 3 + 3] = self.buffer[((j + 1) * self.width + i - 1) * 3 + 3] + db * 3 | |
end | |
self.buffer[((j + 1) * self.width + i) * 3 + 1] = self.buffer[((j + 1) * self.width + i) * 3 + 1] + dr * 5 | |
self.buffer[((j + 1) * self.width + i) * 3 + 2] = self.buffer[((j + 1) * self.width + i) * 3 + 2] + dg * 5 | |
self.buffer[((j + 1) * self.width + i) * 3 + 3] = self.buffer[((j + 1) * self.width + i) * 3 + 3] + db * 5 | |
if i < self.width - 1 then | |
self.buffer[((j + 1) * self.width + i + 1) * 3 + 1] = self.buffer[((j + 1) * self.width + i + 1) * 3 + 1] + dr * 1 | |
self.buffer[((j + 1) * self.width + i + 1) * 3 + 2] = self.buffer[((j + 1) * self.width + i + 1) * 3 + 2] + dg * 1 | |
self.buffer[((j + 1) * self.width + i + 1) * 3 + 3] = self.buffer[((j + 1) * self.width + i + 1) * 3 + 3] + db * 1 | |
end | |
end | |
end | |
self.buffer[(j * self.width + i) * 3 + 1] = c | |
self.buffer[(j * self.width + i) * 3 + 2] = nil | |
self.buffer[(j * self.width + i) * 3 + 3] = nil | |
end | |
end | |
end | |
function surface.loadFont(surf) | |
local font = {width = surf.width, height = surf.height - 1} | |
font.buffer = { } | |
font.indices = {0} | |
font.widths = { } | |
local startc, hitc, curc = surf.buffer[((surf.height - 1) * surf.width) * 3 + 1] | |
for i = 0, surf.width - 1 do | |
curc = surf.buffer[((surf.height - 1) * surf.width + i) * 3 + 1] | |
if curc ~= startc then | |
hitc = curc | |
break | |
end | |
end | |
for j = 0, surf.height - 2 do | |
for i = 0, surf.width - 1 do | |
font.buffer[j * font.width + i + 1] = surf.buffer[(j * surf.width + i) * 3 + 1] == hitc | |
end | |
end | |
local curchar = 1 | |
for i = 0, surf.width - 1 do | |
if surf.buffer[((surf.height - 1) * surf.width + i) * 3 + 1] == hitc then | |
font.widths[curchar] = i - font.indices[curchar] | |
curchar = curchar + 1 | |
font.indices[curchar] = i + 1 | |
end | |
end | |
font.widths[curchar] = font.width - font.indices[curchar] | |
return font | |
end | |
function surface.getTextSize(str, font) | |
local cx, cy, maxx = 0, 0, 0 | |
local ox, char = cx | |
for i = 1, #str do | |
char = str:byte(i) - 31 | |
if char + 31 == 10 then -- newline | |
cx = ox | |
cy = cy + font.height + 1 | |
elseif font.indices[char] then | |
cx = cx + font.widths[char] + 1 | |
else | |
cx = cx + font.widths[1] | |
end | |
if cx > maxx then | |
maxx = cx | |
end | |
end | |
return maxx - 1, cy + font.height | |
end | |
function surf:drawText(str, font, x, y, b, t, c) | |
local cx, cy = x + self.ox, y + self.oy | |
local ox, char, idx = cx | |
for i = 1, #str do | |
char = str:byte(i) - 31 | |
if char + 31 == 10 then -- newline | |
cx = ox | |
cy = cy + font.height + 1 | |
elseif font.indices[char] then | |
for i = 0, font.widths[char] - 1 do | |
for j = 0, font.height - 1 do | |
x, y = cx + i, cy + j | |
if font.buffer[j * font.width + i + font.indices[char] + 1] then | |
if x >= self.cx and x < self.cx + self.cwidth and y >= self.cy and y < self.cy + self.cheight then | |
idx = (y * self.width + x) * 3 | |
if b or self.overwrite then | |
self.buffer[idx + 1] = b | |
end | |
if t or self.overwrite then | |
self.buffer[idx + 2] = t | |
end | |
if c or self.overwrite then | |
self.buffer[idx + 3] = c | |
end | |
end | |
end | |
end | |
end | |
cx = cx + font.widths[char] + 1 | |
else | |
cx = cx + font.widths[1] | |
end | |
end | |
end | |
local smap = { } | |
surface.smap = smap | |
function surface.loadSpriteMap(surf, spwidth, spheight, sprites) | |
if surf.width % spwidth ~= 0 or surf.height % spheight ~= 0 then | |
error("sprite width/height does not match smap width/height") | |
end | |
local smap = setmetatable({ }, {__index = surface.smap}) | |
smap.surf = surf | |
smap.spwidth = spwidth | |
smap.spheight = spheight | |
smap.sprites = sprites or ((surf.width / spwidth) * (surf.height / spheight)) | |
smap.perline = surf.width / spwidth | |
return smap | |
end | |
function smap:pos(index, scale) | |
if index < 0 or index >= self.sprites then | |
error("sprite index out of bounds") | |
end | |
return (index % self.perline) * self.spwidth, math.floor(index / self.perline) * self.spheight | |
end | |
function smap:sprite(index, x, y, width, height) | |
local sx, sy = self:pos(index) | |
return self.surf, x, y, width or self.spwidth, height or self.spheight, sx, sy, self.spwidth, self.spheight | |
end | |
end return surface | |
end)() | |
local fontData = (function() | |
return [[ | |
0 0 0 0 0 0000 00 0 00 0 0 0 0 0 0 00 0 00 00 0 0000 000 0000 00 00 00 000 00 000 00 000 0000 0000 000 0 0 000 000 0 0 0 00 0 0 0 00 000 00 000 000 00000 0 0 0 0 0 0 0 0 0 0 0000 000 0 000 0 0 0 0 00 0 0 0 0 00 0 00 0 00 | |
0 0 0 00000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 00 0 0 0 0 0 0 0 0 0 00 000 00 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 000 000 000 00 0 00 0 0 00 0 00 0 000 00 000 000 0 00 000 000 0 0 0 0 0 0 0 0 0 0 0000 0 0 0 0 0 | |
0 0 0 000 0 00 0 0 0 0 0 000 000 0 0 0 0 0 0 0 0 000 000 0 00 000 0 0 0 00 0 0000 000 0 0 0 000 000 0 00 0000 0 0 00 0 0 0 0 0 00 0 0 000 0 0 000 00 0 0 0 0 0 0 0 0 00 000 00 0 0 0 0 0 0 0 0 0 0 0 0 000 0 0 000 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 00 00 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 | |
00000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0000 0 0 0 0 0 0 0 0 0 00 000 00 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 000 0 000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0000 0 0 0 00 00 0 0 0 0 | |
0 0 0 0000 0 00 00 0 0 0 0 0 0 00 000 0000 00 0 000 00 0 00 000 0 0 00 0 0 000 00 000 0000 0 00 0 0 000 00 0 0 0000 0 0 0 0 00 0 0 0 0 0 000 0 00 00 0 0 0 0 000 0000 000 0 000 0000 000 000 000 000 000 0 0 0 0 0 0 0 0 0 0 0 0 0 00 000 000 0 000 0 000 00 0 0 0 0 0 0000 00 0 00 | |
0 000 00 0 0 0 | |
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]] end)() | |
local font = surface.loadFont(surface.load(fontData, true)) | |
local wapi = (function() | |
local jua = nil | |
local idPatt = "#R%d+" | |
if not ((socket and socket.websocket) or http.websocketAsync) then | |
error("You do not have CC:Tweaked/CCTweaks installed or you are not on the latest version.") | |
end | |
local newws = socket and socket.websocket or http.websocketAsync | |
local async | |
if socket and socket.websocket then | |
async = false | |
else | |
async = true | |
end | |
callbackRegistry = {} | |
wsRegistry = {} | |
local function gfind(str, patt) | |
local t = {} | |
for found in str:gmatch(patt) do | |
table.insert(t, found) | |
end | |
if #t > 0 then | |
return t | |
else | |
return nil | |
end | |
end | |
local function findID(url) | |
local found = gfind(url, idPatt) | |
return tonumber(found[#found]:sub(found[#found]:find("%d+"))) | |
end | |
local function newID() | |
return #callbackRegistry + 1 | |
end | |
local function trimID(url) | |
local found = gfind(url, idPatt) | |
local s, e = url:find(found[#found]) | |
return url:sub(1, s-1) | |
end | |
function open(callback, url, headers) | |
local id | |
if async then | |
id = newID() | |
end | |
local newUrl | |
if async then | |
newUrl = url .. "#R" .. id | |
newws(newUrl, headers) | |
else | |
if headers then | |
error("Websocket headers not supported under CCTweaks") | |
end | |
local ws = newws(url) | |
ws.send = ws.write | |
id = ws.id() | |
wsRegistry[id] = ws | |
end | |
callbackRegistry[id] = callback | |
return id | |
end | |
function init(jua) | |
jua = jua | |
if async then | |
jua.on("websocket_success", function(event, url, handle) | |
local id = findID(url) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].success then | |
callbackRegistry[id].success(findID(url), handle) | |
end | |
end) | |
jua.on("websocket_failure", function(event, url) | |
local id = findID(url) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].failure then | |
callbackRegistry[id].failure(findID(url)) | |
end | |
table.remove(callbackRegistry, id) | |
end) | |
jua.on("websocket_message", function(event, url, data) | |
local id = findID(url) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].message then | |
callbackRegistry[id].message(findID(url), data) | |
end | |
end) | |
jua.on("websocket_closed", function(event, url) | |
local id = findID(url) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].closed then | |
callbackRegistry[id].closed(findID(url)) | |
end | |
table.remove(callbackRegistry, id) | |
end) | |
else | |
jua.on("socket_connect", function(event, id) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].success then | |
callbackRegistry[id].success(id, wsRegistry[id]) | |
end | |
end) | |
jua.on("socket_error", function(event, id, msg) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].failure then | |
callbackRegistry[id].failure(id, msg) | |
end | |
table.remove(callbackRegistry, id) | |
end) | |
jua.on("socket_message", function(event, id) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].message then | |
local data = wsRegistry[id].read() | |
callbackRegistry[id].message(id, data) | |
end | |
end) | |
jua.on("socket_closed", function(event, id) | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].closed then | |
callbackRegistry[id].closed(id) | |
end | |
table.remove(callbackRegistry, id) | |
end) | |
end | |
end | |
return { | |
open = open, | |
init = init | |
} | |
end)() | |
local rapi = (function() | |
local jua = nil | |
local idPatt = "#R%d+" | |
callbackRegistry = {} | |
local function gfind(str, patt) | |
local t = {} | |
for found in str:gmatch(patt) do | |
table.insert(t, found) | |
end | |
if #t > 0 then | |
return t | |
else | |
return nil | |
end | |
end | |
local function findID(url) | |
local found = gfind(url, idPatt) | |
return tonumber(found[#found]:sub(found[#found]:find("%d+"))) | |
end | |
local function newID() | |
return #callbackRegistry + 1 | |
end | |
local function trimID(url) | |
local found = gfind(url, idPatt) | |
local s, e = url:find(found[#found]) | |
return url:sub(1, s-1) | |
end | |
function request(callback, url, headers, postData) | |
local id = newID() | |
local newUrl = url .. "#R" .. id | |
http.request(newUrl, postData, headers) | |
callbackRegistry[id] = callback | |
end | |
function init(jua) | |
jua = jua | |
jua.on("http_success", function(event, url, handle) | |
local id = findID(url) | |
if callbackRegistry[id] then | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].success then | |
callbackRegistry[id].success(true, trimID(url), handle) | |
else | |
callbackRegistry[id](true, trimID(url), handle) | |
end | |
table.remove(callbackRegistry, id) | |
end | |
end) | |
jua.on("http_failure", function(event, url, handle) | |
local id = findID(url) | |
if callbackRegistry[id] then | |
if type(callbackRegistry[id]) == "table" and callbackRegistry[id].failure then | |
callbackRegistry[id].failure(false, trimID(url), handle) | |
else | |
callbackRegistry[id](false, trimID(url), handle) | |
end | |
table.remove(callbackRegistry, id) | |
end | |
end) | |
end | |
return { | |
request = request, | |
init = init | |
} | |
end)() | |
local kapi = (function() | |
local w | |
local r | |
local jua | |
local json | |
local await | |
local endpoint = "krist.ceriat.net" | |
local wsEndpoint = "ws://"..endpoint | |
local httpEndpoint = "http://"..endpoint | |
local function asserttype(var, name, vartype, optional) | |
if not (type(var) == vartype or optional and type(var) == "nil") then | |
error(name..": expected "..vartype.." got "..type(var), 3) | |
end | |
end | |
function init(juai, jsoni, wi, ri) | |
asserttype(juai, "jua", "table") | |
asserttype(jsoni, "json", "table") | |
asserttype(wi, "w", "table", true) | |
asserttype(ri, "r", "table") | |
jua = juai | |
await = juai.await | |
json = jsoni | |
w = wi | |
r = ri | |
end | |
local function prints(...) | |
local objs = {...} | |
for i, obj in ipairs(objs) do | |
print(textutils.serialize(obj)) | |
end | |
end | |
local function url(call) | |
return httpEndpoint..call | |
end | |
local function api_request(cb, api, data) | |
local success, url, handle = await(r.request, url(api), {["Content-Type"]="application/json"}, data and json.encode(data)) | |
if success then | |
cb(success, json.decode(handle.readAll())) | |
handle.close() | |
else | |
cb(success) | |
end | |
end | |
local function authorize_websocket(cb, privatekey) | |
asserttype(cb, "callback", "function") | |
asserttype(privatekey, "privatekey", "string", true) | |
api_request(function(success, data) | |
cb(success, data and data.url:gsub("wss:", "ws:")) | |
end, "/ws/start", { | |
privatekey = privatekey | |
}) | |
end | |
function address(cb, address) | |
asserttype(cb, "callback", "function") | |
asserttype(address, "address", "string") | |
api_request(function(success, data) | |
data.address.address = address | |
cb(success, data.address) | |
end, "/addresses/"..address) | |
end | |
function addressTransactions(cb, address, limit, offset) | |
asserttype(cb, "callback", "function") | |
asserttype(address, "address", "string") | |
asserttype(limit, "limit", "number", true) | |
asserttype(offset, "offset", "number", true) | |
api_request(function(success, data) | |
cb(success, data.transactions) | |
end, "/addresses/"..address.."/transactions?limit="..(limit or 50).."&offset="..(offset or 0)) | |
end | |
function addressNames(cb, address) | |
asserttype(cb, "callback", "function") | |
asserttype(address, "address", "string") | |
api_request(function(success, data) | |
cb(success, data.names) | |
end, "/addresses/"..address.."/names") | |
end | |
function addresses(cb, limit, offset) | |
asserttype(cb, "callback", "function") | |
asserttype(limit, "limit", "number", true) | |
asserttype(offset, "offset", "number", true) | |
api_request(function(success, data) | |
cb(success, data.addresses) | |
end, "/addresses?limit="..(limit or 50).."&offset="..(offset or 0)) | |
end | |
function rich(cb, limit, offset) | |
asserttype(cb, "callback", "function") | |
asserttype(limit, "limit", "number", true) | |
asserttype(offset, "offset", "number", true) | |
api_request(function(success, data) | |
cb(success, data.addresses) | |
end, "/addresses/rich?limit="..(limit or 50).."&offset="..(offset or 0)) | |
end | |
function transactions(cb, limit, offset) | |
asserttype(cb, "callback", "function") | |
asserttype(limit, "limit", "number", true) | |
asserttype(offset, "offset", "number", true) | |
api_request(function(success, data) | |
cb(success, data.transactions) | |
end, "/transactions?limit="..(limit or 50).."&offset="..(offset or 0)) | |
end | |
function latestTransactions(cb, limit, offset) | |
asserttype(cb, "callback", "function") | |
asserttype(limit, "limit", "number", true) | |
asserttype(offset, "offset", "number", true) | |
api_request(function(success, data) | |
cb(success, data.transactions) | |
end, "/transactions/latest?limit="..(limit or 50).."&offset="..(offset or 0)) | |
end | |
function transaction(cb, txid) | |
asserttype(cb, "callback", "function") | |
asserttype(txid, "txid", "number") | |
api_request(function(success, data) | |
cb(success, data.transaction) | |
end, "/transactions/"..txid) | |
end | |
function makeTransaction(cb, privatekey, to, amount, metadata) | |
asserttype(cb, "callback", "function") | |
asserttype(privatekey, "privatekey", "string") | |
asserttype(to, "to", "string") | |
asserttype(amount, "amount", "number") | |
asserttype(metadata, "metadata", "string", true) | |
api_request(function(success, data) | |
cb(success, data.transaction) | |
end, "/transactions", { | |
privatekey = privatekey, | |
to = to, | |
amount = amount, | |
metadata = metadata | |
}) | |
end | |
local wsEventNameLookup = { | |
blocks = "block", | |
ownBlocks = "block", | |
transactions = "transaction", | |
ownTransactions = "transaction", | |
names = "name", | |
ownNames = "name", | |
ownWebhooks = "webhook", | |
motd = "motd" | |
} | |
local wsEvents = {} | |
local wsReqID = 0 | |
local wsReqRegistry = {} | |
local wsEvtRegistry = {} | |
local wsHandleRegistry = {} | |
local function newWsID() | |
local id = wsReqID | |
wsReqID = wsReqID + 1 | |
return id | |
end | |
local function registerEvent(id, event, callback) | |
if wsEvtRegistry[id] == nil then | |
wsEvtRegistry[id] = {} | |
end | |
if wsEvtRegistry[id][event] == nil then | |
wsEvtRegistry[id][event] = {} | |
end | |
table.insert(wsEvtRegistry[id][event], callback) | |
end | |
local function registerRequest(id, reqid, callback) | |
if wsReqRegistry[id] == nil then | |
wsReqRegistry[id] = {} | |
end | |
wsReqRegistry[id][reqid] = callback | |
end | |
local function discoverEvents(id, event) | |
local evs = {} | |
for k,v in pairs(wsEvtRegistry[id]) do | |
if k == event or string.match(k, event) or event == "*" then | |
for i,v2 in ipairs(v) do | |
table.insert(evs, v2) | |
end | |
end | |
end | |
return evs | |
end | |
wsEvents.success = function(id, handle) | |
-- fire success event | |
wsHandleRegistry[id] = handle | |
if wsEvtRegistry[id] then | |
local evs = discoverEvents(id, "success") | |
for i, v in ipairs(evs) do | |
v(id, handle) | |
end | |
end | |
end | |
wsEvents.failure = function(id) | |
-- fire failure event | |
if wsEvtRegistry[id] then | |
local evs = discoverEvents(id, "failure") | |
for i, v in ipairs(evs) do | |
v(id) | |
end | |
end | |
end | |
wsEvents.message = function(id, data) | |
local data = json.decode(data) | |
--print("msg:"..tostring(data.ok)..":"..tostring(data.type)..":"..tostring(data.id)) | |
--prints(data) | |
-- handle events and responses | |
if wsReqRegistry[id] and wsReqRegistry[id][tonumber(data.id)] then | |
wsReqRegistry[id][tonumber(data.id)](data) | |
elseif wsEvtRegistry[id] then | |
local evs = discoverEvents(id, data.type) | |
for i, v in ipairs(evs) do | |
v(data) | |
end | |
if data.event then | |
local evs = discoverEvents(id, data.event) | |
for i, v in ipairs(evs) do | |
v(data) | |
end | |
end | |
local evs2 = discoverEvents(id, "message") | |
for i, v in ipairs(evs2) do | |
v(id, data) | |
end | |
end | |
end | |
wsEvents.closed = function(id) | |
-- fire closed event | |
if wsEvtRegistry[id] then | |
local evs = discoverEvents(id, "closed") | |
for i, v in ipairs(evs) do | |
v(id) | |
end | |
end | |
end | |
local function wsRequest(cb, id, type, data) | |
local reqID = newWsID() | |
registerRequest(id, reqID, function(data) | |
cb(data) | |
end) | |
data.id = tostring(reqID) | |
data.type = type | |
wsHandleRegistry[id].send(json.encode(data)) | |
end | |
local function barebonesMixinHandle(id, handle) | |
handle.on = function(event, cb) | |
registerEvent(id, event, cb) | |
end | |
return handle | |
end | |
local function mixinHandle(id, handle) | |
handle.subscribe = function(cb, event, eventcb) | |
local data = await(wsRequest, id, "subscribe", { | |
event = event | |
}) | |
registerEvent(id, wsEventNameLookup[event], eventcb) | |
cb(data.ok, data) | |
end | |
return barebonesMixinHandle(id, handle) | |
end | |
function connect(cb, privatekey, preconnect) | |
asserttype(cb, "callback", "function") | |
asserttype(privatekey, "privatekey", "string", true) | |
asserttype(preconnect, "preconnect", "function", true) | |
local url | |
if privatekey then | |
local success, auth = await(authorize_websocket, privatekey) | |
url = success and auth or wsEndpoint | |
end | |
local id = w.open(wsEvents, url) | |
if preconnect then | |
preconnect(id, barebonesMixinHandle(id, {})) | |
end | |
registerEvent(id, "success", function(id, handle) | |
cb(true, mixinHandle(id, handle)) | |
end) | |
registerEvent(id, "failure", function(id) | |
cb(false) | |
end) | |
end | |
local domainMatch = "^([%l%d%-%_]*)@?([%l%d%-]+).kst$" | |
local commonMetaMatch = "^(.+)=(.+)$" | |
function parseMeta(meta) | |
asserttype(meta, "meta", "string") | |
local tbl = {meta={}} | |
for m in meta:gmatch("[^;]+") do | |
if m:match(domainMatch) then | |
-- print("Matched domain") | |
local p1, p2 = m:match("([%l%d-_]*)@"), m:match("@?([%l%d-]+).kst") | |
tbl.name = p1 | |
tbl.domain = p2 | |
elseif m:match(commonMetaMatch) then | |
-- print("Matched common meta") | |
local p1, p2 = m:match(commonMetaMatch) | |
tbl.meta[p1] = p2 | |
else | |
-- print("Unmatched standard meta") | |
table.insert(tbl.meta, m) | |
end | |
-- print(m) | |
end | |
-- print(textutils.serialize(tbl)) | |
return tbl | |
end | |
local g = string.gsub | |
sha256 = loadstring(g(g(g(g(g(g(g(g('Sa=XbandSb=XbxWSc=XlshiftSd=unpackSe=2^32SYf(g,h)Si=g/2^hSj=i%1Ui-j+j*eVSYk(l,m)Sn=l/2^mUn-n%1VSo={0x6a09e667Tbb67ae85T3c6ef372Ta54ff53aT510e527fT9b05688cT1f83d9abT5be0cd19}Sp={0x428a2f98T71374491Tb5c0fbcfTe9b5dba5T3956c25bT59f111f1T923f82a4Tab1c5ed5Td807aa98T12835b01T243185beT550c7dc3T72be5d74T80deb1feT9bdc06a7Tc19bf174Te49b69c1Tefbe4786T0fc19dc6T240ca1ccT2de92c6fT4a7484aaT5cb0a9dcT76f988daT983e5152Ta831c66dTb00327c8Tbf597fc7Tc6e00bf3Td5a79147T06ca6351T14292967T27b70a85T2e1b2138T4d2c6dfcT53380d13T650a7354T766a0abbT81c2c92eT92722c85Ta2bfe8a1Ta81a664bTc24b8b70Tc76c51a3Td192e819Td6990624Tf40e3585T106aa070T19a4c116T1e376c08T2748774cT34b0bcb5T391c0cb3T4ed8aa4aT5b9cca4fT682e6ff3T748f82eeT78a5636fT84c87814T8cc70208T90befffaTa4506cebTbef9a3f7Tc67178f2}SYq(r,q)if e-1-r[1]<q then r[2]=r[2]+1;r[1]=q-(e-1-r[1])-1 else r[1]=r[1]+qVUrVSYs(t)Su=#t;t[#t+1]=0x80;while#t%64~=56Zt[#t+1]=0VSv=q({0,0},u*8)fWw=2,1,-1Zt[#t+1]=a(k(a(v[w]TFF000000),24)TFF)t[#t+1]=a(k(a(v[w]TFF0000),16)TFF)t[#t+1]=a(k(a(v[w]TFF00),8)TFF)t[#t+1]=a(v[w]TFF)VUtVSYx(y,w)Uc(y[w]W0,24)+c(y[w+1]W0,16)+c(y[w+2]W0,8)+(y[w+3]W0)VSYz(t,w,A)SB={}fWC=1,16ZB[C]=x(t,w+(C-1)*4)VfWC=17,64ZSD=B[C-15]SE=b(b(f(B[C-15],7),f(B[C-15],18)),k(B[C-15],3))SF=b(b(f(B[C-2],17),f(B[C-2],19)),k(B[C-2],10))B[C]=(B[C-16]+E+B[C-7]+F)%eVSG,h,H,I,J,j,K,L=d(A)fWC=1,64ZSM=b(b(f(J,6),f(J,11)),f(J,25))SN=b(a(J,j),a(Xbnot(J),K))SO=(L+M+N+p[C]+B[C])%eSP=b(b(f(G,2),f(G,13)),f(G,22))SQ=b(b(a(G,h),a(G,H)),a(h,H))SR=(P+Q)%e;L,K,j,J,I,H,h,G=K,j,J,(I+O)%e,H,h,G,(O+R)%eVA[1]=(A[1]+G)%e;A[2]=(A[2]+h)%e;A[3]=(A[3]+H)%e;A[4]=(A[4]+I)%e;A[5]=(A[5]+J)%e;A[6]=(A[6]+j)%e;A[7]=(A[7]+K)%e;A[8]=(A[8]+L)%eUAVUY(t)t=t W""t=type(t)=="string"and{t:byte(1,-1)}Wt;t=s(t)SA={d(o)}fWw=1,#t,64ZA=z(t,w,A)VU("%08x"):rep(8):format(d(A))V',"S"," local "),"T",",0x"),"U"," return "),"V"," end "),"W","or "),"X","bit32."),"Y","function "),"Z"," do "))() | |
function makeaddressbyte(byte) | |
local byte = 48 + math.floor(byte / 7) | |
return string.char(byte + 39 > 122 and 101 or byte > 57 and byte + 39 or byte) | |
end | |
function makev2address(key) | |
local protein = {} | |
local stick = sha256(sha256(key)) | |
local n = 0 | |
local link = 0 | |
local v2 = "k" | |
repeat | |
if n < 9 then protein[n] = string.sub(stick,0,2) | |
stick = sha256(sha256(stick)) end | |
n = n + 1 | |
until n == 9 | |
n = 0 | |
repeat | |
link = tonumber(string.sub(stick,1+(2*n),2+(2*n)),16) % 9 | |
if string.len(protein[link]) ~= 0 then | |
v2 = v2 .. makeaddressbyte(tonumber(protein[link],16)) | |
protein[link] = '' | |
n = n + 1 | |
else | |
stick = sha256(stick) | |
end | |
until n == 9 | |
return v2 | |
end | |
function toKristWalletFormat(passphrase) | |
return sha256("KRISTWALLET"..passphrase).."-000" | |
end | |
return { | |
init = init, | |
address = address, | |
addressTransactions = addressTransactions, | |
addressNames = addressNames, | |
addresses = addresses, | |
rich = rich, | |
transactions = transactions, | |
latestTransactions = latestTransactions, | |
transaction = transaction, | |
makeTransaction = makeTransaction, | |
connect = connect, | |
parseMeta = parseMeta, | |
sha256 = sha256, | |
makeaddressbyte = makeaddressbyte, | |
makev2address = makev2address, | |
toKristWalletFormat = toKristWalletFormat | |
} | |
end)() | |
local jua = (function() | |
local juaVersion = "0.0" | |
juaRunning = false | |
eventRegistry = {} | |
timedRegistry = {} | |
local function registerEvent(event, callback) | |
if eventRegistry[event] == nil then | |
eventRegistry[event] = {} | |
end | |
table.insert(eventRegistry[event], callback) | |
end | |
local function registerTimed(time, repeating, callback) | |
if repeating then | |
callback(true) | |
end | |
table.insert(timedRegistry, { | |
time = time, | |
repeating = repeating, | |
callback = callback, | |
timer = os.startTimer(time) | |
}) | |
end | |
local function discoverEvents(event) | |
local evs = {} | |
for k,v in pairs(eventRegistry) do | |
if k == event or string.match(k, event) or event == "*" then | |
for i,v2 in ipairs(v) do | |
table.insert(evs, v2) | |
end | |
end | |
end | |
return evs | |
end | |
function on(event, callback) | |
registerEvent(event, callback) | |
end | |
function setInterval(callback, time) | |
registerTimed(time, true, callback) | |
end | |
function setTimeout(callback, time) | |
registerTimed(time, false, callback) | |
end | |
function tick() | |
local eargs = {os.pullEventRaw()} | |
local event = eargs[1] | |
if eventRegistry[event] == nil then | |
eventRegistry[event] = {} | |
else | |
local evs = discoverEvents(event) | |
for i, v in ipairs(evs) do | |
v(unpack(eargs)) | |
end | |
end | |
if event == "timer" then | |
local timer = eargs[2] | |
for i = #timedRegistry, 1, -1 do | |
local v = timedRegistry[i] | |
if v.timer == timer then | |
v.callback(not v.repeating or nil) | |
if v.repeating then | |
v.timer = os.startTimer(v.time) | |
else | |
table.remove(timedRegistry, i) | |
end | |
end | |
end | |
end | |
end | |
function run() | |
os.queueEvent("init") | |
juaRunning = true | |
while juaRunning do | |
tick() | |
end | |
end | |
function go(func) | |
on("init", func) | |
run() | |
end | |
function stop() | |
juaRunning = false | |
end | |
function await(func, ...) | |
local args = {...} | |
local out | |
local finished | |
func(function(...) | |
out = {...} | |
finished = true | |
end, unpack(args)) | |
while not finished do tick() end | |
return unpack(out) | |
end | |
return { | |
on = on, | |
setInterval = setInterval, | |
setTimeout = setTimeout, | |
tick = tick, | |
run = run, | |
go = go, | |
stop = stop, | |
await = await | |
} | |
end)() | |
local logger = (function() | |
local logger = {} | |
local slackURL = config.slackURL | |
local slackName | |
local function time() | |
return os.day("utc") .. "-" .. os.time("utc") | |
end | |
function logger.init(prints, tSlackName) | |
logger.printf = prints and print or function() end | |
logger.handle = fs.open("/log", "a") | |
slackName = tSlackName or os.getComputerLabel() or "Computer - " .. os.getComputerID() | |
end | |
function logger.log(text) | |
logger.printf(text) | |
logger.handle.write(text .. "\n") | |
logger.handle.flush() | |
end | |
function logger.info(text, slackRelay) | |
logger.printf("[" .. time() .. "] [INFO] " .. text) | |
logger.handle.write("[" .. time() .. "] [INFO] " .. text .. "\n") | |
logger.handle.flush() | |
if slackRelay == "important" then | |
logger.slackMention(text) | |
elseif slackRelay then | |
logger.slackInfo(text) | |
end | |
end | |
function logger.warn(text, slackRelay) | |
logger.printf("[" .. time() .. "] [WARN] " .. text) | |
logger.handle.write("[" .. time() .. "] [WARN] " .. text .. "\n") | |
logger.handle.flush() | |
if slackRelay then | |
logger.slackMention(text) | |
end | |
end | |
function logger.error(text, slackRelay) | |
logger.printf("[" .. time() .. "] [ERROR] " .. text) | |
logger.handle.write("[" .. time() .. "] [ERROR] " .. text .. "\n") | |
logger.handle.flush() | |
if slackRelay then | |
logger.slackMention(text) | |
end | |
end | |
function logger.slackInfo(text) | |
http.post(slackURL, [[payload={"username": "]] .. slackName .. [[", "text":"]] .. textutils.urlEncode(text) .. [["}]]) | |
end | |
function logger.slackMention(text) | |
http.post(slackURL, [[payload={"username": "]] .. slackName .. [[", "text":"<@incinirate> ]] .. textutils.urlEncode(text) .. [["}]]) | |
end | |
function logger.close() | |
logger.handle.close() | |
end | |
return logger | |
end)() | |
logger.init(true) | |
local json = (function() | |
local json = {} | |
------------------------------------------------------------------ utils | |
local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"} | |
local function isArray(t) | |
local max = 0 | |
for k,v in pairs(t) do | |
if type(k) ~= "number" then | |
return false | |
elseif k > max then | |
max = k | |
end | |
end | |
return max == #t | |
end | |
local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true} | |
local function removeWhite(str) | |
while whites[str:sub(1, 1)] do | |
str = str:sub(2) | |
end | |
return str | |
end | |
------------------------------------------------------------------ encoding | |
local function encodeCommon(val, pretty, tabLevel, tTracking) | |
local str = "" | |
-- Tabbing util | |
local function tab(s) | |
str = str .. ("\t"):rep(tabLevel) .. s | |
end | |
local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc) | |
str = str .. bracket | |
if pretty then | |
str = str .. "\n" | |
tabLevel = tabLevel + 1 | |
end | |
for k,v in iterator(val) do | |
tab("") | |
loopFunc(k,v) | |
str = str .. "," | |
if pretty then str = str .. "\n" end | |
end | |
if pretty then | |
tabLevel = tabLevel - 1 | |
end | |
if str:sub(-2) == ",\n" then | |
str = str:sub(1, -3) .. "\n" | |
elseif str:sub(-1) == "," then | |
str = str:sub(1, -2) | |
end | |
tab(closeBracket) | |
end | |
-- Table encoding | |
if type(val) == "table" then | |
assert(not tTracking[val], "Cannot encode a table holding itself recursively") | |
tTracking[val] = true | |
if isArray(val) then | |
arrEncoding(val, "[", "]", ipairs, function(k,v) | |
str = str .. encodeCommon(v, pretty, tabLevel, tTracking) | |
end) | |
else | |
arrEncoding(val, "{", "}", pairs, function(k,v) | |
assert(type(k) == "string", "JSON object keys must be strings", 2) | |
str = str .. encodeCommon(k, pretty, tabLevel, tTracking) | |
str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking) | |
end) | |
end | |
-- String encoding | |
elseif type(val) == "string" then | |
str = '"' .. val:gsub("[%c\"\\]", controls) .. '"' | |
-- Number encoding | |
elseif type(val) == "number" or type(val) == "boolean" then | |
str = tostring(val) | |
else | |
error("JSON only supports arrays, objects, numbers, booleans, and strings", 2) | |
end | |
return str | |
end | |
function json.encode(val) | |
return encodeCommon(val, false, 0, {}) | |
end | |
function json.encodePretty(val) | |
return encodeCommon(val, true, 0, {}) | |
end | |
------------------------------------------------------------------ decoding | |
local decodeControls = {} | |
for k,v in pairs(controls) do | |
decodeControls[v] = k | |
end | |
function json.parseBoolean(str) | |
if str:sub(1, 4) == "true" then | |
return true, removeWhite(str:sub(5)) | |
else | |
return false, removeWhite(str:sub(6)) | |
end | |
end | |
function json.parseNull(str) | |
return nil, removeWhite(str:sub(5)) | |
end | |
local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true} | |
function json.parseNumber(str) | |
local i = 1 | |
while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do | |
i = i + 1 | |
end | |
local val = tonumber(str:sub(1, i - 1)) | |
str = removeWhite(str:sub(i)) | |
return val, str | |
end | |
function json.parseString(str) | |
str = str:sub(2) | |
local s = "" | |
while str:sub(1,1) ~= "\"" do | |
local next = str:sub(1,1) | |
str = str:sub(2) | |
assert(next ~= "\n", "Unclosed string") | |
if next == "\\" then | |
local escape = str:sub(1,1) | |
str = str:sub(2) | |
next = assert(decodeControls[next..escape], "Invalid escape character") | |
end | |
s = s .. next | |
end | |
return s, removeWhite(str:sub(2)) | |
end | |
function json.parseArray(str) | |
str = removeWhite(str:sub(2)) | |
local val = {} | |
local i = 1 | |
while str:sub(1, 1) ~= "]" do | |
local v = nil | |
v, str = json.parseValue(str) | |
val[i] = v | |
i = i + 1 | |
str = removeWhite(str) | |
end | |
str = removeWhite(str:sub(2)) | |
return val, str | |
end | |
function json.parseObject(str) | |
str = removeWhite(str:sub(2)) | |
local val = {} | |
while str:sub(1, 1) ~= "}" do | |
local k, v = nil, nil | |
k, v, str = json.parseMember(str) | |
val[k] = v | |
str = removeWhite(str) | |
end | |
str = removeWhite(str:sub(2)) | |
return val, str | |
end | |
function json.parseMember(str) | |
local k = nil | |
k, str = json.parseValue(str) | |
local val = nil | |
val, str = json.parseValue(str) | |
return k, val, str | |
end | |
function json.parseValue(str) | |
local fchar = str:sub(1, 1) | |
if fchar == "{" then | |
return json.parseObject(str) | |
elseif fchar == "[" then | |
return json.parseArray(str) | |
elseif tonumber(fchar) ~= nil or numChars[fchar] then | |
return json.parseNumber(str) | |
elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then | |
return json.parseBoolean(str) | |
elseif fchar == "\"" then | |
return json.parseString(str) | |
elseif str:sub(1, 4) == "null" then | |
return json.parseNull(str) | |
end | |
return nil | |
end | |
function json.decode(str) | |
str = removeWhite(str) | |
t = json.parseValue(str) | |
return t | |
end | |
function json.decodeFromFile(path) | |
local file = assert(fs.open(path, "r")) | |
local decoded = json.decode(file.readAll()) | |
file.close() | |
return decoded | |
end | |
return json | |
end)() | |
-- Load local config | |
local configHandle = fs.open(".config", "r") | |
local config = loadstring("return " .. configHandle.readAll())() | |
configHandle.close() | |
if config.chest then | |
config.chests = { config.chest } | |
end | |
-- Wrap the peripherals | |
local chestPeriphs = {} | |
for i = 1, #config.chests do | |
chestPeriphs[#chestPeriphs + 1] = peripheral.wrap(config.chests[i]) | |
end | |
local monPeriph = peripheral.wrap(config.monitor) | |
local altMonPeriph = peripheral.wrap(config.altmon) | |
if not monPeriph or not chestPeriphs[1] or not altMonPeriph then | |
os.reboot() | |
end | |
monPeriph.setTextScale(0.5) | |
if altMonPeriph then | |
altMonPeriph.setTextScale(0.5) | |
end | |
--==[[ Lib Functions ]]==-- | |
local function toListName(name, damage) | |
return name .. "::" .. damage | |
end | |
local function fromListName(lName) | |
return lName:match("(.+)%:%:") | |
end | |
local drawStock | |
local list -- Item count list | |
local slotList | |
local function countItems() | |
list = {} | |
slotList = {} | |
for ck = 1, #chestPeriphs do | |
local chestPeriph = chestPeriphs[ck] | |
local cTable = chestPeriph.list() | |
if not cTable then | |
return drawStock(true) | |
end | |
for k, v in pairs(cTable) do | |
local lName = toListName(v.name, v.damage) | |
if not list[lName] then | |
list[lName] = v.count | |
slotList[lName] = { { k, v.count, ck } } | |
else | |
list[lName] = list[lName] + v.count | |
slotList[lName][#slotList[lName] + 1] = { k, v.count, ck } | |
end | |
end | |
end | |
local rm = {} | |
for k, _ in pairs(list) do | |
if not config.items[fromListName(k)] then | |
rm[#rm + 1] = k | |
end | |
end | |
for i = 1, #rm do | |
list[rm[i]] = nil | |
end | |
drawStock() | |
end | |
local function anyFree() | |
local c = 0 | |
for i = 1, 16 do | |
c = c + turtle.getItemSpace(i) | |
end | |
return c > 0 | |
end | |
local function dispense(mcname, count) | |
while count > 0 do | |
-- We don't need to check for item availability here because | |
-- we already did that in processPayment() | |
for i = #slotList[mcname], 1, -1 do | |
local chestPeriph = chestPeriphs[slotList[mcname][i][3]] | |
chestPeriph.pushItems(config.self, slotList[mcname][i][1], count) | |
local psh = math.min(count, slotList[mcname][i][2]) | |
count = count - psh | |
if count <= 0 then | |
break | |
end | |
if not anyFree() then | |
for j = 1, 16 do | |
if turtle.getItemCount(j) > 0 then | |
turtle.select(i) | |
turtle.drop() | |
end | |
end | |
end | |
end | |
end | |
for i = 1, 16 do | |
if turtle.getItemCount(i) > 0 then | |
turtle.select(i) | |
turtle.drop() | |
end | |
end | |
countItems() | |
end | |
local function findItem(name) | |
for k, item in pairs(config.items) do | |
if item.addy == name then | |
return item, toListName(k, item.damage or 0) | |
end | |
end | |
return false | |
end | |
local function escapeSemi(txt) | |
return txt:gsub("[%;%=]", "") | |
end | |
local function processPayment(tx, meta) | |
local item, mcname = findItem(meta.name) | |
if item then | |
local count = math.floor(tonumber(tx.value) / item.price) | |
logger.info("Dispensing " .. count .. " " .. item.disp .. "(s)") | |
logger.slackMention((meta.meta and meta.meta["username"] or "Someone") .. " bought " .. count .. " " .. item.disp .. "(s) from " .. config.title .. "!") | |
if (list[mcname] or 0) < count then | |
logger.warn("More items were requested than available, refunding..") | |
if meta.meta and meta.meta["return"] then | |
await(kapi.makeTransaction, config.pkey, meta.meta["return"], math.floor(tx.value - (list[mcname] * item.price)), "error=We only had '" .. (list[mcname] or 0) .. "' of item '" .. escapeSemi(meta.name) .. "' avaliable right now!") | |
end | |
count = list[mcname] | |
tx.value = math.ceil(list[mcname] * item.price) | |
end | |
if tx.value > count * item.price then | |
if meta.meta and meta.meta["return"] then | |
local refund = tx.value - (count * item.price) | |
if refund >= 1 then | |
await(kapi.makeTransaction, config.pkey, meta.meta["return"], refund, "error=You sent too much krist!") | |
end | |
end | |
end | |
if list[mcname] and list[mcname] ~= 0 then | |
dispense(mcname, count) | |
end | |
else | |
logger.warn("Payment was sent for an invalid item (" .. meta.name .. "), aborting..") | |
if meta.meta and meta.meta["return"] then | |
await(kapi.makeTransaction, config.pkey, meta.meta["return"], tx.value, "error=Item '" .. escapeSemi(meta.name) .. "' does not exist!") | |
end | |
end | |
end | |
local function wrappedWrite(surf, text, x, y, width) | |
local stX = x | |
for word in text:gmatch("%S+") do | |
if x + #word > stX + width then | |
x = stX | |
y = y + 1 | |
end | |
local col = colors.white | |
if word:upper() == word then | |
col = colors.red | |
end | |
surf:drawString(word, x, y, nil, col) | |
x = x + #word + 1 | |
end | |
end | |
local monW, monH = monPeriph.getSize() | |
local altW, altH | |
if altMonPeriph then | |
altW, altH = altMonPeriph.getSize() | |
end | |
-- Setup monitor | |
monPeriph.setPaletteColor(colors.black, 0x222f3e) | |
monPeriph.setPaletteColor(colors.blue, 0x341f97) | |
monPeriph.setPaletteColor(colors.purple, 0x5f27cd) | |
monPeriph.setPaletteColor(colors.white, 0xc8d6e5) | |
monPeriph.setPaletteColor(colors.lightGray, 0x8395a7) | |
monPeriph.setPaletteColor(colors.gray, 0x576574) | |
monPeriph.setPaletteColor(colors.red, 0xee5253) | |
if altMonPeriph then | |
altMonPeriph.setPaletteColor(colors.black, 0x222f3e) | |
altMonPeriph.setPaletteColor(colors.white, 0xc8d6e5) | |
altMonPeriph.setPaletteColor(colors.red, 0xee5253) | |
end | |
-- Draw to monitor | |
local displaySurf = surface.create(monW, monH) | |
local banner = surface.create(monW * 2, 12) | |
local warnToast = surface.create(monW * 2, 12) | |
local disclaimer = surface.create(altW, altH) | |
local function drawDisclaimer() | |
if altMonPeriph then | |
disclaimer:clear() | |
wrappedWrite(disclaimer, "If the lamp below is NOT flashing, then the shop is not open. DO NOT buy unless it is flashing!", | |
1, 1, altW - 2) | |
altMonPeriph.clear() | |
disclaimer:output(altMonPeriph) | |
end | |
end | |
function drawStock(warn) | |
displaySurf:clear() | |
do | |
banner:clear(colors.purple) | |
banner:drawText(config.title, font, 2, 3, colors.white) | |
displaySurf:drawSurfaceSmall(banner, 0, 0) | |
end | |
displaySurf:drawString("Stock", 1, 5, nil, colors.white) | |
displaySurf:drawString("Item Name", 7, 5, nil, colors.white) | |
displaySurf:drawString("Price", 26, 5, nil, colors.white) | |
displaySurf:drawString("Address", 34, 5, nil, colors.white) | |
local sortedList = {} | |
for k, v in pairs(list) do | |
sortedList[#sortedList + 1] = k | |
end | |
table.sort(sortedList, function(str1, str2) | |
str1 = config.items[fromListName(str1)].disp | |
str2 = config.items[fromListName(str2)].disp | |
local i = 0 | |
local c1, c2 | |
repeat | |
i = i + 1 | |
c1 = str1:sub(i, i):lower() | |
c2 = str2:sub(i, i):lower() | |
until i == #str1 or i == #str2 or c1 ~= c2 | |
return c1:byte() < c2:byte() | |
end) | |
local index = 0 | |
for sI = 1, #sortedList do | |
local k = fromListName(sortedList[sI]) | |
local v = tostring(list[sortedList[sI]]) | |
displaySurf:drawString(v, 6 - #v, 7 + index, nil, colors.lightGray) | |
displaySurf:drawString(config.items[k].disp or k, 7, 7 + index, nil, colors.white) | |
local pStr = config.items[k].price .. "kst/i" | |
displaySurf:drawString(pStr, 31 - #pStr, 7 + index, nil, colors.white) | |
displaySurf:drawString(config.items[k].addy .. "@" .. config.name .. ".kst", 34, 7 + index, nil, colors.lightGray) | |
index = index + 1 | |
end | |
displaySurf:fillRect(monW - 30, 4, 30, monH - 4, colors.blue) | |
wrappedWrite(displaySurf, "Welcome! To make a purchase, use /pay to send the exact amount of kst to the respective address. Excess krist will be refunded.", | |
monW - 29, 5, 29) | |
displaySurf:drawString("By @Incin", 0, monH - 1, nil, colors.gray) | |
if warn then | |
warnToast:clear(colors.red) | |
warnToast:drawText("Out of service", font, monW * 2 - surface.getTextSize("Out of service", font) - 2, 4, colors.white) | |
displaySurf:drawSurfaceSmall(warnToast, 0, monH - 4) | |
end | |
monPeriph.clear() | |
displaySurf:output(monPeriph) | |
end | |
countItems() | |
drawDisclaimer() | |
--==[[ Setup Krist APIS ]]==-- | |
local await = jua.await | |
rapi.init(jua) | |
wapi.init(jua) | |
kapi.init(jua, json, wapi, rapi) | |
local ws | |
-- jua.setInterval(countItems, 5) | |
drawStock(true) | |
--if true then error("Fix ur sockets m9") end | |
local lightVal = false | |
local lightCount = 0 | |
jua.setInterval(function() | |
lightVal = not lightVal | |
lightCount = lightCount + 1 | |
rs.setOutput("top", lightVal) | |
if lightCount > 2 then | |
lightCount = 0 | |
countItems() | |
end | |
end, 2) | |
jua.on("terminate", function() | |
if ws then ws.close() end | |
jua.stop() | |
logger.error("Terminated") | |
logger.close() | |
error() | |
end) | |
jua.go(function() | |
print("Startup!") | |
local success | |
success, ws = await(kapi.connect, config.pkey) | |
if success then | |
print("Connected to websocket.") | |
ws.on("hello", function(helloData) | |
print("MOTD: " .. helloData.motd) | |
local subscribeSuccess = await(ws.subscribe, "transactions", function(data) | |
local tx = data.transaction | |
if tx.to == config.host then | |
if tx.metadata then | |
local meta = kapi.parseMeta(tx.metadata) | |
if meta.domain == config.name then | |
logger.info("Received " .. tx.value .. "kst from " .. tx.from .. " (Meta: " .. tx.metadata .. ")") | |
if meta.name == "cloud" then | |
tx.value = 128 | |
meta.name = "mln" | |
processPayment(tx, meta) | |
else | |
processPayment(tx, meta) | |
end | |
end | |
end | |
end | |
end) | |
if subscribeSuccess then | |
print("Subscribed successfully.") | |
drawStock() | |
else | |
logger.error("Failed to subscribe.") | |
drawStock(true) | |
jua.stop() | |
end | |
end) | |
ws.on("closed", function() | |
os.reboot() | |
end) | |
else | |
logger.error("Failed to request a websocket url.") | |
drawStock(true) | |
jua.stop() | |
sleep(10) | |
os.reboot() | |
end | |
end) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment