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

Revision:
0:5fa232ee5fdf
Child:
1:dfd5eaaf96a3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LevelNormal.cpp	Tue Jun 04 20:16:33 2013 +0000
@@ -0,0 +1,376 @@
+/*
+ * SOURCE FILE : LevelNormal.cpp
+ *
+ * Definition of class LevelNormal.
+ * Base class for all "normal" levels.
+ * i.e. Levels that are not special attract modes
+ * but have enemies who are trying to kill you
+ * and so on.
+ *
+ */
+
+#include "LevelNormal.h"
+#if 0
+    #include "GameObjectLocator.h"
+    #include "FrameCounter.h"
+    #include "SpriteNumber.h"
+#endif
+
+// Current instance being processed.
+LevelNormal *LevelNormal::currentInstance;
+
+/***************/
+/* CONSTRUCTOR */
+/***************/
+LevelNormal::LevelNormal() {
+}
+
+/**************/
+/* DESTRUCTOR */
+/**************/
+LevelNormal::~LevelNormal() {
+}
+
+/********************/
+/* INITIALISE LEVEL */
+/********************/
+void LevelNormal::InitialiseLevel( void ) {
+#if 0
+    // Note that if you re-arrange the following code you may need to adjust the
+    // SpriteNumber enumeration in SpriteNumber.h.
+  UInt8 spriteNumber = FirstEnemySprite;
+  // Initialise enemies.
+  GameObject::InitialiseAll( DataForLevel->Enemies, LevelData::MaxEnemies, &spriteNumber );
+  // Initialise humans.
+    spriteNumber = FirstHumanSprite;
+  GameObject::InitialiseAll( DataForLevel->Humans, LevelData::MaxHumans, &spriteNumber );
+  // Use next free sprite number for player.
+  player->SpriteNumber = PlayerSprite;
+  // Do futher initialisation for all enemies.
+  EnemyObject *object;
+  for( UInt8 e = 0; e < LevelData::MaxEnemies; ++e ) {
+    object = (EnemyObject*)DataForLevel->Enemies[ e ];
+    if( object != (EnemyObject*)NULL ) {
+            // Get enemy to chase the player.
+      object->SetChaseObject( player );
+            // Pass array of all enemies to this enemy.
+            object->Enemies = DataForLevel->Enemies;
+            // If enemy is a brain then tell it about the humans to chase.
+            if( object->GetEnemyType() == Brain ) {
+                ((BrainObject*)object)->HumansToChase = DataForLevel->Humans;
+            }
+    }
+  }
+  // Put player in the centre of the arena.
+  player->Xco = PLAYER_START_X;
+  player->Yco = PLAYER_START_Y;
+  // Kill off all player's bullets.
+  player->KillAllBullets();
+  // Kill off all explosions.
+  ExplosionManager::Instance.KillAllExplosions();
+#endif
+}
+
+/**********************************/
+/* DRAW SCORE AND NUMBER OF LIVES */
+/**********************************/
+void LevelNormal::DrawScoreAndLives( void ) {
+#if 0
+  GDExtra::WriteBCDNumber( 16, 0, player->Score, 8 );
+  // Display number of lives but limit this to 20 lives displayed.
+  UInt8 lives = ( player->Lives > 20 ) ? 20 : player->Lives;
+  GD.fill( RAM_PIC + VISIBLE_CHAR_WIDTH - lives, MiniPlayer, lives );
+#endif
+}
+
+/******************/
+/* DRAW THE LEVEL */
+/******************/
+void LevelNormal::DrawLevel( void ) {
+#if 0
+  // Set screen background to black.
+  GD.wr( BG_COLOR, RGB( 0, 0, 0 ) );
+  // Clear the screen to zero characters.
+  GDExtra::ClearScreen( TransparentChar );
+  // Hide all sprties.
+  GDExtra::HideAllSprites();
+  // Display level number.
+  GDExtra::WriteProgString( 0, 0, StringData::LevelString );
+  GDExtra::WriteUInt16( 6, 0, LevelNumber, 10, 2 );
+  // Display score.
+  GDExtra::WriteProgString( 10, 0, StringData::ScoreString );
+  // Update score and lives.
+  DrawScoreAndLives();
+  // Draw border around screen.
+  CharFrame::Draw(
+    ARENA_BORDER_X,
+    ARENA_BORDER_Y,
+    ARENA_BORDER_WIDTH,
+    ARENA_BORDER_HEIGHT
+  );
+#endif
+}
+
+/************************************************/
+/* HANDLE COLLISIONS BETWEEN HUMANS AND ENEMIES */
+/************************************************/
+// Pass index of human in level's humans array in humanIndex.
+// Pass sprite number of sprite that it hit in spriteNumber.
+void LevelNormal::HandleHumanCollision( UInt8 humanIndex, UInt8 spriteNumber ) {
+#if 0
+  // Point to array of enemy object pointers.
+  GameObject **enemies = currentInstance->DataForLevel->Enemies;
+  EnemyObject *enemy;
+  UInt8 enemyIndex, mutantIndex;
+  // Find an enemy with given sprite number.
+  if( GameObject::FindSpriteNumber( enemies, LevelData::MaxEnemies, spriteNumber, &enemyIndex ) ) {
+    // Found enemy. Check if it squashes humans.
+    enemy = (EnemyObject*)enemies[ enemyIndex ];
+        // Get hold of the human that is doomed.
+        GameObject **humans = currentInstance->DataForLevel->Humans;
+        HumanObject *human = (HumanObject*)humans[ humanIndex ];
+        // Human must be walking around. Not rescued or already dead.
+        if( human->CurrentState == HumanObject::WalkingAbout ) {
+            if( enemy->SquashesHumans ) {
+                // Change human to dead state.
+                human->CurrentState = HumanObject::Dead;
+                // Make a noise.
+                SoundManager::Instance.PlaySound( Sounds::HumanDies, 0, 0 );
+            }
+            else if( enemy->GetEnemyType() == Brain ) {
+                // Kill human by inserting a null into humans array.
+                humans[ humanIndex ] = (GameObject*)NULL;
+                // Find a free slot for a new enemy.
+                if( GameObject::FindUnusedObject( enemies, LevelData::MaxEnemies, &mutantIndex ) ) {
+                    // Write a pointer to a mutant with the same index as the human that just died
+                    // into the enemy array.
+                    MutantObject *mutant = currentInstance->DataForLevel->Mutants + humanIndex;
+                    enemies[ mutantIndex ] = mutant;
+                    // Initialise mutant at coordinates of human and chasing the player.
+                    mutant->Start( human, currentInstance->player );
+                    // Make a noise.
+                    // TODO : SoundManager::Instance.PlaySound( Sounds::HumanMutates, 0, 0 );
+                }
+                else {
+                    // Could not find a free slot for a new enemy so just erase the human sprite.
+                    GDExtra::HideSprite( human->SpriteNumber );
+                }
+            }
+        }
+    }
+#endif
+}
+
+/********************************************************/
+/* HANDLE COLLISIONS BETWEEN PLAYER BULLETS AND ENEMIES */
+/********************************************************/
+// Pass index of bullet in player's bullet array in bulletIndex.
+// Pass sprite number of sprite that it hit in spriteNumber.
+void LevelNormal::HandleBulletCollision( UInt8 bulletIndex, UInt8 spriteNumber ) {
+#if 0
+  // Point to array of enemy object pointers.
+  GameObject **enemies = currentInstance->DataForLevel->Enemies;
+  EnemyObject *enemy;
+  UInt8 enemyIndex;
+  // Find an enemy with given sprite number.
+  if( GameObject::FindSpriteNumber( enemies, LevelData::MaxEnemies, spriteNumber, &enemyIndex ) ) {
+    // Found enemy. Check if it is indestructable.
+    enemy = (EnemyObject*)enemies[ enemyIndex ];
+        if( enemy->HitPoints != EnemyObject::Indestructable ) {
+            // Enemy is not indestructable. Decrement hit points and die when it reaches zero.
+            enemy->HitPoints--;
+            if( enemy->HitPoints == 0 ) {
+                // Kill enemy by inserting a NULL into enemies array.
+                enemies[ enemyIndex ] = (GameObject*)NULL;
+                // Hide the enemy sprite.
+                GDExtra::HideSprite( enemy->SpriteNumber );
+                // Add points to player's score.
+                currentInstance->player->AddToScore( enemy->GetPoints() );
+            }
+        }
+        // Tell enemy it has been hit by a bullet.
+        enemy->RegisterHitByBullet();
+        // Kill off the bullet.
+        currentInstance->player->KillBullet( bulletIndex );
+        // Make a noise.
+        SoundManager::Instance.PlaySound( Sounds::Explosion, 0, 0 );
+        // Start explosion animation using coordinates of enemy.
+        ExplosionManager::Instance.StartExplosion( enemy->Xco, enemy->Yco    );
+  }
+#endif
+}
+
+/*********************************************************/
+/* CHECK FOR COLLISIONS BETWEEN PLAYER AND OTHER OBJECTS */
+/*********************************************************/
+// Pass pointer to a flag that will be set true if player is dead in isDead parameter.
+void LevelNormal::CheckPlayerCollisions( bool *isDead ) {
+#if 0
+  UInt8 enemyIndex, humanIndex;
+  // Check if player sprite has hit another sprite.
+  UInt8 hitSpriteNumber = GD.rd( COLLISION + player->SpriteNumber );
+  // If you get 0xFF then no collision found.
+  if( hitSpriteNumber != 0xFF ) {
+    // Check for collision with an enemy.
+    if(
+      GameObject::FindSpriteNumber(
+        DataForLevel->Enemies, LevelData::MaxEnemies, hitSpriteNumber, &enemyIndex
+      )
+    ) {
+      // Hit an enemy. Player is dead.
+      *isDead = true;
+    }
+    // Check for collision with a human that has not already been rescued or killed.
+    else if(
+      GameObject::FindSpriteNumber(
+        DataForLevel->Humans, LevelData::MaxHumans, hitSpriteNumber, &humanIndex
+      )
+    ) {
+      HumanObject *human = (HumanObject*)DataForLevel->Humans[ humanIndex ];
+            if( human->CurrentState == HumanObject::WalkingAbout ) {
+                // Change human state to rescued.
+                human->CurrentState = HumanObject::Rescued;
+                // Give player 50 points (in BCD!).
+                player->AddToScore( 0x50 );
+                // Make a noise.
+                SoundManager::Instance.PlaySound( Sounds::RescueHuman, 0, 0 );
+            }
+    }    
+  }
+#endif
+}
+
+/***********************************************************************************/
+/* WAIT UNTIL SLOT FREE FOR A NEW SOUND, PLAY IT AND WAIT FOR ALL SOUNDS TO FINISH */
+/***********************************************************************************/
+// Pass sound to play in soundToPlay parameter.
+void LevelNormal::PlaySoundAndWait( const UInt8 *soundToPlay ) {
+#if 0
+  // Keep trying to play sound until it works and meanwhile
+  // keep currently playing sounds going.
+  while( ! SoundManager::Instance.PlaySound( soundToPlay, 0, 0 ) ) {
+    // Update sound manager.
+    SoundManager::Instance.Update();
+    // Wait for frame flyback.
+    GD.waitvblank();
+  }
+  // Now wait until all sounds have finished.
+  while( SoundManager::Instance.CountSoundsPlaying() > 0 ) {
+    // Update sound manager.
+    SoundManager::Instance.Update();
+    // Wait for frame flyback.
+    GD.waitvblank();
+  }
+#endif
+}
+
+/*************/
+/* PLAY LOOP */
+/*************/
+// Returns code indicating how level ended.
+// This method should be called from the Play method after the
+// level data has been initialised and the return value returned
+// by the Play method.
+Level::LevelExitCode LevelNormal::PlayLoop( void ) {
+#if 0
+  // Do nothing if level data is NULL or player has not been specified.
+  if( ( DataForLevel != (LevelData*)NULL ) || ( player == (PlayerObject*)NULL ) ) {
+    // Point static pointer to current instance.
+    currentInstance = this;
+    // Do some initialisation first.
+    InitialiseLevel();
+    // Redraw the screen.
+    DrawLevel();
+        // Wait for frame flyback once before entering loop so collision data is recalculated.
+        // At this point there should not be any sprites on the screen so no collisions
+        // should be found.
+        GD.waitvblank();
+    // Repeat until all enemies are dead or player is dead.
+    bool allEnemiesAreDead = false;
+        bool playerIsDead = false;
+    bool gameIsOver = false;
+    bool firstDraw = true;
+    while( ! allEnemiesAreDead && ! gameIsOver ) {
+      // Update sound manager.
+      SoundManager::Instance.Update();
+      // Wait for frame flyback.
+      GD.waitvblank();
+      // Check for collisions between player and other objects.
+      CheckPlayerCollisions( &playerIsDead );
+            // Check for collisions between humans and enemies that squash.
+            GameObject::FindCollisions( DataForLevel->Humans, LevelData::MaxHumans, &LevelNormal::HandleHumanCollision );
+      // Check for collisions between player bullets and enemies.
+      GameObject::FindCollisions( player->GetBullets(), BulletManager::MaxBullets, &LevelNormal::HandleBulletCollision );
+      // Redraw the player's score and number of lives.
+      DrawScoreAndLives();
+      // Draw all the enemies.
+      GameObject::DrawAll( DataForLevel->Enemies, LevelData::MaxEnemies );
+      // Draw all the humans.
+      GameObject::DrawAll( DataForLevel->Humans, LevelData::MaxHumans );
+      // Draw all the explosions.
+      GameObject::DrawAll( ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions );
+      // Draw the player.
+      player->Draw();
+      // Draw the player's bullets.
+      GameObject::DrawAll( player->GetBullets(), BulletManager::MaxBullets );
+      // Increment the frame counter.
+      FrameCounter++;
+      // After first redraw play level start sound and wait for it to end.
+      if( firstDraw ) {
+        PlaySoundAndWait( Sounds::StartLevel );
+        firstDraw = false;
+      }
+            // If player was killed then play death march and wait for it to finish.
+            if( playerIsDead ) {
+                // Player got killed.
+                PlaySoundAndWait( Sounds::PlayerDead );
+                // One less life for player.
+                if( player->Lives > 0 ) {
+                    player->Lives--;
+                }
+                // Game is over when player has no more lives.
+                gameIsOver = ( player->Lives == 0 );
+                // If game is not over then re-initialise level using any remaining enemies.
+                if( ! gameIsOver ) {
+                    // Remove all objects that do not survive a level restart (like enemy bullets).
+                    GameObject::RemoveUnretainedObjects( DataForLevel->Enemies, LevelData::MaxEnemies );
+                    InitialiseLevel();
+                    DrawLevel();
+                    GD.waitvblank();
+                    playerIsDead = false;
+                    firstDraw = true;
+                }
+            }
+            else {
+                // Move all the enemies and check if all dead.
+                allEnemiesAreDead = ! GameObject::MoveAll( DataForLevel->Enemies, LevelData::MaxEnemies );
+                // If there are still some enemies alive then check if those that remain are indestructable.
+                // If only indestructable enemies survive then act as if all enemies are dead.
+                // You need to do this or you would never be able to complete a level that had indestructable
+                // enemies on it.
+                if( ! allEnemiesAreDead ) {
+                    allEnemiesAreDead = EnemyObject::AreAllIndestructable(
+                        (const EnemyObject**)DataForLevel->Enemies,
+                        LevelData::MaxEnemies
+                    );
+                }
+                // Move all the humans.
+                GameObject::MoveAll( DataForLevel->Humans, LevelData::MaxHumans );
+                // Move (update) all the explosions.
+                GameObject::MoveAll( ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions );
+                // Read the player's controls.
+                player->ReadControls();
+                // Move the player.
+                player->Move();
+                // Move the player's bullets.
+                GameObject::MoveAll( player->GetBullets(), BulletManager::MaxBullets );
+            }
+    }
+        // Player completed level or game is over.
+        return gameIsOver ? GameOver : Completed;
+  }
+#endif
+  // Level data or player were not specified.
+  return Completed;
+}
+