123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- #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 ti;
- void frog_start()
- {
- frogx = 120;
- frogy = 232;
- leaping = 0;
- frogdir = 0;
- frogface = 0x0000;
- dying = 0;
- ti = 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++;
- ti = max(0, ti - 1);
- if ((ti == 0) && (dying == 0))
- dying = 1;
-
- // Draw 'time remaining' by clearing a black rectangle
- {
- byte tw = 120 - (ti >> 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 || ti == 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()
- {
- Serial.begin(1000000); // JCB
- GD.begin();
- LOAD_ASSETS();
- game_setup();
- }
|