packpico/api.lua

1156 lines
24 KiB
Lua

local flr=math.floor
-- TODO: Remove this
local scrblitMesh=love.graphics.newMesh(128, "points")
scrblitMesh:setAttributeEnabled("VertexColor", true)
local function color(c)
c=flr(c or 0)%16
pico8.color=c
setColor(c)
end
local function warning(msg)
log(debug.traceback("WARNING: "..msg, 3))
end
local function _horizontal_line(lines, x0, y, x1)
table.insert(lines, {x0+0.5, y+0.5, x1+1.5, y+0.5})
end
local function _plot4points(lines, cx, cy, x, y)
_horizontal_line(lines, cx-x, cy+y, cx+x)
if y~=0 then
_horizontal_line(lines, cx-x, cy-y, cx+x)
end
end
--------------------------------------------------------------------------------
-- PICO-8 API
local api={}
function api.flip()
flip_screen()
love.timer.sleep(1/pico8.fps)
end
function api.camera(x, y)
if x~=nil then
pico8.camera_x=flr(x)
pico8.camera_y=flr(y)
else
pico8.camera_x=0
pico8.camera_y=0
end
restore_camera()
end
function api.clip(x, y, w, h)
if x and y and w and h then
love.graphics.setScissor(x, y, w, h)
pico8.clip={x, y, w, h}
else
love.graphics.setScissor()
pico8.clip=nil
end
end
function api.cls(c)
c = tonumber(c) or 0
if c == nil then
c = 0
end
pico8.clip=nil
love.graphics.setScissor()
love.graphics.clear(c/15, 0, 0, 1)
pico8.cursor={0, 0}
end
function api.folder()
end
function api.ls()
end
api.dir=api.ls
function api.cd()
end
function api.mkdir()
end
function api.install_demos()
end
function api.install_games()
end
function api.keyconfig()
end
function api.splore()
end
function api.pset(x, y, c)
if c then
color(c)
end
love.graphics.point(flr(x), flr(y))
end
function api.pget(x, y)
x=x-pico8.camera_x
y=y-pico8.camera_y
if x>=0 and x<pico8.resolution[1] and y>=0 and y<pico8.resolution[2] then
love.graphics.setCanvas()
local c=pico8.screen:newImageData():getPixel(flr(x), flr(y))*15
love.graphics.setCanvas(pico8.screen)
return c
end
warning(string.format("pget out of screen %d, %d", x, y))
return 0
end
function api.color(c)
color(c)
end
function api.print(str, x, y, col)
if col then
color(col)
end
if x and y then
pico8.cursor[1]=flr(tonumber(x) or 0)
pico8.cursor[2]=flr(tonumber(y) or 0)
end
love.graphics.setShader(pico8.text_shader)
local str=tostring(str):gsub("[%z\1-\9\11-\31\154-\255]", " "):gsub("[\128-\153]", "\194%1").."\n"
local size=0
for line in str:gmatch("(.-)\n") do
love.graphics.print(line, pico8.cursor[1], pico8.cursor[2]+size)
size=size+6
end
if not x and not y then
if pico8.cursor[2]+size>122 then
love.graphics.setShader()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setCanvas(pico8.tmpscr)
love.graphics.draw(pico8.screen)
love.graphics.setCanvas(pico8.screen)
love.graphics.draw(pico8.tmpscr, 0, -size)
love.graphics.setColor(0, 0, 0, 1)
love.graphics.rectangle("fill", 0, pico8.resolution[2]-size, pico8.resolution[1], size)
setColor(pico8.color)
else
pico8.cursor[2]=pico8.cursor[2]+size
end
end
love.graphics.setShader(pico8.draw_shader)
end
api.printh=print
function api.cursor(x, y)
pico8.cursor={x or 0, y or 0}
end
function api.tonum(val)
return tonumber(val) -- not a direct assignment to prevent usage of the radix argument
end
function api.tostr(val, hex)
local kind=type(val)
if kind == "string" then
return val
elseif kind == "number" then
if hex then
val=val*0x10000
local part1=bit.rshift(bit.band(val, 0xFFFF0000), 16)
local part2=bit.band(val, 0xFFFF)
return string.format("0x%04x.%04x", part1, part2)
else
return tostring(val)
end
elseif kind == "boolean" then
return tostring(val)
else
return "[" .. kind .. "]"
end
end
function api.spr(n, x, y, w, h, flip_x, flip_y)
love.graphics.setShader(pico8.sprite_shader)
n=flr(n)
w=w or 1
h=h or 1
local q
if w==1 and h==1 then
q=pico8.quads[n]
if not q then
log('warning: sprite '..n..' is missing')
return
end
else
local id=string.format("%d-%d-%d", n, w, h)
if pico8.quads[id] then
q=pico8.quads[id]
else
q=love.graphics.newQuad(flr(n%16)*8, flr(n/16)*8, 8*w, 8*h, 128, 128)
pico8.quads[id]=q
end
end
if not q then
log('missing quad', n)
end
love.graphics.draw(pico8.spritesheet, q,
flr(x)+(w*8*(flip_x and 1 or 0)),
flr(y)+(h*8*(flip_y and 1 or 0)),
0, flip_x and-1 or 1, flip_y and-1 or 1)
love.graphics.setShader(pico8.draw_shader)
end
function api.sspr(sx, sy, sw, sh, dx, dy, dw, dh, flip_x, flip_y)
dw=dw or sw
dh=dh or sh
-- FIXME: cache this quad
local q=love.graphics.newQuad(sx, sy, sw, sh, pico8.spritesheet:getDimensions())
love.graphics.setShader(pico8.sprite_shader)
love.graphics.draw(pico8.spritesheet, q,
flr(dx)+(flip_x and dw or 0),
flr(dy)+(flip_y and dh or 0),
0, dw/sw*(flip_x and-1 or 1), dh/sh*(flip_y and-1 or 1))
love.graphics.setShader(pico8.draw_shader)
end
function api.rect(x0, y0, x1, y1, col)
if col then
color(col)
end
local w, h=flr(x1-x0), flr(y1-y0)
if w==0 or h==0 then
love.graphics.rectangle("fill", flr(x0), flr(y0), w+1, h+1)
else
love.graphics.rectangle("line", flr(x0)+0.5, flr(y0)+0.5, w, h)
end
end
function api.rectfill(x0, y0, x1, y1, col)
if col then
color(col)
end
if x1<x0 then
x0, x1=x1, x0
end
if y1<y0 then
y0, y1=y1, y0
end
love.graphics.rectangle("fill", flr(x0), flr(y0), flr(x1-x0)+1, flr(y1-y0)+1)
end
function api.circ(ox, oy, r, col)
if col then
color(col)
end
ox=flr(ox)+0.5
oy=flr(oy)+0.5
r=flr(r)
local points={}
local x=r
local y=0
local decisionOver2=1-x
while y<=x do
table.insert(points, {ox+x, oy+y})
table.insert(points, {ox+y, oy+x})
table.insert(points, {ox-x, oy+y})
table.insert(points, {ox-y, oy+x})
table.insert(points, {ox-x, oy-y})
table.insert(points, {ox-y, oy-x})
table.insert(points, {ox+x, oy-y})
table.insert(points, {ox+y, oy-x})
y=y+1
if decisionOver2<0 then
decisionOver2=decisionOver2+2*y+1
else
x=x-1
decisionOver2=decisionOver2+2*(y-x)+1
end
end
if #points>0 then
love.graphics.points(points)
end
end
function api.circfill(cx, cy, r, col)
if col then
color(col)
end
cx=flr(cx)
cy=flr(cy)
r=flr(r)
local x=r
local y=0
local err=1-r
local lines={}
while y<=x do
_plot4points(lines, cx, cy, x, y)
if err<0 then
err=err+2*y+3
else
if x~=y then
_plot4points(lines, cx, cy, y, x)
end
x=x-1
err=err+2*(y-x)+3
end
y=y+1
end
if #lines>0 then
for i=1, #lines do
love.graphics.line(lines[i])
end
end
end
function api.line(x0, y0, x1, y1, col)
if col then
color(col)
end
if x0~=x0 or y0~=y0 or x1~=x1 or y1~=y1 then
warning("line has NaN value")
return
end
x0=flr(x0)
y0=flr(y0)
x1=flr(x1)
y1=flr(y1)
if x0==x1 or y0==y1 then
-- simple case draw a straight line
love.graphics.rectangle("fill", x0, y0, x1-x0+1, y1-y0+1)
else
love.graphics.line(x0+0.5, y0+0.5, x1+0.5, y1+0.5)
-- Final pixel not being reached?
love.graphics.points(x1+0.5, y1+0.5)
end
end
function api.pal(c0, c1, p)
if c0==nil then
local __palette_modified=false
local __display_modified=false
local __alpha_modified=false
for i=0, 15 do
if pico8.draw_palette[i]~=i then
pico8.draw_palette[i]=i
__palette_modified=true
end
if pico8.display_palette[i]~=pico8.palette[i+1] then
pico8.display_palette[i]=pico8.palette[i+1]
__display_modified=true
end
local alpha=i==0 and 0 or 1
if pico8.pal_transparent[i]~=alpha then
pico8.pal_transparent[i]=alpha
__alpha_modified=true
end
end
if __palette_modified then
pico8.draw_shader:send('palette', shdr_unpack(pico8.draw_palette))
pico8.sprite_shader:send('palette', shdr_unpack(pico8.draw_palette))
pico8.text_shader:send('palette', shdr_unpack(pico8.draw_palette))
end
if __display_modified then
pico8.display_shader:send('palette', shdr_unpack(pico8.display_palette))
end
if __alpha_modified then
pico8.sprite_shader:send('transparent', shdr_unpack(pico8.pal_transparent))
end
elseif p==1 and c1~=nil then
c0=flr(c0)%16
c1=flr(c1)%16
if pico8.draw_palette[c0]~=pico8.palette[c1+1] then
pico8.display_palette[c0]=pico8.palette[c1+1]
pico8.display_shader:send('palette', shdr_unpack(pico8.display_palette))
end
elseif c1~=nil then
c0=flr(c0)%16
c1=flr(c1)%16
if pico8.draw_palette[c0]~=c1 then
pico8.draw_palette[c0]=c1
pico8.draw_shader:send('palette', shdr_unpack(pico8.draw_palette))
pico8.sprite_shader:send('palette', shdr_unpack(pico8.draw_palette))
pico8.text_shader:send('palette', shdr_unpack(pico8.draw_palette))
end
end
end
function api.palt(c, t)
if c==nil then
for i=0, 15 do
pico8.pal_transparent[i]=i==0 and 0 or 1
end
else
c=flr(c)%16
pico8.pal_transparent[c]=t and 0 or 1
end
pico8.sprite_shader:send('transparent', shdr_unpack(pico8.pal_transparent))
end
function api.fillp(p)
-- TODO: oh jeez
end
function api.map(cel_x, cel_y, sx, sy, cel_w, cel_h, bitmask)
love.graphics.setShader(pico8.sprite_shader)
cel_x=flr(cel_x)
cel_y=flr(cel_y)
sx=flr(sx)
sy=flr(sy)
cel_w=flr(cel_w)
cel_h=flr(cel_h)
for y=0, cel_h-1 do
if cel_y+y<64 and cel_y+y>=0 then
for x=0, cel_w-1 do
if cel_x+x<128 and cel_x+x>=0 then
local v=pico8.map[flr(cel_y+y)][flr(cel_x+x)]
if v~=0 then
if bitmask==nil or bitmask==0 or bit.band(pico8.spriteflags[v], bitmask)~=0 then
love.graphics.draw(pico8.spritesheet, pico8.quads[v], sx+8*x, sy+8*y)
end
end
end
end
end
end
love.graphics.setShader(pico8.draw_shader)
end
api.mapdraw=api.map
function api.mget(x, y)
x=flr(x or 0)
y=flr(y or 0)
if x>=0 and x<128 and y>=0 and y<64 then
return pico8.map[y][x]
end
return 0
end
function api.mset(x, y, v)
x=flr(x or 0)
y=flr(y or 0)
v=flr(v or 0)%256
if x>=0 and x<128 and y>=0 and y<64 then
pico8.map[y][x]=v
end
end
function api.fget(n, f)
if n==nil then return nil end
if f~=nil then
-- return just that bit as a boolean
if not pico8.spriteflags[flr(n)] then
warning(string.format('fget(%d, %d)', n, f))
return false
end
return bit.band(pico8.spriteflags[flr(n)], bit.lshift(1, flr(f)))~=0
end
return pico8.spriteflags[flr(n)] or 0
end
function api.fset(n, f, v)
-- fset n [f] v
-- f is the flag index 0..7
-- v is boolean
if v==nil then
v, f=f, nil
end
if f then
-- set specific bit to v (true or false)
if v then
pico8.spriteflags[n]=bit.bor(pico8.spriteflags[n], bit.lshift(1, f))
else
pico8.spriteflags[n]=bit.band(pico8.spriteflags[n], bit.bnot(bit.lshift(1, f)))
end
else
-- set bitfield to v (number)
pico8.spriteflags[n]=v
end
end
function api.sget(x, y)
-- return the color from the spritesheet
x=flr(x)
y=flr(y)
if x>=0 and x<128 and y>=0 and y<128 then
local c=pico8.spritesheet_data:getPixel(x, y)*15
return c
end
return 0
end
function api.sset(x, y, c)
x=flr(x)
y=flr(y)
c=flr(c or 0)%16
if x>=0 and x<128 and y>=0 and y<128 then
pico8.spritesheet_data:setPixel(x, y, c/15, 0, 0, 1)
pico8.spritesheet:refresh()
end
end
function api.music(n, fade_len, channel_mask)
if n==-1 then
if pico8.current_music then
for i=0, 3 do
if pico8.music[pico8.current_music.music][i]<64 then
pico8.audio_channels[i].sfx=nil
pico8.audio_channels[i].offset=0
pico8.audio_channels[i].last_step=-1
end
end
pico8.current_music=nil
end
return
end
if n>63 then
n=64
elseif n<0 then
n=0
end
local m=pico8.music[n]
local music_speed=nil
local music_channel=nil
for i=0, 3 do
if m[i]<64 then
local sfx=pico8.sfx[m[i]]
if sfx.loop_start>=sfx.loop_end then
music_speed=sfx.speed
music_channel=i
break
elseif music_speed==nil or music_speed>sfx.speed then
music_speed=sfx.speed
music_channel=i
end
end
end
if not music_channel then
return api.music(-1)
end
pico8.audio_channels[music_channel].loop=false
pico8.current_music={music=n, offset=0, channel_mask=channel_mask or 15, speed=music_speed}
for i=0, 3 do
if pico8.music[n][i]<64 then
pico8.audio_channels[i].sfx=pico8.music[n][i]
pico8.audio_channels[i].offset=0
pico8.audio_channels[i].last_step=-1
end
end
end
function api.sfx(n, channel, offset)
-- n=-1 stop sound on channel
-- n=-2 to stop looping on channel
channel=channel or -1
if n==-1 then
if channel>=0 then pico8.audio_channels[channel].sfx=nil end
return
elseif n==-2 then
if channel>=0 then pico8.audio_channels[channel].loop=false end
return
end
offset=offset or 0
if n>63 then
n=63
elseif n<0 then
n=0
end
if offset>31 then
offset=31
elseif offset<0 then
offset=0
end
if channel==-1 then
-- find a free channel
for i=0, 3 do
if pico8.audio_channels[i].sfx==nil then
channel=i
break
elseif pico8.audio_channels[i].sfx==n then
channel=i
end
end
end
if channel==-1 then return end
local ch=pico8.audio_channels[channel]
ch.sfx=n
ch.offset=offset
ch.last_step=offset-1
ch.loop=true
end
local __scrblit, __scrimg
function api.peek(addr)
addr=flr(addr)
if addr<0 then
return 0
elseif addr<0x2000 then
local lo=pico8.spritesheet_data:getPixel(addr*2%128, flr(addr/64))*15
local hi=pico8.spritesheet_data:getPixel(addr*2%128+1, flr(addr/64))*15
return hi*16+lo
elseif addr<0x3000 then
addr=addr-0x2000
return pico8.map[flr(addr/128)][addr%128]
elseif addr<0x3100 then
return pico8.spriteflags[addr-0x3000]
elseif addr<0x3200 then
local music=pico8.music[flr((addr-0x3100)/4)]
local channel=addr%4
return bit.lshift(bit.band(music.loop, bit.lshift(1, channel)), 7-channel) + music[channel]
elseif addr<0x4300 then
local sfx=pico8.sfx[flr((addr-0x3200)/68)]
local step=(addr-0x3200)%68
if step<64 then
local note=sfx[flr(step/2)]
if addr%2==0 then
return bit.lshift(bit.band(note[2], 0x3), 6)+note[1]
else
return bit.lshift(note[4], 4)+bit.lshift(note[3], 1)+bit.rshift(bit.band(note[2], 0x4), 2)
end
elseif step==64 then
return sfx.editor_mode
elseif step==65 then
return sfx.speed
elseif step==66 then
return sfx.loop_start
elseif step==67 then
return sfx.loop_end
end
elseif addr<0x5e00 then
return pico8.usermemory[addr-0x4300]
elseif addr<0x5f00 then
local val=pico8.cartdata[math.floor((addr-0x5e00)/4)]*0x10000
local shift=(addr%4)*8
return bit.rshift(bit.band(val, bit.lshift(0xFF, shift)), shift)
elseif addr<0x5f80 then
-- TODO: Hardware state
if addr==0x5f26 then
return pico8.cursor[1]
elseif addr==0x5f27 then
return pico8.cursor[2]
end
elseif addr<0x5fc0 then
-- FIXME: Persistence data
elseif addr<0x6000 then
-- FIXME: Unused but memory
elseif addr<0x8000 then
addr=addr-0x6000
local lo, hi
if __scrimg then
lo=__scrimg:getPixel(addr*2%128, flr(addr/64))*15
hi=__scrimg:getPixel(addr*2%128+1, flr(addr/64))*15
else
love.graphics.setCanvas()
local tmpscr = pico8.screen:newImageData()
lo=tmpscr:getPixel(addr*2%128, flr(addr/64))*15
hi=tmpscr:getPixel(addr*2%128+1, flr(addr/64))*15
love.graphics.setCanvas(pico8.screen)
end
return hi*16+lo
end
return 0
end
function api.poke(addr, val)
addr, val=flr(addr), flr(val)%256
if addr<0 or addr>=0x8000 then
error("bad memory access")
elseif addr<0x1000 then
local lo=val%16
local hi=flr(val/16)
pico8.spritesheet_data:setPixel(addr*2%128, flr(addr/64), lo/15, 0, 0, 1)
pico8.spritesheet_data:setPixel(addr*2%128+1, flr(addr/64), hi/15, 0, 0, 1)
elseif addr<0x2000 then
local lo=val%16
local hi=flr(val/16)
pico8.spritesheet_data:setPixel(addr*2%128, flr(addr/64), lo/15, 0, 0, 1)
pico8.spritesheet_data:setPixel(addr*2%128+1, flr(addr/64), hi/15, 0, 0, 1)
pico8.map[flr(addr/128)][addr%128]=val
elseif addr<0x3000 then
addr=addr-0x2000
pico8.map[flr(addr/128)][addr%128]=val
elseif addr<0x3100 then
pico8.spriteflags[addr-0x3000]=val
elseif addr<0x3200 then
local music=pico8.music[flr((addr-0x3100)/4)]
music[addr%4]=bit.band(val, 0x7F)
local loop=bit.lshift(1, addr%4)
if bit.band(val, 0x80)~=0 then
music.loop=bit.bor(music.loop, loop)
else
music.loop=bit.band(music.loop, bit.bnot(loop))
end
elseif addr<0x4300 then
local sfx=pico8.sfx[flr((addr-0x3200)/68)]
local step=(addr-0x3200)%68
if step<64 then
local note=sfx[flr(step/2)]
if addr%2==0 then
note[1]=bit.band(val, 0x3f)
note[2]=bit.rshift(bit.band(val, 0xc0), 6)+bit.band(note[2], 0x4)
else
note[2]=bit.lshift(bit.band(val, 0x1), 2)+bit.band(note[2], 0x3)
note[3]=bit.rshift(bit.band(val, 0xe), 1)
note[4]=bit.rshift(bit.band(val, 0x70), 4)
end
elseif step==64 then
sfx.editor_mode=val
elseif step==65 then
sfx.speed=val
elseif step==66 then
sfx.loop_start=val
elseif step==67 then
sfx.loop_end=val
end
elseif addr<0x5e00 then
pico8.usermemory[addr-0x4300]=val
elseif addr<0x5f00 then
local ind=math.floor((addr-0x5e00)/4)
local oval=pico8.cartdata[ind]*0x10000
local shift=(addr%4)*8
pico8.cartdata[ind]=bit.bor(bit.band(oval, bit.bnot(bit.lshift(0xFF, shift))), bit.lshift(val, shift))/0x10000
elseif addr<0x5f80 then
-- FIXME: Draw state
elseif addr<0x5fc0 then
-- FIXME: Persistence data
elseif addr<0x6000 then
-- FIXME: Unused but memory
elseif addr<0x8000 then
addr=addr-0x6000
local lo=val%16
local hi=flr(val/16)
if __scrblit then
table.insert(__scrblit, {addr*2%128, flr(addr/64), 0, 0, lo/15, 0, 0, 1})
table.insert(__scrblit, {addr*2%128+1, flr(addr/64), 0, 0, hi/15, 0, 0, 1})
else
setColor(lo)
love.graphics.point(addr*2%128, flr(addr/64))
setColor(hi)
love.graphics.point(addr*2%128+1, flr(addr/64))
setColor(pico8.color)
end
end
end
function api.peek4(addr)
local val = 0
val = val + api.peek(addr+0)/0x10000
val = val + api.peek(addr+1)/0x100
val = val + api.peek(addr+2)
val = val + api.peek(addr+3)*0x100
return val
end
function api.poke4(addr, val)
val=val*0x10000
api.poke(addr+0, bit.rshift(bit.band(val, 0x000000FF), 0))
api.poke(addr+1, bit.rshift(bit.band(val, 0x0000FF00), 8))
api.poke(addr+2, bit.rshift(bit.band(val, 0x00FF0000), 16))
api.poke(addr+3, bit.rshift(bit.band(val, 0xFF000000), 24))
end
function api.memcpy(dest_addr, source_addr, len)
if len<1 or dest_addr==source_addr then
return
end
-- Screen Hack
if source_addr+len-1>=0x6000 then
love.graphics.setCanvas()
__scrimg=pico8.screen:newImageData()
end
if dest_addr+len-1>=0x6000 then
__scrblit={}
if scrblitMesh:getVertexCount()<len*2 then
scrblitMesh=love.graphics.newMesh(len*2, "points")
scrblitMesh:setAttributeEnabled("VertexColor", true)
end
end
local offset=dest_addr-source_addr
if source_addr>dest_addr then
for i=dest_addr, dest_addr+len-1 do
api.poke(i, api.peek(i-offset))
end
else
for i=dest_addr+len-1, dest_addr, -1 do
api.poke(i, api.peek(i-offset))
end
end
if __scrimg then
love.graphics.setCanvas(pico8.screen)
end
if __scrblit then
scrblitMesh:setVertices(__scrblit)
scrblitMesh:setDrawRange(1, #__scrblit)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(scrblitMesh)
setColor(pico8.color)
end
__scrblit, __scrimg=nil
end
function api.memset(dest_addr, val, len)
if len<1 then
return
end
for i=dest_addr, dest_addr+len-1 do
api.poke(i, val)
end
end
function api.reload(dest_addr, source_addr, len)
end
function api.cstore(dest_addr, source_addr, len)
end
function api.rnd(x)
return math.random()*(x or 1)
end
function api.srand(seed)
math.randomseed(flr(seed*0x10000))
end
api.flr=math.floor
api.ceil=math.ceil
function api.sgn(x)
return x<0 and-1 or 1
end
api.abs=math.abs
function api.min(a, b)
if a==nil or b==nil then
warning('min a or b are nil returning 0')
return 0
end
if a<b then return a end
return b
end
function api.max(a, b)
if a==nil or b==nil then
warning('max a or b are nil returning 0')
return 0
end
if a>b then return a end
return b
end
function api.mid(x, y, z)
return (x<=y)and((y<=z)and y or((x<z)and z or x))or((x<=z)and x or((y<z)and z or y))
end
function api.cos(x)
return math.cos((x or 0)*math.pi*2)
end
function api.sin(x)
return-math.sin((x or 0)*math.pi*2)
end
api.sqrt=math.sqrt
function api.atan2(x, y)
return (0.75 + math.atan2(x,y) / (math.pi * 2)) % 1.0
end
function api.band(x, y)
return bit.band(x*0x10000, y*0x10000)/0x10000
end
function api.bor(x, y)
return bit.bor(x*0x10000, y*0x10000)/0x10000
end
function api.bxor(x, y)
return bit.bxor(x*0x10000, y*0x10000)/0x10000
end
function api.bnot(x)
return bit.bnot(x*0x10000)/0x10000
end
function api.shl(x, y)
return bit.lshift(x*0x10000, y)/0x10000
end
function api.shr(x, y)
return bit.arshift(x*0x10000, y)/0x10000
end
function api.lshr(x, y)
return bit.rshift(x*0x10000, y)/0x10000
end
function api.rotl(x, y)
return bit.rol(x*0x10000, y)/0x10000
end
function api.rotr(x, y)
return bit.ror(x*0x10000, y)/0x10000
end
function api.load(filename)
_load(filename)
end
function api.save()
end
function api.run()
_load()
end
function api.stop()
end
function api.reboot()
end
function api.shutdown()
end
function api.exit()
end
function api.info()
end
function api.export()
end
function api.import()
end
function api.help()
end
function api.time()
return pico8.frames/30
end
api.t=api.time
function api.login()
return nil
end
function api.logout()
return nil
end
function api.bbsreq()
return nil
end
function api.scoresub()
return nil, 0
end
function api.extcmd(x)
-- TODO: Implement this?
end
function api.radio()
return nil, 0
end
function api.btn(i, p)
if i~=nil or p~=nil then
p=p or 0
if p<0 or p>1 then
return false
end
return not not pico8.keypressed[p][i]
else
local bits=0
for i=0, 5 do
bits=bits+(pico8.keypressed[0][i] and 2^i or 0)
bits=bits+(pico8.keypressed[1][i] and 2^(i+8) or 0)
end
return bits
end
end
function api.btnp(i, p)
if i~=nil or p~=nil then
p=p or 0
if p<0 or p>1 then
return false
end
local init=(pico8.fps/2-1)
local v=pico8.keypressed.counter
if pico8.keypressed[p][i] and (v==init or v==1) then
return true
end
return false
else
local init=(pico8.fps/2-1)
local v=pico8.keypressed.counter
if not (v==init or v==1) then
return 0
end
local bits=0
for i=0, 5 do
bits=bits+(pico8.keypressed[0][i] and 2^i or 0)
bits=bits+(pico8.keypressed[1][i] and 2^(i+8) or 0)
end
return bits
end
end
function api.cartdata(id)
end
function api.dget(index)
index=flr(index)
if index<0 or index>63 then
warning('cartdata index out of range')
return
end
return pico8.cartdata[index]
end
function api.dset(index, value)
index=flr(index)
if index<0 or index>63 then
warning('cartdata index out of range')
return
end
pico8.cartdata[index]=value
end
local tfield={[0]="year", "month", "day", "hour", "min", "sec"}
function api.stat(x)
if x == 4 then
return pico8.clipboard
elseif x == 7 then
return pico8.fps
elseif x == 8 then
return pico8.fps
elseif x == 9 then
return love.timer.getFPS()
elseif x >= 16 and x <= 23 then
local ch=pico8.audio_channels[x%4]
if not ch.sfx then
return -1
elseif x < 20 then
return ch.sfx
else
return flr(ch.offset)
end
elseif x == 30 then
return #pico8.kbdbuffer ~= 0
elseif x == 31 then
return (table.remove(pico8.kbdbuffer, 1) or "")
elseif x == 32 then
return getMouseX()
elseif x == 33 then
return getMouseY()
elseif x == 34 then
local btns=0
for i=0, 2 do
if love.mouse.isDown(i+1) then
btns=bit.bor(btns, bit.lshift(1, i))
end
end
return btns
elseif x == 36 then
return pico8.mwheel
elseif (x >= 80 and x <= 85) or (x >= 90 and x <= 95) then
local tinfo
if x < 90 then
tinfo = os.date("!*t")
else
tinfo = os.date("*t")
end
return tinfo[tfield[x%10]]
elseif x == 100 then
return nil -- TODO: breadcrumb not supported
end
return 0
end
function api.holdframe()
-- TODO: Implement this
end
api.sub=string.sub
api.pairs=pairs
api.type=type
api.assert=assert
api.setmetatable=setmetatable
api.getmetatable=getmetatable
api.cocreate=coroutine.create
api.coresume=coroutine.resume
api.yield=coroutine.yield
api.costatus=coroutine.status
api.trace=debug.traceback
-- The functions below are normally attached to the program code, but are here for simplicity
function api.all(a)
if a==nil or #a==0 then
return function() end
end
local i, li=1
return function()
if (a[i]==li) then i=i+1 end
while(a[i]==nil and i<=#a) do i=i+1 end
li=a[i]
return a[i]
end
end
function api.foreach(a, f)
for v in api.all(a) do
f(v)
end
end
function api.count(a)
local count=0
for i=1, #a do
if a[i]~=nil then count=count+1 end
end
return count
end
function api.add(a, v)
if a==nil then return end
a[#a+1]=v
end
function api.del(a, dv)
if a==nil then return end
for i=1, #a do
if a[i]==dv then
table.remove(a, i)
return
end
end
end
return api