<pre>
/*
File: game.cpp
Uploaded by richprillinger on Thu Nov 29 00:42:20 PST 2001
*/

// Game.cpp: implementation of the cGame class and children.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "game.h"
#include "spritebubble.h"
#include "critter.h"
#include "critterarmed.h"
#include "critterwall.h"
#include "force.h"
#include "listener.h"
#include "spriteicon.h"
#include "spritepolygon.h"
#include "pop.h" //for playSound
#include "graphics.h"
#include "critterviewer.h"
#include "graphicsmfc.h"
#include "graphicsopengl.h"

IMPLEMENT_SERIAL( cGame, CObject, 0);

//#define MINIMALPOLYPOLYS
	/* This switch makes the polypolygons from cGame::RANDOMSPRITE be squares with
	triangles at the tips. */
//#define DRAGGINGPINKILLS
	/* DRAGGINGPINKILLS is on, then draggin the pin cursor kills everything it crosses.
	This turns out to be a bad idea as you end up killing more than you need to, better
	to only activate the Pin cursor kill once per left button press. */
#define FREEZEPICKCRITTER
	/* Comment FREEZEPICKCRITTER in if you want the arrow cursor to freeze a critter
	in place till you click elsewhere on the screen. For most games this is confusing
	and seems like a cheat. We would need this if we wanted to use the Edit | Copy
	control, but we no longer use this. */
//Static constants=============================================
 //******** CONST STATICS **********************************************
const int cGame::COUNTSMALL = 4;
const int cGame::COUNTMEDIUM = 8;
const int cGame::COUNTLARGE = 25;
const int cGame::COUNTHUGE = 80;
const int cGame::MENU_ALL = 0xFFFF; //All flags on
const int cGame::MENU_SHAPE = 1; //Allow the buttons to change shapes of sprites.
const int cGame::MENU_BOUNCEWRAP = 2; //Allow user to flip bounce/wrap settings.
const int cGame::MENU_2D = 4; //Allow cGraphicsMFC
const int cGame::MENU_3D = 8; //Allow cGraphicsOpenGL
const int cGame::MENU_AUTOPLAY = 0x10; //Allow the Player | Autoplay control to work.
const int cGame::MENU_COUNT = 0x20; //Allow the Game | Small, etc. controls
const int cGame::MENU_SHIELD = 0x40; //Activate Player|Shield
const int cGame::MENU_BACKGROUND = 0x80; //Activate View|Solid and View|Bitmap Background controls.
//Depth STatics ========================================
#ifdef THREEDVECTORS
Real cGame::BACKGROUNDOFFSET = 0.1; //Make sure bigger than - cSprite::MAXPRISMDZ
Real cGame::FOREGROUNDOFFSET = 0.9; //Make sure less than cSprite::MAXPRISMDZ.
#else //not THREEDVECTOR means 2D case
Real cGame::BACKGROUNDOFFSET = 0.0; //Make sure bigger than - cSprite::MAXPRISMDZ
Real cGame::FOREGROUNDOFFSET = 0.0; //Make sure less than cSprite::MAXPRISMDZ.
#endif //THREEDVECTOR
//============ cGame Statics =====================================
 int cGame::COUNTSTART = cGame::COUNTMEDIUM;
 Real cGame::NEAREDGEPERCENT = 0.15; /* Used in putNearGameBorderEdge.  0.1 for instance means
 	put critters in outer 10% of _border world box. */
Real cGame::DEFAULTZDEPTH = 1.0; /* Use this when you want to make a 2D world 3D by stacking
	disklike critters up. */
int cGame::MAXSCORE = 1000; //Default _maxscore for a game; can be overridden in constructor.
 /* In looking at the size and speed , keep in mind that our typical world size is 14.0
  by 9.0, defined by the WORLD??? statics.  The speed is in units per second, so a
 speed of 3 means it takes about 3 seconds to cross the window.  A speed of 1 takes 10 
 seconds, a speed of 5 takes 2 seconds and so on. */
 //==================Size Related Statics =======================
  /* The actual window size on my screen is 12 inch by 8 inch.  I make both 20% larger for more room. */
 Real cGame::WORLDWIDTH = 14.4; //14.4
 Real cGame::WORLDHEIGHT = 9.6; //9.6
  /* The next four are used in the cGame constructor to set the corresponding cCritter statics
to these default values. */
#ifndef DONTBOSS
 Real cGame::CRITTERMINRADIUS = 0.3;
 Real cGame::CRITTERMAXRADIUS = 0.8;
 Real cGame::BULLETRADIUS = 0.125;
 Real cGame::CRITTERMAXSPEED = 3.0;
#endif //DONTBOSS
	 /* Default game constructor sets cCritter::MAXSPEED 
	to this value. */
//================Static variable ==========================
CArray<UINT, UINT> cGame::RESOURCEBITMAPIDARRAY;

//======================Static constants for the RANDOMSPRITE Factory Method===========
const int cGame::ST_SPRITETYPENOTUSED = -1; //Indicates you will put in sprites by hand.
const int cGame::ST_SIMPLEPOLYGONS = 0; //Simple triangles, squares, pentagons.
const int cGame::ST_FANCYPOLYGONS = 1; //Diverse regular and star polygons.
const int cGame::ST_ASTEROIDPOLYGONS = 2;  //Polypolygons that have polypolygons at their tips.
const int cGame::ST_POLYPOLYGONS = 3;  //Polypolygons that have polygons at their tips.
const int cGame::ST_BITMAPS = 4;  //cSpriteIcon bitmaps.
const int cGame::ST_BUBBLES = 5;  //balls of various kinds.
const int cGame::ST_TRIPLEPOLYPOLYGONS = 6;  //Polypolygons that have polypolygons at their tips.
const int cGame::ST_ASSORTED = 7;  //Mixed from SIMPLEPOLYGONS through BUBBLES (but not TRIPLE).
//======================Static Factory Method==============================
cSprite* cGame::RANDOMSPRITE(int spritetypeindex)
{
	cPolygon *newpoly;
	cSpriteBubble *newbubble;
	cPolyPolygon *newpolypoly;
	cSpriteIcon *picon;
	if (spritetypeindex == cGame::ST_ASSORTED)
		spritetypeindex = cRandomizer::pinstance()->random(cGame::ST_SIMPLEPOLYGONS, cGame::ST_BUBBLES);
	/* This next block should be a switch, but the compiler won't let me use the cGame:: constants
		in a switch. */
	if(spritetypeindex == cGame::ST_SIMPLEPOLYGONS)
	{
		newpoly = new cPolygon(cRandomizer::pinstance()->random(3,5));
		newpoly->randomize(cSprite::MF_RADIUS | //cSprite::MF_ROTATION |
			 cPolygon::MF_COLOR);
		return newpoly;
	}
	else if(spritetypeindex == cGame::ST_FANCYPOLYGONS)
	{
		newpoly = new cPolygon();
		newpoly->randomize(cSprite::MF_RADIUS | //cSprite::MF_ROTATION |
			cPolygon::MF_COLOR | cPolygon::MF_LINEWIDTH |	cPolygon::MF_DOTS | cPolygon::MF_VERTCOUNT);
		return newpoly;
	}
	else if(spritetypeindex == cGame::ST_ASTEROIDPOLYGONS)
	{
		newpoly = new cPolygon();
		newpoly->setRandomAsteroidPolygon(5, 20, cRandomizer::pinstance()->randomReal(0.0, 0.4));
		newpoly->randomize(cSprite::MF_RADIUS | //cSprite::MF_ROTATION |
			 cPolygon::MF_COLOR);
		return newpoly;
	}
	else if(spritetypeindex == cGame::ST_BUBBLES)
	{
		if (cRandomizer::pinstance()->randomBOOL(0.6)) //60% cSpriteBubblePie
			newbubble = new cSpriteBubblePie();
		else if (cRandomizer::pinstance()->randomBOOL(0.75)) //0.75*0.4 make 30% cSpriteBubble
			newbubble = new cSpriteBubble();
		else //Leave 10% cSpriteBubbleGrayscale
			newbubble = new cSpriteBubbleGrayscale();
		newbubble->randomize(cSprite::MF_RADIUS | //cSprite::MF_ROTATION | 
			cPolygon::MF_COLOR | cPolygon::MF_LINEWIDTH );
		return newbubble;
	}
	else if(spritetypeindex == cGame::ST_POLYPOLYGONS)
	{
#ifdef MINIMALPOLYPOLYS
		return new cPolyPolygon(4, 3);
#else //not MINIMALPOLYPOLYS
		newpolypoly = new cPolyPolygon();
		newpolypoly->randomize(cSprite::MF_RADIUS | //cSprite::MF_ROTATION |	
			cPolygon::MF_COLOR | cPolygon::MF_LINEWIDTH | cPolygon::MF_DOTS | 
			cPolygon::MF_VERTCOUNT); 
		return newpolypoly;
#endif //MINIMALPOLYPOLYS switch
	}
	else if(spritetypeindex == cGame::ST_TRIPLEPOLYPOLYGONS)
	{
//#define POLYPOLYOFBITMAPS 
	/* Comment this in to have the option of showing a polygon with bitmaps
	at its vertices.  Otherwise we get a polygon with polypolygons. At present
	we don't use either one, as there is no menu access for
	 ST_TRIPLEPOLYPOLYGONS, and none of our critter consturctors asks for it. */
#ifdef POLYPOLYOFBITMAPS
	/*This doesn't seem to work right; in cGrahicsMFC the bitmaps don't have
	transparent backgrounds, and in cGraphicsOpenGL, the bitmaps cause a 
	resource leak. */
		newpolypoly = new cPolyPolygon;
		newpolypoly->randomize(cSprite::MF_RADIUS | //cSprite::MF_ROTATION |	
			cPolygon::MF_COLOR | cPolygon::MF_LINEWIDTH | cPolygon::MF_DOTS |
			 cPolygon::MF_VERTCOUNT); 
		newpolypoly->setTipShape(cGame::RANDOMSPRITE(cGame::ST_BITMAPS));
		return newpolypoly;
#else  //not the POLYPOLYOFBITMAPS case
		newpolypoly= new cPolyPolygon();
		newpolypoly->randomize( cPolygon::MF_VERTCOUNT);
		newpolypoly->setTipShape(new cPolyPolygon());
		newpolypoly->randomize(cSprite::MF_RADIUS | //cSprite::MF_ROTATION |	
			cPolygon::MF_COLOR | cPolygon::MF_LINEWIDTH | cPolygon::MF_DOTS | cPolygon::MF_VERTCOUNT); 
		return newpolypoly;
#endif //POLYPOLYOFBITMAPS switch
	}
	else if(spritetypeindex == cGame::ST_BITMAPS)
	{
		int resourceid = cRandomizer::pinstance()->random(cGame::RESOURCEBITMAPIDARRAY.GetSize());
		picon = new cSpriteIcon(cGame::RESOURCEBITMAPIDARRAY[resourceid]); //[6] for just saucers.
		picon->randomize(cSprite::MF_RADIUS);
		return picon;
	}

	return new cSprite(); //Default in the cGame::ST_SPRITETYPENOTUSED case
}

//============================= cGame  ==========================

cGame::cGame():
_seedcount(COUNTSTART), 
_gameover(TRUE),
_maxscore(MAXSCORE),
_scorecorrection(0),
_wrapflag(cCritter::STARTWRAPFLAG),
_bDragging(FALSE),
_pfocus(NULL),
_pplayer(NULL),
_border(cGame::WORLDWIDTH, cGame::WORLDHEIGHT), //Default no zsize, computes faster.
_pcontroller(NULL),
_cursorpos(0.0, 0.0),
_autoplay(0),
_spritetype(cGame::ST_SPRITETYPENOTUSED),
_pbackgroundbitmap(NULL), //NULL means not initialized yet
_menuflags(cGame::MENU_ALL & ~cGame::MENU_AUTOPLAY),
_haveseededonce(FALSE),
_newgame(TRUE)
{
//Fix the statics
	/* Reset the various statics that may have been set to different values by previously
		running some other game. */
#ifndef DONTBOSS
	cCritter::MAXSPEED = cGame::CRITTERMAXSPEED;
	cCritter::MINRADIUS = cGame::CRITTERMINRADIUS;
	cCritter::MAXRADIUS = cGame::CRITTERMAXRADIUS;
	cCritter::BULLETRADIUS = cGame::BULLETRADIUS;
#endif //DONTBOSS
//Allocate the pointer variables.
	_pbiota = new cBiota(this);
	_pcontroller = new cController(); //This is a structure used to store key and mouse info.
//Set the border colors.
 	_border.pcolorstyle()->setFillColor(cColorStyle::CN_WHITE);
  	_border.pcolorstyle()->setLineColor(cColorStyle::CN_YELLOW);
	_border.pcolorstyle()->setLineWidthWeight(0.01);
//Initialize other variables.
	/* We never want to have a NULL player, so we just add a basic cCritter as player. 
		Note that we use the setPlayer accessor rather than just setting _pplayer. */
	if (!cGame::RESOURCEBITMAPIDARRAY.GetSize()) /* If the static array of bitmap IDs 
		isn't initialized yet, we do it now. We only need this array as a way to select
		random bitmaps. If you have special bitmaps for your critters, they don't necessarily
		need to be mentioned here. */
	{
		cGame::RESOURCEBITMAPIDARRAY.Add(IDB_EARTH);
		cGame::RESOURCEBITMAPIDARRAY.Add(IDB_SUN);
		cGame::RESOURCEBITMAPIDARRAY.Add(IDB_FACE);
		cGame::RESOURCEBITMAPIDARRAY.Add(IDB_GNARLY);
		cGame::RESOURCEBITMAPIDARRAY.Add(IDB_SPRING);
		cGame::RESOURCEBITMAPIDARRAY.Add(IDB_BUM);
		cGame::RESOURCEBITMAPIDARRAY.Add(IDB_UFO);
	}
//Select the cursor tools
	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorArrow); //Avaiable cursor tools.
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPlay); 
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPin);
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorReplicate); 
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorDragger);
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorSpawn);
	//_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorZap); 
//Set the player
	setPlayer(new cCritter()); //Sets _pplayer AND adds it to _pbiota.
//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. */
}

cGame::cGame(cBiota *pbiota)
{
	delete _pbiota; //Get rid of the cBiota* that the default constructor gave you.
	_pbiota = pbiota;
	_pbiota->setGame(this); 
//Deleting the _pbiota killed off your _pplayer, so you need to get a new one.
	if (_pbiota->GetSize()>0) // use the first member of pbiota if there is one.
		_pplayer = _pbiota->GetAt(0);
	else //otherwise make a dummy. 
	{
		cCritter* pdummyplayer = new cCritter();
		setPlayer(pdummyplayer);
	}
}

cGame::~cGame()
{
	int playerindex = _index(_pplayer);
	delete _pbiota;
	if (playerindex == cBiota::NOINDEX) //player wasn't deleted by cBiota destructor
		delete _pplayer;
	delete _pcontroller;
	delete _pbackgroundbitmap;
}

 void cGame::Serialize(CArchive& ar)
 {
 	int focusindex, playerindex;
 	CObject::Serialize(ar);
 		/*It's worth noting that when we call this next line in loading mode,
 			the _pbiota will be pointing in a non-NULL cBiota that was created by the 
 			cGame constructor, so we'll need to have the cBiota::Serialize take care of
 			deleting members of an existing cBiota before loading into it. */
 	_pcontroller->Serialize(ar);
 	_pbiota->Serialize(ar);
 		/* I don't serialize _arrayHCURSOR because (a) it gets set by the constructor 
 			and (b) it's like a	pointer in that the numerical value will depend on the 
 			program run, so the number would be useless. */
  	if (ar.IsStoring()) // Save 
 	{
 		focusindex = _index(_pfocus);
 		playerindex =  _index(_pplayer);
 		ar << _seedcount << _gameover << _maxscore << _scorecorrection << _wrapflag <<
 			_bDragging << _border <<  
 			focusindex << playerindex << _autoplay <<  _spritetype << _haveseededonce;
 		if (playerindex == cBiota::NOINDEX)
 			ar << _pplayer;
 	}
  	else //Load 
 	{
 		ar >> _seedcount >> _gameover >> _maxscore >> _scorecorrection >> _wrapflag >>
 			_bDragging >> _border >> 
 			focusindex >> playerindex >> _autoplay >> _spritetype >> _haveseededonce;
 		if (focusindex != cBiota::NOINDEX)
 			_pfocus = _pbiota->GetAt(focusindex);
 		if (playerindex == cBiota::NOINDEX)
 		{
 			delete _pplayer; /* If _pplayer is not in the cBiota, it wasn't deleted in
 				cBiota::Serialize so we delete it here.  But in the else case below,
 				_pplayer equals one of the pointers deleted in the cBiota::_free call
 				in cBiota::Serialize, so it is actually a bad pointer and we don't delete
 				it. */
 			ar >> _pplayer;
 			_pplayer->setOwner(_pbiota);
 			_pplayer->fixPointerRefs();
 		}
 		else /* _pplayer is a bad (already deleted) pointer; we overwrite it without
 					deleting again. */
 			_pplayer = _pbiota->GetAt(playerindex);
 	}
 }

//Cursor toolmethods------------------------
int cGame::_HCURSORindex(HCURSOR hcursor)
{
	for (int i=0; i<_arrayHCURSOR.GetSize(); i++)
		if (_arrayHCURSOR.GetAt(i) == hcursor)
			return i;
	return cBiota::NOINDEX; //Use -1 to mean not found.
}

HCURSOR cGame::nextHCURSOR(HCURSOR hcursor, int updown)
{
	if (!validHCURSOR(hcursor))
		return hcursor;
	if (updown <= 0)
		updown = -1;
	else
		updown = 1;
	int index = _HCURSORindex(hcursor);
	index += updown;
	if (index < 0)
		index = _arrayHCURSOR.GetSize()-1;
	else if (index > _arrayHCURSOR.GetSize()-1)
		index = 0;
	return _arrayHCURSOR[index];
}
	
BOOL cGame::validHCURSOR(HCURSOR hcursor)
{
	return _HCURSORindex(hcursor) != cBiota::NOINDEX;
}

//-----------------Background methods----------
void cGame::setBackgroundBitmap(int resourceid)
{
	if (_pbackgroundbitmap)
		delete _pbackgroundbitmap;	
	_pbackgroundbitmap = new cSpriteIconBackground(resourceid, cRealBox2(_border));
}

void cGame::drawCritters(cGraphics *pgraphics, int drawflags)
{
	_pbiota->draw(pgraphics, drawflags);
}

void cGame::drawWorld(cGraphics *pgraphics, int drawflags)
{
//We draw the background as a rectangle behind the world in either the 2D or 3D case.
	pgraphics->pushMatrix();
	pgraphics->translate(cVector(0.0, 0.0,	//Put background down out of the way.
		_border.loz()- cGame::BACKGROUNDOFFSET)); 
	drawBackground(pgraphics, drawflags); //This call should only draw 2D rectangles.
	pgraphics->popMatrix();
//Second draw a rectangular borderface that's an edge (for 2D case) or draw a 3D box.
	if (!_border.zsize())
	{
		pgraphics->pushMatrix();
		pgraphics->translate(cVector(0.0, 0.0, cGame::FOREGROUNDOFFSET)); //Put over the world.
		drawForeground(pgraphics, drawflags);
		pgraphics->popMatrix();
	}
	else
		drawForeground(pgraphics, drawflags); //Assume if we have a 3D border, we'll draw it 3D.
}

void cGame::drawBackground(cGraphics *pgraphics, int drawflags)
{
//Draw a 2D rectanglular borderface that's either an edge, an edged rect, or an edged bitmap rect.
	cRealBox2 borderface(_border);
	if (drawflags & CPopView::DF_USEBACKGROUNDBITMAP)
	{
		_pbackgroundbitmap->draw(pgraphics, drawflags); 
		//Always draw the background edge to make the bitmap look crisper.			
		borderface.pcolorstyle()->setEdged(TRUE);
		borderface.pcolorstyle()->setFilled(FALSE);
		borderface.draw(pgraphics, drawflags); 
	}
	else
	{
		BOOL filledflag = drawflags & CPopView::DF_USESOLIDBACKGROUND?TRUE:FALSE;
		if (filledflag)
		{
			borderface.pcolorstyle()->setEdged(TRUE);
			borderface.pcolorstyle()->setFilled(TRUE);
		}
		else //not filled
		{
			borderface.pcolorstyle()->setEdged(TRUE);
			borderface.pcolorstyle()->setFilled(FALSE);
		}
		borderface.draw(pgraphics, drawflags); 
	}
}

void cGame::drawForeground(cGraphics *pgraphics, int drawflags)
{
	//cRealBox2 borderface(_border);
	cRealBox borderface(_border);
	borderface.pcolorstyle()->setEdged(TRUE);
	borderface.pcolorstyle()->setFilled(FALSE);
	borderface.draw(pgraphics, drawflags);/* This takes the
		pgame _border, sets it to edged but not filled and calls its draw to draw edges that will
		be on top of any possible overlapping sprites.  Can draw some more foreground
		furniture if pgame likes. */
}

//---------------- Player methods------------

int cGame::score()
{
	ASSERT(_pplayer);
	return _pplayer->score();
}

int cGame::health()
{
	ASSERT(_pplayer);
	return _pplayer->health();
}

Real cGame::age()
{
	ASSERT(_pplayer);
	return _pplayer->age();
}

CRuntimeClass* cGame::playerListenerClass()
{
	ASSERT(_pplayer);
	ASSERT(_pplayer->plistener());
	return (_pplayer->plistener()->GetRuntimeClass());
}

void cGame::setPlayer(cCritter *pplayernew, BOOL onscreen) //onscreen is TRUE by default
{
	if (pplayernew == _pplayer || pplayernew == NULL) //Don't duplicate effort, and don't allow a NULL.
		return;
//First remove the old player
	if (_pfocus == _pplayer)
		_pfocus = NULL;
	int playerindex = _index(_pplayer);
	delete _pplayer; 
	_pplayer = NULL;
	if (playerindex != cBiota::NOINDEX)
		_pbiota->RemoveAt(playerindex);
//Then put in the new player.
	_pplayer = pplayernew;
	_pplayer->setOwner(_pbiota);
	_pplayer->setDefaultPrismDz(cSprite::PLAYERPRISMDZ);
	if (onscreen)
	{
		_pbiota->InsertAt(0, _pplayer);
		_pplayer->setMoveBox(border());
		_pplayer->setDragBox(border());
		_pplayer->setWrapflag(_wrapflag); //Make sure to do this after setting _movebox, as it calls a clamp.
	}
}

void cGame::setAutoplay(int autoplay)
{
	_autoplay = autoplay;
	if (_autoplay)
	{
		_pcontroller->setKeystate(VK_SPACE, cController::KEYONLOCK);
		_pcontroller->setKeystate(VK_LEFT, cController::KEYONLOCK);
		_pcontroller->setKeystate(VK_UP, cController::KEYONLOCK);
	}
	else
	{
		_pcontroller->setKeystate(VK_SPACE, cController::KEYOFF);
		_pcontroller->setKeystate(VK_LEFT, cController::KEYOFF);
		_pcontroller->setKeystate(VK_UP, cController::KEYOFF);
	}
}

//------------------- Basic accessor and mutator methods --------------------------
void cGame::setSeedcount(int seedcount)
{
	_seedcount = seedcount;
	seedCritters();
	_pbiota->processServiceRequests();
		 //Do this here to double check that the adds from a possibly overloaded seedCritters take effect.
}

void cGame::setWrapflag(int wrapflag)
{
	_wrapflag= wrapflag;
	_pbiota->setWrapflag(_wrapflag);
}

void cGame::restart()
{
	/* This  sets	_pgame-_gameoverflag to FALSE to start
		the game in case its not currently running.  In addition, it calls _pgame->reset()
		to reset the player, call pgame->seedCritters, etc. */
	reset(); //Also sets _pplayer->age() to 0, which is what we get in the cGame::age() call.
	_gameover = FALSE; //Start running.
}

void cGame::start()
{
	_gameover = FALSE; //Start running
	_newgame = FALSE; //Next time use restart.
}

void cGame::reset()
{
	_pplayer->reset();  
	seedCritters();
	_pbiota->processServiceRequests();
		 //Do this here to double check that the adds from a possibly overloaded seedCritters take effect.
}

void cGame::setSpritetype(int spritetype)
{
	_spritetype = spritetype;
 	for (int i=0; i < _pbiota->GetSize(); i++)
		if (_pbiota->GetAt(i) != _pplayer)
 			_pbiota->GetAt(i)->setSprite(cGame::RANDOMSPRITE(_spritetype));
}

void cGame::add(cCritter* pcritter)
{
	pcritter->setMoveBox(border());
	pcritter->setDragBox(border());
	pcritter->setWrapflag(_wrapflag); //Make sure to do this after setting _movebox, as it calls a clamp.
	pcritter->add_me(_pbiota); 
/* cBiota::Add is protected to prevent abuse such as adding a member during
the middle of an update before everyone's ready.  It gets invoked only by a call to the
cBiota::processServiceRequests method, acting on all the pcritter who filed an add_me request.
At first I thought I should force the add to happen right away with the following line,
but it turns out that's not necessary, and would be a bad idea as it defeats the whole
purpose of our service requests structure.  If for instance an individual critter calls
the add method during its update, it really is better not to force the processServiceRequests,
and to first finish the updates of the other critters. */ 
//	_pbiota->processServiceRequests(); //DON'T DO THIS
}

//--------------------Fundamental running methods------------------------

	/* OnIdle happens right before the cGame::step method, which is called from inside
		CPopDoc::stepDoc, which is called from inside OnIdle.  Note that the first part of OnIdle, 
		is where the OnKeyDown, OnLButtonDown, etc. messages in queues are processed. */

void cGame::step(Real dt, CPopView *pview)
{
	_pcontroller->update(dt); /* Do controller update first, when you're fresh from the OnIdle
			call that did the checking of messages as this, too, checks user input. The mouse
			click handling code of cController expects you to do the update right after the 
			standard mouse processing of OnIdle, so don't move this call. This call also stores
			the current dt value inside the controller. And don't use the dt = 0.0.*/
	if (_gameover) //Meaning you haven't pressed ENTER for the first time.
		dt = 0.0; /*  Prevents the lurch at startup when I turn _gameover off, also
			prevents anything from happening in the move or update methods when _gameover. */
	if (!visibleplayer())
		_pplayer->setAge(_pplayer->age() + dt); 
			/* Invisible player isn't aged by move, 
			so age it anyway for use in age() method. */
	adjustGameParameters();
	_pbiota->listen(dt); /* Critters listen to the _pcontroller data,
			possibly using _cursorpos. 	The cCritterArmedPlayer, in particular, will
			look at the _cursorpos if left button is down.  Use the dt to adjust velocity if you have a 
			cListenerCursor, */

	_pbiota->move(dt); /* Critters save current position as _oldposition, use
			their _velocity and _acceleration to compute a new position, possibly wrap or bounce
			this position off the _border and then set the new _position. */
	_pbiota->updateMetric(); //Possibly fill a look-up table for use in update and collide.
	_pbiota->update(dt, pview); /* Feel any forces acting on the critter, possibly call sniff
			on pview to	check some pixel colors in the world to maybe back off from something 
			or 	kill something. We don't presently use the dt argument, but could use it for
			shrinking critter radius. */
	if (dt > 0.0) //Prevent constant readjustment when paused
		_pbiota->collide(); //Critter may abruptly change _position and _velocity in here.
	_pbiota->processServiceRequests();
	_pbiota->animate(dt);
		//And now the CPopDoc::stepDoc will redraw each view via UpdateAllViews(timehint(dt))
//Possibly wait for user to start or restart game.
	if (_newgame)
	{
		AfxMessageBox("Hit Enter To    \nPlay Snake!    ", MB_OK | MB_ICONINFORMATION);  // Rich
		start();
	}
	if(_gameover)
	{
		int playmore = ::AfxMessageBox(gameOverMessage(), MB_OK | MB_ICONINFORMATION);
		if (playmore == IDOK)
			restart();
	}
}

/* This issue in this method is to decide whether to let pcriti or pcritj be the
one to control the collision interaction.  We give top priority to walls, as they
bounce things a special way, second priority to bullets as they want to blow
things up, and third priority to everyone else. */
 BOOL cGame::collide(cCritter *pcriti, cCritter *pcritj)
 {
//Give the walls the first priority for colliding.
 	BOOL isiwall = pcriti->IsKindOf(RUNTIME_CLASS(cCritterWall));
 	BOOL isjwall = pcritj->IsKindOf(RUNTIME_CLASS(cCritterWall));
 	if (isiwall && isjwall) // Assume two walls don't collide for now. 
 		return FALSE; //May want to rethink this if we derive a paddle from wall.
 	if (isiwall)
 		return pcriti->collide(pcritj);
 	if (isjwall)
 		return pcritj->collide(pcriti);
//Give the bullets second priority for colliding
 	BOOL isibullet = pcriti->IsKindOf(RUNTIME_CLASS(cCritterBullet));
 	BOOL isjbullet = pcritj->IsKindOf(RUNTIME_CLASS(cCritterBullet));
   	if (isibullet)
 		return pcriti->collide(pcritj);
 	if (isjbullet)
 		return pcritj->collide(pcriti);
//Give third priority to all other critters.
	return pcriti->collide(pcritj); //This the "normal" collide behavior.
 }
//------------------- Critter adjustment methods ------------------------
void cGame::putNearGameBorderEdge(cCritter *pcritter) 
{
		/* Make critter be near edge of box by rerandomizing the position until it's not
			in the central area. This is so they're not right on top of the player if the
			player starts out in the middle.  I use this in cGameSpacewar in particular. 
			The default cGame::NEAREDGEPERCENT is 0.15.  */
	if (!border().zsize())
	{
		cRealBox2 flatborder(border());
		cRealBox2 centralbox = flatborder.innerBox(cGame::NEAREDGEPERCENT * flatborder.minsize());
		while (centralbox.inside(pcritter->position()))
			pcritter->randomizePosition();
	}
	else
	{
		cRealBox3 centralbox = border().innerBox(cGame::NEAREDGEPERCENT * border().minsize());
		while (centralbox.inside(pcritter->position()))
			pcritter->randomizePosition();
	}
}

//-----------------Special overloadables--------------------------------

 void cGame::seedCritters()
 {
 	cCritter *newcritter;
 	_pbiota->purgeNonPlayerNonWallCritters(); /* Clean out all critters but player and
 		walls, in case you have walls. */
 	for (int i=0; i < _seedcount; i++)
 	{
 		newcritter = new cCritter(); 
 		add(newcritter);  /* Always add each new critter to the game as soon as you construct it.
 			There's no problem with going on and initializing fields after it's been added 
 			into the list.  */
 		newcritter->randomize(cCritter::MF_POSITION | cCritter::MF_VELOCITY); 
 		newcritter->setSprite(cGame::RANDOMSPRITE(_spritetype));
   		newcritter->addForce(new cForceEvadeBullet()); /* Default force for evading the 
 			cBullet objects that the player fires. This force is a child of 
 			cForceClassEvade with default constructor equivalent 
 			to cForceClassEvade(cForceEvadeBullet::DARTACCELERATION, cForceEvadeBullet::DARTSPEEDUP,
 			RUNTIME_CLASS(cCritterBullet), FALSE). The FALSE in the fourth arg, means don't
 			bother evading children of cCritterBullet. */
 	} 
}

CString cGame::gameOverMessage()
{
	return CString("To Play Snake Again,     \nHit Enter!");		// Rich
}

CString cGame::statusMessage()
{
	CString cStrStatusBar;
	int nUpdatesPerSecond;
	CString cStrUpdatespersecond;
	CString cStrHealth;
	CString cStrCount;
	CString cStrScore;

	if (!gamepaused())
	{
		nUpdatesPerSecond = int(((CPopApp*)::AfxGetApp())->_timer.updatesPerSecond());
		if (!nUpdatesPerSecond)
			cStrUpdatespersecond.Format("Less than one update per second.");
		else	
			cStrUpdatespersecond.Format("Updates per second: %d.", nUpdatesPerSecond);
		if (((CPopApp*)::AfxGetApp())->_timer.runningNearMaxSpeed())
			cStrUpdatespersecond += " (Near Max)";
	}
	else
			cStrUpdatespersecond.Format("Animation is paused.");
	cStrScore.Format("Score: %d.", score());
	cStrHealth.Format("Health: %d.", health());
	int crittercount = _pbiota->count(RUNTIME_CLASS(cCritter));
	int bulletcount = _pbiota->count(RUNTIME_CLASS(cCritterBullet));
	crittercount -= bulletcount;
	if (visibleplayer()) /*Subtract off 1 for player as well. */
		crittercount -= 1;
	cStrCount.Format("Critters: %d.", crittercount);
	cStrStatusBar = cStrScore + "  " + cStrHealth + "  " + cStrCount + "  " + cStrUpdatespersecond; 
	return cStrStatusBar;
}

/*pgame()->initializeView is called by the pview->OnUpdate when starting a new game. */
void cGame::initializeView(CPopView *pview)
{
	pview->setCursor(((CPopApp*)::AfxGetApp())->_hCursorArrow);
	pview->setRealBox(_border);
	pview->setUseBackgroundBitmap(FALSE); //Default doesn't use bitmap background
	pview->setUseSolidBackground(TRUE); //Default does use a solid rect background when
		//bitmap is absent.
	if (pplayer()->plistener()->IsKindOf(RUNTIME_CLASS(cListenerCursor)))
		pview->setCursorPosToCritter(pplayer()); // Match cursor to player pos.
	pview->setGraphicsClass(RUNTIME_CLASS(cGraphicsMFC)); //Default does this, for 2D game view OR...
	//pview->setGraphicsClass(RUNTIME_CLASS(cGraphicsOpenGL)); //...OR use this for 3D games
	pview->pgraphics()->enableLighting(FALSE); //Turn off the lighting calculations
}

/*pgame()->initializeView(pviewer) is called by the pview->OnUpdate when starting a new game,
and it is also called by setGraphicsClass, so we may sometimes call it twice in a row.  */
void cGame::initializeCritterViewer(cCritterViewer *pviewer)
{
	pviewer->setTrackplayer(FALSE);
	if (pviewer->is3D())
	{
		pviewer->setListener(new cListenerViewerFly()); 
		pviewer->setViewpoint(cVector3(0.0, -1.0, 2.0), _border.center());
			//Args are (directiontoviewer, lookatpoint);
			//Direction to viewer is down a bit, and back off less than that.
	}
	else //2D case.
	{
		pviewer->setListener(new cListenerViewerOrtho());
		pviewer->setViewpoint(cVector::ZAXIS, _border.center());
			/* The two args to setViewpoint are (directiontoviwer, lookatpoint).
			Note that directiontoviewer points FROM the origin TOWARDS the viewer. */
	}
}

//============cGame control methods===================

void cGame::onLButtonDown(CPopView *pview, UINT nFlags, CPoint point)
{
	if (gameover() || gamepaused())
		return; //Don't use mouse or keyborad messages until game starts.
	/*This next line is a little ugly in that it is specifically mentioning one kind of cursor.  
A more general way to do this would be to put the pview pointer into the _pcontroller record that
registers the onLButtonDown, and then when we got this record out to check the value of the
pview->hcursor(). */
	if ((pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorPlay) ||
		playerListenerClass() == RUNTIME_CLASS(cListenerCursor))
	/* We  put a left click into the _pcontroller for the individual critters to see it if
		either we have the hCursorPlay cursor, which is used for shooting, or if our player happens
		to be using	a ListenerCursor. */
		_pcontroller->onLButtonDown(nFlags); /* Don't need to save point, as that's handled
			by the trackCursor method. */
	if (playerListenerClass() == RUNTIME_CLASS(cListenerCursor))
		/* Don't try and use the cursor as a tool if its attached to the player. */
		return; 
	cCritter* pTouched = NULL;
	//Find the first bubble from the tail, if any, which you clicked.
	//pickTopTouched(cursorpos()); //I used to do the pick like this.
	//pTouched = _pbiota->pickClosestTouched(cursorpos());  //And then I did it like this.
	pTouched = _pbiota->pickClosestTouched(pview->pgraphics()->pixelToSightLine(point.x, point.y));
		/* Using the sight line works better once you go to 3D. */
/* Click any cursor except cursor play (shoot cursor) sets focus to a clicked critter,
	or to nothing if you missed 'em. Don't let playcursor setFocus, because a focus critter
	doesn't move, and this would make it too much a sitting duck. */
	if ((pview->hcursor() != ((CPopApp*)::AfxGetApp())->_hCursorPlay) )
		setFocus(pTouched);
//No bubble clicked case doesn't apply for the Pin and the Hand.
	if (!pTouched)
		return;
//Put it on top.
	pTouched->makeServiceRequest("move_to_front"); 
//Click Hand case 
	if (pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorDragger)
	{ //The volunteer condition checks if the critter is willing to be dragged.
		if (pTouched = pTouched->volunteer())
		{
			//setFocus(pTouched); //I don't think I need this line, do I?
			_bDragging = TRUE; 
			onMouseMove(pview, nFlags, point); //Move to the click point.
		}
		return; /* Note that here we leave the _pfocus on the move critter, 
			which has the sideeffect that cBiota::move doesn't move it,	
			which is good. */
	}
//Click Pin case
	if (pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorPin)
		pTouched->die(); //makes "delete_me" request, possibly does more
//Click Spawn case
	if (pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorSpawn)
		pTouched->spawn(); //makes "spawn" request for mutated copy.
//Click Zap case
	if (pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorZap)
		pTouched->zap(); //makes "zap" request for this guy.
//Click Replicate case
	if (pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorReplicate)
		pTouched->replicate(); //makes "replicate" request for this guy.
	_pbiota->processServiceRequests(); /* So you don't change critter
		twice.  If you wait for the timer to trigger CPopDoc::stepDoc
		to call the processServiceRequest, you might possibly manage to
		click or drag the same critter again.  The reason is that you may
		have several OnLButtonDown messages in the message queue, and when they
		are processed you will get several calls to onLButtonDown. */
#ifdef FREEZEPICKCRITTER
	if ((pview->hcursor() != ((CPopApp*)::AfxGetApp())->_hCursorArrow) )
#endif //FREEZEPICKCRITTER
		setFocus(NULL); /* For all of these one-time actions, we release
			the pfocus after the action, because it's confusing to see
			the critter frozen in focus after you zap it for instance.
			(The freeze woudl be because cBiota::move doesn't move the
			pfocus.)  We will for now leave in the freeze on the pick 
			cursor, though maybe that's a bad idea. */
}

void cGame::onLButtonUp(CPopView *pview, UINT nFlags, CPoint point)
{
	if (gameover() || gamepaused())
		return; //Don't use mouse or keyborad messages until game starts.
	/* We let the cController::FAKELBUTTONKEY Key mimic the LButton.  So if it is still down, then we are waiting
for it to come up to generate the onLButtonDown.*/
	if 	(keystate(cController::FAKELBUTTONKEY) == cController::KEYON)
		return;

	if ((pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorPlay) ||
		playerListenerClass() == RUNTIME_CLASS(cListenerCursor))
	/* We only pass on a left click to the individual critters like the player if either we have
		 the hCursorPlay cursor, which is used for shooting, or if our player happens to be using
		a ListenerCursor. */
		_pcontroller->onLButtonUp(nFlags); //trackCursor already saved the point.
	if (playerListenerClass() == RUNTIME_CLASS(cListenerCursor))
		/* Don't try and use the cursor as a tool if its attached to the player. */
		return; 
	_bDragging = FALSE; 
	if (pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorDragger)
		setFocus(NULL);
}

void cGame::onMouseMove(CPopView *pview, UINT nFlags, CPoint point)
{
	if (gameover() || gamepaused())
		return; //Don't use mouse or keyborad messages until game starts.
	/* We pass this on, but ordinarily the critters don't do anything with it. */
	_pcontroller->onMouseMove(nFlags);
	if (playerListenerClass() == RUNTIME_CLASS(cListenerCursor))
		/* Don't try and use the cursor as a tool if it's attached to the player. */
		return; 
//The _cursorpos gets set in CView::OnMouseMove if you're dragging.
// No Drag Case
	if (!(nFlags & MK_LBUTTON || keystate(cController::FAKELBUTTONKEY) == cController::KEYON) )
		return;		//We are treating the cController::FAKELBUTTONKEY Key as an alternate LButton.
// Drag Pin Case
#ifdef DRAGGINGPINKILLS
	if (pview->hcursor() ==((CPopApp*)::AfxGetApp())->_hCursorPin)
	{
		onLButtonDown(pview, nFlags, point);
		return;
	}
#endif//
// Drag Hand Case, with (_hcursor == ((CPopApp*)::AfxGetApp())->_hCursorDragger)
	if (pFocus() && _bDragging)
	{
		cVector cursorforcritter = pview->pixelToCritterPlaneVector(point.x, point.y, pFocus());
			/* It's going to easier, at least to start with, to drag only within the focus critter
			plane. */
		pFocus()->dragTo(cursorforcritter, _pcontroller->dt()); /* Feed the current _dt to dragTo
			so as to set the critter's velocity to match the speed of the drag; this way you
			can "throw" a critter by dragging it. */
		_pbiota->processServiceRequests(); /* In case critter reacts.
		If you wait for the timer to trigger CPopDoc::stepDoc
		to call the processServiceRequest, you might possibly manage to
		click or drag the same critter again.  The reason is that you may
		have several OnMouseMove messages in the message queue, and when they
		are processed you will get several calls to onMouseMove. */
	}
}

	/* Let's use the cController::FAKELBUTTONKEY Key like a left mouse button to make dragging more ergonomic. And store
	info in keyflags. */
void cGame::onKeyDown(CPopView *pview, UINT nChar, UINT nFlags)
{
	if (gameover() || gamepaused())
		return; //Don't use mouse or keyborad messages until game starts.
	if (nChar == cController::FAKELBUTTONKEY) 
	{ //If you press cController::FAKELBUTTONKEY Key when Lbutton is not down, treat it like an lbutton press.
		if(keystate(cController::FAKELBUTTONKEY) == cController::KEYOFF 
			&& keystate(VK_LBUTTON) == cController::KEYOFF)
		{
			int ix, iy;
			Real zbuff;
			pview->vectorToPixel(_cursorpos, ix, iy, zbuff);
			onLButtonDown(pview, MK_LBUTTON, CPoint(ix,iy));
		//	if ((pview->hcursor() == ((CPopApp*)::AfxGetApp())->_hCursorPlay) ||
		//		playerListenerClass() == RUNTIME_CLASS(cListenerCursor))
			/* We only pass a left-click-mimicking Ctrl press to the individual critters like the
			 player if either we have the hCursorPlay cursor, which is used for shooting, or if 
			our player happens to be using	a ListenerCursor. */
				_pcontroller->setKeystate(cController::FAKELBUTTONKEY, cController::KEYON);
		}	
	}
	else
		_pcontroller->onKeyDown(nChar, nFlags);
}

	/* We won't pay attention to most onKeyUp messages.  Instead, 
	we let the _pcontroller use the global ::GetAsyncKeystate to look for released keys.
	This works better becuase then a controller will detect a key release even if a menu is 
	opened after the key is pressed and you then release the key with the  menu still open. 
	In the one case of the "fake left button" key we send an lButtonUp message to the game in case the
	game does some mouse code that's not filtered thorugh the contorller, which will
	in fact set VK_LBUTTON to match cController::FAKELBUTTONKEY. */
void cGame::onKeyUp(CPopView *pview, UINT nChar)
{
	if (gameover() || gamepaused())
		return; //Don't use mouse or keyborad messages until game starts.
	if (nChar == cController::FAKELBUTTONKEY && keystate(VK_LBUTTON) == cController::KEYOFF)
	{ //If you release cController::FAKELBUTTONKEY Key when Lbutton is not down, treat it like an lbutton release.
		int ix, iy;
		Real zbuff;
		pview->vectorToPixel(_cursorpos, ix, iy, zbuff);
		_pcontroller->setKeystate(cController::FAKELBUTTONKEY, cController::KEYOFF); //Do now, or onLButtonUp won't work.
		onLButtonUp(pview, MK_LBUTTON, CPoint(ix, iy));
	}
}

//================= 3D Stuff=====================

void cGame::zStackCritters()
{
	/* For this to work, your constructor should contain this line so that your flat disk 
		critters don't mistakenly get clamped away from being in the range of DEPTH.
		_border.setZRange(0.0, cGame::DEFAULTZDEPTH + 2*cCritter::MAXRADIUS );  
	*/ 
	Real dz = (_border.zsize() - 2*cCritter::MAXRADIUS)/(pbiota()->count() + 2); /* Want to layer the critters in the z
		direction, leaving a space at the top and the bottom. The usual convention is to think
		of the biota as listing critters from closest to furthest, so we move down. */
	Real critterz = _border.hiz() - cCritter::MAXRADIUS - dz;
	
	_pbiota->processServiceRequests(); /* In case you added critters and didn't 
		process the service requests yet to really add them. */
	ASSERT(_pbiota->count());
	for (int i=0; i< _pbiota->count(); i++)
	{
		_pbiota->GetAt(i)->moveToZ(critterz);
		critterz -= dz;
	}
}


</pre>

