Mini game developed for ECE 4180 lab

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

Revision:
0:a358215f57b7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Mar 14 17:42:07 2016 +0000
@@ -0,0 +1,436 @@
+// 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();
+}
+
+
+