ELEC2645 (2019/20) / Mbed 2 deprecated ELEC2645_Project_el17c2w

Dependencies:   mbed

Engine/Engine.cpp

Committer:
Mousky
Date:
2020-05-27
Revision:
13:76bac89c447b
Parent:
12:2af7b4868033

File content as of revision 13:76bac89c447b:

#include "Engine.h"

Engine::Engine()
{
}

Engine::~Engine()
{
}

void Engine::init(int paddle_width,int paddle_height,int ball_size,int ball2_size,int speed,int thing_size, int repaddle_size)
{
    // initialise the game parameters
    _paddle_width = paddle_width;
    _paddle_height = paddle_height;
    _ball_size = ball_size;
    _ball2_size = ball2_size; // add another ball when score reaches 5
    _thing_size = thing_size;
    _repaddle_size = repaddle_size;
    _speed = speed;
    
    // x position on screen - WIDTH is defined in N5110.h
    _p1y = HEIGHT - GAP;
    // puts paddles and ball in middle
    _p1.init(HEIGHT/2,_p1y,_paddle_width);
    
    _ball.init(_ball_size,_speed);
    _ball2.init(_ball2_size,_speed);
    _thing.init(_thing_size);
}

void Engine::read_input(Gamepad &pad)
{
    _d = pad.get_direction();
    _mag = pad.get_mag();
}

///////////////////////  Draw the graph  ///////////////////////////////////////

void Engine::draw(N5110 &lcd)
{
    // draw the elements in the LCD buffer
    // pitch
    lcd.drawRect(0,0,WIDTH,HEIGHT,FILL_TRANSPARENT);
    lcd.drawLine(20,30,70,30,FILL_BLACK);    //the first reflection board.
    lcd.drawLine(25,22,45,22,FILL_BLACK);   // the second reflection board.
    lcd.drawLine(55,25,75,25,FILL_BLACK);    // the third reflection board.
    
    //score
    print_scores(lcd);
    //draw paddles
    _p1.draw(lcd);
    // draw ball1, thing and repaddle
    _ball.draw(lcd);
    _ball2.draw(lcd);
    _thing.draw(lcd);
    /// _repaddle.draw(lcd);
}

//////////////////////////////UPDATE/////////////////////////////////////////////
void Engine::update(N5110 &lcd,Gamepad &pad)
{
    check_goal(pad);
    check_ball2_goal(pad);
    
    // important to update paddles and ball before checking collisions so can
    // correct for it before updating the display
    _p1.update(_d,_mag);
    
    _ball.update();
    /////// check  ball///////////////
    check_wall_collision(pad);
    check_paddle_collisions(pad);
    check_thing_collisions(pad);
    check_reflection(pad);
    
    ///////////////Ball2///////////////
    _ball2.update();
    // get the scores
    int p_score = _p1.get_score();
    Vector2D ball2_pos = _ball2.get_pos();
    Vector2D ball2_velocity = _ball2.get_velocity();   
    Vector2D thing_pos = _thing.get_pos();
    // if the score is bigger than 5, than add another ball
    if (p_score < 5) {
        ball2_pos.x = thing_pos.x;
        ball2_pos.y = thing_pos.y;
        ball2_velocity.x = 0;
        ball2_velocity.y = 0;
        
        _ball2.set_velocity(ball2_velocity);
        _ball2.set_pos(ball2_pos); 
    }
    // the another ball will begin to move in a random direction
    else if (p_score == 5) {
        ball2_velocity.x = _speed * (2*(rand() % 2) - 1);
        ball2_velocity.y = _speed * (2*(rand() % 2) - 1);
        _ball2.set_velocity(ball2_velocity);
        while (_p1.get_score() == 5) {
            read_input(pad);
            check_goal(pad);
            check_ball2_goal(pad);
            _p1.update(_d,_mag);
            _ball.update();
            check_wall_collision(pad);
            check_paddle_collisions(pad);
            check_thing_collisions(pad);
            check_reflection(pad);
            _ball2.update();
            check_ball2(pad);  
            lcd.clear();  
            draw(lcd);
            lcd.refresh();
            wait(1.0f/8);
        }
    }
    check_ball2(pad); // check the states of ball2 for updating
}

////////////////////////////////////////////////////////////////////////////////

/////////////////////////////CHECK BALL1////////////////////////////////////////

// set the refrection of the board and speed up the ball
// two ways of refrection: x-direction and y-direction
void Engine::check_reflection(Gamepad &pad)
{   
    Vector2D ball_pos = _ball.get_pos();
    Vector2D ball_velocity = _ball.get_velocity();
    if ((ball_pos.x >= 20) && (ball_pos.x <= 70) && (ball_pos.y <= 31) && (ball_pos.y >= 29)) {
        ball_velocity.x =  ball_velocity.x*2;
        // audio feedback
        pad.tone(750.0,0.1);    
    }
    else if ((ball_pos.x >=25) && (ball_pos.x <= 45) && (ball_pos.y >= 21) && (ball_pos.y <= 23)){
        ball_velocity.y =  ball_velocity.y*2;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    else if ((ball_pos.x >=55) && (ball_pos.x <= 75) && (ball_pos.y >= 24) && (ball_pos.y <= 26)){
        ball_velocity.y =  ball_velocity.y*2;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    _ball.set_velocity(ball_velocity);
    _ball.set_pos(ball_pos);
}

void Engine::check_wall_collision(Gamepad &pad)
{
    // read current ball attributes
    Vector2D ball_pos = _ball.get_pos();
    Vector2D ball_velocity = _ball.get_velocity();
    
    //adjust to the original speed
    if (abs(ball_velocity.x) > _speed) {
        ball_velocity.x = ball_velocity.x / abs(ball_velocity.x) * 2; 
    }
    if (abs(ball_velocity.y) > _speed) {
        ball_velocity.y = ball_velocity.y / abs(ball_velocity.y) * 2; 
    }

    // check if hit top wall
    if (ball_pos.y <= 1) {  //  1 due to 1 pixel boundary
        ball_pos.y = 1;  // bounce off ceiling without going off screen
        ball_velocity.y = -ball_velocity.y;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    // check if hit bottom wall
    else if (ball_pos.x <= 1) {  //  1 due to 1 pixel boundary
        ball_pos.x = 1;  // bounce off ceiling without going off screen
        ball_velocity.x = -ball_velocity.x;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    else if (ball_pos.x >= 84) {  //  1 due to 1 pixel boundary
        ball_pos.x = 84;  // bounce off ceiling without going off screen
        ball_velocity.x = -ball_velocity.x;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    // update ball parameters
    _ball.set_velocity(ball_velocity);
    _ball.set_pos(ball_pos);
}

void Engine::check_paddle_collisions(Gamepad &pad)
{
    // read current ball attributes
    Vector2D ball_pos = _ball.get_pos();
    Vector2D ball_velocity = _ball.get_velocity();
    Vector2D p1_pos = _p1.get_pos();
    
    //adjust to the original speed
    if (abs(ball_velocity.x) > _speed) {
        ball_velocity.x = ball_velocity.x / abs(ball_velocity.x) * 2;
        }
    if (abs(ball_velocity.y) > _speed) {
        ball_velocity.y = ball_velocity.y / abs(ball_velocity.y) * 2; 
        }
    
    // see if ball has hit the paddle by checking for overlaps
    if (
        (ball_pos.y >= p1_pos.y) && //top
        (ball_pos.y <= p1_pos.y + 2) && //bottom
        (ball_pos.x >= p1_pos.x-4) && //left
        (ball_pos.x <= p1_pos.x + 10)  //right
    ) {
        // if it has, fix position and reflect x velocity
        ball_pos.x = p1_pos.x ;
        ball_velocity.y = -ball_velocity.y;
        // audio feedback
        pad.tone(1000.0,0.1);
    }
    // write new attributes
    _ball.set_velocity(ball_velocity);
    _ball.set_pos(ball_pos);
}

void Engine::check_thing_collisions(Gamepad &pad)
{
    // read current ball attributes
    Vector2D ball_pos = _ball.get_pos();
    Vector2D ball_velocity = _ball.get_velocity();
    // read current thing position
    Vector2D thing_pos = _thing.get_pos();
    
    //adjust to the original speed
    if (abs(ball_velocity.x) > _speed) {
        ball_velocity.x = ball_velocity.x / abs(ball_velocity.x) * 2; 
        }
    if (abs(ball_velocity.y) > _speed) {
        ball_velocity.y = ball_velocity.y / abs(ball_velocity.y) * 2; 
        }
    
    // see if ball has hit the thing by checking for overlaps
    if (
        (ball_pos.y >= thing_pos.y - 1) && //top
        (ball_pos.y <= thing_pos.y + 1) && //bottom
        (ball_pos.x >= thing_pos.x - 3) && //left
        (ball_pos.x <= thing_pos.x + 10)  //right
    ) {
        // if it has, fix position and reflect x velocity
        ball_pos.x = thing_pos.x ;
        ball_velocity.y = -ball_velocity.y;
        // audio feedback
        pad.tone(1000.0,0.1);
    }
    // write new attributes
    _ball.set_velocity(ball_velocity);
    _ball.set_pos(ball_pos);
}

void Engine::check_goal(Gamepad &pad)
{
    Vector2D ball_pos = _ball.get_pos();
    Vector2D thing_pos = _thing.get_pos();
    if (
        (ball_pos.y >= thing_pos.y - 1) && //top
        (ball_pos.y <= thing_pos.y + 1) && //bottom
        (ball_pos.x >= thing_pos.x - 3) && //left
        (ball_pos.x <= thing_pos.x + 10)  //right
    ) {
        _p1.add_score();
        _thing.init(_thing_size);
        pad.tone(1500.0,0.5);
        pad.leds_on();
        wait(0.5);
        pad.leds_off();     
    }
    if (ball_pos.y >= HEIGHT) {
        _ball.init(_ball_size,_speed);
        pad.tone(1500.0,0.5);
        pad.leds_on();
        wait(0.5);
        pad.leds_off();
    }
}

void Engine::print_scores(N5110 &lcd)
{
    // get scores from paddles
    int p1_score = _p1.get_score();
    // print to LCD i
    char buffer1[14];
    sprintf(buffer1,"%2d",p1_score);
    lcd.printString(buffer1,60,1);  // print at top-right corner
}

/////////////////////////////////////////////////////////////////////////////////
////////////////////     Check    Ball2      ////////////////////////////////////
void Engine::check_ball2(Gamepad &pad)
{
    check_ball2_wall_collision(pad);
    check_ball2_paddle_collisions(pad);
    check_ball2_thing_collisions(pad);
    check_ball2_reflection(pad);
}


////////////// Check the states of the second ball  /////////////////////////////
void Engine::check_ball2_reflection(Gamepad &pad)
{   
    Vector2D ball2_pos = _ball2.get_pos();
    Vector2D ball2_velocity = _ball2.get_velocity();
    if ((ball2_pos.x >= 20) && (ball2_pos.x <= 70) && (ball2_pos.y >= 29)&& (ball2_pos.y <= 31)) {
        ball2_velocity.x =  ball2_velocity.x*2;
        // audio feedback
        pad.tone(750.0,0.1);    
    }
    else if ((ball2_pos.x >=25) && (ball2_pos.x <= 45) && (ball2_pos.y >= 21) && (ball2_pos.y <= 23)) {
        ball2_velocity.y =  ball2_velocity.y*2;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    else if ((ball2_pos.x >=55) && (ball2_pos.x <= 75) && (ball2_pos.y >= 24) && (ball2_pos.y <= 26)) {
        ball2_velocity.y =  ball2_velocity.y*2;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    _ball2.set_velocity(ball2_velocity);
    _ball2.set_pos(ball2_pos);
}


void Engine::check_ball2_wall_collision(Gamepad &pad)
{
    // read current ball attributes
    Vector2D ball2_pos = _ball2.get_pos();
    Vector2D ball2_velocity = _ball2.get_velocity();
    
    //adjust to the original speed
    if (abs(ball2_velocity.x) > _speed) {
        ball2_velocity.x = ball2_velocity.x / abs(ball2_velocity.x) * 2; 
        }
    if (abs(ball2_velocity.y) > _speed) {
        ball2_velocity.y = ball2_velocity.y / abs(ball2_velocity.y) * 2; 
        }
    
    // check if hit top wall
    if (ball2_pos.y <= 1) {  //  1 due to 1 pixel boundary
        ball2_pos.y = 1;  // bounce off ceiling without going off screen
        ball2_velocity.y = -ball2_velocity.y;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    // check if hit bottom wall
    else if (ball2_pos.x <= 1) {  //  1 due to 1 pixel boundary
        ball2_pos.x = 1;  // bounce off ceiling without going off screen
        ball2_velocity.x = -ball2_velocity.x;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    else if (ball2_pos.x >= 84) {  //  1 due to 1 pixel boundary
        ball2_pos.x = 84;  // bounce off ceiling without going off screen
        ball2_velocity.x = -ball2_velocity.x;
        // audio feedback
        pad.tone(750.0,0.1);
    }
    // update ball parameters
    _ball2.set_velocity(ball2_velocity);
    _ball2.set_pos(ball2_pos);
}

void Engine::check_ball2_paddle_collisions(Gamepad &pad)
{
    // read current ball attributes
    Vector2D ball2_pos = _ball2.get_pos();
    Vector2D ball2_velocity = _ball2.get_velocity();
    Vector2D p1_pos = _p1.get_pos();
    
    //adjust to the original speed
    if (abs(ball2_velocity.x) > _speed) {
        ball2_velocity.x = ball2_velocity.x / abs(ball2_velocity.x) * 2; 
        }
    if (abs(ball2_velocity.y) > _speed) {
        ball2_velocity.y = ball2_velocity.y / abs(ball2_velocity.y) * 2; 
        }
    
    // see if ball has hit the paddle by checking for overlaps
    if (
        (ball2_pos.y >= p1_pos.y) && //top
        (ball2_pos.y <= p1_pos.y + 2) && //bottom
        (ball2_pos.x >= p1_pos.x-4) && //left
        (ball2_pos.x <= p1_pos.x + 10)  //right
    ) {
        // if it has, fix position and reflect x velocity
        ball2_pos.x = p1_pos.x ;
        ball2_velocity.y = -ball2_velocity.y;
        // audio feedback
        pad.tone(1000.0,0.1);
    }
    // write new attributes
    _ball2.set_velocity(ball2_velocity);
    _ball2.set_pos(ball2_pos);
}

void Engine::check_ball2_thing_collisions(Gamepad &pad)
{
    // read current ball attributes
    Vector2D ball2_pos = _ball2.get_pos();
    Vector2D ball2_velocity = _ball2.get_velocity();
    // read current thing position
    Vector2D thing_pos = _thing.get_pos();
    
    //adjust to the original speed
    if (abs(ball2_velocity.x) > _speed) {
        ball2_velocity.x = ball2_velocity.x / abs(ball2_velocity.x) * _speed; 
        }
    if (abs(ball2_velocity.y) > _speed) {
        ball2_velocity.y = ball2_velocity.y / abs(ball2_velocity.y) * _speed; 
        }
    
    // see if ball has hit the thing by checking for overlaps
    if (
        (ball2_pos.y >= thing_pos.y - 1) && //top
        (ball2_pos.y <= thing_pos.y + 1) && //bottom
        (ball2_pos.x >= thing_pos.x - 3) && //left
        (ball2_pos.x <= thing_pos.x + 10)  //right
    ) {
        // if it has, fix position and reflect x velocity
        ball2_pos.x = thing_pos.x ;
        ball2_velocity.y = -ball2_velocity.y;
        // audio feedback
        pad.tone(1000.0,0.1);
    }
    // write new attributes
    _ball2.set_velocity(ball2_velocity);
    _ball2.set_pos(ball2_pos);
}

void Engine::check_ball2_goal(Gamepad &pad)
{
    Vector2D ball2_pos = _ball2.get_pos();
    Vector2D thing_pos = _thing.get_pos();
    if (
        (ball2_pos.y >= thing_pos.y - 1) && //top
        (ball2_pos.y <= thing_pos.y + 1) && //bottom
        (ball2_pos.x >= thing_pos.x - 3) && //left
        (ball2_pos.x <= thing_pos.x + 10)  //right
    ) {
        _p1.add_score();
        _thing.init(_thing_size);
        pad.tone(1500.0,0.5);
        pad.leds_on();
        wait(0.5);
        pad.leds_off();     
    }
    if (ball2_pos.y >= HEIGHT) {
        _ball2.init(_ball2_size,_speed);
        pad.tone(1500.0,0.5);
        pad.leds_on();
        wait(0.5);
        pad.leds_off();
    }
}


//////////////////////        The World       //////////////////////////////////
/* Every time we get 6 socres , the stand power of the world is triggered
*  The function of the world is used to pause the first ball for a while
*/


void Engine::The_world(N5110 &lcd, Gamepad &pad){
    
    Vector2D ball_pos = _ball.get_pos();
    Vector2D ball_velocity = _ball.get_velocity();
    // check the score whether reach the goal
    while ((_p1.get_score() % 6 == 0) && (_p1.get_score() != 0)){
        //set the sound of the world
        pad.tone(500.0,0.2);
        wait(0.2);
        pad.tone(800.0,0.2);
        pad.tone(1300.0,0.3);
        pad.tone(1600.0,0.3);
        
        // get the state of ball 1 before begin the world
        // make the ball 2 keep moving
        while((_p1.get_score() % 6 == 0) && (_p1.get_score() != 0)){
            read_input(pad);
            check_ball2_goal(pad);
            _p1.update(_d,_mag);
            _ball2.update();
            check_ball2(pad);
            
            // reflesh the lcd and make pause
            lcd.clear();  
            draw(lcd);
            lcd.refresh();
            wait(1.0f/8);

            }
        
    // restore the state of ball 1 after the world
    _ball.set_velocity(ball_velocity);
    }
}


//////////////////////       Bite the Dust    /////////////////////////////////
/* When the socre we get reaches 8, the stand power of bite the dust is triggered
*  The function of bite the dust is time backing tracking 
*/// The second ball will go back to the initial state

void Engine::bite_dust(N5110 &lcd, Gamepad &pad){
    // check the score whether reach the goal
    while ((_p1.get_score() % 8 == 0) && (_p1.get_score() != 0)){
        //set the sound of the world
        pad.tone(1000.0,0.2);
        wait(0.1);
        pad.tone(1000.0,0.2);
        wait(0.1);
        pad.tone(600.0,0.3);

        // get the state of ball 2
        Vector2D ball2_pos = _ball2.get_pos();
        Vector2D ball2_velocity = _ball2.get_velocity();
        Vector2D thing_pos = _thing.get_pos();     
        
        
        int distance_x = floor((ball2_pos.x - thing_pos.x)/3.0);
        int distance_y = floor((ball2_pos.y - thing_pos.y)/3.0);
        for (int i = 1; i <= 3; i ++){
            ball2_pos.x = ball2_pos.x - distance_x;
            ball2_pos.y = ball2_pos.y - distance_y;
            _ball2.set_pos(ball2_pos);

            // reflesh the lcd and make pause
            lcd.clear();  
            draw(lcd);
            lcd.refresh();
            wait(0.5);
        }
        while((_p1.get_score() % 8 == 0) && (_p1.get_score() != 0)){
            int p_score_b = _p1.get_score();
            read_input(pad);
            update(lcd,pad);
            lcd.clear();  
            draw(lcd);
            lcd.refresh();
            wait(1.0f/8);
        }
    }
}