#include "MW4Headers.hpp"

#include "Dropship.hpp"

#include <Adept\Effect.hpp>
#include <Adept\CollisionGrid.hpp>
#include <Adept\EntityManager.hpp>
#include <Adept\DamageObject.hpp>
#include "ai.hpp"
// MSL 5.04 Dropship
#include "MWMission.hpp"

Scalar FindTerrainHeightFromPoint(const Point3D& point, Adept::Entity* ignore);

const Stuff::Time DROPSHIP_TAKEOFF_DELAY = 3.0f;

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

const StateEngine::StateEntry Dropship::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(Dropship__ExecutionStateEngine, DoorOpenMotion),
	STATE_ENTRY(Dropship__ExecutionStateEngine, DoorCloseMotion)
};

const Scalar DOOR_MOVE_AMOUNT = 10.0f;
const Scalar DOOR_OPEN_SPEED = 1.0f;			// mps

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


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

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

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

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

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

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

	Dropship *drop;
	drop = Cast_Object(Dropship *, owningEntity);
	if((new_state == DoorOpenMotionState) && (currentState == FlyingMotionState))
		return currentState;
	if((new_state == DoorCloseMotionState) && (currentState != TaxiState))
		return currentState;
	if((new_state == CrashingDeathState) && (currentState == AlwaysExecuteState))
	{
	//	airplane->shouldDie = true;
		drop->shouldLeaveReckage = true;
		drop->SetDying();
		return currentState;
	}

	BaseClass::RequestState(new_state, data);

	return currentState;
}

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

//#############################################################################
//###############################    Helicopter    ############################
//#############################################################################

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::InitializeClass()
{
	Check_Object(ExecutionStateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			DropshipClassID,
	        "MechWarrior4::Dropship",
			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::Make
		);
	Register_Object(DefaultData);

}

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Dropship::Dropship(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element):
	Helicopter(class_data, message, base_id, element)
{
	Check_Pointer(this);
	Check_Object(message);
	m_CanDie = false;
	m_NumEngines = 4;
	m_DoorAmount =0;
	m_FirstFrame = true;
	m_DeathStart = 0;

	m_DoorAR = FindChildMover ("joint_DoorAR");
	m_DoorFR = FindChildMover ("joint_DoorAL");
	m_DoorAL = FindChildMover ("joint_DoorFR");
	m_DoorFL = FindChildMover ("joint_DoorFL");
	m_HasDoors = false;
	if ((m_DoorAR) && (m_DoorFR) && (m_DoorAL) && (m_DoorFL))
		m_HasDoors = true;

	m_BaseYDoorAR = 0;
	m_BaseYDoorFR = 0;
	m_BaseYDoorAL = 0;
	m_BaseYDoorFL = 0;

// MSL 5.04 Dropship
	//should this dropship be scored?
	m_scoreThisDropship = false;
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::InitializeArmorArray()
{
	Check_Object(this);

	for(int i=0; i<12; i++)
	{
		armorArray[i] = -1.0f;
		DamageObject *damage_object = damageObjects.Find(0);
		if(damage_object)
		{
			armorArray[i] = damage_object->armorLevel;
			damage_object->SetArmorArrayEntry(&armorArray[i]);
		}
		else
		{
			armorArray[i] = -1;
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::Reuse(const CreateMessage *message,ReplicatorID *base_id)
{
	Check_Object(this);
	Check_Object(message);

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

void Dropship::Respawn(Adept::Entity::CreateMessage *message)
{
	m_NumEngines = 4;
	m_DoorAmount =0;
	m_FirstFrame = true;
// MSL 5.04 Dropship
	// without these, it keeps dying every 2 seconds
//	m_CanDie = false;
//	m_DeathStart = 0;
	BaseClass::Respawn (message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("DropShip");
	
	BaseClass::PreCollisionExecute(till);

	//
	//---------------------------------
	// If we aren't executing, stop now
	//---------------------------------
	//
	if ((m_FirstFrame) && (m_HasDoors))
	{
		Point3D loc;
		loc = (Point3D) m_DoorAR->GetLocalToParent ();
		m_BaseYDoorAR = loc.z;
		loc = (Point3D) m_DoorFR->GetLocalToParent ();
		m_BaseYDoorFR = loc.z;
		loc = (Point3D) m_DoorAL->GetLocalToParent ();
		m_BaseYDoorAL = loc.z;
		loc = (Point3D) m_DoorFL->GetLocalToParent ();
		m_BaseYDoorFL = loc.z;
	}
	m_FirstFrame = false;
	Check_Object(executionState);
	int pre_state = executionState->GetState();
	Verify(pre_state != ExecutionStateEngine::NeverExecuteState);

	if (((!m_crashingEffect.GetCurrent ()) || ((m_DeathStart+2.0) < till)) && m_CanDie)
	{
		Check_Object(executionState);

		if (m_OnGround)
			shouldLeaveReckage = true;
		else
			shouldLeaveReckage = false;
		shouldDie = true;
	}

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

	switch (pre_state)
	{
	case ExecutionStateEngine::DoorOpenMotionState:
		DoorOpenAnim(time_slice);
		break;

	case ExecutionStateEngine::DoorCloseMotionState:
		DoorCloseAnim(time_slice);
		break;
	}
}

bool Dropship::orderDoorOpen (void)
{
	if (executionState->GetState () != ExecutionStateEngine::DoorOpenMotionState)
	{
		executionState->RequestState (ExecutionStateEngine::DoorOpenMotionState);
		return false;
	}
	if ((m_DoorAmount == DOOR_MOVE_AMOUNT) || (!m_HasDoors))
	{
		executionState->RequestState (ExecutionStateEngine::TaxiState);
		m_OnGround = true;
		return true;
	}
	return false;
}

bool Dropship::orderDoorClose (void)
{
	if (executionState->GetState () != ExecutionStateEngine::DoorCloseMotionState)
	{
		executionState->RequestState (ExecutionStateEngine::DoorCloseMotionState);
		return false;
	}
	if ((m_DoorAmount == 0) || (!m_HasDoors))
	{
		executionState->RequestState (ExecutionStateEngine::TaxiState);
		m_OnGround = true;
		return true;
	}
	return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::DoorOpenAnim (Stuff::Time slice)
{
	if (!m_HasDoors)
		return;
	m_DoorAmount += (Scalar) (slice / DOOR_OPEN_SPEED);
	Clamp (m_DoorAmount,0,DOOR_MOVE_AMOUNT);

	LinearMatrix4D mat = m_DoorAR->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorAR - m_DoorAmount;
	m_DoorAR->SetNewLocalToParent (mat);

	mat = m_DoorFR->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorFR - m_DoorAmount;
	m_DoorFR->SetNewLocalToParent (mat);

	mat = m_DoorAL->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorAL + m_DoorAmount;
	m_DoorAL->SetNewLocalToParent (mat);

	mat = m_DoorFL->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorFL + m_DoorAmount;
	m_DoorFL->SetNewLocalToParent (mat);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::DoorCloseAnim (Stuff::Time slice)
{
	if (!m_HasDoors)
		return;
	m_DoorAmount -= (Scalar) (slice / DOOR_OPEN_SPEED);
	Clamp (m_DoorAmount,0,DOOR_MOVE_AMOUNT);
	
	LinearMatrix4D mat = m_DoorAR->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorAR - m_DoorAmount;
	m_DoorAR->SetNewLocalToParent (mat);

	mat = m_DoorFR->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorFR - m_DoorAmount;
	m_DoorFR->SetNewLocalToParent (mat);

	mat = m_DoorAL->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorAL + m_DoorAmount;
	m_DoorAL->SetNewLocalToParent (mat);

	mat = m_DoorFL->GetLocalToParent ();
	mat(W_Axis, Z_Axis) = m_BaseYDoorFL + m_DoorAmount;
	m_DoorFL->SetNewLocalToParent (mat);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::TakeOff(Stuff::Scalar runway_distance)
{
	Check_Object(this);
	
	const GameModel *model = GetGameModel();
	Check_Object(model);
	
	m_TakeOffStart = gos_GetElapsedTime ()+DROPSHIP_TAKEOFF_DELAY;


	takeOffTargetPosition = GetLocalToWorld();

	Stuff::Line3D line;
	Point3D point;

	point.x = takeOffTargetPosition.x;
	point.z = takeOffTargetPosition.z;
	point.y = flyingAltitude+100.0f;

	line.m_length = flyingAltitude+300.0f;
	line.m_direction = Vector3D::Down;
	line.SetOrigin(point);

	//
	// Prepare query
	//	
	Stuff::Normal3D inormal;
	Stuff::Line3D iline;
	Stuff::Normal3D normal;
	Entity::CollisionQuery query(&line, &normal, Entity::CanBeWalkedOnFlag, NULL);

	//
	// Cast ray
	//
	Adept::Entity *entity_hit;
	Check_Object(CollisionGrid::Instance);
	entity_hit = CollisionGrid::Instance->ProjectLine(&query);

	line.FindEnd(&point);

	m_StartTakeoffEffect = true;
	m_EffectGroundPosition = point;

//	CreateTakeOffEffect();
//	CreateTakeOffGroundEffect(point);

	takeOffTargetPosition = Vector3D(takeOffTargetPosition.x, point.y + flyingAltitude, takeOffTargetPosition.z);

	localSpaceDrag.linearMotion = model->linearDragCoefficients;
	localSpaceDrag.angularMotion = model->angularDragCoefficients;

	LinearMatrix4D world_to_local;
	world_to_local.Invert(GetLocalToWorld());
	localSpaceAcceleration.linearMotion.Multiply(
		worldSpaceAcceleration.linearMotion,
		world_to_local
	);
	localSpaceAcceleration.angularMotion.Multiply(
		worldSpaceAcceleration.angularMotion,
		world_to_local
	);
	Check_Object(executionState);
	executionState->RequestState(ExecutionStateEngine::TakingOffMotionState);
	m_OnGround = false;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Dropship::Land(void)
{
	Check_Object(this);
	Check_Object(executionState);

	landTargetPosition = GetLocalToWorld();

	Stuff::Line3D line;
	Point3D point;

	point.x = landTargetPosition.x;
	point.z = landTargetPosition.z;
	point.y = landTargetPosition.y-10;

	line.m_length = flyingAltitude+300.0f;
	line.m_direction = Vector3D::Down;
	line.SetOrigin(point);

	//
	// Prepare query
	//	
	Stuff::Normal3D inormal;
	Stuff::Line3D iline;
	Stuff::Normal3D normal;
	Entity::CollisionQuery query(&line, &normal, Entity::CanBeWalkedOnFlag, NULL);

	//
	// Cast ray
	//
	Adept::Entity *entity_hit;
	Check_Object(CollisionGrid::Instance);
	entity_hit = CollisionGrid::Instance->ProjectLine(&query);

	line.FindEnd(&point);

	m_StartLandingEffect = true;
	m_EffectGroundPosition = point;
//	CreateLandingEffect();
//	CreateLandingGroundEffect(point);

	landTargetPosition = Vector3D(landTargetPosition.x, point.y, landTargetPosition.z);

	executionState->RequestState(ExecutionStateEngine::LandingMotionState);
	m_OnGround = true;
}

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

	Point3D current_pos;
	current_pos = GetLocalToWorld();
	Scalar time_slice = GetTimeSlice(till);

	const GameModel *model = GetGameModel();
	Check_Object(model);
	Vector3D new_speed = Vector3D::Identity;
	if (gos_GetElapsedTime () > m_TakeOffStart)
		new_speed.y = 15.0f;
	else
		new_speed.y = 0;

	Clamp(new_speed.y, 0.0f, model->takeOffSpeed);
	localSpaceVelocity.linearMotion = new_speed;
	worldSpaceVelocity.linearMotion.Multiply(new_speed, GetLocalToWorld());

	pitchDemand = 0.0f;

	Vector3D rotation_vector;
#if 0
	rotation_vector.x = pitchDemand * model->pitchSpeed * time_slice;
	Clamp(rotation_vector.x, -model->pitchDegree, model->pitchDegree);
	rotation_vector.y = (yawDemand   * model->fullStopTurnRate);
	rotation_vector.z = (rollDemand   * model->fullStopTurnRate);
#endif
	rotation_vector.x = 0;
	rotation_vector.y = 0;
	rotation_vector.z = 0;
	localSpaceVelocity.angularMotion = rotation_vector;
	worldSpaceVelocity.angularMotion.Multiply(rotation_vector, GetLocalToWorld());

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

	//
	//-------------------------------------------------------------------------
	// Convert the EulerAngles into a matrix and create the new rotation matrix
	// through concatenation
	//-------------------------------------------------------------------------
	//
	UnitQuaternion rotation;
	rotation = initialLocalToParent;
	UnitQuaternion desire;
	desire.Multiply(rotation, spin);
	LinearMatrix4D new_position(true);
	new_position.BuildRotation(desire);

	Vector3D delta;
	delta.Multiply(worldSpaceVelocity.linearMotion, time_slice);
	Vector3D rotated_vector;
	rotated_vector.Multiply(delta, new_position);

	Point3D old_translation(initialLocalToParent);
	Point3D new_translation;
	new_translation.Add(
		old_translation,
		rotated_vector
	);
	new_position.BuildTranslation(new_translation);

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

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

	YawPitchRoll new_rotation;
	new_rotation = desire;
	
	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_position = LinearMatrix4D::Identity;
	new_position.BuildRotation(desire);
	new_position.BuildTranslation(new_translation);

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

	SetNewLocalToParent(new_position);

	if(current_pos.y >= takeOffTargetPosition.y)
	{
		Check_Object(executionState);
		if(m_takeOffEffect.GetCurrent())
		{
			m_takeOffEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
		}
		if(m_takeOffGroundEffect.GetCurrent())
		{
			m_takeOffGroundEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
		}
		executionState->RequestState(ExecutionStateEngine::FlyingMotionState);
		m_OnGround = false;
	}

}

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

	Point3D current_pos;
	current_pos = GetLocalToWorld();
	Scalar time_slice = GetTimeSlice(till);

	const GameModel *model = GetGameModel();
	Check_Object(model);
	Vector3D new_speed = Vector3D::Identity;
	new_speed.x = 0;
	new_speed.z = 0;
	new_speed.y = -15.0f;
	Clamp(new_speed.y, -model->takeOffSpeed, 0.0f);
	localSpaceVelocity.linearMotion = new_speed;
	worldSpaceVelocity.linearMotion.Multiply(new_speed, GetLocalToWorld());

	pitchDemand = 0.0f;

	Vector3D rotation_vector;
	rotation_vector.x = pitchDemand * model->pitchSpeed * time_slice;
	Clamp(rotation_vector.x, -model->pitchDegree, model->pitchDegree);
	rotation_vector.y = (yawDemand   * model->fullStopTurnRate);
	rotation_vector.z = (rollDemand   * model->fullStopTurnRate);
	localSpaceVelocity.angularMotion = rotation_vector;
	worldSpaceVelocity.angularMotion.Multiply(rotation_vector, GetLocalToWorld());

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

	//
	//-------------------------------------------------------------------------
	// Convert the EulerAngles into a matrix and create the new rotation matrix
	// through concatenation
	//-------------------------------------------------------------------------
	//
	UnitQuaternion rotation;
	rotation = initialLocalToParent;
	UnitQuaternion desire;
	desire.Multiply(rotation, spin);
	LinearMatrix4D new_position(true);
	new_position.BuildRotation(desire);

	Vector3D delta;
	delta.Multiply(worldSpaceVelocity.linearMotion, time_slice);
	Vector3D rotated_vector;
	rotated_vector.Multiply(delta, new_position);

	Point3D old_translation(initialLocalToParent);
	Point3D new_translation;
	new_translation.Add(
		old_translation,
		rotated_vector
	);
	new_position.BuildTranslation(new_translation);


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

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

	YawPitchRoll new_rotation;
	new_rotation = desire;
	
	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_position = LinearMatrix4D::Identity;
	new_position.BuildRotation(desire);
	new_position.BuildTranslation(new_translation);

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


	SetNewLocalToParent(new_position);

	Stuff::Point3D fredpos (new_position);
	Stuff::Line3D line;
	Point3D point;

	point.x = fredpos.x;
	point.z = fredpos.z;
	point.y = flyingAltitude+100.0f;

	line.m_length = flyingAltitude+300.0f;
	line.m_direction = Vector3D::Down;
	line.SetOrigin(point);

	//
	// Prepare query
	//	
	Stuff::Normal3D inormal;
	Stuff::Line3D iline;
	Stuff::Normal3D normal;
	Entity::CollisionQuery query(&line, &normal, Entity::CanBeWalkedOnFlag, NULL);

	//
	// Cast ray
	//
	Adept::Entity *entity_hit;
	Check_Object(CollisionGrid::Instance);
	entity_hit = CollisionGrid::Instance->ProjectLine(&query);

	line.FindEnd(&point);

	if(fredpos.y <= point.y)
	{
		Check_Object(executionState);
		if(m_landingEffect.GetCurrent())
		{
			m_landingEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
		}
		if(m_landingGroundEffect.GetCurrent())
		{
			m_landingGroundEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
		}
		executionState->RequestState(ExecutionStateEngine::TaxiState);
		localSpaceVelocity.linearMotion.x = 0;
		localSpaceVelocity.linearMotion.y = 0;
		localSpaceVelocity.linearMotion.z = 0;
		m_OnGround = true;
		pitchDemand = 0;
		yawDemand = 0;
		rollDemand = 0;
		speedDemand = 0;
	}
}

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

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

void Dropship::EngineDead (void)
{
	m_NumEngines--;
	if (m_NumEngines == 0)
	{
		SelfDestruct ();
	}
}

void Dropship::ReactToDestruction(int damage_mode, int damage_type)
{
	switch(damage_mode)
	{
	case InternalDamageObject::DestructionDamageMode:
		{
			if((!IsDestroyed()) && (executionState->GetState() != ExecutionStateEngine::CrashingDeathState) && !m_CanDie)
			{
// MSL 5.04 Dropship
				//added for scoring objective dropships (call it a building) in mission play games
				if (m_scoreThisDropship)
				{	
					Check_Object (Mission::GetInstance());
					Mission::GetInstance()->ScoringReactToBuildingDeath (m_whoShotMeLast, GetReplicatorID());
				}

				if(m_AI)
				{
					Check_Object(m_AI);
					m_AI->Die();
				}

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

}

