Space invaders with a nRF2401A wireless joypad

Dependencies:   Gameduino mbed nRF2401A

Fork of Gameduino_Invaders_game by Chris Dick

Gameduino and an nRF2401A hooked up to an mbed on an mbeduino:

/media/uploads/TheChrisyd/2014-03-08_22.53.54.jpg

Committer:
TheChrisyd
Date:
Sun Mar 09 12:27:20 2014 +0000
Revision:
5:3ede9991d8e0
Parent:
4:bb78bedae411
Update to match Library update

Who changed what in which revision?

UserRevisionLine numberNew contents of line
TheChrisyd 0:8a7c58553b44 1 #include "game.h"
TheChrisyd 0:8a7c58553b44 2
TheChrisyd 2:20a89dc286d5 3 extern GDClass GD;
TheChrisyd 0:8a7c58553b44 4 SPI spigame(ARD_MOSI, ARD_MISO, ARD_SCK); // mosi, miso, sclk
TheChrisyd 0:8a7c58553b44 5 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 6 Trivia: There is NO random number generator
TheChrisyd 0:8a7c58553b44 7 anywhere in Space Invaders....
TheChrisyd 0:8a7c58553b44 8 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 9
TheChrisyd 0:8a7c58553b44 10
TheChrisyd 0:8a7c58553b44 11 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 12 Global definitions
TheChrisyd 0:8a7c58553b44 13 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 14 #define invaderRows 5
TheChrisyd 0:8a7c58553b44 15 #define invadersPerRow 11
TheChrisyd 0:8a7c58553b44 16 #define numInvaders (invaderRows*invadersPerRow)
TheChrisyd 0:8a7c58553b44 17
TheChrisyd 0:8a7c58553b44 18 // Positions of things on screen
TheChrisyd 0:8a7c58553b44 19 // nb. Space Invaders screen is 256x224 pixels
TheChrisyd 0:8a7c58553b44 20 #define screenTop 24
TheChrisyd 0:8a7c58553b44 21 #define screenLeft 88
TheChrisyd 0:8a7c58553b44 22 #define screenWidth 224
TheChrisyd 0:8a7c58553b44 23 #define screenHeight 256
TheChrisyd 0:8a7c58553b44 24 // Player
TheChrisyd 0:8a7c58553b44 25 #define playerMinLeft 18
TheChrisyd 0:8a7c58553b44 26 #define playerMaxRight 188
TheChrisyd 0:8a7c58553b44 27 #define playerYpos 216
TheChrisyd 0:8a7c58553b44 28 #define playerSpeed 1
TheChrisyd 0:8a7c58553b44 29 // Bullet
TheChrisyd 0:8a7c58553b44 30 #define bulletHeight 4
TheChrisyd 0:8a7c58553b44 31 #define bulletSpeed 4
TheChrisyd 0:8a7c58553b44 32 #define bulletTop 35
TheChrisyd 0:8a7c58553b44 33 // Invaders
TheChrisyd 0:8a7c58553b44 34 #define invaderAppearX 26
TheChrisyd 0:8a7c58553b44 35 #define invaderAppearY 64
TheChrisyd 0:8a7c58553b44 36 #define invaderXspacing 16
TheChrisyd 0:8a7c58553b44 37 #define invaderYspacing 16
TheChrisyd 0:8a7c58553b44 38 #define invaderXstep 2
TheChrisyd 0:8a7c58553b44 39 #define invaderYstep 8
TheChrisyd 0:8a7c58553b44 40 #define invaderXmin 10
TheChrisyd 0:8a7c58553b44 41 #define invaderXmax 202
TheChrisyd 0:8a7c58553b44 42 // Saucer
TheChrisyd 0:8a7c58553b44 43 #define saucerYpos 42
TheChrisyd 0:8a7c58553b44 44 #define saucerSpeed 1
TheChrisyd 0:8a7c58553b44 45 #define saucerXmin 0
TheChrisyd 0:8a7c58553b44 46 #define saucerXmax (screenWidth-16)
TheChrisyd 0:8a7c58553b44 47 #define saucerSkip 3
TheChrisyd 0:8a7c58553b44 48 #define saucerFrequency (25*72)
TheChrisyd 0:8a7c58553b44 49 // Shields
TheChrisyd 0:8a7c58553b44 50 #define numShields 4
TheChrisyd 0:8a7c58553b44 51 #define shieldXpos 32
TheChrisyd 0:8a7c58553b44 52 #define shieldYpos 192
TheChrisyd 0:8a7c58553b44 53 #define shieldXstep 45
TheChrisyd 0:8a7c58553b44 54 // Bombs
TheChrisyd 0:8a7c58553b44 55 #define bombSpeed 1
TheChrisyd 0:8a7c58553b44 56 #define bombYmax 230
TheChrisyd 0:8a7c58553b44 57
TheChrisyd 0:8a7c58553b44 58 /*-------------------------------------------------------
TheChrisyd 0:8a7c58553b44 59 Sprite allocation list
TheChrisyd 0:8a7c58553b44 60
TheChrisyd 0:8a7c58553b44 61 nb. Sprite order is important for collision detection
TheChrisyd 0:8a7c58553b44 62 -------------------------------------------------------*/
TheChrisyd 0:8a7c58553b44 63 enum sprite_id {
TheChrisyd 0:8a7c58553b44 64 SP_PLAYER,
TheChrisyd 0:8a7c58553b44 65 SP_FIRST_SHIELD,
TheChrisyd 0:8a7c58553b44 66 SP_LAST_SHIELD = SP_FIRST_SHIELD+(2*numShields)-1,
TheChrisyd 0:8a7c58553b44 67 // Invader bombs (can hit shields and player)
TheChrisyd 0:8a7c58553b44 68 SP_BOMB1, // nb. There's only three bombs in Space invaders...
TheChrisyd 0:8a7c58553b44 69 SP_BOMB2,
TheChrisyd 0:8a7c58553b44 70 SP_BOMB3,
TheChrisyd 0:8a7c58553b44 71 // Invaders (can't be hit by their own bombs)
TheChrisyd 0:8a7c58553b44 72 SP_FIRST_INVADER,
TheChrisyd 0:8a7c58553b44 73 SP_LAST_INVADER = SP_FIRST_INVADER+numInvaders-1,
TheChrisyd 0:8a7c58553b44 74 // Flying saucer (needs two sprites because it's very wide...)
TheChrisyd 0:8a7c58553b44 75 SP_SAUCER1,
TheChrisyd 0:8a7c58553b44 76 SP_SAUCER2,
TheChrisyd 0:8a7c58553b44 77 // Bullet (last ... because it can hit anything)
TheChrisyd 0:8a7c58553b44 78 SP_BULLET
TheChrisyd 0:8a7c58553b44 79 };
TheChrisyd 0:8a7c58553b44 80
TheChrisyd 0:8a7c58553b44 81
TheChrisyd 0:8a7c58553b44 82 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 83 Global vars
TheChrisyd 0:8a7c58553b44 84 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 85 // Joystick object
TheChrisyd 0:8a7c58553b44 86 Joystick joystick;
TheChrisyd 0:8a7c58553b44 87
TheChrisyd 0:8a7c58553b44 88 // This increments once per frame
TheChrisyd 0:8a7c58553b44 89 static unsigned int frameCounter;
TheChrisyd 0:8a7c58553b44 90
TheChrisyd 0:8a7c58553b44 91 // The current wave of invaders [0..n]
TheChrisyd 0:8a7c58553b44 92 static unsigned int invaderWave;
TheChrisyd 0:8a7c58553b44 93
TheChrisyd 0:8a7c58553b44 94 // Number of lives the player has left...
TheChrisyd 0:8a7c58553b44 95 static byte numLives;
TheChrisyd 0:8a7c58553b44 96
TheChrisyd 0:8a7c58553b44 97 // Player's score...
TheChrisyd 0:8a7c58553b44 98 static unsigned int playerScore;
TheChrisyd 0:8a7c58553b44 99
TheChrisyd 0:8a7c58553b44 100 // High score
TheChrisyd 0:8a7c58553b44 101 static unsigned int highScore;
TheChrisyd 0:8a7c58553b44 102
TheChrisyd 0:8a7c58553b44 103 // Number of living space invaders
TheChrisyd 0:8a7c58553b44 104 static unsigned int remainingInvaders;
TheChrisyd 0:8a7c58553b44 105
TheChrisyd 0:8a7c58553b44 106 // Timer for the background heartbeat sound
TheChrisyd 0:8a7c58553b44 107 static int beatCounter;
TheChrisyd 0:8a7c58553b44 108
TheChrisyd 0:8a7c58553b44 109 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 110 General functions
TheChrisyd 0:8a7c58553b44 111 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 112 static PROGMEM prog_char scoreMsg[] = "Score";
TheChrisyd 0:8a7c58553b44 113 static PROGMEM prog_char hiScoreMsg[] = "Hi-Score";
TheChrisyd 0:8a7c58553b44 114 static unsigned int previousPlayerScore, previousHighScore;
TheChrisyd 0:8a7c58553b44 115 void redrawScores()
TheChrisyd 0:8a7c58553b44 116 {
TheChrisyd 0:8a7c58553b44 117 previousPlayerScore = previousHighScore = 0xffff;
TheChrisyd 0:8a7c58553b44 118 }
TheChrisyd 0:8a7c58553b44 119
TheChrisyd 0:8a7c58553b44 120 unsigned int putDigit(unsigned int s, unsigned int d)
TheChrisyd 0:8a7c58553b44 121 {
TheChrisyd 0:8a7c58553b44 122 byte c = '0';
TheChrisyd 0:8a7c58553b44 123 while (s >= d) {
TheChrisyd 0:8a7c58553b44 124 ++c;
TheChrisyd 0:8a7c58553b44 125 s -= d;
TheChrisyd 0:8a7c58553b44 126 }
TheChrisyd 0:8a7c58553b44 127 spigame.write(c);
TheChrisyd 0:8a7c58553b44 128 return s;
TheChrisyd 0:8a7c58553b44 129 }
TheChrisyd 0:8a7c58553b44 130 void printScore(int8 x, const prog_char *m, unsigned int s, int8 xoff)
TheChrisyd 0:8a7c58553b44 131 {
TheChrisyd 0:8a7c58553b44 132 x += screenLeft/8;
TheChrisyd 0:8a7c58553b44 133 int y = screenTop/8;
TheChrisyd 0:8a7c58553b44 134 unsigned int addr = (y*64)+x;
TheChrisyd 0:8a7c58553b44 135 GD.__wstart(addr);
TheChrisyd 0:8a7c58553b44 136 char c = *m;
TheChrisyd 4:bb78bedae411 137 c = *m++;
TheChrisyd 0:8a7c58553b44 138 while (c != 0) {
TheChrisyd 0:8a7c58553b44 139 spigame.write(c);
TheChrisyd 0:8a7c58553b44 140 c = *m++;
TheChrisyd 0:8a7c58553b44 141 }
TheChrisyd 0:8a7c58553b44 142 GD.__end();
TheChrisyd 0:8a7c58553b44 143 addr += (2*64)+xoff;
TheChrisyd 0:8a7c58553b44 144 GD.__wstart(addr);
TheChrisyd 0:8a7c58553b44 145 s = putDigit(s,10000);
TheChrisyd 0:8a7c58553b44 146 s = putDigit(s,1000);
TheChrisyd 0:8a7c58553b44 147 s = putDigit(s,100);
TheChrisyd 0:8a7c58553b44 148 s = putDigit(s,10);
TheChrisyd 0:8a7c58553b44 149 spigame.write(s+'0');
TheChrisyd 0:8a7c58553b44 150 GD.__end();
TheChrisyd 0:8a7c58553b44 151 }
TheChrisyd 0:8a7c58553b44 152 void updateScore()
TheChrisyd 0:8a7c58553b44 153 {
TheChrisyd 0:8a7c58553b44 154 if (playerScore != previousPlayerScore) {
TheChrisyd 0:8a7c58553b44 155 printScore(0,scoreMsg,playerScore,0);
TheChrisyd 0:8a7c58553b44 156 previousPlayerScore = playerScore;
TheChrisyd 0:8a7c58553b44 157 if (playerScore > highScore) {
TheChrisyd 0:8a7c58553b44 158 highScore = playerScore;
TheChrisyd 0:8a7c58553b44 159 }
TheChrisyd 0:8a7c58553b44 160 }
TheChrisyd 0:8a7c58553b44 161 if (highScore != previousHighScore) {
TheChrisyd 0:8a7c58553b44 162 printScore(20,hiScoreMsg,highScore,3);
TheChrisyd 0:8a7c58553b44 163 previousHighScore = highScore;
TheChrisyd 0:8a7c58553b44 164 }
TheChrisyd 0:8a7c58553b44 165 }
TheChrisyd 0:8a7c58553b44 166
TheChrisyd 0:8a7c58553b44 167 static unsigned short int prevLives;
TheChrisyd 0:8a7c58553b44 168 static void redrawBases()
TheChrisyd 0:8a7c58553b44 169 {
TheChrisyd 0:8a7c58553b44 170 prevLives = 0xffff;
TheChrisyd 0:8a7c58553b44 171 }
TheChrisyd 0:8a7c58553b44 172 void updateRemainingBases()
TheChrisyd 0:8a7c58553b44 173 {
TheChrisyd 0:8a7c58553b44 174 if (numLives != prevLives) {
TheChrisyd 0:8a7c58553b44 175 prevLives = numLives;
TheChrisyd 0:8a7c58553b44 176 GD.__wstart((64*((screenTop+240)>>3))+(screenLeft>>3));
TheChrisyd 0:8a7c58553b44 177 spigame.write(numLives+'0');
TheChrisyd 0:8a7c58553b44 178 spigame.write(0);
TheChrisyd 0:8a7c58553b44 179 for (byte i=1; i<numLives; ++i) {
TheChrisyd 0:8a7c58553b44 180 spigame.write(CH_PLAYERL);
TheChrisyd 0:8a7c58553b44 181 spigame.write(CH_PLAYERR);
TheChrisyd 0:8a7c58553b44 182 }
TheChrisyd 0:8a7c58553b44 183 spigame.write(0);
TheChrisyd 0:8a7c58553b44 184 spigame.write(0);
TheChrisyd 0:8a7c58553b44 185 GD.__end();
TheChrisyd 0:8a7c58553b44 186 }
TheChrisyd 0:8a7c58553b44 187 }
TheChrisyd 0:8a7c58553b44 188
TheChrisyd 0:8a7c58553b44 189 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 190 A generic object in the game
TheChrisyd 0:8a7c58553b44 191 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 192 enum object_status {
TheChrisyd 0:8a7c58553b44 193 S_WAITING,
TheChrisyd 0:8a7c58553b44 194 S_ALIVE,
TheChrisyd 0:8a7c58553b44 195 S_DYING,
TheChrisyd 0:8a7c58553b44 196 S_DEAD
TheChrisyd 0:8a7c58553b44 197 };
TheChrisyd 0:8a7c58553b44 198
TheChrisyd 0:8a7c58553b44 199 struct GameObject {
TheChrisyd 0:8a7c58553b44 200 byte sprite; // Which sprite to use for my graphic (see "sprite_id")
TheChrisyd 0:8a7c58553b44 201 byte status; // What I'm doing at the moment
TheChrisyd 0:8a7c58553b44 202 int xpos,ypos; // Position on screen
TheChrisyd 0:8a7c58553b44 203 // State of objects in the game
TheChrisyd 0:8a7c58553b44 204 void initialize(byte s, object_status t=S_WAITING, int x=400, int y=0) {
TheChrisyd 0:8a7c58553b44 205 sprite = s;
TheChrisyd 0:8a7c58553b44 206 status = t;
TheChrisyd 0:8a7c58553b44 207 xpos = x;
TheChrisyd 0:8a7c58553b44 208 ypos = y;
TheChrisyd 0:8a7c58553b44 209 updateSprite(0,0);
TheChrisyd 0:8a7c58553b44 210 }
TheChrisyd 0:8a7c58553b44 211 void updateSprite(byte img, byte frame) {
TheChrisyd 0:8a7c58553b44 212 GD.sprite(sprite,xpos+screenLeft,ypos+screenTop,img,8+(frame<<1),0,0);
TheChrisyd 0:8a7c58553b44 213 }
TheChrisyd 0:8a7c58553b44 214 void doubleSprite(byte img1, byte frame1, byte img2, byte frame2, int8 xoff) {
TheChrisyd 0:8a7c58553b44 215 int x = xpos+screenLeft+xoff;
TheChrisyd 0:8a7c58553b44 216 int y = ypos+screenTop;
TheChrisyd 0:8a7c58553b44 217 GD.sprite(sprite, x, y,img1,8+(frame1<<1),0,0);
TheChrisyd 0:8a7c58553b44 218 GD.sprite(sprite+1,x+16,y,img2,8+(frame2<<1),0,0);
TheChrisyd 0:8a7c58553b44 219 }
TheChrisyd 0:8a7c58553b44 220 byte collision() {
TheChrisyd 0:8a7c58553b44 221 return GD.rd(0x2900+sprite);
TheChrisyd 0:8a7c58553b44 222 }
TheChrisyd 0:8a7c58553b44 223 };
TheChrisyd 0:8a7c58553b44 224
TheChrisyd 0:8a7c58553b44 225 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 226 Player's bullet
TheChrisyd 0:8a7c58553b44 227 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 228 // Forward references to functions
TheChrisyd 0:8a7c58553b44 229 bool killInvader(byte spriteNumber);
TheChrisyd 0:8a7c58553b44 230 void shootShield(byte spriteNumber, int bulletX);
TheChrisyd 0:8a7c58553b44 231 void shootSaucer();
TheChrisyd 0:8a7c58553b44 232 void shootBomb(byte spriteNumber);
TheChrisyd 0:8a7c58553b44 233 void incSaucerCounter();
TheChrisyd 0:8a7c58553b44 234
TheChrisyd 0:8a7c58553b44 235 class BulletObject : GameObject {
TheChrisyd 0:8a7c58553b44 236 byte timer;
TheChrisyd 0:8a7c58553b44 237 bool visibleDeath;
TheChrisyd 0:8a7c58553b44 238 void die(bool v) {
TheChrisyd 0:8a7c58553b44 239 visibleDeath = v;
TheChrisyd 0:8a7c58553b44 240 status = S_DYING;
TheChrisyd 0:8a7c58553b44 241 timer = 12;
TheChrisyd 0:8a7c58553b44 242 }
TheChrisyd 0:8a7c58553b44 243 public:
TheChrisyd 0:8a7c58553b44 244 void reset() {
TheChrisyd 0:8a7c58553b44 245 initialize(SP_BULLET);
TheChrisyd 0:8a7c58553b44 246 updateSprite(GR_BULLET,3);
TheChrisyd 0:8a7c58553b44 247 timer = 0;
TheChrisyd 0:8a7c58553b44 248 }
TheChrisyd 0:8a7c58553b44 249 void fire(GameObject& p) {
TheChrisyd 0:8a7c58553b44 250 if (status == S_WAITING){
TheChrisyd 0:8a7c58553b44 251 status = S_ALIVE;
TheChrisyd 0:8a7c58553b44 252 xpos = p.xpos;
TheChrisyd 0:8a7c58553b44 253 ypos = p.ypos+bulletSpeed-bulletHeight;
TheChrisyd 0:8a7c58553b44 254 playerShootSound = true;
TheChrisyd 0:8a7c58553b44 255 }
TheChrisyd 0:8a7c58553b44 256 }
TheChrisyd 0:8a7c58553b44 257 void update() {
TheChrisyd 0:8a7c58553b44 258 int frame = 3;
TheChrisyd 0:8a7c58553b44 259 switch (status) {
TheChrisyd 0:8a7c58553b44 260 case S_ALIVE: ypos -= bulletSpeed;
TheChrisyd 0:8a7c58553b44 261 if (ypos <= bulletTop) {
TheChrisyd 0:8a7c58553b44 262 ypos = bulletTop;
TheChrisyd 0:8a7c58553b44 263 die(true);
TheChrisyd 0:8a7c58553b44 264 frame = 1;
TheChrisyd 0:8a7c58553b44 265 }
TheChrisyd 0:8a7c58553b44 266 else {
TheChrisyd 0:8a7c58553b44 267 frame = 0;
TheChrisyd 0:8a7c58553b44 268 }
TheChrisyd 0:8a7c58553b44 269 break;
TheChrisyd 0:8a7c58553b44 270 case S_DYING: if (!--timer) {
TheChrisyd 0:8a7c58553b44 271 status = S_WAITING;
TheChrisyd 0:8a7c58553b44 272 incSaucerCounter();
TheChrisyd 0:8a7c58553b44 273 }
TheChrisyd 0:8a7c58553b44 274 else if (visibleDeath) {
TheChrisyd 0:8a7c58553b44 275 frame = 1;
TheChrisyd 0:8a7c58553b44 276 }
TheChrisyd 0:8a7c58553b44 277 break;
TheChrisyd 0:8a7c58553b44 278 }
TheChrisyd 0:8a7c58553b44 279 updateSprite(GR_BULLET,frame);
TheChrisyd 0:8a7c58553b44 280 }
TheChrisyd 0:8a7c58553b44 281 void setY(int y) {
TheChrisyd 0:8a7c58553b44 282 if (status == S_DYING) {
TheChrisyd 0:8a7c58553b44 283 ypos = y;
TheChrisyd 0:8a7c58553b44 284 updateSprite(GR_BULLET,1);
TheChrisyd 2:20a89dc286d5 285 //GD.wr16(SCROLL_Y,GD.rd16(SCROLL_Y)+1);
TheChrisyd 0:8a7c58553b44 286 }
TheChrisyd 0:8a7c58553b44 287 }
TheChrisyd 0:8a7c58553b44 288 // See if the bullet hit anything
TheChrisyd 0:8a7c58553b44 289 void collide() {
TheChrisyd 0:8a7c58553b44 290 if (status == S_ALIVE) {
TheChrisyd 0:8a7c58553b44 291 byte b = collision();
TheChrisyd 0:8a7c58553b44 292 if (b != 0xff) {
TheChrisyd 0:8a7c58553b44 293 if ((b >= SP_FIRST_INVADER) and (b <= SP_LAST_INVADER)) {
TheChrisyd 0:8a7c58553b44 294 if (killInvader(b)) {
TheChrisyd 0:8a7c58553b44 295 die(false);
TheChrisyd 0:8a7c58553b44 296 }
TheChrisyd 0:8a7c58553b44 297 }
TheChrisyd 0:8a7c58553b44 298 if ((b >= SP_FIRST_SHIELD) and (b <= SP_LAST_SHIELD)) {
TheChrisyd 0:8a7c58553b44 299 shootShield(b,xpos);
TheChrisyd 0:8a7c58553b44 300 die(true);
TheChrisyd 0:8a7c58553b44 301 }
TheChrisyd 0:8a7c58553b44 302 if ((b >= SP_SAUCER1) and (b <= SP_SAUCER2)) {
TheChrisyd 0:8a7c58553b44 303 shootSaucer();
TheChrisyd 0:8a7c58553b44 304 die(false);
TheChrisyd 0:8a7c58553b44 305 }
TheChrisyd 0:8a7c58553b44 306 if ((b >= SP_BOMB1) and (b <= SP_BOMB3)) {
TheChrisyd 0:8a7c58553b44 307 shootBomb(b);
TheChrisyd 0:8a7c58553b44 308 die(false);
TheChrisyd 0:8a7c58553b44 309 }
TheChrisyd 0:8a7c58553b44 310 }
TheChrisyd 0:8a7c58553b44 311 }
TheChrisyd 0:8a7c58553b44 312 }
TheChrisyd 0:8a7c58553b44 313 } bullet;
TheChrisyd 0:8a7c58553b44 314
TheChrisyd 0:8a7c58553b44 315 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 316 The player
TheChrisyd 0:8a7c58553b44 317 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 318 class Player : public GameObject {
TheChrisyd 0:8a7c58553b44 319 byte timer;
TheChrisyd 0:8a7c58553b44 320 public:
TheChrisyd 0:8a7c58553b44 321 void reset() {
TheChrisyd 0:8a7c58553b44 322 timer = 2*numInvaders;
TheChrisyd 0:8a7c58553b44 323 initialize(SP_PLAYER,S_WAITING,playerMinLeft,playerYpos);
TheChrisyd 0:8a7c58553b44 324 updateSprite(GR_PLAYER,3);
TheChrisyd 0:8a7c58553b44 325 }
TheChrisyd 0:8a7c58553b44 326
TheChrisyd 0:8a7c58553b44 327 void update() {
TheChrisyd 0:8a7c58553b44 328 int frame = 3;
TheChrisyd 0:8a7c58553b44 329 switch (status) {
TheChrisyd 0:8a7c58553b44 330 case S_WAITING: xpos = playerMinLeft;
TheChrisyd 0:8a7c58553b44 331 ypos = playerYpos;
TheChrisyd 0:8a7c58553b44 332 if (!--timer) {
TheChrisyd 0:8a7c58553b44 333 status = S_ALIVE;
TheChrisyd 0:8a7c58553b44 334 }
TheChrisyd 0:8a7c58553b44 335 break;
TheChrisyd 0:8a7c58553b44 336 case S_ALIVE: if (joystick.left()) {
TheChrisyd 0:8a7c58553b44 337 xpos -= playerSpeed;
TheChrisyd 0:8a7c58553b44 338 if (xpos < playerMinLeft) {
TheChrisyd 0:8a7c58553b44 339 xpos = playerMinLeft;
TheChrisyd 0:8a7c58553b44 340 }
TheChrisyd 0:8a7c58553b44 341 }
TheChrisyd 0:8a7c58553b44 342 if (joystick.right()) {
TheChrisyd 0:8a7c58553b44 343 xpos += playerSpeed;
TheChrisyd 0:8a7c58553b44 344 if (xpos > playerMaxRight) {
TheChrisyd 0:8a7c58553b44 345 xpos = playerMaxRight;
TheChrisyd 0:8a7c58553b44 346 }
TheChrisyd 0:8a7c58553b44 347 }
TheChrisyd 0:8a7c58553b44 348 { byte n = Joystick::buttonA|Joystick::buttonB;
TheChrisyd 0:8a7c58553b44 349 if (joystick.isPressed(n) and joystick.changed(n)) {
TheChrisyd 0:8a7c58553b44 350 bullet.fire(*this);
TheChrisyd 0:8a7c58553b44 351 }
TheChrisyd 0:8a7c58553b44 352 }
TheChrisyd 0:8a7c58553b44 353 frame = 0;
TheChrisyd 0:8a7c58553b44 354 break;
TheChrisyd 0:8a7c58553b44 355 case S_DYING: if (!--timer) {
TheChrisyd 0:8a7c58553b44 356 timer = 3*remainingInvaders;
TheChrisyd 0:8a7c58553b44 357 status = (--numLives>0)? S_WAITING: S_DEAD;
TheChrisyd 0:8a7c58553b44 358 }
TheChrisyd 0:8a7c58553b44 359 else {
TheChrisyd 0:8a7c58553b44 360 frame = ((frameCounter&4)==0)? 1:2;
TheChrisyd 0:8a7c58553b44 361 }
TheChrisyd 0:8a7c58553b44 362 break;
TheChrisyd 0:8a7c58553b44 363 }
TheChrisyd 0:8a7c58553b44 364 updateSprite(GR_PLAYER,frame);
TheChrisyd 0:8a7c58553b44 365 }
TheChrisyd 0:8a7c58553b44 366 void kill() {
TheChrisyd 0:8a7c58553b44 367 if (status == S_ALIVE) {
TheChrisyd 0:8a7c58553b44 368 status = S_DYING;
TheChrisyd 0:8a7c58553b44 369 timer = 50;
TheChrisyd 0:8a7c58553b44 370 playerDeathSound = true;
TheChrisyd 0:8a7c58553b44 371 }
TheChrisyd 0:8a7c58553b44 372 }
TheChrisyd 0:8a7c58553b44 373 bool isAlive() {
TheChrisyd 0:8a7c58553b44 374 return (status==S_ALIVE);
TheChrisyd 0:8a7c58553b44 375 }
TheChrisyd 0:8a7c58553b44 376 bool isDying() {
TheChrisyd 0:8a7c58553b44 377 return (status==S_DYING);
TheChrisyd 0:8a7c58553b44 378 }
TheChrisyd 0:8a7c58553b44 379 bool isDead() {
TheChrisyd 0:8a7c58553b44 380 return (status==S_DEAD);
TheChrisyd 0:8a7c58553b44 381 }
TheChrisyd 0:8a7c58553b44 382 void wakeUp() {
TheChrisyd 0:8a7c58553b44 383 }
TheChrisyd 0:8a7c58553b44 384 } player;
TheChrisyd 0:8a7c58553b44 385
TheChrisyd 0:8a7c58553b44 386 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 387 "Shields" for the player to hide behind
TheChrisyd 0:8a7c58553b44 388 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 389 class Shields {
TheChrisyd 0:8a7c58553b44 390 struct BlastInfo {
TheChrisyd 0:8a7c58553b44 391 byte sprite;
TheChrisyd 0:8a7c58553b44 392 int xpos;
TheChrisyd 0:8a7c58553b44 393 void reset() {
TheChrisyd 0:8a7c58553b44 394 sprite = 255;
TheChrisyd 0:8a7c58553b44 395 }
TheChrisyd 0:8a7c58553b44 396 bool hasBlast() const {
TheChrisyd 0:8a7c58553b44 397 return (sprite!=255);
TheChrisyd 0:8a7c58553b44 398 }
TheChrisyd 0:8a7c58553b44 399 void blast(byte s, int x) {
TheChrisyd 0:8a7c58553b44 400 sprite = s;
TheChrisyd 0:8a7c58553b44 401 xpos = x;
TheChrisyd 0:8a7c58553b44 402 }
TheChrisyd 0:8a7c58553b44 403 };
TheChrisyd 0:8a7c58553b44 404 BlastInfo bulletBlast, bombBlast[3];
TheChrisyd 0:8a7c58553b44 405 void blastShield(BlastInfo& n, bool asBullet) {
TheChrisyd 0:8a7c58553b44 406 if (n.hasBlast()) {
TheChrisyd 4:bb78bedae411 407 int8_t s = (n.sprite-SP_FIRST_SHIELD)>>1;
TheChrisyd 4:bb78bedae411 408 int8_t x = int8(n.xpos-(shieldXpos+(s*shieldXstep)));
TheChrisyd 4:bb78bedae411 409 int8_t y = zapShield(s,x,asBullet);
TheChrisyd 0:8a7c58553b44 410 if (asBullet) {
TheChrisyd 4:bb78bedae411 411 bullet.setY(shieldYpos+y);
TheChrisyd 0:8a7c58553b44 412 }
TheChrisyd 0:8a7c58553b44 413 n.reset();
TheChrisyd 0:8a7c58553b44 414 }
TheChrisyd 0:8a7c58553b44 415 }
TheChrisyd 0:8a7c58553b44 416 public:
TheChrisyd 0:8a7c58553b44 417 void reset() {
TheChrisyd 0:8a7c58553b44 418 remakeShields();
TheChrisyd 0:8a7c58553b44 419 int x = shieldXpos;
TheChrisyd 0:8a7c58553b44 420 byte s = SP_FIRST_SHIELD;
TheChrisyd 0:8a7c58553b44 421 for (int i=0; i<numShields; ++i) {
TheChrisyd 0:8a7c58553b44 422 GD.sprite(s, x+screenLeft, shieldYpos+screenTop,GR_SHIELD1+i,8+0,0,0);
TheChrisyd 0:8a7c58553b44 423 GD.sprite(s+1,x+screenLeft+16,shieldYpos+screenTop,GR_SHIELD1+i,8+2,0,0);
TheChrisyd 0:8a7c58553b44 424 x += shieldXstep;
TheChrisyd 0:8a7c58553b44 425 s += 2;
TheChrisyd 0:8a7c58553b44 426 }
TheChrisyd 0:8a7c58553b44 427 bulletBlast.reset();
TheChrisyd 0:8a7c58553b44 428 for (int8 i=0; i<3; ++i) {
TheChrisyd 0:8a7c58553b44 429 bombBlast[i].reset();
TheChrisyd 0:8a7c58553b44 430 }
TheChrisyd 0:8a7c58553b44 431 }
TheChrisyd 0:8a7c58553b44 432 void update() {
TheChrisyd 0:8a7c58553b44 433 blastShield(bulletBlast,true);
TheChrisyd 0:8a7c58553b44 434 for (int8 i=0; i<3; ++i) {
TheChrisyd 0:8a7c58553b44 435 blastShield(bombBlast[i],false);
TheChrisyd 0:8a7c58553b44 436 }
TheChrisyd 0:8a7c58553b44 437 }
TheChrisyd 0:8a7c58553b44 438 // Zap them in various ways
TheChrisyd 0:8a7c58553b44 439 // nb. We defer the action because updating the sprites
TheChrisyd 0:8a7c58553b44 440 // might be slow and we want to make sure all collision
TheChrisyd 0:8a7c58553b44 441 // detection happens in the vertical blank
TheChrisyd 0:8a7c58553b44 442 void shoot(byte s, int x) {
TheChrisyd 0:8a7c58553b44 443 bulletBlast.blast(s,x);
TheChrisyd 0:8a7c58553b44 444 }
TheChrisyd 0:8a7c58553b44 445 void bomb(byte s, int x) {
TheChrisyd 0:8a7c58553b44 446 for (int8 i=0; i<3; ++i) {
TheChrisyd 0:8a7c58553b44 447 BlastInfo& b = bombBlast[i];
TheChrisyd 0:8a7c58553b44 448 if (!b.hasBlast()) {
TheChrisyd 0:8a7c58553b44 449 b.blast(s,x);
TheChrisyd 0:8a7c58553b44 450 break;
TheChrisyd 0:8a7c58553b44 451 }
TheChrisyd 0:8a7c58553b44 452 }
TheChrisyd 0:8a7c58553b44 453 }
TheChrisyd 0:8a7c58553b44 454 } shields;
TheChrisyd 0:8a7c58553b44 455 void shootShield(byte sprite, int bulletX)
TheChrisyd 0:8a7c58553b44 456 {
TheChrisyd 0:8a7c58553b44 457 shields.shoot(sprite,bulletX);
TheChrisyd 0:8a7c58553b44 458 }
TheChrisyd 0:8a7c58553b44 459
TheChrisyd 0:8a7c58553b44 460 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 461 Flying saucer
TheChrisyd 0:8a7c58553b44 462
TheChrisyd 0:8a7c58553b44 463 The score for the saucer depends on how
TheChrisyd 0:8a7c58553b44 464 many bullets you've fired. If you want
TheChrisyd 0:8a7c58553b44 465 a good score hit it with bullet 22 and
TheChrisyd 0:8a7c58553b44 466 every 15th bullet after that.
TheChrisyd 0:8a7c58553b44 467
TheChrisyd 0:8a7c58553b44 468 The direction of the saucer also depends
TheChrisyd 0:8a7c58553b44 469 on the bullet count. If you're counting
TheChrisyd 0:8a7c58553b44 470 bullets then note the the saucer will
TheChrisyd 0:8a7c58553b44 471 appear on alternate sides and you can
TheChrisyd 0:8a7c58553b44 472 be ready for it.
TheChrisyd 0:8a7c58553b44 473
TheChrisyd 0:8a7c58553b44 474 Repeat after me: There are NO random
TheChrisyd 0:8a7c58553b44 475 numbers in Space Invaders.
TheChrisyd 0:8a7c58553b44 476 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 477 static PROGMEM prog_uchar saucerScores[15] = {
TheChrisyd 0:8a7c58553b44 478 // nb. There's only one '300' here...
TheChrisyd 0:8a7c58553b44 479 10,5,10,15,10,10,5,30,10,10,10,5,15,10,5
TheChrisyd 0:8a7c58553b44 480 };
TheChrisyd 0:8a7c58553b44 481 class Saucer : GameObject {
TheChrisyd 0:8a7c58553b44 482 byte timer, scoreTimer;
TheChrisyd 0:8a7c58553b44 483 byte score;
TheChrisyd 0:8a7c58553b44 484 byte bulletCounter;
TheChrisyd 0:8a7c58553b44 485 unsigned int timeUntilNextSaucer;
TheChrisyd 0:8a7c58553b44 486 bool leftRight,goingRight,showingScore;
TheChrisyd 0:8a7c58553b44 487 void startWaiting() {
TheChrisyd 0:8a7c58553b44 488 status = S_WAITING;
TheChrisyd 0:8a7c58553b44 489 timeUntilNextSaucer = saucerFrequency;
TheChrisyd 0:8a7c58553b44 490 }
TheChrisyd 0:8a7c58553b44 491 public:
TheChrisyd 0:8a7c58553b44 492 void reset() {
TheChrisyd 0:8a7c58553b44 493 initialize(SP_SAUCER1);
TheChrisyd 0:8a7c58553b44 494 timer = 1;
TheChrisyd 0:8a7c58553b44 495 ypos = saucerYpos;
TheChrisyd 0:8a7c58553b44 496 showingScore = false;
TheChrisyd 0:8a7c58553b44 497 bulletCounter = 0;
TheChrisyd 0:8a7c58553b44 498 leftRight = true;
TheChrisyd 0:8a7c58553b44 499 timeUntilNextSaucer = saucerFrequency;
TheChrisyd 0:8a7c58553b44 500 }
TheChrisyd 0:8a7c58553b44 501 void update() {
TheChrisyd 0:8a7c58553b44 502 int xoff=0;
TheChrisyd 0:8a7c58553b44 503 byte gr1=GR_SAUCER, gr2=gr1;
TheChrisyd 0:8a7c58553b44 504 byte fr1=3, fr2=fr1; // Blank sprite
TheChrisyd 0:8a7c58553b44 505 switch (status) {
TheChrisyd 0:8a7c58553b44 506 case S_WAITING: if ((remainingInvaders>7) and !--timeUntilNextSaucer) {
TheChrisyd 0:8a7c58553b44 507 status = S_ALIVE;
TheChrisyd 0:8a7c58553b44 508 timer = saucerSkip;
TheChrisyd 0:8a7c58553b44 509 goingRight = leftRight;
TheChrisyd 0:8a7c58553b44 510 if (goingRight) {
TheChrisyd 0:8a7c58553b44 511 xpos = saucerXmin-saucerSpeed;
TheChrisyd 0:8a7c58553b44 512 }
TheChrisyd 0:8a7c58553b44 513 else {
TheChrisyd 0:8a7c58553b44 514 xpos = saucerXmax+saucerSpeed;
TheChrisyd 0:8a7c58553b44 515 }
TheChrisyd 0:8a7c58553b44 516 saucerSound = true;
TheChrisyd 0:8a7c58553b44 517 }
TheChrisyd 0:8a7c58553b44 518 else {
TheChrisyd 0:8a7c58553b44 519 stopSaucerSnd = true;
TheChrisyd 0:8a7c58553b44 520 }
TheChrisyd 0:8a7c58553b44 521 break;
TheChrisyd 0:8a7c58553b44 522 case S_ALIVE: if (!--timer) {
TheChrisyd 0:8a7c58553b44 523 // The player has to go faster then the saucer so we skip frames...
TheChrisyd 0:8a7c58553b44 524 timer = saucerSkip;
TheChrisyd 0:8a7c58553b44 525 }
TheChrisyd 0:8a7c58553b44 526 else {
TheChrisyd 0:8a7c58553b44 527 if (goingRight) {
TheChrisyd 0:8a7c58553b44 528 xpos += saucerSpeed;
TheChrisyd 0:8a7c58553b44 529 if (xpos > saucerXmax) {
TheChrisyd 0:8a7c58553b44 530 startWaiting();
TheChrisyd 0:8a7c58553b44 531 }
TheChrisyd 0:8a7c58553b44 532 }
TheChrisyd 0:8a7c58553b44 533 else {
TheChrisyd 0:8a7c58553b44 534 xpos -= saucerSpeed;
TheChrisyd 0:8a7c58553b44 535 if (xpos < saucerXmin) {
TheChrisyd 0:8a7c58553b44 536 startWaiting();
TheChrisyd 0:8a7c58553b44 537 }
TheChrisyd 0:8a7c58553b44 538 }
TheChrisyd 0:8a7c58553b44 539 }
TheChrisyd 0:8a7c58553b44 540 fr1 = 0; // Normal saucer
TheChrisyd 0:8a7c58553b44 541 break;
TheChrisyd 0:8a7c58553b44 542 case S_DYING: if (!--timer) {
TheChrisyd 0:8a7c58553b44 543 if (showingScore) {
TheChrisyd 0:8a7c58553b44 544 startWaiting();
TheChrisyd 0:8a7c58553b44 545 }
TheChrisyd 0:8a7c58553b44 546 else {
TheChrisyd 0:8a7c58553b44 547 timer = 60;
TheChrisyd 0:8a7c58553b44 548 showingScore = true;
TheChrisyd 0:8a7c58553b44 549 playerScore += score*10;
TheChrisyd 0:8a7c58553b44 550 }
TheChrisyd 0:8a7c58553b44 551 }
TheChrisyd 0:8a7c58553b44 552 else {
TheChrisyd 0:8a7c58553b44 553 if (showingScore) {
TheChrisyd 0:8a7c58553b44 554 xoff = -5;
TheChrisyd 0:8a7c58553b44 555 gr1 = GR_SAUCER_SCORE;
TheChrisyd 0:8a7c58553b44 556 gr2 = GR_BULLET; fr2 = 2;
TheChrisyd 0:8a7c58553b44 557 if (score == 5) { fr1=0; xoff-=4;}
TheChrisyd 0:8a7c58553b44 558 else if (score == 10) { fr1 = 1; }
TheChrisyd 0:8a7c58553b44 559 else if (score == 15) { fr1 = 2; }
TheChrisyd 0:8a7c58553b44 560 else if (score == 30) { fr1 = 3; }
TheChrisyd 0:8a7c58553b44 561 }
TheChrisyd 0:8a7c58553b44 562 else {
TheChrisyd 0:8a7c58553b44 563 fr1 = 1; // Explosion left
TheChrisyd 0:8a7c58553b44 564 fr2 = 2; // Explosion right
TheChrisyd 0:8a7c58553b44 565 xoff = -5; // Move it a bit to the left
TheChrisyd 0:8a7c58553b44 566 }
TheChrisyd 0:8a7c58553b44 567 }
TheChrisyd 0:8a7c58553b44 568 break;
TheChrisyd 0:8a7c58553b44 569 }
TheChrisyd 0:8a7c58553b44 570 // Saucer sometimes needs two sprites...
TheChrisyd 0:8a7c58553b44 571 doubleSprite(gr1,fr1,gr2,fr2,xoff);
TheChrisyd 0:8a7c58553b44 572 }
TheChrisyd 0:8a7c58553b44 573 void incCounter() {
TheChrisyd 0:8a7c58553b44 574 if (++bulletCounter == 15) {
TheChrisyd 0:8a7c58553b44 575 bulletCounter = 0;
TheChrisyd 0:8a7c58553b44 576 }
TheChrisyd 0:8a7c58553b44 577 leftRight = !leftRight;
TheChrisyd 0:8a7c58553b44 578 }
TheChrisyd 0:8a7c58553b44 579 void kill() {
TheChrisyd 0:8a7c58553b44 580 status = S_DYING;
TheChrisyd 0:8a7c58553b44 581 timer = 36;
TheChrisyd 0:8a7c58553b44 582 saucerDieSound = true;
TheChrisyd 0:8a7c58553b44 583 showingScore = false;
TheChrisyd 0:8a7c58553b44 584 score = *saucerScores+bulletCounter;
TheChrisyd 0:8a7c58553b44 585 }
TheChrisyd 0:8a7c58553b44 586 } saucer;
TheChrisyd 0:8a7c58553b44 587
TheChrisyd 0:8a7c58553b44 588 void incSaucerCounter()
TheChrisyd 0:8a7c58553b44 589 {
TheChrisyd 0:8a7c58553b44 590 saucer.incCounter();
TheChrisyd 0:8a7c58553b44 591 }
TheChrisyd 0:8a7c58553b44 592 void shootSaucer()
TheChrisyd 0:8a7c58553b44 593 {
TheChrisyd 0:8a7c58553b44 594 saucer.kill();
TheChrisyd 0:8a7c58553b44 595 }
TheChrisyd 0:8a7c58553b44 596 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 597 A space invader...
TheChrisyd 0:8a7c58553b44 598 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 599 enum invader_type {
TheChrisyd 0:8a7c58553b44 600 INVADER_T, // Top-row invader
TheChrisyd 0:8a7c58553b44 601 INVADER_M, // Middle-row invader
TheChrisyd 0:8a7c58553b44 602 INVADER_B, // Bottom-row invader
TheChrisyd 0:8a7c58553b44 603 NUM_INVADER_TYPES
TheChrisyd 0:8a7c58553b44 604 };
TheChrisyd 0:8a7c58553b44 605 static PROGMEM prog_uchar invaderGraphic[NUM_INVADER_TYPES] = {
TheChrisyd 0:8a7c58553b44 606 GR_INVADER_T, GR_INVADER_M, GR_INVADER_B
TheChrisyd 0:8a7c58553b44 607 };
TheChrisyd 0:8a7c58553b44 608
TheChrisyd 0:8a7c58553b44 609 static PROGMEM prog_uchar invaderScore[NUM_INVADER_TYPES] = {
TheChrisyd 0:8a7c58553b44 610 30, 20, 10
TheChrisyd 0:8a7c58553b44 611 };
TheChrisyd 0:8a7c58553b44 612
TheChrisyd 0:8a7c58553b44 613 class Invader : public GameObject {
TheChrisyd 0:8a7c58553b44 614 // Bitmasks for my vars
TheChrisyd 0:8a7c58553b44 615 enum var_bits {
TheChrisyd 0:8a7c58553b44 616 TYPEMASK = 0x0003, // Type of invader, 0=top row, 1=middle row, 2=bottom row
TheChrisyd 0:8a7c58553b44 617 ANIM = 0x0010, // Flip-flop for animation frame
TheChrisyd 0:8a7c58553b44 618 GO_RIGHT = 0x0020, // Horizontal direction
TheChrisyd 0:8a7c58553b44 619 GO_DOWN = 0x0040, // If I should go downwards next time
TheChrisyd 0:8a7c58553b44 620 };
TheChrisyd 0:8a7c58553b44 621 byte vars; // All my vars, packed together
TheChrisyd 0:8a7c58553b44 622
TheChrisyd 0:8a7c58553b44 623 byte readTable(const prog_uchar *t) {
TheChrisyd 0:8a7c58553b44 624 return (*t + (vars&TYPEMASK));
TheChrisyd 0:8a7c58553b44 625 }
TheChrisyd 0:8a7c58553b44 626 void updateTheSprite() {
TheChrisyd 0:8a7c58553b44 627 byte img = readTable(invaderGraphic);
TheChrisyd 0:8a7c58553b44 628 byte fr = 3; // Invisible...
TheChrisyd 0:8a7c58553b44 629 switch (status) {
TheChrisyd 0:8a7c58553b44 630 case S_ALIVE: fr = (vars&ANIM)? 0:1; // Two frame animation
TheChrisyd 0:8a7c58553b44 631 break;
TheChrisyd 0:8a7c58553b44 632 case S_DYING: fr = 2; // Explosion graphic
TheChrisyd 0:8a7c58553b44 633 break;
TheChrisyd 0:8a7c58553b44 634 }
TheChrisyd 0:8a7c58553b44 635 updateSprite(img,fr);
TheChrisyd 0:8a7c58553b44 636 }
TheChrisyd 0:8a7c58553b44 637 public:
TheChrisyd 0:8a7c58553b44 638
TheChrisyd 0:8a7c58553b44 639 bool isAlive() const {
TheChrisyd 0:8a7c58553b44 640 return ((status==S_WAITING) or (status==S_ALIVE));
TheChrisyd 0:8a7c58553b44 641 }
TheChrisyd 0:8a7c58553b44 642 void goDown() {
TheChrisyd 0:8a7c58553b44 643 vars |= GO_DOWN;
TheChrisyd 0:8a7c58553b44 644 }
TheChrisyd 0:8a7c58553b44 645
TheChrisyd 0:8a7c58553b44 646 // Put me on screen at (x,y), set my type and sprite number.
TheChrisyd 0:8a7c58553b44 647 // I will be invisible and appear next frame (ie. when you call "update()")
TheChrisyd 0:8a7c58553b44 648 void reset(byte sp, int x, int y, invader_type t) {
TheChrisyd 0:8a7c58553b44 649 initialize(sp,S_WAITING,x,y);
TheChrisyd 0:8a7c58553b44 650 vars = t|GO_RIGHT;
TheChrisyd 0:8a7c58553b44 651 updateTheSprite();
TheChrisyd 0:8a7c58553b44 652 }
TheChrisyd 0:8a7c58553b44 653
TheChrisyd 0:8a7c58553b44 654 // Update me, return "true" if I reach the edge of the screen
TheChrisyd 0:8a7c58553b44 655 bool update() {
TheChrisyd 0:8a7c58553b44 656 bool hitTheEdge = false;
TheChrisyd 0:8a7c58553b44 657 switch (status) {
TheChrisyd 0:8a7c58553b44 658 case S_WAITING: status = S_ALIVE;
TheChrisyd 0:8a7c58553b44 659 break;
TheChrisyd 0:8a7c58553b44 660 case S_ALIVE: if (vars&GO_DOWN) {
TheChrisyd 0:8a7c58553b44 661 ypos += invaderYstep;
TheChrisyd 0:8a7c58553b44 662 vars &= ~GO_DOWN;
TheChrisyd 0:8a7c58553b44 663 vars ^= GO_RIGHT;
TheChrisyd 0:8a7c58553b44 664 }
TheChrisyd 0:8a7c58553b44 665 else {
TheChrisyd 0:8a7c58553b44 666 if (vars&GO_RIGHT) {
TheChrisyd 0:8a7c58553b44 667 xpos += invaderXstep;
TheChrisyd 0:8a7c58553b44 668 hitTheEdge = (xpos >= invaderXmax);
TheChrisyd 0:8a7c58553b44 669 }
TheChrisyd 0:8a7c58553b44 670 else {
TheChrisyd 0:8a7c58553b44 671 xpos -= invaderXstep;
TheChrisyd 0:8a7c58553b44 672 hitTheEdge = (xpos <= invaderXmin);
TheChrisyd 0:8a7c58553b44 673 }
TheChrisyd 0:8a7c58553b44 674 }
TheChrisyd 0:8a7c58553b44 675 vars = vars^ANIM; // Animation flipflop
TheChrisyd 0:8a7c58553b44 676 break;
TheChrisyd 0:8a7c58553b44 677 }
TheChrisyd 0:8a7c58553b44 678 updateTheSprite();
TheChrisyd 0:8a7c58553b44 679 return hitTheEdge;
TheChrisyd 0:8a7c58553b44 680 }
TheChrisyd 0:8a7c58553b44 681 bool die() {
TheChrisyd 0:8a7c58553b44 682 bool result = (status==S_ALIVE);
TheChrisyd 0:8a7c58553b44 683 if (result) {
TheChrisyd 0:8a7c58553b44 684 status = S_DYING;
TheChrisyd 0:8a7c58553b44 685 updateTheSprite();
TheChrisyd 0:8a7c58553b44 686 playerScore += readTable(invaderScore);
TheChrisyd 0:8a7c58553b44 687 alienDeathSound = true;
TheChrisyd 0:8a7c58553b44 688 }
TheChrisyd 0:8a7c58553b44 689 return result;
TheChrisyd 0:8a7c58553b44 690 }
TheChrisyd 0:8a7c58553b44 691 void kill() {
TheChrisyd 0:8a7c58553b44 692 status = S_DEAD;
TheChrisyd 0:8a7c58553b44 693 updateTheSprite();
TheChrisyd 0:8a7c58553b44 694 --remainingInvaders;
TheChrisyd 0:8a7c58553b44 695 }
TheChrisyd 0:8a7c58553b44 696 };
TheChrisyd 0:8a7c58553b44 697
TheChrisyd 0:8a7c58553b44 698 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 699 The array of invaders
TheChrisyd 0:8a7c58553b44 700 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 701 // Table for starting height of invaders on each level
TheChrisyd 0:8a7c58553b44 702 static PROGMEM prog_char invaderHeightTable[] = {
TheChrisyd 0:8a7c58553b44 703 1,2,3,3,3,4,4,4
TheChrisyd 0:8a7c58553b44 704 };
TheChrisyd 0:8a7c58553b44 705
TheChrisyd 0:8a7c58553b44 706 class InvaderList {
TheChrisyd 0:8a7c58553b44 707 byte nextInvader; // The invader to update on the next frame
TheChrisyd 0:8a7c58553b44 708 int dyingInvader; // Which invader is currently dying
TheChrisyd 0:8a7c58553b44 709 int8 deathTimer; // COuntdown during death phase
TheChrisyd 0:8a7c58553b44 710 bool anInvaderHitTheEdge; // When "true" the invaders should go down a line and change direction
TheChrisyd 0:8a7c58553b44 711 bool anInvaderReachedTheBottom;// When "true" an invader has landed... Game Over!
TheChrisyd 0:8a7c58553b44 712 Invader invader[numInvaders]; // The invaders
TheChrisyd 0:8a7c58553b44 713
TheChrisyd 0:8a7c58553b44 714 bool findNextLivingInvader() {
TheChrisyd 0:8a7c58553b44 715 // Find next living invader in the array
TheChrisyd 0:8a7c58553b44 716 bool foundOne = false;
TheChrisyd 0:8a7c58553b44 717 for (int8 i=0; i<numInvaders; ++i) {
TheChrisyd 0:8a7c58553b44 718 if (++nextInvader == numInvaders) {
TheChrisyd 0:8a7c58553b44 719 // Actions taken after all the invaders have moved
TheChrisyd 0:8a7c58553b44 720 nextInvader = 0;
TheChrisyd 0:8a7c58553b44 721 if (anInvaderHitTheEdge) {
TheChrisyd 0:8a7c58553b44 722 for (int8 j=0; j<numInvaders; ++j) {
TheChrisyd 0:8a7c58553b44 723 invader[j].goDown();
TheChrisyd 0:8a7c58553b44 724 }
TheChrisyd 0:8a7c58553b44 725 anInvaderHitTheEdge = false;
TheChrisyd 0:8a7c58553b44 726 }
TheChrisyd 0:8a7c58553b44 727 }
TheChrisyd 0:8a7c58553b44 728 if (invader[nextInvader].isAlive()) {
TheChrisyd 0:8a7c58553b44 729 foundOne = true;
TheChrisyd 0:8a7c58553b44 730 break;
TheChrisyd 0:8a7c58553b44 731 }
TheChrisyd 0:8a7c58553b44 732 }
TheChrisyd 0:8a7c58553b44 733 return foundOne;
TheChrisyd 0:8a7c58553b44 734 }
TheChrisyd 0:8a7c58553b44 735 public:
TheChrisyd 0:8a7c58553b44 736 void reset(int8 level) {
TheChrisyd 0:8a7c58553b44 737 int y = invaderAppearY+(invaderRows*invaderYspacing);
TheChrisyd 0:8a7c58553b44 738 if (invaderWave > 0) {
TheChrisyd 0:8a7c58553b44 739 char w = (*invaderHeightTable+((invaderWave-1)&7));
TheChrisyd 0:8a7c58553b44 740 y += w*invaderYstep;
TheChrisyd 0:8a7c58553b44 741 }
TheChrisyd 0:8a7c58553b44 742 for (int8 row=0; row<invaderRows; ++row) {
TheChrisyd 0:8a7c58553b44 743 int x = invaderAppearX;
TheChrisyd 0:8a7c58553b44 744 for (int8 col=0; col<invadersPerRow; ++col) {
TheChrisyd 0:8a7c58553b44 745 const int8 index = (row*invadersPerRow)+col;
TheChrisyd 0:8a7c58553b44 746 Invader& n = invader[index];
TheChrisyd 0:8a7c58553b44 747 invader_type t = INVADER_B;
TheChrisyd 0:8a7c58553b44 748 if (row > 1) { t = INVADER_M; }
TheChrisyd 0:8a7c58553b44 749 if (row > 3) { t = INVADER_T; }
TheChrisyd 0:8a7c58553b44 750 n.reset(SP_FIRST_INVADER+index,x,y,t);
TheChrisyd 0:8a7c58553b44 751 x += invaderXspacing;
TheChrisyd 0:8a7c58553b44 752 }
TheChrisyd 0:8a7c58553b44 753 y -= invaderYspacing;
TheChrisyd 0:8a7c58553b44 754 }
TheChrisyd 0:8a7c58553b44 755 remainingInvaders = numInvaders;
TheChrisyd 0:8a7c58553b44 756 nextInvader = 0; // Start updating them here...
TheChrisyd 0:8a7c58553b44 757 dyingInvader = -1;
TheChrisyd 0:8a7c58553b44 758 deathTimer = 0;
TheChrisyd 0:8a7c58553b44 759 anInvaderHitTheEdge = false;
TheChrisyd 0:8a7c58553b44 760 anInvaderReachedTheBottom = false;
TheChrisyd 0:8a7c58553b44 761 }
TheChrisyd 0:8a7c58553b44 762 void update() {
TheChrisyd 0:8a7c58553b44 763 if (dyingInvader != -1) {
TheChrisyd 0:8a7c58553b44 764 // We stop marching when an invader dies
TheChrisyd 0:8a7c58553b44 765 if (!--deathTimer) {
TheChrisyd 0:8a7c58553b44 766 invader[dyingInvader].kill();
TheChrisyd 0:8a7c58553b44 767 dyingInvader = -1;
TheChrisyd 0:8a7c58553b44 768 }
TheChrisyd 0:8a7c58553b44 769 }
TheChrisyd 0:8a7c58553b44 770 else if (!player.isDying() and (remainingInvaders>0)) {
TheChrisyd 0:8a7c58553b44 771 // Update an invader
TheChrisyd 0:8a7c58553b44 772 Invader& n = invader[nextInvader];
TheChrisyd 0:8a7c58553b44 773 if (n.isAlive()) {
TheChrisyd 0:8a7c58553b44 774 // Move the invader
TheChrisyd 0:8a7c58553b44 775 if (n.update()) {
TheChrisyd 0:8a7c58553b44 776 anInvaderHitTheEdge = true;
TheChrisyd 0:8a7c58553b44 777 }
TheChrisyd 0:8a7c58553b44 778 if ((n.ypos+8) > player.ypos) {
TheChrisyd 0:8a7c58553b44 779 anInvaderReachedTheBottom = true;
TheChrisyd 0:8a7c58553b44 780 }
TheChrisyd 0:8a7c58553b44 781 }
TheChrisyd 0:8a7c58553b44 782 findNextLivingInvader();
TheChrisyd 0:8a7c58553b44 783 }
TheChrisyd 0:8a7c58553b44 784 }
TheChrisyd 0:8a7c58553b44 785 // Kill the invader with sprite 'n'
TheChrisyd 0:8a7c58553b44 786 bool kill(byte n) {
TheChrisyd 0:8a7c58553b44 787 n -= SP_FIRST_INVADER;
TheChrisyd 0:8a7c58553b44 788 bool result = invader[n].die();
TheChrisyd 0:8a7c58553b44 789 if (result) {
TheChrisyd 0:8a7c58553b44 790 if (dyingInvader != -1) {
TheChrisyd 0:8a7c58553b44 791 invader[dyingInvader].kill();
TheChrisyd 0:8a7c58553b44 792 }
TheChrisyd 0:8a7c58553b44 793 dyingInvader = n;
TheChrisyd 0:8a7c58553b44 794 deathTimer = 16;
TheChrisyd 0:8a7c58553b44 795 }
TheChrisyd 0:8a7c58553b44 796 return result;
TheChrisyd 0:8a7c58553b44 797 }
TheChrisyd 0:8a7c58553b44 798 int nearestColumnToPlayer() {
TheChrisyd 0:8a7c58553b44 799 Invader& n = invader[nextInvader]; // We know this invader is alive so use it as a reference
TheChrisyd 0:8a7c58553b44 800 int r = nextInvader%invadersPerRow; // The column this invader is in
TheChrisyd 0:8a7c58553b44 801 int left = n.xpos-(r*invaderXspacing);
TheChrisyd 0:8a7c58553b44 802 int c = (((player.xpos-left)+(invaderXspacing/2))/invaderXspacing);
TheChrisyd 0:8a7c58553b44 803 if ((c>=0) and (c<invadersPerRow)) {
TheChrisyd 0:8a7c58553b44 804 return c;
TheChrisyd 0:8a7c58553b44 805 }
TheChrisyd 0:8a7c58553b44 806 return -1;
TheChrisyd 0:8a7c58553b44 807 }
TheChrisyd 0:8a7c58553b44 808 const Invader *getColumn(int c) {
TheChrisyd 0:8a7c58553b44 809 while ((c>=0) and (c<numInvaders)) {
TheChrisyd 0:8a7c58553b44 810 const Invader *v = invader+c;
TheChrisyd 0:8a7c58553b44 811 if (v->isAlive()) {
TheChrisyd 0:8a7c58553b44 812 return v;
TheChrisyd 0:8a7c58553b44 813 }
TheChrisyd 0:8a7c58553b44 814 c += invadersPerRow;
TheChrisyd 0:8a7c58553b44 815 }
TheChrisyd 0:8a7c58553b44 816 return 0;
TheChrisyd 0:8a7c58553b44 817 }
TheChrisyd 0:8a7c58553b44 818 bool haveLanded() {
TheChrisyd 0:8a7c58553b44 819 return anInvaderReachedTheBottom;
TheChrisyd 0:8a7c58553b44 820 }
TheChrisyd 0:8a7c58553b44 821 } invaders;
TheChrisyd 0:8a7c58553b44 822
TheChrisyd 0:8a7c58553b44 823 bool killInvader(byte n)
TheChrisyd 0:8a7c58553b44 824 {
TheChrisyd 0:8a7c58553b44 825 return invaders.kill(n);
TheChrisyd 0:8a7c58553b44 826 }
TheChrisyd 0:8a7c58553b44 827
TheChrisyd 0:8a7c58553b44 828 /*---------------------------------------------------------
TheChrisyd 0:8a7c58553b44 829 Space invader bombs
TheChrisyd 0:8a7c58553b44 830
TheChrisyd 0:8a7c58553b44 831 There's three bombs in Space Invaders. Two of them
TheChrisyd 0:8a7c58553b44 832 follow a pattern of columns, the other one always
TheChrisyd 0:8a7c58553b44 833 appears right above the player (to stop you getting
TheChrisyd 0:8a7c58553b44 834 bored...!)
TheChrisyd 0:8a7c58553b44 835
TheChrisyd 0:8a7c58553b44 836 Mantra: There are NO random numbers in Space Invaders...
TheChrisyd 0:8a7c58553b44 837
TheChrisyd 0:8a7c58553b44 838 nb. Column 1 is the most dangerous and column 5
TheChrisyd 0:8a7c58553b44 839 isn't in either table... :-)
TheChrisyd 0:8a7c58553b44 840 ---------------------------------------------------------*/
TheChrisyd 0:8a7c58553b44 841 // Column table for the 'zigzag' bomb
TheChrisyd 0:8a7c58553b44 842 static prog_char zigzagBombColumns[] = {
TheChrisyd 0:8a7c58553b44 843 11,1,6,3,1,1,11,9,2,8,2,11,4,7,10,-1
TheChrisyd 0:8a7c58553b44 844 };
TheChrisyd 0:8a7c58553b44 845 // Column table for the bomb with horizontal bars across it
TheChrisyd 0:8a7c58553b44 846 static prog_char barBombColumns[] = {
TheChrisyd 0:8a7c58553b44 847 1,7,1,1,1,4,11,1,6,3,1,1,11,9,2,8,-1
TheChrisyd 0:8a7c58553b44 848 };
TheChrisyd 0:8a7c58553b44 849 byte bombTimer; // Countdown until next bomb can be dropped
TheChrisyd 0:8a7c58553b44 850 void resetBombTimer()
TheChrisyd 0:8a7c58553b44 851 {
TheChrisyd 0:8a7c58553b44 852 if (!player.isAlive()) {
TheChrisyd 0:8a7c58553b44 853 bombTimer = 60; // We don't drop for this long after you reanimate
TheChrisyd 0:8a7c58553b44 854 }
TheChrisyd 0:8a7c58553b44 855 else {
TheChrisyd 0:8a7c58553b44 856 // You get more bombs as the game progresses :-)
TheChrisyd 0:8a7c58553b44 857 if (playerScore < 200) { bombTimer = 48; }
TheChrisyd 0:8a7c58553b44 858 else if (playerScore < 1000) { bombTimer = 16; }
TheChrisyd 0:8a7c58553b44 859 else if (playerScore < 2000) { bombTimer = 11; }
TheChrisyd 0:8a7c58553b44 860 else if (playerScore < 3000) { bombTimer = 8; }
TheChrisyd 0:8a7c58553b44 861 else { bombTimer = 7; }
TheChrisyd 0:8a7c58553b44 862 }
TheChrisyd 0:8a7c58553b44 863 }
TheChrisyd 0:8a7c58553b44 864 class Bomb : public GameObject {
TheChrisyd 0:8a7c58553b44 865 byte graphic;
TheChrisyd 0:8a7c58553b44 866 byte timer;
TheChrisyd 0:8a7c58553b44 867 byte cycle;
TheChrisyd 0:8a7c58553b44 868 prog_char *columnTable, *tablePtr;
TheChrisyd 0:8a7c58553b44 869 bool readyToDrop() {
TheChrisyd 0:8a7c58553b44 870 return (bombTimer==0);
TheChrisyd 0:8a7c58553b44 871 }
TheChrisyd 0:8a7c58553b44 872 int8 getNextColumn() {
TheChrisyd 0:8a7c58553b44 873 int c = *tablePtr;
TheChrisyd 0:8a7c58553b44 874 if (c == -1) {
TheChrisyd 0:8a7c58553b44 875 tablePtr = columnTable;
TheChrisyd 0:8a7c58553b44 876 c = *tablePtr;
TheChrisyd 0:8a7c58553b44 877 }
TheChrisyd 0:8a7c58553b44 878 else {
TheChrisyd 0:8a7c58553b44 879 ++tablePtr;
TheChrisyd 0:8a7c58553b44 880 }
TheChrisyd 0:8a7c58553b44 881 return c-1;
TheChrisyd 0:8a7c58553b44 882 }
TheChrisyd 0:8a7c58553b44 883 public:
TheChrisyd 0:8a7c58553b44 884 Bomb() {
TheChrisyd 0:8a7c58553b44 885 tablePtr = 0;
TheChrisyd 0:8a7c58553b44 886 }
TheChrisyd 0:8a7c58553b44 887 bool isAlive() {
TheChrisyd 0:8a7c58553b44 888 return (status!=S_WAITING);
TheChrisyd 0:8a7c58553b44 889 }
TheChrisyd 0:8a7c58553b44 890 void die() {
TheChrisyd 0:8a7c58553b44 891 status = S_DYING;
TheChrisyd 0:8a7c58553b44 892 timer = 12;
TheChrisyd 0:8a7c58553b44 893 }
TheChrisyd 0:8a7c58553b44 894 void reset(byte sprite, byte gr, prog_char *ct) {
TheChrisyd 0:8a7c58553b44 895 initialize(sprite);
TheChrisyd 0:8a7c58553b44 896 graphic = gr;
TheChrisyd 0:8a7c58553b44 897 columnTable = ct;
TheChrisyd 0:8a7c58553b44 898 if (!tablePtr) {
TheChrisyd 0:8a7c58553b44 899 tablePtr = ct; // Only set this the first time...
TheChrisyd 0:8a7c58553b44 900 }
TheChrisyd 0:8a7c58553b44 901 cycle = timer = 0;
TheChrisyd 0:8a7c58553b44 902 updateSprite(GR_BOMB_OTHER,3);
TheChrisyd 0:8a7c58553b44 903 }
TheChrisyd 0:8a7c58553b44 904 void update() {
TheChrisyd 0:8a7c58553b44 905 byte gr = GR_BOMB_OTHER;
TheChrisyd 0:8a7c58553b44 906 byte frame = 3;
TheChrisyd 0:8a7c58553b44 907 switch (status) {
TheChrisyd 0:8a7c58553b44 908 case S_WAITING: if (bombTimer == 0) {
TheChrisyd 0:8a7c58553b44 909 int c = -1;
TheChrisyd 0:8a7c58553b44 910 if (columnTable) {
TheChrisyd 0:8a7c58553b44 911 // Follow sequence of columns
TheChrisyd 0:8a7c58553b44 912 c = getNextColumn();
TheChrisyd 0:8a7c58553b44 913 }
TheChrisyd 0:8a7c58553b44 914 else {
TheChrisyd 0:8a7c58553b44 915 // Drop me above the player
TheChrisyd 0:8a7c58553b44 916 c = invaders.nearestColumnToPlayer();
TheChrisyd 0:8a7c58553b44 917 }
TheChrisyd 0:8a7c58553b44 918 const Invader *v = invaders.getColumn(c);
TheChrisyd 0:8a7c58553b44 919 if (v) {
TheChrisyd 0:8a7c58553b44 920 status = S_ALIVE;
TheChrisyd 0:8a7c58553b44 921 xpos = v->xpos;
TheChrisyd 0:8a7c58553b44 922 ypos = v->ypos+8;
TheChrisyd 0:8a7c58553b44 923 resetBombTimer();
TheChrisyd 0:8a7c58553b44 924 }
TheChrisyd 0:8a7c58553b44 925 }
TheChrisyd 0:8a7c58553b44 926 break;
TheChrisyd 0:8a7c58553b44 927 case S_ALIVE: ypos += bombSpeed;
TheChrisyd 0:8a7c58553b44 928 if (ypos > bombYmax) {
TheChrisyd 0:8a7c58553b44 929 ypos = bombYmax;
TheChrisyd 0:8a7c58553b44 930 die();
TheChrisyd 0:8a7c58553b44 931 }
TheChrisyd 0:8a7c58553b44 932 gr = graphic;
TheChrisyd 0:8a7c58553b44 933 if (++timer==2) {
TheChrisyd 0:8a7c58553b44 934 ++cycle;
TheChrisyd 0:8a7c58553b44 935 timer = 0;
TheChrisyd 0:8a7c58553b44 936 }
TheChrisyd 0:8a7c58553b44 937 frame = cycle&3;
TheChrisyd 0:8a7c58553b44 938 break;
TheChrisyd 0:8a7c58553b44 939 case S_DYING: if (!--timer) {
TheChrisyd 0:8a7c58553b44 940 status = S_WAITING;
TheChrisyd 0:8a7c58553b44 941 }
TheChrisyd 0:8a7c58553b44 942 else {
TheChrisyd 0:8a7c58553b44 943 frame = 0; // Bomb blast graphic
TheChrisyd 0:8a7c58553b44 944 }
TheChrisyd 0:8a7c58553b44 945 break;
TheChrisyd 0:8a7c58553b44 946 }
TheChrisyd 0:8a7c58553b44 947 updateSprite(gr,frame);
TheChrisyd 0:8a7c58553b44 948 }
TheChrisyd 0:8a7c58553b44 949 void collide() {
TheChrisyd 0:8a7c58553b44 950 if (status==S_ALIVE) {
TheChrisyd 0:8a7c58553b44 951 byte b = collision();
TheChrisyd 0:8a7c58553b44 952 if (b == SP_PLAYER) {
TheChrisyd 0:8a7c58553b44 953 player.kill();
TheChrisyd 0:8a7c58553b44 954 status = S_DYING;
TheChrisyd 0:8a7c58553b44 955 }
TheChrisyd 0:8a7c58553b44 956 if ((b>=SP_FIRST_SHIELD) and (b<=SP_LAST_SHIELD)) {
TheChrisyd 0:8a7c58553b44 957 shields.bomb(b,xpos);
TheChrisyd 0:8a7c58553b44 958 die();
TheChrisyd 0:8a7c58553b44 959 }
TheChrisyd 0:8a7c58553b44 960 }
TheChrisyd 0:8a7c58553b44 961 }
TheChrisyd 0:8a7c58553b44 962 };
TheChrisyd 0:8a7c58553b44 963
TheChrisyd 0:8a7c58553b44 964 class Bombs {
TheChrisyd 0:8a7c58553b44 965 Bomb zigzag,bar,diag;
TheChrisyd 0:8a7c58553b44 966 public:
TheChrisyd 0:8a7c58553b44 967 void reset() {
TheChrisyd 0:8a7c58553b44 968 resetBombTimer();
TheChrisyd 0:8a7c58553b44 969 prog_char* bombptr = zigzagBombColumns;
TheChrisyd 0:8a7c58553b44 970 zigzag.reset(SP_BOMB1, GR_BOMB_ZIGZAG, bombptr);
TheChrisyd 0:8a7c58553b44 971 bombptr = barBombColumns;
TheChrisyd 0:8a7c58553b44 972 bar .reset(SP_BOMB2, GR_BOMB_BARS, bombptr);
TheChrisyd 0:8a7c58553b44 973 diag .reset(SP_BOMB3, GR_BOMB_DIAG, 0);
TheChrisyd 0:8a7c58553b44 974 }
TheChrisyd 0:8a7c58553b44 975 void update() {
TheChrisyd 0:8a7c58553b44 976 if (player.isAlive()) {
TheChrisyd 0:8a7c58553b44 977 if (bombTimer > 0) {
TheChrisyd 0:8a7c58553b44 978 --bombTimer;
TheChrisyd 0:8a7c58553b44 979 }
TheChrisyd 0:8a7c58553b44 980 zigzag.update();
TheChrisyd 0:8a7c58553b44 981 bar .update();
TheChrisyd 0:8a7c58553b44 982 diag .update();
TheChrisyd 0:8a7c58553b44 983 }
TheChrisyd 0:8a7c58553b44 984 }
TheChrisyd 0:8a7c58553b44 985 void collide() {
TheChrisyd 0:8a7c58553b44 986 zigzag.collide();
TheChrisyd 0:8a7c58553b44 987 bar .collide();
TheChrisyd 0:8a7c58553b44 988 diag .collide();
TheChrisyd 0:8a7c58553b44 989 }
TheChrisyd 0:8a7c58553b44 990 void shoot(byte s) {
TheChrisyd 0:8a7c58553b44 991 if (zigzag.sprite==s) zigzag.die();
TheChrisyd 0:8a7c58553b44 992 if (bar.sprite ==s) bar.die();
TheChrisyd 0:8a7c58553b44 993 if (diag.sprite ==s) diag.die();
TheChrisyd 0:8a7c58553b44 994 }
TheChrisyd 0:8a7c58553b44 995 } bombs;
TheChrisyd 0:8a7c58553b44 996
TheChrisyd 0:8a7c58553b44 997 void shootBomb(byte s)
TheChrisyd 0:8a7c58553b44 998 {
TheChrisyd 0:8a7c58553b44 999 bombs.shoot(s);
TheChrisyd 0:8a7c58553b44 1000 }
TheChrisyd 0:8a7c58553b44 1001 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 1002 Start next wave of invaders
TheChrisyd 0:8a7c58553b44 1003 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 1004 void startNextWave()
TheChrisyd 0:8a7c58553b44 1005 {
TheChrisyd 0:8a7c58553b44 1006 beatCounter = 0;
TheChrisyd 0:8a7c58553b44 1007 player.reset();
TheChrisyd 0:8a7c58553b44 1008 bullet.reset();
TheChrisyd 0:8a7c58553b44 1009 saucer.reset();
TheChrisyd 0:8a7c58553b44 1010 bombs.reset();
TheChrisyd 0:8a7c58553b44 1011 shields.reset();
TheChrisyd 0:8a7c58553b44 1012 invaders.reset(invaderWave);
TheChrisyd 0:8a7c58553b44 1013 if (++invaderWave == 0) {
TheChrisyd 0:8a7c58553b44 1014 invaderWave = 1;
TheChrisyd 0:8a7c58553b44 1015 }
TheChrisyd 0:8a7c58553b44 1016 }
TheChrisyd 0:8a7c58553b44 1017
TheChrisyd 0:8a7c58553b44 1018 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 1019 Reset the game
TheChrisyd 0:8a7c58553b44 1020 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 1021 void resetGame()
TheChrisyd 0:8a7c58553b44 1022 {
TheChrisyd 0:8a7c58553b44 1023 numLives = 3;
TheChrisyd 0:8a7c58553b44 1024 playerScore = 0;
TheChrisyd 0:8a7c58553b44 1025 invaderWave = 0;
TheChrisyd 0:8a7c58553b44 1026 startNextWave();
TheChrisyd 0:8a7c58553b44 1027 redrawScores();
TheChrisyd 0:8a7c58553b44 1028 redrawBases();
TheChrisyd 0:8a7c58553b44 1029 GD.fill((64*((screenTop+239)>>3))+(screenLeft>>3),CH_FLOOR,screenWidth>>3);
TheChrisyd 0:8a7c58553b44 1030 }
TheChrisyd 0:8a7c58553b44 1031
TheChrisyd 0:8a7c58553b44 1032 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 1033 Update the game - called from "loop()"
TheChrisyd 0:8a7c58553b44 1034 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 1035 void updateGame()
TheChrisyd 0:8a7c58553b44 1036 {
TheChrisyd 0:8a7c58553b44 1037 ++frameCounter;
TheChrisyd 0:8a7c58553b44 1038 // Collision detection first (we have to do it all during vertical blanking!)
TheChrisyd 0:8a7c58553b44 1039 bullet.collide();
TheChrisyd 0:8a7c58553b44 1040 bombs.collide();
TheChrisyd 0:8a7c58553b44 1041 // The rest of the game logic
TheChrisyd 0:8a7c58553b44 1042 joystick.read();
TheChrisyd 0:8a7c58553b44 1043 player.update();
TheChrisyd 0:8a7c58553b44 1044 bullet.update();
TheChrisyd 0:8a7c58553b44 1045 saucer.update();
TheChrisyd 0:8a7c58553b44 1046 bombs.update();
TheChrisyd 0:8a7c58553b44 1047 shields.update();
TheChrisyd 0:8a7c58553b44 1048 invaders.update();
TheChrisyd 0:8a7c58553b44 1049 if (!remainingInvaders) {
TheChrisyd 0:8a7c58553b44 1050 startNextWave();
TheChrisyd 0:8a7c58553b44 1051 }
TheChrisyd 0:8a7c58553b44 1052 if (player.isDying()) {
TheChrisyd 0:8a7c58553b44 1053 bombs.reset();
TheChrisyd 0:8a7c58553b44 1054 bullet.reset();
TheChrisyd 0:8a7c58553b44 1055 }
TheChrisyd 0:8a7c58553b44 1056 if (player.isDead()) {
TheChrisyd 0:8a7c58553b44 1057 resetGame();
TheChrisyd 0:8a7c58553b44 1058 }
TheChrisyd 0:8a7c58553b44 1059 if (invaders.haveLanded()) {
TheChrisyd 0:8a7c58553b44 1060 numLives = 1;
TheChrisyd 0:8a7c58553b44 1061 player.kill();
TheChrisyd 0:8a7c58553b44 1062 }
TheChrisyd 0:8a7c58553b44 1063 updateScore();
TheChrisyd 0:8a7c58553b44 1064 updateRemainingBases();
TheChrisyd 0:8a7c58553b44 1065 if (--beatCounter < 0) {
TheChrisyd 0:8a7c58553b44 1066 alienBeatSound = true;
TheChrisyd 0:8a7c58553b44 1067 beatCounter = remainingInvaders+4;
TheChrisyd 0:8a7c58553b44 1068 }
TheChrisyd 0:8a7c58553b44 1069 }
TheChrisyd 0:8a7c58553b44 1070
TheChrisyd 0:8a7c58553b44 1071 /*---------------------------------------------
TheChrisyd 0:8a7c58553b44 1072 This is called once from "setup()"
TheChrisyd 0:8a7c58553b44 1073 ---------------------------------------------*/
TheChrisyd 0:8a7c58553b44 1074 void initGame()
TheChrisyd 0:8a7c58553b44 1075 {
TheChrisyd 0:8a7c58553b44 1076 joystick.recalibrate();
TheChrisyd 0:8a7c58553b44 1077 // Use a copperlist to simulate the colored plastic
TheChrisyd 0:8a7c58553b44 1078 // screen overlay...
TheChrisyd 0:8a7c58553b44 1079 CopperlistBuilder cp;
TheChrisyd 0:8a7c58553b44 1080 cp.begin(0x3700);
TheChrisyd 0:8a7c58553b44 1081 // White at the top
TheChrisyd 0:8a7c58553b44 1082 cp.write16(PALETTE4A+2,0x7fff);
TheChrisyd 0:8a7c58553b44 1083 // Red for the saucer
TheChrisyd 0:8a7c58553b44 1084 cp.wait(screenTop+bulletTop);
TheChrisyd 0:8a7c58553b44 1085 cp.write16(PALETTE4A+2,0x7c00);
TheChrisyd 0:8a7c58553b44 1086 // Back to white again
TheChrisyd 0:8a7c58553b44 1087 cp.wait(screenTop+invaderAppearY);
TheChrisyd 0:8a7c58553b44 1088 cp.write16(PALETTE4A+2,0x7fff);
TheChrisyd 0:8a7c58553b44 1089 // Green for the shields/player
TheChrisyd 0:8a7c58553b44 1090 cp.wait(screenTop+shieldYpos);
TheChrisyd 0:8a7c58553b44 1091 cp.write16(PALETTE4A+2,0x03e0);
TheChrisyd 0:8a7c58553b44 1092 cp.end();
TheChrisyd 0:8a7c58553b44 1093 highScore = 0;
TheChrisyd 0:8a7c58553b44 1094 resetGame();
TheChrisyd 0:8a7c58553b44 1095 }
TheChrisyd 0:8a7c58553b44 1096