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

Files at this revision

API Documentation at this revision

Comitter:
RichardE
Date:
Sat Jun 08 16:44:54 2013 +0000
Parent:
6:8bbdb70bc11c
Child:
8:82d88f9381f3
Commit message:
Now have grunts wandering around on level 1. They follow the player but since no collision detection logic yet nobody ever gets killed.

Changed in this revision

Animations.cpp Show annotated file Show diff for this revision Revisions of this file
Animations.h Show annotated file Show diff for this revision Revisions of this file
BrainBulletObject.cpp Show annotated file Show diff for this revision Revisions of this file
BrainBulletObject.h Show annotated file Show diff for this revision Revisions of this file
BrainObject.cpp Show annotated file Show diff for this revision Revisions of this file
BrainObject.h Show annotated file Show diff for this revision Revisions of this file
BulletVelocityCalculator.cpp Show annotated file Show diff for this revision Revisions of this file
BulletVelocityCalculator.h Show annotated file Show diff for this revision Revisions of this file
CrusherObject.cpp Show annotated file Show diff for this revision Revisions of this file
CrusherObject.h Show annotated file Show diff for this revision Revisions of this file
EnemyObject.cpp Show annotated file Show diff for this revision Revisions of this file
EnemyObject.h Show annotated file Show diff for this revision Revisions of this file
EnemyType.h Show annotated file Show diff for this revision Revisions of this file
GruntObject.cpp Show annotated file Show diff for this revision Revisions of this file
GruntObject.h Show annotated file Show diff for this revision Revisions of this file
HumanObject.cpp Show annotated file Show diff for this revision Revisions of this file
HumanObject.h Show annotated file Show diff for this revision Revisions of this file
Level.h Show annotated file Show diff for this revision Revisions of this file
Level1.cpp Show annotated file Show diff for this revision Revisions of this file
Level2.cpp Show annotated file Show diff for this revision Revisions of this file
LevelNormal.cpp Show annotated file Show diff for this revision Revisions of this file
PlayerObject.cpp Show annotated file Show diff for this revision Revisions of this file
Walker.cpp Show annotated file Show diff for this revision Revisions of this file
Walker.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Animations.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,20 @@
+/*
+ * SOURCE FILE : Animations.cpp
+ *
+ * Animations consisting of arrays of sprite image numbers in program memory.
+ *
+ */
+
+#include "Animations.h"
+#include "SpriteImageId.h"
+
+const UInt8 Animations::WomanAnimation[] = { Woman0Image, Woman1Image, Woman2Image, Woman1Image };
+const UInt8 Animations::ManAnimation[] = { Man0Image, Man1Image, Man2Image, Man1Image };
+
+// Array containing addresses of all the human animations.
+const UInt8 *Animations::HumanAnimations[ HumanAnimationCount ] = {
+  WomanAnimation,
+  ManAnimation,
+};
+
+const UInt8 Animations::CrusherAnimation[] = { Crusher0Image, Crusher1Image, Crusher2Image, Crusher1Image };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Animations.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,35 @@
+/*
+ * SOURCE FILE : Animations.h
+ *
+ * Animations consisting of arrays of sprite image numbers in program memory.
+ *
+ */
+ 
+#ifndef AnimationsIncluded
+
+  #define AnimationsIncluded
+
+  #include "Types.h"
+  
+  class Animations {
+    
+  public :
+  
+    static const UInt8 WomanAnimation[];
+    static const UInt8 ManAnimation[];
+    
+    enum {
+      HumanAnimationCount = 2,    // number of animations there are for humans
+    };
+    
+    // Array containing addresses of all the human animations.
+    static const UInt8 *HumanAnimations[ HumanAnimationCount ];
+    
+        static const UInt8 CrusherAnimation[];
+        
+  };
+  
+#endif
+
+// END of Animations.h
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BrainBulletObject.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,38 @@
+/*
+ * SOURCE FILE : BrainBulletObject.cpp
+ *
+ * Definition of class BrainBulletObject.
+ *
+ */
+
+#include "BrainBulletObject.h"
+
+/***************/
+/* CONSTRUCTOR */
+/***************/
+BrainBulletObject::BrainBulletObject() :
+    HVelocity( 0 ),
+    VVelocity( 0 )
+{
+    DeleteWhenRestricted = true;
+    RetainOnLevelRestart = false;
+    PixelWidth = 4;
+    PixelHeight = 4;
+}
+
+/************************/
+/* DRAW THE GAME OBJECT */
+/************************/
+// Note if Visible is false this should not draw anything
+// and/or hide the visible object.
+void BrainBulletObject::Draw( Gameduino *gd ) {
+  gd->sprite( SpriteNumber, ToPixel( Xco ), ToPixel( Yco ), BrainBulletImage, 0, Gameduino::None, BadGuy );
+}
+
+/************************/
+/* MOVE THE GAME OBJECT */
+/************************/
+void BrainBulletObject::ProtectedMove( void ) {
+    Xco += HVelocity;
+    Yco += VVelocity;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BrainBulletObject.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,62 @@
+/*
+ * SOURCE FILE : BrainBulletObject.h
+ *
+ * Definition of class BrainBulletObject.
+ * This is the enemy object used as a bullet fired by BrainObject enemies.
+ *
+ */
+
+#ifndef BrainBulletObjectDefined
+
+  #define BrainBulletObjectDefined
+
+    #include "EnemyObject.h"
+    
+  class BrainBulletObject : public EnemyObject {
+
+  public :
+
+        // Horizontal and vertical velocities at which bullet is moving.
+        // NOT pixel velocities.
+        Int16 HVelocity, VVelocity;
+        
+    /***************/
+    /* CONSTRUCTOR */
+    /***************/
+    BrainBulletObject();
+
+        /*****************************/
+        /* GET TYPE OF ENEMY THIS IS */
+        /*****************************/
+        // Returns enemy type.
+        virtual EnemyType GetEnemyType( void ) {
+            return BrainBullet;
+        }
+
+    /*******************************************************/
+    /* GET NUMBER OF POINTS AWARDED FOR KILLING THIS ENEMY */
+    /*******************************************************/
+    // Returns number of points (in BCD).
+    virtual UInt8 GetPoints( void ) {
+            return 0x10;
+        }
+
+    /************************/
+    /* DRAW THE GAME OBJECT */
+    /************************/
+    // Note if Visible is false this should not draw anything
+    // and/or hide the visible object.
+    virtual void Draw( Gameduino *gd );
+
+  protected :
+  
+    /************************/
+    /* MOVE THE GAME OBJECT */
+    /************************/
+    virtual void ProtectedMove( void );
+        
+    };
+
+#endif
+
+/* END of BrainBulletObject.h */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BrainObject.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,128 @@
+/*
+ * SOURCE FILE : BrainObject.cpp
+ *
+ * Represents a big square robot that crushes humans.
+ *
+ */
+
+#include "BrainObject.h"
+#include "Gameduino.h"
+#include "FrameCounter.h"
+#include "ArenaConst.h"
+#include "Animations.h"
+#include "LevelData.h"
+#include "SpriteNumber.h"
+#include "BulletVelocityCalculator.h"
+#include "HumanObject.h"
+#include "Random.h"
+
+/***************/
+/* CONSTRUCTOR */
+/***************/
+BrainObject::BrainObject() :
+    HumansToChase( (GameObject**)NULL ),
+    bulletActive( false ),
+    bulletIndex( 0 )
+{
+  // Movement is always restricted (property of GameObject).
+  MovementRestricted = true;
+  // Restrict to boundary of arena.
+  Bounds = &ArenaRectangle;
+  // Restrict bullet to boundary of arena.
+  bullet.Bounds = &ArenaRectangle;
+}
+
+/**************/
+/* DESTRUCTOR */
+/**************/
+BrainObject::~BrainObject() {
+}
+
+/******************************************************/
+/* DETERMINE IF A HUMAN IS A VALID TARGET FOR A BRAIN */
+/******************************************************/
+// Returns true if object is a human that is valid for targeting by brain.
+bool BrainObject::ValidHuman( GameObject *object ) {
+    HumanObject *human = (HumanObject*)object;
+    return ( human != (HumanObject*)NULL ) && ( human->CurrentState == HumanObject::WalkingAbout );
+}
+
+/******************/
+/* START A BULLET */
+/******************/
+// Pass sprite number to use in spriteNumber.
+// Returns pointer to bullet object or NULL on failure.
+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
+        );
+    }
+    return •
+}
+
+/************************/
+/* MOVE THE GAME OBJECT */
+/************************/
+void BrainObject::ProtectedMove( void ) {
+    UInt8 humanIndex, newBulletIndex;
+    BrainBulletObject *newBullet;
+    // Make sure you have some humans to chase.
+    // Locate the nearest human.
+    if(
+        ( HumansToChase != (GameObject**)NULL ) &&
+        GameObject::FindNearestObject( HumansToChase, LevelData::MaxHumans, Xco, Yco, &ValidHuman, &humanIndex )
+    ) {
+        // Move towards target human.
+        GameObject *human = HumansToChase[ humanIndex ];
+        MoveTowards( human, BrainSpeed );
+    }
+    // If no humans to chase then chase chaseObject instead (player).
+    else if( chaseObject != (GameObject*)NULL ) {
+        MoveTowards( chaseObject, BrainSpeed );
+    }
+    // Skip next bit if enemies have not been specified.
+    if( Enemies != (GameObject**)NULL ) {
+        // Check if bullet was active but has now gone away.
+        if( bulletActive && ( Enemies[ bulletIndex ] == (GameObject*)NULL ) ) {
+            bulletActive = false;
+        }
+        // See if a new bullet should be started.
+        if(
+            ! bulletActive && ( Random::Get( 40 ) == 0 ) &&
+            GameObject::FindUnusedObject( Enemies, LevelData::MaxEnemies, &newBulletIndex ) &&
+            ( ( newBullet = StartBullet( FirstEnemySprite + newBulletIndex ) ) != (BrainBulletObject*)NULL )
+        ) {
+            Enemies[ newBulletIndex ] = newBullet;
+            bulletIndex = newBulletIndex;
+            bulletActive = true;
+        }
+    }
+}
+
+/************************/
+/* DRAW THE GAME OBJECT */
+/************************/
+// This is only called after it has been established that the
+// game object is visible.
+void BrainObject::Draw( Gameduino *gd ) {
+  Gameduino::Rotation transform = ( FrameCounter & 8 ) ? Gameduino::FlipX : Gameduino::None;
+  gd->sprite( SpriteNumber, ToPixel( Xco ), ToPixel( Yco ), BrainImage, 0, transform, BadGuy );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BrainObject.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,90 @@
+/*
+ * SOURCE FILE : BrainObject.h
+ *
+ * Represents a wandering human.
+ *
+ */
+
+#ifndef BrainObjectIncluded
+  
+  #define BrainObjectIncluded
+
+  #include "Types.h"
+  #include "EnemyObject.h"
+  #include "GameObjectTypes.h"
+  #include "BrainBulletObject.h"
+
+  class BrainObject : public EnemyObject {
+    
+  public :
+
+        // Points to an array of humans to chase.
+        GameObject **HumansToChase;
+        
+        /***************/
+        /* CONSTRUCTOR */
+        /***************/
+        BrainObject();
+        
+        /**************/
+        /* DESTRUCTOR */
+        /**************/
+        virtual ~BrainObject();
+
+        /*****************************/
+        /* GET TYPE OF ENEMY THIS IS */
+        /*****************************/
+        // Returns enemy type.
+        virtual EnemyType GetEnemyType( void ) {
+            return Brain;
+        }
+
+        /*******************************************************/
+        /* GET NUMBER OF POINTS AWARDED FOR KILLING THIS ENEMY */
+        /*******************************************************/
+        // Returns number of points.
+        virtual UInt8 GetPoints( void ) {
+          return 0x20;  // BCD!
+        }
+    
+        /************************/
+        /* MOVE THE GAME OBJECT */
+        /************************/
+        virtual void ProtectedMove( void );
+    
+        /************************/
+        /* DRAW THE GAME OBJECT */
+        /************************/
+        // This is only called after it has been established that the
+        // game object is visible.
+        virtual void Draw( Gameduino *gd );
+   
+    private :
+
+        enum {
+            BrainSpeed = 24,              // speed at which brains move towards their prey
+        };
+        
+        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 */
+        /******************************************************/
+        // Returns true if object is a human that is valid for targeting by brain.
+        static bool ValidHuman( GameObject *object );
+
+        /******************/
+        /* START A BULLET */
+        /******************/
+        // Pass sprite number to use in spriteNumber.
+        // Returns pointer to bullet object or NULL on failure.
+        BrainBulletObject *StartBullet( UInt8 spriteNumber );
+
+  };
+    
+#endif
+
+/* END of BrainObject.h */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BulletVelocityCalculator.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,63 @@
+/*
+ * SOURCE FILE : BulletVelocityCalculator.cpp
+ *
+ * Definition of class BulletVelocityCalculator.
+ *
+ */
+
+#include <stdlib.h>         // for abs
+#include "BulletVelocityCalculator.h"
+
+// A few constants.
+#define COS11_25 0.980785
+#define SIN11_25 0.195090
+#define COS33_75 0.831470
+#define SIN33_75 0.555570
+#define COS56_25 0.555570
+#define SIN56_25 0.831470
+#define COS78_75 0.195090
+#define SIN78_75 0.980785
+
+/*******************************/
+/* CALCULATE BULLET VELOCITIES */
+/*******************************/
+// Pass distances to target in dx and dy.
+// Pass velocity at which bullet moves in v.
+// Horizontal and vertical velocities returned in variables pointed to by hv and vv.
+void BulletVelocityCalculator::CalculateVelocities( Int16 dx, Int16 dy, Int16 v, Int16 *hv, Int16 *vv ) {
+    Int16 ax = abs( dx );
+    Int16 ay = abs( dy );
+    if( ax < 8 ) {
+        *hv = 0;
+        *vv = v;
+    }
+    else if( ay < 8 ) {
+        *hv = v;
+        *vv = 0;
+    }
+    else {
+        float ratio = (float)ay / (float)ax;
+        if( ratio < 0.5f ) {
+            *hv = v * COS11_25;
+            *vv = v * SIN11_25;
+        }
+        else if( ratio < 1.0f ) {
+            *hv = v * COS33_75;
+            *vv = v * SIN33_75;
+        }
+        else if( ratio < 2.0f ) {
+            *hv = v * COS56_25;
+            *vv = v * SIN56_25;
+        }
+        else {
+            *hv = v * COS78_75;
+            *vv = v * SIN78_75;
+        }
+    }
+    if( dx < 0 ) {
+        *hv = -(*hv);
+    }
+    if( dy < 0 ) {
+        *vv = -(*vv);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BulletVelocityCalculator.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,33 @@
+/*
+ * SOURCE FILE : BulletVelocityCalculator.h
+ *
+ * Definition of class BulletVelocityCalculator.
+ * Used to calculate the horizontal and vertical velocities necessary for a bullet to
+ * hit a target,
+ *
+ */
+
+#ifndef BulletVelocityCalculatorDefined
+
+  #define BulletVelocityCalculatorDefined
+
+    #include "Types.h"
+    
+  class BulletVelocityCalculator {
+
+  public :
+
+        /*******************************/
+        /* CALCULATE BULLET VELOCITIES */
+        /*******************************/
+        // Pass distances to target in dx and dy.
+        // Pass velocity at which bullet moves in v.
+        // Horizontal and vertical velocities returned in variables pointed to by hv and vv.
+        static void CalculateVelocities( Int16 dx, Int16 dy, Int16 v, Int16 *hv, Int16 *vv );
+        
+  };
+
+#endif
+
+/* END of BulletVelocityCalculator.h */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CrusherObject.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,77 @@
+/*
+ * SOURCE FILE : CrusherObject.cpp
+ *
+ * Represents a big square robot that crushes humans.
+ *
+ */
+
+#include "CrusherObject.h"
+#include "Gameduino.h"
+#include "FrameCounter.h"
+#include "ArenaConst.h"
+#include "Animations.h"
+#include "Walker.h"
+
+/***************/
+/* CONSTRUCTOR */
+/***************/
+CrusherObject::CrusherObject() :
+  animationData( Animations::CrusherAnimation ),
+    stunCountdown( 0 ),
+    stunFrameCount( 0 )
+{
+  // Initialise horizontal and vertical speeds.
+    Walker::InitialiseVelocities( &hSpeed, &vSpeed );
+  // Movement is always restricted (property of GameObject).
+  MovementRestricted = true;
+  // Restrict to boundary of arena.
+  Bounds = &ArenaRectangle;
+    // Crushers are indestructable.
+    HitPoints = Indestructable;
+    // Crushers squash humans.
+    SquashesHumans = true;
+}
+
+/**************/
+/* DESTRUCTOR */
+/**************/
+CrusherObject::~CrusherObject() {
+}
+
+/************************/
+/* MOVE THE GAME OBJECT */
+/************************/
+void CrusherObject::ProtectedMove( void ) {
+    if( stunCountdown > 0 ) {
+        stunCountdown--;
+    }
+    else {    
+        Walker::Walk( &Xco, &Yco, &hSpeed, &vSpeed, RestrictionFlags );
+    }
+}
+
+/************************/
+/* DRAW THE GAME OBJECT */
+/************************/
+// Pass pointer to Gameduino to draw on in gd.
+// This is only called after it has been established that the
+// game object is visible.
+void CrusherObject::Draw( Gameduino *gd ) {
+    // Only update stunFrameCount when not stunned.
+    // This halts animation when in stunned state.
+    if( stunCountdown == 0 ) {
+        stunFrameCount = FrameCounter >> 3;
+    }
+    Walker::Draw( gd, SpriteNumber, Xco, Yco, hSpeed, stunFrameCount, animationData );
+}
+
+/********************************************/
+/* INFORM ENEMY IT HAS BEEN HIT BY A BULLET */
+/********************************************/
+// Default implementation does nothing but if special behaviour
+// is required in a derived class then this should be overridden.
+// Note that this does NOT deal with determining if the enemy is dead or not.
+// An enemy ALWAYS dies if HitPoints reaches zero.
+void CrusherObject::RegisterHitByBullet( void ) {
+    stunCountdown = 10;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CrusherObject.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,87 @@
+/*
+ * SOURCE FILE : CrusherObject.h
+ *
+ * Represents a wandering human.
+ *
+ */
+
+#ifndef CrusherObjectIncluded
+  
+  #define CrusherObjectIncluded
+
+  #include "Types.h"
+  #include "EnemyObject.h"
+  #include "GameObjectTypes.h"
+
+  class CrusherObject : public EnemyObject {
+    
+  public :
+
+    /***************/
+    /* CONSTRUCTOR */
+    /***************/
+    CrusherObject();
+    
+    /**************/
+    /* DESTRUCTOR */
+    /**************/
+    virtual ~CrusherObject();
+
+    /*****************************/
+    /* GET TYPE OF ENEMY THIS IS */
+    /*****************************/
+    // Returns enemy type.
+    virtual EnemyType GetEnemyType( void ) {
+        return Crusher;
+    }
+
+    /*******************************************************/
+    /* GET NUMBER OF POINTS AWARDED FOR KILLING THIS ENEMY */
+    /*******************************************************/
+    // Returns number of points.
+    virtual UInt8 GetPoints( void ) {
+      return 0x10;  // BCD!
+    }
+
+    /************************/
+    /* MOVE THE GAME OBJECT */
+    /************************/
+    virtual void ProtectedMove( void );
+
+    /************************/
+    /* DRAW THE GAME OBJECT */
+    /************************/
+    // Pass pointer to a Gameduino to draw on in gd.
+    // This is only called after it has been established that the
+    // game object is visible.
+    virtual void Draw( Gameduino *gd );
+   
+    /********************************************/
+    /* INFORM ENEMY IT HAS BEEN HIT BY A BULLET */
+    /********************************************/
+    // Default implementation does nothing but if special behaviour
+    // is required in a derived class then this should be overridden.
+    // Note that this does NOT deal with determining if the enemy is dead or not.
+    // An enemy ALWAYS dies if HitPoints reaches zero.
+    virtual void RegisterHitByBullet( void );
+
+  private :
+
+    // Address of animation data in program memory.
+    const UInt8 *animationData;
+    
+    // Horizontal and vertical velocities.
+    Int16 hSpeed, vSpeed;
+
+    // Stun countdown. Crusher does not move until this reaches zero.
+    UInt8 stunCountdown;
+    
+    // Frame count at the moment crusher was stunned.
+    UInt8 stunFrameCount;
+        
+  };
+    
+#endif
+
+/* END of CrusherObject.h */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EnemyObject.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,30 @@
+/*
+ * SOURCE FILE : EnemyObject.cpp
+ *
+ * Base class for enemy objects.
+ *
+ */
+
+#include "EnemyObject.h"
+
+// Default object to chase.
+PlayerObject EnemyObject::defaultChaseObject;
+
+/*****************************************************/
+/* CHECK IF ALL SURVIVING ENEMIES ARE INDESTRUCTABLE */
+/*****************************************************/
+// Pass pointer to array of pointers to EnemyObjects in enemies.
+// Pass number of pointers in the array in enemyCount.
+bool EnemyObject::AreAllIndestructable( const EnemyObject **enemies, UInt8 enemyCount ) {
+  const EnemyObject *enemy;
+  bool foundMortal = false;
+  UInt8 i = 0;
+    while( ! foundMortal && ( i < enemyCount ) ) {
+    enemy = enemies[ i ];
+    if( ( enemy != (EnemyObject*)NULL ) && ( enemy->HitPoints != Indestructable ) ) {
+      foundMortal = true;
+    }
+        i++;
+  }
+  return ! foundMortal;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EnemyObject.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,112 @@
+/*
+ * SOURCE FILE : EnemyObject.h
+ *
+ * Base class for enemy objects.
+ *
+ */
+
+#ifndef EnemyObjectIncluded
+  
+  #define EnemyObjectIncluded
+
+  #include "GameObject.h"
+  #include "PlayerObject.h"
+    #include "EnemyType.h"
+
+  class EnemyObject : public GameObject {
+    
+  public :
+
+        enum {
+            Indestructable = 0xFF,
+        };
+
+        // Number of hit points remaining.
+        // When this reaches zero the enemy dies.
+        // However, if this has the special value Indestructable then enemy cannot be killed.
+        UInt8 HitPoints;
+        
+        // Set to true of the enemy squashes humans.
+        bool SquashesHumans;
+        
+        // Pointer to array of pointers to enemies.
+        // Some enemies may need to add new enemies (such as bullets or new spawning enemies) to this array.
+        GameObject **Enemies;
+        
+    /***************/
+    /* CONSTRUCTOR */
+    /***************/
+    EnemyObject() :
+            HitPoints( 1 ),
+            SquashesHumans( false ),
+            Enemies( (GameObject**)NULL ),
+      chaseObject( &defaultChaseObject )
+    {
+    }
+
+    /**************/
+    /* DESTRUCTOR */
+    /**************/
+    virtual ~EnemyObject() {
+    }
+    
+    /************************/
+    /* GET GAME OBJECT TYPE */
+    /************************/
+    // Returns type of game object.
+    virtual GameObjectTypes GetType( void ) {
+      return EnemyObjectType;
+    }
+
+        /*****************************/
+        /* GET TYPE OF ENEMY THIS IS */
+        /*****************************/
+        // Returns enemy type.
+        virtual EnemyType GetEnemyType( void ) = 0;
+        
+    /*******************************************************/
+    /* GET NUMBER OF POINTS AWARDED FOR KILLING THIS ENEMY */
+    /*******************************************************/
+    // Returns number of points (in BCD).
+    virtual UInt8 GetPoints( void ) = 0;
+
+    /***********************/
+    /* SET OBJECT TO CHASE */
+    /***********************/
+    // Pass pointer to object to chase in co.
+    void SetChaseObject( GameObject *co ) {
+      chaseObject = co;
+    }
+    
+        /********************************************/
+        /* INFORM ENEMY IT HAS BEEN HIT BY A BULLET */
+        /********************************************/
+        // Default implementation does nothing but if special behaviour
+        // is required in a derived class then this should be overridden.
+        // Note that this does NOT deal with determining if the enemy is dead or not.
+        // An enemy ALWAYS dies if HitPoints reaches zero.
+        virtual void RegisterHitByBullet( void ) {}
+        
+        /*****************************************************/
+        /* CHECK IF ALL SURVIVING ENEMIES ARE INDESTRUCTABLE */
+        /*****************************************************/
+        // Pass pointer to array of pointers to EnemyObjects in enemies.
+        // Pass number of pointers in the array in enemyCount.
+        static bool AreAllIndestructable( const EnemyObject **enemies, UInt8 enemyCount );
+        
+  protected :
+
+    // Object to chase.
+    GameObject *chaseObject;
+    
+  private :
+  
+    // Default object to chase.
+    static PlayerObject defaultChaseObject;
+    
+  };
+    
+#endif
+
+/* END of EnemyObject.h */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EnemyType.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,23 @@
+/*
+ * 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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GruntObject.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,19 @@
+/*
+ * SOURCE FILE : GruntObject.cpp
+ *
+ * Represents the grunt enemy object.
+ *
+ */
+
+#include "GruntObject.h"
+
+// Speed at which grunt moves.
+Int16 GruntObject::gruntSpeed = FromPixel( 1 ) >> 2;
+
+/************************/
+/* MOVE THE GAME OBJECT */
+/************************/
+void GruntObject::ProtectedMove( void ) {
+    MoveTowards( chaseObject, gruntSpeed );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GruntObject.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,74 @@
+/*
+ * SOURCE FILE : GruntObject.h
+ *
+ * Represents the grunt enemy object.
+ *
+ */
+
+#ifndef GruntObjectIncluded
+  
+  #define GruntObjectIncluded
+
+  #include "EnemyObject.h"
+  #include "SpriteImageId.h"
+  #include "FrameCounter.h"
+  
+  class GruntObject : public EnemyObject {
+    
+  public :
+
+    /***************/
+    /* CONSTRUCTOR */
+    /***************/
+    GruntObject() {
+    }
+
+    /**************/
+    /* DESTRUCTOR */
+    /**************/
+    virtual ~GruntObject() {
+    }
+    
+        /*****************************/
+        /* GET TYPE OF ENEMY THIS IS */
+        /*****************************/
+        // Returns enemy type.
+        virtual EnemyType GetEnemyType( void ) {
+            return Grunt;
+        }
+
+    /*******************************************************/
+    /* GET NUMBER OF POINTS AWARDED FOR KILLING THIS ENEMY */
+    /*******************************************************/
+    // Returns number of points.
+    virtual UInt8 GetPoints( void ) {
+      return 0x5;  // In BCD!
+    }
+
+    /************************/
+    /* MOVE THE GAME OBJECT */
+    /************************/
+    virtual void ProtectedMove( void );
+
+    /************************/
+    /* DRAW THE GAME OBJECT */
+    /************************/
+    // Pass Gameduino to draw on in gd.
+    // This is only called after it has been established that the
+    // game object is visible.
+    virtual void Draw( Gameduino *gd ) {
+      Gameduino::Rotation transform = ( FrameCounter & 8 ) ? Gameduino::FlipX : Gameduino::None;
+      gd->sprite( SpriteNumber, ToPixel( Xco ), ToPixel( Yco ), GruntImage, 0, transform, BadGuy );
+    }
+   
+  private :
+  
+    // Speed at which grunts move.
+    static Int16 gruntSpeed;
+    
+  };
+    
+#endif
+
+/* END of GruntObject.h */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HumanObject.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,92 @@
+/*
+ * SOURCE FILE : HumanObject.cpp
+ *
+ * Represents a wandering human.
+ *
+ */
+
+#include "HumanObject.h"
+#include "Gameduino.h"
+#include "FrameCounter.h"
+#include "ArenaConst.h"
+#include "Animations.h"
+#include "Walker.h"
+
+// Index of next animation to use when constructing.
+UInt8 HumanObject::nextAnimationIndex = 0;
+
+/***************/
+/* CONSTRUCTOR */
+/***************/
+// Pass number of sprite to use in sn.
+// Pass address of animation data in ad.
+// Animation data must consist of AnimationStages bytes which gives the sprite
+// image numbers used for each state of the animation.
+HumanObject::HumanObject() :
+  CurrentState( WalkingAbout ),
+  animationData( Animations::HumanAnimations[ nextAnimationIndex++ ] ),
+  countdown( 100 )
+{
+  // Wrap around animation index.
+  nextAnimationIndex %= Animations::HumanAnimationCount;
+  // Initialise horizontal and vertical speeds.
+    Walker::InitialiseVelocities( &hSpeed, &vSpeed );
+  // Movement is always restricted (property of GameObject).
+  MovementRestricted = true;
+  // Restrict to boundary of arena.
+  Bounds = &ArenaRectangle;
+}
+
+/**************/
+/* DESTRUCTOR */
+/**************/
+HumanObject::~HumanObject() {
+}
+
+/*****************************/
+/* GET TYPE OF HUMAN THIS IS */
+/*****************************/
+// Returns type of human.
+HumanObject::HumanType HumanObject::GetHumanType( void ) {
+    return ( animationData == Animations::WomanAnimation ) ? Woman : Man;
+}
+
+/************************/
+/* MOVE THE GAME OBJECT */
+/************************/
+void HumanObject::ProtectedMove( void ) {
+  switch( CurrentState ) {
+  case WalkingAbout :
+    // Make human bounce of edges of restriction area.
+        Walker::Walk( &Xco, &Yco, &hSpeed, &vSpeed, RestrictionFlags );
+    break;
+  case Rescued :
+    case Dead :
+    // Count down and when count reaches zero make human invisible.
+    if( countdown > 0 ) {
+      countdown--;
+    }
+    Visible = ( countdown > 0 );
+    break;
+  }
+}
+
+/************************/
+/* DRAW THE GAME OBJECT */
+/************************/
+// Pass pointer to a Gameduino to draw on in gd.
+// This is only called after it has been established that the
+// game object is visible.
+void HumanObject::Draw( Gameduino *gd ) {
+  switch( CurrentState ) {
+  case WalkingAbout :
+    Walker::Draw( gd, SpriteNumber, Xco, Yco, hSpeed, FrameCounter >> 3, animationData );
+    break;
+  case Rescued :
+    gd->sprite( SpriteNumber, ToPixel( Xco ), ToPixel( Yco ), FiftyImage, 0, Gameduino::None, GoodGuy );
+    break;
+  case Dead :
+    gd->sprite( SpriteNumber, ToPixel( Xco ), ToPixel( Yco ), SkullImage, 0, Gameduino::None, GoodGuy );
+    break;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HumanObject.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,100 @@
+/*
+ * SOURCE FILE : HumanObject.h
+ *
+ * Represents a wandering human.
+ *
+ */
+
+#ifndef HumanObjectIncluded
+  
+  #define HumanObjectIncluded
+
+  #include "Types.h"
+  #include "GameObject.h"
+  #include "GameObjectTypes.h"
+
+  class HumanObject : public GameObject {
+    
+  public :
+
+    // States a human can be in.
+    // Each human starts in the WalkingAbout state and keeps wandering around as
+    // long as it is in this state.
+    // If the player touches a human than it changes to the Rescued state and
+    // it stops walking and its  appearance changes to show the number of points
+    // awarded. Eventually this disappears and the Visible property inherited from
+    // GameObject is set to false. On next draw the human will be removed from the
+    // array of humans and replaced with NULL.
+    enum State {
+      WalkingAbout,
+      Rescued,
+            Dead,
+    };
+    
+        // Different kinds of human.
+        enum HumanType {
+            Woman,
+            Man,
+        };
+        
+    // Current state of human.
+    State CurrentState;
+    
+    /***************/
+    /* CONSTRUCTOR */
+    /***************/
+    HumanObject();
+    
+    /**************/
+    /* DESTRUCTOR */
+    /**************/
+    virtual ~HumanObject();
+
+    /************************/
+    /* GET GAME OBJECT TYPE */
+    /************************/
+    // Returns type of game object.
+    virtual GameObjectTypes GetType( void ) {
+      return HumanObjectType;
+    }
+
+        /*****************************/
+        /* GET TYPE OF HUMAN THIS IS */
+        /*****************************/
+        // Returns type of human.
+        HumanType GetHumanType( void );
+        
+    /************************/
+    /* MOVE THE GAME OBJECT */
+    /************************/
+    virtual void ProtectedMove( void );
+
+    /************************/
+    /* DRAW THE GAME OBJECT */
+    /************************/
+    // Pass pointer to Gameduino to draw on in gd.
+    // This is only called after it has been established that the
+    // game object is visible.
+    virtual void Draw( Gameduino *gd );
+   
+  private :
+
+    // Address of animation data in program memory.
+    const UInt8 *animationData;
+    
+    // Horizontal and vertical velocities.
+    Int16 hSpeed, vSpeed;
+
+    // Countdown used when in rescued or dead states.
+    UInt8 countdown;
+    
+    // Index of next animation to use when constructing.
+    // Cycles around all human animations.
+    static UInt8 nextAnimationIndex;
+    
+  };
+    
+#endif
+
+/* END of HumanObject.h */
+
--- a/Level.h	Sat Jun 08 15:50:38 2013 +0000
+++ b/Level.h	Sat Jun 08 16:44:54 2013 +0000
@@ -21,14 +21,15 @@
   #include "CharFrame.h"
   #include "PanelControls.h"
   #include "PlayerObject.h"
-#if 0
   #include "ArenaConst.h"
   #include "SpriteImageId.h"
   #include "GameObject.h"
+  #include "HumanObject.h"
   #include "GruntObject.h"
-  #include "BlueMeanyObject.h"
   #include "CrusherObject.h"
   #include "BrainObject.h"
+#if 0
+  #include "BlueMeanyObject.h"
   #include "SoundManager.h"
   #include "Sounds.h"
 #endif
--- a/Level1.cpp	Sat Jun 08 15:50:38 2013 +0000
+++ b/Level1.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -15,7 +15,6 @@
 // Returns code indicating how level ended.
 Level::LevelExitCode Level1::Play( void ) {
   LevelData dataForThisLevel;
-#if 0
   GameObject **ptr = dataForThisLevel.Enemies;
   UInt8 i, humanCount, enemyCount = 0;
   // Enemies of type GruntObject.
@@ -44,7 +43,6 @@
   for( i = 0; i < humanCount; ++i ) {
     *ptr++ = humans + i;
   }
-#endif
   DataForLevel = &dataForThisLevel;
   return PlayLoop();
 }
--- a/Level2.cpp	Sat Jun 08 15:50:38 2013 +0000
+++ b/Level2.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -14,7 +14,6 @@
 /**************/
 // Returns code indicating how level ended.
 Level::LevelExitCode Level2::Play( void ) {
-#if 0
   LevelData dataForThisLevel;
   GameObject **ptr = dataForThisLevel.Enemies;
   UInt8 i, humanCount, enemyCount = 0;
@@ -46,7 +45,4 @@
   }
   DataForLevel = &dataForThisLevel;
   return PlayLoop();
-#else
-  return Completed;
-#endif
 }
--- a/LevelNormal.cpp	Sat Jun 08 15:50:38 2013 +0000
+++ b/LevelNormal.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -57,7 +57,6 @@
     // Use next free sprite number for player.
     player->SpriteNumber = PlayerSprite;
     // Do futher initialisation for all enemies.
-#if 0
     EnemyObject *object;
     for( UInt8 e = 0; e < LevelData::MaxEnemies; ++e ) {
         object = (EnemyObject*)DataForLevel->Enemies[ e ];
@@ -72,7 +71,6 @@
             }
         }
     }
-#endif
     // Put player in the centre of the arena.
     player->Xco = PLAYER_START_X;
     player->Yco = PLAYER_START_Y;
@@ -317,9 +315,9 @@
 #endif
             // Redraw the player's score and number of lives.
             DrawScoreAndLives();
+            // Draw all the enemies.
+            GameObject::DrawAll( gd, DataForLevel->Enemies, LevelData::MaxEnemies );
 #if 0
-            // Draw all the enemies.
-            GameObject::DrawAll( DataForLevel->Enemies, LevelData::MaxEnemies );
             // Draw all the humans.
             GameObject::DrawAll( DataForLevel->Humans, LevelData::MaxHumans );
             // Draw all the explosions.
@@ -364,7 +362,6 @@
                 }
             }
             else {
-#if 0
                 // 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.
@@ -377,6 +374,7 @@
                         LevelData::MaxEnemies
                     );
                 }
+#if 0
                 // Move all the humans.
                 GameObject::MoveAll( DataForLevel->Humans, LevelData::MaxHumans );
                 // Move (update) all the explosions.
--- a/PlayerObject.cpp	Sat Jun 08 15:50:38 2013 +0000
+++ b/PlayerObject.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -172,11 +172,11 @@
   // Check if the fourth digit from the right has changed.
   // If it has then you must have passed through a thousand point
   // boundary so award an extra life but don't let it overflow.
+  if( ( digit != (UInt8)( ( Score & 0xF000 ) >> 12 ) ) && ( Lives < 255 ) ) {
 #if 0
-  if( ( digit != (UInt8)( ( Score & 0xF000 ) >> 12 ) ) && ( Lives < 255 ) ) {
     SoundManager::Instance.PlaySound( Sounds::ExtraLife, 0, 0 );
+#endif
     Lives++;
   }
-#endif
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Walker.cpp	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,64 @@
+/*
+ * SOURCE FILE : Walker.cpp
+ *
+ * Methods for objects that walk sideways on (like humans and crushers).
+ * 
+ */
+
+#include "Gameduino.h"                    // Gameduino stuff
+#include "GDConst.h"
+#include "Walker.h"
+#include "Random.h"
+
+/*************************************************/
+/* INITIALISE HORIZONTAL AND VERTICAL VELOCITIES */
+/*************************************************/
+// Pass pointers to horizontal and vertical velocities in hv and vv.
+void Walker::InitialiseVelocities( Int16 *hv, Int16 *vv ) {
+  // Initialise horizontal and vertical speeds.
+  UInt8 rnd = Random::Get( 4 );
+  *hv = GameObject::FromPixel( 1 ) >> 2;
+  if( rnd & 1 ) {
+    *hv = -(*hv);
+  }
+  *vv = GameObject::FromPixel( 1 ) >> 4;
+  if( rnd & 2 ) {
+    *vv = -(*vv);
+  }
+}
+
+/*********************************************************/
+/* UPDATE COORDINATES AND VELOCITIES TO MAKE OBJECT WALK */
+/*********************************************************/
+// Pass pointers to x and y coordinates in x and y.
+// Pass pointers to horizontal and vertical velocities in hv and vv.
+// Pass restriction flags (as defined in GameObject.h) in restriction Flags.
+void Walker::Walk( Int16 *x, Int16 *y, Int16 *hv, Int16 *vv, UInt8 restrictionFlags ) {
+    // Make object bounce of edges of restriction area.
+    if( restrictionFlags & (UInt8)( GameObject::LeftRestriction | GameObject::RightRestriction ) ) {
+      *hv = -(*hv);
+    }
+    if( restrictionFlags & (UInt8)( GameObject::UpRestriction | GameObject::DownRestriction ) ) {
+      *vv = -(*vv);
+    }
+    *x += *hv;
+    *y += *vv;
+}
+
+/*************************/
+/* DRAW A WALKING OBJECT */
+/*************************/
+// Pass pointer to Gameduino to draw on in gd.
+// Pass sprite number in spriteNumber.
+// Pass x and y coordinates in x and y (NOT pixel coordinates).
+// Pass counter used to pace animation in frameCounter.
+// Pass pointer to animation data (array of AnimationStages sprite image numbers) in animationData.
+void Walker::Draw( Gameduino *gd, UInt8 spriteNumber, Int16 x, Int16 y, Int16 hv, UInt8 frameCounter, const UInt8 *animationData ) {
+    // Reverse sprite image horizontally if horizontal speed is positive.
+    Gameduino::Rotation transform = ( hv < 0 ) ? Gameduino::None : Gameduino::FlipX;
+    // Image number changes with the frame counter.
+    const UInt8 *address = animationData + frameCounter % AnimationStages;
+    // Update sprite location and image. Note the last parameter is BadGuy so that it is possible
+    // for player to collide with a human. GoodGuy would mean collisions would be ignored.
+    gd->sprite( spriteNumber, GameObject::ToPixel( x ), GameObject::ToPixel( y ), *address, 0, transform, BadGuy );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Walker.h	Sat Jun 08 16:44:54 2013 +0000
@@ -0,0 +1,55 @@
+/*
+ * SOURCE FILE : Walker.h
+ *
+ * Methods for objects that walk sideways on (like humans and crushers).
+ *
+ */
+
+#ifndef WalkerDefined
+
+  #define WalkerDefined
+
+    #include "Types.h"
+    #include "GameObject.h"
+    
+  class Walker {
+
+  public :
+
+    enum {
+      AnimationStages = 4,
+    };
+
+        /*************************************************/
+        /* INITIALISE HORIZONTAL AND VERTICAL VELOCITIES */
+        /*************************************************/
+        // Pass pointers to horizontal and vertical velocities in hv and vv.
+        static void InitialiseVelocities( Int16 *hv, Int16 *vv );
+        
+        /*********************************************************/
+        /* UPDATE COORDINATES AND VELOCITIES TO MAKE OBJECT WALK */
+        /*********************************************************/
+        // Pass pointers to x and y coordinates in x and y.
+        // Pass pointers to horizontal and vertical velocities in hv and vv.
+        // Pass restriction flags (as defined in GameObject.h) in restriction Flags.
+        static void Walk( Int16 *x, Int16 *y, Int16 *hv, Int16 *vv, UInt8 restrictionFlags );
+        
+        /*************************/
+        /* DRAW A WALKING OBJECT */
+        /*************************/
+        // Pass pointer to Gameduino to draw on in gd.
+        // Pass sprite number in spriteNumber.
+        // Pass x and y coordinates in x and y (NOT pixel coordinates).
+        // Pass horizontal velocity in hv.
+        // Pass counter used to pace animation in frameCounter.
+        // Pass pointer to animation data (array of AnimationStages sprite image numbers) in animationData.
+        static void Draw( Gameduino *gd, UInt8 spriteNumber, Int16 x, Int16 y, Int16 hv, UInt8 frameCounter, const UInt8 *animationData );
+        
+    private :
+    
+        };
+
+#endif
+
+/* END of Walker.h */
+