This is a straight conversion of the original Gameduino Frogger game.
It is controlled via the four touch buttons on the right hand side.
#include <EEPROM.h>
#include <SPI.h>
#include <GD2.h>
#include "frogger_assets.h"
#define CONTROL_LEFT 1
#define CONTROL_RIGHT 2
#define CONTROL_UP 4
#define CONTROL_DOWN 8
class Controller {
public:
void begin() {
prev = 0;
}
byte read() {
byte r = GD.inputs.tag & (CONTROL_LEFT | CONTROL_RIGHT | CONTROL_UP | CONTROL_DOWN);
byte edge = r & ~prev;
prev = r;
return edge;
}
private:
byte prev;
};
static Controller Control;
static void draw_score(byte x, byte y, long n)
{
GD.cmd_number(8 * x, 8 * y, FONT_HANDLE, 5, n);
}
// Game variables
static unsigned int t;
static int frogx, frogy; // screen position
static int leaping; // 0 means not leaping, 1-8 animates the leap
static int frogdir; // while leaping, which direction is the leap?
static int frogface; // which way is the frog facing, in furmans for CMD_ROTATE
static int dying; // 0 means not dying, 1-64 animation counter
static long score;
static long hiscore;
static byte lives;
static byte done[5];
static byte homes[5] = { 24, 72, 120, 168, 216 };
static int time;
void frog_start()
{
frogx = 120;
frogy = 232;
leaping = 0;
frogdir = 0;
frogface = 0x0000;
dying = 0;
time = 120 << 7;
}
void level_start()
{
for (byte i = 0; i < 5; i++)
done[i] = 0;
}
void game_start()
{
lives = 4;
score = 0;
}
void game_setup()
{
Control.begin();
game_start();
level_start();
frog_start();
}
static void sprite(byte x, byte y, byte anim, uint16_t rot = 0xffff)
{
x -= 16;
y -= 8;
if (rot != 0xffff) {
GD.cmd_loadidentity();
GD.cmd_translate(F16(8),F16(8));
GD.cmd_rotate(rot);
GD.cmd_translate(F16(-8),F16(-8));
GD.cmd_setmatrix();
}
if (x > 224) {
GD.Cell(anim);
GD.Vertex2f(16 * (x - 256), 16 * y);
} else {
GD.Vertex2ii(x, y, SPRITES_HANDLE, anim);
}
}
static void turtle3(byte x, byte y)
{
byte anim = 50 + ((t / 32) % 3);
sprite(x, y, anim);
sprite(x + 16, y, anim);
sprite(x + 32, y, anim);
}
static void turtle2(byte x, byte y)
{
byte anim = 50 + ((t / 32) % 3);
sprite(x, y, anim);
sprite(x + 16, y, anim);
}
void log1(byte x, byte y)
{
sprite(x, y, 86);
sprite(x + 16, y, 87);
sprite(x + 32, y, 88);
}
void log(byte length, byte x, byte y)
{
sprite(x, y, 86);
while (length--) {
x += 16;
sprite(x, y, 87);
}
sprite(x + 16, y, 88);
}
static int riverat(byte y, uint16_t tt)
{
switch (y) {
case 120: return -tt;
case 104: return tt;
case 88: return 5 * tt / 4;
case 72: return -tt / 2;
case 56: return tt / 2;
}
}
static void squarewave(uint16_t freq, byte amp)
{
GD.wr(REG_VOL_SOUND, amp);
GD.wr16(REG_SOUND, (freq << 8) | 0x01);
GD.wr(REG_PLAY, 1);
}
static void sound()
{
byte note;
if (dying) {
note = 84 - (dying / 2);
squarewave(note, 100);
} else if (leaping) {
if (leaping & 1)
note = 60 + leaping;
else
note = 72 + leaping;
squarewave(note, 100);
} else {
squarewave(0, 0); // silence
}
}
static void rotate_around(int x, int y, int a)
{
GD.cmd_loadidentity();
GD.cmd_translate(F16(x),F16(y));
GD.cmd_rotate(a);
GD.cmd_translate(F16(-x),F16(-y));
GD.cmd_setmatrix();
}
void game_over()
{
GD.wr(REG_VOL_SOUND, 0);
for (byte i = 0; i < 60; i++) {
GD.Clear();
// Draw "F R O G G E R" using the sprites 90-94
int x = 160, y = 50, space = 24;
GD.Begin(BITMAPS);
GD.Vertex2ii(x, y, SPRITES_HANDLE, 90); x += space; // F
GD.Vertex2ii(x, y, SPRITES_HANDLE, 91); x += space; // R
GD.Vertex2ii(x, y, SPRITES_HANDLE, 92); x += space; // O
GD.Vertex2ii(x, y, SPRITES_HANDLE, 93); x += space; // G
GD.Vertex2ii(x, y, SPRITES_HANDLE, 93); x += space; // G
GD.Vertex2ii(x, y, SPRITES_HANDLE, 94); x += space; // E
GD.Vertex2ii(x, y, SPRITES_HANDLE, 91); x += space; // R
GD.cmd_text(240, 136, FONT_HANDLE, OPT_CENTER, "GAME OVER");
if (i == 59)
GD.cmd_text(240, 200, FONT_HANDLE, OPT_CENTER, "PRESS TO PLAY");
GD.swap();
}
while (GD.inputs.x == -32768)
GD.get_inputs();
while (GD.inputs.x != -32768)
GD.get_inputs();
}
void loop()
{
static uint32_t counter;
static int prevt;
GD.Clear();
GD.Tag(1);
GD.BitmapHandle(SPRITES_HANDLE);
GD.SaveContext();
GD.ScissorSize(224, 256);
GD.Begin(BITMAPS);
GD.Vertex2ii(0, 0, BACKGROUND_HANDLE, 0); // Background bitmap
GD.wr(REG_TAG_X, frogx - 8);
GD.wr(REG_TAG_Y, frogy);
GD.Tag(2);
GD.AlphaFunc(GREATER, 0); // on road, don't tag transparent pixels
// Completed homes
for (byte i = 0; i < 5; i++) {
if (done[i])
sprite(homes[i], 40, 63);
}
// Yellow cars
sprite(-t, 216, 3);
sprite(-t + 128, 216, 3);
// Dozers
sprite(t, 200, 4);
sprite(t + 50, 200, 4);
sprite(t + 150, 200, 4);
// Purple cars
sprite(-t, 184, 7);
sprite(-t + 75, 184, 7);
sprite(-t + 150, 184, 7);
// Green and white racecars
sprite(2 * t, 168, 8);
// Trucks
sprite(-t/2, 152, 5);
sprite(-t/2 + 16, 152, 6);
sprite(-t/2 + 100, 152, 5);
sprite(-t/2 + 116, 152, 6);
GD.AlphaFunc(GREATER, 0); // on river, tag transparent pixels
// Turtles
for (int i = 0; i < 256; i += 64)
turtle3(riverat(120, t) + i, 120);
// Short logs
for (int i = 0; i < 240; i += 80)
log(1, riverat(104, t) + i, 104);
// Long logs
for (int i = 0; i < 256; i += 128)
log(5, riverat(88, t) + i, 88);
// Turtles again, but slower
for (int i = 0; i < 250; i += 50)
turtle2(riverat(72, t) + i, 72);
// Top logs
for (int i = 0; i < 210; i += 70)
log(2, riverat(56, t) + i, 56);
// The frog himself, or his death animation
GD.TagMask(0);
if (!dying) {
static byte frog_anim[] = {2, 1, 0, 0, 2};
sprite(frogx, frogy, frog_anim[leaping / 2], frogface);
} else {
static byte die_anim[] = {31, 32, 33, 30};
sprite(frogx, frogy, die_anim[dying / 16], frogface);
}
prevt = t;
t++;
time = max(0, time - 1);
if ((time == 0) && (dying == 0))
dying = 1;
// Draw 'time remaining' by clearing a black rectangle
{
byte tw = 120 - (time >> 7);
byte tx = 72;
GD.SaveContext();
GD.ScissorXY(tx, 248);
GD.ScissorSize(tw, 8);
GD.Clear();
GD.RestoreContext();
}
GD.TagMask(1);
// player control. If button pressed, start the 'leaping' counter
byte con = Control.read();
if (!dying && (leaping == 0) && con) {
frogdir = con;
leaping = 1;
score += 10;
} else if (leaping > 0) {
if (leaping <= 8) {
if (frogdir == CONTROL_LEFT) {
frogx -= 2;
frogface = 0xc000;
} if (frogdir == CONTROL_RIGHT) {
frogx += 2;
frogface = 0x4000;
} if (frogdir == CONTROL_UP) {
frogy -= 2;
frogface = 0x0000;
} if (frogdir == CONTROL_DOWN) {
frogy += 2;
frogface = 0x8000;
}
leaping++;
} else {
leaping = 0;
}
}
GD.RestoreContext();
GD.SaveContext();
GD.Begin(BITMAPS);
#define PADX(x) (480 + (x - 3) * 48)
#define PADY(y) (272 + (y - 3) * 48)
GD.Tag(CONTROL_RIGHT);
GD.Vertex2ii(PADX(2), PADY(1), ARROW_HANDLE, 0);
rotate_around(24, 24, 3 * 0x4000);
GD.Tag(CONTROL_UP);
GD.Vertex2ii(PADX(1), PADY(0), ARROW_HANDLE, 0);
rotate_around(24, 24, 2 * 0x4000);
GD.Tag(CONTROL_LEFT);
GD.Vertex2ii(PADX(0), PADY(1), ARROW_HANDLE, 0);
rotate_around(24, 24, 1 * 0x4000);
GD.Tag(CONTROL_DOWN);
GD.Vertex2ii(PADX(1), PADY(2), ARROW_HANDLE, 0);
GD.RestoreContext();
GD.ColorRGB(255, 85, 0);
draw_score(3, 1, score);
draw_score(11, 1, hiscore);
GD.ColorRGB(255, 255, 255);
for (byte i = 0; i < lives; i++)
GD.Vertex2ii(8 * i, 30 * 8, LIFE_HANDLE, 0);
// for (byte i = 0; i < 16; i++)
// GD.wr(atxy(i, 30), (i < lives) ? BG_LIFE : BG_BLACK);
GD.swap();
GD.get_inputs();
byte tag = GD.rd(REG_TAG);
byte touching = (tag == 2);
if (dying) {
if (++dying == 64) {
if (--lives == 0 || time == 0) {
game_over();
game_start();
level_start();
}
frog_start();
}
}
else if (frogx < 8 || frogx > 224) {
dying = 1;
}
else if (frogy >= 136) { // road section
// if touching something, frog dies
if (tag == 2)
dying = 1;
}
else if (frogy > 40) { // river section
if (!leaping) {
// if touching something, frog is safe
if (tag != 1) {
// move frog according to lane speed
int oldx = riverat(frogy, prevt);
int newx = riverat(frogy, t);
int river_velocity = newx - oldx;
frogx += river_velocity;
} else {
dying = 1;
}
}
}
else
{ // riverbank section
if (!leaping) {
byte landed = 0;
for (byte i = 0; i < 5; i ++) {
if (!done[i] && abs(homes[i] - frogx) < 4) {
done[i] = 1;
landed = 1;
score += 10;
}
}
if (landed) {
if (done[0] && done[1] && done[2] && done[3] && done[4])
level_start();
frog_start();
} else // if frog did not land in a home, die!
dying = 1;
}
}
sound();
hiscore = max(score, hiscore);
}
void setup()
{
GD.begin();
LOAD_ASSETS();
game_setup();
}