//===========================================================================//
// 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 "Hovercraft.hpp"

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

#include <ElementRenderer\Element.hpp>
#include <Adept\Map.hpp>

//#############################################################################
//###############################    Hovercraft    ##############################
//#############################################################################

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

DWORD MechWarrior4::Executed_Hovercraft_Count = 0;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Hovercraft::InitializeClass()
{
	Check_Object(ExecutionStateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			HovercraftClassID,
	        "MechWarrior4::Hovercraft",
			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)AnimationStateEngine::Make
		);
	Register_Object(DefaultData);
	
	//Dave Need to add attribute entries
	DIRECT_GAME_MODEL_ATTRIBUTE(
		Hovercraft__GameModel,
		HoverVehicleHeight,
		hoverVehicleHeight,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Hovercraft__GameModel,
		MaxHoverVehicleHeight,
		maxHoverVehicleHeight,
		Scalar
	);

}

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

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

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


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Hovercraft::Hovercraft(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Vehicle(class_data, message, base_id, element)
{
	Check_Pointer(this);
	Check_Object(message);

	HookUpSubsystems();
	CommonCreation(message);
}

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

	#if defined(_ARMOR)
		const GameModel *model = GetGameModel();
		Check_Object(model);
		Verify(model->moveTypeFlag == GameModel::HOVER_MOVETYPE);
	#endif
}

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

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Hovercraft::TurnOn (void)
{
	lastParameterization = gos_GetElapsedTime ();
	executionState->RequestState(ExecutionStateEngine::FlyingMotionState);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Hovercraft::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Hovercraft");

	BaseClass::PreCollisionExecute(till);

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

	Scalar time_slice = GetTimeSlice(till);
	if (pre_state == ExecutionStateEngine::FlyingMotionState)
	{
		ComputeForwardSpeed(time_slice);
		UpdateHoverVehiclePosition(time_slice);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Hovercraft::UpdateHoverVehiclePosition(Scalar time_slice )
{
	
	//
	//---------------------------------------------------------------------
	// Apply velocity/acceleration to position and acceleration to velocity
	//---------------------------------------------------------------------
	//
	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);
	world_translation.AddScaled(world_translation, world_delta, time_slice);
	localSpaceVelocity.linearMotion.y += localSpaceAcceleration.linearMotion.y*time_slice;

	//
	//--------------------------------------
	// 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 = 20.001f + model->hoverVehicleHeight + m_WaterDelta;
	line.m_direction = Vector3D::Down;
	line.m_origin = world_translation;
	line.m_origin.y += 20.0f;
	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
		);
		if (worldSpaceVelocity.linearMotion.y > 10.0f)
		{
			worldSpaceVelocity.linearMotion.y = 10.0f;
			localSpaceVelocity.linearMotion.MultiplyByInverse(
				worldSpaceVelocity.linearMotion,
				vehicle_to_world
			);
		}
		new_local_to_world.BuildTranslation(world_translation);
	}

	//
	//--------------------------------------------------
	// Otherwise, adapt the vehicle to the ground normal
	//--------------------------------------------------
	//
	else
	{
		materialHit = query.m_material;
		line.m_length -= model->hoverVehicleHeight;
		line.FindEnd(&world_translation);
		world_translation.y += m_WaterDelta;
		new_local_to_world.BuildTranslation(world_translation);
		if (m_WaterDelta > 0)
			new_local_to_world.AlignLocalAxisToWorldVector(Vector3D::Up, Y_Axis, X_Axis, Z_Axis);
		else
			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
	Hovercraft::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}
