-
-
Save Pharap/bd9ac5ba58f1805fbe322f4305cb09ee to your computer and use it in GitHub Desktop.
Polish & Trim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Platformer | |
// By V360 (@TheV360gameDev) | |
//TODO: Pressing up shows how many keys you have and cuts off the map renderer to save CPU (except for dark mode) | |
// UI overhaul (more than the title screen!) | |
// Level titles | |
#include <Arduboy2.h> | |
#include <ArduboyTones.h> | |
Arduboy2 arduboy; | |
ArduboyTones sound(arduboy.audio.enabled); | |
#define ARDBITMAP_SBUF arduboy.getBuffer() | |
#include <ArdBitmap.h> | |
ArdBitmap<WIDTH, HEIGHT> ardbitmap; | |
#define LVLWIDTH 32 | |
#define LVLHEIGHT 16 | |
#define LVLNUMBER 3 | |
#define COLSTEP 4 | |
enum class TileType : uint8_t | |
{ | |
None, | |
Block, | |
Exit, | |
Mine, | |
Waters, | |
Water, | |
Key, | |
Lock, | |
LeftConveyor, | |
RightConveyor, | |
Orb, | |
Sprint, | |
Switch, | |
SwitchOff, | |
SwitchOn, | |
Player, | |
}; | |
enum class GameState : uint8_t | |
{ | |
MainMenu, | |
LevelSelect, | |
Gameplay, | |
PauseScreen, | |
LevelEditor, | |
EditorMenu, | |
ModeSelect, | |
Credits, | |
}; | |
#define TILEBLOCK 1 | |
#define TILEEXIT 2 | |
#define TILEMINE 3 | |
#define TILEWATERS 4 | |
#define TILEWATER 5 | |
#define TILEKEY 6 | |
#define TILELOCKBLOCK 7 | |
#define TILELCONVEYOR 8 | |
#define TILERCONVEYOR 9 | |
#define TILEORB 10 | |
#define TILESPRING 11 | |
#define TILESWITCH 12 | |
#define TILESWITCHBLOCKOFF 13 | |
#define TILESWITCHBLOCKON 14 | |
#define TILEPLAYER 15 | |
GameState state = GameState::MainMenu; | |
const uint8_t titleTitle[] PROGMEM = | |
{ | |
0x7d, 0x8f, 0xbb, 0x6b, 0x6b, 0xb5, 0xda, 0x91, 0x9c, 0x3a, 0x66, 0xb3, 0x73, 0x52, 0x2f, 0x92, 0x4b, 0x8e, 0x92, 0x3d, 0xe8, 0xf6, 0x2a, 0x5d, 0x0b, 0x89, 0x2a, 0xd4, 0x29, 0x5e, 0xd4, 0xcb, 0x78, 0xba, 0x4e, 0xe8, 0xc5, 0xeb, 0x3a, 0xa1, 0x97, 0x71, 0x43, 0x2f, 0xe3, 0xca, 0x51, 0xbc, 0xa8, 0x97, 0x71, 0xcd, 0x25, 0x70, 0x52, 0xff, 0xd8, 0xf3, 0x80, 0x1d, 0xa2, 0x3d, 0xc0, 0x10, 0x00, 0x81, 0x70, 0x1a, 0x02, 0x05, 0xf1, 0xc0, 0x19, 0x90, 0x0c, 0x84, 0x23, 0x70, 0x93, 0x21, 0x41, 0x18, 0xdb, 0x1d, 0x28, 0x8c, 0x52, 0x2e, 0x94, 0x0b, 0x65, 0x05, 0x0a, 0xa3, 0x94, 0x11, 0x29, 0x54, 0xa1, 0x48, 0x02, 0x85, 0x51, 0xca, 0x52, 0x0a, 0x03, 0x61, 0x39, 0xd5, 0xb5, 0x32, 0x0f, 0x5c, 0x28, 0x4e, 0x28, 0x58, 0xa1, 0xa0, 0x91, 0x12, 0x07, 0x85, 0x50, 0x28, 0x65, 0x50, 0x2e, 0x14, 0x42, 0xa1, 0x94, 0x41, 0x21, 0x14, 0x4a, 0x19, 0x14, 0x42, 0xa1, 0x94, 0x41, 0x71, 0x42, 0x41, 0x44, 0x28, 0x88, 0x08, 0x65, 0x05, 0x0a, 0xa3, 0x94, 0x0b, 0xe5, 0x42, 0xb9, 0x50, 0x2e, 0x94, 0x0b, 0xe5, 0x42, 0xb9, 0x50, 0x2e, 0x94, 0x0b, 0xe5, 0x42, 0xb9, 0x50, 0x2e, 0x94, 0x0b, 0xe5, 0x42, 0xb9, 0x50, 0x2e, 0x74, 0x0d, 0x0a, 0x05, 0x80, 0x82, 0x30, 0x28, 0x91, 0x40, 0x89, 0x83, 0x72, 0xa1, 0x18, 0x85, 0x62, 0x06, 0x05, 0x0a, 0x40, 0xa1, 0x83, 0x42, 0x01, 0xa0, 0x90, 0x45, 0xa1, 0x01, 0x0a, 0x05 | |
}; | |
const uint8_t titlePlay[] PROGMEM = | |
{ | |
0x1f, 0x9f, 0xbf, 0x8b, 0xab, 0xf7, 0x3d, 0x30, 0x80, 0x89, 0x7b, 0x20, 0x5c, 0xc0, 0x3d, 0x30, 0x80, 0x03, 0x77, 0x22, 0x69, 0xe2, 0x7c, 0x90, 0x56, 0xe4, 0x06, 0x5c, 0xd5, 0x8b, 0x27, 0xcf, 0xc9, 0xf8, 0x33, 0x41, 0x26, 0x9b, 0xc5, 0x64, 0xb3, 0xd8, 0x0c, 0x2e, 0x94, 0x4b, 0x7e, 0x47, 0x0f, 0xe3, 0xc8, 0xbb, 0xc3, 0x72, 0xb9, 0x5c, 0x2e, 0xd7, 0x8b, 0xe5, 0x72, 0xb9, 0x5c, 0xae, 0x17, 0xcb, 0xe5, 0x72, 0xb9, 0x5c, 0x87, 0x7d, 0xce, 0x69, 0x16, 0x8f | |
}; | |
const uint8_t titleEdit[] PROGMEM = | |
{ | |
0x1f, 0x9f, 0x97, 0x2c, 0x36, 0xaf, 0xd8, 0xac, 0x7c, 0x72, 0x0f, 0x04, 0x00, 0x09, 0xb8, 0x07, 0x42, 0x48, 0x74, 0x13, 0x90, 0x21, 0x01, 0x37, 0x43, 0x86, 0x0c, 0xe7, 0x31, 0x72, 0xe1, 0x0d, 0x17, 0xcc, 0xe0, 0xea, 0x11, 0xfc, 0x33, 0x72, 0x08, 0xb6, 0x47, 0xe4, 0x46, 0x28, 0x04, 0x6a, 0x0f, 0xcb, 0xe5, 0x72, 0xb9, 0x5c, 0x87, 0x9d, 0xd3, 0xb8, 0xac, 0x93, 0xfb, 0xb0, 0x5c, 0x2e, 0x97, 0xcb, 0xf5, 0x62, 0xb9, 0x5c, 0x2e, 0x97, 0xeb, 0xc5, 0x72, 0xb9, 0x5c, 0x2e, 0xd7, 0xe1, 0xcc, 0xe1, 0x7a, 0xe0, 0x0c | |
}; | |
const uint8_t titleThanks[] PROGMEM = | |
{ | |
0x1f, 0x9f, 0x1f, 0xde, 0x0c, 0x19, 0x32, 0xdc, 0x03, 0x43, 0x07, 0xee, 0x81, 0x01, 0x1c, 0xb8, 0x07, 0x0e, 0x52, 0xdc, 0x03, 0x43, 0x45, 0xdc, 0x01, 0x04, 0x00, 0x21, 0x38, 0x37, 0xb1, 0x86, 0x0b, 0xa0, 0x02, 0x05, 0x54, 0x60, 0xa1, 0x02, 0x05, 0x64, 0x50, 0xce, 0x5c, 0x28, 0x17, 0xca, 0x85, 0x5f, 0x8a, 0xaa, 0xca, 0x01, 0x00, 0x0c, 0x20, 0x41, 0x00, 0x40, 0x00, 0x40, 0x82, 0x00, 0x08, 0x00, 0x08, 0xc0, 0x11, 0x40, 0xd8, 0x40, 0x81, 0xcd, 0x17, 0x39, 0xf6, 0x98, 0x5b, 0x5e, 0x79, 0xe7, 0xc6, 0x0b, 0xfe, 0x16, 0x6b, 0x9e, 0xc6, 0xe1, 0x22 | |
}; | |
// 64 uint8_ts | |
const uint8_t tiles[16][4] PROGMEM = | |
{ | |
{B0101, B0010, B0101, B0000}, // Nothing | |
{B1111, B1001, B1001, B1111}, // Block | |
{B1110, B1111, B1011, B1110}, // Exit door | |
{B1001, B0110, B0110, B1001}, // Mine | |
{B0100, B0010, B0100, B0010}, // Water surface | |
{B1010, B0000, B0101, B0000}, // Water | |
{B0000, B0110, B0110, B0000}, // Key | |
{B1111, B1011, B1101, B1111}, // Locked block | |
{B1111, B1011, B0101, B1111}, // Left conveyor | |
{B1111, B0101, B1011, B1111}, // Right conveyor | |
{B0110, B1111, B1101, B0110}, // Gravity Orb | |
{B1001, B1111, B1111, B1001}, // Spring | |
{B1100, B1000, B1000, B1100}, // Toggle switch | |
{B0101, B1000, B0001, B1010}, // Off switch block | |
{B0111, B1001, B1001, B1110}, // On switch block | |
{B1111, B1101, B1111, B1101} // Player | |
}; | |
const uint8_t arrow[4] PROGMEM = | |
{ | |
B0010, B0110, B0110, B0010 | |
}; | |
const uint8_t tileInfo[2] PROGMEM = | |
{ | |
B10000010, B01000011 | |
}; | |
// 257 uint8_ts per level, but this doesn't matter because it's in PROGMEM | |
const uint8_t levels[LVLNUMBER][257] PROGMEM = | |
{ | |
{ | |
B00000000, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x44, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01, 0x11, 0x15, 0x55, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0xF0, 0x00, 0x01, 0x11, 0x11, 0x15, 0x55, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 | |
}, | |
{ | |
B00000000, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x44, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01, 0x11, 0x15, 0x55, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0xF0, 0x00, 0x01, 0x11, 0x11, 0x15, 0x55, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 | |
}, | |
{ | |
B00000000, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x51, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x88, 0x99, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x41, 0x10, 0x10, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x51, 0x10, 0x00, 0x00, 0x11, 0x31, 0x10, 0x00, 0x10, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0x05, 0x51, 0x11, 0xF0, 0x11, 0x11, 0x00, 0x00, 0x00, 0xC0, 0x0A, 0x00, 0xDE, 0x00, 0x20, 0x00, 0x05, 0x51, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x77, 0x11, 0x11 | |
} | |
}; | |
// ugh, this reminds me of my early petit computer days with my awful programming | |
// but this is apparently how it's done? | |
const char title0[] PROGMEM = "Welcome"; | |
const char title1[] PROGMEM = "Locked door"; | |
const char title2[] PROGMEM = "test"; | |
const char * const levelTitles[] PROGMEM = | |
{ | |
title0, title1, title2 | |
}; | |
const char mode0[] PROGMEM = "Flip mode"; | |
const char mode1[] PROGMEM = "Dark mode"; | |
const char mode2[] PROGMEM = "L Wind mode"; | |
const char mode3[] PROGMEM = "R Wind mode"; | |
const char mode4[] PROGMEM = "Switch mode"; | |
const char mode5[] PROGMEM = "Water mode"; | |
const char mode6[] PROGMEM = "Volatile mode"; | |
const char mode7[] PROGMEM = "Void mode"; | |
const char * const modeTitles[] PROGMEM = | |
{ | |
mode0, mode1, mode2, mode3, mode4, mode5, mode6, mode7 | |
}; | |
// 512 uint8_ts for an unsquished level | |
uint8_t tileMap[32][16]; | |
// All the stuff needed for the platforming | |
float x = 0; | |
float y = 0; | |
float ax = 0; | |
float ay = 0; | |
bool gr = false; | |
bool dead = false; | |
enum class LevelModes : uint8_t | |
{ | |
Flip = (1 << 0), | |
Dark = (1 << 1), | |
Left = (1 << 2), | |
Right = (1 << 3), | |
Switch = (1 << 4), | |
Water = (1 << 5), | |
Volatile = (1 << 6), | |
Void = (1 << 7), | |
}; | |
const LevelModes bitIndexedLevelModes[8] PROGMEM = | |
{ | |
static_cast<LevelModes>(1 << 0), | |
static_cast<LevelModes>(1 << 1), | |
static_cast<LevelModes>(1 << 2), | |
static_cast<LevelModes>(1 << 3), | |
static_cast<LevelModes>(1 << 4), | |
static_cast<LevelModes>(1 << 5), | |
static_cast<LevelModes>(1 << 6), | |
static_cast<LevelModes>(1 << 7), | |
}; | |
constexpr inline LevelModes getIndexedModeConst(uint8_t index) | |
{ | |
return static_cast<LevelModes>(1 << index); | |
} | |
inline LevelModes getIndexedMode(uint8_t index) | |
{ | |
return static_cast<LevelModes>(pgm_read_byte(&bitIndexedLevelModes[index])); | |
} | |
inline LevelModes operator ~(LevelModes value) | |
{ | |
return static_cast<LevelModes>(~static_cast<uint8_t>(value)); | |
} | |
inline LevelModes operator |(LevelModes left, LevelModes right) | |
{ | |
return static_cast<LevelModes>(static_cast<uint8_t>(left) | static_cast<uint8_t>(right)); | |
} | |
inline LevelModes operator &(LevelModes left, LevelModes right) | |
{ | |
return static_cast<LevelModes>(static_cast<uint8_t>(left) & static_cast<uint8_t>(right)); | |
} | |
inline LevelModes operator ^(LevelModes left, LevelModes right) | |
{ | |
return static_cast<LevelModes>(static_cast<uint8_t>(left) ^ static_cast<uint8_t>(right)); | |
} | |
inline LevelModes setMode(LevelModes source, LevelModes bits) | |
{ | |
return (source | bits); | |
} | |
inline LevelModes clearMode(LevelModes source, LevelModes bits) | |
{ | |
return (source & ~bits); | |
} | |
inline LevelModes toggleMode(LevelModes source, LevelModes bits) | |
{ | |
return (source ^ bits); | |
} | |
inline bool isModeSet(LevelModes source, LevelModes bits) | |
{ | |
return (source & bits) == bits; | |
} | |
inline bool isAnyModeSet(LevelModes source, LevelModes bits) | |
{ | |
return static_cast<uint8_t>(source & bits) != 0; | |
} | |
LevelModes levelModes = static_cast<LevelModes>(0); | |
/* | |
* Level modes: | |
* b0: *flip mode, flips gravity instead of jumping | |
* b1: *dark mode, can't see more than a 3 tile radius around you | |
* b2: *left conveyor mode, constantly moving left | |
* b3: *right conveyor mode, idk lol | |
* b4: *switch mode, when you jump, the switch tiles toggle | |
* b5: *water mode, water pulls you down when gravity is flipped | |
* b6: *volatile mode, don't fall too fast! | |
* b7: *void mode: don't render the normal solid block (challenging!) | |
*/ | |
int8_t flip = 1; | |
uint8_t timer; | |
// Stuff for tiles that do things | |
int keys; // for those people that will fill the level with coins... >:( i would put a limit but that would just mean more wasted space | |
// Menu variables! | |
uint8_t sel = 0; | |
uint8_t currMap = 0; | |
bool showDebug = false; | |
// Editor variables! | |
bool editor = false; | |
bool switched = false; | |
bool placedPlayer = false; | |
uint8_t cursorX = 0; | |
uint8_t cursorY = 0; | |
uint8_t cursorT = 0; | |
// This stuff will run once! | |
void setup(void) | |
{ | |
//Start your Arduboys... | |
arduboy.begin(); //60 fps | |
} | |
void loop(void) | |
{ | |
// I don't want to go on without waiting a frame first! | |
if (!arduboy.nextFrame()) | |
return; | |
arduboy.pollButtons(); | |
if (arduboy.pressed(UP_BUTTON) && arduboy.justPressed(DOWN_BUTTON)) | |
showDebug = !showDebug; | |
arduboy.clear(); | |
switch (state) | |
{ | |
case GameState::MainMenu: updateMainMenu(); break; | |
// no level select | |
case GameState::Gameplay: updateGameplay(); break; | |
case GameState::PauseScreen: updatePauseScreen(); break; | |
case GameState::LevelEditor: updateLevelEditor(); break; | |
case GameState::EditorMenu: updateEditorMenu(); break; | |
case GameState::ModeSelect: updateModeSelect(); break; | |
case GameState::Credits: updateCredits(); break; | |
default: | |
arduboy.println(F("whoops")); | |
arduboy.println(F("there's no state here!")); | |
break; | |
} | |
if (showDebug) | |
{ | |
arduboy.setCursor(0, 50 * ((uint8_t)floor(y / 4) < 8)); | |
arduboy.print(arduboy.cpuLoad()); | |
} | |
arduboy.display(); // Actually make the stuff I drew visible | |
} | |
void updateMainMenu(void) | |
{ | |
ardbitmap.drawCompressed(1, 0, titleTitle, WHITE, ALIGN_NONE, MIRROR_NONE); | |
if (arduboy.justPressed(LEFT_BUTTON)) | |
if(sel > 0) | |
sel--; | |
if (arduboy.justPressed(RIGHT_BUTTON)) | |
if(sel < 2) | |
sel++; | |
if (arduboy.justReleased(A_BUTTON)) | |
{ | |
switch(sel) | |
{ | |
case 0: | |
state = GameState::Gameplay; | |
editor = false; | |
loadMap(currMap); | |
break; | |
case 1: | |
state = GameState::LevelEditor; | |
editor = true; | |
fixMap(); | |
break; | |
case 2: | |
state = GameState::Credits; | |
break; | |
default: break; | |
} | |
} | |
arduboy.fillRect(10 + (39 * sel), 22, 32, 32, WHITE); | |
for (uint8_t i = 0; i < 3; i++) | |
arduboy.drawRect(8 + (39 * i), 20, 36, 36); | |
const uint8_t playColour = (sel != 0) ? WHITE : BLACK; | |
ardbitmap.drawCompressed(10, 22, titlePlay, playColour, ALIGN_NONE, MIRROR_NONE); | |
const uint8_t editColour = (sel != 1) ? WHITE : BLACK; | |
ardbitmap.drawCompressed(49, 22, titleEdit, editColour, ALIGN_NONE, MIRROR_NONE); | |
const uint8_t thanksColour = (sel != 2) ? WHITE : BLACK; | |
ardbitmap.drawCompressed(88, 22, titleThanks, thanksColour, ALIGN_NONE, MIRROR_NONE); | |
} | |
bool tryUnlockBox(uint8_t x, uint8_t y) | |
{ | |
if(keys == 0) | |
return false; | |
if (pixelTile(x, y) != TILELOCKBLOCK) | |
return false; | |
--keys; | |
tileMap[x / 4][y / 4] = TILELOCKBLOCK << 4; | |
return true; | |
} | |
bool tryUnlockFirstSurroundingBox(void) | |
{ | |
return | |
tryUnlockBox(x - 2, y) || | |
tryUnlockBox(x + 2, y) || | |
tryUnlockBox(x, y - 2) || | |
tryUnlockBox(x, y + 2); | |
} | |
bool tryUnlockAnySurroundingBoxes(void) | |
{ | |
bool boxUnlocked = false; | |
boxUnlocked |= tryUnlockBox(x - 2, y); | |
boxUnlocked |= tryUnlockBox(x + 2, y); | |
boxUnlocked |= tryUnlockBox(x, y - 2); | |
boxUnlocked |= tryUnlockBox(x, y + 2); | |
return boxUnlocked; | |
} | |
void updateGameplay(void) | |
{ | |
// First, logic... | |
if (arduboy.pressed(LEFT_BUTTON)) | |
ax -= 0.4f; | |
if (arduboy.pressed(RIGHT_BUTTON)) | |
ax += 0.4f; | |
if (arduboy.justPressed(A_BUTTON) && gr) | |
{ | |
if (isModeSet(levelModes, LevelModes::Flip)) | |
{ | |
flip = -flip; | |
sound.tone(NOTE_C5, 24); | |
} | |
else | |
{ | |
const int8_t multiplier = isModeSet(levelModes, LevelModes::Volatile) ? -2 : -3; | |
ay = flip * multiplier; | |
if (isModeSet(levelModes, LevelModes::Switch)) | |
useSwitch(); | |
sound.tone(NOTE_F4, 24); | |
} | |
} | |
if (arduboy.justReleased(B_BUTTON)) | |
{ | |
state = GameState::PauseScreen; | |
sound.tone(NOTE_F4, 32, NOTE_F5, 32); | |
} | |
ax *= 0.7f; | |
ay = (ay + (0.2f * flip)) * 0.9f; | |
gr = false; | |
for (uint8_t col = 0; col < COLSTEP; ++col) | |
{ | |
// Collisions and special blocks | |
if (isTileSolid(x, y)) | |
continue; | |
const uint8_t prevX = x / 4; | |
const uint8_t prevY = y / 4; | |
x = modFloat(x + (ax / COLSTEP), 128); | |
y = modFloat(y + (ay / COLSTEP), 64); | |
const uint8_t touchedTileL = pixelTile(x - 2, y); | |
const uint8_t touchedTileM = pixelTile(x, y); | |
const uint8_t touchedTileR = pixelTile(x + 2, y); | |
if(tryUnlockAnySurroundingBoxes()) | |
sound.tone(NOTE_C6, 16, NOTE_C5, 16); | |
// Spring | |
if (touchedTileL == TILESPRING || touchedTileR == TILESPRING) | |
{ | |
if(prevX != static_cast<uint8_t>(x / 4) || prevY != static_cast<uint8_t>(y / 4)) | |
if (abs(ay) > 1) | |
{ | |
ay = -ay * 2.5; | |
sound.tone(NOTE_C6, 16, NOTE_C5, 16); | |
} | |
} | |
// Too fast? | |
if ((abs(ay) > 1.62) && isModeSet(levelModes, LevelModes::Volatile)) | |
{ | |
dead = true; | |
} | |
// Collisions | |
// Ceiling | |
if (isTileSolid(x - 1, y - 2) || isTileSolid(x + 1, y - 2)) | |
{ | |
ay = 0; | |
y = (ceil(y / 4) * 4) - 2; | |
gr = flip < 0; | |
} | |
// Floor | |
if (isTileSolid(x - 1, y + 2) || isTileSolid(x + 1, y + 2)) | |
{ | |
ay = 0; | |
y = (floor(y / 4) * 4) + 2; | |
gr = flip > 0; | |
} | |
// Walls | |
if (isTileSolid(x - 2, y - 1) || isTileSolid(x - 2, y + 1)) | |
{ | |
ax = 0; | |
x = (ceil(x / 4) * 4) - 2; | |
} | |
if (isTileSolid(x + 2, y - 1) || isTileSolid(x + 2, y + 1)) | |
{ | |
ax = 0; | |
x = (floor(x / 4) * 4) + 2; | |
} | |
// Special blocks | |
// Water | |
if ((touchedTileL == TILEWATERS) || (touchedTileR == TILEWATERS)) | |
{ | |
const int8_t sign = isModeSet(levelModes, LevelModes::Water) ? 1 : -1; | |
ay += 0.05f * sign; | |
gr = true; | |
} | |
if ((touchedTileL == TILEWATER) || (touchedTileR == TILEWATER)) | |
{ | |
const int8_t sign = isModeSet(levelModes, LevelModes::Water) ? 1 : -1; | |
ay += 0.07f * sign; | |
gr = true; | |
} | |
// Mine | |
if ((touchedTileL == TILEMINE) || (touchedTileR == TILEMINE)) | |
{ | |
dead = true; | |
} | |
// Conveyors | |
if ((pixelTile(x, y + 2) == TILELCONVEYOR) || (pixelTile(x, y - 2) == TILELCONVEYOR)) | |
{ | |
ax -= 0.05f; | |
} | |
if ((pixelTile(x, y + 2) == TILERCONVEYOR) || (pixelTile(x, y - 2) == TILERCONVEYOR)) | |
{ | |
ax += 0.05f; | |
} | |
// Wind | |
if (isModeSet(levelModes, LevelModes::Left)) | |
{ | |
ax -= 0.05f; | |
} | |
if (isModeSet(levelModes, LevelModes::Right)) | |
{ | |
ax += 0.05f; | |
} | |
// Orb | |
if (touchedTileL == TILEORB) | |
{ | |
flip = -flip; | |
tileMap[(uint8_t)((x - 2) / 4)][(uint8_t)(y / 4)] = TILEORB << 4; | |
sound.tone(NOTE_F4, 16, NOTE_C5, 16, NOTE_F5, 16); | |
} | |
if (touchedTileR == TILEORB) | |
{ | |
flip = -flip; | |
tileMap[(uint8_t)((x + 2) / 4)][(uint8_t)(y / 4)] = TILEORB << 4; | |
sound.tone(NOTE_F4, 16, NOTE_C5, 16, NOTE_F5, 16); | |
} | |
// Key | |
if (touchedTileL == TILEKEY) | |
{ | |
++keys; | |
tileMap[(uint8_t)((x - 2) / 4)][(uint8_t)(y / 4)] = TILEKEY << 4; | |
sound.tone(NOTE_C5, 16, NOTE_C6, 16); | |
} | |
if (touchedTileR == TILEKEY) | |
{ | |
++keys; | |
tileMap[(uint8_t)((x + 2) / 4)][(uint8_t)(y / 4)] = TILEKEY << 4; | |
sound.tone(NOTE_C5, 16, NOTE_C6, 16); | |
} | |
// Switch | |
if (touchedTileM == TILESWITCH && (prevX != (x / 4) || prevY != (y / 4))) | |
{ | |
useSwitch(); | |
sound.tone(NOTE_C5, 16, NOTE_E5, 16); | |
} | |
// Dead | |
if (dead) | |
{ | |
dead = false; | |
if (editor) | |
{ | |
fixMap(); | |
loadMap(0); | |
} | |
else | |
{ | |
loadMap(currMap); | |
} | |
sound.tone(NOTE_F5, 16, NOTE_D4, 16); | |
} | |
// Exit | |
if ((touchedTileL == TILEEXIT) || (touchedTileR == TILEEXIT)) | |
{ | |
if (editor) | |
{ | |
fixMap(); | |
state = GameState::LevelEditor; | |
} | |
else | |
{ | |
currMap += 1; | |
loadMap(currMap); | |
} | |
sound.tone(NOTE_C3, 32, NOTE_C4, 32, NOTE_C5, 32); | |
} | |
} | |
// Now draw! | |
if (isModeSet(levelModes, LevelModes::Dark)) | |
{ | |
drawMap(floor(x / 4) + 29, floor(y / 4) + 13, 7, 7); | |
} | |
else | |
{ | |
drawMap(0, 0, 32, 16); | |
} | |
arduboy.drawBitmap(x - 2, y - 2, tiles[TILEPLAYER], 4, 4); | |
if (timer > 0) | |
{ | |
arduboy.setCursor(0, 0); | |
arduboy.println(reinterpret_cast<const __FlashStringHelper *>(pgm_read_word(&levelTitles[currMap]))); | |
--timer; | |
} | |
} | |
void updatePauseScreen(void) | |
{ | |
sel = 0; | |
arduboy.setCursor(0, 0); | |
arduboy.println(F("Paused")); | |
arduboy.print(F("Keys: ")); | |
arduboy.println(keys); | |
arduboy.println(F("A: Menu")); | |
arduboy.println(F("B: Back")); | |
if (arduboy.justReleased(A_BUTTON)) | |
{ | |
if (editor) | |
{ | |
fixMap(); | |
state = GameState::LevelEditor; | |
} | |
else | |
state = GameState::MainMenu; | |
} | |
if (arduboy.justReleased(B_BUTTON)) | |
state = GameState::Gameplay; | |
} | |
void updateLevelEditor(void) | |
{ | |
if (arduboy.justPressed(UP_BUTTON)) | |
cursorY--; | |
if (arduboy.justPressed(DOWN_BUTTON)) | |
cursorY++; | |
cursorY %= 16; | |
if (arduboy.justPressed(LEFT_BUTTON)) | |
cursorX--; | |
if (arduboy.justPressed(RIGHT_BUTTON)) | |
cursorX++; | |
cursorX %= 32; | |
if(cursorT == TILEPLAYER) | |
{ | |
if(arduboy.justReleased(A_BUTTON)) | |
{ | |
tileMap[cursorX][cursorY] = cursorT; | |
if (!placedPlayer) | |
{ | |
placedPlayer = true; | |
cursorT = 1; | |
} | |
} | |
} | |
else | |
{ | |
if(arduboy.pressed(A_BUTTON)) | |
{ | |
if (tileMap[cursorX][cursorY] == TILEPLAYER) | |
placedPlayer = false; | |
tileMap[cursorX][cursorY] = cursorT; | |
} | |
} | |
if (arduboy.justReleased(B_BUTTON)) | |
state = GameState::EditorMenu; | |
drawMap(0, 0, 32, 16); | |
const uint8_t timerColour = (timer < 10) ? INVERT : WHITE; | |
arduboy.drawBitmap(cursorX * 4, cursorY * 4, tiles[cursorT], 4, 4, timerColour); | |
++timer; | |
timer %= 20; | |
} | |
void updateEditorMenu(void) | |
{ | |
arduboy.println(F("Select tile")); | |
if (placedPlayer) | |
arduboy.println(F("Player placed!")); | |
arduboy.drawBitmap(cursorT * 6, 52, arrow, 4, 4); | |
const uint8_t placedLimit = (placedPlayer ? 15 : 16); | |
for (uint8_t i = 0; i < placedLimit; ++i) | |
arduboy.drawBitmap(i * 6, 58, tiles[i], 4, 4); | |
if (arduboy.justPressed(LEFT_BUTTON)) | |
cursorT--; | |
if (arduboy.justPressed(RIGHT_BUTTON)) | |
cursorT++; | |
cursorT %= placedLimit; | |
if (arduboy.justReleased(A_BUTTON) && placedPlayer) | |
{ | |
state = GameState::Gameplay; | |
if (showDebug) | |
outputMap(); | |
loadMap(0); // dont need id | |
} | |
if (arduboy.justReleased(B_BUTTON)) | |
state = GameState::LevelEditor; | |
if (arduboy.justReleased(UP_BUTTON)) | |
state = GameState::MainMenu; | |
if (arduboy.justReleased(DOWN_BUTTON)) | |
{ | |
/*for (uint8_t j = 0; j < LVLHEIGHT; j++) { | |
for (uint8_t i = 0; i < LVLWIDTH; i++) { | |
tileMap[i][j] = 0; | |
} | |
} | |
placedPlayer = false;*/ | |
sel = 0; | |
state = GameState::ModeSelect; | |
} | |
} | |
void updateModeSelect(void) | |
{ | |
arduboy.println(F("Select mode")); | |
arduboy.println(reinterpret_cast<const __FlashStringHelper *>(pgm_read_word(&modeTitles[sel]))); | |
arduboy.drawBitmap(sel * 6, 52, arrow, 4, 4); | |
for (uint8_t i = 0; i < 8; i++) | |
{ | |
const LevelModes mode = getIndexedMode(i); | |
const uint8_t index = 3 - (isModeSet(levelModes, mode) ? 2 : 0); | |
arduboy.drawBitmap(i * 6, 58, tiles[index], 4, 4); | |
} | |
if (arduboy.justPressed(LEFT_BUTTON)) | |
--sel; | |
if (arduboy.justPressed(RIGHT_BUTTON)) | |
++sel; | |
sel %= 8; | |
if (arduboy.justReleased(A_BUTTON)) | |
{ | |
const LevelModes selectedMode = getIndexedMode(sel); | |
levelModes = toggleMode(levelModes, selectedMode); | |
} | |
if (arduboy.justReleased(B_BUTTON)) | |
{ | |
sel = 0; | |
state = GameState::EditorMenu; | |
} | |
} | |
void updateCredits(void) | |
{ | |
if (arduboy.justReleased(A_BUTTON)) | |
{ | |
state = GameState::MainMenu; | |
} | |
arduboy.println(F("~thanks for playing~")); | |
arduboy.println(F("Thanks to Nathan, ")); | |
arduboy.println(F("Arduboy forum, ")); | |
arduboy.println(F("and you!")); | |
} | |
bool isTileSolid(uint8_t x, uint8_t y) | |
{ | |
return isSolid(pixelTile(x, y)); | |
} | |
uint8_t pixelTile(uint8_t x, uint8_t y) | |
{ | |
return tileMap[(x / 4) % 32][(y / 4) % 16] & 0x0F; | |
} | |
bool isSolid(uint8_t tileID) | |
{ | |
return bitRead(pgm_read_byte(&tileInfo[(uint8_t)floor(tileID / 8)]), tileID % 8) != 0; | |
} | |
void loadMap(uint8_t mapIndex) | |
{ | |
keys = 0; | |
ax = 0; | |
ay = 0; | |
flip = 1; | |
timer = editor ? 120 : 0; | |
switched = false; | |
if (mapIndex >= LVLNUMBER) | |
{ | |
mapIndex = 0; | |
state = GameState::Credits; | |
} | |
if (!editor) | |
levelModes = static_cast<LevelModes>(pgm_read_byte(&levels[mapIndex][0])); | |
for (uint8_t j = 0; j < LVLHEIGHT; ++j) | |
for (uint8_t i = 0; i < LVLWIDTH; ++i) | |
{ | |
if (!editor) | |
{ | |
if (i % 2 == 0) | |
tileMap[i][j] = pgm_read_byte(&levels[mapIndex][1 + (i / 2) + (j * 16)]) >> 4; | |
else | |
tileMap[i][j] = pgm_read_byte(&levels[mapIndex][1 + (i / 2) + (j * 16)]) & 0x0F; | |
} | |
if (tileMap[i][j] == TILEPLAYER) | |
{ | |
x = (i * 4) + 2; | |
y = (j * 4) + 2; | |
tileMap[i][j] = TILEPLAYER << 4; | |
placedPlayer = true; | |
} | |
} | |
} | |
void drawMap(uint8_t x, uint8_t y, uint8_t w, uint8_t h) | |
{ | |
for (uint8_t j = y; j < y + h; j++) | |
for (uint8_t i = x; i < x + w; i++) | |
{ | |
const uint8_t tile = (tileMap[i % 32][j % 16] & 0x0F); | |
const uint8_t bit = (state == GameState::Gameplay && isModeSet(levelModes, LevelModes::Void)) ? 1 : 0; | |
if (tile > bit) | |
arduboy.drawBitmap((i % 32) * 4, (j % 16) * 4, &tiles[tile][0], 4, 4); | |
} | |
} | |
void fixMap(void) | |
{ | |
for (uint8_t j = 0; j < LVLHEIGHT; j++) | |
{ | |
for (uint8_t i = 0; i < LVLWIDTH; i++) | |
{ | |
const uint8_t tileValue = (tileMap[i][j] >> 4); | |
if (tileValue > 0) | |
tileMap[i][j] = tileValue; | |
if ((tileMap[i][j] == TILESWITCHBLOCKOFF || tileMap[i][j] == TILESWITCHBLOCKON)) | |
if(switched) | |
{ | |
useSwitch(); | |
switched = false; | |
} | |
} | |
} | |
} | |
void outputMap(void) | |
{ | |
/* Serial.print("{\n"); | |
for (uint8_t j = 0; j < LVLHEIGHT; j++) { | |
for (uint8_t i = 0; i < LVLWIDTH; i+=2) { | |
Serial.print("0x"); | |
Serial.print(tileMap[i][j], HEX); | |
Serial.print(tileMap[i+1][j], HEX); | |
Serial.print(", "); | |
} | |
Serial.print("\n"); | |
} | |
Serial.print("};");*/ | |
} | |
void useSwitch(void) | |
{ | |
for (uint8_t j = 0; j < LVLHEIGHT; j++) | |
for (uint8_t i = 0; i < LVLWIDTH; i++) | |
{ | |
if (tileMap[i][j] == TILESWITCHBLOCKOFF) | |
tileMap[i][j] = TILESWITCHBLOCKON; | |
else if (tileMap[i][j] == TILESWITCHBLOCKON) | |
tileMap[i][j] = TILESWITCHBLOCKOFF; | |
} | |
switched = !switched; | |
} | |
float modFloat(float n, float m) | |
{ | |
const int fn = static_cast<int>(n); | |
const float f = n - fn; | |
return modFloat(static_cast<int>(n), static_cast<int>(m)) + f; | |
} | |
float modFloat(float n, int m) | |
{ | |
const int fn = static_cast<int>(n); | |
const float f = n - fn; | |
return modFloat(static_cast<int>(n), m) + f; | |
} | |
float modFloat(int n, int m) | |
{ | |
const int r = n % m; | |
return (r < 0) ? r + m : r; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment