Redline/source/gameframe.cpp

846 lines
26 KiB
C++
Raw Normal View History

//gameframe.cpp
//Calculate a single physics frame
#include <stdio.h>
#include <math.h>
#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;i<gMapInfo->numObjs;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->lapCount<gGameInfo->numLaps||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((gLastLapTime<gBestLapTime||gBestLapTime==0))
{
gBestLapTime=gLastLapTime;
gBestLapStart=gCurrentLapStart;
if(gBestLapTime<gLocalRecord||gLocalRecord==0)
{
gLocalRecord=gBestLapTime;
int found=false;
for(int i=0;i<gConfig->numPersonalRecords;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->numPersonalRecords<kMaxPersonalRecords)
{
gConfig->records[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;i<kNumCheckPoints;i++)
gCheckPoints[i].passTimes[1].time=gCheckPoints[i].passTimes[0].time;
}
}
}
LogReset();
gReplayIndex=0;
gCurrentLapStart=gFrameCount;
}
if(!gReplay&&gGameInfo->maxTime!=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->lapCount<gGameInfo->numLaps||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;i<gGameInfo->numPlayers;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;i<gGameInfo->numPlayers;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;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(!gDisabledRestart)
gDisabledRestart=gFrameCount;
}
else
{
if(gFrameCount*kFrameTime>=3)
for(int i=0;i<gGameInfo->numPlayers;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->position<gCheckPoints[phys->checkPoint].index||(phys->position>gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index&&gMapInfo->loop))
CheckPointPass(gCarEntities[i]);
}else if(phys->position<gCheckPoints[phys->checkPoint].index&&phys->position>gCheckPoints[(phys->checkPoint+1)%kNumCheckPoints].index)
CheckPointPass(gCarEntities[i]);
}
else{
if(gCheckPoints[(phys->checkPoint+10)%kNumCheckPoints].index<gCheckPoints[phys->checkPoint].index){
if(phys->position>gCheckPoints[phys->checkPoint].index)
CheckPointPass(gCarEntities[i]);
}else if(phys->position>gCheckPoints[phys->checkPoint].index&&phys->position<gCheckPoints[(phys->checkPoint+10)%kNumCheckPoints].index)
CheckPointPass(gCarEntities[i]);
}
}
}
for(int j=0;j<gGameInfo->numPlayers;j++)
{
tCarPhysics *jPhys=(tCarPhysics*)gCarEntities[j]->physics;
if(((gGameInfo->reverse&&phys->position<jPhys->position)||(!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;i<car->numWheels;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].suspension<car->wheels[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->lastRoadIndex<cameraViewEntity->lastRoadIndex)^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)<sqr(1.5)?*MatrixGetZVector(cameraViewEntity->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)<sqr(1.5)?*MatrixGetZVector(cameraViewEntity->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)<sqr(11)&&gCameraMode!=kCameraTripod&&gCameraMode!=kCameraFree&&cameraViewEntity==cameraViewEntity)
//camera interpolation
cameraViewPos=gCameraViewPos+(cameraViewPos-gCameraViewPos)*kFrameTime*10;
gCameraViewPos=cameraViewPos;
tVector3 newPos=cameraViewEntity->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(gLightning<gFrameCount-10)
gLightning=0;
}
TextClearBuffer();
if(!gPaused)
gFrameCount++;
if(RandomProb(gEnvironment->flashIntensity*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;
}