Redline/source/gameinitexit.cpp
maride 02061d74c2 Original 1.0.5 code
(as received from Jonas Echterhoff)
2016-04-02 14:43:55 +02:00

772 lines
20 KiB
C++
Executable File

//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;
}