Retro Invaders a space invaders clone by Chris Favreau. Written for the RetroMbuino development board from outrageouscircuits.com for the game programming contest.

Dependencies:   mbed

This is a space invaders clone written for the Retro Mbuino from outrageous circuits.

Development board: http://www.outrageouscircuits.com/shop/product/15 ).

The game itself is basic space invaders. Shoot them before they get to the bottom of the screen. It has a UFO saucer which you can shoot for extra points. You get 4 shields and each shield can be hit up to 4 times before it is gone. Hmm... as each level increases the speed of the invaders shots goes up. The invaders only speed up when there is less of them. You complete the level when you shoot all the invaders. The game ends when a) you run out of lives (you start with 3) or the invaders get to the bottom.

The LEDs turned out to be a pretty cool addition to the game. I wrote a class that blinks them and turns them on for a specified amount of time. They add a nice extra to the game. I use them on the intro screen and when the UFO is present.

The sound turned out to be really difficult for a few reasons. The biggest was that I had never written a sound engine before. The interrupt service routine working off the timer was the easier part. I also had a lot of trouble because there is no filter to filter out the PWM frequency to the speaker... so I had to run the PWM frequency way up there 30 kHz.

The graphics turned out to be a bit of a bear too. Thanks to Chris Taylor for his really great LCD API. I picked up a couple of frames per second from that. I had modified the DisplayN18 class for blitting a single line buffer to the LCD panel however his is a little faster for some reason? I used a different approach to doing the graphics (as I have very little experience with anything other than double buffered displays). I have a tile map and a list of sprites. Each tile/sprite is 1 bit 8x8. They could be bigger. I ran out of time. That much is not special. What is different from what I can tell is that I use a 1 line buffer that is 160 shorts long. The render function first adds the tile map data into the line buffer first. Then the sprites are added over the existing data. You can have a great deal of different sprites and maps going to the screen and just have to rewrite the LCD memory once per frame. After each line is composited, the line is then drawn to the LCD. Kind of like an Atari 2600. Each sprite/tile has a foreground and background color and can be different from the other tiles/sprites. There is one color reserved for Transparency.

There are 16 colors to choose from. I chose a palette based on the Macintosh OS 4.1 palette I found on WikiPedia. It is a very nice mix of colors.

I found a sprite editor called SpriteX ( https://code.google.com/p/spritesx-ed/ )... it works nicely except that the 16x16 sprites are in a weird format. Time limited me to 8x8 sprites. Oh well.

I used nokring to make the music. It makes RTTTL formatted ring tones which my sound api can play. Here is a useful site that has lots of arcade/video game ring tones with a link to nokring in the utilities page. http://arcadetones.emuunlim.com/files.htm

Other than all that stuff I used state machines to do most of the game logic. Please excuse the horrible coding as I tried to comment a lot of it however it is not very nice to look at. Lots of long functions...

Committer:
cfavreau
Date:
Tue Mar 03 04:26:01 2015 +0000
Revision:
0:c79e1f29f029
Retro Invaders by Chris Favreau for the RetroMbuino Platform - outrageouscircuits.com game programming contest.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
cfavreau 0:c79e1f29f029 1 // RETRO Invaders!
cfavreau 0:c79e1f29f029 2 // Written by Chris Favreau
cfavreau 0:c79e1f29f029 3 // cfavreau@fab-favreau.com
cfavreau 0:c79e1f29f029 4 // for the RetroMbuino development board from outrageouscircuits.com
cfavreau 0:c79e1f29f029 5 //
cfavreau 0:c79e1f29f029 6
cfavreau 0:c79e1f29f029 7 #include "Game.h"
cfavreau 0:c79e1f29f029 8
cfavreau 0:c79e1f29f029 9 enum TILE_INDEX
cfavreau 0:c79e1f29f029 10 {
cfavreau 0:c79e1f29f029 11 eTileBlank,
cfavreau 0:c79e1f29f029 12 eTilePlayer,
cfavreau 0:c79e1f29f029 13 eTilePlayerShot,
cfavreau 0:c79e1f29f029 14 eTileInvaderBolt,
cfavreau 0:c79e1f29f029 15 eTileShield1,
cfavreau 0:c79e1f29f029 16 eTileShield2,
cfavreau 0:c79e1f29f029 17 eTileShield3,
cfavreau 0:c79e1f29f029 18 eTileShield4,
cfavreau 0:c79e1f29f029 19 eTileSaucer,
cfavreau 0:c79e1f29f029 20 eTileInvader1,
cfavreau 0:c79e1f29f029 21 eTileInvader2,
cfavreau 0:c79e1f29f029 22 eTileInvader3,
cfavreau 0:c79e1f29f029 23 eTileInvader4,
cfavreau 0:c79e1f29f029 24 eTileStarfield1,
cfavreau 0:c79e1f29f029 25 eTileStarfield2,
cfavreau 0:c79e1f29f029 26 eTileStarfield3,
cfavreau 0:c79e1f29f029 27 eTileStarfield4,
cfavreau 0:c79e1f29f029 28 eTileStarfield5,
cfavreau 0:c79e1f29f029 29 eTileCount,
cfavreau 0:c79e1f29f029 30 };
cfavreau 0:c79e1f29f029 31
cfavreau 0:c79e1f29f029 32 enum SPRITE_INDEX // Up to 64 sprites
cfavreau 0:c79e1f29f029 33 {
cfavreau 0:c79e1f29f029 34 eSpritePlayer = 0,
cfavreau 0:c79e1f29f029 35 eSpritePlayerShot,
cfavreau 0:c79e1f29f029 36 eSpriteShield1,
cfavreau 0:c79e1f29f029 37 eSpriteShield2,
cfavreau 0:c79e1f29f029 38 eSpriteShield3,
cfavreau 0:c79e1f29f029 39 eSpriteShield4,
cfavreau 0:c79e1f29f029 40 eSpriteSaucer,
cfavreau 0:c79e1f29f029 41 eSpriteInvaderShot1,
cfavreau 0:c79e1f29f029 42 eSpriteInvaderShot2,
cfavreau 0:c79e1f29f029 43 eSpriteInvaderShot3,
cfavreau 0:c79e1f29f029 44 eSpriteInvaderShot4,
cfavreau 0:c79e1f29f029 45 eSpriteInvaderStart,
cfavreau 0:c79e1f29f029 46
cfavreau 0:c79e1f29f029 47 };
cfavreau 0:c79e1f29f029 48
cfavreau 0:c79e1f29f029 49 unsigned char TILE_DATA[]={
cfavreau 0:c79e1f29f029 50 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Blank
cfavreau 0:c79e1f29f029 51 0x18,0x3C,0x7E,0x7E,0x7E,0x3C,0x66,0x42, // Player
cfavreau 0:c79e1f29f029 52 0x00,0x00,0x18,0x3C,0x3C,0x18,0x00,0x00, // Player Shot
cfavreau 0:c79e1f29f029 53 0x00,0x00,0x10,0x18,0x18,0x08,0x00,0x00, // Invader Bolt
cfavreau 0:c79e1f29f029 54 0x3C,0x7E,0x7E,0xFF,0xFF,0xFF,0xFF,0x81, // Shield 1
cfavreau 0:c79e1f29f029 55 0x34,0x76,0x76,0xFF,0xFF,0xFF,0xFF,0x81, // Shield 2
cfavreau 0:c79e1f29f029 56 0x00,0x52,0x56,0xDF,0xFF,0xFF,0xFF,0x81, // Shield 3
cfavreau 0:c79e1f29f029 57 0x00,0x00,0x14,0x5D,0x7D,0xFF,0xFF,0x81, // Shield 4
cfavreau 0:c79e1f29f029 58 0x00,0x3C,0x7E,0xFF,0x7E,0x00,0x00,0x00, // Saucer
cfavreau 0:c79e1f29f029 59 0x00,0x3C,0x52,0x3C,0x42,0x24,0x00,0x00, // Invader 1
cfavreau 0:c79e1f29f029 60 0x00,0x3C,0x4A,0x3C,0x42,0x42,0x00,0x00, // Invader 2
cfavreau 0:c79e1f29f029 61 0x00,0x3C,0x46,0x3C,0x42,0x81,0x00,0x00, // Invader 3
cfavreau 0:c79e1f29f029 62 0x00,0x3C,0x62,0x3C,0x42,0x42,0x00,0x00, // Invader 4
cfavreau 0:c79e1f29f029 63 0x00,0x40,0x00,0x00,0x00,0x04,0x20,0x00, // Star Field 1
cfavreau 0:c79e1f29f029 64 0x00,0x08,0x00,0x00,0x44,0x00,0x00,0x00, // Star Field 2
cfavreau 0:c79e1f29f029 65 0x00,0x00,0x00,0x20,0x00,0x00,0x02,0x00, // Star Field 3
cfavreau 0:c79e1f29f029 66 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x80, // Star Field 4
cfavreau 0:c79e1f29f029 67 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00}; // Star Field 5
cfavreau 0:c79e1f29f029 68
cfavreau 0:c79e1f29f029 69 char intro_song[] = "8g5,8f#5,8f5,8e.5,8d#5,8d.5,8c#5,4b4,1p";
cfavreau 0:c79e1f29f029 70 char saucer_song[] = "8f#4,8g4,8f#4,8g4,8f#4,8g4,8f#4,8g4,1p"; // Add a pause at the end... there is a bug in the PlaySong code that skips the last note
cfavreau 0:c79e1f29f029 71 uint16_t invader_march[4] = {NOTE_G3, NOTE_F3, NOTE_E3, NOTE_D3};
cfavreau 0:c79e1f29f029 72
cfavreau 0:c79e1f29f029 73 Game::Game() : ain(P0_15)
cfavreau 0:c79e1f29f029 74 {
cfavreau 0:c79e1f29f029 75 initialize();
cfavreau 0:c79e1f29f029 76 }
cfavreau 0:c79e1f29f029 77
cfavreau 0:c79e1f29f029 78 void Game::initialize()
cfavreau 0:c79e1f29f029 79 {
cfavreau 0:c79e1f29f029 80 // Seed the random number generator
cfavreau 0:c79e1f29f029 81 srand(this->ain.read_u16());
cfavreau 0:c79e1f29f029 82
cfavreau 0:c79e1f29f029 83 // Start the timer
cfavreau 0:c79e1f29f029 84 timer.start();
cfavreau 0:c79e1f29f029 85 frame_timeout = timer.read_ms() + MS_PER_FRAME;
cfavreau 0:c79e1f29f029 86 frame_count = 0;
cfavreau 0:c79e1f29f029 87
cfavreau 0:c79e1f29f029 88 // Setup the customer character buffer with our sprites
cfavreau 0:c79e1f29f029 89 disp.SetCustomChar(0, TILE_DATA, eTileCount, true);
cfavreau 0:c79e1f29f029 90
cfavreau 0:c79e1f29f029 91 // Start the sound driver
cfavreau 0:c79e1f29f029 92 sound.Init();
cfavreau 0:c79e1f29f029 93 sound.SetInstrument(0, SOUND_SQUARE, 255, 10, 0, 0, 0, 0, 0, 0); // Music?
cfavreau 0:c79e1f29f029 94 sound.SetInstrument(1, SOUND_NOISE, 255, 8, 0, 0, 0, 0, 0, 0); // Explosion?
cfavreau 0:c79e1f29f029 95 sound.SetInstrument(2, SOUND_SQUARE, 255, 0, -50, 0, 0, 0, 0, 0); // Pew Pew
cfavreau 0:c79e1f29f029 96
cfavreau 0:c79e1f29f029 97
cfavreau 0:c79e1f29f029 98 // Set the game mode
cfavreau 0:c79e1f29f029 99 mode = eGameTitle;
cfavreau 0:c79e1f29f029 100 // Set the step state machine to 0
cfavreau 0:c79e1f29f029 101 game_step = 0;
cfavreau 0:c79e1f29f029 102 // Not paused
cfavreau 0:c79e1f29f029 103 paused = false;
cfavreau 0:c79e1f29f029 104
cfavreau 0:c79e1f29f029 105 // Initialize the scores
cfavreau 0:c79e1f29f029 106 score = 0;
cfavreau 0:c79e1f29f029 107 hiscore = 32; // TODO - store this value in the onboard EEPROM ... no mbed class to do that at the present... outta time
cfavreau 0:c79e1f29f029 108
cfavreau 0:c79e1f29f029 109 // Initialize the level
cfavreau 0:c79e1f29f029 110 level = 1;
cfavreau 0:c79e1f29f029 111
cfavreau 0:c79e1f29f029 112 // Initialize our star map
cfavreau 0:c79e1f29f029 113 star_field_timer = 0;
cfavreau 0:c79e1f29f029 114 }
cfavreau 0:c79e1f29f029 115
cfavreau 0:c79e1f29f029 116 bool Game::dotick()
cfavreau 0:c79e1f29f029 117 {
cfavreau 0:c79e1f29f029 118 now = timer.read_ms();
cfavreau 0:c79e1f29f029 119 if (now < frame_timeout) return false;
cfavreau 0:c79e1f29f029 120 frame_timeout = now + MS_PER_FRAME;
cfavreau 0:c79e1f29f029 121 frame_count++;
cfavreau 0:c79e1f29f029 122 return true;
cfavreau 0:c79e1f29f029 123 }
cfavreau 0:c79e1f29f029 124
cfavreau 0:c79e1f29f029 125 void Game::tick()
cfavreau 0:c79e1f29f029 126 {
cfavreau 0:c79e1f29f029 127 if (!dotick()) return;
cfavreau 0:c79e1f29f029 128
cfavreau 0:c79e1f29f029 129 lights.tick();
cfavreau 0:c79e1f29f029 130 inputs.tick();
cfavreau 0:c79e1f29f029 131
cfavreau 0:c79e1f29f029 132 if (!paused)
cfavreau 0:c79e1f29f029 133 {
cfavreau 0:c79e1f29f029 134 switch(mode)
cfavreau 0:c79e1f29f029 135 {
cfavreau 0:c79e1f29f029 136 case (eGameTitle): DoGameTitle(); break;
cfavreau 0:c79e1f29f029 137 case (eGamePlay): DoGamePlay(); break;
cfavreau 0:c79e1f29f029 138 case (eGameOver): DoGameOver(); break;
cfavreau 0:c79e1f29f029 139 }
cfavreau 0:c79e1f29f029 140 }
cfavreau 0:c79e1f29f029 141 else
cfavreau 0:c79e1f29f029 142 {
cfavreau 0:c79e1f29f029 143 disp.printat((CHAR_MAP_WIDTH >> 1) - 6, 8, ".oOPausedOo.");
cfavreau 0:c79e1f29f029 144 if (inputs.hit(eCircle) || (inputs.hit(eSquare)))
cfavreau 0:c79e1f29f029 145 {
cfavreau 0:c79e1f29f029 146 paused = false;
cfavreau 0:c79e1f29f029 147 }
cfavreau 0:c79e1f29f029 148 }
cfavreau 0:c79e1f29f029 149
cfavreau 0:c79e1f29f029 150 disp.render();
cfavreau 0:c79e1f29f029 151 }
cfavreau 0:c79e1f29f029 152
cfavreau 0:c79e1f29f029 153 int ax, ay, avx, avy, af;
cfavreau 0:c79e1f29f029 154 int mx, my;
cfavreau 0:c79e1f29f029 155 bool missle;
cfavreau 0:c79e1f29f029 156 int await;
cfavreau 0:c79e1f29f029 157 int selection = 0;
cfavreau 0:c79e1f29f029 158
cfavreau 0:c79e1f29f029 159 void Game::DoGameTitle()
cfavreau 0:c79e1f29f029 160 {
cfavreau 0:c79e1f29f029 161 switch(game_step)
cfavreau 0:c79e1f29f029 162 {
cfavreau 0:c79e1f29f029 163 default:
cfavreau 0:c79e1f29f029 164 case (0):
cfavreau 0:c79e1f29f029 165 // Setup the title animation sprite
cfavreau 0:c79e1f29f029 166 disp.AddSprite(0, eTileInvader1, 0, 0, PAL_GREEN, PAL_TRANSPARENT, true); // Invader
cfavreau 0:c79e1f29f029 167 disp.AddSprite(1, eTileInvaderBolt, 0, 0, PAL_YELLOW, PAL_TRANSPARENT, true); // Invader Bolt
cfavreau 0:c79e1f29f029 168 // Setup some sounds
cfavreau 0:c79e1f29f029 169 sound.SetInstrument(1, SOUND_NOISE, 255, 8, 0, 0, 0, 0, 0, 0); // Explosion?
cfavreau 0:c79e1f29f029 170 sound.SetInstrument(2, SOUND_SQUARE, 255, 0, -50, 0, 0, 0, 0, 0); // Pew Pew
cfavreau 0:c79e1f29f029 171 // Setup the sound for the invader
cfavreau 0:c79e1f29f029 172 sound.SetInstrument(0, SOUND_SQUARE, 200, 0, -1, 0, 0, 0, 255, 100); // Vibrato + SLide down
cfavreau 0:c79e1f29f029 173 // Next step
cfavreau 0:c79e1f29f029 174 game_step++;
cfavreau 0:c79e1f29f029 175 break;
cfavreau 0:c79e1f29f029 176 case (1):
cfavreau 0:c79e1f29f029 177 // Setup the invader's position and frame
cfavreau 0:c79e1f29f029 178 ax = 16; ay = 8;
cfavreau 0:c79e1f29f029 179 avx = 1;
cfavreau 0:c79e1f29f029 180 af = 0;
cfavreau 0:c79e1f29f029 181 disp.SetSpritePos(0, ax, ay);
cfavreau 0:c79e1f29f029 182 disp.EnableSprite(0, true);
cfavreau 0:c79e1f29f029 183 game_step++;
cfavreau 0:c79e1f29f029 184 break;
cfavreau 0:c79e1f29f029 185 case (2):
cfavreau 0:c79e1f29f029 186 // Draw the score (current and hi score)
cfavreau 0:c79e1f29f029 187 UpdateScore();
cfavreau 0:c79e1f29f029 188 // Display the title and the invader coming down...
cfavreau 0:c79e1f29f029 189 disp.clear();
cfavreau 0:c79e1f29f029 190 disp.printat(10 - 8, 6, "RETRO Invaders!");
cfavreau 0:c79e1f29f029 191 if (selection == 0) disp.setforecolor(PAL_YELLOW);
cfavreau 0:c79e1f29f029 192 disp.printat(10 - 3, 8, "%cPlay%c", (selection == 0) ? '>' : ' ', (selection == 0) ? '<' : ' ');
cfavreau 0:c79e1f29f029 193 disp.setforecolor(PAL_WHITE);
cfavreau 0:c79e1f29f029 194 if (selection == 1) disp.setforecolor(PAL_YELLOW);
cfavreau 0:c79e1f29f029 195 disp.printat(10 - 5, 9, "%cSound %s%c", (selection == 1) ? '>' : ' ', sound.IsMuted() ? "Off" : "On ", (selection == 1) ? '<' : ' ');
cfavreau 0:c79e1f29f029 196 disp.setforecolor(PAL_WHITE);
cfavreau 0:c79e1f29f029 197 disp.SetSpritePos(0, ax, ay);
cfavreau 0:c79e1f29f029 198 disp.SetSpriteChar(0, eTileInvader1 + af);
cfavreau 0:c79e1f29f029 199 af++;
cfavreau 0:c79e1f29f029 200 if (af > 3) af = 0;
cfavreau 0:c79e1f29f029 201 ax += avx;
cfavreau 0:c79e1f29f029 202 if ((ax > 128) || (ax < 16))
cfavreau 0:c79e1f29f029 203 {
cfavreau 0:c79e1f29f029 204 missle = true;
cfavreau 0:c79e1f29f029 205 mx = ax + 4; my = ay;
cfavreau 0:c79e1f29f029 206 disp.EnableSprite(1, true);
cfavreau 0:c79e1f29f029 207 avx *= -1;
cfavreau 0:c79e1f29f029 208 ay += 8;
cfavreau 0:c79e1f29f029 209 sound.PlaySound(0, NOTE_C4, 40);
cfavreau 0:c79e1f29f029 210 lights.set(1, true, 5);
cfavreau 0:c79e1f29f029 211 lights.set(2, true, 5);
cfavreau 0:c79e1f29f029 212 }
cfavreau 0:c79e1f29f029 213 if (ay >= 48)
cfavreau 0:c79e1f29f029 214 {
cfavreau 0:c79e1f29f029 215 // Hide the missle sprite
cfavreau 0:c79e1f29f029 216 disp.EnableSprite(1, false);
cfavreau 0:c79e1f29f029 217 missle = false;
cfavreau 0:c79e1f29f029 218 // Hide the invader
cfavreau 0:c79e1f29f029 219 disp.EnableSprite(0, false);
cfavreau 0:c79e1f29f029 220 // Setup a wait timer count 3 seconds
cfavreau 0:c79e1f29f029 221 await = 3 * FRAMES_PER_SECOND;
cfavreau 0:c79e1f29f029 222 // Play an explosion sound
cfavreau 0:c79e1f29f029 223 sound.PlaySound(1, NOTE_C7, 20);
cfavreau 0:c79e1f29f029 224 // Setup the leds to blink
cfavreau 0:c79e1f29f029 225 lights.blink(1, 5, 1, -1);
cfavreau 0:c79e1f29f029 226 lights.blink(2, 5, 0, -1);
cfavreau 0:c79e1f29f029 227 // go to the next animated sequence step
cfavreau 0:c79e1f29f029 228 game_step++;
cfavreau 0:c79e1f29f029 229 }
cfavreau 0:c79e1f29f029 230 if (missle)
cfavreau 0:c79e1f29f029 231 {
cfavreau 0:c79e1f29f029 232 my +=2;
cfavreau 0:c79e1f29f029 233 disp.SetSpritePos(1, mx, my);
cfavreau 0:c79e1f29f029 234 if (my > LCD_HEIGHT)
cfavreau 0:c79e1f29f029 235 {
cfavreau 0:c79e1f29f029 236 missle = false;
cfavreau 0:c79e1f29f029 237 disp.EnableSprite(1, false);
cfavreau 0:c79e1f29f029 238 }
cfavreau 0:c79e1f29f029 239 }
cfavreau 0:c79e1f29f029 240 if (inputs.hit(eCircle) || inputs.hit(eSquare))
cfavreau 0:c79e1f29f029 241 {
cfavreau 0:c79e1f29f029 242 sound.PlaySound(0, NOTE_C6, 10);
cfavreau 0:c79e1f29f029 243
cfavreau 0:c79e1f29f029 244 // Do the selection
cfavreau 0:c79e1f29f029 245 if (selection == 1)
cfavreau 0:c79e1f29f029 246 {
cfavreau 0:c79e1f29f029 247 // Toggle the sound on/off
cfavreau 0:c79e1f29f029 248 sound.Mute(!sound.IsMuted());
cfavreau 0:c79e1f29f029 249 }
cfavreau 0:c79e1f29f029 250 if (selection == 0)
cfavreau 0:c79e1f29f029 251 {
cfavreau 0:c79e1f29f029 252 sound.SetInstrument(0, SOUND_SQUARE, 255, 0, 0, 0, 0, 0, 0, 0); // Music?
cfavreau 0:c79e1f29f029 253 // Play the intro song
cfavreau 0:c79e1f29f029 254 sound.PlaySong(intro_song, 0, 130, false);
cfavreau 0:c79e1f29f029 255 // Let's play the game! goto step 4
cfavreau 0:c79e1f29f029 256 game_step = 4;
cfavreau 0:c79e1f29f029 257 }
cfavreau 0:c79e1f29f029 258 }
cfavreau 0:c79e1f29f029 259 if (inputs.hit(eUp))
cfavreau 0:c79e1f29f029 260 {
cfavreau 0:c79e1f29f029 261 selection++;
cfavreau 0:c79e1f29f029 262 if (selection > 1) selection = 0;
cfavreau 0:c79e1f29f029 263 }
cfavreau 0:c79e1f29f029 264 if (inputs.hit(eDown))
cfavreau 0:c79e1f29f029 265 {
cfavreau 0:c79e1f29f029 266 selection--;
cfavreau 0:c79e1f29f029 267 if (selection < 0) selection = 1;
cfavreau 0:c79e1f29f029 268 }
cfavreau 0:c79e1f29f029 269 break;
cfavreau 0:c79e1f29f029 270 case (3):
cfavreau 0:c79e1f29f029 271 // Flash the game logo red
cfavreau 0:c79e1f29f029 272 if ((frame_count % FRAMES_PER_SECOND) >= 15)
cfavreau 0:c79e1f29f029 273 disp.setforecolor(PAL_RED);
cfavreau 0:c79e1f29f029 274 else
cfavreau 0:c79e1f29f029 275 disp.setforecolor(PAL_WHITE);
cfavreau 0:c79e1f29f029 276 disp.clear();
cfavreau 0:c79e1f29f029 277 disp.printat((CHAR_MAP_WIDTH >> 1) - 8, 6, "RETRO Invaders!");
cfavreau 0:c79e1f29f029 278 // subtract one from the wait counter
cfavreau 0:c79e1f29f029 279 await--;
cfavreau 0:c79e1f29f029 280 if (await < 1)
cfavreau 0:c79e1f29f029 281 {
cfavreau 0:c79e1f29f029 282 // Stop the leds from blinking
cfavreau 0:c79e1f29f029 283 lights.set(1, false, 1);
cfavreau 0:c79e1f29f029 284 lights.set(2, false, 1);
cfavreau 0:c79e1f29f029 285 // Set the text color back to white
cfavreau 0:c79e1f29f029 286 disp.setforecolor(PAL_WHITE);
cfavreau 0:c79e1f29f029 287 // do the previous animation sequence
cfavreau 0:c79e1f29f029 288 game_step = 1;
cfavreau 0:c79e1f29f029 289 }
cfavreau 0:c79e1f29f029 290 break;
cfavreau 0:c79e1f29f029 291 case (4):
cfavreau 0:c79e1f29f029 292 disp.EnableSprite(0, false); // Hide the invader sprite
cfavreau 0:c79e1f29f029 293 mode = eGamePlay;
cfavreau 0:c79e1f29f029 294 game_step = 0;
cfavreau 0:c79e1f29f029 295 break;
cfavreau 0:c79e1f29f029 296 }
cfavreau 0:c79e1f29f029 297 }
cfavreau 0:c79e1f29f029 298
cfavreau 0:c79e1f29f029 299 void Game::SetupStarfield()
cfavreau 0:c79e1f29f029 300 {
cfavreau 0:c79e1f29f029 301 uint8_t color = 0;
cfavreau 0:c79e1f29f029 302
cfavreau 0:c79e1f29f029 303 for (int y = 0; y < CHAR_MAP_HEIGHT; y++)
cfavreau 0:c79e1f29f029 304 for (int x = 0; x < CHAR_MAP_WIDTH; x++)
cfavreau 0:c79e1f29f029 305 {
cfavreau 0:c79e1f29f029 306 // Pick random number between 0 and 4
cfavreau 0:c79e1f29f029 307 uint8_t num = rand() % 5;
cfavreau 0:c79e1f29f029 308 // Pick a color based on the random number
cfavreau 0:c79e1f29f029 309 switch(num)
cfavreau 0:c79e1f29f029 310 {
cfavreau 0:c79e1f29f029 311 case (0):
cfavreau 0:c79e1f29f029 312 case (1):
cfavreau 0:c79e1f29f029 313 case (2):
cfavreau 0:c79e1f29f029 314 color = PAL_BLUE;
cfavreau 0:c79e1f29f029 315 break;
cfavreau 0:c79e1f29f029 316 case (3):
cfavreau 0:c79e1f29f029 317 case (4):
cfavreau 0:c79e1f29f029 318 color = PAL_LIGHTBLUE;
cfavreau 0:c79e1f29f029 319 break;
cfavreau 0:c79e1f29f029 320 }
cfavreau 0:c79e1f29f029 321 // Set the startfield tile based on the random number and random color
cfavreau 0:c79e1f29f029 322 disp.set_map(128 + (eTileStarfield1 + num), x, y, color, PAL_BLACK);
cfavreau 0:c79e1f29f029 323 }
cfavreau 0:c79e1f29f029 324 }
cfavreau 0:c79e1f29f029 325
cfavreau 0:c79e1f29f029 326 void Game::UpdateStarfield()
cfavreau 0:c79e1f29f029 327 {
cfavreau 0:c79e1f29f029 328 // Check to see if it is time to update the starfield
cfavreau 0:c79e1f29f029 329 star_field_timer++;
cfavreau 0:c79e1f29f029 330 if (star_field_timer < 5) return;
cfavreau 0:c79e1f29f029 331 star_field_timer = 0;
cfavreau 0:c79e1f29f029 332
cfavreau 0:c79e1f29f029 333 uint8_t y = 0;
cfavreau 0:c79e1f29f029 334 uint8_t color = 0;
cfavreau 0:c79e1f29f029 335
cfavreau 0:c79e1f29f029 336 // Replace the Score and Lives with Star tiles before we scroll
cfavreau 0:c79e1f29f029 337 // otherwise they will scroll with us...
cfavreau 0:c79e1f29f029 338 for (int x = 0; x < CHAR_MAP_WIDTH; x++)
cfavreau 0:c79e1f29f029 339 {
cfavreau 0:c79e1f29f029 340 // Pick random number between 0 and 4
cfavreau 0:c79e1f29f029 341 uint8_t num = rand() % 5;
cfavreau 0:c79e1f29f029 342 // Pick a color based on the random number
cfavreau 0:c79e1f29f029 343 switch(num)
cfavreau 0:c79e1f29f029 344 {
cfavreau 0:c79e1f29f029 345 case (0):
cfavreau 0:c79e1f29f029 346 case (1):
cfavreau 0:c79e1f29f029 347 case (2):
cfavreau 0:c79e1f29f029 348 color = PAL_BLUE;
cfavreau 0:c79e1f29f029 349 break;
cfavreau 0:c79e1f29f029 350 case (3):
cfavreau 0:c79e1f29f029 351 case (4):
cfavreau 0:c79e1f29f029 352 color = PAL_LIGHTBLUE;
cfavreau 0:c79e1f29f029 353 break;
cfavreau 0:c79e1f29f029 354 }
cfavreau 0:c79e1f29f029 355 // Set the startfield tile based on the random number and random color
cfavreau 0:c79e1f29f029 356 disp.set_map(128 + (eTileStarfield1 + num), x, 0, color, PAL_BLACK); // Top line of the screen
cfavreau 0:c79e1f29f029 357 }
cfavreau 0:c79e1f29f029 358
cfavreau 0:c79e1f29f029 359 // Scroll the whole field down 1 tile
cfavreau 0:c79e1f29f029 360 disp.shift_map(eShiftDown);
cfavreau 0:c79e1f29f029 361
cfavreau 0:c79e1f29f029 362 for (int x = 0; x < CHAR_MAP_WIDTH; x++)
cfavreau 0:c79e1f29f029 363 {
cfavreau 0:c79e1f29f029 364 // Pick random number between 0 and 4
cfavreau 0:c79e1f29f029 365 uint8_t num = rand() % 5;
cfavreau 0:c79e1f29f029 366 // Pick a color based on the random number
cfavreau 0:c79e1f29f029 367 switch(num)
cfavreau 0:c79e1f29f029 368 {
cfavreau 0:c79e1f29f029 369 case (0):
cfavreau 0:c79e1f29f029 370 case (1):
cfavreau 0:c79e1f29f029 371 case (2):
cfavreau 0:c79e1f29f029 372 color = PAL_BLUE;
cfavreau 0:c79e1f29f029 373 break;
cfavreau 0:c79e1f29f029 374 case (3):
cfavreau 0:c79e1f29f029 375 case (4):
cfavreau 0:c79e1f29f029 376 color = PAL_LIGHTBLUE;
cfavreau 0:c79e1f29f029 377 break;
cfavreau 0:c79e1f29f029 378 }
cfavreau 0:c79e1f29f029 379 // Set the startfield tile based on the random number and random color
cfavreau 0:c79e1f29f029 380 disp.set_map(128 + (eTileStarfield1 + num), x, y, color, PAL_BLACK);
cfavreau 0:c79e1f29f029 381 }
cfavreau 0:c79e1f29f029 382 }
cfavreau 0:c79e1f29f029 383
cfavreau 0:c79e1f29f029 384 void Game::SetupInvaders()
cfavreau 0:c79e1f29f029 385 {
cfavreau 0:c79e1f29f029 386 invaders_x = 10;
cfavreau 0:c79e1f29f029 387 invaders_y = 10;
cfavreau 0:c79e1f29f029 388 invaders_inc = 4;
cfavreau 0:c79e1f29f029 389 invaders_frame = 0;
cfavreau 0:c79e1f29f029 390 invaders_timer = 0;
cfavreau 0:c79e1f29f029 391 active_invaders = 40;
cfavreau 0:c79e1f29f029 392 invaders_wait_time = 5;
cfavreau 0:c79e1f29f029 393 uint8_t color = 0;
cfavreau 0:c79e1f29f029 394 for (int i = 0; i < 40; i++)
cfavreau 0:c79e1f29f029 395 {
cfavreau 0:c79e1f29f029 396 switch(i / 8)
cfavreau 0:c79e1f29f029 397 {
cfavreau 0:c79e1f29f029 398 case (0): color = PAL_WHITE; break;
cfavreau 0:c79e1f29f029 399 case (1): color = PAL_YELLOW; break;
cfavreau 0:c79e1f29f029 400 case (2): color = PAL_ORANGE; break;
cfavreau 0:c79e1f29f029 401 case (3): color = PAL_RED; break;
cfavreau 0:c79e1f29f029 402 case (4): color = PAL_GREEN; break;
cfavreau 0:c79e1f29f029 403 case (5): color = PAL_LIGHTBLUE; break;
cfavreau 0:c79e1f29f029 404 case (6): color = PAL_BLUE; break;
cfavreau 0:c79e1f29f029 405 case (7): color = PAL_MAGENTA; break;
cfavreau 0:c79e1f29f029 406 }
cfavreau 0:c79e1f29f029 407
cfavreau 0:c79e1f29f029 408 uint8_t x = invaders_x + ((i % 8) * 10);
cfavreau 0:c79e1f29f029 409 uint8_t y = invaders_y + ((i / 8) * 10);
cfavreau 0:c79e1f29f029 410 disp.AddSprite(eSpriteInvaderStart + i, eTileInvader1, x, y, color, PAL_TRANSPARENT);
cfavreau 0:c79e1f29f029 411 invaders[i] = true;
cfavreau 0:c79e1f29f029 412 }
cfavreau 0:c79e1f29f029 413
cfavreau 0:c79e1f29f029 414 for (int i = 0; i < 4; i++)
cfavreau 0:c79e1f29f029 415 {
cfavreau 0:c79e1f29f029 416 invader_active_shot[i] = false;
cfavreau 0:c79e1f29f029 417 invader_shot_x[i] = 0;
cfavreau 0:c79e1f29f029 418 invader_shot_y[i] = 0;
cfavreau 0:c79e1f29f029 419 disp.AddSprite(eSpriteInvaderShot1 + i, eTileInvaderBolt, invader_shot_x[i], invader_shot_y[i], PAL_WHITE, PAL_TRANSPARENT);
cfavreau 0:c79e1f29f029 420 }
cfavreau 0:c79e1f29f029 421 invader_shot_interval = FRAMES_PER_SECOND;
cfavreau 0:c79e1f29f029 422 invader_shot_timer = invader_shot_interval;
cfavreau 0:c79e1f29f029 423 invader_march_note = 0;
cfavreau 0:c79e1f29f029 424 }
cfavreau 0:c79e1f29f029 425
cfavreau 0:c79e1f29f029 426 void Game::UpdateInvaders()
cfavreau 0:c79e1f29f029 427 {
cfavreau 0:c79e1f29f029 428 // Check to see if it is time to advance the frame
cfavreau 0:c79e1f29f029 429 invaders_timer++;
cfavreau 0:c79e1f29f029 430 if (invaders_timer > invaders_wait_time)
cfavreau 0:c79e1f29f029 431 {
cfavreau 0:c79e1f29f029 432 invaders_timer = 0;
cfavreau 0:c79e1f29f029 433 invaders_frame++;
cfavreau 0:c79e1f29f029 434 if (invaders_frame > 3) invaders_frame = 0;
cfavreau 0:c79e1f29f029 435 // Play the invader march sound
cfavreau 0:c79e1f29f029 436 if ((invaders_frame == 0) || (invaders_frame == 2))
cfavreau 0:c79e1f29f029 437 {
cfavreau 0:c79e1f29f029 438 sound.PlaySound(0, invader_march[invader_march_note++], 10);
cfavreau 0:c79e1f29f029 439 if (invader_march_note >= 4) invader_march_note = 0;
cfavreau 0:c79e1f29f029 440 }
cfavreau 0:c79e1f29f029 441 // Move the invaders
cfavreau 0:c79e1f29f029 442 invaders_x += invaders_inc;
cfavreau 0:c79e1f29f029 443 }
cfavreau 0:c79e1f29f029 444 // Update all of the invaders
cfavreau 0:c79e1f29f029 445 bool move_down = false;
cfavreau 0:c79e1f29f029 446 for (int i = 0; i < 40; i++)
cfavreau 0:c79e1f29f029 447 {
cfavreau 0:c79e1f29f029 448 // Check to see if this invader is active and not blown up
cfavreau 0:c79e1f29f029 449 if (invaders[i])
cfavreau 0:c79e1f29f029 450 {
cfavreau 0:c79e1f29f029 451 // Calculate the position of this invader
cfavreau 0:c79e1f29f029 452 uint8_t x = invaders_x + ((i % 8) * 10);
cfavreau 0:c79e1f29f029 453 uint8_t y = invaders_y + ((i / 8) * 10);
cfavreau 0:c79e1f29f029 454
cfavreau 0:c79e1f29f029 455 // Check to see if the invader has reach the edge of the play area
cfavreau 0:c79e1f29f029 456 if (x >= 150) move_down = true;
cfavreau 0:c79e1f29f029 457 if (x < 10) move_down = true;
cfavreau 0:c79e1f29f029 458
cfavreau 0:c79e1f29f029 459 disp.SetSpriteChar(eSpriteInvaderStart + i, eTileInvader1 + invaders_frame);
cfavreau 0:c79e1f29f029 460 disp.SetSpritePos(eSpriteInvaderStart + i, x, y);
cfavreau 0:c79e1f29f029 461 }
cfavreau 0:c79e1f29f029 462 disp.EnableSprite(eSpriteInvaderStart + i, invaders[i]);
cfavreau 0:c79e1f29f029 463 }
cfavreau 0:c79e1f29f029 464 // Check to see if we should move the invaders down one notch
cfavreau 0:c79e1f29f029 465 if (move_down)
cfavreau 0:c79e1f29f029 466 {
cfavreau 0:c79e1f29f029 467 invaders_y += 5; // Half the height of the invader space (10 x 10?)
cfavreau 0:c79e1f29f029 468 invaders_inc *= -1;
cfavreau 0:c79e1f29f029 469 invaders_x += invaders_inc;
cfavreau 0:c79e1f29f029 470 // Increase the invader speed if we need to
cfavreau 0:c79e1f29f029 471 if (active_invaders <= 30) invaders_wait_time = 4;
cfavreau 0:c79e1f29f029 472 if (active_invaders <= 20) invaders_wait_time = 3;
cfavreau 0:c79e1f29f029 473 if (active_invaders <= 10) invaders_wait_time = 2;
cfavreau 0:c79e1f29f029 474 if (active_invaders <= 5) invaders_wait_time = 1;
cfavreau 0:c79e1f29f029 475 if (active_invaders <= 2) invaders_wait_time = 0;
cfavreau 0:c79e1f29f029 476 }
cfavreau 0:c79e1f29f029 477
cfavreau 0:c79e1f29f029 478 // Service any active shots
cfavreau 0:c79e1f29f029 479 for (int shot = 0; shot < 4; shot++)
cfavreau 0:c79e1f29f029 480 {
cfavreau 0:c79e1f29f029 481 if (invader_active_shot[shot])
cfavreau 0:c79e1f29f029 482 {
cfavreau 0:c79e1f29f029 483 // Advance downwards
cfavreau 0:c79e1f29f029 484 invader_shot_y[shot] += (2 * level); // Speed the shots up the higher up we get in the levels ... that is the difficulty setting
cfavreau 0:c79e1f29f029 485 disp.SetSpritePos(eSpriteInvaderShot1 + shot, invader_shot_x[shot], invader_shot_y[shot]);
cfavreau 0:c79e1f29f029 486 // Check to see if we are off the screen
cfavreau 0:c79e1f29f029 487 if (invader_shot_y[shot] > (LCD_HEIGHT - 4))
cfavreau 0:c79e1f29f029 488 {
cfavreau 0:c79e1f29f029 489 invader_active_shot[shot] = false;
cfavreau 0:c79e1f29f029 490 disp.EnableSprite(eSpriteInvaderShot1 + shot, false);
cfavreau 0:c79e1f29f029 491 }
cfavreau 0:c79e1f29f029 492 }
cfavreau 0:c79e1f29f029 493 }
cfavreau 0:c79e1f29f029 494 // Do the invader shooting
cfavreau 0:c79e1f29f029 495 invader_shot_timer--;
cfavreau 0:c79e1f29f029 496 if (invader_shot_timer < 0)
cfavreau 0:c79e1f29f029 497 {
cfavreau 0:c79e1f29f029 498 // Reset the shot timer
cfavreau 0:c79e1f29f029 499 invader_shot_timer = invader_shot_interval;
cfavreau 0:c79e1f29f029 500 // Find an inactive shot to use (up to 4 active at a time)
cfavreau 0:c79e1f29f029 501 int shot;
cfavreau 0:c79e1f29f029 502 for (shot = 0; shot < 4; shot++)
cfavreau 0:c79e1f29f029 503 {
cfavreau 0:c79e1f29f029 504 if (!invader_active_shot[shot])
cfavreau 0:c79e1f29f029 505 {
cfavreau 0:c79e1f29f029 506 // Pick a random invader for it to come from (there are 40 of them)
cfavreau 0:c79e1f29f029 507 int invader = rand() % active_invaders;
cfavreau 0:c79e1f29f029 508 // Figure out the active invader's coordinates
cfavreau 0:c79e1f29f029 509 for (int i = 0; i < 40; i++)
cfavreau 0:c79e1f29f029 510 {
cfavreau 0:c79e1f29f029 511 int index = (i + invader) % 40;
cfavreau 0:c79e1f29f029 512 if (invaders[index])
cfavreau 0:c79e1f29f029 513 {
cfavreau 0:c79e1f29f029 514 uint8_t color = 0;
cfavreau 0:c79e1f29f029 515 switch(index / 8)
cfavreau 0:c79e1f29f029 516 {
cfavreau 0:c79e1f29f029 517 case (0): color = PAL_WHITE; break;
cfavreau 0:c79e1f29f029 518 case (1): color = PAL_YELLOW; break;
cfavreau 0:c79e1f29f029 519 case (2): color = PAL_ORANGE; break;
cfavreau 0:c79e1f29f029 520 case (3): color = PAL_RED; break;
cfavreau 0:c79e1f29f029 521 case (4): color = PAL_GREEN; break;
cfavreau 0:c79e1f29f029 522 case (5): color = PAL_LIGHTBLUE; break;
cfavreau 0:c79e1f29f029 523 case (6): color = PAL_BLUE; break;
cfavreau 0:c79e1f29f029 524 case (7): color = PAL_MAGENTA; break;
cfavreau 0:c79e1f29f029 525 }
cfavreau 0:c79e1f29f029 526 // Initialize the shot
cfavreau 0:c79e1f29f029 527 invader_active_shot[shot] = true;
cfavreau 0:c79e1f29f029 528 invader_shot_x[shot] = invaders_x + ((index % 8) * 10) + 4;
cfavreau 0:c79e1f29f029 529 invader_shot_y[shot] = invaders_y + ((index / 8) * 10) + 4;
cfavreau 0:c79e1f29f029 530 disp.EnableSprite(eSpriteInvaderShot1 + shot, true);
cfavreau 0:c79e1f29f029 531 disp.SetSpriteColor(eSpriteInvaderShot1 + shot, color, PAL_TRANSPARENT);
cfavreau 0:c79e1f29f029 532 disp.SetSpritePos(eSpriteInvaderShot1 + shot, invader_shot_x[shot], invader_shot_y[shot]);
cfavreau 0:c79e1f29f029 533 sound.PlaySound(2, NOTE_C7, 20);
cfavreau 0:c79e1f29f029 534 break;
cfavreau 0:c79e1f29f029 535 }
cfavreau 0:c79e1f29f029 536 }
cfavreau 0:c79e1f29f029 537 break;
cfavreau 0:c79e1f29f029 538 }
cfavreau 0:c79e1f29f029 539 }
cfavreau 0:c79e1f29f029 540 }
cfavreau 0:c79e1f29f029 541 }
cfavreau 0:c79e1f29f029 542
cfavreau 0:c79e1f29f029 543 void Game::SetupPlayer()
cfavreau 0:c79e1f29f029 544 {
cfavreau 0:c79e1f29f029 545 player_x = LCD_WIDTH >> 1;
cfavreau 0:c79e1f29f029 546 player_y = LCD_HEIGHT - 10;
cfavreau 0:c79e1f29f029 547 disp.AddSprite(eSpritePlayer, eTilePlayer, player_x, player_y, PAL_LIGHTGREY, PAL_TRANSPARENT, true); // auto midhandle
cfavreau 0:c79e1f29f029 548 player_shot_active = false;
cfavreau 0:c79e1f29f029 549 player_shot_x = 0;
cfavreau 0:c79e1f29f029 550 player_shot_y = 0;
cfavreau 0:c79e1f29f029 551 disp.AddSprite(eSpritePlayerShot, eTilePlayerShot, player_shot_x, player_shot_y, PAL_WHITE, PAL_TRANSPARENT, true); // auto midhandle
cfavreau 0:c79e1f29f029 552 }
cfavreau 0:c79e1f29f029 553
cfavreau 0:c79e1f29f029 554 void Game::UpdatePlayer()
cfavreau 0:c79e1f29f029 555 {
cfavreau 0:c79e1f29f029 556 // Check to see if the left and right buttons have been hit
cfavreau 0:c79e1f29f029 557 if (inputs.pressed(eLeft))
cfavreau 0:c79e1f29f029 558 {
cfavreau 0:c79e1f29f029 559 player_x -= 2;
cfavreau 0:c79e1f29f029 560 if (player_x < 5) player_x = 5;
cfavreau 0:c79e1f29f029 561 }
cfavreau 0:c79e1f29f029 562 if (inputs.pressed(eRight))
cfavreau 0:c79e1f29f029 563 {
cfavreau 0:c79e1f29f029 564 player_x += 2;
cfavreau 0:c79e1f29f029 565 if (player_x > (LCD_WIDTH - 5)) player_x = (LCD_WIDTH - 5);
cfavreau 0:c79e1f29f029 566 }
cfavreau 0:c79e1f29f029 567 disp.SetSpritePos(eSpritePlayer, player_x, player_y);
cfavreau 0:c79e1f29f029 568 if ((inputs.hit(eSquare) || inputs.hit(eCircle)) && !player_shot_active)
cfavreau 0:c79e1f29f029 569 {
cfavreau 0:c79e1f29f029 570 player_shot_x = player_x;
cfavreau 0:c79e1f29f029 571 player_shot_y = player_y;
cfavreau 0:c79e1f29f029 572 player_shot_active = true;
cfavreau 0:c79e1f29f029 573 disp.EnableSprite(eSpritePlayerShot, true);
cfavreau 0:c79e1f29f029 574 sound.PlaySound(2, NOTE_C7, 20);
cfavreau 0:c79e1f29f029 575 }
cfavreau 0:c79e1f29f029 576 if (inputs.pressed(eSquare) && inputs.pressed(eCircle))
cfavreau 0:c79e1f29f029 577 {
cfavreau 0:c79e1f29f029 578 // Pause the game
cfavreau 0:c79e1f29f029 579 paused = true;
cfavreau 0:c79e1f29f029 580 }
cfavreau 0:c79e1f29f029 581 if (player_shot_active)
cfavreau 0:c79e1f29f029 582 {
cfavreau 0:c79e1f29f029 583 player_shot_y -= 5;
cfavreau 0:c79e1f29f029 584 if (player_shot_y < 5)
cfavreau 0:c79e1f29f029 585 {
cfavreau 0:c79e1f29f029 586 player_shot_active = false;
cfavreau 0:c79e1f29f029 587 disp.EnableSprite(eSpritePlayerShot, false);
cfavreau 0:c79e1f29f029 588 }
cfavreau 0:c79e1f29f029 589 disp.SetSpritePos(eSpritePlayerShot, player_shot_x, player_shot_y);
cfavreau 0:c79e1f29f029 590 }
cfavreau 0:c79e1f29f029 591 }
cfavreau 0:c79e1f29f029 592
cfavreau 0:c79e1f29f029 593 void Game::SetupSaucer()
cfavreau 0:c79e1f29f029 594 {
cfavreau 0:c79e1f29f029 595 saucer_x = 0;
cfavreau 0:c79e1f29f029 596 saucer_y = 10;
cfavreau 0:c79e1f29f029 597 disp.AddSprite(eSpriteSaucer, eTileSaucer, saucer_x, saucer_y, PAL_YELLOW, PAL_TRANSPARENT, true);
cfavreau 0:c79e1f29f029 598 disp.EnableSprite(eSpriteSaucer, false);
cfavreau 0:c79e1f29f029 599 saucer_timer = (rand() % 10) * FRAMES_PER_SECOND; // Random time every 10 seconds
cfavreau 0:c79e1f29f029 600 saucer_active = false;
cfavreau 0:c79e1f29f029 601 }
cfavreau 0:c79e1f29f029 602
cfavreau 0:c79e1f29f029 603 void Game::UpdateSaucer()
cfavreau 0:c79e1f29f029 604 {
cfavreau 0:c79e1f29f029 605 if (!saucer_active)
cfavreau 0:c79e1f29f029 606 {
cfavreau 0:c79e1f29f029 607 saucer_timer--;
cfavreau 0:c79e1f29f029 608 if (saucer_timer > 1) return;
cfavreau 0:c79e1f29f029 609 // Calculate a new time for the saucer to be active (after it completes its sequence)
cfavreau 0:c79e1f29f029 610 saucer_timer = (rand() % 10) * FRAMES_PER_SECOND; // Random time every 10 seconds
cfavreau 0:c79e1f29f029 611 // Time to go.. setup the saucer to go
cfavreau 0:c79e1f29f029 612 // Set the saucer active flag
cfavreau 0:c79e1f29f029 613 saucer_active = true;
cfavreau 0:c79e1f29f029 614 // Set the saucer movement increment (same speed and opposite direction as the invaders are currently moving)
cfavreau 0:c79e1f29f029 615 saucer_inc = invaders_inc * -1;
cfavreau 0:c79e1f29f029 616 // Set the saucers starting coordiantes
cfavreau 0:c79e1f29f029 617 if (saucer_inc > 0)
cfavreau 0:c79e1f29f029 618 saucer_x = 0; // left to right movement
cfavreau 0:c79e1f29f029 619 else
cfavreau 0:c79e1f29f029 620 saucer_x = LCD_WIDTH - 4; // right to left movement
cfavreau 0:c79e1f29f029 621 // Set the sprite position and enable the sprite
cfavreau 0:c79e1f29f029 622 disp.SetSpritePos(eSpriteSaucer, saucer_x, saucer_y);
cfavreau 0:c79e1f29f029 623 disp.EnableSprite(eSpriteSaucer, true);
cfavreau 0:c79e1f29f029 624 // Play the saucer song (looped)
cfavreau 0:c79e1f29f029 625 sound.PlaySong(saucer_song, 0, 130, true);
cfavreau 0:c79e1f29f029 626 // Setup the leds to blink
cfavreau 0:c79e1f29f029 627 lights.blink(1, 5, 1, -1);
cfavreau 0:c79e1f29f029 628 lights.blink(2, 5, 0, -1);
cfavreau 0:c79e1f29f029 629 // Set the saucer timer .. we will use it to divide the frame counter
cfavreau 0:c79e1f29f029 630 saucer_timer = 0;
cfavreau 0:c79e1f29f029 631 return;
cfavreau 0:c79e1f29f029 632 }
cfavreau 0:c79e1f29f029 633
cfavreau 0:c79e1f29f029 634 // Divide the frame counter to slow things down (same speed as invaders)
cfavreau 0:c79e1f29f029 635 saucer_timer++;
cfavreau 0:c79e1f29f029 636 if (saucer_timer < 5) return;
cfavreau 0:c79e1f29f029 637 saucer_timer = 0;
cfavreau 0:c79e1f29f029 638
cfavreau 0:c79e1f29f029 639 // Move the saucer
cfavreau 0:c79e1f29f029 640 saucer_x += saucer_inc;
cfavreau 0:c79e1f29f029 641 if ((saucer_x < 1) || (saucer_x > (LCD_WIDTH - 1)))
cfavreau 0:c79e1f29f029 642 {
cfavreau 0:c79e1f29f029 643 lights.set(1, false, 1);
cfavreau 0:c79e1f29f029 644 lights.set(2, false, 1);
cfavreau 0:c79e1f29f029 645 saucer_active = false;
cfavreau 0:c79e1f29f029 646 disp.EnableSprite(eSpriteSaucer, false);
cfavreau 0:c79e1f29f029 647 sound.StopSong();
cfavreau 0:c79e1f29f029 648 saucer_timer = (rand() % 10) * FRAMES_PER_SECOND; // Random time every 10 seconds
cfavreau 0:c79e1f29f029 649 }
cfavreau 0:c79e1f29f029 650 disp.SetSpritePos(eSpriteSaucer, saucer_x, saucer_y);
cfavreau 0:c79e1f29f029 651 }
cfavreau 0:c79e1f29f029 652
cfavreau 0:c79e1f29f029 653 void Game::SetupShields()
cfavreau 0:c79e1f29f029 654 {
cfavreau 0:c79e1f29f029 655 shield_x = LCD_WIDTH >> 1;
cfavreau 0:c79e1f29f029 656 shield_y = LCD_HEIGHT - 20;
cfavreau 0:c79e1f29f029 657 disp.AddSprite(eSpriteShield1, eTileShield1, shield_x - 45, shield_y, PAL_GREEN, PAL_TRANSPARENT, true);
cfavreau 0:c79e1f29f029 658 disp.AddSprite(eSpriteShield2, eTileShield1, shield_x - 15, shield_y, PAL_GREEN, PAL_TRANSPARENT, true);
cfavreau 0:c79e1f29f029 659 disp.AddSprite(eSpriteShield3, eTileShield1, shield_x + 15, shield_y, PAL_GREEN, PAL_TRANSPARENT, true);
cfavreau 0:c79e1f29f029 660 disp.AddSprite(eSpriteShield4, eTileShield1, shield_x + 45, shield_y, PAL_GREEN, PAL_TRANSPARENT, true);
cfavreau 0:c79e1f29f029 661 for (int i = 0; i < 4; i++)
cfavreau 0:c79e1f29f029 662 shield_points[i] = 4;
cfavreau 0:c79e1f29f029 663 }
cfavreau 0:c79e1f29f029 664
cfavreau 0:c79e1f29f029 665 void Game::UpdateShields()
cfavreau 0:c79e1f29f029 666 {
cfavreau 0:c79e1f29f029 667 // Todo - they don't move but they have hit points...
cfavreau 0:c79e1f29f029 668 for (int i = 0; i < 4; i++)
cfavreau 0:c79e1f29f029 669 {
cfavreau 0:c79e1f29f029 670 disp.SetSpriteChar(eSpriteShield1 + i, eTileShield1 + (4 - shield_points[i]));
cfavreau 0:c79e1f29f029 671 }
cfavreau 0:c79e1f29f029 672 }
cfavreau 0:c79e1f29f029 673
cfavreau 0:c79e1f29f029 674 void Game::CheckCollisions()
cfavreau 0:c79e1f29f029 675 {
cfavreau 0:c79e1f29f029 676 // Check the player's shot to see if it hits any:
cfavreau 0:c79e1f29f029 677 // Invader
cfavreau 0:c79e1f29f029 678 for (int i = 0; i < 40; i++)
cfavreau 0:c79e1f29f029 679 {
cfavreau 0:c79e1f29f029 680 // Check to see if this invader is active
cfavreau 0:c79e1f29f029 681 if (invaders[i])
cfavreau 0:c79e1f29f029 682 {
cfavreau 0:c79e1f29f029 683 if (disp.SpriteCollision(eSpritePlayerShot, eSpriteInvaderStart + i))
cfavreau 0:c79e1f29f029 684 {
cfavreau 0:c79e1f29f029 685 // Destroy the invader
cfavreau 0:c79e1f29f029 686 disp.EnableSprite(eSpriteInvaderStart + i, false);
cfavreau 0:c79e1f29f029 687 invaders[i] = false;
cfavreau 0:c79e1f29f029 688 active_invaders--;
cfavreau 0:c79e1f29f029 689 // Destroy the player's shot
cfavreau 0:c79e1f29f029 690 sound.PlaySound(1, NOTE_C6, 30);
cfavreau 0:c79e1f29f029 691 disp.EnableSprite(eSpritePlayerShot, false);
cfavreau 0:c79e1f29f029 692 player_shot_active = false;
cfavreau 0:c79e1f29f029 693 // Increment the score (more points for the ones at the top)
cfavreau 0:c79e1f29f029 694 // 5 points for the top row, 4 points for the next, etc...
cfavreau 0:c79e1f29f029 695 IncScore(5 - (i / 8));
cfavreau 0:c79e1f29f029 696 // Check to see if all of the invaders were destroyed
cfavreau 0:c79e1f29f029 697 if (active_invaders < 1)
cfavreau 0:c79e1f29f029 698 {
cfavreau 0:c79e1f29f029 699 // Level Up and go to the next step (for going to the next level)
cfavreau 0:c79e1f29f029 700 level++;
cfavreau 0:c79e1f29f029 701 game_step = 2;
cfavreau 0:c79e1f29f029 702 }
cfavreau 0:c79e1f29f029 703 }
cfavreau 0:c79e1f29f029 704 }
cfavreau 0:c79e1f29f029 705 }
cfavreau 0:c79e1f29f029 706 // Shield
cfavreau 0:c79e1f29f029 707 for (int i = 0; i < 4; i++)
cfavreau 0:c79e1f29f029 708 {
cfavreau 0:c79e1f29f029 709 if (disp.SpriteCollision(eSpritePlayerShot, eSpriteShield1 + i))
cfavreau 0:c79e1f29f029 710 {
cfavreau 0:c79e1f29f029 711 // Subtract 1 from the shield's points
cfavreau 0:c79e1f29f029 712 shield_points[i]--;
cfavreau 0:c79e1f29f029 713 if (shield_points[i] < 1)
cfavreau 0:c79e1f29f029 714 {
cfavreau 0:c79e1f29f029 715 // Disable this shield
cfavreau 0:c79e1f29f029 716 disp.EnableSprite(eSpriteShield1 + i, false);
cfavreau 0:c79e1f29f029 717 // TODO - Maybe make a noise...
cfavreau 0:c79e1f29f029 718 }
cfavreau 0:c79e1f29f029 719 // Destroy the player's shot
cfavreau 0:c79e1f29f029 720 sound.PlaySound(1, NOTE_C6, 30);
cfavreau 0:c79e1f29f029 721 disp.EnableSprite(eSpritePlayerShot, false);
cfavreau 0:c79e1f29f029 722 player_shot_active = false;
cfavreau 0:c79e1f29f029 723 }
cfavreau 0:c79e1f29f029 724 }
cfavreau 0:c79e1f29f029 725 // Invader Shot
cfavreau 0:c79e1f29f029 726 for (int i = 0; i < 4; i++)
cfavreau 0:c79e1f29f029 727 {
cfavreau 0:c79e1f29f029 728 if (disp.SpriteCollision(eSpritePlayerShot, eSpriteInvaderShot1 + i))
cfavreau 0:c79e1f29f029 729 {
cfavreau 0:c79e1f29f029 730 // Disable this invader shot
cfavreau 0:c79e1f29f029 731 invader_active_shot[i] = false;
cfavreau 0:c79e1f29f029 732 disp.EnableSprite(eSpriteInvaderShot1 + i, false);
cfavreau 0:c79e1f29f029 733 // Destroy the player's shot
cfavreau 0:c79e1f29f029 734 sound.PlaySound(1, NOTE_C6, 30);
cfavreau 0:c79e1f29f029 735 disp.EnableSprite(eSpritePlayerShot, false);
cfavreau 0:c79e1f29f029 736 player_shot_active = false;
cfavreau 0:c79e1f29f029 737 // 1 point for shooting an invader bullet
cfavreau 0:c79e1f29f029 738 IncScore(1);
cfavreau 0:c79e1f29f029 739 }
cfavreau 0:c79e1f29f029 740 }
cfavreau 0:c79e1f29f029 741 // Saucer
cfavreau 0:c79e1f29f029 742 if (disp.SpriteCollision(eSpritePlayerShot, eSpriteSaucer))
cfavreau 0:c79e1f29f029 743 {
cfavreau 0:c79e1f29f029 744 // Destroy the saucer
cfavreau 0:c79e1f29f029 745 lights.set(1, false, 1);
cfavreau 0:c79e1f29f029 746 lights.set(2, false, 1);
cfavreau 0:c79e1f29f029 747 saucer_active = false;
cfavreau 0:c79e1f29f029 748 disp.EnableSprite(eSpriteSaucer, false);
cfavreau 0:c79e1f29f029 749 sound.StopSong();
cfavreau 0:c79e1f29f029 750 saucer_timer = (rand() % 10) * FRAMES_PER_SECOND; // Random time every 10 seconds
cfavreau 0:c79e1f29f029 751 // Destroy the player's shot
cfavreau 0:c79e1f29f029 752 sound.PlaySound(1, NOTE_C6, 30);
cfavreau 0:c79e1f29f029 753 disp.EnableSprite(eSpritePlayerShot, false);
cfavreau 0:c79e1f29f029 754 player_shot_active = false;
cfavreau 0:c79e1f29f029 755 // 10 points for a saucer
cfavreau 0:c79e1f29f029 756 IncScore(10);
cfavreau 0:c79e1f29f029 757 }
cfavreau 0:c79e1f29f029 758
cfavreau 0:c79e1f29f029 759 // Check to see if the invader's shots hit any:
cfavreau 0:c79e1f29f029 760 // Shields
cfavreau 0:c79e1f29f029 761 // Player Ships
cfavreau 0:c79e1f29f029 762 for (int shot = 0; shot < 4; shot++)
cfavreau 0:c79e1f29f029 763 {
cfavreau 0:c79e1f29f029 764 if (!invader_active_shot[shot]) continue;
cfavreau 0:c79e1f29f029 765
cfavreau 0:c79e1f29f029 766 // Check the player
cfavreau 0:c79e1f29f029 767 if (disp.SpriteCollision(eSpritePlayer, eSpriteInvaderShot1 + shot))
cfavreau 0:c79e1f29f029 768 {
cfavreau 0:c79e1f29f029 769 // Play the Explosion sound
cfavreau 0:c79e1f29f029 770 sound.PlaySound(1, NOTE_C6, 30);
cfavreau 0:c79e1f29f029 771 // Destroy the shot
cfavreau 0:c79e1f29f029 772 invader_active_shot[shot] = false;
cfavreau 0:c79e1f29f029 773 disp.EnableSprite(eSpriteInvaderShot1 + shot, false);
cfavreau 0:c79e1f29f029 774 // Decrement the player lives
cfavreau 0:c79e1f29f029 775 lives--;
cfavreau 0:c79e1f29f029 776 // Go to the player blow up sequence
cfavreau 0:c79e1f29f029 777 game_step = 4; // we will check to see if the lives are 0 there!
cfavreau 0:c79e1f29f029 778 }
cfavreau 0:c79e1f29f029 779
cfavreau 0:c79e1f29f029 780 // Check the shields
cfavreau 0:c79e1f29f029 781 for (int shield = 0; shield < 4; shield++)
cfavreau 0:c79e1f29f029 782 {
cfavreau 0:c79e1f29f029 783 if (disp.SpriteCollision(eSpriteInvaderShot1 + shot, eSpriteShield1 + shield))
cfavreau 0:c79e1f29f029 784 {
cfavreau 0:c79e1f29f029 785 // Subtract 1 from the shield's points
cfavreau 0:c79e1f29f029 786 shield_points[shield]--;
cfavreau 0:c79e1f29f029 787 if (shield_points[shield] < 1)
cfavreau 0:c79e1f29f029 788 {
cfavreau 0:c79e1f29f029 789 // Disable this shield
cfavreau 0:c79e1f29f029 790 disp.EnableSprite(eSpriteShield1 + shield, false);
cfavreau 0:c79e1f29f029 791 // TODO - Maybe make a noise...
cfavreau 0:c79e1f29f029 792 }
cfavreau 0:c79e1f29f029 793 sound.PlaySound(1, NOTE_C6, 30);
cfavreau 0:c79e1f29f029 794 // Destroy the shot
cfavreau 0:c79e1f29f029 795 invader_active_shot[shot] = false;
cfavreau 0:c79e1f29f029 796 disp.EnableSprite(eSpriteInvaderShot1 + shot, false);
cfavreau 0:c79e1f29f029 797 }
cfavreau 0:c79e1f29f029 798 }
cfavreau 0:c79e1f29f029 799 }
cfavreau 0:c79e1f29f029 800 // Check to see if the invaders have reached the shields
cfavreau 0:c79e1f29f029 801 // Or if the invaders have reached the ship!
cfavreau 0:c79e1f29f029 802 for (int i = 0 ; i < 40; i++)
cfavreau 0:c79e1f29f029 803 {
cfavreau 0:c79e1f29f029 804 // Check only active invaders
cfavreau 0:c79e1f29f029 805 if (!invaders[i]) continue;
cfavreau 0:c79e1f29f029 806
cfavreau 0:c79e1f29f029 807 for (int shield = 0; shield < 4; shield++)
cfavreau 0:c79e1f29f029 808 {
cfavreau 0:c79e1f29f029 809 // Check only active shields
cfavreau 0:c79e1f29f029 810 if (shield_points[shield] < 1) continue;
cfavreau 0:c79e1f29f029 811 if (disp.SpriteCollision(eSpriteInvaderStart + i, eSpriteShield1 + shield))
cfavreau 0:c79e1f29f029 812 {
cfavreau 0:c79e1f29f029 813 // Destroy the shield
cfavreau 0:c79e1f29f029 814 shield_points[shield] = 0;
cfavreau 0:c79e1f29f029 815 disp.EnableSprite(eSpriteShield1 + shield, false);
cfavreau 0:c79e1f29f029 816 sound.PlaySound(1, NOTE_C6, 10);
cfavreau 0:c79e1f29f029 817 }
cfavreau 0:c79e1f29f029 818 }
cfavreau 0:c79e1f29f029 819
cfavreau 0:c79e1f29f029 820 if (disp.SpriteCollision(eSpriteInvaderStart + i, eSpritePlayer))
cfavreau 0:c79e1f29f029 821 {
cfavreau 0:c79e1f29f029 822 // Game Over!!!!
cfavreau 0:c79e1f29f029 823 lives = 0;
cfavreau 0:c79e1f29f029 824 // Go to the player blow up sequence
cfavreau 0:c79e1f29f029 825 game_step = 4; // we will check to see if the lives are 0 there!
cfavreau 0:c79e1f29f029 826 }
cfavreau 0:c79e1f29f029 827 }
cfavreau 0:c79e1f29f029 828 }
cfavreau 0:c79e1f29f029 829
cfavreau 0:c79e1f29f029 830 void Game::IncScore(int points)
cfavreau 0:c79e1f29f029 831 {
cfavreau 0:c79e1f29f029 832 // Increment the score
cfavreau 0:c79e1f29f029 833 score += points;
cfavreau 0:c79e1f29f029 834 // Update the hi score if necessary
cfavreau 0:c79e1f29f029 835 if (score > hiscore) hiscore = score;
cfavreau 0:c79e1f29f029 836 }
cfavreau 0:c79e1f29f029 837
cfavreau 0:c79e1f29f029 838 void Game::UpdateScore()
cfavreau 0:c79e1f29f029 839 {
cfavreau 0:c79e1f29f029 840 // Hi Score in the middle
cfavreau 0:c79e1f29f029 841 disp.printat(7, 0, "HI%04d", hiscore);
cfavreau 0:c79e1f29f029 842 // Current Score on the left
cfavreau 0:c79e1f29f029 843 disp.printat(0, 0, "%04d", score);
cfavreau 0:c79e1f29f029 844 }
cfavreau 0:c79e1f29f029 845
cfavreau 0:c79e1f29f029 846 void Game::UpdateLives()
cfavreau 0:c79e1f29f029 847 {
cfavreau 0:c79e1f29f029 848 // Men left in the upper right
cfavreau 0:c79e1f29f029 849 if (lives >= 1) disp.setcharat(CHAR_MAP_WIDTH - 1, 0, 128 + eTilePlayer, PAL_LIGHTBLUE, PAL_BLACK);
cfavreau 0:c79e1f29f029 850 if (lives >= 2) disp.setcharat(CHAR_MAP_WIDTH - 2, 0, 128 + eTilePlayer, PAL_LIGHTBLUE, PAL_BLACK);
cfavreau 0:c79e1f29f029 851 if (lives >= 3) disp.setcharat(CHAR_MAP_WIDTH - 3, 0, 128 + eTilePlayer, PAL_LIGHTBLUE, PAL_BLACK);
cfavreau 0:c79e1f29f029 852 }
cfavreau 0:c79e1f29f029 853
cfavreau 0:c79e1f29f029 854 void Game::DoGamePlay()
cfavreau 0:c79e1f29f029 855 {
cfavreau 0:c79e1f29f029 856 switch(game_step)
cfavreau 0:c79e1f29f029 857 {
cfavreau 0:c79e1f29f029 858 case (0):
cfavreau 0:c79e1f29f029 859 disp.clear();
cfavreau 0:c79e1f29f029 860 // Setup the score and lives
cfavreau 0:c79e1f29f029 861 score = 0;
cfavreau 0:c79e1f29f029 862 lives = 3;
cfavreau 0:c79e1f29f029 863 // DEBUG
cfavreau 0:c79e1f29f029 864 //mode = eGameOver; break;
cfavreau 0:c79e1f29f029 865 // Sprites
cfavreau 0:c79e1f29f029 866 // Invaders ( 8 x 5 = 40 total - starts at sprite #10)
cfavreau 0:c79e1f29f029 867 SetupInvaders();
cfavreau 0:c79e1f29f029 868 // Shields ( 3 shields)
cfavreau 0:c79e1f29f029 869 SetupShields();
cfavreau 0:c79e1f29f029 870 // Player ( 1 player)
cfavreau 0:c79e1f29f029 871 SetupPlayer();
cfavreau 0:c79e1f29f029 872 // Saucer ( 1 saucer)
cfavreau 0:c79e1f29f029 873 SetupSaucer();
cfavreau 0:c79e1f29f029 874 // Setup the playfield
cfavreau 0:c79e1f29f029 875 SetupStarfield();
cfavreau 0:c79e1f29f029 876 // Draw the taunt
cfavreau 0:c79e1f29f029 877 disp.printat((CHAR_MAP_WIDTH >> 1) - 8, 8, "We Are Coming...");
cfavreau 0:c79e1f29f029 878 // DEBUG - kill most of the invaders
cfavreau 0:c79e1f29f029 879 /*(
cfavreau 0:c79e1f29f029 880 for (int i = 0; i < 35; i++)
cfavreau 0:c79e1f29f029 881 {
cfavreau 0:c79e1f29f029 882 invaders[i] = false;
cfavreau 0:c79e1f29f029 883 active_invaders--;
cfavreau 0:c79e1f29f029 884 }
cfavreau 0:c79e1f29f029 885 */
cfavreau 0:c79e1f29f029 886 // Next step
cfavreau 0:c79e1f29f029 887 game_step = 1;
cfavreau 0:c79e1f29f029 888 break;
cfavreau 0:c79e1f29f029 889 case (1):
cfavreau 0:c79e1f29f029 890 // This is the main game loop here
cfavreau 0:c79e1f29f029 891 // Check for Input
cfavreau 0:c79e1f29f029 892 // Update the star field
cfavreau 0:c79e1f29f029 893 UpdateStarfield();
cfavreau 0:c79e1f29f029 894 // Move Invaders
cfavreau 0:c79e1f29f029 895 UpdateInvaders();
cfavreau 0:c79e1f29f029 896 // Move Player
cfavreau 0:c79e1f29f029 897 UpdatePlayer();
cfavreau 0:c79e1f29f029 898 // Update the shields
cfavreau 0:c79e1f29f029 899 UpdateShields();
cfavreau 0:c79e1f29f029 900 // Update the saucer
cfavreau 0:c79e1f29f029 901 UpdateSaucer();
cfavreau 0:c79e1f29f029 902 // Update the rest of the screen
cfavreau 0:c79e1f29f029 903 UpdateScore();
cfavreau 0:c79e1f29f029 904 UpdateLives();
cfavreau 0:c79e1f29f029 905 // Make things shoot each other
cfavreau 0:c79e1f29f029 906 // Check for collisions
cfavreau 0:c79e1f29f029 907 CheckCollisions();
cfavreau 0:c79e1f29f029 908 // Check for Game Over
cfavreau 0:c79e1f29f029 909
cfavreau 0:c79e1f29f029 910 // dEBUG
cfavreau 0:c79e1f29f029 911 //disp.printat(0, 15, "%d", active_invaders);
cfavreau 0:c79e1f29f029 912 break;
cfavreau 0:c79e1f29f029 913 case (2):
cfavreau 0:c79e1f29f029 914 // Next Level
cfavreau 0:c79e1f29f029 915 UpdateStarfield();
cfavreau 0:c79e1f29f029 916 UpdateScore();
cfavreau 0:c79e1f29f029 917 UpdateLives();
cfavreau 0:c79e1f29f029 918 // Play the intro song
cfavreau 0:c79e1f29f029 919 sound.PlaySong(intro_song, 0, 130, false);
cfavreau 0:c79e1f29f029 920 // Print out a taunt
cfavreau 0:c79e1f29f029 921 disp.printat((CHAR_MAP_WIDTH >> 1) - 3, 8, "Ready?");
cfavreau 0:c79e1f29f029 922 // Set the wait timer
cfavreau 0:c79e1f29f029 923 wait_timer = 20;
cfavreau 0:c79e1f29f029 924 // Next step to do the waiting
cfavreau 0:c79e1f29f029 925 game_step = 3;
cfavreau 0:c79e1f29f029 926 break;
cfavreau 0:c79e1f29f029 927 case (3):
cfavreau 0:c79e1f29f029 928 // Wait a few ticks before putting the invaders up
cfavreau 0:c79e1f29f029 929 UpdateStarfield();
cfavreau 0:c79e1f29f029 930 UpdateScore();
cfavreau 0:c79e1f29f029 931 UpdateLives();
cfavreau 0:c79e1f29f029 932 wait_timer--;
cfavreau 0:c79e1f29f029 933 if (wait_timer == 0)
cfavreau 0:c79e1f29f029 934 {
cfavreau 0:c79e1f29f029 935 // Initialize the board again
cfavreau 0:c79e1f29f029 936 // Sprites
cfavreau 0:c79e1f29f029 937 // Invaders ( 8 x 5 = 40 total - starts at sprite #10)
cfavreau 0:c79e1f29f029 938 SetupInvaders();
cfavreau 0:c79e1f29f029 939 // Shields ( 3 shields)
cfavreau 0:c79e1f29f029 940 SetupShields();
cfavreau 0:c79e1f29f029 941 // Player ( 1 player)
cfavreau 0:c79e1f29f029 942 SetupPlayer();
cfavreau 0:c79e1f29f029 943 // Saucer ( 1 saucer)
cfavreau 0:c79e1f29f029 944 SetupSaucer();
cfavreau 0:c79e1f29f029 945 // Setup the playfield
cfavreau 0:c79e1f29f029 946 SetupStarfield();
cfavreau 0:c79e1f29f029 947 // Draw the taunt
cfavreau 0:c79e1f29f029 948 disp.printat((CHAR_MAP_WIDTH >> 1) - 8, 8, "We Are Coming...");
cfavreau 0:c79e1f29f029 949 // Next step
cfavreau 0:c79e1f29f029 950 game_step = 1;
cfavreau 0:c79e1f29f029 951 }
cfavreau 0:c79e1f29f029 952 break;
cfavreau 0:c79e1f29f029 953 case (4):
cfavreau 0:c79e1f29f029 954 // Set the wait timer
cfavreau 0:c79e1f29f029 955 wait_timer = 20;
cfavreau 0:c79e1f29f029 956 // Play a LOOONG blow up sound
cfavreau 0:c79e1f29f029 957 sound.PlaySound(1, NOTE_C6, 60);
cfavreau 0:c79e1f29f029 958 // Taunt the player!
cfavreau 0:c79e1f29f029 959 disp.setforecolor(PAL_RED);
cfavreau 0:c79e1f29f029 960 disp.printat((CHAR_MAP_WIDTH >> 1) - 4, 8, "Winning!");
cfavreau 0:c79e1f29f029 961 disp.setforecolor(PAL_WHITE);
cfavreau 0:c79e1f29f029 962 // Next step to wait and do the player explosion sequence
cfavreau 0:c79e1f29f029 963 game_step = 5;
cfavreau 0:c79e1f29f029 964 break;
cfavreau 0:c79e1f29f029 965 case (5):
cfavreau 0:c79e1f29f029 966 // Do the player blow up sequence
cfavreau 0:c79e1f29f029 967 disp.SetSpriteChar(eSpritePlayer, eTileStarfield1 + (wait_timer % 3));
cfavreau 0:c79e1f29f029 968 disp.SetSpriteColor(eSpritePlayer, PAL_RED, PAL_TRANSPARENT);
cfavreau 0:c79e1f29f029 969 wait_timer--;
cfavreau 0:c79e1f29f029 970 // When we are done go back to the play the game step
cfavreau 0:c79e1f29f029 971 if (wait_timer < 1)
cfavreau 0:c79e1f29f029 972 {
cfavreau 0:c79e1f29f029 973 game_step = 1;
cfavreau 0:c79e1f29f029 974 // Set the player sprite back to normal
cfavreau 0:c79e1f29f029 975 disp.SetSpriteChar(eSpritePlayer, eTilePlayer);
cfavreau 0:c79e1f29f029 976 disp.SetSpriteColor(eSpritePlayer, PAL_GREY, PAL_TRANSPARENT);
cfavreau 0:c79e1f29f029 977 // check to see if the lives have reached 0
cfavreau 0:c79e1f29f029 978 if (lives < 1)
cfavreau 0:c79e1f29f029 979 {
cfavreau 0:c79e1f29f029 980 // Disable the player sprite
cfavreau 0:c79e1f29f029 981 disp.EnableSprite(eSpritePlayer, false);
cfavreau 0:c79e1f29f029 982 // Switch the mode to Game Over
cfavreau 0:c79e1f29f029 983 game_step = 0;
cfavreau 0:c79e1f29f029 984 mode = eGameOver;
cfavreau 0:c79e1f29f029 985 // The Invaders have Arrived!
cfavreau 0:c79e1f29f029 986 }
cfavreau 0:c79e1f29f029 987 }
cfavreau 0:c79e1f29f029 988 break;
cfavreau 0:c79e1f29f029 989 }
cfavreau 0:c79e1f29f029 990 }
cfavreau 0:c79e1f29f029 991
cfavreau 0:c79e1f29f029 992 void Game::UpdateGameOverInvaders()
cfavreau 0:c79e1f29f029 993 {
cfavreau 0:c79e1f29f029 994 uint8_t color = 0;
cfavreau 0:c79e1f29f029 995
cfavreau 0:c79e1f29f029 996 // Scroll the whole field up 1 tile
cfavreau 0:c79e1f29f029 997 disp.shift_map(eShiftUp);
cfavreau 0:c79e1f29f029 998
cfavreau 0:c79e1f29f029 999 // Pick a color
cfavreau 0:c79e1f29f029 1000 switch(invaders_frame % 8)
cfavreau 0:c79e1f29f029 1001 {
cfavreau 0:c79e1f29f029 1002 case (0): color = PAL_WHITE; break;
cfavreau 0:c79e1f29f029 1003 case (1): color = PAL_YELLOW; break;
cfavreau 0:c79e1f29f029 1004 case (2): color = PAL_ORANGE; break;
cfavreau 0:c79e1f29f029 1005 case (3): color = PAL_RED; break;
cfavreau 0:c79e1f29f029 1006 case (4): color = PAL_GREEN; break;
cfavreau 0:c79e1f29f029 1007 case (5): color = PAL_LIGHTBLUE; break;
cfavreau 0:c79e1f29f029 1008 case (6): color = PAL_BLUE; break;
cfavreau 0:c79e1f29f029 1009 case (7): color = PAL_MAGENTA; break;
cfavreau 0:c79e1f29f029 1010 }
cfavreau 0:c79e1f29f029 1011
cfavreau 0:c79e1f29f029 1012 // Fill the top with invaders of a color
cfavreau 0:c79e1f29f029 1013 for (int i = 0; i < CHAR_MAP_WIDTH; i++)
cfavreau 0:c79e1f29f029 1014 {
cfavreau 0:c79e1f29f029 1015 disp.setcharat(i, CHAR_MAP_HEIGHT - 1, 128 + eTileInvader1 + (invaders_frame % 4), color, PAL_BLACK);
cfavreau 0:c79e1f29f029 1016 }
cfavreau 0:c79e1f29f029 1017
cfavreau 0:c79e1f29f029 1018 // Next frame
cfavreau 0:c79e1f29f029 1019 invaders_frame++;
cfavreau 0:c79e1f29f029 1020 }
cfavreau 0:c79e1f29f029 1021
cfavreau 0:c79e1f29f029 1022 void Game::DoGameOver()
cfavreau 0:c79e1f29f029 1023 {
cfavreau 0:c79e1f29f029 1024 switch (game_step)
cfavreau 0:c79e1f29f029 1025 {
cfavreau 0:c79e1f29f029 1026 case (0):
cfavreau 0:c79e1f29f029 1027 // Disable all the sprites
cfavreau 0:c79e1f29f029 1028 for (int i = 0; i < MAX_SPRITES; i++)
cfavreau 0:c79e1f29f029 1029 disp.EnableSprite(i, false);
cfavreau 0:c79e1f29f029 1030 // Turn off the leds
cfavreau 0:c79e1f29f029 1031 lights.set(1, false, 1);
cfavreau 0:c79e1f29f029 1032 lights.set(2, false, 1);
cfavreau 0:c79e1f29f029 1033 // Stop the song
cfavreau 0:c79e1f29f029 1034 sound.StopSong();
cfavreau 0:c79e1f29f029 1035 // initialize the game over screen stuff...
cfavreau 0:c79e1f29f029 1036 // play the game over tune
cfavreau 0:c79e1f29f029 1037 wait_timer = 0;
cfavreau 0:c79e1f29f029 1038 star_field_timer = 0;
cfavreau 0:c79e1f29f029 1039 invader_march_note = 0;
cfavreau 0:c79e1f29f029 1040 invaders_frame = 0;
cfavreau 0:c79e1f29f029 1041 game_over_msg = 0;
cfavreau 0:c79e1f29f029 1042 // Setup the coin sound
cfavreau 0:c79e1f29f029 1043 sound.SetInstrument(2, SOUND_SQUARE, 128, 10, 0, 120, 6, 0, 0, 0); // Coin Sound (Change + Decay)
cfavreau 0:c79e1f29f029 1044 // Next step
cfavreau 0:c79e1f29f029 1045 game_step = 1;
cfavreau 0:c79e1f29f029 1046 break;
cfavreau 0:c79e1f29f029 1047 case (1):
cfavreau 0:c79e1f29f029 1048 // Scroll the screen down and fill with invaders
cfavreau 0:c79e1f29f029 1049 // While playing the march
cfavreau 0:c79e1f29f029 1050
cfavreau 0:c79e1f29f029 1051 // Use the starfield timer to slow down the scroll
cfavreau 0:c79e1f29f029 1052 star_field_timer++;
cfavreau 0:c79e1f29f029 1053 if (star_field_timer < 10) return;
cfavreau 0:c79e1f29f029 1054 star_field_timer = 0;
cfavreau 0:c79e1f29f029 1055
cfavreau 0:c79e1f29f029 1056 UpdateGameOverInvaders();
cfavreau 0:c79e1f29f029 1057
cfavreau 0:c79e1f29f029 1058 // Play the march sound
cfavreau 0:c79e1f29f029 1059 sound.PlaySound(0, invader_march[invader_march_note++], 10);
cfavreau 0:c79e1f29f029 1060 if (invader_march_note >= 4) invader_march_note = 0;
cfavreau 0:c79e1f29f029 1061
cfavreau 0:c79e1f29f029 1062 // Subtract one from the wait timer
cfavreau 0:c79e1f29f029 1063 wait_timer++;
cfavreau 0:c79e1f29f029 1064 if (wait_timer >= CHAR_MAP_HEIGHT)
cfavreau 0:c79e1f29f029 1065 {
cfavreau 0:c79e1f29f029 1066 game_step = 2;
cfavreau 0:c79e1f29f029 1067 wait_timer = 0;
cfavreau 0:c79e1f29f029 1068 }
cfavreau 0:c79e1f29f029 1069 break;
cfavreau 0:c79e1f29f029 1070 case (2):
cfavreau 0:c79e1f29f029 1071 // Empty step
cfavreau 0:c79e1f29f029 1072 // Clear the wait timer
cfavreau 0:c79e1f29f029 1073 wait_timer = 0;
cfavreau 0:c79e1f29f029 1074 // Next step
cfavreau 0:c79e1f29f029 1075 game_step = 3;
cfavreau 0:c79e1f29f029 1076 break;
cfavreau 0:c79e1f29f029 1077 case (3):
cfavreau 0:c79e1f29f029 1078
cfavreau 0:c79e1f29f029 1079 // Use the starfield timer to slow down the scroll
cfavreau 0:c79e1f29f029 1080 star_field_timer++;
cfavreau 0:c79e1f29f029 1081 if (star_field_timer < 10) return;
cfavreau 0:c79e1f29f029 1082 star_field_timer = 0;
cfavreau 0:c79e1f29f029 1083
cfavreau 0:c79e1f29f029 1084 UpdateGameOverInvaders();
cfavreau 0:c79e1f29f029 1085
cfavreau 0:c79e1f29f029 1086 wait_timer++;
cfavreau 0:c79e1f29f029 1087 if (wait_timer > 3)
cfavreau 0:c79e1f29f029 1088 {
cfavreau 0:c79e1f29f029 1089 wait_timer = 0;
cfavreau 0:c79e1f29f029 1090 game_step = 4;
cfavreau 0:c79e1f29f029 1091 }
cfavreau 0:c79e1f29f029 1092 // Wait a few frames... then print a thanks to outrageous circuits / GHI
cfavreau 0:c79e1f29f029 1093 break;
cfavreau 0:c79e1f29f029 1094 case (4):
cfavreau 0:c79e1f29f029 1095 {
cfavreau 0:c79e1f29f029 1096 char msg[16] = "";
cfavreau 0:c79e1f29f029 1097
cfavreau 0:c79e1f29f029 1098 switch (game_over_msg)
cfavreau 0:c79e1f29f029 1099 {
cfavreau 0:c79e1f29f029 1100 case (0): sprintf(msg, "The Invaders..."); break;
cfavreau 0:c79e1f29f029 1101 case (1): sprintf(msg, "Have Arrived!"); break;
cfavreau 0:c79e1f29f029 1102 case (2): sprintf(msg, "Game Over!"); break;
cfavreau 0:c79e1f29f029 1103 case (3): sprintf(msg, "Thanks To:"); break;
cfavreau 0:c79e1f29f029 1104 case (4): sprintf(msg, "outrageous"); break;
cfavreau 0:c79e1f29f029 1105 case (5): sprintf(msg, "circuits.com"); break;
cfavreau 0:c79e1f29f029 1106 case (6): sprintf(msg, "GHI"); break;
cfavreau 0:c79e1f29f029 1107 case (7): sprintf(msg, ""); break;
cfavreau 0:c79e1f29f029 1108 case (8): sprintf(msg, "written by"); break;
cfavreau 0:c79e1f29f029 1109 case (9): sprintf(msg, "cfavreau"); break;
cfavreau 0:c79e1f29f029 1110 case (10): game_step = 5; return;
cfavreau 0:c79e1f29f029 1111 }
cfavreau 0:c79e1f29f029 1112 uint8_t len = strlen(msg);
cfavreau 0:c79e1f29f029 1113 disp.printat((CHAR_MAP_WIDTH >> 1) - (len >> 1), CHAR_MAP_HEIGHT - 1, msg);
cfavreau 0:c79e1f29f029 1114 // Play the coin sound
cfavreau 0:c79e1f29f029 1115 sound.PlaySound(2, NOTE_Fs6, 20);
cfavreau 0:c79e1f29f029 1116 // Next message
cfavreau 0:c79e1f29f029 1117 game_over_msg++;
cfavreau 0:c79e1f29f029 1118 game_step = 3;
cfavreau 0:c79e1f29f029 1119 }
cfavreau 0:c79e1f29f029 1120 break;
cfavreau 0:c79e1f29f029 1121 default:
cfavreau 0:c79e1f29f029 1122 case (5):
cfavreau 0:c79e1f29f029 1123 // Reset the game to the title sequence
cfavreau 0:c79e1f29f029 1124 mode = eGameTitle;
cfavreau 0:c79e1f29f029 1125 game_step = 0;
cfavreau 0:c79e1f29f029 1126 // Clear the button hits
cfavreau 0:c79e1f29f029 1127 inputs.clear();
cfavreau 0:c79e1f29f029 1128 break;
cfavreau 0:c79e1f29f029 1129 }
cfavreau 0:c79e1f29f029 1130 }
cfavreau 0:c79e1f29f029 1131