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
Diff: LevelNormal.cpp
- Revision:
- 10:bfa1c307c99d
- Parent:
- 9:fa7e7b37b632
- Child:
- 12:81926431fea7
--- a/LevelNormal.cpp Sun Jun 09 14:28:53 2013 +0000 +++ b/LevelNormal.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -22,6 +22,7 @@ #include "FrameCounter.h" #include "SpriteNumber.h" #include "ArenaConst.h" +#include "EnemyFactory.h" // Current instance being processed. LevelNormal *LevelNormal::currentInstance; @@ -29,8 +30,12 @@ /***************/ /* CONSTRUCTOR */ /***************/ -LevelNormal::LevelNormal() +// Pass pointer to level descriptor data in data parameter. +LevelNormal::LevelNormal( const UInt8 *data ) : + descriptorData( data ) { + // Normal levels are always dynamically alocated. + IsDynamicallyAllocated = true; } /**************/ @@ -50,24 +55,24 @@ // SpriteNumber enumeration in SpriteNumber.h. UInt8 spriteNumber = FirstEnemySprite; // Initialise enemies. - GameObject::InitialiseAll( DataForLevel->Enemies, LevelData::MaxEnemies, &spriteNumber ); + GameObject::InitialiseAll( dataForLevel.Enemies, LevelData::MaxEnemies, &spriteNumber ); // Initialise humans. spriteNumber = FirstHumanSprite; - GameObject::InitialiseAll( DataForLevel->Humans, LevelData::MaxHumans, &spriteNumber ); + 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 ]; + 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; + 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; + ((BrainObject*)object)->HumansToChase = dataForLevel.Humans; } } } @@ -127,7 +132,7 @@ void LevelNormal::HandleHumanCollision( UInt8 humanIndex, UInt8 spriteNumber ) { // Point to array of enemy object pointers. - GameObject **enemies = currentInstance->DataForLevel->Enemies; + GameObject **enemies = currentInstance->dataForLevel.Enemies; EnemyObject *enemy; UInt8 enemyIndex, mutantIndex; // Find an enemy with given sprite number. @@ -135,7 +140,7 @@ // Found enemy. Check if it squashes humans. enemy = (EnemyObject*)enemies[ enemyIndex ]; // Get hold of the human that is doomed. - GameObject **humans = currentInstance->DataForLevel->Humans; + 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 ) { @@ -145,18 +150,21 @@ // 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; + // Kill off human by writing a NULL into the array. + // DO NOT change human to dead state as this will generate a dead human animation + // and this human is not dead, but a mutant. + humans[ humanIndex ] = (HumanObject*)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 ); + // Write a pointer to a new mutant into the enemy array. + MutantObject *mutant = (MutantObject*)EnemyFactory::Instance.MakeEnemy( Mutant ); + if( mutant != (MutantObject*)NULL ) { + 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( currentInstance->gd, human->SpriteNumber ); @@ -174,33 +182,40 @@ void LevelNormal::HandleBulletCollision( UInt8 bulletIndex, UInt8 spriteNumber ) { // Point to array of enemy object pointers. - GameObject **enemies = currentInstance->DataForLevel->Enemies; + 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 ]; + // Remember coordinates for explosion. + Int16 explodeX = enemy->Xco; + Int16 explodeY = enemy->Yco; 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( currentInstance->gd, enemy->SpriteNumber ); // Add points to player's score. currentInstance->player->AddToScore( enemy->GetPoints() ); + // Kill enemy by deleting it and then inserting a NULL into enemies array. + EnemyFactory::Instance.DeleteEnemy( enemy ); + enemy = (EnemyObject*)NULL; + enemies[ enemyIndex ] = (GameObject*)NULL; } } - // Tell enemy it has been hit by a bullet. - enemy->RegisterHitByBullet(); + // If enemy is still alive then tell enemy it has been hit by a bullet. + if( enemy != (EnemyObject*)NULL ) { + enemy->RegisterHitByBullet(); + } // Kill off the bullet. currentInstance->player->KillBullet( currentInstance->gd, 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 ); + // Start explosion animation. + ExplosionManager::Instance.StartExplosion( explodeX, explodeY ); } } @@ -218,7 +233,7 @@ // Check for collision with an enemy. if( GameObject::FindSpriteNumber( - DataForLevel->Enemies, LevelData::MaxEnemies, hitSpriteNumber, &enemyIndex + dataForLevel.Enemies, LevelData::MaxEnemies, hitSpriteNumber, &enemyIndex ) ) { // Hit an enemy. Player is dead. @@ -227,10 +242,10 @@ // Check for collision with a human that has not already been rescued or killed. else if( GameObject::FindSpriteNumber( - DataForLevel->Humans, LevelData::MaxHumans, hitSpriteNumber, &humanIndex + dataForLevel.Humans, LevelData::MaxHumans, hitSpriteNumber, &humanIndex ) ) { - HumanObject *human = (HumanObject*)DataForLevel->Humans[ humanIndex ]; + HumanObject *human = (HumanObject*)dataForLevel.Humans[ humanIndex ]; if( human->CurrentState == HumanObject::WalkingAbout ) { // Change human state to rescued. human->CurrentState = HumanObject::Rescued; @@ -266,6 +281,51 @@ } } +/**************/ +/* PLAY LEVEL */ +/**************/ +// Returns code indicating how level ended. +Level::LevelExitCode LevelNormal::Play( void ) { + UInt8 humanCount, enemyCount = 0; + GameObject **ptr = dataForLevel.Enemies; + // Repeat for all enemy types. + for( UInt8 et = 0; et < (int)EnemyTypeCount; ++et ) { + // Get number of this enemy type on this level. + UInt8 eCount = LevelDescriptor::GetEnemyCount( descriptorData, (EnemyType)et ); + // Create required number of enemies. + for( UInt8 eNum = 0; eNum < eCount; ++eNum ) { + if( enemyCount < LevelData::MaxEnemies ) { + GameObject *newEnemy = EnemyFactory::Instance.MakeEnemy( (EnemyType)et ); + if( newEnemy != (GameObject*)NULL ) { + *ptr++ = newEnemy; + enemyCount++; + } + } + } + } + // Humans. + #define HUMANCOUNT 6 + HumanObject humans[ HUMANCOUNT ]; + ptr = dataForLevel.Humans; + humanCount = ( HUMANCOUNT > LevelData::MaxHumans ) ? LevelData::MaxHumans : HUMANCOUNT; + for( UInt8 i = 0; i < humanCount; ++i ) { + *ptr++ = humans + i; + } + // Play the level. Returns when game is over or level is complete, but NOT + // if the player gets killed and has lives remaining. + Level::LevelExitCode exitCode = PlayLoop(); + // Make sure all memory allocated to enemies is freed. + ptr = dataForLevel.Enemies; + for( UInt8 i = 0; i < LevelData::MaxEnemies; ++i ) { + if( *ptr != (GameObject*)NULL ) { + EnemyFactory::Instance.DeleteEnemy( (EnemyObject*)*ptr ); + *ptr = (GameObject*)NULL; + } + ptr++; + } + return exitCode; +} + /*************/ /* PLAY LOOP */ /*************/ @@ -276,7 +336,7 @@ Level::LevelExitCode LevelNormal::PlayLoop( void ) { // Do nothing if Gameduino has not been specified, level data is NULL or player has not been specified. - if( ( gd != (Gameduino*)NULL ) || ( DataForLevel != (LevelData*)NULL ) || ( player == (PlayerObject*)NULL ) ) { + if( ( gd != (Gameduino*)NULL ) || ( player == (PlayerObject*)NULL ) ) { // Point static pointer to current instance. currentInstance = this; // Do some initialisation first. @@ -300,15 +360,15 @@ // Check for collisions between player and other objects. CheckPlayerCollisions( &playerIsDead ); // Check for collisions between humans and enemies that squash. - GameObject::FindCollisions( gd, DataForLevel->Humans, LevelData::MaxHumans, &LevelNormal::HandleHumanCollision ); + GameObject::FindCollisions( gd, dataForLevel.Humans, LevelData::MaxHumans, &LevelNormal::HandleHumanCollision ); // Check for collisions between player bullets and enemies. GameObject::FindCollisions( gd, player->GetBullets(), BulletManager::MaxBullets, &LevelNormal::HandleBulletCollision ); // Redraw the player's score and number of lives. DrawScoreAndLives(); // Draw all the enemies. - GameObject::DrawAll( gd, DataForLevel->Enemies, LevelData::MaxEnemies ); + GameObject::DrawAll( gd, dataForLevel.Enemies, LevelData::MaxEnemies ); // Draw all the humans. - GameObject::DrawAll( gd, DataForLevel->Humans, LevelData::MaxHumans ); + GameObject::DrawAll( gd, dataForLevel.Humans, LevelData::MaxHumans ); // Draw all the explosions. GameObject::DrawAll( gd, ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions ); // Draw the player. @@ -341,7 +401,7 @@ pc.puts( "Game is over.\r\n" ); #endif // Remove all objects that do not survive a level restart (like enemy bullets). - GameObject::RemoveUnretainedObjects( DataForLevel->Enemies, LevelData::MaxEnemies ); + GameObject::RemoveUnretainedObjects( dataForLevel.Enemies, LevelData::MaxEnemies ); InitialiseLevel( gd ); DrawLevel(); gd->waitvblank(); @@ -351,19 +411,19 @@ } else { // Move all the enemies and check if all dead. - allEnemiesAreDead = ! GameObject::MoveAll( DataForLevel->Enemies, LevelData::MaxEnemies ); + 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, + (const EnemyObject**)dataForLevel.Enemies, LevelData::MaxEnemies ); } // Move all the humans. - GameObject::MoveAll( DataForLevel->Humans, LevelData::MaxHumans ); + GameObject::MoveAll( dataForLevel.Humans, LevelData::MaxHumans ); // Move (update) all the explosions. GameObject::MoveAll( ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions ); // Read the player's controls.