
// Vehicle.cpp
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------




#include "../Game_local.h"
#include "VehicleAnimated.h"

CLASS_DECLARATION( rvVehicle, rvVehicleAnimated )
END_CLASS

static bool OpenQ4_TurboVehicleSpeedsActive( void ) {
	return OpenQ4_TurboModeActive();
}

rvVehicleAnimated::rvVehicleAnimated ( void ) {
}

rvVehicleAnimated::~rvVehicleAnimated ( void ) {
	SetPhysics( NULL );
}

/*
================
rvVehicleAnimated::Spawn
================
*/
void rvVehicleAnimated::Spawn( void ) {

	turnRate   = spawnArgs.GetFloat ( "turnRate", "90" );
	viewAngles = GetPhysics()->GetAxis ( ).ToAngles ( );
	viewAxis   = viewAngles.ToMat3();

	// Initialize the physics object
	physicsObj.SetSelf( this );
	physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
	physicsObj.SetContents( CONTENTS_BODY );
	physicsObj.SetClipMask( MASK_PLAYERSOLID|CONTENTS_VEHICLECLIP );
	physicsObj.SetMaxStepHeight( spawnArgs.GetFloat ( "stepheight", "14" ) );

	// Start just a tad above the floor
	physicsObj.SetOrigin( GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
	physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
	physicsObj.SetDelta( vec3_origin );

	// Gravity
	idVec3 gravity = spawnArgs.GetVector( "gravityDir", "0 0 -1" );
	gravity *= g_gravity.GetFloat ( );
	physicsObj.SetGravity( gravity );
	SetPhysics( &physicsObj );

	animator.RemoveOriginOffset( true );
		
	additionalDelta.Zero();

	BecomeActive( TH_THINK );		
}

/*
================
rvVehicleAnimated::ClientPredictionThink
================
*/
void rvVehicleAnimated::ClientPredictionThink ( void ) {
	Think();
}

/*
================
rvVehicleAnimated::Think
================
*/
void rvVehicleAnimated::Think ( void ) {

	rvVehicle::Think();	

	float rate = 0.0f;
	usercmd_t& cmd = positions[0].mInputCmd;
	idVec3 delta;

	if( positions[0].IsOccupied() && !IsFrozen() && IsMovementEnabled() )	{
		
		if (( g_vehicleMode.GetInteger() == 0 && !( cmd.buttons & BUTTON_STRAFE )) || 	// If we're in the old driving mode and we aren't strafing or...
			( g_vehicleMode.GetInteger() != 0 && ( cmd.buttons & BUTTON_STRAFE )))		// If we're in the new driving mode and we are strafing
		{
			rate = SignZero(cmd.forwardmove) * turnRate;
			rate *= idMath::MidPointLerp( 0.0f, 0.9f, 1.0f, idMath::Fabs(cmd.rightmove) / 127.0f );
			viewAngles.yaw += Sign(cmd.rightmove) * rate * MS2SEC(gameLocal.GetMSec());	
		}
	}

	viewAngles.Normalize360();
	animator.GetDelta( gameLocal.time - gameLocal.GetMSec(), gameLocal.time, delta );
	delta += additionalDelta;
	if ( OpenQ4_TurboVehicleSpeedsActive() && positions[0].IsOccupied() && IsMovementEnabled() ) {
		delta *= OPENQ4_TURBO_VEHICLE_SPEED_SCALE;
	}

	idStr alignmentJoint;
	if ( g_vehicleMode.GetInteger() != 0 && spawnArgs.GetString( "alignment_joint", 0, alignmentJoint ) ) {
		idVec3 offset;
		idMat3 axis;
		GetJointWorldTransform( animator.GetJointHandle( alignmentJoint ), gameLocal.time, offset, axis );
		delta *= axis;
	} else {
		viewAxis = viewAngles.ToMat3() * physicsObj.GetGravityAxis();
		delta *= viewAxis;
	}
			
	physicsObj.SetDelta( delta );
	additionalDelta.Zero();
}

/*
================
rvVehicleAnimated::GetAxis
================
*/
const idMat3& rvVehicleAnimated::GetAxis( int id ) const {
	return viewAxis;
}

/*
================
rvVehicleAnimated::WriteToSnapshot
================
*/
void rvVehicleAnimated::WriteToSnapshot( idBitMsgDelta &msg ) const {
	rvVehicle::WriteToSnapshot ( msg );
	
 	physicsObj.WriteToSnapshot( msg );
	msg.WriteFloat( viewAngles[0] );
	msg.WriteFloat( viewAngles[1] );
	msg.WriteFloat( viewAngles[2] );
}

/*
================
rvVehicleAnimated::ReadFromSnapshot
================
*/
void rvVehicleAnimated::ReadFromSnapshot( const idBitMsgDelta &msg ) {
	rvVehicle::ReadFromSnapshot ( msg );

	physicsObj.ReadFromSnapshot( msg );
	viewAngles[0] = msg.ReadFloat( );
	viewAngles[1] = msg.ReadFloat( );
	viewAngles[2] = msg.ReadFloat( );
}

/*
================
rvVehicleAnimated::Save
================
*/
void rvVehicleAnimated::Save ( idSaveGame *savefile ) const {
	savefile->WriteVec3( storedPosition );

	savefile->WriteStaticObject ( physicsObj );

	savefile->WriteAngles ( viewAngles );
	savefile->WriteFloat ( turnRate );

	// why are we writing out the viewAxis here???
	savefile->WriteMat3 ( viewAxis );

	// TEMP
	animator.Save( savefile );
}

/*
================
rvVehicleAnimated::Restore
================
*/
void rvVehicleAnimated::Restore ( idRestoreGame *savefile ) {
	savefile->ReadVec3( storedPosition );

	physicsObj.SetSelf( this );
	physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
	savefile->ReadStaticObject ( physicsObj );
	RestorePhysics ( &physicsObj );
	physicsObj.EnableClip();

	savefile->ReadAngles ( viewAngles );
	savefile->ReadFloat ( turnRate );

	savefile->ReadMat3 ( viewAxis );

	// TEMP - restore animator, because it was cleared when loading the AF
	animator.Restore( savefile );
	animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
	animator.GetBounds( gameLocal.time, renderEntity.bounds );
	// TEMP

	additionalDelta.Zero();
}

/*
================
rvVehicleAnimated::GetPhysicsToVisualTransform
================
*/
bool rvVehicleAnimated::GetPhysicsToVisualTransform ( idVec3 &origin, idMat3 &axis ) {
	origin = modelOffset;
	axis = viewAxis;
	return true;
}

/*
================
rvVehicleAnimated::RunPrePhysics
================
*/
void rvVehicleAnimated::RunPrePhysics ( void ) {
	storedPosition = physicsObj.GetOrigin();
}

/*
================
rvVehicleAnimated::RunPostPhysics
================
*/
void rvVehicleAnimated::RunPostPhysics ( void ) {

	if ( autoCorrectionBegin + 3000 > static_cast<unsigned>( gameLocal.time )) {
		autoCorrectionBegin = 0;

		idVec3 delta;
		idVec3 actualDelta = physicsObj.GetOrigin() - storedPosition;

		animator.GetDelta( gameLocal.time - gameLocal.GetMSec(), gameLocal.time, delta );

		if ( !autoCorrectionBegin && delta.LengthSqr() * 0.2f > actualDelta.LengthSqr() ) {
			autoCorrectionBegin = gameLocal.time;
		}
	}
}


