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