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:
- 0:5fa232ee5fdf
- Child:
- 1:dfd5eaaf96a3
diff -r 000000000000 -r 5fa232ee5fdf LevelNormal.cpp --- /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; +} +