// gamestub.cpp: implementation of the generic cGameStub3D class.
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "gamestub3d.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"

//#define DEBUGSTUB3D //Sets _seedcount to 0, so player is alone, useful for debuggin his listeners.
#define SHAPEDEMO 
/* If SHAPEDEMO is on, we draw some OpenGL shapes.  In the long run it would be better to have
these fancy shapes be attached to actual critters. */

IMPLEMENT_SERIAL(cGameStub3D, cGame, 0);
IMPLEMENT_SERIAL(cCritterStub3DPlayer, cCritterArmedPlayer, 0);
IMPLEMENT_SERIAL(cCritterStub3DRival, cCritterArmedRobot, 0);
IMPLEMENT_SERIAL(cCritterStub3DProp, cCritter, 0);
IMPLEMENT_SERIAL(cCritterStub3DPlayerBullet, cCritterBullet, 0);
IMPLEMENT_SERIAL(cCritterStub3DRivalBullet, cCritterBullet, 0);

//==============================cGameStub3D
int cGameStub3D::PLAYERHEALTH = 5; 
Real cGameStub3D::CRITTERMINRADIUS = 1.0; //0.5;
Real cGameStub3D::CRITTERMAXRADIUS = 1.0; //1.5;
Real cGameStub3D::CRITTERMAXSPEED = 10.0;

cGameStub3D::cGameStub3D(): //The constructor gets called only once, at first start.
	_rivalcount(3) //In the default stub we don't actually use the _rivalcount
{	
	_menuflags &= ~cGame::MENU_2D; 
		//Default _menuflags from cGame has hopper off, autoplay off.  Turn off 2D.
//Usually we set _seedcount.
	_seedcount = 8; //The number of cCritterStub3DProp that I plan to add.
#ifdef DEBUGSTUB3D
	_rivalcount = 0;
	_seedcount = 1; //Don't use 0, as then for some reason player is trapped on one plane.
#endif //DEBUGSTUB3D
//Use the _spritetype in the generic cGame::seedCritters.
	_spritetype = cGame::ST_FANCYPOLYGONS; //cGame::ST_ASSORTED; //cGame::ST_BITMAPS; 
//Fix your statics
	cCritter::MINRADIUS = cGameStub3D::CRITTERMINRADIUS;
	cCritter::MAXRADIUS = cGameStub3D::CRITTERMAXRADIUS;
	cCritter::MAXSPEED = cGameStub3D::CRITTERMAXSPEED;
		/* Customary to set critter speed, usually the
		bigger the world the higher the speed you want. */
//Set your world shape _border.set(dx, dy, dz).  Do this BEFORE you add the player.
	_border.set(28, 20, 20);
//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. */
//Make a player
	setPlayer(new cCritterStub3DPlayer);
//Set wrap with setWrapflag(cCritter::WRAP) or cCritter::BOUNCE.
	setWrapflag(cCritter::BOUNCE);
//Possibly make and add any permanent critters here.
//Set the cursor tools.
	/*_arrayHCURSOR has only the _hCursorArrow inherited from cGame. 
		 Add a Shooting cursor, a Dragging cursor and a Zap for fun. */
	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPlay); 
	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorDragger); 
	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorZap); 
//	_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorSpawn);
}

/* cGame::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 cGameStub3D::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. */
	cGame::seedCritters(); //Uses _spritetype and _seedcount to default seed with cCritter.
		//Later we may want to have this code use cCritterStub3D critters.
	zStackCritters(); /* A cheap trick to scatter the critters through 3D depth, you don't need this
		if you position the critters in 3D your own way. */
}

/* cGameStub3D::reset does not get called at first startup, is called only when you start a
	 new round or 	level of the game.*/
void cGameStub3D::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 cGameStub3D::initializeView(CPopView *pview)
{
	cGame::initializeView(pview); //Always call the baseclass method first.
	pview->setCursor(((CPopApp*)::AfxGetApp())->_hCursorPlay);
		 //Use the shoot cursor
	pview->setUseBackgroundBitmap(FALSE);
	pview->setUseSolidBackground(FALSE);
	pview->setGraphicsClass(RUNTIME_CLASS(cGraphicsOpenGL)); //For 3D games
}

void cGameStub3D::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 up a bit on y, and back off a bit on z.
	else //2D case.
		pviewer->setViewpoint(cVector::ZAXIS, _border.center());
}

void cGameStub3D::drawForeground(cGraphics *pgraphics, int drawflags)
{
	cGame::drawForeground(pgraphics, drawflags);
	if (pgraphics->IsKindOf(RUNTIME_CLASS(cGraphicsOpenGL)))
		drawStaticObjects(pgraphics);
}

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

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

void cGameStub3D::drawStaticObjects(cGraphics *pgraphics)
{
#ifdef SHAPEDEMO
	cGraphicsOpenGL *pgraphicsgl = (cGraphicsOpenGL *)pgraphics;
	if (!pgraphicsgl) //The cast failed as the pgraphics wasn't a cGraphicsOpenGL
		return;

//Tetrahedron
	pgraphicsgl->setMaterialColor(cColorStyle::CN_LIGHTBLUE);
	::glPushMatrix();
	::glTranslatef( 1.0f, -2.0f, 4.0f );
	::glutSolidTetrahedron();
	::glPopMatrix();
//Cube
	pgraphicsgl->setMaterialColor(cColorStyle::CN_LIGHTRED);
	::glutSolidCube(0.75f); //At origin
//Icosahedron
	pgraphicsgl->setMaterialColor(cColorStyle::CN_LIGHTPURPLE);
	::glPushMatrix();
	::glTranslatef( 0.0f, -3.0f, 0.0f );
	::glScaled(1.0, 1.0, 1.0);	
	::glutSolidIcosahedron();
	::glPopMatrix();
//Now draw some curvy shapes
	::glShadeModel(GL_SMOOTH ); 
		/* GL_SMOOTH interpolates from the vertices across a polygon, while
			GL_FLAT picks one vertex and uses that color across the polygon. Default is
			GL_SMOOTH.  Runs about 75% as fast as GL_FLAT. But we'll use it for the
			sake of the curved shapes. */
//Sphere
	pgraphicsgl->setMaterialColor(cColorStyle::CN_LIGHTGREEN);
	::glPushMatrix();
	::glTranslatef( -5.0f, 0.0f, 0.0f );
	::glutSolidSphere(2.0f, 16, 16);
		//Entries are (radius, longitude lines or slices, latitude lines or stacks)
	::glPopMatrix();
//Teapot
	pgraphicsgl->setMaterialColor(cColorStyle::CN_WHITE);
	::glPushMatrix();
	::glTranslatef( 5.0f, 0.5f, 0.0f ); 
	::glutSolidTeapot(3.0);
	::glPopMatrix();
//Torus
	pgraphicsgl->setMaterialColor(cColorStyle::CN_WHITE);
	::glPushMatrix();
	::glTranslatef( 0.0f, -5.0f, 5.0f ); 
	::glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		//Args are: Angle in degrees, x, y, z of the axis to rotate about.
	::glutSolidTorus(1.0, 3.0, 12, 24);
		/* Args are: (innerRadius, outerRadius, sides, rings).
	Meaning of the second two args are as follows.
	The "sides" are the number of sides in a "vertial" torus crosssection as sliced
	through the ring as if making bagel chips, "sides" is also the number of
	latitude lines if we think of the poles located above and below the torus hole.
	The "rings" is the number "vertical" rings around the torus, the "rings" is
	the number of longitude lines, or the "rings" is the nubmer of sides in
	the shape we see if we look down at the torus from above its hole. */
	::glPopMatrix();

	::glShadeModel(GL_FLAT ); 
		/* GL_SMOOTH interpolates from the vertices across a polygon, while
			GL_FLAT picks one vertex and uses that color across the polygon. Default is
			GL_SMOOTH.  Runs about 75% as fast as GL_FLAT. */
#endif //SHAPEDEMO
}

//====================cCritterStub3DPlayer
cCritterStub3DPlayer::cCritterStub3DPlayer()
{
		//Set the listener
	setListener(new cListenerScooter());
		//Set density high so it's not jostled too much.
	setDensity(cCritter::INFINITEDENSITY); // So it's not jostled.
		//Set the bullet
 	setBulletClass(RUNTIME_CLASS(cCritterStub3DPlayerBullet));
	setWaitShoot(2.0*cCritterArmed::WAITSHOOT); //Don't shoot as fast as usual.
		//Set other variables, perhaps including sprite.
	setHealth(cGameStub3D::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.
	setAbsorberflag(TRUE); //Keeps player from being buffeted about.
}

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

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

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

int cCritterStub3DPlayer::damage(int hitstrength)
{
	return cCritterArmedPlayer::damage(hitstrength);
	return 0;
}

//====================cCritterStub3DPlayerBullet==================
cCritterStub3DPlayerBullet::cCritterStub3DPlayerBullet()
{
	//First the base class constructor does its thing.
	//_dieatedges = FALSE; //Can comment this in if you want the bullet to survive past border
}

//==================== cCritterStub3DRival ====================

cCritterStub3DRival::cCritterStub3DRival(cGame *pownergame):
cCritterArmedRobot(pownergame)
{
		//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
	if (pgame())
		setTarget(pplayer()); //Makes it automatically aim at pplayer.
		//Set the bullet
 	setBulletClass(RUNTIME_CLASS(cCritterStub3DRivalBullet));
		//Set wait time between shots
	setWaitShoot(3.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 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(cCritterStub3DPlayerBullet)));
}

int cCritterStub3DRival::damage(int hitstrength)
{
	return cCritter::damage(hitstrength); //Default bevavior.
}

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

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

//====================cCritterStub3DRivalBullet==================
cCritterStub3DRivalBullet::cCritterStub3DRivalBullet()
{
	//The base class constructor does its thing.
}

void cCritterStub3DRivalBullet::initialize(cCritterArmed *pshooter)
{

	cCritterBullet::initialize(pshooter);
	//Make this a chaser bullet.
	addForce(
		new cForceObjectSeek(ptarget(), 
 			-0.3 * cCritterBulletSilverMissile::CHASEACCELERATION)	);
}

//==================== cCritterStub3DProp ====================

cCritterStub3DProp::cCritterStub3DProp(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(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);
		//Maybe Add some forces
}

int cCritterStub3DProp::damage(int hitstrength)
{
	return cCritter::damage(hitstrength); //Default bevavior.
}

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

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