Updated Space Invaders on the mbed. Improved upon Michael Son's "Mbed Space Invaders" at https://os.mbed.com/users/michaeljson/notebook/mbed-space-invaders/.
Dependencies: mbed wave_player mbed-rtos 4DGL-uLCD-SE SparkfunAnalogJoystick SDFileSystem LSM9DS1_Library_cal_updated
Fork of Two-PlayerSpaceInvaders by
test
main.cpp
- Committer:
- wminix3
- Date:
- 2021-04-29
- Revision:
- 33:d17d71103d41
- Parent:
- 32:a8c6fbc57115
File content as of revision 33:d17d71103d41:
#include "mbed.h" #include "SDFileSystem.h" #include "wave_player.h" #include "enemy.h" #include "player.h" #include "barrier.h" #include "missile.h" #include "globals.h" #include "rtos.h" #include "SparkfunAnalogJoystick.h" #include "LSM9DS1.h" #include <string> // Navigation Switch replaced with Analog Joystick and IMU /* ==== Navigation Switch ===== */ /* class Nav_Switch { public: Nav_Switch(PinName up,PinName down,PinName left,PinName right,PinName fire); int read(); //boolean functions to test each switch bool up(); bool down(); bool left(); bool right(); bool fire(); //automatic read on RHS operator int (); //index to any switch array style bool operator[](int index) { return _pins[index]; }; private: BusIn _pins; }; Nav_Switch::Nav_Switch (PinName up,PinName down,PinName left,PinName right,PinName fire): _pins(up, down, left, right, fire) { _pins.mode(PullUp); //needed if pullups not on board or a bare nav switch is used - delete otherwise wait(0.001); //delays just a bit for pullups to pull inputs high } inline bool Nav_Switch::up() { return !(_pins[0]); } inline bool Nav_Switch::down() { return !(_pins[1]); } inline bool Nav_Switch::left() { return !(_pins[2]); } inline bool Nav_Switch::right() { return !(_pins[3]); } inline bool Nav_Switch::fire() { return !(_pins[4]); } inline int Nav_Switch::read() { return _pins.read(); } inline Nav_Switch::operator int () { return _pins.read(); } */ // Platform initialization uLCD_4DGL uLCD(p28,p27,p29); // LCD (serial tx, serial rx, reset pin;) AnalogOut DACout(p18); // speaker wave_player waver(&DACout); // wav player SDFileSystem sd(p5, p6, p7, p8, "sd"); // SD card and filesystem (mosi, miso, sck, cs) //Nav_Switch myNav(p16, p10, p11, p9, p12); //pin order on Sparkfun breakout. move U from p13 to p16 so we can use another Serial to communicate with mbed DigitalIn pb(p20); // push button for player misisle fire Serial pc(USBTX, USBRX); // communicate with pc (for debugging) //RawSerial secondMbed(p13, p14); // potentially communicate with a second mbed for 2 player. PwmOut red(p21); // added to dim and brighten red LED (in RGB LED) -- Brice PwmOut green(p22); // added to dim and brighten green LED (in RGB LED) -- Brice PwmOut blue(p23); // added to dim and brighten blue LED (in RGB LED) -- Brice SparkfunAnalogJoystick stick(p15, p16, p17); // Sparkfun analog joystick to replace the tactile switch (menu control and more fluid control of ship) LSM9DS1 IMU(p9, p10, 0xD6, 0x3C); // IMU added. Use the IMU accelerometer for a second optional tilt control for menu and ship. // Initialize all global enemy objects enemy_t enemy_1; enemy_t enemy_2; enemy_t enemy_3; enemy_t enemy_4; enemy_t enemy_5; enemy_t enemy_6; enemy_t enemy_7; enemy_t enemy_8; enemy_t enemy_9; enemy_t enemy_10; enemy_t enemy_11; enemy_t enemy_12; enemy_t enemy_13; enemy_t enemy_14; enemy_t enemy_15; // initialize all global barrier objects. barrier_t barrier_1; barrier_t barrier_2; barrier_t barrier_3; barrier_t barrier_4; // Initialize variables // Brice added "volatile" here to protect global variables. volatile int numOfEnemies = 0; // number of enemies volatile int ENEMY_MOVE = 1; // enemy moves volatile int MOVE_DOWN = 0; // enemies move down volatile int DIRECTION = 1; // direction of movement volatile int firing_col = 0; // col down which missiles are fired volatile int hit_player = 0; // determines if player was hit volatile bool lose = false; // determines if player loses the game volatile int lives1 = 3; // number of lives in level 1 volatile int lives2 = 2; // number of lives in level 2 volatile int lives3 = 1; // number of lives in level 3 volatile int level = 1; // the level, number of lives/difficulty volatile bool game_menu = false; // boolean to determine if in the game_menu state volatile bool begin_game = false; // boolean to determine if the game has begun volatile bool gameover = false; // boolean to determine if the game was lost (game_over) volatile int storedScore = 0; //volatile int numPlayers = 1; // ONLY NEEDED FOR 2 PLAYER //volatile bool first_player_ready = false; // 2 Player: Keep track of each player being ready //volatile bool second_player_ready = false; // 2 Player: Keep track of each player being ready //volatile bool begin_game2 = false; // ONLY NEEDED FOR 2 PLAYER. 2 PLAYER game started //volatile int numWins = 0; // MAY BE USED FOR 2 PLAYER. Keep track of the number of wins each player got. //volatile bool two_player_win = false; // global for a two_player_win (win on this mbed) // ONLY NEEDED FOR 2 PLAYER //volatile bool two_player_lose = false; // global for a two_player_lose (loss on this mbed) // ONLY NEEDED FOR 2 PLAYER volatile bool win = false; // add win to global variables so that we can have LED effects and sound effects for a win. Keeps track of the victory state. Timer bestTimer; // Timer started when the game begins and stopped when the game ends. Used to determine if there's a new best time. Mutex SDLock; // Used to put a mutex lock on the SD card for audio or for reading/writing the best time to the SD card. //Mutex mbedLock; // MIGHT BE NEEDED FOR 2 PLAYER // Initialize global player object player_t player; // Intialize global player and enemy missile missile_t missile; // player missile missile_t enemy_missile; // enemy missile //missile_t enemy_missile2; // first extra enemy missile for level 2 (medium difficulty) // Array of enemy objects enemy_t * enemyArray[15]; // Function Prototypes void move_enemy_down(); void playstart(void const *args); // PUT BACK IN // Draws the enemies at the initial starting location void draw_enemies_level() { // Initialize local variables unsigned int start_x_pos = 12; unsigned int start_enemy_y_pos = 20; numOfEnemies = 15; // First Row of Enemies // First Row of Enemies enemy_init(&enemy_1,start_x_pos,start_enemy_y_pos,RED); // initialize x-pos and y-pos and color of enemy_show(&enemy_1); // displays the enemy on uLCD enemy_init(&enemy_2,start_x_pos+15,start_enemy_y_pos,RED); enemy_show(&enemy_2); enemy_init(&enemy_3,start_x_pos+30,start_enemy_y_pos,RED); enemy_show(&enemy_3); enemy_init(&enemy_4,start_x_pos+45,start_enemy_y_pos,RED); enemy_show(&enemy_4); enemy_init(&enemy_5,start_x_pos+60,start_enemy_y_pos,RED); enemy_show(&enemy_5); // Second Row of Enemies enemy_init(&enemy_6,start_x_pos,start_enemy_y_pos+12,RED); enemy_show(&enemy_6); enemy_init(&enemy_7,start_x_pos+15,start_enemy_y_pos+12,RED); enemy_show(&enemy_7); enemy_init(&enemy_8,start_x_pos+30,start_enemy_y_pos+12,RED); enemy_show(&enemy_8); enemy_init(&enemy_9,start_x_pos+45,start_enemy_y_pos+12,RED); enemy_show(&enemy_9); enemy_init(&enemy_10,start_x_pos+60,start_enemy_y_pos+12,RED); enemy_show(&enemy_10); // Third Row of Enemies enemy_init(&enemy_11,start_x_pos,start_enemy_y_pos+24,RED); enemy_show(&enemy_11); enemy_init(&enemy_12,start_x_pos+15,start_enemy_y_pos+24,RED); enemy_show(&enemy_12); enemy_init(&enemy_13,start_x_pos+30,start_enemy_y_pos+24,RED); enemy_show(&enemy_13); enemy_init(&enemy_14,start_x_pos+45,start_enemy_y_pos+24,RED); enemy_show(&enemy_14); enemy_init(&enemy_15,start_x_pos+60,start_enemy_y_pos+24,RED); enemy_show(&enemy_15); enemy_init(&enemy_15,start_x_pos+60,start_enemy_y_pos+24,RED); enemy_show(&enemy_15); // Put enemy objects into array enemyArray[0] = &enemy_1; enemyArray[1] = &enemy_2; enemyArray[2] = &enemy_3; enemyArray[3] = &enemy_4; enemyArray[4] = &enemy_5; enemyArray[5] = &enemy_6; enemyArray[6] = &enemy_7; enemyArray[7] = &enemy_8; enemyArray[8] = &enemy_9; enemyArray[9] = &enemy_10; enemyArray[10] = &enemy_11; enemyArray[11] = &enemy_12; enemyArray[12] = &enemy_13; enemyArray[13] = &enemy_14; enemyArray[14] = &enemy_15; } // Draw and initialize the 4 barriers that the player can hide behind. void draw_barriers_level() { // Initialize local variables unsigned int start_x_pos = 5; // where the first barrier begins unsigned int start_barrier_y_pos = 95; // the y position of the barriers. barrier_init(&barrier_1,start_x_pos,start_barrier_y_pos,GREEN); // initialize x-pos and y-pos and color of barrier barrier_show(&barrier_1); // displays the barrier on uLCD barrier_init(&barrier_2,start_x_pos+32,start_barrier_y_pos,GREEN); barrier_show(&barrier_2); barrier_init(&barrier_3,start_x_pos+64,start_barrier_y_pos,GREEN); barrier_show(&barrier_3); barrier_init(&barrier_4,start_x_pos+96,start_barrier_y_pos,GREEN); barrier_show(&barrier_4); } // Draws the player at the initial starting location void draw_initial_player() { int start_x_pos = 59; // x pos of player (initial) int start_y_pos = 120; // y pos of player player_init(&player,start_x_pos,start_y_pos,WHITE); // intialize x-pos and y-pos and color of player player_show(&player); // display player on uLCD } // Checks all enemies in row 1 void check_hit_enemy_row1() { int hit_enemy; // First Row of Enemies hit_enemy = check_enemy(&enemy_1, &missile); // checks if the missile hits the enemy and returns 1 if hit, 0 if not hit numOfEnemies = numOfEnemies - hit_enemy; // updates the number of enemies left hit_enemy = check_enemy(&enemy_2, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_3, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_4, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_5, &missile); numOfEnemies = numOfEnemies - hit_enemy; } // Same as check_hit_enemy_row1, but checks enemies at row 2 void check_hit_enemy_row2() { int hit_enemy; // Second Row of Enemies hit_enemy = check_enemy(&enemy_6, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_7, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_8, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_9, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_10, &missile); numOfEnemies = numOfEnemies - hit_enemy; } // Same as check_hit_enemy_row1, but checks enemies at row 3 void check_hit_enemy_row3() { int hit_enemy; // Third Row of Enemies hit_enemy = check_enemy(&enemy_11, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_12, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_13, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_14, &missile); numOfEnemies = numOfEnemies - hit_enemy; hit_enemy = check_enemy(&enemy_15, &missile); numOfEnemies = numOfEnemies - hit_enemy; } // Checks if player is hit void check_player_hit() { hit_player = check_player(&player, &enemy_missile); // checks if the missile hits the player and returns 1 if hit, 0 if not hit } // Randomly selects an enemy to fire and updates the position of where the missile will fire from void random_attack_gen() { firing_col = rand() % 5; // selects a random column // first checks if the 3rd row closest to the player is alive if (enemyArray[firing_col+10]->status == ENEMY_ALIVE) { // If alive, the enemy missile position is updated to the center of the enemy enemy_missile.missile_blk_x = enemyArray[firing_col+10]->enemy_blk_x + (enemyArray[firing_col+10]->enemy_width/2); enemy_missile.missile_blk_y = enemyArray[firing_col+10]->enemy_blk_y + enemyArray[firing_col+10]->enemy_height + 1; enemy_missile.status = ENEMY_MISSILE_ACTIVE; // sets the enemy missile as active } // if enemy at 3rd row is dead, checks the enemy in the 2nd row else if (enemyArray[firing_col+5]->status == ENEMY_ALIVE) { // If alive, the enemy missile position is updated to the center of the enemy enemy_missile.missile_blk_x = enemyArray[firing_col+5]->enemy_blk_x + (enemyArray[firing_col+5]->enemy_width/2); enemy_missile.missile_blk_y = enemyArray[firing_col+5]->enemy_blk_y + enemyArray[firing_col+5]->enemy_height + 1; enemy_missile.status = ENEMY_MISSILE_ACTIVE; } // if enemy at 2nd and 3rd row is dead, checks the enemy in the 1st row else if (enemyArray[firing_col]->status == ENEMY_ALIVE) { // If alive, the enemy missile position is updated to the center of the enemy enemy_missile.missile_blk_x = enemyArray[firing_col]->enemy_blk_x + (enemyArray[firing_col]->enemy_width/2); enemy_missile.missile_blk_y = enemyArray[firing_col]->enemy_blk_y + enemyArray[firing_col]->enemy_height + 1; enemy_missile.status = ENEMY_MISSILE_ACTIVE; } } // moves the enemy void enemy_motion() { // will move the enemy every 6 loops if (ENEMY_MOVE % 6 == 0) { // FIrst Row of Enemies MOVE_DOWN = move_enemy(&enemy_1, MOVE_DOWN, DIRECTION); // moves the enemy based on the DIRECTION passed in and also sends global MOVE_DOWN to update in enemy.cpp MOVE_DOWN = move_enemy(&enemy_2, MOVE_DOWN, DIRECTION); // MOVE_DOWN will be 1 enemies reach the left or right edge of the screen MOVE_DOWN = move_enemy(&enemy_3, MOVE_DOWN, DIRECTION); // MOVE_DOWN will be 2 if enemies reach the player, otherwise 0 MOVE_DOWN = move_enemy(&enemy_4, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_5, MOVE_DOWN, DIRECTION); // Second Row of Enemies MOVE_DOWN = move_enemy(&enemy_6, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_7, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_8, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_9, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_10, MOVE_DOWN, DIRECTION); // Third Row of Enemies MOVE_DOWN = move_enemy(&enemy_11, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_12, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_13, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_14, MOVE_DOWN, DIRECTION); MOVE_DOWN = move_enemy(&enemy_15, MOVE_DOWN, DIRECTION); // if MOVE_DOWN is 2, then the enemies have reached the player if (MOVE_DOWN == 2) { lose = true; // set global lose to true to go to gameover screen } // if MOVE_DOWN is 1, update the y-pos of the enemies if (MOVE_DOWN == 1) { move_enemy_down(); // updates the y-pos of the enemies // Flips the direction when the enemy moves down if (DIRECTION == 1) { DIRECTION = 2; } else { DIRECTION = 1; } MOVE_DOWN = 0; // sets MOVE_DOWN back to 0 to stop down movement until } ENEMY_MOVE += 1; } else { ENEMY_MOVE += 1; } } // moves all the enemies down in the y-direction void move_enemy_down() { // First Row of Enemies enemy_erase(&enemy_1); enemy_erase(&enemy_2); enemy_erase(&enemy_3); enemy_erase(&enemy_4); enemy_erase(&enemy_5); enemy_1.enemy_blk_y += enemy_1.enemy_height+4; enemy_2.enemy_blk_y += enemy_2.enemy_height+4; enemy_3.enemy_blk_y += enemy_3.enemy_height+4; enemy_4.enemy_blk_y += enemy_4.enemy_height+4; enemy_5.enemy_blk_y += enemy_5.enemy_height+4; // Second Row of Enemies enemy_erase(&enemy_6); enemy_erase(&enemy_7); enemy_erase(&enemy_8); enemy_erase(&enemy_9); enemy_erase(&enemy_10); enemy_6.enemy_blk_y += enemy_6.enemy_height+4; enemy_7.enemy_blk_y += enemy_7.enemy_height+4; enemy_8.enemy_blk_y += enemy_8.enemy_height+4; enemy_9.enemy_blk_y += enemy_9.enemy_height+4; enemy_10.enemy_blk_y += enemy_10.enemy_height+4; // Third Row of Enemies enemy_erase(&enemy_11); enemy_erase(&enemy_12); enemy_erase(&enemy_13); enemy_erase(&enemy_14); enemy_erase(&enemy_15); enemy_11.enemy_blk_y += enemy_11.enemy_height+4; enemy_12.enemy_blk_y += enemy_12.enemy_height+4; enemy_13.enemy_blk_y += enemy_13.enemy_height+4; enemy_14.enemy_blk_y += enemy_14.enemy_height+4; enemy_15.enemy_blk_y += enemy_15.enemy_height+4; } // thread that plays sounds during game void playstart(void const *args)//Th { //Depending on the state of the game, //queue either screen music or play sound effect upon fire while(true) { FILE *wave_file; // plays intro music during menu screen: Edited from https://freesound.org/people/VABsounds/sounds/443865/ while(game_menu) { SDLock.lock(); // mutex lock the SD wave_file=fopen("/sd/wavfiles/futureEdit2.wav","r"); if(wave_file==NULL) pc.printf("file open error!\n\n\r"); // added this for open error check waver.play(wave_file); fclose(wave_file); SDLock.unlock(); Thread::wait(1000); } // Checks in game sound conditions while(begin_game) // // ADD || begin_game2 FOR 2 PLAYER { // play firing sound when the player fires: Edited from https://freesound.org/people/nsstudios/sounds/321102/ if(!pb && missile.status == PLAYER_MISSILE_INACTIVE) { SDLock.lock(); // mutex lock the SD wave_file=fopen("/sd/wavfiles/laserEdit.wav","r"); if(wave_file==NULL) pc.printf("laser file open error!\n\n\r"); // added this for open error check waver.play(wave_file); fclose(wave_file); SDLock.unlock(); Thread::wait(250); } // if player hit, play hit sound. Edited from https://freesound.org/people/tcpp/sounds/77339/ if (hit_player) { SDLock.lock(); // mutex lock the SD wave_file=fopen("/sd/wavfiles/bigExplosionEdit2.wav","r"); if(wave_file==NULL) pc.printf("explosion file open error!\n\n\r"); // added this for open error check waver.play(wave_file); fclose(wave_file); SDLock.unlock(); Thread::wait(500); } } // victory music: Edited from https://freesound.org/people/honeybone82/sounds/513253/ while(win) { SDLock.lock(); // mutex lock the SD wave_file=fopen("/sd/wavfiles/victoryEdit2.wav", "r"); if(wave_file==NULL) pc.printf("explosion file open error!\n\n\r"); // added this for open error check waver.play(wave_file); fclose(wave_file); SDLock.unlock(); Thread::wait(1000); } // gameover music: Edited from https://freesound.org/people/themusicalnomad/sounds/253886/ while(gameover) { SDLock.lock(); // mutex lock the SD wave_file=fopen("/sd/wavfiles/comicalgameoverEdit.wav","r"); if(wave_file==NULL) pc.printf("gameover file open error!\n\n\r"); // added this for open error check waver.play(wave_file); fclose(wave_file); SDLock.unlock(); Thread::wait(1000); } } } // thread that adds RGB LED Lighting Effects that coincide with the game -- Brice void ledEffects(void const *args)//Th { //Depending on the state of the game, //generate different patterns/colors of lighting while(1) { // gradually brightens and dims each color independently (maximum of 0.5). (A chill build up effect?) while(game_menu) { red = 0.0; green = 0.0; blue = 0.0; for (float i = 0.0; i < 0.5; i = i + 0.05) { red = i; Thread::wait(10); } red = 0.5; Thread::wait(300); for (float i = 0.5; i > 0.0; i = i - 0.05) { red = i; Thread::wait(10); } red = 0.0; for (float i = 0.0; i < 0.25; i = i + 0.05) { green = i; Thread::wait(10); } green = 0.25; Thread::wait(300); for (float i = 0.25; i > 0.0; i = i - 0.05) { green = i; Thread::wait(10); } green = 0.0; for (float i = 0.0; i < 0.5; i = i + 0.05) { blue = i; Thread::wait(10); } blue = 0.5; Thread::wait(300); for (float i = 0.5; i > 0.0; i = i - 0.05) { blue = i; Thread::wait(10); } blue = 0.0; } // Checks in game lighting conditions while(begin_game) // ADD || begin_game2 FOR 2 PLAYER { // play firing sound when the player fires. SEEMS TO BE HIT OR MISS RN if(!pb && missile.status == PLAYER_MISSILE_INACTIVE) { red = 0.0; green = 0.0; blue = 0.0; //red = 0.5; //Thread::wait(200); red = 0.5; green = 0.15; Thread::wait(300); red = 0.0; green = 0.0; } // if player hit, flash a red led. (0.5 maximum) if (hit_player) { red = 0.0; green = 0.0; blue = 0.0; red = 0.5; Thread::wait(60); red = 0.0; Thread::wait(60); red = 0.5; Thread::wait(60); red = 0.0; Thread::wait(60); red = 0.5; Thread::wait(60); red = 0.0; } Thread::wait(500); } // during a win, slowly brighten the green LED, hold it at 0.25 and then dim it. while(win) { for (float i = 0.0; i < 0.25; i = i + 0.05) { green = i; Thread::wait(10); } green = 0.25; Thread::wait(300); for (float i = 0.25; i > 0.0; i = i - 0.05) { green = i; Thread::wait(10); } green = 0.0; Thread::wait(500); } // during a gameover, slowly brighten the red LED, hold it at 0.25, and then dim it while(gameover) { for (float i = 0.0; i < 0.25; i = i + 0.05) { red = i; Thread::wait(10); } red = 0.25; Thread::wait(300); for (float i = 0.25; i > 0.0; i = i - 0.05) { red = i; Thread::wait(10); } red = 0.0; Thread::wait(500); } } } // SLAVE AND MASTER THREADS BELOW ARE USED ONLY WITH 2 PLAYER (STILL BUGGY AND REQUIRES EXTRA HARDWARE) // UNCOMMENT THIS THREAD IF SECOND PLAYER AND COMMENT MASTER THREAD // The slave mbed device (second player) should uncomment this thread -- Brice /* void mbedSlave(void const *args) { char rx; while(1) { //while(numPlayers == 1) Thread::yield(); rx = '0'; pc.printf("Slave thread running.\n\r"); if (secondMbed.readable()) { rx = secondMbed.getc(); pc.printf("rx = %c\n\r", rx); //if (!begin_game2 && rx == 'S') { while (!secondMbed.writeable()) wait(0.5); secondMbed.putc(rx); } Thread::wait(1000); } } */ //first_player_ready = true; //second_player_ready = true; // begin_game2 = true; // numPlayers = 2; //} else if (begin_game2 && rx == 'W') { // secondMbed.putc(rx); //first_player_ready = false; //second_player_ready = false; // begin_game2 = false; // two_player_lose = true; //} //}*/ /* if (begin_game2 && two_player_win) { while(!secondMbed.writeable()) wait(0.5); secondMbed.putc('W'); while(!secondMbed.readable()) wait(0.5); // ok to lock up with wait because we don't want to confirm when before anything else. --Brice rx = secondMbed.getc(); if (rx == 'W') { begin_game2 = false; //first_player_ready = false; //second_player_ready = false; } } */ // Thread::wait(1000); // } //} // UNCOMMENT THIS THREAD IF FIRST PLAYER AND COMMENT SLAVE THREAD // The master mbed device (second player) should uncomment this thread -- Brice /* void mbedMaster(void const *args) { char rx; while(1) { pc.printf("Master called\n\r"); while(numPlayers == 1) Thread::yield(); pc.printf("Num players is 2.\n\r"); rx = '0'; if (!begin_game2) { while(!secondMbed.writeable()) { // pc.printf("not writeable"); wait(0.5); } secondMbed.putc('S'); while(!secondMbed.readable()) { // pc.printf("no read\n\r"); wait(0.5); // okay to lock up until can confirm game is ready. --Brice } rx = secondMbed.getc(); pc.printf("rx = %c\n\r", rx); if (rx == 'S') { begin_game2 = true; pc.printf("both players ready\n\r"); } } else { while (begin_game2) { rx = '0'; if (secondMbed.readable()) { rx = secondMbed.getc(); if (rx == 'W') { secondMbed.putc(rx); begin_game2 = false; two_player_lose = true; } } if (two_player_win) { secondMbed.putc('W'); while(!secondMbed.readable()) wait(0.5); // ok to lock up with wait because we don't want to confirm when before anything else. --Brice rx = secondMbed.getc(); if (rx == 'W') { begin_game2 = false; } } Thread::wait(1000); } } Thread::wait(1000); } } */ int main() { IMU.begin(); // start the IMU if (!IMU.begin()) { // print error message if error with IMU pc.printf("Failed to communicate with IMU\n\r"); } IMU.calibrate(1); // calibrate the IMU // Initialization of Setup variables int blk_x, blk_y; pb.mode(PullUp); // set pushbutton to PullUp mode Thread thread(playstart); // intializes the thread to play sound // Should only have the Slave thread uncommented if second player. // Should only have the Master thread uncommented if first player. //Thread thread2(mbedSlave); // uncommented if second player -- Brice //Thread thread3(mbedMaster); // uncommented if first player -- Brice Thread thread4(ledEffects); // thread added for LED lighting effects -- Brice //secondMbed.baud(9600); // set the secondMbed serial baud rate to 9600. Two-player only. uLCD.baudrate(3000000); // set to 3000000 (the maximum baud rate for the LCD) to increase smooth gameplay // Initialization of Game Menu variables // Additional globals added for two-player and one-player capabilities (by Brice) const int title_x_pos = 2; // initial x-pos of title const int title_y_pos = 3; // initial y-pos of title int start_label_x_pos = 7; // start label x-pos int start_label_y_pos = 7; // start label y-pos int level_cursor_x_pos = 5; // level cursor x-position int level_cursor_y_pos_start = 7; // level cursor y-position //int level_cursor_y_pos = 7; // initial level_cursor_y_pos @ start -- Added by Brice for more menu options int level_cursor_y_pos_end = 11; // BOTTOM CURSOR POS -- Added by Brice for more menu options int gameover_x_pos = 5; // gameover label x-position int gameover_y_pos = 5; // gameover label y-position int win_x_pos = 2; // congratulations label x-position int win_y_pos = 4; // congratulations label y-position int startover_x_pos = 3; // startover label x-position int startover_y_pos = 7; // startover label y-position int newAnalogClick = 0; // to prevent the analog click from changing multiple skins with one click int prevAnalogClick = 0; // to prevent the analog click from changing multiple skins with one click // intialize temporary score and current score int temp = 0; int score = 0; // variables and code used to read the high score from the SD card and store in storedScore for display on the LCD // idea for high score reading and updating: https://os.mbed.com/questions/75718/i-want-to-assign-int-value-saved-in-sd-t/ char buffer[3] = {0}; char c = {0}; char *token; int i = 0; //int storedScore = 0; SDLock.lock(); // mutex lock FILE *sdscore; sdscore=fopen("/sd/highscore.txt","r"); // open highscore.txt file if(sdscore==NULL) pc.printf("file open error!\n\n\r"); // added this for open error check // read the characters for the high score stored in highscore.txt from SD card and store in buffer while ((c != '\n') && (i < 3)) { c = fgetc(sdscore); buffer[i] = c; i++; } fclose(sdscore); SDLock.unlock(); token = strtok(buffer, "\n"); storedScore = atoi(token); // convert string from file to integer. this is the stored high score. // Begin game loop while(1) { // initialize all starting conditions for the beginning of the game game_menu = true; // defaults to display menu screen ENEMY_MOVE = true; // defaults to move enemy DIRECTION = 1; // default to move right hit_player = 0; // default to not player hit MOVE_DOWN = 0; // default to not move down lose = false; // default to not lose lives1 = 3; // defaults to 3 lives lives2 = 2; // 2 lives for medium lives3 = 1; // 1 life for hard score = 0; // default to score of 0 int level_cursor_y_pos = 7; // initial level_cursor_y_pos @ start -- Added by Brice for more menu options uLCD.cls(); // Brice moved this out of the loop since it shouldn't change //uLCD.locate(title_x_pos,title_y_pos); // "SPACE INVADERS" title position //uLCD.printf("SPACE INVADERS"); // Title // Implementation of game_menu while(game_menu) { float accelY = 0.0; // y acceleration // Brice added this in order to move the cursor through the menu options uLCD.locate(level_cursor_x_pos, level_cursor_y_pos); uLCD.printf(" "); //if (myNav.down() && level_cursor_y_pos < level_cursor_y_pos_end) { // for previous Nav switch // Control menu with Analog Joystick if ((stick.angle() <= 280 && stick.angle() >= 260) && level_cursor_y_pos < level_cursor_y_pos_end) { level_cursor_y_pos += 2; // move cursor down if analog is pushed down. (and don't let it go past bottom option). //} else if (myNav.up() && level_cursor_y_pos > level_cursor_y_pos_start) { } else if ((stick.angle() <= 100 && stick.angle() >= 80) && level_cursor_y_pos > level_cursor_y_pos_start) { level_cursor_y_pos -= 2; // move cursor up if analog is pushed up (and don't let it go past top option) } // Control Menu with IMU. Read the y acceleration if it can. if (IMU.accelAvailable()) { IMU.readAccel(); accelY = IMU.calcAccel(IMU.ay); //pc.printf("Calc Accel: %f", accelY); } if ((accelY >= 0.25) && level_cursor_y_pos < level_cursor_y_pos_end) { level_cursor_y_pos += 2; // move cursor down if tilted down (and don't let go past bottom option). //} else if (myNav.up() && level_cursor_y_pos > level_cursor_y_pos_start) { // nav switch remnant. } else if ((accelY <= -0.25) && level_cursor_y_pos > level_cursor_y_pos_start) { level_cursor_y_pos -= 2; // move cursor up if tilted up (and don't let go past top option). } // end of movable cursor uLCD.locate(level_cursor_x_pos,level_cursor_y_pos); // draws cursor next to "START" label uLCD.printf("->"); uLCD.locate(title_x_pos,title_y_pos); // "SPACE INVADERS" title position uLCD.printf("SPACE INVADERS"); // Title //uLCD.locate(start_label_x_pos,start_label_y_pos); // "START" label position // original menu option. //uLCD.printf("ONE-PLAYER"); uLCD.locate(start_label_x_pos,start_label_y_pos); // "START" label position. LEVEL 1: Easiest level uLCD.printf("LEVEL 1"); uLCD.locate(start_label_x_pos,start_label_y_pos + 2); // "LEVEL 2" level. Medium difficulty. uLCD.printf("LEVEL 2"); uLCD.locate(start_label_x_pos,start_label_y_pos + 4); // "LEVEL 3" level. Hardest difficulty. uLCD.printf("LEVEL 3"); //uLCD.locate(start_label_x_pos, start_label_y_pos + 6); //uLCD.printf("TWO-PLAYER"); uLCD.locate(2,13); uLCD.printf("High Score: %d", storedScore); // print the stored high score at the bottom of the LCD. // if pushbutton is pressed, game menu is exited and game begins if(!pb) { game_menu = false; if (level_cursor_y_pos == start_label_y_pos) { //numPlayers = 1; level = 1; // select level 1 } else if (level_cursor_y_pos == start_label_y_pos + 2) { level = 2; // select level 2 } else if (level_cursor_y_pos == start_label_y_pos + 4) { level = 3; // select level 3 } //} else if (level_cursor_y_pos == start_label_y_pos + 6) { // USED FOR 2-PLAYER ONLY //numPlayers = 2; //level = 1; //} Thread::wait(500); // changed this to Thread::wait ... originally wait(0.5); } } //while(numPlayers != 1 && !begin_game2) Thread::yield(); // added to force wait with two-player and one player not ready. -- added by Brice //if (numPlayers == 2 && begin_game2) { // FOR 2-PLAYER ONLY // numWins = 0; // bestTimer.start(); //} else { begin_game = true; // defaults begin_game to true // Start timer to check for best time in single player. bestTimer.start(); //} // clear the screen to start drawing the level uLCD.cls(); // Draw the enemies draw_enemies_level(); // Draw the player draw_initial_player(); // Draw the barriers draw_barriers_level(); // erase 2 leftmost and rightmost barriers if level 2 or 3 (harder) if (level == 2 || level == 3) { barrier_erase(&barrier_1); barrier_erase(&barrier_4); } // erase the 3rd barrier if level 3 (even harder) if (level == 3) { barrier_erase(&barrier_3); } // sets the initial position of player missile and enemy missile (later updated) blk_x = player.player_blk_x+(player.player_width/2); blk_y = player.player_blk_y; missile_init(&missile, blk_x, blk_y, WHITE); int e_blk_x = 0; int e_blk_y = 2; enemy_missile_init(&enemy_missile, e_blk_x, e_blk_y, WHITE); // prints lives depending on which level was selected. if (level == 1) { uLCD.locate(0,0); uLCD.printf("Lives:%i", 3); } else if (level == 2) { uLCD.locate(0,0); uLCD.printf("Lives:%i", 2); } else if (level == 3) { uLCD.locate(0,0); uLCD.printf("Lives:%i", 1); } //uLCD.locate(0,0); //uLCD.printf("Lives:%i", lives); // original, basic lives // prints score (no competition possible since the score always ends as the same) uLCD.locate(9,0); // uLCD.filled_rectangle( uLCD.printf("Score:%03d", score); // game play loop while(begin_game) { // updates score temp = score; score = (15-numOfEnemies)*15*level - bestTimer.read(); // multiple the previous score calculation by the level and subtract the current time if (score < 0) score = 0; // don't allow the score to go below 0. // prints score if score changes (score always ends at same number for win) if (score != temp) { uLCD.locate(9,0); uLCD.printf("Score:%03i", score); } // move enemy enemy_motion(); // check barriers for player missile hit. Player missile hit damages the barriers if (missile.missile_blk_y+1-missile.missile_height >= barrier_1.barrier_blk_y && missile.missile_blk_y+1-missile.missile_height <= barrier_1.barrier_blk_y+barrier_1.barrier_height && missile.status == PLAYER_MISSILE_ACTIVE) { if (level == 1) { check_barrier(&barrier_1, &missile); } check_barrier(&barrier_2, &missile); if (level == 1 || level == 2) { check_barrier(&barrier_3, &missile); } if (level == 1) { check_barrier(&barrier_4, &missile); } } // checks if player missile passes y-pos of row1 if (missile.missile_blk_y+1-missile.missile_height <= enemy_1.enemy_blk_y && missile.missile_blk_y+1-missile.missile_height >= enemy_1.enemy_blk_y-enemy_1.enemy_height) { check_hit_enemy_row1(); } // checks if player missile passes y-pos of row2 if (missile.missile_blk_y+1-missile.missile_height <= enemy_6.enemy_blk_y && missile.missile_blk_y+1-missile.missile_height >= enemy_6.enemy_blk_y-enemy_6.enemy_height) { check_hit_enemy_row2(); } // checks if player missile passes y-pos of row3 if (missile.missile_blk_y+1-missile.missile_height <= enemy_11.enemy_blk_y && missile.missile_blk_y+1-missile.missile_height >= enemy_11.enemy_blk_y-enemy_11.enemy_height) { check_hit_enemy_row3(); } // Random Enemy Fire if (enemy_missile.status == ENEMY_MISSILE_INACTIVE) { random_attack_gen(); /* if (level == 2 || level == 3) { random_attack_gen(); } if (level == 3) { random_attack_gen(); } */ } // check if enemy missile passes the y-pos of the barrier and updates the barriers if they are hit (damage them, erase blocks of them) if (enemy_missile.missile_blk_y >= barrier_1.barrier_blk_y && enemy_missile.missile_blk_y <= barrier_1.barrier_blk_y+barrier_1.barrier_height) { if (level == 1) { check_barrier(&barrier_1, &enemy_missile); } check_barrier(&barrier_2, &enemy_missile); if (level == 1 || level == 2) { check_barrier(&barrier_3, &enemy_missile); } if (level == 1) { check_barrier(&barrier_4, &enemy_missile); } } // checks if enemy missile passes y-pos of player if (enemy_missile.missile_blk_y >= player.player_blk_y && enemy_missile.missile_blk_y <= player.player_blk_y+player.player_height) { check_player_hit(); } update_missile_pos(&missile); // updates player missile position update_enemy_missile_pos(&enemy_missile, level); // updates enemy missile position // Player Movement checked with navigation switch //if (myNav.left() && ((player.player_blk_x-3) > 0)) // With joystick click, change color of player from GREEN -> BLUE -> PINK -> PURPLE -> YELLOW (and loop). prevAnalogClick = newAnalogClick; newAnalogClick = stick.button(); if (newAnalogClick && !prevAnalogClick) { // ensures that the button must be released and clicked again before the color changes again. if (player.player_color == 0x00FF00) { // if GREEN (start) player.player_color = 0x0000FF; // BLUE } else if (player.player_color == 0x0000FF) { // if BLUE player.player_color = 0xFFC0CB; // pink. hot pink: 0xFF69B4 } else if (player.player_color == 0xFFC0CB) { // if pink player.player_color = 0x800080; // Purple: 0x800080. periwinkle purple: 0xCCCCFF } else if (player.player_color == 0x800080) { // if purple player.player_color = 0xFFFF00; // yellow. metallic gold: 0xD4AF37 } else if (player.player_color == 0xFFFF00) { // if yellow player.player_color = 0x00FF00; // set back to GREEN } } // Control Player with Analog Joystick -- Brice float stickDist = stick.xAxis(); if ((stickDist < 0.0) && (player.player_blk_x + stickDist*3 > 0.0)){ player_erase(&player); //player.player_blk_x -= 3; player.player_blk_x += (int)(stickDist*3); player_show(&player); } //else if (myNav.right() && ((player.player_blk_x+3) < (128-player.player_width))) // nav switch else if ((stickDist > 0.0) && ((player.player_blk_x + stickDist*3) < (128 - player.player_width))) { player_erase(&player); player.player_blk_x += (int)(stickDist*3); player_show(&player); } // Control Player with IMU -- Brice float accelX = 0.0; // x acceleration if (IMU.accelAvailable()) { IMU.readAccel(); accelX = IMU.calcAccel(IMU.ax); //pc.printf("Calc Accel: %f", accelY); } if ((accelX <= -0.25) && (player.player_blk_x + accelX*5) > 0.0) { player_erase(&player); //player.player_blk_x -= 3; player.player_blk_x += (int)(accelX*8); player_show(&player); //} else if (myNav.up() && level_cursor_y_pos > level_cursor_y_pos_start) { } else if ((accelX >= 0.25) && ((player.player_blk_x + accelX*5) < (128 - player.player_width))) { player_erase(&player); //player.player_blk_x -= 3; player.player_blk_x += (int)(accelX*8); player_show(&player); } // Player Fire if (pb == 0 && missile.status == PLAYER_MISSILE_INACTIVE) { missile.missile_blk_x = player.player_blk_x+(player.player_width/2); missile.missile_blk_y = player.player_blk_y; missile.status = PLAYER_MISSILE_ACTIVE; } // checks if player destroyed all enemies (a win) if (numOfEnemies == 0) { // idea for high score reading and updating: https://os.mbed.com/questions/75718/i-want-to-assign-int-value-saved-in-sd-t/ // check the timer to see if there's a best time. //int time = bestTimer.read(); bestTimer.stop(); bestTimer.reset(); //score = score - time; uLCD.cls(); char buffer[3] = {0}; char c = {0}; char *token; int i = 0; //int storedScore = 0; SDLock.lock(); // mutex lock SD FILE *sdscore; sdscore=fopen("/sd/highscore.txt","r"); // open highscore.txt if(sdscore==NULL) pc.printf("file open error!\n\n\r"); // added this for open error check // read in characters from stored high score in SD and store in buffer while ((c != '\n') && (i < 3)) { c = fgetc(sdscore); buffer[i] = c; i++; } token = strtok(buffer, "\n"); storedScore = atoi(token); // convert string from file to integer fclose(sdscore); // if the new score is greater than the stored score, print "NEW HIGH SCORE!" on LCD and the new high score if (score > storedScore) { storedScore = score; uLCD.locate(2,10); uLCD.printf("NEW HIGH SCORE!"); uLCD.locate(2,11); uLCD.printf("%d", score); sdscore = fopen("/sd/highscore.txt", "w"); if (sdscore != NULL) { fprintf(sdscore, "%d\r\n", score); // write the new high score to the SD card fclose(sdscore); } else { pc.printf("write: failed!\r\n"); } } SDLock.unlock(); win = true; // sets win to true, for win screen begin_game = false; // ends game // displays video clip ????. Original intention here is a little obscure. Might be buggy. /* uLCD.cls(); uLCD.media_init(); uLCD.set_sector_address(0x00, 0x00); uLCD.display_video(0,0); Thread::wait(1000); // changed from wait(1) to Thread::wait(1000) since we're using threads -- Brice */ //uLCD.cls(); // prints "Congratulations" on uLCD uLCD.locate(win_x_pos,win_y_pos); uLCD.printf("CONGRATULATIONS!"); // prints "Play Again?" and "Press pb..." uLCD.locate(startover_x_pos, startover_y_pos); uLCD.printf("Play Again?"); uLCD.locate(startover_x_pos, startover_y_pos+1); uLCD.printf("Press pb..."); // waits at win screen until pushbutton is pressed while (win) { // if pb is pressed, reset game to start menu if (!pb) { win = false; Thread::wait(500); // changed from wait(0.5) to Thread::wait(500) since we're using threads } } } int prevColor; // used to store the previous color of the player // checks if player was hit if (hit_player) { // updates lives if (level == 1) { lives1 -= 1; // level 1 lives prevColor = player.player_color; player_erase(&player); player.player_color = 0xFF0000; // briefly flash the player red. player_show(&player); Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice player_erase(&player); player.player_color = prevColor; // briefly flash the player red and RETURN TO THE PREVIOUS CHOSEN COLOR player_show(&player); hit_player = 0; player_show(&player); player.status = PLAYER_ALIVE; // prints updated lives number uLCD.locate(0,0); uLCD.printf("Lives:%i", lives1); } else if (level == 2) { // level 2 lives lives2 -= 1; prevColor = player.player_color; player_erase(&player); player.player_color = 0xFF0000; // briefly flash the player red. player_show(&player); Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice player_erase(&player); player.player_color = prevColor; // briefly flash the player red and return to previous color player_show(&player); //Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice hit_player = 0; player_show(&player); player.status = PLAYER_ALIVE; // prints updated lives number uLCD.locate(0,0); uLCD.printf("Lives:%i", lives2); } else if (level == 3) { lives3 -= 1; // level 3 lives prevColor = player.player_color; player_erase(&player); player.player_color = 0xFF0000; // briefly flash the player red. player_show(&player); Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice player_erase(&player); player.player_color = prevColor; // briefly flash the player red and return to previous color. player_show(&player); //Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice hit_player = 0; player_show(&player); player.status = PLAYER_ALIVE; // prints updated lives number uLCD.locate(0,0); uLCD.printf("Lives:%i", lives3); } } // if player loses all lives or enemy reaches the player if (lose || lives1 == 0 || lives2 == 0 || lives3 == 0) { begin_game = false; // set to false to end game uLCD.cls(); // clear the game screen gameover = true; // set to go to display gameover screen // prints "GAMEOVER" to uLCD uLCD.locate(gameover_x_pos, gameover_y_pos); uLCD.printf("GAMEOVER"); Thread::wait(1000); // changed from wait(1) to thread::wait since we're using threads -- Brice // prints "Play Again?" and "Press pb..." uLCD.locate(startover_x_pos, startover_y_pos); uLCD.printf("Play Again?"); uLCD.locate(startover_x_pos, startover_y_pos+1); uLCD.printf("Press pb..."); // stays in gameover screen until pb is pressed while (gameover) { // if pb is pressed, game is reset to the game menu screen if (!pb) { gameover = false; game_menu = true; Thread::wait(500); // changed wait(0.5) to Thread::wait since we're using threads -- Brice } } } } // game play loop /* TWO-PLAYER MODE: BUGGY, BUT THE PRIMITIVES ARE IN PLACE. // I was able to get the two mbeds to send over the proper character codes to get 1 mbed to start the game // in 2 player mode. When the 'W' character was received by this mbed, the game stopped with a loss. // We lack hardware and time to fully test and work out all of the bugs here, but it is close to being // a working feature with the primitives we've commented out here. while(begin_game2) { temp = score; score = (15-numOfEnemies)*15; // prints score if score changes if (score != temp) { uLCD.locate(9,0); uLCD.printf("Score:%i", score); } // move enemy enemy_motion(); // check barriers for player missile hit if (missile.missile_blk_y+1-missile.missile_height >= barrier_1.barrier_blk_y && missile.missile_blk_y+1-missile.missile_height <= barrier_1.barrier_blk_y+barrier_1.barrier_height && missile.status == PLAYER_MISSILE_ACTIVE) { check_barrier(&barrier_1, &missile); check_barrier(&barrier_2, &missile); check_barrier(&barrier_3, &missile); check_barrier(&barrier_4, &missile); } // checks if player missile passes y-pos of row1 if (missile.missile_blk_y+1-missile.missile_height <= enemy_1.enemy_blk_y && missile.missile_blk_y+1-missile.missile_height >= enemy_1.enemy_blk_y-enemy_1.enemy_height) { check_hit_enemy_row1(); } // checks if player missile passes y-pos of row2 if (missile.missile_blk_y+1-missile.missile_height <= enemy_6.enemy_blk_y && missile.missile_blk_y+1-missile.missile_height >= enemy_6.enemy_blk_y-enemy_6.enemy_height) { check_hit_enemy_row2(); } // checks if player missile passes y-pos of row3 if (missile.missile_blk_y+1-missile.missile_height <= enemy_11.enemy_blk_y && missile.missile_blk_y+1-missile.missile_height >= enemy_11.enemy_blk_y-enemy_11.enemy_height) { check_hit_enemy_row3(); } // Random Enemy Fire if (enemy_missile.status == ENEMY_MISSILE_INACTIVE) { random_attack_gen(); } // check if enemy missile passes the y-pos of the barrier and updates the barriers if they are hit. if (enemy_missile.missile_blk_y >= barrier_1.barrier_blk_y && enemy_missile.missile_blk_y <= barrier_1.barrier_blk_y+barrier_1.barrier_height) { check_barrier(&barrier_1, &enemy_missile); check_barrier(&barrier_2, &enemy_missile); check_barrier(&barrier_3, &enemy_missile); check_barrier(&barrier_4, &enemy_missile); } // checks if enemy missile passes y-pos of player if (enemy_missile.missile_blk_y >= player.player_blk_y && enemy_missile.missile_blk_y <= player.player_blk_y+player.player_height) { check_player_hit(); } update_missile_pos(&missile); // updates player missile position update_enemy_missile_pos(&enemy_missile, level); // updates enemy missile position // Player Movement checked with navigation switch //if (myNav.left() && ((player.player_blk_x-3) > 0)) // With joystick click, change color of player from GREEN -> BLUE -> PINK -> PURPLE -> YELLOW (and loop). prevAnalogClick = newAnalogClick; newAnalogClick = stick.button(); if (newAnalogClick && !prevAnalogClick) { if (player.player_color == 0x00FF00) { // if GREEN (start) player.player_color = 0x0000FF; // BLUE } else if (player.player_color == 0x0000FF) { // if BLUE player.player_color = 0xFFC0CB; // pink. hot pink: 0xFF69B4 } else if (player.player_color == 0xFFC0CB) { // if pink player.player_color = 0x800080; // Purple: 0x800080. periwinkle purple: 0xCCCCFF } else if (player.player_color == 0x800080) { // if purple player.player_color = 0xFFFF00; // yellow. metallic gold: 0xD4AF37 } else if (player.player_color == 0xFFFF00) { // if yellow player.player_color = 0x00FF00; // set back to GREEN } } // Control Player with Analog Joystick -- Brice float stickDist = stick.xAxis(); if ((stickDist < 0.0) && (player.player_blk_x + stickDist*3 > 0.0)){ player_erase(&player); //player.player_blk_x -= 3; player.player_blk_x += (int)(stickDist*3); player_show(&player); } //else if (myNav.right() && ((player.player_blk_x+3) < (128-player.player_width))) else if ((stickDist > 0.0) && ((player.player_blk_x + stickDist*3) < (128 - player.player_width))) { player_erase(&player); player.player_blk_x += (int)(stickDist*3); player_show(&player); } // Control Player with IMU -- Brice float accelX = 0.0; // x acceleration if (IMU.accelAvailable()) { IMU.readAccel(); accelX = IMU.calcAccel(IMU.ax); //pc.printf("Calc Accel: %f", accelY); } if ((accelX <= -0.25) && (player.player_blk_x + accelX*5) > 0.0) { player_erase(&player); //player.player_blk_x -= 3; player.player_blk_x += (int)(accelX*8); player_show(&player); //} else if (myNav.up() && level_cursor_y_pos > level_cursor_y_pos_start) { } else if ((accelX >= 0.25) && ((player.player_blk_x + accelX*5) < (128 - player.player_width))) { player_erase(&player); //player.player_blk_x -= 3; player.player_blk_x += (int)(accelX*8); player_show(&player); } // Player Fire if (pb == 0 && missile.status == PLAYER_MISSILE_INACTIVE) { missile.missile_blk_x = player.player_blk_x+(player.player_width/2); missile.missile_blk_y = player.player_blk_y; missile.status = PLAYER_MISSILE_ACTIVE; } // checks if player destroyed all enemies if (numOfEnemies == 0) { // idea for best-time reading and updating: https://os.mbed.com/questions/75718/i-want-to-assign-int-value-saved-in-sd-t/ int time = bestTimer.read(); bestTimer.stop(); bestTimer.reset(); uLCD.cls(); char buffer[3] = {0}; char c = {0}; char *token; int i = 0; int storedTime = 999; SDLock.lock(); FILE *sdtime; sdtime=fopen("/sd/besttime.txt","r"); if(sdtime==NULL) pc.printf("file open error!\n\n\r"); // added this for open error check while ((c != '\n') && (i < 3)) { c = fgetc(sdtime); buffer[i] = c; i++; } token = strtok(buffer, "\n"); storedTime = atoi(token); // convert string from file to integer fclose(sdtime); if (time < storedTime) { uLCD.locate(2,10); uLCD.printf("NEW BEST TIME!"); uLCD.locate(2,11); uLCD.printf("%d seconds!", time); sdtime = fopen("/sd/besttime.txt", "w"); if (sdtime != NULL) { fprintf(sdtime, "%d\r\n", time); fclose(sdtime); } else { pc.printf("write: failed!\r\n"); } } SDLock.unlock(); two_player_win = true; // sets win to true, for win screen win = true; //begin_game = false; // displays video clip ???? uLCD.cls(); uLCD.media_init(); uLCD.set_sector_address(0x00, 0x00); uLCD.display_video(0,0); Thread::wait(1000); // changed from wait(1) to Thread::wait(1000) since we're using threads -- Brice */ //uLCD.cls(); /* // prints "Congratulations" on uLCD uLCD.locate(win_x_pos,win_y_pos); uLCD.printf("CONGRATULATIONS!"); // prints "Play Again?" and "Press pb..." uLCD.locate(startover_x_pos, startover_y_pos); uLCD.printf("Play again?"); uLCD.locate(startover_x_pos, startover_y_pos+1); uLCD.printf("Press pb..."); // waits at win screen until pushbutton is pressed while (two_player_win) { // if pb is pressed, reset game to start menu if (!pb) { win = false; two_player_win = false; begin_game2 = false; numPlayers = 1; game_menu = true; Thread::wait(500); // changed from wait(0.5) to Thread::wait(500) since we're using threads } } REMOVED THIS AND MADE THE WINNER THE FIRST TO 1 WIN if (numWins == 3) { two_player_win = true; while(two_player_win) { // if pb is pressed, reset game to start menu Thread::wait(2000); uLCD.locate(win_x_pos,win_y_pos); uLCD.printf("CONGRATULATIONS!"); // prints "Play Again?" and "Press pb..." uLCD.locate(startover_x_pos, startover_y_pos); uLCD.printf("Play Again?"); uLCD.locate(startover_x_pos, startover_y_pos+1); uLCD.printf("Press pb..."); if (!pb) { two_player_win = false; begin_game2 = false; game_menu = true; Thread::wait(500); // changed from wait(0.5) to Thread::wait(500) since we're using threads } } } } int prevColor; // checks if player was hit if (hit_player) { // updates lives if (level == 1) { lives1 -= 1; prevColor = player.player_color; player_erase(&player); player.player_color = 0xFF0000; // briefly flash the player red. player_show(&player); Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice player_erase(&player); player.player_color = prevColor; // briefly flash the player red. player_show(&player); hit_player = 0; player_show(&player); player.status = PLAYER_ALIVE; // prints updated lives number uLCD.locate(0,0); uLCD.printf("Lives:%i", lives1); } else if (level == 2) { lives2 -= 1; prevColor = player.player_color; player_erase(&player); player.player_color = 0xFF0000; // briefly flash the player red. player_show(&player); Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice player_erase(&player); player.player_color = prevColor; // briefly flash the player red. player_show(&player); //Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice hit_player = 0; player_show(&player); player.status = PLAYER_ALIVE; // prints updated lives number uLCD.locate(0,0); uLCD.printf("Lives:%i", lives2); } else if (level == 3) { lives3 -= 1; prevColor = player.player_color; player_erase(&player); player.player_color = 0xFF0000; // briefly flash the player red. player_show(&player); Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice player_erase(&player); player.player_color = prevColor; // briefly flash the player red. player_show(&player); //Thread::wait(500); // changed from wait(0.5) to Thread::wait since we're using threads -- Brice hit_player = 0; player_show(&player); player.status = PLAYER_ALIVE; // prints updated lives number uLCD.locate(0,0); uLCD.printf("Lives:%i", lives3); } } // if player loses all lives or enemy reaches the player if (lose || lives1 == 0 || lives2 == 0 || lives3 == 0) { //begin_game = false; // set to false to end game lives1 = 3; uLCD.cls(); two_player_lose = true; gameover = true; // set to go to display gameover screen numPlayers = 1; // prints "GAMEOVER" to uLCD uLCD.locate(gameover_x_pos, gameover_y_pos); uLCD.printf("GAMEOVER"); Thread::wait(1000); // changed from wait(1) to thread::wait since we're using threads -- Brice // prints "Play Again?" and "Press pb..." uLCD.locate(startover_x_pos, startover_y_pos); uLCD.printf("Play again?"); uLCD.locate(startover_x_pos, startover_y_pos+1); uLCD.printf("Press pb..."); // stays in gameover screen until pb is pressed while (gameover) { // if pb is pressed, game is reset to the game menu screen if (!pb) { gameover = false; two_player_lose = false; game_menu = true; begin_game2 = false; numPlayers = 1; //game_menu = true; Thread::wait(500); // changed wait(0.5) to Thread::wait since we're using threads -- Brice } } } if (two_player_lose) { uLCD.cls(); numPlayers = 1; Thread::wait(2000); uLCD.locate(gameover_x_pos, gameover_y_pos); uLCD.printf("You lost!"); Thread::wait(1000); // changed from wait(1) to thread::wait since we're using threads -- Brice // prints "Play Again?" and "Press pb..." uLCD.locate(startover_x_pos, startover_y_pos); uLCD.printf("Continue?"); uLCD.locate(startover_x_pos, startover_y_pos+1); uLCD.printf("Press pb..."); } while(two_player_lose) { //uLCD.cls(); if (!pb) { two_player_lose = false; game_menu = true; begin_game2 = false; numPlayers = 1; Thread::wait(500); // changed wait(0.5) to Thread::wait since we're using threads -- Brice } } } }*/ } }