Invaders game for the Gameduino

Dependencies:   Gameduino mbed

Committer:
TheChrisyd
Date:
Thu Dec 20 21:33:52 2012 +0000
Revision:
2:20a89dc286d5
Parent:
1:f44175dd69fd
Child:
4:e82f4a87df9e
Shields aren't destroyed when hit, life sprites aren't displaying properly.

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