A simple game

Dependencies:   4DGL-uLCD-SE mbed-rtos mbed

Fork of rtos_signals by mbed official

main.cpp

Committer:
gmiles3
Date:
2016-11-04
Revision:
4:2bb5deb83b81
Parent:
1:6a8fcc666593

File content as of revision 4:2bb5deb83b81:

#include "mbed.h"
#include "rtos.h"
#include <mpr121.h>
#include "uLCD_4DGL.h"
#include "Speaker.h"

uLCD_4DGL uLCD(p9,p10,p11);
int curr_x_u, curr_y_u, old_x_u, old_y_u; //user's current and old x,y coordinates
int curr_x_e, curr_y_e, old_x_e, old_y_e; //enemy's current and old x,y coordinates
float dx_e, dy_e, fx_e, fy_e; //x,y velocity of enemy and floating point representation of x,y coordinates
int curr_x_b, curr_y_b, old_x_b, old_y_b; //payload's current and old x,y coordinates
int curr_x_p, curr_y_p, old_x_p, old_y_p; //portal's current and old x,y coordinates
Mutex lcd_mutex;

Thread box; //handles user-payload collision logic
Thread portal; //handles portal logic

Speaker amp(p21);

Serial pc(USBTX, USBRX); //debugging

InterruptIn interrupt(p26);
I2C i2c(p28, p27);
Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);

int score = 0;
int oldscore = 0;

volatile int key = -1; //key pressed from keypad; set via interrupts -> volatile so compiler doesn't optimize out value reads

enum State { START, LIVE, PAUSE, DEAD }; //game status
volatile State s = START;

//interrupt method that runs when keypad has a new value
void fallInterrupt() {
    int key_code=0;
    int i=0;
    int value=mpr121.read(0x00);
    value +=mpr121.read(0x01)<<8;
    i=0;
    for (i=0; i<12; i++) {
        if (((value>>i)&0x01)==1) key_code=i+1;
    }
    key = key_code;
}

//handles portal logic
void portal_thread() {
    while(s == LIVE || s == PAUSE) {
        while(s == PAUSE);
        //if payload and portal collide, score a point and randomize portal location
        if (((curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b) || (curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b)) && ((curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b) || (curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b))) {
            score++;
            curr_x_p = (rand() % 106) + 10;
            curr_y_p = (rand() % 106) + 10;
        } else if (((curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b) || (curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b)) && ((curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b) || (curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b))) {
            score++;
            curr_x_p = (rand() % 106) + 10;
            curr_y_p = (rand() % 106) + 10;
        }
        lcd_mutex.lock();
        //redraw portal and play note if score has changed
        uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
        if (score != oldscore) {
            amp.PlayNote(1000.0,0.025,0.5);
            oldscore = score;
            uLCD.rectangle(old_x_p-5, old_y_p-5, old_x_p+5, old_y_p+5, BLACK);
            old_x_p = curr_x_p;
            old_y_p = curr_y_p;
        }
        //print score
        uLCD.locate(1,15);
        uLCD.color(0xFF00FF);
        uLCD.printf("%d", score);
        lcd_mutex.unlock();
    }
}

//handles user-payload collisions
void box_thread() {
    while(s == LIVE) {
        old_x_u = curr_x_u;
        old_y_u = curr_y_u;
        old_x_b = curr_x_b;
        old_y_b = curr_y_b;
        //if 3 pressed, then go into pause state
        if (key==3+1) {
            s = PAUSE;
            lcd_mutex.lock();
            uLCD.locate(1, 2);
            uLCD.color(0xFFFF00);
            uLCD.printf("PAUSE");
            //wait until 3 is unpressed
            while(key==3+1);
            //wait until 3 is pressed
            while(key!=3+1);
            //wait until 3 is unpressed
            while(key==3+1);
            uLCD.locate(1, 2);
            uLCD.color(BLACK);
            uLCD.printf("PAUSE");
            uLCD.color(WHITE);
            //countdown from 3
            for (int i=3; i>=1; i--) {
                uLCD.locate(1, 2);
                uLCD.printf("%2D", i);
                amp.PlayNote(800.0,0.025,0.5);
                wait(1);
            }
            uLCD.locate(1,2);
            uLCD.color(BLACK);
            uLCD.printf("  ");
            lcd_mutex.unlock();
            //play on
            s = LIVE;
        }
        //really complicated collision logic
        //basically checks each way the boxes could be colliding based on which key is pressed (which direction user is moving)
        if (key==0+1) {

            if ((curr_x_b+6 > curr_x_u-6 && curr_x_u > curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
                curr_x_b-=1;
                curr_x_u = curr_x_b+12;
                if (curr_x_b < 7) {
                    curr_x_b = 25;
                    curr_y_b = 63;
                }
            } else {
                curr_x_u-=1;
            }
            if (curr_x_u < 7) curr_x_u = 7;
        } else if (key==8+1) {

            if ((curr_x_b-6 < curr_x_u+6 && curr_x_u < curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
                curr_x_b+=1;
                curr_x_u = curr_x_b-12;
                if (curr_x_b > 120) {
                    curr_x_b = 25;
                    curr_y_b = 63;
                }
            } else {
                curr_x_u+=1;
            }
            if (curr_x_u > 120) curr_x_u = 120;
        } else if (key==4+1) {

            if ((curr_y_b-6 < curr_y_u+6 && curr_y_u < curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
                curr_y_b+=1;
                curr_y_u = curr_y_b-12;
                if (curr_y_b > 120) {
                    curr_x_b = 25;
                    curr_y_b = 63;
                }
            } else {
                curr_y_u+=1;
            }
            if (curr_y_u > 120) curr_y_u = 120;
        } else if (key==5+1) {

            if ((curr_y_b+6 > curr_y_u-6 && curr_y_u > curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
                curr_y_b-=1;
                curr_y_u = curr_y_b+12;
                if (curr_y_b < 7) {
                    curr_x_b = 25;
                    curr_y_b = 63;
                }
            } else {
                curr_y_u-=1;
            }
            if (curr_y_u < 7) curr_y_u = 7;
        }
        //redraw user and payload
        lcd_mutex.lock();
        uLCD.rectangle(20, 58, 30, 68, WHITE);
        uLCD.filled_rectangle(old_x_u-5, old_y_u-5, old_x_u+5, old_y_u+5, BLACK);
        uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
        uLCD.filled_rectangle(old_x_b-5, old_y_b-5, old_x_b+5, old_y_b+5, BLACK);
        uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
        lcd_mutex.unlock();
    }
}

//main thread handles setup of game, angry ball collision logic, gameover, and game resets
int main() {
    //setup keypad interrupts
    interrupt.fall(&fallInterrupt);
    interrupt.mode(PullUp);
    uLCD.baudrate(3000000);
    //load in Angry Balls image from SD
    uLCD.media_init();
    uLCD.set_sector_address(0x003B, 0xD400);
    uLCD.display_image(0,0);
    //wait 3 seconds to allow keypad time to set up while player can look at the pretty image
    wait(3);
    uLCD.textbackground_color(WHITE);
    uLCD.text_width(1);
    uLCD.text_height(1);
    uLCD.color(RED);
    uLCD.locate(0, 0);
    uLCD.printf("Angry Balls!");
    uLCD.locate(0, 1);
    uLCD.color(BLACK);
    uLCD.printf("Press 3 to play");
    //start tone
    amp.PlayNote(700.0,0.2,0.5);
    amp.PlayNote(1000.0,0.2,0.0);
    amp.PlayNote(600.0,0.1,0.5);
    amp.PlayNote(1000.0,0.1,0.0);
    amp.PlayNote(700.0,0.5,0.5);
    //wait until 3 is pressed
    while(key != 3+1);
    //wait until 3 is unpressed
    while(key == 3+1);
    //clear screen
    uLCD.cls();
    uLCD.textbackground_color(BLACK);
    uLCD.background_color(BLACK);
    //setup initial state for every object
    curr_x_u = 9;
    curr_y_u = 63;
    curr_x_b = 25;
    curr_y_b = 63;
    curr_x_e = 75;
    curr_y_e = 75;
    dx_e = -0.75;
    dy_e = 0.75;
    curr_x_p = 110;
    curr_y_p = 15;
    old_x_p = curr_x_p;
    old_y_p = curr_y_p;
    //draw everything
    uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
    uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
    uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
    uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
    uLCD.color(WHITE);
    uLCD.locate(1,2);
    uLCD.printf("Place ball!");
    //logic for placing ball before game starts based on key presses; prevents ball from going through walls & middle boundary
    while(key != 3+1) {
        old_x_e = curr_x_e;
        old_y_e = curr_y_e;
        if (key==0+1) {
            curr_x_e-=2;
            if (curr_x_e < 71) curr_x_e = 71;
        }
        if (key==8+1) {
            curr_x_e+=2;
            if (curr_x_e > 120) curr_x_e = 120;
        }
        if (key==4+1) {
            curr_y_e+=2;
            if (curr_y_e > 120) curr_y_e = 120;
        }
        if (key==5+1) {
            curr_y_e-=2;
            if (curr_y_e < 7) curr_y_e = 7;
        }
        //draw ball
        uLCD.locate(1,2);
        uLCD.printf("Place ball!");
        uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
        uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
        uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
    }
    //wait until 3 is unpressed
    while(key==3+1);
    uLCD.locate(1,2);
    uLCD.color(BLACK);
    uLCD.printf("Place ball!");
    fx_e = curr_x_e;
    fy_e = curr_y_e;
    uLCD.color(WHITE);
    //countdown til start
    for (int i=3; i>=1; i--) {
        uLCD.locate(1, 2);
        uLCD.printf("%2D", i);
        amp.PlayNote(800.0,0.025,0.5);
        wait(1);
    }
    uLCD.locate(1,2);
    uLCD.color(BLACK);
    uLCD.printf("  ");
    //game is live, start other two threads
    s = LIVE;
    box.start(box_thread);
    portal.start(portal_thread);
    //collision logic for angry ball
    //check if ball has collided with a wall, the user, or the payload (gameover)
    while(s == LIVE || s == PAUSE) {
        while (s==PAUSE);
        old_x_e = curr_x_e;
        old_y_e = curr_y_e;
        if (curr_x_e < 7) {
            curr_x_e = 7;
            dx_e = -dx_e;
        } else if (curr_x_e > 120) {
            curr_x_e = 120;
            dx_e = -dx_e;
        } else if (curr_y_e > 120) {
            curr_y_e = 120;
            dy_e = -dy_e;
        } else if (curr_y_e < 7) {
            curr_y_e = 7;
            dy_e = -dy_e;
        } else if (((dx_e < 0 && curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u) || (dx_e > 0 && curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u)) && ((curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u) || (curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u))) {
            dx_e = -dx_e;
        } else if (((dy_e < 0 && curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u) || (dy_e > 0 && curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u)) && ((curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u) || (curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u))) {
            dy_e = -dy_e;
        }
        if (((dx_e < 0 && curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b) || (dx_e > 0 && curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b)) && ((curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b) || (curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b))) {
            s = DEAD;
        } else if (((dy_e < 0 && curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b) || (dy_e > 0 && curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b)) && ((curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b) || (curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b))) {
            s = DEAD;
        }
        //update floating point coordinates with velocity values
        fx_e += dx_e;
        fy_e += dy_e;
        //cast floating coordinates to integer coordinates
        curr_x_e = (int) fx_e;
        curr_y_e = (int) fy_e;
        //redraw ball
        lcd_mutex.lock();
        uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
        uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
        lcd_mutex.unlock();
        //if ball-payload collision has occurred, game is now dead
        if (s == DEAD) {
            //print gameover, score, instructions to start over, and play gameover sound
            lcd_mutex.lock();
            uLCD.cls();
            uLCD.locate(1,2);
            uLCD.color(RED);
            uLCD.printf("GAMEOVER");
            uLCD.locate(1,4);
            uLCD.color(0x4B0082);
            uLCD.printf("Score:%d", score);
            uLCD.locate(1,8);
            uLCD.color(WHITE);
            uLCD.printf("Press 3 to retry");
            amp.PlayNote(300.0,0.5,0.5);
            amp.PlayNote(300.0,0.05,0.0);
            amp.PlayNote(280.0,0.5,0.5);
            amp.PlayNote(300.0,0.05,0.0);
            amp.PlayNote(260.0,1.0,0.5);
            //wait until 3 is pressed
            while(key != 3+1);
            //wait until 3 is unpressed
            while(key == 3+1);
            //reinitialize game state for new game; same code as above
            uLCD.cls();
            score = 0;
            oldscore = 0;
            curr_x_u = 9;
            curr_y_u = 63;
            curr_x_b = 25;
            curr_y_b = 63;
            curr_x_e = 75;
            curr_y_e = 75;
            dx_e = -0.75;
            dy_e = 0.75;
            curr_x_p = 110;
            curr_y_p = 15;
            old_x_p = curr_x_p;
            old_y_p = curr_y_p;
            uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
            uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
            uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
            uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
            uLCD.color(WHITE);
            uLCD.locate(1,2);
            uLCD.printf("Place ball!");
            while(key != 3+1) {
                old_x_e = curr_x_e;
                old_y_e = curr_y_e;
                if (key==0+1) {
                    curr_x_e-=2;
                    if (curr_x_e < 71) curr_x_e = 71;
                }
                if (key==8+1) {
                    curr_x_e+=2;
                    if (curr_x_e > 120) curr_x_e = 120;
                }
                if (key==4+1) {
                    curr_y_e+=2;
                    if (curr_y_e > 120) curr_y_e = 120;
                }
                if (key==5+1) {
                    curr_y_e-=2;
                    if (curr_y_e < 7) curr_y_e = 7;
                }
                uLCD.locate(1,2);
                uLCD.printf("Place ball!");
                uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
                uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
                uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
            }
            while(key==3+1);
            uLCD.locate(1,2);
            uLCD.color(BLACK);
            uLCD.printf("Place ball!");
            fx_e = curr_x_e;
            fy_e = curr_y_e;
            uLCD.color(WHITE);
            for (int i=3; i>=1; i--) {
                uLCD.locate(1, 2);
                uLCD.printf("%2D", i);
                amp.PlayNote(800.0,0.025,0.5);
                wait(1);
            }
            uLCD.locate(1,2);
            uLCD.color(BLACK);
            uLCD.printf("  ");
            lcd_mutex.unlock();
            //game is live again
            //restart the other threads
            s = LIVE;
            box.start(box_thread);
            portal.start(portal_thread);
        }
    }
}