//===========================================================================//
// File:	Vehicle.cpp											
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 09/09/98  JSE  Inital base class based off of Shadowrun/Adept             //
// 09/23/98  BDB  State engine update									     //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "Vehicle.hpp"
#include "Subsystem.hpp"
#include "Engine.hpp"
#include "Torso.hpp"
#include "VehicleInterface.hpp"
#include "Weapon.hpp"
#include "MWDamageObject.hpp"
#include "bridge.hpp"
#include "tank.hpp"
#include "MWApplication.hpp"
#include "SubsystemClassData.hpp"
#include "aiutils.hpp"
#include "MWMission.hpp"

#include <Adept\GUITextManager.hpp>
#include <Adept\GUITextObject.hpp>
#include <Adept\DamageObject.hpp>
#include <Adept\EntityManager.hpp>
#include <Adept\Effect.hpp>
#include <Adept\Map.hpp>
#include <Adept\Renderer.hpp>

#include <Adept\CollisionGrid.hpp>
#include <Adept\CollisionVolume.hpp>
#include <AI.hpp>
#include "gameinfo.hpp"

#include "MWPlayer.hpp"
#include <MLR\MLRShadowLight.hpp>
#include <Adept\LightManager.hpp>
#include <Adept\Mission.hpp>
#include "mwguimanager.hpp"
#include "hudcomm.hpp"
// MSL 5.02 Shadow
#include <Adept\CameraComponent.hpp>


const Stuff::Time sensor_map_update_frequency = 1.0f;

#ifdef NEW_SENSOR_TIMING
extern __int64	tVehicleTime;
#endif


void MechWarrior4::VehicleSecurityCheckStart()
{
	_asm
	{
		nop
	}	
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
	Vehicle::GetHeightAtPoint(
		const Stuff::Point3D& point,
		Adept::Entity* from_who
	)
{
	Verify(Adept::Map::GetInstance() != 0);

	Stuff::Line3D line;
	line.m_length = 100.0f;
	line.m_direction = Vector3D::Down;
	line.m_origin = point;
	line.m_origin.y += 50.0f;
	Stuff::Normal3D normal;
	Entity::CollisionQuery query(&line,&normal,Entity::CanBeWalkedOnFlag,from_who);
	Check_Object(CollisionGrid::Instance);
	if (CollisionGrid::Instance->ProjectLine(&query) == Adept::Map::GetInstance())
	{
		Stuff::Point3D hit_spot;
		line.FindEnd(&hit_spot);
		return (hit_spot.y);
	}

	return -100000.0;
}

//#############################################################################
//##################	Vehicle::ExecutionStateEngine    ######################
//#############################################################################

const StateEngine::StateEntry
	Vehicle::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(Vehicle__ExecutionStateEngine, FallingMotion),
	STATE_ENTRY(Vehicle__ExecutionStateEngine, FlyingMotion),
	STATE_ENTRY(Vehicle__ExecutionStateEngine, AnimatedMotion),
	STATE_ENTRY(Vehicle__ExecutionStateEngine, DrivingMotion),
	STATE_ENTRY(Vehicle__ExecutionStateEngine, Destroyed),
	STATE_ENTRY(Vehicle__ExecutionStateEngine, AIMotion),
	STATE_ENTRY(Vehicle__ExecutionStateEngine, Dying),
	STATE_ENTRY(Vehicle__ExecutionStateEngine, Dropping)
};


Vehicle::ExecutionStateEngine::ClassData*
	Vehicle::ExecutionStateEngine::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::ExecutionStateEngine::InitializeClass()
{
	Check_Object(BaseClass::DefaultData);
	Verify(!DefaultData);

	DefaultData =
		new ClassData(
			Vehicle__ExecutionStateEngineClassID,
			"Vehicle::ExecutionStateEngine",
			BaseClass::DefaultData,
			ELEMENTS(StateEntries), StateEntries,
			(Entity::ExecutionStateEngine::Factory)Make,
			(Entity::ExecutionStateEngine::FactoryRequest::Factory)
				&FactoryRequest::ConstructFactoryRequest
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::ExecutionStateEngine::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Vehicle::ExecutionStateEngine*
	Vehicle::ExecutionStateEngine::Make(
		Vehicle *vehicle,
		FactoryRequest *request
	)
{
	Check_Object(vehicle);
	Check_Object(request);
	gos_PushCurrentHeap(Heap);
	Vehicle::ExecutionStateEngine *engine =
		new Vehicle::ExecutionStateEngine(DefaultData, vehicle, request);
	gos_PopCurrentHeap();
	return engine;
		
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Vehicle::ExecutionStateEngine::RequestState(
		int new_state,
		void* data
	)
{
	Check_Object(this);
	Check_Object(owningEntity);

	//
	//-------------------------------------------
	// If there is no change of state just return
	//-------------------------------------------
	//
	if (new_state == currentState)
		return currentState;
	
	Vehicle *veh;
	veh = Cast_Object (Vehicle *,owningEntity);

	if ((currentState == AIMotionState) && veh->m_AI && (!veh->m_AI->TurningOn ()))
	{
		return currentState;
	}

	//
	//----------------------------------------------
	// Now, switch the state and tickle the watchers
	//----------------------------------------------
	//
	switch (BaseClass::RequestState(new_state, data))
	{
	case DestroyedState:
		owningEntity->EnterNeverExecuteState();
		break;
	}

	return currentState;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::ExecutionStateEngine::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//#############################################################################
//###############################    Vehicle    ##############################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Vehicle::ClassData*
	Vehicle::DefaultData = NULL;

DWORD MechWarrior4::Executed_Vehicle_Count = 0;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::InitializeClass()
{
	Check_Object(BaseClass::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			VehicleClassID,
	        "MechWarrior4::Vehicle",
			BaseClass::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			(Entity::GameModel::Factory)GameModel::ConstructOBBStream,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
			AnimationStateEngine::Make
		);
	Register_Object(DefaultData);

	
	INDIRECT_STATE_ATTRIBUTE(
		Vehicle,
		ExecutionState,
		executionState,
		Vehicle__ExecutionStateEngine
	);

	CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
		SpeedDemandMPS,speedDemandMPS,Scalar,ScalarClassID);
	CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
		CurrentSpeedMPS,currentSpeedMPS,Scalar,ScalarClassID);

	CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
		LocalGroundPitch,localGroundPitch,Scalar,ScalarClassID);
	CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
		LocalGroundRoll,localGroundRoll,Scalar,ScalarClassID);

	CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
		RawLocalGroundPitch,rawLocalGroundPitch,Scalar,ScalarClassID);
	CUSTOM_DIRECT_ATTRIBUTE(Vehicle,
		RawLocalGroundRoll,rawLocalGroundRoll,Scalar,ScalarClassID);
	
	CUSTOM_DIRECT_ATTRIBUTE(
		Vehicle,
		VehicleMovingSFX,
		vehicleMovingSFX,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		Vehicle,
		VehicleIdleSFX,
		vehicleIdleSFX,
		int,
		IntClassID
	);		
	
	CUSTOM_DIRECT_ATTRIBUTE(
		Vehicle,
		VehicleDyingSFX,
		vehicleDyingSFX,
		int,
		IntClassID
	);		

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		FullStopTurnRate,
		fullStopTurnRate,
		Radian
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		TopSpeedTurnRate,
		topSpeedTurnRate,
		Radian
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MaxReverseSpeed,
		maxReverseSpeed,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MinReverseSpeed,
		minReverseSpeed,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MaxSpeed,
		maxSpeed,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MinMaxSpeed,
		minMaxSpeed,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MinSpeed,
		minSpeed,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		Acceleration,
		acceleration,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		Decceleration,
		decceleration,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		ReverseAccelerationMultiplier,
		reverseAccelerationMultiplier,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		ReverseDeccelerationMultiplier,
		reverseDeccelerationMultiplier,
		Scalar
	);
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		TargetLockTime,
		targetLockTime,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MaxGimpSpeed,
		maxGimpSpeed,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MinStandTransitionSpeed,
		minStandTransitionSpeed,
		Scalar
	);


	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		MaxSlope,
		modelmaxSlope,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		StartSlopeDeceleration,
		modelstartSlopeDeceleration,
		Scalar
	);


	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		SlopeDecel1,
		slopeDecel1,
		Scalar
	);


	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		SlopeDecel2,
		slopeDecel2,
		Scalar
	);


	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		SlopeDecel3,
		slopeDecel3,
		Scalar
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		AttackType,
		m_attackType,
		int,
		IntClassID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Vehicle__GameModel,
		TreadLength,
		m_treadLength,
		Scalar
	);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Vehicle*
	Vehicle::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);
	gos_PushCurrentHeap(Heap);
	Vehicle *new_entity = new
		Vehicle(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	
	
	Check_Object(EntityManager::GetInstance());
	EntityManager::GetInstance()->AddMover(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Vehicle::Vehicle(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	MWObject(class_data, message, base_id, element),
	currentTrailEffect(NULL),
	m_SensorCellMapX(-1),
	m_SensorCellMapZ(-1),
	m_LastSensorCellPositionUpdate(0),
	m_SecondaryExplosion (NULL)
{
	Check_Pointer(this);
	Check_Object(message);

	MW4AI::UserConstants::IncrementRefCount();
	CommonCreation(message);

	engine = NULL;
	vehicleInterface = NULL;
	shadow = NULL;
// MSL 5.02 Shadow
	shadowIntensity = 0.0f;

	respawnCount = 0;

	m_doesHaveLightAmp = false;

	teamNumber = MWApplication::No_Team;

	m_groundRaycastHeight = 20.0f;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Vehicle::~Vehicle()
{
	DESTRUCTOR("Vehicle");

	Check_Object(this);
	if (shadow)
	{
		Check_Object(shadow);
		delete shadow;
		shadow = NULL;
// MSL 5.02 Shadow
		shadowIntensity = 0.0f;
	}
	Check_Object(EntityManager::GetInstance());
	EntityManager::GetInstance()->RemoveMover(this);
	MW4AI::UserConstants::DecrementRefCount();

	RemoveFromSensorCellMap();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::Reuse(
		const CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(this);
	Check_Object(message);

	STOP(("Not implemented"));
	BaseClass::Reuse(message, base_id);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Vehicle::TurnOff (void)
{
	m_LastState = executionState->GetState ();
	executionState->RequestState(ExecutionStateEngine::AIMotionState);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Vehicle::TurnOn (void)
{
	lastParameterization = gos_GetElapsedTime ();
//	executionState->RequestState(ExecutionStateEngine::AnimatedMotionState);
	if (executionState->GetState () == ExecutionStateEngine::AIMotionState)
	executionState->RequestState(m_LastState);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::CommonCreation(CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);

	m_DeathDamageMode = m_DeathDamageType = -1;
	m_LastState = ExecutionStateEngine::DrivingMotionState;
	Verify (MW4AI::UserConstants::Instance ());
	Stuff::Scalar upslope;
	const GameModel *model = GetGameModel();
	Check_Object(model);
	switch (model->moveTypeFlag)
	{
		case MechWarrior4::MWObject__GameModel::LEG_MOVETYPE:
		case MechWarrior4::MWObject__GameModel::LEGJUMP_MOVETYPE:
			upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::leg_up_slope));	
			break;
		case MechWarrior4::MWObject__GameModel::TRACK_MOVETYPE:
			upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::track_up_slope));	
			break;
		case MechWarrior4::MWObject__GameModel::WHEEL_MOVETYPE:
			upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::wheel_up_slope));	
			break;
		case MechWarrior4::MWObject__GameModel::HOVER_MOVETYPE:
			upslope = Arccos (MW4AI::UserConstants::Instance ()->Get (MW4AI::UserConstants::hover_up_slope));	
			break;
		default:
			upslope = Pi_Over_2;
			break;
	}
//	if (model->startSlopeDeceleration < upslope)
	{
		startSlopeDeceleration = upslope;
		maxSlope = startSlopeDeceleration + (15.0f * Radians_Per_Degree);

	}
#ifdef LAB_ONLY
		m_DebugFast = false;
#endif

	speedDemand = 0.0f;
	yawDemand = 0.0f;
	pitchDemand = 0.0f;
	rollDemand = 0.0f;
	speedDemandMPS = 0.0f;
	currentSpeedMPS = 0.0f;
	currentSpeedKPH = 0.0;
	speedDemandKPH = 0.0;

	m_needSelfDestruct = 0;

	dirRequest = Point3D (0,0,0);
	followDirRequest = false;
	followDirRequestReverse = false;

	internalViewPoint = false;
	usingEyeSpring = false;

	localGroundRoll = 0.0f;
	localGroundPitch = 0.0f;

	rawLocalGroundPitch = localGroundPitch;
	rawLocalGroundRoll = localGroundRoll;

	materialHit = -1;
	currentTrailID = ResourceID::Null;
	currentTrailEffect.Remove();

	vehicleRunsInterface = true;
	
	
	correctingPosition = false;
	correctingRotation = false;
	correctionPosition = Point3D::Identity;
	correctionAngle = YawPitchRoll::Identity;
	rotationCorrectionTime = 0.0f;
	positionCorrectionTime = 0.0f;
	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::Respawn(Entity::CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);


	BaseClass::Respawn(message);
	CreateMessage *vehicle_message = Cast_Pointer(CreateMessage *, message);
	CommonCreation(vehicle_message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Vehicle::GetExecutionSlot()
{
	Check_Object(this);
	return VehicleExecutionSlot;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::PreCollisionExecute(Time till)
{
	Check_Object(this);

	PRECOLLISION_LOGIC("Vehicle");
	#if defined(LAB_ONLY)
		if (!instanceName)
			MWGameInfo::g_LastVehicle[0] = '\0';
		else
		{
			strncpy(MWGameInfo::g_LastVehicle, instanceName, sizeof(MWGameInfo::g_LastVehicle)-1);
			MWGameInfo::g_LastVehicle[sizeof(MWGameInfo::g_LastVehicle)-1] = '\0';
		}
		MWGameInfo::g_LastVehiclePos = GetLocalToWorld ();
	#endif

	//
	//----------------------------------------
	// Run our interface if we are supposed to
	//----------------------------------------
	//
	Set_Statistic(Executed_Vehicle_Count, Executed_Vehicle_Count+1);
	if (vehicleRunsInterface && vehicleInterface != NULL)
	{
		Check_Object(vehicleInterface);
		vehicleInterface->PreCollisionExecute(till);
		Verify(!vehicleInterface->IsUsingPostCollision() || EntityManager::GetInstance()->IsInPostCollisionExecution(vehicleInterface));
	}

	UpdateSensorCellMapPosition();

	//
	//-------------------------------------------------------------
	// Let the base class deal with setting up the motion variables
	//-------------------------------------------------------------
	//
	BaseClass::PreCollisionExecute(till);

	//
	//----------------------------------------------------------------------
	// Any executing vehicle should not be in never execute state and should
	// always use post collision
	//----------------------------------------------------------------------
	//
	Check_Object(executionState);
	int pre_state = executionState->GetState();
	Verify(pre_state != ExecutionStateEngine::NeverExecuteState);
	
	//
	//-------------------------------------------------------------------------
	// Do the appropriate type of simulation.  Animated motion state is for BRB
	//-------------------------------------------------------------------------
	//

	FindGroundAngle ();

	Verify(GetTimeSlice(till) < 1.0f);
	Scalar time_slice = GetTimeSlice(till);
	switch (pre_state)
	{
		case ExecutionStateEngine::AnimatedMotionState:
			switch (animStateEngine->GetState())
			{
				case AnimationStateEngine::Test1State:
				case AnimationStateEngine::Test2State:
				case AnimationStateEngine::Test3State:
				case AnimationStateEngine::Test4State:
					break;
				default:
					animStateEngine->RequestState(AnimationStateEngine::Test1State);
					break;
			}

			if (animationArrayCount > 0)
			{
				animStateEngine->RunStates(time_slice);
			}
			StraightLineMotionSimulation(till);
			break;

		//
		//-----------------------------------------------
		// Flying or driving state needs to apply gravity
		//-----------------------------------------------
		//
		case ExecutionStateEngine::DrivingMotionState:
		case ExecutionStateEngine::DyingState:
		case ExecutionStateEngine::FlyingMotionState:
			{
				UnitVector3D world_down_in_local;
				GetLocalToWorld().GetWorldDownInLocal(&world_down_in_local);
				localSpaceAcceleration.linearMotion.AddScaled(
					localSpaceAcceleration.linearMotion,
					world_down_in_local,
					g_Gravity
				);
			}
			break;

		//
		//------------------------------------
		// This state is not used for vehicles
		//------------------------------------
		//
		case ExecutionStateEngine::AlwaysExecuteState:
			STOP(("AlwaysExecuteState is not a valid state for vehicle"));
			break;

		case ExecutionStateEngine::AIMotionState:
			{
				PRECOLLISION_LOGIC("Vehicle::Boardgame");
			}
			break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if defined(LAB_ONLY)
	void Vehicle::SyncMatrices(bool update_matrix)
	{
		SYNC_LOGIC("Vehicle");
		BaseClass::SyncMatrices(update_matrix);
	}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Vehicle::CollisionHandler(
		Stuff::LinearMatrix4D *new_position,
		Stuff::DynamicArrayOf<CollisionData> *collisions
	)
{
	Check_Object(this);
	Check_Object(collisions);
	Verify(GetInterestLevel() != DormantInterestLevel);

	bool shoulddamage=true;
	//
	//----------------------------------------------------------
	// If we are destroyed, delete the collision list and return
	//----------------------------------------------------------
	//
	if (IsDestroyed())
	{
		delete collisions;
		Verify(!newCollisions);
		return false;
	}

	//
	//--------------------------------
	// Ask the AI about the collision
	//--------------------------------
	//
	if (m_AI)
	{
		if (!m_AI->ReactToCollision(collisions,shoulddamage))			// ai will return false if collision should not occur
		{
			Verify(!newCollisions);
			delete collisions;
			return false;
		}
	}


	//
	//----------------------------------------
	// Turn off collision if we are going fast
	//----------------------------------------
	//
	#ifdef LAB_ONLY
		if (m_DebugFast)
		{
			Verify(!newCollisions);
			delete collisions;
			return false;
		}
	#endif

	//
	//-----------------------
	// Iterate the collisions
	//-----------------------
	//
	for (int i=0; i<collisions->GetLength(); ++i)
	{
		CollisionData *data = &(*collisions)[i];
		Check_Pointer(data);

		//
		//---------------------------------------------------------------------
		// Look to see if we have run into a collider, and if so, calculate the
		// relative velocity
		//---------------------------------------------------------------------
		//
		Entity *other = data->m_otherEntity;
		if (other->IsACollider())
		{
			Adept::Mover *adeptmover = Cast_Object(Adept::Mover*, other); 
			Vector3D rel_vel;
			rel_vel.Subtract(adeptmover->worldSpaceVelocity.linearMotion, worldSpaceVelocity.linearMotion); 
			Scalar speed = rel_vel.GetLengthSquared();
			MWObject *mover = Cast_Object(MWObject*, other);

			//
			//-------------------------------
			// If its a mech, get its ratings
			//-------------------------------
			//
			if (other->GetClassID() == MechClassID)
			{
				Mech *mech = Cast_Object(Mech*, mover);
				int mech_class;
				Scalar damage_received;
				Scalar mech_speed;
				if (mech->GetTonage() >= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_tonage))
				{
					damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_damage);
					mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_building_speed);
					mech_class = HeavyCanWalkThruType;
				}
				else if (mech->GetTonage() >= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_tonage))
				{
					damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_damage);
					mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_building_speed);
					mech_class = MediumCanWalkThruType;
				}
				else
				{
					damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_light_damage);
					mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_light_building_speed);
					mech_class = AnythingCanWalkThruType;
				}

				//
				//----------------------
				// See if it squishes us
				//----------------------
				//
				if (GetWalkThruType() >= mech_class)
				{
					if (shoulddamage)
					{
						TakeDamageMessage damage_message(
							GetReplicatorID(),
							other->GetReplicatorID(),
							100000.0f,
// MSL 5.05 Advance Mode
							0, // Modifier
							RammingDamageFromAboveType,
							data->m_normal,
							data->m_worldIntersectionPoint,
							0.0f,
							false,
							TakeDamageMessage::DefaultWeaponType
						);
						Receive(&damage_message);
					}
				}

				//
				//-----------------------------------------------
				// See if we are going fast enough to get damaged
				//-----------------------------------------------
				//
				else if (speed >= mech_speed)
				{
					speed = Sqrt(speed)*mover->GetTonage();

					//
					//------------------------
					// Evaluate the DFA states
					//------------------------
					//
					Scalar min_dfa_vel = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_dfa_speed);
					Entity::ExecutionStateEngine *engine = mech->executionState;
					bool their_dfa = mech->worldSpaceVelocity.linearMotion.y <= -min_dfa_vel && engine->GetState() == ExecutionStateEngine::FlyingMotionState;

					//
					//---------------------------------------
					// Use the appropriate collision modifier
					//---------------------------------------
					//
					char damage_type;
					if (their_dfa)
					{
						damage_type = RammingDamageFromAboveType;
						speed *= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_dfa_damage_given);
					}
					else
					{
						damage_type = RammingDamageFromAheadType;
						speed *= damage_received;
					}

					//
					//----------------------------------
					// Set the damage message to ourself
					//----------------------------------
					//
					if (shoulddamage)
					{
// MSL 5.06 Armor Mode
						MWApplication *m_App;
						m_App = MWApplication::GetInstance ();
						m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;

						TakeDamageMessage damage_message(
							GetReplicatorID(),
							other->GetReplicatorID(),
							speed,
							m_ArmorMode,
							damage_type,
							data->m_normal,
							data->m_worldIntersectionPoint,
							0.0f,
							false,
							TakeDamageMessage::DefaultWeaponType
						);
						Receive(&damage_message);
					}
				}
			}
		}

		//
		//--------------------------------------------------------------
		// If we've run into a static object, see if we can just kill it
		//--------------------------------------------------------------
		//
		else if (GetWalkThruType() <= other->GetWalkThruType())
		{
//			if (shoulddamage)
			{
				TakeDamageMessage damage_message(
					other->GetReplicatorID(),
					GetReplicatorID(),
					100000.0f,
// MSL 5.05 Advance Mode
					0,
					RammingDamageFromAboveType,
					data->m_otherNormal,
					data->m_worldIntersectionPoint,
					0.0f,
					false,
					TakeDamageMessage::DefaultWeaponType
				);
				other->Receive(&damage_message);
			}
		}
	}

	//
	//-----------------
	// Stop the vehicle
	//-----------------
	//
	Entity::ExecutionStateEngine *engine = executionState;
	if (engine->GetState() == ExecutionStateEngine::NeverExecuteState || !IsUsingPostCollision())
	{
		delete collisions;
		Verify(!newCollisions);
		return false;
	}
	Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));
	newCollisions = collisions;
	currentSpeedMPS = 0.0f;
	currentSpeedKPH = currentSpeedMPS * 3.6f;
	*new_position = GetLocalToParent();
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::PostCollisionExecute(Time till)
{
	Check_Object(this);
	Check_Object(executionState);
	POSTCOLLISION_LOGIC("Vehicle");

	//
	//------------------------------------------------------------------------
	// If we are trying to go to a particular spot, figure out where that is
	// relative to us, negating the values if we are trying to back up to that
	// spot
	//------------------------------------------------------------------------
	//
	if (followDirRequest)
	{
		Point3D distance_to_target;
		Point3D loc (GetLocalToWorld ());
		LinearMatrix4D tempmat (GetLocalToWorld ());

		tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);
		distance_to_target.MultiplyByInverse(dirRequest, tempmat);
		if (followDirRequestReverse)
		{
			distance_to_target.x = -distance_to_target.x;
			distance_to_target.z = -distance_to_target.z;
		}

		//
		//-------------------------------------------------------------------
		// If we are still some distance from the target spot, figure out yaw
		// to the target angle
		//-------------------------------------------------------------------
		//
		yawDemand = 0.0f;
		if (!Small_Enough(distance_to_target.z))
		{
			Scalar heading_adjustment = Arctan(distance_to_target.x, distance_to_target.z);

			//
			//--------------------------------------------------------------
			// Only turn if we are off by more than a degree.  If so, figure
			// out how long we need to turn at full speed to point at the
			// target
			//--------------------------------------------------------------
			//
			Scalar turn_amount = Abs(heading_adjustment);
			if (turn_amount > Radians_Per_Degree)
			{
				const GameModel *model = GetGameModel();
				Scalar time_slice = GetTimeSlice(till);
				Scalar turn_step = heading_adjustment * time_slice;
				Scalar max_step = Lerp(model->fullStopTurnRate, model->topSpeedTurnRate, currentSpeedMPS/GetMaxSpeed());
//				Scalar max_step = model->fullStopTurnRate * time_slice;
				max_step *= time_slice;
				Verify(max_step >= SMALL);

				//
				//-------------------------------------------------------------
				// If we need to turn more than we can arrange this time slice,
				// turn at full rate
				//-------------------------------------------------------------
				//
				yawDemand = turn_step/max_step;
				Clamp(yawDemand, -1.0f, 1.0f);
			}
		}
#if 0
		if (!strnicmp("ambush2", instanceName, 7))
		{
			SPEWALWAYS(("afarrier", (char*)instanceName));
			SPEWALWAYS(("afarrier", " local position = %f,%f,%f", loc.x, loc.y, loc.z));
			SPEWALWAYS(("afarrier", " world target = %f,%f,%f", dirRequest.x, dirRequest.y, dirRequest.z));
			SPEWALWAYS(("afarrier", " local target = %f,%f,%f", distance_to_target.x, distance_to_target.y, distance_to_target.z));
			SPEWALWAYS(("afarrier", " yaw = %f", yawDemand));
		}
#endif
	}

	//
	//------------------------------------------
	// Reset the parameterization for next frame
	//------------------------------------------
	//
	lastParameterization = till;
	initialLocalToParent = GetLocalToParent();

	//
	//---------------------------------------------------------------------
	// We need to execute watchers for our children if they are part of us,
	// then we create another trail object
	//---------------------------------------------------------------------
	//
	CreateTrail();

//	if(m_needSelfDestruct)
//		ProcessSelfDestruct();

	//
	//------------------------------------------------
	// If we have a shadow, first get its current data
	//------------------------------------------------
	//
	if (shadow && TiledLightManager::s_ShadowMode != TiledLightManager::e_NoShadows && Mission::GetInstance()->GetMainLight())
	{
		POSTCOLLISION_LOGIC("Vehicle::Cast Shadow");
		Check_Object(shadow);
		gosFX::LightManager::Info info;
		shadow->GetInfo(&info);

		//
		//-----------------------------------------------------------------
		// Get the orientation of our main light and set our shadow to that
		//-----------------------------------------------------------------
		//
		gosFX::LightManager::Info main_info;
		gosFX::Light *main_light = Mission::GetInstance()->GetMainLight();
		Check_Object(main_light);
		main_light->GetInfo(&main_info);
		info.m_origin = main_info.m_origin;

		//
		//----------------------------------------------------------
		// Cast a ray from our center of collision toward the ground
		//----------------------------------------------------------
		//
		Stuff::Line3D line;
		line.m_length = 500.0f;
		info.m_origin.GetLocalForwardInWorld(&line.m_direction);
		CollisionVolume *cv = GetSolidVolume();
		if (cv)
		{
			Check_Object(cv);
			LinearMatrix4D new_bounds;
			new_bounds.Multiply(cv->m_localSpaceBounds.localToParent, GetLocalToWorld());
			line.m_origin = new_bounds;
			Stuff::Normal3D normal;
			CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
			Check_Object(CollisionGrid::Instance);
			CollisionGrid::Instance->ProjectLine(&query);

			//
			//--------------------------------------------------------------------
			// Set our shadow position to the end of the line, and compute the new
			// radius of the light (it will be inner radius / cos(light angle).
			// We will automatically set the radius to the height of the center
			// of the collision volume over the vehicles origin
			//--------------------------------------------------------------------
			//
			Point3D end;
			line.FindEnd(&end);
			info.m_origin.BuildTranslation(end);
			Stuff::Point3D vehicle_translation(GetLocalToWorld());
			info.m_inner = line.m_origin.y - vehicle_translation.y;
			if(Small_Enough(line.m_direction.y))
			{
			info.m_outer=info.m_inner+10.0f;
			}
			else
			{
			info.m_outer = info.m_inner / -line.m_direction.y;
			}

// MSL 5.02 Shadow
//		
// 	Needed a way to clip out a shadow when it is out of range.
//  Out of range is when it is past the fogend setting of the current mission.
//
			//
			// if (distance to object is > max fog distance then we turn off the lightmap for the shadow, otherwise we turn it on)
			//
			
			//
			// First get the distance from the vehicle to the camera
			//
			if (Adept::Player::GetInstance()->playerInterface)
			{
				MWMission * mission = Cast_Object(MWMission*, MWMission::GetInstance());
				VehicleInterface * vi = Cast_Object(VehicleInterface*, Adept::Player::GetInstance()->playerInterface);
				if (vi->m_reticuleCamera && mission)
				{
					Stuff::Point3D camera_local_to_world;
					Stuff::Point3D vehicle_local_to_world;
					camera_local_to_world = vi->m_reticuleCamera->GetLocalToWorld();
					vehicle_local_to_world = this->GetLocalToWorld();
					Stuff::Point3D vehicleDistance;
					vehicleDistance.Subtract(camera_local_to_world, vehicle_local_to_world);
					Stuff::Scalar vehicle_height = vehicle_local_to_world.y;
					Stuff::Scalar distanceSquared = vehicleDistance.GetLengthSquared();
					Stuff::Scalar fog_start_squared;
					Stuff::Scalar fog_end_squared;
					Stuff::Scalar fog_end_height;
					Stuff::Scalar fog_distance;
					Stuff::Scalar fog_height;
					//
					// Then get the fog distance
					fog_start_squared = mission->m_generalFogStart * mission->m_generalFogStart;
					fog_end_squared = mission->m_generalFogEnd * mission->m_generalFogEnd;
					fog_end_height = mission->m_heightFogEnd;
					//
					// Then compare
					//
					MidLevelRenderer::MLRShadowLight * mlrshadowlight = shadow->GetMLRLight();
					info.m_intensity = shadowIntensity; // default
					fog_distance = shadowIntensity * ((fog_end_squared - distanceSquared)/(fog_end_squared - fog_start_squared));
					fog_height = shadowIntensity * (vehicle_height/fog_end_height);
					if (mlrshadowlight)
					{
						if ((fog_end_squared > distanceSquared) || (vehicle_height < fog_end_height))
						{
							if (fog_end_squared > distanceSquared)
							{
								shadow->GetMLRLight()->ShowShadow(true);
								//
								// Set the intensity here so it pops less.
								//
								if (!Small_Enough(fog_end_squared - fog_start_squared))
								{
									Min_Clamp(distanceSquared, fog_start_squared);
									fog_distance = shadowIntensity * ((fog_end_squared - distanceSquared)/(fog_end_squared - fog_start_squared));
								}
							}
							if (fog_distance < fog_height)
								info.m_intensity = fog_distance;
							else
								info.m_intensity = fog_height;
						}
						else
						{
							shadow->GetMLRLight()->ShowShadow(false);
						}

					}
				}

			}
			shadow->ChangeLight(&info);
		}
	}

	//
	//---------------------------------
	// Let the parent handle things now
	//---------------------------------
	//
	BaseClass::PostCollisionExecute(till);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::ComputeForwardSpeed(Stuff::Scalar time_slice)
{
	Check_Object(this);
	const GameModel *model = GetGameModel();
	Check_Object(model);
	Stuff::Scalar maxSpeedMod;
	bool wallslope=false;

	//
	//----------------------------------------------------
	// Set the demands to zero if the vehicle is shut down
	//----------------------------------------------------
	//
	if (vehicleShutDown)
	{
		speedDemand = 0.0f;
		speedDemandKPH = 0.0f;
	}

	Verify (model->moveTypeFlag != MWObject__GameModel::WATER_MOVETYPE);			// these should be boats

	maxSpeedMod = 1.0f;
	switch (materialHit)
	{
		case WaterMaterial:
	//		PAUSE (("vehicle walked on water or swamp material"));
		case ConcreteMaterial:
			switch (model->moveTypeFlag)
			{
				case MWObject__GameModel::WHEEL_MOVETYPE:
					maxSpeedMod = 1.25f;
					break;
			}
			break;
		case SnowMaterial:
			switch (model->moveTypeFlag)
			{
				case MWObject__GameModel::TRACK_MOVETYPE:
					maxSpeedMod = 0.5f;
					break;
				case MWObject__GameModel::WHEEL_MOVETYPE:
					maxSpeedMod = 0.25f;
					break;
			}
			break;
		case FakeShallowWaterMaterial:
			switch (model->moveTypeFlag)
			{
				case MWObject__GameModel::WHEEL_MOVETYPE:
					maxSpeedMod = 0.0f;
					break;
				case MWObject__GameModel::TRACK_MOVETYPE:
					maxSpeedMod = 0.0f;
					break;
			}
			break;
		case FakeMidWaterMaterial:
			switch (model->moveTypeFlag)
			{
				case MWObject__GameModel::WHEEL_MOVETYPE:
				case MWObject__GameModel::TRACK_MOVETYPE:
					maxSpeedMod = 0.0f;
					break;
			}
			break;
		case FakeOceanicWaterMaterial:
			switch (model->moveTypeFlag)
			{
				case MWObject__GameModel::WHEEL_MOVETYPE:
				case MWObject__GameModel::TRACK_MOVETYPE:
					maxSpeedMod = 0.0f;
					break;
			}
			break;
// MSL 5.03 Lava
		case CrackedLavaMaterial:
		case OpenLavaMaterial:
			switch (model->moveTypeFlag)
			{
				case MWObject__GameModel::WHEEL_MOVETYPE:
				case MWObject__GameModel::TRACK_MOVETYPE:
				//collide against the lava
					maxSpeedMod = 0.0f;
					break;
			}
		break;
	}

	if (maxSpeedMod == 0.0f)				// tell the ai we had a collision based on material type
	{
		if ((m_AI) && (m_AI->IsDerivedFrom (MoverAI::DefaultData)))
		{
			MoverAI *ai = Cast_Object(MoverAI*, m_AI);
			ai->SlopeCollide();
		}
	}

	//
	//------------------------------------------------------------------------
	// Figure out our desired speed.  If we are stopped, set the KPH variables
	// to zero and quit
	//------------------------------------------------------------------------
	//
	if (speedDemand >= 0.0f)
		speedDemandMPS = (speedDemand * GetMaxSpeed()*maxSpeedMod);
	else
		speedDemandMPS = -(speedDemand * model->maxReverseSpeed*maxSpeedMod);
	if (speedDemandMPS == 0.0f && currentSpeedMPS == 0.0f)
	{
		speedDemandKPH = 0.0f;
		currentSpeedKPH = 0.0f;
		SetIdle ();
		return;
	}

	//
	//----------------------------------------------------------------------
	// Figure out the grade we are on.  If it is less than 45 degrees, we
	// can do regular motion on it.
	//
	// NOTE: This approach is a big steamy pile of doo-doo.  I will kill it!
	//----------------------------------------------------------------------
	//
	Scalar grade;
	Scalar cosang;
	UnitVector3D forward;
	GetLocalToWorld().GetLocalForwardInWorld(&forward);
	cosang = (forward.x*m_WorldAvgNormal.x)+(forward.y*m_WorldAvgNormal.y)+(forward.z*m_WorldAvgNormal.z);

//	if (m_LocalNormal.z < 0)
	if (cosang < 0)				// going up the slope,	
	{
		grade = Abs (m_RawGroundPitch);
		m_RawGroundPitch = m_RawGroundPitch < 0 ? m_RawGroundPitch * -1.0f : m_RawGroundPitch;
	}
	else
	{
		grade = 0;
		m_RawGroundPitch = m_RawGroundPitch > 0 ? m_RawGroundPitch * -1.0f : m_RawGroundPitch;
	}

	Scalar decceleration = model->decceleration;
//	bool ai_grade_collide = false;
	if (grade <= startSlopeDeceleration)
	{
		Scalar clamped_pitch = m_RawGroundPitch;
		Scalar clamped_roll = rawLocalGroundRoll;
		
		//
		//----------------------------------------------------
		// The maximum slope we will request is +/- 44 degrees
		//----------------------------------------------------
		//
		Scalar max_value = startSlopeDeceleration - Radians_Per_Degree;
		Clamp(clamped_pitch, -max_value, max_value);
		Clamp(clamped_roll, -max_value, max_value);

		//
		//----------------------------------------------------------------------
		// If we are currently driving forward, and we are not heading downhill,
		// figure out how much to reduce our speed demand by
		//----------------------------------------------------------------------
		//
		if (currentSpeedMPS > 0.0f)
		{
			if (clamped_pitch >= 0.0f)
			{
				Stuff::Scalar speed_effect = 0.4f * clamped_pitch / startSlopeDeceleration;
				speedDemandMPS -= speedDemandMPS * speed_effect;
			}

			//
			//------------------------------------------------------
			// Otherwise, if we are driving downhill, speed up a bit
			//------------------------------------------------------
			//
			else
			{
				Stuff::Scalar speed_effect = -0.3f * clamped_pitch / startSlopeDeceleration;
				speedDemandMPS += speedDemandMPS * speed_effect;
			}
		}
	}

	//
	//------------------------------------------------------------------------
	// We need to deal if an extreme case, so see if we are running up a cliff
	// face.  If we are, we are going to want to stop
	//------------------------------------------------------------------------
	//
	else if (m_RawGroundPitch > 0.0f)
	{
		speedDemandMPS = 0.0f;

		//
		//------------------------------------------------------------------
		// If the grade is higher than we can tolerate, this is treated as a
		// collision - tell the AI
		//------------------------------------------------------------------
		//
		if (grade >= maxSlope)
		{
//			SetCollide();
			decceleration = model->slopeDecel3;
#if 0
			if ((m_AI) && (m_AI->IsDerivedFrom (MoverAI::DefaultData)))
			{
				MoverAI *ai = Cast_Object(MoverAI*, m_AI);
				ai->SlopeCollide();
			}
#endif
			wallslope = true;
		}

		//
		//--------------------------------------------------------------------------
		// We can tolerate the slope, but will slow down, so pick which deceleration
		// band to use
		//--------------------------------------------------------------------------
		//
		else
		{
#if 0
			ai_grade_collide = m_AI && m_AI->IsDerivedFrom(MoverAI::DefaultData);
			if ((m_AI) && (m_AI->IsDerivedFrom (MoverAI::DefaultData)))
			{
				MoverAI *ai = Cast_Object(MoverAI*, m_AI);
				ai->SlopeCollide();
			}
#endif
			Verify(startSlopeDeceleration <= (Pi_Over_4+Pi_Over_8));
			Scalar band_size = (maxSlope - startSlopeDeceleration)/3.0f;
			Scalar grade_band_one = startSlopeDeceleration + band_size;

			if (grade < grade_band_one)
				decceleration = model->slopeDecel1;
			else
			{
				Scalar grade_band_two = grade_band_one + band_size;
				decceleration =
					(grade < grade_band_two) ? model->slopeDecel2 : model->slopeDecel3;
			}
		}
	}

	//
	//--------------------------------------------
	// Anything can go down - ain't gravity great?
	//--------------------------------------------
	//
	else
	{
		Stuff::Scalar adjust_pitch = -m_RawGroundPitch;
		Max_Clamp(adjust_pitch, Pi_Over_4);
		Stuff::Scalar speed_effect = 0.3f * adjust_pitch / Pi_Over_4;
		speedDemandMPS += speedDemandMPS * speed_effect;
	}

	//
	//--------------------------------------------------------------------
	// If we are not going backwards, find out what speed we want after we
	// consider braking
	//--------------------------------------------------------------------
	//
	if (currentSpeedMPS >= 0.0f)
	{
		Stuff::Scalar decel_speed = currentSpeedMPS - (model->decceleration * time_slice);

		if (!wallslope)
		{
			Min_Clamp (decel_speed,20);
		}
		//
		//--------------------------------------------------------------------
		// If we end up wanting to go backwards and this is a collision to the
		// AI, tell the AI we collided
		//--------------------------------------------------------------------
		//
#if 0
		if (decel_speed <= 0.0f && ai_grade_collide)
		{
			if (m_AI)
			{
				MoverAI *ai = Cast_Object(MoverAI*, m_AI);
				ai->SlopeCollide();
			}
		}
#endif

		//
		//--------------------------------------
		// Otherwise, adjust our speed as needed
		//--------------------------------------
		//
		if (speedDemandMPS > currentSpeedMPS)
		{
			currentSpeedMPS += (model->acceleration * time_slice);
			Max_Clamp(currentSpeedMPS, speedDemandMPS);
		}
		else if (speedDemandMPS < currentSpeedMPS)
		{
			currentSpeedMPS -= (model->decceleration * time_slice);
			Min_Clamp(currentSpeedMPS, speedDemandMPS);
		}
	}

	//
	//--------------------------------------------------------------
	// We are backing up, so tell the AI if we had a grade collision
	//--------------------------------------------------------------
	//
	else
	{
		Stuff::Scalar decel_spped = currentSpeedMPS + (model->reverseDeccelerationMultiplier * time_slice);
		if (!wallslope)
		{
			Max_Clamp (decel_spped,-20);
		}
#if 0
		if ((decel_spped >= 0) && (ai_grade_collide))
		{
			if (m_AI)
			{
				MoverAI *ai = Cast_Object(MoverAI *,m_AI);
				ai->SlopeCollide();
			}
		}
#endif

		//
		//-------------------------------------------------------------
		// Deal with the stand transition if that is the case we are in
		//-------------------------------------------------------------
		//

		//
		//-------------------------------------
		// Otherwise adjust our speed as needed
		//-------------------------------------
		//
		if (speedDemandMPS < currentSpeedMPS)
		{
			currentSpeedMPS -= (model->acceleration * model->reverseAccelerationMultiplier * time_slice);
			Min_Clamp(currentSpeedMPS, speedDemandMPS);
		}
		else if (speedDemandMPS > currentSpeedMPS)
		{
			currentSpeedMPS += (model->decceleration * model->reverseDeccelerationMultiplier * time_slice);
			Max_Clamp(currentSpeedMPS, speedDemandMPS);
		}
	}

	//
	//----------------------------------------------------
	// Now adjust all this to the limits in the model file
	//----------------------------------------------------
	//
	Clamp(currentSpeedMPS, model->maxReverseSpeed*maxSpeedMod, GetMaxSpeed()*maxSpeedMod);

	//
	//---------------------------
	// Set up the velocity vector
	//---------------------------
	//
	localSpaceVelocity.linearMotion.z = currentSpeedMPS;

	//
	//----------------------
	// Set the KPH variables
	//----------------------
	//
	currentSpeedKPH = currentSpeedMPS * 3.6f;
	speedDemandKPH = speedDemandMPS * 3.6f;

	if (currentSpeedKPH == 0)
		SetIdle ();
	else
		SetMoving ();
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// called when the unit is dieing and needs to slow to a stop
void Vehicle::ComputeFrictionSpeed(Stuff::Scalar time_slice)
{
	Check_Object(this);
	Stuff::Scalar friction;

	speedDemand = 0.0f;
	speedDemandKPH = 0.0f;
	yawDemand = 0;
	speedDemandMPS = 0;
	if (speedDemandMPS == 0.0f && currentSpeedMPS == 0.0f)
	{
		speedDemandKPH = 0.0f;
		currentSpeedKPH = 0.0f;
		return;
	}
#ifdef _ARMOR
	const GameModel *model = GetGameModel();
	Check_Object(model);
	Verify (model->moveTypeFlag != MWObject__GameModel::WATER_MOVETYPE);			// these should be boats
#endif

	friction = 10.0f;
	switch (materialHit)
	{
		case WaterMaterial:
		case ConcreteMaterial:
			friction = 8.0f;	
			break;
		case SnowMaterial:
			friction = 5.0f;	
			break;
		case FakeShallowWaterMaterial:
			friction = 50.0f;	
			break;
		case FakeMidWaterMaterial:
			friction = 1000.0f;				// stop it now
			break;
		case FakeOceanicWaterMaterial:
			friction = 1000.0f;				// stop it now
			break;
	}


	//
	//----------------------------------------------------------------------
	// Figure out the grade we are on.  If it is less than 45 degrees, we
	// can do regular motion on it.
	//
	// NOTE: This approach is a big steamy pile of doo-doo.  I will kill it!
	//----------------------------------------------------------------------
	//
	Scalar grade;
	Scalar cosang;
	UnitVector3D forward;
	GetLocalToWorld().GetLocalForwardInWorld(&forward);
	cosang = (forward.x*m_WorldAvgNormal.x)+(forward.y*m_WorldAvgNormal.y)+(forward.z*m_WorldAvgNormal.z);

	if (cosang < 0)				// going up the slope,	
	{
		grade = Abs (m_RawGroundPitch);
		friction += 2.0f * (((float) sin (grade)) * g_Gravity);
	}
	else
	{
		grade = Abs (m_RawGroundPitch);
		friction -= 2.0f * (((float) sin (grade)) * g_Gravity);
	}

	Clamp (friction,0.5f,1000.0f);
	if (currentSpeedMPS < 0)
	{
		currentSpeedMPS += friction * time_slice;
		Max_Clamp (currentSpeedMPS,0);
	}
	else
	{
		currentSpeedMPS -= friction * time_slice;
		Min_Clamp (currentSpeedMPS,0);
	}


	localSpaceVelocity.linearMotion.z = currentSpeedMPS;

	//
	//----------------------
	// Set the KPH variables
	//----------------------
	//
	currentSpeedKPH = currentSpeedMPS * 3.6f;
	speedDemandKPH = speedDemandMPS * 3.6f;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Vehicle::FindGroundAngle(void)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Vehicle::FindGroundAngle");

	//
	//---------------------------------------------------
	// Look for the centerpoint of where our feet are now
	//---------------------------------------------------
	//
	const LinearMatrix4D& local_to_world = GetLocalToWorld();
	Stuff::Line3D line;
	line.m_length = 2000.0f;
	line.m_direction = Vector3D::Down;
	line.m_origin = local_to_world;
	line.m_origin.y += 1000.0f;
	Stuff::Normal3D normal;
	CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
	Check_Object(CollisionGrid::Instance);
	
	Entity *entity_hit = CollisionGrid::Instance->ProjectLine(&query);

	if (!entity_hit)
		STOP(("Vehicle is off edge"));

	materialHit = query.m_material;
	//
	//----------------------------------------------------------------
	// Figure out the slope of the ground under the center of the vehicle
	//----------------------------------------------------------------
	//
	Stuff::Vector3D world_avg_normal(*query.m_normal);
	m_WorldAvgNormal = world_avg_normal;
	m_RawGroundPitch = Arccos (world_avg_normal.y);

	LinearMatrix4D tempmat (GetLocalToWorld ());
	tempmat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);

	m_LocalNormal.MultiplyByInverse(world_avg_normal, tempmat);
	rawLocalGroundRoll = -Arctan(m_LocalNormal.x, m_LocalNormal.y);
	rawLocalGroundPitch = -Arctan(m_LocalNormal.z, m_LocalNormal.y);

	//
	//--------------------------------------------------------------
	// take our normal sum and figure out the local pitch/roll angle
	//--------------------------------------------------------------
	//
	localGroundRoll = rawLocalGroundRoll;
	localGroundPitch = rawLocalGroundPitch;

	Point3D tr;
	line.FindEnd(&tr);
	Check_Object (Map::GetInstance());
	Check_Object (Map::GetInstance()->GetGameModel ());
	m_WaterDelta = Map::GetInstance()->GetGameModel ()->m_waterLevel - tr.y;
	if ((m_WaterDelta >= ShallowWaterDepth) && (m_WaterDelta < MidWaterDepth))
	{
		materialHit = FakeShallowWaterMaterial;
	}
	else if ((m_WaterDelta >= MidWaterDepth) && (m_WaterDelta < DeepWaterDepth))
	{
		materialHit = FakeMidWaterMaterial;
	}
	else if (m_WaterDelta >= DeepWaterDepth)
	{
		materialHit = FakeOceanicWaterMaterial;
	}

	Min_Clamp (m_WaterDelta,0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Vehicle::FindGroundAngleUnitDiamond(void)
{
	Check_Object(this);

	//
	//----------------------------------------------------
	// Figure out where the four points of the diamond are
	//----------------------------------------------------
	//
	const Stuff::LinearMatrix4D &local_to_world = GetLocalToWorld();
	Check_Object(&local_to_world);


	//
	//------------------------------------------
	// Set up our variables and the query object
	//------------------------------------------
	//
	Stuff::Point3D local_point;
	local_point = local_to_world;
	Stuff::Vector3D world_avg_normal;
	Stuff::Normal3D normal;
	Stuff::Line3D line;
	CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);

	//
	//---------------------------------------------------------------------------
	// Test the forward ray.  If we don't hit anything, assume the ground is flat
	//---------------------------------------------------------------------------
	//
	line.m_length = 20.0f;
	line.m_direction = Vector3D::Down;
	line.m_origin.Multiply(Stuff::Point3D(0.0f, 0.0f,  1.5f), local_to_world);
	line.m_origin.y += 5.0f;
	Check_Object(CollisionGrid::Instance);
	Stuff::Point3D collide_point;
	if (CollisionGrid::Instance->ProjectLine(&query))
		world_avg_normal = *query.m_normal;
	else
	{
		localGroundPitch = 0;
		localGroundRoll = 0;
		return;
	}

	//
	//---------------------
	// Test the reverse ray
	//---------------------
	//
	line.m_length = 20.0f;
	line.m_origin.Multiply(Stuff::Point3D(0.0f, 0.0f,  -1.5f), local_to_world);
	line.m_origin.y += 5.0f;
	if (CollisionGrid::Instance->ProjectLine(&query))
		world_avg_normal += *query.m_normal;
	else
	{
		localGroundPitch = 0;
		localGroundRoll = 0;
		return;
	}

	//
	//------------------
	// Test the left ray
	//------------------
	//
	line.m_length = 20.0f;
	line.m_origin.Multiply(Stuff::Point3D(1.0f, 0.0f,  0.0f), local_to_world);
	line.m_origin.y += 5.0f;
	if (CollisionGrid::Instance->ProjectLine(&query))
		world_avg_normal += *query.m_normal;
	else
	{
		localGroundPitch = 0;
		localGroundRoll = 0;
		return;
	}

	//
	//-------------------
	// Test the right ray
	//-------------------
	//
	line.m_length = 20.0f;
	line.m_origin.Multiply(Stuff::Point3D(-1.0f, 0.0f,  0.0f), local_to_world);
	line.m_origin.y += 5.0f;
	if (CollisionGrid::Instance->ProjectLine(&query))
		world_avg_normal += *query.m_normal;
	else
	{
		localGroundPitch = 0;
		localGroundRoll = 0;
		return;
	}

	//
	//---------------------------------------------------------------------
	// put the summed normal into local space, then figure out the relative
	// roll and pitch angles
	//---------------------------------------------------------------------
	//
	Stuff::Vector3D local_normal;
	local_normal.MultiplyByInverse(world_avg_normal, local_to_world);
	localGroundRoll = Arctan(local_normal.x, local_normal.y);
	localGroundPitch = Arctan(local_normal.z, local_normal.y);
	rawLocalGroundPitch = localGroundPitch;
	rawLocalGroundRoll = localGroundRoll;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::UpdateVehiclePosition(Scalar time_slice)
{
	Check_Object(this);
	
	//
	//---------------------------------------------------------------------
	// Apply velocity/acceleration to position and acceleration to velocity
	//---------------------------------------------------------------------
	//
	PRECOLLISION_LOGIC("Vehicle::UpdateVehiclePosition");
	const LinearMatrix4D &vehicle_to_world = GetLocalToWorld();
	Point3D world_translation(vehicle_to_world);
	Vector3D delta;
	delta.AddScaled(
		localSpaceVelocity.linearMotion,
		localSpaceAcceleration.linearMotion,
		0.5f * time_slice
	);
	Vector3D world_delta;
	world_delta.Multiply(delta, vehicle_to_world);
	Max_Clamp(world_delta.y, 0.0f);
	world_translation.AddScaled(world_translation, world_delta, time_slice);
	localSpaceVelocity.linearMotion.y += localSpaceAcceleration.linearMotion.y*time_slice;
	Max_Clamp(localSpaceVelocity.linearMotion.y, 0.0f);

	//
	//--------------------------------------
	// Update our turn rates and orientation
	//--------------------------------------
	//
	const GameModel *model = GetGameModel();
	Check_Object(model);
	Scalar temp = Lerp(model->fullStopTurnRate, model->topSpeedTurnRate, currentSpeedMPS/GetMaxSpeed());
	YawPitchRoll new_rotation(vehicle_to_world);
	temp *= time_slice;
	new_rotation.yaw += yawDemand * temp;
	new_rotation.pitch += pitchDemand * temp;
	new_rotation.roll += rollDemand * temp;



	//
	//----------------------------------------------
	// Adjust position from network
	//----------------------------------------------
	//

	YawPitchRoll correction_angle;
	Point3D correction_position;
	
	GetNetworkAdjustment(time_slice, correction_position, correction_angle);


	new_rotation.yaw.angle += correction_angle.yaw.angle;
	new_rotation.pitch.angle += correction_angle.pitch.angle;
	new_rotation.roll.angle += correction_angle.roll.angle;
	world_translation += correction_position;


	//
	//----------------------------------------------
	// Cast a ray down to see what we are driving on
	//----------------------------------------------
	//
	Stuff::Line3D line;
	line.m_length = m_groundRaycastHeight + 0.001f;
	line.m_direction = Vector3D::Down;
	line.m_origin = world_translation;
	line.m_origin.y += m_groundRaycastHeight;
	Stuff::Normal3D normal;
	CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
	Check_Object(CollisionGrid::Instance);




	//
	//------------------------------------------------
	// If we miss the ground, go to where gravity said
	//------------------------------------------------
	//
	LinearMatrix4D new_local_to_world;
	new_local_to_world.BuildRotation(new_rotation);
	if (!CollisionGrid::Instance->ProjectLine(&query))
	{
		materialHit = MaterialCount;
		worldSpaceVelocity.linearMotion.Multiply(
			localSpaceVelocity.linearMotion,
			vehicle_to_world
		);
		Max_Clamp(worldSpaceVelocity.linearMotion.y, 0.0f);


		
		new_local_to_world.BuildTranslation(world_translation);
	}

	//
	//--------------------------------------------------
	// Otherwise, adapt the vehicle to the ground normal
	//--------------------------------------------------
	//
	else
	{
		materialHit = query.m_material;
		line.FindEnd(&world_translation);

		
		new_local_to_world.BuildTranslation(world_translation);
		new_local_to_world.AlignLocalAxisToWorldVector(normal, Y_Axis, X_Axis, Z_Axis);

		//
		//--------------------------------------------------
		// Update the velocities to conform to the landscape
		//--------------------------------------------------
		//
		localSpaceVelocity.linearMotion.x = 0.0f;
		localSpaceVelocity.linearMotion.y = 0.0f;
		localSpaceVelocity.linearMotion.z = currentSpeedMPS;
		worldSpaceVelocity.linearMotion.Multiply(
			localSpaceVelocity.linearMotion,
			new_local_to_world
		);
	}
	SetNewLocalToParent(new_local_to_world);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::HookUpSubsystems()
{
	Stuff::ChainIteratorOf<Subsystem*> iterator(&subsystemsInVehicle);
	Subsystem *subsystem;
	while ((subsystem = iterator.ReadAndNext()) != NULL)
	{
		if (subsystem->IsDerivedFrom(Engine::DefaultData))
		{
			engine = Cast_Object(Engine *, subsystem);
		}
		else if (subsystem->IsDerivedFrom(Torso::DefaultData))
		{
			Torso* torso = Cast_Object(Torso*, subsystem);
			if (std::find(m_Torsos.begin(),m_Torsos.end(),torso) == m_Torsos.end())
			{
				m_Torsos.push_back(torso);
			}
		}
		//Tell the Damage Zone that holds the subsystem that it is there!!!
	}
	if(engine)
		engine->ConnectEngine(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::BecomeInteresting(bool render_me)
{
	Check_Object(this);
	LOAD_LOGIC("Become Interesting::Vehicle");






	//
	//--------------------------------------------------------------------
	// Let our parent deal with the video renderer.  If we have a shadow,
	// don't make another
	//--------------------------------------------------------------------
	//
	BaseClass::BecomeInteresting(render_me);

	CollisionVolume *cv = NULL;;
	cv = GetSolidVolume();
	if (!cv)
	{
		cv = this->GetHierarchicalVolume();
	}
	if (cv)
	{
		ExtentBox collisionBox(cv->m_localSpaceBounds);
		Point3D rotated_axis_extents;
		rotated_axis_extents.Multiply(cv->m_localSpaceBounds.axisExtents, cv->m_localSpaceBounds.localToParent);
		m_groundRaycastHeight = cv->m_localSpaceBounds.localToParent.entries[7];
		m_groundRaycastHeight += Stuff::Fabs(rotated_axis_extents.y);
	}


	if (!render_me || shadow)
		return;

	//
	//------------------------------
	// Otherwise, load up our shadow
	//------------------------------
	//
	gosFX::Light *main_light = Mission::GetInstance()->GetMainLight();
	const GameModel *model = GetGameModel();
	Check_Object(model);
	if (!model->shadowStream || !main_light)
	{
		shadow = NULL;
// MSL 5.02 Shadow
		shadowIntensity = 0.0f;
	}
	else
	{
		Resource shadow_stream(model->shadowStream);
		Verify(shadow_stream.DoesResourceExist());
		gos_PushCurrentHeap(MidLevelRenderer::LightsHeap);
		int version = MidLevelRenderer::ReadMLRVersion(&shadow_stream);
		Check_Object(TiledLightManager::Instance);
		while (shadow_stream.GetBytesRemaining() > 0)
		{
			int light_type;
			shadow_stream >> light_type;
			Verify(light_type == MidLevelRenderer::MLRLight::ShadowLight);
			MidLevelRenderer::MLRShadowLight *shadow_light =
				new MidLevelRenderer::MLRShadowLight(&shadow_stream, version);
			Check_Object(shadow_light);
			shadow =
				Cast_Object(ShadowLight*, TiledLightManager::Instance->MakeLight(shadow_light));
			shadow->m_shadowCaster = GetElement();
// MSL 5.02 Shadow
			gosFX::LightManager::Info info;
			shadow->GetInfo(&info);
			shadowIntensity = info.m_intensity;
		}
		gos_PopCurrentHeap();
	}




}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::BecomeUninteresting()
{
	Check_Object(this);
	BaseClass::BecomeUninteresting();

	//
	//------------------
	// Remove our shadow
	//------------------
	//
	if (shadow)
	{
		Check_Object(shadow);
		delete shadow;
		shadow = NULL;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::CreateTrail()
{
	Check_Object(this);
	POSTCOLLISION_LOGIC("Vehicle::Create Trail");

	const GameModel *model = GetGameModel();
	Check_Object(model);

	//
	//---------------------------------------------------------------------
	// If we hit legal material and can have a trail, get the ID of the one
	// we should use and see if it is the same as the one we currently have
	//---------------------------------------------------------------------
	//
	if (materialHit>=0 && materialHit<MaterialCount && model->trailEffectsTable != ResourceID::Null)
	{
		ResourceID trail_id;
		char material = materialHit;
		if(GetLocalToWorld().entries[7] - 1.0f <= Map::GetInstance()->GetGameModel ()->m_waterLevel)
			material = WaterMaterial;

		model->GetEffectResourceID(&trail_id, model->trailEffectsTable, material);
		if (trail_id == currentTrailID)
			return;

		//
		//----------------------------------------
		// If we currently have an effect, kill it
		//----------------------------------------
		//
		if (currentTrailEffect.GetCurrent())
		{
			currentTrailEffect.GetCurrent()->executionState->RequestState(
				Effect::ExecutionStateEngine::StoppingState);
			currentTrailEffect.Remove();
		}

		//
		//-------------------------------------
		// Make the new effect if it isn't null
		//-------------------------------------
		//
		if (trail_id != ResourceID::Null)
		{
			Effect *effect = CreateLoopingEffect(trail_id, this, true);
			if(effect)
			{
				Check_Object(effect);
				effect->BecomeInteresting (true);
				effect->videoStatus = true;
				effect->executionState->RequestState(Effect::ExecutionStateEngine::RunningState);
				currentTrailEffect.Add(effect);
			}
		}
		currentTrailID = trail_id;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::RemoveFromExecution()
{
	Check_Object(this);

	if(currentTrailEffect.GetCurrent())
	{
		currentTrailEffect.GetCurrent()->executionState->RequestState(
			Effect::ExecutionStateEngine::StoppingState);
		currentTrailEffect.Remove();
	}
	BaseClass::RemoveFromExecution();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::ProcessSelfDestruct()
{
	Check_Object(this);

	if (!IsDestroyed())
	{
		m_weaponShotMeLast = -50;
		ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0);
		// Remove our shadow
		if (shadow)
		{
			Check_Object(shadow);
			delete shadow;
			shadow = NULL;
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::SelfDestruct()
{
	Check_Object(this);

	m_whoShotMeLast = GetReplicatorID();

	if (!IsDestroyed() && executionState->GetState() != Entity::ExecutionStateEngine::NeverExecuteState)
	{
		m_needSelfDestruct = 1;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Point3D&
	Vehicle::EstimateFuturePosition(
		Stuff::Point3D *new_position,
		Stuff::Scalar seconds,
		bool consider_terrain
	)
{
	Check_Object(this);
	Check_Pointer(new_position);

	//
	//-------------------------------------------------
	// If we aren't moving, return our current position
	//-------------------------------------------------
	//
	*new_position = GetLocalToWorld();
	if ((Small_Enough(currentSpeedMPS)) || (IsDestroyed() == true))
		return *new_position;

	//
	//------------------------------------------------------------------------
	// Figure out our height based on current position.  If we are supposed to
	// consider the terrain, snap our position to the terrain
	//------------------------------------------------------------------------
	//
	Stuff::Scalar old_height = new_position->y;
	if (consider_terrain)
	{
		Stuff::Scalar h = GetHeightAtPoint(*new_position, this);
		if (h != -100000.0f)
			old_height = h;
	}

	//
	//--------------------------
	// Project our forward speed
	//--------------------------
	//
	Stuff::UnitVector3D local_forward;
	GetLocalToWorld().GetLocalForwardInWorld(&local_forward);
	new_position->AddScaled(*new_position, local_forward, seconds * currentSpeedMPS);

	//
	//-------------------------------------------------------------
	// Assume that we swerve left at 1 mps at full stick deflection
	//
	// NOTE: This looks like doo-doo too
	//-------------------------------------------------------------
	//
	Stuff::UnitVector3D local_left;
	GetLocalToWorld().GetLocalLeftInWorld(&local_left);
	new_position->AddScaled(*new_position, local_left, seconds * yawDemand);

	//
	//--------------------------------------------------------------------
	// If we are considering terrain, see how our altitude would have been
	// affected
	//--------------------------------------------------------------------
	//
	if (consider_terrain)
	{
		Stuff::Scalar new_height = GetHeightAtPoint(*new_position, this);
		if (new_height != -100000.0f)
			new_position->y += new_height - old_height;
	}

	return *new_position;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Vehicle::ClearLancemate(void)
{
	if (MWGUIManager::GetInstance ())
	{
		HUDComm *comm;
		HUDComponent *hud;
		hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
		Verify (hud);
		comm = Cast_Object (HUDComm *,hud);
		comm->DeselectLancemate ();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Vehicle::ProcessCommand(int command)
{

	MWPlayer *player;

	MWApplication *app = MWApplication::GetInstance();

	if (app->networkingFlag)
		player = Cast_Object(MWPlayer *, app->servedConnectionData[GetReplicatorID().connectionID].clientPlayer);
	else
		player = Cast_Object(MWPlayer *, MWPlayer::GetInstance());

	if (app->networkingFlag)
	{
		switch(command)
		{
			case VehicleCommand::LancemateAttackMyTargetCommand:
			case VehicleCommand::LancemateDefendMyTargetCommand:
			case VehicleCommand::LancemateFormOnMeCommand:
			case VehicleCommand::LancemateHoldFireCommand:
			case VehicleCommand::LancemateGoToMyNavPointCommand:
			case VehicleCommand::LancemateStopCommand:
			case VehicleCommand::LancemateShutDownCommand:
			case VehicleCommand::LancemateAttackNearestCommand:
			case VehicleCommand::LancemateRepairCommand:
				return;
		}
	}

	switch(command)
	{
		case VehicleCommand::JumpCommand:
			JumpRequest();
			break;
		case VehicleCommand::StopJumpCommand:
			StopJumpRequest();
			break;
		case VehicleCommand::PowerDownCommand:
			if (vehicleShutDown)
				ShutDownRequest(false);
			else
				ShutDownRequest(true);
			break;
		case VehicleCommand::CrouchCommand:
			CrouchRequest();
			break;
		case VehicleCommand::GetUpCommand:
			GetUpRequestRequest();
			break;
		case VehicleCommand::PressFlushCommand:
			SetCooling(1);
			break;
		case VehicleCommand::ReleaseFlushCommand:
			SetCooling(0);
			break;
		case VehicleCommand::SelfDestructCommand:
			SelfDestruct();
			break;
		case VehicleCommand::EjectCommand:
			Eject ();
			break;
		case VehicleCommand::SetTargetCommand:
			break;
		case VehicleCommand::ToggleSearchLightCommand:
			ToggleSearchLight();
			break;
		case VehicleCommand::LancemateAttackMyTargetCommand:
			if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
			{
				if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_1) == true)
				{
					if (MWGUIManager::GetInstance ())
					{
						HUDComm *comm;
						HUDComponent *hud;
						hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
						Verify (hud);
						comm = Cast_Object (HUDComm *,hud);
						comm->SelectLancemate (1);
					}
				}
			}
			else
			{
				if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_ATTACKPLAYERSTARGET) == true)
				{
					ClearLancemate ();
				}
			}
			break;

		case VehicleCommand::LancemateDefendMyTargetCommand:
			if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
			{
				if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_2) == true)
				{
					if (MWGUIManager::GetInstance ())
					{
						HUDComm *comm;
						HUDComponent *hud;
						hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
						Verify (hud);
						comm = Cast_Object (HUDComm *,hud);
						comm->SelectLancemate (2);
					}
				}
			}
			else
			{
				if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_DEFENDPLAYERSTARGET) == true)
				{
					ClearLancemate ();
				}
			}
			break;

		case VehicleCommand::LancemateFormOnMeCommand:
			if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
			{
				if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_3) == true)
				{
					if (MWGUIManager::GetInstance ())
					{
						HUDComm *comm;
						HUDComponent *hud;
						hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
						Verify (hud);
						comm = Cast_Object (HUDComm *,hud);
						comm->SelectLancemate (3);
					}
				}
			}
			else
			{
				if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_FORMONME) == true)
				{
					ClearLancemate ();
				}
			}
			break;

		case VehicleCommand::LancemateHoldFireCommand:
			if (player->m_LancemateCommandProxy.GetMode() == MWPlayer::LancemateCommandProxy::INVALID)
			{
				if (player->m_LancemateCommandProxy.SetMode(MWPlayer::LancemateCommandProxy::LANCEMATE_ALL) == true)
				{
					if (MWGUIManager::GetInstance ())
					{
						HUDComm *comm;
						HUDComponent *hud;
						hud = MWGUIManager::GetInstance ()->Component (MWGUIManager::HUD_COMM);
						Verify (hud);
						comm = Cast_Object (HUDComm *,hud);
						comm->SelectAllLancemates ();
					}
				}
			}
			else
			{
				if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_HOLDFIRE) == true)
				{
					ClearLancemate ();
				}
			}
			break;

		case VehicleCommand::LancemateGoToMyNavPointCommand:
			if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_GOTOMYNAVPOINT) == true)
			{
				ClearLancemate ();
			}
			break;

		case VehicleCommand::LancemateStopCommand:
			if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_STOP) == true)
			{
				ClearLancemate ();
			}
			break;

		case VehicleCommand::LancemateShutDownCommand:
			if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_SHUTDOWN) == true)
			{
				ClearLancemate ();
			}
			break;

		case VehicleCommand::LancemateAttackNearestCommand:
			if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_ATTACKNEARESTTHREAT) == true)
			{
				ClearLancemate ();
			}
			break;

		case VehicleCommand::LancemateRepairCommand:
			if (player->m_LancemateCommandProxy.IssueCommand(this,MW4AI::LancemateCommands::LANCEMATE_REPAIRATNEARESTREPAIRBAY) == true)
			{
				ClearLancemate ();
			}
			break;
		default:
			break;
	}
}

Scalar Vehicle::GetMaxSpeed()
{
	Check_Object(this);
	if(GetEngine())
		return GetEngine()->GetMaxSpeed();
	else
	{
		const GameModel *model = GetGameModel();
		return model->maxSpeed;
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Vehicle::GetNetworkPosition(Stuff::Point3D &current_position, Stuff::YawPitchRoll &current_rotation)
{
	current_position = GetLocalToWorld();
	current_rotation = GetLocalToWorld();
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Vehicle::GetDeadReckonedNetworkPosition(Stuff::Point3D &current_position, Stuff::YawPitchRoll &current_rotation)
{

	current_position = GetLocalToWorld();
	current_rotation = GetLocalToWorld();

	current_position += correctionPosition;
	current_rotation.yaw += correctionAngle.yaw;
	current_rotation.pitch += correctionAngle.pitch;
	current_rotation.roll += correctionAngle.roll;

}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Vehicle::ClearNetworkPosition()
{
	correctionPosition = Point3D::Identity;
	correctionAngle = YawPitchRoll::Identity;

	rotationCorrectionTime = 0.0f;
	positionCorrectionTime = 0.0f;
	
	correctingPosition = false;
	correctingRotation = false;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Vehicle::SetNetworkCorretionPosition(Stuff::Point3D new_position, Stuff::YawPitchRoll new_rotation, Point3D velocity, Scalar vel_request, Scalar latency, Scalar update_rate)
{
	
	YawPitchRoll original_rotation;
	Point3D original_translation;
	original_rotation = GetLocalToWorld();
	original_translation = GetLocalToWorld();

	new_rotation.Normalize();
	original_rotation.Normalize();
	correctionAngle.yaw.angle = new_rotation.yaw.angle - original_rotation.yaw.angle;
	correctionAngle.pitch.angle = new_rotation.pitch.angle - original_rotation.pitch.angle;
	correctionAngle.roll.angle = new_rotation.roll.angle - original_rotation.roll.angle;
	correctionAngle.Normalize();

	correctionPosition.Subtract(new_position, original_translation);
	

	Scalar rotation_length_squared = Abs(correctionAngle.yaw.angle*correctionAngle.yaw.angle + correctionAngle.pitch.angle*correctionAngle.pitch.angle + correctionAngle.roll.angle*correctionAngle.roll.angle);

	Scalar correction_scale = rotation_length_squared / (15.0f*Degrees_Per_Radian);
	Clamp(correction_scale, 0.0f, 1.0f);
    rotationCorrectionTime = Lerp( 0.1f, 0.5f, correction_scale);

	correction_scale = correctionPosition.GetLengthSquared() / 25.0f;
	Clamp(correction_scale, 0.0f, 1.0f);
	positionCorrectionTime = Lerp( 0.1f, 0.5f, correction_scale);


	correctingPosition = true;
	correctingRotation = true;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

bool Vehicle::GetNetworkAdjustment(Stuff::Scalar time_slice, Point3D &adjustment_position, Stuff::YawPitchRoll &adjustment_rotation)
{
	
	adjustment_position = Point3D::Identity;
	adjustment_rotation = YawPitchRoll::Identity;



	if (correctingPosition)
	{

		// if we have an amount of time to correct over
		if (positionCorrectionTime > 0.0f)
		{		
			// else incrementaly add in some of that position.
			Stuff::Scalar lerp_value = positionCorrectionTime / time_slice;
			Max_Clamp(lerp_value, 1.0f);

			adjustment_position.Lerp(Point3D::Identity, correctionPosition, lerp_value);

			correctionPosition -= adjustment_position;
			positionCorrectionTime -= time_slice;
		
		}
		else
		{
			// else we are done
			adjustment_position = correctionPosition;
			correctionPosition = Point3D::Identity;
			positionCorrectionTime = 0.0f;
			correctingPosition = false;
		}
	}

	

	if (correctingRotation)
	{
		if (rotationCorrectionTime > 0.0f)
		{
			
			Stuff::Scalar lerp_value = rotationCorrectionTime / time_slice;
			Max_Clamp(lerp_value, 1.0f);

			
			adjustment_rotation.Lerp(YawPitchRoll::Identity, correctionAngle, lerp_value);

			correctionAngle.yaw.angle -= adjustment_rotation.yaw.angle;
			correctionAngle.pitch -= adjustment_rotation.pitch.angle;
			correctionAngle.roll.angle -= adjustment_rotation.roll.angle;
			rotationCorrectionTime -= time_slice;

		}
		else
		{
			// else we are done
			adjustment_rotation = correctionAngle;
			correctionAngle = YawPitchRoll::Identity;
			rotationCorrectionTime = 0.0f;
			correctingRotation = false;
		}
	}

	
	return true;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Vehicle::ReactToDestruction(int damage_mode, int damage_type)
{
	switch(damage_mode)
	{
		case InternalDamageObject::DestructionDamageMode:
			SetDead ();
			break;
	}
	BaseClass::ReactToDestruction (damage_mode,damage_type);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::AddStatsToString(std::string& s)
{
	if (GetTeam() != MWApplication::No_Team)
	{
		s += "Team: ";
		s += IntToString(GetTeam());
		s += "  ";
	}

	MWObject::AddStatsToString(s);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Vehicle::AddToSensorCellMap()
{
	SensorCellMap* sensor_cell_map = GetSensorCellMap();

	if (sensor_cell_map != 0)
	{
		sensor_cell_map->Add(this,m_SensorCellMapX,m_SensorCellMapZ);
	}
}

void
	Vehicle::RemoveFromSensorCellMap()
{
	if (m_SensorCellMapX < 0)
	{
		return;
	}

	SensorCellMap* sensor_cell_map = GetSensorCellMap();

	if (sensor_cell_map != 0)
	{
		sensor_cell_map->Remove(this,m_SensorCellMapX,m_SensorCellMapZ);
	}
}

SensorCellMap*
	Vehicle::GetSensorCellMap()
{
	if (MWMission::GetInstance() == 0)
	{
		return(0);
	}

	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
	return (&mwmission->m_VehicleAndTurretCellMap);
}

void
	Vehicle::UpdateSensorCellMapPosition()
{
	#ifdef NEW_SENSOR_TIMING
	__int64 start_time = GetCycles();
	#endif

	if (m_LastSensorCellPositionUpdate + sensor_map_update_frequency > gos_GetElapsedTime())
	{
		#ifdef NEW_SENSOR_TIMING
		tVehicleTime += GetCycles() - start_time;
		#endif
		return;
	}

	m_LastSensorCellPositionUpdate = gos_GetElapsedTime();

	SensorCellMap* sensor_cell_map = GetSensorCellMap();

	if (sensor_cell_map == 0)
	{
		#ifdef NEW_SENSOR_TIMING
		tVehicleTime += GetCycles() - start_time;
		#endif
		return;
	}

	int x,z;
	sensor_cell_map->GetCellPos(x,z,GetLocalToWorld());

	if ((x != m_SensorCellMapX) ||
		(z != m_SensorCellMapZ))
	{
		RemoveFromSensorCellMap();

		m_SensorCellMapX = x;
		m_SensorCellMapZ = z;

		AddToSensorCellMap();
	}

	#ifdef NEW_SENSOR_TIMING
	tVehicleTime += GetCycles() - start_time;
	#endif
}


void MechWarrior4::VehicleSecurityCheckStop()
{
	_asm
	{
		nop
	}	
}
