Skip to content

Instantly share code, notes, and snippets.

@Pharap
Created April 10, 2018 06:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pharap/bd9ac5ba58f1805fbe322f4305cb09ee to your computer and use it in GitHub Desktop.
Save Pharap/bd9ac5ba58f1805fbe322f4305cb09ee to your computer and use it in GitHub Desktop.
Polish & Trim
// 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