Picozine 4 0 3 Print PDF
Picozine 4 0 3 Print PDF
Picozine 4 0 3 Print PDF
3 DON'T WAIT
6 AI MOVE SPECIAL ROGUELIKE
8 A* pathfinding in PICO-8
19 Traps For Absolutely Every Imaginable Occasion
23 The Roguelike *shiny* game-feel
29 Dungeon walls
36 Sharing music between carts
40 DONUT MAZE
ROGUE-run-learn
share-love-play
code-create-draw
make-design-BE
think-write-break
participate-retry
PICO-8 is a fanzine made by and for PICO-8 users.
The title is used with permission from Lexaloffle Games LLP.
For more information: www.pico-8.com
Contact: @arnaud_debock
Cover illustration by @pietepiet
Special thanks to @dan_sanderson and @lexaloffle
2
DON'T WAIT
I made this to illustrate some of my reasoning behind a particular
design decision in the roguelike games I've made. It's probably
better to play it before reading this, it only takes a couple of
minutes.
I hope it speaks for itself fairly well but I'll put it in con-
text. In Rogue, and many other games following in its lineage,
you could press a key (often ".") to skip a turn. In my games
Zaga-33 and 868-HACK (and the unreleased Imbroglio) you can't (at
least, not freely - there are ways). This goes against the genre
expectations and so I sometimes get complaints - "why can't I wait
in place?", "i can't find the wait button", "how do you wait?",
"this game is bad because sometimes the enemy hits you", etc.
3
The three levels of this little game demonstrate three different
approaches to "wait".
Level 1: you can't wait. All you can do is take a step or hit
an enemy. Both player and enemies die in just one hit - this
wouldn't be ideal for a more complex roguelike because a lot of
the play in these games is making decisions about which resources
to sacrifice ("do I take some damage or do I use up an item?") but
it helps make everything very clear for this example. It means
if an enemy is an even number of steps away it is guaranteed to
get the first hit and kill you, if an odd number you can safely
kill it. The twist is that you don't move when you hit an ene-
my, allowing you to convert those evens to odds if you're careful
about the order you meet them in. (See Aaron Steed's Ending for
a fully fleshed out game based around this idea.)
Level 2: you can wait. You can just press a key and all the ene-
mies move closer, turn scary evens to nice safe odds. Depending
on the order you hit them in you might need to wait a lot or very
little, but it doesn't matter because you can complete it either
way. The tactics in this level end up being a lot simpler; maybe
it makes more sense that you can wait but something interesting
has been lost. I'd also say there's a loss in simplicity - an ex-
tra control is an extra thing to tell you about, extra text on the
screen. But a lot of games have this rule and it works well for
them. (In Rogue and many classic roguelikes waiting to get the
first hit isn't free the way it is here because there are various
timers that tick up each turn even as you wait, governing hunger,
enemy spawns, corruption, etc. - you'll starve to death in 100
turns so maybe you don't want to spend one of them standing still.
These costs tend to be quite minor compared to being damaged, but
still they do sometimes create situations where it's better to
take an extra hit than an extra turn. But when you're making a
smaller game that doesn't have so many ornate systems counterbal-
ancing each other these costs might need to be made explicit.)
Level 3: the enemies can wait too. I've been told that it's unre-
alistic to not have a wait button, but if so then it's unrealistic
for enemies not to wait as well - very unrealistic for the player
4
to always get the first hit. One of the elements cited in the
Berlin Interpretation as a common characteristic of roguelikes is
"Rules that apply to the player apply to monsters as well". And
of course they do exactly what you've been doing in the previous
level - waiting whenever you're two steps away, refusing to get
hit first. Maximum realism! This fundamentally doesn't work
with the "one hit point" rule, leaving the level impossible, but
you can see how unpleasant it would be to have this tactic turned
against you in a less strict game too.
Michael Brough
@smestorp
5
AI MOVE SPECIAL ROGUELIKE
Here's a tutorial on how design a basic AI for monsters moving on
a grid.
http://www.lexaloffle.com/bbs/?pid=18367&tid=2986&autoplay=1#pp
6
You can see the values by pressing (z) during the test.
run_mon(e) will move the monster by checking the 4x nearby
squares.If it finds a square with a lower distance it will move
in this direction.Once they reach a square with a dist of 0 ar-
cher will aim at hero by defining a shoot_dir value.
Benjamin Soule
@benjamin_soule_
7
A* pathfinding in PICO-8
A* pathfinding is an efficient way to find the shortest path from
one position to another if possible. It is widely used in video
games but also in the transportation and computer networking in-
dustries.
1.The Map
function _init()
end
function _update()
end
function _draw()
cls()
mapdraw(0,0,0,0,16,16)
end
8
They include:
- Blank - 000
- Wall - 001
- Goal - 016
- Start - 017
- Path - 018
The Walls block the path from the Start to the Goal, we will draw
a path with the Path sprite.
Now we can create a map with a Start and Goal position somewhere
and walls that will block the path.
9
2. Breadth-first Pathfinding
Next we want to search through the locations starting from the
Start using breadth first search, this will find the neighbours
of each location searched and then search the neighbours ignor-
ing the walls. This code doesn’t look for anything just yet, it
just iterates through each map position in a kind of expanding
circle outward starting at Start.
function _init()
start = getSpecialTile(17)
goal = getSpecialTile(16)
frontier = {}
insert(frontier, start)
came_from = {}
came_from[vectoindex(start)] = "none"
10
while #frontier > 0 do
current = popend(frontier)
11
function getSpecialTile(tileid)
for x=0,15 do
for y=0,15 do
local tile = mget(x,y)
if tile == tileid then
return {x,y}
end
end
end
printh("did not find tile: "..tileid)
end
function reverse(t)
for i=1,(#t/2) do
local temp = t[i]
local oppindex = #t-(i-1)
t[i] = t[oppindex]
t[oppindex] = temp
end
end
if (x+y) % 2 == 0 then
reverse(neighbours)
end
Now we can add some code to stop searching if we find the Goal,
add this just below current = popEnd(frontier) in the while
loop.
This will break out of the while loop if the current map posi-
tion is the goal, no need to search the rest of the map.
We can now draw a line using the Path tiles along the points in
came_from . We will reverse them so they are drawn from Start to
Goal.
13
local sindex = vectoindex(start)
while cindex != sindex do
add(path, current)
current = came_from[cindex]
cindex = vectoindex(current)
end
reverse(path)
for point in all(path) do
mset(point[1],point[2],18)
end
Now we should see something like this when we run the game.
This is great! the path goes straight from the Start to the Goal
but if we add a new sprite at position 19 and add this to the
while loop.
14
local nextIndex = vectoindex(next)
if (nextIndex != vectoindex(start)) and
(nextIndex != vectoindex(goal)) then
mset(next[1],next[2],19)
end
3. A* Pathfinding
return top[1]
Then we need to add a heuristic, this is how far away from the
goal each point is. A* uses this heuristic to calculate the total
cost of traveling to each node as it is searching and will check
the lowest cost paths first.
function heuristic(a, b)
return abs(a[1] - b[1]) + abs(a[2] - b[2])
end
Finally we will update the while loop to use a running cost and
prioritising the shortest paths. It also updates paths already
checked if a faster way through that tile has been found.
frontier = {}
insert(frontier, start, 0)
came_from = {}
came_from[vectoindex(start)] = nil
cost_so_far = {}
cost_so_far[vectoindex(start)] = 0
came_from[nextIndex] = current
17
Congratulations! You have just implemented A* pathfinding! Get
creative and apply it to something amazing and show everybody!
http://www.lexaloffle.com/bbs/?tid=3131
Further reading
http://www.redblobgames.com/pathfinding/a-star/introduction.html
https://en.wikipedia.org/wiki/A*_search_algorithm
https://en.wikipedia.org/wiki/Priority_queue
http://www.raywenderlich.com/4946/introduction-to-a-pathfinding
Richard Adem
@richy486
18
Traps For Absolutely Every
Imaginable Occasion
So if you are making a roguelike then you are going to put traps
in it, because you must. You must because the only reason anyone
ever makes or attempts to play roguelikes is because they want to
bash against infinite unknowable brick wall universes over and
over again until they find the one brick that is loose and then
they can say, look, I did it, I won. This is the thing to say when
you finally win a roguelike even though it really means "I won
(that time)" and "I gave myself over to be entombed in a random
superstructure but I was able to accurately observe the rules of
its generation" and "I am drawing a map of where the loose brick
is, slowly, blindly: this is what I am doing with my life." You
are going to put traps in your roguelike because you don't just
want a tall beautiful noble and unyielding maze: you want a mean
maze that pushes back. Nobody can contend with mystery and the
unknown without the horror of Unintended Consequences and so you
will include traps.
The absolute best sort of trap you can make for your game will be
instantly recognizable to your player and they will want to step
on it. I recommend a small switch on the ground that counts down
how many steps you need to take in order to step on the trap and
then a large sign that celebrates the player for stepping on the
trap. It might be good to give the player some gold for stepping
on the trap as well. You want the trap to be as exciting as pos-
sible.
19
1. The sink, from NetHack. The ideal trap, taking a friendly form,
having a handful of positive uses (identifying rings, free drinks)
and many horrifying ones (black ooze!!!!), no player can resist
the delightful sink.
20
prickly complexity or they might think that you have a sense of
humor and that you are attempting to communicate with them. Traps
make a world snap and snarl with brutish life so make as many of
them as possible.
1. Treasure Trap
2. Boulder Trap
3. Arithmetic Trap
The specific form I chose for Treasure Trap was Every Single Wall
in VNOOFIS. Touching a wall allows a player to See (or Feel)
What's Going On which is a Lovely Treasure that cannot be abided
without beautiful anguish so the wall produces a hostile Slime.
The Slime will hurry along to the place where the player is but
the player can retreat with Powerful Knowledge. This gives a very
nice feeling of Impending Doom That Can Perhaps Be Avoided, so
everyone feels Clever.
The Boulder Trap in VNOOFIS happens when you are confronted by Too
Many Slimes, Maybe so you step into a wall, smashing it with your
body into a carpet of nerves and flesh. You gain an Avenue of Es-
cape but you must keep going because the wall is growing back in
as an Angry Blister which will Explode if you attempt to return.
You have traded Flexibility for Future Complexity and again you
feel Clever.
The Arithmetic Trap is what I think is the most fun part of dying
in Shiren the Wanderer, saying to everyone who can hear you, "oh
no the enemies have killed each other in a fashion that was not
21
advantageous to me, creating an enemy that has statistics that are
far beyond mine." In VNOOFIS there is the Segmented Worm, wander-
ing around, difficult to avoid in their own right, but combined
with a Hostile Slime! Well! You can only imagine the thrill of
encountering this very special opportunity for defeat.
Kyle Reimergartin
@mooonmagic
22
The Roguelike*shiny*game-feel
What is the greatest feeling in roguelikes? Winning the game, in
most cases. But what is another greatest feeling in roguelikes?
Finding something *shiny*. The most obvious example would be a
piece of equipment, but new enemies, bosses, or any world design
element has a very large *shiny* potential. Game mechanics like
level-ups or a new ability can also feel *shiny*. Winning the game
is *shiny*. *Shininess* is an amazing game-feel that we all feel
and love and the roguelike genre is the genre that has the best
of it.
Roguelikes are RPGs. They can easily use the classic RPG's equip-
ment and environment renewing, and they should. But roguelikes are
also the closest you can get to the arcade genre in RPGs. And the
arcade genre (or the "try again" genre if you prefer) is the best
genre for keeping you very close to the screen, fingers mindlessly
pushing buttons, and indeed having you the closest to the game.
Therefore arcade is the best genre for game-feels in general. And
therefore roguelikes are the best at *shiny* feels.
23
MacGuffins can be of different importance but they all share that
the player will most likely go for them and when he gets one of
them, it will make him happy. MacGuffins are *shiny*.
24
Gaslamp Games, there is a point in the game where you get a lute-
fisk box, an item that can convert any other item in a quantity of
lutefisk than you can then give to the lutefisk god who would give
you a piece of equipment if you gave him enough lutefisk. Then,
any object has a minimal interest. Another solution is funny/in-
teresting tooltips. These can be information about the game's lore
or bad puns or pop culture references, or anything you think the
player will have some interest in. If you really want an item to
be a waste of the player's inventory, make it so but then better
put a very good pun on its tooltip or not make it shiny at all.
25
Quests, value, lore, puns, achievements... Is there a way to make
something shiny without affecting the game's mechanics or pouring
lots of literary work in it?
Do keep in mind that these palette tricks work only once the play-
er is accustomed to see the item with its original sprite. But
such a simple trick can make any item *shiny*!! Also note that
this can be used more extensively to make A LOT of items with a
few sprites, take the MMO-roguelite Realm of the Mad God by Wild
Shadow Studio for example, where most tiers of a same piece of
equipment is the same sprite with different palettes. There, the
tier palette is also an indicator that an item is better than what
you currently have and, in direct consequence, *shinier*.
26
It also works with enemies, environments and probably other things
so use your imagination!!
And palette swapping is not the only cheap graphical trick to make
something *shiny*! In fact any graphical or audio effect you can
add to an element of your game can make it more *shiny*. One very
efficient graphical effect is ridiculous lighting.
27
scary but exciting way. I have a preference for the shaking as it
is generally very easy to implement. Just store two global values
shakeX and shakeY, add them to the coordinates of all the things
when you draw them, and multiply them both by -0.8 each frame.
Make a function add_shake() that gives random values to shakeX and
shakeY and call that function ANYTIME ANYTHING HAPPENS.
4.To sum it up
Roguelikes are the best at *shiny* feels and to achieve making
something *shiny* in one, you should make it a MacGuffin, based on
anticipation and in-game value, and you should add lots of graph-
ical effects to it, such as palette swapping, ridiculous lighting
and simple animations. Along the way have also been mentioned that
like game elements, game mechanics can also be *shiny* and that
you shouldn't be afraid to make things too extravagant.
If you read all of the above, you should now be very capable of
making anything *shiny* in your own roguelike or any other kind
of game really. The *shiny* feel is the most adapted to roguelikes
but you can certainly use it in most, if not all, games. So make
me proud and go do just that!!
Dog Trasevol
@TRASEVOL_DOG
28
Dungeon walls
Hi. My name is Rodrigo Franco, and I'm not a game developer. I
have been building web apps for over a decade — and playing video
games for twice that — but I never got my hands dirty and tried
to code a game. Then I found Pico-8 (http://www.lexaloffle.com/
pico-8.php) — its minimal approach was simple enough to make me
face my own ineptitude. As a blank canvas, it was terrifying but
also inspiring. I was sold.
Since I was a kid, my favorite gaming genre has been RPGs. From
tabletop to early computer incursions, like Atari's adventure and
the Ultima series, being able to live a classic "dungeon crawl"
experience fascinated me. I think that's why I knew that when I
finally started working on a game, it would be something like a
roguelike.
29
Everything was fine, until Christina sent me the final art:
30
These are not your grandma's 8x8 sprites. How could I handle that?
Besides everything being 16x16, I wanted just part of the wall
tiles to be solid. This would allow me to accomplish an isometric
feel:
The way I was doing this (using the sprite flags) didn't work well
with that. I did some research, but never found a proper solu-
tion, so I decided to come up with something myself. Here's how
I solved it.
First, I created some tables to hold the props I was about to cre-
ate, all the objects that would be added to the world and what I
called 'solid regions':
solids = {}
props = {}
world = {}
31
Then I came up with an ideal way to add items to the world. I
wanted it to be something like this:
add(world, props.muddy_wall(0,15))
props ={
muddy_wall = function (_x,_y)
local prop = {sprite=64, width=16, height=24, solidwidth=16,
solidheight=16, x=_x, y=_y}
add(solids, {prop.x, prop.y, prop.solidwidth, prop.solid-
height} )
return prop
end
}
32
2.Drawing the elements
foreach(world, props._draw)
A simple spr method call with the prop params. That was easy!
Now the real deal: How can we make props solid? As I said earli-
er, for each prop added to the world table, we also added an item
to solids with x and y coordinates and width and height of a solid
area. It's time to put this to good use.
I'm not going to enter in details about how my actors traverse the
dungeon. Just imagine I have a function called mov that expects
an actor as param. This function calculates the actor's attempted
position, and if the position is valid, moves the actor there.
Something like this:
function move(actor)
if valid_move(actor.attempted_x, actor.attempted_y)
do_move(actor)
end
end
33
function move(actor)
if solid_area(actor.attempted_x, actor.attempted_y)
do_not_move(actor)
else
do_move(actor)
end
end
function solid_area(x,y, a)
local x = x + a.h
local y = y + a.w
Let's break this down. Here's what I'm doing, bullet by bullet:
34
3. Wrapping up
Rodrigo Franco
@caffo
35
Sharing music between carts
I’ve written an album, “9 Songs for PICO-8” with the intention
that other people can use the songs in their PICO-8 games. They’re
in a bunch of different styles, but nothing far from what you’ve
heard before. That being said, it’s not as easy as just taking an
audio file and dropping it into your own project. Let’s have a
look at how to take a song from one cart and put it in another!
36
music in it, you’ll see what looks like a bunch of gibberish. Each
of these lines is one of sixty-four “sound effects,” (numbered
from 0 to 63). The numbers and letters are the code which tells
the audio system what notes to play, in what order, using what
articulation, and how fast.
Sharing music between carts. The easiest way to share music be-
tween carts is to open them both up in the external editor, and
copy and paste the entire sfx and music sections onto the second
cart. Then, when you open it in PICO-8, you’ll see all the music
and sounds from one cart on the other. This is great for collab-
orative work, but be careful because if two people are working on
sounds separately, you certainly risk overwriting someone’s work.
That brings us to the more precise and careful way of doing this:
copying specific lines. This is easy with the sound effects them-
selves, you just count from the first line after sfx (starting at
0, see diagram below). That number will correspond to the name of
the sound effect when it was in PICO-8. You can pick and choose
which sounds to copy over. Be careful to paste them on the same
line they came from! Repeat this process for the relevant music
lines as well, and you’re all set.
37
If you want to take it a step further, you can paste the sound ef-
fects to whatever line you want. This is good if you want to con-
solidate, or if you want to move a song from a high tracker number
to a lower one. Remember how the music steps just point to sound
effects? Well, if you copy over the relevant sound effects to
whatever line you want (and you’re like me and can’t easily write
out hexadecimal), it’s not hard to just open the file in PICO-8
and point the tracker to the relevant sounds after the fact.
@RobbyDuguay.
Robby
38
DONUT MAZE
function addcel(x,y,dx,dy)
--zep
@lexaloffle
39