#include "munga.h"
#pragma hdrstop

#include "entity.h"
#include "interest.h"
#include "exptbl.h"
#include "hostmgr.h"
#include "player.h"
#include "audio.h"
#include "fileutil.h"
#include "registry.h"
#include "event.h"
#include "app.h"
#include "nttmgr.h"
#include "subsystm.h"
#include "namelist.h"
#include "notation.h"

#if defined(TRACE_EXECUTE_ENTITY)
	static BitTrace Execute_Entity("Execute Entity");
	#define SET_EXECUTE_ENTITY() Execute_Entity.Set()
	#define CLEAR_EXECUTE_ENTITY() Execute_Entity.Clear()
#else
	#define SET_EXECUTE_ENTITY()
	#define CLEAR_EXECUTE_ENTITY()
#endif

#if defined(TRACE_PERFORM_ENTITY)
	static BitTrace Perform_Entity("Perform Entity");
	#define SET_PERFORM_ENTITY() Perform_Entity.Set()
	#define CLEAR_PERFORM_ENTITY() Perform_Entity.Clear()
#else
	#define SET_PERFORM_ENTITY()
	#define CLEAR_PERFORM_ENTITY()
#endif

#if defined(TRACE_PERFORM_SUBSYSTEMS)
	static BitTrace Perform_Subsystems("Perform Subsystems");
	#define SET_PERFORM_SUBSYSTEMS() Perform_Subsystems.Set()
	#define CLEAR_PERFORM_SUBSYSTEMS() Perform_Subsystems.Clear()
#else
	#define SET_PERFORM_SUBSYSTEMS()
	#define CLEAR_PERFORM_SUBSYSTEMS()
#endif

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

//#############################################################################
// Virtual Data support
//
Derivation* Entity::GetClassDerivations()
{
	static Derivation classDerivations(Simulation::GetClassDerivations(), "Entity");
	return &classDerivations;
}

Entity::SharedData
	Entity::DefaultData(
		Entity::GetClassDerivations(),
		Entity::GetMessageHandlers(),
		Entity::GetAttributeIndex(),
		Entity::StateCount,
		Entity::Make
	);

//#############################################################################
// Message Support
//
const Receiver::HandlerEntry
	Entity::MessageHandlerEntries[]=
{
	{
		Entity::MakeMessageID,
		"Make",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::MakeReadyMessageID,
		"MakeReady",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::RemakeEntityMessageID,
		"RemakeEntity",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::RemakeReadyMessageID,
		"RemakeReady",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::RemakeCompleteMessageID,
		"RemakeComplete",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::RemakeIncompleteMessageID,
		"RemakeIncomplete",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	MESSAGE_ENTRY(Entity, DestroyEntity),
	{
		Entity::TransferEntityMessageID,
		"TransferEntity",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::TransferCompleteMessageID,
		"TransferComplete",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::TransferIncompleteMessageID,
		"TransferIncomplete",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	MESSAGE_ENTRY(Entity, BecomeInteresting),
	MESSAGE_ENTRY(Entity, BecomeUninteresting),
	MESSAGE_ENTRY(Entity, Update),
	{
		Entity::SubscribeReplicantMessageID,
		"SubscribeReplicant",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	{
		Entity::UnsubscribeReplicantMessageID,
		"UnsubscribeReplicant",
		(Entity::Handler)&Entity::DefaultMessageHandler
	},
	MESSAGE_ENTRY(Entity, TakeDamage),
	MESSAGE_ENTRY(Entity, PlayerLink),
	MESSAGE_ENTRY(Entity, TakeDamageStream)
};

Entity::MessageHandlerSet& Entity::GetMessageHandlers()
{
	static Entity::MessageHandlerSet messageHandlers(ELEMENTS(Entity::MessageHandlerEntries), Entity::MessageHandlerEntries, Receiver::GetMessageHandlers());
	return messageHandlers;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::Receive(Event* event)
{
	Check(this);
	Check(event);

	//
	//-------------------------------------------------------------------------
	// If the entity is being transferred, post the message to the new owner of
	// the entity
	//-------------------------------------------------------------------------
	//

	//
	//--------------------------------------------------------------------------
	// If the object is not valid, filter the message to determine which ones to
	// defer and which ones to immediately process
	//--------------------------------------------------------------------------
	//
	if (!IsValid())
	{
		//switch case commented out by Ryan Bunker on 1/6/07
		//switch (event->messageToSend->messageID)
		{
		//
		//----------------------------------------------------------------------
		// The messages which should be allowed through should just break out of
		// this switch
		//----------------------------------------------------------------------
		//

		//default:
		//
		//-------------------
		// Defer the messages
		//-------------------
		//
			event->Defer();
			return;
		}
	}

	//
	//------------------------------------------------
	// Go ahead and dispatch the message to the entity
	//------------------------------------------------
	//
	Receiver::Receive(event);

	#if defined(USE_FPU_ANALYSIS)
		if (!Fpu_Ok())
		{
			Registry *registry = application->GetRegistry();
			Receiver::SharedData *shared_data =
				registry->GetStaticData(GetClassID());
			Derivation *derivation = shared_data->derivedClasses;
			Receiver::MessageHandlerSet *handlers =
				shared_data->activeMessageHandlers;
			DEBUG_STREAM << derivation->className << ',' << GetInstance() << endl << std::flush;
			DEBUG_STREAM << handlers->GetName(event->messageToSend->messageID)
			 << endl;
			Fail("Event corrupted FPU!");
		}
	#else
		Check_Fpu();
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::Dispatch(Receiver::Message* incoming)
{
	Message *message = (Message*) incoming;
	Check(this);
	Check(message);

	//
	// Call Receiver if it is not entity aware!!!!!
	//
	if (message->messageID < Receiver::NextMessageID)
	{
		Receiver::Dispatch(message);
	}

	//
	//-----------------------------------------
	// Initialize the entity information fields
	//-----------------------------------------
	//
	message->entityID = entityID;
	message->interestZoneID = interestZoneID;

	//
	//-------------------------------------------------------------------
	// If this is a replicant instance, reroute the message to the master
	//-------------------------------------------------------------------
	//
	if (GetInstance() == ReplicantInstance)
	{
		application->SendMessage(
			ownerID,
			EntityManager::EntityManagerClientID,
			message
		);
	}

	//
	//--------------------------------------------------------
	// If this entity is invalid, post the message as an event
	//--------------------------------------------------------
	//
	else if (!IsValid())
	{
		// ECH 1/28/96 - application->Post(DefaultEventPriority, this, message);
		application->Post(EntityInvalidEventPriority, this, message);
	}

	//
	//--------------------------------------------
	// Otherwise, go ahead and receive the message
	//--------------------------------------------
	//
	else
	{
		Receiver::Receive(message);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::DispatchToReplicants(Message *what)
{
	Check(this);
	Check(what);

	what->entityID = entityID;
	what->interestZoneID = interestZoneID;
	Check(application);
	InterestManager *int_mgr = application->GetInterestManager();
	Check(int_mgr);
	int_mgr->EntityBroadcastToReplicants(this, what);
	Check_Fpu();
}

//#############################################################################
// Attribute Support
//
const Entity::IndexEntry
	Entity::AttributePointers[]=
{
	ATTRIBUTE_ENTRY(Entity, LocalToWorld,		localToWorld),
	ATTRIBUTE_ENTRY(Entity, LocalOrigin,		localOrigin),
	ATTRIBUTE_ENTRY(Entity, DamageZoneCount,	damageZoneCount),
	ATTRIBUTE_ENTRY(Entity, DamageZones,		damageZones)
};

Entity::AttributeIndexSet& Entity::GetAttributeIndex()
{
	static Entity::AttributeIndexSet attributeIndex(ELEMENTS(Entity::AttributePointers),
		Entity::AttributePointers,
		Simulation::GetAttributeIndex()
	);
	return attributeIndex;
}

//#############################################################################
// Model Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::WriteUpdateRecord(
		Simulation::UpdateRecord *record,
		int update_model
	)
{
	Check(this);
	Check_Pointer(record);

	switch (update_model)
	{
		case DefaultUpdateModelBit:
		{
			Simulation::WriteUpdateRecord(record, update_model);

			//
			//----------------------------
			// Write out the record update
			//----------------------------
			//
			UpdateRecord *update = (UpdateRecord*)record;
			update->localOrigin = localOrigin;
			updateOrigin = localOrigin;
			update->recordLength = sizeof(*update);
		}
		break;
		case DamageZoneUpdateModelBit:
				record->timeStamp = lastPerformance;
				record->recordID = (Word)update_model;
				WriteDamageUpdateRecord(record);
		break;

		default:
				Simulation::WriteUpdateRecord(record, update_model);
				break;
	}
	record->subsystemID = (Word)(EntitySubsystemID + 1);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::UpdateMessageHandler(UpdateMessage *message)
{
	Check(this);
	Check(message);

	//
	//------------------------------------------------------
	// Position to the beginning of the update record blocks
	//------------------------------------------------------
	//
	MemoryStream stream(message, message->messageLength, sizeof(*message));

	//
	//------------------------------------------------------------------------
	// Step through each block until there are no more remaining, and send the
	// update out the the simulation indicated by the subsystemID
	//------------------------------------------------------------------------
	//
	while (stream.GetBytesRemaining())
	{
		Simulation::UpdateRecord *update =
			(Simulation::UpdateRecord*)stream.GetPointer();
		Check_Pointer(update);
		Simulation *simulation = GetSimulation((int)update->subsystemID-1);
		Check(simulation);
		simulation->ReadUpdateRecord(update);
		stream.AdvancePointer(update->recordLength);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ReadUpdateRecord(Simulation::UpdateRecord *record)
{
	Check(this);
	Check_Pointer(record);

	switch(record->recordID)
	{
		case DefaultUpdateModelBit:
		{
			//
			//-----------------------
			// Read the record update
			//-----------------------
			//
			UpdateRecord *update = (UpdateRecord*)record;
			updateOrigin = update->localOrigin;

			//
			//------------------------------------------------------------------
			// If we aren't able to run yet, we must update local origin as well
			//------------------------------------------------------------------
			//
			Check(application);
			int app_state = application->GetApplicationState();
			if (
				app_state < Application::RunningMission
				 || app_state == Application::CreatingMission
			)
			{
				localOrigin = updateOrigin;
				localToWorld = localOrigin;
			}
			Simulation::ReadUpdateRecord(record);
		}
		break;
		case DamageZoneUpdateModelBit:
				ReadDamageUpdateRecord(record);
		break;
		default:
			Simulation::ReadUpdateRecord(record);
	}



	Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::ReadDamageUpdateRecord(Simulation::UpdateRecord *update_record)
{
	//
	//------------------------------------------------------------------------
	// Step through each block until there are no more remaining, and send the
	// update out the the damageZone indicated by the damageZoneIndex
	//------------------------------------------------------------------------
	//
	MemoryStream stream(
		update_record,
		update_record->recordLength,
		sizeof(*update_record)
	);

	while (stream.GetBytesRemaining())
	{
		DamageZone::UpdateRecord *damage_zone_record =
			(DamageZone::UpdateRecord*)stream.GetPointer();
		int damage_zone_index = damage_zone_record->damageZoneIndex;
		damageZones[damage_zone_index]->ReadUpdateRecord(damage_zone_record);
		stream.AdvancePointer(damage_zone_record->recordLength);
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#define MAX_UPDATE_LENGTH 1400

static long Update_Buffer[MAX_UPDATE_LENGTH >> 2];
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::WriteDamageUpdateRecord(Simulation::UpdateRecord *update_record)
{
	Check(this);
	Check_Pointer(update_record);

	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Make a memory stream this size of a network packet
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	MemoryStream stream(
		update_record,
		sizeof(Update_Buffer) - ((char*)update_record - (char*)Update_Buffer),
		sizeof(*update_record)
	);
	DamageZone::UpdateRecord *record_start =
		(DamageZone::UpdateRecord*)stream.GetPointer();

	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Update Every DamageZone For NOW
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	for(int ii=0;ii<damageZoneCount;++ii)
	{
		DamageZone::UpdateRecord *damage_zone_update =
			(DamageZone::UpdateRecord*)stream.GetPointer();
		damageZones[ii]->WriteUpdateRecord(damage_zone_update);
		stream.AdvancePointer(damage_zone_update->recordLength);
	}

	//
	//-----------------------------------------------
	// Check to see if an update message is necessary
	//-----------------------------------------------
	//
	if (stream.GetPointer() != record_start)
	{
		Simulation::UpdateRecord *damage_update =
			(Simulation::UpdateRecord*)update_record;
		damage_update->recordLength =
			(char*)stream.GetPointer() - (char*)update_record;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::UpdateMessage*
	Entity::Execute(const Time& till)
{
	SET_EXECUTE_ENTITY();
	Check(this);
	Check(&till);

	MemoryStream
		stream(Update_Buffer, sizeof(Update_Buffer), sizeof(UpdateMessage));
	char *record_start = (char*)stream.GetPointer();

	//
	//-----------------------------------------------------------------------
	// Check to see if the application will let us run.  If We are starting a
	// mission, only the player class objects can execute
	//-----------------------------------------------------------------------
	//
	Check(application);
	if (
		application->GetApplicationState() == Application::RunningMission
		 || application->GetApplicationState() == Application::EndingMission
		 || IsPreRunnable()
	)
	{
		PerformAndWatch(till, &stream);
	}
	else
	{
		WriteSimulationUpdate(&stream);
	}

	#if defined(USE_FPU_ANALYSIS)
		if (!Fpu_Ok())
		{
			Registry *registry = application->GetRegistry();
			Receiver::SharedData *shared_data =
				registry->GetStaticData(GetClassID());
			Derivation *derivation = shared_data->derivedClasses;
			DEBUG_STREAM << derivation->className << ',' << GetInstance() << endl << std::flush;
			Fail("Simulation corrupted FPU!");
		}
	#endif

	//
	//-----------------------------------------------
	// Check to see if an update message is necessary
	//-----------------------------------------------
	//
	if (stream.GetPointer() != record_start)
	{
		UpdateMessage *update = (UpdateMessage*)Update_Buffer;
		update->messageID = UpdateMessageID;
		update->messageLength = (char*)stream.GetPointer() - (char*)Update_Buffer;
		update->messageFlags = 0;
		CLEAR_EXECUTE_ENTITY();
		return update;
	}

	//
	//-------------------------------------
	// Otherwise, return NULL for no update
	//-------------------------------------
	//
	Check_Fpu();
	CLEAR_EXECUTE_ENTITY();
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Simulation*
	Entity::GetSimulation(int index)
{
	Check(this);

	if (index == EntitySubsystemID)
	{
		return this;
	}

	Check_Fpu();
	if ((unsigned)index < subsystemCount)
	{
		return subsystemArray[index];
	}
	else
	{
		return NULL;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Subsystem*
	Entity::FindSubsystem(const char* name)
{
	Check(this);
	Check_Pointer(name);

	for (int i=0; i<subsystemCount; ++i)
	{
		if (subsystemArray[i])
		{
			Check(subsystemArray[i]);
			if (!stricmp(subsystemArray[i]->GetName(), name))
			{
				return subsystemArray[i];
			}
		}
	}
	Check_Fpu();
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#define MAX_SUB_FILE_NAME (256)

int
	Entity::FindSubsytemID
	(
#if DEBUG_LEVEL>0
		const char *model_name,
		const char *subsystem_name
#else
		const char *,
		const char *subsystem_name
#endif
	)
{
	Check_Pointer(model_name);
	Check_Pointer(subsystem_name);

	int subsytem_id = EntitySubsystemID;
	int sub_count = 0;

	CString sub_file_string;
	CString sub_directory_string("models\\");
	CString model_name_string("model_name");
	CString sub_post_string(".sub");

	sub_file_string =
		sub_directory_string + model_name_string + sub_post_string;

	NotationFile sub_file(sub_file_string);
	Check(&sub_file);

	NameList *page_list = sub_file.MakePageList();
	Register_Object(page_list);
	NameList::Entry *entry=page_list->GetFirstEntry();
	while (entry != NULL)
	{
		Check(entry);
		Check_Pointer(entry->GetName());
		if (!stricmp(subsystem_name, entry->GetName()))
		{
			subsytem_id = sub_count;
			break;
		}

		++sub_count;
		entry = entry->GetNextEntry();
	}

	Unregister_Object(page_list);
	delete page_list;

	return subsytem_id;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::PerformAndWatch(
		const Time &till,
		MemoryStream *update_stream
	)
{
	Check(this);
	Check(&till);

	//
	//-----------------------------------------
	// Execute all the subsystems, then ourself
	//-----------------------------------------
	//
	SET_PERFORM_SUBSYSTEMS();
	if (subsystemArray)
	{
		Check_Pointer(subsystemArray);

		//
		//---------------------------------------------------
		// If this is not a replicant, execute all subsystems
		//---------------------------------------------------
		//
		if (GetInstance() != ReplicantInstance)
		{
			for (int i=0; i<subsystemCount; ++i)
			{
				if (subsystemArray[i])
				{
					Subsystem *subsystem = subsystemArray[i];
					Check(subsystem);
					if (subsystem->IsNonReplicantExecutable())
					{
						subsystem->PerformAndWatch(till, update_stream);
						#if defined(USE_FPU_ANALYSIS)
							if (!Fpu_Ok())
							{
								Registry *registry = application->GetRegistry();
								Receiver::SharedData *shared_data =
									registry->GetStaticData(GetClassID());
								Derivation *derivation = shared_data->derivedClasses;
								DEBUG_STREAM << derivation->className
								 << "\nExecuting subsystem[" << i << "], "
								 << subsystem->GetName() << endl;
								Fail("Simulation corrupted FPU!");
							}
						#endif
					}
				}
			}
		}

		//
		//--------------------------------------------------------------------
		// If this is a replicant, execute only those subsystems which need to
		//--------------------------------------------------------------------
		//
		else
		{
			for (int i=0; i<subsystemCount; ++i)
			{
				if (subsystemArray[i])
				{
					Subsystem *subsystem = subsystemArray[i];
					Check(subsystem);
					if (subsystem->IsReplicantExecutable())
					{
						subsystem->PerformAndWatch(till, update_stream);
						#if defined(USE_FPU_ANALYSIS)
							if (!Fpu_Ok())
							{
								Registry *registry = application->GetRegistry();
								Receiver::SharedData *shared_data =
									registry->GetStaticData(GetClassID());
								Derivation *derivation = shared_data->derivedClasses;
								DEBUG_STREAM << derivation->className
								 << " replicant\nExecuting subsystem[" << i << "], "
								 << subsystem->GetName() << endl;
								Fail("Simulation corrupted FPU!");
							}
						#endif
					}
				}
			}
		}
	}
	CLEAR_PERFORM_SUBSYSTEMS();

	SET_PERFORM_ENTITY();
	Simulation::PerformAndWatch(till, update_stream);
	Check_Fpu();
	CLEAR_PERFORM_ENTITY();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::PlayerLinkMessageHandler(PlayerLinkMessage *message)
{
	Check(application);
	HostManager *host = application->GetHostManager();
	Check(host);
	playerLink =(Player*) host->GetEntityPointer(message->playerID);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::TakeDamageStreamMessageHandler(TakeDamageStreamMessage *message)
{
	Check(this);
	Check(message);

	//
	//-------------------------------------------------
	// Extract the common information out of the stream
	//-------------------------------------------------
	//
	Damage   damage_data;
	EntityID shooting_entity = message->shootingEntity;
	int damage_zone_index = message->damageZoneIndex;
	damage_data.impactPoint = message->impactPoint;

	//
	//---------------------------------------------------
	// Figure out how many damage messages there are here
	//---------------------------------------------------
	//
	DynamicMessage damage_stream(
		message,
		message->messageLength,
		sizeof(*message)
	);
	int entry_count;
	damage_stream >> entry_count;

	for (int i=0; i<entry_count; ++i)
	{
		//
		//-------------------------------------------------
		// Read in damageType, damageAmount and subsystemID
		//-------------------------------------------------
		//
		damage_stream >> damage_data.damageType >> damage_data.damageAmount;
		int subsystem;
		damage_stream >> subsystem;

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Apply the damage to the appropriate damageZone
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		TakeDamageMessage
			message(
				TakeDamageMessageID,
				sizeof(TakeDamageMessage),
				shooting_entity,
				damage_zone_index,
				damage_data,
				subsystem
			);
		Dispatch(&message);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::TakeDamageMessageHandler(TakeDamageMessage *message)
{
	//
	//-------------------------------------
	// YOU MUST CHECK FOR -1 DamageZone!!!!
	//-------------------------------------
	//
	if (!damageZones || message->damageZone == -1)
	{
		return;
	}

	//
	//----------------------------------------------------------------------
	// If we have no damage zones allocated, we ignore any damage sent to us
	//----------------------------------------------------------------------
	//
	Verify(message->damageZone < damageZoneCount);
	Verify(message->damageZone >= 0 );
	Check_Pointer(damageZones);
	Check(damageZones[message->damageZone]);
	damageZones[message->damageZone]->TakeDamage(message->damageData);
	Check_Fpu();
}

//#############################################################################
// Construction and Destruction
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::Entity(
	Entity::MakeMessage *creation_message,
	Entity::SharedData &virtual_data
):
	Simulation(creation_message->classToCreate,virtual_data),
	staticVideoSocket(NULL),
	dynamicVideoSocket(NULL),
	audioSocket(NULL)
{
	Check_Pointer(this);
	Check(creation_message);

	//
	// Get host manager pointer
	//
	HostManager *host_manager;

	Check(application);
	host_manager = application->GetHostManager();
	Check(host_manager);

	//
	//-----------------------------
	// Initialize the entity fields
	//-----------------------------
	//
	playerLink = NULL;
	resourceID = creation_message->resourceID;
	owningPlayer = (Player*)host_manager->GetEntityPointer(
		creation_message->owningPlayerID
	);
	simulationFlags = creation_message->instanceFlags;
	entityID = creation_message->entityID;
	ownerID = creation_message->ownerID;
	creationTime = Now();
	interestZoneID = creation_message->interestZoneID;
	interestCount = 0;

	// initialize attributes
	localOrigin = creation_message->localOrigin;
	updateOrigin = localOrigin;
	localToWorld = localOrigin;

	// initialize camera stuff
	cameraOffset = Origin::Identity;

	SetInvalidFlag();

	// initialize model stuff
	lastPerformance = creationTime;
	subsystemCount = 0;
	subsystemArray = NULL;
	damageZones = NULL;
	damageZoneCount = 0;

	//
	//--------------------------------
	// Allocate the damageZones array
	//--------------------------------
	//

	//
	//  Read In DamageZones and Create DamageZoneID's
	//
	if (resourceID != ResourceDescription::NullResourceID)
	{
		ResourceDescription *dmg_res =
			application->GetResourceFile()->SearchList(
				resourceID,
				ResourceDescription::DamageZoneStreamResourceType
			);

		//
		//--------------------------------------------
		// Allocate damageZones for this entity only
		// if they were defined in the resource
		//--------------------------------------------
		//
		if (dmg_res)
		{
			dmg_res->Lock();
			DynamicMemoryStream	damage_zone_stream(
				dmg_res->resourceAddress,
				dmg_res->resourceSize);

			damage_zone_stream >> damageZoneCount;
			damageZones = new (::DamageZone(*[damageZoneCount]));
			Register_Pointer(damageZones);
			dmg_res->Unlock();
		}
	}

	//
	//---------------------------------------------------------
	// Create a new entity ID and assign ownership to this host
	// If	(this is a master entity) or
	// 	(this is a dynamic hermit) or
	//		(this is the initial independent)
	//---------------------------------------------------------
	//
	if (
		(GetInstance() == MasterInstance) ||
		(GetInstance() == HermitInstance && IsDynamic()) ||
		(
			(GetInstance() == IndependantInstance) &&
			(creation_message->ownerID == NullHostID ||
			 creation_message->ownerID == MapHostID)
		)
	)
	{
		entityID = host_manager->MakeUniqueEntityID();
		ownerID = host_manager->GetLocalHostID();
		creation_message->entityID = entityID;
		creation_message->ownerID = ownerID;
	}

	//
	//--------------------------------------------
	// Tell the host manager about this new entity
	//--------------------------------------------
	//
	Check(host_manager);
	host_manager->NotifyOfEntityCreation(this, creation_message);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
	Entity::Make(Entity::MakeMessage *creation_message)
{
	return new Entity(creation_message,DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Entity::CreateMakeMessage(
		MakeMessage *creation_message,
#if DEBUG_LEVEL>0
		NotationFile *model_file,
#else
		NotationFile *,
#endif
		const ResourceDirectories *
	)
{
	Check(creation_message);
	Check(model_file);
	char *p;

	creation_message->messageLength = sizeof(Entity::MakeMessage);
	creation_message->messageID = Entity::MakeMessageID;
	creation_message->owningPlayerID = EntityID::Null;
	creation_message->messageFlags = 0;
	creation_message->classToCreate = RegisteredClass::TrivialEntityClassID;
	creation_message->instanceFlags = 0; //DefaultFlags;

	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "X position coordinate missing!\n";
		return False;
	}
	creation_message->localOrigin.linearPosition.x = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Y position coordinate missing!\n";
		return False;
	}
	creation_message->localOrigin.linearPosition.y = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Z position coordinate missing!\n";
		return False;
	}
	creation_message->localOrigin.linearPosition.z = atof(p);

	EulerAngles pyr;
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Pitch missing!\n";
		return False;
	}
	pyr.pitch = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Yaw missing!\n";
		return False;
	}
	pyr.yaw = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Roll missing!\n";
		return False;
	}
	pyr.roll = atof(p);
	creation_message->localOrigin.angularPosition = pyr;
	return True;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Entity::CreateMakeMapMessage(
		MakeMapMessage *creation_message,
		ResourceDescription::ResourceID resource_id,
#if DEBUG_LEVEL>0
		NotationFile *model_file,
#else
		NotationFile *,
#endif
		const ResourceDirectories *
	)
{
	Check(creation_message);
	Check(model_file);
	char *p;

	creation_message->messageLength = sizeof(Entity::MakeMapMessage);
	creation_message->messageID = Entity::MakeMessageID;
	creation_message->messageFlags = Entity::MakeMessage::MapStreamMarkerFlag;


	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "X position coordinate missing!\n";
		return False;
	}
	creation_message->localOrigin.linearPosition.x = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Y position coordinate missing!\n";
		return False;
	}
	creation_message->localOrigin.linearPosition.y = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Z position coordinate missing!\n";
		return False;
	}
	creation_message->localOrigin.linearPosition.z = atof(p);

	EulerAngles pyr = EulerAngles::Identity;
#if 0
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Pitch missing!\n";
		return False;
	}
	pyr.pitch = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Yaw missing!\n";
		return False;
	}
	pyr.yaw = atof(p);
	if ((p = strtok(NULL, " ")) == NULL)
	{
		std::cerr << "Roll missing!\n";
		return False;
	}
	pyr.roll = atof(p);
#endif

	creation_message->localOrigin.angularPosition = pyr;
	creation_message->resourceID = resource_id;

	return True;

}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::DestroyEntityMessageHandler(Message *)
{
	CondemnToDeathRow();
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::~Entity()
{
	//
	// Notify the host manager that we are going away
	//
	Check(application);
	application->GetHostManager()->NotifyOfEntityDestruction(this);

	//
	// Delete the subsystem array
	//
	if (subsystemArray)
	{
		Check_Pointer(subsystemArray);
		for (int i=0; i<subsystemCount; ++i)
		{
			if (subsystemArray[i])
			{
				Unregister_Object(subsystemArray[i]);
				delete subsystemArray[i];
			}
		}
		Unregister_Pointer(subsystemArray);
		delete subsystemArray;
	}
	if (damageZones)
	{
		Check_Pointer(damageZones);
		for (int i=0; i<damageZoneCount; ++i)
		{
			if (damageZones[i])
			{
				Unregister_Object(damageZones[i]);
				delete damageZones[i];
			}
		}
		Unregister_Pointer(damageZones);
		delete damageZones;
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Entity::CondemnToDeathRow()
{
	Check(this);
	Check(application);
	EntityManager *entity_manager = application->GetEntityManager();
	Check(entity_manager);

	//
	//-------------------------------------------------
	// Make sure we are not entered into deathrow twice
	//-------------------------------------------------
	//
	if (!IsCondemned())
	{
		entity_manager->deathRow->Add(this);
		SetCondemnedFlag();
		NeverExecute();
	}
	Check_Fpu();
}

//#############################################################################
// Test Support
//

Logical
	Entity::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}

//##########################################################################
// Renderer Support
//
void
	Entity::AddStaticVideoComponent(Component *component)
{
	Check(component);
	Check(&staticVideoSocket);
	staticVideoSocket.Add(component);
	Check_Fpu();
}

void
	Entity::AddDynamicVideoComponent(Component *component)
{
	Check(component);
	Check(&dynamicVideoSocket);
	dynamicVideoSocket.Add(component);
	Check_Fpu();
}

void
	Entity::AddAudioComponent(Component *component)
{
	Check(component);
	Check(&audioSocket);
	audioSocket.Add(component);
	Check_Fpu();
}

Enumeration
	Entity::GetAudioRepresentation(Entity *linked_entity)
{
	Check(this);
	Check(linked_entity);

	return
		(linked_entity == this) ?
			InternalAudioRepresentation : ExternalAudioRepresentation;
}

//#############################################################################
// Interest Support
//
void
	Entity::BecomeInteresting()
{
	BecomeInterestingMessage
		message(
			Entity::BecomeInterestingMessageID,
			sizeof(BecomeInterestingMessage)
		);

	Receiver::Dispatch(&message);
	Check_Fpu();
}

void Entity::BecomeInterestingMessageHandler(const Receiver::Message*)
{
	++interestCount;
	Check_Fpu();
}

void
	Entity::BecomeUninterestingMessageHandler()
{
	--interestCount;
	Check_Fpu();
}

Enumeration
	Entity::GetInterestPriority(Entity *linked_entity)
{
	Check(this);
	Check(linked_entity);

	return (linked_entity == this) ? HighInterestPriority : LowInterestPriority;
}



int
	Entity::GetDamageZoneIndex(const CString &damage_zone_name)  const
{

	for(int ii=0;ii<damageZoneCount;++ii)
	{
		if(damageZones[ii]->damageZoneName.Compare(damage_zone_name) == 0)
		{
			return ii;
		}
	}
	return -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Entity::CreateDamageZoneStream(
		ResourceFile *resource_file,
		const char *model_name,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check(model_file);
	Check_Pointer(directories);

	//
	//-------------------
	// Find the .dmg file
	//-------------------
	//
	const char* dmg_file_cname;
	if (
		!model_file->GetEntry(
			"gamedata",
			"DamageZones",
			&dmg_file_cname
		)
	)
	{
		std::cerr << model_name << " is missing .dmg file specification!\n";
		return -1;
	}

	CString dmg_filename =
		MakePathedFilename(directories->modelDirectory, dmg_file_cname);

	NotationFile *dmg_file = new NotationFile(dmg_filename);
	Register_Object(dmg_file);
	int page_count = dmg_file->PageCount();
	if (!page_count)
	{
		std::cerr << dmg_filename << " is empty or missing!\n";
		Unregister_Object(dmg_file);
		delete dmg_file;
		return -1;
	}

	DynamicMemoryStream damage_stream;

	damage_stream << page_count;
	NameList *dmg_namelist = dmg_file->MakePageList();
	Register_Object(dmg_namelist);

	NameList::Entry *dmg_entry = dmg_namelist->GetFirstEntry();

	CString	dmg_page_name;
	while(dmg_entry)
	{
		dmg_page_name = dmg_entry->GetName();
		DamageZone::CreateStreamedDamageZone(
			model_file,
			model_name,
			NULL,
			dmg_page_name,
			&damage_stream,
			dmg_file,
			directories
		);
		dmg_entry = dmg_entry->GetNextEntry();
	}

	//
	//~~~~~~~~~~~~~~~~~~~~~~~
	// Add Stream to Resource
	//~~~~~~~~~~~~~~~~~~~~~~~
	//
	ResourceDescription *new_res =
		resource_file->AddResourceMemoryStream(
			model_name,
			ResourceDescription::DamageZoneStreamResourceType,
			1,
			ResourceDescription::Preload,
			&damage_stream
		);
	Check(new_res);

	Unregister_Object(dmg_file);
	delete dmg_file;
	Unregister_Object(dmg_namelist);
	delete dmg_namelist;

	return  new_res->resourceID;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
	Entity::CreateExplosionTableStream(
		ResourceFile *resource_file,
		const char *model_name,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check(model_file);
	Check_Pointer(directories);
	//
	//~~~~~~~~~~~~~~~~~
	// Find *.dmg file
	//~~~~~~~~~~~~~~~~~
	//
	const char *entry_data;
	if (!model_file->GetEntry(
			"gamedata",
			"DamageZones",
			&entry_data
		)
	)
	{
		DEBUG_STREAM << model_name << " is missing .dmg file specification!\n" << std::flush;
		return -1;
	}
	CString filename =
		MakePathedFilename(directories->modelDirectory, entry_data);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Make a notation file of the DMG Entries
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	NotationFile *dmg_file = new NotationFile(filename);
	Register_Object(dmg_file);

	if(dmg_file->IsEmpty())
	{
		DEBUG_STREAM << model_name << " *.dmg file is empty or missing!\n" << std::flush;
		Unregister_Object(dmg_file);
		delete dmg_file;
		return -1;
	}

	DynamicMemoryStream explosion_stream;
	NameList *dmg_namelist = dmg_file->MakePageList();
	Register_Object(dmg_namelist);

	NameList::Entry *dmg_entry = dmg_namelist->GetFirstEntry();
	while(dmg_entry)
	{
		const char *entry_data;
		if (dmg_file->GetEntry(
				dmg_entry->GetName(),
				"ExplosionTable",
				&entry_data
			)
		)
		{
			CString filename =
				MakePathedFilename(directories->modelDirectory, entry_data);

			NotationFile *exp_file = new NotationFile(filename);
			Register_Object(exp_file);

			if(exp_file->IsEmpty())
			{
				std::cerr<<model_name<<" : "<<filename<<" is emoty or missing! \n";
				Unregister_Object(dmg_file);
				delete dmg_file;
				Unregister_Object(dmg_namelist);
				delete dmg_namelist;
				Unregister_Object(exp_file);
				delete exp_file;
				return ResourceDescription::NullResourceID;
			}
			else
			{
				ExplosionTable::CreateStreamedExplosionTable(
					resource_file,
					model_name,
					exp_file,
					filename,
					&explosion_stream
				);
			}
         Unregister_Object(exp_file);
			delete exp_file;
		}
		else
		{
			//
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// Write Out no Explosions for this DamageZone
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			//
			int zero_int(0);
			explosion_stream << zero_int;
		}
		dmg_entry = dmg_entry->GetNextEntry();
	}

	ResourceDescription *new_res =
		resource_file->AddResourceMemoryStream(
			model_name,
			ResourceDescription::ExplosionTableStreamResourceType,
			1,
			ResourceDescription::Preload,
			&explosion_stream
	);
	Check(new_res);
	Unregister_Object(dmg_file);
	delete dmg_file;
	Unregister_Object(dmg_namelist);
	delete dmg_namelist;
	return new_res->resourceID;
}

#if defined(TEST_CLASS)
#	include "entity.tcp"
#endif
