Mini game developed for ECE 4180 lab

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

main.cpp

Committer:
slandry8
Date:
2016-03-14
Revision:
0:a358215f57b7

File content as of revision 0:a358215f57b7:

// Cactus Jumper video game
//
#include "mbed.h"
#include "rtos.h"
#include "uLCD_4DGL.h"
#include "entity.h"


uLCD_4DGL uLCD(p9,p10,p11); // serial tx, serial rx, reset pin;
Mutex posMutex;
int state = STATE_START;
int score;
int k;
char *scCompBuf;
char *writtenScore;
char *scoreStr;
bool initialFlash = false;
bool scoreDrawn = false;
bool collided = false;
bool newHighScore = false;
bool titleDrawn = false;
char readchar = ' ';
class Nav_Switch
{
public:
    Nav_Switch(PinName up,PinName down,PinName left,PinName right,PinName fire);
    int read();
//boolean functions to test each switch
    bool up();
    bool down();
    bool left();
    bool right();
    bool fire();
//automatic read on RHS
    operator int ();
//index to any switch array style
    bool operator[](int index) {
        return _pins[index];
    };
private:
    BusIn _pins;
 
};
Nav_Switch::Nav_Switch (PinName up,PinName down,PinName left,PinName right,PinName fire):
    _pins(up, down, left, right, fire)
{
    _pins.mode(PullUp); //needed if pullups not on board or a bare nav switch is used - delete otherwise
    //Thread::wait(1); //delays just a bit for pullups to pull inputs high
    wait(0.001);
}
inline bool Nav_Switch::up()
{
    return !(_pins[0]);
}
inline bool Nav_Switch::down()
{
    return !(_pins[1]);
}
inline bool Nav_Switch::left()
{
    return !(_pins[2]);
}
inline bool Nav_Switch::right()
{
    return !(_pins[3]);
}
inline bool Nav_Switch::fire()
{
    return !(_pins[4]);
}
inline int Nav_Switch::read()
{
    return _pins.read();
}
inline Nav_Switch::operator int ()
{
    return _pins.read();
}
 
Nav_Switch myNav( p25, p22, p23, p21, p24);
int i = 111; //variable used for horizontal player movement
int j = 96; //variable used for vertical player movement
//initialize player and cursor 
Player *player = new Player();
Cursor *curs = new Cursor();
//thread for player input detection
void fire_thread(void const *args) {
    while(true) {
        //if player moves to the right during gameplay
        if(myNav.right() && state == STATE_GAME) {
             posMutex.lock();
            i = ((i > 110) ? 110 : i + DELTA_X_P);
            (*player).col = i;
            (*player).hasMoved = true;
            (*player).moveFlag = 2;
             posMutex.unlock();
             Thread::wait(35);
        }
        //if player moves to the left during gameplay
        if(myNav.left() && state == STATE_GAME) {
            posMutex.lock();
            i = ((i < 2) ? 2 : i - DELTA_X_P);
            (*player).col = i;
            (*player).hasMoved = true;
            (*player).moveFlag = 1;
            posMutex.unlock();
            Thread::wait(35);
        }
        posMutex.lock();
        //if player jumps during gameplay and is not already in the jumping or falling state
        if((myNav.up() && !((*player).isJumping || (*player).isFalling)) && state == STATE_GAME) {
            (*player).isJumping = true;
            (*player).upCounter = 48;
            (*player).fallCounter = 48;
        }
        posMutex.unlock();
        //if fire is pressed on start screen, begin game
        if(myNav.fire() && state != STATE_GAME && state != STATE_SCORE && state != STATE_LOSE) {
            state = STATE_GAME;
            Thread::wait(35);
        }
        //Go to either start screen or new height screen when fire is pressed on game over screen
        if(myNav.fire() && state == STATE_LOSE) {
            if(!newHighScore) {
                titleDrawn = false;
                state = STATE_START;
            } else {
                state = STATE_SCORE;
            }
            Thread::wait(500);
        }
        //Navigation controls for high score screen
        //if fire is pressed on the new high score screen
        if(myNav.fire() && state == STATE_SCORE) {
            //check whether to enter an initial or begin writing initials to file
            if((*curs).curChar >= 'A' && (*curs).curChar <= 'Z') {
                (*curs).initBuf[(*curs).c] = (*curs).curChar;
            }
            (*curs).letterPressed = true;
            Thread::wait(200);
        }
        //If up is pressed on the new high score screen
        if(myNav.up() && state == STATE_SCORE) {
            //make sure cursor would not go out of bounds if moved
            if(!((*curs).curChar - 8 < 'A')) {
            (*curs).row -= (*curs).delta_y;
            (*curs).hasMoved = true;
            (*curs).moveFlag = 4;
            (*curs).curChar-=8;
            }
            Thread::wait(100);
        }
        //If down is pressed on the new high score screen
        if(myNav.down() && state == STATE_SCORE) {
            //make sure cursor would not go out of boudns if moved
            if(!((*curs).curChar + 8 > '[')) {
            (*curs).row += (*curs).delta_y;
            (*curs).hasMoved = true;
            (*curs).moveFlag = 8;
            (*curs).curChar+=8;
            }
            Thread::wait(100);
        }
        //If left is pressed on the new high score screen
        if(myNav.left() && state == STATE_SCORE) {
            //make sure cursor would not go out of boudns if moved
            if(!((*curs).curChar == 'A') && !((*curs).curChar == 'I') && !((*curs).curChar == 'Q') && !((*curs).curChar == 'Y')) {
            (*curs).col -= (*curs).delta_x;
            (*curs).hasMoved = true;
            (*curs).moveFlag = 1;
            (*curs).curChar--;
            }
            Thread::wait(100);
        }
        //If right is pressed on the new high score screen
        if(myNav.right() && state == STATE_SCORE) {
            //make sure cursor would not go out of boudns if moved
            if(!((*curs).curChar == 'H') && !((*curs).curChar == 'P') && !((*curs).curChar == 'X') && !((*curs).curChar == '[')) {
            (*curs).col += (*curs).delta_x;
            (*curs).hasMoved = true;
            (*curs).moveFlag = 2;
            (*curs).curChar++;
            }
            Thread::wait(100);
        }
    }
}
int main()
{
        //create thread for handling user input
        Thread thread(fire_thread);
        //initialize screen pointer array; allocate screen objects in memory
        Screen *screenP[2];
        screenP[0] = new Screen(0x780D,true,-127);
        screenP[1] = new Screen(0x780D,true,0);
        //set baud rate & initialize media on uLCD
        uLCD.baudrate(3000000);
        uLCD.media_init();
        while(true) {
            if(!(state | STATE_START)) {
                //start screen
                if(!titleDrawn) { //we want to only draw this information once
                    newHighScore = false;
                    //initialize game information
                    initialize(screenP, player, &score, &initialFlash, &scoreDrawn, &collided, &i, &j, curs);
                    //draw title screen and text
                    uLCD.set_sector_address(0x001D, 0x78D9);
                    uLCD.display_image(0,0);
                    uLCD.textbackground_color(0x8f7738);
                    uLCD.locate(3,12);
                    uLCD.color(RED);
                    uLCD.printf("Press fire");
                    uLCD.locate(4,13);
                    uLCD.printf("to start");
                    uLCD.background_color(COVER_COLOR);
                    //read in high score to display
                    uLCD.set_sector_address(0x001D, 0x8700);
                    uLCD.locate(3,15);
                    readchar = ' ';
                    while(readchar != '\xFF') {
                        readchar = uLCD.read_byte();
                        uLCD.putc(readchar);
                    }
                }
                titleDrawn = true;
                score = 0;
                //The remainder of this if() causes the "blinking" of the "Press fire to start" on the title screen
                Thread::wait(250);
                uLCD.locate(3,12);
                uLCD.color(0x8f7738);
                uLCD.printf("Press fire");
                uLCD.locate(4,13);
                uLCD.printf("to start");
                Thread::wait(250);
                uLCD.locate(3,12);
                uLCD.color(RED);
                uLCD.printf("Press fire");
                uLCD.locate(4,13);
                uLCD.printf("to start");
            } else if(state & STATE_GAME) {
                
                if(!initialFlash) {
                    //we want to clear the screen and draw the background scenery only once when the game starts
                    uLCD.filled_rectangle(0,0,127,127,COVER_COLOR);
                    drawScenery(&uLCD);
                    initialFlash = true;
                }
                //draw the obstacles
                drawScreens(&uLCD, screenP);
                posMutex.lock();
                if((*player).isJumping || (*player).isFalling) {
                    //check if end conditions for each state is currently met
                    if(((*player).upCounter < 0) && ((*player).isJumping)) {
                        //if the player has reached the top of their jump
                        (*player).isJumping = false;
                    }
                    if(((*player).fallCounter < 0) && ((*player).isFalling)) {
                        //if the player has stopped falling
                        (*player).isFalling = false;
                    }
                    //check if the player can enter into a new state
                    if(!(*player).isJumping && ((*player).fallCounter > 0) && (!(*player).isFalling)) {
                        //set the falling condition for the player
                        (*player).isFalling = true;
                    }
                //decrement jumping or falling counters, if applicable
                    if((*player).isFalling) {
                        (*player).row+=DELTA_Y;
                        (*player).fallCounter-=DELTA_Y;
                    }
                    if((*player).isJumping) {
                        (*player).row-=DELTA_Y;
                        (*player).upCounter-=DELTA_Y;
                    }
                    drawPlayerAirborneCoverUp(&uLCD, player);
                    if((*player).hasMoved) {
                        drawPlayerDiagCoverUp(&uLCD, player);
                    }
                }
                if((*player).hasMoved) {
                    //cover up residual pixels from previous location of the player if they have moved
                    //drawPlayerCoverUp(&uLCD, player);
                    drawPlayerCoverUp(&uLCD, player, (*player).moveFlag);
                }
                (*player).hasMoved = false;
                (*player).moveFlag = 0;
                //detect collision
                CollisionDetect(screenP,player,&collided);
                if(collided) {
                    //player has lost if collision detected
                    state = STATE_LOSE;
                }
                drawPlayer(&uLCD, player);
                drawScore(&uLCD, score);
                posMutex.unlock();            
                //Evaluate screens, delete old off-screen screens if necessary, and update the score
                evalScreens(screenP);
                score++;
            } else if((state & STATE_LOSE) >> 1) {
                //draw game over screen, plus NEW HIGH SCORE text if applicable
                uLCD.textbackground_color(0xf09860);
                uLCD.locate(6,0);
                uLCD.printf("GAME OVER   ");
                uLCD.set_sector_address(0x001D, 0x9300);
                uLCD.locate(1,1);
                k = 0;
                if(scCompBuf != NULL) {
                    //delete the previous score that was checked if the game has run once before
                    delete scCompBuf;
                }
                scCompBuf = new char[15];
                readchar = ' ';
                //read in the score from the memory location on the SD card
                while(readchar != '\xFF') {
                    readchar = uLCD.read_byte();
                    if(readchar >= '0' && readchar <= '9') {
                    scCompBuf[k] = readchar;
                    }
                    k++;
                }
                //determine if player should go to high score or title screen on fire
                if(score > atoi(scCompBuf)) {
                    //new high score
                    newHighScore = true;
                    uLCD.locate(3,3);
                    uLCD.textbackground_color(COVER_COLOR);
                    uLCD.printf("NEW HIGH SCORE");
                }
            } else if((state & STATE_SCORE) >> 2) {
            //name entry for new high score
            if(!scoreDrawn) {
                //Only draw background pieces once   
                uLCD.filled_rectangle(0,0,127,127,COVER_COLOR);
                uLCD.locate(0,0);
                uLCD.textbackground_color(COVER_COLOR);
                uLCD.printf("HIGH SCORE: %d\n\n", score);
                uLCD.printf("Initials:\n\n");
                uLCD.locate(0,4);
                //Letter select for initials
                uLCD.printf(" A B C D E F G H \n\n");
                uLCD.printf(" I J K L M N O P \n\n");
                uLCD.printf(" Q R S T U V W X \n\n");
                uLCD.printf(" Y Z ");
                uLCD.filled_rectangle(36,81,40,85,0x000000);
                (*curs).draw(&uLCD);
                scoreDrawn = true;
            }
            if((*curs).letterPressed) {
            //draw initials
                if((*curs).curChar >= 'A' && (*curs).curChar <= 'Z') { //If selected character is not the END character
                    //pick correct location on screen to draw initial
                    uLCD.locate(10+(*curs).c,2);
                    uLCD.printf("%c",(*curs).initBuf[(*curs).c]);
                    (*curs).c = (((*curs).c < 2) ? (*curs).c + 1 : 2);
                    (*curs).letterPressed = false;
                } else {
                //write score to file
                if(writtenScore == NULL) {
                    //allocate array
                    writtenScore = new char[4 + 15];   
                } else {
                    delete writtenScore;
                    writtenScore = new char[4 + 15];
                }
                if(scoreStr != NULL) {
                    //delete any previous string representation of score
                    delete scoreStr;
                }
                scoreStr = new char[15];
                //write score to display into a character array 
                sprintf(writtenScore,"%s     %d",(*curs).initBuf,score);
                //overwrite 0 that would appear after initials on score screen with blank space
                writtenScore[3] = ' ';
                //set location of title screen display score
                uLCD.set_sector_address(0x001D, 0x8700);
                uLCD.locate(0,12);
                uLCD.printf("\n\nStoring score...");
                for (int i=0; i<strlen(writtenScore); i++) {
                    uLCD.write_byte(writtenScore[i]); //write a byte to SD card
                }
                uLCD.flush_media();
                //set location for only the score
                uLCD.set_sector_address(0x001D, 0x9300);
                //read in score to character array
                sprintf(scoreStr,"%d",score);
                for (int i=0; i<strlen(scoreStr); i++) {
                    uLCD.write_byte(scoreStr[i]); //write a byte to SD card
                }
                //Reset game once score has been saved
                titleDrawn = false;
                state = STATE_START;
                }
            }
            if((*curs).hasMoved) {
                //cover up old cursor
                (*curs).coverUp(&uLCD);
                //draw new cursor
                (*curs).draw(&uLCD);
                (*curs).hasMoved = false;
                (*curs).moveFlag = 0;
            }
        }
    }
}

void initialize(Screen **s, Player *p, int *score, bool* initFlash, bool *scDrawn, bool *collide, int *c, int *r, Cursor *curs) {
        //initialize screen (only delete if the screen pointer is not a null pointer
        if(s[0] != NULL) {
            delete s[0];
        }
        if(s[1] != NULL) {
            delete s[1];
        }
        s[0] = new Screen(0x780D,true,-127);
        s[1] = new Screen(0x780D,true,0);
        //Initialize or re-initialize player
        if(p != NULL) {
            delete p;
        }
        p = new Player();
        *score = 0;
        //Initialize or re-initialize flags used during gameplay
        *initFlash = false;
        *scDrawn = false;
        *collide = false;
        *c = 111;
        *r = 96;
        //Initialize or re-initialize the cursor
        if(curs != NULL) {
            delete curs;
        }
        curs = new Cursor();
}