/*
File: gameworms.cpp
Uploaded by joecheng on Wed Nov 28 10:25:42 PST 2001
*/

// gameworms.cpp: implementation of the generic cGameWorms class.
//
// Cleaned up version 11/16/01 -Rich
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "gameworms.h"
#include "critterwall.h"				/* Lee 11-04-01 */
#include "listener.h"
#include "spriteicon.h"
#include "spritemultiicon.h"
#include "pop.h" //for playSound
#include "popview.h"
#include "force.h"
#include "spritebubble.h"

IMPLEMENT_SERIAL(cGameWorms, cGame, 0);
IMPLEMENT_SERIAL(cCritterMazeWall, cCritterWall, 0);	/* Lee 11-04-01 */
IMPLEMENT_SERIAL(cCritterWormsPlayer, cCritter, 0);		/* Lee 11-11-01 */
IMPLEMENT_SERIAL(cCritterPlayerWormsSeg, cCritter, 0);	/* Lee 11-11-01 */
IMPLEMENT_SERIAL(cCritterWormsSeg, cCritter, 0);		/* Lee 11-11-01 */
IMPLEMENT_SERIAL(cCritterWormsEnemyHead, cCritter, 0);	/* Lee 11-11-01 */
IMPLEMENT_SERIAL(cCritterWormsPlayerEgg, cCritter, 0 );
IMPLEMENT_SERIAL(cCritterWormsEnemyEgg, cCritter, 0 );
IMPLEMENT_SERIAL(cCritterWormsFood, cCritter, 0 );

//Defines
#define PLAYERSPRITELOOP
//#define NOWALLS
#define NOEAT
#define NOEGG
#define NOFOOD
	/* We have the option of havin the cCritterWormsPlayer either use
	an "animated" cSpriteLoop or use a directionally-sensitive cSpriteDirectional.
	Comment the #define PLAYERSPRITELOOP in for the cSpriteLoop,
	comment it out for the	cSpriteDirectional. */

//==============================cGameWorms
int cGameWorms::PLAYERHEALTH = 5; 
int cGameWorms::WORMCOUNT = 3; //1;		/* Lee added 11-04-01 */
static int randomLow = -4;				/* 11-12 Joe */
static int randomHigh = 4;				/* 11-12 Joe */
static int randomMinTime = 2;			/* min time in seconds before new animal 11-13  Joe */   
static int randomMaxTime = 6;			/* max time in seconds before new animal 11-13  Joe */
static int minFoodTime = 5;				/* min time between the creation of random food critter */
static int maxFoodTime = 10;			/* max time between the creation of random food critter */
static int worldWidth = 38.0;
static int worldHeight = 29.0;
int playerStartLength = 3;
int playerLives = 3;
int wallThickness = 0.4;

cGameWorms::cGameWorms(): //This constructor gets called only once, at first start.
	_rivalcount(3), _playerLength( playerStartLength )
{
   //Setup the random amount of time until an animal is released /* 11-13 Joe  */
   _timeUntilFood = cRandomizer::pinstance()->randomReal( randomMinTime, randomMaxTime );

	_menuflags &= ~cGame::MENU_SHIELD; //We don't use the shield control in this game.

	//Usually we set _seedcount.
	_seedcount = 6; //The number of cCritterWormsSeg that I plan to add.

	// statics
	cCritter::MAXSPEED = 10.0;

	// cursor tools
	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPlay); 
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorDragger); 
	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPin);
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorReplicate); 
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorZap); 

	//Set your world shape
	_border.set(worldWidth, worldHeight);

	//Set the backgroudn bitmap
	setBackgroundBitmap(IDB_GARDEN);

	//Set wrap with setWrapflag(cCritter::WRAP) or cCritter::BOUNCE.
	cCritterWall::THICKNESS = 0.4; /* Lee 11-04-01 */
	setWrapflag(cCritter::BOUNCE); /* Lee 11-04-01 */

   initializeLocations();

#ifndef NOWALLS
   initiateMap1();
#endif

	//Make a player   
   initiatePlayer(_playerLength);	/* 11-5 Joe */
}

void cGameWorms::step( Real dt, CPopView *pactiveview )
{  
#ifndef NOFOOD
   if( pbiota()->count(RUNTIME_CLASS(cCritterWormsFood)) < 1 )
      _timeUntilFood -= dt;

   if( _timeUntilFood <= 0 )
   {
      cCritterWormsFood * newFood = new cCritterWormsFood();
      add( newFood );

      // Reset the time until food counter
      _timeUntilFood = cRandomizer::pinstance()->randomReal( minFoodTime, maxFoodTime );
   }
#endif
   
   cGame::step( dt, pactiveview );
}

void cGameWorms::initiateMap1()
{
	add(new cCritterMazeWall( cVector(-16.5, 12),	cVector(-13.5, 12))); // 1 
	add(new cCritterMazeWall( cVector(-16.5, 12),	cVector(-16.5, 6))); // 2
	add(new cCritterMazeWall( cVector(-16.5, 3),	cVector(-13.5, 3))); // 3
	add(new cCritterMazeWall( cVector(-13.5, 3),	cVector(-13.5, 6))); // 4
	add(new cCritterMazeWall( cVector(-13.5, 6),	cVector(-10.5, 6))); // 5
	add(new cCritterMazeWall( cVector(-13.5, 9),	cVector(-10.5, 9))); // 6 
	add(new cCritterMazeWall( cVector(-10.5, 9),	cVector(-10.5, 12))); // 7
	add(new cCritterMazeWall( cVector(-10.5, 12),	cVector(-7.5, 12))); // 8
	add(new cCritterMazeWall( cVector(-7.5, 9),	cVector(-7.5, 3))); // 9
	add(new cCritterMazeWall( cVector(-7.5, 6),	cVector(-1.5, 6))); // 10
	add(new cCritterMazeWall( cVector(-4.5, 12),	cVector(-4.5, 9))); // 11 
	add(new cCritterMazeWall( cVector(-4.5, 9),	cVector(1.5, 9))); // 12
	add(new cCritterMazeWall( cVector(-1.5, 12),	cVector(4.5, 12))); // 13
	add(new cCritterMazeWall( cVector(-4.5, 3),	cVector(1.5, 3))); // 14
	add(new cCritterMazeWall( cVector(1.5, 3),	cVector(1.5, 0))); // 15
	add(new cCritterMazeWall( cVector(-1.5, 0),	cVector(-1.5, .4))); // 16 
	add(new cCritterMazeWall( cVector(-4.5, 0),	cVector(-4.5, -3))); // 17
	add(new cCritterMazeWall( cVector(-4.5, -3),	cVector(1.5, -3))); // 18
	add(new cCritterMazeWall( cVector(-7.5, 0),	cVector(-13.5, 0))); // 19
	add(new cCritterMazeWall( cVector(-10.5, 3),	cVector(-10.5, -3))); // 20
	add(new cCritterMazeWall( cVector(-16.5, 0),	cVector(-16.5, -6))); // 21 
	add(new cCritterMazeWall( cVector(-16.5, -3),	cVector(-13.5, -3))); // 22
	add(new cCritterMazeWall( cVector(-16.5, -9),	cVector(-10.5, -9))); // 23
	add(new cCritterMazeWall( cVector(-13.5, -9),	cVector(-13.5, -6))); // 24
	add(new cCritterMazeWall( cVector(-16.5, -12),	cVector(-13.5, -12))); // 25
	add(new cCritterMazeWall( cVector(-10.5, -12),	cVector(-7.5, -12))); // 26 
	add(new cCritterMazeWall( cVector(-7.5, -12),	cVector(-7.5, -9))); // 27
	add(new cCritterMazeWall( cVector(-10.5, -6),	cVector(-4.5, -6))); // 28
	add(new cCritterMazeWall( cVector(-7.5, -6),	cVector(-7.5, -3))); // 29
	add(new cCritterMazeWall( cVector(-1.5, -6),	cVector(-1.5, -9))); // 30
	add(new cCritterMazeWall( cVector(-1.5, -9),	cVector(-4.5, -9))); // 31 
	add(new cCritterMazeWall( cVector(-4.5, -9),	cVector(-4.5, -12))); // 32
	add(new cCritterMazeWall( cVector(-1.5, -12),	cVector(4.5, -12))); // 33
	add(new cCritterMazeWall( cVector(1.5, -12),	cVector(1.5, -6))); // 34
	add(new cCritterMazeWall( cVector(4.5, -9),	cVector(4.5, -8.6))); // 35
	add(new cCritterMazeWall( cVector(7.5, -12),	cVector(16.5, -12))); // 36 
	add(new cCritterMazeWall( cVector(10.5, -12),	cVector(10.5, -9))); // 37
	add(new cCritterMazeWall( cVector(7.5, -9),	cVector(7.5, -3))); // 38
	add(new cCritterMazeWall( cVector(13.5, -9),	cVector(16.5, -9))); // 39
	add(new cCritterMazeWall( cVector(13.5, -6),	cVector(16.5, -6))); // 40
	add(new cCritterMazeWall( cVector(16.5, -6),	cVector(16.5, -3))); // 41 
	add(new cCritterMazeWall( cVector(10.5, -3),	cVector(13.5, -3))); // 42
	add(new cCritterMazeWall( cVector(13.5, -3),	cVector(13.5, 0))); // 43
	add(new cCritterMazeWall( cVector(13.5, 0),	cVector(16.5, 0))); // 44
	add(new cCritterMazeWall( cVector(4.5, -3),	cVector(4.5, 0))); // 45
	add(new cCritterMazeWall( cVector(4.5, 0),	cVector(7.5, 0))); // 46 
	add(new cCritterMazeWall( cVector(7.5, 0),	cVector(7.5, 3))); // 47
	add(new cCritterMazeWall( cVector(10.5, 0),	cVector(10.5, 3))); // 48
	add(new cCritterMazeWall( cVector(10.5, 3),	cVector(16.5, 3))); // 49
	add(new cCritterMazeWall( cVector(10.5, 6),	cVector(16.5, 6))); // 50
	add(new cCritterMazeWall( cVector(13.5, 6),	cVector(13.5, 9))); // 51 
	add(new cCritterMazeWall( cVector(16.5, 9),	cVector(16.5, 12))); // 52
	add(new cCritterMazeWall( cVector(16.5, 12),	cVector(10.5, 12))); // 53
	add(new cCritterMazeWall( cVector(10.5, 9),	cVector(7.5, 9))); // 54
	add(new cCritterMazeWall( cVector(7.5, 9),	cVector(7.5, 12))); // 55
	add(new cCritterMazeWall( cVector(7.5, 6),	cVector(1.5, 6))); // 56 
	add(new cCritterMazeWall( cVector(4.5, 9),	cVector(4.5, 3)));	// 57
	add(new cCritterMazeWall( cVector(4.5, -6),	cVector(10.5, -6))); // 58
}

void cGameWorms::initializeLocations()
{
   startLocations[0] = cVector( 14, 11 );
   startLocations[1] = cVector( -14,-11 );
   startLocations[2] = cVector( 14, -11 );
   startLocations[3] = cVector( 5.5, 3 );
   startLocations[4] = cVector( 5, -4 );
   startLocations[5] = cVector( -9, 3 );
   startLocations[6] = cVector( 5, -10 );
   startLocations[7] = cVector( -6, 0 );
   startLocations[8] = cVector( -9, 9 );
   startLocations[9] = cVector( 0, 1 );
   startLocations[10] = cVector( -6, -3 );
   startLocations[11] = cVector( 9, 5);
   startLocations[12] = cVector( 2, 10 );
   startLocations[13] = cVector( -2, -11 );
   startLocations[14] = cVector( 5, 3 );
   startLocations[15] = cVector( 5, 3 );
   startLocations[16] = cVector( 5, 3 );
   startLocations[17] = cVector( 5, 3 );
   startLocations[18] = cVector( 5, 3 );
   startLocations[19] = cVector( 5, 3 );
}

void cGameWorms::seedCritters()
{
	pbiota()->purgeNonPlayerNonWallCritters(); 
  
	cCritterWormsBase * pprop_prior;	/* This is a special trick for the
										follow-the-leader thing we'll set up in 
										the next loop.  Not standard. */   

	for (int k=0; k < cGameWorms::WORMCOUNT; k++)
	{
      cCritterWormsSnake * snake = new cCritterWormsSnake( this, _seedcount - 1 );	/* 11-12 Joe  SnakeClass*/

      int locationNumber = 13;

		for (int i = 0; i < _seedcount - 1; i++)           /* 11-5 Joe */
		// To keep the number of segments <= _seedcount, adding a segment w/o
		// incrementing i during the first loop of the iteration.
		{
			if (i==0)
			{																/* 11-5 Joe */
				pprop_prior = new cCritterWormsEnemyHead( pplayer() );		/* 11-5 Joe */
				add( pprop_prior );											/* 11-5 Joe */
				pprop_prior->setMoveBox(cRealBox2(cVector(-19, -14.5), 
											cVector(19, 14.5)));			/* 11-12 Joe */
				pprop_prior->moveTo( startLocations[locationNumber] );	/* 11-12 Joe */
				pprop_prior->setSnake( snake );								/* 11-12 Joe SnakeClass*/
				snake ->setHead( pprop_prior );								/* 11-12 Joe SnakeClass*/
			}																/* 11-5 Joe */

			cCritterWormsSeg *pprop = new cCritterWormsSeg(pprop_prior);	/* Lee 11-11-01 */
			add(pprop);
			pprop->setMoveBox( cRealBox2(cVector(-19, -14.5), cVector(19, 14.5)));	/* 11-12 Joe */
			pprop->moveTo( startLocations[locationNumber] );				/* 11-12 Joe */
			pprop->setSnake( snake );										/* 11-12 Joe SnakeClass*/
			//pprop->randomize(cCritter::MF_POSITION);
			pprop_prior = pprop;
		}
      snake->setTail( pprop_prior );										/* 11-12 Joe SnakeClass*/
   }
}


/* This is used to setup a player and its tail, or body segments */
void cGameWorms::initiatePlayer( int _playerseedcount )
{
	cCritterWormsPlayer * newPlayer = new cCritterWormsPlayer;
	//cCritter * pprop_prior
	cCritterWormsBase *pprop_prior;											/* 11-12 Joe SnakeClass*/
	cCritterWormsSnake * snake = new cCritterWormsSnake(this, _playerseedcount-1);	/* 11-12 Joe SnakeClass*/

	newPlayer->setSnake( snake );							/* 11-12 Joe SnakeClass*/
	snake->setHead( newPlayer );							/* 11-12 Joe SnakeClass*/
	newPlayer->setMoveBox( cRealBox2( cVector( -100, -100 ), cVector( 100, 100 ) ) );	/* 11-12 Joe */
	newPlayer->moveTo( cVector( -17, 14 ) );                                            /* 11-12 Joe */

	for (int i = 0; i < _playerseedcount - 1; i++)		/* Lee 11-11-01 */
   // To keep the number of segments <= _seedcount, adding a segment w/o
   // incrementing i during the first loop of the iteration.
	{
		if (i==0)
			pprop_prior = newPlayer;

		cCritterPlayerWormsSeg *pprop = new cCritterPlayerWormsSeg(pprop_prior);	/* Lee 11-11-01 */
		add(pprop);
		pprop->setMoveBox( cRealBox2( cVector( -100, -100 ), cVector( 100, 100 ) ) );	/* 11-12 Joe */
		pprop->moveTo( cVector( -17, 14 ) );                                            /* 11-12 Joe */
		pprop ->setSnake( snake );												/* 11-12 Joe SnakeClass*/
		//pprop->randomize(cCritter::MF_POSITION);
		pprop_prior = pprop;
	}

	setPlayer(newPlayer);
   snake->setTail( pprop_prior );												/* 11-12 Joe SnakeClass*/
}


// cGameWorms::reset does not get called at first startup, is called only
// when you start a new round or level of the game.
void cGameWorms::reset() 
{
   initiatePlayer( playerStartLength);                                              /* 11-5 Joe */
	cGame::reset();								//Calls pplayer()->reset() and seedCritters().
}

/* This gets called at startup and when you begin a new game.  This is the
place to set the background or the kind of cursor you want to use. */
void cGameWorms::initializeView(CPopView *pview)
{
	cGame::initializeView(pview);				//Always call baseclass
	pview->setCursor(((CPopApp*)::AfxGetApp())->_hCursorPlay);
	pview->setUseBackgroundBitmap(TRUE);		//TRUE if you want the background, FALSE if not.
}

CString cGameWorms::statusMessage()
  {
  	CString cStrStatusBar;
	int nUpdatesPerSecond;
	CString cStrUpdatespersecond;
	CString cStrPlayerSize;
	CString cStrCount;
	CString cStrScore;

	if (!gamepaused())
	{
		nUpdatesPerSecond = int(((CPopApp*)::AfxGetApp())->_timer.updatesPerSecond());
		if (!nUpdatesPerSecond)
			cStrUpdatespersecond.Format("Less than one update per second.");
		else	
			cStrUpdatespersecond.Format("Updates per second: %d.", nUpdatesPerSecond);
		if (((CPopApp*)::AfxGetApp())->_timer.runningNearMaxSpeed())
			cStrUpdatespersecond += " (Near Max)";
	}
	else
			cStrUpdatespersecond.Format("Animation is paused.");
	cStrScore.Format("Score: %d.", score());
	cStrPlayerSize.Format("Health: %d.", playerSize());
	int crittercount = _pbiota->count(RUNTIME_CLASS(cCritter));
	//int bulletcount = _pbiota->count(RUNTIME_CLASS(cCritterBullet));
   int wallCount = pbiota()->count(RUNTIME_CLASS(cCritterMazeWall));
	crittercount -= wallCount;
	if (visibleplayer()) /*Subtract off 1 for player as well. */
		crittercount -= 1;
	cStrCount.Format("Critters: %d.", crittercount);
	cStrStatusBar = cStrScore + "  " + cStrPlayerSize + "  " + cStrCount + "  " + cStrUpdatespersecond; 
	return cStrStatusBar;
  }

BOOL cGameWorms::collide(cCritter *pcriti, cCritter *pcritj)
{
	/* For now we just copy cGame.  But we may sometimes want to take that
	code and edit it so as to make sure that certain kinds of pcritj get
	to call pcritj->collide(pcriti) instead of the (normal) other way
	around.*/
		//*
   BOOL collide;

   BOOL isiwall = pcriti->IsKindOf(RUNTIME_CLASS(cCritterWall));
 	BOOL isjwall = pcritj->IsKindOf(RUNTIME_CLASS(cCritterWall));
 	if (isiwall && isjwall) // Assume two walls don't collide for now. 
 		return FALSE; //May want to rethink this if we derive a paddle from wall.
 	if (isiwall)
 		return pcriti->collide(pcritj);
 	if (isjwall)
 		return pcritj->collide(pcriti);

   // For all collisions involving the Player's Head, call the Player's head's collide method
   if( pcritj->IsKindOf( RUNTIME_CLASS( cCritterWormsPlayer ) ) )
      collide = pcritj->collide( pcriti );
   else if( pcriti->IsKindOf( RUNTIME_CLASS( cCritterWormsPlayer ) ) )
      collide = pcriti->collide( pcritj );

   // For all collision that involve the Enemy's Head and NOT the Player's head call the 
   // Enemy's Head's collide method
   else if( pcritj->IsKindOf( RUNTIME_CLASS( cCritterWormsEnemyHead ) ) )
      collide = pcritj->collide( pcriti );
   else if( pcriti->IsKindOf( RUNTIME_CLASS( cCritterWormsEnemyHead ) ) )
      collide = pcriti->collide( pcritj );

   // All important egg collisions are dealt with in the heads already
   // Let the other classes collide method deal with the collision is the collision involves
   // an egg.
   else if( pcriti->IsKindOf( RUNTIME_CLASS( cCritterWormsPlayerEgg ) ) )
      collide = pcritj->collide( pcriti );
   else if( pcriti->IsKindOf( RUNTIME_CLASS( cCritterWormsEnemyEgg ) ) )
      collide = pcritj->collide( pcriti );

   // Finally call the "normal" collide 
   else
      BOOL collide = cGame::collide(pcriti, pcritj);              //* 11-12 Joe */
	
	return collide;                  /* 11-12 Joe */
}

void cGameWorms::adjustGameParameters()
{
	// (1) End the game if the player is dead
	if (!health()  && !_gameover)		//Player's been killed and game's not over.
	{
		_gameover = TRUE;
		pplayer()->addScore(_scorecorrection);		// So user can reach _maxscore 
		((CPopApp*)::AfxGetApp())->playSound("Tada",
			SND_RESOURCE|SND_ASYNC);
      // Player want his default Length back
      _playerLength = playerStartLength;
		return; 
	}

	// (2) Perhaps do this to reseed the screen if rivals are gone.
	int othercrittercount = pbiota()->count(RUNTIME_CLASS(cCritter)) -
		pbiota()->count(RUNTIME_CLASS(cCritterWall)) -					/* Lee added 11-11-01 */
		pbiota()->count(RUNTIME_CLASS(cCritterPlayerWormsSeg)) - 1 -		/* Lee added 11-11-01 */
      pbiota()->count(RUNTIME_CLASS(cCritterWormsFood)) - 
      pbiota()->count(RUNTIME_CLASS(cCritterWormsPlayerEgg)) -
      pbiota()->count(RUNTIME_CLASS(cCritterWormsEnemyEgg));

	/*** possibly add to level here, maybe increase snake intel? ***/	/* Lee added 11-11-01 */
	//Number of critters minus bullets minus player equals other critters.
	if (!othercrittercount) //Player is alone with bullets
	{
      // Player gets to keep its lenght when the levels change
      _playerLength = playerSize();
      //_playerLength = pbiota()->count(RUNTIME_CLASS(cCritterPlayerWormsSeg));
		seedCritters();
		initiatePlayer( _playerLength );			/* 11-13 Joe */
		//initiateMap1();						/* 11-14 Joe */
	}

	// (3) Maybe check some other conditions.
}

void cGameWorms::Serialize(CArchive& ar)
{
	cGame::Serialize(ar);
	if (ar.IsStoring())			// Save any extra variables
		ar << _rivalcount ;		//Line of form ar << ...
	else						//Load any extra variables 
		ar >> _rivalcount;		//Line of form ar >> ...
}

//void cGameWorms::insertAnimal( Real dt )
//{
//}

cCritterWormsPlayer::cCritterWormsPlayer()
: _eggDropped( FALSE ), _livesLeft( playerLives )
{
   //_timeUntilEgg = cRandomizer::pinstance()->randomReal( randomMinTime, randomMaxTime );
   cCritterWormsBase::cCritterWormsBase();			/* 11-12 Joe SnakeClass*/

	//Set the listener
	setListener(new cListenerArrow());              /* 11-12 Joe */

	setHealth(cGameWorms::PLAYERHEALTH);
	setMaxspeed( cCritter::MAXSPEED );				/* 11-12 Joe */

	setSprite( new cSpriteIcon(IDB_PLAYERSNAKEHEAD) );
	//setRadius(1.5*cCritter::MINRADIUS);
   setRadius( 1.0 );
}

void cCritterWormsPlayer::reset()
{
	setHealth(cGameWorms::PLAYERHEALTH);
}

BOOL cCritterWormsPlayer::collide(cCritter *pcritter)
{
	//BOOL collideflag = cCritter::collide(pcritter);
#ifdef NOEAT
   return FALSE;
#endif

	//May sometimes do more stuff here if collideflag is TRUE.
   //if( collideflag )
   if (cCritter::distanceTo( pcritter ) < radius() + pcritter->radius() )        /* 11-24 Joe */
   {
  	  if (pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsSeg)))
      {
	     cCritterWormsSeg * seg = (cCritterWormsSeg *)pcritter;
         seg->getSnake()->eatenTail();
         //getSnake()->growSnake();
         return false;
         //pcritj->die();
	     //return FALSE;
      }
      else if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsEnemyHead)) )
      {
         cCritterWormsEnemyHead * head = (cCritterWormsEnemyHead *)pcritter;
         if( getSnake()->getSize() == head->getSnake()->getSize() )
            return false;
         
         else if( getSnake()->getSize() > head->getSnake()->getSize() )
         {
            head->getSnake()->eatenHead();
            head->die();
            getSnake()->growSnake();
            return false;
         }
         else
         {
            getSnake()->getGame()->setGameover( true );
            return false;
         }
      }
      else if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsEnemyEgg)))
      {
         pcritter->die();
         getSnake()->growSnake();
         return false;
      }
      else if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsPlayerEgg)))
         return false;
      else if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsFood)))
      {
         pcritter->die();
         getSnake()->growSnake();
         return false;
      }
      else if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterPlayerWormsSeg)))
         return false;
   }
	//return collideflag;
   return cCritter::collide( pcritter );
}

void cCritterWormsPlayer::update(Real dt, CPopView *pview)
{
	cCritter::update(dt, pview);	//Always call this first
   if( !_eggDropped )
      getSnake()->dropEgg( dt, _eggDropped );
   seedRandomFood( dt );
}

void cCritterWormsPlayer::seedRandomFood(Real dt)
{
}

void cCritterWormsPlayer::die()
{
   _livesLeft--;
   cCritter::die();
}

//==================== cCritterWormsSeg ====================

cCritterWormsSeg::cCritterWormsSeg(cCritterWormsBase *pcritter) /* Lee added 11-11-01 */
{
	setPrevious( pcritter );							/* 11-12 Joe SnakeClass*/
	//   setMaxspeed( cCritter::MAXSPEED + 1.5 );		/* 11-12 Joe */
	setSprite( new cSpriteIcon( IDB_ENEMYSNAKESEG ) );	/* Lee added 11-11-01 */
	setRadius( 1.0 );									/* 11-6 Joe */

	//Add some forces
	if (pcritter)
		addForce(new cForceObjectSpringRod(pcritter, 1.0, 1000));  
		// want distance between critters Lee added 11-11-01 */
}

BOOL cCritterWormsSeg::collide(cCritter *pcritter)
{
	// BOOL collideflag = cCritter::collide(pcritter);
		//May sometimes do more stuff here if collideflag is TRUE.

	//return collideflag;
   return false;
}

void cCritterWormsSeg::update(Real dt, CPopView *pview)
{
	cCritter::update(dt, pview); //Always call this first
}

void cCritterWormsSeg::die()
{
   cCritter::die();
   getSnake()->getGame()->pplayer()->addScore( 50 );
}

//==================== cCritterPlayerWormsSeg ====================

cCritterPlayerWormsSeg::cCritterPlayerWormsSeg(cCritterWormsBase *pcritter)
{  
	setPrevious( pcritter );						/* 11-12 Joe SnakeClass*/
	//   setMaxspeed( cCritter::MAXSPEED + 2.5 );	/* 11-12 Joe */

	setSprite( new cSpriteIcon( IDB_PLAYERSNAKESEG ) );
	setRadius( 1.0 );								/* 11-6 Joe */

	//Add some forces
	if (pcritter)
		addForce(new cForceObjectSpringRod(pcritter, 0.7, 1000));
}

BOOL cCritterPlayerWormsSeg::collide(cCritter *pcritter) /* Lee added 11-11-01 */
{
   /*if (cCritter::distanceTo( pcritter ) < radius() + pcritter->radius() )
   {
      if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsPlayerEgg)))
         return false;
   }*/
   return false;
}

void cCritterPlayerWormsSeg::update(Real dt, CPopView *pview) /* Lee added 11-11-01 */
{
	cCritter::update(dt, pview); //Always call this first
}

//===================cCritterMazeWall				/* Lee added 11-04-01 */
cCritterMazeWall::cCritterMazeWall(const cVector &enda, const cVector &endb,
										Real thickness):
cCritterWall(enda, endb, thickness)
{
	setFixedflag(TRUE);

   psprite()->setFillColor(cColorStyle::CN_DARKGREEN);          // Rich
   psprite()->setLineColor(cColorStyle::CN_NOTASDARKGREEN);     // Rich
   psprite()->setLineWidthWeight(0.04);                         // Rich
}

//===================cCritterWormsEnemyHead			/* 11-5 Joe */
cCritterWormsEnemyHead::cCritterWormsEnemyHead( cCritter * pcritter )
{
	_timeUntilEgg = cRandomizer::pinstance()->randomReal( randomMinTime, randomMaxTime );
	addForce( new cForceObjectAccelerateTowards( pcritter, 10 ) );     /* 11-12 Joe */
	setSprite( new cSpriteIcon(IDB_ENEMYSNAKEHEAD) );
	//randomize(cCritter::MF_VELOCITY);				/* Lee added 11-11-01 */
	setRadius( 1.0 );
   //setTangent( cVector2( 0.3, 0.7 ) );
   //setSpeed( 5.0 );
   //cCritter::setBounciness( 0 );   
}

BOOL cCritterWormsEnemyHead::collide( cCritter * pcritter )
{
	//BOOL collideflag = cCritter::collide(pcritter);
#ifdef NOEAT
   return FALSE;
#endif

   if (cCritter::distanceTo( pcritter ) < radius() + pcritter->radius() )     /* 11-24-01 Joe */
	//May sometimes do more stuff here if collideflag is TRUE.
   //if( collideflag )
   {
      if (pcritter->IsKindOf(RUNTIME_CLASS(cCritterPlayerWormsSeg)))
      {
         cCritterPlayerWormsSeg * seg = (cCritterPlayerWormsSeg *)pcritter;
         seg->getSnake()->eatenTail();
		 //getSnake()->growSnake();
         //pcritj->die();
	     //return FALSE;
      }
      if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsPlayerEgg)))
      {
         pcritter->die();
         getSnake()->growSnake();
      }
      if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsEnemyEgg)))
         return false;
      if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsFood)))
      {
         pcritter->die();
         getSnake()->growSnake();
         return false;
      }
      if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsSeg)))
         return false;
      /* 11-26 Joe */
      if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterMazeWall)))
      {
         setTangent( cVector2( (0 - tangent().x()), (0 - tangent().y()) ) );
         return true;
         /*
         BOOL collided = collide( pcritter );
         yaw( cRandomizer::pinstance()->randomReal( 0.0, 359.0 ) );
         return collided;
         */
      }
      /* 11-26 Joe */
   }
	//return collideflag;
   return cCritter::collide( pcritter );
}

void cCritterWormsEnemyHead::update( Real dt, CPopView * pview )
{
   bool useless;
   cCritter::update(dt, pview);		//Always call this first
   getSnake()->dropEgg( dt, useless );

   
//   setSpeed( cCritter::MAXSPEED );
   /* 11-24 Joe
   // Code to only allow movements in either 0, 90, 180, 270 degrees
   cVector direction = tangent();
   Real x = direction.x();
   Real y = direction.y();
   // The tangent vector points upwards
   if( y > 0 )
   {
      if( x < 0 )
      {
         if( (0.0 - x) <= y )
            setTangent( cVector2( 0.0, 1.0 ) );
         else
            setTangent( cVector2( -1.0, 0.0 ) );
      }
      else 
      {
         if( x <= y ) 
            // the critter moves up
            setTangent( cVector2( 0.0, 1.0 ) );
         else if( x > y )
            setTangent( cVector2( 1.0, 0.0 ) );
      }
   }
   // The tangent vector points downwards
   if( y < 0 )
   {
      if( x < 0 )
      {
         if( ( 0 - x ) <= ( 0 - y ) )
            setTangent( cVector2( 0.0, -1.0 ) );
         else
            setTangent( cVector2( 1.0, 0.0 ) );
      }
      else
      {
         if( x <= ( 0.0 - y ) )
            // the critter moves up
            setTangent( cVector2( 0.0, -1.0 ) );
         else if( x > y )
            setTangent( cVector2( -1.0, 0.0 ) );
      }   //
   }
   /* end 11-24 Joe */
}

void cCritterWormsEnemyHead::die()
{
   cCritter::die();
   getSnake()->getGame()->pplayer()->addScore( 100 );
}

//====================cCritterWormsSnake     /* 11-12 Joe SnakeClass */


inline cCritterWormsSnake::cCritterWormsSnake( cGameWorms * game, int size )
      : _pHead( NULL ), _pTail( NULL ), _pGame( game ), _size(size)
{ 
   _timeUntilEgg = cRandomizer::pinstance()->randomReal( randomMinTime, randomMaxTime );
}

void cCritterWormsSnake::eatenTail()
{
   cCritterWormsBase * newTail;

   // if the tail does not exist then the snake dies.
   if( !_pTail )
   {
      eatenHead();
      return;
   }

   newTail = _pTail->getPrevious();
   _pTail->die();
   
   _pTail = newTail;
   _size--;

   // If this causes the snake to have no body then the snake dies.
   if( _size < 1 )
      eatenHead();
}

void cCritterWormsSnake::eatenHead()
{
   deleteAll();
}

/* 11-12 Joe */
void cCritterWormsSnake::growSnake()
{
   if (_pHead->IsKindOf(RUNTIME_CLASS(cCritterWormsPlayer)))
   {
      cCritterPlayerWormsSeg * newSeg = new cCritterPlayerWormsSeg( _pTail );
      newSeg->setMoveBox( cRealBox2( cVector( 0-(worldWidth/2), 0-(worldHeight/2) )
                                    , cVector( worldWidth/2, worldHeight/2 ) ) );
      newSeg->moveTo( _pTail->position() );
      _pGame->add( newSeg );
      newSeg->setSnake( this );
      _pTail = newSeg;
      _size++;
   }
   if (_pHead->IsKindOf(RUNTIME_CLASS(cCritterWormsEnemyHead)))
   {
      cCritterWormsSeg * newSeg = new cCritterWormsSeg( _pTail );
      newSeg->setMoveBox( cRealBox2( cVector( 0-(worldWidth/2), 0-(worldHeight/2) )
                                    , cVector( worldWidth/2, worldHeight/2 ) ) );
      newSeg->moveTo( _pTail->position() );
      _pGame->add( newSeg );
      newSeg->setSnake( this );
      _pTail = newSeg;
      _size++;
   }
}

void cCritterWormsSnake::deleteAll()
{
   while( _size > 0 )
      eatenTail();
   if( _pHead )
      _pHead->die();
}

void cCritterWormsSnake::dropEgg( Real dt, bool & dropped )
{
   _timeUntilEgg -= dt;

#ifndef NOEGG
   if( _timeUntilEgg <= 0 && _size > 1 )
   {

      if( _size < 1 )
      {	 
         return;
		 // If the snake drops the last last body segement, the snake dies
         _pTail->die();
         _pHead->die();
      }
      else
      {
         if( _pHead->IsKindOf(RUNTIME_CLASS(cCritterWormsPlayer)) )
         {
            eatenTail();
            cCritterWormsPlayerEgg * newEgg = new cCritterWormsPlayerEgg;
            newEgg->setSprite( new cSpriteIcon( IDB_SUN ) );
            newEgg->setRadius( 1 );
            newEgg->setMoveBox( cRealBox2( _pTail->position(), _pTail->position() ) );
            newEgg->moveTo( _pTail->position() );
            newEgg->setFixedflag( true );   
            _pGame->add( newEgg );
            dropped = true;
         }
         else if( _pHead->IsKindOf(RUNTIME_CLASS(cCritterWormsEnemyHead)) )
         {
            eatenTail();
            cCritterWormsEnemyEgg * newEgg = new cCritterWormsEnemyEgg();
            newEgg->setSprite( new cSpriteIcon( IDB_EARTH ) );
            newEgg->setRadius( 1 );
            newEgg->setMoveBox( cRealBox2( _pTail->position(), _pTail->position() ) );
            newEgg->moveTo( _pTail->position() );
            newEgg->setFixedflag( true );
            _pGame->add( newEgg );
         }
      }
      // Drop egg must reset the random amount of time.
      _timeUntilEgg = cRandomizer::pinstance()->randomReal( randomMinTime, randomMaxTime );
   }
#endif
}

//============================cCritterWormsFood  /* 11-15 Joe */
cCritterWormsFood::cCritterWormsFood()
{
   setMoveBox( cRealBox2( cVector( 0-(worldWidth/2), 0-(worldHeight/2) )
                                    , cVector( worldWidth/2, worldHeight/2 ) ) );
   moveTo( cVector( cRandomizer::pinstance()->randomReal( 0-(worldWidth/2), worldWidth/2 )
                              , cRandomizer::pinstance()->randomReal( 0-(worldHeight/2), worldHeight/2 ) ) );
   setSprite( new cSpriteIcon( IDB_EXPLODE3 ) );
   setVelocity( cVector( 10,10 ) );
}

BOOL cCritterWormsFood::collide( cCritter * pcritter )
{
   if (cCritter::distanceTo( pcritter ) < radius() + pcritter->radius() )
   {
      if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsEnemyEgg)))
         pcritter->die();
      else if( pcritter->IsKindOf(RUNTIME_CLASS(cCritterWormsPlayerEgg)))
         pcritter->die();
      
   }
   return false;
}

void cCritterWormsBase::drawHighlight( cGraphics * pgraphics, Real highlightratio )
{
   cCritter::drawHighlight( pgraphics, highlightratio );
   Real focusradius = (highlightratio * radius()) / 1.8; //Rich
   cSpriteCircle focuscircle;
   focuscircle.setFilled(FALSE);
   focuscircle.setLineColor(cColorStyle::CN_LIGHTGRAY);
   focuscircle.setRadius(focusradius);                      // Rich
   focuscircle.setPrismDz(0.0);
}