//carphysics.cpp
//simulation of all forces and tourques moving the cars around 

#include <math.h>

#include "stdtypes.h"
#include "reg_tool_3.h"
#include "rt3_redline.h"

#include "vectors.h"
#include "entities.h"
#include "carphysics.h"
#include "gamemem.h"
#include "gameframe.h"
#include "roads.h"
#include "text.h"
#include "particles.h"
#include "tracks.h"
#include "collision.h"
#include "config.h"
#include "environment.h"
#include "gamesound.h"
#include "renderframe.h"
#include "networkphysics.h"

#define kMaxSmoke 120.0

#define kIndicatorTime 0.5
#define kMaxTwistAngle (PI/12)
#define kSpinRotations 10.0
#define kTrackPrecision 5 //each nTH frame a skid mark is generated

void InstallCarAddOns(tCarPhysics *phys)
{
	tCarDefinition *car=&(phys->car);
	for(int i=0;i<car->numAddOns;i++)
		//is this add-on installed?
		if((phys->addOns>>i)&1)
			if(!(phys->addOns&car->addOns[i].conflicts))
			{
				car->mass+=car->addOns[i].mass;
				car->massCenter=car->massCenter+car->addOns[i].massCenter;
				car->rearLift+=car->addOns[i].rearLift;
				car->frontLift+=car->addOns[i].frontLift;
				car->frontAirResistance+=car->addOns[i].frontAirResistance;	
				
				if(car->addOns[i].powerPercent>0)
				{
					car->power*=car->addOns[i].powerPercent;
					car->powerRPM*=car->addOns[i].powerPercent;
				}
				if(car->addOns[i].frontSwayBar>0)
					car->frontSwayBar=car->addOns[i].frontSwayBar;
				if(car->addOns[i].exhaustFire>0)
					car->exhaustFire=car->addOns[i].exhaustFire;
				if(car->addOns[i].rearSwayBar>0)
					car->rearSwayBar=car->addOns[i].rearSwayBar;
				if(car->addOns[i].damperStrength>0)
					car->damperStrength=car->addOns[i].damperStrength;
				if(car->addOns[i].engineInertia>0)
					car->engineInertia=car->addOns[i].engineInertia;
				if(car->addOns[i].topGearRatio>0)
					car->gearRatios[car->numGears-1]=car->addOns[i].topGearRatio;
				if(car->addOns[i].maxRPM>0)
					car->maxRPM=car->addOns[i].maxRPM;
				if(car->addOns[i].track>0)
				{
					car->wheels[0].pos.x-=car->addOns[i].track;
					car->wheels[1].pos.x+=car->addOns[i].track;
					car->wheels[2].pos.x-=car->addOns[i].track;
					car->wheels[3].pos.x+=car->addOns[i].track;
				}
				if(car->addOns[i].frontWheelWidth>0)
				{
					car->wheels[0].width=car->addOns[i].frontWheelWidth;
					car->wheels[1].width=car->addOns[i].frontWheelWidth;
				}
				if(car->addOns[i].rearWheelWidth>0)
				{
					car->wheels[2].width=car->addOns[i].rearWheelWidth;
					car->wheels[3].width=car->addOns[i].rearWheelWidth;
				}
				if(car->addOns[i].frontMaxSuspension>0)
				{
					car->wheels[0].maxSuspension=car->addOns[i].frontMaxSuspension;
					car->wheels[1].maxSuspension=car->addOns[i].frontMaxSuspension;
				}
				if(car->addOns[i].rearMaxSuspension>0)
				{
					car->wheels[2].maxSuspension=car->addOns[i].rearMaxSuspension;
					car->wheels[3].maxSuspension=car->addOns[i].rearMaxSuspension;
				}
				if(car->addOns[i].torquePercent>0)
					car->torque*=car->addOns[i].torquePercent;
				if(car->addOns[i].differentialLockCoefficient>0)
					car->differentialLockCoefficient=car->addOns[i].differentialLockCoefficient;
				if(car->addOns[i].finalDriveRatio>0)
					car->finalDriveRatio=car->addOns[i].finalDriveRatio;
				if(car->addOns[i].gearSwitchTime>0)
					car->gearSwitchTime=car->addOns[i].gearSwitchTime;
			}
}


float WheelGroundDistance(tGameEntity *carEntity,tVector3 wheelPos,tVector3 yDir,tWheelDefinition *wheel,tVector3 *groundNormal,float oldHeight,int *surfaceType,float *bump)
//returns the distance between the wheel at wheelPos and the ground.
//gives the normal of the ground under the wheel in groundNormal
//and the surface type of the ground in surfaceType
{
	tVector3 wheelGroundPoint=wheelPos-yDir*(wheel->radius+oldHeight);
	return GetGroundOffsetAndBump(wheelGroundPoint,&carEntity->lastRoadIndex,groundNormal,surfaceType,bump)+oldHeight;
}

void CalcWheelPositions(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
//Calculates the positions and directions/angles of all wheels of the car passed in carEntity.
{
	for(int i=0;i<car->numWheels;i++)
	{
		wheels[i].pos=car->wheels[i].pos*carEntity->dir;
		wheels[i].velo=((wheels[i].pos*carEntity->rVelo)-wheels[i].pos)*kFPS+carEntity->velo;
		wheels[i].oldAngle=phys->wheels[i].angle;
		if(phys->wheels[i].rotation>=2*PI)
			phys->wheels[i].rotation-=2*PI;
		else if(phys->wheels[i].rotation<0)
			phys->wheels[i].rotation+=2*PI;
		phys->wheels[i].angle=car->wheels[i].maxAngle*phys->steering;//*(0.5+0.5*sqr(phys->steering));
	}
}

void CalcWheelGroundOffsets(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
//Calculates the spring supression for all wheels of the car passed in carEntity.
{
	for(int i=0;i<car->numWheels;i++)
	{
		float dist=WheelGroundDistance(carEntity,wheels[i].pos+carEntity->pos,*MatrixGetYVector(carEntity->dir),&car->wheels[i],&wheels[i].groundNormal,phys->wheels[i].suspension,&phys->wheels[i].surfaceType,&(wheels[i].bump));
		if(wheels[i].onGround=dist<car->wheels[i].maxSuspension)
		{
			phys->wheels[i].suspension=dist;
			if(phys->wheels[i].suspension<0)
				phys->wheels[i].suspension=0;
		}
		else
			phys->wheels[i].suspension=car->wheels[i].maxSuspension;	
	}
}


void CalcSwayBars(tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
//Calculates the Forces generated by the cars Anti-Sway-Bars.
{
	float force;
	int arcade=(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo);

	force=(phys->wheels[1].suspension-phys->wheels[0].suspension)*(car->frontSwayBar+(arcade?car->mass*10:0));
	wheels[0].suspensionForce+=force;
	wheels[1].suspensionForce-=force;
	force=(phys->wheels[3].suspension-phys->wheels[2].suspension)*(car->rearSwayBar+(arcade?car->mass*10:0));
	wheels[2].suspensionForce+=force;
	wheels[3].suspensionForce-=force;
}


void CalcWheelSupension(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
//Calculates spring and damper forces
{
	//Calculate the distance of each wheel from car's center of gravity
	float totalInvMassDistance=0;
	float invMassDistance[kMaxWheels];
	for(int i=0;i<car->numWheels;i++)
	{
		invMassDistance[i]=1/~(car->massCenter-car->wheels[i].pos);
		totalInvMassDistance+=invMassDistance[i];
	}
	
	//factor used to calculate the spring forces
	float totalSpringForceFactor=car->mass*2*gEnvironment->gravity;
	
	tVector3 zDir=*MatrixGetZVector(carEntity->dir);
	float airRes=~carEntity->velo*0.5*fabs(zDir*carEntity->velo)*gEnvironment->airDensity;
	for(int i=0;i<car->numWheels;i++)
	{
		//from the distance to CG, determine the fraction of the cars mass this wheel has to carry.
		//not entierly correct, but should suffice for most cases
		float massFraction=invMassDistance[i]/totalInvMassDistance;
		float calcSuspension=(phys->wheels[i].suspension/car->wheels[i].maxSuspension)*2-1;
		calcSuspension=calcSuspension*calcSuspension*calcSuspension;
		calcSuspension=(calcSuspension*0.5+0.5)*car->wheels[i].maxSuspension;
		calcSuspension=phys->wheels[i].suspension;
		//calculate spring force, based on totalSpringForceFactor, massFraction, and the ratio of spring compression
		//wheels[i].suspensionForce=(car->wheels[i].maxSuspension-phys->wheels[i].suspension)*totalSpringForceFactor*massFraction/car->wheels[i].maxSuspension;
		wheels[i].suspensionForce=(car->wheels[i].maxSuspension-calcSuspension)*totalSpringForceFactor*massFraction/car->wheels[i].maxSuspension;

		//if(gGameInfo->arcade&&gFrameCount*kFrameTime<=1)wheels[i].suspensionForce*=gFrameCount*kFrameTime;
		
		//if the springs are fully compressed, we need to add another force, applied by the ground,
		//which keeps the wheel from falling into the ground
		if(!phys->wheels[i].suspension)
		{
			float aerodynamicForce=airRes*(i<2?car->frontLift:car->rearLift);

			float fGroundForce=-wheels[i].velo*wheels[i].groundNormal*kFPS*car->mass/car->numWheels-aerodynamicForce/2;
			if(fGroundForce>car->mass/car->numWheels*gEnvironment->gravity-aerodynamicForce/2)
				fGroundForce=car->mass/car->numWheels*gEnvironment->gravity-aerodynamicForce/2;
			if(fGroundForce>0)
				wheels[i].suspensionForce+=fGroundForce;
		}
		
		//the spring and ground force make up the normal force to the wheel, used to calculate tire traction
		wheels[i].normalForce=wheels[i].suspensionForce;

		//and now, calculate damper forces
		if(wheels[i].onGround){
			//wheels speed WRT the ground
			float wheelYVelo=wheels[i].velo*wheels[i].groundNormal;
			
			//friction caused by the suspension (constant, not dependant on the speed of wheel Y movement)
			float suspensionResistance=sign(wheelYVelo)*car->supsensionFriction*massFraction;
			
			//force caused by dampers (a linear function of wheel Y velocity)
			//dampers stronger in first second to avoid arcade mode shake bug.
			suspensionResistance+=wheelYVelo*car->damperStrength*massFraction*((gGameInfo->arcade&&gFrameCount*kFrameTime<=1)?5:1);
			
			//do not apply forces which are higher than necessary to damp all motion 
			if(fabs(suspensionResistance)>fabs(wheelYVelo*kFPS*car->mass/car->numWheels))
				suspensionResistance=wheelYVelo*kFPS*car->mass/car->numWheels;
				
			//to prevent unexpected behavior when loosing ground contact (ie. jumping with the car)
			if(suspensionResistance>2*car->mass)
				suspensionResistance=2*car->mass;
				
			//apply damper resistance
			wheels[i].suspensionForce-=suspensionResistance;
		}		
	}
	
	CalcSwayBars(car,phys,wheels);
	
	int wall=true;
	for(int i=0;i<car->numWheels;i++)
		if(wheels[i].groundNormal.y!=0)
			wall=false;
	if(wall)
		if(wheels[1].pos.y>wheels[0].pos.y)
		{
			wheels[1].suspensionForce+=100000;
			wheels[3].suspensionForce+=100000;		
		}
		else
		{
			wheels[0].suspensionForce+=100000;
			wheels[2].suspensionForce+=100000;		
		}
}


tVector3 PacejkaCalcWheelRoadForces(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,int i,float engineTorque,float frictionTorque);


float CalcTorque(float rpm,tCarDefinition *car)
//Calculate the maximal torque the car can currently produce, based on engine revs.
//this is a function based on the car's power and torque ratings, and does not
//necessaryly correspond to the actual car's torque curve, but it is usually close enough.
//the advantage of this is that we do not need to know a car's actual torque chart to simulate a car.
{
	float torqueResult;
	if(rpm<car->torqueRPM)
		torqueResult=car->torque*(-sqr(rpm/car->torqueRPM-1)+1);
	else {
		float maxPowerTorque=car->power/(car->powerRPM*2*PI/60);
		float aproxFactor=(car->torque-maxPowerTorque)/(2*car->torqueRPM*car->powerRPM-sqr(car->powerRPM)-sqr(car->torqueRPM));
		float torque=aproxFactor*sqr(rpm-car->torqueRPM)+car->torque;
		torqueResult=torque>0?torque:0;
	} 
	if(rpm>car->maxRPM)
	{
		torqueResult*=1-((rpm-car->maxRPM)*0.006);
		if(torqueResult<0)
			torqueResult=0;
	}
	if(rpm<0)
		torqueResult=0;
	return torqueResult;
}

#include "random.h"
void CalcWheelRoadForceNoClutch(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
//Calculater the force the wheels apply onto road when the clutch is fully engaged
{
	//Calculate the drivetrain ratio
	float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio;
	if(gGameInfo->arcade==kGameModeTurbo)
		ratio*=0.8;
	
	//Calculate the engine inertia at the final drive
	float inertia=car->engineInertia*sqr(ratio);
	
	//Calculate the frictional braking torque of the engine (in N*m)
	float engineFrictionTorque=25+phys->rpm*0.02;
	if(car->engineBaseFriction||car->engineRPMFriction)
		engineFrictionTorque=car->engineBaseFriction+car->idleRPM*car->engineRPMFriction;
	
	//Calculate the current engine torque (in N*m) 
	float engineTorque=(CalcTorque(phys->rpm,car)+engineFrictionTorque)*phys->throttle;
	
	if(phys->damage>kFatalDamage)
		engineTorque=0;
	if(phys->damage>kEngineDamage)
		engineTorque=RandomProb(0.6)?engineTorque:0;
	
	//Calculate average radius of powered wheels
	float wheelRadius=0;
	for(int i=0;i<car->numWheels;i++)
		wheelRadius+=car->wheels[i].radius*car->wheels[i].powered;

	//Calculate average angular velocity of powered wheels
	float averageAngularVelo=0;
	for(int i=0;i<car->numWheels;i++)
		averageAngularVelo+=car->wheels[i].powered*phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius);

	float rpm=0;
	for(int i=0;i<car->numWheels;i++)
	{
		//Calculate the inertia at the wheel
		wheels[i].inertia=car->wheels[i].inertia+car->wheels[i].powered*inertia;
		
		//Calculate the amount of braking applied to the wheel	
		float braked=phys->brake+phys->handbrake*car->wheels[i].handbraked;
		if(braked>1)braked=1;
		
		float glow=braked*fabs(phys->wheels[i].angularVelo)*0.02;
		if(glow>1)glow=1;
		phys->wheels[i].glow+=(glow-0.2)*0.3*kFrameTime;
		if(phys->wheels[i].glow>1)phys->wheels[i].glow=1;
		else if(phys->wheels[i].glow<0)phys->wheels[i].glow=0;

		//Calculate the torque applied by the brakes
		float brakeFriction=braked*car->wheels[i].braked+fabs(phys->wheels[i].angularVelo)*wheels[i].normalForce*gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor;
		
		//Calculate the torque applied by differential lock
		float lockingTorque=(averageAngularVelo-phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius))*car->wheels[i].powered*car->differentialLockCoefficient;

		//Calculate the total braking torque (brakes, friction) applied on the wheel
		float wheelFrictionTorque=engineFrictionTorque*car->wheels[i].powered*fabs(ratio)+car->wheels[i].friction+brakeFriction;
		
		//Calculate the engine torque applied on the wheels
		float wheelEngineTorque=engineTorque*car->wheels[i].powered*ratio+lockingTorque;
		
		if(wheels[i].onGround)
			//Calculate the force the wheel applies onto the road 
			wheels[i].roadForce=PacejkaCalcWheelRoadForces(carEntity,car,phys,wheels,i,wheelEngineTorque,wheelFrictionTorque);
		else{
			//wheel applies no force onto the road
			wheels[i].roadForce=Vector(0,0,0);
			phys->wheels[i].slipVelo=0;
		
			//Calculate wheel's new angular velocity
			float angularAcceleration=((wheelEngineTorque-wheelFrictionTorque*sign(phys->wheels[i].angularVelo))/wheels[i].inertia);
			if(-angularAcceleration*kFrameTime>phys->wheels[i].angularVelo)
				phys->wheels[i].angularVelo=0;
			else
				phys->wheels[i].angularVelo+=angularAcceleration*kFrameTime;
		}
		//rotate the wheel according to it's angular velocity
		phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime;
		
		//Calulate average wheels angular velocities.
		rpm+=phys->wheels[i].angularVelo*car->wheels[i].powered;
	}
	
	//Calculate new engine RPM
	phys->engineAngularVelo=rpm*ratio;
	phys->rpm=phys->engineAngularVelo*60.0/(2*PI);
}


//Calculates how much throttle has to be given to maintain the car's idle RPM
float CalcIdleThrottle(tCarDefinition *car)
{
	float engineFrictionTorque=25+car->idleRPM*0.02;
	if(car->engineBaseFriction||car->engineRPMFriction)
		engineFrictionTorque=car->engineBaseFriction+car->idleRPM*car->engineRPMFriction;
	float engineTorque=CalcTorque(car->idleRPM,car)+engineFrictionTorque;
	return engineFrictionTorque/engineTorque;
}

float CalcEngine(tCarDefinition *car,tCarPhysics *phys)
{
	if(phys->idleThrottle==0)
		phys->idleThrottle=CalcIdleThrottle(car);
	float rawEngineTorque=CalcTorque(phys->rpm,car);
	float engineFrictionTorque=25+phys->rpm*0.02;
	if(car->engineBaseFriction||car->engineRPMFriction)
		engineFrictionTorque=car->engineBaseFriction+phys->rpm*car->engineRPMFriction;
	float engineTorque=(rawEngineTorque+fabs(engineFrictionTorque))*phys->throttle;
	if(phys->rpm>5*car->maxRPM)//safety against engine physics getting out of hand.
	{
		phys->rpm=0;
		phys->gear=0;
	}
	if(phys->damage>kFatalDamage)
		engineTorque=0;
	if(phys->damage>kEngineDamage)
		engineTorque=RandomProb(0.6)?engineTorque:0;

	float engineAngularAcceleration=(engineTorque-engineFrictionTorque)/car->engineInertia;
	phys->rpm=phys->engineAngularVelo*(60.0/(2*PI));
	if(phys->rpm<car->idleRPM)
	{
		phys->rpm=car->idleRPM;
		phys->clutch=0;
	}
	phys->engineAngularVelo+=engineAngularAcceleration*kFrameTime;
	return engineAngularAcceleration;
}

float CalcWheelTorques(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,float ratio)
{
	float oldDrivetrainAngularVelo=phys->drivetrainAngularVelo;
	phys->drivetrainAngularVelo=0;

	//Calculate average radius of powered wheels
	float wheelRadius=0;
	for(int i=0;i<car->numWheels;i++)
		wheelRadius+=car->wheels[i].radius*car->wheels[i].powered;

	//Calculate average angular velocity of powered wheels
	float averageAngularVelo=0;
	for(int i=0;i<car->numWheels;i++)
		averageAngularVelo+=car->wheels[i].powered*phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius);

	for(int i=0;i<car->numWheels;i++)
	{
		wheels[i].inertia=car->wheels[i].inertia;

		//Calculate the amount of braking applied to the wheel	
		float braked=phys->brake+phys->handbrake*car->wheels[i].handbraked;
		if(braked>1)braked=1;
		if(gFrameCount*kFrameTime<kStartGameDelaySeconds)
			braked=1;
		
		float glow=braked*fabs(phys->wheels[i].angularVelo)*0.02;
		if(glow>1)glow=1;
		phys->wheels[i].glow+=(glow-0.2)*0.3*kFrameTime;
		if(phys->wheels[i].glow>1)phys->wheels[i].glow=1;
		else if(phys->wheels[i].glow<0)phys->wheels[i].glow=0;

		//Calculate the torque applied by the brakes
		float brakeFriction=braked*car->wheels[i].braked+fabs(phys->wheels[i].angularVelo)*wheels[i].normalForce*gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor;
				
		//Calculate the torque applied by differential lock
		float lockingTorque=(averageAngularVelo-phys->wheels[i].angularVelo*(car->wheels[i].radius/wheelRadius))*car->wheels[i].powered*car->differentialLockCoefficient;
		
		//Calculate the total braking torque (brakes, friction) applied on the wheel
		float wheelFrictionTorque=car->wheels[i].friction+brakeFriction;

		float wheelEngineTorque=phys->clutchTorque*car->wheels[i].powered*ratio+lockingTorque;
		if(wheels[i].onGround)
			wheels[i].roadForce=PacejkaCalcWheelRoadForces(carEntity,car,phys,wheels,i,wheelEngineTorque,wheelFrictionTorque);
		else{
			float angularAcceleration=((wheelEngineTorque-wheelFrictionTorque*sign(phys->wheels[i].angularVelo))/wheels[i].inertia);
			if(-angularAcceleration*kFrameTime>phys->wheels[i].angularVelo)
				phys->wheels[i].angularVelo=0;
			else
				phys->wheels[i].angularVelo+=angularAcceleration*kFrameTime;
			wheels[i].roadForce=Vector(0,0,0);
			phys->wheels[i].slipVelo=0;
		}
		phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime;
		phys->drivetrainAngularVelo+=phys->wheels[i].angularVelo*car->wheels[i].powered;
	}
	phys->drivetrainAngularVelo*=ratio;
	return (phys->drivetrainAngularVelo-oldDrivetrainAngularVelo)/kFrameTime;
}

void CalcClutchTorqueTransfer(tCarDefinition *car,tCarPhysics *phys,float ratio,float engineAngularAcceleration,float drivetrainAngularAcceleration)
{
	if(ratio==0||phys->clutch==0)
	{
		phys->clutchTorque=0;
		return;
	}
	float angularAcceleration=(phys->engineAngularVelo+engineAngularAcceleration*kFrameTime-phys->drivetrainAngularVelo+drivetrainAngularAcceleration*kFrameTime)/kFrameTime;
	float i1=car->engineInertia;
	float i2=0;
	for(int i=0;i<car->numWheels;i++)
		if(car->wheels[i].powered)
			i2+=car->wheels[i].inertia/sqr(ratio);
	float inertia=(i1*i2)/(i1+i2);
	float optimalTorque=inertia*angularAcceleration;
	
	float maxTorque=car->maxClutchTorqueTransfer*phys->clutch*2;
	phys->clutchTorque=optimalTorque<maxTorque?optimalTorque:maxTorque;
	phys->engineAngularVelo-=phys->clutchTorque/i1*kFrameTime;
}

void CalcWheelRoadForceClutch(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
{
	float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio;
	if(gGameInfo->arcade==kGameModeTurbo)
		ratio*=0.8;
	float engineAngularAcceleration=CalcEngine(car,phys);
	float drivetrainAngularAcceleration=CalcWheelTorques(carEntity,car,phys,wheels,ratio);
	CalcClutchTorqueTransfer(car,phys,ratio,engineAngularAcceleration,drivetrainAngularAcceleration);
}

void CalcWheelRoadForce(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
{
	if(phys->clutch==1.0&&phys->gear!=0)
		CalcWheelRoadForceNoClutch(carEntity,car,phys,wheels);
	else
		CalcWheelRoadForceClutch(carEntity,car,phys,wheels);	
}

void CalcTotalWheelForce(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels,tVector3 *totalWheelForce,tVector3 *totalWheelTorque)
{
	*totalWheelForce=Vector(0,0,0);
	*totalWheelTorque=Vector(0,0,0);
	for(int i=0;i<car->numWheels;i++){
		tVector3 wheelForce=wheels[i].roadForce+wheels[i].suspensionForce*wheels[i].groundNormal;
		tVector3 wheelGroundPoint=wheels[i].pos-*MatrixGetYVector(carEntity->dir)*(car->wheels[i].radius+phys->wheels[i].suspension);
		tVector3 wheelCarTorque=(wheelGroundPoint-car->massCenter*carEntity->dir)%wheelForce;
		*totalWheelTorque=*totalWheelTorque+wheelCarTorque;	
		*totalWheelForce=*totalWheelForce+wheelForce;
	}
	if(phys->rpm<car->jerkRPM)
		phys->rpm=car->jerkRPM;
}

#define kMaxDraftDistance 60.0
#define kMaxDraftAngle (PI/15.0)
#define kMaxDraftVeloAngle (PI/10.0)

float CalcDraftFactor(tGameEntity *car1)
{
	float draftFactor=0;
	for(int i=0;i<gGameInfo->numPlayers;i++)
		if(gCarEntities[i]!=car1)
		{
			tGameEntity *car2=gCarEntities[i];
			tVector3 v=car2->pos-car1->pos;
			float dir,veloDir,dist;
			
			dir=1-acos((!car1->velo)*!v)/kMaxDraftAngle;
			if(isnan(dir))dir=1;
			if(dir<0)dir=0;

			veloDir=1-acos((!car1->velo)*(!car2->velo))/kMaxDraftVeloAngle;
			if(isnan(veloDir))veloDir=1;
			if(veloDir<0)veloDir=0;

			dist=1-(~v)/kMaxDraftDistance;
			if(dist<0)dist=0;

			draftFactor+=dir*veloDir*dist;
		}
	if(draftFactor>1)
		draftFactor=1;
	return draftFactor;
}

//#define __NO_AERODYNAMICS
void CalcAerodynamics(tGameEntity *carEntity,tCarDefinition *car,tVector3 *aerodynamicForce,tVector3 *aerodynamicTorque)
{
#ifndef __NO_AERODYNAMICS
	tVector3 xDir=*MatrixGetXVector(carEntity->dir);
	tVector3 yDir=*MatrixGetYVector(carEntity->dir);
	tVector3 zDir=*MatrixGetZVector(carEntity->dir);
	float fVelo=~carEntity->velo;
	float draftFactor=CalcDraftFactor(carEntity);
	
	float airRes=fVelo*0.5*fabs(zDir*carEntity->velo)*gEnvironment->airDensity;
	tVector3 frontSpoilPoint=Vector(0,car->massCenter.y,car->wheels[0].pos.z)*carEntity->dir;
	tVector3 rearSpoilPoint=Vector(0,car->massCenter.y,car->wheels[2].pos.z)*carEntity->dir;
	*aerodynamicForce=yDir*airRes*(car->frontLift+car->rearLift);
	*aerodynamicTorque=(frontSpoilPoint-car->massCenter*carEntity->dir)%(yDir*airRes*(car->frontLift))+(rearSpoilPoint-car->massCenter*carEntity->dir)%(yDir*airRes*(car->rearLift));	
	
	tVector3 dragCenter=Vector(0,car->massCenter.y,0);
	float dragFactor=fabs(zDir*carEntity->velo*car->frontAirResistance)+fabs(yDir*carEntity->velo*car->topAirResistance)+fabs(xDir*carEntity->velo*car->sideAirResistance); 
	*aerodynamicForce=*aerodynamicForce-carEntity->velo*0.5*dragFactor*gEnvironment->airDensity*(1-0.9*draftFactor);
	*aerodynamicTorque=*aerodynamicTorque+((dragCenter-car->massCenter)*carEntity->dir)%*aerodynamicForce;
#else
	*aerodynamicForce=Vector(0,0,0);
	*aerodynamicTorque=Vector(0,0,0);
#endif
}

void CarLights(tCarPhysics *phys)
{
	phys->lightFlags&=~(kLightFlagRevLight+kLightFlagBrakeLight);
	if((int)(gFrameCount*kFrameTime/kIndicatorTime)&1)
		phys->lightFlags&=~(kLightFlagLIndLight+kLightFlagRIndLight);
	if(phys->brake>0)
		phys->lightFlags|=kLightFlagBrakeLight;
	if(phys->gear==-1)
		phys->lightFlags|=kLightFlagRevLight;
	if(gEnvironment->spotLightEnable&&gFrameCount==0)
		phys->lightFlags|=kLightFlagDriveLight;
}

//Create Tracks and Particle Effects for the car
void CarCreateEffects(tGameEntity *carEntity,tCarDefinition *car,tCarPhysics *phys,tWheelCalcData *wheels)
{
	float totalSlip=0;
	
	for(int i=0;i<car->numWheels;i++)
		{
			tSurfaceType *surf=gSurfaceTypes->types+phys->wheels[i].surfaceType;
			
			totalSlip+=fabs(phys->wheels[i].slipVelo)/car->numWheels;
			
			//does this surface smoke?
			if(wheels[i].onGround)
				if(surf->smokeEnable&&!((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&carEntity==gViewedEntity))
				{
					//is the wheel slipping enough to smoke?
					float smoke=(fabs(phys->wheels[i].slipVelo)-surf->minSmokeSlideVelo)/(surf->maxSmokeSlideVelo-surf->minSmokeSlideVelo);
					if(smoke>0)
					{
						//create particles
						tParticlesDef def;
						ParticlesInitDef(&def);
						
						if(smoke>1)smoke=1;
						smoke*=kMaxSmoke;
						def.sprite=surf->smokeTexture;
						def.maxSpread=surf->smokeSpread;
						def.maxVelo=surf->smokeMaxVelo;
						def.gravity=surf->smokeGravity;
						def.maxLife=surf->smokeMaxLife;
						def.brightness=0.5+(gEnvironment->ambient.x+gEnvironment->ambient.y+gEnvironment->ambient.z)*0.166;
						def.screenTexture=surf->smokeStickEnable?surf->smokeTexture:-1;
						def.xSize=surf->smokeSize;def.ySize=surf->smokeSize;
						tVector3 baseVelo=Vector(0,0.03,-0.1*phys->wheels[i].slip)*carEntity->dir*surf->smokeSpeed+carEntity->velo*0.3;
						ParticlesCreate(smoke*kFrameTime,wheels[i].pos+carEntity->pos,baseVelo,&def);
					}
				}

			//does this surface leave tracks?
			if(surf->trackEnable&&!((gCameraMode==kCameraCockpit||gCameraMode==kCameraCockpitCarHidden)&&carEntity==gViewedEntity))
				//we only generate tracks every kTrackPrecision-th frame for performance reasons
				if(!(gFrameCount%kTrackPrecision))
				{
					//Calculate track intensity
					float tracks=(fabs(phys->wheels[i].slipVelo)-surf->minTrackSlideVelo)/(surf->maxTrackSlideVelo-surf->minTrackSlideVelo);
					if(tracks>0&&wheels[i].onGround)
					{
						//create tracks.
						if(tracks>1)tracks=1;
						tVector3 pos1=wheels[i].pos+carEntity->pos-wheels[i].groundNormal*(phys->wheels[i].suspension+car->wheels[i].radius);
						pos1.y-=wheels[i].bump;
						tVector3 pos2=pos1+wheels[i].velo*kFrameTime;
						phys->wheels[i].lastTrack=TracksAdd(pos1,pos2,wheels[i].groundNormal,car->wheels[i].width,gSurfaceTypes->types[phys->wheels[i].surfaceType].trackTexture,tracks,phys->wheels[i].lastTrack);
					}
					else phys->wheels[i].lastTrack=0;
				}
		}
	
	//make the car dirtier
	phys->dirt+=totalSlip*kFrameTime*0.1;	
	if(phys->dirt>100)
		phys->dirt=100;
	//exhaust fire
	if(car->exhaustFire+(phys->damage>kEngineDamage?100000:0))
	{
		float exhaustFire=(((phys->oldThrottle-phys->throttle)*kFPS*phys->rpm)-(car->exhaustFire+(phys->damage>kEngineDamage?100000:0)))/((car->exhaustFire+(phys->damage>kEngineDamage?100000:0)));
		if(exhaustFire>0)
		{
			if(exhaustFire>1)exhaustFire=1;
			CarPlayCrashNoise(carEntity,FileGetReference("exhaust.wav"),exhaustFire);
			if(exhaustFire>0.5)
			{
				tParticlesDef def;
				ParticlesInitDef(&def);

				def.sprite=FileGetReference("exhaustfire.pct");
				def.maxSpread=0.1;
				def.maxVelo=0.2;
				def.gravity=-0.2;
				def.maxLife=0.3;
				def.screenTexture=-1;
				def.xSize=0.6;def.ySize=0.6;
				def.grow=true;
				tVector3 baseVelo=Vector(0,0,0);
				//two exhaust pipes. Both may be at the same space to make it look like one.
				ParticlesCreate(exhaustFire*200*kFrameTime,car->exhaust1Pos*carEntity->dir+carEntity->pos,baseVelo,&def);
				ParticlesCreate(exhaustFire*200*kFrameTime,car->exhaust2Pos*carEntity->dir+carEntity->pos,baseVelo,&def);
			}
		}
	}	
	phys->oldThrottle=phys->throttle;
	
	//exhaust smoke.
	float v=~carEntity->velo;
	if(v<10&&phys->damage<kFatalDamage)
	{
		tParticlesDef def;
		ParticlesInitDef(&def);
		
		float smoke=0.2+0.8*phys->throttle;
		def.sprite=FileGetReference("exhaustsmoke.pct");
		def.maxSpread=0.1;
		def.maxVelo=0.2;
		def.gravity=-0.8;
		def.maxLife=2;
		def.a=1-(v*0.1);
		def.screenTexture=-1;
		def.xSize=0.4;def.ySize=0.4;
		def.grow=true;
		tVector3 baseVelo=-*MatrixGetZVector(carEntity->dir)*smoke*2;
		//two exhaust pipes. Both may be at the same space to make it look like one.
		ParticlesCreate(smoke*40*kFrameTime,car->exhaust1Pos*carEntity->dir+carEntity->pos,baseVelo,&def);
		ParticlesCreate(smoke*40*kFrameTime,car->exhaust2Pos*carEntity->dir+carEntity->pos,baseVelo,&def);
	}
	
	if(phys->damage>kEngineDamage)
	{
		tParticlesDef def;
		ParticlesInitDef(&def);
		
		float smoke=0.2+0.8*phys->throttle;
		def.sprite=FileGetReference("exhaustsmoke.pct");
		def.maxSpread=0.2;
		def.maxVelo=0.3;
		def.gravity=-0.7;
		def.maxLife=2;
		def.a=1;
		def.screenTexture=-1;
		def.xSize=1.5;def.ySize=1.5;
		def.grow=true;
		tVector3 baseVelo=Vector(0,1,0);
		//two exhaust pipes. Both may be at the same space to make it look like one.
		ParticlesCreate(smoke*60*kFrameTime,Vector(0,1,2.3)*carEntity->dir+carEntity->pos,baseVelo,&def);
	}
}

void CarPhysicsEntitySimulation(tGameEntity *carEntity)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);
	tWheelCalcData wheels[kMaxWheels];
	
	if(carEntity==gViewedEntity)
		phys->regCode=RT3_GetLicenseCode();
		
	//Wheels position and velocity
	CalcWheelPositions(carEntity,car,phys,wheels);
	
	//wheels position relative to the ground
	CalcWheelGroundOffsets(carEntity,car,phys,wheels);

	//wheels springs & suspension
	CalcWheelSupension(carEntity,car,phys,wheels);
		
	if(carEntity->physicsMachine==kPhysicsRemote||gReplay)
	{
		for(int i=0;i<kMaxWheels;i++)
			wheels[i].roadForce=Vector(0,0,0);
		//Sum of wheel forces
		tVector3 totalWheelForce,totalWheelTorque;
		CalcTotalWheelForce(carEntity,car,phys,wheels,&totalWheelForce,&totalWheelTorque);

		//Gravity
		tVector3 gravityForce=Vector(0,-car->mass*gEnvironment->gravity,0);

		//Apply Forces
		tVector3 totalForce=totalWheelForce+gravityForce;
		tVector3 acceleration=totalForce*(1/car->mass);
		carEntity->velo=carEntity->velo+acceleration*kFrameTime;
	}
	else
	{	
		//Force the wheel projects onto the road
		CalcWheelRoadForce(carEntity,car,phys,wheels);
			
		//Sum of wheel forces
		tVector3 totalWheelForce,totalWheelTorque;
		CalcTotalWheelForce(carEntity,car,phys,wheels,&totalWheelForce,&totalWheelTorque);

		//Aerodynamics
		tVector3 aerodynamicForce,aerodynamicTorque;
		CalcAerodynamics(carEntity,car,&aerodynamicForce,&aerodynamicTorque);
		
		//Gravity
		tVector3 gravityForce=Vector(0,-car->mass*gEnvironment->gravity,0);
			
		//Apply Forces
		tVector3 totalForce=totalWheelForce+aerodynamicForce+gravityForce;
		tVector3 acceleration=totalForce*(1/car->mass);
		carEntity->accel=acceleration;
		carEntity->velo=carEntity->velo+acceleration*kFrameTime;

		//Apply Torques
		tVector3 totalTorque=totalWheelTorque+aerodynamicTorque;

		tMatrix3 invMatrix;
		MatrixTranspose(carEntity->dir,invMatrix);
		totalTorque=totalTorque*invMatrix;
		float zTorq=1;
		if(gFrameCount<kFPS*5)
			zTorq=gFrameCount/(kFPS*5);
		tVector3 angularAcceleration=Vector(totalTorque.x/car->inertia.x,totalTorque.y/car->inertia.y,zTorq*totalTorque.z/car->inertia.z)*carEntity->dir;
		tMatrix3 accelerationMatrix;
		RotationVectorToMatrix(angularAcceleration*kFrameTime*kFrameTime,accelerationMatrix);
		MatrixMult(carEntity->rVelo,accelerationMatrix,carEntity->rVelo);	
		
		//if(carEntity->physicsMachine==kPhysicsLocal&&!gReplay)
		//{
		//Lights
		CarLights(phys);
		
		int wheelOnGround=0;
		for(int i=0;i<car->numWheels;i++)
		{
			if(wheels[i].onGround)
				wheelOnGround++;
			phys->wheels[i].onGround=wheels[i].onGround;
		}
//		if(wheelOnGround>2)//no y network Accel when on ground to dampen spring motion on bumpy surfaces
//			carEntity->accel.y=0;
		
		//Crash Recovery
		if(((wheelOnGround<car->numWheels&&wheelOnGround<4)||phys->collision>gFrameCount-10)&&phys->damage<kFatalDamage)
		{
			if(gFrameCount-phys->crashTime>kFPS*5)
				if(sqr(carEntity->velo)<sqr(2)||(phys->collision>gFrameCount-10&&sqr(carEntity->velo)<sqr(15)))
				{
					RoadCenterCar(carEntity);	
					phys->crashTime=gFrameCount;
					phys->stuckTime=gFrameCount;
				}
		}
		else
			phys->crashTime=gFrameCount;
		
		carEntity->lastActivity=gFrameCount;

		int wheelsOnGround=0;
		for(int i=0;i<car->numWheels;i++)
			if(wheels[i].onGround)
				wheelsOnGround++;
		phys->onGround=wheelsOnGround>=2;

	}
	
	//Smoke and Tracks!
	CarCreateEffects(carEntity,car,phys,wheels);
}

#define kMaxCornerAccel 15
#define kMaxEngineAccel 6
#define kMaxBrakeAccel 8
#define kEngineFactor 0.8

void CarPhysicsEntitySimple(tGameEntity *carEntity)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;

	tVector3 velo=carEntity->velo;

	tVector3 rVelo=MatrixToEulerAngles(carEntity->rVelo);
	rVelo.y=0;
	EulerAnglesToMatrix(rVelo,carEntity->rVelo);
	
	CarPhysicsEntitySimulation(carEntity);

	carEntity->velo.x=(5*velo.x+carEntity->velo.x)/6.0;
	carEntity->velo.z=(5*velo.z+carEntity->velo.z)/6.0;

	tCarDefinition *car=&(phys->car);
	
	float groundFriction=0;
	for(int i=0;i<car->numWheels;i++)
		groundFriction+=gSurfaceTypes->types[phys->wheels[i].surfaceType].grip/(float)car->numWheels;
	
	//Calculate the drivetrain ratio
	float ratio=car->gearRatios[phys->gear+1]*car->finalDriveRatio;
	
	//Calculate the engine inertia at the final drive
	float inertia=car->engineInertia*sqr(ratio);
	
	//Calculate the frictional braking torque of the engine (in N*m)
	float engineFrictionTorque=25+phys->rpm*0.02;
	if(car->engineBaseFriction||car->engineRPMFriction)
		engineFrictionTorque=car->engineBaseFriction+phys->rpm*car->engineRPMFriction;
	
	//Calculate the current engine torque (in N*m) 
	float engineTorque=(CalcTorque(phys->rpm,car)+engineFrictionTorque)*phys->throttle-engineFrictionTorque;
	
	if(engineTorque<0&&carEntity->velo**MatrixGetZVector(carEntity->dir)*ratio<0)
		engineTorque*=-1;

	tVector3 flatDir=!Vector(MatrixGetZVector(carEntity->dir)->x,0,MatrixGetZVector(carEntity->dir)->z);
	tVector3 flatVelo=Vector(velo.x,0,velo.z);

	float fEngineForce=(car->aiSpeedIndex!=0?car->aiSpeedIndex:1)*kEngineFactor*engineTorque*ratio/car->wheels[2].radius;
	if(fEngineForce>car->mass*kMaxEngineAccel*groundFriction)
		fEngineForce=car->mass*kMaxEngineAccel*groundFriction;
	tVector3 engineForce=flatDir*fEngineForce;
	
	float arcadeDraftBoost=CalcDraftFactor(carEntity)*(~carEntity->velo)/70.0;
	
	if(phys->arcadeDraftBoost<arcadeDraftBoost)
		phys->arcadeDraftBoost+=2*kFrameTime;
	if(phys->arcadeDraftBoost>arcadeDraftBoost)
		phys->arcadeDraftBoost-=2*kFrameTime;
	
	if(phys->arcadeDraftBoost>1)phys->arcadeDraftBoost=1;
	if(phys->arcadeDraftBoost<0)phys->arcadeDraftBoost=0;
	
	if(carEntity->controlType==kControlTypeAIInput)
		phys->arcadeDraftBoost=0;
		
	//engineForce=engineForce+!engineForce*14*car->mass*phys->arcadeDraftBoost;

	tVector3 airRes=-carEntity->velo*~carEntity->velo*0.5*car->frontAirResistance*gEnvironment->airDensity;
	
	float surfaceBrake=0;
	for(int i=0;i<car->numWheels;i++)
		surfaceBrake+=gSurfaceTypes->types[phys->wheels[i].surfaceType].brakeFactor*fabs(phys->wheels[i].angularVelo)/(float)car->numWheels;
	float brake=phys->arcadeBrake+phys->handbrake*0.5;
	if(brake>1)brake=1;
	brake+=surfaceBrake;
	tVector3 brakeForce=-!flatVelo*brake*car->mass*kMaxBrakeAccel;
	if(sqr(brakeForce/car->mass)*kFrameTime>sqr(flatVelo))
		brakeForce=-!flatVelo*car->mass*kFrameTime;
		
	
	tVector3 totalForce;
	if(phys->onGround)
		totalForce=engineForce+airRes+brakeForce;
	else
		totalForce=airRes;
	tVector3 totalAccel=totalForce/car->mass;
	if(gGameInfo->arcade==kGameModeTurbo)
		totalAccel=totalAccel*2.3;
	
	carEntity->velo=carEntity->velo+(totalAccel*kFrameTime);

	tMatrix3 m;
	MatrixIdentity(m);
	MatrixRotY(m,phys->steering*0.1);
	
	flatDir=flatDir*~flatVelo;
	flatDir=flatDir*m;
	float rev=sign(flatDir*flatVelo);
	flatDir=flatDir*rev;
	float diff=~(flatVelo-flatDir);
	float cornerAccel=kFrameTime*kMaxCornerAccel*groundFriction;
	if(gGameInfo->arcade==kGameModeTurbo)
		cornerAccel*=1.9;
	if(cornerAccel>diff)cornerAccel=diff;
	
	tVector3 vCornerAccel=-!(flatVelo-flatDir)*cornerAccel;
	carEntity->velo=carEntity->velo+vCornerAccel;
	carEntity->accel=totalAccel+vCornerAccel*kFPS;

	float handBrakeFactor=1+phys->handbrake*2;
	if(rev<0)
		handBrakeFactor=1;
	float veloSteer=((15/(~velo+1))+1)*handBrakeFactor;
	float maxRotSteer=10*kFrameTime*handBrakeFactor;
	float acardeSteerVeloInput=rev*phys->steering*~velo*kFrameTime*0.1*handBrakeFactor;
	if(!phys->onGround)
		acardeSteerVeloInput=0;
	if(acardeSteerVeloInput>phys->arcadeSteerVelo){
		phys->arcadeSteerVelo+=0.015*veloSteer*kFrameTime;
		if(acardeSteerVeloInput<phys->arcadeSteerVelo)
			phys->arcadeSteerVelo=acardeSteerVeloInput;
	}
	else{
		phys->arcadeSteerVelo-=0.015*veloSteer*kFrameTime;
		if(acardeSteerVeloInput>phys->arcadeSteerVelo)
			phys->arcadeSteerVelo=acardeSteerVeloInput;
	}
	if(fabs(phys->arcadeSteerVelo)>maxRotSteer)
		phys->arcadeSteerVelo=sign(phys->arcadeSteerVelo)*maxRotSteer;
	MatrixRotY(carEntity->rVelo,phys->arcadeSteerVelo);
}


void CarPhysicsEntityMagic(tGameEntity *carEntity)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;

	MatrixIdentity(carEntity->rVelo);

	tVector3 x[4],a[4];
	x[0]=Vector(1.2,0,2);
	x[1]=Vector(-1.2,0,2);
	x[2]=Vector(1.2,0,-2);
	x[3]=Vector(-1.2,0,-2);
	tVector3 howerAccel=Vector(0,0,0);
	tVector3 howerRotAccel=Vector(0,0,0);
	for(int i=0;i<4;i++)
	{
		x[i]=x[i]*carEntity->dir;
		tVector3 normal;
		float offs=GetGroundOffset(x[i]+carEntity->pos,&carEntity->lastRoadIndex,&normal,NULL);
		if(offs<0.2)offs=0.2;
		a[i]=normal*(1.25/offs)*(1+0.5*phys->steering*(i&1?1:-1))*(1+0.1*phys->brake*(i&2?1:-1))*(1-0.1*phys->throttle*(i&2?1:-1));
		howerAccel=howerAccel+a[i];
		howerRotAccel=howerRotAccel+x[i]%a[i];
	}
	howerAccel=howerAccel*(1+sin(gFrameCount*kFrameTime*2)*0.12);

	tMatrix3 invMatrix;
	MatrixTranspose(carEntity->dir,invMatrix);
	howerRotAccel=Vector(howerRotAccel.x*5,0,howerRotAccel.z*30);
	tMatrix3 accelerationMatrix;
	RotationVectorToMatrix(howerRotAccel*kFrameTime*kFrameTime,accelerationMatrix);
	MatrixMult(carEntity->rVelo,accelerationMatrix,carEntity->rVelo);	
	
	tVector3 velo=carEntity->velo;
	tVector3 gravityAccel=Vector(0,-gEnvironment->gravity,0);
	tVector3 engineAccel=phys->throttle**MatrixGetZVector(carEntity->dir)*(20);
	tVector3 brakeAccel=phys->brake*!(*MatrixGetZVector(carEntity->dir)+((sqr(carEntity->velo)>0.001)?carEntity->velo*0.1:Vector(0,0,0)))*-(22);
	if(gFrameCount*kFrameTime<=kStartGameDelaySeconds)
	{
		engineAccel=Vector(0,0,0);
		brakeAccel=Vector(0,0,0);
	}
	if(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1)
		brakeAccel=phys->brake*(((sqr(carEntity->velo)>0.001)?!carEntity->velo:Vector(0,0,0)))*-(22);
	brakeAccel.y=0;
	tVector3 dampAccel=Vector(0,0,0);
	tVector3 airResAccel=Vector(0,0,0);
	if(sqr(carEntity->velo)>0.001)
	{
		dampAccel=-!carEntity->velo*5;
		airResAccel=-!carEntity->velo*sqr(carEntity->velo)*0.003;
		if(sqr(dampAccel*kFrameTime)>sqr(carEntity->velo))
			dampAccel=-carEntity->velo*kFPS;
		dampAccel.x*=0.1;
		dampAccel.z*=0.1;		
	}
	tVector3 totalAccel=gravityAccel+howerAccel+engineAccel+brakeAccel+dampAccel+airResAccel;

	carEntity->velo=carEntity->velo+totalAccel*kFrameTime;
	carEntity->lastActivity=gFrameCount;

	tVector3 flatDir=!Vector(MatrixGetZVector(carEntity->dir)->x,0,MatrixGetZVector(carEntity->dir)->z);
	tVector3 flatVelo=Vector(velo.x,0,velo.z);
	
	tMatrix3 m;
	MatrixIdentity(m);
	MatrixRotY(m,phys->steering*0.1);

	flatDir=flatDir*~flatVelo;
	flatDir=flatDir*m;
	float rev=sign(flatDir*flatVelo);
	flatDir=flatDir*rev;
	float diff=~(flatVelo-flatDir);

	float cornerAccel=kFrameTime*kMaxCornerAccel;
	if(gGameInfo->arcade==kGameModeTurbo)
		cornerAccel*=1.9;
	if(cornerAccel>diff)cornerAccel=diff;
	
	tVector3 vCornerAccel=-!(flatVelo-flatDir)*cornerAccel;
	carEntity->velo=carEntity->velo+vCornerAccel;
	carEntity->accel=totalAccel+vCornerAccel*kFPS;
	
	float acardeSteerVeloInput=phys->steering*kFrameTime*(3+~velo*0.04);
	if(acardeSteerVeloInput>phys->arcadeSteerVelo){
		phys->arcadeSteerVelo+=0.15*kFrameTime;
		if(acardeSteerVeloInput<phys->arcadeSteerVelo)
			phys->arcadeSteerVelo=acardeSteerVeloInput;
	}
	else{
		phys->arcadeSteerVelo-=0.15*kFrameTime;
		if(acardeSteerVeloInput>phys->arcadeSteerVelo)
			phys->arcadeSteerVelo=acardeSteerVeloInput;
	}	
	if(gFrameCount*kFrameTime>=kStartGameDelaySeconds)
		MatrixRotY(carEntity->rVelo,phys->arcadeSteerVelo);
	
	if(gEnvironment->spotLightEnable&&gFrameCount==0)
		phys->lightFlags|=kLightFlagDriveLight;

	if(phys->collision>gFrameCount-10)
	{
		if(gFrameCount-phys->crashTime>kFPS*5)
			if(sqr(carEntity->velo)<sqr(2))
			{
				RoadCenterCar(carEntity);	
				phys->crashTime=gFrameCount;
				phys->stuckTime=gFrameCount;
			}
	}else
		phys->crashTime=gFrameCount;
	
	*MatrixGetYVector(carEntity->dir)=!(*MatrixGetYVector(carEntity->dir)+(Vector(0,1,0)-*MatrixGetYVector(carEntity->dir))*kFrameTime*0.5);
	*MatrixGetXVector(carEntity->dir)=!(*MatrixGetYVector(carEntity->dir)%*MatrixGetZVector(carEntity->dir));
	*MatrixGetZVector(carEntity->dir)=!(*MatrixGetXVector(carEntity->dir)%*MatrixGetYVector(carEntity->dir));
}



void CarPhysicsEntity(tGameEntity *carEntity)
{		
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);
	if(car->magic)	
		CarPhysicsEntityMagic(carEntity);
	else if((gGameInfo->arcade==kGameModeTurbo||gGameInfo->arcade==kGameModeArcade||carEntity->controlType==kControlTypeAIInput)&&carEntity->physicsMachine==kPhysicsLocal)
		CarPhysicsEntitySimple(carEntity);
	else
		CarPhysicsEntitySimulation(carEntity);
}
#define kPredictTime 0.15
//Move the car around

void MatrixReAdjust2(tMatrix3 m)
{
	*(tVector3*)m[1]=!*(tVector3*)m[1];
	*(tVector3*)m[0]=!(*(tVector3*)m[1]%*(tVector3*)m[2]);
	*(tVector3*)m[2]=!(*(tVector3*)m[0]%*(tVector3*)m[1]);
}

void FullyResetCar(tGameEntity *carEntity)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	carEntity->pos=gMapInfo->startPos+Vector(0,10,0);
	carEntity->oldPos=carEntity->pos;
	carEntity->netPos=carEntity->pos;
	carEntity->velo=Vector(0,0,0);
	carEntity->netVelo=Vector(0,0,0);
	carEntity->collVelo=Vector(0,0,0);
	carEntity->accel=Vector(0,0,0);
	MatrixIdentity(carEntity->dir);
	MatrixIdentity(carEntity->rVelo);
	MatrixIdentity(carEntity->oldDir);
	MatrixIdentity(carEntity->netDir);
	MatrixIdentity(carEntity->netRVelo);
	for(int i=0;i<kNumAvgRVelos;i++)
	{
		MatrixIdentity(carEntity->lastRVelos[i]);
		carEntity->lastVelos[i]=Vector(0,0,0);
		carEntity->lastAccel[i]=Vector(0,0,0);
	}
	carEntity->remoteCameraPos=Vector(0,0,0);
	carEntity->zDist=0;
	phys->maxSpeed=0;
	phys->averageSpeed=0;
	phys->accel100=0;
	phys->accel200=0;
	phys->accelQuarter=0;
	phys->accelKM=0;
	phys->odo;
	phys->position=0;
	phys->lead=0;
	phys->rpm=0;
	phys->engineAngularVelo=0;
	phys->drivetrainAngularVelo=0;
	phys->clutchTorque=0;
	for(int i=0;i<kMaxWheels;i++)
	{
		phys->wheels[i].angle=0;
		phys->wheels[i].suspension=0;
		phys->wheels[i].slipVelo=0;
		phys->wheels[i].slip=0;
		phys->wheels[i].slipAngle=0;
		phys->wheels[i].rotation=0;
		phys->wheels[i].angularVelo=0;
		phys->wheels[i].glow=0;
	}
}


void CarMotionEntity(tGameEntity *carEntity)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);

	for(int i=kNumAvgRVelos-2;i>=0;i--)
	{
		MatrixCopy(carEntity->lastRVelos[i],carEntity->lastRVelos[i+1]);
		carEntity->lastVelos[i+1]=carEntity->lastVelos[i];
		carEntity->lastAccel[i+1]=carEntity->lastAccel[i];
	}
	MatrixCopy(carEntity->rVelo,carEntity->lastRVelos[0]);
	carEntity->lastVelos[0]=carEntity->velo;
	carEntity->lastAccel[0]=carEntity->accel;


	carEntity->lastActivity=gFrameCount;
	//move the car
	
	if(carEntity->physicsMachine==kPhysicsRemote)
	{
		if(!gReplay)
		{
			float smoothness=0;
			for(int i=0;i<kNumLastCollisions;i++)
				if((gFrameCount-phys->lastCollisions[i].frameCount)*kFrameTime<0.5)
					smoothness+=(8.0f/kNumLastCollisions);
			if(smoothness>1)
				smoothness=1;
			float predictTime=kPredictTime*(1+smoothness*1.5);
			carEntity->netVelo=carEntity->netVelo+carEntity->accel*kFrameTime;
			carEntity->netPos=carEntity->netPos+carEntity->netVelo*kFrameTime;
			carEntity->velo=carEntity->velo+((carEntity->netPos+carEntity->netVelo*predictTime+carEntity->accel*predictTime*predictTime*0.5)-(carEntity->pos+carEntity->velo*predictTime))/predictTime;
			MatrixMult(carEntity->netDir,carEntity->rVelo,carEntity->netDir);
			tVector3 netx=*MatrixGetXVector(carEntity->netDir);
			tVector3 nety=*MatrixGetYVector(carEntity->netDir);
			tVector3 netz=*MatrixGetZVector(carEntity->netDir);
			tVector3 curx=*MatrixGetXVector(carEntity->dir);
			tVector3 curz=*MatrixGetZVector(carEntity->dir);

			*MatrixGetXVector(carEntity->dir)=curx+(netx-curx)*0.3;
			*MatrixGetYVector(carEntity->dir)=nety;
			*MatrixGetZVector(carEntity->dir)=curz+(netz-curz)*0.3;
			MatrixReAdjust2(carEntity->dir);
			
			if(sqr(carEntity->pos-carEntity->netPos)>sqr(5+smoothness*10))
			{
				carEntity->pos=carEntity->netPos;
				carEntity->velo=carEntity->netVelo;
				MatrixCopy(carEntity->netDir,carEntity->dir);
			}
		}
		for(int i=0;i<car->numWheels;i++)
			phys->wheels[i].rotation+=phys->wheels[i].angularVelo*kFrameTime;
	}
	if(gReplay)
	{
		carEntity->velo=carEntity->velo+carEntity->accel*kFrameTime;
		MatrixMult(carEntity->netDir,carEntity->rVelo,carEntity->netDir);
		tVector3 netx=*MatrixGetXVector(carEntity->netDir);
		tVector3 nety=*MatrixGetYVector(carEntity->netDir);
		tVector3 netz=*MatrixGetZVector(carEntity->netDir);
		tVector3 curx=*MatrixGetXVector(carEntity->dir);
		tVector3 curz=*MatrixGetZVector(carEntity->dir);

		*MatrixGetXVector(carEntity->dir)=curx+(netx-curx)*0.3;
		*MatrixGetYVector(carEntity->dir)=nety;
		*MatrixGetZVector(carEntity->dir)=curz+(netz-curz)*0.3;
		MatrixReAdjust2(carEntity->dir);
	}
	carEntity->pos=carEntity->pos+carEntity->velo*kFrameTime;

	
	//rotate the car around it's center of gravity
	tVector3 massCenter=car->massCenter*carEntity->dir;
	tVector3 massMotion=((massCenter*carEntity->rVelo)-massCenter);
	MatrixMult(carEntity->dir,carEntity->rVelo,carEntity->dir);
	carEntity->pos=carEntity->pos-massMotion;	
	
	//update road position index
	float oldPosition=phys->position;
	phys->position=RoadGetPosition(carEntity->pos,carEntity->lastRoadIndex,NULL);
	
	if(carEntity==gViewedEntity)
		for(int i=0;i<gNumCornerSigns;i++)
			if(gGameInfo->reverse){
				if(phys->position<gCornerSigns[i].pos&&oldPosition>gCornerSigns[i].pos)
				{
					gAccelSignDisplayIntensity=1.5;
					gAccelSignDisplayCorner=-gCornerSigns[i].accel;
				}
			}else{
				if(phys->position>gCornerSigns[i].pos&&oldPosition<gCornerSigns[i].pos)
				{
					gAccelSignDisplayIntensity=1.5;
					gAccelSignDisplayCorner=gCornerSigns[i].accel;
				}
			}
		

	//have we crossed the road position lap line?
	if(gGameInfo->reverse)
	{
		if(oldPosition>phys->position+50)
			phys->lap--;
		else if(oldPosition<phys->position-50)
			phys->lap++;
	}
	else			
	{
		if(oldPosition>phys->position+50)
			phys->lap++;
		else if(oldPosition<phys->position-50)
			phys->lap--;
	}

	//test if we are stuck (used for ai recovery)
	int stuck=gFrameCount*kFrameTime>5&&sqr(carEntity->velo)<sqr(8)&&phys->lapCount<=gGameInfo->numLaps;
	if(stuck)
		phys->stuckTime++;
	else
		phys->stuckTime=0;

	//are we moving backwards? (used to display "wrong direction" message).
	if((gGameInfo->reverse?oldPosition<=phys->position:oldPosition>=phys->position)&&sqr(carEntity->velo)>sqr(5))
		phys->wrongDriectionFrames++;
	else
		phys->wrongDriectionFrames-=10;
	if(phys->wrongDriectionFrames<0)
		phys->wrongDriectionFrames=0;
	else if(phys->wrongDriectionFrames>kFPS*5)
	{
		phys->wrongDriectionFrames=kFPS*5;
		if(gGameInfo->network&kGameInfoNetworkCantGoBackwards)
		{
			if(carEntity==gViewedEntity)
				TextPrintfToBufferFormatedFading(Vector(kFadingMessageXPos,kFadingMessageYPos),kFadingMessageSize,kTextAlignMiddle,0.9,1.8,"Wrong Way!");
			RoadCenterCar(carEntity);
		}
	}

	float velo=~carEntity->velo;
	//Check if we have beaten our speed record for this game
	if(velo>phys->maxSpeed)
		phys->maxSpeed=velo;

	//Update our average speed
	if(phys->lapCount&&(phys->lapCount<=gGameInfo->numLaps||gGameInfo->numLaps==-1)&&!gReplay)
	{
		phys->averageSpeed=((phys->averageSpeed*(phys->averageSpeedFrames))+velo)/(phys->averageSpeedFrames+1);
		phys->averageSpeedFrames++;
	}
	if(velo>=100/3.6&&phys->accel100==0)
		phys->accel100=gFrameCount*kFrameTime-kStartGameDelaySeconds;
	if(velo>=200/3.6&&phys->accel200==0)
		phys->accel200=gFrameCount*kFrameTime-kStartGameDelaySeconds;
	phys->odo+=velo*kFrameTime;
	if(phys->odo>=402.32454&&phys->accelQuarter==0)
		phys->accelQuarter=gFrameCount*kFrameTime-kStartGameDelaySeconds;
	if(phys->odo>=1000&&phys->accelKM==0)
		phys->accelKM=gFrameCount*kFrameTime-kStartGameDelaySeconds;
	/*if(Button()&&phys->accel200!=0&&!gReplay)
	{
		printf("0-100km/h: %.1f seconds\n0-200km/h: %.1f seconds\n100-200km/h: %.1f seconds\nQuarter Mile: %.1f seconds\n1km: %.1f seconds\nTop Speed:%.1f km/h\n",phys->accel100,phys->accel200,phys->accel200-phys->accel100,phys->accelQuarter,phys->accelKM,phys->maxSpeed*3.6);
		phys->accel200=0;
	}*/
	if(isnan(carEntity->pos.x)||isnan(carEntity->pos.y)||isnan(carEntity->pos.z))
		FullyResetCar(carEntity);
}