pico-8 cartridge // http://www.pico-8.com version 41 __lua__ -- panel extreme -- by kistaro windrider ------------------------------- -- bsd 0-clause license ------------------------------- -- copyright 2023 kistaro windrider -- -- permission to use, copy, -- modify, and/or distribute -- this software for any -- purpose with or without fee -- is hereby granted. -- -- the software is provided -- "as is" and the author -- disclaims all warranties -- with regard to this software -- including all implied -- warranties of merchantability -- and fitness. in no event -- shall the author be liable -- for any special, direct, -- indirect, or consequential -- damages or any damages -- whatsoever resulting from -- loss of use, data or profits, -- whether in an action of -- contract, negligence or -- other tortious action, -- arising out of or in -- connection with the use or -- performance of this software. ------------------------------- ------------------------------- -- credits - ------------------------------- -- wysiwyg p8scii control - -- code editor (used for all - -- fancy text) -- eeooty - -- https://www.lexaloffle.com/bbs/?tid=52315 ------------------------------- -- major, release, revision -- change major version to break -- save compatibility version={0,0,1} ------------------------------- -- globals ------------------------------- -- mode: table -- {:update(), :draw()}. -- receives frame callbacks. -- version: [3]int constant. -- major, minor, release. -- change major version to -- break save compatibility. -- constant. ------------------------------- function _init() cartdata("panel_extreme"..version[1]) -- hi-color mode: 15 is bg -- set color 15 to gradient background poke(0x5f5f,0x3f) --blend lines memset(0x5f70,0xaa,16) mode = init_title() end function _update60() mode:update() end function _draw() mode:draw() end -- if t[mname] is a thing, -- invoke it as a method. else, -- try each object in t function outer_or_each_opt(t, mname) local fun = t[mname] if fun then fun(t) return end foreach(t, function(o) local f = o[mname] if(f) f(o) end) end -- 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 mutables. function mknew(tt, more) local mt = {__index=tt} -- check "more" only once ever if more then tt.new = function(ret) if (not ret) ret = {} more(ret) setmetatable(ret, mt) return ret end else tt.new=function(ret) if (not ret) ret = {} setmetatable(ret, mt) return ret end end end -->8 -- title -- (currently a placeholder) function init_title() --background gradient pal({[0]=0x82,0x82,0x84,0x84,4,4,0x89,0x89,0x8e,0x8e,0x8f,0x8f,15,15,0x87,0x87},2) return view.of{ bg.new{c=15}, view.new{ views=printz"⁶j30⁵ii⁶w⁶tᶜ9a e\n⁶j00⁵ijᶜcp n l\n⁶j04³iᶜ2extreme\n⁶j04³i⁶=ᶜeextreme\n⁶j04³hᶜ8extreme\n\0", update = function(self) self.y=20+8*sin(time()/2.1) self.x=32+6*cos(time()/3.7) end, }, } end -->8 -- window manager ------------------------------- -- view ------------------------------- -- composits drawable items. -- add items to .views to -- composit them. x and y are -- relative reverse camera -- offsets. drawable items will -- observe appropriate incoming -- camera and clip state. -- clipping respects existing -- clipping so stacked views -- intersect. ------------------------------- view = { x=0, y=0, w=128, h=128, } mknew(view, function(x) if (not x.views) x.views = {} end) function view.of(subviews) return view.new{views=subviews} end function view:update() outer_or_each_opt(self.views, "update") end function view:draw() local oldcam, oldclip = $0x5f28, $0x5f20 poke2(0x5f28, %0x5f28-self.x) poke2(0x5f2a, %0x5f2a-self.y) clip(-%0x5f28, -%0x5f2a, self.w, self.h, true) local v = self.views outer_or_each_opt(self.views, "draw") poke4(0x5f20, oldclip) poke4(0x5f28, oldcam) end -- draws opaque rectangles. -- default bg is equivalent to -- clip-aware cls. -- restores prior fill palette. bg = { x=0,y=0,w=128,h=128,fp=0,c=0 } mknew(bg) function bg:draw() local oldfp=fillp(self.fp) rectfill(self.x,self.y,self.x+self.w,self.y+self.h,self.c) fillp(oldfp) end ------------------------------- -- animator ------------------------------- -- animator is a drawable item -- that manages a linked list of -- temporary drawables. a -- temporary drawable is dropped -- when its :update returns -- a non-false value. ------------------------------- animator = {} mknew(animator) function animator:update() local p,n=self, self.next while n do if n.item:update() then local nn = n.next if (not nn) self.tail = p p.next = n.next else p = n end n = p.next end end function animator:draw() local n = self.next while n do n.item:draw() n = n.next end end -- add a new drawable under all -- existing other items. -- (it draws first) function animator:push_under(i) self.next={next=self.next, item=i} end -- add a new drawable over all -- existing other items. -- (it draws last) function animator:push_over(i) local target, node = self.tail or self, {item=i} target.next=node self.tail=node end -->8 -- panel drawing -- empty panel placeholder nopanel = {} -- panel in ordinary state panel = { -- plt: palette as table -- sx, sy: ulc of emblem on -- sprite sheet -- ey: y offset for emblem; -- usually 2, can wiggle -- as: animation state -- 0 == plain -- negative == mid-fall -- positive == bounce -- frames left state="idle" } mknew(panel) jiggle = split"3,3,3,2,2,2,3,3,2,2,1,1,2,2,3,3,2,1" -- palettes for blocks: -- lit, bg, shadow, emblem. -- declared separately to -- avoid reconstructiong for -- every panel spawned redblkp = {9,8,3,7} ylwblkp = {10,9,4,7} grnblkp = {6,11,3,7} blublkp = {7,12,13,7} prpblkp = {14,13,2,7} newblock = { function() return panel.new{ id=1, plt=redblkp, sx=83,sy=1, ey=2, as=0, } end, function() return panel.new{ id=2, plt=ylwblkp, sx=83,sy=11, ey=2,as=0, } end, function() return panel.new{ id=3, plt=grnblkp, sx=78,sy=11, ey=2,as=0, } end, function() return panel.new{ id=4, plt=blublkp, sx=83,sy=6, ey=2,as=0, } end, function() return panel.new{ id=5, plt=prpblkp, sx=73,sy=11, ey=2,as=0, } end, } function panel:update() local dat_as = self.as if (dat_as < 0) self.ey = 1 if (dat_as == 0) self.ey = 2 if dat_as > 0 then self.ey = jiggle[dat_as] self.as -= 1 end end function panel:draw() pal(self.plt) sspr(72,0,9,9) sspr(self.sx,self.sy, 5,5, 2,self.ey) end -->8 -- general drawing function printz(str) return {draw=function() print(str, 0, 0) end} end -- horizontal marching ants ants = { w=60, h=1, stride=10, -- bgcolor=0, --nil: transparent ldcolor=7, fgcolor=6, -- trcolor=5, --nil: no trailer speed=2/60, offset=0, progress=0.0, } mknew(ants) function ants:update() local stride = self.stride local off = self.offset + self.speed * stride if (off > stride) off -= stride self.offset = off end function ants:draw() local w,h,s,p,bg=self.w, self.h, self.stride,self.progress,self.bgcolor if (bg) rectfill(0,0,w,h,bg) if (p<=0) return local x0 = (self.offset-s)&-1 local bw = (p*s)&-1 if bw > 1 then local bwm2,c = bw-2,self.fgcolor for x=x0,w,s do rectfill(x,0,x+bwm2,h,c) end end if bw > 0 then local c = self.ldcolor for x=x0+bw-1,w,s do rectfill(x,0,x,h,c) end end local c = self.trcolor if p < 1 and c then for x = x0-1+s,w,s do rectfill(x,0,x,h,c) end end end -->8 -- gameplay logic game={ incoming_frac = 0, speed = 0, gravity_lag = 30, pop_lag = 20, stop_frames = 0, stop_max = 1, life = 180, life_max = 180, } mknew(game, function(ret) local board = {} for i=1,12 do board[i] = { nopanel, nopanel, nopanel, nopanel, nopanel, nopanel, } end ret.board = board ret.garbage_queue = {} ret.garbage_source = source.new() ret.floor_source = source.new() end) function game:update() end function game:draw() end __gfx__ 0000000000111000011011000000000009999900066666000eeeee00077777000aaaaa0001111111000000000000000000000000000000000000000000000000 00000000012221001221221000000000988888206bbbbb30eddddd207cccccd0a999994012222222300040400000000000000000000000000000000000000000 00700700121110000112110000000000987878206b77bb30edd7dd207c7cccd0a979794012222222300444440000000000000000000000000000000000000000 00077000121000000012100000000000987778206b777b30ed777d207c77ccd0a997994012222222300444440000000000000000000000000000000000000000 00077000121000000001000000000000988788206bb77b30edd7dd207c777cd0a979794012222222300044400000000000000000000000000000000000000000 00700700010000000001000000000000988888206bbbbb30eddddd207cccccd0a999994012222222300004000000000000000000000000000000000000000000 000000000000000000121000000000000222220003333300022222000ddddd000444440012222222300444440000000000000000000000000000000000000000 00000000000000000001000000000000000000000000000000000000000000000000000012222222300444400000000000000000000000000000000000000000 00000000000000000000000000000000011111004040440004000400000000000000000003333333000444000000000000000000000000000000000000000000 00000000000000000000000000000000122222304440444044404440000000000000000000000000000440000000000000000000000000000000000000000000 00000000000000000000000000000000122222300400044040400400007070000000000000000000000400000000000000000000000000000000000000000000 00000000000000000000000000000000122222300000000000000000007070000000000000040004440004000000000000000000000000000000000000000000 00000000000000000000000000000000122222304040400004004440000000000000000000444044444044400000000000000000000000000000000000000000 00000000000000000000000000000000122222300400440040404040007770000000000004444444444044400000000000000000000000000000000000000000 00000000000000000000000000000000033333004040444004004440000000000000000000444044444444440000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000040004440444440000000000000000000000000000000000000000 __map__ 0102020202020203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1112121212121213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 2122222222222223000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000