Space Invaders - Embedded Systems Project 15/16 - Avinash Patel 200860407
Dependencies: Joystick N5110 SDFileSystem mbed
Diff: main.cpp
- Revision:
- 8:b2faec20ed8f
- Parent:
- 7:babc367a3333
- Child:
- 9:cbb982b7e353
diff -r babc367a3333 -r b2faec20ed8f main.cpp --- a/main.cpp Mon May 02 16:28:07 2016 +0000 +++ b/main.cpp Tue May 03 20:16:06 2016 +0000 @@ -1,3 +1,8 @@ +/** +@file main.cpp +@brief Implementation File +*/ + /* Space Invaders - Avinash Patel 200860407 @@ -12,14 +17,11 @@ #include "mbed.h" #include "main.h" -int row_no = 5; -int col_no = 14; - int main() { - //Wait for 2 seconds to allow power to settle + ///Wait for 2 seconds to allow power to settle wait(1); - //Initalises the board and perhiperals + ///Initalises the board and perhiperals init_K64F(); init_serial(); init_shoot(); @@ -28,67 +30,166 @@ lcd.init(); lcd.clear(); - //Configures the function pointer for the invaders normal missile + ///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 + ///Samples joystick every 0.05 second move_joystick.attach(&move_joystick_isr, 0.05); - - while (true) { - if (game_state == menu) { //Menu screen + if (game_state == menu) { ///Menu screen + lcd.clear(); + fsm_state = 0; ///Sets the fsm state to 0 + MenuScreen(); + } else if (game_state == paused) { ///Paused screen + ///Clears the screen lcd.clear(); - fsm_state = 0; //Sets the fsm state to 0 - while (game_state == menu) { - lcd.printString("Space Invaders", 0, 0); - lcd.printString("Menu", 28, 1); - lcd.drawLine(0, 15, 83, 15, 2); - - lcd.printString("New Game", 5, 2); - lcd.printString("Load Game", 5, 3); - lcd.printString("High Scores", 5, 4); - lcd.printString("Settings", 5, 5); + fsm_state = 0; ///Ssets the fsm state to 0 + PauseScreen(); + } else if (game_state == game) { ///Game screen + AttachTickers(); + Game(); + } else if (game_state == save) { + ///Creates file pointer and attaches the file + FILE *fp; + fp = fopen("/sd/game_save.txt", "w"); - //Draws the cursor - cursor_y_pos = ((fsm_state+2)*8)+3; //Adds 2 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3 - lcd.drawRect(74, cursor_y_pos, 2, 2, 1); - - if (g_move_joystick_flag) { - g_move_joystick_flag = false; - - //Moves the cursor to match the selected option, only if the joystick isn't centred - MoveCursor(fsm_main_menu); + if (fp == NULL) { ///If file can't be opened then error + lcd.clear(); + lcd.printString("Error Opening", 1, 1); + wait(2.0); + } else { + ///Prints booleans to file + fprintf(fp, "%d\n", invaders_in_state2); + fprintf(fp, "%d,%d,%d,%d,%d,%d\n", invader_direction, cannon_missile_on_screen, ufo_on_screen, ufo_direction, ufo_bonus, is_muted); + ///Prints integers to file + fprintf(fp, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", score, shot_count, number_of_lives, cannon_xpos, cannon_missile_x_pos, cannon_missile_y_pos, no_of_alive_invaders, down_count, ufo_x_pos); + ///Prints small invader info to file + for (int small = 0; small < 5; ++small) { + fprintf(fp, "%d,%d,%d\n", small_invader[small].x_pos, small_invader[small].y_pos, small_invader[small].status); + } + ///Prints medium invader info to file + for (int medium = 0; medium < 5; ++medium) { + fprintf(fp, "%d,%d,%d\n", medium_invader[medium].x_pos, medium_invader[medium].y_pos, medium_invader[medium].status); + } + ///Prints large invader info to file + for (int large = 0; large < 5; ++large) { + fprintf(fp, "%d,%d,%d\n", large_invader[large].x_pos, large_invader[large].y_pos, large_invader[large].status); } - - if (g_shoot_pressed_flag) { - g_shoot_pressed_flag = false; - - game_state = fsm_main_menu[fsm_state].output; - - //If the game state is equal to the game initalise it - if (game_state == game) { - InitaliseGame(); + ///Stores invader missile data + for (int missile = 0; missile < 2; ++missile) { + fprintf(fp, "%d,%d,%d\n", invader_normal_missile[missile].x_pos, invader_normal_missile[missile].y_pos, invader_normal_missile[missile].fired); + } + ///Stores barrier data + for (int barrier_no = 0; barrier_no < 3; ++barrier_no) { + fprintf(fp, "%d,%d\n", barrier[barrier_no].x_pos, barrier[barrier_no].y_pos); + ///Stores barrier before bitmaps + for (int row = 0; row < 8; ++row) { + for (int col = 0; col < 14; ++col) { + if (col < 13) { + fprintf(fp, "%d,", barrier[barrier_no].before_bitmap[row][col]); + } else { + fprintf(fp, "%d\n", barrier[barrier_no].before_bitmap[row][col]); + } + } + } + ///Stores barrier after bitmaps + for (int row = 0; row < 8; ++row) { + for (int col = 0; col < 14; ++col) { + if (col < 13) { + fprintf(fp, "%d,", barrier[barrier_no].after_bitmap[row][col]); + } else { + fprintf(fp, "%d\n", barrier[barrier_no].after_bitmap[row][col]); + } + } } } - if(joystick.get_button_flag()) { - joystick.set_button_flag(0); - } - - lcd.refresh(); - sleep(); + fclose(fp); + game_state = paused; } - } else if (game_state == paused) { //Paused screen - //Clears the screen - lcd.clear(); - //Resets the fsm state to 0 - fsm_state = 0; - PauseScreen(); - } else if (game_state == game) { //Game screen - AttachTickers(); - Game(); + } else if (game_state == load) { + ///Creates file pointer and attaches the file + FILE *fp; + fp = fopen("/sd/game_save.txt", "r"); + rewind(fp); + + if (fp == NULL) { ///If file can't be opened then error + lcd.clear(); + lcd.printString("Error Opening", 1, 1); + wait(2.0); + + game_state = menu; + } else { + int test; + ///Prints booleans to file + fscanf(fp, "%d\n", &invaders_in_state2); + test = fscanf(fp, "%d,%d,%d,%d,%d,%d", &invader_direction, &cannon_missile_on_screen, &ufo_on_screen, &ufo_direction, &ufo_bonus, &is_muted); + + pc.printf("Bool: %d\n", test); + ///Prints integers to file + test = fscanf(fp, "%d,%d,%d,%d,%d,%d,%d,%d,%d", &score, &shot_count, &number_of_lives, &cannon_xpos, &cannon_missile_x_pos, &cannon_missile_y_pos, &no_of_alive_invaders, &down_count, &ufo_x_pos); + pc.printf("Int: %d\n", test); + ///Prints small invader info to file + for (int small = 0; small < 5; ++small) { + test = fscanf(fp, "%d,%d,%d", &(small_invader[small].x_pos), &(small_invader[small].y_pos), &(small_invader[small].status)); + pc.printf("Small: %d\n", test); + } + ///Prints medium invader info to file + for (int medium = 0; medium < 5; ++medium) { + test = fscanf(fp, "%d,%d,%d", &(medium_invader[medium].x_pos), &(medium_invader[medium].y_pos), &(medium_invader[medium].status)); + pc.printf("Medium: %d\n", test); + } + ///Prints large invader info to file + for (int large = 0; large < 5; ++large) { + test = fscanf(fp, "%d,%d,%d", &(large_invader[large].x_pos), &(large_invader[large].y_pos), &(large_invader[large].status)); + pc.printf("Large: %d\n", test); + } + ///Stores invader missile data + for (int missile = 0; missile < 2; ++missile) { + test = fscanf(fp, "%d,%d,%d", &(invader_normal_missile[missile].x_pos), &(invader_normal_missile[missile].y_pos), &(invader_normal_missile[missile].fired)); + pc.printf("Missile: %d\n", test); + } + ///Stores barrier data + for (int barrier_no = 0; barrier_no < 3; ++barrier_no) { + test = fscanf(fp, "%d,%d", &(barrier[barrier_no].x_pos), &(barrier[barrier_no].y_pos)); + pc.printf("Barrier: %d\n", test); + ///Stores barrier before bitmaps + for (int row = 0; row < 8; ++row) { + for (int col = 0; col < 14; ++col) { + if (col < 13) { + test = fscanf(fp, "%d,", &(barrier[barrier_no].before_bitmap[row][col])); + pc.printf("Barrier Before: %d\n", test); + } else { + test = fscanf(fp, "%d", &(barrier[barrier_no].before_bitmap[row][col])); + pc.printf("Barrier Before: %d\n", test); + } + } + } + ///Stores barrier after bitmaps + for (int row = 0; row < 8; ++row) { + for (int col = 0; col < 14; ++col) { + if (col < 13) { + test = fscanf(fp, "%d,", &(barrier[barrier_no].after_bitmap[row][col])); + pc.printf("Barrier After: %d\n", test); + } else { + test = fscanf(fp, "%d", &(barrier[barrier_no].after_bitmap[row][col])); + pc.printf("Barrier After: %d\n", test); + } + } + } + } + + + fclose(fp); + game_state = game; + + g_update_screen_flag = true; + g_move_joystick_flag = true; + g_move_enemies_flag = true; + g_fire_buzzer_flag = true; + } } sleep(); @@ -97,20 +198,20 @@ void init_K64F() { - // on-board LEDs are active-low, so set pin high to turn them off. + /// 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 + /// 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 + while(1) { /// if error, hang while flashing error message r_led = 0; wait(0.2); r_led = 1; @@ -120,15 +221,15 @@ void init_serial() { - // set to highest baud - ensure terminal software matches + /// set to highest baud - ensure terminal software matches pc.baud(115200); } -//Seeds the random number generator with noise from an analog in pin +///Seeds the random number generator with noise from an analog in pin void init_rng() { - AnalogIn rng_seed(PTC10); //Creates a AnalogIn on a unused pin - srand(floor(10000*rng_seed.read())); //Sets the seed as 10000x the input of the analog in + AnalogIn rng_seed(PTC10); ///Creates a AnalogIn on a unused pin + srand(floor(10000*rng_seed.read())); ///Sets the seed as 10000x the input of the analog in } void init_shoot() @@ -149,12 +250,12 @@ void shoot_pressed_isr() { - //Only sets the shoot pressed flag 0.1s after the last press + ///Only sets the shoot pressed flag 0.1s after the last press if (!g_shoot_button_debounce_flag) { g_shoot_pressed_flag = true; g_shoot_button_debounce_flag = true; - //Attaches a timeout to clear the debounce flag 0.125 seconds after it was set + ///Attaches a timeout to clear the debounce flag 0.125 seconds after it was set shoot_button_debounce.attach(&shoot_button_debounce_isr, 0.125); } } @@ -166,15 +267,15 @@ void move_joystick_isr() { - //Always set the move flag in a game + ///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 + ///Only sets the flag if the regulator is not set g_move_joystick_flag = true; g_joystick_cursor_regulator_flag = true; - //Attachs a timeout to clear the regulator in 0.1s to prevent the cursor from behaving erratically + ///Attachs a timeout to clear the regulator in 0.1s to prevent the cursor from behaving erratically joystick_cursor_regulator.attach(&joystick_cursor_regulator_isr, 0.1); } } @@ -204,11 +305,22 @@ g_cannon_hit_flag = false; } +void move_ufo_isr() +{ + g_move_ufo_flag = true; +} + +void fire_buzzer_isr() +{ + g_fire_buzzer_flag = true; +} + void Game() { - //Stays within the loop while the selected state is game + ///Stays within the loop while the selected state is game while (game_state == game) { - //If the game is over detach all the tickers + pc.printf("Mute: %d\n", is_muted); + ///If the game is over detach all the tickers if (number_of_lives == 0) { DetachTickers(); @@ -217,26 +329,27 @@ char buffer[14]; sprintf(buffer, "Score: %d", score); lcd.printString(buffer, 1, 3); - } else if (no_of_alive_invaders == 0) { //If the player wins a round - //Resets the no of alive invaders + } 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 + down_count = 0; + ///Detaches the enemy ticker while reinitalising invaders move_enemies.detach(); - //Reinitalises objects + ///Reinitalises objects InitSmallInvaders(); InitMediumInvaders(); InitLargeInvaders(); - //Reattaches enemy ticker + ///Reattaches enemy ticker move_enemies.attach(&move_enemies_isr, 1); } else { - //Updates pixels on the screen + ///Updates pixels on the screen if (g_update_screen_flag) { g_update_screen_flag = false; UpdateScreen(); } - //Controls cannon movement + ///Controls cannon movement if (g_move_joystick_flag) { g_move_joystick_flag = false; @@ -244,67 +357,116 @@ DrawBarriers(); } - //Controls enemy movement + ///Controls enemy movement if (g_move_enemies_flag) { g_move_enemies_flag = false; - //Increses the speed the invaders move + ///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 + //Attach the buzzer timeout if mute is unselected + if (!is_muted) { + fire_buzzer.attach(&fire_buzzer_isr, ticker_period/2.0f); + } + + ///Clears the old bitmaps ClearSmallInvaders(); ClearMediumInvaders(); ClearLargeInvaders(); MoveInvaderXPositions(); - //Switches the bitmap state + ///Switches the bitmap state invaders_in_state2 = !invaders_in_state2; - //Draws the invaders + ///Draws the invaders DrawSmallInvaders(); DrawMediumInvaders(); DrawLargeInvaders(); - //Attemots to fire the invaders missiles + ///Attempts to fire the invaders missiles AttemptToFireInvaderNormalMissiles(); + + ///Attempts to spawn UFO + AttemptToSpawnUFO(); } - //Spawns a player bullet if the shoot button is pressed and there isn't a bullet on the screen + if (g_fire_buzzer_flag && !is_muted) { + g_fire_buzzer_flag = false; + + if (play_sound) { + buzzer.period(1/fsm_buzzer[buzzer_state].frequency); + buzzer.write(0.5); + play_sound = false; + } else { + buzzer.write(0); + buzzer_state = fsm_buzzer[buzzer_state].next_state; + play_sound = true; + } + } + + ///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(); + + ///Counts fired missiles to see if the ufo bonus can be applied + if (ufo_bonus && shot_count > 14) { + shot_count = 0; + } else if (shot_count > 22) { + shot_count = 0; + } else { + ++shot_count; + } } } - //Move the cannon shot + ///Move the cannon shot if (g_move_cannon_missile_flag) { g_move_cannon_missile_flag = false; MoveCannonMissile(); } - //Moves the invaders 1st normal missile + ///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 + ///Moves the invaders 2nd normal missile if (g_move_invader_normal_missile_flag[1]) { g_move_invader_normal_missile_flag[1] = false; MoveInvaderNormalMissile(1); } + ///Moves the UFO + if (g_move_ufo_flag) { + g_move_ufo_flag = false; + + ///Clears the UFO + ClearUFO(); + + ///Moves the UFO in the correct direction + if (ufo_direction == RIGHT) { + ufo_x_pos += 2; + } else { + ufo_x_pos -= 2; + } + + ///Draws the UFO + DrawUFO(); + } + if (joystick.get_button_flag()) { joystick.set_button_flag(0); - //Detach all game tickers + ///Detach all game tickers DetachTickers(); game_state = paused; } @@ -316,20 +478,29 @@ void InitaliseGame() { - //Clears the screen buffer and runs init functions + ///Clears the screen buffer and runs init functions memset(screen_buffer, 0, sizeof(screen_buffer)); no_of_alive_invaders = 15; score = 0; number_of_lives = 3; + down_count = 0; + cannon_xpos = 15; + ufo_bonus = false; + shot_count = 0; InitSmallInvaders(); InitMediumInvaders(); InitLargeInvaders(); InitBarriers(); - //Sets the flags so enemies pop up straight away + ///If the sound is set activate the buzzer flag + if (!is_muted) { + g_fire_buzzer_flag = true; + } + ///Sets the flags so enemies pop up straight away g_update_screen_flag = true; g_move_joystick_flag = true; g_move_enemies_flag = true; - //Forces the missiles to have the fired flags to flase + g_fire_buzzer_flag = true; + ///Forces the missiles to have the fired flags to flase cannon_missile_on_screen = false; invader_normal_missile[0].fired = false; invader_normal_missile[1].fired = false; @@ -337,7 +508,7 @@ void UpdateScreen() { - //Loops through the screen buffer and sets pixels on the LCD + ///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]) { @@ -353,7 +524,7 @@ void MoveCannon() { - //Clears the cannon + ///Clears the cannon for (int col = 0; col < 9; ++col) { for (int row = 0; row < 5; ++row) { if(cannon_bitmap[row][col]) { @@ -362,7 +533,7 @@ } } - //Changes the position of the cannon when the joystick is moved, capping at 0 and 75 so it always fits on the screen + ///Changes the position of the cannon 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) { @@ -375,7 +546,7 @@ } } - //Redraws the cannon + ///Redraws the cannon for (int col = 0; col < 9; ++col) { for (int row = 0; row < 5; ++row) { if(cannon_bitmap[row][col]) { @@ -385,17 +556,17 @@ } } -//Sets the position and status of the small invaders +///Sets the position and status of the small invaders void InitSmallInvaders() { for (int i = 0; i < 5; ++i) { - small_invader[i].x_pos = 2 + (i*13); // Large invaders are 12 across so add 13 for a gap space + small_invader[i].x_pos = 2 + (i*13); /// Large invaders are 12 across so add 13 for a gap space 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 +///Cycles through all the small invaders. If they're not already dead clear them void ClearSmallInvaders() { for (int i = 0; i < 5; ++i) { @@ -405,7 +576,7 @@ } } -//Cycles through the the screen invader bitmap and sets the pixels in the buffer to 0 +///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) { @@ -425,7 +596,7 @@ small_invader[invader_no].status = (small_invader[invader_no].status == dying) ? dead : alive; } -//Cycles through all the small invaders. If they're alive set them in the screen buffer +///Cycles through all the small invaders. If they're alive set them in the screen buffer void DrawSmallInvaders() { for (int i = 0; i < 5; ++i) { @@ -435,7 +606,7 @@ } } -//Cycles through the the screen invader bitmap and sets the pixels in the buffer +///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) { @@ -453,17 +624,17 @@ } } -//Sets the position and aliveness of the medium invaders +///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); // Large invaders are 12 across so add 13 for a gap space + medium_invader[i].x_pos = 1 + (i*13); /// Large invaders are 12 across so add 13 for a gap space medium_invader[i].y_pos = 8; medium_invader[i].status = alive; } } -//Cycles through all the medium invaders. If they're not already dead clear them +///Cycles through all the medium invaders. If they're not already dead clear them void ClearMediumInvaders() { for (int i = 0; i < 5; ++i) { @@ -473,7 +644,7 @@ } } -//Cycles through the the screen invader bitmap and sets the pixels in the buffer to 0 +///Cycles through the the screen invader bitmap and sets the pixels in the buffer to 0 void ClearSingleMediumInvader(int invader_no) { for (int col = 0; col < 10; ++col) { @@ -493,7 +664,7 @@ medium_invader[invader_no].status = (medium_invader[invader_no].status == dying) ? dead : alive; } -//Cycles through all the medium invaders. If they're alive set them in the screen buffer +///Cycles through all the medium invaders. If they're alive set them in the screen buffer void DrawMediumInvaders() { for (int i = 0; i < 5; ++i) { @@ -503,7 +674,7 @@ } } -//Cycles through the the screen invader bitmap and sets the pixels in the buffer +///Cycles through the the screen invader bitmap and sets the pixels in the buffer void DrawSingleMediumInvader(int invader_no) { for (int col = 0; col < 10; ++col) { @@ -521,7 +692,7 @@ } } -//Sets the position and status of the large invaders +///Sets the position and status of the large invaders void InitLargeInvaders() { for (int i = 0; i < 5; ++i) { @@ -531,7 +702,7 @@ } } -//Cycles through all the large invaders. If they're not already dead clear them +///Cycles through all the large invaders. If they're not already dead clear them void ClearLargeInvaders() { for (int i = 0; i < 5; ++i) { @@ -541,7 +712,7 @@ } } -//Loops through the large invader bitmap, if the pixel in the bitmap is set to 1, set the pixel in the buffer to 0 +///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) { @@ -561,7 +732,7 @@ large_invader[invader_no].status = (large_invader[invader_no].status == dying) ? dead : alive; } -//Cycles through all the large invaders. If they're alive set them in the screen buffer +///Cycles through all the large invaders. If they're alive set them in the screen buffer void DrawLargeInvaders() { for (int i = 0; i < 5; ++i) { @@ -571,7 +742,7 @@ } } -//Cycles through the the screen invader bitmap and sets the pixels in the buffer +///Cycles through the the screen invader bitmap and sets the pixels in the buffer void DrawSingleLargeInvader(int invader_no) { for (int col = 0; col < 12; ++col) { @@ -589,13 +760,13 @@ } } -//Sets the position and loads the bitmap into the barrier objects +///Sets the position and loads the bitmap into the barrier objects 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 + ///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)); } @@ -603,7 +774,7 @@ void DrawBarriers() { - //Clears the barrier and redraws it with damage applied + ///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) { @@ -615,27 +786,27 @@ } } } - //Copies the after array to the before array + ///Copies the after array to the before array memcpy(barrier[i].before_bitmap, barrier[i].after_bitmap, sizeof(barrier[i].after_bitmap)); } } void MoveInvaderXPositions() { - //Moves the invader in the current direction + ///Moves the invader in the current direction if (invader_direction == RIGHT) { - //Checking the right limit + ///Checking the right limit int right_invader_limit = CalculateInvaderRightLimit(); MoveInvadersRight(right_invader_limit); } else { - //Checks the left limit + ///Checks the left limit int left_invader_limit = CalculateInvaderLeftLimit(); MoveInvadersLeft(left_invader_limit); } } -//Checks the status off the invaders per column, starting from the left -//If they're alive return the row number +///Checks the status off the invaders per column, starting from the left +///If they're alive return the row number int CalculateInvaderLeftLimit() { for (int i = 0; i < 5; ++i) { @@ -644,12 +815,12 @@ } } - //Sort gameover out stuff after + ///Sort gameover out stuff after return 4; } -//Checks the status off the invaders per column, starting from the right -//If they're alive return the row number +///Checks the status off the invaders per column, starting from the right +///If they're alive return the row number int CalculateInvaderRightLimit() { for (int i = 4; i >= 0; --i) { @@ -658,59 +829,60 @@ } } - //Sort gameover stuff + ///Sort gameover stuff return 0; } void MoveInvadersLeft(int limit) { - //Checks the first large invader to see if it can travel anymore + ///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) { - //Moves the invaders 2 to the left + ///Moves the invaders 2 to the left 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 + ///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 + ///Checks the first large invader to see if it can travel anymore if (large_invader[limit].x_pos < 71) { - //Moves the invaders 2 to the right + ///Moves the invaders 2 to the right 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 + ///Shifts the Invaders down and passes in the new direction MoveInvaderYPositions(LEFT); } } void MoveInvaderYPositions(bool new_direction) { - //Finds the invaders lower limit + ///Finds the invaders lower limit Invader lowest_invader = CalculateInvaderYLimit(); - //When moving down lowest_invader should not equal none + ///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 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; } + ++down_count; invader_direction = new_direction; } else { number_of_lives = 0; @@ -719,7 +891,7 @@ Invader CalculateInvaderYLimit() { - //Checks to see which row of invaders are still alive to work out maximum y positions + ///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) { @@ -729,7 +901,7 @@ } } -//Queries the invaders on their status and returns the type of invader which is alive +///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) { @@ -743,59 +915,38 @@ } } -/* -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() { - //Sets the cannon fired flag to true + ///Sets the cannon fired flag to true cannon_missile_on_screen = true; - //Offset cannon missile x position by 4 of the cannons x pos + ///Offset cannon missile x position by 4 of the cannons x pos cannon_missile_x_pos = cannon_xpos + 4; - //Will always have a starting y of 40 + ///Will always have a starting y of 40 cannon_missile_y_pos = 40; - //Attach the move cannon missile + ///Attach the move cannon missile move_cannon_missile.attach(&move_cannon_missile_isr, 0.05); } void MoveCannonMissile() { - //Checks bullet will not go beyond the bounds of the screen buffer + ///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 + ///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 + ///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 + ///Increments the shot going up the screen --cannon_missile_y_pos; - //Checks to see if the shot will hit anything + ///Checks to see if the shot will hit anything CollisionDetectionCannonMissile(); } else { - //Loops throught the shot bitmap and clears the pixels in the screen buffer + ///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 + ///Clears the position where the bullet was screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] = empty_pixel; } @@ -804,38 +955,48 @@ } } -//Checks to see what the shot hits. If it hits nothing the shot gets pushed to the screen buffer +///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; int pixel_check = screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row]; - pc.printf("Pixel check: %d\n", pixel_check); - if (pixel_check >= first_small_invader_pixel && pixel_check < ufo_pixel) { //Collides with a small invader - //Find the object no of the small invader it hit, clears it and increments the score + if (pixel_check >= first_small_invader_pixel && pixel_check < ufo_pixel) { ///Collides with a small invader + ///Find the object no of the small invader it hit, clears it and increments the score object_no = CannonMissileHitInvader(first_small_invader_pixel, row, small_invader); ClearSingleSmallInvader(object_no); score += 40; break; - } else if (pixel_check >= first_medium_invader_pixel && pixel_check < first_small_invader_pixel) { //Collides with a medium invader - //Find the object no of the medium invader it hit, clears it and increments the score + } else if (pixel_check >= first_medium_invader_pixel && pixel_check < first_small_invader_pixel) { ///Collides with a medium invader + ///Find the object no of the medium invader it hit, clears it and increments the score object_no = CannonMissileHitInvader(first_medium_invader_pixel, row, medium_invader); ClearSingleMediumInvader(object_no); score += 20; break; - } else if (pixel_check >= first_large_invader_pixel && pixel_check < first_medium_invader_pixel) { //Collides with a large invader - //Find the object no of the large invader it hit, clears it and increments the score + } else if (pixel_check >= first_large_invader_pixel && pixel_check < first_medium_invader_pixel) { ///Collides with a large invader + ///Find the object no of the large invader it hit, clears it and increments the score object_no = CannonMissileHitInvader(first_large_invader_pixel, row, large_invader); ClearSingleLargeInvader(object_no); score += 10; break; - } else if (pixel_check >= first_barrier_pixel && pixel_check < (first_barrier_pixel + 3)) { //Collides with a barrier - //Adds the destruction done to the barrier to the bitmap and redraws the barriers + } else if (pixel_check >= first_barrier_pixel && pixel_check < (first_barrier_pixel + 3)) { ///Collides with a barrier + ///Adds the destruction done to the barrier to the bitmap and redraws the barriers CannonMissileHitBarrier(row); DrawBarriers(); break; - } else if (pixel_check == ufo_pixel) { //Collides with a UFO - pc.printf("UFO Hit\n"); + } else if (pixel_check == ufo_pixel) { ///Collides with a UFO + ///Clears the UFO, calculates score depending on the ufo bonus + ClearUFO(); + ufo_on_screen = false; + move_ufo.detach(); + if (ufo_bonus && shot_count == 15) { ///If the shot bonus is active and on the 15th shot, give 300 points + score += 300; + } else if (shot_count == 23) { ///If the shot bonus isn't active and on the 23rd shot, give 300 points and activate the bonus + ufo_bonus = true; + score += 300; + } else { ///Randomly assign score between 50, 100, 150 points + score += 50 * ((rand() % 3) + 1); + } cannon_missile_on_screen = false; move_cannon_missile.detach(); break; @@ -845,7 +1006,7 @@ } } -//Finds the invader number the missile hits, sets the hit invader to dying, decrements the no_of_alive_invaders and stops the shot from travalling up the screen +///Finds the invader number the missile 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; @@ -857,18 +1018,18 @@ return invader_no; } -//Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation +///Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation void CannonMissileHitBarrier(int row) { int barrier_no = screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] - first_barrier_pixel; - //Essentially inverse of barrier init + ///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 + 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 - //Element by element multiplication of the 2 bitmaps to clear the required pixals + ///Makes sure bitmap index does not go out of bounds. If it does go to the next iteration + ///Element by element multiplication of the 2 bitmaps to clear the required pixals 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]; } @@ -880,16 +1041,15 @@ void AttemptToFireInvaderNormalMissiles() { - //Fires the normal missiles - //Loops through the 2 allowed missiles and if they're not on the screen, randomly fire + ///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 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 + int loop_limit = 0; ///Stops loop from never ending Invader missile_source; do { fired_column = rand() % 5; @@ -897,8 +1057,8 @@ ++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 + ///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) { @@ -915,19 +1075,19 @@ 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 + ///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 + ///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 MoveInvaderNormalMissile(int missile_no) { - //Loops through the bitmap and clears the missile from the screen buffer + ///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]) { @@ -936,25 +1096,25 @@ } } - //Checks missile will not exceed screen buffer + ///Checks missile will not exceed screen buffer if (invader_normal_missile[missile_no].y_pos < 44) { - //Increments the position of the missile + ///Increments the position of the missile ++invader_normal_missile[missile_no].y_pos; - //Collision detection + ///Collision detection CollisionDetectionInvaderNormalMissile(missile_no); } else { - //Sets the missiles fired flag as false and detaches the tickers + ///Sets the missiles fired flag as false and detaches the tickers invader_normal_missile[missile_no].fired = false; move_invader_normal_missile[missile_no].detach(); } } -//Checks the bottom centre point for a collision. If it doesn't push the bitmap to the screen buffer +///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 + ///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) { @@ -962,7 +1122,7 @@ 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 + ///Finds barrier number InvaderNormalMissileHitBarrier(invader_normal_missile[missile_no]); invader_normal_missile[missile_no].fired = false; move_invader_normal_missile[missile_no].detach(); @@ -979,12 +1139,12 @@ void InvaderNormalMissileHitCannon() { - //Decrements the number of lives, pauses the game for 2 seconds - //Marks the cannon as hit + ///Decrements the number of lives, pauses the game for 2 seconds + ///Marks the cannon as hit g_cannon_hit_flag = true; - //Detaches all tickers + ///Detaches all tickers DetachTickers(); - //Creates a Timeout object on the stack with a period of 2 seconds to pause the game for 2 seconds + ///Creates a Timeout object on the stack with a period of 2 seconds to pause the game for 2 seconds Timeout cannon_hit; cannon_hit.attach(&cannon_hit_isr, 2); while (g_cannon_hit_flag) { @@ -994,17 +1154,17 @@ --number_of_lives; } -//Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation +///Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation void InvaderNormalMissileHitBarrier(const struct InvaderNormalMissiles (&missile)) { int barrier_no = screen_buffer[missile.x_pos + 1][missile.y_pos + 3] - first_barrier_pixel; - //Essentially inverse of barrier init + ///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 + ///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 + ///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]; } @@ -1012,12 +1172,13 @@ } } -//Detaches game related tickers +///Detaches game related tickers void DetachTickers() { update_screen.detach(); move_enemies.detach(); - //Only detaches if missiles are on the screen + + ///Only detaches if missiles are on the screen if (cannon_missile_on_screen) { move_cannon_missile.detach(); } @@ -1028,12 +1189,12 @@ } } -//Attaches game related tickers +///Attaches game related tickers void AttachTickers() { update_screen.attach(&update_screen_isr, 0.05); move_enemies.attach(&move_enemies_isr, ticker_period); - //Only attaches if missiles were on the screen + ///Only attaches if missiles were on the screen if (cannon_missile_on_screen) { move_cannon_missile.attach(&move_cannon_missile_isr, 0.05); } @@ -1046,28 +1207,28 @@ void PauseScreen() { - //Prints the pause screen, score etc + ///Prints the pause screen, score etc PrintPauseScreen(); while (game_state == paused) { - //Draws the cursor - cursor_y_pos = ((fsm_state+3)*8)+3; //Adds 3 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3 - lcd.drawRect(74, cursor_y_pos, 2, 2, 1); //Draws the cursor + ///Draws the cursor + cursor_y_pos = ((fsm_state+3)*8)+3; ///Adds 3 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3 + lcd.drawRect(74, cursor_y_pos, 2, 2, 1); ///Draws the cursor if (g_move_joystick_flag) { g_move_joystick_flag = false; - //Moves the cursor to match the selected option, only if the joystick isn't centred + ///Moves the cursor to match the selected option, only if the joystick isn't centred MoveCursor(fsm_paused); } - //If the button is pressed the selected option will appear on the screen + ///If the button is pressed the selected option will appear on the screen if (g_shoot_pressed_flag) { g_shoot_pressed_flag = false; game_state = fsm_paused[fsm_state].output; } - //Resets the joystick flag to false if pressed + ///Resets the joystick flag to false if pressed if (joystick.get_button_flag()) { joystick.set_button_flag(0); } @@ -1080,17 +1241,17 @@ void PrintPauseScreen() { - //Prints paused and a line underneath + ///Prints paused and a line underneath lcd.printString("Paused", 24, 0); - //Displays the current score + ///Displays the current score char buffer[14]; sprintf(buffer, "Score: %d", score); lcd.printString(buffer, 0, 1); - //Displays the no of lives + ///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 + ///Prints options on pause menu lcd.printString("Resume", 5, 3); lcd.printString("Save", 5, 4); lcd.printString("Quit", 5, 5); @@ -1098,16 +1259,124 @@ void MoveCursor(const struct FSMMenus *fsm) { - //If the joystick is is pushed down half way clear the cursor and set the next state + ///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 + ///Clears the cursor lcd.drawRect(74, cursor_y_pos, 2, 2, 2); - //Sets the new state + ///Sets the new state fsm_state = fsm[fsm_state].next_state[0]; } else if (joystick.GetYValue() > 0.75f) { - //Clears the cursor + ///Clears the cursor lcd.drawRect(74, cursor_y_pos, 2, 2, 2); - //Sets the new state + ///Sets the new state fsm_state = fsm[fsm_state].next_state[1]; } +} + +void MenuScreen() +{ + ///Print the menu text + PrintMenuScreen(); + + while (game_state == menu) { + ///Draws the cursor + cursor_y_pos = ((fsm_state+2)*8)+3; ///Adds 2 to the fsm state to get the bank, multiplies it by 8 to get the pixel no and offsets it by 3 + lcd.drawRect(74, cursor_y_pos, 2, 2, 1); + + if (g_move_joystick_flag) { + g_move_joystick_flag = false; + + ///Moves the cursor to match the selected option, only if the joystick isn't centred + MoveCursor(fsm_main_menu); + } + + ///If shoot is pressed go to the selected screen + if (g_shoot_pressed_flag) { + g_shoot_pressed_flag = false; + + game_state = fsm_main_menu[fsm_state].output; + + ///If the game state is equal to the game initalise it + if (game_state == game) { + InitaliseGame(); + } + } + + ///Do nothing when the joystick is pressed + if(joystick.get_button_flag()) { + joystick.set_button_flag(0); + } + + lcd.refresh(); + sleep(); + } +} + +void PrintMenuScreen() +{ + ///Prints Space invaders, then menu below, then a line underneath + lcd.printString("Space Invaders", 0, 0); + lcd.printString("Menu", 28, 1); + lcd.drawLine(0, 15, 83, 15, 2); + + ///Prints menu options + lcd.printString("New Game", 5, 2); + lcd.printString("Load Game", 5, 3); + lcd.printString("High Scores", 5, 4); + lcd.printString("Settings", 5, 5); +} + +void AttemptToSpawnUFO() +{ + ///1/8 chance of UFO spawning if it hasn't already + if (!ufo_on_screen && (rand() % 8 == 1) && down_count > 1 && no_of_alive_invaders > 3) { + ///Marks the UFO as on the screen and sets its position and direction + ufo_on_screen = true; + ufo_direction = (rand() % 2); + if (ufo_direction == RIGHT) { + ufo_x_pos = -14; + } else { + ufo_x_pos = 83; + } + + ///Attachs the move UFO ticker + move_ufo.attach(&move_ufo_isr, 0.1); + } +} + +void ClearUFO() +{ + ///Clears the UFO + for (int col = 0; col < 14; ++col) { + for (int row = 0; row < 5; ++row) { + ///Only clear the pixel in the buffer if it is within bounds + if (ufo_x_pos + col >= 0 && ufo_x_pos + col < 84) { + if (ufo_bitmap[row][col]) { + screen_buffer[ufo_x_pos + col][ufo_y_pos + row] = empty_pixel; + } + } + } + } +} + +void DrawUFO() +{ + ///Checks if the ufo can be within the screen bounds + ///If it is, push the ufo to the screen buffer + if (ufo_x_pos > -14 && ufo_x_pos < 84) { + for (int col = 0; col < 14; ++col) { + for (int row = 0; row < 5; ++row) { + ///Only clear the pixel in the buffer if it is within bounds + if (ufo_x_pos + col >= 0 && ufo_x_pos + col < 84) { + if (ufo_bitmap[row][col]) { + screen_buffer[ufo_x_pos + col][ufo_y_pos + row] = ufo_pixel; + } + } + } + } + } else { + ///Otherwise mark it as not on the screen and detach the ticker + ufo_on_screen = false; + move_ufo.detach(); + } } \ No newline at end of file