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

Dependencies:   Joystick N5110 SDFileSystem mbed

Committer:
avi23
Date:
Tue May 03 20:16:06 2016 +0000
Revision:
8:b2faec20ed8f
Parent:
7:babc367a3333
Child:
9:cbb982b7e353
Save game kind of works. Load does not

Who changed what in which revision?

UserRevisionLine numberNew contents of line
avi23 8:b2faec20ed8f 1 /**
avi23 8:b2faec20ed8f 2 @file main.cpp
avi23 8:b2faec20ed8f 3 @brief Implementation File
avi23 8:b2faec20ed8f 4 */
avi23 8:b2faec20ed8f 5
avi23 1:b300d052d549 6 /*
avi23 1:b300d052d549 7 Space Invaders - Avinash Patel 200860407
avi23 1:b300d052d549 8
avi23 1:b300d052d549 9 Week 19 - Set up joystick class
avi23 1:b300d052d549 10 Week 20 - Changed to space invaders as constrained too much by screen resolution
avi23 1:b300d052d549 11 - Core cannon is drawn and can move, enemies are visible and they switch between states every second
avi23 2:d34c95990605 12 Week 21 - Begun to set up barriers
avi23 5:34855f712350 13 Easter - Barriers work, invader and player can shoot. Begun setting up menus
avi23 7:babc367a3333 14 Week 22 - Most menus work
avi23 7:babc367a3333 15 Week 23 - Menus work
avi23 1:b300d052d549 16 */
avi23 0:427469992efe 17 #include "mbed.h"
avi23 7:babc367a3333 18 #include "main.h"
avi23 0:427469992efe 19
avi23 0:427469992efe 20 int main()
avi23 0:427469992efe 21 {
avi23 8:b2faec20ed8f 22 ///Wait for 2 seconds to allow power to settle
avi23 5:34855f712350 23 wait(1);
avi23 8:b2faec20ed8f 24 ///Initalises the board and perhiperals
avi23 0:427469992efe 25 init_K64F();
avi23 0:427469992efe 26 init_serial();
avi23 2:d34c95990605 27 init_shoot();
avi23 5:34855f712350 28 init_rng();
avi23 5:34855f712350 29 joystick.init();
avi23 0:427469992efe 30 lcd.init();
avi23 0:427469992efe 31 lcd.clear();
avi23 0:427469992efe 32
avi23 8:b2faec20ed8f 33 ///Configures the function pointer for the invaders normal missile
avi23 5:34855f712350 34 move_invader_normal_missile_isr[0] = &move_invader_normal_missile_0_isr;
avi23 5:34855f712350 35 move_invader_normal_missile_isr[1] = &move_invader_normal_missile_1_isr;
avi23 0:427469992efe 36
avi23 8:b2faec20ed8f 37 ///Samples joystick every 0.05 second
avi23 5:34855f712350 38 move_joystick.attach(&move_joystick_isr, 0.05);
avi23 2:d34c95990605 39
avi23 2:d34c95990605 40 while (true) {
avi23 8:b2faec20ed8f 41 if (game_state == menu) { ///Menu screen
avi23 8:b2faec20ed8f 42 lcd.clear();
avi23 8:b2faec20ed8f 43 fsm_state = 0; ///Sets the fsm state to 0
avi23 8:b2faec20ed8f 44 MenuScreen();
avi23 8:b2faec20ed8f 45 } else if (game_state == paused) { ///Paused screen
avi23 8:b2faec20ed8f 46 ///Clears the screen
avi23 2:d34c95990605 47 lcd.clear();
avi23 8:b2faec20ed8f 48 fsm_state = 0; ///Ssets the fsm state to 0
avi23 8:b2faec20ed8f 49 PauseScreen();
avi23 8:b2faec20ed8f 50 } else if (game_state == game) { ///Game screen
avi23 8:b2faec20ed8f 51 AttachTickers();
avi23 8:b2faec20ed8f 52 Game();
avi23 8:b2faec20ed8f 53 } else if (game_state == save) {
avi23 8:b2faec20ed8f 54 ///Creates file pointer and attaches the file
avi23 8:b2faec20ed8f 55 FILE *fp;
avi23 8:b2faec20ed8f 56 fp = fopen("/sd/game_save.txt", "w");
avi23 6:89d4a7f7588b 57
avi23 8:b2faec20ed8f 58 if (fp == NULL) { ///If file can't be opened then error
avi23 8:b2faec20ed8f 59 lcd.clear();
avi23 8:b2faec20ed8f 60 lcd.printString("Error Opening", 1, 1);
avi23 8:b2faec20ed8f 61 wait(2.0);
avi23 8:b2faec20ed8f 62 } else {
avi23 8:b2faec20ed8f 63 ///Prints booleans to file
avi23 8:b2faec20ed8f 64 fprintf(fp, "%d\n", invaders_in_state2);
avi23 8:b2faec20ed8f 65 fprintf(fp, "%d,%d,%d,%d,%d,%d\n", invader_direction, cannon_missile_on_screen, ufo_on_screen, ufo_direction, ufo_bonus, is_muted);
avi23 8:b2faec20ed8f 66 ///Prints integers to file
avi23 8:b2faec20ed8f 67 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);
avi23 8:b2faec20ed8f 68 ///Prints small invader info to file
avi23 8:b2faec20ed8f 69 for (int small = 0; small < 5; ++small) {
avi23 8:b2faec20ed8f 70 fprintf(fp, "%d,%d,%d\n", small_invader[small].x_pos, small_invader[small].y_pos, small_invader[small].status);
avi23 8:b2faec20ed8f 71 }
avi23 8:b2faec20ed8f 72 ///Prints medium invader info to file
avi23 8:b2faec20ed8f 73 for (int medium = 0; medium < 5; ++medium) {
avi23 8:b2faec20ed8f 74 fprintf(fp, "%d,%d,%d\n", medium_invader[medium].x_pos, medium_invader[medium].y_pos, medium_invader[medium].status);
avi23 8:b2faec20ed8f 75 }
avi23 8:b2faec20ed8f 76 ///Prints large invader info to file
avi23 8:b2faec20ed8f 77 for (int large = 0; large < 5; ++large) {
avi23 8:b2faec20ed8f 78 fprintf(fp, "%d,%d,%d\n", large_invader[large].x_pos, large_invader[large].y_pos, large_invader[large].status);
avi23 6:89d4a7f7588b 79 }
avi23 8:b2faec20ed8f 80 ///Stores invader missile data
avi23 8:b2faec20ed8f 81 for (int missile = 0; missile < 2; ++missile) {
avi23 8:b2faec20ed8f 82 fprintf(fp, "%d,%d,%d\n", invader_normal_missile[missile].x_pos, invader_normal_missile[missile].y_pos, invader_normal_missile[missile].fired);
avi23 8:b2faec20ed8f 83 }
avi23 8:b2faec20ed8f 84 ///Stores barrier data
avi23 8:b2faec20ed8f 85 for (int barrier_no = 0; barrier_no < 3; ++barrier_no) {
avi23 8:b2faec20ed8f 86 fprintf(fp, "%d,%d\n", barrier[barrier_no].x_pos, barrier[barrier_no].y_pos);
avi23 8:b2faec20ed8f 87 ///Stores barrier before bitmaps
avi23 8:b2faec20ed8f 88 for (int row = 0; row < 8; ++row) {
avi23 8:b2faec20ed8f 89 for (int col = 0; col < 14; ++col) {
avi23 8:b2faec20ed8f 90 if (col < 13) {
avi23 8:b2faec20ed8f 91 fprintf(fp, "%d,", barrier[barrier_no].before_bitmap[row][col]);
avi23 8:b2faec20ed8f 92 } else {
avi23 8:b2faec20ed8f 93 fprintf(fp, "%d\n", barrier[barrier_no].before_bitmap[row][col]);
avi23 8:b2faec20ed8f 94 }
avi23 8:b2faec20ed8f 95 }
avi23 8:b2faec20ed8f 96 }
avi23 8:b2faec20ed8f 97 ///Stores barrier after bitmaps
avi23 8:b2faec20ed8f 98 for (int row = 0; row < 8; ++row) {
avi23 8:b2faec20ed8f 99 for (int col = 0; col < 14; ++col) {
avi23 8:b2faec20ed8f 100 if (col < 13) {
avi23 8:b2faec20ed8f 101 fprintf(fp, "%d,", barrier[barrier_no].after_bitmap[row][col]);
avi23 8:b2faec20ed8f 102 } else {
avi23 8:b2faec20ed8f 103 fprintf(fp, "%d\n", barrier[barrier_no].after_bitmap[row][col]);
avi23 8:b2faec20ed8f 104 }
avi23 8:b2faec20ed8f 105 }
avi23 6:89d4a7f7588b 106 }
avi23 6:89d4a7f7588b 107 }
avi23 5:34855f712350 108
avi23 8:b2faec20ed8f 109 fclose(fp);
avi23 8:b2faec20ed8f 110 game_state = paused;
avi23 1:b300d052d549 111 }
avi23 8:b2faec20ed8f 112 } else if (game_state == load) {
avi23 8:b2faec20ed8f 113 ///Creates file pointer and attaches the file
avi23 8:b2faec20ed8f 114 FILE *fp;
avi23 8:b2faec20ed8f 115 fp = fopen("/sd/game_save.txt", "r");
avi23 8:b2faec20ed8f 116 rewind(fp);
avi23 8:b2faec20ed8f 117
avi23 8:b2faec20ed8f 118 if (fp == NULL) { ///If file can't be opened then error
avi23 8:b2faec20ed8f 119 lcd.clear();
avi23 8:b2faec20ed8f 120 lcd.printString("Error Opening", 1, 1);
avi23 8:b2faec20ed8f 121 wait(2.0);
avi23 8:b2faec20ed8f 122
avi23 8:b2faec20ed8f 123 game_state = menu;
avi23 8:b2faec20ed8f 124 } else {
avi23 8:b2faec20ed8f 125 int test;
avi23 8:b2faec20ed8f 126 ///Prints booleans to file
avi23 8:b2faec20ed8f 127 fscanf(fp, "%d\n", &invaders_in_state2);
avi23 8:b2faec20ed8f 128 test = fscanf(fp, "%d,%d,%d,%d,%d,%d", &invader_direction, &cannon_missile_on_screen, &ufo_on_screen, &ufo_direction, &ufo_bonus, &is_muted);
avi23 8:b2faec20ed8f 129
avi23 8:b2faec20ed8f 130 pc.printf("Bool: %d\n", test);
avi23 8:b2faec20ed8f 131 ///Prints integers to file
avi23 8:b2faec20ed8f 132 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);
avi23 8:b2faec20ed8f 133 pc.printf("Int: %d\n", test);
avi23 8:b2faec20ed8f 134 ///Prints small invader info to file
avi23 8:b2faec20ed8f 135 for (int small = 0; small < 5; ++small) {
avi23 8:b2faec20ed8f 136 test = fscanf(fp, "%d,%d,%d", &(small_invader[small].x_pos), &(small_invader[small].y_pos), &(small_invader[small].status));
avi23 8:b2faec20ed8f 137 pc.printf("Small: %d\n", test);
avi23 8:b2faec20ed8f 138 }
avi23 8:b2faec20ed8f 139 ///Prints medium invader info to file
avi23 8:b2faec20ed8f 140 for (int medium = 0; medium < 5; ++medium) {
avi23 8:b2faec20ed8f 141 test = fscanf(fp, "%d,%d,%d", &(medium_invader[medium].x_pos), &(medium_invader[medium].y_pos), &(medium_invader[medium].status));
avi23 8:b2faec20ed8f 142 pc.printf("Medium: %d\n", test);
avi23 8:b2faec20ed8f 143 }
avi23 8:b2faec20ed8f 144 ///Prints large invader info to file
avi23 8:b2faec20ed8f 145 for (int large = 0; large < 5; ++large) {
avi23 8:b2faec20ed8f 146 test = fscanf(fp, "%d,%d,%d", &(large_invader[large].x_pos), &(large_invader[large].y_pos), &(large_invader[large].status));
avi23 8:b2faec20ed8f 147 pc.printf("Large: %d\n", test);
avi23 8:b2faec20ed8f 148 }
avi23 8:b2faec20ed8f 149 ///Stores invader missile data
avi23 8:b2faec20ed8f 150 for (int missile = 0; missile < 2; ++missile) {
avi23 8:b2faec20ed8f 151 test = fscanf(fp, "%d,%d,%d", &(invader_normal_missile[missile].x_pos), &(invader_normal_missile[missile].y_pos), &(invader_normal_missile[missile].fired));
avi23 8:b2faec20ed8f 152 pc.printf("Missile: %d\n", test);
avi23 8:b2faec20ed8f 153 }
avi23 8:b2faec20ed8f 154 ///Stores barrier data
avi23 8:b2faec20ed8f 155 for (int barrier_no = 0; barrier_no < 3; ++barrier_no) {
avi23 8:b2faec20ed8f 156 test = fscanf(fp, "%d,%d", &(barrier[barrier_no].x_pos), &(barrier[barrier_no].y_pos));
avi23 8:b2faec20ed8f 157 pc.printf("Barrier: %d\n", test);
avi23 8:b2faec20ed8f 158 ///Stores barrier before bitmaps
avi23 8:b2faec20ed8f 159 for (int row = 0; row < 8; ++row) {
avi23 8:b2faec20ed8f 160 for (int col = 0; col < 14; ++col) {
avi23 8:b2faec20ed8f 161 if (col < 13) {
avi23 8:b2faec20ed8f 162 test = fscanf(fp, "%d,", &(barrier[barrier_no].before_bitmap[row][col]));
avi23 8:b2faec20ed8f 163 pc.printf("Barrier Before: %d\n", test);
avi23 8:b2faec20ed8f 164 } else {
avi23 8:b2faec20ed8f 165 test = fscanf(fp, "%d", &(barrier[barrier_no].before_bitmap[row][col]));
avi23 8:b2faec20ed8f 166 pc.printf("Barrier Before: %d\n", test);
avi23 8:b2faec20ed8f 167 }
avi23 8:b2faec20ed8f 168 }
avi23 8:b2faec20ed8f 169 }
avi23 8:b2faec20ed8f 170 ///Stores barrier after bitmaps
avi23 8:b2faec20ed8f 171 for (int row = 0; row < 8; ++row) {
avi23 8:b2faec20ed8f 172 for (int col = 0; col < 14; ++col) {
avi23 8:b2faec20ed8f 173 if (col < 13) {
avi23 8:b2faec20ed8f 174 test = fscanf(fp, "%d,", &(barrier[barrier_no].after_bitmap[row][col]));
avi23 8:b2faec20ed8f 175 pc.printf("Barrier After: %d\n", test);
avi23 8:b2faec20ed8f 176 } else {
avi23 8:b2faec20ed8f 177 test = fscanf(fp, "%d", &(barrier[barrier_no].after_bitmap[row][col]));
avi23 8:b2faec20ed8f 178 pc.printf("Barrier After: %d\n", test);
avi23 8:b2faec20ed8f 179 }
avi23 8:b2faec20ed8f 180 }
avi23 8:b2faec20ed8f 181 }
avi23 8:b2faec20ed8f 182 }
avi23 8:b2faec20ed8f 183
avi23 8:b2faec20ed8f 184
avi23 8:b2faec20ed8f 185 fclose(fp);
avi23 8:b2faec20ed8f 186 game_state = game;
avi23 8:b2faec20ed8f 187
avi23 8:b2faec20ed8f 188 g_update_screen_flag = true;
avi23 8:b2faec20ed8f 189 g_move_joystick_flag = true;
avi23 8:b2faec20ed8f 190 g_move_enemies_flag = true;
avi23 8:b2faec20ed8f 191 g_fire_buzzer_flag = true;
avi23 8:b2faec20ed8f 192 }
avi23 0:427469992efe 193 }
avi23 2:d34c95990605 194
avi23 0:427469992efe 195 sleep();
avi23 0:427469992efe 196 }
avi23 0:427469992efe 197 }
avi23 0:427469992efe 198
avi23 0:427469992efe 199 void init_K64F()
avi23 0:427469992efe 200 {
avi23 8:b2faec20ed8f 201 /// on-board LEDs are active-low, so set pin high to turn them off.
avi23 0:427469992efe 202 r_led = 1;
avi23 0:427469992efe 203 g_led = 1;
avi23 0:427469992efe 204 b_led = 1;
avi23 0:427469992efe 205
avi23 8:b2faec20ed8f 206 /// since the on-board switches have external pull-ups, we should disable the internal pull-down
avi23 8:b2faec20ed8f 207 /// resistors that are enabled by default using InterruptIn
avi23 0:427469992efe 208 sw2.mode(PullNone);
avi23 0:427469992efe 209 sw3.mode(PullNone);
avi23 0:427469992efe 210 }
avi23 0:427469992efe 211
avi23 0:427469992efe 212 void error()
avi23 0:427469992efe 213 {
avi23 8:b2faec20ed8f 214 while(1) { /// if error, hang while flashing error message
avi23 0:427469992efe 215 r_led = 0;
avi23 0:427469992efe 216 wait(0.2);
avi23 0:427469992efe 217 r_led = 1;
avi23 0:427469992efe 218 wait(0.2);
avi23 0:427469992efe 219 }
avi23 0:427469992efe 220 }
avi23 0:427469992efe 221
avi23 0:427469992efe 222 void init_serial()
avi23 0:427469992efe 223 {
avi23 8:b2faec20ed8f 224 /// set to highest baud - ensure terminal software matches
avi23 0:427469992efe 225 pc.baud(115200);
avi23 0:427469992efe 226 }
avi23 0:427469992efe 227
avi23 8:b2faec20ed8f 228 ///Seeds the random number generator with noise from an analog in pin
avi23 5:34855f712350 229 void init_rng()
avi23 5:34855f712350 230 {
avi23 8:b2faec20ed8f 231 AnalogIn rng_seed(PTC10); ///Creates a AnalogIn on a unused pin
avi23 8:b2faec20ed8f 232 srand(floor(10000*rng_seed.read())); ///Sets the seed as 10000x the input of the analog in
avi23 5:34855f712350 233 }
avi23 5:34855f712350 234
avi23 2:d34c95990605 235 void init_shoot()
avi23 0:427469992efe 236 {
avi23 2:d34c95990605 237 shoot_button.mode(PullUp);
avi23 2:d34c95990605 238 shoot_button.fall(&shoot_pressed_isr);
avi23 2:d34c95990605 239 }
avi23 2:d34c95990605 240
avi23 2:d34c95990605 241 void update_screen_isr()
avi23 2:d34c95990605 242 {
avi23 2:d34c95990605 243 g_update_screen_flag = true;
avi23 0:427469992efe 244 }
avi23 0:427469992efe 245
avi23 5:34855f712350 246 void move_enemies_isr()
avi23 5:34855f712350 247 {
avi23 5:34855f712350 248 g_move_enemies_flag = true;
avi23 5:34855f712350 249 }
avi23 5:34855f712350 250
avi23 2:d34c95990605 251 void shoot_pressed_isr()
avi23 2:d34c95990605 252 {
avi23 8:b2faec20ed8f 253 ///Only sets the shoot pressed flag 0.1s after the last press
avi23 6:89d4a7f7588b 254 if (!g_shoot_button_debounce_flag) {
avi23 6:89d4a7f7588b 255 g_shoot_pressed_flag = true;
avi23 6:89d4a7f7588b 256 g_shoot_button_debounce_flag = true;
avi23 6:89d4a7f7588b 257
avi23 8:b2faec20ed8f 258 ///Attaches a timeout to clear the debounce flag 0.125 seconds after it was set
avi23 6:89d4a7f7588b 259 shoot_button_debounce.attach(&shoot_button_debounce_isr, 0.125);
avi23 6:89d4a7f7588b 260 }
avi23 5:34855f712350 261 }
avi23 5:34855f712350 262
avi23 5:34855f712350 263 void move_cannon_missile_isr()
avi23 5:34855f712350 264 {
avi23 5:34855f712350 265 g_move_cannon_missile_flag = true;
avi23 5:34855f712350 266 }
avi23 5:34855f712350 267
avi23 5:34855f712350 268 void move_joystick_isr()
avi23 5:34855f712350 269 {
avi23 8:b2faec20ed8f 270 ///Always set the move flag in a game
avi23 5:34855f712350 271 if (game_state == game) {
avi23 5:34855f712350 272 g_move_joystick_flag = true;
avi23 5:34855f712350 273 } else if (!g_joystick_cursor_regulator_flag) {
avi23 8:b2faec20ed8f 274 ///Only sets the flag if the regulator is not set
avi23 5:34855f712350 275 g_move_joystick_flag = true;
avi23 5:34855f712350 276 g_joystick_cursor_regulator_flag = true;
avi23 5:34855f712350 277
avi23 8:b2faec20ed8f 278 ///Attachs a timeout to clear the regulator in 0.1s to prevent the cursor from behaving erratically
avi23 6:89d4a7f7588b 279 joystick_cursor_regulator.attach(&joystick_cursor_regulator_isr, 0.1);
avi23 5:34855f712350 280 }
avi23 5:34855f712350 281 }
avi23 5:34855f712350 282
avi23 6:89d4a7f7588b 283 void joystick_cursor_regulator_isr()
avi23 6:89d4a7f7588b 284 {
avi23 5:34855f712350 285 g_joystick_cursor_regulator_flag = false;
avi23 5:34855f712350 286 }
avi23 5:34855f712350 287
avi23 6:89d4a7f7588b 288 void shoot_button_debounce_isr()
avi23 6:89d4a7f7588b 289 {
avi23 6:89d4a7f7588b 290 g_shoot_button_debounce_flag = false;
avi23 6:89d4a7f7588b 291 }
avi23 6:89d4a7f7588b 292
avi23 5:34855f712350 293 void move_invader_normal_missile_0_isr()
avi23 5:34855f712350 294 {
avi23 5:34855f712350 295 g_move_invader_normal_missile_flag[0] = true;
avi23 5:34855f712350 296 }
avi23 5:34855f712350 297
avi23 5:34855f712350 298 void move_invader_normal_missile_1_isr()
avi23 5:34855f712350 299 {
avi23 5:34855f712350 300 g_move_invader_normal_missile_flag[1] = true;
avi23 5:34855f712350 301 }
avi23 5:34855f712350 302
avi23 5:34855f712350 303 void cannon_hit_isr()
avi23 5:34855f712350 304 {
avi23 5:34855f712350 305 g_cannon_hit_flag = false;
avi23 2:d34c95990605 306 }
avi23 2:d34c95990605 307
avi23 8:b2faec20ed8f 308 void move_ufo_isr()
avi23 8:b2faec20ed8f 309 {
avi23 8:b2faec20ed8f 310 g_move_ufo_flag = true;
avi23 8:b2faec20ed8f 311 }
avi23 8:b2faec20ed8f 312
avi23 8:b2faec20ed8f 313 void fire_buzzer_isr()
avi23 8:b2faec20ed8f 314 {
avi23 8:b2faec20ed8f 315 g_fire_buzzer_flag = true;
avi23 8:b2faec20ed8f 316 }
avi23 8:b2faec20ed8f 317
avi23 5:34855f712350 318 void Game()
avi23 6:89d4a7f7588b 319 {
avi23 8:b2faec20ed8f 320 ///Stays within the loop while the selected state is game
avi23 5:34855f712350 321 while (game_state == game) {
avi23 8:b2faec20ed8f 322 pc.printf("Mute: %d\n", is_muted);
avi23 8:b2faec20ed8f 323 ///If the game is over detach all the tickers
avi23 5:34855f712350 324 if (number_of_lives == 0) {
avi23 5:34855f712350 325 DetachTickers();
avi23 5:34855f712350 326
avi23 5:34855f712350 327 lcd.clear();
avi23 5:34855f712350 328 lcd.printString("Game Over.", 1, 2);
avi23 7:babc367a3333 329 char buffer[14];
avi23 7:babc367a3333 330 sprintf(buffer, "Score: %d", score);
avi23 7:babc367a3333 331 lcd.printString(buffer, 1, 3);
avi23 8:b2faec20ed8f 332 } else if (no_of_alive_invaders == 0) { ///If the player wins a round
avi23 8:b2faec20ed8f 333 ///Resets the no of alive invaders
avi23 5:34855f712350 334 no_of_alive_invaders = 15;
avi23 8:b2faec20ed8f 335 down_count = 0;
avi23 8:b2faec20ed8f 336 ///Detaches the enemy ticker while reinitalising invaders
avi23 5:34855f712350 337 move_enemies.detach();
avi23 8:b2faec20ed8f 338 ///Reinitalises objects
avi23 5:34855f712350 339 InitSmallInvaders();
avi23 5:34855f712350 340 InitMediumInvaders();
avi23 5:34855f712350 341 InitLargeInvaders();
avi23 8:b2faec20ed8f 342 ///Reattaches enemy ticker
avi23 5:34855f712350 343 move_enemies.attach(&move_enemies_isr, 1);
avi23 5:34855f712350 344 } else {
avi23 8:b2faec20ed8f 345 ///Updates pixels on the screen
avi23 5:34855f712350 346 if (g_update_screen_flag) {
avi23 5:34855f712350 347 g_update_screen_flag = false;
avi23 5:34855f712350 348
avi23 5:34855f712350 349 UpdateScreen();
avi23 5:34855f712350 350 }
avi23 5:34855f712350 351
avi23 8:b2faec20ed8f 352 ///Controls cannon movement
avi23 5:34855f712350 353 if (g_move_joystick_flag) {
avi23 5:34855f712350 354 g_move_joystick_flag = false;
avi23 5:34855f712350 355
avi23 5:34855f712350 356 MoveCannon();
avi23 5:34855f712350 357 DrawBarriers();
avi23 5:34855f712350 358 }
avi23 5:34855f712350 359
avi23 8:b2faec20ed8f 360 ///Controls enemy movement
avi23 5:34855f712350 361 if (g_move_enemies_flag) {
avi23 5:34855f712350 362 g_move_enemies_flag = false;
avi23 5:34855f712350 363
avi23 8:b2faec20ed8f 364 ///Increses the speed the invaders move
avi23 5:34855f712350 365 move_enemies.detach();
avi23 5:34855f712350 366 ticker_period = 0.1+(no_of_alive_invaders*0.06);
avi23 5:34855f712350 367 move_enemies.attach(&move_enemies_isr, ticker_period);
avi23 5:34855f712350 368
avi23 8:b2faec20ed8f 369 //Attach the buzzer timeout if mute is unselected
avi23 8:b2faec20ed8f 370 if (!is_muted) {
avi23 8:b2faec20ed8f 371 fire_buzzer.attach(&fire_buzzer_isr, ticker_period/2.0f);
avi23 8:b2faec20ed8f 372 }
avi23 8:b2faec20ed8f 373
avi23 8:b2faec20ed8f 374 ///Clears the old bitmaps
avi23 5:34855f712350 375 ClearSmallInvaders();
avi23 5:34855f712350 376 ClearMediumInvaders();
avi23 5:34855f712350 377 ClearLargeInvaders();
avi23 5:34855f712350 378
avi23 5:34855f712350 379 MoveInvaderXPositions();
avi23 8:b2faec20ed8f 380 ///Switches the bitmap state
avi23 5:34855f712350 381 invaders_in_state2 = !invaders_in_state2;
avi23 5:34855f712350 382
avi23 8:b2faec20ed8f 383 ///Draws the invaders
avi23 5:34855f712350 384 DrawSmallInvaders();
avi23 5:34855f712350 385 DrawMediumInvaders();
avi23 5:34855f712350 386 DrawLargeInvaders();
avi23 5:34855f712350 387
avi23 8:b2faec20ed8f 388 ///Attempts to fire the invaders missiles
avi23 5:34855f712350 389 AttemptToFireInvaderNormalMissiles();
avi23 8:b2faec20ed8f 390
avi23 8:b2faec20ed8f 391 ///Attempts to spawn UFO
avi23 8:b2faec20ed8f 392 AttemptToSpawnUFO();
avi23 5:34855f712350 393 }
avi23 5:34855f712350 394
avi23 8:b2faec20ed8f 395 if (g_fire_buzzer_flag && !is_muted) {
avi23 8:b2faec20ed8f 396 g_fire_buzzer_flag = false;
avi23 8:b2faec20ed8f 397
avi23 8:b2faec20ed8f 398 if (play_sound) {
avi23 8:b2faec20ed8f 399 buzzer.period(1/fsm_buzzer[buzzer_state].frequency);
avi23 8:b2faec20ed8f 400 buzzer.write(0.5);
avi23 8:b2faec20ed8f 401 play_sound = false;
avi23 8:b2faec20ed8f 402 } else {
avi23 8:b2faec20ed8f 403 buzzer.write(0);
avi23 8:b2faec20ed8f 404 buzzer_state = fsm_buzzer[buzzer_state].next_state;
avi23 8:b2faec20ed8f 405 play_sound = true;
avi23 8:b2faec20ed8f 406 }
avi23 8:b2faec20ed8f 407 }
avi23 8:b2faec20ed8f 408
avi23 8:b2faec20ed8f 409 ///Spawns a player bullet if the shoot button is pressed and there isn't a bullet on the screen
avi23 5:34855f712350 410 if (g_shoot_pressed_flag) {
avi23 5:34855f712350 411 g_shoot_pressed_flag = false;
avi23 5:34855f712350 412
avi23 5:34855f712350 413 if (!cannon_missile_on_screen) {
avi23 5:34855f712350 414 FireCannonMissile();
avi23 8:b2faec20ed8f 415
avi23 8:b2faec20ed8f 416 ///Counts fired missiles to see if the ufo bonus can be applied
avi23 8:b2faec20ed8f 417 if (ufo_bonus && shot_count > 14) {
avi23 8:b2faec20ed8f 418 shot_count = 0;
avi23 8:b2faec20ed8f 419 } else if (shot_count > 22) {
avi23 8:b2faec20ed8f 420 shot_count = 0;
avi23 8:b2faec20ed8f 421 } else {
avi23 8:b2faec20ed8f 422 ++shot_count;
avi23 8:b2faec20ed8f 423 }
avi23 5:34855f712350 424 }
avi23 5:34855f712350 425 }
avi23 5:34855f712350 426
avi23 8:b2faec20ed8f 427 ///Move the cannon shot
avi23 5:34855f712350 428 if (g_move_cannon_missile_flag) {
avi23 5:34855f712350 429 g_move_cannon_missile_flag = false;
avi23 5:34855f712350 430
avi23 5:34855f712350 431 MoveCannonMissile();
avi23 5:34855f712350 432 }
avi23 5:34855f712350 433
avi23 8:b2faec20ed8f 434 ///Moves the invaders 1st normal missile
avi23 5:34855f712350 435 if (g_move_invader_normal_missile_flag[0]) {
avi23 5:34855f712350 436 g_move_invader_normal_missile_flag[0] = false;
avi23 5:34855f712350 437
avi23 5:34855f712350 438 MoveInvaderNormalMissile(0);
avi23 5:34855f712350 439 }
avi23 5:34855f712350 440
avi23 8:b2faec20ed8f 441 ///Moves the invaders 2nd normal missile
avi23 5:34855f712350 442 if (g_move_invader_normal_missile_flag[1]) {
avi23 5:34855f712350 443 g_move_invader_normal_missile_flag[1] = false;
avi23 5:34855f712350 444
avi23 5:34855f712350 445 MoveInvaderNormalMissile(1);
avi23 5:34855f712350 446 }
avi23 5:34855f712350 447
avi23 8:b2faec20ed8f 448 ///Moves the UFO
avi23 8:b2faec20ed8f 449 if (g_move_ufo_flag) {
avi23 8:b2faec20ed8f 450 g_move_ufo_flag = false;
avi23 8:b2faec20ed8f 451
avi23 8:b2faec20ed8f 452 ///Clears the UFO
avi23 8:b2faec20ed8f 453 ClearUFO();
avi23 8:b2faec20ed8f 454
avi23 8:b2faec20ed8f 455 ///Moves the UFO in the correct direction
avi23 8:b2faec20ed8f 456 if (ufo_direction == RIGHT) {
avi23 8:b2faec20ed8f 457 ufo_x_pos += 2;
avi23 8:b2faec20ed8f 458 } else {
avi23 8:b2faec20ed8f 459 ufo_x_pos -= 2;
avi23 8:b2faec20ed8f 460 }
avi23 8:b2faec20ed8f 461
avi23 8:b2faec20ed8f 462 ///Draws the UFO
avi23 8:b2faec20ed8f 463 DrawUFO();
avi23 8:b2faec20ed8f 464 }
avi23 8:b2faec20ed8f 465
avi23 5:34855f712350 466 if (joystick.get_button_flag()) {
avi23 5:34855f712350 467 joystick.set_button_flag(0);
avi23 5:34855f712350 468
avi23 8:b2faec20ed8f 469 ///Detach all game tickers
avi23 5:34855f712350 470 DetachTickers();
avi23 5:34855f712350 471 game_state = paused;
avi23 5:34855f712350 472 }
avi23 5:34855f712350 473 }
avi23 5:34855f712350 474
avi23 5:34855f712350 475 sleep();
avi23 5:34855f712350 476 }
avi23 2:d34c95990605 477 }
avi23 2:d34c95990605 478
avi23 7:babc367a3333 479 void InitaliseGame()
avi23 7:babc367a3333 480 {
avi23 8:b2faec20ed8f 481 ///Clears the screen buffer and runs init functions
avi23 7:babc367a3333 482 memset(screen_buffer, 0, sizeof(screen_buffer));
avi23 7:babc367a3333 483 no_of_alive_invaders = 15;
avi23 7:babc367a3333 484 score = 0;
avi23 7:babc367a3333 485 number_of_lives = 3;
avi23 8:b2faec20ed8f 486 down_count = 0;
avi23 8:b2faec20ed8f 487 cannon_xpos = 15;
avi23 8:b2faec20ed8f 488 ufo_bonus = false;
avi23 8:b2faec20ed8f 489 shot_count = 0;
avi23 7:babc367a3333 490 InitSmallInvaders();
avi23 7:babc367a3333 491 InitMediumInvaders();
avi23 7:babc367a3333 492 InitLargeInvaders();
avi23 7:babc367a3333 493 InitBarriers();
avi23 8:b2faec20ed8f 494 ///If the sound is set activate the buzzer flag
avi23 8:b2faec20ed8f 495 if (!is_muted) {
avi23 8:b2faec20ed8f 496 g_fire_buzzer_flag = true;
avi23 8:b2faec20ed8f 497 }
avi23 8:b2faec20ed8f 498 ///Sets the flags so enemies pop up straight away
avi23 7:babc367a3333 499 g_update_screen_flag = true;
avi23 7:babc367a3333 500 g_move_joystick_flag = true;
avi23 7:babc367a3333 501 g_move_enemies_flag = true;
avi23 8:b2faec20ed8f 502 g_fire_buzzer_flag = true;
avi23 8:b2faec20ed8f 503 ///Forces the missiles to have the fired flags to flase
avi23 7:babc367a3333 504 cannon_missile_on_screen = false;
avi23 7:babc367a3333 505 invader_normal_missile[0].fired = false;
avi23 7:babc367a3333 506 invader_normal_missile[1].fired = false;
avi23 7:babc367a3333 507 }
avi23 7:babc367a3333 508
avi23 5:34855f712350 509 void UpdateScreen()
avi23 2:d34c95990605 510 {
avi23 8:b2faec20ed8f 511 ///Loops through the screen buffer and sets pixels on the LCD
avi23 5:34855f712350 512 for (int col = 0; col < 84; ++col) {
avi23 5:34855f712350 513 for (int row = 0; row < 48; ++row) {
avi23 5:34855f712350 514 if (screen_buffer[col][row]) {
avi23 5:34855f712350 515 lcd.setPixel(col, row);
avi23 5:34855f712350 516 } else {
avi23 5:34855f712350 517 lcd.clearPixel(col, row);
avi23 5:34855f712350 518 }
avi23 5:34855f712350 519 }
avi23 5:34855f712350 520 }
avi23 5:34855f712350 521
avi23 5:34855f712350 522 lcd.refresh();
avi23 2:d34c95990605 523 }
avi23 2:d34c95990605 524
avi23 2:d34c95990605 525 void MoveCannon()
avi23 0:427469992efe 526 {
avi23 8:b2faec20ed8f 527 ///Clears the cannon
avi23 5:34855f712350 528 for (int col = 0; col < 9; ++col) {
avi23 5:34855f712350 529 for (int row = 0; row < 5; ++row) {
avi23 1:b300d052d549 530 if(cannon_bitmap[row][col]) {
avi23 5:34855f712350 531 screen_buffer[cannon_xpos+col][cannon_ypos+row] = empty_pixel;
avi23 0:427469992efe 532 }
avi23 0:427469992efe 533 }
avi23 0:427469992efe 534 }
avi23 0:427469992efe 535
avi23 8:b2faec20ed8f 536 ///Changes the position of the cannon when the joystick is moved, capping at 0 and 75 so it always fits on the screen
avi23 0:427469992efe 537 if (joystick.GetXValue() < 0.25f) {
avi23 1:b300d052d549 538 cannon_xpos--;
avi23 1:b300d052d549 539 if (cannon_xpos < 0) {
avi23 1:b300d052d549 540 cannon_xpos = 0;
avi23 0:427469992efe 541 }
avi23 0:427469992efe 542 } else if (joystick.GetXValue() > 0.75f) {
avi23 1:b300d052d549 543 cannon_xpos++;
avi23 1:b300d052d549 544 if (cannon_xpos > 75) {
avi23 1:b300d052d549 545 cannon_xpos = 75;
avi23 0:427469992efe 546 }
avi23 0:427469992efe 547 }
avi23 0:427469992efe 548
avi23 8:b2faec20ed8f 549 ///Redraws the cannon
avi23 5:34855f712350 550 for (int col = 0; col < 9; ++col) {
avi23 5:34855f712350 551 for (int row = 0; row < 5; ++row) {
avi23 1:b300d052d549 552 if(cannon_bitmap[row][col]) {
avi23 5:34855f712350 553 screen_buffer[cannon_xpos+col][cannon_ypos+row] = cannon_pixel;
avi23 0:427469992efe 554 }
avi23 0:427469992efe 555 }
avi23 0:427469992efe 556 }
avi23 0:427469992efe 557 }
avi23 0:427469992efe 558
avi23 8:b2faec20ed8f 559 ///Sets the position and status of the small invaders
avi23 0:427469992efe 560 void InitSmallInvaders()
avi23 0:427469992efe 561 {
avi23 5:34855f712350 562 for (int i = 0; i < 5; ++i) {
avi23 8:b2faec20ed8f 563 small_invader[i].x_pos = 2 + (i*13); /// Large invaders are 12 across so add 13 for a gap space
avi23 2:d34c95990605 564 small_invader[i].y_pos = 1;
avi23 5:34855f712350 565 small_invader[i].status = alive;
avi23 0:427469992efe 566 }
avi23 0:427469992efe 567 }
avi23 0:427469992efe 568
avi23 8:b2faec20ed8f 569 ///Cycles through all the small invaders. If they're not already dead clear them
avi23 2:d34c95990605 570 void ClearSmallInvaders()
avi23 2:d34c95990605 571 {
avi23 5:34855f712350 572 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 573 if (small_invader[i].status) {
avi23 5:34855f712350 574 ClearSingleSmallInvader(i);
avi23 5:34855f712350 575 }
avi23 5:34855f712350 576 }
avi23 5:34855f712350 577 }
avi23 5:34855f712350 578
avi23 8:b2faec20ed8f 579 ///Cycles through the the screen invader bitmap and sets the pixels in the buffer to 0
avi23 5:34855f712350 580 void ClearSingleSmallInvader(int invader_no)
avi23 5:34855f712350 581 {
avi23 5:34855f712350 582 for (int col = 0; col < 8; ++col) {
avi23 5:34855f712350 583 for (int row = 0; row < 6; ++row) {
avi23 5:34855f712350 584 if (invaders_in_state2) {
avi23 5:34855f712350 585 if (small_invader_bitmap_1[row][col]) {
avi23 5:34855f712350 586 screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = empty_pixel;
avi23 2:d34c95990605 587 }
avi23 5:34855f712350 588 } else {
avi23 5:34855f712350 589 if (small_invader_bitmap_2[row][col]) {
avi23 5:34855f712350 590 screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = empty_pixel;
avi23 2:d34c95990605 591 }
avi23 2:d34c95990605 592 }
avi23 2:d34c95990605 593 }
avi23 2:d34c95990605 594 }
avi23 5:34855f712350 595
avi23 5:34855f712350 596 small_invader[invader_no].status = (small_invader[invader_no].status == dying) ? dead : alive;
avi23 2:d34c95990605 597 }
avi23 2:d34c95990605 598
avi23 8:b2faec20ed8f 599 ///Cycles through all the small invaders. If they're alive set them in the screen buffer
avi23 0:427469992efe 600 void DrawSmallInvaders()
avi23 0:427469992efe 601 {
avi23 5:34855f712350 602 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 603 if (small_invader[i].status == alive) {
avi23 5:34855f712350 604 DrawSingleSmallInvader(i);
avi23 5:34855f712350 605 }
avi23 5:34855f712350 606 }
avi23 5:34855f712350 607 }
avi23 5:34855f712350 608
avi23 8:b2faec20ed8f 609 ///Cycles through the the screen invader bitmap and sets the pixels in the buffer
avi23 5:34855f712350 610 void DrawSingleSmallInvader(int invader_no)
avi23 5:34855f712350 611 {
avi23 5:34855f712350 612 for (int col = 0; col < 8; ++col) {
avi23 5:34855f712350 613 for (int row = 0; row < 6; ++row) {
avi23 2:d34c95990605 614 if (invaders_in_state2) {
avi23 5:34855f712350 615 if (small_invader_bitmap_1[row][col]) {
avi23 5:34855f712350 616 screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = first_small_invader_pixel + invader_no;
avi23 0:427469992efe 617 }
avi23 0:427469992efe 618 } else {
avi23 5:34855f712350 619 if (small_invader_bitmap_2[row][col]) {
avi23 5:34855f712350 620 screen_buffer[small_invader[invader_no].x_pos + col][small_invader[invader_no].y_pos + row] = first_small_invader_pixel + invader_no;
avi23 0:427469992efe 621 }
avi23 0:427469992efe 622 }
avi23 0:427469992efe 623 }
avi23 0:427469992efe 624 }
avi23 1:b300d052d549 625 }
avi23 1:b300d052d549 626
avi23 8:b2faec20ed8f 627 ///Sets the position and aliveness of the medium invaders
avi23 1:b300d052d549 628 void InitMediumInvaders()
avi23 1:b300d052d549 629 {
avi23 5:34855f712350 630 for (int i = 0; i < 5; ++i) {
avi23 8:b2faec20ed8f 631 medium_invader[i].x_pos = 1 + (i*13); /// Large invaders are 12 across so add 13 for a gap space
avi23 2:d34c95990605 632 medium_invader[i].y_pos = 8;
avi23 5:34855f712350 633 medium_invader[i].status = alive;
avi23 1:b300d052d549 634 }
avi23 1:b300d052d549 635 }
avi23 1:b300d052d549 636
avi23 8:b2faec20ed8f 637 ///Cycles through all the medium invaders. If they're not already dead clear them
avi23 2:d34c95990605 638 void ClearMediumInvaders()
avi23 2:d34c95990605 639 {
avi23 5:34855f712350 640 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 641 if (medium_invader[i].status) {
avi23 5:34855f712350 642 ClearSingleMediumInvader(i);
avi23 2:d34c95990605 643 }
avi23 2:d34c95990605 644 }
avi23 2:d34c95990605 645 }
avi23 2:d34c95990605 646
avi23 8:b2faec20ed8f 647 ///Cycles through the the screen invader bitmap and sets the pixels in the buffer to 0
avi23 5:34855f712350 648 void ClearSingleMediumInvader(int invader_no)
avi23 5:34855f712350 649 {
avi23 5:34855f712350 650 for (int col = 0; col < 10; ++col) {
avi23 5:34855f712350 651 for (int row = 0; row < 6; ++row) {
avi23 5:34855f712350 652 if (invaders_in_state2) {
avi23 5:34855f712350 653 if (medium_invader_bitmap_1[row][col]) {
avi23 5:34855f712350 654 screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = empty_pixel;
avi23 5:34855f712350 655 }
avi23 5:34855f712350 656 } else {
avi23 5:34855f712350 657 if (medium_invader_bitmap_2[row][col]) {
avi23 5:34855f712350 658 screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = empty_pixel;
avi23 5:34855f712350 659 }
avi23 5:34855f712350 660 }
avi23 5:34855f712350 661 }
avi23 5:34855f712350 662 }
avi23 5:34855f712350 663
avi23 5:34855f712350 664 medium_invader[invader_no].status = (medium_invader[invader_no].status == dying) ? dead : alive;
avi23 5:34855f712350 665 }
avi23 5:34855f712350 666
avi23 8:b2faec20ed8f 667 ///Cycles through all the medium invaders. If they're alive set them in the screen buffer
avi23 1:b300d052d549 668 void DrawMediumInvaders()
avi23 1:b300d052d549 669 {
avi23 5:34855f712350 670 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 671 if (medium_invader[i].status == alive) {
avi23 5:34855f712350 672 DrawSingleMediumInvader(i);
avi23 5:34855f712350 673 }
avi23 5:34855f712350 674 }
avi23 5:34855f712350 675 }
avi23 5:34855f712350 676
avi23 8:b2faec20ed8f 677 ///Cycles through the the screen invader bitmap and sets the pixels in the buffer
avi23 5:34855f712350 678 void DrawSingleMediumInvader(int invader_no)
avi23 5:34855f712350 679 {
avi23 5:34855f712350 680 for (int col = 0; col < 10; ++col) {
avi23 5:34855f712350 681 for (int row = 0; row < 6; ++row) {
avi23 2:d34c95990605 682 if (invaders_in_state2) {
avi23 5:34855f712350 683 if (medium_invader_bitmap_1[row][col]) {
avi23 5:34855f712350 684 screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = first_medium_invader_pixel + invader_no;
avi23 1:b300d052d549 685 }
avi23 1:b300d052d549 686 } else {
avi23 5:34855f712350 687 if (medium_invader_bitmap_2[row][col]) {
avi23 5:34855f712350 688 screen_buffer[medium_invader[invader_no].x_pos + col][medium_invader[invader_no].y_pos + row] = first_medium_invader_pixel + invader_no;
avi23 1:b300d052d549 689 }
avi23 1:b300d052d549 690 }
avi23 1:b300d052d549 691 }
avi23 1:b300d052d549 692 }
avi23 1:b300d052d549 693 }
avi23 1:b300d052d549 694
avi23 8:b2faec20ed8f 695 ///Sets the position and status of the large invaders
avi23 1:b300d052d549 696 void InitLargeInvaders()
avi23 1:b300d052d549 697 {
avi23 5:34855f712350 698 for (int i = 0; i < 5; ++i) {
avi23 1:b300d052d549 699 large_invader[i].x_pos = 0 + (i*13);
avi23 2:d34c95990605 700 large_invader[i].y_pos = 15;
avi23 5:34855f712350 701 large_invader[i].status = alive;
avi23 5:34855f712350 702 }
avi23 5:34855f712350 703 }
avi23 5:34855f712350 704
avi23 8:b2faec20ed8f 705 ///Cycles through all the large invaders. If they're not already dead clear them
avi23 5:34855f712350 706 void ClearLargeInvaders()
avi23 5:34855f712350 707 {
avi23 5:34855f712350 708 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 709 if (large_invader[i].status) {
avi23 5:34855f712350 710 ClearSingleLargeInvader(i);
avi23 5:34855f712350 711 }
avi23 1:b300d052d549 712 }
avi23 1:b300d052d549 713 }
avi23 1:b300d052d549 714
avi23 8:b2faec20ed8f 715 ///Loops through the large invader bitmap, if the pixel in the bitmap is set to 1, set the pixel in the buffer to 0
avi23 5:34855f712350 716 void ClearSingleLargeInvader(int invader_no)
avi23 2:d34c95990605 717 {
avi23 5:34855f712350 718 for (int col = 0; col < 12; ++col) {
avi23 5:34855f712350 719 for (int row = 0; row < 6; ++row) {
avi23 5:34855f712350 720 if (invaders_in_state2) {
avi23 5:34855f712350 721 if (large_invader_bitmap_1[row][col]) {
avi23 5:34855f712350 722 screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = empty_pixel;
avi23 5:34855f712350 723 }
avi23 5:34855f712350 724 } else {
avi23 5:34855f712350 725 if (large_invader_bitmap_2[row][col]) {
avi23 5:34855f712350 726 screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = empty_pixel;
avi23 2:d34c95990605 727 }
avi23 2:d34c95990605 728 }
avi23 5:34855f712350 729 }
avi23 5:34855f712350 730 }
avi23 5:34855f712350 731
avi23 5:34855f712350 732 large_invader[invader_no].status = (large_invader[invader_no].status == dying) ? dead : alive;
avi23 5:34855f712350 733 }
avi23 5:34855f712350 734
avi23 8:b2faec20ed8f 735 ///Cycles through all the large invaders. If they're alive set them in the screen buffer
avi23 5:34855f712350 736 void DrawLargeInvaders()
avi23 5:34855f712350 737 {
avi23 5:34855f712350 738 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 739 if (large_invader[i].status == alive) {
avi23 5:34855f712350 740 DrawSingleLargeInvader(i);
avi23 5:34855f712350 741 }
avi23 5:34855f712350 742 }
avi23 5:34855f712350 743 }
avi23 5:34855f712350 744
avi23 8:b2faec20ed8f 745 ///Cycles through the the screen invader bitmap and sets the pixels in the buffer
avi23 5:34855f712350 746 void DrawSingleLargeInvader(int invader_no)
avi23 5:34855f712350 747 {
avi23 5:34855f712350 748 for (int col = 0; col < 12; ++col) {
avi23 5:34855f712350 749 for (int row = 0; row < 6; ++row) {
avi23 5:34855f712350 750 if (invaders_in_state2) {
avi23 5:34855f712350 751 if (large_invader_bitmap_1[row][col]) {
avi23 5:34855f712350 752 screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = first_large_invader_pixel + invader_no;
avi23 5:34855f712350 753 }
avi23 5:34855f712350 754 } else {
avi23 5:34855f712350 755 if (large_invader_bitmap_2[row][col]) {
avi23 5:34855f712350 756 screen_buffer[large_invader[invader_no].x_pos + col][large_invader[invader_no].y_pos + row] = first_large_invader_pixel + invader_no;
avi23 2:d34c95990605 757 }
avi23 2:d34c95990605 758 }
avi23 2:d34c95990605 759 }
avi23 2:d34c95990605 760 }
avi23 2:d34c95990605 761 }
avi23 2:d34c95990605 762
avi23 8:b2faec20ed8f 763 ///Sets the position and loads the bitmap into the barrier objects
avi23 2:d34c95990605 764 void InitBarriers()
avi23 2:d34c95990605 765 {
avi23 5:34855f712350 766 for (int i = 0; i < 3; ++i) {
avi23 2:d34c95990605 767 barrier[i].x_pos = 10 + (i*25);
avi23 2:d34c95990605 768 barrier[i].y_pos = 33;
avi23 8:b2faec20ed8f 769 ///Copies the bitmap into the structs
avi23 2:d34c95990605 770 memcpy(barrier[i].before_bitmap, barrier_bitmap, sizeof(barrier_bitmap));
avi23 2:d34c95990605 771 memcpy(barrier[i].after_bitmap, barrier_bitmap, sizeof(barrier_bitmap));
avi23 2:d34c95990605 772 }
avi23 2:d34c95990605 773 }
avi23 2:d34c95990605 774
avi23 2:d34c95990605 775 void DrawBarriers()
avi23 2:d34c95990605 776 {
avi23 8:b2faec20ed8f 777 ///Clears the barrier and redraws it with damage applied
avi23 5:34855f712350 778 for (int i = 0; i < 3; ++i) {
avi23 5:34855f712350 779 for (int col = 0; col < 14; ++col) {
avi23 5:34855f712350 780 for (int row = 0; row < 8; ++row) {
avi23 2:d34c95990605 781 if (barrier[i].before_bitmap[row][col]) {
avi23 5:34855f712350 782 screen_buffer[barrier[i].x_pos + col][barrier[i].y_pos + row] = empty_pixel;
avi23 2:d34c95990605 783 }
avi23 2:d34c95990605 784 if (barrier[i].after_bitmap[row][col]) {
avi23 5:34855f712350 785 screen_buffer[barrier[i].x_pos + col][barrier[i].y_pos + row] = first_barrier_pixel + i;
avi23 2:d34c95990605 786 }
avi23 2:d34c95990605 787 }
avi23 2:d34c95990605 788 }
avi23 8:b2faec20ed8f 789 ///Copies the after array to the before array
avi23 2:d34c95990605 790 memcpy(barrier[i].before_bitmap, barrier[i].after_bitmap, sizeof(barrier[i].after_bitmap));
avi23 2:d34c95990605 791 }
avi23 2:d34c95990605 792 }
avi23 2:d34c95990605 793
avi23 5:34855f712350 794 void MoveInvaderXPositions()
avi23 2:d34c95990605 795 {
avi23 8:b2faec20ed8f 796 ///Moves the invader in the current direction
avi23 2:d34c95990605 797 if (invader_direction == RIGHT) {
avi23 8:b2faec20ed8f 798 ///Checking the right limit
avi23 7:babc367a3333 799 int right_invader_limit = CalculateInvaderRightLimit();
avi23 5:34855f712350 800 MoveInvadersRight(right_invader_limit);
avi23 5:34855f712350 801 } else {
avi23 8:b2faec20ed8f 802 ///Checks the left limit
avi23 7:babc367a3333 803 int left_invader_limit = CalculateInvaderLeftLimit();
avi23 5:34855f712350 804 MoveInvadersLeft(left_invader_limit);
avi23 5:34855f712350 805 }
avi23 5:34855f712350 806 }
avi23 5:34855f712350 807
avi23 8:b2faec20ed8f 808 ///Checks the status off the invaders per column, starting from the left
avi23 8:b2faec20ed8f 809 ///If they're alive return the row number
avi23 5:34855f712350 810 int CalculateInvaderLeftLimit()
avi23 5:34855f712350 811 {
avi23 5:34855f712350 812 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 813 if (small_invader[i].status == alive || medium_invader[i].status == alive || large_invader[i].status == alive) {
avi23 5:34855f712350 814 return i;
avi23 5:34855f712350 815 }
avi23 5:34855f712350 816 }
avi23 5:34855f712350 817
avi23 8:b2faec20ed8f 818 ///Sort gameover out stuff after
avi23 5:34855f712350 819 return 4;
avi23 5:34855f712350 820 }
avi23 5:34855f712350 821
avi23 8:b2faec20ed8f 822 ///Checks the status off the invaders per column, starting from the right
avi23 8:b2faec20ed8f 823 ///If they're alive return the row number
avi23 5:34855f712350 824 int CalculateInvaderRightLimit()
avi23 5:34855f712350 825 {
avi23 5:34855f712350 826 for (int i = 4; i >= 0; --i) {
avi23 5:34855f712350 827 if (small_invader[i].status == alive || medium_invader[i].status == alive || large_invader[i].status == alive) {
avi23 5:34855f712350 828 return i;
avi23 5:34855f712350 829 }
avi23 5:34855f712350 830 }
avi23 5:34855f712350 831
avi23 8:b2faec20ed8f 832 ///Sort gameover stuff
avi23 5:34855f712350 833 return 0;
avi23 5:34855f712350 834 }
avi23 5:34855f712350 835
avi23 5:34855f712350 836 void MoveInvadersLeft(int limit)
avi23 5:34855f712350 837 {
avi23 8:b2faec20ed8f 838 ///Checks the first large invader to see if it can travel anymore
avi23 5:34855f712350 839 if (large_invader[limit].x_pos > 1) {
avi23 5:34855f712350 840 for (int i = 0; i < 5; ++i) {
avi23 8:b2faec20ed8f 841 ///Moves the invaders 2 to the left
avi23 5:34855f712350 842 small_invader[i].x_pos -= 2;
avi23 5:34855f712350 843 medium_invader[i].x_pos -= 2;
avi23 5:34855f712350 844 large_invader[i].x_pos -= 2;
avi23 2:d34c95990605 845 }
avi23 2:d34c95990605 846 } else {
avi23 8:b2faec20ed8f 847 ///Shifts the Invaders down and passes in the new direction
avi23 5:34855f712350 848 MoveInvaderYPositions(RIGHT);
avi23 2:d34c95990605 849 }
avi23 2:d34c95990605 850 }
avi23 2:d34c95990605 851
avi23 5:34855f712350 852 void MoveInvadersRight(int limit)
avi23 2:d34c95990605 853 {
avi23 8:b2faec20ed8f 854 ///Checks the first large invader to see if it can travel anymore
avi23 5:34855f712350 855 if (large_invader[limit].x_pos < 71) {
avi23 8:b2faec20ed8f 856 ///Moves the invaders 2 to the right
avi23 5:34855f712350 857 for (int i = 0; i < 5; ++i) {
avi23 5:34855f712350 858 small_invader[i].x_pos += 2;
avi23 5:34855f712350 859 medium_invader[i].x_pos += 2;
avi23 5:34855f712350 860 large_invader[i].x_pos += 2;
avi23 5:34855f712350 861 }
avi23 2:d34c95990605 862 } else {
avi23 8:b2faec20ed8f 863 ///Shifts the Invaders down and passes in the new direction
avi23 5:34855f712350 864 MoveInvaderYPositions(LEFT);
avi23 2:d34c95990605 865 }
avi23 5:34855f712350 866 }
avi23 5:34855f712350 867
avi23 5:34855f712350 868 void MoveInvaderYPositions(bool new_direction)
avi23 5:34855f712350 869 {
avi23 8:b2faec20ed8f 870 ///Finds the invaders lower limit
avi23 5:34855f712350 871 Invader lowest_invader = CalculateInvaderYLimit();
avi23 5:34855f712350 872
avi23 8:b2faec20ed8f 873 ///When moving down lowest_invader should not equal none
avi23 5:34855f712350 874 if (lowest_invader == none) {
avi23 5:34855f712350 875 error();
avi23 5:34855f712350 876 }
avi23 5:34855f712350 877
avi23 8:b2faec20ed8f 878 ///If an invader touches the bottom the game ends, otherwise the invaders descend
avi23 5:34855f712350 879 if (small_invader[0].y_pos < 33 - (7*lowest_invader)) {
avi23 5:34855f712350 880 for (int i = 0; i < 5; ++i) {
avi23 2:d34c95990605 881 small_invader[i].y_pos += 3;
avi23 2:d34c95990605 882 medium_invader[i].y_pos += 3;
avi23 2:d34c95990605 883 large_invader[i].y_pos += 3;
avi23 2:d34c95990605 884 }
avi23 8:b2faec20ed8f 885 ++down_count;
avi23 5:34855f712350 886 invader_direction = new_direction;
avi23 5:34855f712350 887 } else {
avi23 5:34855f712350 888 number_of_lives = 0;
avi23 5:34855f712350 889 }
avi23 5:34855f712350 890 }
avi23 5:34855f712350 891
avi23 5:34855f712350 892 Invader CalculateInvaderYLimit()
avi23 5:34855f712350 893 {
avi23 8:b2faec20ed8f 894 ///Checks to see which row of invaders are still alive to work out maximum y positions
avi23 5:34855f712350 895 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) {
avi23 5:34855f712350 896 return large;
avi23 5:34855f712350 897 } 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) {
avi23 5:34855f712350 898 return medium;
avi23 2:d34c95990605 899 } else {
avi23 5:34855f712350 900 return small;
avi23 2:d34c95990605 901 }
avi23 5:34855f712350 902 }
avi23 2:d34c95990605 903
avi23 8:b2faec20ed8f 904 ///Queries the invaders on their status and returns the type of invader which is alive
avi23 5:34855f712350 905 Invader LowestInvaderInColumn(int column)
avi23 5:34855f712350 906 {
avi23 5:34855f712350 907 if (large_invader[column].status == alive) {
avi23 5:34855f712350 908 return large;
avi23 5:34855f712350 909 } else if (medium_invader[column].status == alive) {
avi23 5:34855f712350 910 return medium;
avi23 5:34855f712350 911 } else if (small_invader[column].status == alive) {
avi23 5:34855f712350 912 return small;
avi23 5:34855f712350 913 } else {
avi23 5:34855f712350 914 return none;
avi23 5:34855f712350 915 }
avi23 2:d34c95990605 916 }
avi23 2:d34c95990605 917
avi23 5:34855f712350 918 void FireCannonMissile()
avi23 5:34855f712350 919 {
avi23 8:b2faec20ed8f 920 ///Sets the cannon fired flag to true
avi23 5:34855f712350 921 cannon_missile_on_screen = true;
avi23 5:34855f712350 922
avi23 8:b2faec20ed8f 923 ///Offset cannon missile x position by 4 of the cannons x pos
avi23 5:34855f712350 924 cannon_missile_x_pos = cannon_xpos + 4;
avi23 8:b2faec20ed8f 925 ///Will always have a starting y of 40
avi23 5:34855f712350 926 cannon_missile_y_pos = 40;
avi23 8:b2faec20ed8f 927 ///Attach the move cannon missile
avi23 5:34855f712350 928 move_cannon_missile.attach(&move_cannon_missile_isr, 0.05);
avi23 5:34855f712350 929 }
avi23 5:34855f712350 930
avi23 5:34855f712350 931 void MoveCannonMissile()
avi23 5:34855f712350 932 {
avi23 8:b2faec20ed8f 933 ///Checks bullet will not go beyond the bounds of the screen buffer
avi23 5:34855f712350 934 if (cannon_missile_y_pos > -1) {
avi23 8:b2faec20ed8f 935 ///Loops throught the shot bitmap and clears the pixels in the screen buffer
avi23 5:34855f712350 936 for (int row = 0; row < 4; ++row) {
avi23 8:b2faec20ed8f 937 ///Clears the position where the bullet was
avi23 5:34855f712350 938 screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] = empty_pixel;
avi23 5:34855f712350 939 }
avi23 5:34855f712350 940
avi23 8:b2faec20ed8f 941 ///Increments the shot going up the screen
avi23 5:34855f712350 942 --cannon_missile_y_pos;
avi23 5:34855f712350 943
avi23 8:b2faec20ed8f 944 ///Checks to see if the shot will hit anything
avi23 5:34855f712350 945 CollisionDetectionCannonMissile();
avi23 5:34855f712350 946 } else {
avi23 8:b2faec20ed8f 947 ///Loops throught the shot bitmap and clears the pixels in the screen buffer
avi23 5:34855f712350 948 for (int row = 1; row < 4; ++row) {
avi23 8:b2faec20ed8f 949 ///Clears the position where the bullet was
avi23 5:34855f712350 950 screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] = empty_pixel;
avi23 5:34855f712350 951 }
avi23 5:34855f712350 952
avi23 5:34855f712350 953 cannon_missile_on_screen = false;
avi23 5:34855f712350 954 move_cannon_missile.detach();
avi23 5:34855f712350 955 }
avi23 5:34855f712350 956 }
avi23 5:34855f712350 957
avi23 8:b2faec20ed8f 958 ///Checks to see what the shot hits. If it hits nothing the shot gets pushed to the screen buffer
avi23 5:34855f712350 959 void CollisionDetectionCannonMissile()
avi23 5:34855f712350 960 {
avi23 5:34855f712350 961 for (int row = 0; row < 4; ++row) {
avi23 5:34855f712350 962 int object_no;
avi23 7:babc367a3333 963 int pixel_check = screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row];
avi23 8:b2faec20ed8f 964 if (pixel_check >= first_small_invader_pixel && pixel_check < ufo_pixel) { ///Collides with a small invader
avi23 8:b2faec20ed8f 965 ///Find the object no of the small invader it hit, clears it and increments the score
avi23 5:34855f712350 966 object_no = CannonMissileHitInvader(first_small_invader_pixel, row, small_invader);
avi23 5:34855f712350 967 ClearSingleSmallInvader(object_no);
avi23 5:34855f712350 968 score += 40;
avi23 5:34855f712350 969 break;
avi23 8:b2faec20ed8f 970 } else if (pixel_check >= first_medium_invader_pixel && pixel_check < first_small_invader_pixel) { ///Collides with a medium invader
avi23 8:b2faec20ed8f 971 ///Find the object no of the medium invader it hit, clears it and increments the score
avi23 5:34855f712350 972 object_no = CannonMissileHitInvader(first_medium_invader_pixel, row, medium_invader);
avi23 5:34855f712350 973 ClearSingleMediumInvader(object_no);
avi23 5:34855f712350 974 score += 20;
avi23 5:34855f712350 975 break;
avi23 8:b2faec20ed8f 976 } else if (pixel_check >= first_large_invader_pixel && pixel_check < first_medium_invader_pixel) { ///Collides with a large invader
avi23 8:b2faec20ed8f 977 ///Find the object no of the large invader it hit, clears it and increments the score
avi23 5:34855f712350 978 object_no = CannonMissileHitInvader(first_large_invader_pixel, row, large_invader);
avi23 5:34855f712350 979 ClearSingleLargeInvader(object_no);
avi23 5:34855f712350 980 score += 10;
avi23 5:34855f712350 981 break;
avi23 8:b2faec20ed8f 982 } else if (pixel_check >= first_barrier_pixel && pixel_check < (first_barrier_pixel + 3)) { ///Collides with a barrier
avi23 8:b2faec20ed8f 983 ///Adds the destruction done to the barrier to the bitmap and redraws the barriers
avi23 7:babc367a3333 984 CannonMissileHitBarrier(row);
avi23 5:34855f712350 985 DrawBarriers();
avi23 5:34855f712350 986 break;
avi23 8:b2faec20ed8f 987 } else if (pixel_check == ufo_pixel) { ///Collides with a UFO
avi23 8:b2faec20ed8f 988 ///Clears the UFO, calculates score depending on the ufo bonus
avi23 8:b2faec20ed8f 989 ClearUFO();
avi23 8:b2faec20ed8f 990 ufo_on_screen = false;
avi23 8:b2faec20ed8f 991 move_ufo.detach();
avi23 8:b2faec20ed8f 992 if (ufo_bonus && shot_count == 15) { ///If the shot bonus is active and on the 15th shot, give 300 points
avi23 8:b2faec20ed8f 993 score += 300;
avi23 8:b2faec20ed8f 994 } else if (shot_count == 23) { ///If the shot bonus isn't active and on the 23rd shot, give 300 points and activate the bonus
avi23 8:b2faec20ed8f 995 ufo_bonus = true;
avi23 8:b2faec20ed8f 996 score += 300;
avi23 8:b2faec20ed8f 997 } else { ///Randomly assign score between 50, 100, 150 points
avi23 8:b2faec20ed8f 998 score += 50 * ((rand() % 3) + 1);
avi23 8:b2faec20ed8f 999 }
avi23 5:34855f712350 1000 cannon_missile_on_screen = false;
avi23 5:34855f712350 1001 move_cannon_missile.detach();
avi23 5:34855f712350 1002 break;
avi23 5:34855f712350 1003 } else {
avi23 5:34855f712350 1004 screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] = cannon_missile_pixel;
avi23 5:34855f712350 1005 }
avi23 5:34855f712350 1006 }
avi23 5:34855f712350 1007 }
avi23 5:34855f712350 1008
avi23 8:b2faec20ed8f 1009 ///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
avi23 5:34855f712350 1010 int CannonMissileHitInvader(int first_pixel, int row, struct Invaders (&invader)[5])
avi23 5:34855f712350 1011 {
avi23 5:34855f712350 1012 int invader_no = screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] - first_pixel;
avi23 5:34855f712350 1013 invader[invader_no].status = dying;
avi23 5:34855f712350 1014 cannon_missile_on_screen = false;
avi23 5:34855f712350 1015 --no_of_alive_invaders;
avi23 5:34855f712350 1016 move_cannon_missile.detach();
avi23 5:34855f712350 1017
avi23 5:34855f712350 1018 return invader_no;
avi23 5:34855f712350 1019 }
avi23 5:34855f712350 1020
avi23 8:b2faec20ed8f 1021 ///Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation
avi23 7:babc367a3333 1022 void CannonMissileHitBarrier(int row)
avi23 5:34855f712350 1023 {
avi23 7:babc367a3333 1024 int barrier_no = screen_buffer[cannon_missile_x_pos][cannon_missile_y_pos + row] - first_barrier_pixel;
avi23 8:b2faec20ed8f 1025 ///Essentially inverse of barrier init
avi23 5:34855f712350 1026 int relative_x_pos = cannon_missile_x_pos - 10 - (barrier_no*25) - 1;
avi23 8:b2faec20ed8f 1027 int relative_y_pos = (cannon_missile_y_pos + row) - 33 - 1; ///Don't know why it's -1 and not -2
avi23 8:b2faec20ed8f 1028 ///Loops through the damage bitmap and modifies the barrier's after bitmap
avi23 5:34855f712350 1029 for (int col = 0; col < 3; ++col) {
avi23 5:34855f712350 1030 for (int row_bit = 0; row_bit < 3; ++row_bit) {
avi23 8:b2faec20ed8f 1031 ///Makes sure bitmap index does not go out of bounds. If it does go to the next iteration
avi23 8:b2faec20ed8f 1032 ///Element by element multiplication of the 2 bitmaps to clear the required pixals
avi23 5:34855f712350 1033 if (relative_x_pos + col >= 0 && relative_x_pos + col < 14 && relative_y_pos + row_bit >= 0 && relative_y_pos + row_bit < 8) {
avi23 5:34855f712350 1034 barrier[barrier_no].after_bitmap[relative_y_pos + row_bit][relative_x_pos + col] *= barrier_cannon_missile_damage_bitmap[row_bit][col];
avi23 5:34855f712350 1035 }
avi23 5:34855f712350 1036 }
avi23 5:34855f712350 1037 }
avi23 5:34855f712350 1038 cannon_missile_on_screen = false;
avi23 5:34855f712350 1039 move_cannon_missile.detach();
avi23 5:34855f712350 1040 }
avi23 5:34855f712350 1041
avi23 5:34855f712350 1042 void AttemptToFireInvaderNormalMissiles()
avi23 5:34855f712350 1043 {
avi23 8:b2faec20ed8f 1044 ///Fires the normal missiles
avi23 8:b2faec20ed8f 1045 ///Loops through the 2 allowed missiles and if they're not on the screen, randomly fire
avi23 5:34855f712350 1046 for (int i = 0; i < 2; ++i) {
avi23 5:34855f712350 1047 if (!invader_normal_missile[i].fired) {
avi23 8:b2faec20ed8f 1048 ///If the random mumber is 1, fire a missile
avi23 8:b2faec20ed8f 1049 ///Higher chance when there are less invaders on the screen, up to a 1/3 chance
avi23 5:34855f712350 1050 if ((rand() % (no_of_alive_invaders + 2)) == 1) {
avi23 5:34855f712350 1051 int fired_column;
avi23 8:b2faec20ed8f 1052 int loop_limit = 0; ///Stops loop from never ending
avi23 5:34855f712350 1053 Invader missile_source;
avi23 5:34855f712350 1054 do {
avi23 5:34855f712350 1055 fired_column = rand() % 5;
avi23 5:34855f712350 1056 missile_source = LowestInvaderInColumn(fired_column);
avi23 5:34855f712350 1057 ++loop_limit;
avi23 5:34855f712350 1058 } while (missile_source == none && loop_limit < 10);
avi23 5:34855f712350 1059
avi23 8:b2faec20ed8f 1060 ///Finds the centre point of the chosen invader and fires the missile
avi23 8:b2faec20ed8f 1061 ///If the loop limit is reached, the missile source will be none and the for loop will jump to the next iteration
avi23 5:34855f712350 1062 if (missile_source == none) {
avi23 5:34855f712350 1063 continue;
avi23 5:34855f712350 1064 } else if (missile_source == large) {
avi23 5:34855f712350 1065 FireNormalInvaderMissile(i, missile_source, large_invader[fired_column]);
avi23 5:34855f712350 1066 } else if (missile_source == medium) {
avi23 5:34855f712350 1067 FireNormalInvaderMissile(i, missile_source, medium_invader[fired_column]);
avi23 5:34855f712350 1068 } else {
avi23 5:34855f712350 1069 FireNormalInvaderMissile(i, missile_source, small_invader[fired_column]);
avi23 5:34855f712350 1070 }
avi23 5:34855f712350 1071 }
avi23 5:34855f712350 1072 }
avi23 5:34855f712350 1073 }
avi23 5:34855f712350 1074 }
avi23 5:34855f712350 1075
avi23 5:34855f712350 1076 void FireNormalInvaderMissile(int missile_no, Invader source, const struct Invaders (&invader))
avi23 5:34855f712350 1077 {
avi23 8:b2faec20ed8f 1078 ///Finds the centre point of the chosen invader and fires the missile
avi23 8:b2faec20ed8f 1079 ///Enums are implicity converted to ints --> the middle x position of the invader found by offsetting the invader type by 3
avi23 5:34855f712350 1080 invader_normal_missile[missile_no].x_pos = invader.x_pos + (3 + source);
avi23 5:34855f712350 1081 invader_normal_missile[missile_no].y_pos = invader.y_pos + 6;
avi23 5:34855f712350 1082 invader_normal_missile[missile_no].fired = true;
avi23 5:34855f712350 1083
avi23 8:b2faec20ed8f 1084 ///Uses a function pointer to attach the ticker
avi23 5:34855f712350 1085 move_invader_normal_missile[missile_no].attach(move_invader_normal_missile_isr[missile_no], 0.05);
avi23 5:34855f712350 1086 }
avi23 5:34855f712350 1087
avi23 7:babc367a3333 1088 void MoveInvaderNormalMissile(int missile_no)
avi23 7:babc367a3333 1089 {
avi23 8:b2faec20ed8f 1090 ///Loops through the bitmap and clears the missile from the screen buffer
avi23 7:babc367a3333 1091 for (int col = 0; col < 3; ++col) {
avi23 7:babc367a3333 1092 for (int row = 0; row < 4; ++row) {
avi23 7:babc367a3333 1093 if (invader_normal_missile_bitmap[row][col]) {
avi23 7:babc367a3333 1094 screen_buffer[invader_normal_missile[missile_no].x_pos + col][invader_normal_missile[missile_no].y_pos + row] = empty_pixel;
avi23 7:babc367a3333 1095 }
avi23 7:babc367a3333 1096 }
avi23 7:babc367a3333 1097 }
avi23 7:babc367a3333 1098
avi23 8:b2faec20ed8f 1099 ///Checks missile will not exceed screen buffer
avi23 7:babc367a3333 1100 if (invader_normal_missile[missile_no].y_pos < 44) {
avi23 8:b2faec20ed8f 1101 ///Increments the position of the missile
avi23 7:babc367a3333 1102 ++invader_normal_missile[missile_no].y_pos;
avi23 7:babc367a3333 1103
avi23 8:b2faec20ed8f 1104 ///Collision detection
avi23 7:babc367a3333 1105 CollisionDetectionInvaderNormalMissile(missile_no);
avi23 7:babc367a3333 1106
avi23 7:babc367a3333 1107 } else {
avi23 8:b2faec20ed8f 1108 ///Sets the missiles fired flag as false and detaches the tickers
avi23 7:babc367a3333 1109 invader_normal_missile[missile_no].fired = false;
avi23 7:babc367a3333 1110 move_invader_normal_missile[missile_no].detach();
avi23 7:babc367a3333 1111 }
avi23 7:babc367a3333 1112 }
avi23 7:babc367a3333 1113
avi23 8:b2faec20ed8f 1114 ///Checks the bottom centre point for a collision. If it doesn't push the bitmap to the screen buffer
avi23 7:babc367a3333 1115 void CollisionDetectionInvaderNormalMissile(int missile_no)
avi23 7:babc367a3333 1116 {
avi23 8:b2faec20ed8f 1117 ///Invader missile coordinates shifted to match centre bottom of bitmap
avi23 7:babc367a3333 1118 int relative_x_pos = invader_normal_missile[missile_no].x_pos + 1;
avi23 7:babc367a3333 1119 int relative_y_pos = invader_normal_missile[missile_no].y_pos + 3;
avi23 7:babc367a3333 1120 if (screen_buffer[relative_x_pos][relative_y_pos] == cannon_pixel) {
avi23 7:babc367a3333 1121 InvaderNormalMissileHitCannon();
avi23 7:babc367a3333 1122 invader_normal_missile[missile_no].fired = false;
avi23 7:babc367a3333 1123 move_invader_normal_missile[missile_no].detach();
avi23 7:babc367a3333 1124 } 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)) {
avi23 8:b2faec20ed8f 1125 ///Finds barrier number
avi23 7:babc367a3333 1126 InvaderNormalMissileHitBarrier(invader_normal_missile[missile_no]);
avi23 7:babc367a3333 1127 invader_normal_missile[missile_no].fired = false;
avi23 7:babc367a3333 1128 move_invader_normal_missile[missile_no].detach();
avi23 7:babc367a3333 1129 } else {
avi23 7:babc367a3333 1130 for (int col = 0; col < 3; ++col) {
avi23 7:babc367a3333 1131 for (int row = 0; row < 4; ++row) {
avi23 7:babc367a3333 1132 if (invader_normal_missile_bitmap[row][col]) {
avi23 7:babc367a3333 1133 screen_buffer[invader_normal_missile[missile_no].x_pos + col][invader_normal_missile[missile_no].y_pos + row] = invader_normal_missile_pixel;
avi23 7:babc367a3333 1134 }
avi23 7:babc367a3333 1135 }
avi23 7:babc367a3333 1136 }
avi23 7:babc367a3333 1137 }
avi23 7:babc367a3333 1138 }
avi23 7:babc367a3333 1139
avi23 7:babc367a3333 1140 void InvaderNormalMissileHitCannon()
avi23 7:babc367a3333 1141 {
avi23 8:b2faec20ed8f 1142 ///Decrements the number of lives, pauses the game for 2 seconds
avi23 8:b2faec20ed8f 1143 ///Marks the cannon as hit
avi23 7:babc367a3333 1144 g_cannon_hit_flag = true;
avi23 8:b2faec20ed8f 1145 ///Detaches all tickers
avi23 7:babc367a3333 1146 DetachTickers();
avi23 8:b2faec20ed8f 1147 ///Creates a Timeout object on the stack with a period of 2 seconds to pause the game for 2 seconds
avi23 7:babc367a3333 1148 Timeout cannon_hit;
avi23 7:babc367a3333 1149 cannon_hit.attach(&cannon_hit_isr, 2);
avi23 7:babc367a3333 1150 while (g_cannon_hit_flag) {
avi23 7:babc367a3333 1151 sleep();
avi23 7:babc367a3333 1152 }
avi23 7:babc367a3333 1153 AttachTickers();
avi23 7:babc367a3333 1154 --number_of_lives;
avi23 7:babc367a3333 1155 }
avi23 7:babc367a3333 1156
avi23 8:b2faec20ed8f 1157 ///Calculates where to start drawing the damage bitmap over the barrier bitmap and performs the operation
avi23 7:babc367a3333 1158 void InvaderNormalMissileHitBarrier(const struct InvaderNormalMissiles (&missile))
avi23 7:babc367a3333 1159 {
avi23 7:babc367a3333 1160 int barrier_no = screen_buffer[missile.x_pos + 1][missile.y_pos + 3] - first_barrier_pixel;
avi23 8:b2faec20ed8f 1161 ///Essentially inverse of barrier init
avi23 7:babc367a3333 1162 int relative_x_pos = (missile.x_pos + 1) - 10 - (barrier_no*25) - 1;
avi23 7:babc367a3333 1163 int relative_y_pos = (missile.y_pos + 3) - 33;
avi23 8:b2faec20ed8f 1164 ///Loops through the damage bitmap and modifies the barrier's after bitmap
avi23 7:babc367a3333 1165 for (int col = 0; col < 3; ++col) {
avi23 7:babc367a3333 1166 for (int row_bit = 0; row_bit < 2; ++row_bit) {
avi23 8:b2faec20ed8f 1167 ///Makes sure bitmap index does not go out of bounds. If it does go to the next iteration
avi23 7:babc367a3333 1168 if (relative_x_pos + col >= 0 && relative_x_pos + col < 14 && relative_y_pos + row_bit >= 0 && relative_y_pos + row_bit < 8) {
avi23 7:babc367a3333 1169 barrier[barrier_no].after_bitmap[relative_y_pos + row_bit][relative_x_pos + col] *= barrier_invader_normal_missile_damage_bitmap[row_bit][col];
avi23 7:babc367a3333 1170 }
avi23 7:babc367a3333 1171 }
avi23 7:babc367a3333 1172 }
avi23 7:babc367a3333 1173 }
avi23 7:babc367a3333 1174
avi23 8:b2faec20ed8f 1175 ///Detaches game related tickers
avi23 5:34855f712350 1176 void DetachTickers()
avi23 5:34855f712350 1177 {
avi23 5:34855f712350 1178 update_screen.detach();
avi23 5:34855f712350 1179 move_enemies.detach();
avi23 8:b2faec20ed8f 1180
avi23 8:b2faec20ed8f 1181 ///Only detaches if missiles are on the screen
avi23 5:34855f712350 1182 if (cannon_missile_on_screen) {
avi23 5:34855f712350 1183 move_cannon_missile.detach();
avi23 5:34855f712350 1184 }
avi23 5:34855f712350 1185 for (int i = 0; i < 2; ++i) {
avi23 5:34855f712350 1186 if (invader_normal_missile[i].fired) {
avi23 5:34855f712350 1187 move_invader_normal_missile[i].detach();
avi23 5:34855f712350 1188 }
avi23 5:34855f712350 1189 }
avi23 5:34855f712350 1190 }
avi23 5:34855f712350 1191
avi23 8:b2faec20ed8f 1192 ///Attaches game related tickers
avi23 5:34855f712350 1193 void AttachTickers()
avi23 5:34855f712350 1194 {
avi23 5:34855f712350 1195 update_screen.attach(&update_screen_isr, 0.05);
avi23 5:34855f712350 1196 move_enemies.attach(&move_enemies_isr, ticker_period);
avi23 8:b2faec20ed8f 1197 ///Only attaches if missiles were on the screen
avi23 5:34855f712350 1198 if (cannon_missile_on_screen) {
avi23 5:34855f712350 1199 move_cannon_missile.attach(&move_cannon_missile_isr, 0.05);
avi23 5:34855f712350 1200 }
avi23 5:34855f712350 1201 for (int i = 0; i < 2; ++i) {
avi23 5:34855f712350 1202 if (invader_normal_missile[i].fired) {
avi23 5:34855f712350 1203 move_invader_normal_missile[i].attach(move_invader_normal_missile_isr[i], 0.05);
avi23 5:34855f712350 1204 }
avi23 5:34855f712350 1205 }
avi23 6:89d4a7f7588b 1206 }
avi23 6:89d4a7f7588b 1207
avi23 6:89d4a7f7588b 1208 void PauseScreen()
avi23 6:89d4a7f7588b 1209 {
avi23 8:b2faec20ed8f 1210 ///Prints the pause screen, score etc
avi23 7:babc367a3333 1211 PrintPauseScreen();
avi23 7:babc367a3333 1212
avi23 6:89d4a7f7588b 1213 while (game_state == paused) {
avi23 8:b2faec20ed8f 1214 ///Draws the cursor
avi23 8:b2faec20ed8f 1215 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
avi23 8:b2faec20ed8f 1216 lcd.drawRect(74, cursor_y_pos, 2, 2, 1); ///Draws the cursor
avi23 6:89d4a7f7588b 1217
avi23 6:89d4a7f7588b 1218 if (g_move_joystick_flag) {
avi23 6:89d4a7f7588b 1219 g_move_joystick_flag = false;
avi23 6:89d4a7f7588b 1220
avi23 8:b2faec20ed8f 1221 ///Moves the cursor to match the selected option, only if the joystick isn't centred
avi23 6:89d4a7f7588b 1222 MoveCursor(fsm_paused);
avi23 6:89d4a7f7588b 1223 }
avi23 6:89d4a7f7588b 1224
avi23 8:b2faec20ed8f 1225 ///If the button is pressed the selected option will appear on the screen
avi23 6:89d4a7f7588b 1226 if (g_shoot_pressed_flag) {
avi23 6:89d4a7f7588b 1227 g_shoot_pressed_flag = false;
avi23 6:89d4a7f7588b 1228 game_state = fsm_paused[fsm_state].output;
avi23 6:89d4a7f7588b 1229 }
avi23 6:89d4a7f7588b 1230
avi23 8:b2faec20ed8f 1231 ///Resets the joystick flag to false if pressed
avi23 6:89d4a7f7588b 1232 if (joystick.get_button_flag()) {
avi23 6:89d4a7f7588b 1233 joystick.set_button_flag(0);
avi23 6:89d4a7f7588b 1234 }
avi23 6:89d4a7f7588b 1235
avi23 6:89d4a7f7588b 1236 lcd.refresh();
avi23 6:89d4a7f7588b 1237
avi23 6:89d4a7f7588b 1238 sleep();
avi23 6:89d4a7f7588b 1239 }
avi23 6:89d4a7f7588b 1240 }
avi23 6:89d4a7f7588b 1241
avi23 6:89d4a7f7588b 1242 void PrintPauseScreen()
avi23 6:89d4a7f7588b 1243 {
avi23 8:b2faec20ed8f 1244 ///Prints paused and a line underneath
avi23 6:89d4a7f7588b 1245 lcd.printString("Paused", 24, 0);
avi23 8:b2faec20ed8f 1246 ///Displays the current score
avi23 6:89d4a7f7588b 1247 char buffer[14];
avi23 6:89d4a7f7588b 1248 sprintf(buffer, "Score: %d", score);
avi23 6:89d4a7f7588b 1249 lcd.printString(buffer, 0, 1);
avi23 8:b2faec20ed8f 1250 ///Displays the no of lives
avi23 6:89d4a7f7588b 1251 sprintf(buffer, "Lives: %d", number_of_lives);
avi23 6:89d4a7f7588b 1252 lcd.printString(buffer, 0, 2);
avi23 6:89d4a7f7588b 1253 lcd.drawLine(0, 23, 83, 23, 2);
avi23 8:b2faec20ed8f 1254 ///Prints options on pause menu
avi23 6:89d4a7f7588b 1255 lcd.printString("Resume", 5, 3);
avi23 6:89d4a7f7588b 1256 lcd.printString("Save", 5, 4);
avi23 6:89d4a7f7588b 1257 lcd.printString("Quit", 5, 5);
avi23 6:89d4a7f7588b 1258 }
avi23 6:89d4a7f7588b 1259
avi23 6:89d4a7f7588b 1260 void MoveCursor(const struct FSMMenus *fsm)
avi23 6:89d4a7f7588b 1261 {
avi23 8:b2faec20ed8f 1262 ///If the joystick is is pushed down half way clear the cursor and set the next state
avi23 6:89d4a7f7588b 1263 if (joystick.GetYValue() < 0.25f) {
avi23 8:b2faec20ed8f 1264 ///Clears the cursor
avi23 6:89d4a7f7588b 1265 lcd.drawRect(74, cursor_y_pos, 2, 2, 2);
avi23 8:b2faec20ed8f 1266 ///Sets the new state
avi23 6:89d4a7f7588b 1267 fsm_state = fsm[fsm_state].next_state[0];
avi23 6:89d4a7f7588b 1268 } else if (joystick.GetYValue() > 0.75f) {
avi23 8:b2faec20ed8f 1269 ///Clears the cursor
avi23 6:89d4a7f7588b 1270 lcd.drawRect(74, cursor_y_pos, 2, 2, 2);
avi23 8:b2faec20ed8f 1271 ///Sets the new state
avi23 6:89d4a7f7588b 1272 fsm_state = fsm[fsm_state].next_state[1];
avi23 6:89d4a7f7588b 1273 }
avi23 8:b2faec20ed8f 1274 }
avi23 8:b2faec20ed8f 1275
avi23 8:b2faec20ed8f 1276 void MenuScreen()
avi23 8:b2faec20ed8f 1277 {
avi23 8:b2faec20ed8f 1278 ///Print the menu text
avi23 8:b2faec20ed8f 1279 PrintMenuScreen();
avi23 8:b2faec20ed8f 1280
avi23 8:b2faec20ed8f 1281 while (game_state == menu) {
avi23 8:b2faec20ed8f 1282 ///Draws the cursor
avi23 8:b2faec20ed8f 1283 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
avi23 8:b2faec20ed8f 1284 lcd.drawRect(74, cursor_y_pos, 2, 2, 1);
avi23 8:b2faec20ed8f 1285
avi23 8:b2faec20ed8f 1286 if (g_move_joystick_flag) {
avi23 8:b2faec20ed8f 1287 g_move_joystick_flag = false;
avi23 8:b2faec20ed8f 1288
avi23 8:b2faec20ed8f 1289 ///Moves the cursor to match the selected option, only if the joystick isn't centred
avi23 8:b2faec20ed8f 1290 MoveCursor(fsm_main_menu);
avi23 8:b2faec20ed8f 1291 }
avi23 8:b2faec20ed8f 1292
avi23 8:b2faec20ed8f 1293 ///If shoot is pressed go to the selected screen
avi23 8:b2faec20ed8f 1294 if (g_shoot_pressed_flag) {
avi23 8:b2faec20ed8f 1295 g_shoot_pressed_flag = false;
avi23 8:b2faec20ed8f 1296
avi23 8:b2faec20ed8f 1297 game_state = fsm_main_menu[fsm_state].output;
avi23 8:b2faec20ed8f 1298
avi23 8:b2faec20ed8f 1299 ///If the game state is equal to the game initalise it
avi23 8:b2faec20ed8f 1300 if (game_state == game) {
avi23 8:b2faec20ed8f 1301 InitaliseGame();
avi23 8:b2faec20ed8f 1302 }
avi23 8:b2faec20ed8f 1303 }
avi23 8:b2faec20ed8f 1304
avi23 8:b2faec20ed8f 1305 ///Do nothing when the joystick is pressed
avi23 8:b2faec20ed8f 1306 if(joystick.get_button_flag()) {
avi23 8:b2faec20ed8f 1307 joystick.set_button_flag(0);
avi23 8:b2faec20ed8f 1308 }
avi23 8:b2faec20ed8f 1309
avi23 8:b2faec20ed8f 1310 lcd.refresh();
avi23 8:b2faec20ed8f 1311 sleep();
avi23 8:b2faec20ed8f 1312 }
avi23 8:b2faec20ed8f 1313 }
avi23 8:b2faec20ed8f 1314
avi23 8:b2faec20ed8f 1315 void PrintMenuScreen()
avi23 8:b2faec20ed8f 1316 {
avi23 8:b2faec20ed8f 1317 ///Prints Space invaders, then menu below, then a line underneath
avi23 8:b2faec20ed8f 1318 lcd.printString("Space Invaders", 0, 0);
avi23 8:b2faec20ed8f 1319 lcd.printString("Menu", 28, 1);
avi23 8:b2faec20ed8f 1320 lcd.drawLine(0, 15, 83, 15, 2);
avi23 8:b2faec20ed8f 1321
avi23 8:b2faec20ed8f 1322 ///Prints menu options
avi23 8:b2faec20ed8f 1323 lcd.printString("New Game", 5, 2);
avi23 8:b2faec20ed8f 1324 lcd.printString("Load Game", 5, 3);
avi23 8:b2faec20ed8f 1325 lcd.printString("High Scores", 5, 4);
avi23 8:b2faec20ed8f 1326 lcd.printString("Settings", 5, 5);
avi23 8:b2faec20ed8f 1327 }
avi23 8:b2faec20ed8f 1328
avi23 8:b2faec20ed8f 1329 void AttemptToSpawnUFO()
avi23 8:b2faec20ed8f 1330 {
avi23 8:b2faec20ed8f 1331 ///1/8 chance of UFO spawning if it hasn't already
avi23 8:b2faec20ed8f 1332 if (!ufo_on_screen && (rand() % 8 == 1) && down_count > 1 && no_of_alive_invaders > 3) {
avi23 8:b2faec20ed8f 1333 ///Marks the UFO as on the screen and sets its position and direction
avi23 8:b2faec20ed8f 1334 ufo_on_screen = true;
avi23 8:b2faec20ed8f 1335 ufo_direction = (rand() % 2);
avi23 8:b2faec20ed8f 1336 if (ufo_direction == RIGHT) {
avi23 8:b2faec20ed8f 1337 ufo_x_pos = -14;
avi23 8:b2faec20ed8f 1338 } else {
avi23 8:b2faec20ed8f 1339 ufo_x_pos = 83;
avi23 8:b2faec20ed8f 1340 }
avi23 8:b2faec20ed8f 1341
avi23 8:b2faec20ed8f 1342 ///Attachs the move UFO ticker
avi23 8:b2faec20ed8f 1343 move_ufo.attach(&move_ufo_isr, 0.1);
avi23 8:b2faec20ed8f 1344 }
avi23 8:b2faec20ed8f 1345 }
avi23 8:b2faec20ed8f 1346
avi23 8:b2faec20ed8f 1347 void ClearUFO()
avi23 8:b2faec20ed8f 1348 {
avi23 8:b2faec20ed8f 1349 ///Clears the UFO
avi23 8:b2faec20ed8f 1350 for (int col = 0; col < 14; ++col) {
avi23 8:b2faec20ed8f 1351 for (int row = 0; row < 5; ++row) {
avi23 8:b2faec20ed8f 1352 ///Only clear the pixel in the buffer if it is within bounds
avi23 8:b2faec20ed8f 1353 if (ufo_x_pos + col >= 0 && ufo_x_pos + col < 84) {
avi23 8:b2faec20ed8f 1354 if (ufo_bitmap[row][col]) {
avi23 8:b2faec20ed8f 1355 screen_buffer[ufo_x_pos + col][ufo_y_pos + row] = empty_pixel;
avi23 8:b2faec20ed8f 1356 }
avi23 8:b2faec20ed8f 1357 }
avi23 8:b2faec20ed8f 1358 }
avi23 8:b2faec20ed8f 1359 }
avi23 8:b2faec20ed8f 1360 }
avi23 8:b2faec20ed8f 1361
avi23 8:b2faec20ed8f 1362 void DrawUFO()
avi23 8:b2faec20ed8f 1363 {
avi23 8:b2faec20ed8f 1364 ///Checks if the ufo can be within the screen bounds
avi23 8:b2faec20ed8f 1365 ///If it is, push the ufo to the screen buffer
avi23 8:b2faec20ed8f 1366 if (ufo_x_pos > -14 && ufo_x_pos < 84) {
avi23 8:b2faec20ed8f 1367 for (int col = 0; col < 14; ++col) {
avi23 8:b2faec20ed8f 1368 for (int row = 0; row < 5; ++row) {
avi23 8:b2faec20ed8f 1369 ///Only clear the pixel in the buffer if it is within bounds
avi23 8:b2faec20ed8f 1370 if (ufo_x_pos + col >= 0 && ufo_x_pos + col < 84) {
avi23 8:b2faec20ed8f 1371 if (ufo_bitmap[row][col]) {
avi23 8:b2faec20ed8f 1372 screen_buffer[ufo_x_pos + col][ufo_y_pos + row] = ufo_pixel;
avi23 8:b2faec20ed8f 1373 }
avi23 8:b2faec20ed8f 1374 }
avi23 8:b2faec20ed8f 1375 }
avi23 8:b2faec20ed8f 1376 }
avi23 8:b2faec20ed8f 1377 } else {
avi23 8:b2faec20ed8f 1378 ///Otherwise mark it as not on the screen and detach the ticker
avi23 8:b2faec20ed8f 1379 ufo_on_screen = false;
avi23 8:b2faec20ed8f 1380 move_ufo.detach();
avi23 8:b2faec20ed8f 1381 }
avi23 0:427469992efe 1382 }