Space Invaders - Embedded Systems Project 15/16 - Avinash Patel 200860407

Dependencies:   Joystick N5110 SDFileSystem mbed

Revision:
6:89d4a7f7588b
Parent:
5:34855f712350
Child:
7:babc367a3333
--- a/main.cpp	Wed Apr 27 00:19:52 2016 +0000
+++ b/main.cpp	Thu Apr 28 14:14:54 2016 +0000
@@ -135,7 +135,7 @@
     AnalogIn* y_axis_;
     InterruptIn* button_;
 
-    //Ticker to prevent joystick button bounce
+    //Timeout to prevent joystick button bounce
     Timeout* button_debounce_;
 
     //Stores X and Y offsets
@@ -178,6 +178,7 @@
 
 //Timeout
 Timeout joystick_cursor_regulator; //Stops the cursor from jumping
+Timeout shoot_button_debounce; //Stops the cannon from firing when transitioning from a menu to the game
 
 //Buffer holding pixel data of screen with point representations
 int screen_buffer[84][48];
@@ -203,13 +204,13 @@
 int fsm_state = 0; //Stores the state for menu fsm's
 int cursor_y_pos = 0; //Stores the cursor position
 //Cannon related integers
+int number_of_lives = 3;
 int cannon_xpos = 24;
 const int cannon_ypos = 43;
-int number_of_lives = 3;
 //Cannon missile related integers
 int cannon_missile_x_pos = 28;
 int cannon_missile_y_pos = 40;
-//Invader related integers 
+//Invader related integers
 int no_of_alive_invaders = 15;
 //Invader missile related integers
 int invader_strong_missile_x_pos;
@@ -222,7 +223,7 @@
 enum Status {dead, dying, alive}; //Contains the status of invaders
 enum Invader {small, medium, large, none}; //Contains the type of invader passed into
 //Contains the current state of the game
-enum GameState {menu, game, paused, save, load};
+enum GameState {menu, game, paused, save, load, scores, settings};
 GameState game_state = menu;
 
 //Bit-maps
@@ -356,12 +357,18 @@
     bool before_bitmap[8][14];
     bool after_bitmap[8][14];
 } barrier[3];
-//FSM for paused menu
-struct FSMPaused {
+//FSM for menus
+struct FSMMenus {
     GameState output;
     int next_state[2];
 };
-FSMPaused fsm_paused[3] = {
+FSMMenus fsm_main_menu[4] = {
+    {game, {1, 3}},
+    {load, {2, 0}},
+    {scores, {3, 1}},
+    {settings, {0, 2}}
+};
+FSMMenus fsm_paused[3] = { //Pause menu FSM
     {game, {1, 2}},
     {save, {2, 0}},
     {menu, {0, 1}}
@@ -376,6 +383,7 @@
 volatile bool g_move_invader_normal_missile_flag[2] = {false, false};
 volatile bool g_cannon_hit_flag = false;
 volatile bool g_joystick_cursor_regulator_flag = false;
+volatile bool g_shoot_button_debounce_flag = false;
 
 // function prototypes
 // error function hangs flashing an LED
@@ -430,6 +438,10 @@
 void CollisionDetectionInvaderNormalMissile(int missile_no);
 void DetachTickers();
 void AttachTickers();
+//Pause Menu Functions
+void PauseScreen();
+void PrintPauseScreen();
+void MoveCursor(const struct FSMMenus *fsm);
 //void InitUFO();
 void DrawUFO();
 //ISR's
@@ -440,6 +452,7 @@
 void move_cannon_missile_isr();
 void cannon_hit_isr();
 void joystick_cursor_regulator_isr();
+void shoot_button_debounce_isr();
 //Function pointer to move invader normal missile isr
 void (*move_invader_normal_missile_isr[2])();
 void move_invader_normal_missile_0_isr();
@@ -468,76 +481,78 @@
     //Samples joystick every 0.05 second
     move_joystick.attach(&move_joystick_isr, 0.05);
 
+    
+
     while (true) {
         if (game_state == menu) { //Menu screen
             lcd.clear();
-            lcd.printString("Menu", 0, 1);
+            fsm_state = 0; //Sets the fsm state to 0
+            while (game_state == menu) {
+                lcd.printString("Space Invaders", 0, 0);
+                lcd.printString("Menu", 28, 1);
+                lcd.drawLine(0, 15, 83, 15, 2);
+
+                lcd.printString("New Game", 5, 2);
+                lcd.printString("Load Game", 5, 3);
+                lcd.printString("High Scores", 5, 4);
+                lcd.printString("Settings", 5, 5);
+
+                //Draws the cursor
+                cursor_y_pos = ((fsm_state+2)*8)+3; //Adds 2 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3
+                lcd.drawRect(74, cursor_y_pos, 2, 2, 1);
+
+                if (g_move_joystick_flag) {
+                    g_move_joystick_flag = false;
+
+                    //Moves the cursor to match the selected option, only if the joystick isn't centred
+                    MoveCursor(fsm_main_menu);
+                }
+
+                if (g_shoot_pressed_flag) {
+                    g_shoot_pressed_flag = false;
+
+                    game_state = fsm_main_menu[fsm_state].output;
 
-            if(joystick.get_button_flag()) {
-                joystick.set_button_flag(0);
-                
-                //Clears the screen buffer and initalises the game
-                memset(screen_buffer, 0, sizeof(screen_buffer));
-                no_of_alive_invaders = 15;
-                score = 0;
-                number_of_lives = 3;
-                InitSmallInvaders();
-                InitMediumInvaders();
-                InitLargeInvaders();
-                InitBarriers();
+                    //If the game state is equal to the game initalise it
+                    if (game_state == game) {
+                        //Clears the screen buffer and runs init functions
+                        memset(screen_buffer, 0, sizeof(screen_buffer));
+                        no_of_alive_invaders = 15;
+                        score = 0;
+                        number_of_lives = 3;
+                        InitSmallInvaders();
+                        InitMediumInvaders();
+                        InitLargeInvaders();
+                        InitBarriers();
+                        //Sets the flags so enemies pop up straight away
+                        g_update_screen_flag = true;
+                        g_move_joystick_flag = true;
+                        g_move_enemies_flag = true;
+                        //Forces the missiles to have the fired flags to flase
+                        cannon_missile_on_screen = false;
+                        invader_normal_missile[0].fired = false;
+                        invader_normal_missile[1].fired = false;
+                    }
+                }
 
-                game_state = game;
+                if(joystick.get_button_flag()) {
+                    joystick.set_button_flag(0);
+                }
+
+                lcd.refresh();
+                sleep();
             }
         } else if (game_state == paused) { //Paused screen
-            //Prints paused and a line underneath
-            lcd.printString("Paused", 24, 0);
-            lcd.drawLine(0, 7, 83, 7, 2);
-            //Displays the current score
-            char buffer[14];
-            sprintf(buffer, "Score: %d", score);
-            lcd.printString(buffer, 0, 1);
-            //Displays the no of lives
-            sprintf(buffer, "Lives: %d", number_of_lives);
-            lcd.printString(buffer, 0, 2);
-            lcd.drawLine(0, 23, 83, 23, 2);
-            //Prints options on pause menu
-            lcd.printString("Resume", 10, 3);
-            lcd.printString("Save", 10, 4);
-            lcd.printString("Quit", 10, 5);
-
-            //Draws the cursor
-            cursor_y_pos = ((fsm_state+3)*8)+3; //Adds 3 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3
-            lcd.drawRect(64, cursor_y_pos, 2, 2, 1);
-
-            if (g_move_joystick_flag) {
-                g_move_joystick_flag = false;
-
-                //If the joystick is is pushed down half way clear the cursor and set the next state
-                if (joystick.GetYValue() < 0.25f) {
-                    //Clears the cursor
-                    lcd.drawRect(64, cursor_y_pos, 2, 2, 2);
-                    //Sets the new state
-                    fsm_state = fsm_paused[fsm_state].next_state[0];
-                } else if (joystick.GetYValue() > 0.75f) {
-                    //Clears the cursor
-                    lcd.drawRect(64, cursor_y_pos, 2, 2, 2);
-                    //Sets the new state
-                    fsm_state = fsm_paused[fsm_state].next_state[1];
-                }
-            }
-
-            if (g_shoot_pressed_flag) {
-                g_shoot_pressed_flag = false;
-                game_state = fsm_paused[fsm_state].output;
-            }
-            
+            //Clears the screen
+            lcd.clear();
+            //Resets the fsm state to 0
+            fsm_state = 0;
+            PauseScreen();
         } else if (game_state == game) { //Game screen
-
             AttachTickers();
             Game();
         }
 
-        pc.printf("P: %d\n", g_shoot_pressed_flag);
         sleep();
     }
 }
@@ -596,8 +611,14 @@
 
 void shoot_pressed_isr()
 {
-    g_shoot_pressed_flag = true;
-    pc.printf("B\n");
+    //Only sets the shoot pressed flag 0.1s after the last press
+    if (!g_shoot_button_debounce_flag) {
+        g_shoot_pressed_flag = true;
+        g_shoot_button_debounce_flag = true;
+
+        //Attaches a timeout to clear the debounce flag 0.125 seconds after it was set
+        shoot_button_debounce.attach(&shoot_button_debounce_isr, 0.125);
+    }
 }
 
 void move_cannon_missile_isr()
@@ -611,19 +632,25 @@
     if (game_state == game) {
         g_move_joystick_flag = true;
     } else if (!g_joystick_cursor_regulator_flag) {
-    //Only sets the flag if the regulator is not set
+        //Only sets the flag if the regulator is not set
         g_move_joystick_flag = true;
         g_joystick_cursor_regulator_flag = true;
 
-        //Attachs a timeout to clear the regulator in 0.2s to prevent the cursor from behaving erratically
-        joystick_cursor_regulator.attach(&joystick_cursor_regulator_isr, 0.125);
+        //Attachs a timeout to clear the regulator in 0.1s to prevent the cursor from behaving erratically
+        joystick_cursor_regulator.attach(&joystick_cursor_regulator_isr, 0.1);
     }
 }
 
-void joystick_cursor_regulator_isr() {
+void joystick_cursor_regulator_isr()
+{
     g_joystick_cursor_regulator_flag = false;
 }
 
+void shoot_button_debounce_isr()
+{
+    g_shoot_button_debounce_flag = false;
+}
+
 void move_invader_normal_missile_0_isr()
 {
     g_move_invader_normal_missile_flag[0] = true;
@@ -640,9 +667,8 @@
 }
 
 void Game()
-{  
+{
     //Stays within the loop while the selected state is game
-    pc.printf("G: %d\n", g_shoot_pressed_flag);
     while (game_state == game) {
         //If the game is over detach all the tickers
         if (number_of_lives == 0) {
@@ -737,15 +763,8 @@
             if (joystick.get_button_flag()) {
                 joystick.set_button_flag(0);
 
-                //Clears the screen
-                lcd.clear();
-
-                //Resets the fsm state to 0
-                fsm_state = 0;
-                
                 //Detach all game tickers
                 DetachTickers();
-                
                 game_state = paused;
             }
         }
@@ -754,7 +773,6 @@
     }
 }
 
-
 void UpdateScreen()
 {
     //Loops through the screen buffer and sets pixels on the LCD
@@ -1290,13 +1308,12 @@
         g_cannon_hit_flag = true;
         //Detaches all tickers
         DetachTickers();
-        //Creates a Ticker object on the stack with a period of 2 seconds to pause the game for 2 seconds
-        Ticker cannon_hit;
+        //Creates a Timeout object on the stack with a period of 2 seconds to pause the game for 2 seconds
+        Timeout cannon_hit;
         cannon_hit.attach(&cannon_hit_isr, 2);
         while (g_cannon_hit_flag) {
             sleep();
         }
-        cannon_hit.detach();
         AttachTickers();
         --number_of_lives;
         invader_normal_missile[missile_no].fired = false;
@@ -1442,4 +1459,72 @@
             move_invader_normal_missile[i].attach(move_invader_normal_missile_isr[i], 0.05);
         }
     }
+}
+
+void PauseScreen()
+{
+    while (game_state == paused) {
+        //Prints the pause screen, score etc
+        PrintPauseScreen();
+
+        //Draws the cursor
+        cursor_y_pos = ((fsm_state+3)*8)+3; //Adds 3 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3
+        lcd.drawRect(74, cursor_y_pos, 2, 2, 1); //Draws the cursor
+
+        if (g_move_joystick_flag) {
+            g_move_joystick_flag = false;
+
+            //Moves the cursor to match the selected option, only if the joystick isn't centred
+            MoveCursor(fsm_paused);
+        }
+
+        //If the button is pressed the selected option will appear on the screen
+        if (g_shoot_pressed_flag) {
+            g_shoot_pressed_flag = false;
+            game_state = fsm_paused[fsm_state].output;
+        }
+
+        //Resets the joystick flag to false if pressed
+        if (joystick.get_button_flag()) {
+            joystick.set_button_flag(0);
+        }
+
+        lcd.refresh();
+
+        sleep();
+    }
+}
+
+void PrintPauseScreen()
+{
+    //Prints paused and a line underneath
+    lcd.printString("Paused", 24, 0);
+    //Displays the current score
+    char buffer[14];
+    sprintf(buffer, "Score: %d", score);
+    lcd.printString(buffer, 0, 1);
+    //Displays the no of lives
+    sprintf(buffer, "Lives: %d", number_of_lives);
+    lcd.printString(buffer, 0, 2);
+    lcd.drawLine(0, 23, 83, 23, 2);
+    //Prints options on pause menu
+    lcd.printString("Resume", 5, 3);
+    lcd.printString("Save", 5, 4);
+    lcd.printString("Quit", 5, 5);
+}
+
+void MoveCursor(const struct FSMMenus *fsm)
+{
+    //If the joystick is is pushed down half way clear the cursor and set the next state
+    if (joystick.GetYValue() < 0.25f) {
+        //Clears the cursor
+        lcd.drawRect(74, cursor_y_pos, 2, 2, 2);
+        //Sets the new state
+        fsm_state = fsm[fsm_state].next_state[0];
+    } else if (joystick.GetYValue() > 0.75f) {
+        //Clears the cursor
+        lcd.drawRect(74, cursor_y_pos, 2, 2, 2);
+        //Sets the new state
+        fsm_state = fsm[fsm_state].next_state[1];
+    }
 }
\ No newline at end of file