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

Dependencies:   Joystick N5110 SDFileSystem mbed

main.cpp

Committer:
avi23
Date:
2016-04-28
Revision:
6:89d4a7f7588b
Parent:
5:34855f712350
Child:
7:babc367a3333

File content as of revision 6:89d4a7f7588b:

/*
Space Invaders - Avinash Patel 200860407

Week 19 - Set up joystick class
Week 20 - Changed to space invaders as constrained too much by screen resolution
        - Core cannon is drawn and can move, enemies are visible and they switch between states every second
Week 21 - Begun to set up barriers
Easter  - Barriers work, invader and player can shoot. Begun setting up menus
*/
#include "mbed.h"
#include "N5110.h"

//Direction invaders are travelling
#define LEFT 0
#define RIGHT 1

//Joystick Class
class Joystick
{
public:
    //Constructor
    Joystick(PinName x_axis_pin, PinName y_axis_pin, PinName button_pin) {
        //Dynamically allocates the pins and button debounce ticker
        x_axis_ = new AnalogIn(x_axis_pin);
        y_axis_ = new AnalogIn(y_axis_pin);
        button_ = new InterruptIn(button_pin);
        button_debounce_ = new Timeout();
    }

    //Deconstructor
    ~Joystick() {
        //Clears all dynamically allocated memory
        delete x_axis_;
        delete y_axis_;
        delete button_;
        delete button_debounce_;
    }

    //Initalises the Joystick
    //Sets up the ISRs and grabs the offsets for each axis
    void init() {
        //Sets up the button ISR
        button_->mode(PullDown);
        button_->rise(this, &Joystick::button_isr);

        //Initalises the vairables and flags
        x_offset_ = 0;
        y_offset_ = 0;
        g_button_flag_ = false;
        g_button_debounce_flag_ = false;

        //Samples the joystick 5 times and takes an average to get the offset
        float x_sum = 0;
        float y_sum = 0;

        for (int i = 0; i < 5; ++i) {
            x_sum += x_axis_->read();
            y_sum += y_axis_->read();
        }

        x_offset_ = 0.5f - x_sum/5.0f;
        y_offset_ = 0.5f - y_sum/5.0f;
    }

    //Take 5 readings and returns the average measurement, accounting for joystick offset x and y values
    float GetXValue() {

        float x_sum = 0;

        for (int i = 0; i < 5; ++i) {
            x_sum += x_axis_->read();
        }

        float x_value = x_sum/5.0f + x_offset_;

        //Caps the value for the POT between 0 and 1
        if (x_value < 0.0f) {
            return 0;
        } else if (x_value > 1.0f) {
            return 1;
        } else {
            return x_value;
        }
    }

    float GetYValue() {

        float y_sum = 0;

        for (int i = 0; i < 5; ++i) {
            y_sum += y_axis_->read();
        }

        float y_value = y_sum/5.0f + y_offset_;

        //Caps the value for the POT between 0 and 1
        if (y_value < 0.0f) {
            return 0;
        } else if (y_value > 1.0f) {
            return 1;
        } else {
            return y_value;
        }
    }

    //Getter and setters for flags
    int get_button_flag() {
        return g_button_flag_;
    }

    void set_button_flag(int value) {
        g_button_flag_ = value;
    }

private:
    //Button ISR Method
    void button_isr() {
        if (!g_button_debounce_flag_) {
            g_button_flag_ = true;
            g_button_debounce_flag_ = true;

            //Sets up the debounce ticker
            button_debounce_->attach(this, &Joystick::button_debounce_isr, 0.2);
        }
    }

    //Button debounce ISR method
    void button_debounce_isr() {
        g_button_debounce_flag_ = false;
    }

private:
    //Pin inputs
    AnalogIn* x_axis_;
    AnalogIn* y_axis_;
    InterruptIn* button_;

    //Timeout to prevent joystick button bounce
    Timeout* button_debounce_;

    //Stores X and Y offsets
    float x_offset_;
    float y_offset_;

    //Stores interrupt flags
    volatile bool g_button_flag_;
    volatile bool g_button_debounce_flag_;
};

// K64F on-board LEDs
DigitalOut r_led(LED_RED);
DigitalOut g_led(LED_GREEN);
DigitalOut b_led(LED_BLUE);

// K64F on-board switches
InterruptIn sw2(SW2);
InterruptIn sw3(SW3);

// UART connection for PC
Serial pc(USBTX,USBRX);

//Joystick
Joystick joystick(PTB3, PTB2, PTB11);

//Shoot button
InterruptIn shoot_button(PTB18);

//LCD object
//         VCC,    SCE,   RST,   D/C,   MOSI,  SCLK,   LED
N5110 lcd (PTE26 , PTA0 , PTC4 , PTD0 , PTD2 , PTD1 , PTC3);

//Tickers
Ticker update_screen;
Ticker move_joystick;
Ticker move_cannon_missile;
Ticker move_enemies;
Ticker move_invader_normal_missile[2];

//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];
const int empty_pixel = 0;
const int cannon_pixel = 1;
const int first_barrier_pixel = 2;
const int first_large_invader_pixel = 5;
const int first_medium_invader_pixel = 10;
const int first_small_invader_pixel = 15;
const int ufo_pixel = 20;
const int cannon_missile_pixel = 21;
const int invader_normal_missile_pixel = 22;

//Booleans
//Invader related bools
bool invaders_in_state2 = true;
bool invader_direction = RIGHT;
//Cannon missile bool
bool cannon_missile_on_screen = false;

//Integers
int score = 0; //Stores the score
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;
//Cannon missile related integers
int cannon_missile_x_pos = 28;
int cannon_missile_y_pos = 40;
//Invader related integers
int no_of_alive_invaders = 15;
//Invader missile related integers
int invader_strong_missile_x_pos;
int invader_strong_missile_y_pos;

//Floats
float ticker_period = 1; //Stores the period of the invader move ticker

//Enums
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, scores, settings};
GameState game_state = menu;

//Bit-maps
//Cannon bit-map
const bool cannon_bitmap[5][9] = {
    {0, 0, 0, 0, 1, 0, 0, 0, 0},
    {0, 0, 0, 1, 1, 1, 0, 0, 0},
    {0, 1, 1, 1, 1, 1, 1, 1, 0},
    {1, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 1, 1, 1, 1, 1, 1, 1, 1}
};
//Bitmaps for small invaders
const bool small_invader_bitmap_1[6][8] = { //First state
    {0, 0, 0, 1, 1, 0, 0, 0},
    {0, 1, 1, 1, 1, 1, 1, 0},
    {1, 1, 0, 1, 1, 0, 1, 1},
    {1, 1, 1, 1, 1, 1, 1, 1},
    {0, 1, 0, 1, 1, 0, 1, 0},
    {1, 0, 1, 0, 0, 1, 0, 1}
};
const bool small_invader_bitmap_2[6][8] = { //Second state
    {0, 0, 0, 1, 1, 0, 0, 0},
    {0, 1, 1, 1, 1, 1, 1, 0},
    {1, 1, 0, 1, 1, 0, 1, 1},
    {1, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 0, 0, 0, 0, 0, 1},
    {0, 1, 0, 0, 0, 0, 1, 0}
};
//Bitmaps for medium invaders
const bool medium_invader_bitmap_1[6][10] = { //First state
    {1, 0, 0, 1, 0, 0, 1, 0, 0, 1},
    {1, 0, 1, 1, 1, 1, 1, 1, 0, 1},
    {1, 1, 1, 0, 1, 1, 0, 1, 1, 1},
    {0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
    {0, 0, 1, 0, 0, 0, 0, 1, 0, 0},
    {0, 1, 0, 0, 0, 0, 0, 0, 1, 0}
};
const bool medium_invader_bitmap_2[6][10] = { //Second state
    {0, 0, 0, 1, 0, 0, 1, 0, 0, 0},
    {0, 0, 1, 1, 1, 1, 1, 1, 0, 0},
    {0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 0, 1, 0, 0, 0, 0, 1, 0, 1},
    {0, 0, 0, 1, 1, 1, 1, 0, 0, 0}
};
//Bitmaps for large invaders
const bool large_invader_bitmap_1[6][12] = { //First state
    {0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0},
    {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
    {1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0},
    {0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0}
};
const bool large_invader_bitmap_2[6][12] = { //Second state
    {0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0},
    {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
    {1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0},
    {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}
};
//Normal invader missile bitmap
const bool invader_normal_missile_bitmap[4][3] = {
    {0, 1, 0},
    {1, 1, 1},
    {0, 1, 0},
    {0, 1, 0}
};
//Strong invader missile bitmap
const bool invader_strong_missile_bitmap_1[4][3] = { //First state
    {0, 0, 1},
    {0, 1, 0},
    {1, 0, 0},
    {0, 1, 0}
};
const bool invader_strong_missile_bitmap_2[4][3] = { //Second state
    {0, 1, 0},
    {1, 0, 0},
    {0, 1, 0},
    {0, 1, 1}
};
//Barrier bitmap
const bool barrier_bitmap[8][14] = {
    {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
    {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1},
    {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1},
    {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
    {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}
};
//Cannon barrier damage bitmap
const bool barrier_cannon_missile_damage_bitmap[3][3] = {
    {0, 1, 0},
    {1, 0, 0},
    {1, 0, 1}
};
//Normal invader barrier damage bitmap
const bool barrier_invader_normal_missile_damage_bitmap[2][3] = {
    {1, 0, 1},
    {0, 1, 0}
};
//UFO Bitmap
const bool ufo_bitmap[5][14] = {
    {0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0},
    {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
    {0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0},
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
    {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}
};

//Structs
//Contains invader data
struct Invaders {
    int x_pos;
    int y_pos;
    enum Status status;
} small_invader[5], medium_invader[5], large_invader[5]; //Five of each invader
//Contains data for Invader normal missiles
struct InvaderNormalMissiles {
    int x_pos;
    int y_pos;
    bool fired;
} invader_normal_missile[2];
//Contains data for barriers
struct Barriers {
    int x_pos;
    int y_pos;
    bool before_bitmap[8][14];
    bool after_bitmap[8][14];
} barrier[3];
//FSM for menus
struct FSMMenus {
    GameState output;
    int next_state[2];
};
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}}
};

//ISR Flags
volatile bool g_update_screen_flag = true;
volatile bool g_move_joystick_flag = true;
volatile bool g_move_enemies_flag = true;
volatile bool g_shoot_pressed_flag = false;
volatile bool g_move_cannon_missile_flag = false;
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
void error();
// setup serial port
void init_serial();
// set-up the on-board LEDs and switches
void init_K64F();
//Init shoot button
void init_shoot();
//Init Random Number Generator
void init_rng();
// Added functions
void InitaliseGame();
void Game();
void UpdateScreen();
void MoveCannon();
void InitSmallInvaders();
void ClearSmallInvaders();
void ClearSingleSmallInvader(int invader_no);
void DrawSmallInvaders();
void DrawSingleSmallInvader(int invader_no);
void InitMediumInvaders();
void ClearMediumInvaders();
void ClearSingleMediumInvader(int invader_no);
void DrawMediumInvaders();
void DrawSingleMediumInvader(int invader_no);
void InitLargeInvaders();
void ClearLargeInvaders();
void ClearSingleLargeInvader(int invader_no);
void DrawLargeInvaders();
void DrawSingleLargeInvader(int invader_no);
void InitBarriers();
void DrawBarriers();
void MoveInvaderXPositions();
int CalculateInvaderLeftLimit();
int CalculateInvaderRightLimit();
void MoveInvadersLeft(int limit);
void MoveInvadersRight(int limit);
void MoveInvaderYPositions(bool new_direction);
Invader CalculateInvaderYLimit();
Invader LowestInvaderInColumn(int column);
void FireCannonMissile();
void MoveCannonMissile();
void CollisionDetectionCannonMissile();
int CannonMissileHitInvader(int first_pixel, int row, struct Invaders (&invader)[5]);
void CannonMissileHitBarrier(int first_pixel, int row);
void InvaderNormalMissileHitBarrier(int first_pixel, const struct InvaderNormalMissiles (&missile));
void AttemptToFireInvaderNormalMissiles();
void FireNormalInvaderMissile(int missile_no, Invader source, const struct Invaders (&invader));
void MoveInvaderNormalMissile(int missile_no);
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
void update_screen_isr();
void move_joystick_isr();
void move_enemies_isr();
void shoot_pressed_isr();
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();
void move_invader_normal_missile_1_isr();

int row_no = 5;
int col_no = 14;

int main()
{
    //Wait for 2 seconds to allow power to settle
    wait(1);
    //Initalises the board and perhiperals
    init_K64F();
    init_serial();
    init_shoot();
    init_rng();
    joystick.init();
    lcd.init();
    lcd.clear();

    //Configures the function pointer for the invaders normal missile
    move_invader_normal_missile_isr[0] = &move_invader_normal_missile_0_isr;
    move_invader_normal_missile_isr[1] = &move_invader_normal_missile_1_isr;

    //Samples joystick every 0.05 second
    move_joystick.attach(&move_joystick_isr, 0.05);

    

    while (true) {
        if (game_state == menu) { //Menu screen
            lcd.clear();
            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 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;
                    }
                }

                if(joystick.get_button_flag()) {
                    joystick.set_button_flag(0);
                }

                lcd.refresh();
                sleep();
            }
        } else if (game_state == paused) { //Paused screen
            //Clears the screen
            lcd.clear();
            //Resets the fsm state to 0
            fsm_state = 0;
            PauseScreen();
        } else if (game_state == game) { //Game screen
            AttachTickers();
            Game();
        }

        sleep();
    }
}

void init_K64F()
{
    // on-board LEDs are active-low, so set pin high to turn them off.
    r_led = 1;
    g_led = 1;
    b_led = 1;

    // since the on-board switches have external pull-ups, we should disable the internal pull-down
    // resistors that are enabled by default using InterruptIn
    sw2.mode(PullNone);
    sw3.mode(PullNone);
}

void error()
{
    while(1) {  // if error, hang while flashing error message
        r_led = 0;
        wait(0.2);
        r_led = 1;
        wait(0.2);
    }
}

void init_serial()
{
    // set to highest baud - ensure terminal software matches
    pc.baud(115200);
}

//Seeds the random number generator with noise from an analog in pin
void init_rng()
{
    AnalogIn rng_seed(PTC10);
    srand(floor(10000*rng_seed.read()));
}

void init_shoot()
{
    shoot_button.mode(PullUp);
    shoot_button.fall(&shoot_pressed_isr);
}

void update_screen_isr()
{
    g_update_screen_flag = true;
}

void move_enemies_isr()
{
    g_move_enemies_flag = true;
}

void shoot_pressed_isr()
{
    //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()
{
    g_move_cannon_missile_flag = true;
}

void move_joystick_isr()
{
    //Always set the move flag in a game
    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
        g_move_joystick_flag = true;
        g_joystick_cursor_regulator_flag = true;

        //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()
{
    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;
}

void move_invader_normal_missile_1_isr()
{
    g_move_invader_normal_missile_flag[1] = true;
}

void cannon_hit_isr()
{
    g_cannon_hit_flag = false;
}

void Game()
{
    //Stays within the loop while the selected state is game
    while (game_state == game) {
        //If the game is over detach all the tickers
        if (number_of_lives == 0) {
            DetachTickers();

            lcd.clear();
            lcd.printString("Game Over.", 1, 2);
        } else if (no_of_alive_invaders == 0) { //If the player wins a round
            //Resets the no of alive invaders
            no_of_alive_invaders = 15;
            //Detaches the enemy ticker while reinitalising invaders
            move_enemies.detach();
            //Reinitalises objects
            InitSmallInvaders();
            InitMediumInvaders();
            InitLargeInvaders();
            //Reattaches enemy ticker
            move_enemies.attach(&move_enemies_isr, 1);
        } else {
            //Updates pixels on the screen
            if (g_update_screen_flag) {
                g_update_screen_flag = false;

                UpdateScreen();
            }

            //Controls cannon movement
            if (g_move_joystick_flag) {
                g_move_joystick_flag = false;

                MoveCannon();
                DrawBarriers();
            }

            //Controls enemy movement
            if (g_move_enemies_flag) {
                g_move_enemies_flag = false;

                //Increses the speed the invaders move
                move_enemies.detach();
                ticker_period = 0.1+(no_of_alive_invaders*0.06);
                move_enemies.attach(&move_enemies_isr, ticker_period);

                //Clears the old bitmaps
                ClearSmallInvaders();
                ClearMediumInvaders();
                ClearLargeInvaders();

                MoveInvaderXPositions();
                //Switches the bitmap state
                invaders_in_state2 = !invaders_in_state2;

                //Draws the invaders
                DrawSmallInvaders();
                DrawMediumInvaders();
                DrawLargeInvaders();

                //Attemots to fire the invaders missiles
                AttemptToFireInvaderNormalMissiles();
            }

            //Spawns a player bullet if the shoot button is pressed and there isn't a bullet on the screen
            if (g_shoot_pressed_flag) {
                g_shoot_pressed_flag = false;

                if (!cannon_missile_on_screen) {
                    FireCannonMissile();
                }
            }

            //Move the cannon shot
            if (g_move_cannon_missile_flag) {
                g_move_cannon_missile_flag = false;

                MoveCannonMissile();
            }

            //Moves the invaders 1st normal missile
            if (g_move_invader_normal_missile_flag[0]) {
                g_move_invader_normal_missile_flag[0] = false;

                MoveInvaderNormalMissile(0);
            }

            //Moves the invaders 2nd normal missile
            if (g_move_invader_normal_missile_flag[1]) {
                g_move_invader_normal_missile_flag[1] = false;

                MoveInvaderNormalMissile(1);
            }

            if (joystick.get_button_flag()) {
                joystick.set_button_flag(0);

                //Detach all game tickers
                DetachTickers();
                game_state = paused;
            }
        }

        sleep();
    }
}

void UpdateScreen()
{
    //Loops through the screen buffer and sets pixels on the LCD
    for (int col = 0; col < 84; ++col) {
        for (int row = 0; row < 48; ++row) {
            if (screen_buffer[col][row]) {
                lcd.setPixel(col, row);
            } else {
                lcd.clearPixel(col, row);
            }
        }
    }

    lcd.refresh();
}

void MoveCannon()
{
    //Clears the ship
    for (int col = 0; col < 9; ++col) {
        for (int row = 0; row < 5; ++row) {
            if(cannon_bitmap[row][col]) {
                screen_buffer[cannon_xpos+col][cannon_ypos+row] = empty_pixel;
            }
        }
    }

    //Changes the position of the ship when the joystick is moved, capping at 0 and 75 so it always fits on the screen
    if (joystick.GetXValue() < 0.25f) {
        cannon_xpos--;
        if (cannon_xpos < 0) {
            cannon_xpos = 0;
        }
    } else if (joystick.GetXValue() > 0.75f) {
        cannon_xpos++;
        if (cannon_xpos > 75) {
            cannon_xpos = 75;
        }
    }

    //Redraws the ship
    for (int col = 0; col < 9; ++col) {
        for (int row = 0; row < 5; ++row) {
            if(cannon_bitmap[row][col]) {
                screen_buffer[cannon_xpos+col][cannon_ypos+row] = cannon_pixel;
            }
        }
    }
}

//Sets the position and aliveness of the small invaders
void InitSmallInvaders()
{
    for (int i = 0; i < 5; ++i) {
        small_invader[i].x_pos = 2 + (i*13);
        small_invader[i].y_pos = 1;
        small_invader[i].status = alive;
    }
}

//Cycles through all the small invaders. If they're not already dead clear them
void ClearSmallInvaders()
{
    for (int i = 0; i < 5; ++i) {
        if (small_invader[i].status) {
            ClearSingleSmallInvader(i);
        }
    }
}

//Cycles through the the screen invader bitmap and sets the pixels in the buffer to 0
void ClearSingleSmallInvader(int invader_no)
{
    for (int col = 0; col < 8; ++col) {
        for (int row = 0; row < 6; ++row) {
            if (invaders_in_state2) {
                if (small_invader_bitmap_1[row][col]) {
                    screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = empty_pixel;
                }
            } else {
                if (small_invader_bitmap_2[row][col]) {
                    screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = empty_pixel;
                }
            }
        }
    }

    small_invader[invader_no].status = (small_invader[invader_no].status == dying) ? dead : alive;
}

void DrawSmallInvaders()
{
    for (int i = 0; i < 5; ++i) {
        if (small_invader[i].status == alive) {
            DrawSingleSmallInvader(i);
        }
    }
}

//Cycles through the the screen invader bitmap and sets the pixels in the buffer
void DrawSingleSmallInvader(int invader_no)
{
    for (int col = 0; col < 8; ++col) {
        for (int row = 0; row < 6; ++row) {
            if (invaders_in_state2) {
                if (small_invader_bitmap_1[row][col]) {
                    screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = first_small_invader_pixel + invader_no;
                }
            } else {
                if (small_invader_bitmap_2[row][col]) {
                    screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = first_small_invader_pixel + invader_no;
                }
            }
        }
    }
}

//Sets the position and aliveness of the medium invaders
void InitMediumInvaders()
{
    for (int i = 0; i < 5; ++i) {
        medium_invader[i].x_pos = 1 + (i*13);
        medium_invader[i].y_pos = 8;
        medium_invader[i].status = alive;
    }
}

void ClearMediumInvaders()
{
    for (int i = 0; i < 5; ++i) {
        if (medium_invader[i].status) {
            ClearSingleMediumInvader(i);
        }
    }
}

void ClearSingleMediumInvader(int invader_no)
{
    for (int col = 0; col < 10; ++col) {
        for (int row = 0; row < 6; ++row) {
            if (invaders_in_state2) {
                if (medium_invader_bitmap_1[row][col]) {
                    screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = empty_pixel;
                }
            } else {
                if (medium_invader_bitmap_2[row][col]) {
                    screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = empty_pixel;
                }
            }
        }
    }

    medium_invader[invader_no].status = (medium_invader[invader_no].status == dying) ? dead : alive;
}

void DrawMediumInvaders()
{
    for (int i = 0; i < 5; ++i) {
        if (medium_invader[i].status == alive) {
            DrawSingleMediumInvader(i);
        }
    }
}

void DrawSingleMediumInvader(int invader_no)
{
    for (int col = 0; col < 10; ++col) {
        for (int row = 0; row < 6; ++row) {
            if (invaders_in_state2) {
                if (medium_invader_bitmap_1[row][col]) {
                    screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = first_medium_invader_pixel + invader_no;
                }
            } else {
                if (medium_invader_bitmap_2[row][col]) {
                    screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = first_medium_invader_pixel + invader_no;
                }
            }
        }
    }
}

//Sets the position and aliveness of the large invaders
void InitLargeInvaders()
{
    for (int i = 0; i < 5; ++i) {
        large_invader[i].x_pos = 0 + (i*13);
        large_invader[i].y_pos = 15;
        large_invader[i].status = alive;
    }
}

void ClearLargeInvaders()
{
    for (int i = 0; i < 5; ++i) {
        if (large_invader[i].status) {
            ClearSingleLargeInvader(i);
        }
    }
}

//Loops through the large invader bitmap, if the pixel in the bitmap is set to 1, set the pixel in the buffer to 0
void ClearSingleLargeInvader(int invader_no)
{
    for (int col = 0; col < 12; ++col) {
        for (int row = 0; row < 6; ++row) {
            if (invaders_in_state2) {
                if (large_invader_bitmap_1[row][col]) {
                    screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = empty_pixel;
                }
            } else {
                if (large_invader_bitmap_2[row][col]) {
                    screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = empty_pixel;
                }
            }
        }
    }

    large_invader[invader_no].status = (large_invader[invader_no].status == dying) ? dead : alive;
}

void DrawLargeInvaders()
{
    for (int i = 0; i < 5; ++i) {
        if (large_invader[i].status == alive) {
            DrawSingleLargeInvader(i);
        }
    }
}

void DrawSingleLargeInvader(int invader_no)
{
    for (int col = 0; col < 12; ++col) {
        for (int row = 0; row < 6; ++row) {
            if (invaders_in_state2) {
                if (large_invader_bitmap_1[row][col]) {
                    screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = first_large_invader_pixel + invader_no;
                }
            } else {
                if (large_invader_bitmap_2[row][col]) {
                    screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = first_large_invader_pixel + invader_no;
                }
            }
        }
    }
}

void InitBarriers()
{
    for (int i = 0; i < 3; ++i) {
        barrier[i].x_pos = 10 + (i*25);
        barrier[i].y_pos = 33;
        //Copies the bitmap into the structs
        memcpy(barrier[i].before_bitmap, barrier_bitmap, sizeof(barrier_bitmap));
        memcpy(barrier[i].after_bitmap, barrier_bitmap, sizeof(barrier_bitmap));
    }
}

void DrawBarriers()
{
    //Clears the barrier and redraws it with damage applied
    for (int i = 0; i < 3; ++i) {
        for (int col = 0; col < 14; ++col) {
            for (int row = 0; row < 8; ++row) {
                if (barrier[i].before_bitmap[row][col]) {
                    screen_buffer[barrier[i].x_pos + col][barrier[i].y_pos + row] = empty_pixel;
                }
                if (barrier[i].after_bitmap[row][col]) {
                    screen_buffer[barrier[i].x_pos + col][barrier[i].y_pos + row] = first_barrier_pixel + i;
                }
            }
        }
        //Copies the after array to the before array
        memcpy(barrier[i].before_bitmap, barrier[i].after_bitmap, sizeof(barrier[i].after_bitmap));
    }
}

void MoveInvaderXPositions()
{
    //Checks the left limit
    int left_invader_limit = CalculateInvaderLeftLimit();

    //Checking the right limit
    int right_invader_limit = CalculateInvaderRightLimit();

    //End of block
    if (invader_direction == RIGHT) {
        MoveInvadersRight(right_invader_limit);
    } else {
        //Checks the first large invader to see if it can travel anymore
        MoveInvadersLeft(left_invader_limit);
    }
}

int CalculateInvaderLeftLimit()
{
    for (int i = 0; i < 5; ++i) {
        if (small_invader[i].status == alive || medium_invader[i].status == alive || large_invader[i].status == alive) {
            return i;
        }
    }

    //Sort gameover out stuff after
    return 4;
}

int CalculateInvaderRightLimit()
{
    for (int i = 4; i >= 0; --i) {
        if (small_invader[i].status == alive || medium_invader[i].status == alive || large_invader[i].status == alive) {
            return i;
        }
    }

    //Sort gameover stuff
    return 0;
}

void MoveInvadersLeft(int limit)
{
    //Checks the first large invader to see if it can travel anymore
    if (large_invader[limit].x_pos > 1) {
        for (int i = 0; i < 5; ++i) {
            small_invader[i].x_pos -= 2;
            medium_invader[i].x_pos -= 2;
            large_invader[i].x_pos -= 2;
        }
    } else {
        //Shifts the Invaders down and passes in the new direction
        MoveInvaderYPositions(RIGHT);
    }
}

void MoveInvadersRight(int limit)
{
    //Checks the first large invader to see if it can travel anymore
    if (large_invader[limit].x_pos < 71) {
        for (int i = 0; i < 5; ++i) {
            small_invader[i].x_pos += 2;
            medium_invader[i].x_pos += 2;
            large_invader[i].x_pos += 2;
        }
    } else {
        //Shifts the Invaders down and passes in the new direction
        MoveInvaderYPositions(LEFT);
    }
}

void MoveInvaderYPositions(bool new_direction)
{
    //Finds the invaders lower limit
    Invader lowest_invader = CalculateInvaderYLimit();

    //When moving down lowest_invader should not equal none
    if (lowest_invader == none) {
        error();
    }

    //If an invader touches the bottom the game ends, otherwise the invaders descend
    if (small_invader[0].y_pos < 33 - (7*lowest_invader)) {
        for (int i = 0; i < 5; ++i) {
            small_invader[i].y_pos += 3;
            medium_invader[i].y_pos += 3;
            large_invader[i].y_pos += 3;
        }
        invader_direction = new_direction;
    } else {
        number_of_lives = 0;
    }
}

Invader CalculateInvaderYLimit()
{
    //Checks to see which row of invaders are still alive to work out maximum y positions
    if (large_invader[0].status == alive || large_invader[1].status == alive || large_invader[2].status == alive || large_invader[3].status == alive || large_invader[4].status == alive) {
        return large;
    } else if (medium_invader[0].status == alive || medium_invader[1].status == alive || medium_invader[2].status == alive || medium_invader[3].status == alive || medium_invader[4].status == alive) {
        return medium;
    } else {
        return small;
    }
}

//Queries the invaders on their status and returns the type of invader which is alive
Invader LowestInvaderInColumn(int column)
{
    if (large_invader[column].status == alive) {
        return large;
    } else if (medium_invader[column].status == alive) {
        return medium;
    } else if (small_invader[column].status == alive) {
        return small;
    } else {
        return none;
    }
}

/*
void InitUFO()
{

}
*/

void DrawUFO()
{
    //Draws the UFO
    int x_pos = 20;
    int y_pos = 25;
    for (int col = 0; col < col_no; ++col) {
        for (int row = 0; row < row_no; ++row) {
            if(ufo_bitmap[row][col]) {
                screen_buffer[x_pos + col][y_pos + row] = ufo_pixel;
            }
        }
    }
}

void FireCannonMissile()
{
    cannon_missile_on_screen = true;

    //Add 4 to cannon x_pos to get shot x_pos
    cannon_missile_x_pos = cannon_xpos + 4;
    cannon_missile_y_pos = 40;
    move_cannon_missile.attach(&move_cannon_missile_isr, 0.05);
}

void MoveCannonMissile()
{
    //Checks bullet will not go beyond the bounds of the screen buffer
    if (cannon_missile_y_pos > -1) {
        //Loops throught the shot bitmap and clears the pixels in the screen buffer
        for (int row = 0; row < 4; ++row) {
            //Clears the position where the bullet was
            screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] = empty_pixel;
        }

        //Increments the shot going up the screen
        --cannon_missile_y_pos;

        //Checks to see if the shot will hit anything
        CollisionDetectionCannonMissile();
    } else {
        //Loops throught the shot bitmap and clears the pixels in the screen buffer
        for (int row = 1; row < 4; ++row) {
            //Clears the position where the bullet was
            screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] = empty_pixel;
        }

        cannon_missile_on_screen = false;
        move_cannon_missile.detach();
    }
}

void MoveInvaderNormalMissile(int missile_no)
{
    //Checks missile will not exceed screen buffer
    if (invader_normal_missile[missile_no].y_pos < 44) {
        //Loops through the bitmap and clears the missile from the screen buffer
        for (int col = 0; col < 3; ++col) {
            for (int row = 0; row < 4; ++row) {
                if (invader_normal_missile_bitmap[row][col]) {
                    screen_buffer[invader_normal_missile[missile_no].x_pos + col][invader_normal_missile[missile_no].y_pos + row] = empty_pixel;
                }
            }
        }

        //Increments the position of the missile
        ++invader_normal_missile[missile_no].y_pos;

        //Collision detection
        CollisionDetectionInvaderNormalMissile(missile_no);

    } else {
        //Loops through the bitmap and clears the pixels still on the screen
        for (int col = 0; col < 3; ++col) {
            for (int row = 0; row < 4; ++row) {
                if (invader_normal_missile_bitmap[row][col]) {
                    screen_buffer[invader_normal_missile[missile_no].x_pos + col][invader_normal_missile[missile_no].y_pos + row] = empty_pixel;
                }
            }
        }

        invader_normal_missile[missile_no].fired = false;
        move_invader_normal_missile[missile_no].detach();
    }
}

//Checks to see what the shot hits. If it hits nothing the shot gets pushed to the screen buffer
void CollisionDetectionCannonMissile()
{
    for (int row = 0; row < 4; ++row) {
        int object_no;
        if (screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] >= first_small_invader_pixel && screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] < (first_small_invader_pixel + 5)) { //Collides with a small invader
            object_no = CannonMissileHitInvader(first_small_invader_pixel, row, small_invader);
            ClearSingleSmallInvader(object_no);
            score += 40;
            break;
        } else if (screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] >= first_medium_invader_pixel && screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] < (first_medium_invader_pixel + 5)) { //Collides with a medium invader
            object_no = CannonMissileHitInvader(first_medium_invader_pixel, row, medium_invader);
            ClearSingleMediumInvader(object_no);
            score += 20;
            break;
        } else if (screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] >= first_large_invader_pixel && screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] < (first_large_invader_pixel + 5)) { //Collides with a large invader
            object_no = CannonMissileHitInvader(first_large_invader_pixel, row, large_invader);
            ClearSingleLargeInvader(object_no);
            score += 10;
            break;
        } else if (screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] >= first_barrier_pixel && screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] < (first_barrier_pixel + 3)) { //Collides with a barrier
            CannonMissileHitBarrier(first_barrier_pixel, row);
            DrawBarriers();
            break;
        } else if (screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] == ufo_pixel) { //Collides with a UFO
            pc.printf("UFO Hit\n");
            cannon_missile_on_screen = false;
            move_cannon_missile.detach();
            break;
        } else {
            screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] = cannon_missile_pixel;
        }
    }
}

//Checks the bottom centre point for a collision. If it doesn't push the bitmap to the screen buffer
void CollisionDetectionInvaderNormalMissile(int missile_no)
{
    //Invader missile coordinates shifted to match centre bottom of bitmap
    int relative_x_pos = invader_normal_missile[missile_no].x_pos + 1;
    int relative_y_pos = invader_normal_missile[missile_no].y_pos + 3;
    if (screen_buffer[relative_x_pos][relative_y_pos] == cannon_pixel) {
        //Decrements the number of lives, pauses the game for 2 seconds
        //Marks the cannon as hit
        g_cannon_hit_flag = true;
        //Detaches all tickers
        DetachTickers();
        //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();
        }
        AttachTickers();
        --number_of_lives;
        invader_normal_missile[missile_no].fired = false;
        move_invader_normal_missile[missile_no].detach();
    } else if (screen_buffer[relative_x_pos][relative_y_pos] >= first_barrier_pixel && screen_buffer[relative_x_pos][relative_y_pos] < (first_barrier_pixel + 3)) {
        //Finds barrier number
        InvaderNormalMissileHitBarrier(first_barrier_pixel, invader_normal_missile[missile_no]);
        invader_normal_missile[missile_no].fired = false;
        move_invader_normal_missile[missile_no].detach();
    } else {
        for (int col = 0; col < 3; ++col) {
            for (int row = 0; row < 4; ++row) {
                if (invader_normal_missile_bitmap[row][col]) {
                    screen_buffer[invader_normal_missile[missile_no].x_pos + col][invader_normal_missile[missile_no].y_pos + row] = invader_normal_missile_pixel;
                }
            }
        }
    }
}

//Finds the invader number the shot hits, sets the hit invader to dying, decrements the no_of_alive_invaders and stops the shot from travalling up the screen
int CannonMissileHitInvader(int first_pixel, int row, struct Invaders (&invader)[5])
{
    int invader_no = screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] - first_pixel;
    invader[invader_no].status = dying;
    cannon_missile_on_screen = false;
    --no_of_alive_invaders;
    move_cannon_missile.detach();

    return invader_no;
}

//Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation
void CannonMissileHitBarrier(int first_pixel, int row)
{
    int barrier_no = screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] - first_pixel;
    //Essentially inverse of barrier init
    int relative_x_pos = cannon_missile_x_pos - 10 - (barrier_no*25) - 1;
    int relative_y_pos = (cannon_missile_y_pos + row) - 33 - 1; //Don't know why it's -1 and not -2
    //Loops through the damage bitmap and modifies the barrier's after bitmap
    for (int col = 0; col < 3; ++col) {
        for (int row_bit = 0; row_bit < 3; ++row_bit) {
            //Makes sure bitmap index does not go out of bounds. If it does go to the next iteration
            if (relative_x_pos + col >= 0 && relative_x_pos + col < 14 && relative_y_pos + row_bit >= 0 && relative_y_pos + row_bit < 8) {
                barrier[barrier_no].after_bitmap[relative_y_pos + row_bit][relative_x_pos + col] *= barrier_cannon_missile_damage_bitmap[row_bit][col];
            }
        }
    }
    cannon_missile_on_screen = false;
    move_cannon_missile.detach();
}

//Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation
void InvaderNormalMissileHitBarrier(int first_pixel, const struct InvaderNormalMissiles (&missile))
{
    int barrier_no = screen_buffer[missile.x_pos + 1][missile.y_pos + 3] - first_pixel;
    //Essentially inverse of barrier init
    int relative_x_pos = (missile.x_pos + 1) - 10 - (barrier_no*25) - 1;
    int relative_y_pos = (missile.y_pos + 3) - 33;
    //Loops through the damage bitmap and modifies the barrier's after bitmap
    for (int col = 0; col < 3; ++col) {
        for (int row_bit = 0; row_bit < 2; ++row_bit) {
            //Makes sure bitmap index does not go out of bounds. If it does go to the next iteration
            if (relative_x_pos + col >= 0 && relative_x_pos + col < 14 && relative_y_pos + row_bit >= 0 && relative_y_pos + row_bit < 8) {
                barrier[barrier_no].after_bitmap[relative_y_pos + row_bit][relative_x_pos + col] *= barrier_invader_normal_missile_damage_bitmap[row_bit][col];
            }
        }
    }
}

void AttemptToFireInvaderNormalMissiles()
{
    //Fires the normal missiles
    //Loops through the 2 allowed missiles and if they're not on the screen, randomly fire
    for (int i = 0; i < 2; ++i) {
        if (!invader_normal_missile[i].fired) {
            //If the random mumber is 1, fire a missile
            //Higher chance when there are less invaders on the screen, up to a 1/3 chance
            if ((rand() % (no_of_alive_invaders + 2)) == 1) {
//            if ((rand() % 5) == 1) {
                int fired_column;
                int loop_limit = 0; //Stops loop from never ending
                Invader missile_source;
                do {
                    fired_column = rand() % 5;
                    missile_source = LowestInvaderInColumn(fired_column);
                    ++loop_limit;
                } while (missile_source == none && loop_limit < 10);

                //Finds the centre point of the chosen invader and fires the missile
                //If the loop limit is reached, the missile source will be none and the for loop will jump to the next iteration
                if (missile_source == none) {
                    continue;
                } else if (missile_source == large) {
                    FireNormalInvaderMissile(i, missile_source, large_invader[fired_column]);
                } else if (missile_source == medium) {
                    FireNormalInvaderMissile(i, missile_source, medium_invader[fired_column]);
                } else {
                    FireNormalInvaderMissile(i, missile_source, small_invader[fired_column]);
                }
            }
        }
    }
}

//Pass by reference to re
void FireNormalInvaderMissile(int missile_no, Invader source, const struct Invaders (&invader))
{
    //Finds the centre point of the chosen invader and fires the missile
    //Enums are implicity converted to ints --> the middle x position of the invader found by offsetting the invader type by 3
    invader_normal_missile[missile_no].x_pos = invader.x_pos + (3 + source);
    invader_normal_missile[missile_no].y_pos = invader.y_pos + 6;
    invader_normal_missile[missile_no].fired = true;

    //Uses a function pointer to attach the ticker
    move_invader_normal_missile[missile_no].attach(move_invader_normal_missile_isr[missile_no], 0.05);
}

void DetachTickers()
{
    update_screen.detach();
    move_enemies.detach();
    if (cannon_missile_on_screen) {
        move_cannon_missile.detach();
    }
    for (int i = 0; i < 2; ++i) {
        if (invader_normal_missile[i].fired) {
            move_invader_normal_missile[i].detach();
        }
    }
}

void AttachTickers()
{
    update_screen.attach(&update_screen_isr, 0.05);
    move_enemies.attach(&move_enemies_isr, ticker_period);

    if (cannon_missile_on_screen) {
        move_cannon_missile.attach(&move_cannon_missile_isr, 0.05);
    }
    for (int i = 0; i < 2; ++i) {
        if (invader_normal_missile[i].fired) {
            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];
    }
}