Hey All!
PICO-8 0.2.5 is now up on lexaloffle, Humble, itch.io, and for PocketCHIP and web (Education Edition).
Edit: 0.2.5b is now up for Linux users who had trouble connecting to the BBS: https://www.lexaloffle.com/bbs/?pid=116441#p
Built-in Help
PICO-8 0.2.5 has built-in documentation on API functions and other topics. Type "HELP" at the prompt to see a list of topics in blue, and then e.g. "HELP GFX" to get more information about that thing.
While in the code editor, you can also press CTRL-U to get help on whatever is under the cursor, including operators and Lua keywords.
> HELP FILLP |
String Indexing
Single characters can now be grabbed from strings using a familiar str[i] style syntax that means something similar to sub(str, i, i). The index is rounded down to the closest integer, and can be negative to index from the end of the string.
S="ABCDE" S[2.6] -- "B" S[-2] -- "D" S[99] -- "" |
Strings in Lua are immutable, so this can only be used in an expression (on the right hand side of an assignment). e.g. s[2] = "z" is not allowed.
rnd(str) is now also accepted and returns a random character from string str.
// update: this might not be true from 0.2.5c: https://www.lexaloffle.com/bbs/?pid=116415#p
I've reverted sub() to pre-0.2.4 behaviour, which means that sub("abcde",2,_) returns "bcde". 0.2.4 was returning single characters when the 3rd parameter is non-numeric, which is now much better handled by str[i] style indexing. (Apologies if you were already using this behaviour!)
Editor Features
gfx_grid_lines in config.txt now takes a colour if you'd like super-visible grid lines in the sprite editor:
Map selections are now on a separate floating layer:
Pressing cursor keys in the map when nothing is selected now moves the camera, as I noticed some new users struggling to find/understand the pan tool and instead instinctively reaching for the cursor keys.
PICO1K Tools
Some changes intended to be useful for PICO1K Jam starting in September:
-
ctrl-click on the compressed code capacity to get a realtime compressed size counter in bytes. This is the same size given with the INFO command, and when storing the cartridge as a tiny (code-only) binary rom with "EXPORT -T FOO.P8.ROM"
-
"EXPORT -T @CLIP" can be used to get a hexdump of that same data written to clipboard. This is not very useful -- just a way to visualise exactly how much data is stored.
- SAVE @URL saves 3 characters when there is no sprite data included by omitting the redundant "&g=".
Web-Exportable Audio
extcmd("audio_rec"), extcmd("audio_end") can be used in web exports. The output file shows up as a downloadable .wav file. I'm hoping this will open the door for some cute sound generation tools.
Variable Width P8SCII Fonts
PICO-8 0.2.5 custom fonts can now specify how wide each character is when printed. This previously handled by doing things like injecting extra P8SCII commands into the output string, but this was cumbersome and meant that such fonts could not be shared as plain data. There were already pixel-wise cursor positioning commands in P8SCII, so I felt it makes sense to make this simpler. Also, I needed it for Picotron 8)
To specify a 3-bit width adjustment and 1-bit y offset for character 16..255, the data at 0x5608..0x567f is used. These would otherwise be the bitmap data for characters 1..15 which are all control characters and thus never printed.
Each 4-bit nibble in 0x5608..0x567f (low nibbles first) encodes the adjustments for one character:
bits 0x7: adjust character width by 0,1,2,3,-4,-3,-2,-1 bit 0x8: when set, draw the character one pixel higher (useful for latin accents) |
An addition bit must be set at 0x5605 to enable size adjustments: poke(0x5605, 1). Otherwise the data at 0x5608..0x567f is ignored.
To test this out, try loading the font snippet generation tool:
> LOAD #FONT_SNIPPET |
And then paste the following after the comment starting with "font attributes"
POKE(0x5605,1) -- turn on adjustments POKE(0x5634,0x70) -- set nibble for i to 0x7 |
You can now observe that the big "i" in "quick" near the top of the screen only has 1px of space to the right instead of 2px.
Here's a helper function for setting the 4-bit nibble (val) for a given character (c):
FUNCTION ADJUST_CHAR(C, VAL) LOCAL ADDR = 0X5600 + ORD(C)\2 LOCAL SHFT = (ORD(C) & 1) * 4 LOCAL MASK = 0XF << SHFT POKE(ADDR, (PEEK(ADDR) & ~MASK) | (VAL << SHFT)) END |
File Listings (for Dev Tool Carts)
Locally running programs can now use ls(path) to get a listing of any local directory. Entries that end with a "/" are directories. Use stat(124) to get the current path.
PATH = STAT(124) -- E.G. /FOO/ FILES = LS(PATH) FOR F IN ALL(FILES) DO COLOR(F[-1] == "/" AND 14 OR 6) PRINT(F) END |
BBS cartridges are not able to access local file listings -- this is intended for developing tools to use locally. For example: a utility that makes it easier to browser cartridge data and copy between cartridges using reload() / cstore() which are able to read and write any local cartridge file using the 4th parameter.
Keyboard Scancode Remapper
If you're having trouble getting PICO-8 to detect some keys (especially numeric keypad keys / unusual laptop layouts), there is now a built-in tool for mapping those keypresses to something SDL2 understand. Run PICO-8 from commandline with -scancodes:
pico8 -scancodes
Or use some other tool to find out which scancodes the keys in question are producing, and then map them to something else in config.txt (look for map_scancodes)
Dynamic Libcurl Support (Linux)
The 32-bit and 64-bit linux builds now try to dynamically load libcurl by default in order to make bbs requests (e.g. download carts from splore). This will hopefully make installing on some platforms easier, including Steam Decks. When libcurl.so is not available, it drops down to the old wget behaviour (requires wget to be installed). If this works well, I'll also see about moving to the same scheme for raspi and mac builds.
More Doodlemud
Doodlemud is a toy multiplayer game designed to test the backend services that PICO-8's high score table (and future projects) will be based on. Try it here: https://www.doodlemud.com/#skatepark
This version is running on a completely new backend, custom written from scratch as a self-contained program using libwebsockets. My first attempt was built out of opensource components (nchan, redis, openresty, nginx), but because of PICO-8's unusual requirements, this was making customisation, debugging and devops more complex that it needed to be. Sometimes just reinventing the wheel with a bespoke C program is still the right approach even in the world of web services. There are currently 5 nodes running which are selected based on the user's location when making a new room:
Next Steps
It looks like PICO-8's 0.2.5 API is unlikely to change now (really, this time!), and the next task is to port the new additions to Voxatron for Voxatron 0.3.7. After that I'll continue working on the BBS functionality needed to log in from PICO-8 and submit high scores. If you're curious, you can see the current plan for the SCORESUB() function by typing HELP SCORESUB.
That's all for now -- I hope you enjoy the update and as usual let me know here or in bugs/ if you find anything spooky going on.
Full Changelog:
I know this is unrelated but, did you know can use Pico 8 Education on Android without using a external keyboard and it's fully functional but it can be a bit junkie.
If you want to know more about this, can we please talk on discord?
Amazing stuff as always @zep! Super cool that you keep supporting community activities too.
Alright, @zep ! I can see you put quite a bit of effort in this latest release including my suggest for grid in the sprite editor. Thanks so much !
Suggestions:
Add to HELP []
to demonstrate how arrays work.
Add to stat()
all elements, including mouse. If need be the coder can use the PGUP and PGDN to scroll the screen for an especially long HELP. If the cursor is off the edge of the screen, scrolled below to see a long help for instance, then typing any normal key would immediately return to that spot in the text and cursor location.
By allowing HELP to scroll up and down you could have it like QBasic or GFA where a working code example is included where desired for every single help command and function.
Please return "-" as a valid character for filenames to load and save. I could do this earlier and now it will only take "_" .
Add ability of 0x5f5c and 0x5f5d to normal keyboard.
I'm still not able to use my real joystick for the correct arrow keys HERE:
https://www.lexaloffle.com/bbs/?tid=47506
Suggest ability to remap online buttons to fix that problem.
Suggest ability to import audio from WAV or PCM to direct code that can be used in Pico-8, to make it easier for those who don't want to use external tools to do so.
import *.wav
Have onboard Palette editor to modify the 16-colors. This color-set is saved with the cart *.p8 but does not occupy visual source-code space. So a pal()
in the code would reset the colors yes, but to the custom set saved for this cart.
Show a piano keyboard in addition to the usual 'screamtracker' arrangement for developing sounds and music.
Beef up the sound driver so it's possible to get all sounds the Atari 2600 can make. Bonus if you can get sound ranges equivalent to NES. So you can have glass sounds like a wind chime and deep sounds like a rumbling space engine.
Allow for the arrow keys and PgUp and PgDn on the number keypad to allow you to maneuver source-code. Currently it does not.
Have option to read raw keystrokes, so this is possible.
if _btnk(113) and _btnk(119) and _btnk(101) then print("you are holding down q w e keys.") end |
INPUT
This is a long time in coming and still is not here. Functions like any normal INPUT. Type a value followed by the ENTER key. Use backspace to correct in entry.
_input("what is your name?",name) _input("enter lucky number:",valu) |
Self. This would be useful not just to build a calculator but have self-modifying code.
text="code={}" if flag then text="code={1,2,3}" end _self(text) |
If variable flag
is true, then the array code
will have 3-elements of 1, 2, and 3.
If not, it will be a clean array, {}
.
Also this is possible:
_input("calculate:",calc) _self("print("..calc..")") |
So if you typed: 1+2*3/4
followed by ENTER you would get 2.5
- or type really anything else including variables, sin(), cos(), the works.
Would be nice to have "##" to return length of string minus P8SCII codes. So:
a="\#1background" print(#a,7) print(##a) |
Results would be 12
and 10
For those curious the \#
converts to chr(1)
. . .
All these things, and all these suggestions, @zep, just means I really do enjoy Pico-8 and want to see it grow in users and usability. :)
Probably worth noting that the change to how strings behave breaks a couple of carts which use the token optimization rnd(x) -> rnd"x"
. Instead of casting the inputted string to a number, it treats it as a table and returns a random element of it, making things like rnd"2" deterministically return the same number, and things like rnd"0.5" throw an error as sampling "." will break any math/comparisons done on the random number.
exciting changes! the compressed size counter is a great addition to export -T
-- we're going to see some really wild stuff in the pico8 demoscene soon, I think :D
Btw, this doesn't seem to be true:
> Added: sub(str,pos,pos) can be written as str[pos] (but returns nil when pos is not a valid index)
?("abc")[8]=="" true ?sub("abc",8,8)=="" true |
If I'm reading the changelog correctly, I think the return values should be nil
and ""
, so this should print false
and then true
. (or, this is working as intended and the changelog is wrong?)
PICO-8 0.2.5b (Linux)
If you have trouble accessing the BBS with 0.2.5 under Linux (i386/amd64), I've uploaded a quick patch (0.2.5b) that improves dropping down to wget when libcurl fails to load.
It also gives some extra debugging information in log.txt -- if you had this problem please search for libcurl in log.txt and paste it here! (log.txt can be found in the folder that opens after typing "FOLDER CONFIG" from pico-8).
@johanp
Thanks, and of course!
@Doubleshotgun
I couldn't find you on discord -- maybe [email protected]?
There might be a few tweaks I could make to get it working better on such devices, but I suspect there will always be show-stopping UI issues that won't be solved until there's a touch-enabled build designed for that. I'm quite impressed it almost works though!
@dw817
> Suggest ability to remap online buttons to fix that problem.
It's a shame this doesn't work automatically with the web gamepad API, but yes -- I think the ability to remap will be a necessary fallback solution. I've wishlisted it.
> Please return "-" as a valid character for filenames to load and save
You can use - in 0.2.5 filenames (just not as the first character).
> Allow for the arrow keys and PgUp and PgDn on the number keypad to allow you to maneuver source-code. Currently it does not.
You can fix this with "pico8 -scancodes" from commandline. I might be able to add hard-coded mappings for NUMPAD_PAGEUP etc. but I'm not sure that they are standardized enough to do that.
Thanks for the other suggestions, although it's unlikely there will be much change in the API & language.
@Meep
oof -- this is a big problem. Looks like 0.2.5c will be arriving soon!
@pancelor, @Liquidream
Thanks, that was outdated information and is fixed in the changelog now.
Thanks, @zep. Any additions you make are usually good editions.
As you mentioned Doodlemud above, is there a chance Pico-8 will have the ability for every cart can get a 256-bytes or so of online storage ?
Would be great to have games with high-scores you can compete for with other onliners players as well as sharing other gaming data - within the 256-byte constraint.
0.2.5b solves the issue, thanks!
in the log file, I only see found libcurl.so.4
Progress like this is making me more excited for what's next!
A small addition could be a poke() to set the value returned by mget when outside map boundaries instead of always being 0. And the manual might have a typo about it, since it says mset returns 0?
MGET(X, Y)
MSET(X, Y, VAL)
Get or set map value (VAL) at X,Y
When X,Y is outside of the map range, MSET returns 0.
@TheRoboZ, you reminded me of something I had forgotten. Conditional checks for validity in commands.
If you did: a=(function)
The command in all cases would not be executed, however it would be tested to make sure it is valid. For instance if the function were mset(x,y)
a check would be made to ensure the arguments inside the mset were within the parameters.
And yes, an additional poke for reading outside values of mset()
dset()
pset()
and sget()
to return not just 0. It could be -1, NIL, or FALSE if out of bounds.
While it's on the board, I'd also still like the ability LOAD and SAVE state, just like a true emulator. To load during execution, press SHIFT 0-9 and to save during execution press CTRL 0-9. Must receive POKE inside code to activate.
That and optional screen filters such as NTSC, Interpolation, 2xSAI, Super Eagle, and HQ - available and configurable in offline play and online play.
Maybe add additional argument to export image in code. `export ( "image" { , memory=0x00 (default) or choose another memory location such as the screen 0x60 } ).
My idea is more to specify a default tile value other than 0, to save tokens instead of writing the conditions yourself. In prince of Persia this would be use to always return a wall instead of an empty tile
why is the macos version still x86_64 only? i've been waiting on an arm64 build to be able to run it, and still there is nothing
@PineberryFox: Seems to me you forgot (at the very least) to say... "please" 🤨
@PineberryFox The x64 build works fine on my ARM Mac. Do you need to install Rosetta?
@Liquidream This isn't some free open-source project. When you pay for a commercial product that's advertised to run on your system, you expect to be able to run it without installing any emulators.
Rosetta is distributed by Apple to cover this exact use case and it runs transparently. If you don't want to install it for a $15 product, I don't know what to tell you.
the point of rosetta2 is to give developers time to transition, not to be a permanent solution for consumers. it will be discontinued soon. i'm making noise in hope that this project moves before that point.
in all honesty i have several computers, including a raspberry pi, a windows laptop, a couple linux machines, two intel macs, two arm macs, and many more. so i'm actually as unaffected as one could possibly be. but i have still stepped away from the pico-8, as i don't want its mac support to be a ticking time-bomb
this is the only paid application (nay, the only application) i have that doesn't support the arm CPUs yet. asking an application advertising mac support to truly support the only macs apple sells feels like a very different thing than asking for say, sparc support — which we WOULD have if the pico-8 were open-source
tl;dr: sorry for being a bit frustrated over a problem that will only truly be a problem in the future
but why bring that frustration and unfriendly tone to tiny 1-person (2-person?) company instead of megacorp that changed the definition of its platform from under developers’ feet?
anyway arm support will certainly come at some point, it’s just that pico-8 is not a fast-moving project and has other priorities.
@merwok: two reasons. first and foremost because it isn't a difficult change to make. in most cases it's a single flag in the build tools. especially since the system already runs on arm devices (raspberry pi) it is clear that there can't be any kind of technical limitation. second, the transition was more than two years ago and there have been multiple releases of the pico-8 since that time.
@PineberryFox
There is an arm build in the works, but it won't be out for a while still. Unfortunately it's not as simple as setting a flag in the build tools. I'd be more than happy to issue a refund if you feel you didn't get what you paid for -- you can get me at hey[at]lexaloffle[dot]com.
@merwok
Great, thanks for the log entry. It looks like the libcurl requests are now working in 0.2.5b anyway -- the change is that it now trys to dlopen not only libcurl.so (which I take to mean "preferred version", but seems is not always present), but also explicitly libcurl.so.* (which is what it picked up in your case).
@dw817
> As you mentioned Doodlemud above, is there a chance Pico-8 will have the ability for every cart can get a 256-bytes or so of online storage ?
You'll be able to abuse the score's 'extra' field for that! Type HELP SCORESUB to see how it will work.
The rate limit on table access allows for 2 different score tables to be used at the same time, so you could use one for 'storage' (by bumping the top score of whoever is submitting the change by 0x0.0001), and then the other table for per-player data.
Display options (pixel shaders etc.) are wishlisted, but would be a post-1.0 kind of thing.
I did look at state saving a while ago, but it is very difficult to get a complete, reconstructable Lua state that is secure and shareable cross-platform, so I put it in the not-worth-it basket. Voxatron has partial Lua state saving, but even then I had to scale it back further for 0.3.7 because it is so bug-prone.
@TheRoboZ
> to set the value returned by mget when outside map boundaries
I really like this idea, and am already doing something similar in Picotron for exactly the kind of case you describe. The main issue is that there is no space left in 0x5f00~0x5f7f! I'll have a look see.
@zep again i'm sorry for being frustrated. thank you for the update! i certainly won't be requesting a refund. just glad to hear something is in the works, and i eagerly — but patiently — await its release
thank you!
Hi @zep ! Wow I'm feeling quite proud now you addressed my very concerns.
OK how about this ? Save-State + Load-State could be nothing more than just saving/loading the extended memory ? 0x8000-0xFFFF.
You could then map variables directly to that memory. Then just load/save that chunk of memory 0x8000-0xFFFF.
Let me think ...
deck={} for i=1,52 do deck[i]=i end mapstate(deck,0x8000) |
mapstate()
the 2nd argument is optional. The system would be smart enough to know where to store all the variables and arrays and subsequent mapstate()
commands following this one would neatly store then in the memory to follow after the first.
So in this case that would store deck{}, 4-bytes per element, 52-in all yielding a total of 208-bytes to store at 0x8000 as that is the beginning of load/save state storage.
The advantage is you just use the array as normal but when you LOAD STATE that automatically updates deck{} (and anything else) as an earlier command was given to map it starting at 0x8000.
SAVE STATE would quickly take all variables that were mapped earlier with mapstate(), stuff them into memory at 0x8000 and then save it off.
Now these COULD be individual files yet I don't know how fast that would be to be both loading and saving external files for load/save states. cartname.st0
through cartname.st9
One distinct advantage of course is the coder would never need to worry about making a 'save game' feature as that would truly be built in by using the load/save state ability of mapstate()
As for the high-score idea. Wow ! That's a lot more complex than I imagined. I guess you need all that security for - as you say - someone would mess with the values and make themselves high-score even if they weren't.
Yeah, Firefox really dropped the ball on the gamepad API, refusing to include support for Xbox360 and bluetooth controllers for some dumb reason or other... It's to the point where I actually wrote a plugin-side JavaScript injection just to remap the gamepad myself.
builtin help is something ive always wanted for a long time, really excited about that, because I can finally code pico8 completely offline if I need to without switching between the help txt and the program, which isn't a problem, but it's not as easy as just typing "help tline"
@zep, I'd really love to help write up the remaining help files you've done. I've been trying to maintain the unofficial API for a while now (here's the link if you didn't know) and I've got a mostly complete knowledge of the fantasy console.
This is one of my favorite features that you've added in a long time, and has made a lot of the work I did on my book obsolete - that said, I'd love to fill in the remaining chunks of functions with good definitions.
Please get in touch here or on twitter if you'd like!
Some of the features I'd like to work on:
- Keyboard Shortcuts
- Metatables
- Memory
- And some other things, if you'd be interested in the offer.
Just let me know! I've been making stuff like this for a while, and would love to help. Just posted a cart that I'd made a tweet about previously too.
@zep Thanks for the update! I had a cart right at the token + compressed size limits using some custom sprite and P8SCII-for-spacing fonts, so I'm already trying out the variable-width font and swapping in [] for string indexing.
One minor documentation bug: HELP SUB describes the previous 0.2.4-only behavior of sub(str,i,_)
Oh, right. It would probably help to actually post my code, in case anyone happens to have any use for it.
It's a Greasemonkey/Tampermonkey/Violentmonkey script intended for Firefox, specifically to add mappings for my gamepad to the gamepad API, specifically for use with web games like Pico-8. ^^;;
You should still make a full button remapping GUI at some point, but might you be able to pick apart this tiny little hunk of JavaScript might act as a small bandaid to slightly boost compatibility as a baseline, after the gamepad API says it isn't an Xinput-compatible device...?
Was dir()
intended to be removed? I realise ls(stat(124))
is equivalent now.
I found a typo
"The range of possile values is: -32768.0 to 32767.99999"
It's spelled "possile" instead of "possible"
> Added: ~ can be used as xor instead of ^^ (same as Lua 5.3/5.4)
nice! feature request: can you add ~=
as well? (x^^=1
works but x~=1
does not: syntax error near '~='
)
edit: hmm, I hope this wouldn't cause ambiguous parsing... e.g. if x~=3 then
versus x~=3
(those aren't ambiguous but maybe a more complicated or future example would be?)
about the "Variable Width P8SCII Fonts":
Is there a command to calculate the width of a text in pixels?
The fast "#string*8" will not work any more.
@GPI print
can do that: (from the manual)
> PRINT returns the right-most x position that occurred while printing. This can be used to find out the width of some text by printing it off-screen:
> W = PRINT("HOGE", 0, -20) -- returns 16
hmmm... i would prefer a "PRINTSIZE" function... drawing outside the screen is not really elegant.
that’s a matter of taste ¯\_(ツ)_/¯
pico-8 is all about giving us the minimal required tools to find techniques to do advanced things.
going from #string*5
to custom strlen
function that checks normal/wide glyph to printing off-screen is a normal progression. the thing is possible, and people who find the technique distateful can hide it in a function.
I have a small suggestion for the help
feature.
When I first used it I did help(map)
and got the message no help topic under cursor
. It took me a minute to realize that the syntax of the command was supposed to be help map
, like a command rather than a function.
Maybe the error message could be something like invalid usage. try help <topic> instead of help(<topic>)
. Or if you don't mind encouraging the function-like syntax, help
could accept optional parentheses.
Can't seem to be able to move in Doodlemud. I can spawn my character but pressing left / right does nothing to my lil dude. Tried on two different PCs. Am I missing something?
Hi Zep, how did you do the top page Pico8 0.2.5 animation, the trees,water, fireflies movement are stunning. voxatron? thx
[Please log in to post a comment]