Log In  

Electronic musician and game developer.

:: Unfold ::

Cart #37710 | 2017-02-22 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

So I recently rediscovered the magic that is Puyo Puyo! However, I'm absolutely terrible at it. I'm a pretty slow thinker and can never analyse the situation quickly enough to predict the fall of blocks and construct large chains.

So, I wrote this little training tool that allows you to play Puyo in single-player as slowly as you like! You can spend an infinite amount of time deliberating each move, and even go upwards.

Threw in some sfx and autotiling for fun :)

[update 2017-02-22]

  • configurable rotation controls, mainly for those using the web player on mobile devices

[update 2016-08-13]

  • the 13th row (outside the top of the board) is now treated correctly
  • chain text can't go off-screen (animation fix)

[update 2016-08-12]

  • revamped menu / settings
  • fixed nuisance puyos not popping (oops)
  • added 'blind piece' mode which greys out the current piece, forcing into the habit of looking ahead

[update 2016-07-29]

  • the first two pairs can only consist of red, green and blue puyos, regardless of colour count
  • puyos placed outside the top of the board will be removed (hopefully correctly)
  • improved controls some more
  • adjusted purple puyo graphics (thanks to MissingNumbers for input!)
  • optimised the way puyos are stored (no more lag when the board gets full)

[update 2016-07-23]

  • can now choose the number of colours to play with
  • can optionally play with random nuisance puyos added to the field, to simulate an opponent (for all you people that complained it was too easy)
  • improved controls, fixed some bugs, now possible to fail
P#27223 2016-08-22 21:54 ( Edited 2017-05-05 20:53)

:: Unfold ::

Hey folks! This is a demo cart to showcase use of coroutines to create animations and dialogue.
By having a single active 'script' coroutine we can do the following:

  • reveal text frame by frame
  • do nothing until player presses [x]
  • give the player a choice of answers [up/down + x] to select
  • do nothing until npc has finished walking
  • do several of the above simultaneously, and only continue once all of them are finished.

all this without blocking the _update() function

For more info on coroutines, check out the forum post. Also keep an eye out for Fanzine #5, where Dan Sanderson will be exploring these topics!

P#25342 2016-07-15 13:00 ( Edited 2016-07-17 23:38)

:: Unfold ::

Hi! Some folks on the IRC were struggling with metatables, and Ivoah suggested I post an explanation here to help more people get to grips with them. Here goes nothing:

A table is a mapping of keys to values. They're explained quite well in the PICO-8 manual so I won't go into more detail. In particular you should know that t.foo is just a nicer way of writing t["foo"] and also that t:foo() is a nicer way of calling the function t.foo(t)

A metatable is a table with some specially named properties defined inside. You apply a metatable to any other table to change the way that table behaves. This can be used to:

  1. define custom operations for your table (+, -, etc.)
  2. define what should happen when somebody tries to look up a key that doesn't exist
  3. specify how your table should be converted to a string (e.g. for printing)
  4. change the way the garbage collector treats your table (e.g. tables with weak keys)

Point #2 is especially powerful because it allows you to set default values for missing properties, or specify a prototype object which contains methods shared by many tables.

You can attach a metatable to any other table using the setmetatable function.

All possible metatable events are explained on the lua-users wiki:
>>> list of metatable events <<<

And that's really all you need to know!

Edit: it appears that __tostring doesn't currently work in PICO-8 Lua. That means some of the code below is only relevant to vanilla Lua. See the end of the post for workarounds.

Vectors Example

I'll now demonstrate how metatables could be used to make 2D points/vectors with custom operators.

-- define a new metatable to be shared by all vectors
local mt = {}

-- function to create a new vector
function makevec2d(x, y)
    local t = {
        x = x,
        y = y
    setmetatable(t, mt)
    return t

-- define some vector operations such as addition, subtraction:
function mt.__add(a, b)
    return makevec2d(
        a.x + b.x,
        a.y + b.y

function mt.__sub(a, b)
    return makevec2d(
        a.x - b.x,
        a.y - b.y

-- more fancy example, implement two different kinds of multiplication:
-- number*vector -> scalar product
-- vector*vector -> cross product
-- don't worry if you're not a maths person, this isn't important :)

function mt.__mul(a, b)
    if type(a) == "number" then
        return makevec2d(b.x * a, b.y * a)
    elseif type(b) == "number" then
        return makevec2d(a.x * b, a.y * b)
    return a.x * b.x + a.y * b.y

-- check if two vectors with different addresses are equal to each other
function mt.__eq(a, b)
    return a.x == b.x and a.y == b.y

-- custom format when converting to a string:
function mt.__tostring(a)
    return "(" .. a.x .. ", " .. a.y .. ")"

Now we can use our newly defined 'vector' type like this:

local a = makevec2d(3, 4)
local b = 2 * a

print(a)      -- calls __tostring internally, so this prints "(3, 4)"
print(b)      -- (6, 8)
print(a + b)  -- (9, 12)

Pretty neat right?

Object Orientation

I mentioned that metatables can be used to define what should happen when a key lookup fails, and that this can be used to create custom methods shared by many tables. For example we might want to be able to do this:

a = makevec2d(3, 4)
a:magnitude()  -- calculate the length of the vector, returning 5

In Lua this is not always necessary, for example, we could define an ordinary function to do the job for us:

function magnitude(vec)
    return sqrt(vec.x^2 + vec.y^2)
magnitude(a)  -- returns 5

In fact, for PICO-8 I would recommend that approach, because it's as efficient as you can get, and it uses the least number of tokens.

But I think it's educational to see how metatables can make it possible to use Lua in a more OOP style.

First off, we define all our methods in a table somewhere. Note, you can define them in the metatable itself, but I'll put them in a different table to prevent confusion.

local methods = {}
function methods.magnitude(self)
    return sqrt(self.x^2 + self.y^2)

The __index property of a metatable is referred to when you try to look up a key 'k' which is not present in the original table 't'.

If index is a function, it is called like index(t, k)
If index is a table, a lookup is performed like index[k]

So we can add the magnitude function to all our existing vector objects like this:

mt.__index = methods

And now we can do a:magnitude()
Which is a shortcut for a.magnitude(a)
Which is a shortcut for a["magnitude"](a)

Hopefully given all this information, it's clear what's happening:

We never defined a magnitude property in 'a', so when we try to lookup the string "magnitude", the lookup fails and Lua refers to the metatable's __index property instead.

Since __index is a table, it looks in there for any property called "magnitude" and finds the magnitude function we defined. This function is then called with the parameter 'a' which we implicitly passed when we used the : operator.

Well, that's it from me! I hope somebody finds this post useful, and please let me know if there is something you don't understand, or something that I left out or could have explained better.

Edit: at the time of writing, __tostring doesn't work in PICO-8 Lua.

This means you'll either have to convert your objects explicitly, like so:

function vec2str(vec)
    return "(" .. vec.x .. ", " .. vec.y .. ")"


or, if you are using the object oriented approach outlined above, you could do this:

function methods.tostring(vec)
    return "(" .. vec.x .. ", " .. vec.y .. ")"

local oldprint = print

-- override the print function to look for a method called 'tostring'
function print(val, ...)
    if type(val) == "table" and val.tostring then
        oldprint(val:tostring(), ...)
        oldprint(val, ...)


edit: minor bbs formatting fix

P#20507 2016-05-12 19:12 ( Edited 2017-04-18 20:55)

:: Unfold ::

Cart #16538 | 2015-11-11 | Code ▽ | Embed ▽ | No License

Updated version for UoB Game Dev Society halloween jam :)

  • now has a fancy title screen and animations
  • now has diagonal chains (like in normal reversi/othello)
  • individual cats can be flipped (including white -> black)

Cart #15501 | 2015-10-17 | Code ▽ | Embed ▽ | No License

Puzzle game concept for the 1 Hour Game Jam.

I didn't have time to design difficult puzzles, but the concept is there. :)

Follows the rules of Reversi (but only for horizontal+vertical chains)

To move: make a bridge from a white kitten to your cursor, over 1 or more non-white kittens.

Sleepy kittens will become normal (white) when flipped
Fire kittens will destroy their neighbours when flipped
Queasy kittens will become sleepy when flipped

To win the level, make all kittens normal (white)!

ARROWS: move
Z/C: flip
X: restart level

P#15502 2015-10-17 17:18 ( Edited 2015-11-12 04:28)

:: Unfold ::

Hi zep! Just a small suggestion, but I'm finding the low-resolution cursor movement really quite awkward to use when I'm so comfortable with existing graphics software on the PC.

Would it be possible (as an optional setting) to either:

a) detach the in-app cursor from the 128x128 grid by creating some special rendering code independent from the console display?

b) hide the in-app cursor and show the system cursor instead?

What are others' opinions on this? I suppose it's not a major issue for most people, and I completely understand if you don't want to do it, to preserve the style/feel of the console. :)

P#13192 2015-08-27 08:45 ( Edited 2015-09-10 23:51)

Follow Lexaloffle:          
Generated 2023-09-25 03:59:05 | 0.075s | Q:18