Frogger game for the Gameduino

Dependencies:   Gameduino mbed

main.cpp

Committer:
TheChrisyd
Date:
2012-12-21
Revision:
3:91b295944b9a
Parent:
2:39f014e52c1c

File content as of revision 3:91b295944b9a:

#include "mbed.h"
#include "GD.h"
#include "frogger_background.h"
#include "frogger_sprite.h"
#include "shield.h"


Serial debug(USBTX, USBRX);
GDClass GD(ARD_MOSI, ARD_MISO, ARD_SCK, ARD_D9, USBTX, USBRX) ;

#define CONTROL_LEFT  1
#define CONTROL_RIGHT 2
#define CONTROL_UP    4
#define CONTROL_DOWN  8
DigitalIn down(ARD_A2);
DigitalIn up(ARD_A3);
DigitalIn left(ARD_A1);
DigitalIn right(ARD_A0);

class Controller {
public:

    void begin() {
        down.mode(PullUp);
        up.mode(PullUp);
        left.mode(PullUp);
        right.mode(PullUp);
        prev = 0;
    }

    byte read() {
        byte r = 0;
        if (!down)
            r |= CONTROL_DOWN;
        if (!up)
            r |= CONTROL_UP;
        if (!left)
            r |= CONTROL_LEFT;
        if (!right)
            r |= CONTROL_RIGHT;
        byte edge = r & ~prev;
        prev = r;
        return edge;
    }
private:
    byte prev;
};

static Controller Control;

#define BG_BLUE   0
#define BG_ZERO   12
#define BG_BLACK  34
#define BG_LIFE   35

static int atxy(byte x, byte y) {
    return RAM_PIC + 64 * y + (x + 11);
}

static void draw_score(uint16_t dst, long n) {
    GD.wr(dst + 0, BG_ZERO + (n / 10000) % 10);   // ten-thousands
    GD.wr(dst + 1, BG_ZERO + (n / 1000) % 10);    // thousands
    GD.wr(dst + 2, BG_ZERO + (n / 100) % 10);     // hundreds
    GD.wr(dst + 3, BG_ZERO + (n / 10) % 10);      // tens
    GD.wr(dst + 4, BG_ZERO + n % 10);             // ones
}

// 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, as a sprite ROT field
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 };

void frog_start() {
    frogx = 120;
    frogy = 232;
    leaping = 0;
    frogdir = 0;
    frogface = 0;
    dying = 0;
}

void level_start() {
    for (byte i = 0; i < 5; i++)
        done[i] = 0;

    GD.begin();
    //wait_ms(500);
    GD.copy(RAM_CHR, froggerbg_chr, sizeof(froggerbg_chr));
    GD.copy(RAM_PAL, froggerbg_pal, sizeof(froggerbg_pal));
    GD.fill(RAM_PIC, BG_BLACK, 4096);
    for (byte y = 0; y < 32; y++)
        GD.copy(atxy(0, y), froggerbg_pic + y * 28, 28);
    GD.fill(1 * 64 + 11, BG_BLUE, 28);

    byte i = 255;
    for (int y = 0; y < 256; y += 16) {
        GD.sprite(i--, 0, y, 63, 0);
        GD.sprite(i--, 16, y, 63, 0);
        GD.sprite(i--, 24, y, 63, 0);
        GD.sprite(i--, 40, y, 63, 0);
        GD.sprite(i--, 56, y, 63, 0);
        GD.sprite(i--, 72, y, 63, 0);
        GD.sprite(i--, 72 + 240, y, 63, 0);
        GD.sprite(i--, 72 + 256, y, 63, 0);
        GD.sprite(i--, 72 + 272, y, 63, 0);
        GD.sprite(i--, 72 + 288, y, 63, 0);
        GD.sprite(i--, 72 + 304, y, 63, 0);
        GD.sprite(i--, 72 + 320, y, 63, 0);
        GD.sprite(i--, 72 + 336, y, 63, 0);
    }

    GD.copy(PALETTE16A, sprite_sprpal, sizeof(sprite_sprpal));
    GD.uncompress(RAM_SPRIMG, sprite_sprimg);
}

void game_start() {
    lives = 4;
    score = 0;
}

void setup() {
    Control.begin();

    game_start();
    level_start();
    frog_start();
}

static void sprite(byte x, byte y, byte anim, byte rot = 0) {
    draw_sprite(x + 80, y, anim, rot);
}

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;
        default:
            return 0;
    }
}

// midi frequency table
static int midifreq[128] = {
    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
};

//#define MIDI(n) pgm_read_word(midifreq + (n))

static void squarewave(uint16_t freq, byte amp) {
    GD.voice(0, 0, freq,     amp,    amp);
    GD.voice(1, 0, 3 * freq, amp/3,  amp/3);
    GD.voice(2, 0, 5 * freq, amp/5,  amp/5);
    GD.voice(3, 0, 7 * freq, amp/7,  amp/7);
    GD.voice(4, 0, 9 * freq, amp/9,  amp/9);
    GD.voice(5, 0, 11 * freq, amp/11,  amp/11);
}

static void sound() {
    byte note;

    if (dying) {
        note = 84 - (dying / 2);
        squarewave(midifreq[note], 100);
    } else if (leaping) {
        if (leaping & 1)
            note = 60 + leaping;
        else
            note = 72 + leaping;
        squarewave(midifreq[note], 100);
    } else {
        squarewave(0, 0);  // silence
    }
}

void loop() {
    GD.__wstartspr(0);

    // 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);

    // 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
    byte frogspr = GD.spr; // record which sprite slot frog got, for collision check below

    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);
    }

    GD.__end();
    t++;

    // 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 = 3;
            }
            if (frogdir == CONTROL_RIGHT) {
                frogx += 2;
                frogface = 5;
            }
            if (frogdir == CONTROL_UP) {
                frogy -= 2;
                frogface = 0;
            }
            if (frogdir == CONTROL_DOWN) {
                frogy += 2;
                frogface = 6;
            }
            leaping++;
        } else {
            leaping = 0;
        }
    }

    GD.waitvblank();
    byte touching = (GD.rd(COLLISION + frogspr) != 0xff);

    if (dying) {
        if (++dying == 64) {
            if (--lives == 0) {
                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 (touching)
            dying = 1;
    } else if (frogy > 40) {     // river section
        if (!leaping) {
            // if touching something, frog is safe
            if (touching) {
                // move frog according to lane speed
                int oldx = riverat(frogy, t - 1);
                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();
    if (score > hiscore) {
        hiscore = score;
    }
    draw_score(atxy(3, 1), score);
    draw_score(atxy(11, 1), hiscore);
    for (byte i = 0; i < 16; i++)
        GD.wr(atxy(i, 30), (i < lives) ? BG_LIFE : BG_BLACK);
}


int main() {
    setup();
    while (1) {
        loop();
    }
}