Log In  
Follow
kittenm4ster

you found my profile! as a reward for visiting, you may look at the paragraph below...

through a patented process, I have determined with 99% accuracy that these two carts which I have pinned are the two best dog-themed games on the BBS:

Maze Dog
by eggnog
Happy Larry and the Vampire Bat
by dollarone
SHOW MORE
poke(0x5f5c, 255) -- disable btnp repeat

lol ur welcome

love,
kittenm4ster

P#137860 2023-11-24 19:53

SHOW MORE

Cart #buttworm-0 | 2023-11-21 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
13

This software product was made for https://itch.io/jam/picostevemo and is based on the 2001 science fiction horror novel Dreamcatcher by American writer Stephen King.

P#137708 2023-11-21 04:20 ( Edited 2023-11-21 04:25)

SHOW MORE

I think table serialization is a pretty well-known token-saving technique but I hadn't seen anyone post one like this that just operates via peek and poke rather than using strings, so I thought I'd share my table serializer and deserializer here (originally written for PIZZA PANDA) in case this is useful to anyone.

The serializer writes binary data directly to memory so you can use the output however you want, e.g. you can use the "storing binary data as strings" technique to store it as a string, but you could also just store it anywhere in the cart's data.

The deserializer (the part you need to include in your final cart) is 170 tokens and it similarly reads bytes directly from memory.

The serialized format is pretty efficient and uses data types which are more specific than Lua itself, in order to save storage space; each one of the following is a separate "type":

  • 8-bit integer
  • 16-bit integer
  • full "number" (32 bits)
  • boolean true
  • boolean false
  • string
  • empty table
  • array
  • table

This way if your table has a bunch of little 8-bit integers in it, you're not storing a bunch of whole 32-bit numbers for no reason.

The serialized format has the following limitations in order to keep the deserializer small:

  • max 255 properties in a table
  • max 255 characters in a string
  • no "function" type support

Serialize Table

function serialize_table(addr, t, isarray)
    poke(addr, count_props(t))
    addr += 1

    if isarray then
        for v in all(t) do
            addr = serialize_value(addr, v)
        end
    else
        for k, v in pairs(t) do
            addr = serialize_value(addr, k)
            addr = serialize_value(addr, v)
        end
    end

    return addr
end

function serialize_value(addr, v)
    if type(v) == "number" then
        if v & 0x00ff == v then
            poke(addr, 0)
            addr += 1
            poke(addr, v)
            return addr + 1
        elseif v & 0xffff == v then
            poke(addr, 1)
            addr += 1
            poke2(addr, v)
            return addr + 2
        else
            poke(addr, 2)
            addr += 1
            poke4(addr, v)
            return addr + 4
        end
    elseif type(v) == "boolean" and v == true then
        poke(addr, 3)
        return addr + 1
    elseif type(v) == "boolean" and v == false then
        poke(addr, 4)
        return addr + 1
    elseif type(v) == "string" then
        poke(addr, 5)
        addr += 1

        local len = #v
        assert(len <= 255, "string must be <= 255 chrs")
        poke(addr, len)
        addr += 1

        for i = 1, len do
            poke(addr, ord(v[i]))
            addr += 1
        end

        return addr
    elseif type(v) == "table" then
        if is_empty(v) then
            poke(addr, 6)
            return addr + 1
        elseif is_array(v) then
            poke(addr, 7)
            return serialize_table(addr + 1, v, true)
        else
            poke(addr, 8)
            return serialize_table(addr + 1, v)
        end
    end
end

function count_props(t)
    local propcount = 0

    for k, v in pairs(t) do
        propcount += 1
    end

    return propcount
end

function is_array(t)
    return #t == count_props(t)
end

function is_empty(t)
    for k, v in pairs(t) do
        return false
    end

    return true
end

Deserialize Table

-- 170 tokens
-- limitations:
-- * max 255 properties in a table
-- * max 255 characters in a string
-- * no "function" type support

function deserialize_table(addr, isarray)
    local t, propcount, k, v = {}, @addr
    addr += 1

    for i = 1, propcount do
        if isarray then
            k = i
        else
            k, addr = deserialize_value(addr)
        end
        v, addr = deserialize_value(addr)
        t[k] = v
    end

    return t, addr
end

function deserialize_value(addr)
    local vtype, v = @addr
    addr += 1

    if vtype == 0 then     -- 8-bit integer
        return @addr, addr + 1
    elseif vtype == 1 then -- 16-bit integer
        return %addr, addr + 2
    elseif vtype == 2 then -- number
        return $addr, addr + 4
    elseif vtype == 3 then -- boolean true
        return true, addr
    elseif vtype == 4 then -- boolean false
        return false, addr
    elseif vtype == 5 then -- string
        local len = @addr
        addr += 1

        v = ""
        for i = 1, len do
            v ..= chr(@addr)
            addr += 1
        end

        return v, addr
    elseif vtype == 6 then -- empty table
        return {}, addr
    elseif vtype == 7 then -- array
        return deserialize_table(addr, true)
    elseif vtype == 8 then -- table
        return deserialize_table(addr)
    end
end

btw if you want to see more context for how I used this in an actual project, here is the source code of that project: https://github.com/andmatand/pizza-panda

also here is a quick example:

-- step 1. do something like this in your build cart
t1 = {
  -- put lots of stuff in here
}

-- this returns the address right after the end of what
-- was stored so you can store multiple tables in a row
addr = serialize_table(0x2000, t1)

t2 = {
  -- another table
}
addr = serialize_table(addr, t2)

-- etc.

-- step 2. do something like this in your final published cart
t1, addr = deserialize_table(0x2000)
t2 = deserialize_table(addr)
P#137585 2023-11-17 23:25 ( Edited 2023-11-18 00:22)

SHOW MORE

Cart #pizza_panda-1 | 2023-10-11 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
109

Straight out of the Bamboo Forest!

Meet PIZZA PANDA, a fluffy little fast-driving bear, as he beeps, bounces, and barrels his way through 20+ levels in search of the lost pizza slices. He'll have to zigzag through city streets to beat the clock, delivering the freshly reconstructed pizzas straight into the mouths of hungry citizens.

But that's not all! Puzzling hats, helpless rats, and meddlesome cats also await PIZZA PANDA and his trusty red automobile. It's non-stop action all the way to the top of the winner's podium!

Controls

  • Jump: 🅾️
  • Honk/Start Level: ❎
  • Move Left/Right: ⬅️/➡️

(open the pause menu to restart a level or return to the overworld)

Source Code

I had to minify the source all to heck in the final cart and use a bunch of data serialization and compression, but I've posted the original files here for the curious: https://github.com/andmatand/pizza-panda

P#134926 2023-09-26 19:54 ( Edited 2023-12-14 04:23)

SHOW MORE

I'm pretty sure I remember this working correctly in a previous version but as of version 0.2.5g if you navigate left/right through patterns in the music editor using the -/= keys on the keyboard, if you get to an empty pattern, the keys no longer work and I haven't been able to find any way to regain control of moving through patterns except by using the mouse.

P#129249 2023-05-01 18:45

SHOW MORE

as of 0.2.2, all menuitems now have their functions called when left/right are pushed while the menuitem is selected?? It took me an embarrassingly long time to realize this... (probably due largely to a bug where the menu doesn't actually close but the items are still triggered, even if the function does not return true which helped to obscure this behavior)

@zep it seems like this change to menuitem's functionality was intended to be extra functionality and only improve the capabilities of menuitems, but the problem is that the new functionality is always activated, even it's not needed or wanted, so if the user accidentally presses left or right (which is very easy to do with a joystick or with the mobile D-pad UI, and this is how I noticed it!) the menuitem's function gets triggered, and the only way to prevent that (i.e. in order to maintain the old behavior where left/right do not trigger the function) is to use up more tokens in the menuitem's function to check which button was pressed!

it seems backwards from most of the nice feature changes we've had in recent releases which tend to result in added convenience and token efficiency :D

I know I'm responding very late to this change :D but it seems like it would have been preferable if the new functionality was only activated if an extra param was given to menuitem or something like that...because it is very frustrating for a user to accidentally trigger "restart level" for instance when they were just intending to move down the list of items using a joystick!

P#127695 2023-03-28 04:29 ( Edited 2023-04-18 21:29)

SHOW MORE

Cart #regioni-8 | 2023-02-12 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
10

solo un piccolo cart per aiutarmi a imparare

aprire il menu per attivare la modalità "quiz"

bird sprite adapted from @SirTimofFrost's picobirds :>

P#125516 2023-02-08 06:59 ( Edited 2023-02-12 20:02)

SHOW MORE

Does anyone know if there is a way to exit early when a print command is running with the \^d p8scii special command that adds delay frames in between each character?

I'm in token crunch mode and realizing this new special command could be extremely handy for easy RPG-style printing delay with zero code in certain situations, but if there was a way to exit early, it would be even more useful!

And if this is not currently possible, consider it a feature request :p

P#111113 2022-04-30 18:25

SHOW MORE

Cart #cassino-6 | 2022-03-25 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
5

I've been playing a bunch of Cassino lately in real life, so just for fun I thought I'd try making a simple PICO-8 version of it where you play against an AI.

The suits are all just built-in PICO-8 glyphs so note that the stars are spades.

If you've never played Cassino before, it's a really fun two-player card game (it can technically be played with more than 2 players but it doesn't work nearly as well) with an uncommon "fishing" mechanic and I highly recommend trying it with a real human!

TODO:

  • multiple rounds with alternating deals until someone reaches 21 points
  • SFX
  • title screen/better cart label?
  • instructions?
  • option to disable points for sweeps?
P#108913 2022-03-19 23:13 ( Edited 2022-03-25 19:43)

SHOW MORE

Feature Request:

a config.txt option to allow PICO-8 to respond to gamepad input even when the window is not in focus

I imagine this option would be disabled by default, but it would come in handy for a workflow which is very common for me, wherein both PICO-8 and an external text editor are open side by side (and sometimes also a terminal window for viewing printh output) and I want to be able to control something in the game with my gamepad, but then also be able to type in a different window using my keyboard, without having to switch windows as frequently. Obviously in this out-of-focus situation PICO-8 would have to ignore keyboard input, but still respond to gamepad input.

love,
kittenm4ster

P#97868 2021-09-27 04:36 ( Edited 2021-09-27 04:40)

SHOW MORE
  1. Run the cart at this link: https://www.lexaloffle.com/bbs/?tid=41062
  2. Pick up the hen with the web
  3. Slam into the right side of the screen repeatedly

observe that part of the lines which make up the web are visible on the left (opposite) side of the screen

This happens in v0.2.2 of the BBS player, the native version, and the HTML export from the native version. It does not happen in the previous version.

P#88214 2021-02-26 19:16 ( Edited 2021-02-26 19:32)

SHOW MORE

Cart #spiderbat-0 | 2021-01-04 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
87

This was made in two weeks for Toy Box Jam 2020, a jam in which we had to make something using only premade graphics/sfx/music assets.

P#86117 2021-01-04 00:50 ( Edited 2021-01-04 00:51)

SHOW MORE

@zep it looks like if you do a cstore from a cart in splore or an exported binary, what it saves to the .lexaloffle/pico-8/cstore folder is only those regions of cart ROM that were cstored, and then next time you launch it from splore (or next time you run the exported binary), only those regions of the ROM are copied to base RAM; everything else is blank

I'm guessing the cstore file is intended be overlayed on top of the original ROM contents when copying from cart ROM to cart RAM (and that's why it only saves the changed parts?), but it looks like there was a mistake in implementation and the cstore file contents actually are the only thing copied to base RAM?

I can't reproduce the behavior when using a p8 or p8.png file; this happens only with splore or from an exported binary

To reproduce:

  1. load a cart from the splore or launch a binary export of a cart that contains data and calls cstore specifying a partial section of cart memory, e.g. cstore(0, 0, 0x2000) (which is what my jigsaw puzzle cart does but I'm about to change it to write the entire ROM as a workaround ;p)
  2. exit splore or close the binary export
  3. relaunch the cart
  4. see that now all data regions of cart ROM are "permanently" empty, except for the spritesheet
P#85307 2020-12-11 21:03 ( Edited 2020-12-12 06:44)

SHOW MORE

~ ~ ~
The classic holiday pastime of jigsaw puzzles, now in the cozy confines of your PICO-8 fantasy console!
~ ~ ~

Like all "Pro" versions of Jigsaw Puzzle Pack sold by KITTENM4STERSOFT, this version allows any PNG image (up to 128x128 px) to be transformed into a custom puzzle via convenient drag-and-drop!

Features

  • 4 Pixel Art Puzzles (+1 custom) In 1 Cartridge!
  • Drag-and-drop Custom Puzzles!
  • Auto-Save!
  • Devkit Mouse support!
  • Traditional Background Music!
  • RandoCut technology for random cuts every time!

Controls

D-Pad: move cursor
[O]: Pick up/drop
[X]: (Hold) move faster

When Mouse Is Enabled (in pause menu)

  • click to pick up/drop or click-and-drag
  • player 2 D-pad (ESDF) moves camera
  • scroll wheel scrolls up and down (just for the heck of it)

Credits

Images

  1. "Snowman" by Aubrianne Anderson, CC4-BY-NC-SA
  2. Untitled (VW Van) copyright Marco Vale, used with permission
  3. Untitled (picnic) by zep, CC4-BY-NC-SA
  4. John Berger from "Portraits 2" by PixellerJeremy, CC4-BY-NC-SA

Fonts

Music

"The Christmas Song" written in 1945 by Robert Wells and Mel Tormé, arranged by kittenm4ster

Libraries

This puzzle pack was made possible by PX9 Image Compression and Storing Binary Data as Strings

Everything Else

Everything else is by kittenm4ster and licensed CC4-BY-NC-SA

P#85091 2020-12-11 06:34 ( Edited 2020-12-15 22:26)

SHOW MORE

I seem to have found a weird bug. Normally sfx(-1, -2) will stop sfx on all channels, but if it is triggered by a menuitem callback, it doesn't work; in that case only explicitly stopping sfx on each channel works

to reproduce, enter some notes on sfx 8 (so you can hear when playback stops), then use this code and compare the behavior of the two menu items:

function stop_all_sfx_short()
  sfx(-1, -2)
end

function stop_all_sfx_long()
  for i = 0, 3 do
    sfx(-1, i)
  end
end

function _init()
  menuitem(1, 'stop sfx (-2)', stop_all_sfx_short)
  menuitem(2, 'stop sfx (long)', stop_all_sfx_long)

  sfx(8)
end

function _update()
end

function _draw()
end
P#84681 2020-11-25 21:37 ( Edited 2020-11-25 21:38)

SHOW MORE

In the web player here on the BBS which currently says it's version 0.2.1, it appears print() now displays numbers as hex:

EDIT: oh wow I didn't realize v0.2.1 was out for real and not just in the BBS web version; I downloaded it and tried running the same cart (#xmasfish) and figured out how to reproduce it so I have corrected the title and description since my first report wrongly said the issue was with tostr()

as you can see here, it only displays as hex when x,y arguments are given to print():

P#78864 2020-07-04 00:17 ( Edited 2020-07-04 00:45)

SHOW MORE

btnp seems to completely stop working iff _update60 is used instead of the normal _update

to reproduce:

  1. run the program below
  2. press Esc
  3. type some sort of statement (for some reason the bug only occurs if you type something before typing resume) e.g. ?"hello"
  4. type resume
  5. push or hold down the button (O)

expected: "pushed" will appear onscreen
actual: "pushed" no longer appears onscreen

function _update60()
  pushed = btnp(4)
end

function _draw()
  cls()
  if pushed then
    print("pushed", 52, 60, 8)
  end
end
P#77182 2020-05-25 18:44 ( Edited 2020-05-25 19:18)

SHOW MORE

shift + q/w can be used to move up/down in the spritesheet, but when zoomed, the number of sprites it moves by seems to be greater than it should be, i.e. different from how normal q/w (left/right) works when zoomed.

(version 0.2.0i)

P#76336 2020-05-10 22:34 ( Edited 2020-05-10 23:04)

SHOW MORE

@zep seems like something in 0.2.0d (I don't know if it's present in earlier bugfixes of 0.2.0) is wonky with coroutines not updating sometimes or something?? different unpredictable problems are actually happening almost every time I run it; check this out (the dialogue is updated in a coroutine):

so far it seems like most of the time it seems to lead to crashing because variables that are declared inside coroutines are attempted to be referenced by code in the main thread but the variable hasn't been defined yet, which seems to also point to the culprit being coroutines mysteriously not updating every frame like they should (the _update60 method in this cart calls coresume on both of the coroutines every frame; there is one for the dialogue and one for controlling the presentation of the "pins" in each level--both of those are the things that seem to be breaking)

EDIT: okay I've done a bit more testing and there is definitely an issue where a coroutine just starts updating suuuper slowly (seems likely the same issue as is visible in the GIF above) and basically yields in the middle of itself where I don't have any yield statement. For reference, here is some code inside a coroutine where I added debug printh statements:

    repeat
        printh('offset.y:' .. offset.y)
        printh('vy:' .. vy)
        if offset.y >= maxtargetoffset then
            vy = -abs(vy)
        end

        printh('one')
        if targetzipy then
            if vy < 0 and offset.y < targetzipy then
                vy -= .1
            end
        elseif offset.y <= 0 then
            vy = abs(vy)
        end

        printh('two')
        offset.y += vy

        printh('three')
        for _, t in pairs(targets) do
            if not t.isknocked then
                t.y += vy
            end
        end

        printh('four')
        while playercount == 0 do yield() end
        yield()
    until state ~= state_play or all_offscreen(targets)

and here is the console output:

vy:0.09
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
one
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
two
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines
resuming coroutine 1
resuming coroutine 2
done updating coroutines

you can see it's yielding all by itself in the middle of those lines for some reason?? (i.e. the "offset.y:whatever", "vy:0.09", "one", "two", "three", "four" should all be next to each other in the console but they are interrupted by several frames)

P#75406 2020-04-26 04:39 ( Edited 2020-04-26 05:14)

SHOW MORE

after moving (via cut/paste) some SFX in the new super-cool pattern editor view, it seems sometimes it breaks the SFX editor's copy/paste in that when I highlight a few notes, copy them, and try to paste them, it keeps saying a message like "pasted 2 sfx" (instead of "notes") and has no apparent effect (at least not that I can see from within the SFX editor). I haven't found any way to fix it besides reloading the cart.

EDIT: actually I just saw it happen even without using the pattern editor...copy/paste just mysteriously stops working sometimes. it looks like entering some notes makes it start working again though?

P#75306 2020-04-24 18:09 ( Edited 2020-04-24 18:22)

View Older Posts
Follow Lexaloffle:          
Generated 2024-03-19 02:18:12 | 0.327s | Q:68