Space Invaders - Embedded Systems Project 15/16 - Avinash Patel 200860407
Dependencies: Joystick N5110 SDFileSystem mbed
Diff: main.cpp
- Revision:
- 5:34855f712350
- Parent:
- 4:a99953ef9e42
- Child:
- 6:89d4a7f7588b
--- a/main.cpp Thu Mar 31 16:03:26 2016 +0000 +++ b/main.cpp Wed Apr 27 00:19:52 2016 +0000 @@ -5,6 +5,7 @@ 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" @@ -13,46 +14,46 @@ #define LEFT 0 #define RIGHT 1 -//Preprocessor for screen map -/* -#define EMPTY 0 -#define CANNON 1 -#define SMALL 2 -#define MEDIUM 3 -#define LARGE 4 -#define BARRIER 5 -#define UFO 6 -#define CANNON_SHOT 7 -*/ - //Joystick Class class Joystick { public: + //Constructor Joystick(PinName x_axis_pin, PinName y_axis_pin, PinName button_pin) { - //Dynamically allocates the pins + //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(PullUp); - button_->fall(this, &Joystick::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_ = 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++) { + for (int i = 0; i < 5; ++i) { x_sum += x_axis_->read(); y_sum += y_axis_->read(); } @@ -66,7 +67,7 @@ float x_sum = 0; - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { x_sum += x_axis_->read(); } @@ -86,7 +87,7 @@ float y_sum = 0; - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { y_sum += y_axis_->read(); } @@ -114,7 +115,18 @@ private: //Button ISR Method void button_isr() { - g_button_flag_ = 1; + 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: @@ -123,12 +135,16 @@ AnalogIn* y_axis_; InterruptIn* button_; + //Ticker to prevent joystick button bounce + Timeout* button_debounce_; + //Stores X and Y offsets float x_offset_; float y_offset_; //Stores interrupt flags - volatile int g_button_flag_; + volatile bool g_button_flag_; + volatile bool g_button_debounce_flag_; }; // K64F on-board LEDs @@ -144,7 +160,7 @@ Serial pc(USBTX,USBRX); //Joystick -Joystick joystick(PTB2, PTB3, PTB11); +Joystick joystick(PTB3, PTB2, PTB11); //Shoot button InterruptIn shoot_button(PTB18); @@ -152,10 +168,19 @@ //LCD object // VCC, SCE, RST, D/C, MOSI, SCLK, LED N5110 lcd (PTE26 , PTA0 , PTC4 , PTD0 , PTD2 , PTD1 , PTC3); -Ticker update_screen; -//Map holding pixel data of screen -int screen_map[84][48]; +//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 + +//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; @@ -163,12 +188,45 @@ const int first_medium_invader_pixel = 10; const int first_small_invader_pixel = 15; const int ufo_pixel = 20; -const int cannon_shot_pixel = 21; +const int cannon_missile_pixel = 21; +const int invader_normal_missile_pixel = 22; -//Ship bit-map and location +//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 cannon_xpos = 24; const int cannon_ypos = 43; -Ticker move_cannon; +int number_of_lives = 3; +//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}; +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}, @@ -176,32 +234,8 @@ {1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1} }; - -Ticker move_cannon_shot; -bool cannon_shot_on_screen = false; -int cannon_shot_x_pos = 28; -int cannon_shot_y_pos = 40; -bool game_over = false; - -//Enemies -Ticker move_enemies; -bool invaders_in_state2 = true; -bool invader_direction = RIGHT; -//Struct to store enemy data -struct Invaders { - int x_pos; - int y_pos; - bool is_alive; -} small_invader[5], medium_invader[5], large_invader[5]; -int right_column_alive = 4; -int left_column_alive = 0; -enum LowestInvaderRow {small, medium, large}; -LowestInvaderRow lowest_invader_row_alive = large; -//Limits the first invader can travel -int minimum_invader_x_pos = 0; -int maximum_invader_x_pos = 20; //Bitmaps for small invaders -const bool small_invader_bitmap_1[6][8] = { +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}, @@ -209,8 +243,7 @@ {0, 1, 0, 1, 1, 0, 1, 0}, {1, 0, 1, 0, 0, 1, 0, 1} }; - -const bool small_invader_bitmap_2[6][8] = { +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}, @@ -219,7 +252,7 @@ {0, 1, 0, 0, 0, 0, 1, 0} }; //Bitmaps for medium invaders -const bool medium_invader_bitmap_1[6][10] = { +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}, @@ -227,8 +260,7 @@ {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] = { +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}, @@ -237,7 +269,7 @@ {0, 0, 0, 1, 1, 1, 1, 0, 0, 0} }; //Bitmaps for large invaders -const bool large_invader_bitmap_1[6][12] = { +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}, @@ -245,8 +277,7 @@ {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] = { +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}, @@ -254,15 +285,27 @@ {0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0}, {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1} }; - -//Barriers -struct Barriers { - int x_pos; - int y_pos; - bool before_bitmap[8][14]; - bool after_bitmap[8][14]; -} barrier[3]; -int no_of_barriers = 3; +//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}, @@ -273,8 +316,18 @@ {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} }; - -//TEST FOR SPEC UFO +//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}, @@ -283,15 +336,46 @@ {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0} }; -int row_no = 5; -int col_no = 14; +//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 paused menu +struct FSMPaused { + GameState output; + int next_state[2]; +}; +FSMPaused fsm_paused[3] = { + {game, {1, 2}}, + {save, {2, 0}}, + {menu, {0, 1}} +}; //ISR Flags volatile bool g_update_screen_flag = true; -volatile bool g_move_cannon_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_shot_flag = false; -volatile bool g_move_enemies_flag = true; +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; // function prototypes // error function hangs flashing an LED @@ -302,215 +386,158 @@ 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 InitMediumInvaders(); +void ClearSingleMediumInvader(int invader_no); void DrawMediumInvaders(); +void DrawSingleMediumInvader(int invader_no); void InitLargeInvaders(); -void CLearLargeInvaders(); +void ClearLargeInvaders(); +void ClearSingleLargeInvader(int invader_no); void DrawLargeInvaders(); +void DrawSingleLargeInvader(int invader_no); void InitBarriers(); void DrawBarriers(); -void ShiftInvaderXPositions(); -void ShiftInvaderYPositions(bool new_direction); +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(); //void InitUFO(); void DrawUFO(); -//void DrawEnemies(struct Invaders invader[], int no_of_invaders); - //ISR's void update_screen_isr(); -void move_cannon_isr(); +void move_joystick_isr(); void move_enemies_isr(); void shoot_pressed_isr(); -void move_cannon_shot_isr(); +void move_cannon_missile_isr(); +void cannon_hit_isr(); +void joystick_cursor_regulator_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 down_count = 0; +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(); - update_screen.attach(&update_screen_isr, 0.05); - move_cannon.attach(&move_cannon_isr, 0.05); - move_enemies.attach(&move_enemies_isr, 0.5); + //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; - InitSmallInvaders(); - InitMediumInvaders(); - InitLargeInvaders(); - InitBarriers(); - - lcd.refresh(); + //Samples joystick every 0.05 second + move_joystick.attach(&move_joystick_isr, 0.05); while (true) { - //IF the game is over detach all the tickers - if (game_over) { - move_cannon.detach(); - move_enemies.detach(); - + if (game_state == menu) { //Menu screen lcd.clear(); - lcd.printString("Game Over", 1, 2); - } else { - //Updates pixels on the screen - if (g_update_screen_flag) { - g_update_screen_flag = false; + lcd.printString("Menu", 0, 1); - //Loops through the screen map and sets pixels on the LCD - for (int col = 0; col < 84; col++) { - for (int row = 0; row < 48; row++) { - if (screen_map[col][row]) { - lcd.setPixel(col, row); - } else { - lcd.clearPixel(col, row); - } - } - } + 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(); + + game_state = game; } - //Controls cannon movement - if (g_move_cannon_flag) { - g_move_cannon_flag = false; - - MoveCannon(); - DrawBarriers(); + } 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); - lcd.refresh(); - } - //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; + //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 (!cannon_shot_on_screen) { - cannon_shot_on_screen = true; - - pc.printf("Shot Fired\n"); - - //Add 4 to cannon x_pos to get shot x_pos - cannon_shot_x_pos = cannon_xpos + 4; - cannon_shot_y_pos = 40; - move_cannon_shot.attach(&move_cannon_shot_isr, 0.05); + //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]; } } - //Move the cannon's shot - if (g_move_cannon_shot_flag) { - g_move_cannon_shot_flag = false; - - //Checks bullet will not go beyond the bounds of the screen map - if (cannon_shot_y_pos > -1) { - //Loops throught the shot bitmap and clears the pixels in the screen map - for (int row = 0; row < 3; row++) { - //Clears the position where the bullet was - screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] = empty_pixel; - } - - //Increments the shot going up the screen - cannon_shot_y_pos--; - //Checks to see what the shot hits. If it hits nothing the shot gets pushed to the screen map - for (int row = 0; row < 3; row++) { - int object_no; - if (screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] >= first_small_invader_pixel && screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] < (first_small_invader_pixel + 5)) - { - object_no = screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] - first_small_invader_pixel; - small_invader[object_no].is_alive = false; - pc.printf("Small (%d) Hit\n", object_no); - cannon_shot_on_screen = false; - move_cannon_shot.detach(); - break; - } else if (screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] >= first_medium_invader_pixel && screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] < (first_medium_invader_pixel + 5)) { - object_no = screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] - first_medium_invader_pixel; - medium_invader[object_no].is_alive = false; - pc.printf("Medium (%d) Hit\n", object_no); - cannon_shot_on_screen = false; - move_cannon_shot.detach(); - break; - } else if (screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] >= first_large_invader_pixel && screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] < (first_large_invader_pixel + 5)) { - object_no = screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] - first_large_invader_pixel; - large_invader[object_no].is_alive = false; - pc.printf("Large (%d) Hit\n", object_no); - cannon_shot_on_screen = false; - move_cannon_shot.detach(); - break; - } else if (screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] >= first_barrier_pixel && screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] < (first_barrier_pixel + 3)) { - object_no = screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] - first_barrier_pixel; - pc.printf("Barrier (%d) Hit\n", object_no); - cannon_shot_on_screen = false; - move_cannon_shot.detach(); - break; - } else if (screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] == ufo_pixel) { - pc.printf("UFO Hit\n"); - cannon_shot_on_screen = false; - move_cannon_shot.detach(); - break; - } else { - screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] = cannon_shot_pixel; - } - } - } else { - //Loops throught the shot bitmap and clears the pixels in the screen map - for (int row = 1; row < 3; row++) { - //Clears the position where the bullet was - screen_map[cannon_shot_x_pos][cannon_shot_y_pos + row] = empty_pixel; - } - cannon_shot_on_screen = false; - } + if (g_shoot_pressed_flag) { + g_shoot_pressed_flag = false; + game_state = fsm_paused[fsm_state].output; } - } - //Controls enemy movement - if (g_move_enemies_flag) { - g_move_enemies_flag = false; + + } else if (game_state == game) { //Game screen - //Added stuff to move to function - //Checks which columns of invaders are alive on the right and left sides and changes the limits of the first invader - //MOVE TO COLLISION DETECTION WHEN IT'S DONE - //Checking the left side - for (int i = 0; i < 5; i++) { - if (small_invader[i].is_alive || medium_invader[i].is_alive || large_invader[i].is_alive) { - left_column_alive = i; - break; - } - } - minimum_invader_x_pos = 0 - left_column_alive*13; - //Checking the right side - for (int j = 4; j >= 0; j--) { - if (small_invader[j].is_alive || medium_invader[j].is_alive || large_invader[j].is_alive) { - right_column_alive = j; - break; - } - } - maximum_invader_x_pos = 72 - right_column_alive*13; - - //End of block - - //Clears the old bitmaps - ClearSmallInvaders(); - ClearMediumInvaders(); - CLearLargeInvaders(); - - ShiftInvaderXPositions(); - - - DrawSmallInvaders(); - DrawMediumInvaders(); - DrawLargeInvaders(); - //DrawUFO(); - - invaders_in_state2 = !invaders_in_state2; - - lcd.refresh(); - - //TEST CODE - - //END + AttachTickers(); + Game(); } + pc.printf("P: %d\n", g_shoot_pressed_flag); sleep(); } } @@ -526,7 +553,6 @@ // resistors that are enabled by default using InterruptIn sw2.mode(PullNone); sw3.mode(PullNone); - } void error() @@ -545,11 +571,17 @@ 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() @@ -557,29 +589,195 @@ g_update_screen_flag = true; } +void move_enemies_isr() +{ + g_move_enemies_flag = true; +} + void shoot_pressed_isr() { g_shoot_pressed_flag = true; + pc.printf("B\n"); +} + +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.2s to prevent the cursor from behaving erratically + joystick_cursor_regulator.attach(&joystick_cursor_regulator_isr, 0.125); + } +} + +void joystick_cursor_regulator_isr() { + g_joystick_cursor_regulator_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 move_cannon_shot_isr() -{ - g_move_cannon_shot_flag = true; +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) { + 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); + + //Clears the screen + lcd.clear(); + + //Resets the fsm state to 0 + fsm_state = 0; + + //Detach all game tickers + DetachTickers(); + + game_state = paused; + } + } + + sleep(); + } } -void move_cannon_isr() + +void UpdateScreen() { - g_move_cannon_flag = true; + //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++) { + for (int col = 0; col < 9; ++col) { + for (int row = 0; row < 5; ++row) { if(cannon_bitmap[row][col]) { - //lcd.clearPixel(cannon_xpos+col, cannon_ypos+row); - screen_map[cannon_xpos+col][cannon_ypos+row] = empty_pixel; + screen_buffer[cannon_xpos+col][cannon_ypos+row] = empty_pixel; } } } @@ -598,80 +796,76 @@ } //Redraws the ship - for (int col = 0; col < 9; col++) { - for (int row = 0; row < 5; row++) { + for (int col = 0; col < 9; ++col) { + for (int row = 0; row < 5; ++row) { if(cannon_bitmap[row][col]) { - //lcd.setPixel(cannon_xpos+col, cannon_ypos+row); - screen_map[cannon_xpos+col][cannon_ypos+row] = cannon_pixel; + screen_buffer[cannon_xpos+col][cannon_ypos+row] = cannon_pixel; } } } } -void move_enemies_isr() -{ - g_move_enemies_flag = true; -} - //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); + for (int i = 0; i < 5; ++i) { + small_invader[i].x_pos = 2 + (i*13); small_invader[i].y_pos = 1; - small_invader[i].is_alive = true; + 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 (!invaders_in_state2) { - for (int col = 0; col < 8; col++) { - for (int row = 0; row < 6; row++) { - if(small_invader_bitmap_1[row][col] && small_invader[i].x_pos + col >= 0 && small_invader[i].x_pos + col < 84) { - //lcd.clearPixel(small_invader[i].x_pos + col, small_invader[i].y_pos + row); - screen_map[small_invader[i].x_pos + col][small_invader[i].y_pos + row] = empty_pixel; - } + 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 { - for (int col = 0; col < 8; col++) { - for (int row = 0; row < 6; row++) { - if(small_invader_bitmap_2[row][col] && small_invader[i].x_pos + col >= 0 && small_invader[i].x_pos + col < 84) { - //lcd.clearPixel(small_invader[i].x_pos + col, small_invader[i].y_pos + row); - screen_map[small_invader[i].x_pos + col][small_invader[i].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 each small invader clear and redraws them if they're alive - for (int i = 0; i < 5; i++) { - //Checks if the invader is alive - if (small_invader[i].is_alive) { - //Reads off the bitmap and sets the allowed pixels + 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) { - for (int col = 0; col < 8; col++) { - for (int row = 0; row < 6; row++) { - if(small_invader_bitmap_1[row][col]) { - //lcd.setPixel(small_invader[i].x_pos + col, small_invader[i].y_pos + row); - screen_map[small_invader[i].x_pos + col][small_invader[i].y_pos + row] = first_small_invader_pixel + i; - } - } + 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 { - for (int col = 0; col < 8; col++) { - for (int row = 0; row < 6; row++) { - if(small_invader_bitmap_2[row][col]) { - //lcd.setPixel(small_invader[i].x_pos + col, small_invader[i].y_pos + row); - screen_map[small_invader[i].x_pos + col][small_invader[i].y_pos + row] = first_small_invader_pixel + i; - } - } + 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; } } } @@ -681,62 +875,61 @@ //Sets the position and aliveness of the medium invaders void InitMediumInvaders() { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { medium_invader[i].x_pos = 1 + (i*13); medium_invader[i].y_pos = 8; - medium_invader[i].is_alive = true; + medium_invader[i].status = alive; } } void ClearMediumInvaders() { - for (int i = 0; i < 5; i++) { - if (!invaders_in_state2) { - for (int col = 0; col < 10; col++) { - for (int row = 0; row < 6; row++) { - if(medium_invader_bitmap_1[row][col] && medium_invader[i].x_pos + col >= 0 && medium_invader[i].x_pos + col < 84) { - //lcd.clearPixel(medium_invader[i].x_pos + col, medium_invader[i].y_pos + row); - screen_map[medium_invader[i].x_pos + col][medium_invader[i].y_pos + row] = empty_pixel; - } - } - } - } else { - for (int col = 0; col < 10; col++) { - for (int row = 0; row < 6; row++) { - if(medium_invader_bitmap_2[row][col] && medium_invader[i].x_pos + col >= 0 && medium_invader[i].x_pos + col < 84) { - //lcd.clearPixel(medium_invader[i].x_pos + col, medium_invader[i].y_pos + row); - screen_map[medium_invader[i].x_pos + col][medium_invader[i].y_pos + row] = empty_pixel; - } - } - } + 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 each small invader clear and redraws them if they're alive - for (int i = 0; i < 5; i++) { - //Checks if the invader is alive - if (medium_invader[i].is_alive) { - //Reads off the bitmap and sets the allowed pixels + 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) { - for (int col = 0; col < 10; col++) { - for (int row = 0; row < 6; row++) { - if(medium_invader_bitmap_1[row][col]) { - //lcd.setPixel(medium_invader[i].x_pos + col, medium_invader[i].y_pos + row); - screen_map[medium_invader[i].x_pos + col][medium_invader[i].y_pos + row] = first_medium_invader_pixel + i; - } - } + 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 { - for (int col = 0; col < 10; col++) { - for (int row = 0; row < 6; row++) { - if(medium_invader_bitmap_2[row][col]) { - //lcd.setPixel(medium_invader[i].x_pos + col, medium_invader[i].y_pos + row); - screen_map[medium_invader[i].x_pos + col][medium_invader[i].y_pos + row] = first_medium_invader_pixel + i; - } - } + 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; } } } @@ -746,108 +939,71 @@ //Sets the position and aliveness of the large invaders void InitLargeInvaders() { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { large_invader[i].x_pos = 0 + (i*13); large_invader[i].y_pos = 15; - large_invader[i].is_alive = true; + large_invader[i].status = alive; + } +} + +void ClearLargeInvaders() +{ + for (int i = 0; i < 5; ++i) { + if (large_invader[i].status) { + ClearSingleLargeInvader(i); + } } } -void CLearLargeInvaders() +//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 i = 0; i < 5; i++) { - //Reads off the bitmap and sets the allowed pixels - if (!invaders_in_state2) { - for (int col = 0; col < 12; col++) { - for (int row = 0; row < 6; row++) { - if(large_invader_bitmap_1[row][col] && large_invader[i].x_pos + col >= 0 && large_invader[i].x_pos + col < 84) { - ///lcd.clearPixel(large_invader[i].x_pos + col, large_invader[i].y_pos + row); - screen_map[large_invader[i].x_pos + col][large_invader[i].y_pos + row] = empty_pixel; - } + 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; } } - } else { - for (int col = 0; col < 12; col++) { - for (int row = 0; row < 6; row++) { - if(large_invader_bitmap_2[row][col] && large_invader[i].x_pos + col >= 0 && large_invader[i].x_pos + col < 84) { - //lcd.clearPixel(large_invader[i].x_pos + col, large_invader[i].y_pos + row); - screen_map[large_invader[i].x_pos + col][large_invader[i].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 DrawLargeInvaders() -{ - //For each small invader clear and redraws them if they're alive - for (int i = 0; i < 5; i++) { - //Checks if the invader is alive - if (large_invader[i].is_alive) { - //Reads off the bitmap and sets the allowed pixels - if (invaders_in_state2) { - for (int col = 0; col < 12; col++) { - for (int row = 0; row < 6; row++) { - if(large_invader_bitmap_1[row][col]) { - //lcd.setPixel(large_invader[i].x_pos + col, large_invader[i].y_pos + row); - screen_map[large_invader[i].x_pos + col][large_invader[i].y_pos + row] = first_large_invader_pixel + i; - } - } - } - } else { - for (int col = 0; col < 12; col++) { - for (int row = 0; row < 6; row++) { - if(large_invader_bitmap_2[row][col]) { - //lcd.setPixel(large_invader[i].x_pos + col, large_invader[i].y_pos + row); - screen_map[large_invader[i].x_pos + col][large_invader[i].y_pos + row] = first_large_invader_pixel + i; - } - } - } - } - } - } -} -/* -void DrawInvaders(struct Invaders invader[], int no_of_invaders, bool bitmap[][]) -{ - //For each small invader clear and redraws them if they're alive - for (int i = 0; i < no_of_invaders; i++) { - //Clears the enemy position - lcd.drawRect(invader[i].x_pos, invader[i].y_pos, 12, 6, 2); - - //Checks if the invader is alive - if (invader[i].is_alive) { - //Reads off the bitmap and sets the allowed pixels - int col = 0; - int row = 0; - //Flips the bitmap everytime the function is called - if (invaders_in_state2) { - for (col = 0; col < 12; col++) { - for (row = 0; row < 6; row++) { - if(large_invader_bitmap_1[row][col]) { - lcd.setPixel(large_invader[i].x_pos + col, large_invader[i].y_pos + row); - } - } - } - } else { - for (col = 0; col < 12; col++) { - for (row = 0; row < 6; row++) { - if(large_invader_bitmap_2[row][col]) { - lcd.setPixel(large_invader[i].x_pos + col, large_invader[i].y_pos + row); - } - } - } - } - } - } -} -*/ - - void InitBarriers() { - for (int i = 0; i < no_of_barriers; i++) { + 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 @@ -859,16 +1015,14 @@ void DrawBarriers() { //Clears the barrier and redraws it with damage applied - for (int i = 0; i < no_of_barriers; i++) { - for (int col = 0; col < 14; col++) { - for (int row = 0; row < 8; row++) { + 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]) { - //lcd.clearPixel(barrier[i].x_pos + col, barrier[i].y_pos + row); - screen_map[barrier[i].x_pos + col][barrier[i].y_pos + row] = empty_pixel; + screen_buffer[barrier[i].x_pos + col][barrier[i].y_pos + row] = empty_pixel; } if (barrier[i].after_bitmap[row][col]) { - //lcd.setPixel(barrier[i].x_pos + col, barrier[i].y_pos + row); - screen_map[barrier[i].x_pos + col][barrier[i].y_pos + row] = first_barrier_pixel + i; + screen_buffer[barrier[i].x_pos + col][barrier[i].y_pos + row] = first_barrier_pixel + i; } } } @@ -877,57 +1031,124 @@ } } -void ShiftInvaderXPositions() +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 - if (large_invader[0].x_pos < maximum_invader_x_pos) { - 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 { - ShiftInvaderYPositions(LEFT); + 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 { - //Checks the first large invader to see if it can travel anymore - if (large_invader[0].x_pos > minimum_invader_x_pos) { - 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 { - ShiftInvaderYPositions(RIGHT); - } + //Shifts the Invaders down and passes in the new direction + MoveInvaderYPositions(RIGHT); } } -void ShiftInvaderYPositions(bool new_direction) +void MoveInvadersRight(int limit) { - //Checks to see which row of invaders are still alive to work out maximum y positions - if (large_invader[0].is_alive || large_invader[1].is_alive || large_invader[2].is_alive || large_invader[3].is_alive || large_invader[4].is_alive) { - lowest_invader_row_alive = large; - } else if (medium_invader[0].is_alive || medium_invader[1].is_alive || medium_invader[2].is_alive || medium_invader[3].is_alive || medium_invader[4].is_alive) { - lowest_invader_row_alive = medium; + //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 { - lowest_invader_row_alive = small; + //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 < 39 - (7*lowest_invader_row_alive)) { - for (int i = 0; i < 5; i++) { + 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; } + 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 { - game_over = true; + return small; } +} - //TEST CODE - down_count++; +//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; + } } /* @@ -942,12 +1163,283 @@ //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++) { + for (int col = 0; col < col_no; ++col) { + for (int row = 0; row < row_no; ++row) { if(ufo_bitmap[row][col]) { - //lcd.setPixel(x_pos + col, y_pos + row); - screen_map[x_pos + col][y_pos + row] = ufo_pixel; + 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 Ticker object on the stack with a period of 2 seconds to pause the game for 2 seconds + Ticker 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; + 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); + } + } } \ No newline at end of file