Dev Log 5 - Implementing Tarot Spreads

2025-01-04 - [53] 10:20

I'm a bit farther than this post implies, but I figured I'd give an update explaining that I have arbitrary tarot spreads working! At least for now, I have 13 different tarot spreads represented. They are:

All of these tarot spreads fit within 20 by 16 tiles and use fewer than 16 cards, which turns out to be very useful, as I'll explain a little later.

Some spread examples can be seen in the 3 images below.

=> Game Boy screenshot showing the 10 card Celtic Cross tarot spread. A cursor is over a horizontal card near the center of the screen which is laid on top of a vertical card. That card is specified on the bottom of the screen as tarot card position 2 and the card is "THE LOVERS".

=> Game Boy screenshot showing the 15 card Year tarot spread. A cursor is over a vertical card near the center of the screen. That card is specified on the bottom of the screen as tarot card position 15 and the card is "KING of CUPS".

=> Game Boy screenshot showing the 10 card Tetraktys (10 card pyramid, much like how bowling pins are set up) tarot spread. A cursor is over a vertical card near the center of the screen. That card is specified on the bottom of the screen as tarot card position 7 and the card is "8 of SWORDS".

16 Card Limit

So while it's not necessarily a hard limit, I am currently limited to displaying only 16 unique cards in a spread if I don't want to cause some lag while drawing out the cards. This is because each card uses 6 unique tiles along while about 27 different tiles can easily be reused. Adding a blank tile so the background can be blanked makes the total amount of tiles 124 out of the 128 we can easily use. The bottom third of the image below shows the card tiles for the Celtic Cross tarot spread from before.

=> Image of tiles loaded into Video RAM of the Game Boy ROM, which is split into 3 sections. The top section is exclusively sprite data, the bottom section is exclusively background/window tile data, and the middle section can be used for both sprites and background/window tiles. The bottom section shows card tiles 16 sets of cards for the current tarot spread. The top section shows card back and card flipping tiles for sprite animations, along with a vertical and horizontal cursor and some sprites for a tiny horizontal and vertical card with a magnifying glass. The middle section shows the numbers 1 through 16, some border tiles, and text character tiles.

As mentioned in the previous dev log, a card back slides into position, does a flipping animation, then disappears. As the sprites disappear, card tiles are drawn in place. This is done for all cards of the tarot spread, no matter if there is only 1 card or 16 cards (none of the spreads so far use all 16).

Card Position

The background consists of 8x8 pixel tiles, as does the window layer. While the background is technically 32x32 tiles in size (256x256 pixels), the screen can only show a rectangle of 160x144 pixels (equivalent to 20x18 tiles). It is important that these cards are aligned to the 8x8 pixel grid. The Celtic Cross spread from before is shown below with the 8x8 pixel grid over it, showing that all 10 cards are aligned to the grid.

=> Game Boy screenshot showing the 10 card Celtic Cross tarot spread from earlier. The cursor is not visible and the bottom of the screen does not have any text. This screenshot only shows the 10 cards laid out and a 20 by 18 grid of squares.

A benefit of using 16 cards per spread no matter what is that I can multiply a spread offset by 16 by shifting the bits of the offset left 4 times. In the code shown below, I use 2 bytes per card, a Y and X value, for the offset of each card of each spread. That means a spread could use 32 bytes, which means I can bit shift the spread offset left 5 times to quickly get to the Y value of the first card offset of the spread. From that location, I can take the card offset and bit shift left 1 time to get to the Y value of the specific card offset of the specific spread.

; Top-left tile position of small cards in spread
; byte 1 = tile Y value
; byte 2 = tile X value
; pad with $ff bytes
SpreadCardTileOrigins:
.1Card:
	db 6, 8
	ds 16*2-1*2, $ff
.3Card:
	db 6, 3
	db 6, 8
	db 6, 13
	ds 16*2-3*2, $ff
.5Card:
	db 6, 0
	db 6, 4
	db 6, 8
	db 6, 12
	db 6, 16
	ds 16*2-5*2, $ff
.5CardCross:
	db 6, 3
	db 6, 8
	db 6, 13
	db 1, 8
	db 11, 8
	ds 16*2-5*2, $ff
.6CardCross:
	db 6, 8
	db 6, 7
	db 11, 8
	db 6, 3
	db 1, 8
	db 6, 13
	ds 16*2-6*2, $ff
.celticCross:
	db 6, 6
	db 6, 5
	db 11, 6
	db 6, 1
	db 1, 6
	db 6, 11
	db 12, 16
	db 8, 16
	db 4, 16
	db 0, 16
	ds 16*2-10*2, $ff
.splitPath:
	db 0, 8
	db 4, 8
	db 4, 4
	db 8, 4
	db 12, 4
	db 4, 12
	db 8, 12
	db 12, 12
	ds 16*2-8*2, $ff
.zodiac:
	db 0, 8
	db 1, 11
	db 2, 14
	db 6, 16
	db 10, 14
	db 11, 11
	db 12, 8
	db 11, 5
	db 10, 2
	db 6, 0
	db 2, 2
	db 1, 5
	db 6, 8
	ds 16*2-13*2, $ff
.year:
	db 0, 8
	db 1, 11
	db 2, 14
	db 6, 16
	db 10, 14
	db 11, 11
	db 12, 8
	db 11, 5
	db 10, 2
	db 6, 0
	db 2, 2
	db 1, 5
	db 5, 8
	db 6, 4
	db 6, 12
	ds 16*2-15*2, $ff
.weekAhead:
	db 3, 2
	db 3, 6
	db 3, 10
	db 3, 14
	db 8, 2
	db 8, 8
	db 8, 14
	ds 16*2-7*2, $ff
.horseshoe:
	db 2, 2
	db 6, 2
	db 9, 5
	db 11, 8
	db 9, 11
	db 6, 14
	db 2, 14
	ds 16*2-7*2, $ff
.tetraktys:
	db 12, 14
	db 12, 10
	db 12, 6
	db 12, 2
	db 8, 12
	db 8, 8
	db 8, 4
	db 4, 10
	db 4, 6
	db 0, 8
	ds 16*2-10*2, $ff
.treeOfLife:
	db 0, 8
	db 1, 12
	db 1, 4
	db 5, 12
	db 5, 4
	db 4, 8
	db 9, 12
	db 9, 4
	db 8, 8
	db 12, 8
	ds 16*2-10*2, $ff

As tiles are 8x8 pixels in size, I can get particular start pixel values by bit shifting offsets left 3 times, which multiplies values by 8. The Game Boy uses a lot of powers of 2, which is very convenient, so I only really need to deal with simple bit shifts and simple additions.

Sprites

When drawing the face down card sprites that move to position, or the cursor that goes over the card, I need to convert the Y and X value offsets to pixel values sprites are good with. Sprites have 2 modes, an 8x8 mode and an 8x16 mode, where 2 tiles are stacked vertically as a single sprite. Because sprites are allowed to be 16 pixels tall and because sprites can be offscreen, either to the left of the screen or above the screen, the Y value of a sprite is not the top left pixel offset of the screen you want the sprite to go to. It's actually 16 pixels more than what you expect. Same thing with the X value, but 8 pixels instead of 16. That means if you want a sprite to be aligned to the top left of the screen, you need to set the Y value to 16 and the X value to 8.

Conclusion

It's nice to be able to do multiple different tarot spreads and potentially add or change the spreads in the future by just adding or changing a couple values! While not discussed in the dev log, I have the ability currently to set a card vertically, horizontally, or horizontally over a vertical card. So far none of the tarot spreads use a horizontal card that isn't on top of a vertical card, but maybe I'll find a spread soon that can use it.

I'm pretty sure this post is rambly like the last one and pretty sure that will continue to be the case. My apologies for that. I suppose these dev logs are sort of mostly to show progress is being made, as opposed to teaching someone how to make a Game Boy game. Teaching is not the intention of these dev logs. With that said, explaining is at least partially the intention of these dev logs.

The next dev log will probably be about controller input handling, which I already have implemented in the ROM. After that, I'll likely talk about Bank Switching in order to allow more than 32KiB of total storage space for the ROM.

Contact/Reply

If you would like to reply to this post, feel free to send me an email or misfin message.

=> Email: vi@vigrey.com | Misfin: vi@vigrey.com

Proxy Information
Original URL
gemini://vigrey.com/tarot/gb/log/5.gmi
Status Code
Success (20)
Meta
text/gemini; charset=UTF-8
Capsule Response Time
1324.41775 milliseconds
Gemini-to-HTML Time
3.203882 milliseconds

This content has been proxied by September (ba2dc).