FINAL VERSION

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 ///////// pre-processor directives ////////
00002 
00003 #include "mbed.h"
00004 #include "Gamepad.h"
00005 #include "N5110.h"
00006 #include "BreakoutEngine.h"
00007 #include "Bitmap.h"
00008 #include "Sprites.h"
00009 #include "SDFileSystem.h"
00010 
00011 #ifdef WITH_TESTING
00012 # include "tests.h"
00013 #endif
00014 
00015 /////////////// structs /////////////////
00016 
00017 struct UserInput {
00018     Direction d;
00019     float mag;
00020 };
00021 
00022 /////////////// objects ///////////////
00023 
00024 N5110 lcd(PTC9,PTC0,PTC7,PTD2,PTD1,PTC11);
00025 Gamepad pad;
00026 BreakoutEngine breakout;
00027 SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd"); // MOSI, MISO, SCK, CS
00028 Serial serial(USBTX, USBRX);  // for PC debug
00029 
00030 ///////////// prototypes ///////////////
00031 
00032 void init();
00033 void update_game(UserInput input);
00034 void render();
00035 void main_menu();
00036 void settings();
00037 void how_to_play();
00038 void loss_screen();
00039 void victory_screen();
00040 void title_screen();
00041 void main_game();
00042 void flash_screen(N5110 &lcd);
00043 void countdown();
00044 void save_hi_score(int hi_score);
00045 int get_hi_score();
00046 bool compare_to_hi_score(int score);
00047 void print_hi_score(int col,int row);
00048 void flash_hi_score_screen();
00049 void reset_victory(int score, int bonus);
00050 void reset_loss();
00051 
00052 Bitmap breakwhite(breakwhite_data, 48, 84);  // assign the title screen sprites
00053 Bitmap breakblack(breakblack_data, 48, 84);
00054 Bitmap arrowup(arrowup_data, 5, 7);  // assign the arrow up sprite data
00055 Bitmap arrowdown(arrowdown_data, 5, 7);  // assign the arrow down sprite data
00056 Bitmap three(three_data, 48, 84);  // assign the 3 sprite data
00057 Bitmap two(two_data, 48, 84);  // assign the 2 sprite data
00058 Bitmap one(one_data, 48, 84);  // assign the 1 sprite data
00059 
00060 bool tilt = false;
00061 float sens = 0.5; // default sens
00062 int number_of_frames = 0; // tracks the number of frames passed to use for bonus score
00063 
00064 ///////////// functions ////////////////
00065 
00066 int main()
00067 {
00068     
00069 #ifdef WITH_TESTING
00070     int number_of_failures = run_all_tests();
00071 
00072     if(number_of_failures > 0) return number_of_failures;
00073 #endif
00074 
00075     init();     // initialise the gamepad
00076     title_screen();  // run the title screen
00077 }
00078 
00079 
00080 
00081 
00082 void init()  // initialies all classes and libraries
00083 {
00084     // need to initialise LCD and Gamepad
00085     lcd.init();
00086     pad.init();
00087     breakout.init(PADDLE_WIDTH,PADDLE_HEIGHT,BALL_SIZE,BALL_SPEED); // init the objects
00088 }
00089 
00090 void render()  // draws each frame on the LCD
00091 {
00092     // clear screen, re-draw and refresh
00093     lcd.clear();
00094     breakout.draw(lcd);
00095     lcd.refresh();
00096 }
00097 
00098 
00099 //////////// MENUING & GAME LOOP FUNCTIONS //////////////////
00100 
00101 
00102 void title_screen()
00103 {
00104     tilt = false;   // reset the tilt variable so that on start, joystick is default
00105     breakout.reset_mult();
00106     lcd.clear();
00107 
00108     lcd.setBrightness(1); // stops the game from dimming (hardware bug?)
00109 
00110     breakblack.render(lcd, 0, 0);  // render the first frame
00111     lcd.refresh();
00112     pad.leds_off();
00113     wait(0.5);
00114     
00115     pad.reset_flags();
00116 
00117     while (pad.check_event(Gamepad::START_PRESSED) == false) {  // while start is not pressed alternate sprites
00118 
00119         lcd.clear();
00120 
00121         breakwhite.render(lcd, 0, 0);
00122         lcd.refresh();
00123         pad.leds_on();
00124         wait(0.5);
00125 
00126         lcd.clear();
00127 
00128         breakblack.render(lcd, 0, 0);
00129         lcd.refresh();
00130         pad.leds_off();
00131         wait(0.5);
00132     }
00133 
00134     pad.tone(750.0,0.3);
00135     wait(0.2);
00136     main_menu();  // load main menu
00137 }
00138 
00139 
00140 void main_menu()
00141 {
00142     lcd.setBrightness(1);  // fixes random screen dimming (hardware bug?)
00143     
00144     lcd.clear();
00145     lcd.printString("   START ",0,1);    // start game with default as joystick
00146     lcd.printString("   SETTINGS ",0,2);   // choose between joystick and tilt
00147     lcd.printString("   HOW TO PLAY ",0,3);  // brief text on how to interact with the game
00148     lcd.printString("HI-SCORE: ",0,5);
00149     print_hi_score(55,5);
00150     lcd.refresh();
00151     wait(0.3);  // load initial frame 
00152     
00153     pad.reset_flags();  // manually resets all button flags which fixes button bounce and prevents inputs carrying through to different menus
00154     pad.leds_off(); // prevents hardware bug where right most led flickers
00155     
00156     int pointer = 1;
00157 
00158     while (pad.check_event(Gamepad::A_PRESSED) == false) {
00159         lcd.clear();
00160         lcd.printString("   START ",0,1);    // start game with default as joystick
00161         lcd.printString("   SETTINGS ",0,2);   // choose between joystick and tilt
00162         lcd.printString("   HOW TO PLAY ",0,3);  // brief text on how to interact with the game
00163         lcd.printString("HI-SCORE: ",0,5);
00164         print_hi_score(55,5);
00165         lcd.printString(" >",0,pointer);
00166         lcd.refresh();
00167         wait(0.1);
00168         if (pad.get_direction() == N && pointer > 1) {  // if L is pressed and pointer isnt already on START, move it up one line
00169             pointer -= 1;
00170             pad.tone(750.0,0.3);
00171             wait(0.1);   
00172         } else if (pad.get_direction() == S && pointer < 3) {  // if R is pressed and pointer isnt already on HOW TO PLAY, move it down one line
00173             pointer += 1;
00174             pad.tone(750.0,0.3);
00175             wait(0.1);  
00176         }
00177         if (pad.check_event(Gamepad::X_PRESSED) & pad.check_event(Gamepad::Y_PRESSED)) {
00178             save_hi_score(0);  // resets hi score
00179         }
00180         //printf("Pointer 1 = %d",pointer);
00181 
00182     }
00183     if (pointer == 1) {    // if START was selected on exit of the while loop, run main game with the appropriate tilt/joystick setting
00184         pad.tone(750.0,0.3);
00185         number_of_frames = 0;
00186         main_game();
00187     } else if (pointer == 2) { // if SETTINGS was selected, enter the settings menu
00188         pad.tone(750.0,0.3);
00189         settings();
00190     } else if (pointer == 3) { // if HOW TO PLAY was selected, display instructions on how to play
00191         pad.tone(750.0,0.3);
00192         how_to_play();
00193     }
00194 }
00195 
00196 void settings()
00197 {
00198     lcd.clear();    // load initial frame
00199     lcd.printString("   JOYSTICK ",0,1);    // choose joystick
00200     lcd.printString("   TILT ",0,2);   // choose tilt
00201     lcd.printString("SENS :",0,4);
00202     lcd.drawRect(42,30, 40,10,FILL_TRANSPARENT);
00203     lcd.drawRect(42,30,40 * pad.read_pot() + 1,10,FILL_BLACK); // represents the sensitivity which is changed by turning the pot
00204     lcd.refresh();
00205     wait(0.1);   
00206     
00207     pad.reset_flags();  // fixes button bounce
00208 
00209     int pointer = 1;  // init the pointer
00210 
00211     while (pad.check_event(Gamepad::B_PRESSED) == false) { // while B is not pressed to return to main menu
00212         lcd.clear();
00213         lcd.printString("   JOYSTICK ",0,1);    // choose joystick
00214         lcd.printString("   TILT ",0,2);   // choose tilt
00215         lcd.printString("SENS :",0,4);
00216         lcd.printString(" >",0,pointer);
00217         lcd.drawRect(42,30, 40,10,FILL_TRANSPARENT);
00218         lcd.drawRect(42,30,40 * pad.read_pot() + 1,10,FILL_BLACK); // have it so it fills half the transparent one (default position)
00219         lcd.refresh();
00220         wait(0.1);
00221         if (pad.get_direction() == N && pointer > 1) {  // if L is pressed and pointer isnt already on JOYSTICK, move it up one line
00222             pointer -= 1;
00223             pad.tone(750.0,0.3);
00224             wait(0.1);  
00225         } else if (pad.get_direction() == S && pointer < 2) {  // if R is pressed and pointer isnt already on TILT, move it down one line
00226             pointer += 1;
00227             pad.tone(750.0,0.3);
00228             wait(0.1);  
00229         }
00230 
00231         if (pad.check_event(Gamepad::A_PRESSED)) { // if A is pressed, switch the tilt option accordingly
00232             pad.tone(750.0,0.3);
00233             wait(0.1);
00234             if (pointer == 1) {  // if A is pressed on JOYSTICK, tilt = false
00235                 tilt = false;
00236             } else if (pointer == 2) { // if A is pressed on TILT, tilt == true
00237                 tilt = true;
00238             }
00239         }
00240 
00241         if (pad.check_event(Gamepad::B_PRESSED)) {  // when B is pressed return to main menu
00242             pad.tone(750.0,0.3);
00243             sens = pad.read_pot() + 0.5f; // sens is set as the pot value at the instant the settings menu is exited +the minimum value of 0.5
00244             breakout.set_paddle_motion(tilt,sens); // sets the paddles motion options
00245             //printf("Sensitivity = %d",sens);
00246             wait(0.3);
00247             main_menu();
00248         }
00249         //printf("Pointer 2 = %d",pointer);
00250     }
00251 }
00252 
00253 void how_to_play()  // explains how to interact with the game
00254 {
00255     lcd.clear();
00256     lcd.printString("   B = LASER  ",0,0);
00257     lcd.printString(" START = PAUSE  ",0,1);
00258     lcd.printString("  DESTROY ALL   ",0,2);
00259     lcd.printString("    BRICKS. ",0,3);
00260     arrowdown.render(lcd, 38, 43);
00261     lcd.refresh();
00262     wait(0.1);           // load initial frame 
00263     
00264     pad.reset_flags();  // fixes button bounce
00265 
00266     while (pad.check_event(Gamepad::B_PRESSED) == false) { // while B is not pressed to return to main menu, display instruction on how to interact with the game
00267 
00268         if (pad.get_direction() == S) {
00269             lcd.clear();
00270             lcd.printString(" CONTINUE TO  ",0,2);
00271             lcd.printString("INCREASE SCORE.  ",0,3);
00272             lcd.printString(" BONUS SCORE  ",0,4);
00273             lcd.printString("BASED ON TIME.  ",0,5);
00274             arrowup.render(lcd, 38, 0);
00275             lcd.refresh();
00276             wait(0.1);
00277         }
00278         if (pad.get_direction() == N) {
00279             lcd.clear();
00280             lcd.printString("   B = LASER  ",0,0);
00281             lcd.printString(" START = PAUSE  ",0,1);
00282             lcd.printString("  DESTROY ALL   ",0,2);
00283             lcd.printString("    BRICKS ",0,3);
00284             arrowdown.render(lcd, 38, 43);
00285             lcd.refresh();
00286             wait(0.1);
00287         }
00288     }
00289     wait(0.3);
00290     main_menu();  // when B is pressed, the loop is exited and main menu is entered once again
00291 }
00292 
00293 
00294 void main_game()    // the Game loop
00295 {   
00296     pad.reset_flags();  // fixes button bounce
00297     
00298     int fps = 8;  // frames per second
00299     bool pause = false; // set pause screen to false
00300     //printf("Pause = %d",pointer);
00301 
00302     countdown(); // run the countdown
00303 
00304     render();  // first draw the initial frame
00305     wait(1.0f/fps);  // and wait for one frame period
00306 
00307 
00308     // game loop - read input, update the game state and render the display
00309     while (1) {
00310         breakout.read_input(pad); // read input from pad
00311         breakout.update(pad);  // update game
00312         lcd.setBrightness(1); // stops the game from dimming (bug)
00313         render();  // draw new frame
00314 
00315         if (breakout.check_loss(pad) == true) { // if life lost flash screen
00316             flash_screen(lcd);
00317         }
00318         if (pad.check_event(Gamepad::START_PRESSED)) { // if BACK pressed, toggle pause
00319             pause = !pause;
00320             pad.reset_flags();  // fixes button bounce?
00321         }
00322 
00323         while (pause == true) { // if pause is true, display pause screen
00324             lcd.clear();
00325             lcd.printString("    PAUSED ",0,1);
00326             lcd.printString(" START = PLAY",0,3);
00327             lcd.printString("  BACK = MENU",0,4);
00328             lcd.refresh();
00329             wait(0.1);
00330             if (pad.check_event(Gamepad::START_PRESSED)) { // if START pressed, toggle pause, leaving pause screen
00331                 pause = !pause;
00332             }
00333             if (pad.check_event(Gamepad::BACK_PRESSED)) { // if BACK pressed, return to the title screen
00334                 reset_loss();
00335                 title_screen();
00336             }
00337         }
00338 
00339         if (breakout.get_paddle_lives() == 0) { // when all lives lost, enter loss screen
00340             pad.leds_off(); //turns last led off (doesnt run through and update board so last led doent turn off via lives leds
00341             loss_screen();
00342         }
00343 
00344         if (breakout.get_num_left() == 0) { // when all bricks destroyed, display victory screen
00345             victory_screen();
00346         }
00347         number_of_frames ++; // track the number of frames passed to add to the score (inversely proportional)
00348         wait(1.0f/fps); // wait for 1 frame
00349     }
00350 }
00351 
00352 
00353 void loss_screen()   // loss screen when lives of paddle == 0
00354 {
00355     if (compare_to_hi_score(breakout.get_score()) == true) {  // little hi-score sequence if current score is higher than prev
00356         flash_hi_score_screen();
00357     }
00358 
00359     lcd.clear();
00360     char buffer[14]; // init the buffer
00361     sprintf(buffer,"%2d",breakout.get_score());  // print the score to the buffer
00362     lcd.printString(buffer,WIDTH/2 - 12,2);  // print buffer on lcd
00363     lcd.printString("   RESTART? ",2,1);
00364     lcd.printString("  PRESS START ",1,4);
00365     lcd.refresh();
00366     pad.tone(750.0,0.3);
00367     wait(0.4);
00368 
00369     pad.tone(300.0,0.3);
00370     wait(0.4);
00371     
00372     pad.reset_flags();
00373 
00374     while (pad.check_event(Gamepad::START_PRESSED) == false) {  // flashes the score, waits for START to return to title screen
00375         lcd.clear();
00376 
00377         lcd.printString("   RESTART? ",2,1);
00378         lcd.printString("  PRESS START ",1,4);
00379         lcd.refresh();
00380 
00381         wait(0.4);
00382 
00383         lcd.clear();
00384 
00385         lcd.printString(buffer,WIDTH/2 - 12,2);
00386         lcd.printString("   RESTART? ",2,1);
00387         lcd.printString("  PRESS START ",1,4);
00388         lcd.refresh();
00389 
00390         wait(0.4);
00391     }
00392     reset_loss();
00393     title_screen();
00394 }
00395 
00396 
00397 void victory_screen() // victory screen when all bricks are destroyed
00398 {
00399     int bonus = NULL;
00400 
00401     if (breakout.get_score()/2 - ((breakout.get_score()/2) * number_of_frames)/720 > 0) {   // beat within 90 seconds (8 fps) and you get a bonus
00402         bonus = breakout.get_score()/2 - ((breakout.get_score()/2) * number_of_frames)/720;
00403     } else {
00404         bonus = 0; // else you get no bonus
00405     }
00406 
00407     if (compare_to_hi_score(bonus+breakout.get_score()) == true) {   // little hi-score sequence if current score is higher than prev
00408         flash_hi_score_screen();
00409     }
00410 
00411     lcd.clear();
00412 
00413     char buffer1[14];
00414     sprintf(buffer1,"%2d",breakout.get_score());
00415     lcd.printString(buffer1,WIDTH/2 - 12,2);
00416 
00417     char buffer2[14];
00418     sprintf(buffer2,"%2d",bonus);
00419     lcd.printString(buffer2,WIDTH/2 + 12,3);
00420     lcd.printString(" BONUS: ",2,3);
00421 
00422     lcd.printString("   VICTORY! ",2,0);
00423     lcd.printString(" CONT = START ",0,4);
00424     lcd.printString("  MENU = BACK ",0,5);
00425     lcd.refresh();
00426     pad.tone(2500.0,0.1);
00427     wait(0.4);
00428 
00429     pad.tone(2500.0,0.1);
00430     wait(0.2);
00431 
00432     pad.tone(4000.0,0.6);
00433     wait(0.6);
00434     
00435     pad.reset_flags();
00436 
00437     while (pad.check_event(Gamepad::START_PRESSED) || pad.check_event(Gamepad::BACK_PRESSED) == false) {  // while neither START or BACK is pressed, flash the score and display options
00438         lcd.clear();
00439 
00440         lcd.printString("   VICTORY! ",2,0);     
00441         lcd.printString(" CONT = START ",0,4);
00442         lcd.printString("  MENU = BACK ",0,5);
00443 
00444         lcd.refresh();
00445 
00446         wait(0.4);
00447 
00448         lcd.clear();
00449 
00450         lcd.printString(buffer1,WIDTH/2 - 12,2);
00451         lcd.printString(buffer2,WIDTH/2 + 12,3);
00452         lcd.printString(" BONUS:",2,3);
00453 
00454         lcd.printString("   VICTORY! ",2,0);
00455         lcd.printString(" CONT = START ",0,4);
00456         lcd.printString("  MENU = BACK ",0,5);
00457         lcd.refresh();
00458 
00459         pad.tone(2500.0,0.1);
00460 
00461         lcd.refresh();
00462 
00463         wait(0.4);
00464 
00465         if (pad.check_event(Gamepad::START_PRESSED)) {
00466             reset_victory(breakout.get_score(),bonus);  // reset game
00467             main_game();  // reload game
00468         } else if (pad.check_event(Gamepad::BACK_PRESSED)) {
00469             reset_loss();    // reset game
00470             title_screen();  // load title screen
00471         } 
00472     }
00473 }
00474 
00475 
00476 void flash_screen(N5110 &lcd)   // flash the screen when a life is lost
00477 {
00478     lcd.setBrightness(0);
00479     wait(0.1);
00480     lcd.setBrightness(1);
00481     wait(0.1);
00482     lcd.setBrightness(0);
00483     wait(0.1);
00484     lcd.setBrightness(1);
00485     wait(0.1);
00486     lcd.setBrightness(0);
00487     wait(0.1);
00488     lcd.setBrightness(1);
00489     wait(0.1);
00490     lcd.setBrightness(0);
00491     wait(0.1);
00492     lcd.setBrightness(1);
00493 }
00494 
00495 void countdown()       // draw the countdown
00496 {
00497     lcd.setBrightness(1); // stops the game from dimming (bug)
00498 
00499     lcd.clear();
00500     three.render(lcd, 0, 0);   // render the 3
00501     lcd.refresh();
00502     pad.tone(500.0,0.5);
00503     wait(1);    // wait 1 second
00504 
00505     lcd.setBrightness(1);
00506 
00507     lcd.clear();
00508     two.render(lcd, 0, 0);   // render 2
00509     lcd.refresh();
00510     pad.tone(500.0,0.5);
00511     wait(1);    // wait 1 second
00512 
00513     lcd.setBrightness(1);
00514 
00515     lcd.clear();
00516     one.render(lcd, 0, 0);  // render 1
00517     lcd.refresh();
00518     pad.tone(1000.0,1);
00519     wait(1);     // wait 1 second
00520 }
00521 
00522 
00523 //////////////// SD CARD FUNCTIONS ////////////////////////
00524 
00525 
00526 void save_hi_score(int hi_score)  // save score to SD card
00527 {
00528     serial.baud(115200);  // max speed
00529     FILE *fp; // file pointer
00530     fp = fopen("/sd/hi_score.txt", "w");
00531     if (fp == NULL) {  // if it can't open the file then print error message
00532         serial.printf("Error\n");
00533     } else {  // opened file so can write
00534         fprintf(fp, "%d",hi_score);
00535         serial.printf("Written to file.\n");
00536         fclose(fp);  // close the file after writing
00537     }
00538 }
00539 
00540 
00541 int get_hi_score()  // retrieve score from SD card
00542 {
00543     serial.baud(115200); // max speed
00544     FILE *fp;
00545     fp = fopen("/sd/hi_score.txt", "r");
00546     int stored_hi_score = NULL;
00547     if (fp == NULL) {  // if it can't open the file then print error message
00548         serial.printf("Error\n");
00549     } else {  // opened file so can write
00550         fscanf(fp, "%d",&stored_hi_score);
00551         serial.printf("Read %d from file.\n",stored_hi_score);
00552         fclose(fp);  // close the file after reading
00553     }
00554     return stored_hi_score;  // returns the stored hi score
00555 }
00556 
00557 
00558 bool compare_to_hi_score(int score)  // returns true if new score higher than current stored hi score
00559 {
00560     if (score >= get_hi_score()) {
00561         //printf("hi score! \n");
00562         save_hi_score(score);  // writes the new score to the SD card
00563         return true;
00564     } else {
00565         return false;
00566     }
00567 }
00568 
00569 
00570 void print_hi_score(int col,int row)  // print hi score to lcd at specified location
00571 {
00572     char buffer[14];  // creates buffer
00573     sprintf(buffer,"%2d",get_hi_score()); // puts hi-score in buffer
00574     lcd.printString(buffer,col,row);   // prints buffer to the screen
00575 
00576 }
00577 
00578 
00579 void reset_loss()  // reset the game when returning to title screen from any point
00580 {
00581     breakout.reset_paddle_lives(); // resets lives back to 6
00582     breakout.set_prev_score(0);   // resets prev score to 0
00583     number_of_frames = 0;  // reset the number of frames
00584     breakout.reset_mult(); // reset multiplier
00585     breakout.reset_game(); // return game to initial positions
00586 }
00587 
00588 
00589 void reset_victory(int score, int bonus)  // reset the game after a victory
00590 {
00591     breakout.set_prev_score(score + bonus);  // saves score
00592     number_of_frames = 0;  // reset the number of frames
00593     breakout.inc_mult();  // increment multiplier
00594     breakout.reset_game(); // return game to initial positions
00595 }
00596 
00597 
00598 void flash_hi_score_screen()  // flash the hi score, called when hi score acheieved
00599 {
00600     lcd.clear();
00601     lcd.printString("      NEW ",0,2);
00602     lcd.printString("   HI-SCORE! ",0,3);
00603     lcd.refresh();
00604 
00605     pad.tone(2500.0,0.2);
00606     wait(0.2);
00607 
00608     pad.tone(4000.0,0.4);
00609     wait(1);
00610 
00611     lcd.clear();
00612     print_hi_score(30,3);
00613     lcd.refresh();
00614 
00615     pad.tone(2500.0,0.2);
00616     wait(0.2);
00617 
00618     pad.tone(4000.0,0.4);
00619     wait(1);
00620 
00621     lcd.clear();
00622     lcd.printString("      NEW ",0,2);
00623     lcd.printString("   HI-SCORE! ",0,3);
00624     lcd.refresh();
00625 
00626     pad.tone(2500.0,0.2);
00627     wait(0.2);
00628 
00629     pad.tone(4000.0,0.4);
00630     wait(1);
00631 
00632     lcd.clear();
00633     print_hi_score(30,3);
00634     lcd.refresh();
00635 
00636     pad.tone(2500.0,0.2);
00637     wait(0.2);
00638 
00639     pad.tone(4000.0,0.4);
00640     wait(1.3);
00641 }