//===========================================================================//
// File:	DamageObject.cpp                                                    //
// Project: Adept           Brick: DamageObject                              //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/08/99	 DPB	Created File
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#include "AdeptHeaders.hpp"

#include "DamageObject.hpp"
#include "Entity.hpp"
#include "Tool.hpp"
#include "Effect.hpp"
#include "Map.hpp"
#include "NameTable.hpp"
#include "Site.hpp"


//#############################################################################
//###########################    DamageObject   ###############################
//#############################################################################
extern int g_nMR;

DamageObject::ClassData*
	DamageObject::DefaultData = NULL;

HGOSHEAP
	Adept::g_DamageObjectHeap = NULL;

void __cdecl Adept::AdeptDamageSecurityCheckStart(void)
{
	_asm
	{
		nop
	}	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DamageObject::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			DamageObjectClassID,
			"Adept::DamageObject",
			Receiver::DefaultData,
			0,
			NULL
		);
	Check_Object(DefaultData);

	
	Verify(!g_DamageObjectHeap);
	g_DamageObjectHeap = gos_CreateMemoryHeap("DamageObject");
	Check_Pointer(g_DamageObjectHeap);

}

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

	
	Check_Pointer(g_DamageObjectHeap);
	gos_DestroyMemoryHeap(g_DamageObjectHeap);
	g_DamageObjectHeap = NULL;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DamageObject::DamageObject(
	ClassData *class_data,
	MemoryStream *stream
):
	Receiver(class_data)
{
	Check_Pointer(this);
	Check_Object(stream);

	//Need to stream stuff in here
	*stream >> baseArmorValue;
	*stream >> currentArmorValue;
	*stream >> scaleSplashDamage;
	*stream >> damageObjectName;
	*stream >> internalDamageObjectID;
	*stream >> armorZone;
	*stream >> damageLevel;
	*stream >> m_armorType;
	*stream >> maxArmorValue;
	*stream >> m_specialConnectedToZone;

	armorLevel = 1.0f;
	damageArrayEntry = NULL;
	armorArrayEntry = NULL;
	internalDamageObject = NULL;
	{for(int qqq = 0; qqq < DmgModifier_Count; qqq++)
	{
		m_aDamageModifier[qqq * 2] = 1.0f; // real value
		m_aDamageModifier[qqq * 2 + 1] = 1.0f; // backup value
	}}
	m_HitPointPointer = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void*
	DamageObject::operator new(size_t size)
{
	/*
	gos_PushCurrentHeap(g_DamageObjectHeap);

	void *temp = ::new(size_t);

	gos_PopCurrentHeap();

	return temp;
	*/
	return gos_Malloc(size,g_DamageObjectHeap);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DamageObject::Reuse()
{
	Check_Object(this);

	m_HitPointPointer = NULL;

	currentArmorValue = baseArmorValue;
	damageLevel = NoArmorDamage;
	if(damageArrayEntry)
		*damageArrayEntry = damageLevel;

	Scalar damage_percentage;
	damage_percentage = 1.0f;
	armorLevel = damage_percentage;
	if (armorArrayEntry)
		*armorArrayEntry = damage_percentage;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DamageObject::ConstructDamageObjectStream(
		MemoryStream *stream,
		Page *page
	)	
{
	Check_Object(stream);
	Check_Object(page);

	int internal_damage_zone_id;
	int armor_zone;

	const char *armor_zone_text = "";
	page->GetEntry("ArmorZone", &armor_zone_text);
	
	armor_zone = ArmorZoneTextToAscii(armor_zone_text);

	const char *zone_name;
	page->GetEntry("InternalDamageZone", &zone_name, true);

	internal_damage_zone_id = InternalDamageObject::InternalZoneTextToAscii(zone_name);

	const char *attached_zone_string;
	int attached_to_zone = NullArmorZone;
	if(page->GetEntry("SpecialAttachedToZone", &attached_zone_string))
		attached_to_zone = ArmorZoneTextToAscii(attached_zone_string);

	Stuff::Scalar
		base_armor_value,
		current_armor_value,
		scale_splash_damage,
		max_armor_value;

	base_armor_value = 0.0f;
	page->GetEntry("BaseArmorValue", &base_armor_value);
	Verify(base_armor_value >= 0.0f);

	max_armor_value = base_armor_value;
	page->GetEntry("MaxArmorValue", &max_armor_value);
	Verify(max_armor_value > 0.0f);


	if (!page->GetEntry("CurrentArmorValue", &current_armor_value))
	{
		current_armor_value = base_armor_value;
	}


	if (!page->GetEntry("ScaleSplashDamage", &scale_splash_damage))
	{
		scale_splash_damage = 1.0f;
	}

	int damage_level = NoArmorDamage;

	RegisteredClass::ClassID class_id = DefaultData->GetClassID();

	MString damage_object_name = page->GetName();

	int armor_type = DefaultArmorType;

	*stream << class_id;
	*stream << base_armor_value;
	*stream << current_armor_value;
	*stream << scale_splash_damage;
	*stream << damage_object_name;
	*stream << internal_damage_zone_id;
	*stream << armor_zone;
	*stream << damage_level;
	*stream << armor_type;
	*stream << max_armor_value;
	*stream << attached_to_zone;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DamageObject::Save(MemoryStream *stream)
{
	Check_Object(this);
	
	RegisteredClass::ClassID class_id = DefaultData->GetClassID();

	*stream << class_id;
	*stream << baseArmorValue;
	*stream << currentArmorValue;
	*stream << damageObjectName;
	*stream << internalDamageObjectID;
	*stream << armorZone;
	*stream << damageLevel;
	*stream << m_armorType;
	*stream << maxArmorValue;
	*stream << m_specialConnectedToZone;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DamageObject::~DamageObject()
{
}
// from here by jcem
Stuff::Scalar DamageObject::GetDamageModifier (int nIdx) const
{
	gosASSERT((0 <= nIdx) && (nIdx < DmgModifier_Count * 2));
	return m_aDamageModifier[nIdx];
}

void DamageObject::SetDamageModifier (int nIdx, Stuff::Scalar newvalue)
{
	gosASSERT((0 <= nIdx) && (nIdx < DmgModifier_Count * 2));
	m_aDamageModifier[nIdx] = newvalue;
}
// to here by jcem
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DamageObject::Initialize(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	parentEntity = entity;

	DetermineDamageLevel();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	DamageObject::DetermineDamageLevel()
{
	Check_Object(this);
	int damage_level = NoArmorDamage;
	Scalar damage_percentage;
	if (baseArmorValue == 0)
		damage_percentage = 0;
	else
		damage_percentage = currentArmorValue / 	baseArmorValue;
	if(damage_percentage < 0.01f)
	{
		if(internalDamageObject)
		{
			if(internalDamageObject->currentInternalDamage <= 0.0f)
			{
				return InternalDestroyedDamage;
			}
		}
		damage_level = ArmorDestroyedDamage;
	}
	else if(damage_percentage < 0.1f)
		damage_level = BadArmorDamage;
	else if(damage_percentage < 0.5f)
		damage_level = MediumArmorDamage;
	else if(damage_percentage < 0.75f)
		damage_level = PartialArmorDamage;

	return damage_level;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DamageObject::TakeDamage(Entity__TakeDamageMessage *message, Entity *entity_hit)
{
	Check_Object(this);
	Check_Object(parentEntity);

	Scalar internal_damage_taken = 0.0f;

// from here by jcem
	if (message->typeOfDamage == SplashDamageType)
		message->amountOfDamage *= m_aDamageModifier[DmgModifier_Splash * 2];
	else
// to here by jcem
	message->amountOfDamage *= m_aDamageModifier[DmgModifier_Default * 2];

// MSL 5.05 Advance Mode
	if (message->amountOfModifier)
	{
		message->amountOfDamage *= 0.5f;
	}

	if (message->amountOfDamage < 0.1f)
		message->amountOfDamage = 0.1f;

		
	parentEntity->ReactToHit(message,this);

	if (entity_hit != NULL)
	{
		entity_hit->ApplyVisualDamage(message, armorZone);
	}

    if (g_nMR == 2) {
		Stuff::Scalar amount_of_damage = message->amountOfDamage;

		if (message->typeOfDamage == SplashDamageType)
		{
			amount_of_damage *= scaleSplashDamage;
		}	


		if (GetCurrentDamageLevel() == Destroyed)
		{
			//internal_damage_taken = amount_of_damage;
			internalDamageObject->TakeDamage(amount_of_damage/*internal_damage_taken*/, message);
		}

        parentEntity->ReactToDamageTaken(amount_of_damage/*damage_actually_taken*/,message->inflictingEntityID,message->weaponID,(int)message->typeOfDamage);
        return;
    }

    // if we are the server deal the damage...

	// FUTURE UPGRADES!!!!!!
	// If we are an instant weapon (beam) on the server take the damage
	// Anything else gets the info stuck in a damage accumulator.
	// That accumulator gets sent now and again.


	if (Network::GetInstance()->AmIServer())
	{
		Stuff::Scalar amount_of_damage = message->amountOfDamage;

		//only apply this if there is armor left
// MSL 5.03 Damage vs Armor Type Modified
// MSL 5.04 Penalty reduced from .667 to .333
// MSL 5.05 Corrected Formulas and changed to 40/40 reduction/penalty
		if(currentArmorValue > 0)
		{
			if (m_armorType == ReflectiveArmor)
			{
				if(message->typeOfDamage == BeamDamageType)
				{
					amount_of_damage = amount_of_damage - (amount_of_damage * 0.4f);
				}
				if(message->typeOfDamage == ProjectileDamageType)
				{
					amount_of_damage = amount_of_damage + (amount_of_damage * 0.4f);
				}
				if(message->typeOfDamage == MissileDamageType)
				{
					amount_of_damage = amount_of_damage + (amount_of_damage * 0.4f);
				}
			}

			if (m_armorType == ReactiveArmor)
			{
				if(message->typeOfDamage == ProjectileDamageType)
				{
					amount_of_damage = amount_of_damage - (amount_of_damage * 0.4f);
				}
				if(message->typeOfDamage == MissileDamageType)
				{
					amount_of_damage = amount_of_damage - (amount_of_damage * 0.4f);
				}
				if(message->typeOfDamage == BeamDamageType)
				{
					amount_of_damage = amount_of_damage + (amount_of_damage * 0.4f);
				}
			}

			if (m_armorType == SolarianArmor)
			{
				if(message->typeOfDamage == ProjectileDamageType)
				{
					amount_of_damage = amount_of_damage - (amount_of_damage * 0.75f);
				}
				if(message->typeOfDamage == MissileDamageType)
				{
					amount_of_damage = amount_of_damage - (amount_of_damage * 0.75f);
				}
				if(message->typeOfDamage == BeamDamageType)
				{
					amount_of_damage = amount_of_damage - (amount_of_damage * 0.75f);
				}
			}
		}
	
		if(currentArmorValue == baseArmorValue)
		{
			internalDamageObject->m_firstHitTime = gos_GetElapsedTime() + 1.0f;
		}

		if (message->typeOfDamage == SplashDamageType)
		{
			amount_of_damage *= scaleSplashDamage;
		}

		Stuff::Scalar damage_actually_taken = amount_of_damage;

		if(currentArmorValue > 0.0f)
		{
			currentArmorValue -= amount_of_damage;
			if (m_HitPointPointer)
			{
				*m_HitPointPointer -= amount_of_damage;
			}
			if(currentArmorValue < 0.0f)
			{
				damage_actually_taken += currentArmorValue;

				internal_damage_taken = 0.0f - currentArmorValue;
				if (m_HitPointPointer)
				{
					*m_HitPointPointer += internal_damage_taken;			// will be removed in internal damage object
				}
				currentArmorValue = 0.0f;
			}
		}
		else
		{
			internal_damage_taken = amount_of_damage;
		}
		if (internal_damage_taken > 0.0f) 
		{
			Check_Object(internalDamageObject);
			damage_actually_taken += internalDamageObject->TakeDamage(internal_damage_taken, message);
		}

		parentEntity->ReactToDamageTaken(damage_actually_taken,message->inflictingEntityID,message->weaponID,(int)message->typeOfDamage);
	}
	else
	{
		Stuff::Scalar amount_of_damage = message->amountOfDamage;

		if (message->typeOfDamage == SplashDamageType)
		{
			amount_of_damage *= scaleSplashDamage;
		}	


		if (GetCurrentDamageLevel() == Destroyed)
		{
			internal_damage_taken = amount_of_damage;
			internalDamageObject->TakeDamage(internal_damage_taken, message);
		}
	}

    RefrehDamageState(); // jcem
}

void DamageObject::RefrehDamageState()
{
    // jcem - Recalc/Refresh current DamageLevel/damageArrayEntry/armorLevel/armorArrayEntry
    // this will be called after the modification of (currentArmorValue, internalDamageObject->currentInternalDamage...)
	damageLevel = DetermineDamageLevel();
	if(damageArrayEntry)
	{
		*damageArrayEntry = damageLevel;
	}
	
	
	Scalar damage_percentage;
	if(internalDamageObject)
		damage_percentage = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage);
	else
		damage_percentage = (currentArmorValue) / (baseArmorValue);
	armorLevel = damage_percentage;
	if (armorArrayEntry)
		*armorArrayEntry = damage_percentage;
}

void DamageObject::RepairDamage(Scalar fRepair)
{
    Stuff::Scalar fValue;

    if (internalDamageObject)
	{
        fValue = internalDamageObject->baseInternalDamage * fRepair;
        internalDamageObject->currentInternalDamage += fValue;
        if (internalDamageObject->baseInternalDamage < internalDamageObject->currentInternalDamage)
		{
            fValue = internalDamageObject->currentInternalDamage - internalDamageObject->baseInternalDamage;
            internalDamageObject->currentInternalDamage = internalDamageObject->baseInternalDamage;
        }
		else
		{
            fValue = 0.0;
        }
    }
	else
	{
        fValue = 0.0;
    }
    fValue += (baseArmorValue * fRepair);
    currentArmorValue += fValue;
    if (baseArmorValue < currentArmorValue)
        currentArmorValue = baseArmorValue;

    RefrehDamageState();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	DamageObject::ArmorZoneTextToAscii(const char *armor_name)
{	
	if(!_stricmp(armor_name, "NullZone"))
		return NullArmorZone;
	else if(!_stricmp(armor_name, "LeftLeg"))
		return LeftLegArmorZone;
	else if(!_stricmp(armor_name, "RightLeg"))
		return RightLegArmorZone;
	else if(!_stricmp(armor_name, "LeftArm"))
		return LeftArmArmorZone;
	else if(!_stricmp(armor_name, "RightArm"))
		return RightArmArmorZone;
	else if(!_stricmp(armor_name, "RightTorso"))
		return RightTorsoArmorZone;
	else if(!_stricmp(armor_name, "LeftTorso"))
		return LeftTorsoArmorZone;
	else if(!_stricmp(armor_name, "CenterTorso"))
		return CenterTorsoArmorZone;
	else if(!_stricmp(armor_name, "CenterRearTorso"))
		return CenterRearTorsoArmorZone;
	else if(!_stricmp(armor_name, "Head"))
		return HeadArmorZone;
	else if(!_stricmp(armor_name, "Special1"))
		return SpecialArmorZone1;
	else if(!_stricmp(armor_name, "Special2"))
		return SpecialArmorZone2;
	return DefaultArmorZone;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char *
	DamageObject::ArmorZoneAsciiToText(int armor_zone)
{	
	switch(armor_zone)
	{
	case NullArmorZone:
		return "Null";
	case LeftLegArmorZone:
		return "LeftLeg";
	case RightLegArmorZone:
		return "RightLeg";
	case LeftArmArmorZone:
		return "LeftArm";
	case RightArmArmorZone:
		return "RightArm";
	case RightTorsoArmorZone:
		return "RightTorso";
	case LeftTorsoArmorZone:
		return "LeftTorso";
	case CenterTorsoArmorZone:
		return "CenterTorso";
	case CenterRearTorsoArmorZone:
		return "CenterRearTorso";
	case HeadArmorZone:
		return "Head";
	case SpecialArmorZone1:
		return "Special1";
	case SpecialArmorZone2:
		return "Special2";
	}

	return "Default";
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DamageObject::SetType(int new_type)
{
	Check_Object(this);
// MSL 5.0 Armor
//	Verify(new_type >= FerroFiberusArmor);
	Verify(new_type >= StandardArmor);
	Verify(new_type < TypeCount);

	m_armorType = new_type;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DamageObject::AppendArmor(Scalar additive_armor)
{
	Check_Object(this);

	baseArmorValue += additive_armor;
	currentArmorValue += additive_armor;
	if (m_HitPointPointer)
	{
		*m_HitPointPointer += additive_armor;
	}

	Clamp(baseArmorValue, 1.0f, maxArmorValue);
	if (currentArmorValue > maxArmorValue)
	{
		if (m_HitPointPointer)
		{
			*m_HitPointPointer -= (currentArmorValue - maxArmorValue);
		}
	}
	Clamp(currentArmorValue, 1.0f, maxArmorValue);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	DamageObject::CanRepair()
{
	Check_Object(this);
	Check_Object(internalDamageObject);

	bool value = false;
	if((internalDamageObject->GetCurrentDamageLevel() != InternalDamageObject::Destroyed) &&
		(GetCurrentDamageLevel() != NoDamage))
	{
		value = true;
	}
	return value;
}

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

//#############################################################################
//###########################    InternalDamageObject   #######################
//#############################################################################

InternalDamageObject::ClassData*
	InternalDamageObject::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	InternalDamageObject::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			InternalDamageObjectClassID,
			"Adept::InternalDamageObject",
			Receiver::DefaultData,
			0,
			NULL
		);
	Check_Object(DefaultData);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
InternalDamageObject::InternalDamageObject(
	ClassData *class_data,
	MemoryStream *stream
):
	Receiver(class_data),
	m_sites(NULL, true),
	// The same sites were being returned for each call. The iterators
	// are now seperated into a weapon port iterator and and eject port
	// iterator. Added this change to Vengeance, too --CBT 1/10/2002
	m_weaponSiteIterator (&m_sites),
	m_ejectionSiteIterator (&m_sites)
{
	Check_Pointer(this);
	Check_Object(stream);

	//Need to stream stuff in here
	*stream >> baseInternalDamage;
	*stream >> currentInternalDamage;
	*stream >> parentEntityName;
	*stream >> damageMode;
	*stream >> damageZone;
	*stream >> damagePropagationZoneID;
	*stream >> m_internalType;
	*stream >> m_specialConnectedTo;
	MemoryStreamIO_Read(stream, &damageEffects);

// MSL 5.02 headshot
	if (damageMode == HeadShotDamageMode)
	{
		m_headShotFlag = true;
// MSL 5.05 Damage Mode
//		damageMode = DestructionDamageMode;
	}
	else
		m_headShotFlag = false;


// MSL 5.03 Gyro Hit
	if (damageMode == GyroHitDamageMode)
	{
		m_gyrohitFlag = true;
// MSL 5.05 Damage Mode
		damageMode = DestructionDamageMode;
	}
	else
		m_gyrohitFlag = false;

	currentDamageEffectResourceID = ResourceID::Null;
	damagePropagationObject = NULL;
	parentEntity = NULL;
	currentDamageEffect = NULL;
	m_HitPointPointer = NULL;

	m_firstHitTime = gos_GetElapsedTime();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void*
	InternalDamageObject::operator new(size_t size)
{
	/*
	gos_PushCurrentHeap(g_DamageObjectHeap);

	void *temp  = ::new(size_t);

	gos_PopCurrentHeap();

	return temp;
	*/
	return gos_Malloc(size,g_DamageObjectHeap);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	InternalDamageObject::Reuse()
{
	Check_Object(this);

	m_HitPointPointer = NULL;
	currentInternalDamage = baseInternalDamage;
	currentDamageEffectResourceID = ResourceID::Null;
	currentDamageEffect = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	InternalDamageObject::ConstructInternalDamageObjectStream(
		MemoryStream *stream,
		Page *page,
		RegisteredClass::ClassID class_id
	)	
{
	Check_Object(stream);
	Check_Object(page);

	int
		damage_zone;
	int
		damage_prop_zone = NullZone;

	const char *internal_zone = "";
	page->GetEntry("DamageZone", &internal_zone, true);
	
	damage_zone = InternalZoneTextToAscii(internal_zone);

	const char *zone_name;
	if(page->GetEntry("DamagePropagationZone",&zone_name))
		damage_prop_zone = InternalZoneTextToAscii(zone_name);

	int attached_to = NullZone;
	const char *attached_to_string;
	if(page->GetEntry("SpecialAttachedToZone", &attached_to_string))
		attached_to = InternalZoneTextToAscii(attached_to_string);

	const char *parent_name = "NullParent";
	page->GetEntry("ParentEntityName", &parent_name);
	MString parent_entity_name = parent_name;

	int
		damage_mode;

	const char *mode_name = "";
	page->GetEntry("DamageMode", &mode_name);

	damage_mode = DamageModeTextToAscii(mode_name);

	Stuff::Scalar
		base_internal_damage,
		current_internal_damage,
		base_armor_value,
		current_armor_value;

	page->GetEntry("BaseInternalDamage", &base_internal_damage, true);
	Verify(base_internal_damage > 0);

	if (!page->GetEntry("CurrentInternalDamage", &current_internal_damage))
	{
		current_internal_damage = base_internal_damage;
	}

	base_armor_value = 0.0f;
	page->GetEntry("BaseArmorValue", &base_armor_value);
	Verify(base_armor_value >= 0.0f);

	if (!page->GetEntry("CurrentArmorValue", &current_armor_value))
	{
		current_armor_value = base_armor_value;
	}

	//
	//---------------------------------------------
	//Create the array of Damage Effect ResourceIDs
	//---------------------------------------------
	//
	DynamicArrayOf<DamageEffectObject> damage_effects;
	ChainOf<Note*> *list = page->MakeNoteChain("DamageEffect");
	Check_Object(list);
	Page::NoteIterator notes(list);

	int entry_count = notes.GetSize();
	Verify(entry_count >= 0);

	damage_effects.SetLength(entry_count);

	Note *note;
	int i;

	//
	// For each entry, make entry in damage effect array
	//
	for (
		i = 0;
		(note = notes.ReadAndNext()) != NULL && i < entry_count;
		i++
	)
	{
		Check_Object(note);
		const char *entry_data;
		note->GetEntry(&entry_data);
		Check_Pointer(entry_data);			

		MString entry_string;
		MString effect_file_name;

		entry_string = entry_data;
		effect_file_name = entry_string.GetNthToken(0, ",");

		Check_Object(Tool::Instance);
		Check_Object(ResourceManager::Instance);
		Resource effect_resource;
		Tool::Instance->ConstructDataList(&effect_resource, effect_file_name);
		Check_Object(&effect_resource);
		Verify(effect_resource.DoesResourceExist());
		MString percent_armor_string;
		Scalar percent_armor;

		percent_armor_string = entry_string.GetNthToken(1, ",");
		sscanf(
			percent_armor_string,
			"%f ",
			&percent_armor
		);
		
		damage_effects[i].resourceID = effect_resource.GetResourceID();
		damage_effects[i].armorPercent = percent_armor;
	}
	Check_Object(list);
	delete list;

	int internal_type = DefaultInternalType;
	*stream << class_id;
	*stream << base_internal_damage;
	*stream << current_internal_damage;
	*stream << parent_entity_name;
	*stream << damage_mode;
	*stream << damage_zone;
	*stream << damage_prop_zone;
	*stream << internal_type;
	*stream << attached_to;
	MemoryStreamIO_Write(stream, &damage_effects);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	InternalDamageObject::Save(MemoryStream *stream, RegisteredClass::ClassID class_id)
{
	Check_Object(this);

	*stream << class_id;
	*stream << baseInternalDamage;
	*stream << currentInternalDamage;
	*stream << parentEntityName;
	*stream << damageMode;
	*stream << damageZone;
	*stream << damagePropagationZoneID;
	*stream << m_internalType;
	*stream << m_specialConnectedTo;
	MemoryStreamIO_Write(stream, &damageEffects);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
InternalDamageObject::~InternalDamageObject()
{
}

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

	parentEntity = entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	InternalDamageObject::TakeDamage(Scalar damage_taken, Entity__TakeDamageMessage *message)
{
	Check_Object(this);
	Check_Object(parentEntity);
	Stuff::Scalar damage_already_distributed = 0.0f;

	Scalar rv = 0;

    if (g_nMR == 2) {
		PlayDamageEffects(message);
		//parentEntity->ReactToInternalDamage(damageMode);
        return rv;
    }

	if (Network::GetInstance()->AmIServer())
	{
		if (currentInternalDamage > 0.0f)
		{
			rv = damage_taken;

			if((damageMode == DestructionDamageMode) &&
				(damageZone > NullZone) &&
				(damageZone < VehicleHullZone) &&
				(gos_GetElapsedTime() < m_firstHitTime) &&
				(message->typeOfDamage != HighExplosiveDamageType))
			{
				if (m_HitPointPointer)
				{
					*m_HitPointPointer -= damage_taken;
				}
				currentInternalDamage -= damage_taken;
				if (currentInternalDamage < 0.9f)
				{
					if (m_HitPointPointer)
					{
						*m_HitPointPointer += (0.9f - currentInternalDamage);
					}
				}
				Min_Clamp(currentInternalDamage, 0.09f);
			}
			else
			{
				//
				// If we've already used up some of the damage here, we don't want to propagate it
				// below. Note that since we check for currentInternalDamage being <= 0 below we know that
				// we only get there if damage_take > currentInternalDamage so it is safe to assume
				// currentInternalDamage is the amount of damage already taken.
				//
				damage_already_distributed = currentInternalDamage;
				if (m_HitPointPointer)
				{
					*m_HitPointPointer -= damage_taken;
				}
				currentInternalDamage -= damage_taken;
			}

			PlayDamageEffects(message);
// MSL 5.02 Head Shot
// MSL 5.03 Gyro Hit
			if (m_headShotFlag == true)
					parentEntity->ReactToInternalDamage(HeadShotDamageMode);
			else
				if (m_gyrohitFlag == true)
					parentEntity->ReactToInternalDamage(GyroHitDamageMode);
			else
				parentEntity->ReactToInternalDamage(damageMode);
			InflictCriticalHit();
		}
		if(currentInternalDamage <= 0.0f)
		{
			if(damagePropagationObject)
			{
				Check_Object(damagePropagationObject);
				Scalar prop_amount = 0.0f;
// MSL 5.05 Used to determine percentage of damage propagated.
				switch(m_internalType)
				{
				case StandardInternal:
					prop_amount = 0.85f;
					break;
				case EndoSteelInternal:
					prop_amount = 1.0f;
					break;
				}
				
// MSL 5.05 If Advancemode then modify propagation damage
				if (message->amountOfModifier)
				{
					prop_amount *= 0.5f;
				}
				//
				// If two items point to each other they can cause recursion that we don't want
				// when both become destroyed. The legs pointing at each other causes this
				//
				// This solution won't work if they ever put in second order propagation, ie...
				//    lleg -> rleg (if destroyed) -> centertorso, or lleg->ltorso->ctorso
				// The first case isn't supported by the code, and the second won't work once the
				// ltorso is destroyed. The damage won't get to the center torso

				if (damagePropagationObject->currentInternalDamage > 0.0f)
				{
					rv += damagePropagationObject->TakeDamage((Scalar)((damage_taken - damage_already_distributed) * prop_amount), message);
				}
			}
			if(!parentEntity->IsDestroyed())
				parentEntity->ReactToDestruction(damageMode, message->typeOfDamage);
		}
	}
	else
	{
		// create the effects
		PlayDamageEffects(message);
// MSL 5.02 Head Shot
// MSL 5.03 Gyro Hit
			if (m_headShotFlag == true)
					parentEntity->ReactToInternalDamage(HeadShotDamageMode);
			else
				if (m_gyrohitFlag == true)
					parentEntity->ReactToInternalDamage(GyroHitDamageMode);
			else
				parentEntity->ReactToInternalDamage(damageMode);
	}

	return (rv);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	InternalDamageObject::PlayDamageEffects(Entity__TakeDamageMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	
	if (message->quickDamage)			// boardgame mode hit
		return;

	ResourceID want_to_play_effect;
	want_to_play_effect = GetDamageEffectID();
	if((currentDamageEffectResourceID == want_to_play_effect) ||
		(want_to_play_effect == ResourceID::Null))
	{
		return;
	}

	currentDamageEffectResourceID = want_to_play_effect;

	ClassID class_ID;
	class_ID = Entity::GetClassIDFromDataListID(currentDamageEffectResourceID);
	Verify(class_ID != NullClassID);

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

	Entity *entity;
	ReplicatorID effect_id = Connection::Hermit->GetNextReplicatorID();
	entity = Entity::CreateEntity(&stream, &effect_id, false);
	Check_Object(entity);
	currentDamageEffect =
		Cast_Object(Effect *, entity);
	Check_Object(Map::GetInstance());
	Map::GetInstance()->AddChild(entity);
	LinearMatrix4D effect_position = LinearMatrix4D::Identity;
//	effect_position.AlignAxis(message->normal, Y_Axis, X_Axis, Z_Axis);
	effect_position.BuildTranslation(message->hitOffset);
	LinearMatrix4D effect_offset = LinearMatrix4D::Identity;
	Point3D effect_offset_position;
	Check_Object(parentEntity);
	effect_offset_position.MultiplyByInverse(message->hitOffset, parentEntity->GetLocalToWorld());
	effect_offset.BuildTranslation(effect_offset_position);
	currentDamageEffect->SetFollowEntity(parentEntity);
	currentDamageEffect->effectOffset = effect_offset;
	entity->SetNewLocalToParent(effect_position);
	entity->SyncMatrices(true);
}

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

ResourceID
	InternalDamageObject::GetDamageEffectID()
{
	Check_Object(this);
	Scalar closest_armor = 1.0f;
	ResourceID return_id;

	return_id = ResourceID::Null;
	
	for (int i = 0; i < damageEffects.GetLength(); i++)
	{
		Scalar damage_num = damageEffects[i].armorPercent;
		if ((damage_num >= (currentInternalDamage / baseInternalDamage))
			&& (damage_num <= closest_armor))
		{
			closest_armor = damage_num;	
			return_id = damageEffects[i].resourceID;
		}
	}
	return return_id;
}


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

int InternalDamageObject::GetCurrentDamageLevel()
{
	if (currentInternalDamage == baseInternalDamage)
	{
		return NoDamage;
	}
	if (currentInternalDamage <= 0.0f)
	{
		return Destroyed;
	}
	else
		return Damaged;
}

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

void InternalDamageObject::SetCurrentDamageLevel(int level)
{

	if (level != GetCurrentDamageLevel())
	{
		switch(level)
		{
		case NoDamage:
			if (m_HitPointPointer)
			{
				*m_HitPointPointer += (baseInternalDamage - currentInternalDamage);
			}
			currentInternalDamage = baseInternalDamage;
			break;

		case Damaged:
			if (m_HitPointPointer)
			{
				*m_HitPointPointer += ((baseInternalDamage*0.5f) - currentInternalDamage);
			}
			currentInternalDamage = baseInternalDamage*0.5f;
			break;

		case Destroyed:
			if (m_HitPointPointer)
			{
				*m_HitPointPointer += (0-currentInternalDamage);
			}
			currentInternalDamage = 0.0f;
			//parentEntity->ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0);
			parentEntity->ReactToDestruction(damageMode, 0);
			break;
		}
	}

	
}

// The same sites were being returned for each call. The iterators
// are now seperated into a weapon port iterator and and eject port
// iterator. Added this change to Vengeance, too --CBT 1/10/2002
Site*
	InternalDamageObject::GetNextWeaponSite()
{
	Check_Object(this);
	
	Site *site = m_weaponSiteIterator.GetCurrent();
	if(!site)
	{
		m_weaponSiteIterator.First();
		site = m_weaponSiteIterator.GetCurrent();
	}
	else
	{	//you have previously retuned a site, begin on the next
		m_weaponSiteIterator.Next();
		if(!m_weaponSiteIterator.GetCurrent())
			m_weaponSiteIterator.First();
		site = m_weaponSiteIterator.GetCurrent();
	}

	bool found = false;
	int num = 0;
	int total = m_sites.GetSize();

	while((site) && (!found) && (num < total))
	{
		Check_Object(site);
		char *name_lower = _strlwr((char *)site->GetName());
		// CBT 1/24/02 - the mispelled string "missle" is included due to historical reasons
		if((strstr(name_lower, "gun")) || (strstr(name_lower, "missile"))
			|| (strstr(name_lower, "missle")) || (strstr(name_lower, "torsoport")))
			found = true;
		else
		{
			m_weaponSiteIterator.Next();
			if(!m_weaponSiteIterator.GetCurrent())
				m_weaponSiteIterator.First();
			site = m_weaponSiteIterator.GetCurrent();
			num ++;
		}
	}

	if(found)
		return site;

	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Site*
	InternalDamageObject::GetNextEjectSite()
{
	Site *site = m_ejectionSiteIterator.GetCurrent();
	if(!site)
	{
		m_ejectionSiteIterator.First();
		site = m_ejectionSiteIterator.GetCurrent();
	}
	else
	{	//you have previously returned a site, begin on the next
		m_ejectionSiteIterator.Next();
		if(!m_ejectionSiteIterator.GetCurrent())
			m_ejectionSiteIterator.First();
		site = m_ejectionSiteIterator.GetCurrent();
	}

	bool found = false;
	int num = 0;
	int total = m_sites.GetSize();

	while((site) && (!found) && (num < total))
	{
		Check_Object(site);
		char *name_lower = _strlwr((char *)site->GetName());
		if(strstr(name_lower, "shellport"))
			found = true;
		else
		{
			m_ejectionSiteIterator.Next();
			if(!m_ejectionSiteIterator.GetCurrent())
				m_ejectionSiteIterator.First();
			site = m_ejectionSiteIterator.GetCurrent();
			num ++;
		}
	}

	if(found)
		return site;

	return NULL;
}

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

void InternalDamageObject::SilentDestruction()
{
	if (m_HitPointPointer)
	{
		*m_HitPointPointer += (0-currentInternalDamage);
	}
	currentInternalDamage = 0.0f;
}

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

int DamageObject::GetCurrentDamageLevel()
{
	if (currentArmorValue == baseArmorValue)
	{
		return NoDamage;
	}
	if (currentArmorValue <= 0.0f)
	{
		return Destroyed;
	}
	else
		return Damaged;
}

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

void DamageObject::SetCurrentDamageLevel(int level)
{
	if (level != GetCurrentDamageLevel())
	{
		switch(level)
		{
		case NoDamage:
			if (m_HitPointPointer)
			{
				*m_HitPointPointer += (baseArmorValue - currentArmorValue);
			}
			currentArmorValue = baseArmorValue;
			break;

		case Damaged:
			if (m_HitPointPointer)
			{
				*m_HitPointPointer += ((baseArmorValue*0.5f) - currentArmorValue);
			}
			currentArmorValue = baseArmorValue*0.5f;
			break;

		case Destroyed:
			if (m_HitPointPointer)
			{
				*m_HitPointPointer += (0 - currentArmorValue);
			}
			currentArmorValue = 0.0f;
			break;
		}

	}

	damageLevel = DetermineDamageLevel();
	if(damageArrayEntry)
	{
		*damageArrayEntry = damageLevel;
	}

	Scalar damage_percentage;
	if(internalDamageObject)
		damage_percentage = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage);
	else
		damage_percentage = (currentArmorValue) / (baseArmorValue);
	armorLevel = damage_percentage;
	if (armorArrayEntry)
		*armorArrayEntry = damage_percentage;
}

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

void DamageObject::RefreshCurrentDamageLevel()
{
	damageLevel = DetermineDamageLevel();
	if(damageArrayEntry)
	{
		*damageArrayEntry = damageLevel;
	}

	Scalar damage_percentage;
	if(internalDamageObject)
		damage_percentage = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage);
	else
		damage_percentage = (currentArmorValue) / (baseArmorValue);
	armorLevel = damage_percentage;
	if (armorArrayEntry)
		*armorArrayEntry = damage_percentage;
}

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

void DamageObject::SilentDestruction()
{
	if (m_HitPointPointer)
	{
		*m_HitPointPointer += (0 - currentArmorValue);
	}
	currentArmorValue = 0.0f;
}

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

Scalar DamageObject::GetHighResDamageLevel()
{
	Scalar damage_level = currentArmorValue/baseArmorValue;
	Clamp(damage_level, 0.0f, 1.0f);
	return damage_level;
}

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

void DamageObject::SetHighResDamageLevel(Scalar damage_percentage)
{
	if (m_HitPointPointer)
	{
		*m_HitPointPointer += ((baseArmorValue * damage_percentage) - currentArmorValue);
	}

	currentArmorValue = baseArmorValue * damage_percentage;
	
	damageLevel = DetermineDamageLevel();
	if(damageArrayEntry)
	{
		*damageArrayEntry = damageLevel;
	}

	Scalar fred;
	if(internalDamageObject)
		fred = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage);
	else
		fred = (currentArmorValue) / (baseArmorValue);
	armorLevel = fred;
	if (armorArrayEntry)
		*armorArrayEntry = fred;
}

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

Scalar InternalDamageObject::GetHighResDamageLevel()
{
	Scalar damage_level = currentInternalDamage/baseInternalDamage;
	Clamp(damage_level, 0.0f, 1.0f);
	return damage_level;
}

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

void InternalDamageObject::SetHighResDamageLevel(Scalar damage_percentage)
{
	
	if (GetCurrentDamageLevel() != Destroyed)
	{
		if (m_HitPointPointer)
		{
			*m_HitPointPointer += ((baseInternalDamage*damage_percentage)-currentInternalDamage);
		}
		currentInternalDamage = baseInternalDamage*damage_percentage;

		if (damage_percentage == 0.0f)
		{
			if (m_HitPointPointer)
			{
				*m_HitPointPointer += (0-currentInternalDamage);
			}
			currentInternalDamage = 0.0f;
			//parentEntity->ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0);
			parentEntity->ReactToDestruction(damageMode, 0);
		}

	}



}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	InternalDamageObject::DamageModeTextToAscii(const char *mode_name)
{
	
	if(!_stricmp(mode_name, "GimpLeftDamageMode"))
		return GimpLeftDamageMode;
	else if(!_stricmp(mode_name, "GimpRightDamageMode"))
		return GimpRightDamageMode;
	else if(!_stricmp(mode_name, "DestructionDamageMode"))
		return DestructionDamageMode;
// MSL 5.02 headshot
	else if (!_stricmp(mode_name, "HeadShotDamageMode"))
		return HeadShotDamageMode;
// MSL 5.03 Gyro Hit
	else if (!_stricmp(mode_name, "GyroHitDamageMode"))
		return GyroHitDamageMode;
	else if(!_stricmp(mode_name, "DetachableDamageMode"))
		return DetachableDamageMode;
// MSL 5.02 Engine
	else if(!_stricmp(mode_name, "EngineDamageMode"))
		return EngineDamageMode;
// MSL 5.05 Torso
	else if(!_stricmp(mode_name, "TorsoLeftDamageMode"))
		return TorsoLeftDamageMode;
	else if(!_stricmp(mode_name, "TorsoRightDamageMode"))
		return TorsoRightDamageMode;
	else if(!_stricmp(mode_name, "ArmLeftDamageMode"))
		return ArmLeftDamageMode;
	else if(!_stricmp(mode_name, "ArmRightDamageMode"))
		return ArmRightDamageMode;
		
	return GeneralDamageMode;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char *
	InternalDamageObject::DamageModeAsciiToText(int damage_mode)
{
	switch(damage_mode)
	{
	case GimpLeftDamageMode:
		return "GimpLeftDamageMode";
	case GimpRightDamageMode:
		return "GimpRightDamageMode";
	case DestructionDamageMode:
		return "DestructionDamageMode";
// MSL 5.02 headshot
	case HeadShotDamageMode:
		return "HeadShotDamageMode";
// MSL 5.03 Gyro Hit
	case GyroHitDamageMode:
		return "GyroHitDamageMode";
	case DetachableDamageMode:
		return "DetachableDamageMode";
	case EngineDamageMode:
		return "EngineDamageMode";
// MSL 5.05 Torso
	case TorsoLeftDamageMode:
		return "TorsoLeftDamageMode";
	case TorsoRightDamageMode:
		return "TorsoRightDamageMode";
	case ArmLeftDamageMode:
		return "ArmLeftDamageMode";
	case ArmRightDamageMode:
		return "ArmRightDamageMode";
	}
	return "GeneralDamageMode";
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	InternalDamageObject::InternalZoneTextToAscii(const char *mode_name)
{	
	if(!_stricmp(mode_name, "NullZone"))
		return NullZone;
	else if(!_stricmp(mode_name, "LeftLeg"))
		return LeftLegZone;
	else if(!_stricmp(mode_name, "RightLeg"))
		return RightLegZone;
	else if(!_stricmp(mode_name, "LeftArm"))
		return LeftArmZone;
	else if(!_stricmp(mode_name, "RightArm"))
		return RightArmZone;
	else if(!_stricmp(mode_name, "RightTorso"))
		return RightTorsoZone;
	else if(!_stricmp(mode_name, "LeftTorso"))
		return LeftTorsoZone;
	else if(!_stricmp(mode_name, "CenterTorso"))
		return CenterTorsoZone;
	else if(!_stricmp(mode_name, "Head"))
		return HeadZone;
	else if(!_stricmp(mode_name, "Special1"))
		return SpecialZone1;
	else if(!_stricmp(mode_name, "Special2"))
		return SpecialZone2;
	else if(!_stricmp(mode_name, "VehicleHull"))
		return VehicleHullZone;
	else if(!_stricmp(mode_name, "VehicleWeapon"))
		return VehicleWeaponZone;
	else if(!_stricmp(mode_name, "VehicleSpecial"))
		return VehicleSpecialZone;

	return DefaultZone;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char *
	InternalDamageObject::InternalZoneAsciiToText(int internal_zone)
{	
	switch(internal_zone)
	{
	case NullZone:
		return "Null";
	case LeftLegZone:
		return "LeftLeg";
	case RightLegZone:
		return "RightLeg";
	case LeftArmZone:
		return "LeftArm";
	case RightArmZone:
		return "RightArm";
	case RightTorsoZone:
		return "RightTorso";
	case LeftTorsoZone:
		return "LeftTorso";
	case CenterTorsoZone:
		return "CenterTorso";
	case HeadZone:
		return "Head";
	case SpecialZone1:
		return "Special1";
	case SpecialZone2:
		return "Special2";
	case VehicleHullZone:
		return "VehicleHull";
	case VehicleWeaponZone:
		return "VehicleWeapon";
	case VehicleSpecialZone:
		return "VehicleSpecial";
	}

	return "Default";
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	InternalDamageObject::SetType(int new_type)
{
	Check_Object(this);
	Verify(new_type >= StandardInternal);
	Verify(new_type < TypeCount);

	m_internalType = new_type;
}

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

void __cdecl Adept::AdeptDamageSecurityCheckStop(void)
{
	_asm
	{
		nop
	}	
}
