-- title: kistaro-compositor
-- author: Kistaro Windrider (kistaro@gmail.com)
-- desc: Rendering compositor library. Includes Pico-8 camera features.
-- site: https://dragon.style/@kistaro
-- license: MIT License
-- version: 0.1
-- script: lua
function BOOT()
end
function TIC()
end
--stdlib--
-- generate standard "overlay"
-- constructor for type tt.
-- if more is defined, generated
-- new calls more(ret) after ret is
-- definitely not nil, before calling
-- setmetatable. Use to initialize
-- mutable items.
function MkNew(tt, more)
local mt = {__index=tt}
-- check "more" only once ever
if more then
tt.new = function(ret)
ret = ret or {}
more(ret)
setmetatable(ret, mt)
return ret
end
else
tt.new = function(ret)
ret = ret or {}
setmetatable(ret, mt)
return ret
end
end
end
--Window compositor--
CamCtl = {
XOff = 0,
YOff = 0,
XAbsClip = 0,
YAbsClip = 0,
W = 240,
H = 136,
OriginStack = {},
ClipStack = {},
}
-- Overwrite current camera origin.
-- (0, 0) is now this pixel coordinate.
function CamCtl:SetAbsOrigin(x, y)
self.XOff, self.YOff = x, y
end
-- Push current camera origin onto
-- the history stack and set the
-- new origin.
function CamCtl:PushAbsOrigin(x, y)
table.insert(self.OriginStack, {self.XOff, self.YOff})
self.XOff, self.YOff = x, y
end
function CamCtl:SetRelOrigin(x, y)
self.XOff = self.XOff + x
self.YOff = self.Yoff + y
end
function CamCtl:PushRelOrigin(x, y)
table.insert(self.OriginStack, {self.XOff, self.YOff})
self.XOff = self.XOff + x
self.YOff = self.YOff + Y
end
function CamCtl:SetAbsCam(x, y)
self.XOff, self.YOff = -x, -y
end
function CamCtl:PushAbsCam(x, y)
table.insert(self.OriginStack, {self.XOff, self.YOff})
self.XOff, self.YOff = -x, -y
end
function CamCtl:SetRelCam(x, y)
self.XOff = self.XOff - x
self.YOff = self.YOff - y
end
function CamCtl:PushRelCam(x, y)
table.insert(self.OriginStack, {self.XOff, self.YOff})
self.XOff = self.XOff - x
self.YOff = self.YOff - y
end
function CamCtl:PopOrigin()
if #self.OriginStack == 0 then
self.XOff, self.YOff = 0, 0
return
end
self.XOff, self.YOff = unpack(table.Remove(self.OriginStack))
end
CamCtl.PopCam = CamCtl.PopOrigin
function CamCtl:SetNewClip(x, y, w, h)
if x then
self.XAbsClip = self.XOff + x
self.YAbsClip = self.YOff + y
self.W, self.H = w, h
clip(self.XAbsClip, self.YAbsClip, self.W, self.H)
return
end
self.XAbsClip, self.YAbsClip, self.W, self.H = 0,0,240,136
clip()
end
function CamCtl:PushNewClip(x, y, w, h)
table.insert(self.ClipStack, {self.XAbsClip, self.YAbsClip, self.W, self.H})
self:SetNewClip(x, y, w, h)
end
function CamCtl:SetReClip(x,y,w,h)
if not x then
return
end
x = x + self.XOff
local x2 = math.min(x + w, self.XAbsClip + self.W)
self.XAbsClip = math.max(x, self.XAbsClip)
self.W = max(0, x2 - self.XAbsClip)
y = y + self.YOff
local y2 = math.min(y+h, self.YAbsClip + self.H)
self.YAbsClip = math.max(y, self.YAbsClip)
self.H = max(0, y2 - self.YAbsClip)
clip(self.XAbsClip, self.YAbsClip, self.W, self.H)
end
function CamCtl:PushReClip(x,y,w,h)
table.insert(self.ClipStack, {self.XAbsClip, self.YAbsClip, self.W, self.H})
self.SetReClip(x,y,w,h)
end
function CamCtl:PopClip()
if #self.ClipStack == 0 then
self.XAbsClip, self.YAbsClip, self.W, self.H = 0,0,240,136
clip()
return
end
local x,y,w,h = unpack(table.remove(self.ClipStack)))
self.XAbsClip, self.YAbsClip, self.W, self.H = x,y,w,h
clip(x,y,w,h)
end
-- Install
-- (swizzle all drawing functions!)
function CamCtl:Install()
local adjust_leading_2 = {
"circ", "circb",
"elli", "ellib",
"pix",
"rect", "rectb",
}
for _, fn in ipairs(adjust_leading_2) do
local f_orig = _G[fn]
_G[fn] = function(x, y, ...)
return f_orig(x + self.XOff, y + self.YOff, ...)
end
end
local keep_first_adjust_2_opt = {
"spr", "font", "print"
}
for _, fn in ipairs(keep_first_adjust_2_opt) do
local f_orig = _G[fn]
_G[fn] = function(v, x, y, ...)
if x then
x = x + self.XOff
end
if y then
y = y + self.YOff
end
return f_orig(v, x, y, ...)
end
end
local tri_family = {"tri", "trib", "textri"}
for _, fn in ipairs(tri_family) do
local f_orig = _G[fn]
_G[fn] = function(x1, y1, x2, y2, x3, y3, ...)
return f_orig(
x1 + self.XOff, y1 + self.YOff,
x2 + self.XOff, y2 + self.YOff,
x3 + self.XOff, y3 + self.YOff,
...
)
end
end
local map_orig = map
map = function(mx, my, w, h, sx, sy, ...)
XXX(kistaro): implement!
-- if called with no args, calculate new map
-- coordinates, clipping at 0, to respect
-- camera movement! this will require full tile
-- movement to be reflected in mx/my, w and h to
-- stay 30 x 17 unless they go to 31 x 18 for
-- partial tiles at edges, and sx and sy to become
-- offsets. If sx and sy are provided, do the
-- obvious thing instead, which is much easier.
end
local line_orig = line
line = function(x0, y0, x1, y1, color)
return line_orig(
x0 + self.XOff, y0 + self.YOff,
x1 + self.XOff, y1 + self.YOff,
color
)
end
end
--
-- 000:0222220021111110212121102122211021121110211111100111110000000000
-- 001:0bbbbb00b9999980b999b980b99bb980b9bbb980b99999800888880000000000
-- 002:0555550056666670565566705655567056655670566666700777770000000000
-- 003:0444440043333320433433204344432043434320433333200222220000000000
-- 004:0ddddd00deeeeef0dededef0deedeef0dededef0deeeeef00fffff0000000000
--
--
-- 000:00000000ffffffff00000000ffffffff
-- 001:0123456789abcdeffedcba9876543210
-- 002:0123456789abcdef0123456789abcdef
--
--
-- 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000
--
--
-- 000:100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
--
--
-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f4be69ce61309940205d
--