Joe Shotton / Mbed 2 deprecated ELEC2645_Project_ll16j23s

Dependencies:   mbed ll16j23s_test_docs

SnakeEngine/SnakeEngine.cpp

Committer:
JoeShotton
Date:
2020-05-26
Revision:
10:a2d643b3c782
Parent:
9:0571880085cc

File content as of revision 10:a2d643b3c782:

#include "SnakeEngine.h"

SnakeEngine::SnakeEngine() {
    //constructor
    _menu_select = 0;
    _map_select = 0;
    _pot2 = 0.5;
    _death = false;
    score = 0;

}

SnakeEngine::~SnakeEngine()
{
//destructor
}
 
void SnakeEngine::game_init(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){
    game_reset(); //resets game
    transition_black(lcd); //transition animations
    transition_white(lcd);
    map_run(lcd); //displays map
    lcd.refresh();
    _food.init(pad, lcd, mag); //initialises food (including new seed for random function
    _body.init();   //initialises body
    menu_flash(pad, 3); //flashes LEDs
    game_state = 3;     //sets game_state to the game itself, which changes the active while loop in main.cpp
} 


void SnakeEngine::game_run(Gamepad &pad, N5110 &lcd){
    map_run(lcd); //displays the map and calculates relevant map collisions
    _body.run(pad, lcd, _death);    
    _food.run(lcd);   
    snake_food_collision(pad, lcd);
    if (_death == true) {       //checks death (either snake-snake collision or snake-wall-collision)
        //printf("Dead!");        
        game_state = 4;         //sets game_state to death, which changes the active while loop in main.cpp
        death_init(pad, lcd);   //displays death menu
    }
    //snake-wall collision 
} 

void SnakeEngine::snake_food_collision(Gamepad &pad, N5110 &lcd) {
    if (_food._x == _body._x_head && _food._y == _body._y_head){ //if coords of food and head match
        //printf("FOOD!");
        pad.led(3,0.9); //turns on green LEDs
        pad.led(6,0.9);
        while (_food.rand_pos(pad, lcd) == false){  //while new food coordinates are in the snake/walls
            _food.rand_pos(pad, lcd);               //choose new coords by rerunning the function
            //printf("Reselected food position\n");
        }
        _body.add_length(5); //sets length increase
        score++;             
        pad.led(3,0.0);      //turns off green LEDs
        pad.led(6,0.0);
    }
}

void SnakeEngine::map_run(N5110 &lcd) {
    switch(_map_select+1) { //selects chosen map
        case 2:
            map2_draw(lcd);
            snake_map2_collision();    
            break;
        case 3:
            map3_draw(lcd);
            snake_map3_collision();   
            break;
        case 4:
            map4_draw(lcd);
            snake_map4_collision();  
            break;
    }
}

void SnakeEngine::snake_map2_collision() { //ring map
    if (_body._x_head < 2 || _body._x_head > 81 || _body._y_head < 2 || _body._y_head > 45){ //if snakehead coords exceed the ring coords
        _death = true; 
    }
}

void SnakeEngine::snake_map3_collision() { //cross map
    if ((_body._x_head == 40 || _body._x_head == 42) && (_body._y_head < 16 || _body._y_head > 30)){ 
    //if head is at the either of the N/S walls' x coords, and above (y=18) or below (y=30), trigger death 
        _death = true; 
    } else if ((_body._x_head < 16 || _body._x_head > 66) && (_body._y_head == 22 || _body._y_head == 24)){
    //if head is at west of x=18 or east of x=66, and at either of the W/E wall's y coords, trigger death 
        _death = true; 
    }
}

void SnakeEngine::snake_map4_collision() { //lanes
    _map4_location = 0;
    if (_body._x_head == 8 || _body._x_head == 10 || _body._x_head == 40 || _body._x_head == 42 || _body._x_head == 72 || _body._x_head == 74){
         _map4_location += 1; //adjust tracker if x matches upper/lower walls
    } //printf("x matches upper/lower walls");
    if (_body._y_head < 12 || _body._y_head > 34){
         _map4_location += 3; //adjust tracker if y matches upper/lower walls
    } //printf("y matches upper/lower walls");
    if (_body._x_head == 24 || _body._x_head == 26 || _body._x_head == 56 || _body._x_head == 58){
         _map4_location += 2; //adjust tracker if x matches mid walls
    } //printf("x matches mid walls");
    if (_body._y_head > 14 && _body._y_head < 32){
         _map4_location += 2; //adjust tracker if y matches mid walls
    } //printf("y matches mid walls");
    if (_map4_location == 4){ //tracker will only be 4 if the correct combination of arguments is triggered - ie 1+3 (upper/lower walls) or 2+2 (mid walls)   
    _death = true; 
    } //printf("Wall collision");
}

void SnakeEngine::map2_draw(N5110 &lcd){ //rings
    lcd.drawRect(0, 0, 84, 48,FILL_TRANSPARENT); //outer pixels of ring
    lcd.drawRect(1, 1, 82, 46,FILL_TRANSPARENT); //inner pixels
}

void SnakeEngine::map3_draw(N5110 &lcd){ //cross
    lcd.drawRect(40, 0, 4, 16,FILL_BLACK); //N wall
    lcd.drawRect(68, 22, 16, 4,FILL_BLACK);//E wall
    lcd.drawRect(40, 32, 4, 16,FILL_BLACK);//S wall
    lcd.drawRect(0, 22, 16, 4,FILL_BLACK); //W wall
}

void SnakeEngine::map4_draw(N5110 &lcd){ //lanes   
    lcd.drawRect(8, 0,  4, 12,FILL_BLACK);//NW line
    lcd.drawRect(8, 36, 4, 12,FILL_BLACK);//SW line    
    lcd.drawRect(40, 0,  4, 12,FILL_BLACK);//N-mid line
    lcd.drawRect(40, 36, 4, 12,FILL_BLACK);//S-mid line
    lcd.drawRect(72, 0,  4, 12,FILL_BLACK);//NE line
    lcd.drawRect(72, 36, 4, 12,FILL_BLACK);//SE line 
    lcd.drawRect(24, 16, 4, 16,FILL_BLACK);//mid-W line
    lcd.drawRect(56, 16, 4, 16,FILL_BLACK);//mid-E line
}

void SnakeEngine::transition_black(N5110 &lcd) {
    for (int j = 0; j < 21; j += 4) {   //j iterator controls the size and location of current loop
        //printf("j: %d\n", j); 
        for (int i = 1; i < 84 - (2*j); i += 2) { //i iterator controls length of rectangles
            lcd.drawRect(j, j, 1 + i, 4, FILL_BLACK); //top horizontal rectangle grows to the right by adding i iterator to width              
            lcd.drawRect(83 - j - i, 44 - j, 1 + i, 4, FILL_BLACK);  //bottom horizontal rectangle grows to the left by subtracting i iterator from x position,
            //and adding i iterator to width
            wait_ms(5);   
            lcd.refresh(); //refreshes screen without clearing it to maintain previous loops' rectangles
        }
        for (int i = 1; i < 43 - (2*j); i += 2) { //i iterator controls length of rectangles
            lcd.drawRect(80 - j, 4 + j, 4, i,FILL_BLACK); //right vertical rectangle grows down by adding i iterator to height    
            lcd.drawRect(j, 44 - j - i, 4, i,FILL_BLACK); //left vertical rectangle grows up by subtracting i iterator from y position, adds i to height   
            wait_ms(5);   
            lcd.refresh(); //refreshes screen without clearing it to maintain previous loops
        }
    }
}

void SnakeEngine::transition_white(N5110 &lcd) {
    //functionally same as black transition function
    for (int j = 0; j < 21; j += 4) {   //j iterator controls the size and location of current loop
        //printf("j: %d\n", j); 
        for (int i = 1; i < 84 - (2*j); i += 2) { //i iterator controls length of rectangles
            lcd.drawRect(j, j, 1 + i, 4, FILL_WHITE); //top horizontal rectangle grows to the right by adding i iterator to width              
            lcd.drawRect(83 - j - i, 44 - j, 1 + i, 4, FILL_WHITE);  //bottom horizontal rectangle grows to the left by subtracting i iterator from x position,
            //and adding i iterator to width
            wait_ms(5);   
            lcd.refresh(); //refreshes screen without clearing it to maintain previous loops' rectangles
        }
        for (int i = 1; i < 43 - (2*j); i += 2) { //i iterator controls length of rectangles
            lcd.drawRect(80 - j, 4 + j, 4, i,FILL_WHITE); //right vertical rectangle grows down by adding i iterator to height    
            lcd.drawRect(j, 44 - j - i, 4, i,FILL_WHITE); //left vertical rectangle grows up by subtracting i iterator from y position, adds i to height   
            wait_ms(5);   
            lcd.refresh(); //refreshes screen without clearing it to maintain previous loops
        }
    }
}

void SnakeEngine::menu1_init(Gamepad &pad, N5110 &lcd){
         contrast(pad, lcd); //adjusts contrast before transitions
         transition_black(lcd); 
         transition_white(lcd);
         lcd.refresh();
         //printf("Menu 2\n");
         lcd.printString("SNAKE",27,0); //displays relevant text
         lcd.printString("Play",30,2);
         lcd.drawCircle(24,19,3,FILL_TRANSPARENT); //draws empty circles for option display 
         lcd.printString("Maps",30,3);
         lcd.drawCircle(24,27,3,FILL_TRANSPARENT);
         lcd.refresh();   
         menu_flash(pad, 2); //flashes orange LEDs
}

void SnakeEngine::menu1_select(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){    
    //printf("Menu 1\n");
    if (pad.X_held() == true){ //detect if 'up' selection
        _menu_select--; 
    } else if (pad.B_held() == true){ //detect if 'down' selection
        _menu_select++;
    }
    _menu_select = ((_menu_select % 2) + 2) % 2; //wrap around numbers, ie down on 1 goes to 0 and up on 0 goes to 1
    select_circles(lcd, _menu_select + 1); //draw black circle in selected option
    //printf("Option: %d\n", _menu_select + 1);
    
    if (pad.A_held() == true){ //if option 1 selected and 'A' held
        if (_menu_select == 0){
            game_init(pad, lcd, mag);      //Initialise game
        } else {   //otherwise must be option 2
            menu2_init(pad, lcd);    //Initialise menu 2
            menu_flash(pad, 2); //flashes orange LEDs
        }
    }
}

void SnakeEngine::menu2_init(Gamepad &pad, N5110 &lcd){
         //printf("Menu 2\n");
         lcd.clear();   
         lcd.printString("     MAPS",0,0);          //displays relevant text
         lcd.printString("Empty",30,2);
         lcd.drawCircle(24,19,3,FILL_TRANSPARENT); //draws empty circles for option display 
         lcd.printString("Ring",30,3);
         lcd.drawCircle(24,27,3,FILL_TRANSPARENT);
         lcd.printString("Cross",30,4);
         lcd.drawCircle(24,35,3,FILL_TRANSPARENT);
         lcd.printString("Lanes",30,5);
         lcd.drawCircle(24,43,3,FILL_TRANSPARENT);
         lcd.refresh();   
         game_state = 2;  //sets game_state to menu 2, which changes the active while loop in main.cpp
}

void SnakeEngine::menu2_select(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){   
    
    select_circles(lcd, _map_select+1);
    
    //printf("Menu 2\n");
    if (pad.X_held() == true){
        _map_select--;
    } else if (pad.B_held() == true){
        _map_select++; 
    } else if (pad.Y_held() == true){
        preview(pad, lcd); //allows short preview of map to be displayed
    } else if (pad.A_held() == true){
        game_init(pad, lcd, mag); //intialises game
    }
    //printf("Map: %d\n", _map_select);
    _map_select = ((_map_select % 4) + 4) % 4; //wrap around numbers, ie down on 3 goes to 0 and up on 0 goes to 3 
}


void SnakeEngine::death_init(Gamepad &pad, N5110 &lcd){
         _death = 0;         //resets death flag
         _menu_select = 0;   //resets menu select so 'play again' is default   
         transition_black(lcd);
         transition_white(lcd);
         //printf("Game over\n");
         lcd.printString("  GAME OVER",3,0); //displays relevant text
         lcd.printString("    Score:",0,1);
         char buffer[14];                    //creates a buffer for the score
         sprintf(buffer,"      %2d",score);   
         lcd.printString(buffer,0,2);
         lcd.printString("Again!",30,4);
         lcd.drawCircle(24,35,3,FILL_TRANSPARENT); //draws empty circles for option selection
         lcd.printString("Maps",30,5);
         lcd.drawCircle(24,43,3,FILL_TRANSPARENT);
         lcd.refresh();
         menu_flash(pad, 1);   //flashes red LEDs
} 

void SnakeEngine::death_select(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){
    //printf("Menu 1\n");
    if (pad.X_held() == true){ //detect if 'up' selection
        _menu_select--;
    } else if (pad.B_held() == true){ //detect if 'down' selection
        _menu_select++;
    }
    _menu_select = ((_menu_select % 2) + 2) % 2; //wrap around numbers, ie down on 2 goes to 1 and up on 1 goes to 2
    select_circles(lcd, _menu_select + 3); //draw black circle in selected option
    //printf("Option: %d\n", _menu_select + 1);
    
    if (pad.A_held() == true){ //if option 1 selected and 'A' held
        if (_menu_select == 0){
            game_init(pad, lcd, mag); 
            game_state = 3;   
        } else {
            menu2_init(pad, lcd);    
            game_state = 2;    
        }
    }
}  

void SnakeEngine::select_circles(N5110 &lcd, int line) {
    for(int i = 19; i < 52; i += 8) { //iterates over all the circle options' y values 
        lcd.drawCircle(24,i,2,FILL_WHITE); //remove previous options' black circles
    }       
    lcd.drawCircle(24, 11 + line * 8, 2, FILL_BLACK); //draws black circle in relevant option's blank circle 
    lcd.refresh();
    wait_ms(200);
}

void SnakeEngine::preview(Gamepad &pad, N5110 &lcd){
    lcd.clear(); 
    switch(_map_select + 1) { //selects relevant map
        case 1:
            lcd.clear(); 
            lcd.printString("(Empty)",21,2);
            break;
        case 2:
            map2_draw(lcd);
            //lcd.printString("Ring",30,2);
            break;   
        case 3:
            map3_draw(lcd);
            //lcd.printString("Cross",27,2);
            break;
        case 4:
            map4_draw(lcd);
            //lcd.printString("Spots",27,2);
            break;
        }
    lcd.refresh();
    pad.led(2, 1);
    pad.led(5, 1);
    wait_ms(1000); //displays for 1 second
    pad.led(2, 0);
    pad.led(5, 0);
    lcd.clear();
    menu2_init(pad, lcd); //re-initialises menu 2
}

void SnakeEngine::contrast(Gamepad &pad, N5110 &lcd){
    _pot2 = pad.read_pot2();
    lcd.setContrast(0.25 + _pot2 * 0.5); //maps pot value to range 0.25 - 0.75
    //printf("Contrast: %f\n", 0.25 + _pot2 * 0.5);  
}

void SnakeEngine::menu_flash(Gamepad &pad, int led){
    for(int i = 1; i < 7; i++){
        pad.led(led, i % 2); //flash left side LEDs - odd numbers become 1 to turn on, even numbers become 0 to turn off  
        pad.led(led + 3, i % 2); //flash right side LEDs    
        wait_ms(100);
    }   
}

void SnakeEngine::game_reset(){
    score = 0;
    _body.reset();
}