// gamestub.cpp: implementation of the generic cGameStub class.
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "gamestub.h"
#include "critterarmed.h"
#include "listener.h"
#include "spriteicon.h"
#include "pop.h" //for playSound
#include "popview.h"
#include "force.h"
#include "spritebubble.h"
#include "graphicsopengl.h"
#include "graphicsmfc.h"
#include "critterviewer.h"
#include "spritemultiicon.h"

IMPLEMENT_SERIAL(cGameStub, cGame, 0);
IMPLEMENT_SERIAL(cCritterStubPlayer, cCritterArmedPlayer, 0);
IMPLEMENT_SERIAL(cCritterStubRival, cCritterArmedRobot, 0);
IMPLEMENT_SERIAL(cCritterStubProp, cCritter, 0);
IMPLEMENT_SERIAL(cCritterStubPlayerBullet, cCritterBullet, 0);
IMPLEMENT_SERIAL(cCritterStubRivalBullet, cCritterBullet, 0);


//#define DEBUGSTUB  //Comment DEBUGSTUB in to start with only one critter besides player.
//==============================cGameStub
int cGameStub::PLAYERHEALTH = 3; //Start with a very high health for demo purposes,
		//Do you don't die so fast.  Usually use a lower value for a real game.
int cGameStub::DEFAULTSEEDCOUNT = 8;
int cGameStub::DEFAULTRIVALCOUNT = 0;

cGameStub::cGameStub() //The constructor gets called only once, at first start.
{	
	_rivalcount = cGameStub::DEFAULTRIVALCOUNT; //The number of cCritterRival that I plan to add.
	_seedcount = cGameStub::DEFAULTSEEDCOUNT; //The number of cCritterStubProp that I plan to add.
	_wrapflag = cCritter::BOUNCE;
#ifdef DEBUGSTUB
	_rivalcount = 0;
	_seedcount = 1;
#endif //DEBUGSTUB
	_spritetype = cGame::ST_BUBBLES; //Default sprite for cCritterStubProp critters
	_menuflags &= ~cGame::MENU_RIDEPLAYER; /* Since I use so many bitmaps in this one,
		if I ride the player in 3D it runs bad with all the close up bitmaps. */
//Fix your statics
	cCritter::MAXSPEED = 4.0; /* Customary to set critter speed, usually the
		bigger the world the higher the speed you want. */
//Set the cursor tools.
	/*_arrayHCURSOR has only the _hCursorArrow inherited from cGame. 
		 Add a Shooting cursor and a Dragging cursor for fun. */
	_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(width, height)
	_border.set(20.0, 40.0);//Standard is (14.4, 9.6), can override here
//Set the backgroudn bitmap
	setBackgroundBitmap(IDB_BACKGROUND); /*  Sets the cSpriteBackground for the 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. */
//Set wrap with setWrapflag(cCritter::WRAP) or cCritter::BOUNCE.
//Make a player
	setPlayer(new cCritterStubPlayer(this)); //"this" arg sets default player _movebox to match _border.
//Possibly make and add any permanent critters here.
}

/* seedCritters makes and positions any temporary critters.  This gets
	automatically called when we select a game by the CPopDoc::setGameClass call.  It also gets
	called by cGame::reset() if you restart a game.*/
void cGameStub::seedCritters()
{
	pbiota()->purgeNonPlayerNonWallCritters(); 
		/* Clean out any old non-player non-wall critters. Although we don't have 
			walls yet, you might want to put some in. */
	/* Note that I can have some switch behavior in here depending on _level. */
	
 	for (int i=0; i < _seedcount; i++)
		 new cCritterStubProp(this); 
	for (i=0; i<_rivalcount; i++)
		new cCritterStubRival(this);
}

/* cGameStub::reset does not get called at first startup, is called only when you start a
	 new round or level of the game.*/
void cGameStub::reset() 
{
	_rivalcount = cGameStub::DEFAULTRIVALCOUNT;
	_seedcount = cGameStub::DEFAULTSEEDCOUNT;
	cGame::reset(); //Calls pplayer()->reset() and seedCritters().
	//Soemtimes reset other parameters here.
}

/* 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 cGameStub::initializeView(CPopView *pview)
{
	cGame::initializeView(pview); //Always call the baseclass method.
	pview->setUseBackgroundBitmap(TRUE); //Default is FALSE
	//pview->pviewpointcritter()->setTrackplayer(TRUE); //Always look at the player.
}

void cGameStub::initializeViewpoint(cCritterViewer *pviewer)
{
		/* The two args to setViewpoint are (directiontoviewer, lookatpoint).
			Note that directiontoviewer points FROM the origin TOWARDS the viewer. */
	if (pviewer->is3D())
	{
		pviewer->setViewpoint(cVector3(0.0, -1.0, 2.0), _border.center());
			//Direction to viewer is down a bit, and back off less than that.
		//pviewer->zoom(4.0);
	}
	else //2D case.
	{
		pviewer->setViewpoint(cVector::ZAXIS, _border.center());
		//pviewer->zoom(2.0);
	}
}

CString cGameStub::statusMessage()
{
	CString cStrStatusBar = cGame::statusMessage(); //Get the standard message
		//Add to it or change it.
	return cStrStatusBar;
}

void cGameStub::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 
		playSound("Tada");
		return; 
	}
// (2) Perhaps do this to reseed the screen if rivals and props are gone.
	int othercrittercount = pbiota()->count(RUNTIME_CLASS(cCritter)) -
		pbiota()->count(RUNTIME_CLASS(cCritterBullet)) - 1;
		//Number of critters minus bullets minus player equals other critters.
	if (!othercrittercount) //Player is alone with bullets
		seedCritters();
// (3) Maybe check some other conditions.
	/* You might for instance have something like this:
	if (_level == 1 && score() > 100)
	{
		_level = 2;
		//Possibly delete some critters added in the constructor.
		seedCritters();
	}
	*/

	int propcrittercount =
		pbiota()->count(RUNTIME_CLASS(cCritterStubProp));
	if (propcrittercount < _seedcount)
	{	
		new cCritterStubProp(this);
		new cCritterStubProp(this);
	}
}

void cGameStub::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 >> ...
}

//====================cCritterStubPlayer
cCritterStubPlayer::cCritterStubPlayer(cGame *pownergame):
cCritterArmedPlayer(pownergame)
{
		//Set the listener
	setListener(new cListenerArrow());
		//Set the bullet
 	setBulletClass(RUNTIME_CLASS(cCritterStubPlayerBullet));
	//setWaitShoot(2.0*cCritterArmed::WAITSHOOT); //Don't shoot as fast as usual.
		//Set other variables, perhaps including sprite.
	setHealth(cGameStub::PLAYERHEALTH);
		/* Maybe add some forces like gravity; the player will ignore them, but the
			bullets it shoots will use them. */
	setMaxspeed(30.0); //So you can catch them.

	setMoveBox(cRealBox(
		_movebox.locorner(),
		_movebox.hicorner() -
		//Move high corner almost to the bottom of world.
		(_movebox.ysize()-2*radius())* cVector::YAXIS
		));

	setAttitudeToMotionLock(FALSE);
	setAttitudeTangent(cVector::YAXIS);
}

void cCritterStubPlayer::reset()
{
	cCritterArmedPlayer::reset();
		//Perhaps use setHealth to restore the health to a high value.
	setHealth(cGameStub::PLAYERHEALTH);
}

BOOL cCritterStubPlayer::collide(cCritter *pcritter)
{
	BOOL collideflag = cCritter::collide(pcritter);
		/* May sometimes do more stuff here if collideflag is TRUE.  In the code
		below, for instance, we have the player "eat" cCritterStubProp critters, that is,
		increasing the cCritterStubPlayerown health and killing the cCritterStubProp
		when they touch. If you don't want this behavior take these lines out. */
	if (collideflag && pcritter->IsKindOf(RUNTIME_CLASS(cCritterStubProp)))
	{
		//setHealth(health() + 1);
		playSound("Ding");
		pcritter->die();
	}
	return collideflag;
}

void cCritterStubPlayer::update(CPopView *pactiveview, Real dt)
{
	cCritterArmedPlayer::update(pactiveview, dt);//Always call this first
}

int cCritterStubPlayer::damage(int hitstrength)
{
	int damagereward = cCritterArmedPlayer::damage(hitstrength); //Default bevavior.
	//Maybe change the sprite or something as well.
	return damagereward;
}

cCritterBullet* cCritterStubPlayer::shoot()
{
	cCritterBullet *pbullet = cCritterArmedPlayer::shoot();
	cCritter* paimtarget =
		pgame()->pbiota()->pickClosestAhead (cLine(position(), aimvector()), this);
	pbullet->addForce(new cForceObjectSeek(paimtarget, 50.0));
	return pbullet;
}

//====================cCritterStubPlayerBullet==================
cCritterStubPlayerBullet::cCritterStubPlayerBullet()
{
		/* The default constructor will do enough, but you 
		can override the default triangle sprite and size here. */
	//setSprite(pgame()->randomSprite(cGame::ST_POLYPOLYGONS)); 
	setRadius(2.0*cCritter::BULLETRADIUS); //Make bigger than usual.

	_dieatedges = FALSE;
}

void cCritterStubPlayerBullet::update(CPopView *pactiveview, Real dt)
{
	cCritterBullet::update(pactiveview, dt); //Always call base update first
}


//==================== cCritterStubProp ====================

cCritterStubProp::cCritterStubProp(cGame *pownergame):
cCritter(pownergame)
{
		//Set the health and value.  Make a sprite and use setSprite to install it.
		//For instance you might use a random sprite.
	if (pgame())
		setSprite(pgame()->randomSprite(pgame()->spritetype())); 
	else
		setSprite(pgame()->randomSprite(cGame::ST_BUBBLES)); 
		//Randomize psotion velocity if you like.
	randomize(cCritter::MF_POSITION | MF_VELOCITY);
		//Set a particular radius if you like.
	setRadius(0.5);
		//Add some forces
	BOOL clockwise = _personality & 1; /* Use a random bit to decide whether
		to circle clockwise or counterclockwise. */
	addForce(new cForceVortex(0.3, clockwise?-PI/2.0:PI/2.0, cVector::ZEROVECTOR));
		//circle around the center of the world.
	if (pgame())
		addForce( new cForceObjectSeek(pgame()->pplayer(), -0.1)); 
		//Weakly Avoid the player.

	randomizePosition(cRealBox(
		_movebox.locorner() +
			(_movebox.ysize() - 2*radius()) * cVector::YAXIS,
		_movebox.hicorner()
		));

	addForce(new cForceGravity());
	addForce(new cForceDrag());

	setMaxspeed(8.0);
}

int cCritterStubProp::damage(int hitstrength)
{
	int damagereward = cCritter::damage(hitstrength); //Default bevavior.
	//Maybe change the sprite or something as well.
	return damagereward;
}

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

void cCritterStubProp::update(CPopView *pactiveview, Real dt)
{
	cCritter::update(pactiveview, dt); //Always call this first
	if (_outcode & BOX_LOY)
	{
		pplayer()->damage(1);
		die();
	}
	if (_outcode & BOX_HIY)
			die();
}

//==================== cCritterStubRival ====================

cCritterStubRival::cCritterStubRival(cGame *pownergame):
cCritterArmedRobot(pownergame)
{
	setWrapflag(cCritter::WRAP);
		//Set the health and value. 
		// Make a sprite and use setSprite to install it.
	if (pgame())
	{
		setSprite(pgame()->randomSprite(cGame::ST_BITMAPS)); 
		//Choose randomly among available bitmaps
	//	setSprite(new cSpriteIcon(IDB_FACE)); //Or you could use a specific bitmap.
		//Always set target for cCritterArmedRobot
		setTarget(pplayer()); //Makes it automatically aim at pplayer.
	}
		//Set the bullet
 	setBulletClass(RUNTIME_CLASS(cCritterStubRivalBullet));
		//Set wait time between shots
	setWaitShoot(10.0); //Seconds between shots fired.
	//setGunlength(0.0);//Erase the "ugly" gun line from these if you like
		// COMMENT NEXT LINE OUT TO TURN SHOOTING ON.
	//setArmed(FALSE); //TURN OFF THE SHOOTING.
	randomize(cSprite::MF_RADIUS);// Randomize size if you like.
	randomize(cCritter::MF_POSITION | MF_VELOCITY);
		//Add some forces
	addForce(new cForceClassEvade(7.0, 1.5, 
		RUNTIME_CLASS(cCritterStubPlayerBullet)));
	if (pgame()) //Then we know we added this to a game so pplayer() is valid
 		addForce(new cForceObjectSeek(pplayer(), 0.3));
 }

int cCritterStubRival::damage(int hitstrength)
{
	int damagereward = cCritter::damage(hitstrength); //Default bevavior.
	//Maybe change the sprite or something as well.
	return damagereward;
}	

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

void cCritterStubRival::update(CPopView *pactiveview, Real dt)
{
	cCritterArmedRobot::update(pactiveview, dt);//Always call this first
}

//====================cCritterStubRivalBullet==================
cCritterStubRivalBullet::cCritterStubRivalBullet()
{
//	setSprite(new cSpriteBubblePie()); //Maybe use bubbles for fun.
//	randomize(cPolygon::MF_COLOR); //randomize color of sprite.
//	_dieatedges = FALSE; //Include this in case you want the bullet to wrap around
	setRadius(2.0*cCritter::BULLETRADIUS);
}

void cCritterStubRivalBullet::initialize(cCritterArmed *pshooter)
{
	cCritterBullet::initialize(pshooter);
	addForce( new cForceObjectSeek(ptarget(), 10.0)); 
//	setRadius(0.5 * pshooter->radius()); //Make bullet size depend on shooter, don't have to do this.
//	setWrapflag(cCritter::WRAP);
}

