pancelor [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=27691 Web clipboard broken (0.2.6c dev8) <p>repro steps:</p> <ul> <li>run cart bitplane_grid-3 in the web player</li> <li>press ctrl-c</li> <li>paste into an external text editor</li> </ul> <p>expected behavior: clipboard should have <code>poke(0x5f5e,0xff)</code> in it<br /> actual behavior: clipboard is unchanged (my specs: linux, firefox, 0.2.6c dev8)</p> <p>(cart from <a href="https://www.lexaloffle.com/bbs/?tid=54215">here</a>)<br /> <table><tr><td> <a href="/bbs/?pid=134709#p"> <img src="/bbs/thumbs/pico8_bitplane_grid-3.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=134709#p"> bitplane_grid</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=134709#p"> [Click to Play]</a> </td></tr></table> </p> <p>Maybe this is a known bug, which is part of why it's &quot;0.2.6cdev8&quot; instead of &quot;0.2.6c&quot;? But here's a ping just in case you don't know about it <a href="https://www.lexaloffle.com/bbs/?uid=1"> @zep</a>. (web clipboard stuff seems like a huge pain, I don't envy you trying to fix it...)</p> <hr /> <p>The cart has some debug print statements -- open the browser dev tools and you can see it print out &quot;copying str:&quot; right before calling <code>printh(str,'@clip')</code>. I noticed there's no red &quot;press c to copy&quot; prompt that the web player used to show, maybe that's a clue.</p> <p>I also noticed that a lone ctrl press is now caught by <code>stat(31)</code>; my debug logging prints <code>ch code ソ 218</code> on web, but completely misses this key press in the desktop app (linux, 0.2.6b). I think that may be a separate bug? Shift and alt are not caught in the same way, and I don't think I'd care about a bare ctrl press while reading text input</p> https://www.lexaloffle.com/bbs/?tid=147118 https://www.lexaloffle.com/bbs/?tid=147118 Mon, 10 Feb 2025 02:28:58 UTC Toggle Music mod for any cart <p>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.</p> <p>One easy trick is just typing <code>music=min</code> to kill the music completely. If you'd rather be able to toggle the music on and off, here's a quick snippet:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>-- 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,&quot;♪music&quot;,function() _music_muted=not _music_muted if _music_muted then _music_fn(-1,500) menuitem(nil,&quot;&hellip;music&quot;) else _music_fn(_music_last) menuitem(nil,&quot;♪music&quot;) end return true end)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/music-toggle.gif" alt="" /> <p>It should work for most carts, just paste it onto the end of the cart's code. It might be convenient to use this as a dev as well (not just as a player) -- it remembers the last call to music() and re-calls it when the player unmutes.</p> https://www.lexaloffle.com/bbs/?tid=146963 https://www.lexaloffle.com/bbs/?tid=146963 Mon, 03 Feb 2025 08:36:48 UTC pancelor's postcarts <p> <table><tr><td> <a href="/bbs/?pid=156720#p"> <img src="/bbs/thumbs/pico8_pancelors_postcarts-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=156720#p"> pancelors_postcarts</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=156720#p"> [Click to Play]</a> </td></tr></table> </p> <p>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!</p> <p>(I posted this on <a href="https://pancelor.itch.io/tweetcart-gallery">itch</a> a while back but forgot to post it here until just now, as I was reading zep's <a href="https://www.lexaloffle.com/bbs/?tid=145008">release notes</a> for the BBS's new postcart feature. So I'm uploading my postcart gallery now; better late than never!)</p> <h2>Controls</h2> <p>These carts are non-interactive animations for you to look at. The only interaction is opening the menu (<strong>Enter/P</strong>), where you can change the active cart.</p> <ul> <li>Press <strong>left/right</strong> on the cart name to manually swap out the active cart</li> <li>The carts will auto-advance to a random cart every 20~60 seconds. You can disable this by setting &quot;Cycle: no&quot; in the menu</li> </ul> <p>An example of navigating the menu:<br /> <div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/gallery-818f.gif" alt="" /> <p></div></div></div></p> <hr /> <p>There are 25+ carts in this thing, be sure to check out at least one of these before you leave: system restored, adrift, wormys, weird bug, firefly spring</p> <p>Hope you enjoy!</p> <h2>Technical details:</h2> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"><br /> Search for <code>carts={</code> to find the source code for each cart (inside the &quot;script&quot; function of each table entry). The code isn't very readable, but that's where you can find it. There are links to the canonical versions elsewhere, which you can try to pick apart using my <a href="https://www.lexaloffle.com/bbs/?tid=49949">twiddler tool</a>, if you'd like.</p> <p>For this gallery cartridge, I had to set up this whole harness that runs each cart in a sort of sandbox, letting them act as intended without messing with each other. There were lots of finicky parts to this:</p> <ul> <li>resetting the draw state after each cart</li> <li>using coroutines to allow carts to use <code>goto</code> and other unusual control flow</li> <li>saving a screenshot of the initial pico-8 state, for the carts that depend on it</li> <li>redefining the <code>time()</code> function so that carts would think they started at t=0</li> <li>saving a copy of the values of special characters like 🐱, in case carts redefine those values</li> <li>etc, etc</li> </ul> <p>I had to make some minor changes to some carts (mainly changing <code>flip()</code> calls to <code>yield()</code> calls) but mostly I was able to wholesale copy-paste them directly into this gallery cartridge, which felt pretty neat.<br /> </div></div></div></p> https://www.lexaloffle.com/bbs/?tid=145063 https://www.lexaloffle.com/bbs/?tid=145063 Sat, 02 Nov 2024 12:52:43 UTC music desync (0.1.1b) <p><a href="https://www.lexaloffle.com/bbs/?uid=1"> @zep</a> o/ thanks for fixing the sfx arpeggio effects! here's another audio bug for you:</p> <p>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 <a href="https://www.lexaloffle.com/bbs/?tid=143443">music visualizer demo cart</a>, reproduced here for convenience:</p> <p> <table><tr><td> <a href="/bbs/?pid=152071#p"> <img src="/bbs/thumbs/pico64_datufbuyi-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=152071#p"> datufbuyi</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=152071#p"> [Click to Play]</a> </td></tr></table> </p> <p>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:</p> <p>&gt; <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/Screenshot_20240822_155741.png" alt="" /> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/Screenshot_20240822_160359.png" alt="" /></p> <p>That last number (the one that desyncs by 11 ticks for me) is <code>stat(400+i,11)</code>. I believe this represents the number of ticks played on channel i for the current pattern.</p> <p><em>This also happens locally</em> in the linux executable, but the tracks only desync by a single tick. I can't hear it, but you can see it by pausing after pattern 4.</p> <p>I think this is new as of 0.1.1, but it's possible I just didn't notice in 0.1.0h when I uploaded this cart, or the desync might be less consistent than it currently seems</p> https://www.lexaloffle.com/bbs/?tid=143812 https://www.lexaloffle.com/bbs/?tid=143812 Thu, 22 Aug 2024 23:26:15 UTC sfx/music data layout <p>Inspired by <a href="https://www.lexaloffle.com/bbs/?tid=2341">https://www.lexaloffle.com/bbs/?tid=2341</a>, 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)</p> <p>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.</p> <h2>Library</h2> <p>Code:<br /> <div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>-- sfx/music data wrangler -- by pancelor -- The docs call this the &quot;index&quot;, I call it the &quot;header&quot; -- 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,&quot;bad sfx header 1&quot;) 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,&quot;bad sfx header 2&quot;) assert(unused3==0,&quot;bad sfx header 3&quot;) assert(unused4==0,&quot;bad sfx header 4&quot;) 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 =&gt; tracks[3], 0x8 =&gt; tracks[4], ... 0x80 =&gt; 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&amp;1==1,&quot;unknown track format&quot;) 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,&quot;bad sfx track&quot;) -- note: if len&lt;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&amp;1==1,&quot;unknown track format&quot;) 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,&quot;not implemented&quot;) -- 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)&gt;&gt;i&amp;1~=0 and stat(400+i,12) or -1 end -- i: channel index 0..7 function channel_current_row(i) return stat(464,0)&gt;&gt;i&amp;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</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> <p>Sources:</p> <ul> <li>/system/apps/sfx.p64/data.lua</li> <li><a href="https://www.lexaloffle.com/dl/docs/picotron_synth.html#Index">https://www.lexaloffle.com/dl/docs/picotron_synth.html#Index</a></li> </ul> <h2>Demo cart</h2> <p>Here's a tiny music &quot;visualizer&quot; that uses this library:<br /> <table><tr><td> <a href="/bbs/?pid=152071#p"> <img src="/bbs/thumbs/pico64_datufbuyi-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=152071#p"> datufbuyi</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=152071#p"> [Click to Play]</a> </td></tr></table> </p> <h2>License</h2> <p>This library is licensed as <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a> (basically, credit me and link back to this post, and you can use this code for anything)</p> <p>(This does not include the music playing in the demo cart. The music is by <a href="https://www.lexaloffle.com/bbs/?uid=12744"> @iaoth</a> and is licensed as <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> -- see <a href="https://www.lexaloffle.com/bbs/?pid=51460">https://www.lexaloffle.com/bbs/?pid=51460</a>)</p> https://www.lexaloffle.com/bbs/?tid=143443 https://www.lexaloffle.com/bbs/?tid=143443 Fri, 02 Aug 2024 22:56:07 UTC spritesheet remapping 0x5f54 crashes after reboot <p><a href="https://www.lexaloffle.com/bbs/?uid=1"> @zep</a> o/</p> <p>0.2.6b / linux:</p> <ol> <li>launch pico8 executable</li> <li>run <code>poke(0x5f54,0x80)</code> (in the console)</li> <li>run <code>reboot</code></li> <li>run <code>poke(0x5f54,0x80)</code> (copied from <a href="https://www.lexaloffle.com/bbs/?tid=140421">the 0.2.6 release notes</a>)</li> <li>the executable crashes</li> </ol> <p>Step 2 is optional -- it still crashes in step 5 if you skip step 2<br /> In Step 4, running <code>poke(0x5f54,0x60)</code> instead does <em>not</em> crash.</p> <p>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:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>&gt; 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 Unit: [email protected] User Unit: app-pico8-b4acdb07c9cf4629b15a6b78f024fa7f.scope Slice: user-1000.slice Owner UID: 1000 (pancelor) Boot ID: bc016245c64e47f1b3755470beec5d96 Machine ID: 4eec57f899074b4eb49773a72b91be1c Hostname: pancelor-manjaro Storage: /var/lib/systemd/coredump/core.pico8.1000.bc016245c64e47f1b3755470beec5d96.12604.1718761894000000.zst (present) Size on Disk: 10.3M Message: Process 12604 (pico8) of user 1000 dumped core. Stack trace of thread 12604: #0 0x00000000004734e6 n/a (/home/pancelor/.config/itch/apps/pico-8/pico-8/pico8 + 0x734e6) ELF object binary architecture: AMD x86-64 Failed to invoke gdb: No such file or directory</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>tl;dr: SEGV at 0x734e6</p> https://www.lexaloffle.com/bbs/?tid=142755 https://www.lexaloffle.com/bbs/?tid=142755 Wed, 19 Jun 2024 01:38:37 UTC coroutine stack pollution <p> <table><tr><td> <a href="/bbs/?pid=149328#p"> <img src="/bbs/thumbs/pico8_wutewuwiho-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=149328#p"> wutewuwiho</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=149328#p"> [Click to Play]</a> </td></tr></table> </p> <p>hi <a href="https://www.lexaloffle.com/bbs/?uid=1"> @zep</a>! This cart should not error, but it does (pico8 0.2.6b / linux)</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>-- 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=&quot;flr rvals:&quot; for i,v in ipairs(rets) do str ..= &quot;\n\t&quot;..i..&quot;: &quot;..tostr(v) end str ..=&quot;\nstat(1): &quot;..stat(1) print(str..&quot;\n&quot;) assert(false) end -- uncomment this to fix it -- if flr==flr then end end end) cls() --remove second param to fix it assert(coresume(coro, &quot;uh-oh&quot;)) print&quot;no errors&quot;</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <hr /> <p>It seems like the string &quot;uh oh&quot; is being popped from the stack in a completely wrong place.</p> <p>See also <a href="https://www.lexaloffle.com/bbs/?tid=141267">https://www.lexaloffle.com/bbs/?tid=141267</a> , a picotron coroutine bug. This cart is a modified version of <a href="https://www.lexaloffle.com/bbs/?uid=46556"> @jesstelford</a>'s code there. I'm unsure if the root cause is related, but the result is the same.</p> <p>Notably, this cart consistently crashes every single time (unlike the picotron version), and seems directly related to stat(1) going over 1.0</p> <p>I marked a bunch of things you can uncomment to make this test case pass. (there are some very weird things you can do to make it suddenly work)</p> <hr /> <p>I'm fuzzy on the details, but it seems like something like this is happening:</p> <ol> <li><code>coresume(coro, &quot;uh-oh&quot;)</code> pushes &quot;uh-oh&quot; to the internal lua/C/lua_State stack, where it stays for a while (because there isn't a <code>yield()</code> that pops that value)</li> <li>the coroutine takes a long time (<code>stat(1)&gt;1</code>), and pico8 pauses my cart to do its own thing (flip, etc)</li> <li>pico8 prepares to hand control back to my cart, by restoring the state my cart was in when it got forcibly paused. it restores the stack with &quot;uh-oh&quot; on it, but it does it wrong somehow</li> <li>my code resumes running, maybe right at the point where <code>flr()</code> returns? (that would be quite a coincidence, but it might explain why irrelevant changes like <code>local x=1</code> would squash the bug)</li> <li><code>flr</code> returns the correct value, but also the rest of the stack, which is &quot;uh oh&quot;.</li> </ol> https://www.lexaloffle.com/bbs/?tid=142550 https://www.lexaloffle.com/bbs/?tid=142550 Sun, 02 Jun 2024 04:59:16 UTC 0x5f36 automatic character wrap + camera <p>I'm pretty sure this is a bug; I'm on pico8 0.2.6b / linux</p> <p>You can turn on character wrapping with 0x5f36 / 24374, but it acts unexpectedly when you move the camera:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>cls(1) poke(0x5f36,0x80) --turn on character wrapping msg='0123456789abcdefghijklmnopqrstuvwxyz' ?msg,0,0,12 camera(16,0) ?msg,16,12,13</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Expected behavior: both prints should wrap at the edge of the screen.<br /> Actual behavior:</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/game_0.png" alt="" /> <hr /> <p>There's a similar issue with p8scii &quot;\^r&quot; wrapping:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>cls(1) msg='\^rf0123456789abcdefghijklmnopqrstuvwxyz' --note p8scii wrap at start ?msg,0,0,13 camera(16,0) ?msg,16,18,14</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/untitled_2_1.png" alt="" /> https://www.lexaloffle.com/bbs/?tid=142400 https://www.lexaloffle.com/bbs/?tid=142400 Thu, 23 May 2024 21:45:57 UTC menuitem never runs with P8SCII flip() <p>cart A: (broken)</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>msg=&quot;havent pressed test yet&quot; menuitem(1,&quot;★test&quot;,function() msg=&quot;pressed test!&quot; end) ::_:: ?&quot;\^1\^c&quot; --https://www.lexaloffle.com/dl/docs/pico-8_manual.html#Control_Codes print(time(),0,0,7) print(msg) goto _</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>cart B: (working)</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>msg=&quot;havent pressed test yet&quot; menuitem(1,&quot;★test&quot;,function() msg=&quot;pressed test!&quot; end) ::_:: flip()?&quot;\^c&quot; --this line is the only difference print(time(),0,0,7) print(msg) goto _</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>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 &quot;havent pressed test yet&quot;</p> <p>cart B works as expected -- it says &quot;pressed test!&quot;</p> <p>My system is linux / pico8 0.2.6b</p> https://www.lexaloffle.com/bbs/?tid=142313 https://www.lexaloffle.com/bbs/?tid=142313 Fri, 17 May 2024 11:49:05 UTC Make Ten <p> <table><tr><td> <a href="/bbs/?pid=148268#p"> <img src="/bbs/thumbs/pico8_make_ten-3.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=148268#p"> make_ten</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=148268#p"> [Click to Play]</a> </td></tr></table> </p> <p>Select numbers that add to ten, to remove them from the board. How high can you score in just 2 minutes?</p> <p>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)</p> <h2>How to play</h2> <ul> <li>Drag your mouse to select groups of numbers that add up to 10.</li> <li>I recommend playing in fullscreen; it's easier to click on larger numbers.</li> <li>In the original game, you're awarded one point per number removed,</li> <li>Get the highest score you can within 2 minutes. Good luck!</li> </ul> <p>This was my submission for TweetTweetJam 9, written in 490 characters of code:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>z={}c=-8s=0g='⁶!5f58⁵d8&quot;'::_::?&quot;⁶1⁶c0⁶!5f2d3&quot; e=1x=stat(32)y=stat(33)if btn(5)do f=mid(16,96,y)if(p==a)p=x q=f?&quot;⁷i6v1c1&quot; 😐=c&amp;min(x,p)⌂=c&amp;min(f,q)♪=c&amp;max(x,p)&hearts;=c&amp;max(f,q)else if(p)p=a if(n==c)e=0?&quot;⁷i7f1a&quot; end?g,d n=2for i=32,207do w=z[i]or rnd(9)\1+1u=i%16*8v=c&amp;i\2if(u-😐|♪-u|v-⌂|&hearts;-v&gt;0)w*=e n-=w ⬅️+=➡️&amp;~e z[i]=w?w,u,v,w end?&quot;⁶jc1score &quot;..s,-🅾️ if(btnp(6))🅾️=⬇️ if(p)rect(😐,⌂,♪+9,&hearts;+8) d=-t()?&quot;⁶x2&sup1;b█&quot;,d-5,106 if(d==-128)➡️=0g=&quot;⁶j8rtime up!&quot;⬇️=🅾️?&quot;⁷v3fg&quot; if(s&lt;⬅️)s+=1?&quot;⁷x5v3c0&quot; ?&quot;⁶.&sup1;&sup3;⁷ᶠ&sup3;⁴\0\0&quot;,x-2,y-2,7 goto _</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>There's a <a href="https://gist.github.com/pancelor/7e43d62bcf4bb757f454eecebbbd897b">commented version here</a>.</p> <p>See also: <a href="https://pancelor.itch.io/make-ten">itch</a> | <a href="https://mastodon.social/@pancelor/112421461290262396">mastodon</a> | <a href="https://cohost.org/pancelor/post/5925753-make-ten">cohost</a></p> <h2>Deluxe edition</h2> <p>Coming soon! Check <a href="https://pancelor.itch.io/make-ten">itch</a> for updates.</p> https://www.lexaloffle.com/bbs/?tid=142209 https://www.lexaloffle.com/bbs/?tid=142209 Sat, 11 May 2024 11:27:15 UTC if-shorthand parser weirdness <p><a href="https://www.lexaloffle.com/bbs/?uid=1"> @zep</a> This maybe isn't a bug, but it feels very weird to me:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>cls() if (1)&lt;2 print&quot;a&quot; print&quot;b&quot;</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>this prints <code>a b</code>, but I would expect a syntax error instead -- the condition is &quot;1&lt;2&quot; but the if-shorthand parens are only surrounding the 1, instead of the whole condition.</p> <p>(if you change it to <code>if (1)&lt;0</code> then it only prints &quot;b&quot;, which verifies that the condition isn't being interpreted as just <code>if (1)</code> or something like that)</p> <p>oh, and my system is pico8 0.2.6b / linux</p> https://www.lexaloffle.com/bbs/?tid=142179 https://www.lexaloffle.com/bbs/?tid=142179 Thu, 09 May 2024 01:02:15 UTC parser bug: shorthand if+print+comment <p>Hi <a href="https://www.lexaloffle.com/bbs/?uid=1"> @zep</a>!</p> <p>pico8 0.2.6b / linux:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>cls() if (false)?1 --hi print(2) print(3)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>expected output: <code>2 3</code><br /> actual output: <code>3</code></p> <p>Here's an altered version that works as expected:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>cls() if (false)?1 print(2) print(3)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>(this prints <code>2 3</code>, as expected)</p> <hr /> <p>3 things seem required to trigger this bug:</p> <ol> <li>shorthand if</li> <li>shorthand print on the same line</li> <li>!! further text after the shorthand print (&quot; --hi&quot;, in this example. but just a single trailing space triggers the bug too)</li> </ol> <p>When these are all true, the next line seems to get scooped up into the shorthand line. This can include attaching an <code>else</code> to the wrong <code>if</code>, like in this more complicated example:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>cls() if false then if (false)?1 --hi else print(2) print(3) end print(4)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>expected output: <code>2 3 4</code><br /> actual output: <code>4</code></p> https://www.lexaloffle.com/bbs/?tid=142148 https://www.lexaloffle.com/bbs/?tid=142148 Tue, 07 May 2024 11:54:35 UTC photo_carousel <p>This is a silent tutorial video; skip to 2:10 for the juicy bit:<br /> <object width="640" height="400"><param name="movie" value="https://www.youtube.com/v/bkB_kN-3L8U&hl=en&fs=1&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="https://www.youtube.com/v/bkB_kN-3L8U&hl=en&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="400"></embed></object></p> <p>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.</p> <h2>Installing</h2> <ul> <li><code>load #photo_carousel</code></li> <li><code>mkdir /appdata/system/wallpapers</code></li> <li><code>save /appdata/system/wallpapers/photo_carousel.p64</code></li> <li>run the cart once, to generate the appdata folder</li> <li><code>cd /appdata/photo_carousel</code></li> <li><code>folder</code></li> <li>using your host OS, copy any PNG files into this folder (don't put them in subfolders)</li> <li>set your wallpaper to <code>photo_carousel</code> in System Settings</li> </ul> <h2>Settings</h2> <p>Run <code>podtree /appdata/photo_carousel/settings.pod</code> 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)</p> <p>Set the transition time to 0 to disable transitions</p> <h2>Details</h2> <h3>Converting PNGs</h3> <p>This cart uses <a href="https://www.lexaloffle.com/bbs/?pid=importpng#p">importpng</a> to convert PNG files into Picotron graphics. It does this once, in the background, and caches the results.</p> <p>If you edit one of your PNGs, photo_carousel won't notice (afaik it's impossible to get last-edited time of PNGs in Picotron) Rename your file to generate a new cache entry, or delete <code>/appdata/photo_carousel/cache.pod</code> to regenerate the entire cache.</p> <p><a href="https://www.lexaloffle.com/bbs/?pid=importpng#p">importpng</a> is fast, but it's sometimes lacking in its ability to convert images into Picotron's palette. If you want better colors, you should use an external tool (like Aseprite) to convert your PNG into the base Picotron palette (you'll want the base palette, because the global palette changes to the base palette whenever you focus on some other window)</p> <p>In the future I may use <a href="https://www.lexaloffle.com/bbs/?pid=pngframe#p">PNG Frame</a> to get better colors by default, but I haven't done that work yet.</p> <h3>Transitions</h3> <p>You can add your own transitions -- you can skim through src/subdraw.lua without needing to understand anything else in the cart, and add new entries to the list of &quot;subdraws&quot; (transitions). Its very shadery/demosceney code</p> <p>Many of the subdraws use some helper functions in src/tools.lua</p> <p>Post your transition code here!</p> <h3>CPU usage</h3> <p>The cart uses about 0.002 cpu (0.2%) most of the time. During transitions, it can spike up to 25% cpu or so (depending on the transition). If you care, you can disable the transitions as a whole (by editing the settings) or individually (by editing the multi-line comments in src/subdraw.lua)</p> <h3>Cart</h3> <p>Here's the cart file. (It won't work here on the BBS, you need to download it)</p> <p> <table><tr><td> <a href="/bbs/?pid=145966#p"> <img src="/bbs/thumbs/pico64_photo_carousel-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=145966#p"> photo_carousel</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=145966#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=141520 https://www.lexaloffle.com/bbs/?tid=141520 Sun, 07 Apr 2024 11:59:48 UTC coroutine bug <p>hey <a href="https://www.lexaloffle.com/bbs/?uid=1"> @zep</a>, I've found a nasty coroutine(?)/multival bug. I'm on Linux + picotron 0.1.0d.</p> <p>tl;dr: sometimes <code>select(&quot;#&quot;,tostr(i))</code> is 2, possibly triggered by calling coresume() with extra args.</p> <hr /> <p>I ran into this initially because <code>add({},3,nil)</code> is a runtime error now (it used to work in PICO-8, but now it throws <code>bad argument #2 to 'add' (position out of bounds)</code>). I had some code: <code>add(list,quote(arg))</code> 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: <code>add(list,(quote(arg)))</code>)</p> <p>Version A of the code is very short, and trips the assert inside <code>spin</code> maybe 50% of the time? sometimes many runs in a row don't trigger the assert, but sometimes many runs in a row <em>all</em> trigger the assert. (maybe that's just statistics tho). Version B is a bit more complex but always trips the assert instantly for me.</p> <hr /> <p>Version A: (short, inconsistent) <div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>printh&quot;---&quot; function _init() local bad,good = cocreate_spin() fn = bad end function _draw() cls() local _,prog = fn(0.70) ?stat(1) ?prog or &quot;done&quot; end function cocreate_spin() local coro = cocreate(spin) return function( cpu_limit) if costatus(coro)==&quot;suspended&quot; then --this one breaks sometimes return assert(coresume(coro,cpu_limit or 0.90)) end end, function() if costatus(coro)==&quot;suspended&quot; then --this one always works return assert(coresume(coro)) end end end local total = 1000000 function spin() for i=1,total do if i%10000==0 --[[and stat(1)&gt;cpu_limit]] then yield(i/total) end local n = select(&quot;#&quot;,quote(i)) if n!=1 then assert(false,tostr(n)..&quot; retvals?! &quot;..i) end end end function quote(t) return tostr(t) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> <p>Version B: (longer, very consistent) <div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>printh&quot;---&quot; function _init() poke(0x5f36,0x80) -- wrap text window{ title=&quot;import png&quot;, width=160, height=64, } job = job_importpng() end function _update() job:work(0.70) end function _draw() cls() print(stat(1)) print(costatus(job.coro)) end function job_importpng() local coro = cocreate(pq_many) assert(coresume(coro)) local job = { coro = coro, progress = 0.00, } -- returns true iff job has more work to do function job:work( cpu_limit) if costatus(self.coro)==&quot;suspended&quot; then local _,dat = assert(coresume(self.coro,cpu_limit or 0.90)) if dat then self.progress = dat return true -- more work to do end end end return job end function pq_many() for i=1,1000000 do if i&amp;2047==0 then yield() end pq(i) end end -- quotes all args and prints to host console -- usage: -- pq(&quot;handles nils&quot;, many_vars, {tables=1, work=11, too=111}) function pq(arg) local s= {} local n = select(&quot;#&quot;,quote(arg)) if n!=1 then local second = select(2,quote(arg)) assert(false,tostr(n)..&quot; retvals?: &quot;..quote(arg)..&quot; &quot;..tostr(second)) end add(s,(quote(arg))) -- add(s,quote(arg)) printh(table.concat(s,&quot; &quot;)) end -- quote a single thing -- like tostr() but for tables -- don't call this directly; call pq or qq instead function quote(t) if type(t)~=&quot;table&quot; then return tostr(t) end local s={} for k,v in pairs(t) do add(s,tostr(k)..&quot;=&quot;..quote(v)) end return &quot;{&quot;..table.concat(s,&quot;,&quot;)..&quot;}&quot; end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> <hr /> <p>As noted in Version A, the two coresume-wrapping-functions inside cocreate_spin() act differently -- I've never been able to trip the assert with the &quot;good&quot; version. I've tried versions of this code with no coroutines and haven't been able to trip the assert.</p> <p>idk what else to say, this bug seems baffling -- sometimes <code>select(&quot;#&quot;,quote(i))</code> is 2, despite quote() being a wrapper for tostr()</p> https://www.lexaloffle.com/bbs/?tid=141267 https://www.lexaloffle.com/bbs/?tid=141267 Sat, 30 Mar 2024 08:35:58 UTC import png <p> <table><tr><td> <a href="/bbs/?pid=144661#p"> <img src="/bbs/thumbs/pico64_importpng-9.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=144661#p"> importpng</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=144661#p"> [Click to Play]</a> </td></tr></table> </p> <h2>Guide</h2> <p>Currently (in Picotron 0.1.0e) it's hard to use a PNG image as a sprite. You can <code>fetch(&quot;myimage.png&quot;)</code>, but the result isn't in the right image format. So, here's a small tool to convert PNG files into picotron sprites.</p> <p>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.</p> <p>Drag in a .hex file (e.g. from lospec.com) or a .pal file (e.g. from <a href="https://www.lexaloffle.com/bbs/?pid=okpal#p">OkPal</a>) before importing your png to change the import palette.</p> <h2>Details</h2> <h3>Import speed</h3> <p>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.</p> <h3>Color quantization</h3> <p>You can use any colors, and the tool will try it's best to fit them to the palette. It isn't very smart about converting colors (it uses nearest euclidean distance in RGB space, and doesn't do any dithering). If you want better color conversion, use some external tool and make sure the PNG is in the correct palette before converting it with this tool.</p> <p>See also PNG Frame, in &quot;Related tools&quot; below.</p> <h3>Wallpapers</h3> <p>If you're making a desktop wallpaper, I think you should stick to the default palette. If you use a custom palette, then your colors will change whenever you focus some window besides the desktop, I'm pretty sure.</p> <p>To make a wallpaper:</p> <ul> <li>make sure your wallpaper folder exists (<code>mkdir /appdata/system/wallpapers</code>)</li> <li>save a new cart into that folder with this code: <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function _draw() spr(1) _draw = function() end end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div></li> <li>convert your image using this tool, and copy it into your clipboard</li> <li>paste your image into sprite 1 of the default sprite file</li> </ul> <h3>Library usage</h3> <p>This code is organized to make it easy to use as a library in your code. The library is self-contained in <code>lib/importpng.lua</code>, and you interact with it by calling <code>myjob = job_importpng(myimg)</code>. The returned &quot;job&quot; is essentially a coroutine, but one that will let you limit its processing time. Call <code>myjob:work(limit)</code> during _update() until the job is done, or call <code>myjob:join()</code> a single time to finish it all at once (likely lagging the game). See the code for more details.</p> <p>License: <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC4-BY-NC-SA</a></p> <h2>Related tools</h2> <ul> <li>Cutievirus' <a href="https://www.lexaloffle.com/bbs/?pid=pngframe#p">PNG Frame</a> -- a similar tool with much better color conversion. If your png is already in the right palette, importpng is faster than PNG Frame. But if you want better color quantization, use PNG Frame.</li> <li>My <a href="https://gist.github.com/pancelor/43512028087bf34e5d9773a61a03f93a">Aseprite PICO-8/Picotron export extension</a> lets you export Picotron pods directly to your clipboard from Aseprite.</li> <li>My <a href="https://www.lexaloffle.com/bbs/?tid=140800">sprite importer</a> lets you import PICO-8 spritesheets into Picotron.</li> <li>My <a href="https://www.lexaloffle.com/bbs/?pid=photo_carousel#p">photo carousel</a> uses this importer to show your custom PNGs as an animated wallpaper.</li> <li>drakmaniso's <a href="https://www.lexaloffle.com/bbs/?pid=okpal#p">OkPal</a> palette editor.</li> </ul> <h2>changelog</h2> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <h3>v0.4 (#importpng-9)</h3> <ul> <li>add <a href="https://qoiformat.org/">QOI</a> image support - thanks to vebrun for prompting me to add it!</li> </ul> <h3>v0.3 (#importpng-7)</h3> <ul> <li>support for custom palettes</li> <li>make it easy to use importpng as a library</li> </ul> <h3>v0.2 (#importpng-6)</h3> <ul> <li>show image as it imports</li> <li>draw black in preview (not transparent anymore)</li> <li>export to compressed pod format</li> </ul> <h3>v0.1 (#importpng-4)</h3> <ul> <li>initial release<br /> </div></div></div></li> </ul> https://www.lexaloffle.com/bbs/?tid=141149 https://www.lexaloffle.com/bbs/?tid=141149 Wed, 27 Mar 2024 11:24:42 UTC p8x8: convert PICO-8 carts to Picotron <p> <table><tr><td> <a href="/bbs/?pid=144532#p"> <img src="/bbs/thumbs/pico64_p8x8-7.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=144532#p"> p8x8</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=144532#p"> [Click to Play]</a> </td></tr></table> </p> <h3>p8x8: convert PICO-8 carts into Picotron carts (some assembly required)</h3> <p>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.</p> <p>Lots more info (instructions, compatibility notes, CC license, etc) here: <a href="https://github.com/pancelor/p8x8/">https://github.com/pancelor/p8x8/</a></p> <p>Teaser video here: <a href="https://mastodon.social/@pancelor/112162470395945383">https://mastodon.social/@pancelor/112162470395945383</a></p> <h2>changelog</h2> <h3>v1.8 (#p8x8-8, unreleased)</h3> <ul> <li>music/sfx conversion!! just waiting on picotron 010h to add instrument effects</li> </ul> <h3>v1.7 (#p8x8-7)</h3> <ul> <li>fix secret palette</li> <li>add more <a href="https://github.com/pancelor/p8x8/tree/main/doc">docs</a></li> <li>add <code>p8x8_symbol_visual()</code> and <code>p8x8_datastring()</code> alongside <code>p8x8_symbol()</code> to help convert symbols. <a href="https://github.com/pancelor/p8x8/blob/main/doc/symbols.md">details</a></li> </ul> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <h3>v1.6 (#p8x8-6)</h3> <ul> <li>speed up dget/dset (batch disk writes)</li> <li>speed up map() (use picotron's builtin map() which does much better camera culling)</li> <li>add support for pico8 &quot;secret palette&quot;</li> </ul> <h3>v1.5 (#p8x8-5)</h3> <ul> <li>custom fonts work now <ul> <li>docs + snippet for making custom fonts work even if you set them up with p8scii</li> </ul></li> <li>fix player 2 btn/btnp</li> <li>some progress on porting sfx</li> <li>minor bugfix: prevent crash when input cart has no <strong>gfx</strong> section</li> </ul> <h3>v1.4 (#p8x8-4)</h3> <ul> <li>bugfixes for add(), rnd()</li> <li>organize github docs</li> <li>fix sfx()/music() (used to be noop; now passthrough to p64)</li> </ul> <h3>v1.3 (#p8x8-3)</h3> <ul> <li>better default fullscreen border</li> <li>better warnings</li> </ul> <h3>v1.2 (#p8x8-2)</h3> <ul> <li>support fullscreen border images</li> <li>easier main.lua tweaking (fullscreen, pause_when_unfocused)</li> <li>more docs / warnings / ux</li> </ul> <h3>v1.1 (#p8x8-1)</h3> <ul> <li>nicer error/warning handling (e.g. when there's no code section)</li> <li>handle windows CRLF carts properly</li> <li>generate warnings much faster (smarter sorting algorithm)</li> </ul> <h3>v1.0 (#p8x8-0)</h3> <ul> <li>public release<br /> </div></div></div></li> </ul> https://www.lexaloffle.com/bbs/?tid=141107 https://www.lexaloffle.com/bbs/?tid=141107 Tue, 26 Mar 2024 14:35:54 UTC sedish: edit system files on startup <p>Here's my <code>/appdata/system/startup.lua</code> file (picotron automatically runs it on startup)</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>-- 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 &quot;&quot;)..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(&quot;sedish: couldn't find file &quot;..fname) return end for i,mod in ipairs(mods) do local cut,paste = unpack(mod) -- printh(cut..&quot; &quot;..paste) if not cut or not paste then printh(&quot;sedish: bad cut/paste data in &quot;..fname) return end local changed src,changed = _replace(src,cut,paste) if not changed then printh(&quot;sedish: mod #&quot;..i..&quot; did nothing to &quot;..fname) end if not src or #src==0 then printh(&quot;sedish: bad result in &quot;..fname) return end end -- printh(&quot;storing &quot;..fname..&quot;: &quot;..sub(src,1,100):gsub(&quot;[\n\r]&quot;,&quot;&quot;)) store(fname,src) end sedish(&quot;/system/lib/gui_ed.lua&quot;,{ { -- fix alt-tab annoyance (pressing alt-tab inserts tab character into code) [[if (keyp(&quot;tab&quot;)) then]], [[if keyp&quot;tab&quot; and not key&quot;alt&quot; then]], }, }) sedish(&quot;/system/apps/podtree.p64/main.lua&quot;,{ { -- fix podtree bug (pressing enter crashes the program) [[if(key(&quot;shift&quot;)) return true]], [[if(_ENV.key(&quot;shift&quot;)) return true]], }, }) -- (you need to open a new gfx tab for these gfx.p64 edits to work) sedish(&quot;/system/apps/gfx.p64/pal.lua&quot;,{ { -- show pal numbers [[pcols[x + y*epr])]], [[pcols[x + y*epr])if pal_swatch==1 and x%2==0 then print(pcols[x + y*epr],x*ww+5,y*ww+1,0) end]], }, }) -- sedish(&quot;/system/apps/gfx.p64/nav.lua&quot;,{ -- { -- display sprite numbers in hex -- [[string.format(&quot;%03d&quot;,current_item)]], -- [[string.format(&quot;x%02x&quot;,current_item)]], -- }, -- }) sedish(&quot;/system/apps/gfx.p64/update.lua&quot;,{ { -- don't flip when you press ctrl-v (noticeable when undoing) [[if (keyp(&quot;v&quot;))]], [[if (keyp&quot;v&quot; and not key&quot;ctrl&quot;)]], }, }) sedish(&quot;/system/lib/gui_ed.lua&quot;,{ { -- fix custom key callbacks in gui [[content.key_callback(k)]], [[content.key_callback[k](k)]], }, }) -- doesn't work for startup terminal -- need to wait for it to regenerate, e.g. after running a fullscreen app sedish(&quot;/system/apps/terminal.lua&quot;,{ { -- add custom terminal_cd event (to let my z.lua util &quot;run&quot; cd for you) [[-- scroll down only if needed]], [[on_event(&quot;terminal_cd&quot;,function(msg) cd(msg.path) end)]], }, { -- show slightly different terminal prompt when in upgraded terminal [[return result -- custom prompt goes here]], [[return &quot;\f6&quot;..pwd()..&quot;\f7 &gt; &quot;]], }, }) printh &quot;sedish done&quot;</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h2>what is this</h2> <p>I made this because picotron inserts a tab into my code whenever I alt-tab. I figured out how to edit the code editor itself to not do that, and then I made this script to automatically make the required edits. (you can't just edit the files on disk, because they get regenerated every time picotron boots)</p> <p>this won't be necessary for too long hopefully, but it works great for now!</p> <p>(the name &quot;sedish&quot; comes from the (loose) inspiration for this: <a href="https://ss64.com/bash/sed.html">sed</a>)</p> <h2>post your tweaks</h2> <p>If anyone has other system tweaks like this one (fixing alt-tab), post them here!</p> https://www.lexaloffle.com/bbs/?tid=140847 https://www.lexaloffle.com/bbs/?tid=140847 Mon, 18 Mar 2024 04:05:44 UTC pico8 sprite+map importer <p>update: there are basically 3 tools that are relevant here:</p> <ul> <li>this tool (sprimp) -- maybe still useful, but superceded by p8x8:</li> <li><a href="https://www.lexaloffle.com/bbs/?pid=p8x8#p">p8x8</a>: a tool to fully convert PICO-8 carts to Picotron carts (some assembly required) -- this grew out of sprimp</li> <li>my <a href="https://gist.github.com/pancelor/43512028087bf34e5d9773a61a03f93a">aseprite exporter plugin</a> -- still useful</li> </ul> <p>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.</p> <hr /> <p> <table><tr><td> <a href="/bbs/?pid=143506#p"> <img src="/bbs/thumbs/pico64_sprimp-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=143506#p"> sprimp</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=143506#p"> [Click to Play]</a> </td></tr></table> </p> <p>tada! now you can import pico8 spritesheets. and map too!</p> <h2>importing a .p8 file</h2> <ul> <li>put your game.p8 file somewhere inside picotron's file system <ul> <li>the filename should not have any dashes (&quot;-&quot;), they generally cause problems currently (picotron 0.1.0b2)</li> </ul></li> <li>run this importer (sprimp)</li> <li>import the p8 cart <ul> <li>you can drag-and-drop the cart onto the sprimp window</li> <li>or you can use &quot;Import .p8 file&quot; in the menu (the dialog that pops up says &quot;save as&quot; -- please ignore this. this is the best way I know how to launch a system filepicker)</li> </ul></li> </ul> <h2>exporting sprites</h2> <ul> <li>press &quot;Save gfx..&quot;. choose a filename; the default is <code>/ram/cart/gfx/0.gfx</code> (which is the default spritesheet for your current cart)</li> <li><strong>IMPORTANT</strong>: if the spritesheet was already open in picotron's gfx editor, you'll need to close the file and reopen it (right click on the tab, close, then press the plus)</li> <li>if you saved to <code>/ram/cart/...</code> save your cart afterwards (ctrl-s)</li> </ul> <h2>exporting map</h2> <ul> <li>(same as exporting sprites, just click &quot;Save map..&quot; instead)</li> <li>for an easy transition from pico8 maps to picotron maps, check out <a href="https://www.lexaloffle.com/bbs/?pid=143245#p">this snippet</a>, which includes <code>mget</code> and <code>mset</code> replacements</li> </ul> <h2>other ways to import (copypaste, aseprite)</h2> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <h3>importing sprite by copy-paste</h3> <ul> <li>open the pico8 sprite editor</li> <li>copy the entire spritesheet (press tab, ctrl-a, ctrl-c)</li> <li>your clipboard now has a <code>[gfx]...</code> code in it</li> <li>open this tool (sprimp) in picotron</li> <li>past the <code>[gfx]...</code> code into the tool</li> </ul> <h3>importing a single sprite</h3> <ul> <li>you can copy from pico8 and paste into picotron directly, zep made that work already</li> <li>you can even paste your entire spritesheet into a single picotron sprite -- it's sorta wild. but you probably wanted to spread the spritesheet out into individual sprites, right? that's where this tool can help</li> </ul> <h3>importing from aseprite</h3> <p>you can use <a href="https://gist.github.com/pancelor/43512028087bf34e5d9773a61a03f93a">my exporter script</a> for aseprite to export aseprite images into the <code>[gfx]...</code> format, letting you paste them into picotron or sprimp (or even pico8!)<br /> </div></div></div></p> <h2>future plans</h2> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <ul> <li>Yes: Make importing sprites/maps easy. (mostly done! still some UI/UX work tho)</li> <li>Done but not yet published: importing p8 code tabs into .lua files. with a big disclaimer that you'll need to do a bunch of manual work to fix the code afterward (edit: this is now published as <a href="https://www.lexaloffle.com/bbs/?pid=p8x8#p">p8x8</a>)</li> <li>Maybe: sfx/music. I likely won't do this myself. If you have a working sfx/music importer and want to join forces, let me know!</li> <li>No: make it possible to drag-and-drop a pico8 cart into picotron and have everything &quot;just work&quot;. this is <em>not</em> meant to be a perfect emulator, just a stepping stone</li> </ul> <p>maybe zep will add improvements to picotron itself, and this whole project will no longer be relevant. that would be great! but whether or not that happens, this was a fun small project for me to learn some basics of picotron tool-making<br /> </div></div></div></p> <h2>changelog</h2> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <h3>v0.2</h3> <ul> <li>add .p8 import -- thank you <a href="https://www.lexaloffle.com/bbs/?uid=16423"> @Krystman</a> for the idea!</li> <li>add drag-and-drop support</li> <li>add map import/export</li> </ul> <h3>v0.1</h3> <ul> <li>sprite import/export<br /> </div></div></div></li> </ul> https://www.lexaloffle.com/bbs/?tid=140800 https://www.lexaloffle.com/bbs/?tid=140800 Sun, 17 Mar 2024 13:58:25 UTC adrift <p> <table><tr><td> <a href="/bbs/?pid=143111#p"> <img src="/bbs/thumbs/pico64_susizker-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=143111#p"> susizker</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=143111#p"> [Click to Play]</a> </td></tr></table> </p> <p>just a port of a tweetcart of mine to the new system :)</p> <p>move this into <code>/system/screensavers/</code> and it'll show up as an option in your settings</p> <p>If you run it on its own as a cart, you'll want to run <code>reset() vid(0)</code> in the console afterwards to get things back to normal</p> <p>edit: if you want it to be permanently available, you need to put it in <code>/appdata/system/screensavers</code> (otherwise, you need to re-add it every time you start picotron). create the folder by copying the system folder: <code>cp /system/screensavers /appdata/system/screensavers</code></p> https://www.lexaloffle.com/bbs/?tid=140680 https://www.lexaloffle.com/bbs/?tid=140680 Fri, 15 Mar 2024 13:37:14 UTC ghost patrol <p> <table><tr><td> <a href="/bbs/?pid=135418#p"> <img src="/bbs/thumbs/pico8_ghostpatrol-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=135418#p"> ghostpatrol</a><br><br> by <a href="/bbs/?uid=27691"> pancelor</a> <br><br><br> <a href="/bbs/?pid=135418#p"> [Click to Play]</a> </td></tr></table> </p> <p><em>Oh no, ghosts are approaching the town!</em></p> <p>Mayor Wombledon has begged <strong>ALFREDO THE GHOUL BANISHER</strong> to team up with <strong>THE WILY WIZ</strong> to protect the town. Can they put aside their differences and work together, or will evil spirits devour the populace?</p> <h2>Controls</h2> <ul> <li>Arrow keys: move</li> <li>Z: swap heroes</li> <li>Enter/P: pause (level select, volume controls)</li> <li>X: next level</li> </ul> <h2>Outcomes</h2> <ul> <li>1 night protected: You have the villagers' sincere gratitude 🙏</li> <li>5 nights protected: The villagers are beginning to hope again 😭</li> <li>10 nights protected: Valiant Heroes 🏆</li> <li>40 nights protected: Local Deity 🤯</li> </ul> <h2>Tips</h2> <ul> <li>Alfredo can dig up graves with his fearsome claws.</li> <li>The Wiz can swap places with nearly anything.</li> <li>You can change levels freely in the pause menu.</li> <li>Never give up; always fight with your full strength.</li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/scr1.gif" alt="" /> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/90_banner.png" alt="" /> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/27691/scr2-small.png" alt="" /> <p><a href="https://pancelor.itch.io/ghost-patrol">1024 bytes</a>, made by pancelor for <a href="https://itch.io/jam/pico-1k-2023">PICO-1K 2023</a>. Thanks to timp + shrinko8 for compression help!</p> <p>The layouts were randomly generated, but they don't change. My best times for the first 5 levels are 7 / 11 / 15 / 39 / 41.</p> <p>If you enjoyed this, you might also enjoy my game <a href="https://pancelor.itch.io/hungry-eggbug">Hungry Eggbug</a>, or the board game that loosely inspired this game: <a href="https://boardgamegeek.com/boardgame/51/ricochet-robots">Ricochet Robots</a>.</p> https://www.lexaloffle.com/bbs/?tid=54459 https://www.lexaloffle.com/bbs/?tid=54459 Wed, 04 Oct 2023 21:47:32 UTC