//===========================================================================//
// File:	CombatAI.cpp																	     //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//	09/21/99  PDT	Created File											//
//					Question: why is this even here when we have SourceSafe? ... //
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Microsoft										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "mw4headers.hpp"
#include <Adept\AdeptHeaders.hpp>
#include "gameinfo.hpp"

#include "mw4.hpp"
#include "combatai.hpp"
#include "VehicleInterface.hpp"
#include <Adept\Application.hpp>
#include "MWPlayer.hpp"
#include "MWDamageObject.hpp"
#include "weapon.hpp"
#include "AI_Moods.hpp"
#include "Airplane.hpp"
#include "Mech.hpp"
#include <Adept\Map.hpp>
#pragma warning (push)
#include <stlport\stack>
#pragma warning (pop)
#include "MWMission.hpp"
#include "AI_Weapons.hpp"
#include "AI_Groups.hpp"
#include "Sensor.hpp" // temporary
#include "AI_Graveyard.hpp"
#include "AI_FireStyle.hpp"
#include "HeatManager.hpp"
#include "Building.hpp"
#include "MWEntityManager.hpp"
#include <Adept\CollisionVolume.hpp>
#include "MWDebugHelper.hpp"
#include "AI_Damage.hpp"
#include "LongTomWeapon.hpp"
#include "AI_Statistics.hpp"
#include "Narc.hpp"
#include "SubsystemClassData.hpp"
#include <Adept\Site.hpp>	// TODO: remove -- unnecessary


using namespace MW4AI;
using namespace MechWarrior4;

const Stuff::Scalar offset_size__to_hit = 0.4f;
const Stuff::Scalar fire_offset_multiplier__to_hit = 1.2f;
const Stuff::Scalar min_speed_kph_considered_moving_quickly = 50.0f;
const Stuff::Scalar friendly_fire_multiplier = 2.5f;
const Stuff::Scalar bad_area_radius = 20.0f;
const Stuff::Scalar mission_fog_margin_multiplier = 0.9f;
const Stuff::Scalar min_node_radius_used_for_attack_interval = 80.0f;
const Stuff::Scalar auto_full_throttle_range = 250.0f;
const Stuff::Scalar initial_friendly_fire_delay = 2.0f;
const Stuff::Scalar max_friendly_fire_delay = 8.0f;
const Stuff::Scalar being_targeted_dissipate_time = 1.0f;
const Stuff::Scalar torso_centering_duration = 5.0f;
const Stuff::Scalar look_decay_time = 3.0f;
const Stuff::Scalar miss_offset_usage_limit = 3.5;
const Stuff::Scalar can_see_result_refresh_time = 2.0f;
const Stuff::Scalar max_miss_offset_distance = 8.0f;
const Stuff::Scalar multi_torso_raycast_failure_refresh = 3.2f;
const Stuff::Scalar no_path_warning_remove_time = 10.0f;
const Stuff::Scalar min_target_switch_time = 12.0f;
const Stuff::Scalar target_switch_score_multiplier = 0.7f;
const Stuff::Scalar target_switch_shot_by_multiplier = 1.5f;
const Stuff::Scalar min_squared_distance_check_angle_to_miss = 80.0f * 80.0f;
const Stuff::Scalar min_attack_interval_update = 5.0f;
const Stuff::Scalar max_fire_source_cache_refresh = 2.0f;
const Stuff::Scalar nearest_enemy_refresh = 4.0f;
const Stuff::Scalar min_move_request_wait = 2.5f;
const int fire_attempts__to_hit = 4;
const int max_fire_attempts__to_miss = 7;
const int max_fire_attempts__to_hit = 2;
const int max_fire_attempts__to_hit_total = 8;
const int max_blocked_move_points_size = 12;
const int max_most_recent_tactics_list_size = 4;
const int max_opportunity_fire_attempts = 2;

__int64	tCombatAITime;
__int64	tCombatAITime_UpdateSquad;
__int64	tCombatAITime_UpdateMovement;
__int64	tCombatAITime_UpdateAttacking;
__int64	tCombatAITime_UpdateTactic;
__int64 tCombatAITime_ShotWithin;
__int64 tCombatAITime_UpdateFiring;
__int64 tCombatAITime_RandomExtraneousCrap;
__int64 tCombatAITime_Firing_OpportunityFire;
__int64 tCombatAITime_FireWithCurrentQuery;
__int64 tCombatAITime_NotifyShot;
__int64 tCombatAITime_SituationalAnalysis;
__int64 tCombatAITime_SituationalAnalysis_Generate;
__int64 tCombatAITime_SituationalAnalysis_Evaluate;
__int64 tCombatAITime_SituationalAnalysis_PointRemoval;
__int64 tCombatAITime_FireToHit;
__int64 tCombatAITime_FireToMiss;
__int64 tCombatAITime_WastedFire;
__int64 tCombatAITime_WeaponFiring;
__int64 tCombatAITime_Track;
__int64 tCombatAITime_TorsoTwisting;
__int64 tCombatAITime_GetNearest;
__int64 tCombatAITime_FindObject;
__int64 tCombatAITime_CanSee;
__int64 tWeaponFiring;
__int64 tCombatAITime_RayCasting;
__int64 tRayCasting;
int tFireToMissRetries;


Stuff::Scalar GetFogDistance()
{
	if (MWMission::GetInstance() == 0)
	{
		return (100000);
	}

	const Adept::Mission::GameModel* game_model = MWMission::GetInstance()->GetGameModel();
	return (game_model->m_generalFogEnd * mission_fog_margin_multiplier);
}

bool CanSeeThroughFog(const Stuff::Point3D& point1, const Stuff::Point3D& point2, bool ignore_fog)
{
	Stuff::Scalar fog_distance = 1000;

	if (ignore_fog == false)
	{
		fog_distance = GetFogDistance();
	}

	return (GetApproximateLength(point1,point2) < fog_distance);
}

void GetSensorList(const MechWarrior4::Sensor::sensorDataArray& sensed_data,
				   std::vector<MWObject*>& objects)
{
	{for (int i = 0;
		  i < sensed_data.GetLength();
		  ++i)
	{
		MechWarrior4::SensorData* d = sensed_data[i];

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

			if ((current != 0) &&
				(current->IsDestroyed() == false) &&
				(current->IsDerivedFrom(MWObject::DefaultData) == true))
			{
				objects.push_back(Cast_Object(MWObject*,current));
			}
		}
	}}
}

CombatAI::PatienceMonitor::PatienceMonitor(int maximum)
	: m_Maximum(maximum)
	, m_Patience(0)
	, m_Frustration(1)
{
}

bool CombatAI::PatienceMonitor::Check()
{
	if (m_Frustration > 1)
	{
		--m_Frustration;
	}

	if (m_Patience > 0)
	{
		--m_Patience;
		return (false);
	}

	return (true);
}

void CombatAI::PatienceMonitor::Notify(bool success)
{
	if (success == true)
	{
		if (m_Frustration > 1)
		{
			--m_Frustration;
		}
	}
	else
	{
		++m_Frustration;
		m_Frustration *= 2;
		m_Patience += m_Frustration;

		if (m_Patience > m_Maximum)
		{
			m_Patience = m_Maximum;
		}

		if (m_Frustration > m_Maximum)
		{
			m_Frustration = m_Maximum;
		}
	}
}

//#############################################################################
//###############################    CombatAI     #############################
//#############################################################################

CombatAI::ClassData* CombatAI::DefaultData = NULL;


const Receiver::MessageEntry
CombatAI::MessageEntries[]=
{
	MESSAGE_ENTRY(CombatAI, DebugText),
};


void CombatAI::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
	new ClassData(CombatAIClassID,
				  "MechWarrior4::CombatAI",
				  inherited::DefaultData,
				  ELEMENTS(MessageEntries), MessageEntries,
				  (Entity::Factory)Make,
				  (Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
				  ExecutionStateEngine::DefaultData,
				  (Entity::GameModel::Factory)GameModel::ConstructGameModel,
				  NULL,
				  (Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
				  (Entity::GameModel::ModelWrite)GameModel::WriteToText,
				  (Entity::GameModel::ModelSave)GameModel::SaveGameModel);
	Register_Object(DefaultData);

#if !defined(NO_TIMERS)
	AddStatistic("CombatAI", "%", gos_timedata, (void*)&tCombatAITime, 0);
	AddStatistic("+- Extra crap", "%", gos_timedata, (void*)&tCombatAITime_RandomExtraneousCrap, 0);
	AddStatistic("+- UpdateSquad", "%", gos_timedata, (void*)&tCombatAITime_UpdateSquad, 0);
	AddStatistic("+- UpdateMovement", "%", gos_timedata, (void*)&tCombatAITime_UpdateMovement, 0);
	AddStatistic("+- UpdateAttacking", "%", gos_timedata, (void*)&tCombatAITime_UpdateAttacking, 0);
	AddStatistic(" +- Update Tactic", "%", gos_timedata, (void*)&tCombatAITime_UpdateTactic, 0);
	AddStatistic("    +- ShotWithin", "%", gos_timedata, (void*)&tCombatAITime_ShotWithin, 0);
	AddStatistic("  +- Firing", "%", gos_timedata, (void*)&tCombatAITime_UpdateFiring, 0);
	AddStatistic("    +- Opportunity", "%", gos_timedata, (void*)&tCombatAITime_Firing_OpportunityFire, 0);
	AddStatistic("    +- Fire-To-Hit", "%", gos_timedata, (void*)&tCombatAITime_FireToHit, 0);
	AddStatistic("    +- Fire-To-Miss", "%", gos_timedata, (void*)&tCombatAITime_FireToMiss, 0);
	AddStatistic("    +- Wasted Fire", "%", gos_timedata, (void*)&tCombatAITime_WastedFire, 0);
	AddStatistic("    +- FireWithCurrentQuery", "%", gos_timedata, (void*)&tCombatAITime_FireWithCurrentQuery, 0);
	AddStatistic("	    +- Ray Casting", "%", gos_timedata, (void*)&tCombatAITime_RayCasting, 0);
	AddStatistic("      +- Weapon Firing Time", "%", gos_timedata, (void*)&tCombatAITime_WeaponFiring, 0);
	AddStatistic("    +- CanSee","%",gos_timedata, (void*)&tCombatAITime_CanSee, 0);
	AddStatistic("  +- Track", "%", gos_timedata, (void*)&tCombatAITime_Track, 0);
	AddStatistic("  +- Torso Twisting", "%", gos_timedata, (void*)&tCombatAITime_TorsoTwisting, 0);
	AddStatistic("  +- GetNearest", "%", gos_timedata, (void*)&tCombatAITime_GetNearest, 0);
	AddStatistic("  +- Situational Analysis", "%", gos_timedata, (void*)&tCombatAITime_SituationalAnalysis, 0);
	AddStatistic("    +- Generate", "%", gos_timedata, (void*)&tCombatAITime_SituationalAnalysis_Generate, 0);
	AddStatistic("    +- Evaluate", "%", gos_timedata, (void*)&tCombatAITime_SituationalAnalysis_Evaluate, 0);
	AddStatistic("    +- Point Removal", "%", gos_timedata, (void*)&tCombatAITime_SituationalAnalysis_PointRemoval, 0);
	AddStatistic("+- FindObject", "%", gos_timedata, (void*)&tCombatAITime_FindObject, 0);
	AddStatistic("CombatAI Notify Shot", "%", gos_timedata, (void*)&tCombatAITime_NotifyShot, 0);
	AddStatistic("Fire-To-Miss retries", "%", gos_DWORD, (void*)&tFireToMissRetries, Stat_AutoReset);
#endif
}

void CombatAI::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

CombatAI* CombatAI::Make(CreateMessage *message,ReplicatorID *base_id)
{
	Check_Object(message);
	AutoHeap local_heap (g_CombatAIHeap);
	CombatAI *new_entity = new CombatAI(DefaultData, message, base_id, NULL);
	Check_Object(new_entity);
	new_entity->SyncMatrices(true);
	return new_entity;
}

CombatAI::CombatAI(ClassData *class_data,
				   CreateMessage *message,
				   ReplicatorID *base_id,
				   ElementRenderer::Element *element)
	: inherited(class_data,message,base_id,element)
	, m_LastAttackIntervalRefresh(0)
	, m_Interface(*this)
	, m_HitResult(FireData::HIT_NOTHING)
	, m_OverridingMovement(false)
	, m_AttackThrottle(100.0f)
	, m_MinFiringDelay(0)
	, m_MaxFiringDelay(0)
	, m_LastTimeProjectileShotAtMe(0)
	, m_ThrottleOverrideStopTime(0)
	, m_HoldingFire(false)
	, m_NumArmsBlownOff(0)
	, m_NumLegsBlownOff(0)
	, m_InternalArmorBreached(false)
	, m_LastUnableToFireTime(0)
	, m_LastFireTime(0)
	, m_ForceDestinationRecalc(false)
	, m_ApproximateTimeBetweenFrames(0)
	, m_LastFrameTime(0)
	, m_LastFriendlyFireTime(0)
	, m_FriendlyFireDelay(0)
	, m_LastTimeTargeted(0)
	, m_TimeBeingTargeted(0)
	, m_LastTimeRammedTarget(0)
	, m_LastTimeJumped(0)
	, m_SquadTargetingRadius(0)
	, m_ExplicitFireStretch(0)
	, m_WaitingForHeatToReachMinSkill(false)
	, m_SearchLightController(*this)
	, m_LookState(LOOK_CENTER)
	, m_LastLookTime((Stuff::Scalar)gos_GetElapsedTime())
	, m_LeftArmExists(true)
	, m_RightArmExists(true)
	, m_AttackRadiusMultiplier(0)
	, m_LastOffsetToMiss(0,0,0)
	, m_MissOffsetStartTime(0)
	, m_JumpDuration(0)
	, m_Last_Till_Value(0)
	, m_CheapShotTarget(-2)
	, m_CheapShotShouldHit(false)
	, m_CheapShotShouldSpendAmmo(true)
	, m_LastTimeMoved((Stuff::Scalar)gos_GetElapsedTime())
	, m_PerWeaponRayCasting(false)
	, m_LastTimeSwitchedTargets((Stuff::Scalar)gos_GetElapsedTime())
	, m_AutoTargetingEnabled(true)
	, m_LastTimeLookChanged(0)
	, m_MinSpeed(0)
	, m_GoalPitch(0)
	, m_GoalYaw(0)
	, m_MinAttackInterval(0)
	, m_MaxAttackInterval(0)
	, m_CurrentVulnerableComponent(0)
	, m_FiringAttemptMonitor(12)
	, m_OpportunityFireMonitor(12)
	, m_LastAttackOrderDamageRating(0)
	, m_ShutDownDueToHeat(false)
	, m_MoveRequestFinishedTime(0)
	, m_IgnoreFog(false)
{
	AutoHeap local_heap (g_CombatAIHeap);
	Check_Pointer(this);
	Check_Object(message);

	if (Network::GetInstance()->AmIServer())
	{
		m_LastTargetedAlignment = GetSelf().GetAlignment();
		m_LastPos = (Point3D)GetSelf().GetLocalToWorld();
		m_LastMoveDest.Zero();
		m_LastProjectileOrigin.Zero();
		m_ExplicitFirePoint.Zero();
		m_LastAimPoint.Zero();

		m_CachedNearest[0] = -2;
		m_CachedNearestTime[0] = 0;
		m_CachedNearest[1] = -2;
		m_CachedNearestTime[2] = 0;

		UpdateFrameTiming();

		Tactics::Registrar::IncrementRefCount(m_Interface);

		if (UserConstants::Instance() != 0)
		{
			m_SquadTargetingRadius = UserConstants::Instance()->Get(UserConstants::lancemate_can_attack_radius);
		}

		if (IsLaserTurret() == true)
		{
			Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(GetSelf().weaponChain));
			MechWarrior4::Weapon* weapon = 0;
			while ((weapon = i.ReadAndNext()) != 0)
			{
				weapon->SetNonrandomWaitTimes(true);
			}
		}
	}

	#ifdef LAB_ONLY
	m_LastFireRejected = false;
	#endif
}

CombatAI::~CombatAI()
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (Network::GetInstance()->AmIServer())
	{
		Tactics::Registrar::DecrementRefCount();
	}
}

void CombatAI::DebugTextMessageHandler(Adept::ReceiverDataMessageOf<int> *message)
{
	Check_Object(this);
	Check_Object(message);
	Verify(message->messageID == DebugTextMessageID);
}

void CombatAI::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

void CombatAI::Reset(bool reset_movement)
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (GetAttackState() == ATTACKING)
	{
		StopAttacking();
	}

	Target(0);

	if (reset_movement == true)
	{
		StopMoving();
	}
}

void CombatAI::OrderAttack(bool fOverrideCurrentMove)
{
	AutoHeap local_heap (g_CombatAIHeap);

	if (Target() == 0)
	{
		return;
	}
	
	if ((m_Tactic.GetPointer() != 0) &&
		(m_Tactic->GetExplicit() == false))
	{
		return;
	}

	OrderAttackTactic(Tactics::Registrar::GetInstance().SelectTactic(m_Interface,m_MostRecentTactics),fOverrideCurrentMove,false);
}

void CombatAI::OrderAttackTactic(Tactics::TacticID tactic, bool fOverrideCurrentMove, bool is_explicit)
{
	AutoHeap local_heap (g_CombatAIHeap);

	if (Target() == 0)
	{
		return;
	}

	if (GetAttackState() == ATTACKING)
	{
		if (m_Tactic->GetID() == tactic)
		{
			return;
		}

		if ((is_explicit == true) &&
			(m_Tactic->GetIgnoreExplicitTactic() == tactic))
		{
			return;
		}
	}

	if ((Target() != 0) &&
		(CanAct() == true))
	{
		if ((GetAttackState() != ATTACKING) ||
			(m_Tactic->GetID() != tactic))
		{
			m_LastAttackOrderPosition.Assimilate(new Stuff::LinearMatrix4D(GetSelf().GetLocalToWorld()));
			m_LastTimeJumped = (Stuff::Scalar)gos_GetElapsedTime();
			m_LastAttackOrderDamageRating = MW4AI::Damage::GetDamageRating(GetSelf());

			if (GetAttackState() == ATTACKING)
			{
				StopAttacking();
			}

			StartAttacking(tactic,fOverrideCurrentMove,is_explicit);
		}
	}
	else
	{
		if (GetAttackState() != NOT_ATTACKING)
		{
			StopAttacking();
		}
	}
}

void CombatAI::OrderStopAttacking()
{
	AutoHeap local_heap (g_CombatAIHeap);

	m_LastAttackOrderPosition.Delete();
	StopAttacking();
}

void CombatAI::OrderShootPoint(const Stuff::Point3D& point, int stretch)
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (CanAct() == true)
	{
		m_LastAttackOrderPosition.Assimilate(new Stuff::LinearMatrix4D(GetSelf().GetLocalToWorld()));

		if (GetAttackState() != NOT_ATTACKING)
		{
			StopAttacking();
		}

		m_ExplicitFirePoint = point;
		m_ExplicitFireStretch = stretch;
	}
}

void CombatAI::OrderJump(Stuff::Scalar duration)
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (GetSelf().IsDerivedFrom(Mech::DefaultData) == false)
	{
		return;
	}

	Mech* mech = Cast_Object(Mech*,&GetSelf());
	Check_Object(mech);

	if (mech->GetJumpJet() == 0)
	{
		return;
	}

	mech->JumpRequest();

	m_LastTimeJumped = (Stuff::Scalar)gos_GetElapsedTime();
	m_JumpDuration = duration;
}

void CombatAI::SetFallDampingEnabled(bool enabled)
{
	if (GetSelf().IsDerivedFrom(Mech::DefaultData) == false)
	{
		return;
	}

	Mech* mech = Cast_Object(Mech*,&GetSelf());

	if (enabled == true)
	{
		mech->EnableFallDamping((Stuff::Point3D)Target()->GetLocalToWorld());
	}
	else
	{
		mech->DisableFallDamping();
	}
}

void CombatAI::OrderJump()
{
	AutoHeap local_heap (g_CombatAIHeap);
//	OrderJump(0.8f);

	if (RequestJump(m_Last_Till_Value) == true)
	{
		m_LastTimeJumped = (Stuff::Scalar)gos_GetElapsedTime();
		m_JumpDuration = 1.0f;
	}
}

void CombatAI::OrderStopJumping()
{
	m_JumpDuration = 0;

	if (GetSelf().IsDerivedFrom(Mech::DefaultData) == true)
	{
		Mech* mech = Cast_Object(Mech*,&GetSelf());
		Check_Object(mech);

		mech->StopJumpRequest();
	}
}

void CombatAI::UpdateJumping()
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (m_JumpDuration == 0)
	{
		return;
	}

	if ((m_LastTimeJumped + m_JumpDuration < gos_GetElapsedTime()) &&
		(GetSelf().IsDerivedFrom(Mech::DefaultData) == true))
	{
		Mech* mech = Cast_Object(Mech*,&GetSelf());
		Check_Object(mech);

		mech->StopJumpRequest();
		
		m_JumpDuration = 0;
	}
}

void CombatAI::Update(Stuff::Time till)
{
	AutoHeap local_heap (g_CombatAIHeap);
	#ifdef LAB_ONLY
	ThisIsAMethodToSetABreakpointInIfCurrentVehicle();
	#endif

	COMBAT_LOGIC("Update");
	TIME_FUNCTION(tCombatAITime);

	m_Last_Till_Value = (Stuff::Scalar)till;

	#ifdef LAB_ONLY
	if (MWMission::GetInstance() != 0)
	{
		DiagnosticsInterface::GetInstance().SetExecutingObject(&GetSelf());
	}
	#endif

	if (GetSelf().IsDestroyed() == true)
	{
		return;
	}

	tWeaponFiring = 0;
	tRayCasting = 0;

	{
		TIME_FUNCTION(tCombatAITime_RandomExtraneousCrap);
		UpdateFrameTiming();

		if (ShouldRun(true) == true)
		{
			UpdateSquad();
			UpdateFriendlyFireTiming();
			UpdateBeingTargeted();
			UpdateWaitingForHeatToReachZero();
			m_SearchLightController.Update();
			UpdateLookState();
			UpdateJumping();
			UpdateNoPathWarnings();
			UpdateGoalPitchAndYaw();

			if (Target() == 0)
			{
				m_DistanceFromTargetSquared = 0;
			}
			else
			{
				Stuff::Point3D delta;
				delta.Subtract((Stuff::Point3D)GetSelf().GetLocalToWorld(),
							   (Stuff::Point3D)GetTarget().GetLocalToWorld());
				delta.y = 0;
				m_DistanceFromTargetSquared = delta.GetLengthSquared();
			}
		}
	}

	UpdateMoving();
	UpdateAttacking();

	tCombatAITime -= tWeaponFiring;

	#ifdef LAB_ONLY
	if (MWMission::GetInstance() != 0)
	{
		DiagnosticsInterface::GetInstance().SetExecutingObject(0);
	}
	#endif
}

void CombatAI::PreCollisionExecute(Stuff::Time till)
{
  extern int g_nMR;
  if (g_nMR == 2)
       return;

    AI_LOGIC("Pre-Collision::CombatAI");
#if defined(LAB_ONLY)
		if (!instanceName)
			MWGameInfo::g_LastMWObject[0] = '\0';
		else
		{
			strncpy(MWGameInfo::g_LastMWObject, instanceName, sizeof(MWGameInfo::g_LastMWObject)-1);
			MWGameInfo::g_LastMWObject[sizeof(MWGameInfo::g_LastMWObject)-1] = '\0';
		}
		MWGameInfo::g_LastMWObjectPos = GetLocalToWorld();
#endif
	Update(till);
	inherited::PreCollisionExecute(till);

	#ifdef LAB_ONLY
	if ((CombatTacticInterface::g_DisableMovement == true) &&
		(m_TimeNotMoving == 0))
	{
		StopMoving();
	}
	#endif
}

void CombatAI::PostCollisionExecute(Time till)
{
  extern int g_nMR;
  if (g_nMR == 2)
       return;
	AI_LOGIC("Post-Collision::CombatAI");

	inherited::PostCollisionExecute(till);

	if (m_CheapShotTarget != -2)
	{
		Adept::Entity* entity = NameTable::GetInstance()->FindData(m_CheapShotTarget);

		if ((entity == 0) ||
			(entity->IsDestroyed() == true) ||
			(entity->IsDerivedFrom(MechWarrior4::MWObject::DefaultData) == false))
		{
			return;
		}

		MWObject* mwobject = Cast_Object(MWObject*,entity);
		Check_Object(mwobject);

		CheapShot(*mwobject,m_CheapShotShouldHit,m_CheapShotShouldSpendAmmo);

		m_CheapShotTarget = -2;
	}
}

void CombatAI::UpdateNoPathWarnings()
{
	{for (std::vector<Stuff::Scalar>::iterator i = m_MostRecentNoPathWarnings.begin();
		  i != m_MostRecentNoPathWarnings.end();
		  ++i)
	{
		if ((*i) + no_path_warning_remove_time < (Stuff::Scalar)gos_GetElapsedTime())
		{
			m_MostRecentNoPathWarnings.erase(i);
			UpdateNoPathWarnings();
			return;
		}
	}}
}

void CombatAI::UpdateSquad()
{
	AutoHeap local_heap (g_CombatAIHeap);
	COMBAT_LOGIC("Update Squad");
	TIME_FUNCTION(tCombatAITime_UpdateSquad);

	{for (MWObject::GroupList::const_iterator i = GetSelf().GetGroups().begin();
		  i != GetSelf().GetGroups().end();
		  ++i)
	{
		gosREPORT((Adept::Mission::GetInstance() != 0),"The mission object does not exist");
		gosREPORT((Adept::Mission::GetInstance()->IsDerivedFrom(MechWarrior4::MWMission::DefaultData) == true),"The mission object is not the correct type");

		MechWarrior4::MWMission* mission = Cast_Object(MechWarrior4::MWMission*,Adept::Mission::GetInstance());
		Check_Object(mission);

		if (mission->GetGroupContainer().GroupExists(*i) == true)
		{
			Group& g = mission->GetGroupContainer().GetGroup(*i);

			g.UpdateAI(*this);
		}
	}}

	if (m_SquadOrders.GetPointer() != 0)
	{
		m_SquadOrders->Update();
	}
}

void CombatAI::UpdateMoving()
{
	COMBAT_LOGIC("Update Movement");
	TIME_FUNCTION(tCombatAITime_UpdateMovement);

	UpdateThrottleOverride();

	Point3D pos(GetSelf().GetLocalToWorld());

	if (m_LastPos == pos)
	{
		m_TimeNotMoving += (Stuff::Scalar)m_ApproximateTimeBetweenFrames;
	}
	else
	{
		m_TimeNotMoving = 0;
		m_LastPos = pos;
		m_LastTimeMoved = (Stuff::Scalar)gos_GetElapsedTime();
	}
}

void CombatAI::UpdateAttacking()
{
	COMBAT_LOGIC("Update Attacking");
	TIME_FUNCTION(tCombatAITime_UpdateAttacking);

	if (GetAttackState() == ATTACKING)
	{
		if ((Target() == 0) ||									// if we have no target, we're not attacking and we return
			(CanAct() == false))
		{
			StopAttacking();
			return;
		}

		if (Target()->IsDestroyed() == true)
		{
			StopAttacking();
			Target(0);
			return;
		}

		if (m_Tactic->ShouldAbandon(m_Interface) == true)
		{
			MW4AI::Tactics::TacticID tactic_to_ignore((MW4AI::Tactics::TacticID)0);
			if (m_Tactic->ShouldAbandonImmediately() == true)
			{
				tactic_to_ignore = m_Tactic->GetID();
			}

			bool overriding_movement = m_OverridingMovement;
			Stuff::Scalar last_unable_to_fire_time(m_LastUnableToFireTime);

			StopAttacking();
			StartAttacking(Tactics::Registrar::GetInstance().SelectTactic(m_Interface,m_MostRecentTactics),overriding_movement,false);

			m_LastUnableToFireTime = last_unable_to_fire_time;

			if (tactic_to_ignore != (MW4AI::Tactics::TacticID)0)
			{
				m_Tactic->SetIgnoreExplicitTactic(tactic_to_ignore);
			}
		}

		if (ShouldRun(true) == true)
		{
			UpdateCurrentVulnerableComponent();
		}

		// TODO: change this elite skill level to its own user constant
		{
			TIME_FUNCTION(tCombatAITime_UpdateTactic);
			m_Tactic->Update(m_Interface);

			if (ShouldRun(true) == true)
			{
				m_Tactic->UpdateSatisfaction();
			}
		}
		UpdateAutoTargeting();
	}
	else
	{
		if (m_ExplicitFirePoint.GetLengthSquared() != 0)
		{
			AttackExplicitFirePoint();
			return;
		}

		if (Target() != 0)
		{
			TrackToPoint(m_LastAimPoint);
			TurnToPoint(m_LastAimPoint);
			return;
		}

		if ((m_GoalYaw == 0) &&
			(m_GoalPitch == 0))
		{
			if (m_CurMove == 0)
			{
				CenterTorso();
			}
			else
			{
				if (m_CurMove->MoveType() != MOVE_LOOKOUT)
				{
					CenterTorso();
				}
			}
		}
	}

	tCombatAITime_UpdateAttacking -= tWeaponFiring;
}

void CombatAI::AttackExplicitFirePoint()
{
	AutoHeap local_heap (g_CombatAIHeap);
	Verify(m_ExplicitFirePoint.GetLengthSquared() != 0);

	if (m_TimeNotMoving == 0)
	{
		StopMoving();
	}

	m_LastAimPoint = m_ExplicitFirePoint;

	Stuff::LinearMatrix4D my_los;
	GetSelf().GetLineOfSight(my_los);

	bool facing_point = MatrixFacesPoint(my_los,m_LastAimPoint,GetMaxFireCheatAngle());

	if (facing_point == true)
	{
		OffsetTargetPoint(m_LastAimPoint,(Stuff::Scalar)m_ExplicitFireStretch);
	}

	TrackToPoint(m_LastAimPoint);
	TurnToPoint(m_LastAimPoint);

	if (facing_point == true)
	{
		FireStyles::MaximumFire maximum_fire;
		FireAtAimPoint(maximum_fire,0,0,MW4AI::FIRE_FROM_ANYWHERE,my_los,0,0,true);
	}
}

bool CombatAI::TryToFireAt(MW4AI::FireStyles::FireStyle& style, MWObject& who, bool should_hit)
{
	if (m_Deactive == true)
	{
		m_CheapShotTarget = who.objectID;
		m_CheapShotShouldHit = should_hit;
		m_CheapShotShouldSpendAmmo = ShouldSpendAmmoAgainst(who,style);

		return (true);
	}

	if (should_hit == true)
	{
		if (TryToHit(who,style) == true)
		{
			tCombatAITime_UpdateFiring -= tWeaponFiring;
			return (true);
		}
	}
	else
	{
		if (TryToMiss(who,style) == true)
		{
			tCombatAITime_UpdateFiring -= tWeaponFiring;
			return (true);
		}
	}

	return (false);
}

bool CombatAI::MightBeAbleToFireAt(MW4AI::FireStyles::FireStyle& style, MWObject& who)
{
	m_SavedWeapons.clear();

	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(GetSelf().weaponChain));
	MechWarrior4::Weapon* weapon = 0;
	while ((weapon = i.ReadAndNext()) != 0)
	{
		m_SavedWeapons.push_back(weapon);
	}

	if (GetWeaponsThatCanFire(m_SavedWeapons,
							  ShouldSpendAmmoAgainst(who,style),
							  m_MinFiringDelay,
							  m_MaxFiringDelay) == false)
	{
		return (false);
	}

	const Stuff::Point3D los(who.GetLineOfSight());
	if (GetWeaponsThatCanFireAt(m_SavedWeapons,los) == false)
	{
		return (false);
	}

	return (GetWeaponsThatCanFireFrom(m_SavedWeapons,GetBestFireSource(who)));
}

void CombatAI::TryToFire(MW4AI::FireStyles::FireStyleID style_id)
{
	AILOGFUNC("CombatAI::TryToFire");

	if (Target() == 0)
	{
		return;
	}

	{
		COMBAT_LOGIC("Update Attacking::Firing");
		TIME_FUNCTION(tCombatAITime_UpdateFiring);

		if ((GetHoldingFire() == true) ||
			(ShouldHoldFireToAvoidFriendlyFire() == true) ||
			(GetSelf().vehicleShutDown == true))
		{
			return;
		}

		if (GetSelf().IsDerivedFrom(Mech::DefaultData) == true)
		{
			Mech* mech = Cast_Object(Mech*,&GetSelf());
			mech->NullArms();
		}

		FireStyles::FireStyle* style = 0;

		if (style_id == FireStyles::FIRE_MAXIMUM)
		{
			style = &m_MaximumFire;
		}
		else
		{
			style = &m_OpportunityFire;
		}

		if (GetSelf().GetTorsos().size() > 1)
		{
			MultiTurretFire(*style);
			return;
		}

		SetLookState(*style);

		if ((style->ShouldFireAtPrimaryTarget() == true) &&
			(m_FiringAttemptMonitor.Check() == true) &&
			(MightBeAbleToFireAt(*style,GetTarget()) == true))
		{
			if (((m_Tactic.GetPointer() != 0) && (m_Tactic->ShouldIgnoreLOS() == true)) ||
				(CanSee(GetTarget(),GetMaxFireCheatAngle()) == true))
			{
				bool should_hit(ShotShouldHit(GetTarget()));
				if (TryToFireAt(*style,GetTarget(),should_hit) == true)
				{
					m_FiringAttemptMonitor.Notify(true);
					return;
				}

				m_FiringAttemptMonitor.Notify(false);
			}
			else
			{
				m_LastUnableToFireTime = 0;
			}
		}
		else
		{
			m_LastUnableToFireTime = (Stuff::Scalar)gos_GetElapsedTime();
		}

		OpportunityFire(*style);
	}
}

void CombatAI::OpportunityFire(MW4AI::FireStyles::FireStyle& style)
{
	TIME_FUNCTION(tCombatAITime_Firing_OpportunityFire);

	if (m_OpportunityFireMonitor.Check() == false)
	{
		return;
	}

	if ((CanOpportunityFire(style) == false) ||
		(MW4AI::AnyWeaponReady(GetSelf(),m_MinFiringDelay,m_MaxFiringDelay) == false))
	{
		return;
	}

	m_SecondaryTargets.clear();
	InitializeSecondaryTargets(m_SecondaryTargets);

	if (m_SecondaryTargets.size() == 0)
	{
		return;
	}

	Stuff::LinearMatrix4D my_los;
	GetSelf().GetLineOfSight(my_los);

	int attempts = 0;

	while (m_SecondaryTargets.size() > 0)
	{
		secondary_target_list::iterator target = SelectSecondaryTarget(m_SecondaryTargets);
		Verify(target != m_SecondaryTargets.end());

		MWObject& who = **target;
		Check_Object(&who);

		m_SecondaryTargets.erase(target);

		if (MightBeAbleToFireAt(style,who) == true)
		{
			Stuff::Point3D mwobject_pos(who.GetLocalToWorld());

			if ((MatrixFacesPoint(my_los,mwobject_pos,GetMaxFireCheatAngle()) == true) &&
				(CanSeeThroughFog((Stuff::Point3D)my_los,mwobject_pos,GetIgnoreFog()) == true))
			{
				if (TryToFireAt(style,who,ShotShouldHit(who)) == true)
				{
					m_OpportunityFireMonitor.Notify(true);
					return;
				}
			}
		}

		++attempts;
		if (attempts >= max_opportunity_fire_attempts)
		{
			break;
		}
	}

	m_OpportunityFireMonitor.Notify(false);
}

	void AddVolumeToList(Adept::CollisionVolume& collision_volume, MWObject& who, std::list<CombatAI::AimPoint>& list_out)
	{
		OBB& obb = collision_volume.m_worldSpaceBounds;

		CombatAI::AimPoint temp((Stuff::Point3D)obb.localToParent,&who);
		list_out.push_back(temp);

		Stuff::ChainIteratorOf<CollisionVolume*> i(&collision_volume.m_children);
		CollisionVolume* volume = 0;

		while ((volume = i.ReadAndNext()) != 0)
		{
			Check_Object(volume);
			AddVolumeToList(*volume,who,list_out);
		}
	}

bool CombatAI::TryToHitAtPoints(MWObject& who, MW4AI::FireStyles::FireStyle& style, const std::list<CombatAI::AimPoint>& aim_points, bool can_hit_dead_components)
{
	MW4AI::FireSource fire_source = GetBestFireSource(GetTarget());
	Stuff::LinearMatrix4D source_matrix = GetFireSourceMatrix(fire_source);

	int num_tries(0);
	int num_times_hit_something_else(0);
	const int max_hit_something_else(6);

	{for (std::list<AimPoint>::const_iterator i = aim_points.begin();
		  i != aim_points.end();
		  ++i)
	{
		Stuff::Scalar offset_size = offset_size__to_hit;

		m_LastAimPoint = (*i).aim_point;

		if (MatrixFacesPoint(source_matrix,m_LastAimPoint,GetMaxFireCheatAngle()) == false)
		{
			continue;
		}

		int max_points_searched = fire_attempts__to_hit;

		if (IsMovingQuickly(who) == true)
		{
			max_points_searched = 1;	// we want to hit the exact point when the target is moving fast
		}

		{for (int i_guesses = 0;
			  i_guesses < max_points_searched;
			  ++i_guesses)
		{
			++num_tries;

			FireData fire_data(GetSelf(),(Stuff::Point3D)source_matrix,m_LastAimPoint);
			fire_data.Project();

			switch (fire_data.GetHitResult(&who,can_hit_dead_components))
			{
				case FireData::HIT_TARGET:
				{
					m_HitResult = FireData::HIT_TARGET;
					Fire(who,
						 (*i).entity,
						 fire_source,
						 source_matrix,
						 m_LastAimPoint,
						 style,
						 true);
					return (true);
				}

				case FireData::HIT_SOMETHING_ELSE:
				{
					++num_times_hit_something_else;

					if ((num_tries > max_hit_something_else) &&
						(num_times_hit_something_else == num_tries))
					{
						return (false);
					}

					continue;
				}
			}

			if (num_tries > max_fire_attempts__to_hit_total * fire_attempts__to_hit)
			{
				return (false);
			}

			if (i_guesses + 1 < max_points_searched)
			{
				m_LastAimPoint = (*i).aim_point;

				OffsetTargetPoint(m_LastAimPoint,offset_size);
				offset_size *= fire_offset_multiplier__to_hit;
			}
		}}
	}}

	return (false);
}

	void AddDefaultAimPoints(MWObject& who, component_vector& components, std::list<CombatAI::AimPoint>& aim_points)
	{
		CollisionVolume* cv = who.GetSolidVolume();

		if (cv == 0)
		{
			cv = who.GetHierarchicalVolume();
		}

		if (cv != 0)
		{
			LinearMatrix4D world_bounds;
			world_bounds.Multiply(cv->m_localSpaceBounds.localToParent, who.GetLocalToWorld());

			CombatAI::AimPoint temp((Stuff::Point3D)world_bounds,&who);
			aim_points.push_back(temp);
		}

		if ((components.size() == 1) &&
			(who.IsDerivedFrom(Vehicle::DefaultData) == true))
		{
			CombatAI::AimPoint temp2((Stuff::Point3D)who.GetLocalToWorld(),&who);
			aim_points.push_back(temp2);
		}
	}

void CombatAI::GetAimPoints(MWObject& who, std::list<CombatAI::AimPoint>& aim_points)
{
	component_vector components;
	DamageObjectChainToVector(who.damageObjects,components);

	if (components.size() < 2)
	{
		AddDefaultAimPoints(who,components,aim_points);
		return;
	}

	do
	{
		Adept::DamageObject* best_next_component = PickComponent(who,components);	// TODO: also use random, least damaged, etc.  ...

		if (best_next_component != 0)
		{
			AimPoint temp((Stuff::Point3D)best_next_component->parentEntity->GetLocalToWorld(),
						  best_next_component->parentEntity);
			aim_points.push_back(temp);
			component_vector::iterator found = std::find(components.begin(),components.end(),best_next_component);
			Verify(found != components.end());

			components.erase(found);
		}
	}
	while (components.size() > 0);

	if (aim_points.size() == 0)
	{
		AddDefaultAimPoints(who,components,aim_points);
	}
}

bool CombatAI::TryToHit(MWObject& who, MW4AI::FireStyles::FireStyle& style)
{
	AILOGFUNC("CombatAI::TryToHit");

	#ifdef LAB_ONLY
		__int64 startTime = GetCycles();
	#endif

	COMBAT_LOGIC("Update Attacking::Firing::To Hit");
	TIME_FUNCTION(tCombatAITime_FireToHit);

	Check_Object(&who);

	std::list<CombatAI::AimPoint> aim_points;
	GetAimPoints(who,aim_points);

	if (TryToHitAtPoints(who,style,aim_points) == true)
	{
		#ifdef LAB_ONLY
			if (m_LastFireRejected == true)
			{
				tCombatAITime_WastedFire += GetCycles() - startTime;
			}
		#endif

		LOG_AND_RETURN(true);
	}

	if (who.GetHierarchicalVolume() != 0)
	{
		aim_points.clear();

		CollisionVolume* collision_volume = who.GetHierarchicalVolume();
		AddVolumeToList(*collision_volume,who,aim_points);

		if (TryToHitAtPoints(who,style,aim_points,true) == true)
		{
			#ifdef LAB_ONLY
				if (m_LastFireRejected == true)
				{
					tCombatAITime_WastedFire += GetCycles() - startTime;
				}
			#endif

			LOG_AND_RETURN(true);
		}
	}

	LOG_AND_RETURN(false);
}

Stuff::Point3D CombatAI::GetNewAimPoint_ToMiss(MWObject& who, const Stuff::Point3D& self_pos)
{
	CollisionVolume* cv = who.GetSolidVolume();
	if (cv == 0)
	{
		cv = who.GetHierarchicalVolume();
	}

	Stuff::Scalar radius(0);
	Point3D center;

	if (cv == 0)
	{
		radius = 15.0f * (1.0f + Stuff::Random::GetFraction());
		center = who.GetLineOfSight();
	}
	else
	{
		radius = cv->m_worldSpaceBounds.sphereRadius * ((Stuff::Random::GetFraction() * 0.5f) + 1.0f);
		center = (Point3D)cv->m_worldSpaceBounds.localToParent;
	}

	Stuff::Point3D delta;
	delta.Subtract(center,self_pos);

	if (Small_Enough(delta.GetLengthSquared()) == true)
	{
		return (center);
	}

	delta.Normalize(delta);
	delta *= radius;

	Stuff::Point3D rv(center);

	if (Stuff::Random::GetFraction() < 0.5f)
	{
		rv.x -= delta.z;
		rv.z += delta.x;
	}
	else
	{
		rv.x += delta.z;
		rv.z -= delta.x;
	}

	rv.y += (Stuff::Random::GetFraction() - 0.5f) * (radius * 0.6f);

	return (rv);
}

bool CombatAI::TryToMiss(MWObject& who, MW4AI::FireStyles::FireStyle& style)
{
	AILOGFUNC("CombatAI::TryToMiss");

	#ifdef LAB_ONLY
		__int64 startTime = GetCycles();
	#endif

	COMBAT_LOGIC("Update Attacking::Firing::To Miss");
	TIME_FUNCTION(tCombatAITime_FireToMiss);

	int attempts = 0;

	Stuff::Point3D self_pos(GetSelf().GetLocalToWorld());
	Stuff::Point3D target_pos(who.GetLocalToWorld());

	Stuff::Point3D weapon_center(MW4AI::GetWeaponCenter(GetSelf()));

	bool check_shot_angle = GetLengthSquared(target_pos,self_pos) < min_squared_distance_check_angle_to_miss;
	Stuff::Scalar cheat_angle = GetMaxFireCheatAngle();

	m_LastAimPoint = GetNewAimPoint_ToMiss(who,self_pos);

	MW4AI::FireSource fire_source = GetBestFireSource(who);
	Stuff::LinearMatrix4D source_matrix(GetFireSourceMatrix(fire_source));

	Stuff::Scalar min_line_length = GetApproximateLength(self_pos,target_pos) - max_miss_offset_distance;

	while (1)
	{
		if ((attempts == 0) &&
			(m_LastOffsetToMiss.GetLengthSquared() != 0))
		{
			m_LastAimPoint = target_pos;
			m_LastAimPoint += m_LastOffsetToMiss;
		}
		else
		{
			m_LastOffsetToMiss.Subtract(m_LastAimPoint,target_pos);
		}

		if ((check_shot_angle == false) ||
			(MatrixFacesPoint(source_matrix,m_LastAimPoint,cheat_angle) == true))
		{
			FireData fire_data(GetSelf(),(Stuff::Point3D)source_matrix,m_LastAimPoint);
			fire_data.Project();

			Scalar m = min_line_length;
			if (attempts == max_fire_attempts__to_miss)
			{
				m -= 40.0f;									// on last attempt, we ignore the line length (in cases where target cannot be missed without hitting something else)
			}

			if (fire_data.GetLine().m_length > m)
			{
				m_HitResult = fire_data.GetHitResult(0);

				if (m_HitResult == FireData::HIT_TARGET)
				{
					m_HitResult = FireData::HIT_NOTHING;	// TODO: m_HitResult seems to be getting used for two different things!

					if ((attempts == 0) &&
						(m_LastOffsetToMiss.GetLengthSquared() != 0))
					{
						if (m_MissOffsetStartTime == 0)
						{
							m_MissOffsetStartTime = (Stuff::Scalar)gos_GetElapsedTime();
						}
						else
						{
							if ((IsMovingQuickly(who) == true) ||
								(m_MissOffsetStartTime + miss_offset_usage_limit < gos_GetElapsedTime()))
							{
								m_MissOffsetStartTime = 0;
								m_LastOffsetToMiss.Zero();
							}
						}
					}

					Fire(who,0,fire_source,source_matrix,m_LastAimPoint,style,false);

					#ifdef LAB_ONLY
						if (m_LastFireRejected == true)
						{
							tCombatAITime_WastedFire += GetCycles() - startTime;
						}
					#endif

					tFireToMissRetries += attempts;
					LOG_AND_RETURN(true);
				}
			}
		}

		m_LastAimPoint = GetNewAimPoint_ToMiss(who,self_pos);

		m_MissOffsetStartTime = 0;
		m_LastOffsetToMiss.Zero();

		++attempts;
		if (attempts > max_fire_attempts__to_miss)
		{
			tFireToMissRetries += attempts;
			LOG_AND_RETURN(false);
		}
	}
}

bool CombatAI::Fire(MWObject& who,
					Adept::Entity* component,
					MW4AI::FireSource fire_source,
					const Stuff::LinearMatrix4D& source_matrix,
					const Stuff::Point3D& target_point,
					MW4AI::FireStyles::FireStyle& style,
					bool should_hit)
{
	AILOGFUNC("CombatAI::Fire");

	Check_Object(&who);

	{
		COMBAT_LOGIC("Update Attacking::Firing::With Current Query");
		TIME_FUNCTION(tCombatAITime_FireWithCurrentQuery);	// NOTE: you must have a valid current query coming in to this method!

		Verify(m_HitResult != FireData::HIT_SOMETHING_ELSE);

		m_LastUnableToFireTime = 0;

		Vehicle* vehicle = 0;
		if (who.IsDerivedFrom(Vehicle::DefaultData) == true)
		{
			vehicle = Cast_Object(Vehicle*,&who);
			Check_Object(vehicle);
		}

		Adept::Entity* t = 0;
		if (should_hit == true)
		{
			t = &who;
		}

		FireAtAimPoint(style,&who,component,fire_source,source_matrix,t,vehicle,ShouldSpendAmmoAgainst(who,style));
	}

	tCombatAITime_FireWithCurrentQuery -= tWeaponFiring;

	return (true);
}

void CombatAI::FireAtAimPoint(MW4AI::FireStyles::FireStyle& style,
							  MWObject* who,
							  Adept::Entity* component,
							  MW4AI::FireSource fire_source,
							  const Stuff::LinearMatrix4D& source_matrix,
							  Adept::Entity* who_to_hit,
							  Vehicle* vehicle_shooting_at,
							  bool can_spend_ammo)
{
	FireData fire_data(GetSelf(),
					   (Stuff::Point3D)source_matrix,
					   m_LastAimPoint);

	Stuff::Scalar cur_heat = 0;
	Stuff::Scalar max_heat = 100000;

	HeatManager* hm = GetSelf().m_heatManager;
	if (hm != 0)
	{
		cur_heat = hm->EstimateMaxCurrentHeat();
		max_heat = hm->GetMaxHeat();
	}

	FireParamPackage params(fire_data,
							who,
							m_MinFiringDelay,
							m_MaxFiringDelay,
							who_to_hit,
							vehicle_shooting_at,
							component,
							m_ApproximateTimeBetweenFrames,
							cur_heat,
							max_heat,
							can_spend_ammo,
							SuicideIsOK(),
							fire_source,
							CanFireNARC(),
							CanFireHeatGeneratingWeapons(),
							m_PerWeaponRayCasting);

	if (LineMightHitFriendlyUnits(fire_data.GetLine()) == true)
	{
		#ifdef LAB_ONLY
		m_LastFireRejected = true;
		#endif

		m_FiringAttemptMonitor.Notify(false);

		return;
	}

	WeaponUpdate* weapon_update = new WeaponUpdate;
	weapon_update->originator.Add(&(GetSelf()));
	if (who_to_hit)
		weapon_update->target.Add(who_to_hit);
	else
		weapon_update->target.Remove();

	style.Execute(params,GetSelf().weaponChain,who_to_hit,*weapon_update);

	if (weapon_update->weaponFired == 0)
	{
		m_FiringAttemptMonitor.Notify(false);

	#ifdef LAB_ONLY
		m_LastFireRejected = true;
	#endif
	}
	#ifdef LAB_ONLY
	else
	{
		m_LastFireRejected = false;
	}
	#endif

	MWEntityManager* entity_manager = Cast_Object(MWEntityManager*,Adept::EntityManager::GetInstance());
	entity_manager->AIWeaponUpdate(weapon_update);

	m_LastFireTime = (Stuff::Scalar)gos_GetElapsedTime();
}

void CombatAI::MultiTurretFire(MW4AI::FireStyles::FireStyle& style)
{
	secondary_target_list targets;
	targets.push_back(&GetTarget());
	InitializeSecondaryTargets(targets);

	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(GetSelf().weaponChain));
	MechWarrior4::Weapon* weapon = 0;
	while ((weapon = i.ReadAndNext()) != 0)
	{
		if ((weapon->ReadyToFire() == true) &&
			(weapon->GetGameModel() != 0))
		{
			bool skip = false;
			{for (std::list<CachedTorsoRayCastFailure>::iterator i = m_CachedTorsoRayCastFailures.begin();
				  i != m_CachedTorsoRayCastFailures.end();
				  ++i)
			{
				if ((*i).weapon == weapon)
				{
					if ((*i).time + multi_torso_raycast_failure_refresh > (Stuff::Scalar)gos_GetElapsedTime())
					{
						skip = true;
					}

					break;
				}
			}}

			if (skip == true)
			{
				continue;
			}

			Stuff::LinearMatrix4D weapon_matrix(weapon->sitePointer->GetLocalToWorld());
			Stuff::UnitVector3D unit_forward;
			weapon_matrix.GetLocalForwardInWorld(&unit_forward);
			Stuff::Line3D line((Stuff::Point3D)weapon_matrix,
							   unit_forward,
							   weapon->GetGameModel()->maxDistance);

			MWObject* nearest = &GetTarget();
			
			if (nearest == 0)
			{
				return;
			}

			bool should_hit(ShotShouldHit(*nearest));
			MWObject* desired_hit_result = nearest;

			Stuff::Point3D target_point;

			if (should_hit == true)
			{
				component_vector components;
				DamageObjectChainToVector(nearest->damageObjects,components);
				Adept::DamageObject* damage_object = GetComponent_MostDamaged(*nearest,components);

				if ((damage_object != 0) &&
					(damage_object->parentEntity != 0))
				{
					target_point = damage_object->parentEntity->GetLocalToWorld();
				}
				else
				{
					target_point = nearest->GetLineOfSight();
				}
			}
			else
			{
				Stuff::Point3D self_pos(GetSelf().GetLocalToWorld());
				Stuff::Point3D target_pos(nearest->GetLocalToWorld());

				target_point = GetNewAimPoint_ToMiss(*nearest,self_pos);
				desired_hit_result = 0;
			}
			m_LastAimPoint = target_point;

			if ((MW4AI::WeaponCanFire(*weapon,
									 GetApproximateLength(target_point,(Stuff::Point3D)weapon_matrix),
									 m_MinFiringDelay,
									 m_MaxFiringDelay) == true) &&
				(MatrixFacesPoint(weapon_matrix,target_point,GetMaxFireCheatAngle()) == true))
			{
				Stuff::Point3D forward(unit_forward);
				forward *= 4.0f;
				forward += (Stuff::Point3D)weapon_matrix;

				FireData fire_data(GetSelf(),*weapon,target_point);
				fire_data.GetQuery().m_raySource = 0;
				fire_data.GetLine().m_origin = forward;
				fire_data.Project();

				if (fire_data.GetHitResult(desired_hit_result) == MW4AI::FireData::HIT_TARGET)
				{
					if (should_hit == true)
					{
						MW4AI::SetRaySourceToBestComponent(fire_data.GetQuery());
					}
					else
					{
						fire_data.GetQuery().m_raySource = 0;
					}

					//weapon->FireWeapon(&fire_data.GetQuery(),100.0f,Stuff::Point3D::Identity);
					MW4AI::FireSource fire_source = GetBestFireSource(GetTarget());
					Stuff::LinearMatrix4D source_matrix = GetFireSourceMatrix(fire_source);
					Fire(*nearest,
						 0,
						 fire_source,
						 source_matrix,
						 m_LastAimPoint,
						 style,
						 true);
				}
				else
				{
					std::list<CachedTorsoRayCastFailure>::iterator found = m_CachedTorsoRayCastFailures.end();
					{for (std::list<CachedTorsoRayCastFailure>::iterator i = m_CachedTorsoRayCastFailures.begin();
						  i != m_CachedTorsoRayCastFailures.end();
						  ++i)
					{
						if ((*i).weapon == weapon)
						{
							found = i;
							break;
						}
					}}

					if (found == m_CachedTorsoRayCastFailures.end())
					{
						CachedTorsoRayCastFailure ctrcf;
						ctrcf.weapon = weapon;
						ctrcf.time = (Stuff::Scalar)gos_GetElapsedTime() + Stuff::Random::GetFraction();
						m_CachedTorsoRayCastFailures.push_back(ctrcf);
					}
					else
					{
						(*found).time = (Stuff::Scalar)gos_GetElapsedTime() + Stuff::Random::GetFraction();
					}
				}
			}
		}
	}
}

void CombatAI::StartAttacking(Tactics::TacticID tactic, bool fOverrideCurrentMove, bool is_explicit)
{
	Verify(GetAttackState() != ATTACKING);
	Verify(Target() != 0);

	while (m_MostRecentTactics.size() > max_most_recent_tactics_list_size)
	{
		m_MostRecentTactics.erase(m_MostRecentTactics.begin());
	}

	m_MostRecentTactics.push_back(tactic);

	m_Tactic = Tactics::CreateTactic(m_Interface,tactic);
	m_Tactic->SetExplicit(is_explicit);
	m_OverridingMovement = fOverrideCurrentMove;
	m_ExplicitFirePoint.Zero();
	m_ExplicitFireStretch = 0;
	m_LastUnableToFireTime = 0;
	m_LastTimeSwitchedTargets = (Stuff::Scalar)gos_GetElapsedTime();

	if (m_OverridingMovement == true)
	{
		StopMoving();
	}

	SetWeaponsToMaxWaitValue(GetSelf());
}

void CombatAI::StopAttacking()
{
//	Verify(GetAttackState() != NOT_ATTACKING);

	if (m_Tactic.GetPointer() != 0)
	{
		m_Tactic.Delete();
	}

	m_LastAimPoint.Zero();
	m_ExplicitFirePoint.Zero();
	m_ExplicitFireStretch = 0;
	m_LastMoveOrderPosition.Delete();
	m_LastUnableToFireTime = 0;

	KillTracking();

	m_OverridingMovement = false;

	ResetTorso();
}

void CombatAI::AddStatsToString(std::string& s)
{
	AutoHeap local_heap (g_CombatAIHeap);

	MoverAI::AddStatsToString(s);

	if (Target() != 0)
	{
		if (GetAttackState() == ATTACKING)
		{
			s += "ATTACKING\n";

			s += "TACTIC: ";
			s += m_Tactic->GetName();

			{for (std::vector<CanSeeResult>::const_iterator i = m_CanSeeResults.begin();
				  i != m_CanSeeResults.end();
				  ++i)
			{
				if ((*i).m_Entity == GetTarget().objectID)
				{
					if ((*i).m_Result == MW4AI::FireData::HIT_TARGET)
					{
						s += "  CanSee: TRUE!";
					}
					else
					{
						s += "  CanSee: false";
					}
					break;
				}
			}}
//			s += " (";
//			m_Tactic->AddStatsToString(m_Interface,s);
//			s += ")";
			// TODO: put some kind of diagnostics back in
		}
		else
		{
			s += "NOT ATTACKING";
		}
	}

	s += "\nGunnery skill: ";
	s += IntToString(GunnerySkill());
	s += "\n";
}

Stuff::Scalar CombatAI::CalcTorsoTwistDemand(const Point3D& point, Torso& torso, bool for_multi_torso)
{
	const Stuff::Scalar TWIST_LEFT = 1;
	const Stuff::Scalar TWIST_RIGHT = -1;

	Stuff::Scalar body_yaw;

	if (for_multi_torso == true)
	{
		body_yaw = YawToPoint(torso.twistJoint->GetLocalToWorld(),point);
	}
	else
	{
		body_yaw = YawToPoint(GetSelf().GetLocalToWorld(),point);
	}

	const Stuff::Scalar twist_max(GetModifiedMaxTorsoTwistSpeed(torso));

															// if we can rotate 360, or the point is in front of us, turn based on torso only
	if (
		 (
		   (torso.GetGameModel() != 0) &&
		   (torso.GetGameModel()->twistRadius > (Pi - 0.02f))
		 )
	       ||
		 (Stuff::Fabs(body_yaw) < (Pi_Over_2))
	   )
	{
															// special case for when we are twisted past 180 degrees
		if ((Stuff::Fabs(body_yaw) < (Pi_Over_2)) &&
			(Stuff::Fabs(torso.yawValue) > (Pi_Over_2)))
		{
			if (torso.yawValue < 0)
			{
// MSL 5.05
				if (GetSelf().IsDerivedFrom(Mech::DefaultData) == true)
				{
// MSL 5.00 - Added if ShutDown then TursoTwist is set to 0
	                if (m_ShutDownDueToHeat == true)
		            {
			            return (0);
					}
                }
				return (TWIST_LEFT * twist_max);
			}

			return (TWIST_RIGHT * twist_max);
		}

		const Stuff::Scalar torso_yaw = YawToPoint(torso.GetTwistJointMatrix(),point);

		Stuff::Scalar twist_rate = twist_max * Stuff::Fabs(torso_yaw);
		Clamp(twist_rate,0,1);

// MSL 5.05
		if (GetSelf().IsDerivedFrom(Mech::DefaultData) == true)
		{
// MSL 5.00 - Added if ShutDown then TursoTwist is set to 0
	        if (m_ShutDownDueToHeat == true)
		    {
            return (0);
			}
        }
		if (torso_yaw < 0)
		{
			return (TWIST_RIGHT * twist_rate);
		}

		return (TWIST_LEFT * twist_rate);
	}

														// otherwise, turn based on body
	if (body_yaw > 0)
	{
		return (TWIST_LEFT * twist_max);
	}

	return (TWIST_RIGHT * twist_max);
}

Stuff::Scalar CombatAI::GetModifiedMaxTorsoTwistSpeed(Torso& torso)
{
	Stuff::Scalar elite((Stuff::Scalar)EliteSkill());

	Verify(UserConstants::Instance() != 0);

	Stuff::Scalar max_speed(torso.GetTwistSpeed());
	Stuff::Scalar min_skill(UserConstants::Instance()->Get(UserConstants::torso_twist_use_full_multiplier));
	if (elite >= min_skill)
	{
		return (max_speed);
	}

	Stuff::Scalar normalized_skill(elite / min_skill);

	Stuff::Scalar min_speed(UserConstants::Instance()->Get(UserConstants::torso_twist_multiplier_at_0));
	min_speed *= max_speed;

	return (min_speed + ((max_speed - min_speed) * normalized_skill));
}

bool CombatAI::CanTrack()
{
	if ((GetSelf().GetTorso() == 0) ||
		(CanAct() == false))
	{
		return (false);
	}

	if ((m_CurMove != 0) &&
		(m_CurMove->MoveType() == MOVE_LOOKOUT))
	{
		return (false);
	}

	return (true);
}

void CombatAI::TrackToPoint(const Point3D& point)
{
	Execute_TrackToPoint(point);
}

void CombatAI::Execute_TrackToPoint(const Point3D& point)
{
	COMBAT_LOGIC("Update Attacking::Torso Twisting");
	TIME_FUNCTION(tCombatAITime_TorsoTwisting);

	if (CanTrack() == false)
	{
		return;
	}

	KillTracking();

	Stuff::Point3D aim_point(point);

	bool calc_independent_targets = false;
	secondary_target_list targets;

	if (GetSelf().GetTorsos().size() > 1)
	{
		calc_independent_targets = true;
		targets.push_back(&GetTarget());
		InitializeSecondaryTargets(targets);
	}

	{for (std::vector<Torso*>::const_iterator i = GetSelf().GetTorsos().begin();
		  i != GetSelf().GetTorsos().end();
		  ++i)
	{
		Torso* torso = *i;
		Check_Object(torso);

		if (calc_independent_targets == true)
		{
			Stuff::Point3D joint_pos(torso->twistJoint->GetLocalToWorld());
			Stuff::Point3D my_pos(GetSelf().GetLocalToWorld());

			my_pos.y = joint_pos.y;
			Stuff::Point3D diff;
			diff.Subtract(joint_pos,my_pos);
			diff.y = 0;

			if (Small_Enough(diff.GetLengthSquared()) == true)
			{
				return;
			}

			Stuff::UnitVector3D direction(diff);

			Stuff::Line3D line(joint_pos,
							   direction,
							   1000.0f);

			MWObject* nearest_enemy = 0;
			if ((Target() != 0) &&
				(Target()->IsDerivedFrom(MWObject::DefaultData) == true))
			{
				nearest_enemy = Cast_Object(MWObject*,Target());
			}

			if (nearest_enemy != 0)
			{
				aim_point = (Stuff::Point3D)(nearest_enemy->GetLineOfSight());
			}
		}

		Stuff::Point3D long_tom_pitch_point(GetLongTomPitchPoint());
		if (long_tom_pitch_point.GetLengthSquared() != 0)
		{
			torso->PitchToPoint(long_tom_pitch_point);
		}
		else
		{
			torso->PitchToPoint(aim_point);
		}

		torso->yawDemand = CalcTorsoTwistDemand(aim_point,*torso,calc_independent_targets);
	}}
}

bool CombatAI::CanTurn()
{
	if ((CanAct() == false) ||
		(CanMove() == false))
	{
		return (false);
	}

	if ((m_OverridingMovement == false) &&
		(m_ExplicitFirePoint.GetLengthSquared() == 0))
	{
		return (false);
	}

	if (GetSelf().IsDerivedFrom(Airplane::DefaultData) == true)
	{
		return (true);
	}

	if ((GetSelf_AsVehicle() != 0) &&
		(m_LastMoveDest == Vector3D(0,0,0)) &&
		(m_TimeNotMoving > 0.5f))
	{
		if ((m_OverridingMovement == true) ||
			(m_ExplicitFirePoint.GetLengthSquared() != 0))
		{
			if ((GetSelf_AsVehicle() == 0) ||
				(Small_Enough(GetSelf_AsVehicle()->currentSpeedKPH) == true))
			{
				return (true);
			}
		}
	}

	return (false);
}

void CombatAI::TurnToPoint(const Point3D& point, bool must_be_torso_twisted)
{
	if (CanTurn() == false)
	{
		return;
	}

	if (GetSelf().IsDerivedFrom(Airplane::DefaultData) == true)
	{
		Airplane* airplane = Cast_Object(Airplane*,&(GetSelf()));

		if ((airplane->executionState->GetState() != Airplane::ExecutionStateEngine::NeverExecuteState) &&
			(airplane->executionState->GetState() != Airplane::ExecutionStateEngine::AlwaysExecuteState) &&
			(airplane->executionState->GetState() != Airplane::ExecutionStateEngine::TaxiState))
		{
			Execute_TurnToPoint(point);
		}

		return;
	}

	const Torso::GameModel *model = GetSelf().GetTorso()->GetGameModel();
	Check_Object(model);
	Stuff::Scalar twist_radius = model->twistRadius;

	if (twist_radius > Pi_Over_4)
	{
		twist_radius = Pi_Over_4;
	}
	else
	{
		twist_radius *= 0.8f;
	}

	if ((must_be_torso_twisted == false) ||
		(GetSelf().GetTorso() == 0))
	{
		Execute_TurnToPoint(point);
		return;
	}

	if (Fabs(GetSelf().GetTorso()->yawValue) > twist_radius)
	{
		Execute_TurnToPoint(point);
	}
}

void CombatAI::FacePointOrLookOut(const Stuff::Point3D& point)
{
	Stuff::Scalar yaw(YawToPoint(GetSelf().GetLocalToWorld(),point));

	if (Fabs(yaw) > 0.2f)
	{
		ClearMoveOrder();
		Execute_TurnToPoint(point);
	}
	else
	{
		if (m_TimeNotMoving > 6.5f)
		{
			orderMoveLookOut();
		}
	}
}

void CombatAI::PitchToPoint(const Stuff::Point3D& point)
{
	if (GetSelf().IsDerivedFrom(Airplane::DefaultData) == false)
	{
		return;
	}

	// TODO: this is the same code as in Torso::PitchToPoint  ...  please replace!
	LinearMatrix4D pitch_matrix(GetSelf().GetLocalToWorld());

	UnitVector3D unit_forward;
	pitch_matrix.GetLocalForwardInWorld(&unit_forward);

	Point3D projection(unit_forward);

	Stuff::Vector3D delta_to_point;
	delta_to_point.Subtract((Stuff::Point3D)pitch_matrix,point);

	Stuff::Scalar distance = delta_to_point.GetApproximateLength();
	
	projection *= distance;
	projection += (Stuff::Point3D)pitch_matrix;

	Stuff::Scalar pitch = Tan(Fabs(point.y - projection.y) / distance);

	if (pitch > 1)
	{
		pitch = 1;
	}

	Airplane* plane = Cast_Object(Airplane*,&GetSelf());

	plane->pitchDemand = (plane->GetGameModel()->pitchSpeed) * pitch;

	if (point.y > projection.y)
	{
		plane->pitchDemand *= -1.0f;
	}
}

bool CombatAI::ShotShouldHit(MWObject& target)
{
	Stuff::Scalar chance_to_hit = GetBaseChanceToHit();

	AddTowardGunnery ();
	ModifyChanceToHit(chance_to_hit,target,GetSelf().GetLocalToWorld(),ApproximateDistanceToTarget());

	if (chance_to_hit <= 0)
	{
		#ifdef LAB_ONLY
		if (MW4AI::Statistics::Enabled() == true)
		{
			MW4AI::Statistics::NotifyToHitRoll(GetSelf().objectID,GetBaseChanceToHit(),0,0);
		}
		#endif

		return (false);
	}

	const Stuff::Scalar fraction = Stuff::Random::GetFraction();

	#ifdef LAB_ONLY
	if (MW4AI::Statistics::Enabled() == true)
	{
		MW4AI::Statistics::NotifyToHitRoll(GetSelf().objectID,GetBaseChanceToHit(),chance_to_hit,fraction);
	}
	#endif
	
	return (fraction < chance_to_hit);
}

void CombatAI::Info(void (*printCallback)(char* s))
{
	AutoHeap local_heap (g_CombatAIHeap);
	inherited::Info(printCallback);
	std::string stats;
	AddStatsToString(stats);
	printCallback((char*)stats.c_str());
}

void CombatAI::GetDirectPath(std::vector<Stuff::Point3D>& points)
{
	AutoHeap local_heap (g_CombatAIHeap);

/*
	{for (std::vector<Torso*>::const_iterator i = GetSelf().GetTorsos().begin();
		  i != GetSelf().GetTorsos().end();
		  ++i)
	{
		Torso* torso = *i;
		Check_Object(torso);

		Stuff::LinearMatrix4D matrix(torso->twistJoint->GetLocalToWorld());

		points.push_back((Stuff::Point3D)matrix);

		Stuff::UnitVector3D unit_forward;
		matrix.GetLocalForwardInWorld(&unit_forward);

		Stuff::Point3D forward(unit_forward);
		forward *= 10;
		forward += (Stuff::Point3D)matrix;

		points.push_back(forward);
	}}
*/

/*
	// show line from GetLineOfSight()
	points.push_back(GetSelf().GetLineOfSight());

	Stuff::LinearMatrix4D matrix;
	GetSelf().GetLineOfSight(matrix);
	Stuff::UnitVector3D forward;
	matrix.GetLocalForwardInWorld(&forward);
	Stuff::Point3D f(forward);
	f *= 10.0f;
	f += GetSelf().GetLineOfSight();
	points.push_back(f);
*/

	// show combat ai dest
	if (m_LastMoveDest == Vector3D(0,0,0))
	{
		return;
	}

	Stuff::Point3D start(GetSelf().GetLocalToWorld());
	start.y += 3.0f;
	points.push_back(start);

	Stuff::Point3D end(m_LastMoveDest);
	end.y = start.y;
	points.push_back(end);

/*
	// show airplane collision height lines
	UnitVector3D unit_forward;
	GetSelf().GetLocalToWorld().GetLocalForwardInWorld(&unit_forward);
	Point3D forward(unit_forward);
	forward *= 25.0f;

	Point3D point(GetSelf().GetLocalToWorld());
	point += forward;
	point.y += 100.0f;

	Point3D point2(GetSelf().GetLocalToWorld());
	forward *= 4.0f;
	point2 += forward;
	point2.y += 100.0f;

	UnitVector3D unit_left;
	GetSelf().GetLocalToWorld().GetLocalLeftInWorld(&unit_left);
	Point3D left(unit_left);
	left *= 30.0f;

	UnitVector3D unit_right;
	GetSelf().GetLocalToWorld().GetLocalRightInWorld(&unit_right);
	Point3D right(unit_right);
	right *= 30.0f;

	Point3D point3(point2);
	
	point2 += left;
	point3 += right;

	points.push_back(point);
	point.y -= 500;
	points.push_back(point);

	points.push_back(point2);
	point2.y -= 500;
	points.push_back(point2);

	points.push_back(point3);
	point3.y -= 500;
	points.push_back(point3);
*/
}

void CombatAI::FloatToPoint(const Stuff::Point3D& point)
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (GetSelf().IsDerivedFrom(Airplane::DefaultData) == false)
	{
		return;
	}

	StopMoving();

	m_LastMoveDest = point;
	m_ForceDestinationRecalc = false;

	m_LastMoveOrderPosition.Assimilate(new Stuff::LinearMatrix4D(GetSelf().GetLocalToWorld()));

	SetRamTarget(0);

	Airplane* airplane = Cast_Object(Airplane*,&GetSelf());
	Check_Object(airplane);

	airplane->Float(point);
}

bool CombatAI::MoveRequestPending()
{
	bool rv(false);

	if ((m_CurMove == 0) ||
		(m_CurMove->Done() == true))
	{
		rv = false;
		m_MoveRequestFinishedTime = 0;
		return (false);
	}
	else
	{
		if ((m_CurPath != 0) &&
			(m_CurPath->Calced() == true))
		{
			if (m_CurPath->State () == CRailPath::RAIL_MOVE_WAITING)
			{
				rv = true;
			}
			else
			{
				rv = false;
			}
		}
		else
		{
			rv = true;
		}
	}

	if (rv == true)
	{
		m_MoveRequestFinishedTime = 0;
		return (true);
	}

	if (m_MoveRequestFinishedTime == 0)
	{
		m_MoveRequestFinishedTime = gos_GetElapsedTime();
	}

	return (m_MoveRequestFinishedTime + min_move_request_wait > gos_GetElapsedTime());
}

void CombatAI::MoveToPoint(const Stuff::Point3D& point)
{
	if (GetSelf_AsVehicle() == 0)
	{
		return;
	}

	if ((m_ForceDestinationRecalc == false) &&
		(MoveRequestPending() == true))
	{
		return;
	}

	m_LastMoveDest = point;
	m_ForceDestinationRecalc = false;

	m_LastMoveOrderPosition.Assimilate(new Stuff::LinearMatrix4D(GetSelf().GetLocalToWorld()));

	SetRamTarget(0);

	orderMoveToLocPoint(point,
						(int)MaxVehicleSpeed(),
						true,
						false);
}

void CombatAI::MoveToPoint(const Stuff::Point3D& point, bool move_forward, bool dont_use_throttle, MWObject* ram_target)
{
	if (CanMove() == false)
	{
		return;
	}

	if ((m_ForceDestinationRecalc == false) &&
		(MoveRequestPending() == true))
	{
		return;
	}

	SetRamTarget(ram_target);

	m_LastMoveDest = point;
	m_ForceDestinationRecalc = false;

	Stuff::Scalar speed;

	if (move_forward == false)
	{
		speed = -MaxVehicleSpeed();
	}
	else
	{
		if (dont_use_throttle == true)
		{
			speed = MaxVehicleSpeed();
		}
		else
		{
			speed = RecommendedCurrentSpeed();
		}
	}

	m_LastMoveOrderPosition.Assimilate(new Stuff::LinearMatrix4D(GetSelf().GetLocalToWorld()));

	orderMoveToLocPoint(point,
						(int)speed,
						move_forward,
						false);
}

void CombatAI::NotifyProjectileApproaching(const Stuff::Point3D& origin)
{
	m_LastTimeProjectileShotAtMe = (Stuff::Scalar)gos_GetElapsedTime();
	m_LastProjectileOrigin = origin;
}

void CombatAI::NotifyInTargetingReticule(MWObject& who)
{
	Check_Object(&who);

	if (m_LastTimeTargeted != 0)
	{
		m_TimeBeingTargeted += ((Stuff::Scalar)gos_GetElapsedTime() - m_LastTimeTargeted);
	}

	m_LastTimeTargeted = (Stuff::Scalar)gos_GetElapsedTime();
	m_LastTargetedAlignment = who.GetAlignment();
}

void CombatAI::NotifyHeatShutdownImminent()
{
	inherited::NotifyHeatShutdownImminent();

	m_WaitingForHeatToReachMinSkill = true;

	ModifyMood(UserConstants::Instance()->Get(UserConstants::shutdown));

	m_ShutDownDueToHeat = true;

	m_UsingCoolant = true;
	GetSelf().SetCooling(1);
}

void CombatAI::NotifyFriendlyFire(Adept::Entity& who_shot_me)
{
	inherited::NotifyFriendlyFire(who_shot_me);

	if (m_SquadOrders.GetPointer() != 0)
	{
		m_SquadOrders->NotifyFriendlyFire(who_shot_me);
	}
}

void CombatAI::UpdateWaitingForHeatToReachZero()
{
	if (m_ShutDownDueToHeat == true)
	{
		if (GetSelf().vehicleShutDown == false)
		{
			m_ShutDownDueToHeat = false;
		}
		else
		{
			GetSelf().ShutDownRequest(false);
		}
	}

	if (m_WaitingForHeatToReachMinSkill == true)
	{
		bool stop_waiting(false);

		HeatManager* hm = GetSelf().m_heatManager;
		if (hm != 0)
		{
			if (GetNormalizedCurrentHeat() <= ((Stuff::Scalar)MinHeatSkill() / 100.0f) + 0.01f)
			{
				stop_waiting = true;
			}
		}
		else
		{
			stop_waiting = true;
		}

		bool turn_off_coolant(false);

		if (stop_waiting == true)
		{
			m_WaitingForHeatToReachMinSkill = false;
			turn_off_coolant = true;
		}
		else
		{
			if ((GetUnableToFireDuration(1.0f) == true) ||
				(Target() == 0) ||
				(GetAttackState() == NOT_ATTACKING) ||
				(Target()->IsDestroyed() == true))
			{
				turn_off_coolant = true;
			}
		}

		if ((turn_off_coolant == true) &&
			(m_UsingCoolant == true))
		{
			m_UsingCoolant = false;
			GetSelf().SetCooling(0);
		}
	}
	else
	{
		if ((GetSelf().m_heatManager != 0) &&
			(GetSelf().m_heatManager->GetHeat() > 0))
		{
			if (GetNormalizedCurrentHeat() >= ((Stuff::Scalar)MaxHeatSkill() / 100.0f) - 0.01f)
			{
				m_WaitingForHeatToReachMinSkill = true;

				if ((m_UsingCoolant == false) &&
					(GetSelf().m_heatManager != 0))
				{
					m_UsingCoolant = true;
					GetSelf().SetCooling(1);
				}
			}
		}
	}
}

void CombatAI::UpdateBeingTargeted()
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (m_LastTimeTargeted == 0)
	{
		m_TimeBeingTargeted = 0;
	}
	else
	{
		if (m_LastTimeTargeted + being_targeted_dissipate_time < gos_GetElapsedTime())
		{
			m_LastTimeTargeted = 0;
			m_TimeBeingTargeted = 0;
		}
	}
}

void CombatAI::SetSpeed(Stuff::Scalar speed)
{
	AutoHeap local_heap (g_CombatAIHeap);

	if (CanMove() == false)
	{
		return;
	}

	CommandEntry* ce = CreateSetSpeedCommand(speed);
	Check_Pointer(ce);
	ExecuteCommand(ce);
}

void CombatAI::ThrottleOverride(Stuff::Scalar duration, bool force)
{
	Verify(UserConstants::Instance() != 0);

	Stuff::Scalar elite((Stuff::Scalar)EliteSkill());

	if ((force == false) &&
		(elite < UserConstants::Instance()->Get(UserConstants::min_throttle_override)))
	{
		return;
	}

	m_ThrottleOverrideStopTime = (Stuff::Scalar)gos_GetElapsedTime() + duration;

	SetSpeed(1.0f);
}

void CombatAI::UpdateThrottleOverride()
{
	if (m_ThrottleOverrideStopTime == 0)
	{
		return;
	}

	if (m_ThrottleOverrideStopTime < gos_GetElapsedTime())
	{
		m_ThrottleOverrideStopTime = 0;
		SetSpeed(RecommendedCurrentSpeed()/MaxVehicleSpeed());
		return;
	}
}

Stuff::Scalar CombatAI::MaxVehicleSpeed()
{
	const Vehicle__GameModel* vehicle_model = static_cast<const Vehicle__GameModel*>(GetSelf_AsVehicle()->GetGameModel());
	Check_Object(vehicle_model);

	return (vehicle_model->maxSpeed);
}

Stuff::Scalar CombatAI::RecommendedCurrentSpeed()
{
	if ((Target() != 0) &&
		(GetLengthSquared((Stuff::Point3D)GetSelf().GetLocalToWorld(),
						  (Stuff::Point3D)GetTarget().GetLocalToWorld()) < (auto_full_throttle_range * auto_full_throttle_range)))
	{
		return ((MaxVehicleSpeed() * m_AttackThrottle) / 100);
	}

	if ((Stuff::Scalar)EliteSkill() < UserConstants::Instance()->Get(UserConstants::min_throttle_override))
	{
		return ((MaxVehicleSpeed() * m_AttackThrottle) / 100);
	}

	return (MaxVehicleSpeed());
}

void CombatAI::NotifyShot(ObjectID who, bool fCanBeRecursive)
{
	AutoHeap local_heap (g_CombatAIHeap);
	inherited::NotifyShot(who,false);

	if (fCanBeRecursive == true)
	{
		Adept::Entity* entity = NameTable::GetInstance()->FindData(who);

		if ((entity != 0) &&
			(entity->IsDestroyed() == false) &&
			(entity != getEntity()) &&
			(getEntity() != 0) &&
			(entity->GetAlignment() != getEntity()->GetAlignment()))
		{
			Verify(Adept::Mission::GetInstance() != 0);
			Verify(Adept::Mission::GetInstance()->IsDerivedFrom(MechWarrior4::MWMission::DefaultData) == true);

			MechWarrior4::MWMission* mission = Cast_Object(MechWarrior4::MWMission*,Adept::Mission::GetInstance());
			Check_Object(mission);

			GroupContainer& gc = mission->GetGroupContainer();

			{for (MWObject::GroupList::iterator i = GetSelf().GetGroups().begin();
				  i != GetSelf().GetGroups().end();
				  ++i)
			{
				if (gc.GroupExists(*i) == true)
				{
					Group& group = gc.GetGroup(*i);
					group.NotifyShot(who);
				}
			}}
		}
	}
}

void CombatAI::NotifyLastShotWasFriendlyFire(Adept::Entity& who_i_hit)
{
	AutoHeap local_heap (g_CombatAIHeap);
	Check_Object(&who_i_hit);

	if (Target() == 0)
	{
		return;
	}

	if (who_i_hit.objectID != GetSelf().objectID)
	{
		if (GetTarget().objectID == who_i_hit.objectID)
		{
			if ((m_Tactic.GetPointer() == 0) ||
				(m_Tactic->GetID() != MW4AI::Tactics::TACTIC_DEFEND))
			{
				return;
			}
		}

		if ((m_LastFriendlyFireTime != 0) &&
			(m_LastFriendlyFireTime == (Stuff::Scalar)gos_GetElapsedTime()))
		{
			return;
		}

		if (m_FriendlyFireDelay <= 0)
		{
			m_FriendlyFireDelay = initial_friendly_fire_delay;
		}
		else
		{
			m_FriendlyFireDelay *= friendly_fire_multiplier;
		}

		m_LastFriendlyFireTime = (Stuff::Scalar)gos_GetElapsedTime();
	}
}

void CombatAI::UpdateFriendlyFireTiming()
{
	if (m_FriendlyFireDelay != 0)
	{
		if ((m_FriendlyFireDelay < 0) ||
			(m_FriendlyFireDelay > max_friendly_fire_delay))
		{
			m_FriendlyFireDelay = max_friendly_fire_delay;
		}

		if (gos_GetElapsedTime() > m_LastFriendlyFireTime + (m_FriendlyFireDelay * 2))
		{
			m_LastFriendlyFireTime = 0;
			m_FriendlyFireDelay = 0;
		}
	}
}

void CombatAI::NotifyAlignmentChanged()
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (Target() == 0)
	{
		return;
	}

	if ((m_Tactic.GetPointer() != 0) &&
		(m_Tactic->GetID() == MW4AI::Tactics::TACTIC_DEFEND))	// TODO: clean this up -- should not have to match by ID
	{
		if (GetTarget().GetAlignment() != GetSelf().GetAlignment())
		{
			Reset();
		}
	}
	else
	{
		if (GetTarget().GetAlignment() == GetSelf().GetAlignment())
		{
			Reset();
		}
	}
}

void CombatAI::EnsureNotTargeting(Adept::ObjectID object)
{
	AutoHeap local_heap (g_CombatAIHeap);
	if ((Target() != 0) &&
		(GetTarget().objectID == object))
	{
		Reset();
	}
}

bool CombatAI::PointIsValid(const Point3D& point, bool ignore_mission_bounds, bool ignore_squads) const
{
	AutoHeap local_heap (g_CombatAIHeap);
	if (IsDerivedFrom(MechWarrior4::Airplane::DefaultData) == true)
	{
		return (true);
	}

	if ((point.x <= MinX) ||
		(point.x >= MaxX) ||
		(point.z <= MinZ) ||
		(point.z >= MaxZ))
	{
		return (false);
	}

	Verify(Adept::Mission::GetInstance() != 0);
	Verify(Adept::Mission::GetInstance()->IsDerivedFrom(MechWarrior4::MWMission::DefaultData) == true);
	MechWarrior4::MWMission* mission = Cast_Object(MechWarrior4::MWMission*,Adept::Mission::GetInstance());

	if (ignore_mission_bounds == false)
	{
		if (mission->GetMissionArea(point) != MWMission::InMissionArea)
		{
			return (false);
		}
	}

	if (m_CombatLeashRadius != 0)
	{
		if (GetLengthSquared(point,m_CombatLeash) > m_CombatLeashRadius * m_CombatLeashRadius)
		{
			return (false);
		}
	}

	if (PointIsOK(point) == false)
	{
		return (false);
	}

	if ((ignore_squads == false) &&
		(m_SquadOrders.GetPointer() != 0) &&
		(m_SquadOrders->PointIsValid(point) == false))
	{
		return (false);
	}

	return (true);
}

void CombatAI::NotifyShotFired(const Adept::Entity::CollisionQuery& query, MWObject& shooter)
{
	AutoHeap local_heap (g_CombatAIHeap);
	COMBAT_LOGIC("NotifyShot");
	TIME_FUNCTION(tCombatAITime_NotifyShot);

	inherited::NotifyShotFired(query,shooter);

	Verify(query.m_line != 0);

	if (getEntity() == &shooter)
	{
	}
	else
	{
		if ((GetSelf().GetGroups().size() > 0) &&
			(shooter.GetGroups().size() > 0))
		{
			{for (MWObject::GroupList::const_iterator i = GetSelf().GetGroups().begin();
				  i != GetSelf().GetGroups().end();
				  ++i)
			{
				MWObject::GroupList::const_iterator found = std::find(shooter.GetGroups().begin(),shooter.GetGroups().end(),*i);

				if (found != shooter.GetGroups().end())
				{
					Group& group_we_are_both_in = Groups::GetGroup(*found);

					group_we_are_both_in.NotifyShotFired(*(query.m_line),GetSelf(),shooter);
				}
			}}
		}
	}
}

void CombatAI::NotifyInternalHit()
{
	AutoHeap local_heap (g_CombatAIHeap);
	Verify(UserConstants::Instance() != 0);

	inherited::NotifyInternalHit();

	if (m_InternalArmorBreached == false)
	{
		ModifyMood(UserConstants::Instance()->Get(UserConstants::internal_armor_breached));
		m_InternalArmorBreached = true;
	}
}

void CombatAI::NotifyComponentDestroyed(int zone)
{
	AutoHeap local_heap (g_CombatAIHeap);
	Verify(m_NumArmsBlownOff >= 0);
	Verify(m_NumArmsBlownOff <= 2);
	Verify(m_NumLegsBlownOff >= 0);
	Verify(m_NumLegsBlownOff <= 2);
	Verify(UserConstants::Instance() != 0);

	inherited::NotifyComponentDestroyed(zone);

	Stuff::Scalar mood_modifier(0);

	switch (zone)
	{
		case Adept::InternalDamageObject::LeftArmZone:
		case Adept::InternalDamageObject::RightArmZone:
		{
			++m_NumArmsBlownOff;
			Verify(m_NumArmsBlownOff <= 2);

			if (m_NumArmsBlownOff == 1)
			{
				mood_modifier = UserConstants::Instance()->Get(UserConstants::first_arm_hit);
			}
			else
			{
				mood_modifier = UserConstants::Instance()->Get(UserConstants::second_arm_hit);
			}

			if (zone == Adept::InternalDamageObject::LeftArmZone)
			{
				m_LeftArmExists = false;
			}
			else
			{
				m_RightArmExists = false;
			}

			break;
		}

		case Adept::InternalDamageObject::LeftLegZone:
		case Adept::InternalDamageObject::RightLegZone:
		{
			++m_NumLegsBlownOff;
			Verify(m_NumLegsBlownOff <= 2);

			mood_modifier = UserConstants::Instance()->Get(UserConstants::first_leg_hit);

			break;
		}
	}

	if (mood_modifier != 0)
	{
		ModifyMood(mood_modifier);
	}
}

void CombatAI::NotifyFailedPilotingRoll()
{
	Verify(UserConstants::Instance() != 0);

	inherited::NotifyFailedPilotingRoll();

	ModifyMood(UserConstants::Instance()->Get(UserConstants::fall_down));
}

void CombatAI::ModifyMood(Stuff::Scalar add)
{
	Stuff::Scalar mood(CurrentMood() + add);
	Clamp(mood,(Stuff::Scalar)MOODMIN,(Stuff::Scalar)MOODMAX);

	CurrentMood(mood);
}

void CombatAI::ModifyChanceToHit(Stuff::Scalar& chance_to_hit, MWObject& target, const Stuff::LinearMatrix4D& my_matrix, Stuff::Scalar distance_from_target)
{
	Verify(UserConstants::Instance() != 0);

	bool fCrouched = false;
	if ((target.animStateEngine != 0) &&
		(target.animStateEngine->GetState() == MechAnimationStateEngine::CrouchState))
	{
		fCrouched = true;
	}

	chance_to_hit *= UserConstants::Instance()->GetMultiplier_Tonnage(target.GetTonage());
	chance_to_hit *= UserConstants::Instance()->GetMultiplier_CrouchState(fCrouched);

	Stuff::Scalar target_speed_kph(0);
	Stuff::Scalar yaw_demand(0);

	if (target.IsDerivedFrom(Vehicle::DefaultData) == true)
	{
		Vehicle* vehicle = Cast_Object(Vehicle*,&target);
		Check_Object(vehicle);

		target_speed_kph = Stuff::Fabs(vehicle->currentSpeedKPH);
		yaw_demand = Stuff::Fabs(vehicle->yawDemand);
	}

	Stuff::Scalar my_speed_kph(0);

	if (GetSelf().IsDerivedFrom(Vehicle::DefaultData) == true)
	{
		my_speed_kph = GetSelf_AsVehicle()->currentSpeedKPH;
	}

	if ((&target == &GetTarget()) &&
		(m_LookState != LOOK_CENTER))
	{
		chance_to_hit *= UserConstants::Instance()->Get(UserConstants::if_firing_with_one_arm);
	}

	chance_to_hit *= UserConstants::Instance()->GetMultiplier_Speed(target_speed_kph);
	chance_to_hit *= UserConstants::Instance()->GetMultiplier_ShooterSpeed(my_speed_kph);
	chance_to_hit *= UserConstants::Instance()->GetMultiplier_Distance(distance_from_target);
	chance_to_hit *= UserConstants::Instance()->GetMultiplier_Bearing((Stuff::Point3D)my_matrix,target.GetLocalToWorld(),target_speed_kph);
	chance_to_hit *= UserConstants::Instance()->GetMultiplier_TurnRate(yaw_demand);

	Stuff::Scalar fog_distance = 1000.0f;

	if (GetIgnoreFog() == false)
	{
		fog_distance = GetFogDistance();
	}

	if (
		 (
		   (fog_distance <= UserConstants::Instance()->Get(UserConstants::max_fog_dist_to_use_blind_skill))
		 )
	   ||
		 (
		   (Adept::Mission::GetInstance() != 0) &&
		   (Adept::Mission::GetInstance()->GetGameModel() != 0) &&
		   (Adept::Mission::GetInstance()->m_isNightMission == true)
		 )
	   )
	{
		chance_to_hit *= (((Stuff::Scalar)BlindFightingSkill()) * 0.01f);
	}

	switch (UserConstants::Instance()->FindCategory(distance_from_target,
													UserConstants::distance_adjacent,
													UserConstants::distance_very_far))
	{
		case UserConstants::distance_adjacent:
		case UserConstants::distance_near:
		case UserConstants::distance_medium:
		{
			chance_to_hit *= (((Stuff::Scalar)ShortRangeGunnerySkill()) * 0.01f);
			break;
		}

		case UserConstants::distance_very_far:
		{
			chance_to_hit *= (((Stuff::Scalar)LongRangeGunnerySkill()) * 0.01f);
			break;
		}
	}
}

void CombatAI::NotifyShutdown()
{
	AutoHeap local_heap (g_CombatAIHeap);
	inherited::NotifyShutdown();
}

void CombatAI::StopMoving()
{
	AutoHeap local_heap (g_CombatAIHeap);
	m_LastMoveDest.Zero();
	m_LastMoveOrderPosition.Delete();
	ClearMoveOrder();
}

void CombatAI::NotifyNoPath()
{
	AutoHeap local_heap (g_CombatAIHeap);
//	StopMoving();
	MoverAI::NotifyNoPath ();

	if (m_Tactic.GetPointer() != 0)
	{
		SuggestPointWasNotAGoodDestination(m_LastMoveDest);
		m_ForceDestinationRecalc = true;
		m_MostRecentNoPathWarnings.push_back((Stuff::Scalar)gos_GetElapsedTime());
	}
	else
	{
		if (m_SquadOrders.GetPointer() != 0)
		{
			m_SquadOrders->NotifyNoPath();
		}
	}
}

bool CombatAI::ReactToCollision(Stuff::DynamicArrayOf<CollisionData> *collisions,bool& shoulddamage)
{
	AutoHeap local_heap (g_CombatAIHeap);
	for (int i=0; i<collisions->GetLength(); ++i)
	{
		CollisionData *data = &(*collisions)[i];
		if ((Target() != 0) &&
			(data->m_otherEntity == Target()))
		{
			m_LastTimeRammedTarget = (Stuff::Scalar)gos_GetElapsedTime();
			break;
		}
	}

	return inherited::ReactToCollision(collisions,shoulddamage);
}

Stuff::Scalar CombatAI::ApproximateDistanceToTarget()
{
	AutoHeap local_heap (g_CombatAIHeap);
	Stuff::Point3D delta;
	delta.Subtract((Stuff::Point3D)GetSelf().GetLocalToWorld(),
				   (Stuff::Point3D)GetTarget().GetLocalToWorld());

	return (delta.GetApproximateLength());
}

bool CombatAI::GetUnableToFireDuration(Stuff::Scalar duration) const
{
	if (m_LastUnableToFireTime == 0)
	{
		return (false);
	}

	return (m_LastUnableToFireTime + duration < gos_GetElapsedTime());
/*
	const Stuff::Scalar time((Stuff::Scalar)gos_GetElapsedTime());

	if (m_LastTimeSwitchedTargets + duration > time)
	{
		return (false);
	}

	if (m_LastFireTime + duration > time)
	{
		return (false);
	}

		(m_LastUnableToFireTime + duration > time))
	{
		return (false);
	}

	if (m_LastUnableToFireTime == 0)
	{
		return (false);
	}

	return (m_LastUnableToFireTime + duration < gos_GetElapsedTime());


	return (true);
*/
}


MWObject* CombatAI::GetNearest(bool fMustBeEnemy)
{
	COMBAT_LOGIC("Get Nearest");
	TIME_FUNCTION(tCombatAITime_GetNearest);

	if (GetSelf().GetSensor() == 0)
	{
		return (0);
	}

	const int index = (int)fMustBeEnemy;

	if ((m_CachedNearest[index] != -2) &&
		(m_CachedNearestTime[index] + nearest_enemy_refresh > gos_GetElapsedTime()))
	{
		Adept::Entity* cached = NameTable::GetInstance()->FindData(m_CachedNearest[index]);

		if ((cached != 0) &&
			(cached->IsDerivedFrom(MWObject::DefaultData) == true))
		{
			return (Cast_Object(MWObject*,cached));
		}
	}

	std::vector<MWObject*> objects;
	GetSensorList(GetSelf().GetSensor()->RefreshAndGetSensorData(),objects);

	Stuff::Point3D my_pos(GetSelf().GetLocalToWorld());

	Stuff::Scalar best_distance = 0;
	std::vector<MWObject*>::iterator best = objects.end();

	{for (std::vector<MWObject*>::iterator i = objects.begin();
		  i != objects.end();
		  ++i)
	{
		if ((*i)->objectID != GetSelf().objectID)
		{
			if ((fMustBeEnemy == false) ||
				((*i)->GetAlignment() != GetSelf().GetAlignment()))
			{
				Stuff::Scalar distance = GetLengthSquared(my_pos,(Stuff::Point3D)(*i)->GetLocalToWorld());

				if ((best == 0) ||
					(distance < best_distance))
				{
					best = i;
					best_distance = distance;
				}
			}
		}
	}}

	if (best != objects.end())
	{
		m_CachedNearest[index] = (*best)->objectID;
		m_CachedNearestTime[index] = gos_GetElapsedTime();

		return (Cast_Object(MWObject*,*best));
	}
	else
	{
		m_CachedNearest[index] = -2;
		m_CachedNearestTime[index] = 0;
	}

	return (0);
}

Adept::DamageObject* CombatAI::PickComponent(MWObject& who, const component_vector& components)
{
	Verify(UserConstants::Instance() != 0);

	if ((IsMovingQuickly(who) == true) ||
		(components.size() == 0))
	{
		return (GetComponent_TorsoThenTopDown(components));
	}

	Stuff::Scalar elite((Stuff::Scalar)EliteSkill());

	if (elite < UserConstants::Instance()->Get(UserConstants::switch_to_random_component_picking))
	{
		return (GetComponent_LeastDamaged(components));
	}

	if (elite < UserConstants::Instance()->Get(UserConstants::switch_to_most_damaged_component_picking))
	{
		return (GetComponent_Random(components));
	}

	return (GetComponent_MostDamaged(who,components));
}

bool CombatAI::IsMovingQuickly(MWObject& who)
{
	if (who.IsDerivedFrom(Vehicle::DefaultData) == false)
	{
		return (false);
	}

	const Vehicle* v = Cast_Object(const Vehicle*,&who);

	if ((v != 0) &&
		(v->currentSpeedKPH >= min_speed_kph_considered_moving_quickly))
	{
		Stuff::LinearMatrix4D los;
		who.GetLineOfSight(los);
		if (MatrixFacesPoint(los,(Stuff::Point3D)GetSelf().GetLocalToWorld(),Stuff::Pi * 0.5f) == true)
		{
			return (false);
		}

		return (true);
	}

	return (false);
}

void CombatAI::InitializeSecondaryTargets(CombatAI::secondary_target_list& secondary_targets)
{
	if (GetSelf().GetSensor() == 0)
	{
		return;
	}

	secondary_targets.reserve(GetSelf().GetSensor()->numberOfContacts);
	const MechWarrior4::Sensor::sensorDataArray& sensed_data = GetSelf().GetSensor()->RefreshAndGetSensorData();

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

		if ((d != 0) &&
			(d->object.GetCurrent() != 0))
		{
			MWObject* obj = d->object.GetCurrent();

			if ((obj->GetAlignment() != GetSelf().GetAlignment()) &&
				(obj != Target()) &&
				(obj != &GetSelf()) &&
				(obj->GetTargetDesirability() != MWObject::desirability_never) &&
				(obj->GetTargetDesirability() != 0) &&
				(obj->weaponChain.IsEmpty() == false) &&
				(obj->GetAI() != 0))
			{
				secondary_targets.push_back(obj);
			}
		}
	}}
}

CombatAI::secondary_target_list::iterator CombatAI::SelectSecondaryTarget(CombatAI::secondary_target_list& secondary_targets)
{
	Verify(secondary_targets.size() > 0);

	if (secondary_targets.size() == 1)
	{
		return (secondary_targets.begin());
	}

	Stuff::Point3D my_pos = (Stuff::Point3D)GetSelf().GetLocalToWorld();

	secondary_target_list::iterator best = secondary_targets.begin();
	Stuff::Scalar best_distance_squared = GetLengthSquared((Stuff::Point3D)(*best)->GetLocalToWorld(),my_pos);

	secondary_target_list::iterator i = best;
	++i;

	{for (;
		  i != secondary_targets.end();
		  ++i)
	{
		Check_Object(*i);

		Stuff::Scalar distance_squared = GetLengthSquared((Stuff::Point3D)(*i)->GetLocalToWorld(),my_pos);

		if (distance_squared < best_distance_squared)
		{
			best_distance_squared = distance_squared;
			best = i;
		}
	}}

	return (best);
}

void CombatAI::RefreshCanSeeResults()
{
	int i = 0;
	std::vector<CanSeeResult>::iterator iter = m_CanSeeResults.begin();
	while (i < m_CanSeeResults.size())
	{
		if (m_CanSeeResults[i].m_Time + can_see_result_refresh_time < gos_GetElapsedTime())
		{
			m_CanSeeResults.erase(iter);
			RefreshCanSeeResults();
		}
		else
		{
			++i;
			++iter;
		}
	}
}

bool CombatAI::CanSee(MWObject& entity, Stuff::Scalar cheat_angle)
{
	TIME_FUNCTION(tCombatAITime_CanSee);

	Stuff::Point3D hisLOS(entity.GetLineOfSight());

	Stuff::Scalar fog_distance = 1000.0f;

	if (GetIgnoreFog() == false)
	{
		fog_distance = GetFogDistance();
	}

	if (m_Deactive == true)
	{
		if (GetApproximateLength(hisLOS,GetSelf().GetLineOfSight()) >= fog_distance)
		{
			return (false);
		}

		return (true);
	}

	Stuff::LinearMatrix4D my_los;
	GetSelf().GetLineOfSight(my_los);

	if (GetSelf().IsDerivedFrom(Dropship::DefaultData) == true)
	{
		return (GetApproximateLength(hisLOS,(Stuff::Point3D)GetSelf().GetLocalToWorld()) < fog_distance);
	}

	if (MatrixFacesPoint(my_los,hisLOS,cheat_angle) == false)
	{
		switch (m_LookState)
		{
			case LOOK_CENTER:
			{
				return (false);
			}

			case LOOK_LEFT:
			{
				if (MatrixFacesPoint(GetFireSourceMatrix(MW4AI::FIRE_FROM_LEFT_ARM),hisLOS,cheat_angle) == false)
				{
					return (false);
				}
				break;
			}

			case LOOK_RIGHT:
			{
				if (MatrixFacesPoint(GetFireSourceMatrix(MW4AI::FIRE_FROM_RIGHT_ARM),hisLOS,cheat_angle) == false)
				{
					return (false);
				}
				break;
			}

			default:
			{
				Verify(!"Invalid look state.");
				break;
			}
		}
	}

	if (GetLongTomPitchPoint().GetLengthSquared() > 0)
	{
		return (true);
	}

	Stuff::Point3D myLOS(GetSelf().GetLineOfSight());

	if (GetApproximateLength(hisLOS,myLOS) >= fog_distance)
	{
		return (false);
	}

	RefreshCanSeeResults();

	{for (std::vector<CanSeeResult>::const_iterator i = m_CanSeeResults.begin();
		  i != m_CanSeeResults.end();
		  ++i)
	{
		if (((*i).m_Angle == cheat_angle) &&
			((*i).m_Entity == entity.objectID))
		{
			return ((*i).m_Result == MW4AI::FireData::HIT_TARGET);
		}
	}}

	MW4AI::FireData projection(GetSelf(),myLOS,hisLOS);
	projection.Project();

	MW4AI::FireData::HIT_RESULT result(projection.GetHitResult(&entity,true));

	CanSeeResult csr;
	csr.m_Result = result;
	csr.m_Time = (Stuff::Scalar)gos_GetElapsedTime();
	csr.m_Entity = entity.objectID;
	csr.m_Angle = cheat_angle;
	m_CanSeeResults.push_back(csr);

	return (result == MW4AI::FireData::HIT_TARGET);
}

bool CombatAI::GetIgnoringFriendlyFire() const
{
	{for (MWObject::GroupList::const_iterator i = GetSelf().GetGroups().begin();
		  i != GetSelf().GetGroups().end();
		  ++i)
	{
		gosREPORT((Adept::Mission::GetInstance() != 0),"The mission object does not exist");
		gosREPORT((Adept::Mission::GetInstance()->IsDerivedFrom(MechWarrior4::MWMission::DefaultData) == true),"The mission object is not the correct type");

		MechWarrior4::MWMission* mission = Cast_Object(MechWarrior4::MWMission*,Adept::Mission::GetInstance());
		Check_Object(mission);

		if (mission->GetGroupContainer().GroupExists(*i) == true)
		{
			const Group& g = mission->GetGroupContainer().GetGroup(*i);

			if (g.IgnoresFriendlyFire() == true)
			{
				return (true);
			}
		}
	}}

	return (MoverAI::GetIgnoringFriendlyFire());
}

bool CombatAI::GetEscapeRegionFocusIfAvailable(Stuff::Point3D& focus) const
{
	{for (MWObject::GroupList::const_iterator i = GetSelf().GetGroups().begin();
		  i != GetSelf().GetGroups().end();
		  ++i)
	{
		gosREPORT((Adept::Mission::GetInstance() != 0),"The mission object does not exist");
		gosREPORT((Adept::Mission::GetInstance()->IsDerivedFrom(MechWarrior4::MWMission::DefaultData) == true),"The mission object is not the correct type");

		MechWarrior4::MWMission* mission = Cast_Object(MechWarrior4::MWMission*,Adept::Mission::GetInstance());
		Check_Object(mission);

		if (mission->GetGroupContainer().GroupExists(*i) == true)
		{
			const Group& g = mission->GetGroupContainer().GetGroup(*i);

			if (g.HasAI() == true)
			{
				Adept::Entity* leader = g.IDToEntity(*(g.GetElements().begin()));

				if ((leader != 0) &&
					(leader != getEntity()))
				{
					focus = (Stuff::Point3D)leader->GetLocalToWorld();
					return (true);
				}
			}
		}
	}}

	return (false);
}

Stuff::Scalar CombatAI::GetMaxFireCheatAngle()
{
	if (IsLaserTurret() == true)
	{
		return (UserConstants::Instance()->Get(UserConstants::max_turret_fire_cheat_angle) * Radians_Per_Degree);
	}

	if (GetSelf().IsDerivedFrom(Airplane::DefaultData) == true)
	{
		return (UserConstants::Instance()->Get(UserConstants::max_airplane_fire_cheat_angle) * Radians_Per_Degree);
	}

	return (UserConstants::Instance()->Get(UserConstants::max_fire_cheat_angle) * Radians_Per_Degree);
}

bool CombatAI::IsLaserTurret()
{
	if ((GetSelf().IsDerivedFrom(Building::DefaultData) == true) &&
		(MW4AI::HasAnyProjectileWeapons(GetSelf()) == false))
	{
		return (true);
	}

	return (false);
}

void CombatAI::CenterTorso()
{
	{for (std::vector<Torso*>::const_iterator i = GetSelf().GetTorsos().begin();
		  i != GetSelf().GetTorsos().end();
		  ++i)
	{
		Torso* torso = *i;
		Check_Object(torso);

		if (Small_Enough(torso->yawValue) == false)
		{
			Stuff::Scalar yaw_demand = torso->yawValue * -1;
			yaw_demand *= 4;	// arbitrary, but makes it happen faster

			if (yaw_demand > 1)
			{
				yaw_demand = 1;
			}
			else
			{
				if (yaw_demand < -1)
				{
					yaw_demand = -1;
				}
			}

			torso->yawDemand = yaw_demand;
		}

		if (Small_Enough(torso->pitchValue) == false)
		{
			Stuff::Scalar pitch_demand = torso->pitchValue * -1;
			pitch_demand *= 4;	// arbitrary, but makes it happen faster

			if (pitch_demand > 1)
			{
				pitch_demand = 1;
			}
			else
			{
				if (pitch_demand < -1)
				{
					pitch_demand = -1;
				}
			}

			torso->pitchDemand = pitch_demand;
		}
	}}
}

void CombatAI::SurrenderPose()
{
	AutoHeap local_heap (g_CombatAIHeap);
	CenterTorso();

	Torso* torso = GetSelf().GetTorso();
	if (torso != 0)
	{
		torso->LookSurrender();
	}
}

bool CombatAI::SuicideIsOK() const
{
	if (CurrentMood() < MOODMIN + 1)
	{
		return (true);
	}

	if (m_NumArmsBlownOff + m_NumLegsBlownOff > 1)
	{
		return (true);
	}

	return (false);
}

void CombatAI::SetLookState(LookState state)
{
	if (m_LookState == state)
	{
		return;
	}

	m_LastTimeLookChanged = (Stuff::Scalar)gos_GetElapsedTime();
	m_LookState = state;
}

void CombatAI::SetLookState(const MW4AI::FireStyles::FireStyle& style)
{
	if (ShouldRun(true) == false)
	{
		return;
	}

	m_LastLookTime = (Stuff::Scalar)gos_GetElapsedTime();

	if ((GetAttackState() != ATTACKING) ||
		(style.ShouldFireAtPrimaryTarget() == false) ||
		(GetSelf().GetTorsos().size() != 1) ||
		(GetSelf().GetTorso()->twistJoint == 0) ||
		(GetSelf().IsDerivedFrom(Mech::DefaultData) == false) ||
		(EliteSkill() < UserConstants::Instance()->Get(UserConstants::min_look_left_or_right)) ||
		(EliteSkill() > UserConstants::Instance()->Get(UserConstants::max_look_left_or_right)))
	{
		SetLookState(LOOK_CENTER);
		return;
	}

	// NOTE: this implementation is a cheesy hack; I will replace it before too long.  It does the hjob, though.

	const Stuff::Scalar yaw(YawToPoint(GetSelf().GetTorso()->twistJoint->GetLocalToWorld(),(Stuff::Point3D)GetTarget().GetLocalToWorld()));
	
	if (yaw < -Pi_Over_4)
	{
		SetLookState(LOOK_RIGHT);
		return;
	}

	if (yaw > Pi_Over_4)
	{
		SetLookState(LOOK_LEFT);
		return;
	}

	SetLookState(LOOK_CENTER);
}

void CombatAI::UpdateLookState()
{
	if (GetSelf().IsDerivedFrom(Mech::DefaultData) == false)
	{
		return;
	}

	if (m_LastLookTime + look_decay_time < gos_GetElapsedTime())
	{
		m_LookState = LOOK_CENTER;
	}

	Mech* mech = Cast_Object(Mech*,&GetSelf());
	Torso* torso = mech->GetTorso();
	Check_Object(torso);

	switch (m_LookState)
	{
		case LOOK_LEFT:
		{
			torso->LookLeft();
			break;
		}

		case LOOK_RIGHT:
		{
			torso->LookRight();
			break;
		}

		case LOOK_CENTER:
		{
			torso->LookForward();
			break;
		}

		case LOOK_BACK:
		{
			torso->LookBack();
			break;
		}
	}
}

void CombatAI::SuggestPointWasNotAGoodDestination(const Stuff::Point3D& dest)
{
	while (m_BlockedMovePoints.size() > max_blocked_move_points_size)
	{
		m_BlockedMovePoints.erase(m_BlockedMovePoints.begin());
	}

	m_BlockedMovePoints.push_back(dest);
}

bool CombatAI::PointIsInBadNeighborhood(const Stuff::Point3D& point) const
{
	{for (std::vector<Stuff::Point3D>::const_iterator i = m_BlockedMovePoints.begin();
		  i != m_BlockedMovePoints.end();
		  ++i)
	{
		if (GetLengthSquared(point,*i) <= bad_area_radius * bad_area_radius)
		{
			return (true);
		}
	}}

	return (false);
}

MW4AI::FireSource CombatAI::GetBestFireSource(MWObject& who)
{
	{for (std::vector<CachedFireSource>::iterator i = m_FireSourceCache.begin();
		  i != m_FireSourceCache.end();
		  ++i)
	{
		if ((*i).object_id == who.objectID)
		{
			if ((*i).last_update + max_fire_source_cache_refresh > gos_GetElapsedTime())
			{
				return ((*i).fire_source);
			}

			m_FireSourceCache.erase(i);
			break;
		}
	}}

	CachedFireSource cfs;
	cfs.last_update = gos_GetElapsedTime() + Stuff::Random::GetFraction();
	cfs.object_id = who.objectID;
	cfs.fire_source = GetBestFireSource(who.GetLineOfSight());

	m_FireSourceCache.push_back(cfs);

	if (m_FireSourceCache.size() > 4)
	{
		m_FireSourceCache.erase(m_FireSourceCache.begin());
	}

	return (cfs.fire_source);
}

MW4AI::FireSource CombatAI::GetBestFireSource(const Stuff::Point3D& target_point)
{
	Stuff::LinearMatrix4D my_los;
	GetSelf().GetLineOfSight(my_los);

	if (MatrixFacesPoint(my_los,m_LastAimPoint,GetMaxFireCheatAngle()) == true)
	{
		return (MW4AI::FIRE_FROM_ANYWHERE);
	}

	Stuff::Scalar min(UserConstants::Instance()->Get(UserConstants::min_look_time_at_0));
	Stuff::Scalar max(UserConstants::Instance()->Get(UserConstants::min_look_time_at_100));
	Stuff::Scalar min_arm_look_time(min + ((max - min) * ((Stuff::Scalar)EliteSkill() * 0.01f)));

	if (Small_Enough(min_arm_look_time) == true)
	{
		min_arm_look_time = 0;
	}

	if (m_LookState == LOOK_LEFT)
	{
		if ((m_LeftArmExists == true) &&
			(m_LastTimeLookChanged + min_arm_look_time < (Stuff::Scalar)gos_GetElapsedTime()) &&
			(MatrixFacesPoint(GetFireSourceMatrix(MW4AI::FIRE_FROM_LEFT_ARM),m_LastAimPoint,GetMaxFireCheatAngle()) == true))
		{
			return (MW4AI::FIRE_FROM_LEFT_ARM);
		}
	}
	else
	{
		if ((m_LookState == LOOK_RIGHT) &&
			(m_RightArmExists == true) &&
			(m_LastTimeLookChanged + min_arm_look_time < (Stuff::Scalar)gos_GetElapsedTime()) &&
			(MatrixFacesPoint(GetFireSourceMatrix(MW4AI::FIRE_FROM_RIGHT_ARM),m_LastAimPoint,GetMaxFireCheatAngle()) == true))
		{
			return (MW4AI::FIRE_FROM_RIGHT_ARM);
		}
	}

	return (MW4AI::FIRE_FROM_ANYWHERE);
}

Stuff::LinearMatrix4D CombatAI::GetFireSourceMatrix(MW4AI::FireSource fire_source)
{
	if (fire_source != MW4AI::FIRE_FROM_ANYWHERE)
	{
		Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(GetSelf().weaponChain));
		MechWarrior4::Weapon* weapon = 0;

		while ((weapon = i.ReadAndNext()) != 0)
		{
			if (fire_source == MW4AI::FIRE_FROM_LEFT_ARM)
			{
				if (WeaponIsArmWeapon(*weapon,true) == true)
				{
					return (WeaponSiteLocalToWorld(*weapon));
				}
			}
			else
			{
				if (WeaponIsArmWeapon(*weapon,false) == true)
				{
					return (WeaponSiteLocalToWorld(*weapon));
				}
			}
		}
	}

	Stuff::LinearMatrix4D my_los;
	GetSelf().GetLineOfSight(my_los);

	return (my_los);
}

void CombatAI::GetAttackInterval(Stuff::Scalar& min, Stuff::Scalar& max)
{
	if (m_LastAttackIntervalRefresh + min_attack_interval_update > gos_GetElapsedTime())
	{
		min = m_MinAttackInterval;
		max = m_MaxAttackInterval;
		return;
	}

	min = GetMinWeaponDistance(GetSelf());
	max = GetMaxWeaponDistance(GetSelf());

	if (max <= min)
	{
		max = min + 100.0f;
	}

	Stuff::Scalar max_allowable_radius(1000.0f);

	if (GetIgnoreFog() == false)
	{
		max_allowable_radius = GetFogDistance();
	}


	if (MW4AI::g_MissionGraph != 0)
	{
		Stuff::Scalar node_radius = 0;

		{
			CRailNode* my_node = MW4AI::g_MissionGraph->FindClosestNode((Stuff::Point3D)GetSelf().GetLocalToWorld(),false);
			if (my_node != 0)
			{
				node_radius = (my_node->Radius() * 1.5f);
			}

			CRailNode* target_node = MW4AI::g_MissionGraph->FindClosestNode((Stuff::Point3D)GetTarget().GetLocalToWorld(),false);
			if (target_node != 0)
			{
				if (node_radius != 0)
				{
					node_radius += (target_node->Radius() * 1.5f);
					node_radius *= 0.5f;
				}
				else
				{
					node_radius = (target_node->Radius() * 1.5f);
				}
			}
		}

		if (node_radius < max_allowable_radius)
		{
			max_allowable_radius = node_radius;
		}

		if (max_allowable_radius < min_node_radius_used_for_attack_interval)
		{
			max_allowable_radius = min_node_radius_used_for_attack_interval;
		}
	}

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

	if (min > (max * 0.3f))
	{
		min = max * 0.3f;
	}

	m_LastAttackIntervalRefresh = gos_GetElapsedTime() + Stuff::Random::GetFraction();
	m_MinAttackInterval = min;
	m_MaxAttackInterval = max;
}

Stuff::Scalar CombatAI::GetCombatRadiusForRange(Stuff::Scalar percentile_range)
{
	Verify(percentile_range >= 0);
	Verify(percentile_range <= 100);

	percentile_range *= GetAttackRadiusMultiplier();
	if (percentile_range > 100)
	{
		percentile_range = 100;
	}

	if (percentile_range < 0)
	{
		percentile_range = 0;
	}

	Stuff::Scalar range = percentile_range * 0.01f;

	Stuff::Scalar min;
	Stuff::Scalar max;
	GetAttackInterval(min,max);

	Stuff::Scalar rv = min + ((max - min) * range);
	return (rv);
}

Stuff::Scalar CombatAI::GetMoodRadiusMultiplier() const
{
	Verify(UserConstants::Instance() != 0);

	UserConstants::ID id;
	int int_mood = (int)CurrentMood();

	switch ((MW4AI::Moods::ID)int_mood)
	{
		case MW4AI::Moods::DESPERATE_START:
		case MW4AI::Moods::DESPERATE_END:
		{
			id = UserConstants::interval_multiplier_desperate;
			break;
		}

		case MW4AI::Moods::DEFENSIVE_START:
		case MW4AI::Moods::DEFENSIVE_END:
		{
			id = UserConstants::interval_multiplier_defensive;
			break;
		}

		case MW4AI::Moods::NEUTRAL_START:
		case MW4AI::Moods::NEUTRAL_END:
		{
			id = UserConstants::interval_multiplier_neutral;
			break;
		}

		case MW4AI::Moods::AGRESSIVE_START:
		case MW4AI::Moods::AGRESSIVE_END:
		{
			id = UserConstants::interval_multiplier_aggressive;
			break;
		}

		case MW4AI::Moods::BRUTAL_START:
		case MW4AI::Moods::BRUTAL_END:
		{
			id = UserConstants::interval_multiplier_brutal;
			break;
		}

		default:
		{
			Verify("Mood not found!" == 0);
			return (1);
		}
	}

	return (UserConstants::Instance()->Get(id));
}

void CombatAI::SetAttackRadiusMultiplier(Stuff::Scalar multiplier)
{
	Verify(multiplier <= 1.0f);

	m_AttackRadiusMultiplier = multiplier;
}

bool CombatAI::ShouldSpendAmmoAgainst(MWObject& who, MW4AI::FireStyles::FireStyle& style)
{
	if ((style.ShouldFireAtPrimaryTarget() == false) ||
		(&who == Target()))
	{
		if ((EliteSkill() >= UserConstants::Instance()->Get(UserConstants::min_dont_use_limited_ammo_vs_non_mechs)) &&
			(EliteSkill() <= UserConstants::Instance()->Get(UserConstants::max_dont_use_limited_ammo_vs_non_mechs)) &&
			(who.IsDerivedFrom(Mech::DefaultData) == false) &&
			(HasAnyUnlimitedAmmoWeapons(GetSelf()) == true))
		{
			return (false);
		}
		else
		{
			return (true);
		}
	}

	return (false);
}

void CombatAI::ThisIsAMethodToSetABreakpointInIfCurrentVehicle()
{
#if defined(LAB_ONLY)
	AutoHeap local_heap (g_CombatAIHeap);
	if (Adept::Player::GetInstance() != 0)
	{
		MechWarrior4::MWPlayer* p = Cast_Object(MechWarrior4::MWPlayer*,Adept::Player::GetInstance());
		if ((p != 0) &&
			(p->GetInterface() != 0) &&
			(p->GetInterface()->vehicle != 0) &&
			(p->GetInterface()->vehicle == &GetSelf()))
		{
			if ((DebugHelper::GetInstance() != 0) &&
				(DebugHelper::GetInstance()->ShouldBreakInCurrentVehicle() == true))
			{
				PAUSE(("This is the current vehicle."));
			}
		}
	}
#endif
}

Stuff::Scalar CombatAI::GetNormalizedCurrentHeat() const
{
	HeatManager* hm = GetSelf().m_heatManager;

	if (hm == 0)
	{
		return (0);
	}

	Stuff::Scalar max_heat(hm->GetMaxHeat());

	if (Small_Enough(max_heat) == true)
	{
		return (0);
	}

	Stuff::Scalar cur_heat(hm->EstimateMaxCurrentHeat());

	if (cur_heat > max_heat)
	{
		cur_heat = max_heat;
	}

	if (cur_heat < 0)
	{
		cur_heat = 0;
	}

	return (cur_heat / max_heat);
}

bool CombatAI::LeftmostOrRightmostWeaponHitsSomethingElse(const MW4AI::FireParamPackage& params)
{
	std::vector<Weapon*> weapons;
	GetLeftmostAndRightmostWeapons(GetSelf(),weapons);

	if (weapons.size() < 2)
	{
		return (false);
	}

	{for (std::vector<Weapon*>::const_iterator i = weapons.begin();
		  i != weapons.end();
		  ++i)
	{
		Check_Object(*i);

		std::pair<FireData,Stuff::Time> result = CalculateFireData(params,**i);

		Stuff::Point3D end = result.first.GetDest();

		FireData new_fire_data(params.fire_data.GetSource(),
							   **i,
							   end);
		new_fire_data.Project();

		switch (new_fire_data.GetHitResult((Adept::Entity*)params.who_to_hit,true))
		{
			case FireData::HIT_NOTHING:
			{
				if ((params.who_to_hit == false) &&
					(params.vehicle_shooting_at != 0))
				{
					return (true);
				}

				break;
			}
			case FireData::HIT_SOMETHING_ELSE:
			{
				return (true);
			}
			case FireData::HIT_TARGET:
			{
				break;
			}
		}
	}}

	return (false);
}

Stuff::Scalar CombatAI::GetComponentScore(int armor_component, int internal_component, Stuff::Scalar max_score, bool is_left)
{
	const Stuff::Scalar LEFT(1);
	const Stuff::Scalar RIGHT(-1);

	if (MW4AI::Damage::GetInternalDamage(GetSelf(),internal_component) == Adept::InternalDamageObject::Destroyed)
	{
		if (is_left == true)
		{
			return (max_score * RIGHT);
		}

		return (max_score * LEFT);
	}

	const Stuff::Scalar armor_level = MW4AI::Damage::GetHighResArmorLevel(GetSelf(),armor_component);

	if (armor_level != 1)
	{
		const Stuff::Scalar rv(0.66f * max_score * (1 - armor_level));

		if (is_left == true)
		{
			return (rv * LEFT);
		}

		return (rv * RIGHT);
	}

	return (0);
}

Stuff::Scalar FindLongTomAngleForDistance(LongTomWeaponSubsystem& long_tom,
										  Stuff::Scalar target_distance)
{
	Stuff::Scalar d = target_distance;
	Stuff::Scalar v = long_tom.GetGameModel()->initialLinearVelocity.y;
	Stuff::Scalar g = g_Gravity;

	Stuff::Scalar n = (v * v * v * v) - (g * g * d * d);
	if (n < 0)
	{
		n = (v * v * v * v) + (g * g * d * d);
	}

	Stuff::Scalar t = ((2 * v * v) - (2 * Stuff::Sqrt(n))) / (g * g);

	t = Stuff::Sqrt(Stuff::Fabs(t));

	Stuff::Scalar x = d / (v * t);
	Clamp(x,0,1);

	Stuff::Scalar angle = Stuff::Arccos(x);
	return (angle);
}

Stuff::Point3D CombatAI::GetLongTomPitchPoint()
{
	if ((Target() == 0) ||
		(GetEyeSitePointer() == 0))
	{
		return (Stuff::Point3D(0,0,0));
	}

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

	while ((weapon = i.ReadAndNext()) != 0)
	{
		if ((weapon->IsDestroyed() == false) &&
			(DECRYPT(weapon->encryptedAmmoCount) > 0) &&
			(weapon->IsDerivedFrom(LongTomWeaponSubsystem::DefaultData) == true))
		{
			LongTomWeaponSubsystem* long_tom = Cast_Object(LongTomWeaponSubsystem*,weapon);
			Check_Object(long_tom);

			Stuff::Point3D my_pos(GetSelf().GetLocalToWorld());
			Stuff::Point3D target_pos(GetTarget().GetLocalToWorld());

			Stuff::Point3D delta;
			delta.Subtract(target_pos,my_pos);

			Stuff::Scalar distance = delta.GetLength();
			distance += (target_pos.y - my_pos.y);

			Stuff::Scalar angle(FindLongTomAngleForDistance(*long_tom,delta.GetLength()));

			Stuff::UnitVector3D forward;
			GetEyeSitePointer()->GetLocalToWorld().GetLocalForwardInWorld(&forward);

			Stuff::Point3D point_forward(forward);
			point_forward.y = 0;
			point_forward.Normalize(point_forward);

			Stuff::Point3D my_torso_pos(GetEyeSitePointer()->GetLocalToWorld());

			point_forward *= 100;
			point_forward += my_torso_pos;
			point_forward.y = my_torso_pos.y + (100 * Stuff::Tan(angle));

			return (point_forward);
		}
	}

	return (Stuff::Point3D(0,0,0));
}

bool CombatAI::CanFireNARC()
{
	if (MWMission::GetInstance() == 0)
	{
		return (false);
	}

	MWMission* mission = Cast_Object(MWMission*,MWMission::GetInstance());

	Narc* my_teams_narc = mission->GetNarc(GetSelf().GetAlignment());
	if ((my_teams_narc == 0) ||
		(my_teams_narc->IsDestroyed() == true))
	{
		return (true);
	}

	if ((my_teams_narc->attachedEntity.GetCurrent() == NULL) ||
		(my_teams_narc->attachedEntity.GetCurrent()->IsDestroyed()))
	{
		return (true);
	}

	if ((Target() != 0) &&
		(Target()->GetAlignment() != my_teams_narc->attachedEntity.GetCurrent()->GetAlignment()))
	{
		return (true);
	}

	return (false);
}

bool CombatAI::CanFireHeatGeneratingWeapons() const
{
	if (m_WaitingForHeatToReachMinSkill == true)
	{
		return (false);
	}

	if ((GetSelf().m_heatManager != 0) &&
		(GetSelf().m_heatManager->IsCooling() != 0))
	{
		return (false);
	}

	return (true);
}

void CombatAI::CheapShot(MWObject& who, bool should_hit, bool should_spend_ammo)
{
	const Stuff::Scalar distance_to_enemy(GetApproximateLength((Stuff::Point3D)GetSelf().GetLocalToWorld(),
															   (Stuff::Point3D)who.GetLocalToWorld()));

	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(GetSelf().weaponChain));
	MechWarrior4::Weapon* weapon = 0;
	Adept::Entity* target = 0;

	if (should_hit == true)
	{
		component_vector components;
		DamageObjectChainToVector(who.damageObjects,components);

		if (components.size() > 0)
		{
			Adept::DamageObject* damage_object = GetComponent_Random(components);

			if (damage_object != 0)
			{
				target = damage_object->parentEntity;
			}
		}
	}

	while ((weapon = i.ReadAndNext()) != 0)
	{
		if (MW4AI::WeaponCanFire(*weapon,distance_to_enemy,m_MinFiringDelay,m_MaxFiringDelay))
		{
			if ((DECRYPT(weapon->encryptedAmmoCount) == -1) ||
				(should_spend_ammo == true))
			{
				weapon->QuickFireWeapon(target);
			}
		}
	}
}

bool CombatAI::MoveDone() const
{
	if ((m_CurMove == 0) ||
		(m_CurMove->Done() == true) ||
		(m_CurMove->MoveType() == MOVE_LOOKOUT))
	{
		return (true);
	}

	if (m_CurMove->MoveType() == MOVE_FOLLOW)
	{
		if ((m_OrderSpeed == 0) ||
			(m_TimeNotMoving > 2.0f))
		{
			return (true);
		}
	}

	return (false);
}

void CombatAI::Crouch()
{
	if (GetSelf_AsVehicle() == 0)
	{
		return;
	}

	if (GetSelf_AsVehicle()->IsDerivedFrom(Mech::DefaultData) == true)
	{
		Mech* mech = Cast_Object(Mech*,&GetSelf());

		mech->CrouchRequest();
	}
}

bool CombatAI::Crouching()
{
	if (GetSelf_AsVehicle() == 0)
	{
		return (false);
	}

	if (GetSelf_AsVehicle()->IsDerivedFrom(Mech::DefaultData) == true)
	{
		Mech* mech = Cast_Object(Mech*,&GetSelf());
		if (mech->animStateEngine == 0)
		{
			return (false);
		}

		return (mech->animStateEngine->GetState() == MechAnimationStateEngine::CrouchState);
	}

	return (false);
}

bool CombatAI::MovedWithin(Stuff::Scalar duration) const
{
	if ((m_CurMove != 0) &&
		(m_CurMove->Done() == false))
	{
		return (true);
	}

	return (m_LastTimeMoved + duration > (Stuff::Scalar)gos_GetElapsedTime());
}

void CombatAI::ContinuousFlyBy(const Stuff::Point3D& loc)
{
	Verify(!"Should never get here -- not a plane!");
}

void CombatAI::Save (MemoryStream *stream)
{
	if (m_Tactic.GetPointer() != 0)
	{
		*stream << true;
		*stream << (int)m_Tactic->GetID();
	}
	else
	{
		*stream << false;
	}

	*stream << m_LastPos;
	*stream << m_LastMoveDest;
	*stream << m_LastAimPoint;
	*stream << (int)m_HitResult;
	*stream << m_DistanceFromTargetSquared;
	*stream << m_OverridingMovement;
	*stream << m_AttackThrottle;
	*stream << m_LastFireTime;
	*stream << m_MinFiringDelay;
	*stream << m_MaxFiringDelay;
	*stream << m_LastTimeProjectileShotAtMe;
	*stream << m_LastProjectileOrigin;
	*stream << m_ThrottleOverrideStopTime;
	*stream << m_HoldingFire;
	*stream << m_InternalArmorBreached;
	*stream << m_LeftArmExists;
	*stream << m_RightArmExists;
	*stream << m_NumArmsBlownOff;
	*stream << m_NumLegsBlownOff;
	*stream << m_LastUnableToFireTime;
	*stream << m_ForceDestinationRecalc;
	*stream << m_LastFrameTime;
	*stream << m_ApproximateTimeBetweenFrames;
	*stream << m_LastFriendlyFireTime;
	*stream << m_FriendlyFireDelay;
	*stream << m_LastTimeTargeted;
	*stream << m_TimeBeingTargeted;
	*stream << m_LastTargetedAlignment;
	*stream << m_LastTimeRammedTarget;
	*stream << m_LastTimeJumped;
	*stream << m_JumpDuration;
	*stream << m_PerformedFirstUpdate;
	*stream << m_SquadTargetingRadius;
	*stream << m_ExplicitFirePoint;
	*stream << m_ExplicitFireStretch;

	if (m_LastAttackOrderPosition.GetPointer() != 0)
	{
		*stream << true;
		*stream << *m_LastAttackOrderPosition;
	}
	else
	{
		*stream << false;
	}

	if (m_LastMoveOrderPosition.GetPointer() != 0)
	{
		*stream << true;
		*stream << *m_LastMoveOrderPosition;
	}
	else
	{
		*stream << false;
	}

	*stream << m_WaitingForHeatToReachMinSkill;

	*stream << m_MostRecentTactics.size();
	{for (std::vector<MW4AI::Tactics::TacticID>::iterator i = m_MostRecentTactics.begin();
		  i != m_MostRecentTactics.end();
		  ++i)
	{
		*stream << (int)(*i);
	}}

	*stream << (int)m_SearchLightController.GetState();
	*stream << (int)m_LookState;
	*stream << m_LastLookTime;

	*stream << m_BlockedMovePoints.size();
	{for (std::vector<Stuff::Point3D>::iterator i = m_BlockedMovePoints.begin();
		  i != m_BlockedMovePoints.end();
		  ++i)
	{
		*stream << *i;
	}}

	*stream << m_AttackRadiusMultiplier;
	*stream << m_LastOffsetToMiss;
	*stream << m_MissOffsetStartTime;
	*stream << m_Last_Till_Value;

	// TODO: save squad orders ...

	*stream << m_CheapShotTarget;
	*stream << m_CheapShotShouldHit;
	*stream << m_CheapShotShouldSpendAmmo;
	*stream << m_LastTimeMoved;
	*stream << m_PerWeaponRayCasting;
	*stream << m_AutoTargetingEnabled;

	MoverAI::Save(stream);
}

void CombatAI::Load (MemoryStream *stream)
{
	AutoHeap local_heap (g_CombatAIHeap);

	bool tactic_exists;
	*stream >> tactic_exists;

	if (tactic_exists == true)
	{
		int tactic_id;
		*stream >> tactic_id;
		m_Tactic = Tactics::CreateTactic(m_Interface,(MW4AI::Tactics::TacticID)tactic_id);
	}
	else
	{
		m_Tactic.Delete();
	}

	*stream >> m_LastPos;
	*stream >> m_LastMoveDest;
	*stream >> m_LastAimPoint;

	int hit_result;
	*stream >> hit_result;
	m_HitResult = (MW4AI::FireData::HIT_RESULT)hit_result;

	*stream >> m_DistanceFromTargetSquared;
	*stream >> m_OverridingMovement;
	*stream >> m_AttackThrottle;
	*stream >> m_LastFireTime;
	*stream >> m_MinFiringDelay;
	*stream >> m_MaxFiringDelay;
	*stream >> m_LastTimeProjectileShotAtMe;
	*stream >> m_LastProjectileOrigin;
	*stream >> m_ThrottleOverrideStopTime;
	*stream >> m_HoldingFire;
	*stream >> m_InternalArmorBreached;
	*stream >> m_LeftArmExists;
	*stream >> m_RightArmExists;
	*stream >> m_NumArmsBlownOff;
	*stream >> m_NumLegsBlownOff;
	*stream >> m_LastUnableToFireTime;
	*stream >> m_ForceDestinationRecalc;

	*stream >> m_LastFrameTime;
	*stream >> m_ApproximateTimeBetweenFrames;
	*stream >> m_LastFriendlyFireTime;
	*stream >> m_FriendlyFireDelay;
	*stream >> m_LastTimeTargeted;
	*stream >> m_TimeBeingTargeted;
	*stream >> m_LastTargetedAlignment;
	*stream >> m_LastTimeRammedTarget;
	*stream >> m_LastTimeJumped;
	*stream >> m_JumpDuration;
	*stream >> m_PerformedFirstUpdate;
	*stream >> m_SquadTargetingRadius;
	*stream >> m_ExplicitFirePoint;
	*stream >> m_ExplicitFireStretch;

	bool last_attack_order_pos;
	*stream >> last_attack_order_pos;
	if (last_attack_order_pos == true)
	{
		Stuff::LinearMatrix4D temp;
		*stream >> temp;
		m_LastAttackOrderPosition.Assimilate(&temp);
	}

	bool last_move_order_pos;
	*stream >> last_move_order_pos;
	if (last_move_order_pos == true)
	{
		Stuff::LinearMatrix4D temp;
		*stream >> temp;
		m_LastMoveOrderPosition.Assimilate(&temp);
	}

	*stream >> m_WaitingForHeatToReachMinSkill;

	std::vector<MW4AI::Tactics::TacticID>::size_type most_recent_tactics_size;
	*stream >> most_recent_tactics_size;

	m_MostRecentTactics.clear();
	{for (std::vector<MW4AI::Tactics::TacticID>::size_type i = 0;
		  i < most_recent_tactics_size;
		  ++i)
	{
		int tactic_id;
		*stream >> tactic_id;

		m_MostRecentTactics.push_back((MW4AI::Tactics::TacticID)tactic_id);
	}}

	int search_light_state;
	*stream >> search_light_state;
	m_SearchLightController.SetState((MW4AI::SearchLightController::State)search_light_state);

	int look_state;
	*stream >> look_state;
	m_LookState = (LookState)look_state;

	*stream >> m_LastLookTime;

	std::vector<Stuff::Point3D>::size_type blocked_move_points_size;
	*stream >> blocked_move_points_size;

	m_BlockedMovePoints.clear();
	{for (std::vector<Stuff::Point3D>::size_type i = 0;
		  i < blocked_move_points_size;
		  ++i)
	{
		Stuff::Point3D temp;
		*stream >> temp;
		m_BlockedMovePoints.push_back(temp);
	}}

	*stream >> m_AttackRadiusMultiplier;
	*stream >> m_LastOffsetToMiss;
	*stream >> m_MissOffsetStartTime;
	*stream >> m_Last_Till_Value;

//		Stuff::Auto_Ptr<MW4AI::SquadOrders> m_SquadOrders;

	*stream >> m_CheapShotTarget;
	*stream >> m_CheapShotShouldHit;
	*stream >> m_CheapShotShouldSpendAmmo;
	*stream >> m_LastTimeMoved;
	*stream >> m_PerWeaponRayCasting;
	*stream >> m_AutoTargetingEnabled;

	MoverAI::Load (stream);
}

void CombatAI::NotifyRespawned()
{
	Reset();

	m_IgnoreFog = false;
	m_HitResult = FireData::HIT_NOTHING;
	m_OverridingMovement = false;
	m_AttackThrottle = 100.0f;
	m_MinFiringDelay = 0;
	m_MaxFiringDelay = 0;
	m_LastTimeProjectileShotAtMe = 0;
	m_ThrottleOverrideStopTime = 0;
	m_HoldingFire = false;
	m_NumArmsBlownOff = 0;
	m_NumLegsBlownOff = 0;
	m_InternalArmorBreached = false;
	m_LastUnableToFireTime = 0;
	m_LastFireTime = 0;
	m_ForceDestinationRecalc = false;
	m_ApproximateTimeBetweenFrames = 0;
	m_LastFrameTime = 0;
	m_LastFriendlyFireTime = 0;
	m_FriendlyFireDelay = 0;
	m_LastTimeTargeted = 0;
	m_TimeBeingTargeted = 0;
	m_LastTimeRammedTarget = 0;
	m_LastTimeJumped = 0;
	m_SquadTargetingRadius = 0;
	m_ExplicitFireStretch = 0;
	m_WaitingForHeatToReachMinSkill = false;
	m_LookState = LOOK_CENTER;
	m_LastLookTime = (Stuff::Scalar)gos_GetElapsedTime();
	m_LeftArmExists = true;
	m_RightArmExists = true;
	m_AttackRadiusMultiplier = 0;
	m_LastOffsetToMiss = Stuff::Point3D(0,0,0);
	m_MissOffsetStartTime = 0;
	m_JumpDuration = 0;
	m_Last_Till_Value = 0;
	m_CheapShotTarget = -2;
	m_CheapShotShouldHit = false;
	m_CheapShotShouldSpendAmmo = true;
	m_LastTimeMoved = (Stuff::Scalar)gos_GetElapsedTime();
	m_PerWeaponRayCasting = false;
	m_LastTargetedAlignment = GetSelf().GetAlignment();
	m_LastPos = (Point3D)GetSelf().GetLocalToWorld();
	m_LastMoveDest.Zero();
	m_LastProjectileOrigin.Zero();
	m_ExplicitFirePoint.Zero();
	m_LastAimPoint.Zero();
	m_AutoTargetingEnabled = true;
	m_LastTimeSwitchedTargets = (Stuff::Scalar)gos_GetElapsedTime();
	m_LastAttackIntervalRefresh = 0;
	m_CurrentVulnerableComponent = 0;
	m_CachedNearest[0] = -2;
	m_CachedNearestTime[0] = 0;
	m_CachedNearest[1] = -2;
	m_CachedNearestTime[1] = 0;
	m_ShutDownDueToHeat = false;
	m_MoveRequestFinishedTime = 0;

	MoverAI::NotifyRespawned();
}

MWObject* CombatAI::GetNearestEnemyToLine(Stuff::Line3D& line, const secondary_target_list& targets)
{
	Stuff::Scalar least_distance = GetLengthSquared(line.m_origin,(Stuff::Point3D)GetTarget().GetLineOfSight());
	MWObject* nearest = 0;
	
	{for (secondary_target_list::const_iterator i = targets.begin();
		  i != targets.end();
		  ++i)
	{
		if ((*i)->GetTargetDesirability() != MWObject::desirability_never)
		{
			Stuff::Point3D closest_point;
			line.GetClosestPointTo((Stuff::Point3D)((*i)->GetLineOfSight()),&closest_point);
			const Stuff::Scalar distance(GetLengthSquared(line.m_origin,closest_point));

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

	if (nearest == 0)
	{
		return (&GetTarget());
	}

	return (nearest);
}

MWObject* CombatAI::FindBestAutoTarget()
{
	const Stuff::Point3D my_pos(GetSelf().GetLocalToWorld());
	Stuff::Scalar target_distance(1);
	Stuff::Scalar min_desirability(-1);
	
	if (Target() != 0)
	{
		target_distance = GetApproximateLength(my_pos,(Stuff::Point3D)GetTarget().GetLocalToWorld());
		min_desirability = GetTarget().GetTargetDesirability();
	}

	Adept::Entity* shot_by = ShotBy(gos_GetElapsedTime() - 5.0f);

	std::vector<MWObject*> objects;
	GetSensorList(GetSelf().GetSensor()->RefreshAndGetSensorData(),objects);

	std::vector<Stuff::Scalar> scores;

	std::vector<MWObject*>::iterator i = objects.begin();
	while (i != objects.end())
	{
		if ((Target() != 0) &&
			((*i) == Target()))
		{
			Stuff::Scalar score(1.0f);

			if (Target() == shot_by)
			{
				score *= target_switch_shot_by_multiplier;

				if ((shot_by != 0) &&
					(shot_by->IsPlayerVehicle() == true))
				{
					score *= 20.0f;
				}
			}

			scores.push_back(score);

			++i;
		}
		else
		{
			if (((*i)->GetAlignment() != GetSelf().GetAlignment()) &&
				((*i)->GetAlignment() != Entity::DefaultAlignment) &&
				((*i)->GetTargetDesirability() >= min_desirability) &&
				((*i)->GetTargetDesirability() != MWObject::desirability_never))
			{
				Stuff::Scalar distance(GetApproximateLength(my_pos,(Stuff::Point3D)(*i)->GetLocalToWorld()));
				distance += 0.1f;

				Stuff::Scalar score(target_distance / distance);

				score *= target_switch_score_multiplier;
				if ((*i)->objectID == m_ShotBy)
				{
					score *= target_switch_shot_by_multiplier;

					if ((*i)->IsPlayerVehicle() == true)
					{
						score *= (Stuff::Scalar)(CurrentMood()) * 0.5f;
					}
				}

				scores.push_back(score);

				++i;
			}
			else
			{
				objects.erase(i);
			}
		}
	}

	std::vector<Stuff::Scalar>::iterator greatest = std::max_element(scores.begin(),scores.end());

	if (greatest == scores.end())
	{
		return (0);
	}

	int index = index_of(scores.begin(),scores.end(),*greatest);
	if ((Target() != 0) &&
		(objects[index] == Target()))
	{
		return (0);
	}

	return (objects[index]);
}

void CombatAI::UpdateAutoTargeting()
{
	if ((m_AutoTargetingEnabled == false) ||
		(m_LastTimeSwitchedTargets + min_target_switch_time > (Stuff::Scalar)gos_GetElapsedTime()) ||
		(Target() == 0) ||
		(Target()->IsDestroyed() == true) ||
		(m_Tactic.GetPointer() == 0) ||
		(m_Tactic->GetID() == MW4AI::Tactics::TACTIC_DEFEND) ||
		(m_Tactic->GetID() == MW4AI::Tactics::TACTIC_RETREAT))
	{
		return;
	}

	m_LastTimeSwitchedTargets = (Stuff::Scalar)gos_GetElapsedTime();

	MWObject* target = 0;

	if (m_SquadOrders.GetPointer() != 0)
	{
		target = m_SquadOrders->GetAutoTarget();
	}
	else
	{
		target = FindBestAutoTarget();
	}

	if ((target != 0) &&
		(target != Target()))
	{
		Target(target);
	}
}

bool CombatAI::GetExtendedSensorData(std::vector<MWObject*>& sensor_data_list)
{
	if (m_SquadOrders.GetPointer() != 0)
	{
		return (m_SquadOrders->GetExtendedSensorData(sensor_data_list));
	}

	return (false);
}

Stuff::Scalar CombatAI::GetLeastSquaredSensorDistance(const Stuff::Point3D& point) const
{
	if (m_SquadOrders.GetPointer() != 0)
	{
		return (m_SquadOrders->GetLeastSquaredSensorDistance(point));
	}

	return (inherited::GetLeastSquaredSensorDistance(point));
}

bool CombatAI::LineMightHitFriendlyUnits(const Stuff::Line3D& line)
{
	if ((Target() != 0) &&
		(Target()->IsDerivedFrom(MWObject::DefaultData) == true))
	{
		return (MW4AI::MightHitFriendlies(line,line.m_length,GetSelf(),Cast_Object(MWObject*,Target())));
	}

	return (MW4AI::MightHitFriendlies(line,line.m_length,GetSelf(),0));
}

Stuff::Scalar CombatAI::GetMinSpeed() const
{
	if (Attacking() == false)
	{
		return (0);
	}

	return (m_MinSpeed);
}

void CombatAI::SetMinSpeed(Stuff::Scalar min_speed)
{
	m_MinSpeed = min_speed;
}

void CombatAI::SetGoalPitch(Stuff::Scalar pitch)
{
	if (GetSelf().GetTorso() == 0)
	{
		return;
	}

	m_GoalPitch = pitch;
}

void CombatAI::SetGoalYaw(Stuff::Scalar yaw)
{
	if (GetSelf().GetTorso() == 0)
	{
		return;
	}

	m_GoalYaw = yaw;
}

void CombatAI::UpdateGoalPitchAndYaw()
{
	if (m_GoalYaw != 0)
	{
		Torso* torso = GetSelf().GetTorso();
		if (Fabs(torso->yawValue - m_GoalYaw) < 0.2f)
		{
			m_GoalYaw = 0;
		}
		else
		{
			torso->yawDemand = m_GoalYaw - torso->yawValue;
			Clamp(torso->yawDemand,-0.7f,0.7f);
		}
	}

	if (m_GoalPitch != 0)
	{
		Torso* torso = GetSelf().GetTorso();
		if (Fabs(torso->pitchValue - m_GoalPitch) < 0.2f)
		{
			m_GoalPitch = 0;
		}
		else
		{
			torso->pitchDemand = m_GoalPitch - torso->pitchValue;
			Clamp(torso->pitchDemand,-0.7f,0.7f);
		}
	}
}

void CombatAI::UpdateCurrentVulnerableComponent()
{
	if ((EliteSkill() < UserConstants::Instance()->Get(UserConstants::min_vulnerable_leg_hiding)) ||
		(EliteSkill() > UserConstants::Instance()->Get(UserConstants::max_vulnerable_leg_hiding)) ||
		(CurrentMood() < UserConstants::Instance()->Get(UserConstants::min_mood_vulnerable_leg_hiding)) ||
		(CurrentMood() > UserConstants::Instance()->Get(UserConstants::max_mood_vulnerable_leg_hiding)) ||
		(GetSelf().IsDerivedFrom(Mech::DefaultData) == false))
	{
		m_CurrentVulnerableComponent = 0;
		return;
	}

	Stuff::Scalar hide_side(0);

	hide_side += GetComponentScore(Adept::DamageObject::LeftLegArmorZone,Adept::InternalDamageObject::LeftLegZone,0.75f,true);
	hide_side += GetComponentScore(Adept::DamageObject::RightLegArmorZone,Adept::InternalDamageObject::RightLegZone,0.75f,false);

	if ((hide_side < 0.25f) &&
		(hide_side > -0.25f))
	{
		m_CurrentVulnerableComponent = 0;
		return;
	}

	if (hide_side < -1)
	{
		m_CurrentVulnerableComponent = -1;
		return;
	}

	if (hide_side > 1)
	{
		m_CurrentVulnerableComponent = 1;
		return;
	}

	m_CurrentVulnerableComponent = hide_side;
}

bool CombatAI::CanMove() const
{
	if ((m_OverridingMovement == false) ||
		(GetSelf_AsVehicle() == 0))
	{
		return (false);
	}

	return (true);
}

