diff --git a/NXTInvaders.nxc b/NXTInvaders.nxc index b038c41..ca69a36 100644 --- a/NXTInvaders.nxc +++ b/NXTInvaders.nxc @@ -1,449 +1,450 @@ -/*------------------------------------------------------------------------------ -; 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:"); - //NumOut(35, LCD_LINE1, player.lives); - params[0] = 35; - 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(); -} +/*------------------------------------------------------------------------------ +; 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(); +} \ No newline at end of file