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


#include "MW4Headers.hpp"

#include "MWMover.hpp"
#include "MWObject.hpp"
#include "CombatAI.hpp"

#include <Adept\EntityManager.hpp>
#include <Adept\DamageObject.hpp>
#include <Adept\Effect.hpp>
#include <Adept\Map.hpp>
#include <Adept\Site.hpp>
#include <Adept\Mission.hpp>
#include <Adept\Renderer.hpp>

// ngLog addition
#include "MWApplication.hpp"
#include "Mech.hpp"

extern int g_nMR;
//#############################################################################
//##################    Mover::ExecutionStateEngine    ########################
//#############################################################################

const StateEngine::StateEntry
	MWMover::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(MWMover__ExecutionStateEngine, RegularMotion),
	STATE_ENTRY(MWMover__ExecutionStateEngine, CameraMotion)
};

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

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

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

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

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

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


//#############################################################################
//###############################    MWMover    ##############################
//#############################################################################

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

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

	INDIRECT_STATE_ATTRIBUTE(
		MWMover,
		ExecutionState,
		executionState,
		MWMover__ExecutionStateEngine
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		MWMover,
		IsDark,
		m_isDark,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		MWMover,
		LTread,
		LTread,
		AffineMatrix4D,
		AffineMatrix4DClassID
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		MWMover,
		RTread,
		RTread,
		AffineMatrix4D,
		AffineMatrix4DClassID
	);
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
	MWMover::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);

	//
	//------------------------------------------
	// Create the memory stream for the armature
	//------------------------------------------
	//
	//Assume that we don't have not changed our armature at all
	message->armatureStreamResourceID = armatureStreamResourceID;
	message->siteStreamResourceID = siteStreamResourceID;

	if((myParentVehicle != NULL) && (myParentVehicle->m_hasExecuted))
	{
		//If we do then save them to a stream and reset the armatureStreamID
		if(!childEntityChain.IsEmpty())
		{
			DynamicMemoryStream armature_stream(sizeof(WORD));
			armature_stream << static_cast<WORD>(0);
			ReplicatorID span = ReplicatorID::Null;
			ChainIteratorOf<Entity *> child_iterator(&childEntityChain);
			Entity *entity;

			while((entity = child_iterator.ReadAndNext()) != NULL)
			{
				Check_Object(entity);
				if (entity->IsDerivedFrom(MWMover::DefaultData))
				{
					Replicator::CreateMessage *create_message =
						entity->SaveMakeMessage(&armature_stream, res_file);
					if (create_message)
					{
						Check_Object(create_message);
						span += create_message->replicatorID;
						armature_stream.AdvancePointer(create_message->messageLength);
					}
				}
			}

			//
			//-------------------------------------------------------------------------
			// If we actually wrote out some children, rewind the stream and save it to
			// the resource.  Otherwise, just set the resourceID to NULL
			//-------------------------------------------------------------------------
			//
			
			if (armature_stream.GetBytesUsed() > sizeof(WORD))
			{
				armature_stream.Rewind();
				Verify(span.localID > 0);
				armature_stream << span.localID;
				Resource armature_resource(NULL, res_file);
				armature_resource.Save(&armature_stream, NULL);
				Check_Object(&armature_resource);
				message->armatureStreamResourceID = armature_resource.GetResourceID();
				message->replicatorID += span;
			}
			else
			{
				message->armatureStreamResourceID = ResourceID::Null;
			}
		}
		message->siteStreamResourceID = ResourceID::Null;
		if(!m_sites.IsEmpty())
		{
			SortedChainIteratorOf<Site*, MString> site_iterator(&m_sites);
			Site *site;
			DynamicMemoryStream site_stream;
			while((site = site_iterator.ReadAndNext()) != NULL)
			{
				Check_Object(site);
				site->SaveToStream(&site_stream);
			}
			if (site_stream.GetBytesUsed() > 0)
			{
				site_stream.Rewind();
				Resource site_resource(NULL, res_file);
				site_resource.Save(&site_stream, NULL);
				message->siteStreamResourceID = site_resource.GetResourceID();
			}
			else
			{
				message->siteStreamResourceID = ResourceID::Null;
			}
		}
	}

	Str_Copy(message->jointName, (char*)instanceName, sizeof(message->jointName));
	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWMover::MWMover(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	BaseClass(class_data, message, base_id, element),
	m_sites(NULL, true)
{
	Check_Pointer(this);
	Check_Object(message);

	if (!instanceName)
		instanceName = message->jointName;
	myParentVehicle = NULL;
	lightIntensity = 0.0f;
	
	armatureStreamResourceID = message->armatureStreamResourceID;
	siteStreamResourceID = message->siteStreamResourceID;

	if(message->armatureStreamResourceID != ResourceID::Null)
	{
		LoadArmatureFromResource(
			message->armatureStreamResourceID,
			base_id
		);
	}

	if(message->siteStreamResourceID != ResourceID::Null)
	{
		LoadSitesFromResource(message->siteStreamResourceID);
	}

	m_isDark = 1;

	LTread = Stuff::AffineMatrix4D::Identity;
	RTread = Stuff::AffineMatrix4D::Identity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWMover::~MWMover()
{
	DESTRUCTOR("Mover");

	Check_Object(this);

	m_sites.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::LoadArmatureFromResource(
		const ResourceID &armature_stream_id,
		ReplicatorID *base_id
	)
{
	Resource stream(armature_stream_id);
	Verify(stream.DoesResourceExist());
	stream.AdvancePointer(sizeof(WORD));

	while (stream.GetBytesRemaining() > 0)
	{
		Entity::CreateMessage *message =
			Cast_Pointer(Entity::CreateMessage*, stream.GetPointer());

		Entity *entity = CreateEntity(&stream, base_id);
		Check_Object(entity);
		AddChild(entity);
		
		stream.AdvancePointer(message->messageLength);
	}

	stream.UnloadData();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::LoadSitesFromResource(const ResourceID &site_stream_id)
{
	Resource stream(site_stream_id);
	Verify(stream.DoesResourceExist());

	while (stream.GetBytesRemaining() > 0)
	{
		MString site_name;
		YawPitchRoll rotation;
		Point3D translation;
		LinearMatrix4D local_to_parent;

		stream >> rotation;
		stream >> translation;
		stream >> site_name;

		local_to_parent.BuildRotation(rotation);
		local_to_parent.BuildTranslation(translation);

		Site *site = new Site(local_to_parent, this, site_name);
		m_sites.AddValue(site, site_name);
	}

	stream.UnloadData();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::ReuseArmature()
{
	Check_Pointer(this);

	Entity *entity;
	MWMover *mover;
	ChainIteratorOf<Entity *> child_iterator(GetChildEntityChain());
	if(armatureStreamResourceID != ResourceID::Null)
	{
		Resource child_stream(armatureStreamResourceID);
		child_stream.AdvancePointer(sizeof(WORD));
		while (((entity = child_iterator.ReadAndNext()) != NULL) &&
			(child_stream.GetBytesRemaining() > 0))
		{
			if(entity->IsDerivedFrom(MWMover::DefaultData))
			{
				mover = Cast_Object(MWMover *, entity);
				Entity::CreateMessage *entity_message =
					Cast_Pointer(Entity::CreateMessage*, child_stream.GetPointer());
				mover->ReuseArmature();
				mover->Respawn(entity_message);
				child_stream.AdvancePointer(entity_message->messageLength);
			}
		}
	}
}

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

	BaseClass::Respawn(message);
}

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

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

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

	//
	//------------------
	// Validate children
	//------------------
	//
	ChainIteratorOf<Entity*> children(&childEntityChain);
	Entity *entity;
	while ((entity = children.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
//		Verify(!entity->IsValid());
		entity->BecomeInteresting(render_me);
	}

	Check_Pointer(Mission::GetInstance());
#if 0
	const Mission::GameModel *mission_model = Mission::GetInstance()->GetGameModel();
	Check_Object(mission_model);
#endif

	if(!Mission::GetInstance()->DoesAllowRunningLights())
	{
		m_isDark = 1;
	}
	else
	{
		m_isDark = 0;
	}

	//
	//-----------------------------
	// Now make ourself interesting
	//-----------------------------
	//
	extern char g_SkinSet;
	g_SkinSet = GetSkinPrefix();
	extern int g_PilotDecal;
	extern int g_TeamDecal;
	g_PilotDecal = GetPilotDecal();
	g_TeamDecal = GetTeamDecal();
	
	BaseClass::BecomeInteresting(render_me);

	CheckVideoStates(render_me);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::CheckVideoStates(bool render_me)
{
	//Ok here we go...if we are destroyed than we must fake out the watchers
	if(IsDestroyed())
	{
		//Change to prestine state
		visualRepresentation = PrestineState;
		//Execute
		ExecuteChildComponentWebs();
		//Change back to its destroyed state
		visualRepresentation = DestroyedState;
		//Execute
		ExecuteChildComponentWebs();
	}
}

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

bool
	MWMover::FriendlyFire(const TakeDamageMessage *message)
{

	if (GetParentVehicle() != 0)
		return GetParentVehicle()->FriendlyFire(message);

	
	return Entity::FriendlyFire(message);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::ReactToHit(const Entity__TakeDamageMessage *message,Adept::DamageObject *part_hit)
{
	Check_Object(this);
	Check_Object(message);
	
	if(myParentVehicle)
	{
		Check_Object(myParentVehicle);
		myParentVehicle->ReactToHit(message,part_hit);
		myParentVehicle->NotifyWhoHitMe(message->inflictingEntityID, message->weaponID);
    }
    if (g_nMR == 2)
        return;

	// TODO: move this elsewhere; this should be more general and should take near
	// misses into account as well as direct hits -- PAULT
	if ((GetParentVehicle() != 0) &&
		(GetParentVehicle()->GetAI() != 0))
	{
		switch (message->typeOfDamage)
		{
			case BeamDamageType:
			case MissileDamageType:
			case ProjectileDamageType:
			{
				NotifyAIShot(*(GetParentVehicle()->GetAI()),message->inflictingEntityID);
				break;
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::ReactToDestruction(int damage_mode, int damage_type)
{
	Check_Object(this);

	bool kill_self = true;
	if(IsDestroyed())
		return;

	if ((GetParentVehicle() != 0) &&
		(GetParentVehicle()->GetAI() != 0) &&
		(damageObject != 0) &&
		(damageObject->internalDamageObject != 0))
	{
		GetParentVehicle()->GetAI()->NotifyComponentDestroyed(damageObject->internalDamageObject->damageZone);
	}

// MSL 5.07 Advance Mode
	MWApplication *m_App;
	m_App = MWApplication::GetInstance ();
	int m_AdvanceMode = m_App->GetLocalNetParams()->m_advancemodeOn;

// MSL 5.07 Advance Mode
	if (!m_AdvanceMode)
	{
		if ((damage_mode == InternalDamageObject::TorsoLeftDamageMode) ||
			(damage_mode == InternalDamageObject::TorsoRightDamageMode))
		{
			damage_mode = InternalDamageObject::DestructionDamageMode;
		}
//		if ((damage_mode == InternalDamageObject::ArmLeftDamageMode) ||
//			(damage_mode == InternalDamageObject::ArmRightDamageMode))
//		{
//			damage_mode = InternalDamageObject::DetachableDamageMode;
//		}
	}

	switch(damage_mode)
	{
	case InternalDamageObject::EngineDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->EngineDead ();
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
// MERC Code FIX
//			CreatePartDestructionEffect(resource_id);
//			DestroyChildren(damage_mode);
			break;
		}
	case InternalDamageObject::GimpLeftDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->SetLeftGimp();
				myParentVehicle->DestroyLeftLegSpecialZones();
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
			CreatePartDestructionEffect(resource_id);
			DestroyChildren(damage_mode);
			break;
		}
	case InternalDamageObject::GimpRightDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->SetRightGimp();
				myParentVehicle->DestroyRightLegSpecialZones();
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
			CreatePartDestructionEffect(resource_id);
			DestroyChildren(damage_mode);
			break;
		}
// MSL 5.05 Torso
	case InternalDamageObject::ArmLeftDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->DestroyLeftArmSpecialZones();
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
			CreatePartDestructionEffect(resource_id);
			DestroyChildren(damage_mode);
			break;
		}
// MSL 5.05 Torso
	case InternalDamageObject::ArmRightDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->DestroyRightArmSpecialZones();
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
			CreatePartDestructionEffect(resource_id);
			DestroyChildren(damage_mode);
			break;
		}
	case InternalDamageObject::DetachableDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
			CreatePartDestructionEffect(resource_id);
			DestroyChildren(damage_mode);
			break;
		}
// MSL 5.05 Torso
	case InternalDamageObject::TorsoLeftDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->DestroyLeftArm();
				myParentVehicle->DestroyLeftArmSpecialZones();
				myParentVehicle->DestroyLeftTorsoSpecialZones();
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
			CreatePartDestructionEffect(resource_id);
			DestroyChildren(damage_mode);
			break;
		}
// MSL 5.05 Torso
	case InternalDamageObject::TorsoRightDamageMode:
		{
			ResourceID resource_id = ResourceID::Null;
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->DestroyRightArm();
				myParentVehicle->DestroyRightArmSpecialZones();
				myParentVehicle->DestroyRightTorsoSpecialZones();
				const MWObject__GameModel *model = myParentVehicle->GetGameModel();
				resource_id = model->partDestructionEffectResource;
			}
			CreatePartDestructionEffect(resource_id);
			DestroyChildren(damage_mode);
			break;
		}
// MSL 5.03 Gyro Hit
	case InternalDamageObject::GyroHitDamageMode:
		{
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->ReactToDestruction(damage_mode, damage_type);
				kill_self = false;
			}
			else
			{
				DestroyChildren(damage_mode);
			}
			break;
		}
	case InternalDamageObject::HeadShotDamageMode:
		{
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->ReactToDestruction(damage_mode, damage_type);
				kill_self = false;
			}
			else
			{
				DestroyChildren(damage_mode);
			}
			break;
		}
	case InternalDamageObject::DestructionDamageMode:
		{
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->ReactToDestruction(damage_mode, damage_type);
				kill_self = false;
			}
			else
			{
				DestroyChildren(damage_mode);
			}
			break;
		}
	}
	Check_Object(Mission::GetInstance());
	ExecuteChildComponentWebs();

	if(kill_self)
		Entity::ReactToDestruction(damage_mode, damage_type);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::CreatePartDestructionEffect(const ResourceID& resource_id)
{
	Check_Object(this);
	
	if(resource_id != ResourceID::Null)
	{
		ClassID class_ID;
		class_ID = Entity::GetClassIDFromDataListID(resource_id);
		Verify(class_ID != NullClassID);

		Effect::CreateMessage effect_create_message(
			sizeof(Effect::CreateMessage),
			DefaultEventPriority,
			Entity::CreateMessage::DefaultFlags,
			class_ID,
			Effect::DefaultFlags,
			resource_id,
			GetLocalToWorld(),
			0.0f,
			Entity::ExecutionStateEngine::AlwaysExecuteState,
			NameTable::NullObjectID,
			Entity::DefaultAlignment
		);
		MemoryStream stream(&effect_create_message, effect_create_message.messageLength);

		Entity *entity;
		ReplicatorID effect_id = Connection::Hermit->GetNextReplicatorID();
		entity = Entity::CreateEntity(&stream, &effect_id, false);
		Check_Object(entity);
//		Effect *effect = Cast_Object(Effect *, entity);
		Check_Object(Map::GetInstance());
		Map::GetInstance()->AddChild(entity);
//		effect->SetFollowEntity(this);
//		effect->effectOffset = LinearMatrix4D::Identity;
		entity->SyncMatrices(true);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::ReactToInternalDamage(int damage_mode)
{
	Check_Object(this);

	if ((GetParentVehicle() != 0) &&
		(GetParentVehicle()->GetAI() != 0))
	{
		GetParentVehicle()->GetAI()->NotifyInternalHit();

	}

	switch(damage_mode)
	{
	case InternalDamageObject::GimpLeftDamageMode:
		{
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->ReactToInternalDamage(damage_mode);
			}
			break;
		}
	case InternalDamageObject::GimpRightDamageMode:
		{
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->ReactToInternalDamage(damage_mode);
			}
			break;
		}
	case InternalDamageObject::HeadShotDamageMode:
		{
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->HeadShotInternal();
			}
			break;
		}
	case InternalDamageObject::GyroHitDamageMode:
		{
			if(myParentVehicle)
			{
				Check_Object(myParentVehicle);
				myParentVehicle->GyroHitInternal();
			}
			break;
		}
	}

	// Fix Bug #3100 
	// Damage Object are now refreshed when a mech sustains internal damage

	if ((GetParentVehicle() != 0) &&
		(GetParentVehicle()->IsDerivedFrom(Mech::DefaultData)))
	{
		GetParentVehicle()->ReactToInternalDamage(damage_mode);
	}

}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::NotifyProjectileApproaching(const Stuff::Point3D& origin)
{
	if ((GetParentVehicle() != 0) &&
		(GetParentVehicle()->GetAI() != 0))
	{
		GetParentVehicle()->GetAI()->NotifyProjectileApproaching(origin);
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::NotifyAIShot(AI& ai, Adept::ReplicatorID inflicting_entity_id)
{
	Connection	*connection = Network::GetInstance()->GetConnection(inflicting_entity_id.connectionID);

	if (connection == NULL)
		return;

	Replicator* r = connection->FindReplicator(inflicting_entity_id);
	if (r == 0)
	{
		return;
	}
	Verify(r->IsDerivedFrom(Entity::DefaultData));
	Entity* e = Cast_Object(Entity*,r);
	ai.NotifyShot(e->objectID);

	if ((GetParentVehicle() != 0) &&
		(GetParentVehicle()->GetAlignment() == e->GetAlignment()) &&
		(e->IsDerivedFrom(MWObject::DefaultData) == true))
	{
		MWObject* mwobject = Cast_Object(MWObject*,e);
		
		if (mwobject->GetAI() != 0)
		{
			mwobject->GetAI()->NotifyLastShotWasFriendlyFire(*GetParentVehicle());
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::ToggleLightsOff(bool toggle)  //True if going off
{
	Check_Object(this);
	ChainIteratorOf<Entity*> children(&childEntityChain);
	Entity *entity;

	Check_Pointer(Mission::GetInstance());
	//
	// If running lights aren't allowed, don't allow turning them on
	//
	if(!Mission::GetInstance()->DoesAllowRunningLights())
	{
		return;
	}

	while ((entity = children.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		if(entity->IsDerivedFrom(MWMover::DefaultData))
		{
			MWMover *mover = Cast_Object(MWMover *, entity);
			mover->ToggleLightsOff(toggle);
		}
	}
	m_isDark = toggle;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::SetDamageObject(DamageObject *damage_object)
{
	Check_Object(this);
	Check_Object(damage_object);
	
	Entity::SetDamageObject(damage_object);
	ConnectSitesToZone(damage_object->internalDamageObject);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::ConnectSitesToZone(InternalDamageObject *zone)
{
	Check_Object(this);
	Check_Object(zone);

	SortedChainIteratorOf<Site *, MString> iterator(&m_sites);
	Site *site;

	while((site = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(site);
		if(!zone->m_sites.Find(site->GetName()))
			zone->m_sites.AddValue(site, site->GetName());
	}
}

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

	Verify(executionState->GetState() != ExecutionStateEngine::NeverExecuteState);

	DetermineUV (GetTimeSlice (till));

	BaseClass::PreCollisionExecute(till);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MWMover::DetermineUV (Stuff::Time slice)
{
	Check_Object (this);
	
	Vehicle *vehicle = NULL;
	if (IsDerivedFrom(Vehicle::DefaultData))
		vehicle = Cast_Object(Vehicle *, this);
	else if (myParentVehicle && myParentVehicle->IsDerivedFrom(Vehicle::DefaultData))
		vehicle = Cast_Object(Vehicle *, myParentVehicle);
		
	if (!vehicle)
		return;

	Check_Object(vehicle);
		
	Stuff::Vector3D linearVelocity = vehicle->localSpaceVelocity.linearMotion;
	Stuff::Scalar forward = linearVelocity.GetForwardComponent();
	
	if (Stuff::Small_Enough(forward))
		return;
	
	const Vehicle__GameModel *model = vehicle->GetGameModel();
	Check_Object(model);
	Stuff::Scalar treadLength = model->m_treadLength;
	
	if (treadLength<=0)
		return;
	
	Verify(treadLength);
	forward = forward*(Stuff::Scalar)(slice)/treadLength;
	
	// Treads move in the "backward" direction
	// as the vehicles move in the "forward" direction
	LTread(3,0) -= forward;
	RTread(3,0) -= forward;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMover::ReactToDamageTaken(Stuff::Scalar damage_taken, const Adept::ReplicatorID& inflicting_entity, int weaponID, int damageType)
{
	if (myParentVehicle != 0)
	{
		myParentVehicle->ReactToDamageTaken(damage_taken,inflicting_entity, weaponID, damageType);
	}
}
