This is a cut down version of the variable inspector I wrote a while ago. It's a bit more limited, but a lot more light weight. And there's a way to use it even if you have no tokens to spare (described below).
You use it to view Lua table variables:
- Run your game
- Press Esc to break into it
- Type: dinsp(variable)
Then use the mouse to expand values and scroll wheel to scroll up/down.

Code snippet follows (for some reason I can't use the code formatting tags):
poke(0x5f2d,3)cursor()pal()clip()function dadd(n,v,i,p)add(lines,{n=n,v=v,x=type(v)=="table"and"+"or" ",i=i},p) end function dexp(l,p)if l.x=="+" then for n,v in pairs(l.v) do p+=1 dadd(n,v,l.i.." ",p)end l.x="-"end end
function dinp()local x,y,b,w=stat(32),stat(33),stat(34),stat(36)line(x,y,x,y+2,10)if btnp(❎) then local i=(y-10)\6+1 local p=s+i local l=lines

UPDATE: The dungeon generation logic has been revamped. Rooms are not always rectangular, and can often have a bit more cover (or hiding places for monsters). Also added a couple of new enemy types. Gold has been removed (I needed the extra tokens), but it didn't really do anything anyway.
Grab your magic staff and venture deep into the demon realm to defeat Bahmott and his evil servants!
Trial of the Sorcerer is a procedurally generated 3D first person shooter inspired by Wolfenstein 3D and Catacomb Abyss.
Shoot monsters, collect loot, find keys to unlock doors, and try to find the exit to the next level.
And don't forget to pickup the power crystals to level-up your magic staff along the way.












Still needs quite a bit of finishing off.
I'm posting it now mainly so I can play it on my phone :-)
Levels are randomly generated. Shoot monsters, collect loot, find keys to unlock doors and reach the exit.







Strap into one of the world's fastest race cars and blast around the legendary Bronzerock circuit like it's 1988!
...or something - there's not a lot of physical or historical accuracy in this game :)
Z/X=accelerate/brake
Left/right=steering
Driving off road slows you down. Driving too fast around corners causes you to slide.
You can use the pause menu to view your last/best lap times (my best so far is 1:43.23)
This is work-in-progress, so some caveats:
- There are no other cars yet.
- Lots of things need tweaking/adjusting.



This isn't really a game - unless you consider it a short "Walking simulator" - it's more of a tech demo.
The engine is a basic Wolfenstein-3D like 3D engine. It has floor and ceiling textures and render reasonably sized and complex rooms at 60 frames-per-second, in a 128x96 viewport.
- Arrow keys = move
- X = toggle map mode
If anyone feels like something out of it, it's fairly easy to get started with (details below).










A little arcady "flight sim" I started writing.
Buzz past randomly generated islands in a wrap-around ocean.
I will probably add stuff to blow up eventually :)
There is no throttle! Just steer with arrows.





This is Mot's 8-Ball Pool, a little pool simulation inspired by 3D pool on the C64 and Amiga.
Shoot a round of pool against a friend, or one of the 7 different AI characters.
UI
The UI at the bottom of the screen shows how each player is progressing.
The player's name flashes when it is their turn.
The color they must sink is displayed next to their name, and the balls already sunk are displayed above.
A white ball indicates they have a free ball.

When player fouls, the reason for the foul is displayed in a scrolling message along the bottom of the screen.







 (1).gif)


Not sure if anyone has posted a smap() yet, but it's reasonably straightforward to implement with tline, so here's mine.
Parameters are:
cx,cy,cw,ch Specify a region in the tile map. Measured in tiles.
sx,sy,sw,sh Region on the screen to map to.
flipx,flipy Whether to flip horizontally and/or vertically.
layers Layer flags. Same as for map().
You need to supply at least the c and s parameters.
function smap(cx,cy,cw,ch,sx,sy,sw,sh,flipx,flipy,layers) -- negative screen sizes if(sw<0)flipx=not flipx sx+=sw if(sh<0)flipy=not flipy sy+=sh sw,sh=abs(sw),abs(sh) -- delta local dx,dy=cw/sw,ch/sh -- apply flip if flipx then cx+=cw dx=-dx end if flipy then cy+=ch dy=-dy end -- clip if(sx<0)cx-=sx*dx sx=0 if(sy<0)cy-=sy*dy sy=0 if(sw>128)sw=128 if(sh>128)sh=128 -- render with tlines -- pick direction that results -- in fewest tline calls if sh<sw then -- horizontal lines for y=sy,sy+sh-1 do tline(sx,y,sx+sw-1,y,cx,cy,dx,0,layers) cy+=dy end else -- vertical lines for x=sx,sx+sw-1 do tline(x,sy,x,sy+sh-1,cx,cy,0,dy,layers) cx+=dx end end end |

Mot's Animation System
Mot's Animation System is a visual editor for creating 2D animations.
You start by sequencing sprites and/or tilemaps together to create the basic components. Then combine them using timelines and key-frames to make animations. Or combine them again to make bigger, more complex animations.
Animations can trigger sounds and music, and call back into to your main program, e.g. to trigger game play events.
You could use it for:
- Character animations, like run cycles
- Larger multi-sprite animated characters
- Cutscenes
- Animated birthday cards
Animations can be loaded and played in your own carts (the loading and playback code is 1836 tokens). They can either be stored in Lua strings or loaded from a separate cart.







OK, this may be a bit bonkers, but I figured out how to play Ramps with a racing wheel, with proper analogue steering and acceleration/braking.
It's a little involved and only works on Windows.
Mouse input
First, you need a version of Ramps (or whatever game you plan to play) edited to accept mouse input. Like this version:
WARNING: Attempting to drive with an actual mouse may cause high blood pressure and throwing things.
Also I've only tried this in PICO-8 itself - not sure if it works when running in a browser.
Joystick -> mouse
Next, you need a utility to convert game controller input to mouse movement.
I'm a bit new here, so I don't know if adding network capability has previously been discussed (?)
But it feels to me some simple UDP send/receive instructions would add some cool functionality, without adding too much complexity.
Perhaps something like:
[write message to RAM] poke4([ip-locn],ip) poke2([port-locn],port) send(0x4300,length) |
Where [ip-locn],[port-locn] would be some special reserved locations for IP address and port.
UDP packets are usually small. I've seen <=512 bytes recommended in places, which easily fits into the user RAM address range.
And an IPV4 address would fit in a number variable, and would be adequate for LAN play.
Receive could be:
length=recv(0x4300,maxlength) [read message from RAM] |
Returning 0 if nothing is waiting.
It could also populate [ip-locn] with the return IP address so that replying would be straightforward.
Perhaps [port-locn] could be pre-populated with a default port number (0x1C08 ?).

Ramps
This is Ramps! A 3D racer inspired by Powerdrift, Stunt Car Racer, Hard Drivin'.
Choose from 8 varied race tracks, and overtake the other drivers in 5 laps to win.
As well as racing you'll need to navigate jumps, roller-coaster like ramps and occasionally even drive upside down.
Includes 3 difficulty levels, plus a practice mode for getting the feel of the tracks.




This all started as an experiment to see how well Pico-8 could render a Powerdrift-like scaled sprite racetrack (pretty well, as it turns out). Then it was about trying to implement a driving model that could handle the ramps, jumps and loops. And finally wrapping it up into a somewhat finished game.










Instant 3D plus!

Instant 3D! was a random idea, quickly thrown together to see if it was possible. But after seeing the cool things people can do with it, I wanted to clean it up properly, and also present some of the internal functions more cleanly.
Making the 3D functions more accessible means:
- You can often get your game working correctly in 3D even if the Instant 3D "magic" doesn't work correctly, by calling the 3D spr/map functions directly with the right parameters.
- You can do things that the original Instant 3D can't do, like having objects that hover into the air.
I've added a little tutorial of converting a 2D game to 3D to illustrate how this works, at the bottom of this post.
Obviously this "snippet" is still very limited, compared to a general purpose 3D library say. You can't use it to create an FPS or a flight simulator. But I think it's a lot easier to use - start with a 2D game, drop it in, and fix up the bits that don't come out right. And you can still do some cool looking 3D stuff with it.










I often find myself declaring a lot of arrays/object literals that are essentially just data. No computation involved.
For example:
-- background types bg_tree={ tex={ {sx=0,sy=64,sw=32,sh=32} }, w=2,h=2, spacing=2 } bg_lamp={ tex={ {sx=0,sy=32,sw=16,sh=8} }, w=.5,h=.25, spacing=2, trans=12 } |
It's a useful way to create flexible, extendable systems.
Unfortunately it quickly chews through the token count.
So I was wondering what people's views were of changing the token counting rules around such object literals? - say 1 token per object literal.
They would have to be restricted in order to qualify for a reduced token count, i.e. data only, no computation, functions etc, similar to how JSON is a restricted subset of JavaScript.
The compiler could either detect this automatically, or possibly a new "data only literal" syntax could be introduced.
I feel like this probably aligns with the PICO-8 principals, as it's arguably data rather than code, and you still have the compressed size limit. It seems like a much cleaner solution than the string shredding/JSON parsing workarounds that are currently used.

Variable inspector window

Debugging carts with "print" and "printh" can be cumbersome, so I made a little snippet to help.
It adds a little window where you can view variables and drill down into them.
To use it, add the snippet somewhere into your program:
dbg=(function() poke(0x5f2d, 1) -- watched variables local vars,emp={},true -- window state local exp=false -- text cursor local x,y -- scrollbar local sy=0 local sdrag -- mouse state local mx,my,mb,pb,click,mw function clicked(x,y,w,h) return click and mx>=x and mx<x+w and my>=y and my<y+h end function butn(txt,x,y,c) print(txt,x,y,c) return clicked(x,y,4,6) end -- convert value into something easier to traverse and inspect function inspect(v,d) d=d or 0 local t=type(v) if t=="table" then if(d>5)return "[table]" local props={} for key,val in pairs(v) do [ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=76213#p) |


Turn your 2D game into a 3D game!


That's 50% extra D!
100% guaranteed to work, sometimes, if you're lucky.
Simply paste this into the top of your 2D game, and be the envy of all your 2D coding friends!!!1!1
(OK, I'll stop now.)
This works best for top down games like "Pic-oh mummy!" by @Hokutoy, "Pakutto boy" by @Konimiru (both pictured)
Admittedly it's more of a gimmick/experiment - the result isn't very playable due to not being able to see the whole play area.
I mainly just wanted to see if it would work :)
_={ map=map, spr=spr, sspr=sspr, pset=pset, circ=circ, camera=camera, cx=0, cy=0, cyoff=32, ch=32, d=128, ymid=0, s2w=function(x,y) return (x-_.cx)-64,_.ch,128-(y-_.cy) end, proj=function(x,y,z) local scale=_.d/z return x*scale+64,y*scale+_.ymid,scale [ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=75793#p) |








