How we ported Aunt Flora's Mansion to the Game Boy Advance


Porting Aunt Flora's Mansion to the Game Boy Advance (GBA) created some challenges:

  1. How to draw 5x5 tiles and fit the game's 17x12 levels on the GBA screen?
  2. How to implement the game's logic?
  3. How to play music and sound effects?

Drawing the tiles and map

The GBA supports 8x8 tiles, with a 240x160 pixel display. This means that typically the GBA is displaying a tilemap of size 30x20.

Unfortunately, Aunt Flora's Mansion does not fit neatly into these dimensions. The original game had 5x5 tiles, and a 17x12 tilemap, which creates a 85x60 pixel screen.

If we could target 12x12 tiles, then we could fit a 20x13 tilemap, which is only slightly larger than the original game.

If we could target 10x10 tiles, then we could fit a 24x16 tilemap, which is also reasonable. Then, we could double the original's 5x5 pixels, in order to fill a 10x10 tile.

But how do you implement 12x12 and 10x10 tiles on the GBA?

Stretching and Shrinking

There are multiple techniques for implementing 12x12 tiles, but as far as I know, supporting 10x10 tiles only has one technique: use GBA's ability to stretch backgrounds in order to shrink it just the right amount.

What's nice about using the stretching technique is that we can support 12x12 or 10x10, simply by changing the amount of stretching.

The downside with this technique is that we severely limit the number of tiles available to us. But since the original game had so little graphics, we thought it was worth the sacrifice in order to display the game at a reasonable resolution.

So for our HD graphics, we use 12x12 tiles (split into four 6x6 quarters). Our tilesheet starts like this:



There are 256 6x6 tiles.

In order to load this into the GBA, we first stretch each tile, so that a 6x6 tile becomes an 8x8 tile:

Notice how each tile looks... ugly. This is because they've been stretched to fit 8x8.

However, the stretching is very deliberate, and carefully chosen, so that when we assemble a tilemap, we can stretch it back down to 6x6:

This stretching and shrinking is completely invisible to Casey, who drew the art for the HD graphics, but is necessary to make the game work at all.

The same basic technique is used for the 5x5 tiles, but the math is slightly different.

Game Logic

The original game was written in PuzzleScript. PuzzleScript is a unique language designed specifically for block-pushing type puzzles, with statements like:

Horizontal [Marker > Pusher|stationary Horipushable] -> [Marker > Pusher|> HoriPushable]
Vertical [Marker > Pusher|stationary VertPushable] -> [Marker > Pusher|> VertPushable]
[Marker > Pusher|no Marker > Anypushable] -> [> Pusher|Marker > Anypushable]
Horizontal [Marker > Pusher|Vertical Pusher] -> [Pusher|Vertical Pusher]
Vertical [Marker > Pusher|Horizontal Pusher] -> [Pusher|Horizontal Pusher]

Needless to say, this does not translate to C :-).

We had two choices here: 1. implement a generic PuzzleScript interpreter in the GBA, and run the original source code directly, or 2. re-implement Aunt Flora's behavior, ignoring the PuzzleScript.

I briefly considered option 1, because PuzzleScript is open source, and I thought it would be cool to possibly port other PuzzleScript games to the GBA, but ultimately chose option 2 because it seemed more simple.

The game logic is pretty simple.

The first step was to flag certain tiles as solid, that never change, like the walls.

The next step was to detect when the player was pushing a block. Search in the direction of the player, and as long as all the blocks can be pushed in that direction, keep searching. If we find an empty space, then move all the blocks, but if we find a wall, or a block that can't be pushed in that direction, then cancel the push.

Once this was completed, there were two challenges left:

  1. How to implement the pushers -- these blocks push automatically
  2. How to implement undo

Pushers

The very first room of the game demonstrates pushers (shown as mice):

The pushers behave as if they are constantly trying to push the blocks, and if the player unblocks them, they go to work, pushing, until they're blocked again.

There are a lot of ways to implement something like this. If the game was running on a modern console, then you could literally just loop over all the pushers, and try to push, every frame. There are only 24 pushers in the entire map, so this is a cake walk.

However, for the GBA, that felt really inefficient, especially since they will almost always be blocked.

Instead, I assume that all pushers are blocked, and only become unblocked if something changes.

When the player moves or pushes a block, a search is started for every cell that has changed, to see if a pusher can affect that cell. These potentially active pushers are saved to an array. After the search is finished, the potentially active pushers try and push, one after the other. If they are successful, then another search happens for the affected cells, and on and on, until things settle down.

So instead of trying every pusher every frame, pushers are only activated when they are somehow connected to the latest changes.

Undo

Undo systems can be very tricky to implement. For modern systems, one technique is to snapshot the entire state of the world. Undoing will just restore the previous state. That's easy when you have virtually unlimited memory and a tiny game.

For the GBA, we had to be a little more deliberate.

The undo buffer is a circular buffer of entries. A circular buffer is just a fixed sized array, but when you push a value to the buffer, it loops around if you go past the end. This is perfect for undo, because that means as you push more data, it will overwrite the oldest entries, effectively limiting the number of times the player can undo a move.

But what is an entry in the buffer?

For our system, it's a 32-bit number. This number represents either:

  1. The logic of a cell has changed (perhaps an empty space is replaced by a block)
  2. The player has moved

These two actions represent all possible state changes for the game. Cells change, and the player moves -- and that's it!

When a cell changes, it records the coordinate of the change, the old value, and the new value. Thankfully, this can be packed into 32-bits.

When the player moves, it records the player's new coordinates, the old direction the player was facing, and the direction they're now facing.

Lastly, each entry has a flag that determines whether a move has finished or not.

When the player pushes a block, for example, it will record the entries:

  1. Player has a new position and direction
  2. Where the block was is now empty
  3. Where the block got pushed is now filled (move finished)

When the action needs to be undone, the data can be restored, by playing it back until it finds the previous "move finished" entry.

This allows for arbitrarily long sequences of changes that can be reverted if needed.

Music and Sound Effects

Playing music and sound effects on the GBA is quite difficult.

Most people who develop for the GBA use a popular library called maxmod. Maxmod is great because the developer wrote the engine in assembly for good performance, and supports common file formats.

But what fun is that? It's way more fun to do it yourself :-).

Thankfully, we already spent a lot of time writing our own custom sound engine for our previous game, Inky and the Alien Aquarium.

The sound engine is designed to run at 32kHz, which matches the hardware exactly. It also uses uncompressed samples. This ensures that it sounds as good as possible.

One limitation is that songs must be made of simple waves, like square and triangle waves, so that everything can fit.

Songs are stored in text format, like this:

new pattern verse1:1
  00  ---:LEA  ---:CH1  ---:CH1  ---:CH1  ---:BAS
  00  ---:D00  ---:D00  ---:D01  ---:D02  ---:D00
  00  D-4:V06  ---:---  ---:---  ---:---  G-2:V08
  04  ---:---  ---:---  ---:---  ---:---  ---:---
  08  ---:---  A#3:V03  D-4:V03  A#4:V03  OFF:---
  0C  ---:---  ---:---  ---:---  ---:---  ---:---
  10  ---:---  OFF:---  OFF:---  OFF:---  D-2:---
  14  OFF:---  ---:---  ---:---  ---:---  ---:---
  18  G-4:---  ---:---  D-4:---  A#4:---  OFF:---
  1C  ---:---  ---:---  ---:---  ---:---  ---:---
  20  A#4:---  ---:---  OFF:---  OFF:---  G-2:---
  24  ---:---  ---:---  ---:---  ---:---  ---:---
  28  ---:---  A#3:---  D-4:---  A#4:---  OFF:---
  2C  ---:---  ---:---  ---:---  ---:---  ---:---
  30  ---:---  OFF:---  OFF:---  OFF:---  D-2:---
  34  OFF:---  ---:---  ---:---  ---:---  ---:---
  38  G-4:---  ---:---  D-4:---  A#4:---  OFF:---
  3C  ---:---  ---:---  ---:---  ---:---  ---:---
  40  OFF:---  ---:---  OFF:---  OFF:---  ---:END
end

These patterns are arranged together to create the final music you hear.

We needed to create a "song compiler" that takes the text format, validates it, and converts it to a binary format that the sound engine uses. This conversion happens when the game is being built.

One useful aspect of the sound engine is that we can use it in future games, and continue to improve on it. Maybe some day it will support popular formats like .MOD and .XM!

Summary

It can be surprising how much work needs to go into a small puzzle game like this.

Our goal was to not only boost this awesome puzzle game, but to also provide a completed project for others to poke at and hack.

It is available on GitHub here: https://github.com/velipso/auntflora

The license is 0BSD, which is effectively public domain, so feel free to use this as a starting point for your own games. We hope it's both fun to play, and a useful learning tool.

Thanks for reading!

Get Aunt Flora's Mansion (Game Boy Advance)

Leave a comment

Log in with itch.io to leave a comment.