Version of Robotron arcade game using LPC1768, a Gameduino shield, a serial EEPROM (for high scores), two microswitch joysticks and two buttons plus a box to put it in. 20 levels of mayhem.

Dependencies:   25LCxxx_SPI CommonTypes Gameduino mbed

Committer:
RichardE
Date:
Mon Jun 10 20:02:28 2013 +0000
Revision:
12:81926431fea7
Parent:
10:bfa1c307c99d
Child:
13:50779b12ff51
Changed so you can specify how many humans you want on each level. Mainly messing around with LevelDescriptor and LevelCollection classes.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
RichardE 0:5fa232ee5fdf 1 /*
RichardE 0:5fa232ee5fdf 2 * SOURCE FILE : LevelNormal.cpp
RichardE 0:5fa232ee5fdf 3 *
RichardE 0:5fa232ee5fdf 4 * Definition of class LevelNormal.
RichardE 0:5fa232ee5fdf 5 * Base class for all "normal" levels.
RichardE 0:5fa232ee5fdf 6 * i.e. Levels that are not special attract modes
RichardE 0:5fa232ee5fdf 7 * but have enemies who are trying to kill you
RichardE 0:5fa232ee5fdf 8 * and so on.
RichardE 0:5fa232ee5fdf 9 *
RichardE 0:5fa232ee5fdf 10 */
RichardE 0:5fa232ee5fdf 11
RichardE 5:0b0651ac7832 12 // Define this for debugging messages.
RichardE 5:0b0651ac7832 13 #define CHATTY
RichardE 5:0b0651ac7832 14
RichardE 5:0b0651ac7832 15 #ifdef CHATTY
RichardE 5:0b0651ac7832 16 #include "mbed.h"
RichardE 5:0b0651ac7832 17 extern Serial pc;
RichardE 5:0b0651ac7832 18 #endif
RichardE 5:0b0651ac7832 19
RichardE 0:5fa232ee5fdf 20 #include "LevelNormal.h"
RichardE 5:0b0651ac7832 21 #include "GameObjectLocator.h"
RichardE 5:0b0651ac7832 22 #include "FrameCounter.h"
RichardE 5:0b0651ac7832 23 #include "SpriteNumber.h"
RichardE 5:0b0651ac7832 24 #include "ArenaConst.h"
RichardE 10:bfa1c307c99d 25 #include "EnemyFactory.h"
RichardE 0:5fa232ee5fdf 26
RichardE 0:5fa232ee5fdf 27 // Current instance being processed.
RichardE 0:5fa232ee5fdf 28 LevelNormal *LevelNormal::currentInstance;
RichardE 0:5fa232ee5fdf 29
RichardE 0:5fa232ee5fdf 30 /***************/
RichardE 0:5fa232ee5fdf 31 /* CONSTRUCTOR */
RichardE 0:5fa232ee5fdf 32 /***************/
RichardE 10:bfa1c307c99d 33 // Pass pointer to level descriptor data in data parameter.
RichardE 12:81926431fea7 34 LevelNormal::LevelNormal( const LevelDescriptor *data ) :
RichardE 10:bfa1c307c99d 35 descriptorData( data )
RichardE 5:0b0651ac7832 36 {
RichardE 10:bfa1c307c99d 37 // Normal levels are always dynamically alocated.
RichardE 10:bfa1c307c99d 38 IsDynamicallyAllocated = true;
RichardE 0:5fa232ee5fdf 39 }
RichardE 0:5fa232ee5fdf 40
RichardE 0:5fa232ee5fdf 41 /**************/
RichardE 0:5fa232ee5fdf 42 /* DESTRUCTOR */
RichardE 0:5fa232ee5fdf 43 /**************/
RichardE 5:0b0651ac7832 44 LevelNormal::~LevelNormal()
RichardE 5:0b0651ac7832 45 {
RichardE 0:5fa232ee5fdf 46 }
RichardE 0:5fa232ee5fdf 47
RichardE 0:5fa232ee5fdf 48 /********************/
RichardE 0:5fa232ee5fdf 49 /* INITIALISE LEVEL */
RichardE 0:5fa232ee5fdf 50 /********************/
RichardE 5:0b0651ac7832 51 // Pass pointer to Gameduino to draw on in gd.
RichardE 5:0b0651ac7832 52 void LevelNormal::InitialiseLevel( Gameduino *gd )
RichardE 5:0b0651ac7832 53 {
RichardE 0:5fa232ee5fdf 54 // Note that if you re-arrange the following code you may need to adjust the
RichardE 0:5fa232ee5fdf 55 // SpriteNumber enumeration in SpriteNumber.h.
RichardE 5:0b0651ac7832 56 UInt8 spriteNumber = FirstEnemySprite;
RichardE 5:0b0651ac7832 57 // Initialise enemies.
RichardE 10:bfa1c307c99d 58 GameObject::InitialiseAll( dataForLevel.Enemies, LevelData::MaxEnemies, &spriteNumber );
RichardE 5:0b0651ac7832 59 // Initialise humans.
RichardE 0:5fa232ee5fdf 60 spriteNumber = FirstHumanSprite;
RichardE 10:bfa1c307c99d 61 GameObject::InitialiseAll( dataForLevel.Humans, LevelData::MaxHumans, &spriteNumber );
RichardE 5:0b0651ac7832 62 // Use next free sprite number for player.
RichardE 5:0b0651ac7832 63 player->SpriteNumber = PlayerSprite;
RichardE 5:0b0651ac7832 64 // Do futher initialisation for all enemies.
RichardE 5:0b0651ac7832 65 EnemyObject *object;
RichardE 5:0b0651ac7832 66 for( UInt8 e = 0; e < LevelData::MaxEnemies; ++e ) {
RichardE 10:bfa1c307c99d 67 object = (EnemyObject*)dataForLevel.Enemies[ e ];
RichardE 5:0b0651ac7832 68 if( object != (EnemyObject*)NULL ) {
RichardE 0:5fa232ee5fdf 69 // Get enemy to chase the player.
RichardE 5:0b0651ac7832 70 object->SetChaseObject( player );
RichardE 0:5fa232ee5fdf 71 // Pass array of all enemies to this enemy.
RichardE 10:bfa1c307c99d 72 object->Enemies = dataForLevel.Enemies;
RichardE 0:5fa232ee5fdf 73 // If enemy is a brain then tell it about the humans to chase.
RichardE 0:5fa232ee5fdf 74 if( object->GetEnemyType() == Brain ) {
RichardE 10:bfa1c307c99d 75 ((BrainObject*)object)->HumansToChase = dataForLevel.Humans;
RichardE 0:5fa232ee5fdf 76 }
RichardE 5:0b0651ac7832 77 }
RichardE 0:5fa232ee5fdf 78 }
RichardE 5:0b0651ac7832 79 // Put player in the centre of the arena.
RichardE 5:0b0651ac7832 80 player->Xco = PLAYER_START_X;
RichardE 5:0b0651ac7832 81 player->Yco = PLAYER_START_Y;
RichardE 5:0b0651ac7832 82 // Kill off all player's bullets.
RichardE 5:0b0651ac7832 83 player->KillAllBullets( gd );
RichardE 5:0b0651ac7832 84 // Kill off all explosions.
RichardE 8:82d88f9381f3 85 ExplosionManager::Instance.KillAllExplosions();
RichardE 0:5fa232ee5fdf 86 }
RichardE 0:5fa232ee5fdf 87
RichardE 0:5fa232ee5fdf 88 /**********************************/
RichardE 0:5fa232ee5fdf 89 /* DRAW SCORE AND NUMBER OF LIVES */
RichardE 0:5fa232ee5fdf 90 /**********************************/
RichardE 5:0b0651ac7832 91 void LevelNormal::DrawScoreAndLives( void )
RichardE 5:0b0651ac7832 92 {
RichardE 5:0b0651ac7832 93 GDExtra::WriteBCDNumber( gd, 16, 0, player->Score, 8 );
RichardE 5:0b0651ac7832 94 // Display number of lives but limit this to 20 lives displayed.
RichardE 5:0b0651ac7832 95 UInt8 lives = ( player->Lives > 20 ) ? 20 : player->Lives;
RichardE 5:0b0651ac7832 96 gd->fill( Gameduino::RAM_PIC + VISIBLE_CHAR_WIDTH - lives, MiniPlayer, lives );
RichardE 0:5fa232ee5fdf 97 }
RichardE 0:5fa232ee5fdf 98
RichardE 0:5fa232ee5fdf 99 /******************/
RichardE 0:5fa232ee5fdf 100 /* DRAW THE LEVEL */
RichardE 0:5fa232ee5fdf 101 /******************/
RichardE 5:0b0651ac7832 102 void LevelNormal::DrawLevel( void )
RichardE 5:0b0651ac7832 103 {
RichardE 5:0b0651ac7832 104 // Set screen background to black.
RichardE 5:0b0651ac7832 105 gd->wr( Gameduino::BG_COLOR, Gameduino::RGB( 0, 0, 0 ) );
RichardE 5:0b0651ac7832 106 // Clear the screen to zero characters.
RichardE 5:0b0651ac7832 107 GDExtra::ClearScreen( gd, TransparentChar );
RichardE 5:0b0651ac7832 108 // Hide all sprties.
RichardE 5:0b0651ac7832 109 GDExtra::HideAllSprites( gd );
RichardE 5:0b0651ac7832 110 // Display level number.
RichardE 5:0b0651ac7832 111 GDExtra::WriteProgString( gd, 0, 0, StringData::LevelString );
RichardE 5:0b0651ac7832 112 GDExtra::WriteUInt16( gd, 6, 0, LevelNumber, 10, 2 );
RichardE 5:0b0651ac7832 113 // Display score.
RichardE 5:0b0651ac7832 114 GDExtra::WriteProgString( gd, 10, 0, StringData::ScoreString );
RichardE 5:0b0651ac7832 115 // Update score and lives.
RichardE 5:0b0651ac7832 116 DrawScoreAndLives();
RichardE 5:0b0651ac7832 117 // Draw border around screen.
RichardE 5:0b0651ac7832 118 CharFrame::Draw(
RichardE 5:0b0651ac7832 119 gd,
RichardE 5:0b0651ac7832 120 ARENA_BORDER_X,
RichardE 5:0b0651ac7832 121 ARENA_BORDER_Y,
RichardE 5:0b0651ac7832 122 ARENA_BORDER_WIDTH,
RichardE 5:0b0651ac7832 123 ARENA_BORDER_HEIGHT
RichardE 5:0b0651ac7832 124 );
RichardE 0:5fa232ee5fdf 125 }
RichardE 0:5fa232ee5fdf 126
RichardE 0:5fa232ee5fdf 127 /************************************************/
RichardE 0:5fa232ee5fdf 128 /* HANDLE COLLISIONS BETWEEN HUMANS AND ENEMIES */
RichardE 0:5fa232ee5fdf 129 /************************************************/
RichardE 0:5fa232ee5fdf 130 // Pass index of human in level's humans array in humanIndex.
RichardE 0:5fa232ee5fdf 131 // Pass sprite number of sprite that it hit in spriteNumber.
RichardE 5:0b0651ac7832 132 void LevelNormal::HandleHumanCollision( UInt8 humanIndex, UInt8 spriteNumber )
RichardE 5:0b0651ac7832 133 {
RichardE 5:0b0651ac7832 134 // Point to array of enemy object pointers.
RichardE 10:bfa1c307c99d 135 GameObject **enemies = currentInstance->dataForLevel.Enemies;
RichardE 5:0b0651ac7832 136 EnemyObject *enemy;
RichardE 5:0b0651ac7832 137 UInt8 enemyIndex, mutantIndex;
RichardE 5:0b0651ac7832 138 // Find an enemy with given sprite number.
RichardE 5:0b0651ac7832 139 if( GameObject::FindSpriteNumber( enemies, LevelData::MaxEnemies, spriteNumber, &enemyIndex ) ) {
RichardE 5:0b0651ac7832 140 // Found enemy. Check if it squashes humans.
RichardE 5:0b0651ac7832 141 enemy = (EnemyObject*)enemies[ enemyIndex ];
RichardE 0:5fa232ee5fdf 142 // Get hold of the human that is doomed.
RichardE 10:bfa1c307c99d 143 GameObject **humans = currentInstance->dataForLevel.Humans;
RichardE 0:5fa232ee5fdf 144 HumanObject *human = (HumanObject*)humans[ humanIndex ];
RichardE 0:5fa232ee5fdf 145 // Human must be walking around. Not rescued or already dead.
RichardE 0:5fa232ee5fdf 146 if( human->CurrentState == HumanObject::WalkingAbout ) {
RichardE 0:5fa232ee5fdf 147 if( enemy->SquashesHumans ) {
RichardE 0:5fa232ee5fdf 148 // Change human to dead state.
RichardE 0:5fa232ee5fdf 149 human->CurrentState = HumanObject::Dead;
RichardE 0:5fa232ee5fdf 150 // Make a noise.
RichardE 0:5fa232ee5fdf 151 SoundManager::Instance.PlaySound( Sounds::HumanDies, 0, 0 );
RichardE 5:0b0651ac7832 152 } else if( enemy->GetEnemyType() == Brain ) {
RichardE 10:bfa1c307c99d 153 // Kill off human by writing a NULL into the array.
RichardE 10:bfa1c307c99d 154 // DO NOT change human to dead state as this will generate a dead human animation
RichardE 10:bfa1c307c99d 155 // and this human is not dead, but a mutant.
RichardE 10:bfa1c307c99d 156 humans[ humanIndex ] = (HumanObject*)NULL;
RichardE 0:5fa232ee5fdf 157 // Find a free slot for a new enemy.
RichardE 0:5fa232ee5fdf 158 if( GameObject::FindUnusedObject( enemies, LevelData::MaxEnemies, &mutantIndex ) ) {
RichardE 10:bfa1c307c99d 159 // Write a pointer to a new mutant into the enemy array.
RichardE 10:bfa1c307c99d 160 MutantObject *mutant = (MutantObject*)EnemyFactory::Instance.MakeEnemy( Mutant );
RichardE 10:bfa1c307c99d 161 if( mutant != (MutantObject*)NULL ) {
RichardE 10:bfa1c307c99d 162 enemies[ mutantIndex ] = mutant;
RichardE 10:bfa1c307c99d 163 // Initialise mutant at coordinates of human and chasing the player.
RichardE 10:bfa1c307c99d 164 mutant->Start( human, currentInstance->player );
RichardE 10:bfa1c307c99d 165 // Make a noise.
RichardE 10:bfa1c307c99d 166 // TODO : SoundManager::Instance.PlaySound( Sounds::HumanMutates, 0, 0 );
RichardE 10:bfa1c307c99d 167 }
RichardE 5:0b0651ac7832 168 } else {
RichardE 0:5fa232ee5fdf 169 // Could not find a free slot for a new enemy so just erase the human sprite.
RichardE 8:82d88f9381f3 170 GDExtra::HideSprite( currentInstance->gd, human->SpriteNumber );
RichardE 0:5fa232ee5fdf 171 }
RichardE 0:5fa232ee5fdf 172 }
RichardE 0:5fa232ee5fdf 173 }
RichardE 0:5fa232ee5fdf 174 }
RichardE 0:5fa232ee5fdf 175 }
RichardE 0:5fa232ee5fdf 176
RichardE 0:5fa232ee5fdf 177 /********************************************************/
RichardE 0:5fa232ee5fdf 178 /* HANDLE COLLISIONS BETWEEN PLAYER BULLETS AND ENEMIES */
RichardE 0:5fa232ee5fdf 179 /********************************************************/
RichardE 0:5fa232ee5fdf 180 // Pass index of bullet in player's bullet array in bulletIndex.
RichardE 0:5fa232ee5fdf 181 // Pass sprite number of sprite that it hit in spriteNumber.
RichardE 5:0b0651ac7832 182 void LevelNormal::HandleBulletCollision( UInt8 bulletIndex, UInt8 spriteNumber )
RichardE 5:0b0651ac7832 183 {
RichardE 5:0b0651ac7832 184 // Point to array of enemy object pointers.
RichardE 10:bfa1c307c99d 185 GameObject **enemies = currentInstance->dataForLevel.Enemies;
RichardE 5:0b0651ac7832 186 EnemyObject *enemy;
RichardE 5:0b0651ac7832 187 UInt8 enemyIndex;
RichardE 5:0b0651ac7832 188 // Find an enemy with given sprite number.
RichardE 5:0b0651ac7832 189 if( GameObject::FindSpriteNumber( enemies, LevelData::MaxEnemies, spriteNumber, &enemyIndex ) ) {
RichardE 5:0b0651ac7832 190 // Found enemy. Check if it is indestructable.
RichardE 5:0b0651ac7832 191 enemy = (EnemyObject*)enemies[ enemyIndex ];
RichardE 10:bfa1c307c99d 192 // Remember coordinates for explosion.
RichardE 10:bfa1c307c99d 193 Int16 explodeX = enemy->Xco;
RichardE 10:bfa1c307c99d 194 Int16 explodeY = enemy->Yco;
RichardE 0:5fa232ee5fdf 195 if( enemy->HitPoints != EnemyObject::Indestructable ) {
RichardE 0:5fa232ee5fdf 196 // Enemy is not indestructable. Decrement hit points and die when it reaches zero.
RichardE 0:5fa232ee5fdf 197 enemy->HitPoints--;
RichardE 0:5fa232ee5fdf 198 if( enemy->HitPoints == 0 ) {
RichardE 0:5fa232ee5fdf 199 // Hide the enemy sprite.
RichardE 8:82d88f9381f3 200 GDExtra::HideSprite( currentInstance->gd, enemy->SpriteNumber );
RichardE 0:5fa232ee5fdf 201 // Add points to player's score.
RichardE 0:5fa232ee5fdf 202 currentInstance->player->AddToScore( enemy->GetPoints() );
RichardE 10:bfa1c307c99d 203 // Kill enemy by deleting it and then inserting a NULL into enemies array.
RichardE 10:bfa1c307c99d 204 EnemyFactory::Instance.DeleteEnemy( enemy );
RichardE 10:bfa1c307c99d 205 enemy = (EnemyObject*)NULL;
RichardE 10:bfa1c307c99d 206 enemies[ enemyIndex ] = (GameObject*)NULL;
RichardE 0:5fa232ee5fdf 207 }
RichardE 0:5fa232ee5fdf 208 }
RichardE 10:bfa1c307c99d 209 // If enemy is still alive then tell enemy it has been hit by a bullet.
RichardE 10:bfa1c307c99d 210 if( enemy != (EnemyObject*)NULL ) {
RichardE 10:bfa1c307c99d 211 enemy->RegisterHitByBullet();
RichardE 10:bfa1c307c99d 212 }
RichardE 0:5fa232ee5fdf 213 // Kill off the bullet.
RichardE 8:82d88f9381f3 214 currentInstance->player->KillBullet( currentInstance->gd, bulletIndex );
RichardE 0:5fa232ee5fdf 215 // Make a noise.
RichardE 0:5fa232ee5fdf 216 SoundManager::Instance.PlaySound( Sounds::Explosion, 0, 0 );
RichardE 10:bfa1c307c99d 217 // Start explosion animation.
RichardE 10:bfa1c307c99d 218 ExplosionManager::Instance.StartExplosion( explodeX, explodeY );
RichardE 5:0b0651ac7832 219 }
RichardE 0:5fa232ee5fdf 220 }
RichardE 0:5fa232ee5fdf 221
RichardE 0:5fa232ee5fdf 222 /*********************************************************/
RichardE 0:5fa232ee5fdf 223 /* CHECK FOR COLLISIONS BETWEEN PLAYER AND OTHER OBJECTS */
RichardE 0:5fa232ee5fdf 224 /*********************************************************/
RichardE 0:5fa232ee5fdf 225 // Pass pointer to a flag that will be set true if player is dead in isDead parameter.
RichardE 5:0b0651ac7832 226 void LevelNormal::CheckPlayerCollisions( bool *isDead )
RichardE 5:0b0651ac7832 227 {
RichardE 5:0b0651ac7832 228 UInt8 enemyIndex, humanIndex;
RichardE 5:0b0651ac7832 229 // Check if player sprite has hit another sprite.
RichardE 8:82d88f9381f3 230 UInt8 hitSpriteNumber = gd->rd( Gameduino::COLLISION + player->SpriteNumber );
RichardE 5:0b0651ac7832 231 // If you get 0xFF then no collision found.
RichardE 5:0b0651ac7832 232 if( hitSpriteNumber != 0xFF ) {
RichardE 5:0b0651ac7832 233 // Check for collision with an enemy.
RichardE 5:0b0651ac7832 234 if(
RichardE 5:0b0651ac7832 235 GameObject::FindSpriteNumber(
RichardE 10:bfa1c307c99d 236 dataForLevel.Enemies, LevelData::MaxEnemies, hitSpriteNumber, &enemyIndex
RichardE 5:0b0651ac7832 237 )
RichardE 5:0b0651ac7832 238 ) {
RichardE 5:0b0651ac7832 239 // Hit an enemy. Player is dead.
RichardE 5:0b0651ac7832 240 *isDead = true;
RichardE 5:0b0651ac7832 241 }
RichardE 5:0b0651ac7832 242 // Check for collision with a human that has not already been rescued or killed.
RichardE 5:0b0651ac7832 243 else if(
RichardE 5:0b0651ac7832 244 GameObject::FindSpriteNumber(
RichardE 10:bfa1c307c99d 245 dataForLevel.Humans, LevelData::MaxHumans, hitSpriteNumber, &humanIndex
RichardE 5:0b0651ac7832 246 )
RichardE 5:0b0651ac7832 247 ) {
RichardE 10:bfa1c307c99d 248 HumanObject *human = (HumanObject*)dataForLevel.Humans[ humanIndex ];
RichardE 0:5fa232ee5fdf 249 if( human->CurrentState == HumanObject::WalkingAbout ) {
RichardE 0:5fa232ee5fdf 250 // Change human state to rescued.
RichardE 0:5fa232ee5fdf 251 human->CurrentState = HumanObject::Rescued;
RichardE 0:5fa232ee5fdf 252 // Give player 50 points (in BCD!).
RichardE 0:5fa232ee5fdf 253 player->AddToScore( 0x50 );
RichardE 0:5fa232ee5fdf 254 // Make a noise.
RichardE 0:5fa232ee5fdf 255 SoundManager::Instance.PlaySound( Sounds::RescueHuman, 0, 0 );
RichardE 0:5fa232ee5fdf 256 }
RichardE 5:0b0651ac7832 257 }
RichardE 5:0b0651ac7832 258 }
RichardE 0:5fa232ee5fdf 259 }
RichardE 0:5fa232ee5fdf 260
RichardE 0:5fa232ee5fdf 261 /***********************************************************************************/
RichardE 0:5fa232ee5fdf 262 /* WAIT UNTIL SLOT FREE FOR A NEW SOUND, PLAY IT AND WAIT FOR ALL SOUNDS TO FINISH */
RichardE 0:5fa232ee5fdf 263 /***********************************************************************************/
RichardE 0:5fa232ee5fdf 264 // Pass sound to play in soundToPlay parameter.
RichardE 5:0b0651ac7832 265 void LevelNormal::PlaySoundAndWait( const UInt8 *soundToPlay )
RichardE 5:0b0651ac7832 266 {
RichardE 5:0b0651ac7832 267 // Keep trying to play sound until it works and meanwhile
RichardE 5:0b0651ac7832 268 // keep currently playing sounds going.
RichardE 5:0b0651ac7832 269 while( ! SoundManager::Instance.PlaySound( soundToPlay, 0, 0 ) ) {
RichardE 5:0b0651ac7832 270 // Update sound manager.
RichardE 5:0b0651ac7832 271 SoundManager::Instance.Update();
RichardE 5:0b0651ac7832 272 // Wait for frame flyback.
RichardE 9:fa7e7b37b632 273 gd->waitvblank();
RichardE 5:0b0651ac7832 274 }
RichardE 5:0b0651ac7832 275 // Now wait until all sounds have finished.
RichardE 5:0b0651ac7832 276 while( SoundManager::Instance.CountSoundsPlaying() > 0 ) {
RichardE 5:0b0651ac7832 277 // Update sound manager.
RichardE 5:0b0651ac7832 278 SoundManager::Instance.Update();
RichardE 5:0b0651ac7832 279 // Wait for frame flyback.
RichardE 9:fa7e7b37b632 280 gd->waitvblank();
RichardE 5:0b0651ac7832 281 }
RichardE 0:5fa232ee5fdf 282 }
RichardE 0:5fa232ee5fdf 283
RichardE 10:bfa1c307c99d 284 /**************/
RichardE 10:bfa1c307c99d 285 /* PLAY LEVEL */
RichardE 10:bfa1c307c99d 286 /**************/
RichardE 10:bfa1c307c99d 287 // Returns code indicating how level ended.
RichardE 10:bfa1c307c99d 288 Level::LevelExitCode LevelNormal::Play( void ) {
RichardE 12:81926431fea7 289 UInt8 enemyCount = 0;
RichardE 10:bfa1c307c99d 290 GameObject **ptr = dataForLevel.Enemies;
RichardE 10:bfa1c307c99d 291 // Repeat for all enemy types.
RichardE 10:bfa1c307c99d 292 for( UInt8 et = 0; et < (int)EnemyTypeCount; ++et ) {
RichardE 10:bfa1c307c99d 293 // Get number of this enemy type on this level.
RichardE 12:81926431fea7 294 UInt8 eCount = descriptorData->GetEnemyCount( (EnemyType)et );
RichardE 10:bfa1c307c99d 295 // Create required number of enemies.
RichardE 10:bfa1c307c99d 296 for( UInt8 eNum = 0; eNum < eCount; ++eNum ) {
RichardE 10:bfa1c307c99d 297 if( enemyCount < LevelData::MaxEnemies ) {
RichardE 10:bfa1c307c99d 298 GameObject *newEnemy = EnemyFactory::Instance.MakeEnemy( (EnemyType)et );
RichardE 10:bfa1c307c99d 299 if( newEnemy != (GameObject*)NULL ) {
RichardE 10:bfa1c307c99d 300 *ptr++ = newEnemy;
RichardE 10:bfa1c307c99d 301 enemyCount++;
RichardE 10:bfa1c307c99d 302 }
RichardE 10:bfa1c307c99d 303 }
RichardE 10:bfa1c307c99d 304 }
RichardE 10:bfa1c307c99d 305 }
RichardE 12:81926431fea7 306 // Create required number of humans.
RichardE 12:81926431fea7 307 HumanObject humans[ LevelData::MaxHumans ];
RichardE 10:bfa1c307c99d 308 ptr = dataForLevel.Humans;
RichardE 12:81926431fea7 309 UInt8 humanCount = descriptorData->HumanCount;
RichardE 12:81926431fea7 310 humanCount = ( humanCount > LevelData::MaxHumans ) ? LevelData::MaxHumans : humanCount;
RichardE 10:bfa1c307c99d 311 for( UInt8 i = 0; i < humanCount; ++i ) {
RichardE 10:bfa1c307c99d 312 *ptr++ = humans + i;
RichardE 10:bfa1c307c99d 313 }
RichardE 10:bfa1c307c99d 314 // Play the level. Returns when game is over or level is complete, but NOT
RichardE 10:bfa1c307c99d 315 // if the player gets killed and has lives remaining.
RichardE 10:bfa1c307c99d 316 Level::LevelExitCode exitCode = PlayLoop();
RichardE 10:bfa1c307c99d 317 // Make sure all memory allocated to enemies is freed.
RichardE 10:bfa1c307c99d 318 ptr = dataForLevel.Enemies;
RichardE 10:bfa1c307c99d 319 for( UInt8 i = 0; i < LevelData::MaxEnemies; ++i ) {
RichardE 10:bfa1c307c99d 320 if( *ptr != (GameObject*)NULL ) {
RichardE 10:bfa1c307c99d 321 EnemyFactory::Instance.DeleteEnemy( (EnemyObject*)*ptr );
RichardE 10:bfa1c307c99d 322 *ptr = (GameObject*)NULL;
RichardE 10:bfa1c307c99d 323 }
RichardE 10:bfa1c307c99d 324 ptr++;
RichardE 10:bfa1c307c99d 325 }
RichardE 10:bfa1c307c99d 326 return exitCode;
RichardE 10:bfa1c307c99d 327 }
RichardE 10:bfa1c307c99d 328
RichardE 0:5fa232ee5fdf 329 /*************/
RichardE 0:5fa232ee5fdf 330 /* PLAY LOOP */
RichardE 0:5fa232ee5fdf 331 /*************/
RichardE 0:5fa232ee5fdf 332 // Returns code indicating how level ended.
RichardE 0:5fa232ee5fdf 333 // This method should be called from the Play method after the
RichardE 0:5fa232ee5fdf 334 // level data has been initialised and the return value returned
RichardE 0:5fa232ee5fdf 335 // by the Play method.
RichardE 5:0b0651ac7832 336 Level::LevelExitCode LevelNormal::PlayLoop( void )
RichardE 5:0b0651ac7832 337 {
RichardE 5:0b0651ac7832 338 // Do nothing if Gameduino has not been specified, level data is NULL or player has not been specified.
RichardE 10:bfa1c307c99d 339 if( ( gd != (Gameduino*)NULL ) || ( player == (PlayerObject*)NULL ) ) {
RichardE 5:0b0651ac7832 340 // Point static pointer to current instance.
RichardE 5:0b0651ac7832 341 currentInstance = this;
RichardE 5:0b0651ac7832 342 // Do some initialisation first.
RichardE 5:0b0651ac7832 343 InitialiseLevel( gd );
RichardE 5:0b0651ac7832 344 // Redraw the screen.
RichardE 5:0b0651ac7832 345 DrawLevel();
RichardE 5:0b0651ac7832 346 // Wait for frame flyback once before entering loop so collision data is recalculated.
RichardE 5:0b0651ac7832 347 // At this point there should not be any sprites on the screen so no collisions
RichardE 5:0b0651ac7832 348 // should be found.
RichardE 5:0b0651ac7832 349 gd->waitvblank();
RichardE 5:0b0651ac7832 350 // Repeat until all enemies are dead or player is dead.
RichardE 5:0b0651ac7832 351 bool allEnemiesAreDead = false;
RichardE 5:0b0651ac7832 352 bool playerIsDead = false;
RichardE 5:0b0651ac7832 353 bool gameIsOver = false;
RichardE 5:0b0651ac7832 354 bool firstDraw = true;
RichardE 5:0b0651ac7832 355 while( ! allEnemiesAreDead && ! gameIsOver ) {
RichardE 5:0b0651ac7832 356 // Update sound manager.
RichardE 9:fa7e7b37b632 357 SoundManager::Instance.Update();
RichardE 5:0b0651ac7832 358 // Wait for frame flyback.
RichardE 5:0b0651ac7832 359 gd->waitvblank();
RichardE 5:0b0651ac7832 360 // Check for collisions between player and other objects.
RichardE 5:0b0651ac7832 361 CheckPlayerCollisions( &playerIsDead );
RichardE 5:0b0651ac7832 362 // Check for collisions between humans and enemies that squash.
RichardE 10:bfa1c307c99d 363 GameObject::FindCollisions( gd, dataForLevel.Humans, LevelData::MaxHumans, &LevelNormal::HandleHumanCollision );
RichardE 5:0b0651ac7832 364 // Check for collisions between player bullets and enemies.
RichardE 8:82d88f9381f3 365 GameObject::FindCollisions( gd, player->GetBullets(), BulletManager::MaxBullets, &LevelNormal::HandleBulletCollision );
RichardE 5:0b0651ac7832 366 // Redraw the player's score and number of lives.
RichardE 5:0b0651ac7832 367 DrawScoreAndLives();
RichardE 7:e72691603fd3 368 // Draw all the enemies.
RichardE 10:bfa1c307c99d 369 GameObject::DrawAll( gd, dataForLevel.Enemies, LevelData::MaxEnemies );
RichardE 5:0b0651ac7832 370 // Draw all the humans.
RichardE 10:bfa1c307c99d 371 GameObject::DrawAll( gd, dataForLevel.Humans, LevelData::MaxHumans );
RichardE 5:0b0651ac7832 372 // Draw all the explosions.
RichardE 8:82d88f9381f3 373 GameObject::DrawAll( gd, ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions );
RichardE 5:0b0651ac7832 374 // Draw the player.
RichardE 5:0b0651ac7832 375 player->Draw( gd );
RichardE 5:0b0651ac7832 376 // Draw the player's bullets.
RichardE 6:8bbdb70bc11c 377 GameObject::DrawAll( gd, player->GetBullets(), BulletManager::MaxBullets );
RichardE 5:0b0651ac7832 378 // Increment the frame counter.
RichardE 5:0b0651ac7832 379 FrameCounter++;
RichardE 5:0b0651ac7832 380 // After first redraw play level start sound and wait for it to end.
RichardE 5:0b0651ac7832 381 if( firstDraw ) {
RichardE 9:fa7e7b37b632 382 PlaySoundAndWait( Sounds::StartLevel );
RichardE 5:0b0651ac7832 383 firstDraw = false;
RichardE 1:dfd5eaaf96a3 384 }
RichardE 5:0b0651ac7832 385 // If player was killed then play death march and wait for it to finish.
RichardE 5:0b0651ac7832 386 if( playerIsDead ) {
RichardE 5:0b0651ac7832 387 #ifdef CHATTY
RichardE 5:0b0651ac7832 388 pc.puts( "Player got killed.\r\n" );
RichardE 5:0b0651ac7832 389 #endif
RichardE 5:0b0651ac7832 390 // Player got killed.
RichardE 9:fa7e7b37b632 391 PlaySoundAndWait( Sounds::PlayerDead );
RichardE 5:0b0651ac7832 392 // One less life for player.
RichardE 5:0b0651ac7832 393 if( player->Lives > 0 ) {
RichardE 5:0b0651ac7832 394 player->Lives--;
RichardE 5:0b0651ac7832 395 }
RichardE 5:0b0651ac7832 396 // Game is over when player has no more lives.
RichardE 5:0b0651ac7832 397 gameIsOver = ( player->Lives == 0 );
RichardE 5:0b0651ac7832 398 // If game is not over then re-initialise level using any remaining enemies.
RichardE 5:0b0651ac7832 399 if( ! gameIsOver ) {
RichardE 5:0b0651ac7832 400 #ifdef CHATTY
RichardE 5:0b0651ac7832 401 pc.puts( "Game is over.\r\n" );
RichardE 5:0b0651ac7832 402 #endif
RichardE 5:0b0651ac7832 403 // Remove all objects that do not survive a level restart (like enemy bullets).
RichardE 10:bfa1c307c99d 404 GameObject::RemoveUnretainedObjects( dataForLevel.Enemies, LevelData::MaxEnemies );
RichardE 5:0b0651ac7832 405 InitialiseLevel( gd );
RichardE 5:0b0651ac7832 406 DrawLevel();
RichardE 5:0b0651ac7832 407 gd->waitvblank();
RichardE 5:0b0651ac7832 408 playerIsDead = false;
RichardE 5:0b0651ac7832 409 firstDraw = true;
RichardE 5:0b0651ac7832 410 }
RichardE 5:0b0651ac7832 411 }
RichardE 5:0b0651ac7832 412 else {
RichardE 5:0b0651ac7832 413 // Move all the enemies and check if all dead.
RichardE 10:bfa1c307c99d 414 allEnemiesAreDead = ! GameObject::MoveAll( dataForLevel.Enemies, LevelData::MaxEnemies );
RichardE 5:0b0651ac7832 415 // If there are still some enemies alive then check if those that remain are indestructable.
RichardE 5:0b0651ac7832 416 // If only indestructable enemies survive then act as if all enemies are dead.
RichardE 5:0b0651ac7832 417 // You need to do this or you would never be able to complete a level that had indestructable
RichardE 5:0b0651ac7832 418 // enemies on it.
RichardE 5:0b0651ac7832 419 if( ! allEnemiesAreDead ) {
RichardE 5:0b0651ac7832 420 allEnemiesAreDead = EnemyObject::AreAllIndestructable(
RichardE 10:bfa1c307c99d 421 (const EnemyObject**)dataForLevel.Enemies,
RichardE 6:8bbdb70bc11c 422 LevelData::MaxEnemies
RichardE 6:8bbdb70bc11c 423 );
RichardE 5:0b0651ac7832 424 }
RichardE 5:0b0651ac7832 425 // Move all the humans.
RichardE 10:bfa1c307c99d 426 GameObject::MoveAll( dataForLevel.Humans, LevelData::MaxHumans );
RichardE 5:0b0651ac7832 427 // Move (update) all the explosions.
RichardE 5:0b0651ac7832 428 GameObject::MoveAll( ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions );
RichardE 5:0b0651ac7832 429 // Read the player's controls.
RichardE 5:0b0651ac7832 430 player->ReadControls();
RichardE 5:0b0651ac7832 431 // Move the player.
RichardE 5:0b0651ac7832 432 player->Move();
RichardE 5:0b0651ac7832 433 // Move the player's bullets.
RichardE 5:0b0651ac7832 434 GameObject::MoveAll( player->GetBullets(), BulletManager::MaxBullets );
RichardE 0:5fa232ee5fdf 435 }
RichardE 1:dfd5eaaf96a3 436 }
RichardE 5:0b0651ac7832 437 // Player completed level or game is over.
RichardE 5:0b0651ac7832 438 return gameIsOver ? GameOver : Completed;
RichardE 0:5fa232ee5fdf 439 }
RichardE 5:0b0651ac7832 440 else {
RichardE 5:0b0651ac7832 441 // Level data or player were not specified.
RichardE 5:0b0651ac7832 442 return Completed;
RichardE 5:0b0651ac7832 443 }
RichardE 0:5fa232ee5fdf 444 }