Frogger game for the Gameduino

Dependencies:   Gameduino mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "GD.h"
00003 #include "frogger_background.h"
00004 #include "frogger_sprite.h"
00005 #include "shield.h"
00006 
00007 
00008 Serial debug(USBTX, USBRX);
00009 GDClass GD(ARD_MOSI, ARD_MISO, ARD_SCK, ARD_D9, USBTX, USBRX) ;
00010 
00011 #define CONTROL_LEFT  1
00012 #define CONTROL_RIGHT 2
00013 #define CONTROL_UP    4
00014 #define CONTROL_DOWN  8
00015 DigitalIn down(ARD_A2);
00016 DigitalIn up(ARD_A3);
00017 DigitalIn left(ARD_A1);
00018 DigitalIn right(ARD_A0);
00019 
00020 class Controller {
00021 public:
00022 
00023     void begin() {
00024         down.mode(PullUp);
00025         up.mode(PullUp);
00026         left.mode(PullUp);
00027         right.mode(PullUp);
00028         prev = 0;
00029     }
00030 
00031     byte read() {
00032         byte r = 0;
00033         if (!down)
00034             r |= CONTROL_DOWN;
00035         if (!up)
00036             r |= CONTROL_UP;
00037         if (!left)
00038             r |= CONTROL_LEFT;
00039         if (!right)
00040             r |= CONTROL_RIGHT;
00041         byte edge = r & ~prev;
00042         prev = r;
00043         return edge;
00044     }
00045 private:
00046     byte prev;
00047 };
00048 
00049 static Controller Control;
00050 
00051 #define BG_BLUE   0
00052 #define BG_ZERO   12
00053 #define BG_BLACK  34
00054 #define BG_LIFE   35
00055 
00056 static int atxy(byte x, byte y) {
00057     return RAM_PIC + 64 * y + (x + 11);
00058 }
00059 
00060 static void draw_score(uint16_t dst, long n) {
00061     GD.wr(dst + 0, BG_ZERO + (n / 10000) % 10);   // ten-thousands
00062     GD.wr(dst + 1, BG_ZERO + (n / 1000) % 10);    // thousands
00063     GD.wr(dst + 2, BG_ZERO + (n / 100) % 10);     // hundreds
00064     GD.wr(dst + 3, BG_ZERO + (n / 10) % 10);      // tens
00065     GD.wr(dst + 4, BG_ZERO + n % 10);             // ones
00066 }
00067 
00068 // Game variables
00069 static unsigned int t;
00070 static int frogx, frogy;    // screen position
00071 static int leaping;         // 0 means not leaping, 1-8 animates the leap
00072 static int frogdir;         // while leaping, which direction is the leap?
00073 static int frogface;        // which way is the frog facing, as a sprite ROT field
00074 static int dying;           // 0 means not dying, 1-64 animation counter
00075 static long score;
00076 static long hiscore;
00077 static byte lives;
00078 static byte done[5];
00079 static byte homes[5] = { 24, 72, 120, 168, 216 };
00080 
00081 void frog_start() {
00082     frogx = 120;
00083     frogy = 232;
00084     leaping = 0;
00085     frogdir = 0;
00086     frogface = 0;
00087     dying = 0;
00088 }
00089 
00090 void level_start() {
00091     for (byte i = 0; i < 5; i++)
00092         done[i] = 0;
00093 
00094     GD.begin();
00095     //wait_ms(500);
00096     GD.copy(RAM_CHR, froggerbg_chr, sizeof(froggerbg_chr));
00097     GD.copy(RAM_PAL, froggerbg_pal, sizeof(froggerbg_pal));
00098     GD.fill(RAM_PIC, BG_BLACK, 4096);
00099     for (byte y = 0; y < 32; y++)
00100         GD.copy(atxy(0, y), froggerbg_pic + y * 28, 28);
00101     GD.fill(1 * 64 + 11, BG_BLUE, 28);
00102 
00103     byte i = 255;
00104     for (int y = 0; y < 256; y += 16) {
00105         GD.sprite(i--, 0, y, 63, 0);
00106         GD.sprite(i--, 16, y, 63, 0);
00107         GD.sprite(i--, 24, y, 63, 0);
00108         GD.sprite(i--, 40, y, 63, 0);
00109         GD.sprite(i--, 56, y, 63, 0);
00110         GD.sprite(i--, 72, y, 63, 0);
00111         GD.sprite(i--, 72 + 240, y, 63, 0);
00112         GD.sprite(i--, 72 + 256, y, 63, 0);
00113         GD.sprite(i--, 72 + 272, y, 63, 0);
00114         GD.sprite(i--, 72 + 288, y, 63, 0);
00115         GD.sprite(i--, 72 + 304, y, 63, 0);
00116         GD.sprite(i--, 72 + 320, y, 63, 0);
00117         GD.sprite(i--, 72 + 336, y, 63, 0);
00118     }
00119 
00120     GD.copy(PALETTE16A, sprite_sprpal, sizeof(sprite_sprpal));
00121     GD.uncompress(RAM_SPRIMG, sprite_sprimg);
00122 }
00123 
00124 void game_start() {
00125     lives = 4;
00126     score = 0;
00127 }
00128 
00129 void setup() {
00130     Control.begin();
00131 
00132     game_start();
00133     level_start();
00134     frog_start();
00135 }
00136 
00137 static void sprite(byte x, byte y, byte anim, byte rot = 0) {
00138     draw_sprite(x + 80, y, anim, rot);
00139 }
00140 
00141 static void turtle3(byte x, byte y) {
00142     byte anim = 50 + ((t / 32) % 3);
00143     sprite(x, y, anim);
00144     sprite(x + 16, y, anim);
00145     sprite(x + 32, y, anim);
00146 }
00147 
00148 static void turtle2(byte x, byte y) {
00149     byte anim = 50 + ((t / 32) % 3);
00150     sprite(x, y, anim);
00151     sprite(x + 16, y, anim);
00152 }
00153 
00154 void log1(byte x, byte y) {
00155     sprite(x, y,      86);
00156     sprite(x + 16, y, 87);
00157     sprite(x + 32, y, 88);
00158 }
00159 
00160 void log(byte length, byte x, byte y) {
00161     sprite(x, y,      86);
00162     while (length--) {
00163         x += 16;
00164         sprite(x, y, 87);
00165     }
00166     sprite(x + 16, y, 88);
00167 }
00168 
00169 static int riverat(byte y, uint16_t tt) {
00170     switch (y) {
00171         case 120:
00172             return -tt;
00173         case 104:
00174             return tt;
00175         case 88:
00176             return 5 * tt / 4;
00177         case 72:
00178             return -tt / 2;
00179         case 56:
00180             return tt / 2;
00181         default:
00182             return 0;
00183     }
00184 }
00185 
00186 // midi frequency table
00187 static int midifreq[128] = {
00188     32,34,36,38,41,43,46,48,51,55,58,61,65,69,73,77,82,87,92,97,103,110,116,123,130,138,146,155,164,174,184,195,207,220,233,246,261,277,293,311,329,349,369,391,415,440,466,493,523,554,587,622,659,698,739,783,830,880,932,987,1046,1108,1174,1244,1318,1396,1479,1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,8869,9397,9956,10548,11175,11839,12543,13289,14080,14917,15804,16744,17739,18794,19912,21096,22350,23679,25087,26579,28160,29834,31608,33488,35479,37589,39824,42192,44701,47359,50175
00189 };
00190 
00191 //#define MIDI(n) pgm_read_word(midifreq + (n))
00192 
00193 static void squarewave(uint16_t freq, byte amp) {
00194     GD.voice(0, 0, freq,     amp,    amp);
00195     GD.voice(1, 0, 3 * freq, amp/3,  amp/3);
00196     GD.voice(2, 0, 5 * freq, amp/5,  amp/5);
00197     GD.voice(3, 0, 7 * freq, amp/7,  amp/7);
00198     GD.voice(4, 0, 9 * freq, amp/9,  amp/9);
00199     GD.voice(5, 0, 11 * freq, amp/11,  amp/11);
00200 }
00201 
00202 static void sound() {
00203     byte note;
00204 
00205     if (dying) {
00206         note = 84 - (dying / 2);
00207         squarewave(midifreq[note], 100);
00208     } else if (leaping) {
00209         if (leaping & 1)
00210             note = 60 + leaping;
00211         else
00212             note = 72 + leaping;
00213         squarewave(midifreq[note], 100);
00214     } else {
00215         squarewave(0, 0);  // silence
00216     }
00217 }
00218 
00219 void loop() {
00220     GD.__wstartspr(0);
00221 
00222     // Completed homes
00223     for (byte i = 0; i < 5; i++) {
00224         if (done[i])
00225             sprite(homes[i], 40, 63);
00226     }
00227 
00228     // Yellow cars
00229     sprite(-t,       216, 3);
00230     sprite(-t + 128, 216, 3);
00231 
00232     // Dozers
00233     sprite(t, 200, 4);
00234     sprite(t + 50, 200, 4);
00235     sprite(t + 150, 200, 4);
00236 
00237     // Purple cars
00238     sprite(-t,       184, 7);
00239     sprite(-t + 75,  184, 7);
00240     sprite(-t + 150, 184, 7);
00241 
00242     // Green and white racecars
00243     sprite(2 * t,    168, 8);
00244 
00245     // Trucks
00246     sprite(-t/2,       152, 5);
00247     sprite(-t/2 + 16,  152, 6);
00248     sprite(-t/2 + 100, 152, 5);
00249     sprite(-t/2 + 116, 152, 6);
00250 
00251     // Turtles
00252     for (int i = 0; i < 256; i += 64)
00253         turtle3(riverat(120, t) + i, 120);
00254 
00255     // Short logs
00256     for (int i = 0; i < 240; i += 80)
00257         log(1, riverat(104, t) + i, 104);
00258 
00259     // Long logs
00260     for (int i = 0; i < 256; i += 128)
00261         log(5, riverat(88, t) + i, 88);
00262 
00263     // Turtles again, but slower
00264     for (int i = 0; i < 250; i += 50)
00265         turtle2(riverat(72, t) + i, 72);
00266 
00267     // Top logs
00268     for (int i = 0; i < 210; i += 70)
00269         log(2, riverat(56, t) + i, 56);
00270 
00271     // The frog himself, or his death animation
00272     byte frogspr = GD.spr; // record which sprite slot frog got, for collision check below
00273 
00274     if (!dying) {
00275         static byte frog_anim[] = {2, 1, 0, 0, 2};
00276         sprite(frogx, frogy, frog_anim[leaping / 2], frogface);
00277     } else {
00278         static byte die_anim[] = {31, 32, 33, 30};
00279         sprite(frogx, frogy, die_anim[dying / 16], frogface);
00280     }
00281 
00282     GD.__end();
00283     t++;
00284 
00285     // player control.  If button pressed, start the 'leaping' counter
00286     byte con = Control.read();
00287     if (!dying && (leaping == 0) && con) {
00288         frogdir = con;
00289         leaping = 1;
00290         score += 10;
00291     } else if (leaping > 0) {
00292         if (leaping <= 8) {
00293             if (frogdir == CONTROL_LEFT) {
00294                 frogx -= 2;
00295                 frogface = 3;
00296             }
00297             if (frogdir == CONTROL_RIGHT) {
00298                 frogx += 2;
00299                 frogface = 5;
00300             }
00301             if (frogdir == CONTROL_UP) {
00302                 frogy -= 2;
00303                 frogface = 0;
00304             }
00305             if (frogdir == CONTROL_DOWN) {
00306                 frogy += 2;
00307                 frogface = 6;
00308             }
00309             leaping++;
00310         } else {
00311             leaping = 0;
00312         }
00313     }
00314 
00315     GD.waitvblank();
00316     byte touching = (GD.rd(COLLISION + frogspr) != 0xff);
00317 
00318     if (dying) {
00319         if (++dying == 64) {
00320             if (--lives == 0) {
00321                 game_start();
00322                 level_start();
00323             }
00324             frog_start();
00325         }
00326     } else if (frogx < 8 || frogx > 224) {
00327         dying = 1;
00328     } else if (frogy >= 136) {   // road section
00329         // if touching something, frog dies
00330         if (touching)
00331             dying = 1;
00332     } else if (frogy > 40) {     // river section
00333         if (!leaping) {
00334             // if touching something, frog is safe
00335             if (touching) {
00336                 // move frog according to lane speed
00337                 int oldx = riverat(frogy, t - 1);
00338                 int newx = riverat(frogy, t);
00339                 int river_velocity = newx - oldx;
00340                 frogx += river_velocity;
00341             } else {
00342                 dying = 1;
00343             }
00344         }
00345     } else {                     // riverbank section
00346         if (!leaping) {
00347             byte landed = 0;
00348             for (byte i = 0; i < 5; i ++) {
00349                 if (!done[i] && abs(homes[i] - frogx) < 4) {
00350                     done[i] = 1;
00351                     landed = 1;
00352                     score += 10;
00353                 }
00354             }
00355             if (landed) {
00356                 if (done[0] && done[1] && done[2] && done[3] && done[4])
00357                     level_start();
00358                 frog_start();
00359             } else // if frog did not land in a home, die!
00360                 dying = 1;
00361         }
00362     }
00363     sound();
00364     if (score > hiscore) {
00365         hiscore = score;
00366     }
00367     draw_score(atxy(3, 1), score);
00368     draw_score(atxy(11, 1), hiscore);
00369     for (byte i = 0; i < 16; i++)
00370         GD.wr(atxy(i, 30), (i < lives) ? BG_LIFE : BG_BLACK);
00371 }
00372 
00373 
00374 int main() {
00375     setup();
00376     while (1) {
00377         loop();
00378     }
00379 }
00380