/******************************************************************************/
/* gamestub.cpp: implementation of the GameStub class and associated classes. */
/*                                                                            */
/* Based on the built in gamestub.cpp file supplied with POP.  See the user's */
/* guide for more detailed information about the game.                        */
/*																			  */
/* Modified by: Brad Tennis, Dave Buchfuhrer, and Jon Dodge					  */
/******************************************************************************/

//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "gamestub.h"
#include "force.h"
#include "listener.h"
#include "pop.h" //for playSound
#include "spritepolygon.h" 
#include "critterviewer.h"
#include "graphicsopengl.h"
#include "graphicsmfc.h"
#include "critterwall.h"

IMPLEMENT_SERIAL( cGameStub, cGame, 0);
IMPLEMENT_SERIAL( cCritterWalker, cCritter, 0);
IMPLEMENT_SERIAL( cCritterSpiker, cCritter, 0);
IMPLEMENT_SERIAL( cCritterEvan, cCritterArmedRobot, 0);
IMPLEMENT_SERIAL( cCritterCannon, cCritterArmedRobot, 0);
IMPLEMENT_SERIAL( cCritterCannonBall, cCritterBullet, 0);
IMPLEMENT_SERIAL( cCritterMathBall, cCritterBullet, 0);
IMPLEMENT_SERIAL( cCritterPuke, cCritterBullet, 0);
IMPLEMENT_SERIAL( cCritterStubPlayer, cCritterArmedPlayer, 0);
IMPLEMENT_SERIAL( cCritterCoin, cCritter, 0);
IMPLEMENT_SERIAL( cCritterMathPower, cCritter, 0);
IMPLEMENT_SERIAL( cCritterPogoPower, cCritter, 0);
IMPLEMENT_SERIAL( cCritterStubTreasure, cCritter, 0);

// Global Variables
bool _lswitch = false;
	// Used to determine whether or not the game's level needs to be switched or not.
bool _reset = true;
	// Used to determine whether or not the game's level needs to be reset with the player back in the beginning.
bool _gameWon = false;
	// Used to determine if the game has been beaten and we need to revert the game to the beginning.
int currentLevel;
	// Used to store the level that Superfly is currently defeating to help the level loading procedure.
double _bottom;
	// Defines the bottom of the screen, used to determine whether or not the player has fallen into a pit.
int lastOneUp = 0;
	// Stores the last time the player had passed another 1-Up and needs to be given another level.

#define HANDSETVIEWERPOS
	/* HANDSETVIEWERPOS turns on the option of hand setting the viewer pos in 
the InitializeViewer call.  It's more standard to comment this out, but I had some
lingering problems with the default approach for a long thin world, so now I'm preferring
the HANDSETVIEWERPOS optoin. The other way WORKS, but I don't TRUST it anymore for this
paritcualr world as I have so often seen it arbitrarily put my view BEHIND the world 
(negative z), though this coudl have been due to some cCritterViewer trackplayer code
thats' been recently revised.  So you can probably comment out HANDSETVIEWERPOS.*/


//==============================cGameStub=======
Real cGameStub::TREASURERADIUS = 1.2;

// Constructor
cGameStub::cGameStub()
{
//Fix the menu selections you'll allow.
	_menuflags &= ~cGame::MENU_BOUNCEWRAP; 
		/* Default _menuflags from cGame has autoplay off, hopper off. 
		Turn off wrap because it's bad with gravity, we want balls bouncing on bottom.*/ 
		/* Turn off 3D option because it's bad with the long thin background bitmap
		called IDB_STREET.  Other than that, though, 3D is ok for this game. */
	_menuflags &= ~cGame::MENU_TRACKPLAYER;
		/* Don't allow user to turn off the track player option. */ 
	_menuflags &= ~cGame::MENU_RIDEPLAYER;
		/* Don't ride the player */
	_menuflags |= cGame::MENU_HOPPER; //Turn on hopper listener option.
	_menuflags &= ~cGame::MENU_CHANGELISTENER; 
		/* It looks bad to use any listener other than hopper. */
	_border.set(120.0, 15.0, 0.0); //Use a long, thin world.
		/*  Sets the cSpriteBackground for the desired background bitmap.
		Note that you must call this after the _border has its size fully initialized. So each
		cGame child with a different border should call this again in its constructor. */
	setWrapflag(cCritter::BOUNCE);
	setPlayer(new cCritterStubPlayer(this));
	_ptreasure = new cCritterStubTreasure(this);
	currentLevel = 1;
	_reset = true;
	_bottom = -_border.ysize()/2 + 1;
	//_arrayHCURSOR has only the _hCursorArrow inherited from cGame.
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPlay); //No need for the shoot cursor
}

// This function sets various data for viewpoint issues, such as the background image, camera position.
void cGameStub::initializeView(CPopView *pview)
{
	cGame::initializeView(pview);
	pview->setUseBackgroundBitmap(TRUE);
//	pview->setGraphicsClass(RUNTIME_CLASS(cGraphicsOpenGL)); //Comment this in for a 3D start
	pview->pviewpointcritter()->setTrackplayer(TRUE); //Always look at the player.
	pview->pviewpointcritter()->setFoveaProportion(0.45); /* A critterviewer has a _foveaproortion
		that ranges from 0.0 to 1.0.  We make it smaller than the default value of 0.85 so
		that the trackplayer image will scroll before player gets all the way to edge. 
		Reduce still more if you want to scroll even sooner.*/
}

// This function creates the viewpoint, be it 3D or 2D , then creates the viewpoint including zoom on the playing area.
void cGameStub::initializeViewpoint(cCritterViewer *pviewer)
{
	if (pviewer->is3D())
	{
#ifdef HANDSETVIEWERPOS
		pviewer->setViewpoint(cVector3(0.0, -8.0, 40.0), 
			cVector(border().lox(), border().midy()),
			FALSE);  
			/* The setViewpoint args are (directiontoviewer, lookatpoint, trytoseewholeworld),
			the default third argument is TRUE, which means to back off enough to
			see the whole world.  But in a long thin world, we don't want to do that.  */
#else //HANDSETVIEWERPOS
		pviewer->setViewpoint(cVector3(0.0, -0.2, 1.0), 
			cVector(border().lox(), border().midy()));  /* the default third TRUE argument
			will move the viewpoint far from the world and try to see it all. */
		pviewer->zoom(7.0); /* The bigger the world and the further away you are, 
			the more zoom you need. */
		/* I don't like this way as it makes the player trackign a little flaky to be in a
		high zoom. */
#endif //HANDSETVIEWERPOS
	}
	else //2D case
	{
		pviewer->setViewpoint(cVector::ZAXIS, //pplayer()->position());
			cVector(border().lox(), border().midy()));
		pviewer->zoom(4.0);
	}
}

// This function is defined merely to prevent the superclass seedCritters from being called.
void cGameStub::seedCritters()
{

}

// This function has the purpose of determining the state of the game and directing traffic accordingly.
// It handles the game over case where the player has died, earned a 1-Up, or ended a level.
void cGameStub::adjustGameParameters()
{
	// End the game if the player is dead and has no remaining lives.
	if (!health()  && !_gameover)
	{
		_gameover = TRUE;
		// Has the game actually been defeated, despite the lack of lives?
		// (Upon finishing the last level, you are killed)
		if( _gameWon )
			playSound("MYDEARPABST");
		// No, you have not defeated the game.
		else
			playSound("MAMAMIA");

		// Set variables such that another game can be played.
		_gameWon = false;
		currentLevel=1;
		_reset=true;
		lastOneUp = 0;
		pplayer()->setHealth(3);
		
		return; 
	}


	// Has the player's score gone up to the point that they deserve another life?
	if ((pplayer()->score() / 100) > lastOneUp) {
		++lastOneUp;
		pplayer()->addHealth(1);
	}

	// Has the player died or completed a level?
	if (_lswitch || _reset) {

		// Specifically, has the player completed a level?
		if(_lswitch) {
			++currentLevel;
			_lswitch = false;
			playSound("SUCKIT");
		}
		// Specifically, has the player died?
		if(_reset) {
			_reset = false;
		}
		// Either way we need to execute the level loading.
		loadLevel();
	}
 }


// This function handles the necessary calls that create a level.  We implemented level data in a hardcoded fashion,
// and this function contains all that data.  It also handles the bitmap changing processes and the destruction of the former
// level.  Also, this function determines if the game has been beaten.
void cGameStub::loadLevel()
{

		// Clear the level of all critters and walls.
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterWall));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterBullet));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterWalker));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterSpiker));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterCannon));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterCoin));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterMathPower));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterPogoPower));
		pbiota()->purgeCritters(RUNTIME_CLASS(cCritterEvan));

		// Move player to beginning of the level and stop him from moving.
		pplayer()->moveTo(pplayer()->moveBox().locorner() + 5*cVector::YAXIS);
		pplayer()->setVelocity(cVector(0,0,0));

		// First Level: Wild Wild West
		if( currentLevel == 1 )
		{
			setBackgroundBitmap(IDB_WWBG); 

			// Create the floor and stairs.
			double bottomY = _bottom-.5;
			new cCritterWall( cVector(10, bottomY),		cVector(-80, bottomY),	1,	this);
			new cCritterWall( cVector(80, bottomY),		cVector(24, bottomY),	1,	this);
			double stairX = 10;
			double stairY = bottomY+.75;
			double stairSize = 1;
			for( int i = 0; i < 3; ++i )
			{
				new cCritterWall( cVector(stairX, stairY),	cVector(stairX+2*stairSize, stairY), .5*stairSize,	this);
				stairX += 2*stairSize;
				stairY += .5*stairSize;
			}

			// Create the first large platform in the air, with surrounding creatures.
			new cCritterWall( cVector(0, 0),			cVector(-25, 0),		1,	this);
			new cCritterCannon( this, cVector(-16, 1.2) );
			new cCritterCoin(	this, cVector(-16, 3.5) );
			new cCritterWall( cVector(-6, 1.5),			cVector(-4.5,1.5),		2,	this);
			new cCritterCannon( this, cVector(-5.5,3.2) );
			new cCritterCoin(	this, cVector(-5.5,5.5) );
			new cCritterMathPower( this, cVector(-6.5, 1.5) );
			new cCritterSpiker( this, cVector(-8, 1) );
			new cCritterPogoPower( this, cVector(16, 4.5) );
			new cCritterCannon( this, cVector(-15, bottomY+1.2) );
			new cCritterCoin( this, cVector(-15, bottomY+3.5) );
			for( i = 0; i >= -15; i -= 3 )
			{
				new cCritterWalker( this, cVector(i, bottomY+1.5) );
			}
			new cCritterWall( cVector(-50, -1),			cVector(-52, -1),		2,	this);
			new cCritterWalker( this, cVector(-51, 1) );
			for( i = 0; i < 3; ++i )
			{
				new cCritterCoin( this, cVector(-45+i, 6.5-2*i) );
			}

			// Create a bunch of coins, some walkers, and a trap with a spiker after the pit.
			new cCritterWall( cVector(24, bottomY+1.5), cVector(26,bottomY+1.5),	2,	this);
			for( i = 0; i < 5; ++i )
			{
				new cCritterCoin(this, cVector(30+2*i, 0));
			}
			for( i = 0; i < 4; ++i )
			{
				new cCritterWalker(this, cVector(25+4*i, bottomY+1.5));
			}
			new cCritterWall( cVector(41, .5+bottomY/2), cVector(42, .5+bottomY/2),	-1*bottomY, this);
			new cCritterWall( cVector(42, 0),			cVector(52, 0),				1,	this);
			new cCritterCannon( this, cVector(51, bottomY+1.2) );
			new cCritterSpiker( this, cVector(43, bottomY+1) );
			for( i = 0; i < 8; ++i )
			{
				new cCritterCoin( this, cVector(42.5+i,-1.5) );
				new cCritterCoin( this, cVector(42.5+i,-3) );
			}
			
			// Create the exit portal.
			new cCritterStubTreasure(this);
		}

		// Second Level: Trick or Drink
		else if( currentLevel == 2 )
		{
			setBackgroundBitmap(IDB_TODBG); 

			// Create the first spiker trap.
			new cCritterWall( cVector(-45, _bottom-.5),		cVector(-60, _bottom-.5),		1,	this);
			new cCritterWalker( this, cVector(-45, _bottom+1) );
			new cCritterWall( cVector(-40, -3),				cVector(-42, -3),				2,	this);
			new cCritterWall( cVector(-45, -2-_bottom),		cVector(-60, -2-_bottom),		1,	this);
			new cCritterWall( cVector(-60, -.5-_bottom),	cVector(-59.5, -.5-_bottom),	3,	this);
			new cCritterSpiker( this, cVector(-46, -.5-_bottom) );
			for( int i = 0; i < 7; ++i )
				new cCritterCoin( this, cVector(-58.5+2*i, -_bottom-.5) );

			// Now, for some platform-jumping with cannons
			new cCritterWall( cVector(-32, -6.5),			cVector(-29, -6.5),				2,	this);
			new cCritterWall( cVector(-32, -5.25),			cVector(-31.75, -5.25),			.5,	this);
			new cCritterWall( cVector(-29.25, -5.25),		cVector(-29, -5.25),			.5,	this);
			new cCritterWalker( this, cVector(-31, -5) );

			// If you super-jump off of that last walker, you can reach a higher level.  Otherwise, you have to deal
			// with a spiker.
			new cCritterWall( cVector(-20, -2-_bottom),		cVector(-30, -2-_bottom),		1,	this);
			new cCritterPogoPower( this, cVector(-27, -_bottom) );
			new cCritterWall( cVector(-20, -6.5),			cVector(-26, -6.5),				1,	this);
			new cCritterWall( cVector(-20, -5.5),			cVector(-20.5, -5.5),			1,	this);
			new cCritterWall( cVector(-25.5, -5.5),			cVector(-26, -5.5),				1,	this);
			new cCritterSpiker( this, cVector(-23, -4) );
			new cCritterCoin( this, cVector(-25, 0) );

			// After you get past the spiker, you get a nice platform with some Pabst.  However, there is a cannon
			// firing at the Pabst.
			new cCritterWall( cVector( -15, _bottom+1),		cVector(0, _bottom+1),			1,	this);
			for( i = 0; i < 5; ++i )
			{
				new cCritterCoin( this, cVector(-14 + 3*i, _bottom+4) );
			}
			new cCritterWall( cVector(2, _bottom+3),		cVector(4, _bottom+3),			2,	this);
			new cCritterCannon( this, cVector(3, _bottom+4.7) );

			// After the cannon, there are some spikers and walkers on a low platform.
			new cCritterWall( cVector(7, _bottom-.5),			cVector(25, _bottom-.5),			1,	this);
			new cCritterWall( cVector(7, _bottom-.5),			cVector(8, _bottom-.5),				3,	this);
			new cCritterWall( cVector(24, _bottom-.5),			cVector(25, _bottom-.5),			3,	this);
			for( i = 0; i < 5; ++i )
			{
				if( i % 2 == 0 )
					new cCritterWalker( this, cVector(10+3*i, _bottom+2) );
				else
					new cCritterSpiker( this, cVector(10+3*i, _bottom+2) );
			}

			// If you make it here with the pogo stick, your reward is a lot of Pabst, the math power, and
			// the chance to use the math power on some spikers.
			new cCritterMathPower( this, cVector(25, -1-_bottom) );
			new cCritterWall( cVector(30, _bottom-.5),			cVector(35, _bottom-.5),			1,	this);
			new cCritterWall( cVector(35, _bottom-1),			cVector(55, _bottom-1),				1,	this);
			new cCritterWall( cVector(55, _bottom-.5),			cVector(60, _bottom-.5),			1,	this);
			for( i = 36; i < 55; i += 2 )
			{
				new cCritterSpiker( this, cVector(i, _bottom+1) );
				new cCritterCoin( this, cVector(i, _bottom+1) );
			}

			// If you don't get the math power, you still need to be able to beat the level.  You will have to deal with
			// cannons and some walkers, though.
			new cCritterWall( cVector(35, -3.3),					cVector(55, -3.3),				1,	this);
			new cCritterCannon( this, cVector(54, -2.1) );
			new cCritterWall( cVector(53, 5),						cVector(55, 5),					.2,	this);
			new cCritterCannon( this, cVector(54, 5.85) );
			new cCritterWall( cVector(35, -3),						cVector(37, -3),				1,	this);
			for( i = 0; i < 3; ++i )
			{
				new cCritterWalker( this, cVector(37+4*i, -2) );
			}

			// Create the exit portal.
			new cCritterStubTreasure(this);
		}

		// Third Level: TQ Night
		else if(currentLevel == 3)
		{
			setBackgroundBitmap(IDB_TQBG);

			// Start the level with a Spiker.
			new cCritterWall( cVector(-60, _bottom-.5),				cVector(-50, _bottom-.5),		1,	this);
			new cCritterSpiker( this, cVector(-51, _bottom+1) );

			// You can't make it to the first cannon without jumping off of the bullet from it or the cannon below it.
			new cCritterWall( cVector(-40, -3),						cVector(-38, -3),				2,	this);
			new cCritterCannon( this, cVector(-39, -1) );
			new cCritterCannon( this, cVector(-18, _bottom+1.5) );
			new cCritterWall( cVector(-19, _bottom-.5),				cVector(-17, _bottom-.5),		2,	this);

			// Now, for a bonus platform you can only get to using a super bullet jump.
			new cCritterWall( cVector(-30, -_bottom-1.8),			cVector(-5, -_bottom-1.8),		1,	this);
			new cCritterWall( cVector(-29, _bottom-.5),				cVector(-27, _bottom-.5),		2,	this);
			for( int i = 0; i < 15; ++i )
				new cCritterCoin( this, cVector(-29.35+1.33*i, -_bottom-.5) );
			new cCritterPogoPower( this, cVector(-29.35+1.33*i, -_bottom-.5) );

			// The end of this bonus platform is guarded by a Cannon.
			new cCritterWall( cVector(2, 1),						cVector(4, 1),					2,	this);
			new cCritterCannon( this, cVector(3, 3) );

			// Let's meet some cute lil' walkers!
			new cCritterWall( cVector(-12, _bottom-.5),				cVector(5, _bottom-.5),			1,	this);
			new cCritterWall( cVector(-12, _bottom-.5),				cVector(-11.5, _bottom-.5),		3,	this);
			new cCritterWall( cVector(4.5, _bottom-.5),				cVector(5, _bottom-.5),			3,	this);
			for( i = 0; i < 6; ++i )
				new cCritterWalker( this, cVector(-11+3*i, _bottom+.5) );

			// Hop over the recycling bins, but dodge the lemons!
			for( i = 0; i < 6; ++i ) {
				if( i%2 == 0 )
					new cCritterWall( cVector(10+5*i, _bottom),		cVector(12+5*i, _bottom),		2,	this);
				else {
					new cCritterWall( cVector(10+5*i, -1),			cVector(12+5*i, -1),			.1,	this);
					new cCritterWall( cVector(10+5*i, -.9),			cVector(10.1+5*i, -.9),			.2, this);
					new cCritterWall( cVector(11.9+5*i, -.9),		cVector(12+5*i, -.9),			.2, this);
					new cCritterSpiker( this, cVector(11+5*i, 0) );
					if( i != 5 )
						new cCritterCoin( this, cVector(11+5*i, 1.5) );
					else
						new cCritterMathPower( this, cVector(11+5*i, 1.5) );
				}
			}
			new cCritterCannon( this, cVector(45, 1.5) );

			// Now, prove your great skill by preventing this horde of Mexican goombas from taking in their recycling!
			new cCritterWall( cVector(40, _bottom-.5),				cVector(60, _bottom-.5),		1,	this);
			new cCritterWall( cVector(40, _bottom),					cVector(40.3, _bottom),			2,	this);
			new cCritterWall( cVector(59.7, _bottom),				cVector(60, _bottom),			2,	this);
			for( i = 0; i < 4; ++i ) {
				new cCritterWalker( this, cVector(41+6*i, _bottom+.5) );
				new cCritterSpiker( this, cVector(42.5+6.5*i, _bottom+.5) );
			}

			// Create the exit portal
			new cCritterStubTreasure(this);

		}

		// Fourth Level: Evan's Lair
		else if(currentLevel == 4)
		{
			// We don't want to allow Superfly to escape Evan's Lair before he defeats Evan.
			pbiota()->purgeCritters(RUNTIME_CLASS(cCritterStubTreasure));

			setBackgroundBitmap(IDB_LAIR);
			
			// This level is a different size so we spawn Fly in a different place
			pplayer()->moveTo(cVector(-14,5));

			// This level is hard, so we give the player a pogo power
			new cCritterPogoPower(this, cVector(0,0));

			// Create the extra thick walls of Evan's Lair
			new cCritterWall( cVector(-15, _bottom-.5),				cVector(20, _bottom-.5),		1,	this);
			new cCritterWall( cVector(-60, 0),						cVector(-15, 0),		15, this);
			new cCritterWall( cVector(20, 0),						cVector(60, 0),		15, this);
			new cCritterEvan(this, cVector(15,0));

			// We create no exit portal because one will be made upon Evan's defeat.
		}

		// Handle the case where the player has beat the game.
		else
		{
			_gameWon = true;

			pplayer()->die();
		}
 }

// This function creates the information at the bottom of the screen
CString cGameStub::statusMessage()
{
	CString cStrStatusBar;
	int nUpdatesPerSecond;
	CString cStrUpdatespersecond;
	CString cStrScore;
	CString cStrHealth;

	// If the game is unpaused we need to yield some information.
	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.runningNearMinSpeed())
			cStrUpdatespersecond += " (Slower than Real Time)";
	}
	else
			cStrUpdatespersecond.Format("Animation is paused.");
	cStrScore.Format("Score: %d.", score());
	cStrHealth.Format("Lives: %d.", health());
	int crittercount = _pbiota->count(RUNTIME_CLASS(cCritter));
	int bulletcount = _pbiota->count(RUNTIME_CLASS(cCritterBullet));

	cStrStatusBar = cStrScore + cStrHealth + "  " + cStrUpdatespersecond; 
	return cStrStatusBar;
}

void cGameStub::Serialize(CArchive& ar) /* This is a bit of work, as I have to match
	_ppuck , _phockeyrobot , _pmygoal , _probotgoal to the pointers in the cBiota array. */
{
	int treasureindex;
	cGame::Serialize(ar);
	if (ar.IsStoring()) // Save 
 	{
		ar << _index(_ptreasure);
	}
	else //Load
	{
		ar >> treasureindex;
			// I have to cast from cCritter*
		_ptreasure = (cCritterStubTreasure*)(_pbiota->GetAt(treasureindex));
	}
}


//====================Critters for the Stub Game ==========================
//==================================================================


//==================cCritterStubPlayer=============

// Constructor
cCritterStubPlayer::cCritterStubPlayer(cGame *pownergame):
cCritterArmedPlayer(pownergame)
{
	setHealth(3);
	setWrapflag(cCritter::CLAMP); //Use CLAMP so you stop dead at edges.
	setArmed(FALSE);  //Let's not use bullets.
	setMaxspeed(80.0); //Need good speed to overcome gravity.
	setListener(new cListenerHopper(9.0, 0.2, 0.2)); /* The three arguments to the
		cListenerHopper constructor specify the walkspeed, the hopuptime, and the hopagainwait.
		Walkspeed is how fast the left/right (and PgUp/PgDn if in 3D where the ground is xz plane)
			arrow keys move the critter,
		Hopuptime and hopagaintwait are not used.*/
	setListenerAcceleration(1600.0); //So Hopper can overcome gravity
	addForce(new cForceGravity(50.0)); /* Gravity	will affect player using cListenerHopper. */

	setAttitudeToMotionLock(FALSE);  //It looks nicer is you don't turn the player with motion.
	setAttitudeTangent(cVector::XAXIS + cVector::YAXIS); /* Without motion lock, we need setAttitudeTangent 
		to make sure he's pointing up at the start. */
	setSprite(new cSpriteIcon(IDB_BIGFLYWALK));
	setPowered(false);
	setHopping(true);
	setBulletClass(RUNTIME_CLASS(cCritterMathBall));
	setRadius(1.0);
	setBounciness(0);
	setWaitShoot(.75);
	setGunlength(0);
	moveTo(_movebox.locorner()+5*cVector::YAXIS);
}

// This function handles collision detection and executes the necessary calls once the object has been identified.
BOOL cCritterStubPlayer::collide(cCritter *pcritter)
{
	BOOL playerhigherthancritter = position().y() - radius() > pcritter->position().y();
/* If you are "higher" than the pcritter, as in jumping on it, you get a poitn
and the critter dies.  If you are lower than it, you lose health and the
critter also dies. To be higher, let's say your low point has to higher
than the critter's center. We compute playerhigherthancritter before the collide,
as collide can change the positions. */

	// Depending on the critter type, we may need these data.
	cVector oldVel = velocity();
	cVector critterOldVel = pcritter->velocity();
	
	BOOL collided = cCritter::collide(pcritter);
	if (!collided)
		return FALSE;

	// If you're here, you collided.  	

	// Is the object Evan?
	if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterEvan)))	{
		
		// Have you landed on Evan, damaging him?
		if(position().y() - radius() > pcritter->position().y() + pcritter->radius()/1.5) {
			pcritter->damage(10);
			pcritter->setVelocity(cVector(critterOldVel.x(), 0, 0));
			setVelocity(cVector(0,0,0));
			setAcceleration(cVector(0,1000,0));

			return TRUE;
		} 
		
		// You have not damaged Evan.
		else {

			// Were you close to damaging Evan, if so bounce clear.
			if(playerhigherthancritter) {
				setAcceleration(cVector(0,1000,0));
			}

			// Were you able to take the hit without dying?
			if(isPowered()) {
				playSound("MYPOWERS");
				setArmed(false);
				setListener(new cListenerHopper(9.0, 0.2, 0.2));
				setListenerAcceleration(1600);
				setSprite(new cSpriteIcon(IDB_BIGFLYWALK));
				setRadius(1);
				setPowered(false);
				return TRUE;
			}

			// You are dead.
			else {
				_reset=true;
				damage(1);
				return TRUE;
			}		
		}		
	}

	// Is the object a math power-up?
	if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterMathPower))) {
		playSound("MATHEMAGIC");
		setArmed(true);
		pcritter->die();
		setVelocity(oldVel);
		setListener(new cListenerHopper(9.0, 0.2, 0.2));
		setListenerAcceleration(1600);
		setSprite(new cSpriteIcon(IDB_MAGICFLY));
		setRadius(1.0);
		setPowered(true);
		setVelocity(oldVel);
		return TRUE;
	}

	// Is the object a pogo power-up?
	else if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterPogoPower))) {
		playSound("BOUNCETIME");
		setArmed(false);
		pcritter->die();
		setVelocity(oldVel);
		setListener(new cListenerHopper(12.0, 0.2, 0.2));
		setListenerAcceleration(2500);
		setSprite(new cSpriteIcon(IDB_POGOFLY));
		setRadius(1.0);
		setPowered(true);
		setVelocity(oldVel);
		return TRUE;
	}

	// Is the object a Spiker?
	if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterSpiker))) {
		
		// Were you able to take the hit without dying?
		if(isPowered()) {
			playSound("YIPES");
			setAcceleration(cVector(0,800,0));
			setArmed(false);
			setListener(new cListenerHopper(9.0, 0.2, 0.2));
			setListenerAcceleration(1600);
			setSprite(new cSpriteIcon(IDB_BIGFLYWALK));
			setRadius(1);
			setPowered(false);
			setVelocity(cVector(oldVel.x(), velocity().y(), 0));
			return true;
		}

		// You are dead.
		else {
			playSound("OHNO");
			_reset=true;
			damage(1);
			return true;
		}
	}

	// Has the object been jumped upon (puke cannot be jumped upon)?
	if (playerhigherthancritter && !pcritter->IsKindOf(RUNTIME_CLASS(cCritterPuke))) {

		// Is the object a cannonball?
		if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterCannonBall))) {
			setAcceleration(cVector(0,1000,0));
			addScore(2);
		}

		// Is the object a walker?
		else if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterWalker))) {
			setAcceleration(cVector(0,1000,0));
			addScore(5);
		}
	}

	// You have walked into an object that damages you, a walker or a bullet of either kind.
	else {

		// Were you able to take the hit without dying?
		if(isPowered()) {
			playSound("MYPOWERS");
			setArmed(false);
			setListener(new cListenerHopper(9.0, 0.2, 0.2));
			setListenerAcceleration(1600);
			setSprite(new cSpriteIcon(IDB_BIGFLYWALK));
			setRadius(1);
			setPowered(false);
		}

		// You are dead.
		else {
			playSound("OHNO");
			_reset=true;
			damage(1);
		}
	}
	pcritter->die();
	setVelocity(cVector(oldVel.x(), velocity().y(), 0));
	return TRUE;
}

// This function handles the updates to the status of the player in the aspects of orientation (for use with the 
// math power up) or determining whether or not the player is in a pit.
void cCritterStubPlayer::update(CPopView *pactiveview, Real dt)
{
	cCritterArmed::update(pactiveview, dt);		// Always call this first.

	// Is Superfly facing left?
	if(isFacing()) 
		setAttitudeTangent(-1*cVector::XAXIS + cVector::YAXIS); 
	else
		setAttitudeTangent(1*cVector::XAXIS + cVector::YAXIS); 

	// Is the player in a pit?
	if (position().y() <= _bottom)
	{
		playSound("WAA");
		damage(1);
		setArmed(false);
		setListener(new cListenerHopper(9.0, 0.2, 0.2));
		setListenerAcceleration(1600);
		setSprite(new cSpriteIcon(IDB_BIGFLYWALK));
		setRadius(1.0);
		setPowered(false);
		_reset = true;
	}

}


//==================cCritterWalker=============

// Constructor
cCritterWalker::cCritterWalker(cGame *pownergame):
cCritter(pownergame)
{
	if (pgame()) {
		
		// Which level are we in, then set the sprite accordingly.
		switch(currentLevel) {
		case 1:
			setSprite(new cSpriteIcon(IDB_WALKER1));
			break;
		case 2:
			setSprite(new cSpriteIcon(IDB_WALKER2));
			break;
		case 3:
			setSprite(new cSpriteIcon(IDB_WALKER3));
			break;
		default:
			setSprite(new cSpriteIcon(IDB_WALKER1));
		}
		
	}
	addForce(new cForceGravity());
	setMaxspeed(1000000);
	setSpeed(0);
	setVelocity(cVector(-5,0,0));
	setDensity(2.0); 
	setRadius(.8);
	setBounciness(0);
	setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast
	Real playerx = 0.0;

	// If we don't specify a location, randomly place the critter in front of the player.
	if (pownergame)
		playerx = pownergame->pplayer()->position().x();
	randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()),
		_movebox.hicorner())); 
}

// Alternate constructor, specifies a position too
cCritterWalker::cCritterWalker(cGame *pownergame, cVector location)
: cCritter(pownergame)
{
	if (pgame()) {

		// Which level are we in, then set the sprite accordingly.
		switch(currentLevel) {
		case 1:
			setSprite(new cSpriteIcon(IDB_WALKER1));
			break;
		case 2:
			setSprite(new cSpriteIcon(IDB_WALKER2));
			break;
		case 3:
			setSprite(new cSpriteIcon(IDB_WALKER3));
			break;
		default:
			setSprite(new cSpriteIcon(IDB_WALKER1));
		}
		
	}
	addForce(new cForceGravity());
	setMaxspeed(1000000);
	setSpeed(0);
	setVelocity(cVector(-5,0,0));
	setDensity(2.0); 
	setRadius(.8);
	setBounciness(0);
	setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast
	moveTo(location);
}

// This function removes critters from the playing area visually and internally.
void cCritterWalker::die()
{
	cCritter::die();
}

// This function checks to see if the critter has walked out of the playing area.
void cCritterWalker::update(CPopView *pactiveview, Real dt)
{
	cCritter::update(pactiveview, dt); //Always call this first

	if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */
		delete_me(); //tell the game to remove yourself

	// Has the critter falled into a pit?
	if (position().y() <= _bottom)
		delete_me();
}

//==================cCritterSpiker=============

// Constructor
cCritterSpiker::cCritterSpiker(cGame *pownergame):
cCritter(pownergame)
{
	if (pgame()) {

		// Which level are we in, then set the sprite accordingly.
		switch(currentLevel) {
		case 1:
			setSprite(new cSpriteIcon(IDB_SPIKER1));
			break;
		case 2:
			setSprite(new cSpriteIcon(IDB_SPIKER2));
			break;
		case 3:
			setSprite(new cSpriteIcon(IDB_SPIKER3));
			break;
		default:
			setSprite(new cSpriteIcon(IDB_SPIKER1));
		}
		
	}
	addForce(new cForceGravity());
	setMaxspeed(1000000);
	setSpeed(0);
	setVelocity(cVector(-5,0,0));
	setDensity(2.0); 
	setRadius(.8);
	setBounciness(0);

	setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast

	Real playerx = 0.0;

	// If we don't specify a location, randomly place the critter in front of the player.
	if (pownergame)
		playerx = pownergame->pplayer()->position().x();
	randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()),
		_movebox.hicorner())); 
}

// Alternate constructor, specifies a position too
cCritterSpiker::cCritterSpiker(cGame *pownergame, cVector location)
: cCritter(pownergame)
{
	if (pgame()) {

		// Which level are we in, then set the sprite accordingly.
		switch(currentLevel) {
		case 1:
			setSprite(new cSpriteIcon(IDB_SPIKER1));
			break;
		case 2:
			setSprite(new cSpriteIcon(IDB_SPIKER2));
			break;
		case 3:
			setSprite(new cSpriteIcon(IDB_SPIKER3));
			break;
		default:
			setSprite(new cSpriteIcon(IDB_SPIKER1));
		}
		
	}
	addForce(new cForceGravity());
	setMaxspeed(1000000);
	setSpeed(0);
	setVelocity(cVector(-5,0,0));
	setDensity(2.0); 
	setRadius(.8);
	setBounciness(0);

	setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast
	moveTo(location);
}

// This function removes critters from the playing area visually and internally.
void cCritterSpiker::die()
{
	cCritter::die();
}

// This function checks to see if the critter has walked out of the playing area.
void cCritterSpiker::update(CPopView *pactiveview, Real dt)
{
	cCritter::update(pactiveview, dt); //Always call this first

	if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */
		delete_me(); //tell the game to remove yourself

	// Has the critter falled into a pit?
	if (position().y() <= _bottom)
		delete_me();
}

//==================cCritterEvan=============

// Constructor
cCritterEvan::cCritterEvan(cGame *pownergame):
cCritterArmedRobot(pownergame)
{
	setSprite(new cSpriteIcon(IDB_EVAN2));
	addForce(new cForceGravity());
	addForce(new cForceEvadeBullet());
	addForce(new cForceObjectSeek(pgame()->pplayer(), 10));
	addForce(new cForceDrag());
	addForce(new cForceClassEvade(3.5,1,RUNTIME_CLASS(cCritterMathBall)));
 
	setMaxspeed(1000000);
	setSpeed(0);
	setVelocity(cVector(-5,0,0));
	setDensity(2.0); 
	setRadius(2.5);
	setBounciness(0);
	setGunlength(0);
	setWaitShoot(2);
	setBulletClass(RUNTIME_CLASS(cCritterPuke));
	setTarget(pgame()->pplayer());
	setHealth(30);
	setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast
	Real playerx = 0.0;

	// If we don't specify a location, randomly place the critter in front of the player.
	if (pownergame)
		playerx = pownergame->pplayer()->position().x();
	randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()),
		_movebox.hicorner())); 
}

// Alternate constructor, specifies a position too
cCritterEvan::cCritterEvan(cGame *pownergame, cVector location)
: cCritterArmedRobot(pownergame)
{
	setSprite(new cSpriteIcon(IDB_EVAN2));
	addForce(new cForceGravity());
	addForce(new cForceEvadeBullet());
	addForce(new cForceObjectSeek(pgame()->pplayer(), 10));
	addForce(new cForceDrag());
	addForce(new cForceClassEvade(3.5,1,RUNTIME_CLASS(cCritterMathBall)));

	setMaxspeed(1000000);
	setSpeed(0);
	setVelocity(cVector(-5,0,0));
	setDensity(2.0); 
	setRadius(2.5);
	setBounciness(0);
	setGunlength(0);
	setWaitShoot(2);
	setBulletClass(RUNTIME_CLASS(cCritterPuke));
	setTarget(pgame()->pplayer());
	setHealth(30);
	setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast
	moveTo(location);
}

// This function removes critters from the playing area visually and internally.  In Evan's case this
// creates an exit portal.
void cCritterEvan::die()
{
	pplayer()->addScore(150);
	new cCritterStubTreasure(pgame(), cVector(0,0));
	cCritter::die();
}

// This function tests to see if Evan has walked out of the playing area and also gives Evan
// a random chance to jump high into the air.
void cCritterEvan::update(CPopView *pactiveview, Real dt)
{
	cCritterArmedRobot::update(pactiveview, dt); //Always call this first

	if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */
		delete_me(); //tell the game to remove yourself
	
	// Has the critter fallen into a pit?
	if (position().y() <= _bottom)
		delete_me();

	// Has Evan decided to jump?
	if(rand()%175 == 1)
		addAcceleration(cVector(0,1000,0));
}

// This function handles the creation of a bullet and places the necessary forces on it.
cCritterBullet* cCritterEvan::shoot()
{
	// Call the superclass's shoot method to create a bullet.
	cCritterBullet *pbullet = cCritterArmedRobot::shoot();

	// Give the bullet some homing ability.
	pbullet->addForce(new cForceObjectSeek(pgame()->pplayer(), 8.0));
	return pbullet;
}


//====================cCritterCannon====================

// Constructor
cCritterCannon::cCritterCannon(cGame *pownergame)
: cCritterArmedRobot(pownergame)
{
	if (pgame()) {

		// Which level are we in, then set the sprite accordingly.
		switch(currentLevel) {
		case 1:
			setSprite(new cSpriteIcon(IDB_CANNON1));
			break;
		case 2:
			setSprite(new cSpriteIcon(IDB_CANNON2));
			break;
		case 3:
			setSprite(new cSpriteIcon(IDB_CANNON3));
			break;
		default:
			setSprite(new cSpriteIcon(IDB_CANNON1));
		}		
	}
	setMaxspeed(0.0);
	setDensity(2.0);
	setRadius(1);
	setGunlength(0);
	_collidepriority = cCollider::CP_PLAYER + 1;
	setFixedflag(true);
	setWaitShoot(2);
	setBulletClass(RUNTIME_CLASS(cCritterCannonBall));
	_aimvector = -1*cVector::XAXIS;
	Real playerx = 0.0;

	// If we don't specify a location, randomly place the critter in front of the player.
	if (pownergame)
		playerx = pownergame->pplayer()->position().x();
	randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()),
		_movebox.hicorner())); 
}

// Alternate constructor, specifies a position as well.
cCritterCannon::cCritterCannon(cGame *pownergame, cVector location)
: cCritterArmedRobot(pownergame)
{
	if (pgame()) {
		
		// Which level are we in, then set the sprite accordingly.
		switch(currentLevel) {
		case 1:
			setSprite(new cSpriteIcon(IDB_CANNON1));
			break;
		case 2:
			setSprite(new cSpriteIcon(IDB_CANNON2));
			break;
		case 3:
			setSprite(new cSpriteIcon(IDB_CANNON3));
			break;
		default:
			setSprite(new cSpriteIcon(IDB_CANNON1));
		}		
	}
	setDensity(2.0);
	setRadius(1);
	setGunlength(0);
	_collidepriority = cCollider::CP_PLAYER + 1;
	setFixedflag(true);
	setWaitShoot(2);
	setBulletClass(RUNTIME_CLASS(cCritterCannonBall));
	_aimvector = -1*cVector::XAXIS;
	
	moveTo(location);
}

// This function removes critters from the playing area visually and internally.
void cCritterCannon::die()
{
	cCritter::die();
}

// This function checks to see if critter has wandered from the playing area, despite the fact that cannons cannot move.
void cCritterCannon::update(CPopView *pactiveview, Real dt)
{
	cCritterArmedRobot::update(pactiveview, dt); //Always call this first

	if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */
		delete_me(); //tell the game to remove yourself

	// Has the critter falled into a pit?
	if (position().y() <= _bottom)
		delete_me();
}

// This function is used to handle collisions with the mobile critters, other than bullets.
int cCritterCannon::collidesWith(cCritter *pothercritter)
{
	// Is there a collision with the mobile objects, other than bullets?
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterStubPlayer)) || pothercritter->IsKindOf(RUNTIME_CLASS(cCritterWalker)) ||
		pothercritter->IsKindOf(RUNTIME_CLASS(cCritterSpiker)))
		return cCollider::COLLIDEASCALLER;
	else
		return cCollider::DONTCOLLIDE;
}

// This function is used to handle collisions with the player, setting the variable used to determine if Superfly
// is in the middle of a jump.
BOOL cCritterCannon::collide(cCritter *pcritter)
{
	BOOL critterhigher = position().y() + radius() < pcritter->position().y();

	BOOL hasCollided = cCritter::collide(pcritter);
	if(hasCollided && critterhigher) {
		pcritter->setHopping(false);
	}
	return hasCollided;
}

//==================cCritterCannonBall==================

// Constructor
cCritterCannonBall::cCritterCannonBall()
{
	_dieatedges = true;

	// Which level are we in, then set the sprite accordingly.
	switch(currentLevel) {
	case 1:
		setSprite(new cSpriteIcon(IDB_CANNONBALL1));
		break;
	case 2:
		setSprite(new cSpriteIcon(IDB_CANNONBALL2));
		break;
	case 3:
		setSprite(new cSpriteIcon(IDB_CANNONBALL3));
		break;
	default:
		setSprite(new cSpriteIcon(IDB_CANNONBALL1));
	}		

	setRadius(.5);
}

// This function checks to see if the bullet has flown out of the playing area.
void cCritterCannonBall::update(CPopView *pactiveview, Real dt)
{
	cCritterBullet::update(pactiveview, dt); //Always call this first

	// Has the bullet flown out of the playing area?
	if(_outcode & BOX_LOY || _outcode & BOX_HIY)
		die();
}

// This function is used to determine if the bullet has collided with the player or his math bullets.
int cCritterCannonBall::collidesWith(cCritter *pothercritter)
{
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterStubPlayer)))
		return cCollider::COLLIDEASARG;
	else if(pothercritter->IsKindOf(RUNTIME_CLASS(cCritterMathBall)))
		return cCollider::COLLIDEASARG;
	else
		return cCollider::DONTCOLLIDE;
}

//==================cCritterMathBall==================

// Constructor
cCritterMathBall::cCritterMathBall()
{
	_dieatedges = false;
	setSprite(new cSpriteIcon(IDB_MATHBALL));
	setBounciness(1);
	addForce(new cForceGravity());
	setRadius(.5);
	setSpeed(cCritterBullet::BULLETSPEED+2);
}

// This function checks to see if the bullet has flown out of the playing area.
void cCritterMathBall::update(CPopView *pactiveview, Real dt)
{
	cCritterBullet::update(pactiveview, dt); //Always call this first

	// Has the bullet flown out of the playing area?
	if(_outcode & BOX_LOY || _outcode & BOX_HIY)
		die();
}

// This function is used to determine if the bullet has collided with anything it can damage or destroy.
int cCritterMathBall::collidesWith(cCritter *pothercritter)
{
	// Has the math ball damaged or destroyed an object?
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterWalker)) || pothercritter->IsKindOf(RUNTIME_CLASS(cCritterCannonBall)) ||
		pothercritter->IsKindOf(RUNTIME_CLASS(cCritterSpiker)) || pothercritter->IsKindOf(RUNTIME_CLASS(cCritterEvan)) ||
		pothercritter->IsKindOf(RUNTIME_CLASS(cCritterPuke))) 
		return cCollider::COLLIDEASCALLER;

	// Has the math ball run into a wall?  If so bounce off.
	else if(pothercritter->IsKindOf(RUNTIME_CLASS(cCritterWall)))
		return cCollider::COLLIDEASARG;
	else
		return cCollider::DONTCOLLIDE;
}

// This function is used to distribute damage and points.
BOOL cCritterMathBall::collide(cCritter *pcritter)
{
	cVector oldVel = pcritter->velocity();
	
	BOOL collided = cCritter::collide(pcritter);

	// Was there a collision?
	if(collided) {
		
		// Is the collision object Evan?
		if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterEvan))) {
			pcritter->damage(1);
			pcritter->setVelocity(oldVel);
		} 
		
		// If not and it is damageable it is destroyed.
		else {
			if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterWalker)))
				pplayer()->addScore(5);
			else if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterCannonBall)))
				pplayer()->addScore(2);
			else if(pcritter->IsKindOf(RUNTIME_CLASS(cCritterSpiker)))
				pplayer()->addScore(20);
			pcritter->die();
		}
		die();
	}
	return collided;
}

//==================cCritterPuke==================

// Constructor
cCritterPuke::cCritterPuke()
{
	_dieatedges = true;

	setSprite(new cSpriteIcon(IDB_PUKE));
	setMaxspeed(100000);

	setRadius(.5);
}

// This function checks to see if the bullet has flown out of the playing area.
void cCritterPuke::update(CPopView *pactiveview, Real dt)
{
	cCritterBullet::update(pactiveview, dt); //Always call this first

	// Has the bullet flown out of the playing area?
	if(_outcode & BOX_LOY || _outcode & BOX_HIY)
		die();
}

// This function determines whether or not the bullet has collided with the player or his bullets.
int cCritterPuke::collidesWith(cCritter *pothercritter)
{
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterStubPlayer)) || pothercritter->IsKindOf(RUNTIME_CLASS(cCritterMathBall)))
		return cCollider::COLLIDEASARG;
	else
		return cCollider::DONTCOLLIDE;
}


//==================cCritterCoin=====================

// Constructor
cCritterCoin::cCritterCoin(cGame *pownergame)
:cCritter(pownergame)
{
	setMaxspeed(0);
	Real playerx = 0.0;
	setDensity(.000001);

	// If we don't specify a location, randomly place the critter in front of the player.
	if (pownergame)
		playerx = pownergame->pplayer()->position().x();
	randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()),
		_movebox.hicorner())); 
	
	setSprite(new cSpriteIcon(IDB_PABST));
	setRadius(.8);
	_collidepriority = cCollider::CP_PLAYER + 1;
	setFixedflag(TRUE);
}

// Alternate constructor, specifies a location as well.
cCritterCoin::cCritterCoin(cGame *pownergame, cVector location)
:cCritter(pownergame)
{
	setMaxspeed(0);
	Real playerx = 0.0;
	setDensity(.000001);
	moveTo(location);
	setSprite(new cSpriteIcon(IDB_PABST));
	setRadius(.8);
	_collidepriority = cCollider::CP_PLAYER + 1;
	setFixedflag(TRUE);
}

// This function is used to check whether or not the Coin has moved out of the playing area, despite their immobility.
void cCritterCoin::update(CPopView *pactiveview, Real dt)
{
	cCritter::update(pactiveview, dt); //Always call this first

	if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */
		delete_me(); //tell the game to remove yourself
	
	// Has the coin wandered off the bottom?
	if (position().y() <= _bottom)
		delete_me();
}

// This function determines whether or not the coin has collided with the player.
int cCritterCoin::collidesWith(cCritter *pothercritter)
{
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterStubPlayer)))
		return cCollider::COLLIDEASCALLER;
	else
		return cCollider::DONTCOLLIDE;
}

// This function is used to make the coin objects massless by resetting velocity and distributing points.
BOOL cCritterCoin::collide(cCritter *pcritter)
{
	cVector oldVel = pcritter->velocity();
	BOOL collided = cCritter::collide(pcritter);
	if(collided) {
		pplayer()->addScore(10);
		die();
	}
	pcritter->setVelocity(oldVel);
	return collided;
}

//==================cCritterMathPower================

// Constructor
cCritterMathPower::cCritterMathPower(cGame *pownergame)
:cCritter(pownergame)
{
	setMaxspeed(0);
	Real playerx = 0.0;

	// If we don't specify a location, randomly place the critter in front of the player.
	if (pownergame)
		playerx = pownergame->pplayer()->position().x();
	randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()),
		_movebox.hicorner())); 

	setSprite(new cSpriteIcon(IDB_MATHPOWER));
	setRadius(.8);
	setFixedflag(TRUE);
}

// Alternate constructor, specifies a location as well.
cCritterMathPower::cCritterMathPower(cGame *pownergame, cVector location)
:cCritter(pownergame)
{
	setMaxspeed(0);
	moveTo(location);
	setSprite(new cSpriteIcon(IDB_MATHPOWER));
	setRadius(.8);
	setFixedflag(TRUE);
}

// This function is used to determine if the math power-up has collided with the player.
int cCritterMathPower::collidesWith(cCritter *pothercritter)
{
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterStubPlayer)))
		return cCollider::COLLIDEASARG;
	else
		return cCollider::DONTCOLLIDE;
}

// This function determines whether or not the math power-up has wandered off the playing area, despite their immobility.
void cCritterMathPower::update(CPopView *pactiveview, Real dt)
{
	cCritter::update(pactiveview, dt); //Always call this first

	if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */
		delete_me(); //tell the game to remove yourself
	
	// Has the power-up fallen into a pit?
	if (position().y() <= _bottom)
		delete_me();
}

//==================cCritterPogoPower================

// Constructor
cCritterPogoPower::cCritterPogoPower(cGame *pownergame)
:cCritter(pownergame)
{
	setMaxspeed(0);
	Real playerx = 0.0;

	// If we don't specify a location, randomly place the critter in front of the player.
	if (pownergame)
		playerx = pownergame->pplayer()->position().x();
	randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()),
		_movebox.hicorner())); 

	setSprite(new cSpriteIcon(IDB_POGO));
	setRadius(.8);
	setFixedflag(TRUE);
}

// Alternate constructor, specifies a location as well.
cCritterPogoPower::cCritterPogoPower(cGame *pownergame, cVector location)
:cCritter(pownergame)
{
	setMaxspeed(0);
	moveTo(location);
	setSprite(new cSpriteIcon(IDB_POGO));
	setRadius(.8);
	setFixedflag(TRUE);
}

// This function is used to determine if the pogo power-up has collided with the player.
int cCritterPogoPower::collidesWith(cCritter *pothercritter)
{
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterStubPlayer)))
		return cCollider::COLLIDEASARG;
	else
		return cCollider::DONTCOLLIDE;
}

// This function determines whether or not the pogo power-up has wandered off the playing area, despite their immobility.
void cCritterPogoPower::update(CPopView *pactiveview, Real dt)
{
	cCritter::update(pactiveview, dt); //Always call this first

	if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */
		delete_me(); //tell the game to remove yourself
	
	// Has the power-up fallen into a pit?
	if (position().y() <= _bottom)
		delete_me();
}

//==================cCritterStubTreasure=============

// Constructor
cCritterStubTreasure::cCritterStubTreasure(cGame *pownergame):
cCritter(pownergame)
{
	_collidepriority = cCollider::CP_PLAYER + 1; /* Let this guy call collide on the
		player, as his method is overloaded in a special way. */
	setSprite(new cSpriteIcon(IDB_EXIT));
	setRadius(2.5);
	setFixedflag(TRUE);
	moveTo(cVector(_movebox.hix()- 1.5 * cGameStub::TREASURERADIUS,
		_movebox.midy()));
}

// Alternate constructor, specifies a location as well.
cCritterStubTreasure::cCritterStubTreasure(cGame *pownergame, cVector location):
cCritter(pownergame)
{
	_collidepriority = cCollider::CP_PLAYER + 1; /* Let this guy call collide on the
		player, as his method is overloaded in a special way. */
	setSprite(new cSpriteIcon(IDB_EXIT));
	setRadius(2.5);
	setFixedflag(TRUE);
	moveTo(location);
}

// This function is used to determine if the treasure has collided with the player.
int cCritterStubTreasure::collidesWith(cCritter *pothercritter)
{
	if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterStubPlayer)))
		return cCollider::COLLIDEASCALLER;
	else
		return cCollider::DONTCOLLIDE;
}

// This function is used to alert the game that it needs to switch levels and distribute score.
BOOL cCritterStubTreasure::collide(cCritter *pcritter)
{
	if (contains(pcritter)) //disk of pcritter is wholly inside my disk
	{
		pcritter->addScore(150);
		die();
		_lswitch = true;
		return TRUE;
	}
	else
		return FALSE;
}
