ELEC2645 (2019/20) / Mbed 2 deprecated ELEC2645_Project_ll16j23s

Dependencies:   mbed ll16j23s_test_docs

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SnakeEngine.cpp Source File

SnakeEngine.cpp

00001 #include "SnakeEngine.h"
00002 
00003 SnakeEngine::SnakeEngine() {
00004     //constructor
00005     _menu_select = 0;
00006     _map_select = 0;
00007     _pot2 = 0.5;
00008     _death = false;
00009     score = 0;
00010 
00011 }
00012 
00013 SnakeEngine::~SnakeEngine()
00014 {
00015 //destructor
00016 }
00017  
00018 void SnakeEngine::game_init(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){
00019     game_reset(); //resets game
00020     transition_black(lcd); //transition animations
00021     transition_white(lcd);
00022     map_run(lcd); //displays map
00023     lcd.refresh();
00024     _food.init(pad, lcd, mag); //initialises food (including new seed for random function
00025     _body.init();   //initialises body
00026     menu_flash(pad, 3); //flashes LEDs
00027     game_state = 3;     //sets game_state to the game itself, which changes the active while loop in main.cpp
00028 } 
00029 
00030 
00031 void SnakeEngine::game_run(Gamepad &pad, N5110 &lcd){
00032     map_run(lcd); //displays the map and calculates relevant map collisions
00033     _body.run(pad, lcd, _death);    
00034     _food.run(lcd);   
00035     snake_food_collision(pad, lcd);
00036     if (_death == true) {       //checks death (either snake-snake collision or snake-wall-collision)
00037         //printf("Dead!");        
00038         game_state = 4;         //sets game_state to death, which changes the active while loop in main.cpp
00039         death_init(pad, lcd);   //displays death menu
00040     } 
00041 } 
00042 
00043 void SnakeEngine::snake_food_collision(Gamepad &pad, N5110 &lcd) {
00044     if (_food.x == _body.head_x && _food.y == _body.head_y){ //if coords of food and head match
00045         //printf("FOOD!");
00046         pad.led(3,0.9); //turns on green LEDs
00047         pad.led(6,0.9);
00048         while (_food.rand_pos(pad, lcd) == false){  //while new food coordinates are in the snake/walls
00049             _food.rand_pos(pad, lcd);               //choose new coords by rerunning the function
00050             //printf("Reselected food position\n");
00051         }
00052         _body.add_length(5); //sets length increase
00053         score++;             
00054         pad.led(3,0.0);      //turns off green LEDs
00055         pad.led(6,0.0);
00056     }
00057 }
00058 
00059 void SnakeEngine::map_run(N5110 &lcd) {
00060     switch(_map_select + 1) { //selects chosen map
00061         case 2:
00062             map2_draw(lcd);
00063             snake_map2_collision();    
00064             break;
00065         case 3:
00066             map3_draw(lcd);
00067             snake_map3_collision();   
00068             break;
00069         case 4:
00070             map4_draw(lcd);
00071             snake_map4_collision();  
00072             break;
00073     }
00074 }
00075 
00076 void SnakeEngine::snake_map2_collision() { //ring map
00077     if (_body.head_x < 2 || _body.head_x > 81 || _body.head_y < 2 || _body.head_y > 45){ //if snakehead coords exceed the ring coords
00078         _death = true; 
00079     }
00080 }
00081 
00082 void SnakeEngine::snake_map3_collision() { //cross map
00083     if ((_body.head_x == 40 || _body.head_x == 42) && (_body.head_y < 16 || _body.head_y > 30)){ 
00084     //if head is at the either of the N/S walls' x coords, and above (y=18) or below (y=30), trigger death 
00085         _death = true; 
00086     } else if ((_body.head_x < 16 || _body.head_x > 66) && (_body.head_y == 22 || _body.head_y == 24)){
00087     //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 
00088         _death = true; 
00089     }
00090 }
00091 
00092 void SnakeEngine::snake_map4_collision() { //lanes
00093     _map4_location = 0;
00094     if (_body.head_x == 8 || _body.head_x == 10 || _body.head_x == 40 || _body.head_x == 42 || _body.head_x == 72 || _body.head_x == 74){
00095          _map4_location += 1; //adjust tracker if x matches upper/lower walls
00096     } //printf("x matches upper/lower walls");
00097     if (_body.head_y < 12 || _body.head_y > 34){
00098          _map4_location += 3; //adjust tracker if y matches upper/lower walls
00099     } //printf("y matches upper/lower walls");
00100     if (_body.head_x == 24 || _body.head_x == 26 || _body.head_x == 56 || _body.head_x == 58){
00101          _map4_location += 2; //adjust tracker if x matches mid walls
00102     } //printf("x matches mid walls");
00103     if (_body.head_y > 14 && _body.head_y < 32){
00104          _map4_location += 2; //adjust tracker if y matches mid walls
00105     } //printf("y matches mid walls");
00106     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)   
00107     _death = true; 
00108     } //printf("Wall collision");
00109 }
00110 
00111 void SnakeEngine::map2_draw(N5110 &lcd){ //rings
00112     lcd.drawRect(0, 0, 84, 48,FILL_TRANSPARENT); //outer pixels of ring
00113     lcd.drawRect(1, 1, 82, 46,FILL_TRANSPARENT); //inner pixels
00114 }
00115 
00116 void SnakeEngine::map3_draw(N5110 &lcd){ //cross
00117     lcd.drawRect(40, 0, 4, 16,FILL_BLACK); //N wall
00118     lcd.drawRect(68, 22, 16, 4,FILL_BLACK);//E wall
00119     lcd.drawRect(40, 32, 4, 16,FILL_BLACK);//S wall
00120     lcd.drawRect(0, 22, 16, 4,FILL_BLACK); //W wall
00121 }
00122 
00123 void SnakeEngine::map4_draw(N5110 &lcd){ //lanes   
00124     lcd.drawRect(8, 0,  4, 12,FILL_BLACK);//NW line
00125     lcd.drawRect(8, 36, 4, 12,FILL_BLACK);//SW line    
00126     lcd.drawRect(40, 0,  4, 12,FILL_BLACK);//N-mid line
00127     lcd.drawRect(40, 36, 4, 12,FILL_BLACK);//S-mid line
00128     lcd.drawRect(72, 0,  4, 12,FILL_BLACK);//NE line
00129     lcd.drawRect(72, 36, 4, 12,FILL_BLACK);//SE line 
00130     lcd.drawRect(24, 16, 4, 16,FILL_BLACK);//mid-W line
00131     lcd.drawRect(56, 16, 4, 16,FILL_BLACK);//mid-E line
00132 }
00133 
00134 void SnakeEngine::transition_black(N5110 &lcd) {
00135     for (int j = 0; j < 21; j += 4) {   //j iterator controls the size and location of current loop
00136         //printf("j: %d\n", j); 
00137         for (int i = 1; i < 84 - (2*j); i += 2) { //i iterator controls length of rectangles
00138             lcd.drawRect(j, j, 1 + i, 4, FILL_BLACK); //top horizontal rectangle grows to the right by adding i iterator to width              
00139             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,
00140             //and adding i iterator to width
00141             wait_ms(5);   
00142             lcd.refresh(); //refreshes screen without clearing it to maintain previous loops' rectangles
00143         }
00144         for (int i = 1; i < 43 - (2*j); i += 2) { //i iterator controls length of rectangles
00145             lcd.drawRect(80 - j, 4 + j, 4, i,FILL_BLACK); //right vertical rectangle grows down by adding i iterator to height    
00146             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   
00147             wait_ms(5);   
00148             lcd.refresh(); //refreshes screen without clearing it to maintain previous loops
00149         }
00150     }
00151 }
00152 
00153 void SnakeEngine::transition_white(N5110 &lcd) {
00154     //functionally same as black transition function
00155     for (int j = 0; j < 21; j += 4) {   //j iterator controls the size and location of current loop
00156         //printf("j: %d\n", j); 
00157         for (int i = 1; i < 84 - (2*j); i += 2) { //i iterator controls length of rectangles
00158             lcd.drawRect(j, j, 1 + i, 4, FILL_WHITE); //top horizontal rectangle grows to the right by adding i iterator to width              
00159             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,
00160             //and adding i iterator to width
00161             wait_ms(5);   
00162             lcd.refresh(); //refreshes screen without clearing it to maintain previous loops' rectangles
00163         }
00164         for (int i = 1; i < 43 - (2*j); i += 2) { //i iterator controls length of rectangles
00165             lcd.drawRect(80 - j, 4 + j, 4, i,FILL_WHITE); //right vertical rectangle grows down by adding i iterator to height    
00166             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   
00167             wait_ms(5);   
00168             lcd.refresh(); //refreshes screen without clearing it to maintain previous loops
00169         }
00170     }
00171 }
00172 
00173 void SnakeEngine::menu1_init(Gamepad &pad, N5110 &lcd){
00174          contrast(pad, lcd); //adjusts contrast before transitions
00175          transition_black(lcd); 
00176          transition_white(lcd);
00177          lcd.refresh();
00178          //printf("Menu 1\n");
00179          lcd.printString("SNAKE",27,0); //displays relevant text
00180          lcd.printString("Play",30,2);
00181          lcd.drawCircle(24,19,3,FILL_TRANSPARENT); //draws empty circles for option display 
00182          lcd.printString("Maps",30,3);
00183          lcd.drawCircle(24,27,3,FILL_TRANSPARENT);
00184          lcd.refresh();   
00185          menu_flash(pad, 2); //flashes orange LEDs
00186 }
00187 
00188 void SnakeEngine::menu1_select(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){    
00189     //printf("Menu 1\n");
00190     if (pad.X_held() == true){ //detect if 'up' selection
00191         _menu_select--; 
00192     } else if (pad.B_held() == true){ //detect if 'down' selection
00193         _menu_select++;
00194     }
00195     _menu_select = ((_menu_select % 2) + 2) % 2; //wrap around numbers, ie down on 1 goes to 0 and up on 0 goes to 1
00196     select_circles(lcd, _menu_select + 1); //draw black circle in selected option
00197     //printf("Option: %d\n", _menu_select + 1);
00198     
00199     if (pad.A_held() == true){ //if option 1 selected and 'A' held
00200         if (_menu_select == 0){
00201             game_init(pad, lcd, mag);      //Initialise game
00202         } else {   //otherwise must be option 2
00203             menu2_init(pad, lcd);    //Initialise menu 2
00204             menu_flash(pad, 2); //flashes orange LEDs
00205         }
00206     }
00207 }
00208 
00209 void SnakeEngine::menu2_init(Gamepad &pad, N5110 &lcd){
00210          //printf("Menu 2\n");
00211          lcd.clear();   
00212          lcd.printString("     MAPS",0,0);          //displays relevant text
00213          lcd.printString("Empty",30,2);
00214          lcd.drawCircle(24,19,3,FILL_TRANSPARENT); //draws empty circles for option display 
00215          lcd.printString("Ring",30,3);
00216          lcd.drawCircle(24,27,3,FILL_TRANSPARENT);
00217          lcd.printString("Cross",30,4);
00218          lcd.drawCircle(24,35,3,FILL_TRANSPARENT);
00219          lcd.printString("Lanes",30,5);
00220          lcd.drawCircle(24,43,3,FILL_TRANSPARENT);
00221          lcd.refresh();   
00222          game_state = 2;  //sets game_state to menu 2, which changes the active while loop in main.cpp
00223 }
00224 
00225 void SnakeEngine::menu2_select(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){   
00226     
00227     select_circles(lcd, _map_select+1);
00228     
00229     //printf("Menu 2\n");
00230     if (pad.X_held() == true){
00231         _map_select--;
00232     } else if (pad.B_held() == true){
00233         _map_select++; 
00234     } else if (pad.Y_held() == true){
00235         preview(pad, lcd); //allows short preview of map to be displayed
00236     } else if (pad.A_held() == true){
00237         game_init(pad, lcd, mag); //intialises game
00238     }
00239     //printf("Map: %d\n", _map_select);
00240     _map_select = ((_map_select % 4) + 4) % 4; //wrap around numbers, ie down on 3 goes to 0 and up on 0 goes to 3 
00241 }
00242 
00243 
00244 void SnakeEngine::death_init(Gamepad &pad, N5110 &lcd){
00245          _death = 0;         //resets death flag
00246          _menu_select = 0;   //resets menu select so 'play again' is default   
00247          transition_black(lcd);
00248          transition_white(lcd);
00249          //printf("Game over\n");
00250          lcd.printString("  GAME OVER",3,0); //displays relevant text
00251          lcd.printString("    Score:",0,1);
00252          char buffer[14];                    //creates a buffer for the score
00253          sprintf(buffer,"      %2d",score);   
00254          lcd.printString(buffer,0,2);
00255          lcd.printString("Again!",30,4);
00256          lcd.drawCircle(24,35,3,FILL_TRANSPARENT); //draws empty circles for option selection
00257          lcd.printString("Maps",30,5);
00258          lcd.drawCircle(24,43,3,FILL_TRANSPARENT);
00259          lcd.refresh();
00260          menu_flash(pad, 1);   //flashes red LEDs
00261 } 
00262 
00263 void SnakeEngine::death_select(Gamepad &pad, N5110 &lcd, FXOS8700CQ &mag){
00264     //printf("Menu 1\n");
00265     if (pad.X_held() == true){ //detect if 'up' selection
00266         _menu_select--;
00267     } else if (pad.B_held() == true){ //detect if 'down' selection
00268         _menu_select++;
00269     }
00270     _menu_select = ((_menu_select % 2) + 2) % 2; //wrap around numbers, ie down on 2 goes to 1 and up on 1 goes to 2
00271     select_circles(lcd, _menu_select + 3); //draw black circle in selected option
00272     //printf("Option: %d\n", _menu_select + 1);
00273     
00274     if (pad.A_held() == true){ //if option 1 selected and 'A' held
00275         if (_menu_select == 0){
00276             game_init(pad, lcd, mag); 
00277             game_state = 3;   
00278         } else {
00279             menu2_init(pad, lcd);
00280             menu_flash(pad, 2);    
00281             game_state = 2;    
00282         }
00283     }
00284 }  
00285 
00286 void SnakeEngine::select_circles(N5110 &lcd, int line) {
00287     for(int i = 19; i < 52; i += 8) { //iterates over all the circle options' y values 
00288         lcd.drawCircle(24,i,2,FILL_WHITE); //remove previous options' black circles
00289     }       
00290     lcd.drawCircle(24, 11 + line * 8, 2, FILL_BLACK); //draws black circle in relevant option's blank circle 
00291     lcd.refresh();
00292     wait_ms(200);
00293 }
00294 
00295 void SnakeEngine::preview(Gamepad &pad, N5110 &lcd){
00296     lcd.clear(); 
00297     switch(_map_select + 1) { //selects relevant map
00298         case 1:
00299             lcd.clear(); 
00300             lcd.printString("(Empty)",21,2);
00301             break;
00302         case 2:
00303             map2_draw(lcd);
00304             //lcd.printString("Ring",30,2);
00305             break;   
00306         case 3:
00307             map3_draw(lcd);
00308             //lcd.printString("Cross",27,2);
00309             break;
00310         case 4:
00311             map4_draw(lcd);
00312             //lcd.printString("Spots",27,2);
00313             break;
00314         }
00315     lcd.refresh();
00316     pad.led(2, 1);
00317     pad.led(5, 1);
00318     wait_ms(1000); //displays for 1 second
00319     pad.led(2, 0);
00320     pad.led(5, 0);
00321     lcd.clear();
00322     menu2_init(pad, lcd); //re-initialises menu 2
00323 }
00324 
00325 void SnakeEngine::contrast(Gamepad &pad, N5110 &lcd){
00326     _pot2 = pad.read_pot2();
00327     lcd.setContrast(0.25 + _pot2 * 0.5); //maps pot value to range 0.25 - 0.75
00328     //printf("Contrast: %f\n", 0.25 + _pot2 * 0.5);  
00329 }
00330 
00331 void SnakeEngine::menu_flash(Gamepad &pad, int led){
00332     for(int i = 1; i < 7; i++){
00333         pad.led(led, i % 2); //flash left side LEDs - odd numbers become 1 to turn on, even numbers become 0 to turn off  
00334         pad.led(led + 3, i % 2); //flash right side LEDs    
00335         wait_ms(100);
00336     }   
00337 }
00338 
00339 void SnakeEngine::game_reset(){
00340     score = 0;
00341     _body.reset();
00342 }
00343