Published 2023-06-12

(warning: contains some flashing lights)
(Fake08 support added)

Survive 50 micro bullet-hell levels set over more than a dozen environments to face the final boss.

Player 1: Arrow keys to move, hold Z,X,C,V,N, or M to slow your ship down for maximum control
Player 2: ESDF to move and Q,TAB,SHIFT, or A to slow.

Pressing Q/TAB/SHIFT/A at any time will enable player 2 to join at the start of the next level.

Standard: Survive each of the 50 ten-second levels. If anything hits your cockpit you'll need to restart.
Invincible: Experience the game without having to restart. See how few times you can get hit.

Pressing Right on the main menu will take you to a level select screen. Every level is unlocked from the start to make it easier to resume a game, or skip past any difficult levels.

Pressing Down on the level select screen will change game modes.

Pressing Left on any menu screen will turn off background music.

If you complete the game playing all the levels in order then your total number of restarts will be recorded on the cartridge as a high score (single/two player and each game mode count as separate high scores). High scores can be reset in the cart menu.

Huge thanks to Krystman from Lazy Devs Academy for his excellent tutorial series that were instrumental in getting me through this game.

Also big thanks to: SlainteES, Heracleum, SmellyFishstiks, FReDs72, Thelxinoe5, and everyone on the Pico-8 Help Discord.

And thanks to my son Matthew for his invaluable playtesting and encouragement.

None of the enemy behaviour is randomly generated. Every shot either happens exactly the same each time, or is aimed at the player. That way levels can be 'learned' and mastered.

Having two player support was important, actually I was tempted to make it 8 players, but would have found testing difficult. In invincible mode the game doesn't track which player is hit more often because I wanted to emphasis cooperation over competitiveness.

As a 3D artist trying to come up with pixel art was really difficult. It was fun creating 8x8 sprites, but after a while I experimented with using 3D models to generate the sprites. The title screen, asteroids, and craters were all 3D models that I rendered out at the tiny resolution, then used photoshop to set it to the pico8's indexed colours, then saved as png files and dragged into pico8's sprite sheet. I found converting them in photoshop before dragging them in looked a tiny bit better.
I found this method to be a nice simple approach for me, but the results aren't anywhere near what a good pixel artist can do.

Environments and Music
I tend to lose interest in things quite quickly if they become too repetitive, but with limited gameplay variations and enemy types in this game I wanted to keep things interesting by packing in as many environments and different music ques as possible. The environment features are randomly generated to save Map space. Music is never the same for two levels in a row, though there are themes tied to the different environments.

Invincible mode was added as an accessibility feature. The standard game is quite hard in places and it felt a shame to restrict the game only to people with the physical ability to be able to deal with it. Similarly I wanted to have the full level select from the start, so as not to limit how people could play the game. I feel more games should have accessibility options.

Half the game was written with a token count of 7000-8100, I hadn't anticipated how much of development would be refactoring and learning token optimisation tricks (if I couldn't write tables as split"1,2,3" this would never have been finished :) )
The _ENV feature was essential for keeping the token count down in the game. It seems confusing at first, but can be implemented very easily and saved having to write self. for every object variable.

I hadn't anticipated how tricky it could be to smoothly integrate music into a project. I spent a while unsure which part of the code should have ownership of the music controls. In the end I created one master function that controls what music is playing and when and put it straight in '_update' running beside my state engine.