// heroesproto.cpp: implementation of the cGameHeroesOfCIS game class.
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "heroesproto.h"
#include "critterarmed.h"
#include "critterwall.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(cGameHeroesOfCIS, cGame, 0);
IMPLEMENT_SERIAL(cCritterWrenchStatusIcon, cCritter, 0);
IMPLEMENT_SERIAL(cCritterPrintQueueTray, cCritter, 0);
IMPLEMENT_SERIAL(cCritterPrintQueue, cCritter, 0);
IMPLEMENT_SERIAL(cCritterHeroesWall, cCritterWall, 0);
IMPLEMENT_SERIAL(cCritterDoor, cCritter, 0);
IMPLEMENT_SERIAL(cCritterSupplyCabinet, cCritter, 0);
IMPLEMENT_SERIAL(cCritterPrinter, cCritter, 0);
IMPLEMENT_SERIAL(cCritterStudent, cCritter, 0);
IMPLEMENT_SERIAL(cCritterBouncer, cCritter, 0);
IMPLEMENT_SERIAL(cCritterHeroesPlayer, cCritterPlayer, 0);

//==============================cGameHeroesOfCIS

/* Other Class Constants -- Is this really where these belong? */
Real cCritterWrenchStatusIcon::WRENCH_STATUS_ICON_RADIUS = 2.75;
Real cCritterHeroesPlayer::MAX_SPEED = 15.0;
Real cCritterHeroesPlayer::PLAYER_RADIUS = 0.5;
int cCritterHeroesPlayer::SCORE_FIX_PRINTER = 10;
int cCritterHeroesPlayer::SCORE_STOP_BREAKER = 15;
Real cCritterStudent::STUDENT_RADIUS = 0.5;
int cCritterBouncer::SCORE_BOUNCER_LEAVES = 25;
Real cCritterBouncer::BOUNCER_RADIUS = 0.5;
Real cCritterPrinter::PRINTER_RADIUS = .75;
Real cCritterDoor::DOOR_RADIUS = 0.75;
Real cCritterSupplyCabinet::SUPPLY_CABINET_RADIUS = 0.5;
Real cCritterPrintQueue::PRINT_QUEUE_RADIUS = 1.0;
int cCritterPrintQueue::MAX_QUEUE = 10; // There are currently only 10 sprites,
                                        // so this should probably not be larger than 10.
Real cCritterPrintQueueTray::PRINT_QUEUE_TRAY_RADIUS = 0.75;
Real cCritterPrintQueueTray::PRINT_QUEUE_TRAY_OFFSET = 1.5;


/* Game world parameters. */

int cGameHeroesOfCIS::SCORE_LEVEL_MULT = 50;
Real cGameHeroesOfCIS::BLANK_EDGE_WEIGHT = 0.03; 
Real cGameHeroesOfCIS::PLAYFIELD_WEIGHT = 0.7; 
Real cGameHeroesOfCIS::PLAYFIELD_STATUSBOX_SPACER_WEIGHT = 0.03; 
	//Now some statics to define the graphical appearance of the boxes.
Real cGameHeroesOfCIS::BOX_LINEWIDTH_WEIGHT = 0.01; // thickness of the edge lines as a percent of window size.
COLORREF cGameHeroesOfCIS::PLAYFIELD_FILLCOLOR = cColorStyle::CN_LIGHTBLUE;
COLORREF cGameHeroesOfCIS::PLAYFIELD_LINECOLOR = cColorStyle::CN_BLACK;
COLORREF cGameHeroesOfCIS::STATUSBOX_FILLCOLOR = cColorStyle::CN_WHITE;
COLORREF cGameHeroesOfCIS::STATUSBOX_LINECOLOR = cColorStyle::CN_BLACK;

cGameHeroesOfCIS::cGameHeroesOfCIS() //The constructor gets called only once, at first start.
{
	_wrapflag = cCritter::BOUNCE;

	_spritetype = cGame::ST_BUBBLES; //Default sprite
	_menuflags = 0;

	//Fix your statics
	cCritter::MAXSPEED = 4.0; /* Customary to set critter speed, usually the
		bigger the world the higher the speed you want. */

	/**********************
	 * Create game world. *
	 **********************/

    //Set your world shape _border.set(width, height)
	_border.set(28.8, 19.2); //Standard is (14.4, 9.6), can override here

	/*
	 * We use framebox to set our game in a bit from the window border all
	 * the way around.  And then we let the _playfield to be the left part of
	 * the framebox, skip a little space, and put _statusbox in the rest of
	 * the framebox.  We need to prepare _playfield and _statusbox before calling seedBubbles,
	 * as that call will use them.
	 */
	cRealBox framebox = _border.innerBox(cGameHeroesOfCIS::BLANK_EDGE_WEIGHT *
		_border.minsize());
	_playfield = cRealBox(framebox.locorner(),
		cVector(framebox.lox()+PLAYFIELD_WEIGHT*framebox.xsize(), framebox.hiy()));
	_playfield.pcolorstyle()->setFillColor(cGameHeroesOfCIS::PLAYFIELD_FILLCOLOR);
	_playfield.pcolorstyle()->setLineWidthWeight(cGameHeroesOfCIS::BOX_LINEWIDTH_WEIGHT);
	_playfield.pcolorstyle()->setLineColor(cGameHeroesOfCIS::PLAYFIELD_LINECOLOR);
	_statusbox = cRealBox(cVector(_playfield.hix() +
		cGameHeroesOfCIS::PLAYFIELD_STATUSBOX_SPACER_WEIGHT*_border.minsize(), framebox.loy()),
		framebox.hicorner());
	_statusbox.pcolorstyle()->setFillColor(cGameHeroesOfCIS::STATUSBOX_FILLCOLOR);
	_statusbox.pcolorstyle()->setLineWidthWeight(cGameHeroesOfCIS::BOX_LINEWIDTH_WEIGHT);
	_statusbox.pcolorstyle()->setLineColor(cGameHeroesOfCIS::STATUSBOX_LINECOLOR);

	_arrayHCURSOR.SetSize(0); // turn off options menu

//Make a player
	setPlayer(new cCritterHeroesPlayer(this)); //"this" arg sets default player _movebox to match _border.
//Possibly make and add any permanent critters here.

	_level = 1;
}

/*
 * printerExploded checks to see if any printers have printqueues greater than MAXQUEUE
 */
BOOL cGameHeroesOfCIS::printerExploded()
{
	for (int i = 0; i < _printercount; ++i) {
		if (_pprinter[i]->isExploded())
			return TRUE;
	}

	return FALSE;
}

/*
 * randomPrinter returns a pointer to any of the printers in the printer array
 */
cCritterPrinter* cGameHeroesOfCIS::randomPrinter()
{
	ASSERT(_printercount > 0);

	int temp = cRandomizer::pinstance()->random(0, _printercount - 1);
	return _pprinter[temp];
}

/* 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, or if the player advances to a new level.*/
void cGameHeroesOfCIS::seedCritters()
{
	/*
	 * Clean out any old non-player non-wall critters.
	 */
	pbiota()->purgeNonPlayerCritters();
	_printercount = 0;

	/*
	 * Create and place printers and supply cabinet.
	 */
	loadLevel(_level);
}

/* 
 * addPrinter puts pPrinter in the array of printers and increases _printercount by one.
 */
void cGameHeroesOfCIS::addPrinter(cCritterPrinter* pprinter)
{
	if (_printercount < MAX_PRINTERS) {
		_pprinter[_printercount] = pprinter;
		++_printercount;
	}
}

/* 
 * loadLevel creates and positions the printers and print queues for the given level.
 */
void cGameHeroesOfCIS::loadLevel(int level)
{
	// Initialize the wrench status icon
	cCritterWrenchStatusIcon* pwrenchicon = new cCritterWrenchStatusIcon(this);
	((cCritterHeroesPlayer*)pplayer())->setWrenchIcon(pwrenchicon);

	/* Name some variables for legible level creation */
	Real hix = playfield().hix();
	Real hiy = playfield().hiy();
	Real lox = playfield().lox();
	Real loy = playfield().loy();
	Real xsize = playfield().xsize();
	Real ysize = playfield().ysize();

	Real player_r = cCritterHeroesPlayer::PLAYER_RADIUS;
	Real supply_r = cCritterSupplyCabinet::SUPPLY_CABINET_RADIUS;
	Real printer_r = cCritterPrinter::PRINTER_RADIUS;
	Real door_r = cCritterDoor::DOOR_RADIUS;

	Real player_width = player_r * 2;
	Real supply_width = supply_r * 2;
	Real printer_width = printer_r * 2;
	Real door_width = door_r * 2;
	
	switch (level) {
	case 1:
	case 2:
		{
			_leveltime = 60.0;
			Real deskwidth = 1.5;

			_playfield.pcolorstyle()->setFillColor(RGB(77, 136, 92));

			// Initialize the door
			cCritterDoor* pdoor = new cCritterDoor(this);
			pdoor->initializeDoor(0.50, 3);
			pdoor->moveTo(cVector(
				hix - door_r,
				hiy - door_r - player_width));

			// Place a supply cabinet in the lower right
			cCritterSupplyCabinet* pcabinet = new cCritterSupplyCabinet(this);
			pcabinet->moveTo(cVector(
				hix - supply_r, 
				loy + deskwidth + player_width + supply_r));
			pplayer()->moveTo(pcabinet->position());

			// Place Astro in the upper left
			cCritterPrinter* pAstro = new cCritterPrinter(this);
			pAstro->moveTo(cVector(
				lox + printer_r + player_width,
				hiy - printer_r - player_width));
			pAstro->assignPrinterID(cCritterPrinter::PRINTER_ASTRO);
			addPrinter(pAstro);

			// If on level 2, add Clifford in the lower left
			if (level == 2)
			{
				cCritterPrinter* pClifford = new cCritterPrinter(this);
				pClifford->moveTo(cVector(
					lox + printer_r + player_width,
					loy + deskwidth + player_width + printer_r));
				pClifford->assignPrinterID(cCritterPrinter::PRINTER_CLIFFORD);
				addPrinter(pClifford);
			}

			/* Create desk along bottom */
			new cCritterHeroesWall(
					cVector(lox, loy + deskwidth/2),
					cVector(hix, loy + deskwidth/2),
					deskwidth, RGB(159, 84, 34), this);

			/* Create desk on left side */
			new cCritterHeroesWall(
					cVector(lox + deskwidth/2, loy + deskwidth + printer_width + 2 * player_width),
					cVector(lox + deskwidth/2, hiy - printer_width - 2 * player_width),
					deskwidth, RGB(159, 84, 34), this);

			/* Create desk on right side */
			new cCritterHeroesWall(
					cVector(hix - deskwidth/2, loy + deskwidth + supply_width + 2 * player_width),
					cVector(hix - deskwidth/2, hiy - printer_width - 2 * player_width),
					deskwidth, RGB(159, 84, 34), this);

			/* Create table in center */
			new cCritterHeroesWall(
					cVector(lox + xsize/2, loy + ysize/4),
					cVector(lox + xsize/2, hiy - ysize/4),
					xsize/4, RGB(159, 84, 34), this);
		}
		break;

	case 3:
	case 4:
		{
			_leveltime = 90.0;
			Real wallwidth = 1.0;

			_playfield.pcolorstyle()->setFillColor(RGB(104, 119, 176));

			// Place a supply cabinet in the upper right
			cCritterSupplyCabinet* pcabinet = new cCritterSupplyCabinet(this);
			pcabinet->moveTo(cVector(
				lox + supply_r + xsize/3 + player_width + wallwidth/2, 
				hiy - supply_r - player_width - wallwidth));
			pplayer()->moveTo(pcabinet->position());

			// Place a supply cabinet in the lower left
			pcabinet = new cCritterSupplyCabinet(this);
			pcabinet->moveTo(cVector(
				lox + supply_r + player_width, 
				loy + supply_r + player_width + wallwidth));
			pplayer()->moveTo(pcabinet->position());

			// Place Odie in the lower right
			cCritterPrinter* pOdie = new cCritterPrinter(this);
			pOdie->moveTo(cVector(
				hix - printer_r - player_width - wallwidth,
				loy + printer_r + player_width + wallwidth));
			pOdie->assignPrinterID(cCritterPrinter::PRINTER_ODIE);
			addPrinter(pOdie);

			// Place Cujo in the upper right
			cCritterPrinter* pCujo = new cCritterPrinter(this);
			pCujo->moveTo(cVector(
				lox + printer_r + player_width + wallwidth,
				hiy - printer_r - player_width - wallwidth));
			pCujo->assignPrinterID(cCritterPrinter::PRINTER_CUJO);
			addPrinter(pCujo);

			// Place Lassie in the upper left
			cCritterPrinter* pLassie = new cCritterPrinter(this);
			pLassie->moveTo(cVector(
				hix - printer_r - player_width - wallwidth, 
				hiy - printer_r - player_width - wallwidth));
			pLassie->assignPrinterID(cCritterPrinter::PRINTER_LASSIE);
			addPrinter(pLassie);

			// Initialize the door
			cCritterDoor* pdoor = new cCritterDoor(this);
			pdoor->initializeDoor(0.25, 3);
			pdoor->moveTo(cVector(
				lox + door_r + xsize/3 + player_width + wallwidth, 
				loy + door_r));

			if (level == 4)
			{
				// Initialize the door
				pdoor = new cCritterDoor(this);
				pdoor->initializeDoor(0.25, 3);
				pdoor->moveTo(cVector(
					lox - door_r + xsize/3 - player_width - wallwidth, 
					loy + door_r));
			}

			/* Create wall (top half) */
			new cCritterHeroesWall(
					cVector(lox + xsize/3, hiy),
					cVector(lox + xsize/3, hiy - ysize/2),
					wallwidth, RGB(239, 239, 228), this);

			/* Create wall (bottom half) */
			new cCritterHeroesWall(
					cVector(lox + xsize/3, loy),
					cVector(lox + xsize/3, loy + ysize/5),
					wallwidth, RGB(239, 239, 228), this);

			/* Create north wall */
			new cCritterHeroesWall(
					cVector(lox, hiy - wallwidth/2),
					cVector(hix, hiy - wallwidth/2),
					wallwidth, RGB(239, 239, 228), this);

			/* Left lower wall */
			new cCritterHeroesWall(
					cVector(lox, loy + wallwidth/2),
					cVector(lox + xsize/3 - 2*player_width - door_width - wallwidth,
						loy + wallwidth/2),
					wallwidth, RGB(239, 239, 228), this);

			/* Right lower wall */
			new cCritterHeroesWall(
					cVector(lox + xsize/3 + 2*player_width + door_width + wallwidth,
						loy + wallwidth/2),
					cVector(hix, loy + wallwidth/2),
					wallwidth, RGB(239, 239, 228), this);

			/* Create east wall */
			new cCritterHeroesWall(
					cVector(hix - wallwidth/2, hiy),
					cVector(hix - wallwidth/2, loy),
					wallwidth, RGB(239, 239, 228), this);

			/* Create desk */
			new cCritterHeroesWall(
					cVector(hix - xsize/3, hiy - ysize/2),
					cVector(hix - wallwidth, hiy - ysize/2),
					ysize/4, cColorStyle::CN_LIGHTGRAY, this);
		}
		break;

	case 5:
		{
			_leveltime = 120.0;

			_playfield.pcolorstyle()->setFillColor(cColorStyle::CN_BLACK);

			// Place a supply cabinet in the center
			cCritterSupplyCabinet* pcabinet = new cCritterSupplyCabinet(this);
			pcabinet->moveTo(cVector(
				lox + xsize/2, 
				loy + ysize/2));
			pplayer()->moveTo(pcabinet->position());

			// Place Astro in the top center
			cCritterPrinter* pAstro = new cCritterPrinter(this);
			pAstro->moveTo(cVector(
				lox + xsize/2,
				hiy - printer_r - player_width));
			pAstro->assignPrinterID(cCritterPrinter::PRINTER_ASTRO);
			addPrinter(pAstro);

			// Place Odie in the center left
			cCritterPrinter* pOdie = new cCritterPrinter(this);
			pOdie->moveTo(cVector(
				lox + printer_r + player_width,
				loy + ysize/2));
			pOdie->assignPrinterID(cCritterPrinter::PRINTER_ODIE);
			addPrinter(pOdie);

			// Place Lassie in the bottom left
			cCritterPrinter* pLassie = new cCritterPrinter(this);
			pLassie->moveTo(cVector(
				lox + printer_r + player_width,
				loy + printer_r + player_width));
			pLassie->assignPrinterID(cCritterPrinter::PRINTER_LASSIE);
			addPrinter(pLassie);

			// Place Clifford in the center right
			cCritterPrinter* pClifford = new cCritterPrinter(this);
			pClifford->moveTo(cVector(
				hix - printer_r - player_width,
				loy + ysize/2));
			pClifford->assignPrinterID(cCritterPrinter::PRINTER_CLIFFORD);
			addPrinter(pClifford);

			// Place Cujo in the bottom left
			cCritterPrinter* pCujo = new cCritterPrinter(this);
			pCujo->moveTo(cVector(
				hix - printer_r - player_width,
				loy + printer_r + player_width));
			pCujo->assignPrinterID(cCritterPrinter::PRINTER_CUJO);
			addPrinter(pCujo);

			// Initialize the top left door
			cCritterDoor* pdoor = new cCritterDoor(this);
			pdoor->initializeDoor(0.50, 3);
			pdoor->moveTo(cVector(
				lox + door_r + player_width, 
				hiy - door_r - player_width));

			// Initialize the top right door
			pdoor = new cCritterDoor(this);
			pdoor->initializeDoor(0.50, 3);
			pdoor->moveTo(cVector(
				hix - door_r - player_width, 
				hiy - door_r - player_width));

			// Initialize the bottom center door
			pdoor = new cCritterDoor(this);
			pdoor->initializeDoor(0.50, 3);
			pdoor->moveTo(cVector(
				lox + xsize/2, 
				loy + door_r + player_width));
		}
		break;

	default:
		break;
	}
}

/* cGameHeroesOfCIS::start gets called at first startup, not when you start a
	 new round of the game.*/
void cGameHeroesOfCIS::start() 
{
	AfxMessageBox("Starting Level - #1 : LAC Lab", MB_OK | MB_ICONINFORMATION);

	cGame::start(); // Sets _newgame and _gameover to FALSE.
}

/* cGameHeroesOfCIS::reset does not get called at first startup, is called only when you start a
	 new round of the game.*/
void cGameHeroesOfCIS::reset() 
{
	_level = 1;
	AfxMessageBox("Starting Level - #1 : LAC Lab", MB_OK | MB_ICONINFORMATION);

	cGame::reset(); //Calls pplayer()->reset() and seedCritters().
}

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

void cGameHeroesOfCIS::initializeViewpoint(cCritterViewer *pviewer)
{
	/* The two args to setViewpoint are (directiontoviewer, lookatpoint).
	   Note that directiontoviewer points FROM the origin TOWARDS the viewer. */

	if (pviewer->is3D()) {

		//Direction to viewer is down a bit, and back off less than that.
		pviewer->setViewpoint(cVector3(0.0, -1.0, 2.0), _border.center());	
	}
	else {   //2D case.
		pviewer->setViewpoint(cVector::ZAXIS, _border.center());
	}
}

/*
 * Determines the message that will be printed out at the bottom of the window.
 */
CString cGameHeroesOfCIS::statusMessage()
{
	CString cStrStatusBar;
	int nUpdatesPerSecond;
	CString cStrUpdatespersecond;
	CString cStrCount;
	CString cStrScore;
	CString cStrTime;
	CString cStrCollisionCount; //Usually leave empty

	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)";
		if (((CPopApp*)::AfxGetApp())->_timer.runningNearMinSpeed())
			cStrUpdatespersecond += " (Slower than Real Time)";
	}
	else
	cStrUpdatespersecond.Format("Animation is paused.");

	cStrTime.Format("Time Left: %d sec.", int(_leveltime - age())); 
	cStrScore.Format("Score: %d.", score() );
//	cStrCollisionCount.Format("Collide-Checks: %d.", pcollider()->GetSize());
	int studentcount = _pbiota->count(RUNTIME_CLASS(cCritterStudent)) +
				       _pbiota->count(RUNTIME_CLASS(cCritterBouncer));

	cStrCount.Format("Total Students: %d.", studentcount);

	cStrStatusBar = cStrTime + "  " + cStrScore + "  " + cStrCount + "  " + cStrUpdatespersecond + 
		"  " + cStrCollisionCount; 
	return cStrStatusBar;
}

void cGameHeroesOfCIS::adjustGameParameters()
{
// (1) End the game if a printer explodes
	if (printerExploded() && !_gameover) //Printer has exploded and game's not over.
	{
		_gameover = TRUE;

		pplayer()->addScore(_scorecorrection); // So user can reach _maxscore 
		playSound("Pop");
		return;
	}

// (2) After some number of seconds, proceed to the next level.
	if (pplayer()->age() > _leveltime && !_gameover) {
		pplayer()->addScore(_level * SCORE_LEVEL_MULT);
		++_level;
		pplayer()->setAge(0.0);

		if (_level > LAST_LEVEL) {
			_gameover = TRUE;
			playSound("Tada");
			return;
		}

		switch (_level) {
		case 2:
			AfxMessageBox("Level Completed!\nNext Level - #2 : LAC Lab v2.0",
				MB_OK | MB_ICONINFORMATION);
			break;
		case 3:
			AfxMessageBox("Level Completed!\nNext Level - #3 : AC Lab", 
				MB_OK | MB_ICONINFORMATION);
			break;
		case 4:
			AfxMessageBox("Level Completed!\nNext Level - #4 : AC Lab v2.0", 
				MB_OK | MB_ICONINFORMATION);
			break;
		case 5:
			AfxMessageBox("Level Completed!\nNext Level - #5 : CIS NIGHTMARE!", 
				MB_OK | MB_ICONINFORMATION);
			break;
		default:
			break;
		}

		seedCritters();
	}

}

void cGameHeroesOfCIS::drawBackground(cGraphics *pgraphics, int drawflags)
{
    //Draw the fillings of the boxes first, before critters.
	cRealBox2 playfieldfloor(_playfield);
	cRealBox2 statusboxfloor(_statusbox);

	playfieldfloor.pcolorstyle()->setEdged(FALSE);
	playfieldfloor.pcolorstyle()->setFilled(TRUE);
	playfieldfloor.draw(pgraphics, drawflags);

	statusboxfloor.pcolorstyle()->setEdged(FALSE);
	statusboxfloor.pcolorstyle()->setFilled(TRUE);
	statusboxfloor.draw(pgraphics, drawflags);
}

void cGameHeroesOfCIS::drawForeground(cGraphics *pgraphics, int drawflags)
{
//	cGame::drawForeground(pgraphics, drawflags);
	//Don't need the outer border box so don't call the base class drawForeground.
/* Draw the edges of the two boxes, letting them pop into 3D if pgraphics can show it. */
	_statusbox.pcolorstyle()->setEdged(TRUE);
	_statusbox.pcolorstyle()->setFilled(FALSE);
	_playfield.pcolorstyle()->setEdged(TRUE);
	_playfield.pcolorstyle()->setFilled(FALSE);
	_statusbox.draw(pgraphics, drawflags);
	_playfield.draw(pgraphics, drawflags);

}

void cGameHeroesOfCIS::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 status methods------------

int cGameHeroesOfCIS::wrench()
{
	ASSERT(pplayer());
	return ((cCritterHeroesPlayer*)pplayer())->wrench();
}


//==================== cCritterWrenchStatusIcon ====================

cCritterWrenchStatusIcon::cCritterWrenchStatusIcon(cGame *pownergame):
cCritter(pownergame)
{
	// Make status icon immobile and invincible.
	setFixedflag(TRUE);
	setShield(TRUE);

	// Initially, _displayedwrench defaults to -1, so it will be set.
	_displayedwrench = -1;
}

void cCritterWrenchStatusIcon::initializeWrenchStatusIcon()
{
	cRealBox statusbox = ((cGameHeroesOfCIS*)pgame())->statusbox();
	moveTo(
		cVector( statusbox.lox() + statusbox.xsize()/2,
		statusbox.loy() + WRENCH_STATUS_ICON_RADIUS * 1.2));
}

void cCritterWrenchStatusIcon::update(CPopView *pactiveview, Real dt)
{
	/* This code is in place to prevent the meter from rapidly resetting
	   itself to the same graphic. */
	int wrench = ((cGameHeroesOfCIS*)pgame())->wrench();
	if (wrench != _displayedwrench) {
		_displayedwrench = wrench;
		if (wrench > 0)
			setSprite(new cSpriteIcon(IDB_WRENCH));
		else
			setSprite(new cSpriteIcon(IDB_WRENCH_NO));

		setRadius(WRENCH_STATUS_ICON_RADIUS);
	}
}

//==================== cCritterPrintQueueTray ====================

cCritterPrintQueueTray::cCritterPrintQueueTray(cGame *pownergame):
cCritter(pownergame)
{
	// Make print queue tray immobile and invincible.
	setFixedflag(TRUE);
	setShield(TRUE);
}

/*
 * initializePrintQueueTray sets the tray's icon based on the printer
 * it is associated with, and places it a fixed distance below its
 * parent print queue.
 */
void cCritterPrintQueueTray::initializePrintQueueTray(int traySpriteID,
													  cVector queueposition)
{
	setSprite(new cSpriteIcon(traySpriteID));
	setRadius(PRINT_QUEUE_TRAY_RADIUS);

	moveTo(queueposition - cVector(0.0, PRINT_QUEUE_TRAY_OFFSET));
}

//==================== cCritterPrintQueue ====================

cCritterPrintQueue::cCritterPrintQueue(cGame *pownergame):
cCritter(pownergame)
{
	// Make print queues immobile and invincible.
	setFixedflag(TRUE);
	setShield(TRUE);

	// Set print-queue-specific variables.
	_pprintqueuetray = new cCritterPrintQueueTray(pownergame);
	_queue = 0;

	// Initially, _displayedqueue defaults to -1, so it will be set.
	_displayedqueue = -1;
}

/*
 * initializePrintQueue positions the print queue based on the
 * printer it is associated with, and initializes the tray
 * icon as well.
 */
void cCritterPrintQueue::initializePrintQueue(int printerID)
{
	int traySpriteID = IDB_UFO; //IDB_TRAY_UNNAMED;

	cRealBox statusbox = ((cGameHeroesOfCIS*)pgame())->statusbox();
	Real lox = statusbox.lox();
	Real loy = statusbox.loy();
	Real xsize = statusbox.xsize();
	Real ysize = statusbox.ysize();

	switch(printerID) {
	case cCritterPrinter::PRINTER_ODIE:
		moveTo(cVector(
			lox + (1/4.0) * xsize,
			loy + (5/6.0) * ysize));
		traySpriteID = IDB_TRAY_ODIE;
		break;

	case cCritterPrinter::PRINTER_CUJO:
		moveTo(cVector(
			lox + (2/4.0) * xsize,
			loy + (5/6.0) * ysize));
		traySpriteID = IDB_TRAY_CUJO;
		break;

	case cCritterPrinter::PRINTER_LASSIE:
		moveTo(cVector(
			lox + (3/4.0) * xsize,
			loy + (5/6.0) * ysize));
		traySpriteID = IDB_TRAY_LASSIE;
		break;

	case cCritterPrinter::PRINTER_ASTRO:
		moveTo(cVector(
			lox + (1/4.0) * xsize,
			loy + (3/6.0) * ysize));
		traySpriteID = IDB_TRAY_ASTRO;
		break;

	case cCritterPrinter::PRINTER_CLIFFORD:
		moveTo(cVector(
			lox + (2/4.0) * xsize,
			loy + (3/6.0) * ysize));
		traySpriteID = IDB_TRAY_CLIFFORD;
		break;

	case cCritterPrinter::PRINTER_DINO:
		moveTo(cVector(
			lox + (3/4.0) * xsize,
			loy + (3/6.0) * ysize));
		traySpriteID = IDB_SUN; //IDB_TRAY_DINO;
		break;

	default:
		break;
	}

	_pprintqueuetray->initializePrintQueueTray(traySpriteID, position());
}

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

	if (queue() != _displayedqueue){
		switch (queue()) {
		case 0:
			setSprite(new cSpriteIcon(IDB_QUEUE_0));
			break;
		case 1:
			setSprite(new cSpriteIcon(IDB_QUEUE_1));
			break;
		case 2:
			setSprite(new cSpriteIcon(IDB_QUEUE_2));
			break;
		case 3:
			setSprite(new cSpriteIcon(IDB_QUEUE_3)); 
			break;
		case 4:
			setSprite(new cSpriteIcon(IDB_QUEUE_4)); 
			break;
		case 5:
			setSprite(new cSpriteIcon(IDB_QUEUE_5)); 
			break;
		case 6:
			setSprite(new cSpriteIcon(IDB_QUEUE_6)); 
			break;
		case 7:
			setSprite(new cSpriteIcon(IDB_QUEUE_7)); 
			break;
		case 8:
			setSprite(new cSpriteIcon(IDB_QUEUE_8)); 
			break;
		case 9:
			setSprite(new cSpriteIcon(IDB_QUEUE_9)); 
			break;
		case 10:
			setSprite(new cSpriteIcon(IDB_QUEUE_10)); 
			break;
		default:
			break;
		}
		
		setRadius(PRINT_QUEUE_RADIUS);
	}
}

//===================cCritterHeroesWall ====================
cCritterHeroesWall::cCritterHeroesWall(const cVector &enda, const cVector &endb,
	 Real thickness, UINT color, cGame *pownergame):
cCritterWall(enda, endb, thickness, pownergame)
{
	psprite()->setFillColor(color);
}
//==================== cCritterDoor ====================

cCritterDoor::cCritterDoor(cGame *pownergame):
cCritter(pownergame)
{
	// Set critter-specific variables
	setSprite(new cSpriteIcon(IDB_DOOR));
	setRadius(DOOR_RADIUS);

	// Make doors immobile and invincible.
	setFixedflag(TRUE);
	setShield(TRUE);

	_spawnrate = 0.0;
	_spawnmax = 0;
}

void cCritterDoor::initializeDoor(Real spawnrate, int spawnmax)
{
	_spawnrate = spawnrate;
	_spawnmax = spawnmax;
}

void cCritterDoor::spawnStudent()
{
	int temp = cRandomizer::pinstance()->random(0, 5);

	cGameHeroesOfCIS* pheroes = (cGameHeroesOfCIS*)pgame();

	switch (temp) {
	case 0:
	case 1:
	case 2:
		{
			cCritterStudent* pBob = new cCritterStudent(pheroes);
			pBob->initializeStudent(3.0, pheroes->randomPrinter(), FALSE);
			pBob->moveTo(position());
		}
		break;
	case 3:
	case 4:
		{
			cCritterStudent* pBob = new cCritterStudent(pheroes);
			pBob->initializeStudent(4.0, pheroes->randomPrinter(), TRUE);
			pBob->moveTo(position());
		}
		break;
	case 5:
		{
			cCritterBouncer* pJoe = new cCritterBouncer(pheroes);
			pJoe->initializeBouncer(10.0);
			pJoe->moveTo(position());
			pJoe->randomizeVelocity(10.0);
		}
		break;
	default:
		break;
	}
}

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

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

	if (age() >= 2.0) {
		setAge(0.0);

		if (cRandomizer::pinstance()->randomReal(0, 1) > _spawnrate) {
			int temp = cRandomizer::pinstance()->random(1, _spawnmax);
			for (int i = 0; i < temp; ++i) {
				spawnStudent();
			}
		}
	}
}

//==================== cCritterSupplyCabinet ====================

cCritterSupplyCabinet::cCritterSupplyCabinet(cGame *pownergame):
cCritter(pownergame)
{
	// Set critter-specific variables
	setSprite(new cSpriteIcon(IDB_SUPPLY));
	setRadius(SUPPLY_CABINET_RADIUS);

	// Make supply cabinets immobile and invincible.
	setFixedflag(TRUE);
	setShield(TRUE);
}

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

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

//==================== cCritterPrinter ====================

cCritterPrinter::cCritterPrinter(cGame *pownergame):
cCritter(pownergame)
{
	//Make printers immobile and invincible.
	setFixedflag(TRUE);
	setShield(TRUE);

	// Set printer-specific variables
	_printerID = PRINTER_UNNAMED; 
	_pprintqueue = new cCritterPrintQueue(pownergame);
	_broken = FALSE;
	_exploded = FALSE;
}

void cCritterPrinter::assignPrinterID(int printerID)
{
	_printerID = printerID;

	switch(printerID) {
	case PRINTER_ODIE:
		_brokenSpriteID = IDB_ODIE_BROKEN;
		_okSpriteID = IDB_ODIE_OK;
		break;

	case PRINTER_CUJO:
		_brokenSpriteID = IDB_CUJO_BROKEN;
		_okSpriteID = IDB_CUJO_OK;
		break;

	case PRINTER_LASSIE:
		_brokenSpriteID = IDB_LASSIE_BROKEN;
		_okSpriteID = IDB_LASSIE_OK;
		break;

	case PRINTER_ASTRO:
		_brokenSpriteID = IDB_ASTRO_BROKEN;
		_okSpriteID = IDB_ASTRO_OK;
		break;

	case PRINTER_CLIFFORD:
		_brokenSpriteID = IDB_CLIFFORD_BROKEN;
		_okSpriteID = IDB_CLIFFORD_OK;
		break;

	case PRINTER_DINO:
		_brokenSpriteID = IDB_FACE; //IDB_DINO_BROKEN;
		_okSpriteID = IDB_BUM; //IDB_DINO_OK;
		break;

	default:
		_brokenSpriteID = IDB_FACE; //IDB_UNNAMED_BROKEN;
		_okSpriteID = IDB_BUM; //IDB_UNNAMED_OK;
		break;
	}

	_pprintqueue->initializePrintQueue(printerID);

	setSprite(new cSpriteIcon(_okSpriteID));
	setRadius(PRINTER_RADIUS);
}

void cCritterPrinter::breakMe()
{
	if (!isBroken())
	{
		playSound("Explosion");
		_broken = TRUE;

		setSprite(new cSpriteIcon(_brokenSpriteID));
		setRadius(PRINTER_RADIUS);

		setAge(0.0);
	}
}

void cCritterPrinter::fixMe()
{
	if (isBroken())
	{
		_broken = FALSE;

		setSprite(new cSpriteIcon(_okSpriteID));
		setRadius(PRINTER_RADIUS);

		setAge(0.0);
	}
}

void cCritterPrinter::explodeMe()
{
	if (!isExploded())
	{
		_exploded = TRUE;
		setSprite(new cSpriteIcon(IDB_ATOMIC));
		setRadius(PRINTER_RADIUS);
	}

}

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

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

	if (isBroken() && age() >= 1.0) {
		setAge(0.0);
		pprintqueue()->incQueue(1);
		if (pprintqueue()->queueOverflow()) {
			explodeMe();
		}
	}
	if (!isBroken() && age() >= 3.0) {
		setAge(0.0);
		pprintqueue()->decQueue(1);

		if (cRandomizer::pinstance()->randomReal(0, 1) > 0.75)
			breakMe();
	}
}

//==================== cCritterStudent ====================

cCritterStudent::cCritterStudent(cGame *pownergame):
cCritter(pownergame)
{
	// Make students invincible.
	setShield(TRUE);

	// Constrain student movement to playfield.
    setMoveBox(((cGameHeroesOfCIS*)pownergame)->playfield());

	// Uninitiallized students have no targets
	_target = NULL;
}

void cCritterStudent::initializeStudent(
		Real speed, cCritterPrinter* target, BOOL breaker)
{
	setMaxspeed(speed);

	_target = target;
	addForce(new cForceObjectSeek(target, speed));

	_breaker = breaker;
	if (breaker)
		setSprite(new cSpriteIcon(IDB_STUDENT_BAD));
	else
		setSprite(new cSpriteIcon(IDB_STUDENT_GOOD));
	setRadius(STUDENT_RADIUS);
}

void cCritterStudent::setBreaker(BOOL breaker)
{
	_breaker = breaker;
	if (breaker)
		setSprite(new cSpriteIcon(IDB_STUDENT_BAD));
	else
		setSprite(new cSpriteIcon(IDB_STUDENT_GOOD));
	setRadius(STUDENT_RADIUS);
}

BOOL cCritterStudent::collide(cCritter *pcritter)
{
	BOOL collideflag = cCritter::collide(pcritter);

	if (collideflag && pcritter == _target) {
		if(breaker() && !_target->isBroken())
			_target->breakMe();

		setBreaker(FALSE);
//		_target = NULL;
		clearForcearray();
		cCritter* pcritter =
			pgame()->pbiota()->pickClosestCritter(this, RUNTIME_CLASS(cCritterDoor));
		addForce(new cForceObjectSeek(pcritter, maxspeed()));
	}

	if (collideflag && pcritter->IsKindOf(RUNTIME_CLASS(cCritterDoor)) && age() > 2.0) {
		die();
	}

	return collideflag;
}

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

//==================== cCritterBouncer ====================

cCritterBouncer::cCritterBouncer(cGame *pownergame):
cCritter(pownergame)
{
	// Set critter-specific variables
	setSprite(new cSpriteIcon(IDB_STUDENT_CRAZY));
	setRadius(BOUNCER_RADIUS);
	setMaxspeed(0.0);

	// Make students invincible.
	setShield(TRUE);

	// Constrain student movement to playfield.
    setMoveBox(((cGameHeroesOfCIS*)pownergame)->playfield());

	_leaving = FALSE;
}

void cCritterBouncer::initializeBouncer(Real speed)
{
	setMaxspeed(speed);
}

BOOL cCritterBouncer::collide(cCritter *pcritter)
{
	BOOL collideflag = cCritter::collide(pcritter);

	if (collideflag && pcritter->IsKindOf(RUNTIME_CLASS(cCritterDoor)) && age() > 2.0) {
		if (!leaving())
			pgame()->pplayer()->addScore(SCORE_BOUNCER_LEAVES);
		die();
	}

	return collideflag;
}

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

	if (age() < 15.0)
		setSpeed(maxspeed());
	else if (!_leaving) {
		_leaving = TRUE;
		cCritter* pcritter =
			pgame()->pbiota()->pickClosestCritter(this, RUNTIME_CLASS(cCritterDoor));
		addForce(new cForceObjectSeek(pcritter, maxspeed()));

		setSprite(new cSpriteIcon(IDB_STUDENT_CALMER));
		setRadius(BOUNCER_RADIUS);
	}
}

//==================== cCritterHeroesPlayer ======================

cCritterHeroesPlayer::cCritterHeroesPlayer(cGame *pownergame):
cCritterPlayer(pownergame)
{
	_wrench = 0;

	//Set the listener
	setListener(new cListenerArrow());

	setMaxspeed(MAX_SPEED);
	setRadius(PLAYER_RADIUS);

	setArmed(FALSE); // Don't allow player to shoot

	/* Constrain player movement to playfield. */
    setMoveBox(((cGameHeroesOfCIS*)pownergame)->playfield());

	/* Force player to point upwards. */
	setAttitudeToMotionLock(FALSE);
	setAttitudeTangent(cVector::YAXIS);

	// Set starting sprite (sprite will later change based on direction of player movement)
	setSprite(new cSpriteIcon(IDB_GUY_DOWN));
}

void cCritterHeroesPlayer::setWrenchIcon(cCritterWrenchStatusIcon* pwrenchicon)
{
	_pwrenchicon = pwrenchicon;
	pwrenchicon->initializeWrenchStatusIcon();
}

void cCritterHeroesPlayer::reset()
{
	cCritterPlayer::reset();

	_wrench = 0;
}

BOOL cCritterHeroesPlayer::collide(cCritter *pcritter)
{
	BOOL collideflag = cCritter::collide(pcritter);

	/* Handle collisions with supply cabinets. */
	if (collideflag && pcritter->IsKindOf(RUNTIME_CLASS(cCritterSupplyCabinet))) {
		_wrench = 1;
	}

	/* Handle collisions with printers. */
	if (collideflag && pcritter->IsKindOf(RUNTIME_CLASS(cCritterPrinter))) {
		cCritterPrinter* pprinter = (cCritterPrinter*)pcritter;
		if(pprinter->isBroken() && _wrench > 0)
		{
			pprinter->fixMe();
			_wrench = 0;
			_score += SCORE_FIX_PRINTER;
		}
	}
	
	/* Handle collisions with students. */
	if (collideflag && pcritter->IsKindOf(RUNTIME_CLASS(cCritterStudent))) {
		cCritterStudent* pstudent = (cCritterStudent*)pcritter;
		if (pstudent->breaker()){
			pstudent->setBreaker(FALSE);
			_score += SCORE_STOP_BREAKER;
		}
	}
	if (collideflag && pcritter->IsKindOf(RUNTIME_CLASS(cCritterBouncer))
		  && !((cCritterBouncer*)pcritter)->leaving() ) {
		_wrench = 0;
	}

	return collideflag;
}

void cCritterHeroesPlayer::update(CPopView *pactiveview, Real dt)
{
	cCritterPlayer::update(pactiveview, dt); //Always call this first

	/*
	// Change player's icon based on the direction he is facing
	cVector facing = velocity();
	if (facing.x() == 0 && facing.y() > 0) {
		setSprite(new cSpriteIcon(IDB_GUY_UP));
	}
	if (facing.x() == 0 && facing.y() < 0) {
		setSprite(new cSpriteIcon(IDB_GUY_DOWN));
	}
	if (facing.y() == 0 && facing.x() > 0) {
		setSprite(new cSpriteIcon(IDB_GUY_RIGHT));
	}
	if (facing.y() == 0 && facing.x() < 0) {
		setSprite(new cSpriteIcon(IDB_GUY_LEFT));
	}
	*/
}
