//===========================================================================//
// File: Entity.cpp                                                          //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 11/29/94  JMA  Initial coding.                                            //
// 08/25/97  JMA  Infrastructure changes												  //
// 08/25/97  ECH	Infrastructure changes												  //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-97, Virtual World Entertainment, Inc.                  //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"

#include "EntityManager.hpp"
#include "EntityAttribute.hpp"
#include "RendererManager.hpp"
#include "VideoComponentWeb.hpp"
#include "EntityClassData.hpp"
#include "Map.hpp"
#include "Application.hpp"
#include <ElementRenderer\GroupElement.hpp>
#include <ElementRenderer\StateChange.hpp>
#include "DamageObject.hpp"
#include "GameModelAttribute.hpp"
#include "NameTable.hpp"
#include "CollisionVolume.hpp"
#include "CollisionGrid.hpp"
#include "Zone.hpp"
#include "Tile.hpp"

#include <MLR\MLRTexturePool.hpp>
#include <MLR\MLRTexture.hpp>

//#define HUNT_BUG "jmalbert"
//#define TIME_BUG "your_loginname_here"
extern int g_nMR;

char g_LastEntity[255] = "";

//#############################################################################
//#############################    Entity    ##################################
//#############################################################################

HGOSHEAP
	Entity::s_Heap = NULL,
	Entity::s_CollisionHeap = NULL;

Entity::ClassData*
	Entity::DefaultData = NULL;

const Receiver::MessageEntry
	Entity::MessageEntries[]=
{
	MESSAGE_ENTRY(Entity, Destroy),
	MESSAGE_ENTRY(Entity, Update),
	MESSAGE_ENTRY(Entity, TakeDamage),
	MESSAGE_ENTRY(Entity, BecomeInteresting)
};

int Entity::s_EntitiesSynced;
DWORD Entity_Count;
DECLARE_TIMER(static, Renderer_Loading);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::InitializeClass()
{
	Verify(!s_Heap);
	s_Heap = gos_CreateMemoryHeap("Entities", 0, g_LibraryHeap);
	Check_Pointer(s_Heap);

	Verify(!s_CollisionHeap);
	s_CollisionHeap = gos_CreateMemoryHeap("Collision", 0, g_LibraryHeap);
	Check_Pointer(s_CollisionHeap);

	Check_Object(ExecutionStateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			EntityClassID,
			"Adept::Entity",
			BaseClass::DefaultData,
			ELEMENTS(MessageEntries), MessageEntries,
			Entity::Make,
			Entity::CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			Entity::GameModel::ConstructGameModel,
			(Entity::GameModel::Factory)GameModel::ConstructOBBStream,
			Entity::GameModel::ReadAndVerify,
			Entity::GameModel::WriteToText,
			Entity::GameModel::SaveGameModel
		);
	Check_Object(DefaultData);

	INDIRECT_STATE_ATTRIBUTE(
		Entity,
		ExecutionState,
		executionState,
		Entity__ExecutionStateEngine
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		Entity,
		IsDestroyed,
		isDestroyed,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_ATTRIBUTE(
		Entity,
		VisualRepresentation,
		visualRepresentation,
		int,
		IntClassID
	);

	DIRECT_ATTRIBUTE(
		Entity,
		LightIntensity,
		lightIntensity,
		Scalar
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Entity__GameModel,
		Collider,
		collider,
		bool,
		BoolClassID
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Entity__GameModel,
		CanBeWalkedOn,
		canBeWalkedOn,
		bool,
		BoolClassID
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Entity__GameModel,
		CanBeShot,
		canBeShot,
		bool,
		BoolClassID
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Entity__GameModel,
		WaterSurface,
		waterSurface,
		bool,
		BoolClassID
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Entity__GameModel,
		OBBCollides,
		OBBCollides,
		bool,
		BoolClassID
	);

	{
		AttributeEntry *attribute_entry =
			new LocalToWorldAttributeEntry(
				LocalToWorldAttributeID,
				"LocalToWorld"
			);
		Check_Object(attribute_entry);
		Check_Object(DefaultData);
		DefaultData->attributeTable.AddAttributeEntry(attribute_entry);
	}

#if !defined(NO_STATS)
	AddStatistic("Entities", "entities", gos_DWORD, &Entity_Count, 0);
	AddStatistic("Entities Sync'd", "entities", gos_DWORD, &s_EntitiesSynced, Stat_AutoReset);
#endif

	Initialize_Timer(Renderer_Loading, "Renderer Loading");
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Check_Pointer(s_CollisionHeap);
	gos_DestroyMemoryHeap(s_CollisionHeap);
	s_CollisionHeap = NULL;

	Check_Pointer(s_Heap);
	gos_DestroyMemoryHeap(s_Heap);
	s_Heap = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::ClassData*
	Entity::GetClassData()
{
	return Cast_Pointer(ClassData*, classData);
}


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

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

	//
	//-----------------------------------
	// Make sure that the stuff is synced
	//-----------------------------------
	//
#if 0
	SyncMatrices(true);
#endif
	//
	//---------------------------------------------------------------------
	// Make sure there is enough room for the factory request on the stream
	//---------------------------------------------------------------------
	//
	stream->AllocateBytes(sizeof(CreateMessage));
	BaseClass::SaveMakeMessage(stream, res_file);
	CreateMessage *message =
		Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	//
	//----------------------
	// Save the data list ID
	//----------------------
	//
	Verify(dataListResource.DoesResourceExist());
	message->dataListID = dataListResource.GetResourceID();

	//
	//-------------------------------------
	// Save the initial state of the entity
	//-------------------------------------
	//
	message->localToParent = initialLocalToParent;
	message->initialAge =
		static_cast<Scalar>(lastExecuted - lastParameterization);
	Check_Object(executionState);
	executionState->Save(&message->executionState);

	//
	//--------------------------------
	// Save the Save the instance Name
	//--------------------------------
	//
	if(NameTable::GetInstance())
	{
		Check_Object(NameTable::GetInstance());
		message->nameID = NameTable::GetInstance()->FindID(instanceName);
	}
	else
	{
		message->nameID = NameTable::NullObjectID;
	}

	//
	//----------------------
	//Save out the Alignment
	//----------------------
	//
	message->alignment = alignment;

	return message;
}

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

	//
	//-----------------------------------------
	// Verify that an entity can be created now
	//-----------------------------------------
	//
	BaseClass::Reuse(message, base_id);

#ifdef _ARMOR
	Application *app = Application::GetInstance();
	Check_Object(app);
	Verify(!app->InPreCollisionPhase());
#endif

	#if defined(HUNT_BUG)
		SPEW((HUNT_BUG, "Reusing %s(+", GetClassString()));
		Spew(HUNT_BUG, GetReplicatorID());
		if (owningConnection==Connection::Local)
			SPEW((HUNT_BUG, "*+"));
		switch (GetReplicatorMode())
		{
		case MasterMode:
			SPEW((HUNT_BUG, ") as master"));
			break;
		case IndependentMode:
			SPEW((HUNT_BUG, ") as independent"));
			break;
		case HermitMode:
			SPEW((HUNT_BUG, ") as hermit"));
			break;
		case ReplicantMode:
			SPEW((HUNT_BUG, ") as replicant"));
			break;
		case ClientMasterMode:
			SPEW((HUNT_BUG, ") as ClientMasterMode"));
			break;
		case ServerMasterMode:
			SPEW((HUNT_BUG, ") as ServerMasterMode"));
			break;
		}
	#endif

	//
	//----------------------
	// Set up the data list and
	//----------------------
	//
	lastExecuted = gos_GetElapsedTime();
	lastParameterization = lastExecuted - message->initialAge;
	SetInterestMask(DormantInterestLevel);
	updateFlags = 0;
	#if defined(TIME_BUG)
		SPEW((TIME_BUG, "Reusing %s(+", GetClassString()));
		Spew(TIME_BUG, GetReplicatorID());
		SPEW((TIME_BUG, ") at time %f", lastExecuted.GetSeconds()));
	#endif

	lightIntensity = 0.0f;

	//
	//----------------------------------------------------------------------
	// Set up the game model and set the flags that come from the game model
	//----------------------------------------------------------------------
	//
	Verify(dataListResource.DoesResourceExist());
	Verify(gameModelResource.DoesResourceExist());
	const GameModel *model = GetGameModel();
	Check_Object(model);
	replicatorFlags &= ~GameModelMask;
	replicatorFlags |= model->defaultFlags;
	if (IsACollider() && IsTileBound())
		replicatorFlags &= ~ColliderFlag;

	//
	//--------------------------
	// Create the locator object
	//--------------------------
	//
	Check_Object(VideoRenderer::Instance);
	Resource element_stream(model->elementResourceID);
	Verify(element_stream.DoesResourceExist());

	//
	//-------------------------
	// Read in the entity state
	//-------------------------
	//
	gos_PushCurrentHeap(ElementRenderer::g_Heap);
	Check_Object(entityElement);
#ifdef _DEBUG
	{static void* s_p = (void*)0x08f40500;
	if (s_p == (void*)this) {
		int n = 0;
		n++;
		n--;
	}}
#endif // _DEBUG
	entityElement->SetClientData(this);
	unsigned state;
	element_stream >> state;
	entityElement->SetElementState(state);
	element_stream >> entityElement->m_localOBB;
	Verify(!solidVolume);
	Verify(!hierarchicalVolume);

	//
	//-------------------------------------------------------------------
	// Read a state change from stream, but if it has no changes, kill it
	//-------------------------------------------------------------------
	//
	ElementRenderer::StateChange *states =
		new ElementRenderer::StateChange(
			&element_stream,
			ElementRenderer::CurrentERFVersion
		);
	Check_Object(states);
	if (states->IsRealStateChange())
		entityElement->AdoptStateChange(states);
	else
	{
		Check_Object(states);
		delete states;
	}
	gos_PopCurrentHeap();

	//
	//-------------------------------
	// Set the position of the entity
	//-------------------------------
	//
	initialLocalToParent = message->localToParent;
	entityElement->SetLocalToParent(message->localToParent);
	NeedMatrixSync();

	//
	//------------------------------
	// Load up the execution engines
	//------------------------------
	//
	Check_Object(executionState);
	executionState->Reuse(&message->executionState);

	//
	//------------------------------------------------
	//Load the instance Name if it exists in the Table
	//------------------------------------------------
	//
	objectID = message->nameID;
	dontHit = NULL;
	if (oldCollisions)
	{
		delete oldCollisions;
		oldCollisions = NULL;
	}
	Verify(!newCollisions);

	alignment = message->alignment;
	isDestroyed = 0;
//	if (visualRepresentation == PrestineBothState || visualRepresentation == DestroyedBothState )
//		visualRepresentation=PrestineBothState;
// else
		visualRepresentation=PrestineInternalState;
	ClearDestroyedFlag();
	ClearTriedToMoveOffMapThisFrameFlag();


	RendererComponentWeb *renderer_web = GetComponentWeb(RendererManager::VideoRendererType);
	if(renderer_web)
	{
		VideoComponentWeb *video_web = Cast_Object(VideoComponentWeb *, renderer_web);
		video_web->CleanDamage();
	}
	ExecuteChildComponentWebs();

}

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

	BaseClass::Respawn(message);
	//
	//-----------------------------------------
	// Verify that an entity can be created now
	//-----------------------------------------
	//
#ifdef _ARMOR
	Application *app = Application::GetInstance();
	Check_Object(app);
	Verify(!app->InPreCollisionPhase());
#endif

	#if defined(HUNT_BUG)
		SPEW((HUNT_BUG, "Reusing %s(+", GetClassString()));
		Spew(HUNT_BUG, GetReplicatorID());
		if (owningConnection==Connection::Local)
			SPEW((HUNT_BUG, "*+"));
		switch (GetReplicatorMode())
		{
		case MasterMode:
			SPEW((HUNT_BUG, ") as master"));
			break;
		case IndependentMode:
			SPEW((HUNT_BUG, ") as independent"));
			break;
		case HermitMode:
			SPEW((HUNT_BUG, ") as hermit"));
			break;
		case ReplicantMode:
			SPEW((HUNT_BUG, ") as replicant"));
			break;
		case ClientMasterMode:
			SPEW((HUNT_BUG, ") as ClientMasterMode"));
			break;
		case ServerMasterMode:
			SPEW((HUNT_BUG, ") as ServerMasterMode"));
			break;
		}
	#endif

	//
	//----------------------
	// Set up the data list and
	//----------------------
	//
	lastExecuted = gos_GetElapsedTime();
	lastParameterization = lastExecuted - message->initialAge;
	updateFlags = 0;
	#if defined(TIME_BUG)
		SPEW((TIME_BUG, "Reusing %s(+", GetClassString()));
		Spew(TIME_BUG, GetReplicatorID());
		SPEW((TIME_BUG, ") at time %f", lastExecuted.GetSeconds()));
	#endif

	lightIntensity = 0.0f;

	//
	//----------------------------------------------------------------------
	// Set up the game model and set the flags that come from the game model
	//----------------------------------------------------------------------
	//
	Verify(dataListResource.DoesResourceExist());
	Verify(gameModelResource.DoesResourceExist());
	
	replicatorFlags &= InterestLevelMask|GameModelMask;
	replicatorFlags |= message->replicatorFlags & ~(InterestLevelMask|GameModelMask);
	if (IsACollider() && IsTileBound())
		replicatorFlags &= ~ColliderFlag;


	// here we go...
	// if we are the server than we own everything..
	if (owningConnection != Connection::Hermit)
	{
		Verify(GetReplicatorMode() != HermitMode);
		
		SetReplicatorMode(MasterMode);


		if (Network::GetInstance()->AmIServer())
		{
			
			// if we don't own it and it is a master it will be the ServerMasterMode ( the receiver of a ClientMaster )
			if ((owningConnection != Connection::Local) && (owningConnection != Connection::Hermit))
			{
				if (GetReplicatorMode() == MasterMode)
					SetReplicatorMode(ServerMasterMode);
			}
			
		}
		else
		{
			// we are a client so we don't own anything.
			
			if ((owningConnection == Connection::Local) && (GetReplicatorMode() == MasterMode))
			{
				SetReplicatorMode(ClientMasterMode);
			}
			else if ((owningConnection != Connection::Local) && GetReplicatorMode() == MasterMode)
			{
				SetReplicatorMode(ReplicantMode);
			}
		}
	}

	if (damageBit != -1)
	{
		EntityManager::GetInstance()->SetNetworkDamageBit(false, damageBit);
	}

	//
	//-------------------------------
	// Set the position of the entity
	//-------------------------------
	//
	initialLocalToParent = message->localToParent;
	entityElement->SetLocalToParent(message->localToParent);
	NeedMatrixSync();

	//
	//------------------------------
	// Load up the execution engines
	//------------------------------
	//
	if(executionState)
		executionState->Reuse(&message->executionState);

	//
	//------------------------------------------------
	//Load the instance Name if it exists in the Table
	//------------------------------------------------
	//

	alignment = message->alignment;
	isDestroyed = 0;
	visualRepresentation=PrestineState;
	ClearDestroyedFlag();
	ClearTriedToMoveOffMapThisFrameFlag();
	if (oldCollisions)
	{
		delete oldCollisions;
		oldCollisions = NULL;
	}
	Verify(!newCollisions);

	m_whoShotMeLast = ReplicatorID::Null;
	m_weaponShotMeLast = -1;

	RendererComponentWeb *renderer_web = GetComponentWeb(RendererManager::VideoRendererType);
	if(renderer_web)
	{
		VideoComponentWeb *video_web = Cast_Object(VideoComponentWeb *, renderer_web);
		video_web->CleanDamage();
	}

	ExecuteChildComponentWebs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SentenceToDeathRow()
{
	Check_Object(this);

	Check_Object(EntityManager::GetInstance());
	if (!EntityManager::GetInstance()->deathRow.IsPlugMember(this))
		EntityManager::GetInstance()->deathRow.Add(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity *
	Entity::CreateEntity(
		MemoryStream *stream,
		ReplicatorID *base_id,
		bool use_armory
	)
{
	Check_Object(stream);

	Entity::CreateMessage *message =
		Cast_Pointer(Entity::CreateMessage*, stream->GetPointer());
	Entity *entity;

#if 0
	if(use_armory)
	{
		Check_Object(EntityManager::GetInstance());
		if ((entity = EntityManager::GetInstance()->RequestFromArmory(message->dataListID)) != NULL)
		{
			Check_Object(entity);
			entity->Reuse(message);
			entity->PostReuse();
			return entity;
		}
	}
#endif
	RegisteredClass::ClassID class_id = message->classID;
	Entity::ClassData *class_data =
		Cast_Pointer(Entity::ClassData*, RegisteredClass::FindClassData(class_id));
	Check_Object(class_data);
	entity =
		Cast_Object(
			Entity*,
			(*class_data->replicatorFactory)(message, base_id)
		);
	Check_Object(entity);
#if 0
	Check_Object(EntityManager::GetInstance());
	EntityManager::GetInstance()->EntityCreation(entity);
#endif
	
	#if defined(LAB_ONLY)
		class_data->creationCount++;
	#endif

	return entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::Entity(
	ClassData *class_data,
	const CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Replicator(class_data, message, base_id),
	parentEntity(NULL),
	childEntityChain(NULL),
	m_deathEntity(NULL)
{
	Check_Pointer(this);
	Check_Object(message);

	minTileRow=maxTileRow=0xff;
	minTileColumn=maxTileColumn=0xff;
	collisionAllowed=true;

	executionState = NULL;
	//
	//-----------------------------------------
	// Verify that an entity can be created now
	//-----------------------------------------
	//
#ifdef _ARMOR
	Application *app = Application::GetInstance();
	Check_Object(app);
	Verify(!app->InPreCollisionPhase());
#endif
	++Entity_Count;

	#if defined(HUNT_BUG)
		SPEW((HUNT_BUG, "Creating %s(+", GetClassString()));
		Spew(HUNT_BUG, GetReplicatorID());
		if (owningConnection==Connection::Local)
			SPEW((HUNT_BUG, "*+"));
		switch (GetReplicatorMode())
		{
		case MasterMode:
			SPEW((HUNT_BUG, ") as master"));
			break;
		case IndependentMode:
			SPEW((HUNT_BUG, ") as independent"));
			break;
		case HermitMode:
			SPEW((HUNT_BUG, ") as hermit"));
			break;
		case ReplicantMode:
			SPEW((HUNT_BUG, ") as replicant"));
			break;
		case ClientMasterMode:
			SPEW((HUNT_BUG, ") as ClientMasterMode"));
			break;
		case ServerMasterMode:
			SPEW((HUNT_BUG, ") as ServerMasterMode"));
			break;
		}
	#endif

	//
	//----------------------
	// Set up the data list and
	//----------------------
	//
	lastExecuted = gos_GetElapsedTime();
	lastParameterization = lastExecuted - message->initialAge;
	updateFlags = 0;
	#if defined(TIME_BUG)
		SPEW((TIME_BUG, "Creating %s(+", GetClassString()));
		Spew(TIME_BUG, GetReplicatorID());
		SPEW((TIME_BUG, ") at time %f", lastExecuted.GetSeconds()));
	#endif


	isPlayerVehicle = false;

	lightIntensity = 0.0f;

	//
	//----------------------------------------------------------------------
	// Set up the game model and set the flags that come from the game model
	//----------------------------------------------------------------------
	//
	dataListResource.FindID(message->dataListID);
	Verify(dataListResource.DoesResourceExist());
	ResourceID *data_list = Cast_Pointer(ResourceID*, dataListResource.GetPointer());
	gameModelResource.FindID(*data_list);
	Verify(gameModelResource.DoesResourceExist());
	const GameModel *model = GetGameModel();
	Check_Object(model);
	replicatorFlags &= ~GameModelMask;
	replicatorFlags |= model->defaultFlags;
	if (IsACollider() && IsTileBound())
		replicatorFlags &= ~ColliderFlag;
	SetInterestMask(DormantInterestLevel);

	//
	//------------------------------------------------
	//Load the instance Name if it exists in the Table
	//------------------------------------------------
	//
	if(NameTable::GetInstance())
	{
		Check_Object(NameTable::GetInstance());
		instanceName = NameTable::GetInstance()->FindName(message->nameID);
		NameTable::GetInstance()->SetData(message->nameID, this);
//		entityElement->SetName(instanceName);
#if defined(LAB_ONLY)
		if (!instanceName)
			g_LastEntity[0] = '\0';
		else
		{
			strncpy(g_LastEntity, instanceName, sizeof(g_LastEntity)-1);
			g_LastEntity[sizeof(g_LastEntity)-1] = '\0';
		}
#endif
	}
	else
	{
		instanceName = NULL;
//		entityElement->SetName(dataListResource.GetName());
#if defined(LAB_ONLY)
		g_LastEntity[0] = '\0';
#endif
	}

	//
	//--------------------------
	// Create the locator object
	//--------------------------
	//
	Check_Object(VideoRenderer::Instance);
	Resource element_stream(model->elementResourceID);
	Verify(element_stream.DoesResourceExist());
	gos_PushCurrentHeap(ElementRenderer::GroupElement::s_Heap);
	if (element)
	{
		Check_Object(element);
		entityElement = element;
	}
	else
	{
		entityElement = new ElementRenderer::GroupElement;
		Check_Object(entityElement);
	}
	Check_Object(entityElement);
#ifdef _DEBUG
	{static void* s_p = (void*)0x08f40500;
	if (s_p == (void*)this) {
		int n = 0;
		n++;
		n--;
	}}
#endif // _DEBUG
	entityElement->SetClientData(this);
	unsigned state;
	element_stream >> state;
	entityElement->SetElementState(state);
	element_stream >> entityElement->m_localOBB;
	solidVolume = NULL;
	hierarchicalVolume = NULL;

	//
	//-------------------------------------------------------------------
	// Read a state change from stream, but if it has no changes, kill it
	//-------------------------------------------------------------------
	//
	ElementRenderer::StateChange *states =
		new ElementRenderer::StateChange(
			&element_stream,
			ElementRenderer::CurrentERFVersion
		);
	Check_Object(states);
	if (states->IsRealStateChange())
		entityElement->AdoptStateChange(states);
	else
	{
		Check_Object(states);
		delete states;
	}
	gos_PopCurrentHeap();

	//
	//-------------------------------
	// Set the position of the entity
	//-------------------------------
	//
	initialLocalToParent = message->localToParent;
	entityElement->SetLocalToParent(message->localToParent);
	NeedMatrixSync();

	//
	//------------------------------
	// Load up the execution engines
	//------------------------------
	//
	ExecutionStateEngine::ClassData *execution_class =
		class_data->executionStateClass;
	Check_Pointer(execution_class);
	executionState = execution_class->Make(this, &message->executionState);
	Check_Object(executionState);
	objectID = message->nameID;
	dontHit = NULL;

	damageBit = -1;
	damageObject = NULL;

	alignment = message->alignment;
	isDestroyed = 0;
	visualRepresentation=PrestineState;
	m_doesNeedToUpdateWebs = false;
	if(IsDestroyed())
	{
		isDestroyed = 1;
		SetDestroyedRepresentation();
	}
	ClearTriedToMoveOffMapThisFrameFlag();

	oldCollisions = NULL;
	newCollisions = NULL;

	m_whoShotMeLast = ReplicatorID::Null;
	m_weaponShotMeLast = -1;
	for (unsigned i=0; i<RendererManager::RendererTypeCount; ++i)
		componentWebs[i] = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::~Entity()
{
	DESTRUCTOR("Entity");

	Check_Object(this);

	if (oldCollisions)
	{
		DESTRUCTOR("Entity::OldCollisions");
		delete oldCollisions;
		oldCollisions = NULL;
	}
	Verify(!newCollisions);

	//
	//----------------
	// Delete children
	//----------------
	//
	--Entity_Count;
	{
		DESTRUCTOR("Entity::DeleteChildren");
		DeleteChildEntities();
	}

	//
	//-----------------------------
	// Delete the collision volumes
	//-----------------------------
	//
	{
		DESTRUCTOR("Entity::Volumes");
		if (solidVolume)
		{

			Check_Object(solidVolume);
			delete solidVolume;
			solidVolume = NULL;
		}
		if (hierarchicalVolume)
		{
			Check_Object(hierarchicalVolume);
			delete hierarchicalVolume;
			hierarchicalVolume = NULL;
		}
	}

	{
		DESTRUCTOR("Entity::Webs");
		//
		//------------------------------------
		// Delete any remaining component webs
		//------------------------------------
		//
		for (unsigned i=0; i<RendererManager::RendererTypeCount; ++i)
		{
			RendererComponentWeb *web = componentWebs[i];
			if (web)
			{
				Check_Object(web);
				if (web->ShouldWebBeKilled())
				{
					Check_Object(web);
					delete web;
					componentWebs[i] = NULL;
				}
			}
		}
	}

	{
		DESTRUCTOR("Entity::Element");

		//
		//-------------------
		// Delete the element
		//-------------------
		//
		Check_Object(entityElement);
		delete entityElement;
	}

	if (executionState)
	{

		
		DESTRUCTOR("Entity::ExecutionState");
		//
		//----------------------------
		// Remove the execution states
		//----------------------------
		//
		Check_Object(executionState);
		delete executionState;	
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::DestroyMessageHandler(const Message *message)
{
	Check_Object(this);
	Check_Object(message);
	Verify(message->messageID == DestroyMessageID);

	//
	//-----------------
	// Destroy children
	//-----------------
	//
	STOP(("Not implemented"));
#if 0
	DeleteChildEntities();
#endif

	//
	//---------------------------
	// Entered into deathrow once
	//---------------------------
	//
	Check_Object(EntityManager::GetInstance());
	if (!EntityManager::GetInstance()->deathRow.IsPlugMember(this))
	{
		EntityManager::GetInstance()->deathRow.Add(this);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::UpdateMessageHandler(const UpdateMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	Verify(GetReplicatorMode() == ReplicantMode);

	SetNewLocalToParent(message->localToParent);
	SyncMatrices(true);
}

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

bool
	Entity::FriendlyFire(const TakeDamageMessage *message)
{
	return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::TakeDamageMessageHandler(const TakeDamageMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	Verify(message->messageID == TakeDamageMessageID);

	if(damageObject)
	{
		if (!FriendlyFire(message))
		{
			Check_Pointer(damageObject);
			damageObject->TakeDamage((TakeDamageMessage *)message, this);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::BecomeInterestingMessageHandler(const BecomeInterestingMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	Verify(message->messageID == BecomeInterestingMessageID);
	Verify(message->m_render);

	//
	//--------------------------------------------------------------------------
	// Find the zone the entity lives in.  If the zone is no longer interesting,
	// ignore the message
	//--------------------------------------------------------------------------
	//
	ElementRenderer::Element *element = GetElement();
	Check_Object(element);
	element = element->GetParentElement();
	Check_Object(element);
	if (element->GetClassID() == TileClassID)
	{
		element = element->GetParentElement();
		Check_Object(element);
		element = element->GetParentElement();
		Check_Object(element);
	}
	Zone *zone = Cast_Object(Zone*, element);

	//
	//------------------
	// Is this the hole?
	//------------------
	//
	Entity::InterestLevel
		zone_interest = zone->GetInterestLevel(),
		entity_interest = GetInterestLevel();

	switch ((zone_interest<<2) | entity_interest)
	{
	case (Entity::DormantInterestLevel<<2)|Entity::DormantInterestLevel:
		STOP(("Dormant:Dormant is unhandled"));
		break;

	case (Entity::DormantInterestLevel<<2)|Entity::SimulationInterestLevel:
		STOP(("Dormant:Simulation is unhandled"));
		break;

	case (Entity::DormantInterestLevel<<2)|Entity::RenderingInterestLevel:
		STOP(("Dormant:Rendering is unhandled"));
		break;

	case (Entity::SimulationInterestLevel<<2)|Entity::DormantInterestLevel:
		STOP(("Simulation:Dormant is unhandled"));
		break;

	case (Entity::RenderingInterestLevel<<2)|Entity::DormantInterestLevel:
		Verify(zone->m_dormant.IsPlugMember(this));
		Verify(!zone->m_simulated.IsPlugMember(this));
		Verify(!zone->m_rendered.IsPlugMember(this));
		zone->m_dormant.Remove(this);
		zone->m_rendered.Add(this);
		BecomeInteresting(true);
		break;

	case (Entity::SimulationInterestLevel<<2)|Entity::SimulationInterestLevel:
	case (Entity::SimulationInterestLevel<<2)|Entity::RenderingInterestLevel:
		Verify(!zone->m_dormant.IsPlugMember(this));
		Verify(zone->m_simulated.IsPlugMember(this));
		Verify(!zone->m_rendered.IsPlugMember(this));
		break;

	case (Entity::RenderingInterestLevel<<2)|Entity::SimulationInterestLevel:
		Verify(!zone->m_dormant.IsPlugMember(this));
		Verify(zone->m_simulated.IsPlugMember(this));
		Verify(!zone->m_rendered.IsPlugMember(this));
		zone->m_simulated.Remove(this);
		zone->m_rendered.Add(this);
		BecomeInteresting(true);
		break;

	case (Entity::RenderingInterestLevel<<2)|Entity::RenderingInterestLevel:
		Verify(!zone->m_dormant.IsPlugMember(this));
		if (zone->m_simulated.IsPlugMember(this))
		{
			Verify(!zone->m_rendered.IsPlugMember(this));
			zone->m_simulated.Remove(this);
			zone->m_rendered.Add(this);
		}
		else
		{
			Verify(!zone->m_simulated.IsPlugMember(this));
			Verify(zone->m_rendered.IsPlugMember(this));
		}
		break;

	default:
		STOP(("Illegal interest level setting"));
		break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::RequestUpdate(int update_flags)
{
	Check_Object(this);
	Verify(update_flags != 0);
	Verify(GetReplicatorMode() == MasterMode);
	Verify(owningConnection == Connection::Local);

	#if defined(HUNT_BUG)
		SPEW((HUNT_BUG, "Requesting Update for %s(+", GetClassString()));
		Spew(HUNT_BUG, GetReplicatorID());
		SPEW((HUNT_BUG, ")"));
	#endif

	if (!updateFlags)
	{
		updateFlags = update_flags;
		Check_Object(EntityManager::GetInstance());
		EntityManager::GetInstance()->RequestUpdate(this);
	}
	else
		updateFlags |= update_flags;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::UpdateMessage*
	Entity::ConstructUpdate(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	Verify(updateFlags);
	Verify(GetReplicatorMode() == MasterMode);
	Verify(owningConnection == Connection::Local);

	#if defined(HUNT_BUG)
		SPEW((HUNT_BUG, "Constructing Update for %s(+", GetClassString()));
		Spew(HUNT_BUG, GetReplicatorID());
		SPEW((HUNT_BUG, ")"));
	#endif

	//
	//------------------------------------------------------------
	// Make sure there is enough room for the update on the stream
	//------------------------------------------------------------
	//
	stream->AllocateBytes(sizeof(UpdateMessage));
	UpdateMessage *message =
		new(stream->GetPointer()) UpdateMessage(
			replicatorID,
			GetLocalToWorld()
		);
	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetInterestLevel(InterestLevel interest)
{
	Check_Object(this);

	//
	//---------------------------------------------
	// Tell the zone to update our interest setting
	//---------------------------------------------
	//
	ElementRenderer::Element *element = GetElement();
	Check_Object(element);
	Zone *zone = Cast_Object(Zone*, element->GetParentElement());
	zone->SetInterestLevel(this, interest);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::BindToZone()
{
	Check_Object(this);

	//
	//------------------------------------------
	// If we are already zone bound, just return
	//------------------------------------------
	//
	if (!IsTileBound())
		return;

	//
	//---------------------------------------------------------------------
	// We are tile bound now, so find the tile through our element's parent
	//---------------------------------------------------------------------
	//
	ElementRenderer::Element *element = GetElement();
	Check_Object(element);
	Tile *tile = Cast_Object(Tile*, element->GetParentElement());
	ElementRenderer::Element *mid = tile->GetParentElement();
	Check_Object(mid);
	Zone *zone = Cast_Object(Zone*, mid->GetParentElement());

	//
	//----------------------------------------------------------------
	// Unhook the collision, change the flag and hook it back up again
	//----------------------------------------------------------------
	//
	zone->DetachChild(element);
	replicatorFlags &= ~TileBoundFlag;
	zone->AttachChild(element);
	element->Sync();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::BindToTile()
{
	Check_Object(this);

	//
	//------------------------------------------
	// If we are already tile bound, just return
	//------------------------------------------
	//
	if (IsTileBound())
		return;

	//
	//---------------------------------------------------------------------
	// We are tile bound now, so find the tile through our element's parent
	//---------------------------------------------------------------------
	//
	ElementRenderer::Element *element = GetElement();
	Check_Object(element);
	Zone *zone = Cast_Object(Zone*, element->GetParentElement());

	//
	//----------------------------------------------------------------
	// Unhook the collision, change the flag and hook it back up again
	//----------------------------------------------------------------
	//
	zone->DetachChild(element);
	replicatorFlags |= TileBoundFlag;
	zone->AttachChild(element);
	element->Sync();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetCollisionMask(CollisionMask mask)
{
	Check_Object(this);
	Verify(GetParentEntity() == Map::GetInstance());

	//
	//----------------------------------
	// Find the zone we are connected to
	//----------------------------------
	//
	ElementRenderer::Element *element = GetElement();
	Check_Object(element);
	element = element->GetParentElement();
	Check_Object(element);
	if (element->GetClassID() == TileClassID)
	{
		element = element->GetParentElement();
		Check_Object(element);
		element = element->GetParentElement();
		Check_Object(element);
	}
	Zone *zone = Cast_Object(Zone*, element);

	//
	//-----------------------------------------------------------------------
	// Unhook from the current zone and reattach to it after setting our mask
	//-----------------------------------------------------------------------
	//
	zone->UnhookCollision(this);
	replicatorFlags &= ~AlwaysCollidesMask;
	replicatorFlags |= mask;
	zone->HookUpCollision(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetDestroyedFlag(int damage_mode)
{
	Check_Object(this);
	replicatorFlags |= DestroyedFlag;
// MSL 5.05 Torso
	if((damage_mode == InternalDamageObject::DestructionDamageMode) ||
		(damage_mode == InternalDamageObject::DetachableDamageMode) ||
		(damage_mode == InternalDamageObject::ArmLeftDamageMode) ||
		(damage_mode == InternalDamageObject::ArmRightDamageMode))
	{
		if (solidVolume)
		{
			Check_Object(solidVolume);
			if (!solidVolume->IsDestroyed())
				solidVolume->Destroy();
		}
		if (hierarchicalVolume)
		{
			Check_Object(hierarchicalVolume);
			if (!hierarchicalVolume->IsDestroyed())
				hierarchicalVolume->Destroy();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetTriedToMoveOffMapThisFrameFlag()
{
	Check_Object(this);
	replicatorFlags |= TriedToMoveOffMapThisFrameFlag;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetDestroyedRepresentation()
{
	Check_Object(this);

	if(visualRepresentation == PrestineState)
		visualRepresentation = DestroyedState;
	else if(visualRepresentation == PrestineInternalState)
		visualRepresentation = DestroyedInternalState;
	else if(visualRepresentation == PrestineBothState)
		visualRepresentation = DestroyedBothState;
	else if(visualRepresentation == PrestineNoneState)
		visualRepresentation = DestroyedNoneState;

	
	m_doesNeedToUpdateWebs = true;

	//THIS CAN'T BE CALLED HERE EVER!!!!!!!
	//ExecuteChildComponentWebs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetInternalRepresentation()
{
	Check_Object(this);

	if(visualRepresentation == PrestineState || visualRepresentation == PrestineBothState || visualRepresentation == PrestineNoneState)
		visualRepresentation = PrestineInternalState;
	else if(visualRepresentation == DestroyedState || visualRepresentation == DestroyedBothState || visualRepresentation == DestroyedNoneState)
		visualRepresentation = DestroyedInternalState;

	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;

	while((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->SetInternalRepresentation();
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetBothRepresentation()
{
	Check_Object(this);

	if(visualRepresentation == PrestineState || visualRepresentation == PrestineInternalState || visualRepresentation == PrestineNoneState)
		visualRepresentation = PrestineBothState;
	else if(visualRepresentation == DestroyedState || visualRepresentation == DestroyedInternalState || visualRepresentation == DestroyedNoneState)
		visualRepresentation = DestroyedBothState;

	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;

	while((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->SetBothRepresentation();
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetNoneRepresentation()
{
	Check_Object(this);

	if(visualRepresentation == PrestineState || visualRepresentation == PrestineInternalState || visualRepresentation == PrestineBothState)
		visualRepresentation = PrestineNoneState;
	else if(visualRepresentation == DestroyedState || visualRepresentation == DestroyedInternalState || visualRepresentation == DestroyedBothState)
		visualRepresentation = DestroyedNoneState;

	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;

	while((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->SetNoneRepresentation();
	}

}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetExternalRepresentation()
{
	Check_Object(this);

	if(visualRepresentation == PrestineInternalState || visualRepresentation == PrestineBothState || visualRepresentation == PrestineNoneState)
		visualRepresentation = PrestineState;
	else if(visualRepresentation == DestroyedInternalState || visualRepresentation == DestroyedBothState || visualRepresentation == DestroyedNoneState)
		visualRepresentation = DestroyedState;


	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;

	while((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->SetExternalRepresentation();
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ClearDestroyedFlag()
{
	Check_Object(this);
	replicatorFlags &= ~DestroyedFlag;
	if (solidVolume)
	{
		Check_Object(solidVolume);
		solidVolume->Restore();
	}
	if (hierarchicalVolume)
	{
		Check_Object(hierarchicalVolume);
		hierarchicalVolume->Restore();
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ClearTriedToMoveOffMapThisFrameFlag()
{
	Check_Object(this);
	replicatorFlags &= ~TriedToMoveOffMapThisFrameFlag;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetDamageObject(DamageObject *damage_object)
{
	Check_Object(this);
	Check_Object(damage_object);

	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;

	//
	//--------------------------------------------------------------------
	//Need to set the damage object and the damage object for its children
	//--------------------------------------------------------------------
	//
	while((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		if(entity->damageObject == NULL)
		{
			entity->SetDamageObject(damage_object);
		}
	}
	damageObject = damage_object;


}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::DestroyChildren(int damage_mode)
{
	Check_Object(this);
	
	DestroyDamageObjects();

	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;

	while((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->DestroyChildren(damage_mode);
	}
	
	Entity::ReactToDestruction(damage_mode, 0);
}


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

bool
	Entity::IsDestroyable()
{
	if (damageObject != NULL)
		return true;

	return false;
}

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

void
	Entity::ReactToDestruction(int damage_mode,int damage_type)
{
	Check_Object(this);
	isDestroyed = 1;
	SetDestroyedRepresentation();
	SetDestroyedFlag(damage_mode);

	if (damageBit != -1)
	{
		//SPEW(("jerryeds", "ENTITY DESTROYED - TILE : %d : %d", minTileRow, minTileColumn));
		EntityManager::GetInstance()->SetNetworkDamageBit(true, damageBit);
	}

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ApplyVisualDamage(const TakeDamageMessage *message, int armor_zone)
{
	Check_Object(this);
	Check_Object(message);

	if (message->quickDamage)
		return;

	//
	//-------------------------------------------------------------------
	//Apply a procedural Damage Effect to ourselves if we have not before
	//-------------------------------------------------------------------
	//
	if(!IsDestroyed())
	{
		Check_Object(VideoRenderer::Instance);
		LinearMatrix4D damage_matrix = LinearMatrix4D::Identity;
		Point3D damage_location;
		RendererComponentWeb *renderer_web = GetComponentWeb(RendererManager::VideoRendererType);
		if(renderer_web)
		{
			VideoComponentWeb *video_web = Cast_Object(VideoComponentWeb *, renderer_web);
			
			damage_location.MultiplyByInverse(message->hitOffset, GetLocalToWorld());
			damage_matrix.BuildTranslation(damage_location);
			Vector3D damage_rotation;
			damage_rotation.Negate(message->normal);
			damage_matrix.AlignLocalAxisToWorldVector(damage_rotation, Z_Axis, Y_Axis, X_Axis);

			Scalar radius = message->amountOfDamage * 0.1f;

			Clamp(radius, 0.5f, 1.0f);

			if(armor_zone == DamageObject::DefaultArmorZone)
			{
				if(!HasPlacedDamageDecal())
				{
					MidLevelRenderer::MLRTexture *texture =
						(*MidLevelRenderer::MLRTexturePool::Instance)((const char *)"scorch");
					if(texture)
					{
						Check_Object(texture);
						damage_matrix(3,0) -= damage_matrix(2,0);
						damage_matrix(3,1) -= damage_matrix(2,1);
						damage_matrix(3,2) -= damage_matrix(2,2);
						radius *= 5.0f;
						video_web->ApplyDamageDecal(damage_matrix, radius, texture);
						SetDamageDecalFlag();
					}
				}
			}
			else
				video_web->ApplyDamage(damage_matrix, radius);
		}
		else
		{

			
			Entity *entity_to_check = GetParentEntity();

			// traverse parents looking for a web
			while (entity_to_check!=NULL)
			{
				// stop at the map
				if (entity_to_check == Map::GetInstance())
				{
					break;
				}
				else
				{
					// stop at a parent with a web
					RendererComponentWeb *renderer_web = entity_to_check->GetComponentWeb(RendererManager::VideoRendererType);
					if (renderer_web != NULL)
					{
						entity_to_check->ApplyVisualDamage(message,armor_zone);
						break;
					}
				}
				entity_to_check = entity_to_check->GetParentEntity();
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AttributeEntry*
	Entity::GetAttributeEntry(AttributeID attribute_ID)
{
	ClassData *class_data = Cast_Pointer(ClassData*, classData);
	Check_Object(class_data);
	return class_data->attributeTable.GetAttributeEntry(attribute_ID);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AttributeEntry*
	Entity::GetAttributeEntry(const char *attribute_name)
{
	ClassData *class_data = Cast_Pointer(ClassData*, classData);
	Check_Object(class_data);
	return class_data->attributeTable.GetAttributeEntry(attribute_name);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::GetGameModelResourceFromDataListID(
		Resource *resource,
		const ResourceID &data_list
	)
{
	Resource stream(data_list);
	Verify(stream.GetSize() >= sizeof(ResourceID));
	ResourceID resource_id;
	stream >> resource_id;
	resource->FindID(resource_id);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RegisteredClass::ClassID
	Entity::GetClassIDFromDataListID(const ResourceID &model_list)
{
	Resource resource;
	GetGameModelResourceFromDataListID(&resource, model_list);
	GameModel *model = Cast_Pointer(GameModel*,resource.GetPointer());
	return model->classID;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::AddChild(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	//
	//-----------------------------
	// Hook up the entity hierarchy
	//-----------------------------
	//
	Verify(!entity->parentEntity);
	entity->parentEntity = this;
	childEntityChain.Add(entity);
	entity->NeedMatrixSync();
	ElementRenderer::Element *child_element = entity->GetElement();
	Check_Object(child_element);
	Check_Object(entityElement);
	entityElement->AttachChild(child_element);

	//
	//---------------------------------------------------
	// If are bounds are not locked, calculate them again
	//---------------------------------------------------
	//
	if (
		!entityElement->AreBoundsLocked()
		 && entityElement->GetCullMode() == ElementRenderer::Element::VolumeCullMode
	)
	{
		entityElement->NeedNewBounds();
		NeedMatrixSync();
	}

	#if defined(HUNT_BUG)
		SPEW((0, "Attaching %s(+", entity->GetClassString()));
		Spew(0, entity->GetReplicatorID());
		SPEW((0, ") to %s(+", GetClassString()));
		Spew(0, GetReplicatorID());
		SPEW((0, ")"));
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::RemoveChild(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	//
	//-----------------------------
	// Unhook the locator hierarchy
	//-----------------------------
	//
	entity->parentEntity = NULL;
	childEntityChain.Remove(entity);
	entity->NeedMatrixSync();
	ElementRenderer::Element *element = entity->GetElement();
	Check_Object(element);
	entityElement->DetachChild(element);

	#if defined(HUNT_BUG)
		SPEW((0, "Attaching %s(+", entity->GetClassString()));
		Spew(0, entity->GetReplicatorID());
		SPEW((0, ") to %s(+", GetClassString()));
		Spew(0, GetReplicatorID());
		SPEW((0, ")"));
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetNewLocalToParent(const Stuff::LinearMatrix4D &new_local_to_world)
{
	Check_Object(this);

	Check_Object(entityElement);
	Check_Object(parentEntity);
	ClearTriedToMoveOffMapThisFrameFlag();
	//
	//-----------------------------------------------------------
	// If our parent isn't the map, set the dirty flag and matrix
	//-----------------------------------------------------------
	//
	if (parentEntity != Map::GetInstance())
	{
		entityElement->SetLocalToParent(new_local_to_world);
		replicatorFlags |= DirtyMatrixFlag;
	}

	//
	//----------------------------------------------------------------------
	// If we are parented to the map, the zone is dirty, and then bodge in a
	// check to make sure nothing leaves the map
	//----------------------------------------------------------------------
	//
	else
	{
		Point3D translation(new_local_to_world);
		Check_Object(Map::GetInstance());
		if (Map::GetInstance()->FindZone(translation) == -1)
		{
			LinearMatrix4D adjusted_matrix = new_local_to_world;
			const LinearMatrix4D &old_matrix = GetLocalToWorld();
			adjusted_matrix(3,0) = old_matrix(3,0);
			adjusted_matrix(3,2) = old_matrix(3,2);
			entityElement->SetLocalToParent(adjusted_matrix);
			replicatorFlags |= DirtyMatrixFlag | TriedToMoveOffMapThisFrameFlag;
		}
		else
		{
			entityElement->SetLocalToParent(new_local_to_world);
			replicatorFlags |= DirtyMatrixFlag|DirtyZoneFlag|PostCollisionFlag;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ChildPreCollisionChanged(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	STOP(("This function should be overridden!"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::DeleteChildEntities()
{
	Check_Object(this);
	childEntityChain.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SetComponentWeb(
		unsigned renderer_id,
		RendererComponentWeb *web
	)
{
	Check_Object(this);
	if(componentWebs[renderer_id])
	{
		delete componentWebs[renderer_id];
	}
	componentWebs[renderer_id] = web;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ExecuteComponentWebs()
{
	Check_Object(this);
	POSTCOLLISION_LOGIC("Entity::Execute Webs");

	//
	//----------------------------
	// Execute each registered web
	//----------------------------
	//
	for (unsigned i=1; i<RendererManager::RendererTypeCount; ++i)
	{
		ComponentWeb *web = componentWebs[i];
		if (web)
		{
			Check_Object(web);
			web->ExecuteWatcherComponents();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::BecomeInteresting(bool render_me)
{
	Check_Object(this);

	//
	//--------------------------------------
	// Tell the renderers we are interesting
	//--------------------------------------
	//
	LOAD_LOGIC("Become Interesting::Entity");
	Start_Timer(Renderer_Loading);
	Check_Object(RendererManager::Instance);
	RendererManager::Instance->EntityIsInteresting(this, render_me);

	//
	//------------------------------------------------------------------------
	// The entity version will just load the solid volumes.  Go ahead and load
	// the stream resource that should be indicated in the game model
	//------------------------------------------------------------------------
	//
	if (!solidVolume && !hierarchicalVolume)
	{
		LOAD_LOGIC("Become Interesting::Entity::Load Collision Volumes");
		const GameModel *game_model = GetGameModel();
		Check_Object(game_model);

		#if defined(LAB_ONLY)
			if (!instanceName)
				g_LastEntity[0] = '\0';
			else
			{
				strncpy(g_LastEntity, instanceName, sizeof(g_LastEntity)-1);
				g_LastEntity[sizeof(g_LastEntity)-1] = '\0';
			}
		#endif

		//
		//-------------------------------
		// Load in the solid if it exists
		//-------------------------------
		//
		if (game_model->obbStreamResourceID != ResourceID::Null)
		{
			Resource obb_stream(game_model->obbStreamResourceID);
			Verify(obb_stream.DoesResourceExist());
			gos_PushCurrentHeap(s_CollisionHeap);
			solidVolume =
				new CollisionVolume(
					this,
					&obb_stream,
					CollisionVolume::ReadOBBVersion(&obb_stream)
				);
			Check_Object(solidVolume);
			gos_PopCurrentHeap();
		}
	}

	//
	//------------------------------------------------------
	// Mark our interest level according to the render level
	//------------------------------------------------------
	//
#if defined ARMOR
	if (render_me)
		Verify(!Application::GetInstance()->m_localMissionParameters.m_runDedicated);
#endif
	{
		LOAD_LOGIC("Become Interesting::Entity::Set Interest Mask");
		SetInterestMask((render_me) ? RenderingInterestLevel : SimulationInterestLevel);
		#if defined(_ARMOR)
			ElementRenderer::Element *element = GetElement();
			Check_Object(element);
			element = element->GetParentElement();
			if (element)
			{
				if (element->GetClassID() == TileClassID)
				{
					element = element->GetParentElement();
					Check_Object(element);
					element = element->GetParentElement();
					Check_Object(element);
				}
				if (element->GetClassID() == ZoneClassID)
				{
					Zone *zone = Cast_Object(Zone*, element);
					Verify(!zone->m_dormant.IsPlugMember(this));
					if (render_me && zone->GetInterestLevel() == RenderingInterestLevel)
					{
						Verify(!zone->m_simulated.IsPlugMember(this));
						Verify(zone->m_rendered.IsPlugMember(this));
					}
					else
					{
						Verify(zone->m_simulated.IsPlugMember(this));
						Verify(!zone->m_rendered.IsPlugMember(this));
					}
				}
			}
		#endif
	}
	Stop_Timer(Renderer_Loading);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::BecomeUninteresting()
{
	Check_Object(this);

	//
	//----------------------------
	// Execute each registered web
	//----------------------------
	//
	for (unsigned i=1; i<RendererManager::RendererTypeCount; ++i)
	{
		RendererComponentWeb *web = componentWebs[i];
		if (web)
		{
			Check_Object(web);
			if (web->ShouldWebBeKilled())
			{
				Check_Object(web);
				delete web;
				componentWebs[i] = NULL;
			}
		}
	}

	//
	//------------------------
	// Delete the solid volume
	//------------------------
	//
	if (solidVolume)
	{
		Check_Object(solidVolume);
		delete solidVolume;
		solidVolume = NULL;
	}
	if (hierarchicalVolume)
	{
		Check_Object(hierarchicalVolume);
		delete hierarchicalVolume;
		hierarchicalVolume = NULL;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const ResourceID&
	Entity::GetRendererDataResourceID(int renderer_type)
{
	Check_Object(this);
	ResourceID *data_list = Cast_Pointer(ResourceID*, dataListResource.GetPointer());
	Check_Pointer(data_list);
	Check_Object(RendererManager::Instance);
	Verify(
		0 <= renderer_type &&
		renderer_type < RendererManager::Instance->GetModelListSize()
	);
	return data_list[renderer_type];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Entity::GetExecutionSlot()
{
	Check_Object(this);
	return DefaultExecutionSlot;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::PreCollisionExecute(Time till)
{
	Check_Object(this);
	Verify(!EntityManager::GetInstance()->isExecutionSuspended);
	Verify(GetInterestLevel() != DormantInterestLevel);
	collisionAllowed=true;

	//
	//------------------------------------------------------------------------
	// Check to see if a post collision pass in necessary.  If so, attach this
	// entity to the entitymanager post collision chain
	//------------------------------------------------------------------------
	//
	if (IsUsingPostCollision())
	{
		Check_Object(EntityManager::GetInstance());
		EntityManager::GetInstance()->RequestPostCollisionExecution(this);
	}
	else
	{
		lastExecuted = till;
		#if defined(TIME_BUG)
			SPEW((TIME_BUG, "PreCollisionOnly execution of %s(+", GetClassString()));
			Spew(TIME_BUG, GetReplicatorID());
			SPEW((TIME_BUG, ") at time %f", lastExecuted.GetSeconds()));
		#endif
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Entity::CollisionHandler(
		Stuff::LinearMatrix4D *new_position,
		Stuff::DynamicArrayOf<CollisionData> *collisions
	)
{
	Check_Object(this);
	Check_Object(collisions);
	Verify(GetInterestLevel() != DormantInterestLevel);
	Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));

	newCollisions = collisions;
	SPEW((0, "%s should override CollisionHandler", (char*)GetClassString() ));
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SyncMatrices(bool update_matrix)
{
	Check_Object(this);
	SYNC_LOGIC("Entity");

	#if defined(HUNT_BUG)
		bool old_update=update_matrix;
		if (update_matrix)
		{
			SPEW((0, "Entity::SyncMatrices %s(+", GetClassString()));
			Spew(0, GetReplicatorID());
			SPEW((0, ")"));
		}
	#endif
	Set_Statistic(s_EntitiesSynced, s_EntitiesSynced+1);

	//
	//-----------------------------------------
	// If we have a dirty locator, sync with it
	//-----------------------------------------
	//
	if (IsMatrixDirty())
	{
		if (update_matrix)
		{
			Check_Object(entityElement);
			entityElement->Sync();
			update_matrix = false;
		}
		ClearNeedMatrixSync();
	}

	//
	//-----------------------------
	// Sync our children's locators
	//-----------------------------
	//
	if (!childEntityChain.IsEmpty())
	{
		ChainIteratorOf<Entity *> children(&childEntityChain);
		Entity *child;
		while ((child = children.ReadAndNext()) != NULL)
		{
			Check_Object(child);
			child->SyncMatrices(update_matrix);
		}
	}

	#if defined(HUNT_BUG)
		if (old_update)
			SPEW((0, ""));
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::SyncMatricesFirstLevelOnly(bool update_matrix)
{
	Check_Object(this);

	// DANGER DANGER DANGER!!!!!!!!!!!!!!!!!!!!!!!!
	// THIS IS A NETWORK HACK
	// USE IT AT OWN RISK

	#if defined(HUNT_BUG)
		bool old_update=update_matrix;
		if (update_matrix)
		{
			SPEW((0, "Entity::SyncMatrices %s(+", GetClassString()));
			Spew(0, GetReplicatorID());
			SPEW((0, ")"));
		}
	#endif

	//
	//-----------------------------------------
	// If we have a dirty locator, sync with it
	//-----------------------------------------
	//
	if (IsMatrixDirty())
	{
		if (update_matrix)
		{
			Check_Object(entityElement);
			entityElement->Sync();
			update_matrix = false;
		}
		ClearNeedMatrixSync();
	}

	#if defined(HUNT_BUG)
		if (old_update)
			SPEW((0, ""));
	#endif
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::PostCollisionExecute(Time till)
{
	Check_Object(this);
	Verify(GetInterestLevel() != DormantInterestLevel);
	POSTCOLLISION_LOGIC("Entity");

	//
	//----------------------------------------------------------------------
	// Remove the old collision list if it exists, then swap to the new list
	// for next time
	//----------------------------------------------------------------------
	//
	if (oldCollisions)
		delete oldCollisions;
	oldCollisions = newCollisions;
	newCollisions = NULL;

	//
	//---------------------------------------------------------------------
	// If the zone is dirty, let the map figure out where the entity should
	// live
	//---------------------------------------------------------------------
	//
	if (IsZoneDirty())
	{
		POSTCOLLISION_LOGIC("Entity::UpdateZone");
		Verify(parentEntity == Map::GetInstance());
		Check_Object(Map::GetInstance());
		Map::GetInstance()->UpdateZone(this);
		Verify(!IsZoneDirty());
	}

	//
	//-------------------------------------------------------------------
	// Execute the component webs and update the simulation frame counter
	//-------------------------------------------------------------------
	//
	ExecuteComponentWebs();
	lastExecuted = till;
	#if defined(TIME_BUG)
		SPEW((TIME_BUG, "PostCollision execution of %s(+", GetClassString()));
		Spew(TIME_BUG, GetReplicatorID());
		SPEW((TIME_BUG, ") at time %f", lastExecuted.GetSeconds()));
	#endif

	if(m_doesNeedToUpdateWebs)
	{
		m_doesNeedToUpdateWebs = false;
		ExecuteChildComponentWebs();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::PlaceOnEntity(
		LinearMatrix4D *position,
		Motion3D *velocity,
		const LinearMatrix4D& initial_offset,
		const Motion3D& initial_velocity
	)
{
	Check_Object(this);
	Check_Object(position);
	Check_Object(velocity);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::FollowAndBillBoard(
		Stuff::LinearMatrix4D *position,
		Stuff::UnitVector3D *direction
	)
{
	Check_Object(this);
	Check_Object(position);
	Check_Object(direction);

	LinearMatrix4D new_local_to_parent = *position;
	Point3D new_translation;
	new_translation = *position;
	new_local_to_parent.BuildTranslation(new_translation);
	new_local_to_parent.AlignLocalAxisToWorldVector(*direction, Y_Axis, Z_Axis, X_Axis);
	SetNewLocalToParent(new_local_to_parent);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::EnterNeverExecuteState()
{
	Check_Object(this);
	Verify(!newCollisions);
	if(parentEntity)
	{
		Check_Object(parentEntity);
		parentEntity->ChildPreCollisionChanged(this);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::LeaveNeverExecuteState()
{
	Check_Object(this);
	lastExecuted = gos_GetElapsedTime();
	collisionAllowed = false;
	if(parentEntity)
	{
		Check_Object(parentEntity);
		parentEntity->ChildPreCollisionChanged(this);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
	Entity::ProjectLine(
		Line3D *line,
		CollisionMask mask,
		Point3D *target_offset,
		Normal3D *normal
	)
{
	Check_Object(this);
	Check_Object(line);
	Check_Pointer(target_offset);
	Check_Pointer(normal);

	Check_Object(Map::GetInstance());
	CollisionQuery query(line, normal, mask, this);
	return CollisionGrid::Instance->ProjectLine(&query);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Entity::IsWithin(
		Entity *target,
		Scalar distance,
		bool ignorey
	)
{
	Check_Object(this);
	Check_Object(target);
	
	Vector3D target_line = Vector3D::Identity;
	Point3D our_position;
	Point3D target_position;

	our_position = GetLocalToWorld();
	target_position = target->GetLocalToWorld();
	if ((our_position.y < -1500) || (target_position.y < -1500))				// units in hell are never near each other
		return false;

	if (ignorey)
	{
		our_position.y = 0;
		target_position.y = 0;
	}

	target_line.Subtract(target_position, our_position);

	return target_line.GetLengthSquared() <= (distance * distance);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	Entity::GetDistanceFrom(Entity *target)
{
	Check_Object(this);
	Check_Object(target);
	
	Vector3D target_line = Vector3D::Identity;
	Point3D our_position;
	Point3D target_position;

	our_position = GetLocalToWorld();
	target_position = target->GetLocalToWorld();

	target_line.Subtract(target_position, our_position);

	return target_line.GetLength();	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	Entity::GetDistanceSquaredFrom(Entity *target, bool ignore_y)
{
	Check_Object(this);
	Check_Object(target);
	
	Vector3D target_line = Vector3D::Identity;
	Point3D our_position;
	Point3D target_position;

	our_position = GetLocalToWorld();
	target_position = target->GetLocalToWorld();

	target_line.Subtract(target_position, our_position);
	if(ignore_y)
		target_line.y = 0.0f;

	return target_line.GetLengthSquared();	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ExecuteChildComponentWebs()
{
	Check_Object(this);

	ChainIteratorOf<Entity *> iterator(&childEntityChain);
	Entity *entity;
	while((entity = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->ExecuteChildComponentWebs();
	}
	ExecuteComponentWebs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::RemoveCollision()
{
	Check_Object(this);
	SetCollisionMask(NeverCollidesMask);
}

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

bool
	Entity::IsNeverExecuteState()
{
	Check_Object(this);
	
	Check_Object(executionState);
	if (executionState->GetState() == Entity::ExecutionStateEngine::NeverExecuteState)
	{
		return true;
	}

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));

	ResourceID *data_list = Cast_Pointer(ResourceID*, dataListResource.GetPointer());
	if (data_list != NULL)
	{
		Check_Object(RendererManager::Instance);
		size_t len = RendererManager::Instance->GetModelListSize();
		for (size_t i = 0; i < len; i++)
		{
			if (data_list[i] != ResourceID::Null)
			{
				Resource resource(data_list[i]);
				Verify(resource.DoesResourceExist());
			}
		}
	}
}
