From 42d7007a3594abed8f20d77f2ac39a099d931cce Mon Sep 17 00:00:00 2001 From: Miguel Astor Date: Tue, 13 Aug 2013 13:41:57 -0430 Subject: [PATCH] Added code and data files. --- .gitignore | 4 + LICENSE | 25 +++ Makefile | 5 + NXTInvaders.nxc | 449 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 6 +- defeat.ric | Bin 0 -> 872 bytes invader.ric | Bin 0 -> 64 bytes ship.ric | Bin 0 -> 44 bytes splash.ric | Bin 0 -> 872 bytes victory.ric | Bin 0 -> 884 bytes 10 files changed, 488 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 NXTInvaders.nxc create mode 100644 defeat.ric create mode 100644 invader.ric create mode 100644 ship.ric create mode 100644 splash.ric create mode 100644 victory.ric diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f92bec --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Ignore NXT executable files, RIC edit data and Gedit backup files. +*.rxe +*.RPA +*~ diff --git a/LICENSE b/LICENSE index e69de29..4869415 100644 --- a/LICENSE +++ b/LICENSE @@ -0,0 +1,25 @@ +A Space Invaders clone for the Mindstorms NXT. + +Copyright (c) 2013, Miguel Angel Astor Romero +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + *) Redistributions of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + *) Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITSOR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f7007b9 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +all: NXTInvaders.nxc + nbc NXTInvaders.nxc -O=NXTInvaders.rxe -sm+ -d -S=usb + +clean: + rm -f NXTInvaders.rxe diff --git a/NXTInvaders.nxc b/NXTInvaders.nxc new file mode 100644 index 0000000..b038c41 --- /dev/null +++ b/NXTInvaders.nxc @@ -0,0 +1,449 @@ +/*------------------------------------------------------------------------------ +; 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(); +} diff --git a/README.md b/README.md index 116ed91..5e69df0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ NXTInvaders =========== -An Space Invaders clone for the Lego Mindstorms NXT +A Space Invaders clone for the Lego Mindstorms NXT. + +This game is written in the NXC language. To compile on Windows use the BricxCC +IDE avaliable at http://bricxcc.sourceforge.net/nbc/. On Linux systems install +the NBC compiler available on the same page and run make. diff --git a/defeat.ric b/defeat.ric new file mode 100644 index 0000000000000000000000000000000000000000..d4db90c3b2e9bf2893d5aef22e3de4ab60398742 GIT binary patch literal 872 zcmciBu};G<5CG7#JcI>V;scyt-~&q2kq_Y`ToJG<2FCtD`zw(;bj%-6Z;W`fgq7ni zPSRG2gv7uNN#5Q094pEVa6G{Z9;E<5_uD@6KbI;fw}jdz-VWm#0%O@!kPH`OxW9l0 z!X{UW1}bmgy1tF@;O$|6gO^}GC)5lZXssE285Oqb})YpzJY z_Ig5i_N7_mioowi8OQ*{Mn%xJ<1<)vXK-RRN*sgASD0=8EveFi-eQk^U)3!I!?7%C z@kKi{j8yIjy6}vs_L3kOA`~atf))eXGrVlysgm0mTbyv*5^Tc*ygasB+Q*Gh1bXJt zhm!(bj_3%=v7*{A=yOt}GQ%@0ibR!uQ>Z#f3O_{Pib}L>1Se*L&Yy$VTw%8TugN=* LJilMge-_6NBKga& literal 0 HcmV?d00001 diff --git a/invader.ric b/invader.ric new file mode 100644 index 0000000000000000000000000000000000000000..fbf9e5f29edeea3dc15010349a22839b0871c3d4 GIT binary patch literal 64 zcmd;J00C|W4h97VMj+&1U}jKg5fBtov7i1W@9v%d|NpF8|6{=t0cj2nA)p)sPz}Qe Khz`aNOdkN2YYzVa literal 0 HcmV?d00001 diff --git a/ship.ric b/ship.ric new file mode 100644 index 0000000000000000000000000000000000000000..44b42c827669ecf957b6a3e2fb06f73b0be7b704 GIT binary patch literal 44 lcmd;J00DLe76u*$Mj&JXVu6}}|AZKrf#QsCS%wb`3;;o(1Hb?P literal 0 HcmV?d00001 diff --git a/splash.ric b/splash.ric new file mode 100644 index 0000000000000000000000000000000000000000..38c4ae31f813cffb6d280becc96aa8759a6361b2 GIT binary patch literal 872 zcmcK3J5Izf5CBj|E=+?WaRFb1$dnv}Lr@W;EmGzPxd;;JQsxHipcH8xZIP@99>%}0 zX($j(v8$Kanek_H#aukFJ9f`AmeKdMfAU`+vK7O^EF#hb3F3Xt)^AvzU<6pcuqU?; zxdo6SPAc=Mdx(5=u~g+JYuj8x-v{ZCgt~zNQ)pmevzit#djzdjYn=9d8*jzq!L( zUt~h*4&hPE>`>r~vg$-vrx`17Rank+!w$18oAca!j#N+#Z;$zc^%)Wj=F{SF{6K?f z=!ofO{p?ZdfZiZ8c|^USHo~JDsdYDX5i}GhKXm8@R3`%aDHQ$fLQsQ0jT^?PU*>zd HH_OErgMI4O literal 0 HcmV?d00001 diff --git a/victory.ric b/victory.ric new file mode 100644 index 0000000000000000000000000000000000000000..c4bb2f90bbbcc66cd7212ccd8e64a2723e109815 GIT binary patch literal 884 zcmchTze~eF6vtmk=>?&lI0?d~W57Q^huF&~xO8(Z*ndE}WQexl;6EXq9sL6;DRk)A ze?Stqjtz7$(3t0YclF0!^bPmrbMM_Jx$}Ul4-s6r4mgx8v-2N|aBClNQR(+t4dTAm zh7?bI z0%x^_f&neO6d^&atgQ^`!nLyBNf;_+{vb?f3=*Zw*ssG5L#+HVmX(D+e5ZX7-*!O{ z^aJo4}DsppZGra#sEu$U<)j5(uC=dbmuw*mmH4q^x@j%9(a_Oa0F0%0+Rrt9{>OV literal 0 HcmV?d00001