A retro gaming programme, designed for use on a portable embedded system. Incorporates power saving techniques.

Dependencies:   ConfigFile N5110 PowerControl beep mbed

main.cpp

Committer:
el13drt
Date:
2015-05-11
Revision:
79:a92239ce3b20
Parent:
78:e2fc547c6b99
Child:
80:55c724c1286c

File content as of revision 79:a92239ce3b20:

/**
@file main.cpp
@brief Program implementation
*/

#include "PowerControl/PowerControl.h"//for sleep modes
#include "PowerControl/EthernetPowerControl.h"//for Ethernet Power Down
#include "mbed.h"
#include "N5110.h"
#include "beep.h"
#include "tower.h"
#include <ctime>

///writes to CFG file.
void write()
{
    ///Sets a Key 1 and Value 1.
    if (!cfg.setValue("Open1", player1initials)) {//key/value
        error("Failure to set a value.\n");
    }
    ///Sets a Key 2 and Value 2.
    if (!cfg.setValue("Open2", player2initials)) {//key/value
        error("Failure to set a value.\n");
    }
    ///Sets a Key 3 and Value 3.
    if (!cfg.setValue("Open3", player3initials)) {//key/value
        error("Failure to set a value.\n");
    }
    ///If a file does not already exist, create one.
    ///If one already exits, append the file.
    if (!cfg.write("/local/towerMemory.cfg")) {//Write key and value to CFG file
        error("Failure to write a configuration file.\n");
    }
}

///Reads CFG file.
void read()
{
    //Checks if key matches.
    char *key1 = "Open1";
    char *key2 = "Open2";
    char *key3 = "Open3";
    
    char a[3],b[2],c[2];
    
    int count=0;

    ///Checks Key for match, then reads Value - if read is unsuccessful it prints error message.
    if (!cfg.read("/local/towerMemory.cfg")) {
        error("Failure to read a configuration file.\n");
    }
    ///check file size - if size acceptable, prints buffer to location.
    if (cfg.getValue(key1, &player1initials[0], sizeof(player1initials))) {
        serial.printf("%s\n",player1initials);
       
        count=10;//first possible character of score
        highScore1=0;//make sure that multiplacation works correctly
        while(1){
            //if first iteration/not an empty score
            if(highScore1!=0){
                highScore1*=10;
            }
            //convert ASCII to int value
            highScore1+=player1initials[count]-48;
            //increment
            count++;
            //check to see if at end of string array
            if(player1initials[count]==NULL){
                break;
            }
        }
        serial.printf("%i\n",highScore1);
    }
    //check file size - if size acceptable, prints buffer to location.
    if (cfg.getValue(key2, &player2initials[0], sizeof(player2initials))) {
        serial.printf("%s\n",player2initials);
        count=10;
        highScore2=0;
        while(1){
            if(highScore2!=0){
                highScore2*=10;
            }
            highScore2+=player2initials[count]-48;
            count++;
            if(player2initials[count]==NULL){
                break;
            }
        }
        serial.printf("%i\n",highScore2);
    }
    //check file size - if size acceptable, prints buffer to location.
    if (cfg.getValue(key3, &player3initials[0], sizeof(player3initials))) {
        serial.printf("%s\n",player3initials);
        count=10;
        highScore3=0;
        while(1){
            if(highScore3!=0){
                highScore3*=10;
            }
            highScore3+=player3initials[count]-48;
            count++;
            if(player3initials[count]==NULL){
                break;
            }
        }
            
        serial.printf("%i\n",highScore3);
    }
}

///Struct declaration for storing alphabet arrays.
struct State {
    char output1;
    char output2;
    char output3;
    int nextState[2];
};

///Assigns new identifier to Struct.
typedef const struct State STYP;

///Finite State Machine for Struct data members.
STYP fsm[27] = {

    ///Outputs 1,2 and 3 for Initial Arrays.
    {'A','A','A',{1,26}},
    {'B','B','B',{2,0}},
    {'C','C','C',{3,1}},
    {'D','D','D',{4,2}},
    {'E','E','E',{5,3}},
    {'F','F','F',{6,4}},
    {'G','G','G',{7,5}},
    {'H','H','H',{8,6}},
    {'I','I','I',{9,7}},
    {'J','J','J',{10,8}},
    {'K','K','K',{11,9}},
    {'L','L','L',{12,10}},
    {'M','M','M',{13,11}},
    {'N','N','N',{14,12}},
    {'O','O','O',{15,13}},
    {'P','P','P',{16,14}},
    {'Q','Q','Q',{17,15}},
    {'R','R','R',{18,16}},
    {'S','S','S',{19,17}},
    {'T','T','T',{20,18}},
    {'U','U','U',{21,19}},
    {'V','V','V',{22,20}},
    {'W','W','W',{23,21}},
    {'X','X','X',{24,22}},
    {'Y','Y','Y',{25,23}},
    {'Z','Z','Z',{26,24}},
    {'.','.','.',{0,25}},
};

///Creates enumerated type (0,1,2,3 etc. for direction).
enum DirectionName {
    UP,
    DOWN,
    LEFT,
    RIGHT,
    CENTRE,
    UNKNOWN
};

///Struct for Joystick.
typedef struct JoyStick Joystick;
struct JoyStick {
    float x;//current x value
    float x0;//'centred' x value
    float y;//current y value
    float y0;//'centred' y value
    int button;//button state (assume pull-down used, so 1 = pressed, 0 = unpressed)
    DirectionName direction;//current direction
};

///create struct variable
Joystick joystick;

///Called when timerA expires. If buttonA is pressed - set flag A.
void timerExpiredA()
{
    ///Includes backlight reset for user input.
    if((deBounce1.read_ms()>=95)&&(buttonA == 1)) {
        buttonFlagA = 1;
        lcd.setBrightness(1.0);
        serial.printf("flagA set\n");
        deBounce1.reset();
        ///Reset value for debounce timer
    }
}

///Called when timer expires. If buttonB is pressed - Set flag B.
void timerExpiredB()
{
    ///Includes backlight reset for user input.
    if((deBounce2.read_ms()>=95)&&(buttonB == 1)) {
        buttonFlagB = 1;
        lcd.setBrightness(1.0);
        serial.printf("flagB set\n");
        deBounce2.reset();//reset timer
        ///Resets value for debounce timer 
    }
}

///Randomises initial co-ordinates for falling hazards.
void randomise()
{
    ///Creates initial seed for randomisation.
    srand (time(NULL));//initial seed for randomisation

    //initial random x co-ordinates
    //for falling hazards
    //(values between 3 and 76)
    randX1 = rand() % 74 + 5;
    randX2 = rand() % 74 + 5;
    randX3 = rand() % 74 + 5;
    randX4 = rand() % 74 + 5;
    randX5 = rand() % 74 + 5;
    randX6 = rand() % 74 + 5;
}

///Draws the games static background.
void drawBackground()
{
    //x, y, w, h, fill - draw ground
    lcd.drawRect(0,47,84,0,1);
    //x, y, w, h, fill - draw left wall
    lcd.drawRect(2,0,0,47,1);
    //left wall - brick line 1
    lcd.drawLine(1,1,1,48,2);
    //left wall - brick line 2
    lcd.drawLine(0,0,0,48,2);

    //x, y, w, h, fill - draw right wall
    lcd.drawRect(81,0,0,47,1);
    //right wall - brick line 1
    lcd.drawLine(82,0,82,48,2);
    //right wall - brick line 2
    lcd.drawLine(83,1,83,48,2);

    lcd.refresh();
}

///Draws the Introduction screen.
void drawWelcome()
{
    //bottom border
    lcd.drawRect(0,44,84,2,1);
    //top border
    lcd.drawRect(0,1,84,2,1);
    lcd.refresh();

    //print initials 'DRT'
    lcd.printChar('D',30,2);
    wait(0.6);
    lcd.printChar('R',37,2);
    wait(0.6);
    lcd.printChar('T',44,2);
    wait(0.6);

    //print 'presents...'
    lcd.printString("presents...",8,3);
    wait(1.0);

    //dramatic flashing
    buzzer.beep(5000,0.3);
    lcd.inverseMode();
    wait(0.2);
    lcd.normalMode();
    wait(0.2);
    buzzer.beep(5000,0.3);
    lcd.inverseMode();
    wait(0.2);
    lcd.normalMode();
    wait(1.0);

    //more dramatic flashing
    buzzer.beep(5000,0.3);
    lcd.inverseMode();
    wait(0.2);
    lcd.normalMode();
    wait(0.2);
    buzzer.beep(5000,0.3);
    lcd.inverseMode();
    wait(0.2);
    lcd.normalMode();
    wait(0.6);
}

///Draws Pixel Ninja.
void drawNinja()
{
    //x, y, w, h, fill - left leg
    lcd.drawRect(a1,39,0,7,1);
    //right leg
    lcd.drawRect(a2,39,0,7,1);
    //centre stick
    lcd.drawRect(a3,37,0,7,1);
    //back of the head
    lcd.drawRect(a4,33,0,4,1);
    //top of the head
    lcd.drawRect(a5,33,4,0,1);
    //jaw
    lcd.drawRect(a6,38,2,0,1);
    //right shoulder
    lcd.drawRect(a7,40,1,0,1);
    //left shoulder
    lcd.drawRect(a8,40,1,0,1);
    //left arm
    lcd.drawRect(a9,41,0,1,1);
    //right arm
    lcd.drawRect(a10,41,0,1,1);
    //right eye
    lcd.drawRect(a11,35,0,0,1);
    //mouth piece
    lcd.drawRect(a12,37,0,0,1);
    //left eye
    lcd.drawRect(a13,35,0,0,1);
    //sword handle
    lcd.drawRect(a14,36,0,0,1);
    lcd.drawRect(a15,37,0,0,1);
    lcd.drawRect(a16,38,0,0,1);

    lcd.refresh();
}

///Implements Pixel Ninja's boundary conditions.
void ninjaBoundaries()
{
    ///If ninja exceeds max boundary, Ninja equals max boundary.
    //right eye
    if(a11 > 80 )
        a11 = 80;
    //mouth piece
    if(a12 > 80)
        a12 = 80;
    //right arm
    if(a10> 80)
        a10 = 80;
    //right shoulder
    if(a7 > 79)
        a7 = 79;
    //right leg
    if(a2 > 78)
        a2 = 78;
    //jaw
    if(a6 > 78)
        a6 = 78;
    //left eye
    if(a13 > 78)
        a13 = 78;
    //centre
    if(a3 > 77)
        a3 = 77;
    //back of the head
    if(a4 > 76 )
        a4= 76;
    //topf of the head
    if(a5 > 76)
        a5 = 76;
    //left leg
    if(a1>76)
        a1 = 76;
    //sword
    if(a16>75)
        a16 = 75;
    //left shoulder
    if(a8 >74)
        a8 = 74;
    //left arm
    if(a9>74)
        a9 = 74;
    //sword
    if(a15>74)
        a15 = 74;
    //sword
    if(a14 > 73)
        a14 = 73;

    //right eye
    if(a11 < 5 )
        a11 = 5;
    //mouth piece
    if(a12 < 3)
        a12 = 3;
    //right arm
    if(a10< 9)
        a10 = 9;
    //right shoulder
    if(a7 < 8)
        a7 = 8;
    //right leg
    if(a2 < 7)
        a2 = 7;
    //jaw
    if(a6 < 3)
        a6 = 3;
    //left eye
    if(a13 < 3)
        a13 = 3;
    //centre
    if(a3 < 6)
        a3 = 6;
    //back of the head
    if(a4 < 7)
        a4= 7;
    //top of the head
    if(a5 < 3)
        a5 = 3;
    //left leg
    if(a1 < 5)
        a1 = 5;
    //sword
    if(a16 < 8)
        a16 = 8;
    //left shoulder
    if(a8 < 3)
        a8 = 3;
    //left arm
    if(a9 < 3)
        a9 = 3;
    //sword
    if(a15 < 9)
        a15 = 9;
    //sword
    if(a14 < 10)
        a14 = 10;
    ///If ninja falls below min boundary, Ninja equals min boundary.    
    
}

///Resets configuration values for the game.
void resetGame()
{   
    ///Resets Global variables for pixelNinja movement.
    a1 = 22;
    a2 = 24;
    a3 = 23;
    a4 = 22;
    a5 = 22;
    a6 = 24;
    a7 = 25;
    a8 = 20;
    a9 = 20;
    a10 = 26;
    a11 = 26;
    a12 = 26;
    a13 = 24;
    a14 = 19;
    a15 = 20;
    a16 = 21;
    
    ///Resets current score.
    score = 0;

    //in this case the X values are given a
    //new random variable each time the player
    //dies, exits or starts a new game
    
    ///Randomises X co-ordinates.
    randomise();
 
    ///Resets Y co-ordinates.
    randY1 = 0;
    randY2 = 0;
    randY3 = 0;
    randY4 = 0;
    randY5 = 0;
    randY6 = 0;
    
    ///Clears screen.
    lcd.clear();
}

///Draws falling hazards.
void drawHazards()
{
    //X, Y, radius, fill
    lcd.drawCircle(randX1,randY1,2,1);
    lcd.drawCircle(randX2,randY2,2,1);
    lcd.drawCircle(randX3,randY3,2,1);
    lcd.drawCircle(randX4,randY4,2,1);
    lcd.drawCircle(randX5,randY5,2,1);
    lcd.drawCircle(randX6,randY6,2,1);
    
    lcd.refresh();
}

///Increments the hazards to make them "Fall".
void hazardFall()
{
    ///Increments values by chosen difficulty.
    randY1 = randY1 += fall;
    randY2 = randY2 += fall;
    randY3 = randY3 += fall;
    randY4 = randY4 += fall;
    randY5 = randY5 += fall;
    randY6 = randY6 += fall;


    ///Loops objects once they've hit the floor.
    if (randY1>=48)
        randY1=0;

    if (randY2>=48)
        randY2=0;

    if (randY3>=48)
        randY3=0;

    if (randY4>=48)
        randY4=0;

    if (randY5>=48)
        randY5=0;

    //each time the objects loop, a new pseudo random value
    //is assigned to the global variables (randX) to
    //randomise their positions
     
    ///Assigns new random X co-ordinate once looped.
    if (randY6>=48) {
        randY6=0;
        ///Incements current score with each wave of hazards.
        score = score++;//increment score by 1 after each wave of hazards
        randomise();//randomise x co-ordinates again
    }
}

///Clears old pixels and keeps set pixels.
void startrek()
{
    ///Loops thorugh screen pixel by pixel.
    for (int i=3; i<81; i++)//loops through rows
        for (int j=0; j<47; j++)
            if (cells[i][j]) {//if there's a pixel then keep it
                lcd.setPixel(i,j);
            } else {
                lcd.clearPixel(i,j);//else remove the old ones
            }
    lcd.refresh();
}

///Clears old cursor, sets new one.
void refreshCursor1()
{
    ///Loops through select part of the screen.
    for (int i=70; i<80; i++)//loops through rows
        for (int j=17; j<25; j++)
            if (cells[i][j]) {//if there's a pixel then keep it
                lcd.setPixel(i,j);
            } else {
                lcd.clearPixel(i,j);//else remove the old ones
            }
    lcd.refresh();
}

///Clears old cursor, sets new one.
void refreshCursor2()
{
    ///Loops though select part of the screen.
    for (int i=70; i<80; i++)//loops through rows
        for (int j=25; j<32; j++)
            if (cells[i][j]) {//if there's a pixel then keep it
                lcd.setPixel(i,j);
            } else {
                lcd.clearPixel(i,j);//else remove the old ones
            }
    lcd.refresh();
}

///Clears old cursor, sets new one.
void refreshCursor3()
{
    ///Loops through select part of the screen.
    for (int i=70; i<80; i++)//loops through rows
        for (int j=32; j<40; j++)
            if (cells[i][j]) {//if there's a pixel then keep it
                lcd.setPixel(i,j);
            } else {
                lcd.clearPixel(i,j);//else remove the old ones
            }
    lcd.refresh();
}

///Creates buzzer audible/LED to light when either button is pressed.
void actionButtons()
{
    ///If FX is on emit beep and LED flash, else just flash.
    if((FX == 0)&&(buttonA||buttonB)) {
        ledA = 1;
        buzzer.beep(1500,0.3);
    }
    if (buttonA || buttonB) {
        ledA = 1;
    } else {
        ledA = 0;
    }
}

///Implements Joystick navigation for main menu.
void mainMenu(int& mainOption)
{
    ///Includes actionButtons();
    actionButtons();//set audible/light for button

    if (printFlag) {//if flag set, clear flag and print joystick values to serial port
        printFlag = 0;

        //option up
        if (joystick.direction == UP) {
            serial.printf(" UP\n");
            mainOption = mainOption--;
            if (mainOption < 0)mainOption = 0;
        }
        //option down
        if (joystick.direction == DOWN) {
            serial.printf(" DOWN\n");
            mainOption = mainOption++;
            if (mainOption > 2)mainOption = 2;
        }
        //Centre / Unknown orientation
        if (joystick.direction == CENTRE)
            serial.printf(" CENTRE\n");
        if (joystick.direction == UNKNOWN)
            serial.printf(" Unsupported direction\n");

        //'Play Game' option 1
        if (mainOption == 0) {
            lcd.printString("Play Game",3,4);
        }
        //'High Scores' option 2
        if (mainOption == 1) {
            lcd.printString("  Scores ",3,4);
        }
        //'Options' option 3
        if (mainOption == 2) {
            lcd.printString(" Options ",3,4);
        }
    }
}

///Draws the Main Menu.
void drawMainMenu()
{
    //bottom border
    lcd.drawRect(0,47,84,0,1);
    //top border
    lcd.drawRect(0,0,84,2,1);

    //title outline
    lcd.drawRect(3,6,77,10,0);

////castle //x, y, w, h, fill//////////////////////

    //castle main bulk
    lcd.drawRect(59,32,21,8,1);

    //left window bulk
    lcd.drawRect(59,22,2,10,1);
    //centre left window bulk
    lcd.drawRect(65,22,2,10,1);
    //centre right window bulk
    lcd.drawRect(72,22,2,10,1);
    //right window bulk
    lcd.drawRect(78,22,2,10,1);
    //central window bulk
    lcd.drawRect(68,25,3,7,1);

    //central window bulk
    lcd.drawRect(75,28,0,0,1);
    lcd.drawRect(77,28,0,0,1);
    lcd.drawRect(64,28,0,0,1);
    lcd.drawRect(62,28,0,0,1);

    //above left window bulk
    lcd.drawRect(62,25,3,2,1);
    //above right window bulk
    lcd.drawRect(75,25,2,2,1);

    //lower right line
    lcd.drawRect(71,42,9,0,1);
    //upper right line
    lcd.drawRect(70,41,10,0,1);

    //upper left line
    lcd.drawRect(59,41,10,0,1);
    //lower left line
    lcd.drawRect(59,42,9,0,1);

    //bottom left bulk
    lcd.drawRect(59,43,8,3,1);
    //bottom right bulk
    lcd.drawRect(72,43,8,3,1);

    //option arrows - lower
    lcd.drawRect(27,42,4,0,1);
    lcd.drawRect(28,43,2,0,1);
    lcd.drawRect(29,44,0,0,1);

    //option arrows - higher
    lcd.drawRect(27,29,4,0,1);
    lcd.drawRect(28,28,2,0,1);
    lcd.drawRect(29,27,0,0,1);

    //print 'Xtreme Tower'
    lcd.printString("Xtreme Tower",7,1);

    lcd.refresh();
}

///Implements Joystick navigation for Exit Menu.
void exitMenu(int& exitOption)
{
    ///Includes actionButtons();
    actionButtons();
    if (printFlag) {//if flag set, clear flag and print joystick values to serial port
        printFlag = 0;

        if (joystick.direction == LEFT) {
            serial.printf(" LEFT\n");
            exitOption--;
            if(exitOption < 0)exitOption = 0;
        }
        if (joystick.direction == RIGHT) {
            serial.printf(" RIGHT\n");
            exitOption++;
            if(exitOption > 1)exitOption = 1;
        }
        if (joystick.direction == CENTRE)
            serial.printf(" CENTRE\n");
        if (joystick.direction == UNKNOWN)
            serial.printf(" Unsupported direction\n");
    }
    //draws option cursor
    if(exitOption == 0) {
        lcd.printString("YES",33,3);
    } else if(exitOption == 1) {
        lcd.printString(" NO",33,3);
    }
}

///If the joystick is moved left, Pixel Ninja moves left.
void ninjaLeft()
{
    ///Decrements values for left movement.
    a1 = a1-=2;
    a2 = a2-=2;
    a3 = a3-=2;
    a4 = a3+1;
    a5 = a3-3;
    a6 = a3-3;
    a7 = a7-=2;
    a8 = a8-=2;
    a9 = a9-=2;
    a10 = a10-=2;
    a11 = a3-1;
    a12 = a3-3;
    a13 = a3-3;
    a14 = a3+4;
    a15 = a3+3;
    a16 = a3+2;
    ///Also turns head to face left.
}

///If the Joystick is moved right, Pixel Ninja moves right.
void ninjaRight()
{
    ///Increments for right movement.
    a1 = a1+=2;
    a2 = a2+=2;
    a3 = a3+=2;
    a4 = a3-1;
    a5 = a3-1;
    a6 = a3+1;
    a7 = a7+=2;
    a8 = a8+=2;
    a9 = a9+=2;
    a10 = a10+=2;
    a11 = a3+3;
    a12 = a3+3;
    a13 = a3+1;
    a14 = a3-4;
    a15 = a3-3;
    a16 = a3-2;
    ///Turns head to face right.
}

///Draws the Exit Menu.
void drawExitMenu()
{
    //set exit menu
    lcd.clear();
    drawBackground();
    lcd.printString("Exit Game?",14,1);

    lcd.drawRect(8,3,70,30,0);//title outline
    //option arrow - right
    lcd.drawRect(55,25,0,4,1);
    lcd.drawRect(56,26,0,2,1);
    lcd.drawRect(57,27,0,0,1);

    //option arrow - left//
    lcd.drawRect(27,25,0,4,1);
    lcd.drawRect(26,26,0,2,1);
    lcd.drawRect(25,27,0,0,1);

    lcd.refresh();
}

///Implements Joystick navigation for Options Menu.
void optionsMenu(int& option)
{
    //joystick selection
    if (printFlag) {  //if flag set, clear flag and print joystick values to serial port
        printFlag = 0;

        //option up
        if (joystick.direction == UP) {
            serial.printf(" UP\n");
            option = option--;
            if (option < 0)option = 0;
        }
        //option down
        if (joystick.direction == DOWN) {
            serial.printf(" DOWN\n");
            option = option++;
            if (option > 1)option = 1;
        }
        //Centre / Unknown orientation
        if (joystick.direction == CENTRE)
            serial.printf(" CENTRE\n");
        if (joystick.direction == UNKNOWN)
            serial.printf(" Unsupported direction\n");

        //'Difficulty' option 1
        if (option == 0) {
            lcd.drawCircle(72,27,2,1);
            refreshCursor3();
        }
        //'Sound FX' option 2
        if (option == 1) {
            lcd.drawCircle(72,35,2,1);
            refreshCursor2();
        }
    }
    lcd.refresh();
}

///Draws the Options Menu.
void drawOptionsMenu()
{
    lcd.clear();//clear screen
    drawBackground();
    lcd.drawRect(3,6,77,10,0);//title outline
    lcd.drawRect(0,47,84,0,1);//bottom border
    lcd.drawRect(0,0,84,2,1);//top border
    lcd.printString("Options",20,1);//title
    lcd.printString("Difficulty",5,3);
    lcd.printString("Sound FX",5,4);

    lcd.refresh();
}

///Implements Joystick navigation for Difficulty Options.
void difficultyMenu(int& subOption)
{
    ///Includes actionButtons();
    actionButtons();

    //joystick selection
    if (printFlag) {//if flag set, clear flag,print joystick values
        printFlag = 0;

        //option up
        if (joystick.direction == UP) {
            serial.printf(" UP\n");
            subOption = subOption--;
            if (subOption < 1)subOption = 1;
        }
        //option down
        if (joystick.direction == DOWN) {
            serial.printf(" DOWN\n");
            subOption = subOption++;
            if (subOption > 3)subOption = 3;
        }
        //Centre / Unknown orientation
        if (joystick.direction == CENTRE)
            serial.printf(" CENTRE\n");
        if (joystick.direction == UNKNOWN)
            serial.printf(" Unsupported direction\n");

        //'Easy' option 1
        if (subOption == 1) {
            lcd.drawCircle(72,19,2,1);
            refreshCursor2();
            refreshCursor3();
            if(buttonFlagA) { //select easy
                //rightness(1.0);
                buttonFlagA = 0;
                fall = 1;
            }
        }
        //'Normal' option 2
        if (subOption == 2) {
            lcd.drawCircle(72,27,2,1);
            refreshCursor1();
            refreshCursor3();

            if(buttonFlagA) { //select normal
                //rightness(1.0);
                buttonFlagA = 0;
                fall = 2;
            }
        }
        //'Forget It' option 3
        if (subOption == 3) {
            lcd.drawCircle(72,35,2,1);
            refreshCursor1();
            refreshCursor2();

            if(buttonFlagA) { //select difficult
                //rightness(1.0);
                buttonFlagA = 0;
                fall = 3;
            }
        }
    }
    lcd.refresh();
}

///Draws Difficulty Menu.
void drawDifficultyMenu()
{
    lcd.clear();
    drawBackground();
    lcd.drawRect(0,47,84,0,1);//bottom border
    lcd.drawRect(0,0,84,2,1);//top border
    lcd.refresh();
    lcd.printString("*Difficulty*",5,1);//title
    lcd.printString("Easy",5,2);//title
    lcd.printString("Normal",5,3);//title
    lcd.printString("Forget It",5,4);//title
}

///Implements Joystick navigation for FX Menu.
void soundFXMenu(int& fxOption)
{
    ///Inlcudes actionButtons();
    actionButtons();

    //joystick selection
    if (printFlag) {//if flag set, clear flag,print joystick values
        printFlag = 0;

        //option up
        if (joystick.direction == UP) {
            serial.printf(" UP\n");
            fxOption = fxOption--;
            if (fxOption < 0)fxOption = 0;
        }
        //option down
        if (joystick.direction == DOWN) {
            serial.printf(" DOWN\n");
            fxOption = fxOption++;
            if (fxOption > 1)fxOption = 1;
        }
        //Centre / Unknown orientation
        if (joystick.direction == CENTRE)
            serial.printf(" CENTRE\n");
        if (joystick.direction == UNKNOWN)
            serial.printf(" Unsupported direction\n");
    }
    //'ON' option 1
    if (fxOption == 0) {
        lcd.drawCircle(72,27,2,1);//draw cursor 'ON'
        refreshCursor1();
        refreshCursor3();

        if(buttonFlagA) {
            buttonFlagA =0;
            FX = 0;
            serial.printf("FX = %d\n",FX);
        }
    }
    //'OFF' option 2
    if (fxOption == 1) {
        lcd.drawCircle(72,35,2,1);//draw cursor 'OFF'
        refreshCursor1();
        refreshCursor2();

        if(buttonFlagA) {
            buttonFlagA =0;
            FX = 1;
            serial.printf("FX = %d\n",FX);
        }
    }
    lcd.refresh();
}

///Draws the FX Menu.
void drawSoundFXMenu()
{
    lcd.clear();
    drawBackground();
    lcd.drawRect(0,47,84,0,1);//bottom border
    lcd.drawRect(0,0,84,2,1);//top border
    lcd.printString("*Sound FX*",10,1);//title
    lcd.printString("ON",35,3);//title
    lcd.printString("OFF",33,4);//title
    lcd.refresh();
}


///Game flag for main game.
void gameLoop()
{
    ///Sets gameFlag when game Timer expires.
    gameFlag = 1;
}

///Shifts and stores the new scores accordingly.
void newScore()
{
    ///Entry conditions mean function is only used if user equals or beats High Score 3.
    if(score >= highScore3) {//entry condition
        buttonFlagA = 0;//reset flags
        buttonFlagB = 0;
        lcd.clear();//clears screen
        drawBackground();//draws background
        lcd.printString("High Score!!",7,0);//title
        lcd.printString("Enter ID",19,4);//title

        int n;//local variable used for storing temporary global variable
        int initial = 0;//used for isolating which initial is being selected
        char x,y,z;

        //print initial characters
        x=fsm[state1].output1;
        lcd.printChar(x,28,2);
        y=fsm[state2].output2;
        lcd.printChar(y,40,2);
        z=fsm[state3].output3;
        lcd.printChar(z,52,2);

        while(1) {

            //joystick selection
            if (printFlag) {//if flag set, clear flag,print joystick values
                printFlag = 0;

                if(joystick.direction==CENTRE) {
                    preDirection=0;
                }
                if (joystick.direction == UP ) {
                    serial.printf(" UP\n");
                    state1 = state1--;
                    if (state1 < 0)state1 = 0;
                    state2 = state2--;
                    if (state2 < 0)state2 = 0;
                    state3 = state3--;
                    if (state3 < 0)state3 = 0;
                }
                //option down
                if (joystick.direction == DOWN ) {
                    serial.printf(" DOWN\n");
                    state1 = state1++;
                    if (state1 > 26)state1 = 26;
                    state2 = state2++;
                    if (state2 > 26)state2 = 26;
                    state3 = state3++;
                    if (state3 > 26)state3 = 26;
                }
                if (joystick.direction == LEFT && preDirection==0) {
                    serial.printf(" LEFT\n");
                    initial = initial--;
                    if (initial < 0)initial = 0;
                    preDirection=1;
                }
                if (joystick.direction == RIGHT && preDirection==0) {
                    serial.printf(" RIGHT\n");
                    initial = initial++;
                    if (initial > 2)initial = 2;
                    preDirection=1;
                }
                //Centre / Unknown orientation
                if (joystick.direction == CENTRE)
                    serial.printf(" CENTRE\n");
                if (joystick.direction == UNKNOWN)
                    serial.printf(" Unsupported direction\n");
            }
            //if initial 3 display selected character
            if (initial == 0) {
                x=fsm[state1].output1;
                lcd.printChar(x,28,2);
            }
            //if initial 1 display selected character
            if(initial == 1) {
                y=fsm[state2].output2;
                lcd.printChar(y,40,2);
            }
            //if initial 2 display selected character
            if(initial == 2) {
                z=fsm[state3].output3;
                lcd.printChar(z,52,2);
            }
            if(buttonFlagA) {
                actionButtons();
                buttonFlagA = 0;
                buttonFlagB = 0;
                break;
            }
        }
        //if player beats High Score 3, replace it with new score
        if(score >= highScore3 && score<highScore2 ) {
            highScore3 = score;
            sprintf (player3initials, "3.%c%c%c.....%i",x,y,z,highScore3);
        }
        //if player beats High Score 3 and 2, replace HighScore2 with new score
        if(score >= highScore2 && score< highScore1) {
            highScore3 = highScore2;
            n = score;
            highScore2 = n;
            sprintf (player3initials, "3.%c%c%c.....%i",player2initials[2],player2initials[3],player2initials[4],highScore3);
            sprintf (player2initials, "2.%c%c%c.....%i",x,y,z,highScore2);
        }
        //if player beats High Score 1, 2 and 3, replace highScore1 with new score
        if(score >= highScore1 ) {
            highScore3 = highScore2;
            highScore2 = highScore1;
            n = score;
            highScore1 = n;
            sprintf (player3initials, "3.%c%c%c.....%i",player2initials[2],player2initials[3],player2initials[4],highScore3);
            sprintf (player2initials, "2.%c%c%c.....%i",player1initials[2],player1initials[3],player1initials[4],highScore2);
            sprintf (player1initials, "1.%c%c%c.....%i",x,y,z,highScore1);
        }
        state1 = 0;
        state2 = 0;
        state3 = 0;
        write();
    }
}

///Begins Game.
void game(int& exitFlag, int& exitOption)
{
    ///Includes action buttons.
    actionButtons();
    lcd.clear();//clears screen
    drawBackground();//draw background

    while(1) {

        //print score - top left of display
        char buffer [14];//create buffer for string
        int length = sprintf(buffer,"Level:%d",score);//insert buffer
        lcd.printString(buffer,3,0);//display

        actionButtons();
        drawNinja();//set character
        drawHazards();//initiates hazards
        
        if(gameFlag) {
            gameFlag = 0;
            hazardFall();//increments hazards towards floor
            if (printFlag) {  //if flag set, clear flag and print joystick values to serial port
                printFlag = 0;
                if (joystick.direction == RIGHT) {
                    serial.printf(" RIGHT\n");
                    ninjaRight();//move right
                    ninjaBoundaries();
                }
                //check joystick direction
                if (joystick.direction == LEFT) {
                    serial.printf(" LEFT\n");
                    ninjaLeft();//move left
                    ninjaBoundaries();
                }
                if (joystick.direction == CENTRE)
                    serial.printf(" CENTRE\n");
                if (joystick.direction == UNKNOWN)
                    serial.printf(" Unsupported direction\n");

                //integer to represent character being
                //struck by falling object
                int contactPoint = 0;

                //contact points
                if(lcd.getPixel((a3),32))
                    contactPoint++;
                if(lcd.getPixel((a3+3),32))
                    contactPoint++;
                if(lcd.getPixel((a3-3),32))
                    contactPoint++;

                //if contact point is not zero
                //character has been hit
                //and the game ends
                if ( contactPoint !=0) {
                    lcd.printString("Game Over",17,2);
                    lcd.inverseMode();
                    buzzer.beep(2000,0.2);//frequeny/duration
                    wait(0.5);
                    lcd.normalMode();
                    wait(0.5);
                    lcd.inverseMode();
                    buzzer.beep(2000,0.2);
                    wait(0.5);
                    lcd.normalMode();
                    wait(0.5);
                    lcd.inverseMode();
                    buzzer.beep(2000,0.2);
                    wait(0.5);
                    lcd.normalMode();
                    newScore();//enter initial screen if previous scores are beaten
                    //write();
                    resetGame();//reset values
                    break;
                }
                lcd.refresh();//refresh screen
                startrek();//clears unset pixels, keeps set pixels

//Exit Menu (Back button pressed)//
                if(buttonFlagB) {
                    buttonFlagB = 0;//reset flags
                    buttonFlagA = 0;
                    actionButtons();
                    drawExitMenu();//draws the exit menu

                    while(1) {
                        exitMenu(exitOption);//presents exit options

                        //'exit' option YES
                        if((buttonFlagA)&&(exitOption == 0)) { //returns to menu
                            //rightness(1.0);
                            exitOption = 1;//reset intial option value
                            buttonFlagA = 0;//reset flags
                            buttonFlagB = 0;
                            actionButtons();
                            lcd.clear();//clears screen
                            resetGame();//resets scores/objects
                            exitFlag = 1;//sets exit flag
                            break;
                        }
                        //'exit' option NO - returns to game
                        if((buttonFlagA)&&(exitOption == 1)) {
                            buttonFlagA = 0;//resets flags
                            buttonFlagB = 0;
                            break;
                        }
                        sleep();//put while to sleep
                    }
                    //if 'exit' option YES, resets
                    //game values returns to main menu
                    if (exitFlag!=0) { //if exit flag set
                        exitFlag = 0;//reset flag
                        break;//break to main menu
                    }
                }
                serial.printf("Score: %i \n",score);//print Score for debug

            }
        }
    }
}

///Draws Scores Menu.
void scores()
{
    ///Include actionButtons();
    actionButtons();
    lcd.clear();//clear screen
    drawBackground();//set background
    lcd.printString("High Scores",10,0);//title

    //prints scores with names
    lcd.printString(player1initials,5,2);//display
    lcd.printString(player2initials,5,3);//display
    lcd.printString(player3initials,5,4);//display

    while(1) {
        actionButtons();//select

        //back to menu
        if(buttonFlagB) {
            buttonFlagA = 0;//reset flags
            buttonFlagB = 0;
            lcd.clear();
            break;
        }
        sleep();//put while to sleep
    }
}

///Implements selection for Options Menu.
void optionsMenu()
{
    ///Includes actionButtons();
    int option = 0;
    int subOption = fall;//keeps cursor on selected option, even after exiting screen
    int fxOption = 0;
    actionButtons();
    drawOptionsMenu();//draws options menu
    //counters for navigation

    while(1) {
        actionButtons();
        optionsMenu(option);//presents options

//================ Difficulty menu =======================
        if ((option == 0)&&(buttonFlagA)) {
            //rightness(1.0);
            buttonFlagA = 0;//reset flag
            actionButtons();
            drawDifficultyMenu();//draws difficulty menu

            while(1) {
                actionButtons();
                difficultyMenu(subOption);//presents difficulty options

                if(buttonFlagB) {
                    buttonFlagB = 0;//reset flags
                    buttonFlagA = 0;
                    lcd.clear();//clear screen
                    break;//return back
                }
                sleep();//put while to sleep
            }
            drawOptionsMenu();
        }
//================sound FX menu==================================

        if((option ==1)&&(buttonFlagA)) {
            buttonFlagA = 0;//reset flags
            buttonFlagB = 0;
            actionButtons();
            drawSoundFXMenu();//draw menu

            while(1) {
                actionButtons();
                soundFXMenu(fxOption);//presents options

                //back to options menu
                if(buttonFlagB) {
                    buttonFlagB = 0;//reset flags
                    buttonFlagA = 0;
                    lcd.clear();//clear screen
                    break;//return back
                }
                sleep();//put while to sleep
            }
            drawOptionsMenu();
        }
        //back to mainmenu
        if(buttonFlagB) {
            buttonFlagB = 0;//reset flags
            buttonFlagA = 0;
            lcd.clear();//clear
            break;//return back
        }
        sleep();//put while to sleep
    }
}

///Reads default position of Joystick for calibration.
void calibrateJoystick()
{
    joyButton.mode(PullDown);
    //must not move during calibration
    joystick.x0 = xPot; //initial positions in the range 0.0 to 1.0 (0.5 if centred exactly)
    joystick.y0 = yPot;
}

///Updates Joytick position according to voltage readings.
void updateJoystick()
{
    //read current joystick values relative to calibrated values (in range -0.5 to 0.5, 0.0 is centred)
    joystick.x = xPot - joystick.x0;
    joystick.y = yPot - joystick.y0;
    //read button state
    joystick.button = joyButton;

    //calculate direction depending on x,y values
    //tolerance allows a little lee-way in case joystick not exactly in the stated direction
    if ( fabs(joystick.y) < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = CENTRE;
    } else if ( joystick.y > DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = DOWN;
        lcd.setBrightness(1.0);
    } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = UP;
        lcd.setBrightness(1.0);
    } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = RIGHT;
        lcd.setBrightness(1.0);
    } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = LEFT;
        lcd.setBrightness(1.0);
    } else {
        joystick.direction = UNKNOWN;
    }
    //set flag for printing
    printFlag = 1;
}

///Turns off screen backlighting.
void screenOff()
{
    ///Sets lcd birghtness to 0.0.
    lcd.setBrightness(0.0);
}

///When idle, calls the funtion screenOff();
void idleMode()
{
    ///If user input detected, reset timer.
    if((buttonFlagA)||(buttonFlagB)||(joystick.direction)) { //call standby mode if idle
        int time = 60;
        standby.attach(&screenOff,time);
    }
}

int main()
{
    PHY_PowerDown();//powers down the Ethernet
    ledP = 1;//power LED on
    randomise();//randomises falling hazards (initial values only)
    calibrateJoystick();//get centred values of joystick
    pollJoystick.attach(&updateJoystick,1.0/10.0);//read joystick 10 times per second

    lcd.init();//initialise screen
    drawWelcome();//welcome screen
    lcd.clear();//clear screen

    buttonA.mode(PullDown);//pull down buttonA
    buttonB.mode(PullDown);//pull down buttonB

    int exitFlag = 0;//exit flag
    int mainOption = 0;//counter for main menu
    int exitOption = 1;//counter for exit menu

    read();//set high scores using flash memory
    deBounce1.start();//timer buttonA debounce
    deBounce2.start();//timer buttonB debounce
    timerGame.attach(&gameLoop,0.1);//timer game delay
    timerA.attach(&timerExpiredA, 0.1);//checks state of buttonA
    timerB.attach(&timerExpiredB, 0.1);//checks state of buttonB
    idleMode();//turns off screen if idle.

    while(1) {
        drawMainMenu();//draws main menu
        mainMenu(mainOption);//presents main menu options
        actionButtons();//sound light when buttons pressed

        // if 'Play Game' selected
        if ((mainOption == 0)&&(buttonFlagA)) {
            buttonFlagA = 0;
            buttonFlagB = 0;
            game(exitFlag, exitOption);//actual game
        }
        // if 'Scores' selected
        if((mainOption == 1)&&(buttonFlagA)) {
            buttonFlagA = 0;
            buttonFlagB = 0;
            scores();
        }
        // if 'option' selected
        if((mainOption == 2)&&(buttonFlagA)) {
            buttonFlagA = 0;
            buttonFlagB = 0;
            optionsMenu();
        }
        sleep();
    }
}