#include <OpenGL/gl.h>
#include <math.h>
#include "text.h"
#include "entities.h"
#include "config.h"
#include "carphysics.h"
#include "gameframe.h"
#include "gamesystem.h"
#include "interfaceutil.h"
#include "rendercar.h"
#include "renderframe.h"
#include "environment.h"
#include "textures.h"
#include "controls.h"
#include "networkphysics.h"
#include "screen.h"
#include "gametime.h"
#include "network.h"
#include "tracker.h"

#define kOverviewSize 0.22
#define kDotSize 0.01

#define kWhiteDotFile "whitedot.pct"
#define kWhiteDiamondFile "whitediamond.pct"

#define kCornerSignSize 0.1
#define kCornerSignYPos 0.28

void RenderCornerSign()
{
	gTexturesQualityModifier=-255;
	float corner=(fabs(gAccelSignDisplayCorner)-3)/5;
	if(corner<0)corner=0;
	if(corner>1)corner=1;
	
	float r=0.5+0.5*corner;
	float g=1.0-corner;
	float b=0;
	float a=1;
	if(gAccelSignDisplayIntensity>1.3)
		a=1-(gAccelSignDisplayIntensity-1.3)/0.2;
	else if(gAccelSignDisplayIntensity<0.8)
		a=gAccelSignDisplayIntensity/0.8;
	
	if(!gConfig->guideSigns)
		a=0;
		
	TexturesSelectTex(FileGetReference("cornersign.pct"));

	tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics;
	if(phys->wrongDriectionFrames*kFrameTime>2)
	{
		a=(phys->wrongDriectionFrames*kFrameTime-2)*0.3>1?1:(phys->wrongDriectionFrames*kFrameTime-2)*0.3;
		r=1;
		g=0;
		TexturesSelectTex(FileGetReference("wrongdirsign.pct"));
	}
	if(gFrameCount-phys->crashTime>kFPS*1)
		if(sqr(gViewedEntity->velo)<sqr(2)||(phys->collision&&sqr(gViewedEntity->velo)<sqr(15)))
		{
			a=((gFrameCount-phys->crashTime)*kFrameTime-1)*0.5>1?1:((gFrameCount-phys->crashTime)*kFrameTime-1)*0.5;
			r=1;
			g=0.5;
			TexturesSelectTex(FileGetReference("towsign.pct"));
		}

	if(a==0)
		return;
		
	glPushMatrix();

	glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glEnable(GL_BLEND);									
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-1.0f);
	glTranslatef(0,kCornerSignYPos,0);
	glScalef(kCornerSignSize*0.5,kCornerSignSize*0.5,0);

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

	glColor4f(r,g,b,a*(1-gConfig->hudTransparency));
	glBegin(GL_TRIANGLE_STRIP);								
	if(gAccelSignDisplayCorner<0)
	{
		glTexCoord2d(1,1); glVertex2f(1,-1);		
		glTexCoord2d(1,0); glVertex2f(1,1); 	
		glTexCoord2d(0,1); glVertex2f(-1,-1); 	
		glTexCoord2d(0,0); glVertex2f(-1,1); 	
	}
	else
	{
		glTexCoord2d(0,1); glVertex2f(1,-1);		
		glTexCoord2d(0,0); glVertex2f(1,1); 	
		glTexCoord2d(1,1); glVertex2f(-1,-1); 	
		glTexCoord2d(1,0); glVertex2f(-1,1); 	
	}
	glEnd();
		
	glPopAttrib();
	glPopMatrix();
	gTexturesQualityModifier=0;
}
#include "random.h"
void RenderOverview(float opacity,float x,float y)
{
	if(opacity==0)
		return;
	if(!gMapInfo->hasOverview)
		return;
	glPushMatrix();
	gTexturesQualityModifier=-255;

	glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glEnable(GL_BLEND);									
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

	float ratio=(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x);
	glColor4f(gEnvironment->instrumentColor.x,gEnvironment->instrumentColor.y,gEnvironment->instrumentColor.z,(1-gConfig->hudTransparency)*opacity);

	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-1.0f);
	glTranslatef(x,y,0);
	glScalef(kOverviewSize*0.5,kOverviewSize*ratio*0.5,0);
	
	TexturesSelectTex(gMapInfo->overview);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

	glBegin(GL_TRIANGLE_STRIP);								
	glTexCoord2d(1,1); glVertex2f(1,-2);		
	glTexCoord2d(1,0); glVertex2f(1,0); 	
	glTexCoord2d(0,1); glVertex2f(-1,-2); 	
	glTexCoord2d(0,0); glVertex2f(-1,0); 	
	glEnd();
	
	TexturesSelectTex(FileGetReference(kWhiteDotFile));
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);


	for(int i=0;i<gGameInfo->numPlayers;i++)
		if(gCarEntities[i]!=gCarEntities[gReplayViewedEntityID])
		{
			float posX=(gCarEntities[i]->pos.x-gMapInfo->overviewTopLeft.x)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x)-0.5;
			float posY=(gCarEntities[i]->pos.z-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y);
			glLoadIdentity();
			glTranslatef(0.0f,0.0f,-1.0f);
			glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0);
			glScalef(kDotSize*0.5,kDotSize*0.5,0);
			
			tCarPhysics *phys=(tCarPhysics*)(gCarEntities[i]->physics);
			tCarDefinition *car=&(phys->car);
			if(car->numColors)
				glColor4f(car->colors[phys->color].x,car->colors[phys->color].y,car->colors[phys->color].z,(1-gConfig->hudTransparency)*opacity);
			else
				glColor4f(1,1,1,1);		
			
			glBegin(GL_TRIANGLE_STRIP);								
			glTexCoord2d(1,1); glVertex2f(1,-1);		
			glTexCoord2d(1,0); glVertex2f(1,1); 	
			glTexCoord2d(0,1); glVertex2f(-1,-1); 	
			glTexCoord2d(0,0); glVertex2f(-1,1); 	
			glEnd();	
		}	
	glColor4f(1,1,1,1);	
	if(gGameInfo->numLaps==-1)
	{
		glColor4f(gEnvironment->instrumentColor.x,gEnvironment->instrumentColor.y,gEnvironment->instrumentColor.z,(1-gConfig->hudTransparency)*opacity);
		float posX=(gGhostEntity->pos.x-gMapInfo->overviewTopLeft.x)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x)-0.5;
		float posY=(gGhostEntity->pos.z-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y);
		glLoadIdentity();
		glTranslatef(0.0f,0.0f,-1.0f);
		glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0);
		glScalef(kDotSize*0.5,kDotSize*0.5,0);
				
		glBegin(GL_TRIANGLE_STRIP);								
		glTexCoord2d(1,1); glVertex2f(1,-1);		
		glTexCoord2d(1,0); glVertex2f(1,1); 	
		glTexCoord2d(0,1); glVertex2f(-1,-1); 	
		glTexCoord2d(0,0); glVertex2f(-1,1); 	
		glEnd();	
	}

	TexturesSelectTex(FileGetReference(kWhiteDiamondFile));
//	TexturesSelectTex(FileGetReference("dot2.pct"));
	float posX=(gCarEntities[gReplayViewedEntityID]->pos.x-gMapInfo->overviewTopLeft.x)/(gMapInfo->overviewBotRight.x-gMapInfo->overviewTopLeft.x)-0.5;
	float posY=(gCarEntities[gReplayViewedEntityID]->pos.z-gMapInfo->overviewTopLeft.y)/(gMapInfo->overviewBotRight.y-gMapInfo->overviewTopLeft.y);
	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-1.0f);
	glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0);
	glScalef(kDotSize*1*0.5,kDotSize*1*0.5,0);
	
/*	glColor4f(RandomFl(0,1),RandomFl(0,1),RandomFl(0,1),1);		
	glBegin(GL_TRIANGLE_STRIP);								
	glTexCoord2d(1,1); glVertex2f(1,-1);		
	glTexCoord2d(1,0); glVertex2f(1,1); 	
	glTexCoord2d(0,1); glVertex2f(-1,-1); 	
	glTexCoord2d(0,0); glVertex2f(-1,1); 	
	glEnd();*/	

	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-1.0f);
	glTranslatef(x+posX*kOverviewSize,y-posY*kOverviewSize*ratio,0);
	glScalef(kDotSize*2*0.5,kDotSize*2*0.5,0);
	
	tCarPhysics *phys=(tCarPhysics*)(gCarEntities[gReplayViewedEntityID]->physics);
	tCarDefinition *car=&(phys->car);
	if(car->numColors)
		glColor4f(car->colors[phys->color].x,car->colors[phys->color].y,car->colors[phys->color].z,(1-gConfig->hudTransparency)*opacity);
	else
		glColor4f(1,1,1,1);		
	
	glBegin(GL_TRIANGLE_STRIP);								
	glTexCoord2d(1,1); glVertex2f(1,-1);		
	glTexCoord2d(1,0); glVertex2f(1,1); 	
	glTexCoord2d(0,1); glVertex2f(-1,-1); 	
	glTexCoord2d(0,0); glVertex2f(-1,1); 	
	glEnd();	
	
	glPopAttrib();
	glPopMatrix();
	gTexturesQualityModifier=0;
}

#include "error.h"
//display in-game stats
void RaceInfoDisplay(float inOpacity)
{
	if(!gGameInfo->demolition)
	{
		tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics;

		if(inOpacity==0)
			return;
		//if someone has finished the game, print a message on screen
		if(gDisqualified)
			TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,inOpacity,"Disqualified!");

		//if the race has started, print statistics
		float opacity=inOpacity*(gFrameCount*kFrameTime-kStartGameDelaySeconds);
		if(gGameInfo->maxTime!=0)opacity=inOpacity;
		if(opacity>inOpacity)opacity=inOpacity;
		if(opacity<0)opacity=0;
		
		RenderOverview(opacity,0.42,0.3);
		RenderCornerSign();
		
		if(gMapInfo->loop&&gGameInfo->maxTime==0)
			if(gGameInfo->numLaps!=-1)
			{
				//print last lap time
				float lastLapTime=phys->lapCount>1?(phys->lapTimes[phys->lapCount-1]-phys->lapTimes[phys->lapCount-2])*kFrameTime:0;
				TextPrintfToBufferFormatedColored(Vector(-1,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"Last Lap: %d:%02d'%02d",((int)lastLapTime)/60,((int)lastLapTime)%60,((int)(lastLapTime*100))%100);
						
				//print lap counter
				if(phys->lapCount<=gGameInfo->numLaps)
					TextPrintfToBufferFormatedColored(Vector(1,1),0.05,kTextAlignRight,1,1,1,opacity,"Lap: %d/%d",phys->lapCount,gGameInfo->numLaps);
				else
					TextPrintfToBufferFormatedColored(Vector(1,1),0.05,kTextAlignRight,1,1,1,opacity,"Lap: %d/%d",gGameInfo->numLaps,gGameInfo->numLaps);
			}
			else
			{
				//print last lap time
				float lastLapTime=gLastLapTime*kFrameTime;
				TextPrintfToBufferFormatedColored(Vector(-1,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"Last Lap: %d:%02d'%02d",((int)lastLapTime)/60,((int)lastLapTime)%60,((int)(lastLapTime*100))%100);
				float bestLapTime=gBestLapTime*kFrameTime;
				TextPrintfToBufferFormatedColored(Vector(-1,0.84),0.03,kTextAlignLeft,1,1,1,opacity,"Best Lap: %d:%02d'%02d",((int)bestLapTime)/60,((int)bestLapTime)%60,((int)(bestLapTime*100))%100);
				float offset=gConfig->registerLapTimes?0.32:0.24;
				if(gConfig->registerLapTimes)
					if(gWorldRecord!=-1)
					{
						float worldRecord=gWorldRecord*kFrameTime;
						if(worldRecord)
						{
							TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: %d:%02d'%02d (set by %s\255#n\255)",((int)worldRecord)/60,((int)worldRecord)%60,((int)(worldRecord*100))%100,gRecordName);
//							TextPrintfToBufferFormatedColored(Vector(-0.95,0.68),0.03,kTextAlignLeft,1,1,1,opacity,"set by: %s",gRecordName);
						}
						else if(gWaitingForRecord)
							TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: not set");								
						else
							TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: unavailable");	
					}		
					else
						TextPrintfToBufferFormatedColored(Vector(-1,0.76),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: Fetching...");

				float localRecord=gLocalRecord*kFrameTime;
				if(localRecord==0)
					TextPrintfToBufferFormatedColored(Vector(-1.0,1.0-offset),0.03,kTextAlignLeft,1,1,1,opacity,"Personal Record: not set");
				else
					TextPrintfToBufferFormatedColored(Vector(-1.0,1.0-offset),0.03,kTextAlignLeft,1,1,1,opacity,"Personal Record: %d:%02d'%02d",((int)localRecord)/60,((int)localRecord)%60,((int)(localRecord*100))%100);

				//print lap counter
				TextPrintfToBufferFormatedColored(Vector(1,1),0.05,kTextAlignRight,1,1,1,opacity,"Lap: %d",phys->lapCount);
			}
		
		//print current lap time
		if(gGameInfo->maxTime==0)
		{
			float lapTime=phys->lapCount?(gFrameCount-(gGameInfo->numLaps!=-1?(phys->lapTimes[phys->lapCount-1]):gCurrentLapStart))*kFrameTime:0;
			TextPrintfToBufferFormatedColored(Vector(-1,1),0.03,kTextAlignLeft,1,1,1,opacity,"This Lap: %d:%02d'%02d",((int)lapTime)/60,((int)lapTime)%60,((int)(lapTime*100))%100);
		}
		else
		{
			float timeTaken=gFrameCount*kFrameTime-kStartGameDelaySeconds;
			if(timeTaken<0)
				timeTaken=0;
			TextPrintfToBufferFormatedColored(Vector(-1,1),0.03,kTextAlignLeft,1,1,1,opacity,"Time Taken: %d:%02d'%02d",((int)timeTaken)/60,((int)timeTaken)%60,((int)(timeTaken*100))%100);
			float offset=gConfig->registerLapTimes?0.16:0.08;
			if(gConfig->registerLapTimes)
				if(gWorldRecord!=-1)
				{
					float worldRecord=gWorldRecord*kFrameTime;
					if(worldRecord==0)
						TextPrintfToBufferFormatedColored(Vector(-1,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: not set");							
					else if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))<worldRecord)
						TextPrintfToBufferFormatedColored(Vector(-1,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: %d:%02d'%02d (set by %s\255#n\255)",((int)worldRecord)/60,((int)worldRecord)%60,((int)(worldRecord*100))%100,gRecordName);
					else
						TextPrintfToBufferFormatedColored(Vector(-1.0,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: failed");					
				}		
				else
					TextPrintfToBufferFormatedColored(Vector(-1.0,0.92),0.03,kTextAlignLeft,1,1,1,opacity,"World Record: Fetching...");	
			float localRecord=gLocalRecord*kFrameTime;
			if(localRecord==0)
				TextPrintfToBufferFormatedColored(Vector(-1.0,1.0-offset),0.03,kTextAlignLeft,1,1,1,opacity,"Personal Record: not set");
			else if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))<localRecord)
				TextPrintfToBufferFormatedColored(Vector(-1.0,1.0-offset),0.03,kTextAlignLeft,1,1,1,opacity,"Personal Record: %d:%02d'%02d",((int)localRecord)/60,((int)localRecord)%60,((int)(localRecord*100))%100);
			else
				TextPrintfToBufferFormatedColored(Vector(-1.0,1.0-offset),0.03,kTextAlignLeft,1,1,1,opacity,"Personal Record: failed");
				
			if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))<gGameInfo->goldTime)
				TextPrintfToBufferFormatedColored(Vector(-1.0,0.92-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255goldmedal.pct\255: %d:%02d'%02d",((int)gGameInfo->goldTime)/60,((int)gGameInfo->goldTime)%60,((int)(gGameInfo->goldTime*100))%100);
			else
				TextPrintfToBufferFormatedColored(Vector(-1.0,0.92-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255goldmedal.pct\255: failed");
			if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))<gGameInfo->silverTime)
				TextPrintfToBufferFormatedColored(Vector(-1.0,0.84-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255silvermedal.pct\255: %d:%02d'%02d",((int)gGameInfo->silverTime)/60,((int)gGameInfo->silverTime)%60,((int)(gGameInfo->silverTime*100))%100);
			else
				TextPrintfToBufferFormatedColored(Vector(-1.0,0.84-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255silvermedal.pct\255: failed");
			if((phys->lapCount<=1?timeTaken:(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds))<gGameInfo->maxTime)
				TextPrintfToBufferFormatedColored(Vector(-1.0,0.76-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255bronzemedal.pct\255: %d:%02d'%02d",((int)gGameInfo->maxTime)/60,((int)gGameInfo->maxTime)%60,((int)(gGameInfo->maxTime*100))%100);
			else
			{
				TextPrintfToBufferFormatedColored(Vector(-1.0,0.76-offset),0.03,kTextAlignLeft,1,1,1,opacity,"\255bronzemedal.pct\255: failed");
				if(!gDisqualified)
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!");
			}
		}
		
		//print lead/trail
		if(gGameInfo->maxTime==0)
		{
			tVector2 leadPos=Vector(-1,gGameInfo->numLaps!=-1?0.84:(gConfig->registerLapTimes?0.6:0.68));
			if(phys->lead>=0)
				TextPrintfToBufferFormatedColored(leadPos,0.03,kTextAlignLeft,1,1,1,opacity,"Lead: %3.1f",phys->lead);
			else
				TextPrintfToBufferFormatedColored(leadPos,0.03,kTextAlignLeft,1,1,1,opacity,"Trail: %3.1f",-phys->lead);
			
			if(gGameInfo->numPlayers>1&&gGameInfo->numLaps!=-1)
				for(int i=0;i<gGameInfo->numPlayers;i++)
				{
					tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics;
					if(phys2->lead>=0&&phys->lead<0)
					{
						if(gGameInfo->network)
							TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Leader: %s",gGameInfo->playerNames[i]);						
						else
							TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Leader: %s",phys2->car.carName);						
						break;
					}
					else if(phys2->lead==-phys->lead)
					{
						if(gGameInfo->network)
							TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Follower: %s",gGameInfo->playerNames[i]);						
						else
							TextPrintfToBufferFormatedColored(leadPos-Vector(0,0.08),0.03,kTextAlignLeft,1,1,1,opacity,"Follower: %s",phys2->car.carName);						
						break;
					}
				}
			
			if(gGameInfo->numLaps!=-1)
			{
				/*if(GetInterfaceKey(kInterfaceKeyEasterEgg))
				{
					for(int i=0;i<gGameInfo->numPlayers;i++)
					{
						if(gCarEntities[i]->id>=0)
						{
							tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics;
							PrintConsoleString("%s: lap %d,pos %f, lead %f",FileGetName(gGameInfo->playerCars[i]),phys2->lap,phys2->position,phys2->lead);
						}
					}
				}*/
				//print position
				int position=0;
				for(int i=0;i<gGameInfo->numPlayers;i++)
				{
					if(gCarEntities[i]->id>=0)
					{
						tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics;
						if(phys2->lap>phys->lap)position++;
						else if(gGameInfo->reverse)
							{if(phys2->lap==phys->lap&&phys2->position<=phys->position)position++;}
						else
							{if(phys2->lap==phys->lap&&phys2->position>=phys->position)position++;}
					}
				}
				int numPlayers=0;
				for(int i=0;i<gGameInfo->numPlayers;i++)
					if(gCarEntities[i]->id>=0)
						numPlayers++;
				TextPrintfToBufferFormatedColored(Vector(1,0.9),0.05,kTextAlignRight,1,1,1,opacity,"Position: %d/%d",position,numPlayers);
			}
		}
				
/*			if(phys->arcadeDraftBoost>0.2)
		{
			float a=phys->arcadeDraftBoost;
			TextPrintfToBufferFormatedColored(Vector(0,0.7),0.09,kTextAlignMiddle,0,1,0,a,"Drafting");			
		}*/
		
		if(!gReplay)
		//if the race has not yet started print countdown
			if(gFrameCount*kFrameTime<kStartGameDelaySeconds+1)
				if(gFrameCount*kFrameTime>=kStartGameDelaySeconds)
					TextPrintfToBufferFormatedColored(Vector(0,0.15),0.2+0.1*(gFrameCount%(int)kFPS)/kFPS,kTextAlignMiddle,1,1,1,inOpacity*(1-(gFrameCount%(int)kFPS)/kFPS),"GO!");		
				else if(gFrameCount*kFrameTime>=kStartGameDelaySeconds-3)
					TextPrintfToBufferFormatedColored(Vector(0,0.15),0.05+0.02*(gFrameCount/kFPS)+0.1*(gFrameCount%(int)kFPS)/kFPS,kTextAlignMiddle,1,1,1,inOpacity*(1-(gFrameCount%(int)kFPS)/kFPS),"%d",(int)kStartGameDelaySeconds-gFrameCount/(int)kFPS);
	}
	else
	{
		int numDisabled=0;
		for(int i=0;i<gGameInfo->numPlayers;i++)
		{
			if(gCarEntities[i]->id>=0)
			{
				tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
				if(phys->damage>=kFatalDamage)
					numDisabled++;
			}
		}
		if(numDisabled>0&&numDisabled>=gGameInfo->numPlayers-1)
		{
			if(numDisabled==gGameInfo->numPlayers)
				TextPrintfToBufferFormatedColored(Vector(0,0.2),0.1,kTextAlignMiddle,1,1,1,inOpacity,"Everyone wrecked!");
			for(int i=0;i<gGameInfo->numPlayers;i++)
			{
				if(gCarEntities[i]->id>=0)
				{
					tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
					if(phys->damage<kFatalDamage)
						TextPrintfToBufferFormatedColored(Vector(0,0.2),0.1,kTextAlignMiddle,1,1,1,inOpacity,"%s \255#w\255won!",gGameInfo->playerNames[i]);
				}
			}
		}
		else 		
		{
			tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics;
			if(phys->damage>=kFatalDamage)
				TextPrintfToBufferFormatedColored(Vector(0,0.2),0.1,kTextAlignMiddle,1,1,1,inOpacity,"You're wrecked!");
			TextPrintfToBufferFormatedColored(Vector(-1,1),0.03,kTextAlignLeft,1,1,1,inOpacity,"Wrecked: %d/%d",numDisabled,gGameInfo->numPlayers);
		}
	}
}
/*
void SpeedStatsDisplay(float opacity)
{
	tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics;
	
	//Print Top Speed and average speed.
	if(gConfig->metricUnits){
		TextPrintfToBufferFormatedColored(Vector(0,-0.75),0.03,kTextAlignMiddle,1,1,1,opacity,"Top Speed: %3.0f km/h",phys->maxSpeed*3.6);
		TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.03,kTextAlignMiddle,1,1,1,opacity,"Average Speed: %3.0f km/h",phys->averageSpeed*3.6);
	}
	else{
		TextPrintfToBufferFormatedColored(Vector(0,-0.75),0.03,kTextAlignMiddle,1,1,1,opacity,"Top Speed: %3.0f M.P.H.",phys->maxSpeed*2.2369);
		TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.03,kTextAlignMiddle,1,1,1,opacity,"Average Speed: %3.0f M.P.H.",phys->averageSpeed*2.2369);
	}
}

void BestLapsDisplay(float opacity)
{
	if(gMapInfo->loop)
	{
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics;
		//print 5 best laps of player
		TextPrintfToBufferFormatedColored(Vector(-0.5,0.5),0.03,kTextAlignLeft,1,1,1,opacity,"Best Laps:");
		int bestLaps[5];
		for(int i=0;i<5&&i<gGameInfo->numLaps;i++)
		{
			float bestLapTime=INFINITY;
			bestLaps[i]=0;
			for(int j=0;j<gGameInfo->numLaps;j++)
			{
				float lapTime=(phys->lapTimes[j+1]-phys->lapTimes[j]);
				if(lapTime<=0)
					lapTime=10000000;
				if(lapTime<bestLapTime)
				{
					int check=true;
					for(int k=0;k<i;k++)
						if(bestLaps[k]==j)
							check=false;
					if(check){
						bestLapTime=lapTime;
						bestLaps[i]=j;
					}
				}
			}
			char str[8];
			MakeNumString(bestLaps[i]+1,str);
			if(phys->lapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]]>0)
			{
				float bestTime=(phys->lapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]])*kFrameTime;
				TextPrintfToBufferFormatedColored(Vector(-0.5,0.4-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s Lap: %d:%02d'%02d",str,((int)bestTime)/60,((int)bestTime)%60,((int)(bestTime*100))%100);
			}
			else
				TextPrintfToBufferFormatedColored(Vector(-0.5,0.4-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s Lap: Retired",str);
		}	
	}
}

void BestDriversDisplay(float opacity)
{
	//print best players
	TextPrintfToBufferFormatedColored(Vector(-0.5,-0.1),0.03,kTextAlignLeft,1,1,1,opacity,"Best Drivers:");
	int bestDrivers[kMaxPlayers];
	int numPlayersFinished=0;
	for(int i=0;i<gGameInfo->numPlayers;i++)
	{
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
		if(phys->lapCount>gGameInfo->numLaps)
		{
			numPlayersFinished++;
			if(!phys->finishTime)
				phys->finishTime=phys->lapTimes[gGameInfo->numLaps];
		}
	}
	if(gReplay)
	{
		float leaderTime=0;

		float time=0;
		for(int i=0;i<kNumCheckPoints;i++)
			for(int j=0;j<gGameInfo->numLaps;j++)
				if(gCheckPoints[i].passTimes[j].time>time)
					time=gCheckPoints[i].passTimes[j].time;
		float raceLength=kNumCheckPoints*gGameInfo->numLaps+1;
		for(int i=0;i<gGameInfo->numPlayers;i++)
		{					
			tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
			if(phys->lead>0)
			{
				float racePosition=phys->checkPoint+(phys->lapCount>1?phys->lapCount-1:0)*kNumCheckPoints+1;
				float completeness=racePosition/raceLength;
				leaderTime=time*kFPS/completeness;
			}			
		}
		
		for(int i=0;i<gGameInfo->numPlayers;i++)
			if(gCarEntities[i]->controlType==kControlTypeAIInput)
			{
				tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
				if(phys->lapCount<=gGameInfo->numLaps)
				{
					phys->lapCount=gGameInfo->numLaps+1;
					phys->finishTime=leaderTime-phys->lead*kFPS;
					numPlayersFinished++;
				}		
			}
	}
		
	for(int i=0;i<(gReplay?gGameInfo->numPlayers:numPlayersFinished);i++)
	{
		int bestTime=0x7fffffff;
		bestDrivers[i]=0;
		for(int j=0;j<gGameInfo->numPlayers;j++)
		{
			tCarPhysics *phys=(tCarPhysics*)gCarEntities[j]->physics;
			int finishTime=phys->finishTime;
			if(finishTime<=0)
				finishTime=0x7ffffffe;
			if(finishTime<bestTime)
			{
				int check=true;
				for(int k=0;k<i;k++)
					if(bestDrivers[k]==j)
						check=false;
				if(check)
				{
					bestTime=finishTime;
					bestDrivers[i]=j;
				}
			}
		}
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[bestDrivers[i]]->physics;
		tCarDefinition *car=&(phys->car);
		char *carName=gGameInfo->network?gGameInfo->playerNames[bestDrivers[i]]:car->carName;
		if(bestTime!=0x7ffffffe)
		{
			float t=bestTime*kFrameTime-kStartGameDelaySeconds;	
			TextPrintfToBufferFormatedColored(Vector(-0.5,-0.2-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s: %d:%02d'%02d",carName,((int)t)/60,((int)t)%60,((int)(t*100))%100);
		}
		else
			TextPrintfToBufferFormatedColored(Vector(-0.5,-0.2-0.05*i),0.025,kTextAlignLeft,1,1,1,opacity,"%s: Retired",carName);
			
		//print players place
		if(bestDrivers[i]==gGameInfo->playerID)
			if(bestTime!=0x7ffffffe)
			{
				char str[8];
				MakeNumString(i+1,str);
				TextPrintfToBufferFormatedColored(Vector(0.0,0.8),0.07,kTextAlignMiddle,1,1,1,opacity,"%s Place!",str);	
			}
		else
			TextPrintfToBufferFormatedColored(Vector(0.0,0.8),0.07,kTextAlignMiddle,1,1,1,opacity,"Retired!");			
	}
}

//displays game stats when the player has finished the race
void EndTextDisplay(float opacity)
{	
	if(opacity==0)
		return;
	SpeedStatsDisplay(opacity);
	RenderOverview(opacity);
	if(gGameInfo->numLaps!=-1)
	{
		if(gGameInfo->maxTime>0)
		{
			tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics;
			if(phys->lapCount<=1)
				TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!");
			else if(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds>gGameInfo->maxTime)
				TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!");
			else
			{
				if(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds<=gGameInfo->goldTime)
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255goldmedal.pct\255Gold Medal!");	
				else if(phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds<=gGameInfo->silverTime)
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255silvermedal.pct\255Silver Medal!");
				else
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255bronzemedal.pct\255Bronze Medal!");
				float t=phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds;
				TextPrintfToBufferFormatedColored(Vector(0.0,0.6),0.035,kTextAlignMiddle,1,1,1,opacity,"Time taken: %d:%02d'%02d",((int)t)/60,((int)t)%60,((int)(t*100))%100);
			}
		}
		else
		{
			BestLapsDisplay(opacity);
			BestDriversDisplay(opacity);
		}
		if(gGameInfo->network&&gGameInfo->playerID!=0)
			TextPrintfToBufferFormatedColored(Vector(0,-0.86),0.05,kTextAlignMiddle,1,1,1,opacity,"Hit esc to leave game.");			
		else
			TextPrintfToBufferFormatedColored(Vector(0,-0.86),0.05,kTextAlignMiddle,1,1,1,opacity,"Hit esc to exit.");
	}
}
*/


void SpeedStatsDisplay(float opacity)
{
	tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics;
	
	//Print Top Speed and average speed.
	if(gConfig->metricUnits){
		TextPrintfToBufferFormatedColored(Vector(-0.9,-0.5),0.03,kTextAlignLeft,1,1,1,opacity,"Top Speed: %3.0f km/h",phys->maxSpeed*3.6);
		TextPrintfToBufferFormatedColored(Vector(-0.9,-0.6),0.03,kTextAlignLeft,1,1,1,opacity,"Average Speed: %3.0f km/h",phys->averageSpeed*3.6);
	}
	else{
		TextPrintfToBufferFormatedColored(Vector(-0.9,-0.5),0.03,kTextAlignLeft,1,1,1,opacity,"Top Speed: %3.0f M.P.H.",phys->maxSpeed*2.2369);
		TextPrintfToBufferFormatedColored(Vector(-0.9,-0.6),0.03,kTextAlignLeft,1,1,1,opacity,"Average Speed: %3.0f M.P.H.",phys->averageSpeed*2.2369);
	}
}

void BestLapsDisplay(float opacity)
{
	if(gMapInfo->loop)
	{
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics;
		//print 5 best laps of player
		TextPrintfToBufferFormatedColored(Vector(0.9,0.45),0.03,kTextAlignRight,1,1,1,opacity,"Best Laps:");
		int bestLaps[5];
		for(int i=0;i<5&&i<gGameInfo->numLaps;i++)
		{
			float bestLapTime=INFINITY;
			bestLaps[i]=0;
			for(int j=0;j<gGameInfo->numLaps;j++)
			{
				float lapTime=(phys->lapTimes[j+1]-phys->lapTimes[j]);
				if(lapTime<=0)
					lapTime=10000000;
				if(lapTime<bestLapTime)
				{
					int check=true;
					for(int k=0;k<i;k++)
						if(bestLaps[k]==j)
							check=false;
					if(check){
						bestLapTime=lapTime;
						bestLaps[i]=j;
					}
				}
			}
			char str[8];
			MakeNumString(bestLaps[i]+1,str);
			if(phys->lapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]]>0)
			{
				float bestTime=(phys->lapTimes[bestLaps[i]+1]-phys->lapTimes[bestLaps[i]])*kFrameTime;
				TextPrintfToBufferFormatedColored(Vector(0.9,0.35-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s Lap: %d:%02d'%02d",str,((int)bestTime)/60,((int)bestTime)%60,((int)(bestTime*100))%100);
			}
			else
				TextPrintfToBufferFormatedColored(Vector(0.9,0.35-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s Lap: Retired",str);
		}	
	}
}

void BestDriversDisplay(float opacity)
{
	//print best players
	TextPrintfToBufferFormatedColored(Vector(0.9,-0.05),0.03,kTextAlignRight,1,1,1,opacity,"Best Drivers:");
	int bestDrivers[kMaxPlayers];
	int numPlayersFinished=0;
	for(int i=0;i<gGameInfo->numPlayers;i++)
	{
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
		if(phys->lapCount>gGameInfo->numLaps)
			numPlayersFinished++;
	}
	if(gReplay)
	{
		float leaderTime=0;

		float time=0;
		for(int i=0;i<kNumCheckPoints;i++)
			for(int j=0;j<gGameInfo->numLaps;j++)
				if(gCheckPoints[i].passTimes[j].time>time)
					time=gCheckPoints[i].passTimes[j].time;
		float raceLength=kNumCheckPoints*gGameInfo->numLaps+1;
		for(int i=0;i<gGameInfo->numPlayers;i++)
		{					
			tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
			if(phys->lead>0)
			{
				float racePosition=phys->checkPoint+(phys->lapCount>1?phys->lapCount-1:0)*kNumCheckPoints+1;
				float completeness=racePosition/raceLength;
				leaderTime=time*kFPS/completeness;
			}			
		}
		
	/*	for(int i=0;i<gGameInfo->numPlayers;i++)
			if(gCarEntities[i]->controlType==kControlTypeAIInput)
			{
				tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
				if(phys->lapCount<=gGameInfo->numLaps)
				{
					phys->lapCount=gGameInfo->numLaps+1;
					phys->finishTime=leaderTime-phys->lead*kFPS;
					numPlayersFinished++;
				}		
			}*/
	}
		
	for(int i=0;i<(gReplay?gGameInfo->numPlayers:numPlayersFinished);i++)
	{
		int bestTime=0x7fffffff;
		bestDrivers[i]=0;
		for(int j=0;j<gGameInfo->numPlayers;j++)
		{
			tCarPhysics *phys=(tCarPhysics*)gCarEntities[j]->physics;
			int finishTime=phys->finishTime;
			if(finishTime<=0)
				finishTime=0x7ffffffe;
			if(finishTime<bestTime)
			{
				int check=true;
				for(int k=0;k<i;k++)
					if(bestDrivers[k]==j)
						check=false;
				if(check)
				{
					bestTime=finishTime;
					bestDrivers[i]=j;
				}
			}
		}
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[bestDrivers[i]]->physics;
		tCarDefinition *car=&(phys->car);
		char *carName=gGameInfo->network?gGameInfo->playerNames[bestDrivers[i]]:car->carName;
		if(bestTime!=0x7ffffffe)
		{
			float t=bestTime*kFrameTime-kStartGameDelaySeconds;	
			if(bestDrivers[i]==gGameInfo->playerID)
				TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%s: %d:%02d'%02d",carName,((int)t)/60,((int)t)%60,((int)(t*100))%100);
			else
				TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s: \255#w\255%d:%02d'%02d",carName,((int)t)/60,((int)t)%60,((int)(t*100))%100);
		}
		else
			if(bestDrivers[i]==gGameInfo->playerID)
				TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%s: Retired",carName);
			else
				TextPrintfToBufferFormatedColored(Vector(0.9,-0.15-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"%s: \255#w\255Retired",carName);
			
		//print players place
		if(bestDrivers[i]==gGameInfo->playerID)
			if(bestTime!=0x7ffffffe)
			{
				char str[8];
				MakeNumString(i+1,str);
				TextPrintfToBufferFormatedColored(Vector(-0.9,0.95),0.08,kTextAlignLeft,1,1,1,opacity,"%s Place!",str);	
			}
		else
			TextPrintfToBufferFormatedColored(Vector(-0.9,0.95),0.08,kTextAlignLeft,1,1,1,opacity,"Retired!");			
	}
}

//displays game stats when the player has finished the race
void EndTextDisplay(float opacity)
{	
	if(opacity==0)
		return;
	if(!gReplay)
		SpeedStatsDisplay(opacity);
	RenderOverview(opacity,0.42,0.36);
	if(gGameInfo->numLaps!=-1)
	{
		if(gGameInfo->maxTime>0)
		{
			tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics;
			float t=phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds;
			if(gDisqualified)
				TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Disqualified!");
			else if(phys->lapCount<=1)
				TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!");
			else
			{
				if(t<=gGameInfo->goldTime)
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255goldmedal.pct\255 Gold Medal!");	
				else if(t<=gGameInfo->silverTime)
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255silvermedal.pct\255 Silver Medal!");
				else if(t<=gGameInfo->maxTime)
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"\255bronzemedal.pct\255 Bronze Medal!");
				else 
					TextPrintfToBufferFormatedColored(Vector(0,1),0.1,kTextAlignMiddle,1,1,1,opacity,"Time Up!");
			}
			if(phys->lapCount>1)
			{
				TextPrintfToBufferFormatedColored(Vector(0.0,0.6),0.035,kTextAlignMiddle,1,1,1,opacity,"Time taken: %d:%02d'%02d",((int)t)/60,((int)t)%60,((int)(t*100))%100);
				if(t>gGameInfo->maxTime&&!gDisqualified)
				{
					t=t-gGameInfo->maxTime;
					TextPrintfToBufferFormatedColored(Vector(0.0,0.52),0.035,kTextAlignMiddle,1,1,1,opacity,"Improve by: %d:%02d'%02d",((int)t)/60,((int)t)%60,((int)(t*100))%100);					
				}
			}
		}
		else
		{
			BestLapsDisplay(opacity);
			BestDriversDisplay(opacity);
		}
		if(gGameInfo->network&&gGameInfo->playerID!=0)
			TextPrintfToBufferFormatedColored(Vector(-0.9,-0.77),0.029,kTextAlignLeft,1,1,1,opacity,"Hit esc to leave game.");			
		else
			TextPrintfToBufferFormatedColored(Vector(-0.9,-0.77),0.029,kTextAlignLeft,1,1,1,opacity,"Hit esc to exit.");
		if(gGameInfo->network)
			TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",gGameInfo->playerNames[gReplayViewedEntityID]);
		else
		{
			tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[gReplayViewedEntityID],kParserTypeCarDesc,sizeof(tCarDefinition));
			TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",car->carName);
		}
	}
}

void DimScreen(float opacity)
{
	if(opacity==0)
		return;
	glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT);

	glColor4f(0,0,0,0.3*opacity*(1-gConfig->hudTransparency));

	glLoadIdentity();

	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glBegin(GL_TRIANGLE_STRIP);
		glVertex3f(-1.0f, 1.0f,-1.00f);
		glVertex3f(-1.0f,-1.0f,-1.00f);
		glVertex3f( 1.0f, 1.0f,-1.00f);
		glVertex3f( 1.0f,-1.0f,-1.00f);
	glEnd();
	
	glPopAttrib();
}

#define kChatScreenTime 9.0
#define kChatFadeTime 6.0

void ChatDisplay()
{
	if(gInputChatMode)
	{
		int offset=0;
		while(TextWidth(gGameChatMessage+offset,0.04)>1.5*kTextXPos)
			offset++;
		TextPrintfToBufferFormated(Vector(-0.75,0.5),0.04,kTextAlignLeft,"chat: %s%s",gGameChatMessage+offset,(int)(gFrameCount*kFrameTime*4)%2?"_":"");
	}
	float t=TimeGetSeconds();
	
	while((t-gGameChatMessagesRecv[0].timestamp)>kChatScreenTime&&gNumGameChatMessages)
		GameChatMessagesScroll();
	
	int pos=0;
	for(int i=0;i<gNumGameChatMessages;i++)
	{
		float a=1-((t-gGameChatMessagesRecv[i].timestamp)-kChatFadeTime)/(kChatScreenTime-kChatFadeTime);
		if(a>1)a=1;
		if(a<0)a=0;
		if((t-gGameChatMessagesRecv[i].timestamp)<0.2)
			a=(t-gGameChatMessagesRecv[i].timestamp)/0.2;

		char str[256];
		int offset=0;
		do{
			strcpy(str,gGameChatMessagesRecv[i].message+offset);
			int word=true;
			while(TextWidth(str,0.03)>0.9*kTextXPos)
			{
				word=str[strlen(str)-1]==' ';
				str[strlen(str)-1]='\0';
			}
			if(!word)
			{
				int i=strlen(str)-1;
				while(str[i]!=' '&&i>0)
					i--;
				if(i>0)
					str[i]='\0';
			}
			offset+=strlen(str);
			if(offset)
				TextPrintfToBufferFormatedColored(Vector(-0.4,1.0-pos*0.07),0.03,kTextAlignLeft,1,1,1,a,"\255#a\255%s",str);	
			else
				TextPrintfToBufferFormatedColored(Vector(-0.4,1.0-pos*0.07),0.03,kTextAlignLeft,1,1,1,a,str);	
			pos++;		
		}while(offset<strlen(gGameChatMessagesRecv[i].message));
		
	//	TextPrintfToBufferFormatedColored(Vector(-0.4,1.0-i*0.07),0.03,kTextAlignLeft,1,1,1,a,gGameChatMessagesRecv[i].message);
	}
}

void EscDisplay(float opacity)
{
	if(opacity==0)
		return;
/*	if(gReplay)
		TextPrintfToBufferFormatedColored(Vector(-0.8,0.7),0.06,kTextAlignLeft,1,1,1,opacity,"Are you sure you want to end the replay?");
	else*/ 
	if(gInputEscMode==kInputEscModeQuit)
		TextPrintfToBufferFormatedColored(Vector(-0.8,0.7),0.06,kTextAlignLeft,1,1,1,opacity,"Are you sure you want to quit Redline?");	
	else if(gGameInfo->network&&gGameInfo->playerID!=0)
		TextPrintfToBufferFormatedColored(Vector(-0.8,0.7),0.06,kTextAlignLeft,1,1,1,opacity,"Are you sure you want to leave the game?");
	else
		TextPrintfToBufferFormatedColored(Vector(-0.8,0.7),0.06,kTextAlignLeft,1,1,1,opacity,"Are you sure you want to end the game?");
	TextPrintfToBufferFormatedColored(Vector(-0.6,0.35),0.05,kTextAlignLeft,1,1,1,opacity,"Cancel");
	if(gInputEscMode==kInputEscModeQuit)
		TextPrintfToBufferFormatedColored(Vector(-0.6,0.5),0.05,kTextAlignLeft,1,1,1,opacity,"Quit");
	else 
		TextPrintfToBufferFormatedColored(Vector(-0.6,0.5),0.05,kTextAlignLeft,1,1,1,opacity,"Exit");
	if(!gGameInfo->network)
		TextPrintfToBufferFormatedColored(Vector(-0.6,0.2),0.05,kTextAlignLeft,1,1,1,opacity,"Retry");
		

	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-1.0f);

	glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	
	TexturesSelectTex(FileGetReference(kInterfaceMenuPointerTexture));
		
	glColor4f(1,1,1,opacity);	
	glBegin(GL_TRIANGLE_STRIP);		
	float y=((gInputEscSelection?0.5:0.35)-0.05)*kScreenYPos;	
	if(!gGameInfo->network&&gInputEscSelection==2)
		y=(0.2-0.05)*kScreenYPos;
	glTexCoord2d(1,1); glVertex2f((-0.6-0.05)*kScreenXPos+kInterfaceMenuPointerSize,y-kInterfaceMenuPointerSize);		
	glTexCoord2d(1,0); glVertex2f((-0.6-0.05)*kScreenXPos+kInterfaceMenuPointerSize,y+kInterfaceMenuPointerSize); 	
	glTexCoord2d(0,1); glVertex2f((-0.6-0.05)*kScreenXPos-kInterfaceMenuPointerSize,y-kInterfaceMenuPointerSize); 	
	glTexCoord2d(0,0); glVertex2f((-0.6-0.05)*kScreenXPos-kInterfaceMenuPointerSize,y+kInterfaceMenuPointerSize);
	glEnd();

	glPopAttrib();
	
}

void ReplayDisplay(float opacity)
{
	TextPrintfToBufferFormatedColored(Vector(-0.9,0.95),0.08,kTextAlignLeft,1,1,1,opacity*(sin(TimeGetSeconds()*3)*0.5+0.5),"Replay");
	if(gReplayAutoCam)
		TextPrintfToBufferFormatedColored(Vector(-0.9,0.77),0.029,kTextAlignLeft,1,1,1,opacity,"camera automatic");
	{
		tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[gReplayViewedEntityID],kParserTypeCarDesc,sizeof(tCarDefinition));
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[gReplayViewedEntityID]->physics;
		if(phys->plateName)
			TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",phys->plateName);
		else
			TextPrintfToBufferFormatedColored(Vector(0.9,-0.77),0.029,kTextAlignRight,1,1,1,opacity,"Watching %s",car->carName);
	}
	if(gGameInfo->playerID==0)
		TextPrintfToBufferFormatedColored(Vector(-0.9,-0.77),0.029,kTextAlignLeft,1,1,1,opacity,"Hit esc to exit.");
	if(gGameInfo->maxTime)
	{
		float timeTaken=gFrameCount*kFrameTime-kStartGameDelaySeconds;
		tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics;
		if(phys->lapCount>1)
			timeTaken=phys->lapTimes[1]*kFrameTime-kStartGameDelaySeconds;
		if(timeTaken<0)
			timeTaken=0;
		TextPrintfToBufferFormatedColored(Vector(0.5,0.9),0.03,kTextAlignLeft,1,1,1,opacity,"Time Taken: %d:%02d'%02d",((int)timeTaken)/60,((int)timeTaken)%60,((int)(timeTaken*100))%100);
	}
	
	if(gGameInfo->numPlayers>1)
	{
		TextPrintfToBufferFormatedColored(Vector(0.73,0.4),0.025,kTextAlignRight,1,1,1,opacity,"Player");
		TextPrintfToBufferFormatedColored(Vector(0.8,0.4),0.025,kTextAlignRight,1,1,1,opacity,"Lap");
		TextPrintfToBufferFormatedColored(Vector(0.9,0.4),0.025,kTextAlignRight,1,1,1,opacity,"Trail");

		int playerRanks[kMaxPlayers];
		for(int i=0;i<gGameInfo->numPlayers;i++)
			playerRanks[i]=i;
		for(int i=0;i<gGameInfo->numPlayers-1;i++)
			for(int j=0;j<gGameInfo->numPlayers-1-i;j++)
			{
				tCarPhysics *phys2=(tCarPhysics*)gCarEntities[playerRanks[j]]->physics;
				tCarPhysics *phys1=(tCarPhysics*)gCarEntities[playerRanks[j+1]]->physics;

				if(phys1->finishTime>0&&phys2->finishTime<=0)
				{
					int tmp=playerRanks[j];
					playerRanks[j]=playerRanks[j+1];
					playerRanks[j+1]=tmp;
				}
				else if(phys2->finishTime>0&&phys1->finishTime<=0)
					;
				else if(phys2->finishTime>0&&phys1->finishTime>0)
				{
					if(phys2->finishTime>phys1->finishTime)
					{
						int tmp=playerRanks[j];
						playerRanks[j]=playerRanks[j+1];
						playerRanks[j+1]=tmp;
					}
				}
				else if((phys1->lap>phys2->lap)||(gGameInfo->reverse&&(phys2->lap==phys1->lap&&phys1->position<phys2->position))||(!gGameInfo->reverse&&(phys2->lap==phys1->lap&&phys1->position>phys2->position)))
				{
					int tmp=playerRanks[j];
					playerRanks[j]=playerRanks[j+1];
					playerRanks[j+1]=tmp;
				}
			}
		
			
		for(int i=0;i<gGameInfo->numPlayers;i++)
		{
			tCarPhysics *phys=(tCarPhysics*)gCarEntities[playerRanks[i]]->physics;
			tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[playerRanks[i]],kParserTypeCarDesc,sizeof(tCarDefinition));
			char name[512];
			if(playerRanks[i]==gReplayViewedEntityID)
				sprintf(name,"*%s",phys->plateName?phys->plateName:car->carName);
			else
				sprintf(name,phys->plateName?phys->plateName:car->carName);
			
			if(playerRanks[i]==gGameInfo->playerID)
			{
				TextPrintfToBufferFormatedColored(Vector(0.73,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,name);
				if(phys->lapCount<=gGameInfo->numLaps)
					TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%d",phys->lapCount);
				else
					TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"F");
				if(phys->lead<0)
					TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%03.1f",-phys->lead);
				else
					TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,0,1,0,opacity,"%03.1f",0);		
			}
			else
			{
				TextPrintfToBufferFormatedColored(Vector(0.73,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,name);
				if(phys->lapCount<=gGameInfo->numLaps)
					TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255%d",phys->lapCount);
				else
					TextPrintfToBufferFormatedColored(Vector(0.8,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255F");
				if(phys->lead<0)
					TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255%03.1f",-phys->lead);
				else
					TextPrintfToBufferFormatedColored(Vector(0.9,0.33-0.05*i),0.025,kTextAlignRight,1,1,1,opacity,"\255#a\255%03.1f",0);		
			}
		}
	}
	RenderOverview(opacity,0.42,0.36);			
}

float gEscDisplay=0;
float gPauseDisplay=0;
float gEndDisplay=0;
float gInterfaceFadeDisplay=1;
float gLastDisplayTime=0;
float gReplayDisplay=0;

#define kFadeSpeed 1.5

void CalcFades()
{
	float dt=TimeGetSeconds()-gLastDisplayTime;
	gLastDisplayTime+=dt;
	if(dt<0)dt=0;
	
	if(gInputEscMode)
		gEscDisplay+=dt*kFadeSpeed;
	else
		gEscDisplay-=dt*kFadeSpeed;
	if(gEscDisplay>1)gEscDisplay=1;
	else if(gEscDisplay<0)gEscDisplay=0;

	tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics;
	if(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1&&!gReplay)
		gEndDisplay+=dt*kFadeSpeed;
	else
		gEndDisplay-=dt*kFadeSpeed;
	if(gEndDisplay>1)gEndDisplay=1;
	else if(gEndDisplay<0)gEndDisplay=0;

	if(gPaused&&!gInputEscMode)
		gPauseDisplay+=dt*kFadeSpeed;
	else
		gPauseDisplay-=dt*kFadeSpeed;		
	if(gPauseDisplay>1)gPauseDisplay=1;
	else if(gPauseDisplay<0)gPauseDisplay=0;

	if(dt<1)
		gInterfaceFadeDisplay-=dt*kFadeSpeed;
	if(gInterfaceFadeDisplay<0)gInterfaceFadeDisplay=0;

	if(gReplay)
		gReplayDisplay+=dt*kFadeSpeed;
	else
		gReplayDisplay-=dt*kFadeSpeed;
	if(gReplayDisplay>1)gReplayDisplay=1;
	else if(gReplayDisplay<0)gReplayDisplay=0;

}


void InitInfoDisplay()
{
	gEscDisplay=0;
	gPauseDisplay=0;
	gEndDisplay=0;
	gInterfaceFadeDisplay=1;
	gReplayDisplay=0;
	gLastDisplayTime=0;
}

//Show game stats
void GameShowInfo()
{
	CalcFades();

	//if enabled, print frame rate statistics on screen
	if(gConfig->performanceStats)
	{
		TextPrintfToBufferFormated(Vector(-0.35,-0.95),0.025,kTextAlignLeft,"FPS :%5.1f",gFPS);
		if(gGameInfo->network)
		{
			char netStr[7];
			NetworkGetStatusString(netStr);
			TextPrintfToBufferFormated(Vector(0.15,-0.95),0.025,kTextAlignLeft,netStr);
			float rcv,snd;
			NetworkGetBandwidth(&rcv,&snd);
			TextPrintfToBufferFormated(Vector(-0.1,-0.95),0.025,kTextAlignLeft,"R%3.1f/S%3.1f",rcv,snd);			
		}
		#ifdef __POLYCOUNT
		TextPrintfToBufferFormated(Vector(-0.1,-0.95),0.025,kTextAlignLeft,"TPS :%7.0f",gPolyCount*gFPS);
		TextPrintfToBufferFormated(Vector(0.15,-0.95),0.025,kTextAlignLeft,"Polys:%d",gPolyCount);	
		#endif
	}
	
	
	//are we still playing, or have we finished the race?

	ChatDisplay();
	EndTextDisplay(gEndDisplay);		

	CarRenderPanels(gViewedEntity,1-gPauseDisplay-gEscDisplay-gEndDisplay-gReplayDisplay);
	RaceInfoDisplay(1-gPauseDisplay-gEscDisplay-gEndDisplay-gReplayDisplay);
	
	TextPrintfToBufferFormatedColored(Vector(0,0.7),0.1,kTextAlignMiddle,1,1,1,gPauseDisplay,"Game Paused");
	TextPrintfToBufferFormatedColored(Vector(0,-0.3),0.05,kTextAlignMiddle,1,1,1,gPauseDisplay,"Press \255#r\255%s\255#n\255 to Continue.",gConfig->keys[kInputPause].identifier);

	ReplayDisplay(gReplayDisplay);
	
	float dim=gEscDisplay+gPauseDisplay+gEndDisplay;
	if(dim>1)dim=1;
	DimScreen(dim);
	EscDisplay(gEscDisplay);

	float a=2-(TimeGetSeconds()-giTunesLastStatusUpdate)*0.4;
	if(a>0)
	{
		TextPrintfToBufferFormatedColored(Vector(0,-0.5),0.25,kTextAlignMiddle,1,1,1,a,"\255stereo.pct\255");
		if(giTunesPlaying==kITunesPlaying)
		{
			TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.04,kTextAlignMiddle,1,1,1,a,giTunesArtist);
			TextPrintfToBufferFormatedColored(Vector(0,-0.75),0.04,kTextAlignMiddle,1,1,1,a,giTunesSong);
		}
		else if(giTunesPlaying==kITunesPaused)
			TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.04,kTextAlignMiddle,1,1,1,a,"Paused");		
		else
			TextPrintfToBufferFormatedColored(Vector(0,-0.65),0.04,kTextAlignMiddle,1,1,1,a,"Stopped");		
	}
	
	TextPrintBuffer(1-gConfig->hudTransparency,true);
	TextClearBuffer(); 

	InterfaceDrawBackgroundFade(gInterfaceFadeDisplay,true);	
}
