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

#include "MW4Headers.hpp"

#include "Airplane.hpp"
#include "AirplaneAnimationStateEngine.hpp"
#include "helicopter.hpp"
#include "AI.hpp"
#include "aiutils.hpp"
#include "AI_Statistics.hpp"

#include <Adept\EntityManager.hpp>
#include <Adept\DamageObject.hpp>
#include <Adept\Effect.hpp>
#include <Adept\NameTable.hpp>
#include <Adept\CollisionGrid.hpp>
#include <Adept\Map.hpp>
#include <Adept\CollisionVolume.hpp>

stlport::vector<Scalar> *Airplane::m_Heights;
const Stuff::Scalar PlaneHeightSlotSize = 10.0f;
namespace MW4AI
{
	extern Stuff::Scalar g_MaxBuildingHeight;					// above the ground
	extern Stuff::Scalar g_MaxAirHeight;							// above the ground, to stay above movers
	extern Stuff::Scalar MinZ, MaxZ, MinX, MaxX;
}

//#############################################################################
//##################	Airplane::ExecutionStateEngine    #####################
//#############################################################################

const StateEngine::StateEntry
	Airplane::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(Airplane__ExecutionStateEngine, TakingOffMotion),
	STATE_ENTRY(Airplane__ExecutionStateEngine, LandingMotion),
	STATE_ENTRY(Airplane__ExecutionStateEngine, CrashingDeath),
	STATE_ENTRY(Airplane__ExecutionStateEngine, Taxi),
	STATE_ENTRY(Airplane__ExecutionStateEngine, Popup),
	STATE_ENTRY(Airplane__ExecutionStateEngine, Popdown),
	STATE_ENTRY(Airplane__ExecutionStateEngine, Float)
};


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

DWORD MechWarrior4::Executed_Airplane_Count = 0;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Airplane::ExecutionStateEngine::InitializeClass()
{
	Check_Object(StateEngine::DefaultData);
	Verify(!DefaultData);

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

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

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

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

	//
	//----------------------------------------------
	// Now, switch the state and tickle the watchers
	//----------------------------------------------
	//

	Airplane *airplane;
	airplane = Cast_Object(Airplane *, owningEntity);
	if((new_state == TakingOffMotionState) && (currentState == FlyingMotionState))
		return currentState;
	if((new_state == LandingMotionState) && (currentState != FlyingMotionState))
		return currentState;
	if((new_state == CrashingDeathState) && (currentState == AlwaysExecuteState))
	{
	//	airplane->shouldDie = true;
		airplane->shouldLeaveReckage = true;
		airplane->SetDying();
		return currentState;
	}

	switch (BaseClass::RequestState(new_state, data))
	{
		
	case FlyingMotionState:
		airplane->SetInAir();
		break;

	case TakingOffMotionState:
		airplane->SetTakingOff();
		break;
	}

	return currentState;
}

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

//#############################################################################
//###############################    Airplane    ##############################
//#############################################################################

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Airplane::InitializeClass()
{
	Check_Object(ExecutionStateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			AirplaneClassID,
	        "MechWarrior4::Airplane",
			BaseClass::DefaultData,
			NULL, 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::Factory)AirplaneAnimationStateEngine::Make
		);
	Register_Object(DefaultData);
	
	m_Heights = new stlport::vector<Scalar>;
	Check_Pointer (m_Heights);

	//Dave Need to add attribute entries
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		FlyingAltitude,
		flyingAltitude,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		TiltSpeed,
		tiltSpeed,
		Radian
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		TiltDegree,
		tiltDegree,
		Radian
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		PercentageOfTurnToStartTilt,
		percentageOfTurnToStartTilt,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		PercentageOfSpeedToStartTilt,
		percentageOfSpeedToStartTilt,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		MaxTurnAngle,
		maxTurnAngle,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		ThrusterAcceleration,
		thrusterAcceleration,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		PitchSpeed,
		pitchSpeed,
		Radian
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		PitchDegree,
		pitchDegree,
		Radian
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		PercentageOfSpeedToStartPitch,
		percentageOfSpeedToStartPitch,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		TakeOffSpeed,
		takeOffSpeed,
		Scalar
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		Airplane,
		TakingOffSFX,
		takingOffSFX,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		Airplane,
		InAirSFX,
		inAirSFX,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		Airplane,
		IdleSFX,
		idleSFX,
		int,
		IntClassID
	);		

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		MaxClimb,
		maxClimb,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		MaxDescent,
		maxDescent,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		TakeOffResource,
		takeOffResource,
		ResourceID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		TakeOffGroundResource,
		takeOffGroundResource,
		ResourceID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		LandingResource,
		landingResource,
		ResourceID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Airplane__GameModel,
		LandingGroundResource,
		landingGroundResource,
		ResourceID
	);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Airplane*
	Airplane::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);
	gos_PushCurrentHeap(Heap);
	Airplane *new_entity = new
		Airplane(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);

	Check_Object(EntityManager::GetInstance());
	EntityManager::GetInstance()->AddMover(new_entity);
	
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
	Airplane::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
	Check_Object(this);
	Check_Object(stream);

	stream->AllocateBytes(sizeof(CreateMessage));
	BaseClass::SaveMakeMessage(stream, res_file);
	CreateMessage *message =
		Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	message->altitude = flyingAltitude;
	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Airplane::Airplane(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Vehicle(class_data, message, base_id, element),
	m_PopupHeight(0),
	m_PopdownHeight(0),
	m_PopupClimbSpeed(0),
	m_crashingEffect(NULL)
{
	Check_Pointer(this);
	Check_Object(message);

	HookUpSubsystems();
	CommonCreation(message);
}

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

	m_OnGround = true;
	takeOffTargetPosition = Point3D::Identity;
	tiltAngle = 0.0f;
	pitchAngle = 0.0f;
	shouldDie = false;
	hasTakeOffAnimationLoaded = false;
	shouldLeaveReckage = false;
	m_Attacking = false;

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

	

	m_LastState = ExecutionStateEngine::TaxiState;

	flyingAltitude = message->altitude;
	m_AttackAltitude = 0;
	m_Attacking = false;

	if(flyingAltitude == 0.0f)
		flyingAltitude = model->flyingAltitude;
//	AdjustAltitude ();
	Max_Clamp (flyingAltitude,800);

	SetIdle();

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Airplane::Respawn(Entity__CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	
	BaseClass::Respawn(message);
	CreateMessage *airplane_message = Cast_Pointer(CreateMessage *, message);
	CommonCreation(airplane_message);
}


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

void
	Airplane::LoadAnimationScripts()
{
	Vehicle::LoadAnimationScripts();

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

	if (model->animScriptName[0] != NULL)
	{
		hasTakeOffAnimationLoaded = true;
		Check_Object(animStateEngine);
		animStateEngine->RequestState(AirplaneAnimationStateEngine::IdleState);
	}
	else
	{
		hasTakeOffAnimationLoaded = false;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Airplane::~Airplane()
{
	Check_Pointer(this);
}

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

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

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

void Airplane::TurnOn (void)
{
	lastParameterization = gos_GetElapsedTime ();
	executionState->RequestState(m_LastState);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Airplane::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Airplane");
	
	BaseClass::PreCollisionExecute(till);
	UnitVector3D world_down_in_local;

	//
	//---------------------------------
	// If we aren't executing, stop now
	//---------------------------------
	//
	Check_Object(executionState);
	int pre_state = executionState->GetState();
	Verify(pre_state != ExecutionStateEngine::NeverExecuteState);
	Set_Statistic(Executed_Airplane_Count, Executed_Airplane_Count+1);

	Scalar time_slice = GetTimeSlice(till);
	Verify(time_slice > 0.0f);

	switch (pre_state)
	{
	case ExecutionStateEngine::TaxiState:
		GetLocalToWorld().GetWorldDownInLocal(&world_down_in_local);
		localSpaceAcceleration.linearMotion.AddScaled(
			localSpaceAcceleration.linearMotion,
			world_down_in_local,
			g_Gravity
		);
		ComputeForwardSpeed(time_slice);
		UpdateVehiclePosition(time_slice);
		break;
	case ExecutionStateEngine::FlyingMotionState:
		ComputeForwardSpeed(time_slice);
		TiltPlane(time_slice);
		FlyingMovementSimulation(till);	
		break;

	case ExecutionStateEngine::TakingOffMotionState:
	//	TakeOffThrusterSimulation(till);
		instantaniousAngularVelocity = Stuff::Vector3D::Identity;
		animationVelocity = Stuff::Point3D::Identity;

		Check_Object(animStateEngine);

		if (hasTakeOffAnimationLoaded)
		{
			animStateEngine->RunStates(time_slice);
			TakeOffThrusterSimulation(till, animationVelocity, instantaniousAngularVelocity,false);
			if(animStateEngine->CurrentStateLoopedThisFrame())
			{
				animStateEngine->RequestState(AirplaneAnimationStateEngine::IdleState);
				Check_Object(executionState);
				executionState->RequestState(ExecutionStateEngine::FlyingMotionState);
				currentSpeedMPS = 78.0f;						// set to the last animation speed, hack but it works and we are two weeks from shipping.
				m_OnGround = false;
			}
		}
		else
		{
			animationVelocity.z = 10;
			animationVelocity.y = 10;
			TakeOffThrusterSimulation(till, animationVelocity, instantaniousAngularVelocity,true);
			m_OnGround = false;
		//	executionState->RequestState(ExecutionStateEngine::FlyingMotionState);
		}
		break;
		//SPEW(("daberger","AnimVelocity z: %f y: %f", animationVelocity.z, animationVelocity.y));

	case ExecutionStateEngine::LandingMotionState:
		TiltPlane(time_slice);
		LandingMovementSimulation(till);
		break;

	case ExecutionStateEngine::CrashingDeathState:
		CrashingDeathMovementSimulation(till);
		break;

	case ExecutionStateEngine::PopupState:
		PopUpOrDownSimulation(time_slice,m_PopupHeight);
		break;

	case ExecutionStateEngine::PopdownState:
		PopUpOrDownSimulation(time_slice,m_PopdownHeight);
		break;

	case ExecutionStateEngine::FloatState:
		FloatSimulation(time_slice);
		break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Airplane::ComputeForwardSpeed(Stuff::Scalar time_slice)
{
	Check_Object(this);
	const GameModel *model = GetGameModel();
	Check_Object(model);

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

	Verify ((model->moveTypeFlag == MWObject__GameModel::FLYER_MOVETYPE)||(model->moveTypeFlag == MWObject__GameModel::HELI_MOVETYPE)||(model->moveTypeFlag == MWObject__GameModel::DROPSHIP_MOVETYPE));		

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

	//
	//--------------------------------------------------------------------
	// If we are not going backwards, find out what speed we want after we
	// consider braking
	//--------------------------------------------------------------------
	//
	if (currentSpeedMPS >= 0.0f)
	{
		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);
		}
	}

	else
	{

		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, GetMaxSpeed());

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

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

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Airplane::CollisionHandler(
		Stuff::LinearMatrix4D *new_position,
		Stuff::DynamicArrayOf<CollisionData> *collisions
	)
{
	Check_Object(this);
	Entity::ExecutionStateEngine *engine = executionState;
	if (engine->GetState() == ExecutionStateEngine::NeverExecuteState)
	{
		Verify(!newCollisions);
		delete collisions;
		return false;
	}

	Verify(GetInterestLevel() != DormantInterestLevel);
	Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));

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

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

		//
		//-------------------------------------------------------------------
		// Airplanes don't collide with bridges.  The don't collide if flying
		//-------------------------------------------------------------------
		//
		Check_Object(executionState);
		if ((executionState->GetState() == ExecutionStateEngine::FlyingMotionState) ||	 // flying airplanes don't collide
			(executionState->GetState() == ExecutionStateEngine::PopupState) ||
			(executionState->GetState() == ExecutionStateEngine::FloatState) ||
			(executionState->GetState() == ExecutionStateEngine::PopdownState))
			continue;

		//
		//----------------------------------------------
		// If we are crashing, reset to our old position
		//----------------------------------------------
		//
		if (data->m_otherEntity->IsDerivedFrom (Airplane::DefaultData))
			continue;
		if(executionState->GetState() == ExecutionStateEngine::CrashingDeathState)
		{
			Check_Object(entityElement);
			*new_position = entityElement->GetNewLocalToParent();
			Point3D old_translation(entityElement->GetLocalToParent());
			new_position->BuildTranslation(old_translation);
			shouldDie = true;
			shouldLeaveReckage = false;
			DealSplashDamage();
			delete collisions;
			Verify(!newCollisions);
			return true;
		}
		if (data->m_otherEntity->CanBeWalkedOn())
			continue;
	}
	Verify(!newCollisions);
	newCollisions = collisions;
	return false;
}

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

	if (executionState->GetState() == ExecutionStateEngine::FlyingMotionState)
		worldSpaceVelocity.linearMotion.Multiply(localSpaceVelocity.linearMotion,GetLocalToWorld());
	lastParameterization = till;
	initialLocalToParent = GetLocalToParent();
	initialWorldSpaceVelocity = worldSpaceVelocity,
	initialWorldSpaceAcceleration = worldSpaceAcceleration;

	if (executionState->GetState() == ExecutionStateEngine::NeverExecuteState)
	{
		if (oldCollisions)
			delete oldCollisions;
		oldCollisions = NULL;
		if (newCollisions)
			delete newCollisions;
		newCollisions = NULL;
		return;
	}

	if(shouldDie)
	{
		const GameModel *model = GetGameModel();
		Check_Object(model);

		DealSplashDamage();
	
		if(m_crashingEffect.GetCurrent())
		{
			m_crashingEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
			m_crashingEffect.Remove();
			m_OnGround = true;
		}
		Entity::ReactToDestruction(InternalDamageObject::DestructionDamageMode,ProjectileDamageType);

	//	SetDestroyedFlag(InternalDamageObject::DestructionDamageMode);
		CreateEffect(model->secondaryDestroyedEffectResource, this);
		if(shouldLeaveReckage)
		{
			if(!m_deathEntity.GetCurrent())
			{
				Entity *death_entity = CreateStaticHermitEntity(model->deathEntityResource);
				if(death_entity) 
				{
					m_deathEntity.Remove();
					m_deathEntity.Add(death_entity);
				}
			}
		}
		RemoveCollision();
		if (newCollisions)
		{
			delete newCollisions;
			newCollisions = NULL;
		}

// MSL 5.02 bug fix
		// bug fix # 6523 - allow dropships to turn off
		if ( m_AI && executionState->GetState() == Vehicle::ExecutionStateEngine::AIMotionState)
		{
			m_AI->TurnOn();
		}

		executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
		m_OnGround = true;
		entityElement->SetAlwaysCullMode();
		lastParameterization = till;
		initialLocalToParent = GetLocalToParent();
		SetDead();
		SyncMatrices(true);
	}
	else
	{
		BaseClass::PostCollisionExecute(till);
	}
}

	inline Scalar FindTerrainHeight(Line3D& line, Adept::Entity* ignore)
	{
		Normal3D normal;
		Entity::CollisionQuery query(&line, &normal, Entity::CanBeWalkedOnFlag, ignore);

		CollisionGrid::Instance->ProjectLine(&query);

		Point3D point;
		line.FindEnd(&point);

		return (point.y);
	}

	Scalar FindTerrainHeightFromPoint(const Point3D& point, Adept::Entity* ignore)
	{
		if ((point.x <= MW4AI::MinX) ||
			(point.x >= MW4AI::MaxX) ||
			(point.z <= MW4AI::MinZ) ||
			(point.z >= MW4AI::MaxZ))
		{
			return (0);
		}

		Line3D line;
		line.m_length = 1200.0f;
		line.m_direction = Vector3D::Down;
		line.SetOrigin(point);

		return (FindTerrainHeight(line,ignore));
	}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Airplane::FlyingMovementSimulation(Stuff::Time till)
{
	Check_Object(this);

	const GameModel *model = GetGameModel();
	Scalar time_slice = GetTimeSlice(till);
	Verify(time_slice > 0);

//	STOP(("Not finished"));
//	ComputeVelocity(time_slice);

	const LinearMatrix4D &local_to_world = GetLocalToWorld();

	Point3D old_loc(local_to_world);

	//
	// add the local adjustment to the current position
	//
	Point3D new_translation;
	Vector3D local_motion;
	local_motion.Multiply(localSpaceVelocity.linearMotion,local_to_world);

	new_translation = old_loc;
	new_translation.x += local_motion.x * time_slice;
	new_translation.z += local_motion.z * time_slice;

	UnitVector3D unit_forward;
	local_to_world.GetLocalForwardInWorld(&unit_forward);
	Point3D forward(unit_forward);
	forward *= 25.0f;

	Point3D point(local_to_world);
	point += forward;
	point.y += 100.0f;

	Stuff::Scalar first_y(FindTerrainHeightFromPoint(point,this));

	Point3D point2(local_to_world);
	forward *= 4.0f;
	point2 += forward;
	point2.y += 100.0f;

	UnitVector3D unit_left;
	local_to_world.GetLocalLeftInWorld(&unit_left);
	Point3D left(unit_left);
	left *= 30.0f;

	UnitVector3D unit_right;
	local_to_world.GetLocalRightInWorld(&unit_right);
	Point3D right(unit_right);
	right *= 30.0f;

	Point3D point3(point2);
	
	point2 += left;
	point3 += right;

	Scalar second_y(FindTerrainHeightFromPoint(point2,this));
	Scalar third_y(FindTerrainHeightFromPoint(point3,this));

	Scalar worst_y(first_y);
	if (second_y > worst_y)
	{
		worst_y = second_y;
	}
	if (third_y > worst_y)
	{
		worst_y = third_y;
	}

	Scalar delta,change;

	if (m_Attacking)
		delta = (m_AttackAltitude + worst_y) - old_loc.y;
	else
		delta = (flyingAltitude + worst_y) - old_loc.y;

	if (delta > 0)
		change = model->maxClimb * time_slice;
	else
		change = model->maxDescent * time_slice;

	Clamp(delta,-change,change);
	new_translation.y = old_loc.y + delta;

	//
	// get the current yawpitchroll from the local to world matrix
	//
	Scalar temp = Lerp(model->fullStopTurnRate, model->topSpeedTurnRate, currentSpeedMPS/GetMaxSpeed());
	YawPitchRoll new_rotation(local_to_world);
	temp *= time_slice;
	new_rotation.yaw += yawDemand * temp;
	new_rotation.pitch += pitchDemand * temp;
	new_rotation.roll += rollDemand * temp;

//	new_rotation = local_to_world;
	new_rotation.yaw += (localSpaceVelocity.angularMotion.y  * time_slice);
	new_rotation.roll += (localSpaceVelocity.angularMotion.z  * time_slice);
//	new_rotation.pitch += (localSpaceVelocity.angularMotion.x * time_slice);
	new_rotation.roll = -tiltAngle;
	new_rotation.pitch = pitchAngle;


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

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

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


	//
	//----------------------------------------------
	// Set the new position
	//----------------------------------------------
	//

//	worldSpaceVelocity.linearMotion.Multiply(localSpaceVelocity.linearMotion,GetLocalToWorld());

	LinearMatrix4D new_local_to_world = LinearMatrix4D::Identity;
	new_local_to_world.BuildTranslation(new_translation);	
	new_local_to_world.BuildRotation(new_rotation);

	SetNewLocalToParent(new_local_to_world);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Airplane::Land(void)
{
	Check_Object(this);
}

void
	Airplane::TakeOff(Stuff::Scalar runway_distance)
{
	Check_Object(this);
	Check_Object(executionState);
	Check_Object(animStateEngine);

	executionState->RequestState(ExecutionStateEngine::TakingOffMotionState);
	m_OnGround = false;

	if (hasTakeOffAnimationLoaded)
		animStateEngine->RequestState(AirplaneAnimationStateEngine::TakeOffState);
}

void
	Airplane::Popup(Stuff::Scalar to_height)
{
	Check_Object(this);
	Check_Object(executionState);
	Check_Object(animStateEngine);

	if (executionState->GetState() == Airplane::ExecutionStateEngine::PopupState)
	{
		return;
	}

	m_PopdownHeight = ((Stuff::Point3D)GetLocalToWorld()).y;
	m_PopupHeight = to_height;

	m_PopupClimbSpeed = 0;
	m_YPR = GetLocalToWorld();

	executionState->RequestState(ExecutionStateEngine::PopupState);
	m_OnGround = false;
}

void
	Airplane::Popdown(Stuff::Scalar to_height)
{
	Check_Object(this);
	Check_Object(executionState);
	Check_Object(animStateEngine);

	if (executionState->GetState() == Airplane::ExecutionStateEngine::PopdownState)
	{
		return;
	}

	m_PopupHeight = ((Stuff::Point3D)GetLocalToWorld()).y;
	m_PopdownHeight = to_height;

	m_PopupClimbSpeed = 0;
	m_YPR = GetLocalToWorld();

	executionState->RequestState(ExecutionStateEngine::PopdownState);
	m_OnGround = false;
}

void
	Airplane::Float(const Stuff::Point3D& dest)
{
	Check_Object(this);
	Check_Object(executionState);
	Check_Object(animStateEngine);

	if ((executionState->GetState() == Airplane::ExecutionStateEngine::FloatState) &&
		(m_FloatTargetPosition == dest))
	{
		return;
	}

	m_YPR = GetLocalToWorld();

	m_FloatSpeed = 0;

	m_FloatTargetPosition = dest;
	executionState->RequestState(ExecutionStateEngine::FloatState);
	m_OnGround = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Airplane::TakeOffThrusterSimulation(Stuff::Time till, const Stuff::Vector3D& new_velocity, const Stuff::Vector3D& instantanious_angular_velocity,bool canswitch)
{
	Check_Object(this);

	Scalar time_slice = GetTimeSlice(till);
	if (canswitch)
	{
		executionState->RequestState(ExecutionStateEngine::FlyingMotionState);
	}
	
	//instantanious_angular_velocity.Divide(instantanious_angular_velocity, time_slice);


	//
	// get the local to world matrix from the locator
	//
	const Stuff::LinearMatrix4D &local_to_world = GetLocalToWorld();

	//
	// make a vector of where we want to go
	//
	Vector3D point = Point3D::Identity;
	point.Multiply( new_velocity, time_slice );

	//
	// put it in local space
	//
	Vector3D adj;
	adj.Multiply(point, local_to_world);

	//
	// add the local adjustment to the current position
	//
	Point3D new_translation;
	new_translation = local_to_world;
	new_translation += adj;

	//
	// get the current yawpitchroll from the local to world matrix
	//
	YawPitchRoll new_rotation(YawPitchRoll::Identity);
	new_rotation = local_to_world;

	
	//
	// then add in the yaw, pitch, and roll demands from the animation
	//

	new_rotation.yaw += instantanious_angular_velocity.y * time_slice;
	new_rotation.pitch += instantanious_angular_velocity.x * time_slice;
	new_rotation.roll += instantanious_angular_velocity.z * time_slice;


	//
	// create a new matrix and build the translation and rotation
	//

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

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

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

	//
	//----------------------------------------------
	// Set the new position
	//----------------------------------------------
	//

	LinearMatrix4D new_local_to_world;
	new_local_to_world.BuildTranslation(new_translation);
	new_local_to_world.BuildRotation(new_rotation);


	//
	// set the local to parent to the new matrix created above
	//
	
	SetNewLocalToParent(new_local_to_world);


}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Airplane::TakeOffMovementSimulation(Stuff::Time till)
{

	Check_Object(this);	
	Scalar time_slice = GetTimeSlice(till);
	Verify(time_slice > 0.0f);

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

	//
	//-------------------------
	// Compute the new location
	//-------------------------
	//
	localSpaceVelocity.linearMotion.z += speedDemand * model->thrusterAcceleration * time_slice;
	Clamp(localSpaceVelocity.linearMotion.z, 0.0f, GetMaxSpeed());
	worldSpaceVelocity.linearMotion.Multiply(localSpaceVelocity.linearMotion, GetLocalToWorld());
	Point3D plane_in_world(GetLocalToWorld());
	plane_in_world.AddScaled(plane_in_world, worldSpaceVelocity.linearMotion, time_slice);
	LinearMatrix4D local_to_new_world(true);
	
	//
	//-------------------------
	// Compute the new rotation
	//-------------------------
	//
	localSpaceVelocity.angularMotion.x = pitchDemand;
	localSpaceVelocity.angularMotion.y = (yawDemand * model->fullStopTurnRate);
	localSpaceVelocity.angularMotion.z = (rollDemand * model->fullStopTurnRate);
	worldSpaceVelocity.angularMotion.Multiply(localSpaceVelocity.angularMotion, GetLocalToWorld());

	//
	//---------------------------------------------------
	// Make a matrix representing the rotation this frame
	//---------------------------------------------------
	//
	Vector3D world_angular_step;
	world_angular_step.Multiply(worldSpaceVelocity.angularMotion, time_slice);
	UnitQuaternion spin;
	spin = world_angular_step;

	//
	//-------------------------------------------------------------------------
	// Convert the EulerAngles into a matrix and create the new rotation matrix
	// through concatenation
	//-------------------------------------------------------------------------
	//
	UnitQuaternion old_world_rotation;
	old_world_rotation = initialLocalToParent;
	UnitQuaternion new_world_rotation;
	new_world_rotation.Multiply(old_world_rotation, spin);
	
	//
	//----------------------------------------------
	// Adjust position from network
	//----------------------------------------------
	//

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

	YawPitchRoll new_rotation;
	new_rotation = new_world_rotation;
	
	plane_in_world += correction_position;
	new_rotation.yaw.angle += correction_angle.yaw.angle;
	new_rotation.pitch.angle += correction_angle.pitch.angle;
	new_rotation.roll.angle += correction_angle.roll.angle;

	//
	//----------------------------------------------
	// Set the new position
	//----------------------------------------------
	//


	local_to_new_world.BuildTranslation(plane_in_world);
	local_to_new_world.BuildRotation(new_rotation);
	SetNewLocalToParent(local_to_new_world);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Airplane::CrashingDeathMovementSimulation(Stuff::Time till)
{

	Check_Object(this);	
	Scalar time_slice = GetTimeSlice(till);
	Verify(time_slice > 0.0f);

	Vector3D new_speed = localSpaceVelocity.linearMotion;

	UnitVector3D world_down_in_local,world_forward_in_local;
	Vector3D tempvec = Vector3D::Identity;
	GetLocalToWorld().GetWorldDownInLocal(&world_down_in_local);
	GetLocalToWorld().GetWorldForwardInLocal (&world_forward_in_local);

//	new_speed.AddScaled (tempvec,world_forward_in_local,currentSpeedMPS*time_slice);
	new_speed.AddScaled (new_speed,world_down_in_local,g_Gravity*time_slice);

//	new_speed.y -= g_Gravity * time_slice;
//	new_speed.z = Fabs(new_speed.z);

//	localSpaceVelocity.linearMotion= new_speed;
	worldSpaceVelocity.linearMotion.Multiply(new_speed, GetLocalToWorld());

	const Stuff::LinearMatrix4D &local_to_world = GetLocalToWorld();

	Vector3D rotation_vector;
	rotation_vector.x = 1.0f;
	rotation_vector.y = 0.0f;
	rotation_vector.z = 0.0f;
	localSpaceVelocity.angularMotion = rotation_vector;
	worldSpaceVelocity.angularMotion.Multiply(rotation_vector, GetLocalToWorld());

	Point3D new_translation;
	Vector3D local_motion;
	local_motion.Multiply(localSpaceVelocity.linearMotion, GetLocalToWorld());
	new_translation = local_to_world;
	new_translation.x += local_motion.x * time_slice;
	new_translation.y += local_motion.y * time_slice;
	new_translation.z += local_motion.z * time_slice;

	//
	// get the current yawpitchroll from the local to world matrix
	//
	YawPitchRoll new_rotation(YawPitchRoll::Identity);
	new_rotation = local_to_world;
	new_rotation.yaw += (localSpaceVelocity.angularMotion.y  * time_slice);
	new_rotation.pitch += (localSpaceVelocity.angularMotion.x  * time_slice);
	Max_Clamp(new_rotation.pitch, 1.0f);
	new_rotation.roll += (localSpaceVelocity.angularMotion.z  * time_slice);

	LinearMatrix4D new_local_to_world=LinearMatrix4D::Identity;


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

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

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


	new_local_to_world.BuildTranslation(new_translation);
	new_local_to_world.BuildRotation(new_rotation);


	Stuff::Line3D line;
	UnitVector3D forward;
	new_local_to_world.GetLocalForwardInWorld(&forward);
	line.m_length = 3.5f;
	line.m_direction = forward;
	Point3D old_translation(GetLocalToWorld());
	line.m_origin = old_translation;

	

	SetNewLocalToParent(new_local_to_world);

	//
	// Prepare query
	//	
	Stuff::Normal3D normal;
	CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);

	//
	// Cast ray
	//
	Adept::Entity *entity_hit;
	Check_Object(CollisionGrid::Instance);
	entity_hit = CollisionGrid::Instance->ProjectLine(&query);
	
	if(entity_hit != NULL)
	{
		shouldDie = true;
		shouldLeaveReckage = true;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Point3D&
	Airplane::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;

	//
	//--------------------------
	// 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);

	return *new_position;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Airplane::LandingMovementSimulation(Stuff::Time till)
{
	Check_Object(this);


}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Airplane::TiltPlane(Stuff::Scalar time_slice)
{
	const GameModel *model = GetGameModel();

	Scalar max_turn_band = model->fullStopTurnRate;
	Scalar min_turn_band = max_turn_band * model->percentageOfTurnToStartTilt;
	
	Scalar max_speed_band = GetMaxSpeed();
	Scalar min_speed_band = max_speed_band * model->percentageOfSpeedToStartTilt;

	tiltRequest = 0.0f;
	Scalar speed_ratio = 0.0f;
	Scalar turn_ratio = 0.0f;

	if (yawDemand*model->fullStopTurnRate > min_turn_band || yawDemand*model->fullStopTurnRate < -min_turn_band)
	{
		if (currentSpeedMPS > min_speed_band)
		{
			speed_ratio = (currentSpeedMPS - min_speed_band)/(max_speed_band - min_speed_band);
			Max_Clamp(speed_ratio, 1.0f);

			if (yawDemand > 0)
			{
				turn_ratio = (yawDemand*model->fullStopTurnRate - min_turn_band)/(max_turn_band - min_turn_band);
				Max_Clamp(turn_ratio, 1.0f);
			}
			else
			{
				turn_ratio = -(-yawDemand*model->fullStopTurnRate - min_turn_band)/(max_turn_band - min_turn_band);
				Min_Clamp(turn_ratio, -1.0f);
			}

			tiltRequest = model->tiltDegree * turn_ratio * speed_ratio;
		}
	}
	
	if (tiltAngle < tiltRequest)
	{
		tiltAngle += (model->tiltSpeed*time_slice);
		Max_Clamp(tiltAngle, tiltRequest);
	}
	else if (tiltAngle > tiltRequest)
	{
		tiltAngle -= (model->tiltSpeed*time_slice);
		Min_Clamp(tiltAngle, tiltRequest);
	}
	
	Scalar max_pitch_speed_band = GetMaxSpeed();
	Scalar min_pitch_speed_band = max_pitch_speed_band * model->percentageOfSpeedToStartPitch;

	pitchRequest = 0.0f;
	Scalar pitch_speed_ratio = 0.0f;

	if (currentSpeedMPS > min_speed_band)
	{
		pitch_speed_ratio = (currentSpeedMPS - min_pitch_speed_band)/(max_pitch_speed_band - min_pitch_speed_band);
		Max_Clamp(pitch_speed_ratio, 1.0f);

		pitchRequest = model->pitchDegree * pitch_speed_ratio;
	}
	if (pitchAngle < pitchRequest)
	{
		pitchAngle += (model->pitchSpeed*time_slice);
		Max_Clamp(pitchAngle, pitchRequest);
	}
	else if (pitchAngle > pitchRequest)
	{
		pitchAngle -= (model->pitchSpeed*time_slice);
		Min_Clamp(pitchAngle, pitchRequest);
	}
}

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

void
	Airplane::ReactToDestruction(int damage_mode, int damage_type)
{
//	if (!IsDestroyed())
//	{
//		Check_Object(executionState);
		
//	}
	switch(damage_mode)
	{
	case InternalDamageObject::DestructionDamageMode:
		{
			if((!IsDestroyed()) && (executionState->GetState() != ExecutionStateEngine::CrashingDeathState))
			{
				Check_Object(executionState);
				executionState->RequestState(ExecutionStateEngine::CrashingDeathState);
				if(m_AI)
				{
					Check_Object(m_AI);
					m_AI->Die();
				}

				const GameModel *model = GetGameModel();
				Check_Object(model);
				Effect *effect;
				effect = CreateEffect(model->destroyedEffectResource, this, true);
				if(effect)
					m_crashingEffect.Add(effect);

		//		DestroyChildren();
//				Entity::ReactToDestruction(damage_mode, damage_type);
				break;
			}
		}
	}

	#ifdef LAB_ONLY
	if (MW4AI::Statistics::Enabled() == true)
	{
		MW4AI::Statistics::NotifyDestroyed(objectID);
	}
	#endif
}
	
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void
	Airplane::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

void Airplane::AdjustAltitude (void)
{

	if (flyingAltitude < MW4AI::g_MaxBuildingHeight)
	{
		flyingAltitude = MW4AI::g_MaxBuildingHeight+10;
	}
	Max_Clamp (flyingAltitude,800);
#if 0
	stlport::vector<Scalar>::iterator iter;
	Scalar min,max;
	bool flag;
	min = MW4AI::g_MaxBuildingHeight;
	max = min;
	flag = false;
	for (iter = m_Heights->begin ();iter != m_Heights->end ();iter++)
	{
		if (*iter < min)
			min = *iter;
		if (*iter > max)
			max = *iter;
		if (!flag)
		{
			if (((flyingAltitude+PlaneHeightSlotSize) > *iter) && ((flyingAltitude-PlaneHeightSlotSize) < *iter))
			{
				flag = true;
			}
		}
	}

	flyingAltitude = max + PlaneHeightSlotSize;
	m_Heights->push_back (flyingAltitude);
#endif
}

void
	Airplane::PopUpOrDownSimulation(Stuff::Scalar time_slice, Stuff::Scalar height)
{
	//
	//-------------------------
	// Determine our current position
	//-------------------------
	//
	Stuff::Point3D my_pos(GetLocalToWorld());

	//
	//-------------------------
	// Figure out whether we want to move up or down
	//-------------------------
	//
	Stuff::Scalar vertical_velocity(0);
	if (height > my_pos.y)
	{
		vertical_velocity = GetGameModel()->maxClimb;
	}
	else
	{
		vertical_velocity = -(GetGameModel()->maxDescent);
	}

	//
	//-------------------------
	// Set the current fall/climb speed, using acceleration when we start moving and a scaled difference when we're near the destination height
	//-------------------------
	//
	if (Stuff::Fabs(my_pos.y - height) < 8.0f)
	{
		m_PopupClimbSpeed = (vertical_velocity * (Stuff::Fabs(my_pos.y - height) / 8.0f));
	}
	else
	{
		m_PopupClimbSpeed += vertical_velocity * time_slice * 0.4f /* magic number to make it accelerate slowly */;
	}
	Clamp(m_PopupClimbSpeed,-(GetGameModel()->maxDescent),GetGameModel()->maxClimb);

	//
	//-------------------------
	// Compute the new location
	//-------------------------
	//
	localSpaceVelocity.linearMotion.x = 0;
	localSpaceVelocity.linearMotion.y = m_PopupClimbSpeed;
	localSpaceVelocity.linearMotion.z = 0;
	worldSpaceVelocity.linearMotion.Multiply(localSpaceVelocity.linearMotion,GetLocalToWorld());

	Stuff::LinearMatrix4D new_local_to_parent(GetLocalToWorld());

	my_pos.AddScaled(my_pos,worldSpaceVelocity.linearMotion,time_slice);

	m_YPR.yaw += yawDemand * time_slice;
	m_YPR.pitch += pitchDemand * GetGameModel()->fullStopTurnRate * time_slice;
	m_YPR.roll += rollDemand * GetGameModel()->fullStopTurnRate * time_slice;

	Clamp(m_YPR.pitch,-0.35f,0.35f);

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

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

	my_pos += correction_position;
	m_YPR.yaw.angle += correction_angle.yaw.angle;
	m_YPR.pitch.angle += correction_angle.pitch.angle;
	m_YPR.roll.angle += correction_angle.roll.angle;

	//
	//-------------------------------------------------------------------------
	// Plug it all into the matrix and make it happen
	//-------------------------------------------------------------------------
	//
	new_local_to_parent.BuildTranslation(my_pos);
	new_local_to_parent.BuildRotation(m_YPR);
	SetNewLocalToParent(new_local_to_parent);
}

void
	Airplane::FloatSimulation(Stuff::Scalar time_slice)
{
	//
	//-------------------------
	// Determine our current position
	//-------------------------
	//
	Stuff::Point3D my_pos(GetLocalToWorld());

	//
	//-------------------------
	// Determine the delta to where we want to be
	//-------------------------
	//
	Stuff::Scalar height = GetHeightAtPoint(m_FloatTargetPosition,this);
	if (height == -100000.0f)
	{
		height = my_pos.y;
	}
	else
	{
		height += flyingAltitude;
	}

	Stuff::Point3D delta;
	delta.Subtract(m_FloatTargetPosition,my_pos);
	delta.y = height - my_pos.y;

	//
	//-------------------------
	// Determine our velocity
	//-------------------------
	//
	const Stuff::Scalar max_throttle_multiplier(0.8f);
	const Stuff::Scalar distance_to_slow_down(25.0f);

	if (delta.GetApproximateLength() < distance_to_slow_down)
	{
		m_FloatSpeed = GetMaxSpeed() * max_throttle_multiplier * (delta.GetApproximateLength() / distance_to_slow_down);

		if (m_YPR.roll < 0)
		{
			rollDemand = -(m_YPR.roll);
		}
		else
		{
			rollDemand = m_YPR.roll;
		}
	}
	else
	{
		m_FloatSpeed += GetGameModel()->acceleration * time_slice;
		Clamp(m_FloatSpeed,0,GetMaxSpeed() * max_throttle_multiplier);

		if (YawToPoint(GetLocalToWorld(),m_FloatTargetPosition) < 0)
		{
			if (m_YPR.roll < 0.2f)
			{
				rollDemand = 0.5f;
			}
			else
			{
				rollDemand = -0.1f;
			}
		}
		else
		{
			if (m_YPR.roll > -0.2f)
			{
				rollDemand = -0.5f;
			}
			else
			{
				rollDemand = 0.1f;
			}
		}
	}

	if (Small_Enough(delta.GetLengthSquared()) == true)
	{
		return;
	}

	//
	//-------------------------
	// Multiply the delta
	//-------------------------
	//
	delta.Normalize(delta);
	delta *= m_FloatSpeed;

	//
	//-------------------------
	// Compute the new location
	//-------------------------
	//
	worldSpaceVelocity.linearMotion = delta;
	localSpaceVelocity.linearMotion.MultiplyByInverse(worldSpaceVelocity.linearMotion,GetLocalToWorld());

	Stuff::LinearMatrix4D new_local_to_parent(GetLocalToWorld());

	my_pos.AddScaled(my_pos,worldSpaceVelocity.linearMotion,time_slice);

	m_YPR.yaw += yawDemand * time_slice;
	m_YPR.pitch += pitchDemand * GetGameModel()->fullStopTurnRate * time_slice;
	m_YPR.roll += rollDemand * GetGameModel()->fullStopTurnRate * time_slice;
	Clamp(m_YPR.roll,-0.2f,0.2f);
	Clamp(m_YPR.pitch,-0.35f,0.35f);

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

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

	my_pos += correction_position;
	m_YPR.yaw.angle += correction_angle.yaw.angle;
	m_YPR.pitch.angle += correction_angle.pitch.angle;
	m_YPR.roll.angle += correction_angle.roll.angle;

	Stuff::Point3D p = my_pos;
	p.x += 10;
	p.z += 10;
	Stuff::Scalar y_1(FindTerrainHeightFromPoint(p,this));
	p.x -= 20;
	p.z -= 20;
	Stuff::Scalar y_2(FindTerrainHeightFromPoint(p,this));

	Stuff::Scalar y_delta = (flyingAltitude + ((y_1 + y_2) * 0.5f)) - my_pos.y;
	Stuff::Scalar change = 0;

	const GameModel *model = GetGameModel();
	if (y_delta > 0)
		change = model->maxClimb * time_slice;
	else
		change = model->maxDescent * time_slice;

	Clamp(y_delta,-change,change);
	my_pos.y += y_delta;

	//
	//-------------------------------------------------------------------------
	// Plug it all into the matrix and make it happen
	//-------------------------------------------------------------------------
	//
	
	new_local_to_parent.BuildTranslation(my_pos);
	new_local_to_parent.BuildRotation(m_YPR);
	SetNewLocalToParent(new_local_to_parent);
}



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

void Airplane::GetNetworkPosition(Stuff::Point3D &current_position, Stuff::YawPitchRoll &current_rotation)
{
	int pre_state = executionState->GetState();
	
	switch(pre_state)
	{
	case ExecutionStateEngine::PopupState:
	case ExecutionStateEngine::PopdownState:
	case ExecutionStateEngine::FloatState:
		current_position = GetLocalToWorld();
		current_rotation = m_YPR;
		break;

	default:
		current_position = GetLocalToWorld();
		current_rotation = GetLocalToWorld();
		break;
	}



}
