Log In  
Follow
Overkill

Cart #48050 | 2018-01-11 | Code ▽ | Embed ▽ | No License
11

I made this the other day when just playing around with making an ellipse function for PICO-8 https://twitter.com/eggboycolor/status/949173853045043200 -- but I've decided to post on the BBS.

The core of the effect is a scanline-based ellipse drawing function, based on an algorithm that uses Bresenham style error calculation. The code for the ellipse function is described in a paper called "A Fast Bresenham Algorithm for Drawing Ellipses" by John Kennedy, and I've ported the pseudo-code to work with pico8.

Here's the ellipse function code:

-- based on:
-- "a fast bresenham type
-- algorithm for drawing
-- ellipses" by john kennedy
function ellipse(
 cx,cy,xr,yr,c,hlinefunc
)
 xr=flr(xr)
 yr=flr(yr)
 hlinefunc=hlinefunc or rectfill
 local xrsq=shr(xr*xr,16)
 local yrsq=shr(yr*yr,16)
 local a=2*xrsq
 local b=2*yrsq
 local x=xr
 local y=0
 local xc=yrsq*(1-2*xr)
 local yc=xrsq
 local err=0
 local ex=b*xr
 local ey=0
 while ex>=ey do
  local dy=cy-y
  hlinefunc(cx-x,cy-y,cx+x,dy,c)
  dy+=y*2
  hlinefunc(cx-x,dy,cx+x,dy,c)
  y+=1
  ey+=a
  err+=yc
  yc+=a
  if 2*err+xc>0 then
   x-=1
   ex-=b
   err+=xc
   xc+=b
  end
 end

 x=0
 y=yr
 xc=yrsq
 yc=xrsq*(1-2*yr)
 err=0
 ex=0
 ey=a*yr
 while ex<=ey do
  local dy=cy-y
  hlinefunc(cx-x,cy-y,cx+x,dy,c)
  dy+=y*2
  hlinefunc(cx-x,dy,cx+x,dy,c)
  x+=1
  ex+=b
  err+=xc
  xc+=b
  if 2*err+yc>0 then
   y-=1
   ey-=a
   err+=yc
   yc+=a
  end
 end
end

[ Continue Reading.. ]

11
1 comment




This is my WIP cart for Wandering Magic, a little ys-like action rpg I was working on for #3cjam.

I ran out of time for the Jam deadline, but I tried to submit a playable work-in-progress. It will sort of abruptly end once you complete a specific quest, but the game up to that point should be playable.

O (Z on keyboard) = opens the menu
X (X on keyboard) = use magic attack (if you have it)

The Great Fiend has returned, and you need to stop them before it's too late. Along the way, you fight monsters, meet various people who will help you on your travels.

Walk into enemies to use your melee attack.
Defeating enemies gives you money to buy new items.
If you defeat enough enemies you will level up, which also restores your HP and gradually gives you small stat boosts.
Buy items to increase your attack and defense.
Be ready to use cure potions during battles and save often.
This is a bit more of an RPG than an action game, so evading attacks while important isn't the only key to survival. Boosting your attack/defense and healing is required to progress.

Anyway, I hope to release a more complete version some day, I just ran out of time for this jam. Hope you enjoy!

17
7 comments




A cover of the boss theme from Lufia II: Rise of the Sinistrals. Only the first 25 seconds or so. Not sure if I'll finish this, or if it's even possible to fit the whole 1 minute loop within the pattern limit of pico8, but I had fun messing with the tracker for a bit.

0 comments



This quirk exists in Pico8 0.1.10C, and possibly other versions. Try this code out, and you'll see the results for i>=16 are the same:

for i=1,20 do
 print(i .. ' ' .. 1.11^i)
end

1.11 raised to the 20th power (equal to ~8.062) is still well within the overflow limits of pico8, but currently pico8 limits the possible exponent to 16 (resulting in ~5.309).

Here's a workaround for raising to higher power (if y is a positive power), that takes advantage of the property x^m * x^n = x^(m+n):

function pow(x,y)
local r=1
while y>0 do
r*=x^min(y,16)
y-=16
end
return r
end

Using this you can get the correct results. Just wondering, is this limitation intentional? It makes sense for integers, because 2^15-1 is the greatest positive integer you can express, and occasionally you might want to do 2^16 (if you're careful about the sign bit), but for fractions it seems a little strange. It's kind of uncommon to do this kind of math in pico8, so I could maybe live with it being a quirk of pico-8 though. If this behaviour isn't changed, could this limitation be documented in the manual?

1
4 comments



Hey everyone! I made a some little helper functions for allowing some basic handling of positive integers greater than 32767. Feel free to expand/modify these. Use for whatever, no attribution required.

This could be useful for a score counter in a game or experience points in an RPG. There's a minor overhead in code-size and in performance, so these are when you really need a little bit of of extra precision for your numbers.

Unsigned Integer Decimal Strings

This first method handles numeric strings holding arbitrary unsigned integers. I haven't added sign handling or fraction handling, so this is for unsigned integers only, but may come in handy for score / experience counter sort of things. I also added a comparison function so you can tell if a decimal string is greater than/less than/equal to another one.

The disadvantage is number of allocations required for calculating intermediate results, lots of concenation and substring ops. But if you're doing this for non-time critical functionality, and want the extra digits, it's pretty handy.

function decdigit(s,i)
 local c=sub(s,-i,-i)
 return c~='' and 0+c or 0
end

function declen(s)
 for i=1,#s do
  if(sub(s,i,i)~='0')return #s-i+1
 end
 return 1
end

function decadd(a,b)
 local s=''
 local c=0
 for i=1,max(declen(a),declen(b)) do
  local v=decdigit(a,i)+decdigit(b,i)+c
  c=0
  if(v>9)v-=10 c=1
  if(#s>0 or v>0 or c>0 or i==1)s=v..s
 end
 if(c>0)s='1'..s
 return s
end

function deccmp(a,b)
 local m=declen(a)
 local n=declen(b)
 if(m<n)return -1
 if(m>n)return 1
 for i=1,n do
  local u=decdigit(a,i)
  local v=decdigit(b,i)
  if(u<v)return -1
  if(u>v)return 1
 end
 return 0
end

-- Addition test cases
print(decadd('1','1')) -- 2
print(decadd('999','1')) -- 1000
print(decadd('123456789','99999')) -- 123556788
print(decadd('10009','20009')) -- 30018

-- Comparison test cases
print(deccmp('2000','200')) -- 1 (a>b)
print(deccmp('2000','1000')) -- 1 (a>b)
print(deccmp('2000','2000')) -- 0 (equal)
print(deccmp('1','00001')) -- 0 (equal)
print(deccmp('200','2000')) -- -1 (a<b)
print(deccmp('1000','2000')) -- -1 (a<b)

[ Continue Reading.. ]

2
2 comments



Interesting discovery today: Because the latest version of pico8 supports more punctuation characters, it's now possible to encode binary strings as base64!

I prototyped this on pico8 0.1.8. By using this we can slightly improve the size of encoding binary data in text form, it's 1.33333x bigger compared to hex's 2x size. Granted, pico8 does some compression to the code, so not sure if the added entropy of packing things closer together will work in everyone's favor, but this is worth a shot. It won't be standard base64, because there's no distinction between uppercase and lowercase, but there's enough special characters to make up a custom 64-character dictionary for the encoding.

-- Set up the encoding/decoding lookup tables.
b64d={['']=0}b64e={}b64c='0123456789abcdefghijklmnopqrstuvwxyz-_+[]{}|:;=.<>?/~`!@#$%^&*()'
for i=1,#b64c do
local c=sub(b64c,i,i)
b64e[i-1]=c
b64d[c]=i-1
end

-- encodes a table containing 8-bit numbers (binary data) into a base64 string
function encode(t)
local s,e='',b64e
for i=1,flr(#t/3)*3,3 do
s=s..e[flr(t[i]/4)]..e[bor(t[i]%4*16,flr(t[i+1]/16))]..e[bor(t[i+1]%16*4,flr(t[i+2]/64))]..e[t[i+2]%64]
end
if(#t%3==1)s=s..e[flr(t[#t]/4)]..e[t[#t]%4*16]
if(#t%3==2)s=s..e[flr(t[#t-1]/4)]..e[bor(t[#t-1]%4*16,flr(t[#t]/16))]..e[t[#t]%16*4]
return s
end

-- decodes a base64 string into a table of numbers
function decode(s)
local t,d={},b64d
for i=1,#s,4 do
local x,y,z,w=sub(s,i,i),sub(s,i+1,i+1),sub(s,i+2,i+2),sub(s,i+3,i+3)
add(t,bor(d[x]*4,flr(d[y]/16)))
if(#z>0)add(t,bor(d[y]%16*16,flr(d[z]/4)))
if(#w>0)add(t,bor(d[z]%4*64,d[w]))
end
return t
end

-- testing it out
for i in all(decode(encode{1,2,3,4,5}))do
print(i)
end

[ Continue Reading.. ]

5
4 comments



Cart #20597 | 2016-05-15 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
18

I made a map maker for Pico-8! Unlike the built in map editor, it represents maps a little differently -- the format is a collection of rectangular tile fills + a bunch of entities you can place on top. It saves all maps together in a custom data format to the cartridge's shared map/tileset area. It has undo, redo functionality on the tile layer. And there's fun sfx as you edit stuff! The main benefit of this tool is that for maps with simpler layouts, expressing a map as a set of rectangular fills is much smaller than a 16x16 tilemap, so you can hold a lot more screens of map data than using the direct map editor. The less objects, the less map space required.

Anyone can feel free to alter this or reuse bits for their own maps. I made this for a little rpg project I've been planning out, and just to have fun. In-game instructions are included, but assume a default keyboard layout, rather than describing things in terms of player 1 and player 2 buttons. But I've included them here as well, for good measure!

DRAW MODE
Z = draw
X = undo
S = redo
A = tileset

ENTITY MODE
Z = draw
A = tileset

MENUS
Z = acccept
X = cancel

OTHER
F = file menu
E = toggle between draw and entity mode

There's no import/export cart functionality, but it's easy enough to add with reload()/cstore(), or just copying the data out of the gfx section in the p8 file.

18
6 comments



I posted this PICO-8 theme + syntax coloring file for Sublime Text 2 if anybody wants! The syntax highlighting supports PICO-8 Lua code including the PICO-8 library functions. The color scheme uses the PICO-8 palette, and gets pretty close to the look of the editor.

Finally, for an extra touch, I included a settings file to use an unofficial TTF Font by RhythmLynx, reduce the font size, disable anti-aliasing, increase line padding, and force indentation to be a single space like in native PICO-8. (Uses a font size of 7.5, but a font size of 3.5 seems to be a 1x scale font, for those who want extremely hi-res views of their code)

You can get it here!

4
5 comments



Hey, just thought I'd post this code in case anyone else finds it useful.

If you need a few extra sfx slots in your pico8 cart and can spare the code, here's a little thing that will load a sound into RAM. It takes a table of 168 4-bit values in the .p8 sfx format, and writes out a 68-byte sfx block into one of the sfx slots in memory.

It's a complete test program, tweak, adjust and reuse to your liking.

-- unpack a hex string into a
-- table of numbers.
function unhex(s,n)
 n = n or 2
 local t = {}
 for i = 1, #s, n do
  add(t, ('0x' ..
   sub(s, i, i+n-1)) + 0)
 end
 return t
end

-- unpack a data string
-- including two sounds
-- taken out of a p8 file.
sfxdata = unhex(
   '010800003c6751f473234731347'
.. '3006253c615006253c6050c6150'
.. '00000c615000000000000000000'
.. '000000000000000000000000000'
.. '000000000000000000000000000'
.. '000000000000000000000000000'
.. '000000010300003c63515073182'
.. '701f271232711c2711827113271'
.. '0e2710021124300243002430024'
.. '300243002430024300243002430'
.. '024300243002430024300243002'
.. '430024300243002430024300243'
.. '002430024300', 1)

-- load a new sound into sfx
-- memory at runtime.
--
-- arguments:
-- d = dest sfx slot 0 .. 63
-- s = source sfx number in
--     sfxdata table, 0-based
--     index. each sound is 168
--     values of the table.
-- note: sfxdata must exist as
--       a global or upvalue.
function loadsfx(d, s)
 local t = sfxdata
 d = 0x3200 + 68 * d
 s *= 168
 for i = 0, 3 do
  poke(d + 64 + i, t[s + i * 2 + 1] * 16 + t[s + i * 2 + 2])
 end
 for i = 0, 31 do
  local a = d + i * 2
  local b = s + i * 5 + 9
  poke(a, (t[b] * 16 + t[b + 1]) % 64 + t[b + 2] * 64)
  poke(a + 1, band(t[b + 2], 4) / 4 + t[b + 3] % 8 * 2 + t[b + 4] * 16)
 end
end

-- load sound 0 from sfxdata
-- into the sfx ram at slot #2.
loadsfx(2, 0)
-- play sfx slot 2.
sfx(2)

-- loop forever so we get
-- to hear the sound play.
while true do flip() end

[ Continue Reading.. ]

9
1 comment