James Cummins / Mbed 2 deprecated el17jnc

Dependencies:   mbed

main.cpp

Committer:
JamesCummins
Date:
2019-05-09
Revision:
40:a1cdb6ab08af
Parent:
39:dfc489594f11

File content as of revision 40:a1cdb6ab08af:

/*
ELEC2645 Embedded Systems Project
School of Electronic & Electrical Engineering
University of Leeds
Name: James Nathan Cummins
Username: el17jnc
Student ID Number: 201096364
Date: 22/03/19
*/

#include "Gamepad.h"
#include "mbed.h"
#include "N5110.h"
#include "BrickBreakerEngine.h"
#include "ClassicEngine.h"
#include "OptionsEngine.h"
#include "SDFileSystem.h"

#ifdef WITH_TESTS
#include "tests.h"
#endif

#define RADIUS 3


//Objects
Gamepad gamepad;
N5110 lcd(PTC9,PTC0,PTC7,PTD2,PTD1,PTC11);
ClassicEngine classic;
BrickBreakerEngine brick;
OptionsEngine opt;
Map map;
AnalogIn randnoise(PTB0);
FXOS8700CQ accelerometer(I2C_SDA,I2C_SCL);
Ball ball;
Pause pause;
SDFileSystem sd(PTE3,PTE1,PTE2,PTE4,"sd");

/** Enum for start menu options*/
enum StartOption{
    CLASSIC,
    BRICKBREAKER,
    OPTIONS
    };

/**Start selection struct*/
struct StartSelection{
    int output;                 /**<Integer output for line to print arrows to*/
    StartOption next_state[3];  /**<Array of enums for possible next start option*/
    };

//Methods
void startscreen();
StartOption menu();
void init();
void print_start_menu(int output);
void classic_mode();
void brickbreaker_mode();
void options_menu();


////////////////Main Function/////////////////

int fps = 16;   //declared globally so it doesn't have to be passed to
                //the different game mode functions
int main(){
    init();             //first initialise all objects   
     
#ifdef WITH_TESTS                                   //run tests to check code is correct
    int test_failures = no_of_tests_failed();
    if(test_failures > 0) return test_failures;      //main function returns no of failures
#endif

    startscreen();      //then display the introductory screen
    while(1){                //keep game running until power is removed
        StartOption choice_selected = menu();           //get which mode user wants from the start menu
        if(choice_selected == CLASSIC){ classic_mode();}                        //jump to a game mode once
        if(choice_selected == BRICKBREAKER){ brickbreaker_mode();}              //its respective enum is received
        if(choice_selected == OPTIONS){ options_menu();}                        //from the start menu
    }
}
  
  
  
//////////////Start up functions///////////////////

void init(){                    //initialise all objects in the game
    gamepad.init();
    lcd.init();                 //some objects are initialised again elsewhere
    lcd.setContrast(0.55);      //e.g. to reset a game mode when restart is pressed
    classic.init(ball, map);
    brick.init(RADIUS, ball);
    opt.init();
    map.init();
    pause.init();
    accelerometer.init();
    sd.disk_initialize();
    wait(1);
}
    
void startscreen() {
    lcd.clear();
    char gamename[] = {'L', 'A', 'B', 'Y', 'R', 'I', 'N', 'T', 'H', ' ', ' ', '\0'};    //char array containing the game title
    int i = 0;
    for(int a = 0; a < 35; a++){        //from first letter position to end of the screen at 2 pixel intervals
        lcd.clear();
        lcd.drawCircle(24+2*a, 21, 3, FILL_BLACK);      //move the ball 2 pixels at a time
        for (i = 0; i < a/3; i++) {
            lcd.printChar(gamename[i], 15+i*6, 2);      //print the next letter in game title once the ball has moved past
            lcd.refresh();                              //display all contents on LCD display
            }
        wait_ms(50);
    }
    lcd.printString("Press start", 9, 4);       //instruct user how to advance
    lcd.printString("to play >", 15, 5);
    lcd.refresh(); 
    bool advance = false;
    while (!advance){                                       //check for user advancing
        if (gamepad.check_event(gamepad.START_PRESSED)){
            lcd.clear();        //if user presses start, clear screen
            lcd.refresh();      //before advancing to start menu
            advance = true;
            }
        else { advance = false; }
    }
}

StartOption menu(){    
    StartSelection fsm[3] = {       //state machine to power start menu
        {0,{OPTIONS,BRICKBREAKER,CLASSIC}},     //next_state[0] always holds the option above the current one
        {2,{CLASSIC,OPTIONS,BRICKBREAKER}},     //next_state[1] always holds the option below the current one
        {4,{BRICKBREAKER,CLASSIC,OPTIONS}}      //next_state[2] always holds the current option
    };
    StartOption state = CLASSIC;  //start with the arrow on the top option
    int next = 2;  //next_state = 2 so that by default it doesn't change arrow position
    while(!(gamepad.check_event(gamepad.A_PRESSED))){     //select choice with A
        state = fsm[state].next_state[next];
        lcd.clear();
        if(gamepad.get_direction() == N){ next = 0;}            //move arrow up
        else if(gamepad.get_direction() == S){ next = 1;}       //move arrow down
        else {next = 2;}                                        //keep arrow in same position
        print_start_menu(fsm[state].output);    //print on LCD display
        lcd.refresh();
        wait(0.25);     //longer wait than 1/fps to reduce the impact of button bounce
    }
    lcd.clear();
    lcd.refresh();      //clear display before moving on to a game mode
    return state;       //tell main which game mode to run
}

void print_start_menu(int output){
        lcd.printString(">", 0, output);
        lcd.printString("Classic", 36, 0);
        lcd.printString("BrickBreak", 18, 2);
        lcd.printString("Options", 36, 4);
        lcd.printString("(A = Select)", 12, 5);
}

//////////////////Game Mode Functions////////////////////////////////

void classic_mode(){
    classic.init(ball, map);
    pause.classic_help(gamepad, lcd);       //display how to play instructions before start of game
    bool collision = false;                 //used to determine whether to keep iterating or not
    while(!(collision)){
        classic.classic_update(ball, accelerometer, map);       //methods to update the game
        lcd.clear();                                            //and render it as the user plays
        classic.classic_draw(lcd, map, ball);
        lcd.refresh();
        wait(1/fps);
        if(gamepad.check_event(gamepad.BACK_PRESSED)){                  //check for use of the pause menu
            PauseOption choice = pause.pause_menu(gamepad, lcd, fps);   //retrieve user's input
            if(choice == RESUME){}                                          //the different options must be processed
            if(choice == RESTART){ classic.init(ball, map); }               //in main.cpp as they have different 
            if(choice == QUIT){ break; }                                    //return types so can't be passed into main
            if(choice == HELP){ pause.classic_help(gamepad, lcd); }         //by one public method in pause.h
        }
        if(classic.finished()){         //check if the game has been completed
            classic.mode_complete(lcd, gamepad, fps);       //display time taken and break from game mode
            break;
        }
        if(map.check_wall_collision(gamepad, ball)){    //end the game if collision with wall
            collision = classic.mode_failed(lcd, gamepad, ball, map);   //gives the option to restart the game (and stay in while loop) or quit
            if(!(collision)){ classic.init(ball, map); }
        }
    }
}

void brickbreaker_mode(){
    pause.brickbreaker_help(gamepad, lcd);      //display instructions before start of game
    for(int i = 0; i < 45*fps; i++){
        if(i == 1){ brick.set_score(0); }       //reset score when game restarts 
        ball.read_input(accelerometer);         //methods to continue updating and rendering the game
        ball.update();
        /*Vector2D position = _ball.get_position();
        printf("ball_x = %f | ball_y = %f\n", position.x, position.y);  //note: running with tests causes the game to run slow and take ~2min30s*/
        brick.check_square_collision(randnoise, ball);
        lcd.clear();
        brick.brickbreaker_draw(lcd, ball);
        lcd.refresh();
        wait_ms(1000/fps);
        if(gamepad.check_event(gamepad.BACK_PRESSED)){                      //check for use of the pause menu
            PauseOption choice = pause.pause_menu(gamepad, lcd, fps);       //Get the user's selection
            i = pause.brickbreaker_action(choice, gamepad, lcd, i, fps);    //returns which frame to jump to
        }
        brick.time_warning(gamepad, i, fps);    //Use LEDs to display how much time is left
    }
    brick.end(gamepad, lcd);                    //display the 'time up' screen with the user score
    brick.write_high_scores();                  //update high scores accordingly
}

void options_menu(){
    Option choice = BRIGHTNESS;     //variable to store the selected option initialised to the top item in list
    while(!(gamepad.check_event(gamepad.A_PRESSED))){
        lcd.clear();                                    //keep rendering and updating the position of the
        opt.display_options(lcd);                       //menu arrows until a choice is made
        choice = opt.option_selection(gamepad, lcd);
        lcd.refresh();
        wait(0.2);
    }
    if(choice == BRIGHTNESS){ opt.change_brightness(gamepad, lcd); }            //each menu option called by its
    if(choice == BALL_SPEED){ opt.change_ball_speed(gamepad, lcd, ball); }      //respective enum and a method in
    if(choice == HIGH_SCORES){ opt.view_high_scores(gamepad, lcd); }            //options engine processes the option
}