The tale of masks


Hello, it is Sunday again, another Snakyval article. This time I am going to talk a little about carnival masks and the cutscene system.

Carnival masks

One of the ideas sparked by the jam’s festival ingredient was masks. You wear masks to pretend you are someone else (or something else). This device is very useful for storytelling (as it makes compelling changes in world and characters behavior) and can be mechanically useful as well.

In Snakyval’s story, masks serve to explain the snake’s fear of apples (they are snakeeaters in disguise) and to provide the prophet character that introduces more mechanics.

As a Snakyval’s mechanic, it is a collectible item (apple number two), but with some twists that build on the story - it swaps red apples for yellow apples, and makes the snake grow indefinitely to gain points. Also, the snake wears the mask for the rest of level and a firework is fired.

The first 16 masks were drawn early in the development and were released in the jam version. The masks were drawn in two orientations (normal and rotated 90 degrees), taking 32 sprites total:

All masks use two colors, but when rendering to screen, the colors are remapped to different colors to achieve an effect of unique mask. I used an array (again) to store good color pairs that can be used and pick a pair (together with mask sprite id) randomly on level start.

Palette effects are powerful and allow for saving sprites. For example, there is no yellow apple in game files - the only apple is red. Yellow apple is a result of some palette remapping.

When the mask is picked up, it follows snake’s head. For this, the two sprites versions come handy, together with flipping. Again, no if’s are necessary, as the snake’s direction (number 1 to 4) can be combined with original mask number in a smart way.

Masks in post-jam version

After jam I had decided to add few more masks. But I didn’t want to take another precious space (well, I was thinking forward, I must save a lot of space if I want lots of levels and mechanics in future versions of the game). So I had come up with a trick - only default mask orientatition is stored in the cart. The rotated version is generated on demand when needed for a picked mask. This necessitated some PICO-8 memory specification rummaging, as I had to rewrite part of spritesheet in game on demand using some smart bit manipulation.

In PICO-8 the sprites are stored in a spritesheet, line by line. The spritesheet is 128 pixels wide, sprite is 8 pixels wide. Each pixel has one of 16 colors, so it needs 4 bytes. The are two pixels in a byte, first pixel is lower nibble and second pixel is upper nibble (which may seem counter-intuitive). The operation I am performing is not actual rotation, but transposition, ie. flipping X for Y. For this, I had chosen to transpose the sprite in 2x2 pixel (2 bytes) groups, as it is the fastest way do it. The 8x8 sprite has 16 such groups. Each group is transposed by itself using bit operations (you are making 2 bytes from 2 another bytes by shifting, and’ing and or’ing) and the transposed group is put in its new place (with flipped X/Y coordinates). This algorithm is fast and simple, because you are not revisiting already written pixels and each pixel is read once.

This change allowed me to put 8 more masks… and to save 7 sprites used by masks (1 last sprite is needed for the generated rotated mask).

Cutscenes

Cutscenes in Snakyval serve to tell a story. Some may say that cutscenes are not of good use in the game, but to me it was something to make the game not just another arcade/puzzle game. As there may be better ways to present a story (which I am yet to explore), they posed enough technical challenges to me. One of the biggest challenges in many game engines I tried is scene switching and scene state handling. Without an engine it is a matter of some strategy pattern used in a game loop - the scene you are in is decided by the concrete scene instance stored as a current scene with its draw and update methods. With an engine, you often have to find out the idiomatic way of doing it.

The problem is there is little time to learn in short jam and you have to pick your battles. In my case it was PICO-8, Lua language, the game itself. When I made base game without cutscenes in, the scene state was represented by a number. A quite unwieldy if with many branches in _update() and _draw() functions handled the correct scene.

This approach is viable when there are few scenes / game states. Cutscenes are a different creature. There is essentially a list of things that have to happen, just like there is a script in a movie, which leads to lots of different scene states you have to go through. I had decided quite quickly that I need declarative approach to cutscenes. This was a deliberate choice - on one hand I knew there are coroutines in Lua, but on the other hand in PICO-8 they have different APIs and I was not keen to learn another thing (and I didn’t use coroutines in Lua before). Also I knew I could use some functional() approach, but again, I had to limit my experimentation. So, declarative approach prevailed. I made every cutscene a simple list of steps.

State of cutscene is an index of current step, and every step is evaluated in a simple function that check current step, calls proper function and if needed, moves onto another step. The used declarative approach has one significant drawback - it burns Lua language tokens really crazy (one cutscene takes about 500 to 1000 tokens), and some refactoring is needed.

Later on I had decided I could give a try at mixed functional() approach. To save some tokens in an if, I had made a map of cutscene step types and their respective functions. While it worked, it took me an hour to figure out why I had nil references there; I learnt the hard way about Lua variables initialization order and didn’t continue approaching purely functional() style because of lack of time and experience with Lua. But… I am already working on better version and the results are promising; future version of Snakyval will have well implemented cutscenes that won’t chomp precious token budget. More on it in some future devlog post.

What is next

This was all about jam’s Snakyval development. As already stated in previous posts, it was a fun project to make, and for some, a fun game to play. The game is not going to be put in a drawer, it is worked on instead. Expect a snaky sneak peek into some features currently in development next week.

Get Snakyval

Leave a comment

Log in with itch.io to leave a comment.