Commented Source Code
Billabong » Devlog
Note: thanks to writing this devlog article, I noticed lots of opportunity to save characters and have published a new version that has better visuals and is even smaller. Now only 484 characters. I have posted annotated source code for that version here: https://dredds.itch.io/billabong/devlog/729963/annotated-source-code-for-the-latest-version
Here is the annotated source code. I have added white space and comments to make the source more readable, but otherwise the code is exactly the same as the TweetTweetJam submission.
-- single-character names for frequently used functions
a=add
k=poke
t=stat
g=sget
-- Put Pico8 into "devkit" mode, which allows mouse control.
--
-- The address is in decimal, not hex, because a hex value must
-- be prefixed with "0x", and so decimal format is one
-- character shorter.
--
k(24365,3)
---------------------------------------------------------------
-- Draw the maze into the sprite sheet before the game starts.
--
-- The game runs the flood fill algorithm in the sprite sheet,
-- not the screen buffer, so that it can draw moving sprites
-- over the maze without interfering with the flood fill.
--
-- I originally intended the two players to each have a game
-- controller, and each control their own cursor. I couldn't
-- fit that in to the 500 character limit, so used mouse/touch
-- control and a single cursor. The players have to take turns.
--
-- Make the Pico8 graphics functions draw to the sprite
-- sheet, not screen memory
k(24405)
-- When Pico8 starts up, the sprite sheet has a default
-- graphic in sprite 0. This interferes with the flood fill.
-- Clearing the entire sprite sheet is the shortest way to
-- remove it. As a result, the background of the sheet is
-- colour 0, which is transparent.
cls()
-- Iterate over the rows and columns of the sprite sheet,
-- printing random Truchet tiles (characters 254 and 255
-- in the Pico8 character set.
--
-- It takes fewer characters to iterate in pixel coordinates,
-- stepping multiple pixels at a time, than to iterate in
-- character coordinates and then multiply by cell width/height
-- to calculate pixel coordinates.
--
-- The y step is 5 and the x step is 6 to ensure that the
-- tiles join up with no gaps between them. As a result, the
-- maze is a bit squashed along the y axis. But it ends up
-- looking like an isometric 3D rendering. A happy accident!
--
for y=1, 120, 5 do
for x=1, 121, 6 do
?rnd{"◜","◝"}, x, y, 2 -- `?` is a shortcut for `print`
end
end
-- Draw a border around the maze, so that the we only need to
-- check pixel values, and do not need to check x and y
-- coordinates in the flood fill.
rect(0,0,127,120,6)
-- Switch Pico8's graphics functions back to target the screen,
-- not the sprite sheet.
k(24405,96)
-- End of creating the maze
---------------------------------------------------------------
-- Set up the game state, again using single-character variable
-- names to save on characters.
-- "f" = "floodfill". A list of pixels to be checked next frame
-- by the flood fill algorithm. Each element is a list of:
-- {<player-number>, <pixel-x>, <pixel-y>}.
f={}
-- "s" = "scores". The scores for players 1 and 2
s={0,0}
-- "p" = "player". The current player, either 1 or 2
p=1
---------------------------------------------------------------
-- The main game loop.
-- Start the game loop. The end of the game loop `goto`s back
-- here.
::_::
-- Read the mouse coordinates
px=t(32)
py=t(33)
-- If the mouse is clicked on a background pixel within
-- the play area, add the point to the flood fill for the
-- current player and switch to the next player's turn.
--
-- p=3-p calculates the next player:
-- when p is 1, 3-p is 2; when p is 2, 3-p is 1.
-- This is a lot shorter than using a conditional.
--
if (btnp(5) and py < 120 and g(px,py) == 0) a(f,{p,px,py}) p=3-p
-- The flood fill algorithm.
-- "n" == "next": the pixels to be checked *next* frame,
-- calculated by running the flood fill for *this* frame
n={}
while #f > 0 do
-- treat `f` as a stack: pop the top element, then
-- unpack the elements into local variables. The
-- elements of the `f` list are themselves lists so
-- they can be unpacked like this.
q, x, y = unpack(deli(f))
-- Clamp the x and y to the play area. We do this
-- here instead of before adding pixels to the list to
-- save space. We only need to check once, not four times.
--
-- We also save space by passing two arguments to the `mid`
-- function. Missing arguments default to zero.
--
-- (In retrospect, I think the clamping is unnecessary.)
--
x = mid(x,127)
y = mid(y,120)
-- The clamped coordinates might be on the border. That's
-- not a problem, because we only do the next step of the
-- flood fill if the pixel at x,y is the background colour
--
if g(x,y) == 0 then
-- Set the pixel to the colour of the player who launched this flood
sset(x,y,4+4*q)
-- Increase that player's score
s[q]+=1
-- Add pixels above/below/left/right of this pixel to be
-- checked next frame
a(n,{q,x,y+1})
a(n,{q,x,y-1})
a(n,{q,x+1,y})
a(n,{q,x-1,y})
end
end
-- Switch the `f` variable to the pixels we calculated during
-- this frame, so that they are checked in the next frame.
f=n
-- Draw the current frame
-- Clear the screen to a light purplish-grey background colour.
-- I intended to run a groovy background effect behind the
-- transparent maze, but didn't have enough space.
cls(13)
-- Draw the maze by blitting the entire sprite sheet onto
-- the screen.
spr(0,0,0,16,16)
-- Print the scores for player 1 and player 2 in their colour
?s[1],1,122,8
?s[2],80,122,12
-- Draw the mouse pointer. Pico8 renders a circle of radius 1
-- as a small cross with a one-pixel space in the middle, which
-- works as the mouse pointer for this game, and takes fewer
-- characters than printing an appropriate glyph from the
-- Pico8 character set.
circ(px,py,1,7)
-- Indicate who's turn it is by printing a glyph that points
-- at the score of the current player. Character 22 is a
-- left-pointing triangle, and character 23 a right-pointing
-- triangle.
?chr(21+p),62,122
flip() -- show the current frame
-- jump to the start of the game loop to run the next frame
goto _
-- End of the game loop
---------------------------------------------------------------
-- Note: no room for checking for the end game condition or
-- deciding who is the winner. The players have to do
-- that themselves when they have filled every space in the
-- maze, and then restart the game by restarting the app
-- from the pause menu.
Get Billabong
Billabong
A tiny shared-screen multiplayer real-time strategy game for two players, written for #TweetTweetJam 9
Status | Released |
Author | dredds |
Genre | Strategy, Action |
Tags | Multiplayer, PICO-8, Tactical, tweettweetjam |
More posts
- Announcement: The Color of Honey56 days ago
- Annotated source code for the latest versionMay 11, 2024
- Update: improved visualsMay 11, 2024
- New version fixes annoying bugMay 10, 2024
Leave a comment
Log in with itch.io to leave a comment.