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 10:bfa1c307c99d, committed 2013-06-09
- Comitter:
- RichardE
- Date:
- Sun Jun 09 19:34:56 2013 +0000
- Parent:
- 9:fa7e7b37b632
- Child:
- 11:2ddaf46e95cb
- Commit message:
- Changed how levels are represented so that dynamic allocation of memory is used. Maple version couldn't do this. Still only 2 levels. Use EnemyFactory whenever creating or destroying enemies.
Changed in this revision
--- a/BrainObject.cpp Sun Jun 09 14:28:53 2013 +0000 +++ b/BrainObject.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -15,6 +15,7 @@ #include "BulletVelocityCalculator.h" #include "HumanObject.h" #include "Random.h" +#include "EnemyFactory.h" /***************/ /* CONSTRUCTOR */ @@ -28,8 +29,6 @@ MovementRestricted = true; // Restrict to boundary of arena. Bounds = &ArenaRectangle; - // Restrict bullet to boundary of arena. - bullet.Bounds = &ArenaRectangle; } /**************/ @@ -52,30 +51,37 @@ /******************/ // Pass sprite number to use in spriteNumber. // Returns pointer to bullet object or NULL on failure. +// Memory is dynamically allocated since Brain bullet is an EnemyObject. BrainBulletObject *BrainObject::StartBullet( UInt8 spriteNumber ) { - // Give bullet same coordinates as the brain that fired it. - bullet.Xco = Xco; - bullet.Yco = Yco; - // Set correct sprite number. - bullet.SpriteNumber = spriteNumber; - // Must make it visible because last time bullet was - // killed off this property may have been set to false. - bullet.Visible = true; - // Similarly hit points may already be at zero. - // If you don't do this then bullet hit points gets decremented to -1 (0xFF) - // and it becomes indestructible. - bullet.HitPoints = 1; - // Calculate bullet velocities. - // Aim for a point somewhere in the vicinity of the chase object. - if( chaseObject != (GameObject*)NULL ) { - UInt16 targetX = chaseObject->Xco + (Int16)Random::Get( -128, +128 ); - UInt16 targetY = chaseObject->Yco + (Int16)Random::Get( -128, +128 ); - BulletVelocityCalculator::CalculateVelocities( - targetX - Xco, targetY - Yco, - 80, &bullet.HVelocity, &bullet.VVelocity - ); + // Create a new bullet. + BrainBulletObject *newBullet = (BrainBulletObject*)EnemyFactory::Instance.MakeEnemy( BrainBullet ); + if( newBullet != (BrainBulletObject*)NULL ) { + // Give bullet same coordinates as the brain that fired it. + newBullet->Xco = Xco; + newBullet->Yco = Yco; + // Restrict bullet to boundary of arena. + newBullet->Bounds = &ArenaRectangle; + // Set correct sprite number. + newBullet->SpriteNumber = spriteNumber; + // Must make it visible because last time bullet was + // killed off this property may have been set to false. + newBullet->Visible = true; + // Similarly hit points may already be at zero. + // If you don't do this then bullet hit points gets decremented to -1 (0xFF) + // and it becomes indestructible. + newBullet->HitPoints = 1; + // Calculate bullet velocities. + // Aim for a point somewhere in the vicinity of the chase object. + if( chaseObject != (GameObject*)NULL ) { + UInt16 targetX = chaseObject->Xco + (Int16)Random::Get( -128, +128 ); + UInt16 targetY = chaseObject->Yco + (Int16)Random::Get( -128, +128 ); + BulletVelocityCalculator::CalculateVelocities( + targetX - Xco, targetY - Yco, + 80, &(newBullet->HVelocity), &(newBullet->VVelocity) + ); + } } - return • + return newBullet; } /************************/
--- a/BrainObject.h Sun Jun 09 14:28:53 2013 +0000 +++ b/BrainObject.h Sun Jun 09 19:34:56 2013 +0000 @@ -67,7 +67,6 @@ bool bulletActive; // true if a bullet fired by this brain is zipping around UInt8 bulletIndex; // index of active bullet in enemies array - BrainBulletObject bullet; // the bullet belonging to this brain /******************************************************/ /* DETERMINE IF A HUMAN IS A VALID TARGET FOR A BRAIN */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EnemyFactory.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -0,0 +1,96 @@ +/* + * SOURCE FILE : EnemyFactory.cpp + * + * Definition of class EnemyFactory. + * + */ + +#include "EnemyFactory.h" + +// Define this for debugging messages. +#define CHATTY + +#ifdef CHATTY + #include "mbed.h" + extern Serial pc; +#endif + +// An instance of this class. +EnemyFactory EnemyFactory::Instance; + +/***************/ +/* CONSTRUCTOR */ +/***************/ +EnemyFactory::EnemyFactory() : + enemyCount( 0UL ) +{ +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +EnemyFactory::~EnemyFactory() +{ +} + +/*****************/ +/* MAKE AN ENEMY */ +/*****************/ +// Pass enemy type in et. +// Returns a newly created object or NULL on failure. +EnemyObject* EnemyFactory::MakeEnemy( EnemyType et ) { + EnemyObject *enemy; + switch( et ) { + + case Grunt : + enemy = new GruntObject(); + break; + + case BlueMeany : + enemy = new BlueMeanyObject(); + break; + + case Crusher : + enemy = new CrusherObject(); + break; + + case Brain : + enemy = new BrainObject(); + break; + + case BrainBullet : + enemy = new BrainBulletObject(); + break; + + case Mutant : + enemy = new MutantObject(); + break; + + default : + enemy = (EnemyObject*)NULL; + break; + + } + + if( enemy != (EnemyObject*)NULL ) { + enemyCount++; + #ifdef CHATTY + pc.printf( "Enemy type %d created. %lu enemies exist.\r\n", (int)enemy->GetEnemyType(), enemyCount ); + #endif + } + + return enemy; +} + +/*******************/ +/* DELETE AN ENEMY */ +/*******************/ +// Pass pointer to enemy to delete. +// ONLY USE THIS ON OBJECTS CREATED USING MakeEnemy method. +void EnemyFactory::DeleteEnemy( EnemyObject *enemy ) { + enemyCount--; + #ifdef CHATTY + pc.printf( "Enemy type %d deleted. %lu enemies exist.\r\n", (int)enemy->GetEnemyType(), enemyCount ); + #endif + delete enemy; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EnemyFactory.h Sun Jun 09 19:34:56 2013 +0000 @@ -0,0 +1,67 @@ +/* + * SOURCE FILE : EnemyFactory.h + * + * Definition of class EnemyFactory. + * Makes objects descended from EnemyObject. + * It is absolutely vital that all enemies are created and deleted this way if memory leaks are to be avoided. + * Don't forget pesky enemies like MutantObjects that only appear half way through a level. They still need + * to be created this way. + * + */ + +#ifndef EnemyFactoryDefined + + #define EnemyFactoryDefined + + #include "EnemyType.h" + #include "GameObject.h" + #include "HumanObject.h" + #include "GruntObject.h" + #include "CrusherObject.h" + #include "BrainObject.h" + #include "MutantObject.h" + #include "BlueMeanyObject.h" + + class EnemyFactory { + + public : + + // An instance of this class. + static EnemyFactory Instance; + + /***************/ + /* CONSTRUCTOR */ + /***************/ + EnemyFactory(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~EnemyFactory(); + + /*****************/ + /* MAKE AN ENEMY */ + /*****************/ + // Pass enemy type in et. + // Returns a newly created object or NULL on failure. + EnemyObject* MakeEnemy( EnemyType et ); + + /*******************/ + /* DELETE AN ENEMY */ + /*******************/ + // Pass pointer to enemy to delete. + // ONLY USE THIS ON OBJECTS CREATED USING MakeEnemy method. + void DeleteEnemy( EnemyObject *enemy ); + + private : + + // Number of enemies created. + unsigned long enemyCount; + + }; + +#endif + +/* END of EnemyFactory.h */ + +
--- a/EnemyType.h Sun Jun 09 14:28:53 2013 +0000 +++ b/EnemyType.h Sun Jun 09 19:34:56 2013 +0000 @@ -1,23 +1,25 @@ -/* - * SOURCE FILE : EnemyType.h - * - * Enumeration of all the possible enemy types. - * - */ - -#ifndef EnemyTypeIncluded - - #define EnemyTypeIncluded - - enum EnemyType { - Grunt, - BlueMeany, - Crusher, - Brain, - BrainBullet, - }; - -#endif - -/* END of EnemyType.h */ +/* + * SOURCE FILE : EnemyType.h + * + * Enumeration of all the possible enemy types. + * + */ + +#ifndef EnemyTypeIncluded + + #define EnemyTypeIncluded + enum EnemyType { + Grunt, + BlueMeany, + Crusher, + Brain, + BrainBullet, + Mutant, + EnemyTypeCount // number of different kinds of enemies, MUST COME LAST! + }; + +#endif + +/* END of EnemyType.h */ +
--- a/GameObject.cpp Sun Jun 09 14:28:53 2013 +0000 +++ b/GameObject.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -9,6 +9,7 @@ #include "GameObjectLocator.h" #include "ArenaConst.h" #include "GDExtra.h" +#include "EnemyFactory.h" /**********************************/ /* INITIALISE AN ARRAY OF OBJECTS */ @@ -77,7 +78,11 @@ // If not then it wants killing off and a NULL // should be written to the array of pointers // and the sprite should be hidden. + // If it is an enemy then it must be deleted using EnemyFactory. if( ! object->Visible ) { + if( object->GetType() == EnemyObjectType ) { + EnemyFactory::Instance.DeleteEnemy( (EnemyObject*)object ); + } objects[ i ] = (GameObject*)NULL; GDExtra::HideSprite( gd, object->SpriteNumber ); }
--- a/GameRobotRic.cpp Sun Jun 09 14:28:53 2013 +0000 +++ b/GameRobotRic.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -127,20 +127,22 @@ level->SetGameduino( &gd ); // Play the level. exitCode = level->Play(); + // Free memory used by level. + levels.FreeLevel( level ); // If player was killed then decrement lives otherwise // advance to next level. switch( exitCode ) { - case Level::GameOver : - // TODO : Do some sort of game over fuss. - CheckForHighScore( &gd, &highScores, player.Score ); - // Go back to attract level and reset player lives and score. - levelNumber = LevelCollection::AttractLevel; - player.Lives = START_LIVES; - player.Score = 0; - break; - case Level::Completed : - levelNumber++; - break; + case Level::GameOver : + // TODO : Do some sort of game over fuss. + CheckForHighScore( &gd, &highScores, player.Score ); + // Go back to attract level and reset player lives and score. + levelNumber = LevelCollection::AttractLevel; + player.Lives = START_LIVES; + player.Score = 0; + break; + case Level::Completed : + levelNumber++; + break; } } }
--- a/Level.h Sun Jun 09 14:28:53 2013 +0000 +++ b/Level.h Sun Jun 09 19:34:56 2013 +0000 @@ -40,6 +40,9 @@ // Number of this level. UInt8 LevelNumber; + // True if level was dynamically allocated. + bool IsDynamicallyAllocated; + /***************/ /* CONSTRUCTOR */ /***************/
--- a/Level0.cpp Sun Jun 09 14:28:53 2013 +0000 +++ b/Level0.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -12,6 +12,8 @@ /* CONSTRUCTOR */ /***************/ Level0::Level0() { + // Level zero (attract mode) is NOT dynamically allocated. + IsDynamicallyAllocated = false; } /**************/
--- a/Level1.cpp Sun Jun 09 14:28:53 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * SOURCE FILE : Level1.cpp - * - * Definition of class Level1. - * Machine written by program MapleRobotRicLevelGenerator. - * DO NOT EDIT BY HAND! - * - */ - -#include "Level1.h" - -/**************/ -/* PLAY LEVEL */ -/**************/ -// Returns code indicating how level ended. -Level::LevelExitCode Level1::Play( void ) { - LevelData dataForThisLevel; - GameObject **ptr = dataForThisLevel.Enemies; - UInt8 i, humanCount, enemyCount = 0; - // Enemies of type GruntObject. - #define GRUNTOBJECTCOUNT 5 - GruntObject gruntobjects[ GRUNTOBJECTCOUNT ]; - for( i = 0; i < GRUNTOBJECTCOUNT; ++i ) { - if( enemyCount < LevelData::MaxEnemies ) { - *ptr++ = gruntobjects + i; - enemyCount++; - } - } - // Enemies of type CrusherObject. - #define CRUSHEROBJECTCOUNT 5 - CrusherObject crusherobjects[ CRUSHEROBJECTCOUNT ]; - for( i = 0; i < CRUSHEROBJECTCOUNT; ++i ) { - if( enemyCount < LevelData::MaxEnemies ) { - *ptr++ = crusherobjects + i; - enemyCount++; - } - } - // Humans. - #define HUMANCOUNT 5 - HumanObject humans[ HUMANCOUNT ]; - ptr = dataForThisLevel.Humans; - humanCount = ( HUMANCOUNT > LevelData::MaxHumans ) ? LevelData::MaxHumans : HUMANCOUNT; - for( i = 0; i < humanCount; ++i ) { - *ptr++ = humans + i; - } - DataForLevel = &dataForThisLevel; - return PlayLoop(); -} -
--- a/Level1.h Sun Jun 09 14:28:53 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * SOURCE FILE : Level1.h - * - * Definition of class Level1. - * Machine written by program MapleRobotRicLevelGenerator. - * DO NOT EDIT BY HAND! - * - */ - -#ifndef Level1Defined - - #define Level1Defined - - #include "LevelNormal.h" - - class Level1 : public LevelNormal { - - public : - - /**************/ - /* PLAY LEVEL */ - /**************/ - // Returns code indicating how level ended. - virtual LevelExitCode Play( void ); - - }; - -#endif - -/* END of Level1.h */
--- a/Level2.cpp Sun Jun 09 14:28:53 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * SOURCE FILE : Level2.cpp - * - * Definition of class Level2. - * Machine written by program MapleRobotRicLevelGenerator. - * DO NOT EDIT BY HAND! - * - */ - -#include "Level2.h" - -/**************/ -/* PLAY LEVEL */ -/**************/ -// Returns code indicating how level ended. -Level::LevelExitCode Level2::Play( void ) { - LevelData dataForThisLevel; - GameObject **ptr = dataForThisLevel.Enemies; - UInt8 i, humanCount, enemyCount = 0; - // Enemies of type GruntObject. - #define GRUNTOBJECTCOUNT 10 - GruntObject gruntobjects[ GRUNTOBJECTCOUNT ]; - for( i = 0; i < GRUNTOBJECTCOUNT; ++i ) { - if( enemyCount < LevelData::MaxEnemies ) { - *ptr++ = gruntobjects + i; - enemyCount++; - } - } - // Enemies of type BrainObject. - #define BRAINOBJECTCOUNT 2 - BrainObject brainobjects[ BRAINOBJECTCOUNT ]; - for( i = 0; i < BRAINOBJECTCOUNT; ++i ) { - if( enemyCount < LevelData::MaxEnemies ) { - *ptr++ = brainobjects + i; - enemyCount++; - } - } - // Humans. - #define HUMANCOUNT 6 - HumanObject humans[ HUMANCOUNT ]; - ptr = dataForThisLevel.Humans; - humanCount = ( HUMANCOUNT > LevelData::MaxHumans ) ? LevelData::MaxHumans : HUMANCOUNT; - for( i = 0; i < humanCount; ++i ) { - *ptr++ = humans + i; - } - DataForLevel = &dataForThisLevel; - return PlayLoop(); -}
--- a/Level2.h Sun Jun 09 14:28:53 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * SOURCE FILE : Level2.h - * - * Definition of class Level2. - * Machine written by program MapleRobotRicLevelGenerator. - * DO NOT EDIT BY HAND! - * - */ - -#ifndef Level2Defined - - #define Level2Defined - - #include "LevelNormal.h" - - class Level2 : public LevelNormal { - - public : - - /**************/ - /* PLAY LEVEL */ - /**************/ - // Returns code indicating how level ended. - virtual LevelExitCode Play( void ); - - }; - -#endif - -/* END of Level2.h */
--- a/LevelCollection.cpp Sun Jun 09 14:28:53 2013 +0000 +++ b/LevelCollection.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -8,29 +8,8 @@ #include <stdio.h> #include "LevelCollection.h" #include "Level0.h" -#include "Level1.h" -#include "Level2.h" -#if 0 - #include "Level3.h" - #include "Level4.h" - #include "Level5.h" - #include "Level6.h" - #include "Level7.h" - #include "Level8.h" - #include "Level9.h" - #include "Level10.h" - #include "Level10.h" - #include "Level11.h" - #include "Level12.h" - #include "Level13.h" - #include "Level14.h" - #include "Level15.h" - #include "Level16.h" - #include "Level17.h" - #include "Level18.h" - #include "Level19.h" - #include "Level20.h" -#endif +#include "LevelNormal.h" +#include "LevelDescriptor.h" /***************/ /* CONSTRUCTOR */ @@ -53,58 +32,20 @@ return LEVELCOUNT; } -// Individual levels. -static Level0 level0; // Not a real level. This is attract mode. -static Level1 level1; -static Level2 level2; -#if 0 -static Level3 level3; -static Level4 level4; -static Level5 level5; -static Level6 level6; -static Level7 level7; -static Level8 level8; -static Level9 level9; -static Level10 level10; -static Level11 level11; -static Level12 level12; -static Level13 level13; -static Level14 level14; -static Level15 level15; -static Level16 level16; -static Level17 level17; -static Level18 level18; -static Level19 level19; -static Level20 level20; -#endif +// Level 0 which is NOT dynamically allocated. +// Not a real level. This is attract mode. +static Level0 level0; -// Pointers to all the levels. -// If I have got this right then each pointer in the array points to a -// Level that is NOT const, but the array itself is const and cannot be modified. -static Level* const levels[ LEVELCOUNT ] = { - &level0, - &level1, - &level2, -#if 0 - &level3, - &level4, - &level5, - &level6, - &level7, - &level8, - &level9, - &level10, - &level11, - &level12, - &level13, - &level14, - &level15, - &level16, - &level17, - &level18, - &level19, - &level20, -#endif +// Level descriptors. +static const UInt8 ld0[] = { ENDDESCRIPTOR }; +static const UInt8 ld1[] = { Grunt, 5, Crusher, 5, ENDDESCRIPTOR }; +static const UInt8 ld2[] = { Grunt, 10, Brain, 2, ENDDESCRIPTOR }; + +// Array pointing to level data for each level. +static const UInt8* const levelDescriptors[ LEVELCOUNT ] = { + ld0, + ld1, + ld2, }; /***************/ @@ -114,15 +55,29 @@ // Returns pointer to level or NULL if no such level. Level *LevelCollection::GetLevel( UInt8 levelNumber ) { if( levelNumber >= LEVELCOUNT ) { + // Level number out of range. return (Level*)NULL; } + else if( levelNumber == 0 ) { + // Level zero is the attract mode and is not dynamically allocated. + return &level0; + } else { - // Fetch pointer to level from array and tag it with the - // correct level number before returning it. - Level *level = levels[ levelNumber ]; + // Fetch level descriptor for this level and create a Level from it. + Level *level = new LevelNormal( levelDescriptors[ levelNumber ] ); + // Tag it with the correct level number before returning it. level->LevelNumber = levelNumber; return level; } } - +/*******************************/ +/* FREE MEMORY USED BY A LEVEL */ +/*******************************/ +// Pass pointer to a level. +// Frees memory used by level. +void LevelCollection::FreeLevel( Level *level ) { + if( ( level != (Level*)NULL ) && level->IsDynamicallyAllocated ) { + delete level; + } +}
--- a/LevelCollection.h Sun Jun 09 14:28:53 2013 +0000 +++ b/LevelCollection.h Sun Jun 09 19:34:56 2013 +0000 @@ -1,51 +1,61 @@ -/* - * SOURCE FILE : LevelCollection.h - * - * Definition of class LevelCollection. - * - */ - -#ifndef LevelCollectionDefined - - #define LevelCollectionDefined - - #include "Types.h" - #include "Level.h" - - class LevelCollection { - - public : - - enum { - AttractLevel = 0, // just ticking over encouraging player to start - FirstNormalLevel = 1, // first real level excluding attract mode - }; - - /***************/ - /* CONSTRUCTOR */ - /***************/ - LevelCollection(); - - /**************/ - /* DESTRUCTOR */ - /**************/ - virtual ~LevelCollection(); - - /************************/ - /* GET NUMBER OF LEVELS */ - /************************/ - UInt8 GetLevelCount( void ) const; - - /***************/ - /* GET A LEVEL */ - /***************/ - // Pass level number in levelNumber. - // Returns pointer to level or NULL if no such level. - Level *GetLevel( UInt8 levelNumber ); - - }; - -#endif - -/* END of LevelCollection.h */ +/* + * SOURCE FILE : LevelCollection.h + * + * Definition of class LevelCollection. + * + */ + +#ifndef LevelCollectionDefined + + #define LevelCollectionDefined + + #include "Types.h" + #include "Level.h" + + class LevelCollection { + + public : + + enum { + AttractLevel = 0, // just ticking over encouraging player to start + FirstNormalLevel = 1, // first real level excluding attract mode + }; + + /***************/ + /* CONSTRUCTOR */ + /***************/ + LevelCollection(); + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~LevelCollection(); + + /************************/ + /* GET NUMBER OF LEVELS */ + /************************/ + UInt8 GetLevelCount( void ) const; + + /***************/ + /* GET A LEVEL */ + /***************/ + // Pass level number in levelNumber. + // Returns pointer to level or NULL if no such level. + // Note that the level MAY be dynamically allocated and you must + // call FreeLevel when you have finished with the level to release + // memory used. + Level *GetLevel( UInt8 levelNumber ); + + /*******************************/ + /* FREE MEMORY USED BY A LEVEL */ + /*******************************/ + // Pass pointer to a level. + // Frees memory used by level. + void FreeLevel( Level *level ); + + }; + +#endif + +/* END of LevelCollection.h */ +
--- a/LevelData.h Sun Jun 09 14:28:53 2013 +0000 +++ b/LevelData.h Sun Jun 09 19:34:56 2013 +0000 @@ -21,7 +21,6 @@ enum { MaxEnemies = 64, // Maximum number of enemies you can have in a level MaxHumans = 24, // maximum number of humans you can have in a level - MaxMutants = MaxHumans, // Maximum number of mutant humans you can have in a level }; // Array containing pointers to all the enemies in a level. @@ -32,11 +31,6 @@ // A null pointer indicates an unused or rescued human. GameObject *Humans[ MaxHumans ]; - // Array containing mutant humans (NOT pointers to mutants). - // Pointer to the mutants in this array are written into the Enemies array - // whenever a human is mutated by a brain. - MutantObject Mutants[ MaxMutants ]; - /***************/ /* CONSTRUCTOR */ /***************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LevelDescriptor.cpp Sun Jun 09 19:34:56 2013 +0000 @@ -0,0 +1,36 @@ +/* + * SOURCE FILE : LevelDescriptor.cpp + * + * Definition of class LevelDescriptor. + * Describes a level. + * + */ + +#include "LevelDescriptor.h" + +/*****************************************/ +/* GET COUNT FOR A PARTICULAR ENEMY TYPE */ +/*****************************************/ +// Pass pointer to array containing data in data parameter. +// The array alternates between enemy type and count and MUST +// be terminated with a byte of value ENDDESCRIPTOR. +// Pass type of enemy to fetch count for in et. +// Returns number of enemies of the given type on this level. +UInt8 LevelDescriptor::GetEnemyCount( const UInt8 *data, EnemyType et ) { + bool found = false; + while( ! found && ( *data != ENDDESCRIPTOR ) ) { + if( *data == (UInt8)et ) { + found = true; + } + else { + data += 2; + } + } + if( found ) { + return data[ 1 ]; + } + else { + return 0; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LevelDescriptor.h Sun Jun 09 19:34:56 2013 +0000 @@ -0,0 +1,41 @@ +/* + * SOURCE FILE : LevelDescriptor.h + * + * Definition of class LevelDescriptor. + * Describes a level. + * + */ + +#ifndef LevelDescriptorDefined + + #define LevelDescriptorDefined + + #include "Types.h" + #include "EnemyType.h" + + class LevelDescriptor { + + public : + + // Used to mark the end of the descriptor array. + #define ENDDESCRIPTOR ((UInt8)0xFF) + + /*****************************************/ + /* GET COUNT FOR A PARTICULAR ENEMY TYPE */ + /*****************************************/ + // Pass pointer to array containing data in data parameter. + // The array alternates between enemy type and count and MUST + // be terminated with a byte of value ENDDESCRIPTOR. + // Pass type of enemy to fetch count for in et. + // Returns number of enemies of the given type on this level. + static UInt8 GetEnemyCount( const UInt8 *data, EnemyType et ); + + private : + + }; + +#endif + +/* END of LevelDescriptor.h */ + +
--- 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.
--- a/LevelNormal.h Sun Jun 09 14:28:53 2013 +0000 +++ b/LevelNormal.h Sun Jun 09 19:34:56 2013 +0000 @@ -15,6 +15,7 @@ #include "Level.h" #include "LevelData.h" + #include "LevelDescriptor.h" #include "ExplosionManager.h" class LevelNormal : public Level { @@ -24,19 +25,22 @@ /***************/ /* CONSTRUCTOR */ /***************/ - LevelNormal(); + // Pass pointer to level descriptor data in data parameter. + LevelNormal( const UInt8 *data ); /**************/ /* DESTRUCTOR */ /**************/ virtual ~LevelNormal(); + /**************/ + /* PLAY LEVEL */ + /**************/ + // Returns code indicating how level ended. + virtual LevelExitCode Play( void ); + protected : - // Data defining the level. - // You should write to this before calling PlayLoop. - LevelData *DataForLevel; - /*************/ /* PLAY LOOP */ /*************/ @@ -48,6 +52,12 @@ private : + // Data defining the level. + LevelData dataForLevel; + + // Descriptor array of bytes. + const UInt8 *descriptorData; + // Current instance being processed. static LevelNormal *currentInstance;