/*------------------------------------------------------------------------------ ; File : NXTInvaders.nxc ; Description : A Space Invaders clone for the Mindstorms NXT. ; Programmed by : Miguel Angel Astor, sonofgrendel@gmail.com ; Version: 1.0 ; Date: 8/13/2013 ;-----------------------------------------------------------------------------*/ #download "ship.ric" #download "splash.ric" #download "invader.ric" #download "victory.ric" #download "defeat.ric" #define X_MAX 99 #define Y_MAX 63 #define X_MID (X_MAX + 1) / 2 #define Y_MID (Y_MAX + 1) / 2 #define DRAW_PARAMS DRAW_OPT_NORMAL | DRAW_OPT_LOGICAL_XOR | DRAW_OPT_FILL_SHAPE #define ALIEN_WIDTH 11 #define BASE_ALIEN_X 2 #define BASE_ALIEN_Y 32 #define ALIENS 6 /******************* * Data structures * *******************/ struct PLAYER_T{ int x; int lives; int score; }; struct BULLET_T{ int x, y; bool alive; }; struct ALIEN_T{ int x, y; bool alive; }; /**************** * Global data. * ****************/ const int alien_update_speed[] = {50, 72, 95, 118, 140, 163, 186, 209, 231, 254, 277, 300 }; int dead_aliens; bool victory; // keys[0] -> Left button; keys[1] -> Right button; keys[2] -> Center button. bool keys[3]; // State: 0 -> Splash screen; 1 -> Game; 2 -> Hall of fame screen. int state, alien_frame, alien_x_offset; bool alien_x_forward, done; long then, then2; PLAYER_T player; BULLET_T p_bullets[3]; ALIEN_T aliens1[ALIENS]; ALIEN_T aliens2[ALIENS]; BULLET_T a_bullets1[ALIENS]; BULLET_T a_bullets2[ALIENS]; /******************* * Game functions. * *******************/ /** * Catch the 3 possible player inputs. */ void input(){ if(ButtonPressed(BTNLEFT, false)){ keys[0] = true; }else{ keys[0] = false; } if(ButtonPressed(BTNRIGHT, false)){ keys[1] = true; }else{ keys[1] = false; } if(ButtonPressed(BTNCENTER, false)){ keys[2] = true; }else{ keys[2] = false; } } /** * Processes player input, moves game objects and updates the game's state. */ void update(){ long now, delta_t, fire; if(state == 0){ if(keys[2]){ state = 1; } }else if(state == 1){ // Move the player. if(keys[0]){ if(player.x - 1 >= 3){ player.x -= 1; } } if(keys[1]){ if(player.x + 1 <= X_MAX - 4){ player.x += 1; } } // If the center button was pressed, fire a bullet. if(keys[2]){ now = CurrentTick(); delta_t = now - then; if(delta_t >= 500){ // One bullet every 500 miliseconds. for(int i = 0; i < 3; i++){ if(!p_bullets[i].alive){ p_bullets[i].x = player.x; p_bullets[i].y = 0; p_bullets[i].alive = true; PlayTone(262, 100); break; } } then = now; } } // Update the player's bullets. for(int i = 0; i < 3; i++){ if(p_bullets[i].alive){ p_bullets[i].y += 1; if(p_bullets[i].y >= Y_MAX + 2){ p_bullets[i].alive = false; } } } // Update the aliens. now = CurrentTick(); delta_t = now - then2; if(delta_t >= alien_update_speed[11 - dead_aliens]){ // Change the frame. if(alien_frame == 0){ alien_frame = 11; }else{ alien_frame = 0; } // Move in the correct direction. if(alien_x_forward){ alien_x_offset += 1; if(alien_x_offset >= 15){ // Change direction 2 pixels before the edge of the screen. alien_x_offset -= 1; alien_x_forward = false; } }else{ alien_x_offset -= 1; if(alien_x_offset <= 0){ // Change direction 2 pixels before the edge of the screen. alien_x_offset += 1; alien_x_forward = true; } } // Fire on the player. for(int i = 0; i < ALIENS; i++){ // First row. if(aliens1[i].alive && !a_bullets1[i].alive){ fire = rand() % 100; if(fire < 25){ // 0.25 probability of firing. a_bullets1[i].alive = true; a_bullets1[i].x = aliens1[i].x + alien_x_offset + 5; a_bullets1[i].y = aliens1[i].y; PlayTone(400, 100); } } // Second row. if(aliens2[i].alive && !a_bullets2[i].alive){ fire = rand() % 100; if(fire > 75){ // 0.25 probability of firing. a_bullets2[i].alive = true; a_bullets2[i].x = aliens2[i].x + alien_x_offset + 5; a_bullets2[i].y = aliens2[i].y; PlayTone(400, 100); } } } then2 = now; } // Update the alien's bullets. for(int i = 0; i < ALIENS; i++){ // First row. if(a_bullets1[i].alive){ a_bullets1[i].y -= 1; if(a_bullets1[i].y < -2){ a_bullets1[i].alive = false; } } // Second row. if(a_bullets2[i].alive){ a_bullets2[i].y -= 1; if(a_bullets2[i].y < -2){ a_bullets2[i].alive = false; } } } // Check if any bullet shot by the player hit an alien. for(int i = 0; i < 3; i++){ if(p_bullets[i].alive){ if(p_bullets[i].y >= 22 && p_bullets[i].y <= 30){ // Check the second row first. for(int j = 0; j < ALIENS; j++){ if(aliens2[j].alive){ if(p_bullets[i].x >= aliens2[j].x + alien_x_offset && p_bullets[i].x <= aliens2[j].x + alien_x_offset + 11){ aliens2[j].alive = false; p_bullets[i].alive = false; player.score += 100; dead_aliens += 1; PlayTone(100, 250); break; } } } }else if(p_bullets[i].y >= 32 && p_bullets[i].y <= 40){ // Check the first row last. for(int j = 0; j < ALIENS; j++){ if(aliens1[j].alive){ if(p_bullets[i].x >= aliens1[j].x + alien_x_offset && p_bullets[i].x <= aliens1[j].x + alien_x_offset + 11){ aliens1[j].alive = false; p_bullets[i].alive = false; player.score += 150; dead_aliens += 1; PlayTone(100, 250); break; } } } } } } // Check alien bullet collisions with player. // First row. for(int i = 0; i < ALIENS; i++){ if(a_bullets1[i].alive && a_bullets1[i].y <= 2){ if(a_bullets1[i].x >= player.x - 3 && a_bullets1[i].x <= player.x + 4){ // If the bullet hit the player, reset the game. player.x = X_MID; player.lives -= 1; alien_x_offset = 0; alien_x_forward = true; for(int j = 0; j < ALIENS; j++){ a_bullets1[i].alive = false; a_bullets2[i].alive = false; } PlayTone(100, 250); break; } } } // Second row for(int i = 0; i < ALIENS; i++){ if(a_bullets2[i].alive && a_bullets2[i].y <= 2){ if(a_bullets2[i].x >= player.x - 3 && a_bullets2[i].x <= player.x + 4){ // If the bullet hit the player, reset the game. player.x = X_MID; player.lives -= 1; alien_x_offset = 0; alien_x_forward = true; for(int j = 0; j < ALIENS; j++){ a_bullets1[i].alive = false; a_bullets2[i].alive = false; } PlayTone(100, 250); break; } } } // Check victory/defeat conditions. if(dead_aliens == 12){ state = 2; victory = true; PlayTone(500, 1000); ClearScreen(); }else if(player.lives < 0){ state = 2; victory = false; PlayTone(100, 1000); ClearScreen(); } }else{ if(keys[2]){ done = true; } } } /** * Draws all game objects to the LCD screen of the robot. */ void render(){ int params[3] = {0, 0, 0}; if(state == 0){ // Render the splash screen. GraphicOut(0, 0, "splash.ric"); }else if(state == 1){ ClearScreen(); // Render informative text. TextOut(0, LCD_LINE1, "LIVES:"); params[0] = 35; if(player.lives > 0){ GraphicOutEx(0, LCD_LINE1 + 1, "ship.ric", params); if(player.lives > 1){ params[0] = 44; GraphicOutEx(0, LCD_LINE1 + 1, "ship.ric", params); if(player.lives > 2){ params[0] = 53; GraphicOutEx(0, LCD_LINE1 + 1, "ship.ric", params); } } } TextOut(0, LCD_LINE2, "SCORE:"); NumOut(35, LCD_LINE2, player.score); // Render the player's sprite. params[0] = player.x - 3; GraphicOutEx(0, 0, "ship.ric", params); // Render the player's bullets. for(int i = 0; i < 3; i++){ if(p_bullets[i].alive){ RectOut(p_bullets[i].x, p_bullets[i].y, 0, 2, DRAW_PARAMS); } } // Render the aliens. for(int i = 0; i < ALIENS; i++){ if(aliens1[i].alive){ // First row. params[0] = alien_frame; params[1] = aliens1[i].x + alien_x_offset; params[2] = aliens1[i].y; GraphicOutEx(0, 0, "invader.ric", params); } if(aliens2[i].alive){ // Second row. params[0] = alien_frame; params[1] = aliens2[i].x + alien_x_offset; params[2] = aliens2[i].y; GraphicOutEx(0, 0, "invader.ric", params); } } // Render the aliens's bullets. for(int i = 0; i < ALIENS; i++){ if(a_bullets1[i].alive){ RectOut(a_bullets1[i].x, a_bullets1[i].y, 0, 2, DRAW_PARAMS); } if(a_bullets2[i].alive){ RectOut(a_bullets2[i].x, a_bullets2[i].y, 0, 2, DRAW_PARAMS); } } }else{ // Render the hall of fame. if(victory){ params[0] = player.score; GraphicOutEx(0, 0, "victory.ric", params); }else{ GraphicOut(0, 0, "defeat.ric"); } } } /** * The heart of the game. */ void game_loop(){ while(!done){ input(); update(); render(); Wait(50); } } /** * Initializes all game data and call the game loop. */ task main(){ // Initialize game data. state = 0; done = false; alien_frame = 0; alien_x_offset = 0; alien_x_forward = true; // Initialize the player and bullets. player.x = X_MID; player.lives = 3; player.score = 0; for(int i = 0; i < 3; i++){ p_bullets[i].x = 0; p_bullets[i].y = 0; p_bullets[i].alive = false; } // Initialize the aliens. dead_aliens = 0; for(int i = 0; i < ALIENS; i++){ // First row. aliens1[i].x = BASE_ALIEN_X + (i * 14); aliens1[i].y = BASE_ALIEN_Y; aliens1[i].alive = true; // Second row. aliens2[i].x = BASE_ALIEN_X + (i * 14); aliens2[i].y = BASE_ALIEN_Y - 10; aliens2[i].alive = true; // Alien bullets, first row. a_bullets1[i].x = 0; a_bullets1[i].y = 0; a_bullets1[i].alive = false; // Second row. a_bullets2[i].x = 0; a_bullets2[i].y = 0; a_bullets2[i].alive = false; } // Initialize time variables. then = CurrentTick(); then2 = CurrentTick(); // Start the game! PlayTone(500, 1000); game_loop(); }