//gameinitexit.cpp
//initialize a new game and dispose all the game structures after the game has ended


#include <OpenGL/gl.h>
#include <stdio.h>
#include <string.h>
#include "gametime.h"
#include "gamemem.h"
#include "entities.h"
#include "fileio.h"
#include "sky.h"
#include "carphysics.h"
#include "gameframe.h"
#include "roads.h"
#include "parser.h"
#include "network.h"
#include "environment.h"
#include "config.h"
#include "gameinitexit.h"
#include "error.h"
#include "gamesound.h"
#include "text.h"
#include "renderframe.h"
#include "controls.h"
#include "tracks.h"
#include "stencil.h"
#include "log.h"
#include "networkphysics.h"
#include "random.h"
#include "particles.h"
#include "interfaceutil.h"
#include "tracker.h"
#include "screen.h"
#include "textures.h"
#include "gamesystem.h"
#include "interface.h"
#include "interfacemultiplayer.h"
#include "initexit.h"

/*
#if __option(profile)
#include <profiler.h>
#endif
*/


//the map info structure
tMapInfo *gMapInfo=NULL;		

//the game info structure
tGameInfo *gGameInfo=NULL;	

//pointers to the game entities of the cars participating in the game
tGameEntity *gCarEntities[kMaxPlayers];		

tGameEntity *gGhostEntity;

tMapEnv *gMapEnv=NULL;

//the check points along the track (invisible, used only for lead/trail calculation)
tCheckPoint gCheckPoints[kNumCheckPoints];


int gNumCornerSigns;
tCornerSign gCornerSigns[kMaxCornerSigns];

//is this a replay being watched?
int gReplay=false;
int gBackgroundReplay=false;
int gReplayAutoCam=false;
int gReplayViewedEntityID;
int gReplayNextCam;
int gReplayOldFrameCount;

void InitGInfo(tGameInfo *gInfo)
{
	gInfo->inited=false;
	gInfo->version=0;
	gInfo->numPlayers=0;
	gInfo->numNetPlayers=0;
	gInfo->reverse=0;
	gInfo->environment=kFileErr;
	gInfo->numLaps=0;
	gInfo->map=kFileErr;
	gInfo->network=false;
	gInfo->playerID=0;
	gInfo->arcade=kGameModeSim;
	gInfo->maxTime=0;
	gInfo->unused1=false;
	gInfo->unused2=false;
	gInfo->carsOnSpeed=gConfig->carsOnSpeed;
	gInfo->demolition=gConfig->demolition;
	gInfo->playerInited[0]=true;
	strcpy(gInfo->environmentName,"");
	strcpy(gInfo->mapName,"");
	for(int i=0;i<kMaxPlayers;i++)
	{
		gInfo->playerCars[i]=kFileErr;
		gInfo->playerColors[i]=0;
		gInfo->playerAddOns[i]=0;
		gInfo->netID[i]=0;
		gInfo->ping[i]=0;
		gInfo->playerVersions[i]=0;
		strcpy(gInfo->playerCarNames[i],"");
		strcpy(gInfo->playerNames[i],"");
	}
}


//load map describtion from the file refered to by ref.
void ProcessMapInfo()
{
	if(gMapInfo->reverse)
		gGameInfo->reverse=!gGameInfo->reverse;
		
	//get road types data
	gRoadTypes=(tRoadTypeList*)FileGetParsedDataPtr(gMapInfo->roadTypes,kParserTypeRoadTypeDesc,sizeof(tRoadTypeList));	

	//initialize game entities defined by map
	for(int i=0;i<gMapInfo->numObjs;i++)
	{
		if(gMapInfo->obj[i].envFlags==0||(gMapInfo->obj[i].envFlags&gEnvironment->envFlags))
		{
			//create a new entity
			tGameEntity	*entity;
			entity=EntityNew(gFirstEntity);
			entity->renderType=kRenderTypeModel;

			//get entity position and direction from map info
			entity->pos=gMapInfo->obj[i].pos;
			EulerAnglesToMatrix(gMapInfo->obj[i].dir*kDegreeRadians,entity->dir);

			//get extension of file describing entity
			char *extension=FileGetExtension(gMapInfo->obj[i].model);

			entity->physics=MemoryAllocateBlock(sizeof(int));
			*((int*)entity->physics)=gMapInfo->obj[i].color;		

			entity->untouchable=gMapInfo->obj[i].untouchable;
			
			if(extension)
				//is this entity just a model (no interaction with the game, just graphics).
				if(!_stricmp(extension,kFileTypeModel))
					entity->renderData=gMapInfo->obj[i].model;
					
				//or is it a solid entity?
				else if(!_stricmp(extension,kFileTypeSolidEntity))
				{
					//initialize entity
					entity->physicsType=kPhysicsTypeSolid;
					entity->physicsData=gMapInfo->obj[i].model;
					entity->physicsMachine=kPhysicsLocal;

					tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics));
					if(ent->randomColor)
						*((int*)entity->physics)=RandomInt(0,ent->numColors);
						
					entity->renderData=ent->model;
					if(ent->movable)	
						entity->lastActivity=-1000;
				}
				else if(!_stricmp(extension,kFileTypeCarDefinition))
				{
					//initialize entity
					entity->renderType=kRenderTypeCar;
					tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gMapInfo->obj[i].model,kParserTypeCarDesc,sizeof(tCarDefinition));
					entity->renderData=car->model;
					entity->physicsType=kPhysicsTypeCar;
					entity->controlType=kControlTypeNone;
					entity->physicsData=gMapInfo->obj[i].model;
					entity->physics=MemoryAllocateZeroedBlock(sizeof(tCarPhysics));
					entity->physicsMachine=gGameInfo->network?(i==gGameInfo->playerID?kPhysicsLocal:kPhysicsRemote):kPhysicsLocal;
					entity->lastActivity=-1000;
					tCarPhysics *phys=(tCarPhysics*)entity->physics;
					phys->car=*car;
					phys->car.wheels=(tWheelDefinition*)MemoryAllocateBlock(sizeof(tWheelDefinition)*car->numWheels);
					MemoryMove(phys->car.wheels,car->wheels,sizeof(tWheelDefinition)*car->numWheels);
					phys->color=gMapInfo->obj[i].color;
				}

				//is it something we don't understand?
				else 
				{
					char error[256];
					sprintf(error,"Illegal File Type %s Specified for Object",extension);
					FailWithErrorString(error);
				}
		}
		//else PrintConsoleString("%s: %d/%d",FileGetName(gMapInfo->obj[i].model),gMapInfo->obj[i].envFlags,gEnvironment->envFlags);
	}
	
	gMapEnv=NULL;
	for(int i=0;i<gMapInfo->numMapEnvs;i++)
		if(gMapInfo->mapEnv[i].envFlags&gEnvironment->envFlags)
			gMapEnv=gMapInfo->mapEnv+i;
		
	if(gMapEnv)	
		if(gMapEnv->fogBegin)
		{
			GLfloat fogColor[4];	
			*(tVector3*)fogColor=gMapEnv->fogColor;
			fogColor[3]=1;
			glFogfv(GL_FOG_COLOR,fogColor);
			glFogf(GL_FOG_START,gMapEnv->fogBegin);	
			glFogf(GL_FOG_END,gMapEnv->fogEnd);	
		}
	//calculate road checkpoints
	if(gMapInfo->loop)
		RoadInitCheckPoints(gMapInfo->startPos);
	else
	{
		RoadInitCheckPoints(gMapInfo->startPos,gMapInfo->finishPos);	
		gGameInfo->numLaps=1;
	}
	RoadInitCornerSigns();
}

void CalcCamera();

void InitCars()
{
	for(int i=0;i<gGameInfo->numPlayers;i++)
	{
		//get car describtion
		tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gGameInfo->playerCars[i],kParserTypeCarDesc,sizeof(tCarDefinition));
		
		//initialize car entity
		tGameEntity	*entity;
		entity=EntityNew(gFirstEntity);
		entity->renderType=kRenderTypeCar;
		entity->renderData=car->model;
		entity->physicsType=kPhysicsTypeCar;
		entity->controlType=(i==gGameInfo->playerID?kControlTypeUserInput:kControlTypeAIInput);
		//entity->controlType=kControlTypeAIInput;
		entity->physicsData=gGameInfo->playerCars[i];
		entity->physics=MemoryAllocateZeroedBlock(sizeof(tCarPhysics));
		entity->physicsMachine=gGameInfo->network?(i==gGameInfo->playerID?kPhysicsLocal:kPhysicsRemote):kPhysicsLocal;
		if(i>=gGameInfo->numNetPlayers&&gGameInfo->playerID==0)
			entity->physicsMachine=kPhysicsLocal;			
		gCarEntities[i]=entity;
		
		//position car at start position
		int startAtEnd=(gGameInfo->reverse&&!gMapInfo->loop);
		tVector3 startPos=startAtEnd?gMapInfo->finishPos:gMapInfo->startPos;
		
		if(gGameInfo->numLaps==-1&&gMapInfo->loop)
		{
			int roadIndex=0;
			float side=0,speed;
			startPos=RoadGetNextWaypoint(gMapInfo->startPos,&roadIndex,&side,&speed,-700);
			
		}
		tVector3 startDir=RoadGetDir(RoadGetPosition(startPos,0,NULL));
		
		entity->pos=startPos-startDir*((i/2)*10+gMapInfo->startLineOffset+(i%2?gMapInfo->carOffset:0))+!(startDir%Vector(0,1,0))*(i%2?-1:1)*gMapInfo->startCenterOffset;
		entity->netPos=entity->pos;
		*MatrixGetZVector(entity->dir)=startDir;
		*MatrixGetYVector(entity->dir)=Vector(0,1,0);
		MatrixReAdjust(entity->dir);

		tCarPhysics *phys=(tCarPhysics*)entity->physics;
		
		if(i==gGameInfo->playerID)
			gViewedEntity=entity;
			
		//set up car specs
		if(startAtEnd)
			phys->checkPoint=kNumCheckPoints-1;
		phys->car=*car;
		phys->car.wheels=(tWheelDefinition*)MemoryAllocateBlock(sizeof(tWheelDefinition)*car->numWheels);
		MemoryMove(phys->car.wheels,car->wheels,sizeof(tWheelDefinition)*car->numWheels);
		phys->addOns=gGameInfo->playerAddOns[i];
		phys->color=gGameInfo->playerColors[i];
		phys->aiPowerCycle=RandomFl(0,2*PI);
		phys->aiRouteCycle=RandomFl(0,2*PI);
		phys->position=RoadGetPosition(entity->pos,entity->lastRoadIndex,NULL);
		if(i>0)
		{
			tCarPhysics *phys2=(tCarPhysics*)gCarEntities[0]->physics;			
			if(phys->position>phys2->position&&!gGameInfo->reverse)
				phys->lap=-1;
			else if(phys->position<phys2->position&&gGameInfo->reverse)
				phys->lap=-1;
		}
		
		if(gGameInfo->playerNames[i][0])
			phys->plateName=gGameInfo->playerNames[i];
			
		for(int j=0;j<5;j++)
			phys->dirtStretch[j]=RandomFl(-0.5,0.5);
		InstallCarAddOns(phys);
		
		//if car is on left side, prepare for overtaking 
		//(otherwise ai players will immediatly try to change lanes, not a good idea).
		if(i%2)
		{
			phys->overtaking=gCarEntities[0];
			phys->overtakeSide=-1;
		}

	}
}

void InitGameEntities()
{
	//initialize entity list
	EntityResetCount();
	gFirstEntity=(tGameEntity*)MemoryAllocateZeroedBlock(sizeof(tGameEntity));
	gFirstEntity->next=gFirstEntity;
	gFirstEntity->prev=gFirstEntity;
	
	//process map info describtion
	ProcessMapInfo();

	//initialize car entities
	InitCars();

	//initialize camera
	gCameraEntity=EntityNew(gFirstEntity);

	//initialize ghost
	if(gGameInfo->numLaps==-1)
	{
		gGhostEntity=EntityNew(gFirstEntity);
		gGhostEntity->physics=MemoryAllocateZeroedBlock(sizeof(tCarPhysics));
		MemoryMove(gGhostEntity->physics,gCarEntities[0]->physics,sizeof(tCarPhysics));
		gGhostEntity->pos=Vector(10000,10000,10000);
		gGhostEntity->physicsType=kPhysicsTypeGhost;
		gGhostEntity->renderType=kRenderTypeGhost;
		gGhostEntity->controlType=kControlTypeNone;
		gGhostEntity->physicsData=gGameInfo->playerCars[0];
		gGhostEntity->renderData=gCarEntities[0]->renderData;
	}	
}

void InitInfoDisplay();

//initialize a new game
int InitGame()
{
	if(gFileTampered)
		Exit();

	//get map info data
	gMapInfo=(tMapInfo*)FileGetParsedDataPtr(gGameInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo));	
	
	//load enivronment describtion
	LoadEnvironment(gGameInfo->environment);

	//initialize fog
	GLfloat fogColor[4];	
	*(tVector3*)fogColor=gEnvironment->fogColor;
	fogColor[3]=1;
	glFogfv(GL_FOG_COLOR,fogColor);
	glFogf(GL_FOG_START,600);
	glFogf(GL_FOG_END,800);

	InitGameEntities();	
		
	//initialize game end flags
	gInterfaceType=false;
	gGameEnd=false;
	gPaused=false;
	gRaceFinished=false;
	gDisqualified=false;
	gLightning=false;
	gInputEscMode=false;
 	gReplayViewedEntityID=gGameInfo->playerID;
	gCameraMode=gConfig->cameraMode;
	gCameraReverse=0;
	gOutboxPos=0;
	gGameChatMessage[0]='\0';
	gGameChatMessageSize=0;
	gNetPauseTime=0;
		
	SoundSilence();

	//reset replay log
	LogReset();
	GhostLogReset();

	TracksClear();
	ParticlesClear();
	
	InitInfoDisplay();
	
	//call once to reset stencil buffer
	RenderStencilLayer(true,gConfig->stencil);
	
	//initialze lighting
	SetupLighting();

	//render a frame to get graphics loaded
	CalcCamera();
	
	gClipEnable=false;
	float time=TimeGetSeconds();

	gNumGameChatMessages=0;
	gEnableTextureLoad=false;
	gNumTexturesRequested=0;
	RenderFrame(false);
	gNumTexturesLoaded=0;
	gEnableTextureLoad=true;
	gDisabledRestart=0;
	
	for(int i=0;i<gFileTableSize;i++)
	{
		if(gFileTable[i].tagged&&!gFileTable[i].parsed)
		{
//			InterfaceDrawStatusBar("Loading Textures...",gFileTable[i].name,gNumTexturesLoaded/(float)gNumTexturesRequested);
			if(gNumTexturesRequested>0)
				InterfaceDrawStatusBar("Loading Textures...","Please Wait",gNumTexturesLoaded/(float)gNumTexturesRequested);
			gTexturesQualityModifier=gFileTable[i].tagged+1;
			TexturesSelectTex(i);
			SystemPoll(true);
		}
	}
	gTexturesQualityModifier=0;
//	PrintConsoleString("loading took: %f seconds.",TimeGetSeconds()-time);
	gClipEnable=true;

	//synchronize players on network
 	if(gGameInfo->network)
 	{
	 	InterfaceDrawStrings("Waiting for other Players...","",kFileErr);
		if(!NetworkSynch(gGameInfo->numNetPlayers))
			return false;
	}
		
	//start clock
	gTimeStretch=1.0;
	gBestLapTime=0;
	StartFrameCount();
	gPaused=false;
		
	return true;
}

void DisposeEntities()
{
	if(gFirstEntity)
	{
		tGameEntity *entity=(tGameEntity*)gFirstEntity->next;
		while(entity!=gFirstEntity)
		{
			tGameEntity *next=(tGameEntity*)entity->next;
			if(entity->physics)
				MemoryFreeBlock(entity->physics);
			SoundFreeEntitySources(entity);
			//FREE CAR PHYSICS!
			MemoryFreeBlock(entity);
			entity=next;
		}
		MemoryFreeBlock(gFirstEntity);

		gFirstEntity=NULL;
		gViewedEntity=NULL;
		gCameraEntity=NULL;
	}
}

void StartReplay()
{
	SoundSilence();
	tCarPhysics physStore[kMaxPlayers];
	
//	for(int i=0;i<gGameInfo->numPlayers;i++)
//		physStore[i]=*(tCarPhysics*)gCarEntities[i]->physics;
	
	DisposeEntities();

	InitGameEntities();

/*	for(int i=0;i<gGameInfo->numPlayers;i++)
	{
		*(tCarPhysics*)gCarEntities[i]->physics=physStore[i];
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics;
		phys->dirt=0;
		for(int j=0;j<kMaxWheels;j++)
			phys->wheels[j].lastTrack=0;
	}*/

//	for(int i=0;i<gNumGameChatMessages;i++)
//		gGameChatMessagesRecv[i].frameCount-=gFrameCount;
	
	gCameraEntity->pos=Vector(10000,10000,10000);
	if(gCameraMode==kCameraFree)
		gCameraMode=kCameraTripod;
	if(gGameInfo->numLaps==-1)
		gGhostEntity->pos=Vector(10000,10000,10000);

	gFrameCount=gGameInfo->numLaps==-1?(gBestLapTime!=0?gBestLapStart+5:gCurrentLapStart+5):0;
	gGraphFrameCount=gFrameCount;
	gStartTime=TimeGetSeconds()-gFrameCount*kFrameTime;
	gReplayIndex=0;
	gReplayNextCam=gFrameCount+RandomFl(4,12)*kFPS;
	
	TracksClear();
}
extern int gIndexPos;
extern int gGhostIndexPos;

void ReplayProcessNetwork(int *end)
{
	if(gGameInfo->network)
	{
		tNetworkPacket message;
		while(NetworkReceivePacket(&message))
			switch(message.what)
			{
				case kMessageTypeEndReplay:
					*end=true;
					gGameEnd=kEndGameNoLeave;
					break;
				case kMessageTypeGameTerminated:
					*end=true;
					return;
				case kMessageTypeChat:
					InsertGameChatMessage(((tChatMessage*)message.data)->str);
					ChatBufferInsert(((tChatMessage*)message.data),gChatBuffer);
					PlayInterfaceSound(FileGetReference("chat.wav"));
					break;
				case kMessageTypeBye:
					{
						if(message.from==0)
							sprintf(gDisconnectString,"%s quit and stopped hosting.",gGameInfo->playerNames[0]);
						int netID=message.from;
						int id=-1;
						//find the players id
						for(int i=0;i<gGameInfo->numNetPlayers;i++)
							if(gGameInfo->netID[i]==netID)	
								id=i;
						
						if(id!=-1)
						{
							tChatMessage m;
							m.flags=kChatFlagSystem;
							sprintf(m.str,"### %s is quitting.",gGameInfo->playerNames[id]);
							SoundReInit();
							PlayInterfaceSound(FileGetReference("chat.wav"));
							InsertGameChatMessage(m.str);
							ChatBufferInsert(&m,gChatBuffer);
						}
					}
					break;
				case kMessageTypePlayerLeft:
					{
						int netID=message.from;
						int id=-1;
						//find the players id
						for(int i=0;i<gGameInfo->numPlayers;i++)
							if(gGameInfo->netID[i]==netID)	
								id=i;
						if(id!=-1)
						{
							char leftMessage[256];
							//PrintConsoleString("### Player %d left the game. netID: %d",id,netID);
							sprintf(leftMessage,"### %s left the game.",gGameInfo->playerNames[id]);
							InsertGameChatMessage(leftMessage);
							//KillPlayer(id);
							gGameInfo->netID[id]=-1;
						}
					}
					break;
			}
	}
}


int ReplayFrame()
{
	int end=false;
	static int save=false;
	if(GetInterfaceKey(kInterfaceKeyEsc)&&!gGameInfo->network)
		end=true;
	if(GetInterfaceKey(kInterfaceKeyCmd)&&GetInterfaceKey(kInterfaceKeyOpt)&&GetInterfaceKey(kInterfaceKeyS)&&!gBackgroundReplay)
	{
		if(!save)
		{
			LogSaveToDisk();
			save=true;
		}
	}
	else save=false;
	if(gGameEnd)
		end=true;
	ReplayProcessNetwork(&end);
	if(gFrameCount>=gReplayOldFrameCount||gReplayIndex>=(gGameInfo->numLaps==-1?gGhostIndexPos:gIndexPos))
	{
		if(gMapInfo->reverse)
			gGameInfo->reverse=!gGameInfo->reverse;
		StartReplay();
	}
	if(gFrameCount>=gReplayNextCam&&gReplayAutoCam)
	{
		ParticlesClearScreen();
		gCameraReverse=RandomProb(0.4);
		gCameraMode=RandomProb(0.3)?kCameraTripod:RandomInt(kCameraChase,kCameraTop+1);
		int nextViewID;
		do{
			nextViewID=RandomInt(0,gGameInfo->numPlayers);
		}while(gGameInfo->netID[nextViewID]==-1);
		tGameEntity *nextView=gCarEntities[nextViewID];
		if(sqr(nextView->velo)>=1.0)
		{
			gViewedEntity=nextView;
			gReplayViewedEntityID=nextViewID;
		}
		float cameraDuration;
		switch(gCameraMode)
		{
			case kCameraChase:
			case kCameraChaseClose:
				cameraDuration=RandomFl(3,10);break;
			case kCameraLeftSide:
			case kCameraLeftWheel:
				cameraDuration=RandomFl(2,5);break;
			case kCameraTop:
				cameraDuration=RandomFl(3,6);break;					
			case kCameraTripod:
				cameraDuration=RandomFl(8,18);break;
			case kCameraCockpit:
			case kCameraCockpitCarHidden:
				cameraDuration=RandomFl(2,5);
				gCameraReverse=false;
				break;
		}
		gReplayNextCam=gReplayNextCam+cameraDuration*kFPS;
	}
	
	while(!GameReplayFrame()&&gBackgroundReplay);
	
	return end;
}

void RunReplay()
{
	TextClearBufferFading();
//LogLoad(FileGetReference("test.log"),gGameInfo);
	gReplay=true;
	if(gGameInfo->numLaps==-1)
	{
		tCarPhysics *phys=(tCarPhysics*)gCarEntities[0]->physics;
		if(phys->lapCount<=1)
			LogToGhostLog();
	}	
	gReplayOldFrameCount=(gGameInfo->numLaps==-1&&gBestLapTime!=0)?gBestLapTime+gBestLapStart:gFrameCount;
	if(gReplayOldFrameCount<10)return;
 	LogSort();
	//LogSaveToDisk();
	gReplayAutoCam=true;
	 	
	StartReplay();
	while(!ReplayFrame());
		
	if(gGameInfo->network&&gGameInfo->playerID==0)
	{
		NetworkSendPacket(kMessageTypeEndReplay,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf);
	}
//	gReplay=false;
}

void ExitGame()
{
/*	#ifndef __APPLE_CC__
	#if __option(profile)
	ProfilerDump("\pProfiler Dump");
	#endif
	#endif
*/		
	FFBStop();	
	UnPauseGame();
 	if(gGameInfo->network)
 	{
 		if(gGameInfo->playerID==0)
			NetworkSendPacket(kMessageTypeEndGame,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf);
		else if(gGameEnd!=kEndGameNoLeave)
		{
			NetworkSendPacket(kMessageTypeBye,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf);
			NetworkSendPacket(kMessageTypeGameTerminated,NULL,0,kMessagePriorityHigh,gGameInfo->netID[gGameInfo->playerID]);
		}
	}			
	if((gGameEnd==kEndGameNoLeave||(gGameEnd==kEndGame&&!(gGameInfo->network&&gGameInfo->playerID!=0)))&&!gGameInfo->demolition)
	{
		gGameEnd=false;
		while((GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)||GetInterfaceKey(kInterfaceKeyEsc)));
		RunReplay();
		if(gGameInfo->network)
		{
			if(gGameInfo->playerID==0)
				NetworkSendPacket(kMessageTypeEndGame,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf);
			else if(gGameEnd!=kEndGameNoLeave)
			{
				NetworkSendPacket(kMessageTypeBye,NULL,0,kMessagePriorityHigh,kMessageSendToAllButSelf);
				NetworkSendPacket(kMessageTypeGameTerminated,NULL,0,kMessagePriorityHigh,gGameInfo->netID[gGameInfo->playerID]);
			}
		}			
	}
		
/*	if(gGameEnd!=kEndGameRestart)
	{
		float startTime=TimeGetSeconds();
		float t;
		do{
			t=TimeGetSeconds()-startTime;
			RenderFrame(false);
			InterfaceDrawBackgroundFade(t/0.3,true);
			ScreenBlit();
		}while(t<0.3);
	}*/
	
	DisposeEntities();

	gInputChatMode=false;
	gFrameCount=0x7fffffff;
	TextClearBuffer();
	TracksClear();
	SoundSilence();
	ParticlesClear();
	glClear(GL_COLOR_BUFFER_BIT);
	gMapInfo=NULL;
	gGameInfo=NULL;
	gMapEnv=NULL;
	TrackerLapTimeRegistryClose();
	LoadEnvironment(FileGetReference("showroom.senv"));
	FlushKeys();
}

int GetPlayerRanking()
{
	tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics;
	if(phys->lapCount>gGameInfo->numLaps&&!gDisqualified)
	{
		int place=1;
		for(int i=0;i<gGameInfo->numPlayers;i++)
		{
			tCarPhysics *phys2=(tCarPhysics*)gCarEntities[i]->physics;
			if(phys2->lapCount>gGameInfo->numLaps)
				if(phys2->lapTimes[gGameInfo->numLaps]<phys->lapTimes[gGameInfo->numLaps])
					place++;
		}
		if(gGameInfo->maxTime)
			return phys->lapTimes[gGameInfo->numLaps];
		if(gGameInfo->numLaps==-1)
			return gBestLapTime;
		return place;
	}
	else 
		return -1;
}

int RunGame(tGameInfo *gInfo)
{
	gReplay=false;
	int place;
	int reverse=gInfo->reverse;
	do{
		gGameInfo=gInfo;
		if(InitGame())
			while(!gGameEnd)
				GameFrame();
		place=GetPlayerRanking();
		gInfo->reverse=reverse;
		ExitGame();
		gInfo->reverse=reverse;
	}while(gGameEnd==kEndGameRestart);
	gInterfaceGInfo=*gInfo;
	StartBackgroundReplay();			
	return place;
}