//gameframe.cpp //Calculate a single physics frame #include #include #include "gametime.h" #include "entities.h" #include "carphysics.h" #include "gameframe.h" #include "controls.h" #include "gamemem.h" #include "particles.h" #include "renderframe.h" #include "collision.h" #include "networkphysics.h" #include "gamesound.h" #include "gameinitexit.h" #include "config.h" #include "parser.h" #include "roads.h" #include "gamesystem.h" #include "interfaceutil.h" #include "text.h" #include "random.h" #include "log.h" #include "sky.h" #include "tracker.h" #include "environment.h" #define kGraphFrameTimeCount 10 //number of frames used to calculate average FPS int gFrameCount; //number of physics frames calculated int gGraphFrameCount; //number of graphical frames actually drawn int gPaused=false; int gNetPauseTime=0; int gDisabledRestart=0; float gStartPauseTime; float gStartTime; //time at the start of the game (in seconds) float gTimeStretch=1.0; //Used for Time Trial int gCurrentLapStart; int gBestLapStart; int gLastLapTime; int gBestLapTime=0; int gWorldRecord=0,gLocalRecord=0; char gRecordName[255]; float gAccelSignDisplayIntensity,gAccelSignDisplayCorner; //the times when the last graphics frames have been draw float gLastGraphFrameTime[kGraphFrameTimeCount]; int gGameEnd; //flag to signal end of game int gDisqualified; int gRaceFinished; //flag to signal that at least one player has finished all laps. float gFPS; //Calculate physics for a solid entity //(basically any object in the game which is not a car) void SolidPhysicsEntity(tGameEntity *entity) { //get soldid entity definition tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); //is this entity freely movable? if(ent->movable) //gravitational acceleration entity->velo.y-=gEnvironment->gravity*kFrameTime; float dampfactor=0; float v=~entity->velo; if(v<7)dampfactor=(7-v)/7; *MatrixGetXVector(entity->rVelo)=Vector(1,0,0)+(1-dampfactor*kFrameTime)*(*MatrixGetXVector(entity->rVelo)-Vector(1,0,0)); *MatrixGetYVector(entity->rVelo)=Vector(0,1,0)+(1-dampfactor*kFrameTime)*(*MatrixGetYVector(entity->rVelo)-Vector(0,1,0)); *MatrixGetZVector(entity->rVelo)=Vector(0,0,1)+(1-dampfactor*kFrameTime)*(*MatrixGetZVector(entity->rVelo)-Vector(0,0,1)); entity->velo=(1-dampfactor*kFrameTime)*entity->velo; MatrixReAdjust(entity->rVelo); //does this entity follow a constant path? if(ent->followPath) if(ent->pathType==kPathTypeCircular) { MatrixRotY(entity->dir,ent->pathVelo*kFrameTime); entity->velo=*MatrixGetZVector(entity->dir)*ent->pathVelo*ent->pathSize; } else { entity->velo=Vector( 3*sin(gFrameCount*kFrameTime*ent->pathVelo*0.2+1)*ent->pathVelo*0.2, ent->pathSize*sin(gFrameCount*kFrameTime*ent->pathVelo)*ent->pathVelo, 2*sin(gFrameCount*kFrameTime*ent->pathVelo*0.3-2)*ent->pathVelo*0.3 ); entity->lastActivity=gFrameCount; } } //Calculate one physics frame for all game entities void PhysicsFrame() { //do this for every entity in the game tGameEntity *entity=(tGameEntity*)gFirstEntity->next; while(entity!=gFirstEntity) { if(entity->id>=0) { //is this entity handled by this machine? if(entity->physicsMachine==kPhysicsLocal) { //this check is used to prevent entitys which are just standing still //from eating up CPU cycles if(entity->lastActivity+kFPS>gFrameCount) { //handle entity physics switch(entity->physicsType) { case kPhysicsTypeCar: CarPhysicsEntity(entity); break; case kPhysicsTypeSolid: SolidPhysicsEntity(entity); break; } //Matrices tend to 'drift' apart du to inaccurate //floating-point operations. these routines test matrices //and re-adjust them if necessary. if(!MatrixVerify(entity->dir)) MatrixReAdjust(entity->dir); if(!MatrixVerify(entity->rVelo)) MatrixReAdjust(entity->rVelo); } } else if(entity->physicsType==kPhysicsTypeCar) CarPhysicsEntity(entity); } //proceed to next entity. entity=(tGameEntity*)entity->next; } } //Move around all the entities. void MotionFrame() { //do this for every entity in the game tGameEntity *entity=(tGameEntity*)gFirstEntity->next; while(entity!=gFirstEntity) { entity->oldPos=entity->pos; MatrixCopy(entity->dir,entity->oldDir); //this check is used to prevent entitys which are just standing still //from eating up CPU cycles if(entity->lastActivity+kFPS>gFrameCount) { //move entity switch(entity->physicsType) { case kPhysicsTypeCar: case kPhysicsTypeGhost: CarMotionEntity(entity); break; case kPhysicsTypeSolid: entity->pos=entity->pos+entity->velo*kFrameTime; MatrixMult(entity->dir,entity->rVelo,entity->dir); break; } } //Check if the entity is moving, and update lastActivity if(entity->velo*entity->velo>kMinActivityVelo*kMinActivityVelo) entity->lastActivity=gFrameCount; //proceed to next entity. entity=(tGameEntity*)entity->next; } } //checks whether there is time to draw a graphics frame //and calculates frame rate int CheckFrameTime() { int optFrameCount; int retval=false; float curTime; //get current time curTime=(TimeGetSeconds()-gStartTime)*gTimeStretch; //calculate number of frames we should have calculated in that time optFrameCount=curTime*kFPS; //if we have calculated more frames than required, we have //time to draw a screen update. if(gFrameCount>optFrameCount) { //calculate frame rate. if(curTime-gLastGraphFrameTime[0]>0) gFPS=((float)kGraphFrameTimeCount)/(curTime-gLastGraphFrameTime[0]); else gFPS=0; MemoryMove(gLastGraphFrameTime,gLastGraphFrameTime+1,sizeof(float)*(kGraphFrameTimeCount-1)); gLastGraphFrameTime[kGraphFrameTimeCount-1]=curTime; gGraphFrameCount++; retval=true; } if(gFPS<10) gConfig->gfxDynamics-=kFrameTime; else if(gFPS<15) gConfig->gfxDynamics-=kFrameTime*0.3; if(gFPS>35) gConfig->gfxDynamics+=kFrameTime; else if(gFPS>25) gConfig->gfxDynamics+=kFrameTime*0.3; if(gConfig->gfxDynamics<0.0)gConfig->gfxDynamics=0; if(gConfig->gfxDynamics>1.0)gConfig->gfxDynamics=1; return retval; } //after drawing a graphics frame, check whether there is still time //left. in that case wait, so we aren't running too fast. void CheckTimeSkip() { int optFrameCount; float curTime; curTime=(TimeGetSeconds()-gStartTime)*gTimeStretch; optFrameCount=curTime*kFPS; while(gFrameCount>optFrameCount+1) { curTime=(TimeGetSeconds()-gStartTime)*gTimeStretch; optFrameCount=curTime*kFPS; } } //start the frame counters void StartFrameCount() { gStartTime=TimeGetSeconds(); gFrameCount=0; gGraphFrameCount=0; gCurrentLapStart=0; gBestLapStart=0; gLastLapTime=0; gBestLapTime=0; gWorldRecord=-1; if(gGameInfo->numLaps==-1||gGameInfo->maxTime!=0) TrackerFetchRecord(gGameInfo->map,gGameInfo->playerCars[0],gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse); } void PauseGame() { if(!gPaused) { gPaused=true; gStartPauseTime=TimeGetSeconds(); SoundPause(); FFBStop(); } } void UnPauseGame() { if(gPaused) { gPaused=false; gStartTime+=TimeGetSeconds()-gStartPauseTime; SoundReInit(); } } void ResetRocks() { int id=0; for(int i=0;inumObjs;i++) { if(gMapInfo->obj[i].envFlags==0||(gMapInfo->obj[i].envFlags&gEnvironment->envFlags)) { char *extension=FileGetExtension(gMapInfo->obj[i].model); if(extension) if(!_stricmp(extension,kFileTypeSolidEntity)) { tGameEntity *entity=(tGameEntity*)gFirstEntity->next; //see if we find the entity it belongs to while(entity!=gFirstEntity) { //is this the entity the message refers to? if(entity->id==id) { entity->pos=gMapInfo->obj[i].pos; EulerAnglesToMatrix(gMapInfo->obj[i].dir*kDegreeRadians,entity->dir); entity->velo=Vector(0,0,0); MatrixIdentity(entity->rVelo); entity->lastActivity=-1000; } entity=(tGameEntity*)entity->next; } } id++; } } } //tests if entity has passed the next check-point //(check-points are invisible, and only used to calculate //lead and trail delays). void CheckPointPass(tGameEntity *entity) { tCarPhysics *phys=(tCarPhysics*)entity->physics; if(phys->lap==-1) return; if(gGameInfo->numLaps!=-1) { if(!gCheckPoints[phys->checkPoint].passTimes[phys->lap].setBy) { gCheckPoints[phys->checkPoint].passTimes[phys->lap].time=gFrameCount*kFrameTime; gCheckPoints[phys->checkPoint].passTimes[phys->lap].setBy=entity; } else { phys->lead=gCheckPoints[phys->checkPoint].passTimes[phys->lap].time-gFrameCount*kFrameTime; if(!gCheckPoints[phys->checkPoint].passTimes[phys->lap].hit) { tCarPhysics *phys2=(tCarPhysics*)gCheckPoints[phys->checkPoint].passTimes[phys->lap].setBy->physics; phys2->lead=gFrameCount*kFrameTime-gCheckPoints[phys->checkPoint].passTimes[phys->lap].time; gCheckPoints[phys->checkPoint].passTimes[phys->lap].hit=true; } } } else { gCheckPoints[phys->checkPoint].passTimes[0].time=(gFrameCount-gCurrentLapStart)*kFrameTime; if(phys->lapCount>1) phys->lead=gCheckPoints[phys->checkPoint].passTimes[1].time-gCheckPoints[phys->checkPoint].passTimes[0].time; } if(phys->checkPoint==0||(!gMapInfo->loop&&phys->checkPoint==kNumCheckPoints-1)) { if(!gReplay&&entity==gViewedEntity) if(phys->lapCount&&(phys->lapCountnumLaps||gGameInfo->numLaps==-1)) { float lastLapTime; if(gGameInfo->numLaps!=-1) lastLapTime=gFrameCount-phys->lapTimes[phys->lapCount-1]; else lastLapTime=gFrameCount-gCurrentLapStart; lastLapTime*=kFrameTime; TextPrintfToBufferFormatedFading(Vector(0,0.4),0.04,kTextAlignMiddle,0.6,1.2,"Time Taken: %d:%02d'%02d",((int)lastLapTime)/60,((int)lastLapTime)%60,((int)(lastLapTime*100))%100); } if(gGameInfo->numLaps!=-1) phys->lapTimes[phys->lapCount]=gFrameCount; else if(!gReplay){ if(phys->lapCount>=1) { gLastLapTime=gFrameCount-gCurrentLapStart; if(!gDisqualified) { TrackerRegisterLapTime(gGameInfo->map,gGameInfo->playerCars[0],gLastLapTime,gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse); if((gLastLapTimenumPersonalRecords;i++) if(gConfig->records[i].map==gGameInfo->map&&gConfig->records[i].car==gGameInfo->playerCars[0] &&gConfig->records[i].mode==gGameInfo->arcade&&gConfig->records[i].direction==gGameInfo->reverse) { found=true; if(gConfig->records[i].time>gLocalRecord) gConfig->records[i].time=gLocalRecord; } if(!found&&gConfig->numPersonalRecordsrecords[gConfig->numPersonalRecords].time=gLocalRecord; gConfig->records[gConfig->numPersonalRecords].car=gGameInfo->playerCars[0]; gConfig->records[gConfig->numPersonalRecords].map=gGameInfo->map; gConfig->records[gConfig->numPersonalRecords].mode=gGameInfo->arcade; gConfig->records[gConfig->numPersonalRecords].direction=gGameInfo->reverse; gConfig->numPersonalRecords++; } } LogToGhostLog(); for(int i=0;imaxTime!=0&&!gDisqualified) if(phys->lapCount>=1) TrackerRegisterLapTime(gGameInfo->map,gGameInfo->playerCars[0],phys->lapTimes[phys->lapCount]-kStartGameDelaySeconds*kFPS,gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse); phys->lapCount++; if(!gReplay&&entity==gViewedEntity&&gMapInfo->loop) if(phys->lapCountnumLaps||gGameInfo->numLaps==-1) TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,0.9,1.8,"Lap %d",phys->lapCount); else if(phys->lapCount==gGameInfo->numLaps) TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,0.9,1.8,"Final Lap"); if(gGameInfo->numLaps==-1) ResetRocks(); if(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1) { if(!phys->finishTime) phys->finishTime=phys->lapTimes[gGameInfo->numLaps]; if(gGameInfo->network) if(gGameInfo->playerID==0) for(int i=0;inumPlayers;i++) if(gCarEntities[i]==entity) { tFinishTimeMessage m; m.time=phys->finishTime; m.player=i; S32Swap(m.time); S32Swap(m.player); NetworkSendPacket(kMessageTypeFinishTime,&m,sizeof(tFinishTimeMessage),kMessagePriorityHigh,kMessageSendToAllButSelf); } if(!gRaceFinished) { gRaceFinished=true; if(entity!=gViewedEntity&&!gReplay) if(gGameInfo->network) { for(int i=0;inumPlayers;i++) if(gCarEntities[i]==entity) TextPrintfToBufferFormatedFading(Vector(0,1),0.06,kTextAlignMiddle,2,3,"%s won the race!",gGameInfo->playerNames[i]); } else TextPrintfToBufferFormatedFading(Vector(0,1),0.06,kTextAlignMiddle,2,3,"%s won the race!",phys->car.carName); } } } phys->checkPoint=(phys->checkPoint+1)%kNumCheckPoints; } void LapCheck() { if(gGameInfo->demolition) { if(gFrameCount>=gDisabledRestart+kFPS*10&&gDisabledRestart) { tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics; phys->damage=0; RoadCenterCar(gCarEntities[gGameInfo->playerID]); gDisabledRestart=0; } int numDisabled=0; for(int i=0;inumPlayers;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(!gDisabledRestart) gDisabledRestart=gFrameCount; } else { if(gFrameCount*kFrameTime>=3) for(int i=0;inumPlayers;i++) { tCarPhysics *phys=(tCarPhysics*)gCarEntities[i]->physics; if(!(gMapInfo->needsToStop&&(sqr(gCarEntities[i]->velo)>0.5)&&(phys->checkPoint==kNumCheckPoints-1))) { if(phys->lap<=gGameInfo->numLaps||(phys->checkPoint==0&&phys->lap==gGameInfo->numLaps+1)||gGameInfo->numLaps==-1) { //this doesn't all make sense but works for now. if(gGameInfo->reverse){ if(gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index>gCheckPoints[phys->checkPoint].index){ if(phys->positioncheckPoint].index||(phys->position>gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index&&gMapInfo->loop)) CheckPointPass(gCarEntities[i]); }else if(phys->positioncheckPoint].index&&phys->position>gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index) CheckPointPass(gCarEntities[i]); } else{ if(gCheckPoints[(phys->checkPoint+10)%kNumCheckPoints].indexcheckPoint].index){ if(phys->position>gCheckPoints[phys->checkPoint].index) CheckPointPass(gCarEntities[i]); }else if(phys->position>gCheckPoints[phys->checkPoint].index&&phys->positioncheckPoint+10)%kNumCheckPoints].index) CheckPointPass(gCarEntities[i]); } } } for(int j=0;jnumPlayers;j++) { tCarPhysics *jPhys=(tCarPhysics*)gCarEntities[j]->physics; if(((gGameInfo->reverse&&phys->positionposition)||(!gGameInfo->reverse&&phys->position>jPhys->position))&&phys->lap>jPhys->lap+phys->lappedPlayers[j]) { phys->lappedPlayers[j]++; if(gCarEntities[i]==gViewedEntity&&!gReplay&&gGameInfo->netID[j]!=-1&&gCarEntities[j]->id!=-1) TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,2.0,3.0,"You lapped %s!!",gGameInfo->network?gGameInfo->playerNames[j]:jPhys->car.carName); else if(gCarEntities[j]==gViewedEntity&&!gReplay) TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,2.0,3.0,"You were lapped by %s!!",gGameInfo->network?gGameInfo->playerNames[i]:phys->car.carName); } } } if(gGameInfo->numLaps==-1||gGameInfo->maxTime!=0) TrackerWaitForLapTimeRegistry(); gAccelSignDisplayIntensity-=kFrameTime*0.5; } } #define kMinSteerResistance 0.3 #define kNormalSteerAlignment 0.65 #define kSteerResistanceFactor 0.4 #define kSteerAlignmentFactor 1.5 //creates force-feedback effects when the the players car is sliding void FFBResponse() { tCarPhysics *phys=(tCarPhysics*)gViewedEntity->physics; tCarDefinition *car=&(phys->car); float slideVelo=0; for(int i=0;inumWheels;i++) slideVelo+=gSurfaceTypes->types[phys->wheels[i].surfaceType].soundEnable?phys->wheels[i].slipVelo:0; slideVelo/=car->numWheels; slideVelo*=0.02; if(slideVelo>0.5)slideVelo=0.5; FFBiShockDirect(slideVelo,slideVelo); float velo=~gViewedEntity->velo; float bumpHeight=(gSurfaceTypes->types[phys->wheels[0].surfaceType].bumpHeight+gSurfaceTypes->types[phys->wheels[1].surfaceType].bumpHeight)*0.5; FFBSetGoundRumble(velo,bumpHeight*10); float wheelsSlipFactor=1-(fabs(phys->wheels[0].slipAngle)+fabs(phys->wheels[1].slipAngle))*5-(fabs(phys->wheels[0].slip)+fabs(phys->wheels[1].slip))*0.5; if(wheelsSlipFactor<0)wheelsSlipFactor=0; int wheelsOnGround=0; for(int i=0;i<2;i++) if(phys->wheels[i].suspensionwheels[i].maxSuspension) wheelsOnGround++; wheelsSlipFactor*=wheelsOnGround/2.0; float steerResistance=(1-kMinSteerResistance)*(1/(velo*0.2+1))+kMinSteerResistance; float steerAlignment=velo<10?velo/10*kNormalSteerAlignment:kNormalSteerAlignment+((velo-10)/70)*(1-kNormalSteerAlignment); FFBSetSteerResistance(kSteerResistanceFactor*steerResistance*wheelsSlipFactor,kSteerAlignmentFactor*steerAlignment*wheelsSlipFactor); } tVector3 gCameraViewPos; void GetNewTripod(tGameEntity *cameraViewEntity) { float side=RandomProb(0.5)?-1:1; float speed; gCameraEntity->pos=RoadGetNextWaypoint(cameraViewEntity->pos,&cameraViewEntity->lastRoadIndex,&side,&speed,((gCameraEntity->lastRoadIndexlastRoadIndex)^gGameInfo->reverse)?100:-100)+Vector(0,RandomFl(4,13),0); } void CalcCamera() {/* gCameraEntity->pos=gViewedEntity->pos+Vector(0,2000,0); MatrixIdentity(gCameraEntity->dir); MatrixRotX(gCameraEntity->dir,PI/2); return; */ tGameEntity *cameraViewEntity=gCarEntities[gReplayViewedEntityID]; gCameraEntity->velo=cameraViewEntity->velo; tVector3 veloFlat,posFlat; tVector3 cameraPos=gCameraEntity->pos; switch(gCameraMode) { case kCameraFree: if(GetInterfaceKey(kInterfaceKeyLeft)) MatrixRotY(gCameraEntity->dir,kFrameTime*3); if(GetInterfaceKey(kInterfaceKeyRight)) MatrixRotY(gCameraEntity->dir,-kFrameTime*3); if(GetInterfaceKey(kInterfaceKeyUp)) { tMatrix3 m; RotationVectorToMatrix(-kFrameTime*2**MatrixGetXVector(gCameraEntity->dir),m); MatrixMult(gCameraEntity->dir,m,gCameraEntity->dir); } if(GetInterfaceKey(kInterfaceKeyDown)) { tMatrix3 m; RotationVectorToMatrix(kFrameTime*2**MatrixGetXVector(gCameraEntity->dir),m); MatrixMult(gCameraEntity->dir,m,gCameraEntity->dir); } if(GetInterfaceKey(kInterfaceKeySpace)&&!gInputChatMode) cameraPos=cameraPos+*MatrixGetZVector(gCameraEntity->dir)*kFrameTime*25; gCameraEntity->velo=Vector(0,0,0); break; case kCameraChase: veloFlat=Vector(cameraViewEntity->velo.x,0,cameraViewEntity->velo.z); posFlat=Vector(cameraViewEntity->pos.x,0,cameraViewEntity->pos.z); cameraPos=posFlat-(sqr(veloFlat)dir):!veloFlat)*(gCameraReverse?-9:9); cameraPos.y=cameraViewEntity->pos.y+2.7; break; case kCameraChaseClose: veloFlat=Vector(cameraViewEntity->velo.x,0,cameraViewEntity->velo.z); posFlat=Vector(cameraViewEntity->pos.x,0,cameraViewEntity->pos.z); cameraPos=posFlat-(sqr(veloFlat)dir):!veloFlat)*(gCameraReverse?-6:6); cameraPos.y=cameraViewEntity->pos.y+1.85; break; case kCameraLeftSide: cameraPos=cameraViewEntity->pos-*MatrixGetXVector(cameraViewEntity->dir)*(gCameraReverse?-10:10); cameraPos.y=cameraViewEntity->pos.y+2; break; case kCameraTripod: if(sqr(gCameraEntity->pos-cameraViewEntity->pos)>sqr(120)) GetNewTripod(cameraViewEntity); cameraPos=gCameraEntity->pos; gCameraEntity->velo=Vector(0,0,0); break; case kCameraTop: { float heigth=7+~cameraViewEntity->velo; if(heigth>40) heigth=40; cameraPos=cameraViewEntity->pos+Vector(0,heigth,1); } break; case kCameraCockpit: case kCameraCockpitCarHidden: { tCarPhysics *phys=(tCarPhysics*)cameraViewEntity->physics; tCarDefinition *car=&(phys->car); cameraPos=cameraViewEntity->pos +((gCameraMode==kCameraCockpit)?(*MatrixGetXVector(cameraViewEntity->dir)*car->driverPos.x):Vector(0,0,0)) +*MatrixGetYVector(cameraViewEntity->dir)*(car->driverPos.y+0.5) +*MatrixGetZVector(cameraViewEntity->dir)*car->driverPos.z; } break; case kCameraLeftWheel: cameraPos=cameraViewEntity->pos-*MatrixGetXVector(cameraViewEntity->dir)*(gCameraReverse?-1.2:1.2)+*MatrixGetYVector(cameraViewEntity->dir)*0.4+*MatrixGetZVector(cameraViewEntity->dir)*0.5; break; } if(gFrameCount*kFrameTime<1.5&&!gReplay) { float f=(gFrameCount*kFrameTime-1.5)/3*2*PI; cameraPos=cameraViewEntity->pos- *MatrixGetXVector(cameraViewEntity->dir)*sin(f)*15+ *MatrixGetYVector(cameraViewEntity->dir)*(2.0-20.0*f)- *MatrixGetZVector(cameraViewEntity->dir)*cos(f)*15; } tVector3 cameraViewPos=cameraPos-cameraViewEntity->pos; if(sqr(gCameraViewPos-cameraViewPos)pos+cameraViewPos; // if(sqr(gCameraEntity->pos-newPos)>25) // gCameraEntity->lastRoadIndex=0; gCameraEntity->pos=newPos; cameraViewEntity=cameraViewEntity; if(gCameraMode==kCameraCockpit||gCameraMode==kCameraLeftWheel||gCameraMode==kCameraCockpitCarHidden&&!(gFrameCount*kFrameTime<1.5&&!gReplay)) { MemoryMove(gCameraEntity->dir,cameraViewEntity->dir,sizeof(tMatrix4)); if((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&gCameraReverse) { *MatrixGetXVector(gCameraEntity->dir)=-*MatrixGetXVector(gCameraEntity->dir); *MatrixGetZVector(gCameraEntity->dir)=-*MatrixGetZVector(gCameraEntity->dir); } } else if(gCameraMode!=kCameraFree) { *MatrixGetZVector(gCameraEntity->dir)=!(cameraViewEntity->pos-gCameraEntity->pos+Vector(0,1.2,0)); *MatrixGetYVector(gCameraEntity->dir)=Vector(0,1,0); MatrixReAdjust(gCameraEntity->dir); } if(gCameraMode!=kCameraFree) { tVector3 normal; float dist=GetGroundOffset(gCameraEntity->pos,&gCameraEntity->lastRoadIndex,&normal,0); if(dist<0) if(!isinf(dist)) gCameraEntity->pos=gCameraEntity->pos-normal*dist; } } void MoveGhostCar() { if(gGameInfo->numLaps!=-1) return; tCarPhysics *phys=(tCarPhysics*)gCarEntities[0]->physics; if(phys->lapCount<2) return; LogReplayGhostFrame(); } //use this define to disable all game physics. //useful to test raw graphics performance. //#define __NOPHYSICS //Calculate one game physics frame void GameFrame() { /*static int p=false; if(Button()&&!p){gCarEntities[0]->pos=gCameraEntity->pos;p=true;}else if(!Button())p=false;*/ if(!gPaused) { #ifndef __NOPHYSICS //Move all entities MotionFrame(); //check for collision between entities and ground or each other CollisionFrame(); //check if cars have passed any checkpoint or lap start/finish line. LapCheck(); //send local physics data to network NetworkSendPhysics(); #endif } //get AI or user input for local cars ControlFrame(); if(!gPaused) { #ifndef __NOPHYSICS //Calculate car sound effects SoundFrame(); //calculate all local entities physics PhysicsFrame(); //process particles ParticlesProcess(); } //receive physics for remote entities NetworkReceivePhysics(); if(!gPaused) { MoveGhostCar(); //create force-feedback effects when the the players car is sliding FFBResponse(); #endif } CalcCamera(); //check if we have time to draw a graphics frame if(CheckFrameTime()||gPaused) { //draw frame RenderFrame(true); //if we are too fast, waste some time CheckTimeSkip(); if(gLightningflashIntensity*kFrameTime)) gLightning=gFrameCount; if(gFrameCount>gNetPauseTime&&gNetPauseTime!=0) PauseGame(); if(gFrameCount%30) SystemPoll(true); } int GameReplayFrame() { if(!gPaused||!gBackgroundReplay) { CollisionFrame(); LapCheck(); SoundFrame(); ParticlesProcess(); MotionFrame(); LogReplayFrame(); if(!(gBackgroundReplay&&gInterfaceType)) ControlFrame(); PhysicsFrame(); } CalcCamera(); if(gGameInfo->numLaps==-1&&!gBackgroundReplay) TrackerWaitForLapTimeRegistry(); int hasRendered=false; //check if we have time to draw a graphics frame if(CheckFrameTime()||gPaused) { //draw frame RenderFrame(true); //if we are too fast, waste some time CheckTimeSkip(); hasRendered=true; } if(gFrameCount%30&&!gBackgroundReplay) SystemPoll(true); if(!gPaused||!gBackgroundReplay) gFrameCount++; return hasRendered; }