/*
File: gameworms.cpp
Uploaded by leegong on Sun Nov  4 23:54:19 PST 2001
*/

// gameworms.cpp: implementation of the generic cGameWorms class.
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "gameworms.h"
#include "critterarmed.h"
#include "critterwall.h" /* Lee added 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 added 11-04-01 */
IMPLEMENT_SERIAL(cCritterWormsPlayer, cCritterArmedPlayer, 0);
IMPLEMENT_SERIAL(cCritterWormsRival, cCritterArmedRobot, 0);
IMPLEMENT_SERIAL(cCritterWormsProp, cCritter, 0);
IMPLEMENT_SERIAL(cCritterWormsPlayerBullet, cCritterBullet, 0);
IMPLEMENT_SERIAL(cCritterWormsRivalBullet, cCritterBullet, 0);

//Defines
#define PLAYERSPRITELOOP
	/* 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 */

cGameWorms::cGameWorms(): //The constructor gets called only once, at first start.
	_rivalcount(3)
{	
	_menuflags &= ~cGame::MENU_SHIELD; //We don't use the shield control in this game.
//Usually we set _seedcount.
	_seedcount = 6; //The number of cCritterWormsProp that I plan to add.
//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) /* Lee added 11-04-01 */
	_border.set(38.0, 29.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.
	cCritterWall::THICKNESS = 0.4; /* Lee added 11-04-01 */
	setWrapflag(cCritter::BOUNCE); /* Lee added 11-04-01 */
//Make a player
	setPlayer(new cCritterWormsPlayer);
//Possibly make and add any permanent critters here.
//#define DEBUGWALL /* Lee added 11-04-01 */
#ifndef DEBUGWALL /* Lee added 11-04-01 */
	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, 0))); // 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, -9))); // 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

#else /* Lee added 11-04-01 */
	_seedcount = 0; /* Lee added 11-04-01 */
#endif //DEBUGWALL /* Lee added 11-04-01 */
}

void cGameWorms::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. */
	/* Make some new cCritterWormsRival(pplayer()) or new cCritterWormsProp and
		call add() to put them into the game.Here's one way you might do it.*/
	for (int i=0; i<_rivalcount; i++)
	{
		cCritterWormsRival *prival = new cCritterWormsRival(pplayer());
		add(prival);
		prival->randomize(cCritter::MF_POSITION);
			//Need to do cGame::add BEFORE randomize pos
		prival->setWrapflag(cCritter::BOUNCE);
			//Can override the wrapflag setting that was made by add.
	}
	cCritterWormsProp *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; kcCritter::MF_POSITION;//randomize(cCritter::MF_POSITION); /* Lee added 11-04-01 */
			pprop_prior = pprop;
		}
}

/* 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() 
{
	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 cGameWorms::initializeView(CPopView *pview)
{
	cGame::initializeView(pview); //Always call baseclass
	pview->setCursor(((CPopApp*)::AfxGetApp())->_hCursorPlay);
			 //Use the shoot cursor
	pview->setUseBackgroundBitmap(TRUE);
			//TRUE if you want the background, FALSE if not.
}

CString cGameWorms::statusMessage()
{
	CString cStrStatusBar = cGame::statusMessage(); //Get the standard message
		//Add to it or change it.
	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.*/
	return cGame::collide(pcriti, pcritj);
}

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);
		return; 
	}
// (2) Perhaps do this to reseed the screen if rivals 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.
}

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 >> ...
}

//====================cCritterWormsPlayer
cCritterWormsPlayer::cCritterWormsPlayer()
{
		//Set the listener
	setListener(new cListenerFly());
		//Set the bullet
 	setBulletClass(RUNTIME_CLASS(cCritterWormsPlayerBullet));
	setWaitShoot(2.0*cCritterArmed::WAITSHOOT); //Don't shoot as fast as usual.
		//Set other variables, perhaps including sprite.
	setHealth(cGameWorms::PLAYERHEALTH);
		/* Maybe add some forces like gravity; the player will ignore them, but the
			bullets it shoots will use them. */
	setMaxspeed(1.3*cCritterBulletSilverMissile::MAXSPEED); //So you can catch them.

	//Let's test out a cSpriteDirectional or a cSpriteLoop
	//Swith on the 
	
#ifdef PLAYERSPRITELOOP
	cSpriteLoop *pmultisprite = new cSpriteLoop(); //OR Cycle through changes
#else //The not PLAYERSPRITELOOP case we do a directional sprite.
	cSpriteDirectional *pmultisprite = new cSpriteDirectional(); //Change with direction
#endif //End PLAYERSPRITELOOP switch.

	cPolygon *ppolygon = new cPolygon(8);
	ppolygon->setVertex(4, cVector(0, 0)); //Puts a dent in the "back" side.
	ppolygon->setFillColor(cColorStyle::CN_GREEN);
	ppolygon->setDotted(TRUE);
	ppolygon->setDotRadiusWeight(0.3);
	ppolygon->pdotcolorstyle()->setFillColor(cColorStyle::CN_BLUE);
	pmultisprite->add(ppolygon);

	ppolygon = new cPolygon(8);
	ppolygon->setVertex(4, cVector(0, 0)); 
	ppolygon->setFillColor(cColorStyle::CN_YELLOW);
	ppolygon->setDotted(TRUE);
	ppolygon->setDotRadiusWeight(0.3);
	ppolygon->pdotcolorstyle()->setFillColor(cColorStyle::CN_BLUE);
	pmultisprite->add(ppolygon);

	ppolygon = new cPolygon(8);
	ppolygon->setVertex(4, cVector(0, 0)); 
	ppolygon->setFillColor(cColorStyle::CN_RED);
	ppolygon->setDotted(TRUE);
	ppolygon->setDotRadiusWeight(0.3);
	ppolygon->pdotcolorstyle()->setFillColor(cColorStyle::CN_BLUE);
	pmultisprite->add(ppolygon);

	ppolygon = new cPolygon(8);
	ppolygon->setVertex(4, cVector(0, 0)); 
	ppolygon->setFillColor(cColorStyle::CN_PURPLE);
	ppolygon->setDotted(TRUE);
	ppolygon->setDotRadiusWeight(0.3);
	ppolygon->pdotcolorstyle()->setFillColor(cColorStyle::CN_BLUE);
	pmultisprite->add(ppolygon);

	setSprite(pmultisprite);
	setRadius(2.5*cCritter::MINRADIUS);
}

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

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

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

int cCritterWormsPlayer::damage(int hitstrength)
{
	//Let's have the bullets be like food.
	addScore(hitstrength);
	return 0;
}

//====================cCritterWormsPlayerBullet==================
cCritterWormsPlayerBullet::cCritterWormsPlayerBullet()
{
		//Let's use polypolygons for fun.
	setSprite(cGame::RANDOMSPRITE(cGame::ST_POLYPOLYGONS)); 
	setRadius(2.0*cCritterBullet::BULLETRADIUS); //Make bigger than usual.
}

//==================== cCritterWormsRival ====================
cCritterWormsRival::cCritterWormsRival(cCritter *pplayer)
{
		//Set the health and value. 
		// Make a sprite and use setSprite to install it.
	setSprite(new cSpriteIcon(IDB_FACE)); //For instance
		//Always set target for cCritterArmedRobot
	setTarget(pplayer); //Makes it automatically aim at pplayer.
		//Set the bullet
 	setBulletClass(RUNTIME_CLASS(cCritterWormsRivalBullet));
		//Set wait time between shots
	setWaitShoot(3.0); //Seconds between shots fired.
		//Erase the ugly gun line from these
	setGunlength(0.0);
		// COMMENT NEXT LINE OUT TO TURN SHOOTING ON.
	//setArmed(FALSE); //TURN OFF THE SHOOTING.
		//Randomize velocity, and size if you like.
	randomize(cCritter::MF_VELOCITY | cSprite::MF_RADIUS);
		//Add some forces
	addForce(new cForceClassEvade(20.0, 4.0, 
		RUNTIME_CLASS(cCritterWormsPlayerBullet)));
}

int cCritterWormsRival::damage(int hitstrength)
{
		//Let's swell, just for kicks.
	setRadius(radius()*1.3);
	if(radius() > 2.0) //Pop when you get too big!
		return cCritter::damage(hitstrength); //Default bevavior.
	else 
		return 0;
}

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

void cCritterWormsRival::update(Real dt, CPopView *pview)
{
	cCritterArmedRobot::update(dt, pview);//Always call this first
	setAimVector(tangent().defaultNormal()); //Aim at right angles to the player
}

//====================cCritterWormsRivalBullet==================
cCritterWormsRivalBullet::cCritterWormsRivalBullet()
{
		//Let's use bubbles for fun.
	setSprite(new cSpriteBubblePie());
	randomize(cPolygon::MF_COLOR); //randomize color of sprite.
	setRadius(2.0*cCritterBullet::BULLETRADIUS); //Make bigger than usual.
	_dieatedges = FALSE; //So the bullet can wrap around
}

void cCritterWormsRivalBullet::initialize(cCritterArmed *pshooter, Real bulletspeed, int hitstrength)
{

	cCritterBullet::initialize(pshooter, bulletspeed, hitstrength);
	addForce(
		new cForceObjectAccelerateTowards(ptarget(), 
 			-0.3 * cCritterBulletSilverMissile::CHASEACCELERATION)	);

	setRadius(0.5 * pshooter->radius());
//	setWrapflag(cCritter::WRAP);
}

//==================== cCritterWormsProp ====================

cCritterWormsProp::cCritterWormsProp(cCritter *pcritter)
{
		//Set the health and value.  Make a sprite and use setSprite to install it.
		//For instance you might use a random sprite.
	setSprite(cGame::RANDOMSPRITE(cGame::ST_SIMPLEPOLYGONS));
		//Use addForce to add forces.
		//Randomize velocity if you like.
	randomize(cCritter::MF_VELOCITY);
		//Set a particular radius ifyou like.
	setRadius(0.5);
		//Add some forces
		//Just for kicks lets add a spring force.
	if (pcritter)
		addForce(new cForceObjectSpringRod(pcritter,
			radius()+pcritter->radius(), //rodlength
			3.0)); //spring force
}

int cCritterWormsProp::damage(int hitstrength)
{
	if (psprite()->IsKindOf(RUNTIME_CLASS(cPolygon)))/* I have to check this
		because if I try and cast a non-cPolygon sprite, the program will crash. */
	{
		cPolygon *ppolygon = (cPolygon*)psprite();
		if (ppolygon->vertCount() == 3) //Down to three verts
			ppolygon->changeVertexcount(7); //Jump to 10
		else
			ppolygon->changeVertexcount(-1);
	}
	return 0; 
}

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

void cCritterWormsProp::update(Real dt, CPopView *pview)
{
	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);
}