/*
File: gameworms.cpp
Uploaded by richprillinger on Mon Nov 12 19:18:28 PST 2001
*/


// gameworms.cpp: implementation of the generic cGameWorms class.
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "gameworms.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, cCritter, 0); /* Lee added 11-11-01 */
IMPLEMENT_SERIAL(cCritterPlayerWormsSeg, cCritter, 0); /* Lee added 11-11-01 */
IMPLEMENT_SERIAL(cCritterWormsSeg, cCritter, 0); /* Lee added 11-11-01 */

// Defines
#define PLAYERSPRITELOOP

// Globals
int cGameWorms::PLAYERHEALTH = 5; 
int cGameWorms::WORMCOUNT = 3; //1; /* Lee added 11-04-01 */
int _playerseedcount = 3;


////////////////////////////////// SET UP THE GAME ENVIRONMENT
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.
	_seedcount = 6; //The number of cCritterWormsSeg that I plan to add.
	cCritter::MAXSPEED = 10.0; //5.0; //4.0;   /* 11-5 Joe */

	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPlay); 
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorDragger); 
	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPin);		// still useful for now -Rich
	//_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);

	//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   
   initiatePlayer();											/* 11-5 Joe */

#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, .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

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


////////////////////////////////////// ENEMY SNAKE CREATION
void cGameWorms::seedCritters()
{
	pbiota()->purgeNonPlayerNonWallCritters(); 
  
	cCritter * 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++)		// creates enemy snakes
	{
		for (int i = 0; i < _seedcount - 1; i++)        // controls enemy snake length
		// 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();		/* 11-5 Joe */
				add( pprop_prior );								/* 11-5 Joe */
			}													/* 11-5 Joe */

			cCritterWormsSeg *pprop = new cCritterWormsSeg(pprop_prior); /* Lee added 11-11-01 */
			add(pprop);
			pprop_prior = pprop;
		}
	}
}

////////////////////////////////////// PLAYER SNAKE CREATION
void cGameWorms::initiatePlayer()
{
   cCritterWormsPlayer * newPlayer = new cCritterWormsPlayer;
   cCritter *pprop_prior;

   for (int i = 0; i < _playerseedcount + 4; i++) /* Lee added 11-11-01 */  // Rich made longer for testing 11/12/01
																			// it WAS - 1, not + 4  Rich
   {		// 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_prior = pprop;
	}
	setPlayer(newPlayer);
}

////////////////////////////////////// GAME STARTING/ENDING/RESETTING STUFF
/* cGameWorms::reset is called only when you start a new round or level of the game.*/
void cGameWorms::reset() 
{
	initiatePlayer();	/* 11-5 Joe */
	cGame::reset(); //Calls pplayer()->reset() and seedCritters().
					//Sometimes 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;
}

///////////////////////////////////  STOPS COLLISIONS PUSHING SEGMENTS AROUND 
BOOL cGameWorms::collide(cCritter *pcriti, cCritter *pcritj)
{
	if (pcriti->IsKindOf(RUNTIME_CLASS(cCritterWormsPlayer))) /* Lee added 11-11-01 */
	{	// Stops worm heads from being pushed around by other segments
		if (pcriti->IsKindOf(RUNTIME_CLASS(cCritterPlayerWormsSeg)))
			return FALSE;
	}
	if (pcriti->IsKindOf(RUNTIME_CLASS(cCritterWormsSeg))) /* Lee added 11-11-01 */
	{	// Stops worm heads from being pushed around by other segments
		return FALSE;
	}
	if (pcriti->IsKindOf(RUNTIME_CLASS(cCritterPlayerWormsSeg))) /* Lee added 11-11-01 */
	{	// Stops worm heads from being pushed around by other segments
		return FALSE;
	}

	return cGame::collide(pcriti, pcritj);
}

////////////////////////////// STUFF TO CHECK AS GAME PROGRESSES
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(cCritterWall)) - /* Lee added 11-11-01 */
		pbiota()->count(RUNTIME_CLASS(cCritterPlayerWormsSeg)) - 1; /* Lee added 11-11-01 */

		/* 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
		seedCritters();
		// (3) Maybe check some other conditions.
}

///////////////////////////////// FOR SAVING GAMES, ETC
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 >> ...
}

//////////////////////////////////////////////PLAYER SNAKE DEFINITION
cCritterWormsPlayer::cCritterWormsPlayer()
{
	//Set the listener
	setListener(new cListenerFly());

	setHealth(cGameWorms::PLAYERHEALTH);
    setMaxspeed( cCritter::MAXSPEED );        /* 11-5 Joe */
	
#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()
{
	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)
{

}

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

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

cCritterWormsSeg::cCritterWormsSeg(cCritter *pcritter) /* Lee added 11-11-01 */
{
	setSprite(cGame::RANDOMSPRITE(cGame::ST_BUBBLES)); /* Lee added 11-11-01 */
	setRadius( 0.7 );		/* 11-6 Joe */

	if (pcritter)
		addForce(new cForceObjectLinkRod(pcritter,		// changed to LinkRod force 11/12/01 -Rich
			1.0));//,		// distance between critters	/* Lee added 11-11-01 */
			//1000 ));	// springiness force			/* 11-5 Joe Cheng */
}

int cCritterWormsSeg::damage(int hitstrength) /* Lee added 11-11-01 */
{
	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 cCritterWormsSeg::collide(cCritter *pcritter)
{
	BOOL collideflag = cCritter::collide(pcritter);
		//May sometimes do more stuff here if collideflag is TRUE.
	return collideflag;
}

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

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

cCritterPlayerWormsSeg::cCritterPlayerWormsSeg(cCritter *pcritter)
{
	setSprite(cGame::RANDOMSPRITE(cGame::ST_BUBBLES));
	setRadius( 0.7 );		/* 11-6 Joe */

	if (pcritter)
		addForce(new cForceObjectSpringRod(pcritter,
			0.7,     // Distance between critters   11-5 joe cheng 
			1000 )); // springiness   11-5 Joe Cheng
}

int cCritterPlayerWormsSeg::damage(int hitstrength) /* Lee added 11-11-01 */
{
	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 cCritterPlayerWormsSeg::collide(cCritter *pcritter) /* Lee added 11-11-01 */
{
	BOOL collideflag = cCritter::collide(pcritter);
		//May sometimes do more stuff here if collideflag is TRUE.
	return collideflag;
}

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);
}

//===================cCritterWormsEnemyHead        /* 11-5 Joe */
cCritterWormsEnemyHead::cCritterWormsEnemyHead()
{
   setSprite( new cSpriteIcon(IDB_FACE) );
	//randomize(cCritter::MF_VELOCITY); /* Lee added 11-11-01 */
   setRadius( 1.0 );
}