Space Invaders - Embedded Systems Project 15/16 - Avinash Patel 200860407
Dependencies: Joystick N5110 SDFileSystem mbed
main.cpp
- Committer:
- avi23
- Date:
- 2016-04-27
- Revision:
- 5:34855f712350
- Parent:
- 4:a99953ef9e42
- Child:
- 6:89d4a7f7588b
File content as of revision 5:34855f712350:
/* 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_; //Ticker 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 //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 cannon_xpos = 24; const int cannon_ypos = 43; int number_of_lives = 3; //Cannon missile related integers int cannon_missile_x_pos = 28; int cannon_missile_y_pos = 40; //Invader related integers 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}, {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 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_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; // 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(); //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(); //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(); lcd.printString("Menu", 0, 1); 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; } } else if (game_state == paused) { //Paused screen //Prints paused and a line underneath lcd.printString("Paused", 24, 0); lcd.drawLine(0, 7, 83, 7, 2); //Displays the current score char buffer[14]; sprintf(buffer, "Score: %d", score); lcd.printString(buffer, 0, 1); //Displays the no of lives sprintf(buffer, "Lives: %d", number_of_lives); lcd.printString(buffer, 0, 2); lcd.drawLine(0, 23, 83, 23, 2); //Prints options on pause menu lcd.printString("Resume", 10, 3); lcd.printString("Save", 10, 4); lcd.printString("Quit", 10, 5); //Draws the cursor cursor_y_pos = ((fsm_state+3)*8)+3; //Adds 3 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3 lcd.drawRect(64, cursor_y_pos, 2, 2, 1); if (g_move_joystick_flag) { g_move_joystick_flag = false; //If the joystick is is pushed down half way clear the cursor and set the next state if (joystick.GetYValue() < 0.25f) { //Clears the cursor lcd.drawRect(64, cursor_y_pos, 2, 2, 2); //Sets the new state fsm_state = fsm_paused[fsm_state].next_state[0]; } else if (joystick.GetYValue() > 0.75f) { //Clears the cursor lcd.drawRect(64, cursor_y_pos, 2, 2, 2); //Sets the new state fsm_state = fsm_paused[fsm_state].next_state[1]; } } if (g_shoot_pressed_flag) { g_shoot_pressed_flag = false; game_state = fsm_paused[fsm_state].output; } } else if (game_state == game) { //Game screen AttachTickers(); Game(); } pc.printf("P: %d\n", g_shoot_pressed_flag); 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() { 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 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 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 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); } } }