//===========================================================================//
// File:	Truck.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 "Truck.hpp"
#include "gameinfo.hpp"
#include "MWDamageObject.hpp"
#include "obstacle.hpp"
#include "ai.hpp"
#include <adept\effect.hpp>

#include <Adept\EntityManager.hpp>

//#############################################################################
//###############################    Truck    ##############################
//#############################################################################

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

DWORD MechWarrior4::Executed_Truck_Count = 0;

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

	
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Truck::Truck(
	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);
}

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

Truck::~Truck()
{
	Check_Object(EntityManager::GetInstance());
}

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

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

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

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

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

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

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

	PRECOLLISION_LOGIC("Truck");
	#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

	//
	//-------------------------------------------------------------
	// Let the base class deal with setting up the motion variables
	//-------------------------------------------------------------
	//
	BaseClass::PreCollisionExecute(till);
	Set_Statistic(Executed_Truck_Count, Executed_Truck_Count+1);

	//
	//----------------------------------------------------------------------
	// 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
	//-------------------------------------------------------------------------
	//
	Scalar time_slice = GetTimeSlice(till);
	switch (pre_state)
	{
		//
		//----------------------------------------------------
		// Always execute state is for the normal driving code
		//----------------------------------------------------
		//
		case ExecutionStateEngine::DyingState:
			ComputeFrictionSpeed (time_slice);
			UpdateVehiclePosition (time_slice);
			break;
		case ExecutionStateEngine::DrivingMotionState:
			ComputeForwardSpeed(time_slice);
			UpdateVehiclePosition(time_slice);
			break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Truck::PostCollisionExecute (Stuff::Time time)
{
	Check_Object(this);

	POSTCOLLISION_LOGIC("Truck");
	int pre_state = executionState->GetState();
	Verify(pre_state != ExecutionStateEngine::NeverExecuteState);
	
	switch (pre_state)
	{
		case ExecutionStateEngine::DyingState:
			// check for unit stopped falling and moving
			if ((localSpaceVelocity.linearMotion.z == 0.0f) && ((localSpaceVelocity.linearMotion.y == 0.0f)))
			{
				Verify (m_DeathDamageMode != -1);
				Verify (m_DeathDamageType != -1);
				const GameModel *model = GetGameModel();
				Check_Object(model);

				DealSplashDamage();

				SetDead ();
				Entity::ReactToDestruction(m_DeathDamageMode,m_DeathDamageType);

//				CreateEffect(model->secondaryDestroyedEffectResource, this);
				if(!m_deathEntity.GetCurrent())
				{
					Entity *death_entity = CreateStaticHermitEntity(model->deathEntityResource);
					if(death_entity) 
					{
						m_deathEntity.Remove();
						m_deathEntity.Add(death_entity);
					}
				}
				executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
				entityElement->SetAlwaysCullMode();
				RemoveCollision();
				lastParameterization = GetTimeSlice (time);
				initialLocalToParent = GetLocalToParent();
				SyncMatrices(true);
			}
			break;
	}
	BaseClass::PostCollisionExecute (time);
}

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

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

void Truck::ReactToDestruction(int damage_mode, int damage_type)
{
	
	if (damage_mode != InternalDamageObject::DestructionDamageMode)
	{
		SPEW(("jerryeds", "DYING WITHOUT DEATH!!!"));
		SPEW(("daberger", "DYING WITHOUT DEATH!!!"));

		damage_mode = InternalDamageObject::DestructionDamageMode;
	}
	if ((localSpaceVelocity.linearMotion.z == 0.0f) && ((localSpaceVelocity.linearMotion.y == 0.0f)))
	{
		BaseClass::ReactToDestruction (damage_mode,damage_type);
		return;
	}

	switch(damage_mode)
	{
	case InternalDamageObject::DestructionDamageMode:
		{

			if(m_AI)
			{
				Check_Object(m_AI);
				m_AI->Die();
			}
			else
			{
				MW4AI::g_Rect4DHash->RemovePermRect (this);
			}
			if((!IsDestroyed()) && (!m_SecondaryExplosion.GetCurrent ()))
			{
				m_DeathDamageMode = damage_mode;
				m_DeathDamageType = damage_type;
				const GameModel *model = GetGameModel();
				SetDying ();
				Check_Object(model);
				Effect *eff;
				eff = CreateEffect(model->secondaryDestroyedEffectResource, this,true);
				Check_Object (eff);
				m_SecondaryExplosion.Add (eff);
				executionState->RequestState(ExecutionStateEngine::DyingState);
			}
		}
	}
}

