Log In  
Follow
pancelor

hi! here is some text for you to read.
and here are some games for you to play:

the tower
make ten
linecook

more:
https://pancelor.itch.io
https://pancelor.com


repro steps:

  • run cart bitplane_grid-3 in the web player
  • press ctrl-c
  • paste into an external text editor

expected behavior: clipboard should have poke(0x5f5e,0xff) in it
actual behavior: clipboard is unchanged (my specs: linux, firefox, 0.2.6c dev8)

(cart from here)

Cart #bitplane_grid-3 | 2025-02-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
2

Maybe this is a known bug, which is part of why it's "0.2.6cdev8" instead of "0.2.6c"? But here's a ping just in case you don't know about it @zep. (web clipboard stuff seems like a huge pain, I don't envy you trying to fix it...)


The cart has some debug print statements -- open the browser dev tools and you can see it print out "copying str:" right before calling printh(str,'@clip'). I noticed there's no red "press c to copy" prompt that the web player used to show, maybe that's a clue.

[ Continue Reading.. ]

2 comments



Sometimes it'd be nice to mute the music in a game you downloaded from the BBS, but not mute the sound effects. Maybe their music isn't to your taste, maybe you've played it way too much and now you want to listen to your own music instead; there are plenty of reasons.

One easy trick is just typing music=min to kill the music completely. If you'd rather be able to toggle the music on and off, here's a quick snippet:

-- pancelor music toggle v4
-- https://www.lexaloffle.com/bbs/?tid=146963
_music_fn,_music_last=music,-1
function music(...)
	if _music_muted then
		_music_last=... --save 1st arg
	else
		_music_fn(...)
	end
end

menuitem(5,"♪music",function()
	_music_muted=not _music_muted
	if _music_muted then
		_music_fn(-1,500)
		menuitem(nil,"…music")
	else
		_music_fn(_music_last)
		menuitem(nil,"♪music")
	end
	return true
end)

[ Continue Reading.. ]

3
0 comments



Cart #pancelors_postcarts-0 | 2024-11-02 | Code ▽ | Embed ▽ | No License
8

This is a collection of my various tweetcarts and other code art, circa 2019-2023. Most of my non-interactable postcarts etc are in here, including a few that are only included as a part of this collection. 25+ fun little animations!

(I posted this on itch a while back but forgot to post it here until just now, as I was reading zep's release notes for the BBS's new postcart feature. So I'm uploading my postcart gallery now; better late than never!)

Controls

These carts are non-interactive animations for you to look at. The only interaction is opening the menu (Enter/P), where you can change the active cart.

[ Continue Reading.. ]

8
0 comments



@zep o/ thanks for fixing the sfx arpeggio effects! here's another audio bug for you:

The music for embedded BBS carts has a noticeable music desync in some cases (the individual tracks sound out-of-sync from each other). I noticed it on linux+firefox with my music visualizer demo cart, reproduced here for convenience:

Cart #datufbuyi-0 | 2024-08-02 | Embed ▽ | No License
5

Individual tracks desync from each other; it consistently happens after the song switches from pattern 3 to pattern 4. The second screenshot here shows me catching it right when it happens:

>

[ Continue Reading.. ]

1
1 comment



Inspired by https://www.lexaloffle.com/bbs/?tid=2341, here's a description of how sfx and music are stored in Picotron. (Well, there's not much description here yet, just some helpful code. I'll add to this over time)

The data can be in unusual custom formats. The header data is supposed to help in these cases and is accounted for in this code (with some asserts to crash if the data format is unknown). But for most people using this code, the data will be in the standard format.

Library

Code:
[hidden]

-- sfx/music data wrangler
-- by pancelor

-- The docs call this the "index", I call it the "header"
-- https://www.lexaloffle.com/dl/docs/picotron_synth.html#Index
function sfxheader_read()
	local num_instruments, num_tracks, num_patterns, flags = peek2(0x30000, 4)
	-- ...8 unused bytes here...
	local insts_addr, tracks_addr, patterns_addr, unused1 = peek4(0x30010, 4)
	assert(unused1==0,"bad sfx header 1")
	local tick_len, def_len, def_spd = peek2(0x30020, 3)
	local def_spd2, unused2, unused3, unused4 = peek(0x30026, 4)
	-- TODO def_spd2 v. def_spd?
	assert(unused2==0,"bad sfx header 2")
	assert(unused3==0,"bad sfx header 3")
	assert(unused4==0,"bad sfx header 4")
	return {
		num_instruments = num_instruments,
		num_tracks = num_tracks,
		num_patterns = num_patterns,
		flags = flags, --0x1 use default track indexing (base+0x20000, increments of 328 bytes)
		insts_addr = insts_addr, --relative address of instruments
		tracks_addr = tracks_addr, --relative address of track index
		patterns_addr = patterns_addr, --relative address of pattern data
		tick_len = tick_len, --in 1/16ths of a sample at 44100Hz [0 means 5880 -- 120 ticks / second]
		def_len = def_len, --used by patterns that do not have a default length specified
		def_spd = def_spd, --used by patterns that do not have a default speed specified
		def_spd2 = def_spd2, -- ??
	}
end

function pattern_read(i)
	local base = 0x30100 + sfxheader_read().patterns_addr -- normally 0x30100
	local addr = base + i*20
	local tracks = { peek(addr, 8) }
	local flow_flags, channel_mask = peek(addr+8, 2)
	local len = peek2(addr+10)
	-- ...8 unused bytes here...
	return {
		tracks = tracks, -- array with 8 track ids
		flow_flags = flow_flags, -- 0x1 loop forward, 0x2 loop backward, 0x4 stop
		channel_mask = channel_mask, -- 0x1 is tracks[1] unmuted? 0x2 is tracks[2] unmuted? 0x4 => tracks[3], 0x8 => tracks[4], ... 0x80 => tracks[8]
		len = len, --TODO: how does this interact with track length and header def_len/def_len2?
	}
end

function track_read(i)
	local header = sfxheader_read()
	assert(header.flags&1==1,"unknown track format")
	local base = 0x30000 + header.tracks_addr -- normally 0x50000
	local addr = base + i*328
	local len = peek2(addr)
	local spd, loop0, loop1, delay, flags, unused = peek(addr+2, 6)
	assert(unused==0,"bad sfx track")
	-- note: if len<64 then some of this data is irrelevant:
	local pitches = { peek(addr+8,64) }
	local instruments = { peek(addr+72,64) }
	local volumes = { peek(addr+136,64) }
	local effects = { peek(addr+200,64) }
	local effect_params = { peek(addr+264,64) }
	return {
		len = len,
		spd = spd,
		loop0 = loop0,
		loop1 = loop1,
		delay = delay,
		flags = flags, --0x1 mute
		-- 64-length arrays, 1 entry per note:
		pitches = pitches,
		instruments = instruments,
		volumes = volumes, -- a 0xFF entry means muted
		effects = effects, -- stored as their ascii code -- retrieve with ord()
		effect_params = effect_params,
	}
end

-- an alternate version of track_read that extracts a single row
-- ti: track index
-- ri: row index
function track_row_read(ti,ri)
	local header = sfxheader_read()
	assert(header.flags&1==1,"unknown track format")
	local base = 0x30000 + header.tracks_addr -- normally 0x50000
	local addr = base + ti*328 + ri
	return {
		pitch = peek(addr+8),
		instrument = peek(addr+72),
		volume = peek(addr+136),
		effect = peek(addr+200),
		effect_params = peek(addr+264),
	}
end

function instrument_read(i)
	local base = 0x30000 + sfxheader_read().tracks_addr -- normally 0x40000
	local addr = base + i*0x200
	assert(false,"not implemented")
	-- see https://www.lexaloffle.com/dl/docs/picotron_synth.html#Index
	-- or read /system/apps/sfx.p64/data.lua:clear_instrument() for info
end

-- i: channel index 0..7
function channel_current_track(i)
	return stat(464,0)>>i&1~=0 and stat(400+i,12) or -1
end
-- i: channel index 0..7
function channel_current_row(i)
	return stat(464,0)>>i&1~=0 and stat(400+i,9) or -1
end
-- number of ticks played on current pattern
function channel_ticks_played(i)
	return stat(400+i,11)
end
--currently playing pattern, or -1
function current_pattern()
	return stat(466)
end

[ Continue Reading.. ]

5
0 comments



@zep o/

0.2.6b / linux:

  1. launch pico8 executable
  2. run poke(0x5f54,0x80) (in the console)
  3. run reboot
  4. run poke(0x5f54,0x80) (copied from the 0.2.6 release notes)
  5. the executable crashes

Step 2 is optional -- it still crashes in step 5 if you skip step 2
In Step 4, running poke(0x5f54,0x60) instead does not crash.

I'm not sure how to get a stacktrace on my machine, or if it's even possible. this is the best I know how to do:

> coredumpctl debug
           PID: 12604 (pico8)
           UID: 1000 (pancelor)
           GID: 1000 (pancelor)
        Signal: 11 (SEGV)
     Timestamp: Tue 2024-06-18 18:51:34 PDT (3min 17s ago)
  Command Line: /home/pancelor/.config/itch/apps/pico-8/pico-8/pico8 -root_path /run/media/pancelor/data/Users/pancelor/Documents/pancelor/src/pico8/
    Executable: /home/pancelor/.config/itch/apps/pico-8/pico-8/pico8
 Control Group: /user.slice/user-1000.slice/[email protected]/app.slice/app-pico8-b4acdb07c9cf4629b15a6b78f024fa7f.scope

[ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=150091#p)
4
1 comment



Cart #wutewuwiho-1 | 2024-06-02 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
6

hi @zep! This cart should not error, but it does (pico8 0.2.6b / linux)

-- wrap everything in func _init to fix it
--function _init()

--uncomment this to fix it
--poke(0,0)

--make this local to fix it
coro = cocreate(function()
  -- uncomment this to fix it
-- local x=1
  for i=0,32000 do
    -- flr() is arbitary here; it's
    -- just a func that should
    -- return only one value
    local rets = pack(flr(i))
    if #rets~=1 then
      local str="flr rvals:"
      for i,v in ipairs(rets) do
        str ..= "\n\t"..i..": "..tostr(v)
      end
      str ..="\nstat(1): "..stat(1)

[ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=149328#p)
6
1 comment



I'm pretty sure this is a bug; I'm on pico8 0.2.6b / linux

You can turn on character wrapping with 0x5f36 / 24374, but it acts unexpectedly when you move the camera:

cls(1)
poke(0x5f36,0x80) --turn on character wrapping
msg='0123456789abcdefghijklmnopqrstuvwxyz'
?msg,0,0,12
camera(16,0)
?msg,16,12,13

Expected behavior: both prints should wrap at the edge of the screen.
Actual behavior:


There's a similar issue with p8scii "\^r" wrapping:

cls(1)
msg='\^rf0123456789abcdefghijklmnopqrstuvwxyz' --note p8scii wrap at start
?msg,0,0,13
camera(16,0)
?msg,16,18,14
1 comment



cart A: (broken)

msg="havent pressed test yet"
menuitem(1,"★test",function() msg="pressed test!" end)

::_::
?"\^1\^c" --https://www.lexaloffle.com/dl/docs/pico-8_manual.html#Control_Codes
print(time(),0,0,7)
print(msg)
goto _

cart B: (working)

msg="havent pressed test yet"
menuitem(1,"★test",function() msg="pressed test!" end)

::_::
flip()?"\^c" --this line is the only difference
print(time(),0,0,7)
print(msg)
goto _

These two carts should behave the same, but the menuitem function in cart A never runs -- after opening the menu and selecting the menuitem, it still says "havent pressed test yet"

cart B works as expected -- it says "pressed test!"

My system is linux / pico8 0.2.6b

1
0 comments



Cart #make_ten-3 | 2024-06-05 | Code ▽ | Embed ▽ | No License
15

Select numbers that add to ten, to remove them from the board. How high can you score in just 2 minutes?

An homage to Fruit Box. A FruitBox-like? There are some big differences in this version, the most obvious being the lack of music. (the music in the original is incredible)

How to play

  • Drag your mouse to select groups of numbers that add up to 10.
  • I recommend playing in fullscreen; it's easier to click on larger numbers.
  • In the original game, you're awarded one point per number removed,
  • Get the highest score you can within 2 minutes. Good luck!

This was my submission for TweetTweetJam 9, written in 490 characters of code:

z={}c=-8s=0g='⁶!5f58⁵d8"'::_::?"⁶1⁶c0⁶!5f2d3"

[ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=148268#p)
15
1 comment



@zep This maybe isn't a bug, but it feels very weird to me:

cls()
if (1)<2 print"a"
print"b"

this prints a b, but I would expect a syntax error instead -- the condition is "1<2" but the if-shorthand parens are only surrounding the 1, instead of the whole condition.

(if you change it to if (1)<0 then it only prints "b", which verifies that the condition isn't being interpreted as just if (1) or something like that)

oh, and my system is pico8 0.2.6b / linux

4
0 comments



Hi @zep!

pico8 0.2.6b / linux:

cls()
if (false)?1 --hi
print(2)
print(3)

expected output: 2 3
actual output: 3

Here's an altered version that works as expected:

cls()
if (false)?1
print(2)
print(3)

(this prints 2 3, as expected)


3 things seem required to trigger this bug:

  1. shorthand if
  2. shorthand print on the same line
  3. !! further text after the shorthand print (" --hi", in this example. but just a single trailing space triggers the bug too)

When these are all true, the next line seems to get scooped up into the shorthand line. This can include attaching an else to the wrong if, like in this more complicated example:

cls()
if false then
 if (false)?1 --hi
else
 print(2)
 print(3)
end
print(4)

expected output: 2 3 4
actual output: 4

3
1 comment



This is a silent tutorial video; skip to 2:10 for the juicy bit:

This is an animated wallpaper that shows your custom PNG files -- just place them in a particular folder! It also works as a screensaver. It's cpu-friendly, only drawing during transitions.

Installing

  • load #photo_carousel
  • mkdir /appdata/system/wallpapers
  • save /appdata/system/wallpapers/photo_carousel.p64
  • run the cart once, to generate the appdata folder
  • cd /appdata/photo_carousel
  • folder
  • using your host OS, copy any PNG files into this folder (don't put them in subfolders)
  • set your wallpaper to photo_carousel in System Settings

Settings

Run podtree /appdata/photo_carousel/settings.pod to edit the settings. Be sure to not press enter while editing (this crashes Picotron 0.1.0e) and you must save with the mouse, not ctrl-s (another Picotron bug(?) -- ctrl-s saves the current cart, instead of the settings file)

Set the transition time to 0 to disable transitions

[ Continue Reading.. ]

10
5 comments



hey @zep, I've found a nasty coroutine(?)/multival bug. I'm on Linux + picotron 0.1.0d.

tl;dr: sometimes select("#",tostr(i)) is 2, possibly triggered by calling coresume() with extra args.


I ran into this initially because add({},3,nil) is a runtime error now (it used to work in PICO-8, but now it throws bad argument #2 to 'add' (position out of bounds)). I had some code: add(list,quote(arg)) that was crashing as if quote() was returning a second value for some reason, even though the code for quote() definitely returned just one value. (surrounding it in parens (to only keep the first return value) fixed my bug: add(list,(quote(arg))))

Version A of the code is very short, and trips the assert inside spin maybe 50% of the time? sometimes many runs in a row don't trigger the assert, but sometimes many runs in a row all trigger the assert. (maybe that's just statistics tho). Version B is a bit more complex but always trips the assert instantly for me.


[ Continue Reading.. ]

1
2 comments



Cart #importpng-9 | 2024-04-06 | Embed ▽ | License: CC4-BY-NC-SA
24

Guide

Currently (in Picotron 0.1.0e) it's hard to use a PNG image as a sprite. You can fetch("myimage.png"), but the result isn't in the right image format. So, here's a small tool to convert PNG files into picotron sprites.

Drag any .png or .qoi image into the tool to convert it into a sprite pod on your clipboard. You can paste this into the graphics editor, or into code.

Drag in a .hex file (e.g. from lospec.com) or a .pal file (e.g. from OkPal) before importing your png to change the import palette.

Details

Import speed

This tool prioritizes import speed. Images with only a few unique colors (e.g. an image already in the picotron palette) will import much faster than images with many colors.

[ Continue Reading.. ]

24
13 comments



Cart #p8x8-7 | 2024-05-04 | Embed ▽ | No License
74

p8x8: convert PICO-8 carts into Picotron carts (some assembly required)

I'm declaring p8x8 good enough for public release! It's a tool to convert pico8 carts to picotron -- it's not perfect and it requires some manual intervention in most cases, but it's magical being able to play a bunch of games on the new system without much effort.

Lots more info (instructions, compatibility notes, CC license, etc) here: https://github.com/pancelor/p8x8/

Teaser video here: https://mastodon.social/@pancelor/112162470395945383

changelog

v1.8 (#p8x8-8, unreleased)

  • music/sfx conversion!! just waiting on picotron 010h to add instrument effects

v1.7 (#p8x8-7)

  • fix secret palette

[ Continue Reading.. ]

74
39 comments



Here's my /appdata/system/startup.lua file (picotron automatically runs it on startup)

-- take str, delete all chars between
-- indices i0 and i1 (inclusive), and
-- insert newstr into that space
local function splice(str,i0,i1, newstr)
	return sub(str,1,max(1,i0)-1)..(newstr or "")..sub(str,i1+1)
end

-- use str:find to do sed-like file editing.
-- no lua patterns, just literal string matching
-- return str, but replace the first instance of cut with paste
local function _replace(str,cut,paste)
	local i0,i1 = str:find(cut,1,true) -- no pattern-matching
	if not i0 then
		return str,false
	end
	return splice(str,i0,i1,paste),true
end

local function sedish(fname,mods)
	local src = fetch(fname)
	if not src then
		printh("sedish: couldn't find file "..fname)
		return
	end

	for i,mod in ipairs(mods) do
		local cut,paste = unpack(mod)
		-- printh(cut.." "..paste)
		if not cut or not paste then
			printh("sedish: bad cut/paste data in "..fname)
			return
		end
		local changed
		src,changed = _replace(src,cut,paste)
		if not changed then
			printh("sedish: mod #"..i.." did nothing to "..fname)
		end
		if not src or #src==0 then
			printh("sedish: bad result in "..fname)
			return
		end
	end
	-- printh("storing "..fname..": "..sub(src,1,100):gsub("

[ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=143638#p)
10
7 comments



update: there are basically 3 tools that are relevant here:

  • this tool (sprimp) -- maybe still useful, but superceded by p8x8:
  • p8x8: a tool to fully convert PICO-8 carts to Picotron carts (some assembly required) -- this grew out of sprimp
  • my aseprite exporter plugin -- still useful

You probably want to check out p8x8 instead, but I'm leaving this thread as it is since it may still be useful or interesting to some people.


Cart #sprimp-1 | 2024-03-18 | Embed ▽ | License: CC4-BY-NC-SA
28

tada! now you can import pico8 spritesheets. and map too!

importing a .p8 file

  • put your game.p8 file somewhere inside picotron's file system

[ Continue Reading.. ]

28
7 comments



Cart #susizker-0 | 2024-03-15 | Embed ▽ | License: CC4-BY-NC-SA
5

just a port of a tweetcart of mine to the new system :)

move this into /system/screensavers/ and it'll show up as an option in your settings

If you run it on its own as a cart, you'll want to run reset() vid(0) in the console afterwards to get things back to normal

edit: if you want it to be permanently available, you need to put it in /appdata/system/screensavers (otherwise, you need to re-add it every time you start picotron). create the folder by copying the system folder: cp /system/screensavers /appdata/system/screensavers

5
0 comments



Cart #ghostpatrol-2 | 2023-10-07 | Code ▽ | Embed ▽ | No License
12

Oh no, ghosts are approaching the town!

Mayor Wombledon has begged ALFREDO THE GHOUL BANISHER to team up with THE WILY WIZ to protect the town. Can they put aside their differences and work together, or will evil spirits devour the populace?

Controls

  • Arrow keys: move
  • Z: swap heroes
  • Enter/P: pause (level select, volume controls)
  • X: next level

Outcomes

  • 1 night protected: You have the villagers' sincere gratitude 🙏
  • 5 nights protected: The villagers are beginning to hope again 😭
  • 10 nights protected: Valiant Heroes 🏆
  • 40 nights protected: Local Deity 🤯

Tips

  • Alfredo can dig up graves with his fearsome claws.

[ Continue Reading.. ]

12
0 comments





Top    Load More Posts ->