#include "MW4Headers.hpp"

#include "AI_Weapons.hpp"

#include "BeamWeapon.hpp"
#include "ProjectileWeapon.hpp"
#include "MWObject.hpp"
#include "MWDamageObject.hpp"
#include <Adept\CollisionGrid.hpp>
#include "aiutils.hpp"
#include "bombastweapon.hpp"
#include <Adept\Site.hpp>
#include "SubsystemClassData.hpp"
#include <Adept\Map.hpp>
#include "Sensor.hpp"
#include <Adept\CollisionVolume.hpp>

using namespace MW4AI;



const Stuff::Scalar friendly_fire_stretch_distance = 10.0f;

inline bool WeaponReadyToFire(MechWarrior4::Weapon& weapon, Stuff::Time min_delay, Stuff::Time max_delay)
{
	Verify(min_delay >= 0);
	Verify(max_delay >= min_delay);

	Stuff::Time delay = ((max_delay - min_delay) * weapon.GetAIWaitValue()) + min_delay;

	return (gos_GetElapsedTime() >= weapon.GetLastTimeReadyToFire() + delay);
}

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

void MW4AI::SetWeaponsToMaxWaitValue(MechWarrior4::MWObject& object)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;

	while ((weapon = i.ReadAndNext()) != 0)
	{
		weapon->SetMaxWaitValue();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GetMaxWeaponDistance(): gets the maximum distance of a weapon
//

Stuff::Scalar MW4AI::GetMaxWeaponDistance(MechWarrior4::Weapon& weapon)
{
	if (weapon.IsDerivedFrom(MechWarrior4::BeamWeapon::DefaultData) == true)
	{
		MechWarrior4::BeamWeapon* beam = Cast_Object(MechWarrior4::BeamWeapon*,&weapon);
		return (beam->GetGameModel()->maxDistance);
	}

	if (weapon.IsDerivedFrom(MechWarrior4::ProjectileWeapon::DefaultData) == true)
	{
		MechWarrior4::ProjectileWeapon* projectile = Cast_Object(MechWarrior4::ProjectileWeapon*,&weapon);
		return (projectile->GetGameModel()->maxDistance);
	}

	Verify(!"Cannot get max range: weapon type not found.");
	return (0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GetMaxWeaponDistance(): gets the maximum distance of any of an object's weapons
//

Stuff::Scalar MW4AI::GetMaxWeaponDistance(MechWarrior4::MWObject& object)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;

	Stuff::Scalar max = 0;

	while ((weapon = i.ReadAndNext()) != 0)
	{
		Check_Object(weapon);
		Stuff::Scalar this_range = GetMaxWeaponDistance(*weapon);

		if (this_range > max)
		{
			max = this_range;
		}
	}

	return (max);
}

// MSL 5.03 Min Distance (Disabled)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GetMaxWeaponDistance(): gets the maximum distance of a weapon
//

//Stuff::Scalar MW4AI::GetMinWeaponDistance(MechWarrior4::Weapon& weapon)
//{
//	if (weapon.IsDerivedFrom(MechWarrior4::BeamWeapon::DefaultData) == true)
//	{
//		MechWarrior4::BeamWeapon* beam = Cast_Object(MechWarrior4::BeamWeapon*,&weapon);
//		return (beam->GetGameModel()->minDistance);
//	}

//	if (weapon.IsDerivedFrom(MechWarrior4::ProjectileWeapon::DefaultData) == true)
//	{
//		MechWarrior4::ProjectileWeapon* projectile = Cast_Object(MechWarrior4::ProjectileWeapon*,&weapon);
//		return (projectile->GetGameModel()->minDistance);
//	}

//	Verify(!"Cannot get min range: weapon type not found.");
//	return (0);
//}  

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GetMaxWeaponDistance(): gets the maximum distance of any of an object's weapons
//

Stuff::Scalar MW4AI::GetMinWeaponDistance(MechWarrior4::MWObject& object)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;

	weapon = i.ReadAndNext();
	if (weapon == 0)
	{
		return (0);
	}
	Check_Object(weapon);

	Stuff::Scalar min = GetMaxWeaponDistance(*weapon);

	while ((weapon = i.ReadAndNext()) != 0)
	{
		Check_Object(weapon);
		Stuff::Scalar this_range = GetMaxWeaponDistance(*weapon);

		if (this_range < min)
		{
			min = this_range;
		}
	}

	return (min);
}

unsigned int MW4AI::NumWeaponsReady(MechWarrior4::MWObject& vehicle,
									Stuff::Time min_delay,
									Stuff::Time max_delay)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(vehicle.weaponChain));
	MechWarrior4::Weapon* weapon = 0;

	unsigned int rv = 0;

	while ((weapon = i.ReadAndNext()) != 0)
	{
		Check_Object(weapon);
		if (WeaponReadyToFire(*weapon,min_delay,max_delay) == true)
		{
			++rv;
		}
	}

	return (rv);
}

unsigned int MW4AI::NumWeapons(MechWarrior4::MWObject& vehicle)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(vehicle.weaponChain));
	unsigned int rv = 0;

	while (i.ReadAndNext() != 0)							// here's a place where the STL would have made this SO much easier ...
	{
		++rv;
	}

	return (rv);
}

bool MW4AI::AnyWeaponReady(MechWarrior4::MWObject& object, Stuff::Time min_delay, Stuff::Time max_delay)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;
	while ((weapon = i.ReadAndNext()) != 0)
	{
		if (WeaponReadyToFire(*weapon,min_delay,max_delay) == true)
		{
			return (true);
		}
	}

	return (false);
}

bool MW4AI::AnyWeaponNotReady(MechWarrior4::MWObject& object, Stuff::Time min_delay, Stuff::Time max_delay)
{
	return (NumWeaponsReady(object,min_delay,max_delay) < NumWeapons(object));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// WeaponCanFire(): indicates whether the given weapon can fire
//

bool MW4AI::WeaponCanFire(MechWarrior4::Weapon& weapon,
						  Stuff::Scalar line_length,
						  Stuff::Time min_delay,
						  Stuff::Time max_delay)
{
	if ((weapon.executionState == 0) ||
		(weapon.executionState->GetState() == MechWarrior4::Weapon::ExecutionStateEngine::DestroyedState))
	{
		return (false);
	}

	if (weapon.ReadyToFire() == false)
	{
		return (false);
	}

	if (WeaponReadyToFire(weapon,min_delay,max_delay) == false)
	{
		return (false);
	}

	if (line_length != 0)
	{
// MSL 5.03 Min Distance
		if (line_length > GetMaxWeaponDistance(weapon))
//		if ((line_length > GetMaxWeaponDistance(weapon)) || (line_length < GetMinWeaponDistance(weapon)+1))
		{
			return (false);
		}
	}

	if (weapon.IsDerivedFrom(BombastWeapon::DefaultData) == true)
	{
		BombastWeapon* bombast = Cast_Object(BombastWeapon*,&weapon);

		if (bombast->ReadyToFire() == true)
		{
			return (true);
		}

		if ((bombast->executionState->GetState() == BombastWeapon::ExecutionStateEngine::ChargingState) &&
			(bombast->percentCharged > 0.9f))
		{
			return (true);
		}

		return (false);
	}

	return (true);
}

bool MW4AI::GetWeaponsThatCanFire(std::vector<Weapon *>& weapons,
								  bool can_spend_ammo,
								  Stuff::Time min_delay,
								  Stuff::Time max_delay)
{
	bool any_can_fire(false);
	{for (std::vector<Weapon*>::iterator i = weapons.begin();
		  i != weapons.end();
		  ++i)
	{
		if ((*i) == 0)
		{
			continue;
		}

		if ((*i)->ReadyToFire() == false)
		{
			*i = 0;
			continue;
		}

		if (((*i)->executionState == 0) ||
			((*i)->executionState->GetState() == MechWarrior4::Weapon::ExecutionStateEngine::DestroyedState))
		{
			*i = 0;
			continue;
		}

		if (WeaponReadyToFire(**i,min_delay,max_delay) == false)
		{
			*i = 0;
			continue;
		}

		if ((can_spend_ammo == false) &&
			((*i)->GetAmmoCount() != -1))
		{
			*i = 0;
			continue;
		}

		any_can_fire = true;
	}}

	return (any_can_fire);
}

bool MW4AI::GetWeaponsThatCanFireAt(std::vector<Weapon *>& weapons,
									const Stuff::Point3D& aim)
{
	bool any_can_fire(false);

	{for (std::vector<Weapon*>::iterator i = weapons.begin();
		  i != weapons.end();
		  ++i)
	{
		if ((*i) != 0)
		{
			if (((*i)->sitePointer != 0) &&
				(GetApproximateLength(aim,(Stuff::Point3D)(*i)->sitePointer->GetLocalToWorld()) >
				GetMaxWeaponDistance(**i)))
			{
				*i = 0;
				continue;
			}

			if ((*i)->IsDerivedFrom(BombastWeapon::DefaultData) == true)
			{
				BombastWeapon* bombast = Cast_Object(BombastWeapon*,*i);

				if (bombast->ReadyToFire() == true)
				{
					any_can_fire = true;
				}
				else
				{
					if ((bombast->executionState->GetState() == BombastWeapon::ExecutionStateEngine::ChargingState) &&
						(bombast->percentCharged > 0.9f))
					{
						any_can_fire = true;
					}
					else
					{
						*i = 0;
					}
				}

				continue;
			}

			any_can_fire = true;
		}
	}}

	return (any_can_fire);
}

bool MW4AI::GetWeaponsThatCanFireFrom(std::vector<Weapon *>& weapons,
									  FireSource fire_source)
{
	if (fire_source == FIRE_FROM_ANYWHERE)
	{
		return (true);
	}

	bool any_can_fire(false);
	{for (std::vector<Weapon*>::iterator i = weapons.begin();
		  i != weapons.end();
		  ++i)
	{
		if ((*i) != 0)
		{
			if (WeaponIsArmWeapon(**i,(fire_source == FIRE_FROM_LEFT_ARM)) == false)
			{
				*i = 0;
				continue;
			}

			any_can_fire = true;
		}
	}}

	return (any_can_fire);
}

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

bool MW4AI::MightHitFriendlies(const Stuff::Line3D& line,
							   Stuff::Scalar line_length,
							   MechWarrior4::MWObject& shooter,
							   MechWarrior4::MWObject* ignore)
{
	if (shooter.GetSensor() == 0)
	{
		return (false);
	}

	const MechWarrior4::Sensor::sensorDataArray& sensed_data = shooter.GetSensor()->RefreshAndGetSensorData();

	{for (int i = 0;
		  i < shooter.GetSensor()->numberOfContacts;
		  ++i)
	{
		MechWarrior4::SensorData* d = sensed_data[i];

		if (d != 0)
		{
			Adept::Entity* current = d->object.GetCurrent();

			if ((current != 0) &&
				(current != ignore) &&
				(current->objectID != shooter.objectID))
			{
				bool should_avoid_hitting = false;
				if (current->GetAlignment() == shooter.GetAlignment())
				{
					should_avoid_hitting = true;
				}
				else
				{
					if (current->IsDerivedFrom(MWObject::DefaultData) == true)
					{
						MWObject* mwobject = Cast_Object(MWObject*,current);
						if (mwobject->GetTargetDesirability() == MWObject::desirability_never)
						{
							should_avoid_hitting = true;
						}
					}
				}

				if (should_avoid_hitting == true)
				{
					MWObject* mwobject = Cast_Object(MWObject*,current);

					Scalar radius = friendly_fire_stretch_distance;
					CollisionVolume* cv = mwobject->GetSolidVolume();

					if (cv != 0)
					{
						radius += cv->m_worldSpaceBounds.sphereRadius;
					}

					Stuff::Line3D line2(line);

					if (line_length != line2.m_length)
					{
						line2.m_length = line_length;
					}

					if (LinePenetrates(line2,mwobject->GetLineOfSight(),radius) == true)
					{
						Stuff::LinearMatrix4D los;
						shooter.GetLineOfSight(los);

						if (MatrixFacesPoint(los,(Stuff::Point3D)mwobject->GetLineOfSight(),90.0f * Radians_Per_Degree) == true)
						{
							return (true);
						}
					}
				}
			}
		}
	}}

	return (false);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SortedChainOfComponentsToVector(): converts a component_chain to a valid component_vector
//
/*
void SortedChainOfComponentsToVector(component_chain& chain, component_vector& v)
{
	const int first_component = Adept::DamageObject::LeftLegArmorZone;
	const int last_component = Adept::DamageObject::HeadArmorZone;

	{for (int i = first_component;
		  i <= last_component;
		  ++i)
	{
		Adept::DamageObject* damage_object = chain.Find(i);

		if ((damage_object != 0) &&
			(damage_object->parentEntity != 0))
		{
			v.push_back(damage_object);
		}
	}}
}
*/

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NormalizedDamage(): returns a normalized current damage value for the given damage object, where 1=undamaged and 0=destroyed
//
inline Stuff::Scalar NormalizedDamage(const Adept::DamageObject& damage_object)
{
	if (damage_object.baseArmorValue == 0)
	{
		return (0);
	}

	if (damage_object.currentArmorValue >= damage_object.baseArmorValue)
	{
		return (1);
	}

	return (damage_object.currentArmorValue / damage_object.baseArmorValue);
}

class DamageObjectEvaluator
{
  public:
	virtual Stuff::Scalar Evaluate(const Adept::DamageObject& damage_object) const = 0;
};

class DamageObjectEvaluator_MostDamaged
	: public DamageObjectEvaluator
{
  public:
	Stuff::Scalar Evaluate(const Adept::DamageObject& damage_object) const
	{
		return (1 - NormalizedDamage(damage_object));
	}
};

class DamageObjectEvaluator_LeastDamaged
	: public DamageObjectEvaluator
{
  public:
	Stuff::Scalar Evaluate(const Adept::DamageObject& damage_object) const
	{
		return (NormalizedDamage(damage_object));
	}
};

inline Adept::DamageObject* GetBestDamageObject(const component_vector& v, const DamageObjectEvaluator& evaluator)
{
	if (v.size() == 0)
	{
		return (0);
	}

	component_vector best_fit_list;

	{for (component_vector::const_iterator i = v.begin();
		  i != v.end();
		  ++i)
	{
		if (best_fit_list.size() == 0)
		{
			best_fit_list.push_back(*i);
		}
		else
		{
			Stuff::Scalar my_score = evaluator.Evaluate(**i);
			Stuff::Scalar best_fit_list_score = evaluator.Evaluate(**(best_fit_list.begin()));

			if (my_score > best_fit_list_score)
			{
				best_fit_list.clear();
			}

			if (my_score >= best_fit_list_score)
			{
				best_fit_list.push_back(*i);
			}
		}
	}}

	if (best_fit_list.size() > 0)
	{
		return (best_fit_list[gos_rand() % best_fit_list.size()]);
	}

	return (0);
}

	Adept::DamageObject* FindComponent(const component_vector& components, int zone)
	{
		{for (component_vector::const_iterator i = components.begin();
			  i != components.end();
			  ++i)
		{
			if ((*i)->armorZone == zone)
			{
				return (*i);
			}
		}}

		return (0);
	}

	Adept::DamageObject* FindMostArmedComponent(MechWarrior4::MWObject& who, const component_vector& components)
	{
		int battle_values[5];

		{for (int i = 0; i < 5; ++i)
		{
			battle_values[i] = 0;
		}}

		{
			Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(who.weaponChain));
			MechWarrior4::Weapon* weapon = 0;

			while ((weapon = i.ReadAndNext()) != 0)
			{
				if ((weapon->GetParentVehicle() != 0) &&
					(weapon->IsDestroyed() == false) &&
					(weapon->GetGameModel() != 0))
				{
					MWInternalDamageObject *zone = weapon->GetParentVehicle()->internalDamageObjects.Find(weapon->internalLocation);

					if (zone != 0)
					{
						int bv = weapon->GetGameModel()->battleValue;
						switch (zone->damageZone)
						{
							case MWInternalDamageObject::LeftArmZone:
							{
								battle_values[0] += bv;
								break;
							}
							case MWInternalDamageObject::LeftTorsoZone:
							{
								battle_values[1] += bv;
								break;
							}
							case MWInternalDamageObject::CenterTorsoZone:
							{
								battle_values[2] += bv;
								break;
							}
							case MWInternalDamageObject::RightTorsoZone:
							{
								battle_values[3] += bv;
								break;
							}
							case MWInternalDamageObject::RightArmZone:
							{
								battle_values[4] += bv;
								break;
							}
						}
					}
				}
			}
		}

		DamageObject* best_object = 0;
		int iters = 0;
		while ((best_object == 0) &&
			   (iters < 4))
		{
			int best_index = 0;
			{for (int i = 1;
				  i < 5;
				  ++i)
			{
				if (battle_values[i] > battle_values[best_index])
				{
					best_index = i;
				}
			}}

			int best_zone = Adept::DamageObject::CenterTorsoArmorZone;

			switch (best_index)
			{
				case 0:
				{
					best_zone = Adept::DamageObject::LeftArmArmorZone;
					break;
				}
				case 1:
				{
					best_zone = Adept::DamageObject::LeftTorsoArmorZone;
					break;
				}
				case 3:
				{
					best_zone = Adept::DamageObject::RightTorsoArmorZone;
					break;
				}
				case 4:
				{
					best_zone = Adept::DamageObject::RightArmArmorZone;
					break;
				}
			}

			DamageObject* best_object = FindComponent(components,best_zone);

			if (best_object != 0)
			{
				return (best_object);
			}

			battle_values[best_index] = -1;

			++iters;
		}

		return (FindComponent(components,Adept::DamageObject::CenterTorsoArmorZone));
	}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GetComponent_MostDamaged(): gets the most damaged component of a given list
//

Adept::DamageObject* MW4AI::GetComponent_MostDamaged(MechWarrior4::MWObject& who, const component_vector& v)
{
	Verify(v.size() > 0);

	bool any_damaged(false);
	{for (component_vector::const_iterator i = v.begin();
		  i != v.end();
		  ++i)
	{
		if ((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Damaged)
		{
			any_damaged = true;
		}

		switch ((*i)->armorZone)
		{
			case Adept::DamageObject::LeftLegArmorZone:
			{
				if ((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed)
				{
					{for (component_vector::const_iterator j = v.begin();
						  j != v.end();
						  ++j)
					{
						if ((*j)->armorZone == Adept::DamageObject::RightLegArmorZone)
						{
							return (*j);
						}
					}}
				}

				break;
			}

			case Adept::DamageObject::RightLegArmorZone:
			{
				if ((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed)
				{
					{for (component_vector::const_iterator j = v.begin();
						  j != v.end();
						  ++j)
					{
						if ((*j)->armorZone == Adept::DamageObject::LeftLegArmorZone)
						{
							return (*j);
						}
					}}
				}

				break;
			}

			case Adept::DamageObject::CenterTorsoArmorZone:
			{
				if (((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Damaged) ||
					((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed))
				{
					if ((*i)->armorLevel < 0.7f)
					{
						return (*i);
					}
				}
				break;
			}
		}
	}}

	if (any_damaged == false)
	{
		int best_zone;

		switch (gos_rand() & 0x07)
		{
			case 0:		{	best_zone = Adept::DamageObject::LeftArmArmorZone;		break;	}
			case 1:		{	best_zone = Adept::DamageObject::LeftTorsoArmorZone;	break;	}
			case 2:		{	best_zone = Adept::DamageObject::RightTorsoArmorZone;	break;	}
			case 3:		{	best_zone = Adept::DamageObject::RightArmArmorZone;		break;	}
			default:	{	best_zone = Adept::DamageObject::CenterTorsoArmorZone;	break;	}
		}
		
		Adept::DamageObject* damage_object = FindComponent(v,best_zone);

		if (damage_object != 0)
		{
			return (damage_object);
		}
	}

	DamageObjectEvaluator_MostDamaged func;
	return (GetBestDamageObject(v,func));
}

Adept::DamageObject* MW4AI::GetComponent_LeastDamaged(const component_vector& v)
{
	Verify(v.size() > 0);
	DamageObjectEvaluator_LeastDamaged func;
	return (GetBestDamageObject(v,func));
}

Adept::DamageObject* MW4AI::GetComponent_Random(const component_vector& v)
{
	Verify(v.size() > 0);
	if (v.size () == 0)
	{
		return (0);
	}

	return (v[gos_rand() % v.size()]);
}

Adept::DamageObject* MW4AI::GetComponent_TorsoThenTopDown(const component_vector& v)
{
	Verify(v.size() > 0);

	Adept::DamageObject* torso = FindComponent(v,Adept::DamageObject::CenterTorsoArmorZone);

	if (torso != 0)
	{
		return (torso);
	}

	Stuff::Point3D highest(0,-1000,0);
	component_vector::const_iterator best = v.end();

	{for (component_vector::const_iterator i = v.begin();
		  i != v.end();
		  ++i)
	{
		if ((*i)->parentEntity != 0)
		{
			Stuff::Point3D pos = (Stuff::Point3D)(*i)->parentEntity->GetLocalToWorld();

			if (pos.y > highest.y)
			{
				highest = pos;
				best = i;
			}
		}
	}}

	if (best == v.end())
	{
		return (*v.begin());
	}

	return (*best);
}

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

bool MW4AI::WeaponIsArmWeapon(const MechWarrior4::Weapon& weapon, bool left)
{
	if ((weapon.sitePointer != 0) &&
		(weapon.sitePointer->GetParent() != 0) &&
		(weapon.sitePointer->GetParent()->damageObject != 0) &&
		(weapon.sitePointer->GetParent()->damageObject->internalDamageObject != 0))
	{
		Adept::InternalDamageObject* ido = weapon.sitePointer->GetParent()->damageObject->internalDamageObject;

		if (left == true)
		{
			if (ido->damageZone == Adept::InternalDamageObject::LeftArmZone)
			{
				return (true);
			}
		}
		else
		{
			if (ido->damageZone == Adept::InternalDamageObject::RightArmZone)
			{
				return (true);
			}
		}
	}

	return (false);
}

Stuff::LinearMatrix4D MW4AI::WeaponSiteLocalToWorld(const MechWarrior4::Weapon& weapon)
{
	Verify(weapon.sitePointer != 0);
	Verify(weapon.sitePointer->GetParent() != 0);
	Verify(weapon.sitePointer->GetParent()->damageObject != 0);
	Verify(weapon.sitePointer->GetParent()->damageObject->internalDamageObject != 0);
	Verify(weapon.sitePointer->GetParent()->damageObject->internalDamageObject->parentEntity != 0);

	return (weapon.sitePointer->GetLocalToWorld());
}

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

bool MW4AI::HasAnyUnlimitedAmmoWeapons(MechWarrior4::MWObject& object)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;

	while ((weapon = i.ReadAndNext()) != 0)
	{
		if (weapon->GetAmmoCount() == -1)
		{
			return (true);
		}
	}

	return (false);
}

bool MW4AI::HasAnyProjectileWeapons(MechWarrior4::MWObject& object)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;

	while ((weapon = i.ReadAndNext()) != 0)
	{
		if (weapon->IsDerivedFrom(MechWarrior4::ProjectileWeapon::DefaultData) == true)
		{
			return (true);
		}
	}

	return (false);
}

Weapon* FindFarthestWeapon(const std::vector<MechWarrior4::Weapon*>& weapons, const Stuff::Point3D& point)
{
	if (weapons.size() == 0)
	{
		return (0);
	}

	Stuff::Scalar best_distance = 0;
	std::vector<MechWarrior4::Weapon*>::const_iterator best = weapons.end();

	{for (std::vector<MechWarrior4::Weapon*>::const_iterator i = weapons.begin();
		  i != weapons.end();
		  ++i)
	{
		const Stuff::Scalar distance = GetLengthSquared(point,(Stuff::Point3D)(*i)->sitePointer->GetLocalToWorld());
		if (distance >= best_distance)
		{
			best_distance = distance;
			best = i;
		}
	}}
	Verify(best != weapons.end());

	return (*best);
}

void MW4AI::GetLeftmostAndRightmostWeapons(MechWarrior4::MWObject& object,
										   std::vector<MechWarrior4::Weapon*>& weapons)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;
	std::vector<MechWarrior4::Weapon*> all_weapons;

	while ((weapon = i.ReadAndNext()) != 0)
	{
		all_weapons.push_back(weapon);
	}

	if (all_weapons.size() < 2)
	{
		return;
	}

	weapons.push_back(FindFarthestWeapon(all_weapons,GetWeaponCenter(object)));
	weapons.push_back(FindFarthestWeapon(all_weapons,(Stuff::Point3D)weapons.back()->sitePointer->GetLocalToWorld()));
}

Stuff::Point3D MW4AI::GetWeaponCenter(MechWarrior4::MWObject& object)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
	MechWarrior4::Weapon* weapon = 0;

	Stuff::Point3D center(0,0,0);
	Stuff::Scalar num_counted(0);
	while ((weapon = i.ReadAndNext()) != 0)
	{
		center += (Stuff::Point3D)weapon->sitePointer->GetLocalToWorld();
		num_counted += 1;
	}

	if (num_counted == 0)
	{
		return (Stuff::Point3D(0,0,0));
	}

	center.x /= num_counted;
	center.y /= num_counted;
	center.z /= num_counted;

	return (center);
}

void MW4AI::DamageObjectChainToVector(Stuff::SortedChainOf<Adept::DamageObject*,int>& chain, component_vector& v)
{
	AutoHeap local_heap (g_CombatAIHeap);
	Stuff::SortedChainIteratorOf<Adept::DamageObject*,int> i(&chain);
	Adept::DamageObject* damage_object = 0;

	while ((damage_object = i.ReadAndNext()) != 0)
	{
		if ((damage_object->parentEntity != 0) &&
			(damage_object->armorZone != Adept::DamageObject::HeadArmorZone))
		{
		    if (damage_object->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed)
			{
				switch (damage_object->armorZone)
				{
					case Adept::DamageObject::LeftArmArmorZone:
					{
						v.push_back(damage_object);
						break;
					}
					case Adept::DamageObject::RightArmArmorZone:
					case Adept::DamageObject::RightTorsoArmorZone:
					case Adept::DamageObject::LeftTorsoArmorZone:
					case Adept::DamageObject::CenterTorsoArmorZone:
					case Adept::DamageObject::CenterRearTorsoArmorZone:
					{
						v.push_back(damage_object);
						break;
					}
				}
			}
			else
			{
				v.push_back(damage_object);
			}
		}
	}
}

void MW4AI::SetRaySourceToBestComponent(Adept::Entity__CollisionQuery& query)
{
	if ((query.m_raySource != 0) &&
		(query.m_raySource != Adept::Map::GetInstance()) &&
		(query.m_line != 0) &&
		(query.m_raySource->IsDerivedFrom(MWObject::DefaultData) == true))
	{
		MWObject* mwobject = Cast_Object(MWObject*,query.m_raySource);

		component_vector components;
		DamageObjectChainToVector(mwobject->damageObjects,components);

		Adept::Entity* nearest = 0;
		Stuff::Scalar least_distance = 100000.0f;
		least_distance *= least_distance;
		{for (component_vector::iterator i = components.begin();
			  i != components.end();
			  ++i)
		{
			if ((*i)->parentEntity != 0)
			{
				Stuff::Point3D damage_object_point((*i)->parentEntity->GetLocalToWorld());

				Stuff::Point3D closest_point;
				query.m_line->GetClosestPointTo(damage_object_point,&closest_point);
				Stuff::Scalar distance(GetLengthSquared(damage_object_point,closest_point));

				if (distance < least_distance)
				{
					nearest = (*i)->parentEntity;
					least_distance = distance;
				}
			}
		}}

		if (nearest != 0)
		{
			query.m_raySource = nearest;
		}
	}
}

