ELEC2645 (2019/20) / Mbed 2 deprecated ELEC2645_PROJECT_EL18KK

Dependencies:   mbed

Game/Game.cpp

Committer:
KaifK
Date:
2020-05-26
Revision:
28:215e8327d4e6
Parent:
26:04b2d7742a84
Child:
29:532e5c371be4

File content as of revision 28:215e8327d4e6:

#include "Game.h"

SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd");
Ticker ticker;
FILE *fp;

volatile int sound_timer_flag = 0;
volatile int sound_sample = 0;
volatile int level_flag = 1; //initially set

void timer_isr();

Game::Game(N5110 &lcd, Gamepad &pad, Ball &ball) {
    _lcd = &lcd;
    _pad = &pad;
    _ball = &ball;
}
Game::~Game() {}

void Game::init() {
    level_flag = 1;
    _is_goal = false;
    _lives = 3;
    _new_lives_threshold = 0;
    _level = 1;
    _score = 0;
    _speed = 0.30f;
    _new_speed_threshold = 0.00f;
    _x_val = 42.00f;
    _y_val = 0.00f;
    _shot_x = 0;
    _shot_y = 0;
    //Initialising Highscore
    fp = fopen("/sd/highscore.txt", "r"); //check for file
    if (fp == NULL) { // if it can't open the file (file does not exist)
        printf("No exisiting highscore!\n");
        set_highscore(0); // initialise highscore to zero
    } else { // if file exists, read highscore and initialise to that value
        fscanf(fp, "%d", & _highscore);
        printf("Initial Highscore found: %d\n", _highscore);
        set_highscore(_highscore);
        fclose(fp); // ensure you close the file after reading
    }
}
void Game::play() {
    // if all live are lost game ends
    while (_lives > 0) {
        updateSpeed();
        updateLives();
        updateLevel();
        readInput();
        if (_is_goal) { _score++; } 
        else {
            //ball should not go through obstacle hence y value limited to 21
            //this is done after calculations to ensure accurate isGoal checking
            if (_shot_y >= 9 && _shot_y <= 19) { _shot_y = 21; }
            if (_shot_y <= 5) { _shot_y = 3; }
            _lives--;
        }
        _ball -> playShot(_shot_x, _shot_y);
        print_goal_message((int) _is_goal);
    }
    if (_score > _highscore) { set_highscore(_score); }
    _pad -> leds(0.0); //turn of all leds 
}

void Game::readInput() {
    _pad -> reset_buttons(); //forced debounce/reset
    _x_val = 42; //reset x_val
    _y_val = 0; //reset y_val
    while (!_pad -> A_pressed()) {
        pointer_input();
    }
    _pad -> reset_buttons(); //forced debounce/reset
    while (!_pad -> B_pressed()) {
        power_meter_input();
    }
    convert_to_shot_x();
    convert_to_shot_y();
    _is_goal = _ball -> isGoal(_level, _shot_x, _shot_y);
    //printf("x val = %.2f \n",_x_val);
    //printf("shot_x val = %d \n",_shot_x);
    //printf("y val = %.2f \n",_y_val);
    //printf("shot_y val = %d \n",_shot_y);
}
void Game::updateLives() {
    int val[6] = {0, 0, 0, 0, 0, 0};
    // +1 life if score hits a multiple of 5 provided less than 3 lives left
    //update threshold so lives don't  increase if score stays same
    if (_score % 5 == 0 && _score != _new_lives_threshold && _lives != 3) {
        _lives++;
        _new_lives_threshold = _score;
        playGoalSound(4);
    }
    //printf("Lives = %d \n", _lives);
    switch (_lives) {
    case 0:
        break;
    case 1: //red leds only
        val[0] = val[3] = 1;
        break;
    case 2: //red and yellow leds
        val[0] = val[1] = val[3] = val[4] = 1;
        break;
    case 3: //all leds on
        for (int i = 0; i < 6; i++) { val[i] = 1; }
        break;
    default:
        error("Invalid Number of Lives");
        break;
    }
    updateLeds(val);
}
void Game::updateScore() {
    char buffer[12];
    sprintf(buffer, "%d", _score);
    _lcd -> printString(buffer, 2, 5);
}
void Game::updateSpeed() {
    if (abs(_speed) <= 3) { // max speed = 3
        if (_score % 3 == 0 && _score != _new_speed_threshold) {
            // add 0.2 everytime score hits a multiple of 3
            _speed = abs(_speed) + 0.20f; 
            //update threshold so speed does not increase if score stays same
            _new_speed_threshold = _score;
        }
    }
    //printf("speed = %.2f \n", _speed);
    //printf("score = %d \n", _score);
}
void Game::updateLevel() {
    _level = random_level_gen(10); //generate random level
    if (level_flag == 1) { //first level is always level 1
        level_flag = 0; //flag always stays 0 after first time
        _level = 1;
    }
    _ball -> set_level(_level); //set level
    //printf("Level (in game) = %d \n", _level); 
}
void Game::updateLeds(int val[6]) {
    _pad -> led(1, val[0]);
    _pad -> led(2, val[1]);
    _pad -> led(3, val[2]);
    _pad -> led(4, val[3]);
    _pad -> led(5, val[4]);
    _pad -> led(6, val[5]);
}
void Game::playGoalSound(int sound) {
    sound_sample = 0;
    float val = 0.0f;
    int NUM_ELEMENTS = 0;
    ticker.attach( & timer_isr, 226e-7); //sample rate of sound effects
    // set number of elements based on sound required
    if (sound == 1) { NUM_ELEMENTS = NUM_ELEMENTS_1; }
    else if (sound == 2) { NUM_ELEMENTS = NUM_ELEMENTS_3; } 
    else if (sound == 3) { NUM_ELEMENTS = NUM_ELEMENTS_2; } 
    else if (sound == 4) { NUM_ELEMENTS = NUM_ELEMENTS_4; }
    while (sound_sample <= NUM_ELEMENTS) {
        if (sound_timer_flag == 1) {
            sound_timer_flag = 0;
            //play sound effect based on input value 
            // convert from 0 to 255 to 0.0 to 1.0
            if (sound == 1) { val = float(miss[sound_sample]) / 256.0f; } 
            else if (sound == 2) { val = float(goall[sound_sample]) / 256.0f; }
            else if (sound == 3) { val = float(over[sound_sample]) / 256.0f; }
            else if (sound == 4) { val = float(life[sound_sample]) / 256.0f; }
            _pad -> write_dac(val); // write to DAC
            sound_sample++; // move onto next sample
        }
    }
}
void Game::set_highscore(int score) {
    _highscore = score;
    fp = fopen("/sd/highscore.txt", "w");
    if (fp == NULL) { // if it can't open the file then print error message
        printf("Error! Unable to open file!\n");
    } else { // opened file so can write
        printf("Writing to file highscore: %d \n", _highscore);
        fprintf(fp, "%d", _highscore); // ensure data type matches
        printf("Done.\n");
        fclose(fp); // ensure you close the file after writing
    }
}
int Game::get_highscore() {
    fp = fopen("/sd/highscore.txt", "r");
    if (fp == NULL) { // if it can't open the file then print error message
        printf("Error! Unable to open file!\n");
    } else { // opened file so can write
        fscanf(fp, "%d", & _highscore); // ensure data type matches - note address operator (&)
        //printf("Read %d from file.\n",_highscore);
        fclose(fp); // ensure you close the file after reading
    }
    int val = _highscore;
    return val;
}
void Game::pointer_input() {
    updateScore();
    _ball -> displayBackground();
    _ball -> drawBall(BALL_INIT_X, BALL_INIT_Y, 6);
    //draw aim pointer
    _lcd -> drawLine(WIDTH / 2 - 5, 41, _x_val, HEIGHT / 2 + 7, 1); //left side
    _lcd -> drawLine(WIDTH / 2 + 5, 41, _x_val, HEIGHT / 2 + 7, 1); //right side
    _lcd -> drawLine(WIDTH / 2 + 5, 41, WIDTH / 2 - 5, 41, 1); //base
    _x_val += _speed;
    //printf("dir = %d \n", _speed);
    if ((int) _x_val >= 70) {
        _speed = (-1 * _speed);
    }
    //pointer points out of screen i.e. 70=84 therefore, switch direction 
    //value found through trial and error using print statement
    else if ((int) _x_val <= 12) {
        _speed = abs(_speed);
    }
    _lcd -> refresh();
    _lcd -> clear();
    wait(0.01);
}
void Game::power_meter_input() {
    updateScore();
    _ball -> displayBackground();
    _ball -> drawBall(BALL_INIT_X, BALL_INIT_Y, 6);
    //keep direction of pointer
    _lcd -> drawLine(WIDTH / 2 - 5, 41, _x_val, HEIGHT / 2 + 7, 1);
    _lcd -> drawLine(WIDTH / 2 + 5, 41, _x_val, HEIGHT / 2 + 7, 1);
    _lcd -> drawLine(WIDTH / 2 + 5, 41, WIDTH / 2 - 5, 41, 1);
    //fill and empty power meter
    _lcd -> drawRect(77, 27, 6, _y_val, FILL_BLACK);
    _y_val += _speed / 2;
    if ((int) _y_val >= 20) { //power meter full
        _speed = (-1 * _speed);
    } 
    else if ((int) _y_val <= 0) { // power meter empty
        _speed = abs(_speed);
    } 
    _lcd -> refresh();
    _lcd -> clear();
    wait(0.01);
}
void Game::convert_to_shot_x() {
    //convert from range 12-70 (range of pointer) to 0-84 (range of screen)
    _shot_x = ((((int) _x_val - 12) * (84)) / (70 - 12));
    //ball misses goal completely
    if (_shot_x <= 7) {
        _shot_x = 0;
    } else if (_shot_x >= 77) {
        _shot_x = 84;
    }
}
void Game::convert_to_shot_y() {
    _shot_y = ((((int) _y_val) * (24)) / (20));
    if( _shot_y >= 21) {
        _shot_y = 28; // Very little power, so ball does not reach goal
    } 
}
int Game::random_level_gen(int limit) {
    int number = (rand() % limit) + 1; //random level between 1-limit
    return number;
}
void Game::print_goal_message(int n) {
    _ball -> displayBackground();
    _lcd -> drawRect(0, 14, 84, 11, FILL_WHITE); //white background
    _lcd -> drawRect(0, 14, 84, 11, FILL_TRANSPARENT); //black outline

    switch (n) {
    case 0:
        _lcd -> printString("MISS!", 30, 2);
        break;
    case 1:
        _lcd -> printString("GOAL!", 30, 2);
        break;
    }
    _lcd -> refresh();
    wait(0.5);
    // adding 1 offsets the range of inputs from 0-1 to 1-2 (needed for function)
    playGoalSound((int) _is_goal + 1);
    wait(1);
}
void timer_isr() {
    sound_timer_flag = 1; // set flag in the isr   
}