#include "MW4Headers.hpp"

#include "AI_Lancemate.hpp"

#include "Group.hpp"
#include "CombatAI.hpp"
#include <Adept\NameTable.hpp>
#include "AIUtils.hpp"
#include "AI_CombatTacticInterface.hpp"
#include "AI_LancemateCommands.hpp"
#include "AI_FindObject.hpp"
#include "Mech.hpp"
#include "lancemate.hpp"
#include "AI_Groups.hpp"
#include "MWMission.hpp"
#include "MWApplication.hpp"
#pragma warning(push)
#include <stlport\algorithm>
#pragma warning(pop)



using namespace MW4AI;
using namespace MW4AI::Squad;
using namespace MechWarrior4;
using namespace MW4AI::LancemateAudio;

const Stuff::Scalar max_get_out_of_way_distance = 70.0f;
const Stuff::Scalar max_too_close_to_leaders_fire_distance = 40.0f;
const Stuff::Scalar max_dodge_leaders_fire_time = 8.0f;
const Stuff::Scalar max_unable_to_fire_give_up_time = 12.0f;
const Stuff::Scalar max_squared_disengage_distance = 900.0f * 900.0f;
const Stuff::Scalar target_to_avoid_refresh_time = 10.0f;

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

Group::element_list::size_type GetObjectIndex(MWObject& object,
											  const Group& group)
{
	Group::element_list::const_iterator found = std::find(group.GetElements().begin(),group.GetElements().end(),object.objectID);

	if (found == group.GetElements().end())
	{
		return (group.GetElements().size());
	}

	return (std::distance(group.GetElements().begin(),found));
}

inline Stuff::Scalar GetDistanceSquared(Adept::Entity& object1, Adept::Entity& object2)
{
	return (GetLengthSquared((Stuff::Point3D)object1.GetLocalToWorld(),(Stuff::Point3D)object2.GetLocalToWorld()));
}

inline CombatAI* GetLeaderCombatAI(Adept::Entity* leader)
{
	if (leader->IsDerivedFrom(MWObject::DefaultData) == false)
	{
		return (0);
	}

	MWObject* leader_mwobject = Cast_Object(MWObject*,leader);
	Check_Object(leader_mwobject);

	if ((leader_mwobject->GetAI() == 0) ||
		(leader_mwobject->GetAI()->IsDerivedFrom(CombatAI::DefaultData) == false))
	{
		return (0);
	}

	CombatAI* combat_ai = Cast_Object(CombatAI*,leader_mwobject->GetAI());
	Check_Object(combat_ai);

	return (combat_ai);
}

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

namespace MW4AI
{
	class LancemateSquadOrders :
		public SquadOrders
	{
	  public:
		LancemateSquadOrders(CombatAI& combat_ai,
							 Squad::Lancemate& lancemate_ai,
							 Group& group,
							 LancemateAudio::AudioManager& audio_manager);
		~LancemateSquadOrders();

		void Update();
		bool PointIsValid(const Stuff::Point3D& point) const;
		void IssueCommand(Stuff::Auto_Ptr<LancemateCommands::LancemateCommand>& command);
		void NotifyShotFired();
		void NotifyFriendlyFire(Adept::Entity& who);

		bool CanExecuteCommands() const;

		Stuff::Auto_Ptr<LancemateCommands::LancemateCommand> CreateCommand(LancemateCommands::ID command_id,Adept::Entity* pTarget2 = NULL);

		bool ShouldRunScript() const;

		bool GetLeaderAlignment(int& alignment);

		void NotifyNoPath();

	  private:
		int GetMyIndex() const;
		MWObject* GetLeader() const;
		bool UnitIsMovingToLeadersPath(MWObject& leader) const;
		Stuff::Point3D GetPosition() const;
		bool PointIsInLeadersPath(const Stuff::Point3D& point, MWObject& leader) const;

		void UpdateLeaderAvoidance();
		void UpdateShootNearbyEnemies();
		void UpdateFriendlyFireFrustration();

		MWObject* FindEntityToAttack(const Adept::ObjectID* except_who);

	  private:
		CombatAI& m_CombatAI;
		Squad::Lancemate& m_LancemateAI;
		Group& m_Group;
		LancemateAudio::AudioManager& m_AudioManager;
		Stuff::Auto_Ptr<LancemateCommands::LancemateCommand> m_Command;
		int m_FriendlyFireFrustration;
		Stuff::Scalar m_LastTimeNoticedFriendlyFire;
		Stuff::Scalar m_LastTimeFrustrationChanged;
		bool m_TurnedAgainstPlayer;
	};
};


Stuff::Auto_Ptr<LancemateCommands::LancemateCommand> LancemateSquadOrders::CreateCommand(LancemateCommands::ID command_id,Adept::Entity* pTarget2/* = NULL*/)
{
	if ((m_Command.GetPointer() != 0) &&
		(m_Command->CanInterrupt() == false))
	{
        if (!pTarget2)
		m_AudioManager.PlayAudio(AUDIO_NO,m_CombatAI.GetTalkerSuffix());
		return (0);
	}

	if (GetLeader() != 0)
	{
		return (LancemateCommands::CreateLancemateCommand(command_id,m_CombatAI,*GetLeader(),m_AudioManager,pTarget2));
	}

	return (0);
}

void LancemateSquadOrders::NotifyNoPath()
{
	if (m_Command.GetPointer() != 0)
	{
		m_Command->NotifyNoPath();
	}
}

LancemateSquadOrders::LancemateSquadOrders(CombatAI& combat_ai,
										   Squad::Lancemate& lancemate_ai,
										   Group& group,
										   LancemateAudio::AudioManager& audio_manager)
	: m_CombatAI(combat_ai)
	, m_LancemateAI(lancemate_ai)
	, m_Group(group)
	, m_AudioManager(audio_manager)
	, m_FriendlyFireFrustration(0)
	, m_LastTimeNoticedFriendlyFire((Stuff::Scalar)gos_GetElapsedTime())
	, m_LastTimeFrustrationChanged((Stuff::Scalar)gos_GetElapsedTime())
	, m_TurnedAgainstPlayer(false)
{
}

LancemateSquadOrders::~LancemateSquadOrders()
{
}

void LancemateSquadOrders::IssueCommand(Stuff::Auto_Ptr<LancemateCommands::LancemateCommand>& command)
{
	if (m_TurnedAgainstPlayer == true)
	{
		return;
	}

	m_Command = command;
}

void LancemateSquadOrders::Update()
{
	if ((m_TurnedAgainstPlayer == true) ||
		(ShouldRunScript() == true))
	{
		return;
	}

	if (GetLeader() == &m_CombatAI.GetSelf())
	{
		return;
	}

	m_CombatAI.SetIgnoringFriendlyFire(true);

	if (m_Command != 0)
	{
		if (m_Command->Finished() == true)
		{
			m_Command.Delete();

			if (GetLeader() != 0)
			{
				#pragma warning(disable:4239)
				IssueCommand(LancemateCommands::CreateLancemateCommand(LancemateCommands::LANCEMATE_DEFAULT,m_CombatAI,*(GetLeader()),m_AudioManager));
				#pragma warning(default:4239)
			}
		}
		else
		{
			m_Command->Update();
		}

		if (m_Command->CanDistract() == true)
		{
			if (m_CombatAI.GetAttackState() == CombatAI::ATTACKING)
			{
				if ((m_CombatAI.Target() == 0) ||
					(m_CombatAI.GetUnableToFireDuration(max_unable_to_fire_give_up_time) == true) ||
					(GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),
										 (Stuff::Point3D)m_CombatAI.Target()->GetLocalToWorld()) > max_squared_disengage_distance))
				{
					m_CombatAI.Reset(false);
				}
				else
				{
					UpdateShootNearbyEnemies();
				}
			}
			else
			{
				UpdateShootNearbyEnemies();
			}
		}
	}

	UpdateLeaderAvoidance();
	UpdateFriendlyFireFrustration();
}

int LancemateSquadOrders::GetMyIndex() const
{
	Verify(m_Group.GetElements().size() > 0);
	return (index_of(m_Group.GetElements().begin(),m_Group.GetElements().end(),m_CombatAI.GetSelf().objectID));
}

MWObject* LancemateSquadOrders::GetLeader() const
{
	if (m_Group.GetElements().size() == 0)
	{
		return (0);
	}

	Adept::Entity* entity = m_Group.IDToEntity(*(m_Group.GetElements().begin()));

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

	Check_Object(entity);

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

	return (Cast_Object(MWObject*,entity));
}

Stuff::Point3D LancemateSquadOrders::GetPosition() const
{
	return (m_CombatAI.GetSelf().GetLocalToWorld());
}

bool LancemateSquadOrders::UnitIsMovingToLeadersPath(MWObject& leader) const
{
	if (m_CombatAI.GetAttackState() != CombatAI::ATTACKING)
	{
		return (false);
	}

	CombatTacticInterface iface(m_CombatAI);

	Stuff::Point3D dest(GetPosition());
	iface.GetLastMoveDest(dest);

	return (!PointIsValid(dest));
}

void LancemateSquadOrders::UpdateLeaderAvoidance()
{
	if (m_Command.GetPointer() == 0)
	{
		return;
	}

	if (m_Command->CanDistract() == false)
	{
		return;
	}

	int index(GetMyIndex());
	Verify(index < m_Group.GetElements().size());							// if this fails, the combat_ai does not belong to the group

	if (index == 0)
	{
		return;
	}

	MWObject* leader = GetLeader();
	if (leader == 0)
	{
		return;												// the leader cannot be part of the formation; he dictates it
	}

	Check_Object(leader);

	int attempt_index = 0;
	CombatTacticInterface iface(m_CombatAI);

	while (UnitIsMovingToLeadersPath(*leader) == true)
	{
		Stuff::Scalar distance = 0;
		Stuff::Scalar radians = 0;

		Stuff::Point3D my_pos(GetPosition());

		Stuff::UnitVector3D forward_vector;
		m_CombatAI.GetSelf().GetLocalToWorld().GetLocalForwardInWorld(&forward_vector);
		Stuff::Point3D forward(forward_vector);
		forward += my_pos;

		switch (attempt_index % 4)
		{
			case 0:	{ radians = 0.0f;break; }
			case 1:	{ radians = Stuff::Pi_Over_2; break; }
			case 2:	{ radians = -Stuff::Pi_Over_2; break; }
			case 3:	{ radians = Stuff::Pi; break; }
		}

		switch (attempt_index / 4)
		{
			case 0:	{ distance = 10.0f;break; }
			case 1:	{ distance = 20.0f; break; }
			case 2:	{ distance = 50.0f; break; }
			case 3:	{ distance = 100.0f; break; }
		}

		Stuff::Point3D dest(RotateVector(forward,my_pos,radians,distance));

		if (m_CombatAI.PointIsValid(dest) == true)
		{
			m_CombatAI.MoveToPoint(dest);
		}

		++attempt_index;

		if (attempt_index >= 16)
		{
			return;
		}
	}
}

void LancemateSquadOrders::UpdateShootNearbyEnemies()
{
	Verify(m_Group.GetElements().size() > 0);
	Verify(m_Command.GetPointer() != 0);

	if (m_CombatAI.Attacking() == true)
	{
		if (m_CombatAI.Target()->IsDerivedFrom(MWObject::DefaultData) == true)
		{
			MWObject* target = Cast_Object(MWObject*,m_CombatAI.Target());
			if (target->GetTargetDesirability() > 0)
			{
				return;
			}

			if ((target->weaponChain.IsEmpty() == false) ||
				(target->GetAI() != 0))
			{
				return;
			}
		}
	}

	if (UserConstants::Instance() == 0)
	{
		return;
	}

	Stuff::Scalar lancemate_min_radius = m_CombatAI.GetSquadTargetingRadius();
	Verify(lancemate_min_radius > 0);

	Adept::ObjectID id(m_LancemateAI.GetTargetToAvoid());
	Adept::ObjectID* id_ptr = 0;

	if (id >= 0)
	{
		id_ptr = &id;
	}

	MWObject* mwobject = m_Command->FindSomeoneToAttack(id_ptr,FindObject::FC_BEST_TARGET,lancemate_min_radius);

	if ((mwobject != 0) &&
		(mwobject != m_CombatAI.Target()))
	{
		Check_Object(mwobject);

		m_CombatAI.Reset(m_Command->CanDeviateFromPath());
		m_CombatAI.Target(mwobject);
		m_CombatAI.OrderAttack(m_Command->CanDeviateFromPath());
	}
}

bool LancemateSquadOrders::PointIsInLeadersPath(const Stuff::Point3D& point, MWObject& leader) const
{
	const Stuff::LinearMatrix4D& leader_matrix = leader.GetLocalToWorld();

	if (GetLengthSquared((Stuff::Point3D)leader_matrix,
						 point) < (max_get_out_of_way_distance * max_get_out_of_way_distance))
	{
		if ((GetSquaredDistToMatrixForward(point,leader_matrix) < GetSquaredDistToMatrixLeft(point,leader_matrix)) &&
			(GetSquaredDistToMatrixForward(point,leader_matrix) < GetSquaredDistToMatrixRight(point,leader_matrix)))
		{
			return (true);
		}
	}

	return (false);
}

bool LancemateSquadOrders::PointIsValid(const Stuff::Point3D& point) const
{
	Verify(UserConstants::Instance() != 0);

	int index(GetMyIndex());
	Verify(index < m_Group.GetElements().size());				// if this fails, the combat_ai does not belong to the group
	if (index == 0)
	{
		return (true);
	}

	MWObject* leader = GetLeader();
	if (leader == 0)
	{
		return (true);
	}
	Check_Object(leader);

	if ((m_Command.GetPointer() != 0) &&
		(m_Command->OnLeash() == true) &&
		(GetLengthSquared(point,m_Command->GetLeashPoint()) > m_Command->GetLeashRadius() * m_Command->GetLeashRadius()))
	{
		return (false);
	}

	if ((m_LancemateAI.GetLastLeaderFireTime() != 0) &&
		(m_LancemateAI.GetLastLeaderFireTime() + max_dodge_leaders_fire_time > gos_GetElapsedTime()))
	{
		Verify(m_LancemateAI.GetLeaderFireLine() != 0);

		if (LinePenetrates(*m_LancemateAI.GetLeaderFireLine(),point,max_too_close_to_leaders_fire_distance) == true)
		{
			return (false);
		}
	}

	Vehicle* leader_vehicle = Cast_Object(Vehicle*,leader);
	Check_Object(leader_vehicle);

	if (leader_vehicle->speedDemandMPS < Stuff::SMALL)
	{
		return (true);
	}

	return (!PointIsInLeadersPath(point,*leader));
}

void LancemateSquadOrders::NotifyShotFired()
{
	if ((m_CombatAI.GetSelf().GetSensor() == 0) ||
		(m_TurnedAgainstPlayer == true))
	{
		return;
	}

	if ((m_Command.GetPointer() != 0) &&
		(m_Command->CanDistract() == false))
	{
		return;
	}

	if ((m_CombatAI.Attacking() == false) ||
		((m_LancemateAI.GetTargetToAvoid() > 0) && (m_CombatAI.Target()->objectID == m_LancemateAI.GetTargetToAvoid())))
	{
		Adept::ObjectID avoid_id = m_LancemateAI.GetTargetToAvoid();

		Verify(m_Command.GetPointer() != 0);

		MWObject* mwobject = 0;
		
		if (avoid_id >= 0)
		{
			m_Command->FindSomeoneToAttack(&avoid_id);
		}
		else
		{
			m_Command->FindSomeoneToAttack(0);
		}

		if ((mwobject != 0) &&
			(m_CombatAI.Target() != mwobject))
		{
			Check_Object(mwobject);

			m_CombatAI.Reset(m_Command->CanDeviateFromPath());
			m_CombatAI.Target(mwobject);
			m_CombatAI.OrderAttack(m_Command->CanDeviateFromPath());
		}
	}
}


void LancemateSquadOrders::NotifyFriendlyFire(Adept::Entity& who)
{
	if (m_TurnedAgainstPlayer == true)
	{
		MechWarrior4::MWMission *mission = Cast_Object (MechWarrior4::MWMission*,MechWarrior4::MWMission::GetInstance());
		Check_Object (mission);
		mission->EndMissionState (false);
		return;
	}

	if ((m_CombatAI.GetSelf().IsPlayerVehicle() == true) ||
		(who.IsPlayerVehicle() == false))
	{
		return;
	}

// MSL 5.04 Friendly Fire Betty Changes
	return;
//	if ((m_LastTimeFrustrationChanged + 3.0f < gos_GetElapsedTime()) && (m_FriendlyFireFrustration < 4))
	if (m_LastTimeFrustrationChanged + 3.0f < gos_GetElapsedTime())// && (m_FriendlyFireFrustration < 4))
	{
		++m_FriendlyFireFrustration;
		m_LastTimeFrustrationChanged = (Stuff::Scalar)gos_GetElapsedTime();

// MSL 5.04 Friendly Fire Betty Changes
		MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());


		switch (m_FriendlyFireFrustration)
		{
			case 1:
			{
				app->SendSound(who.GetReplicatorID(), VehicleInterface::FRIENDLYFIREPENALTY);
//				m_AudioManager.PlayAudio(AUDIO_FF1,m_CombatAI.GetTalkerSuffix());
				break;
			}
			case 2:
			{
				app->SendSound(who.GetReplicatorID(), VehicleInterface::FRIENDLYFIREPENALTY);
//				m_AudioManager.PlayAudio(AUDIO_FF2,m_CombatAI.GetTalkerSuffix());
				break;
			}
			case 3:
			{
				app->SendSound(who.GetReplicatorID(), VehicleInterface::FRIENDLYFIREPENALTY);
//				m_AudioManager.PlayAudio(AUDIO_FF3,m_CombatAI.GetTalkerSuffix());
//				m_AudioManager.SetPissedOff(true);
				break;
			}
// MSL 5.04 Friendly Fire Betty Changes
//			case 4:
//			{
//				m_TurnedAgainstPlayer = true;
//				m_AudioManager.PlayAudio(AUDIO_FF4,m_CombatAI.GetTalkerSuffix());

//				m_CombatAI.Reset(true);
//				m_CombatAI.Target(&who);
//				m_CombatAI.OrderAttack(true);

// MSL 5.00
//				MechWarrior4::MWMission *mission = Cast_Object (MechWarrior4::MWMission*,MechWarrior4::MWMission::GetInstance());
//				Check_Object (mission);
//				mission->SetEndMissionTime (30.0f);
//		        mission->EndMissionState (false);

//				break;
//			}
			default:
			{
				app->SendSound(who.GetReplicatorID(), VehicleInterface::FRIENDLYFIREPENALTY);
				break;
			}
		}
	}

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

void LancemateSquadOrders::UpdateFriendlyFireFrustration()
{
	if (m_FriendlyFireFrustration > 0)
	{
		if (m_LastTimeNoticedFriendlyFire + 18.0f < gos_GetElapsedTime())
		{
			--m_FriendlyFireFrustration;
			m_LastTimeNoticedFriendlyFire = (Stuff::Scalar)gos_GetElapsedTime();
		}
	}
}

bool LancemateSquadOrders::CanExecuteCommands() const
{
	if (m_TurnedAgainstPlayer == true)
	{
		return (false);
	}

	return (true);
}


bool LancemateSquadOrders::ShouldRunScript() const
{
	if ((GetLeader() != 0) &&
		(GetLeader()->IsPlayerVehicle() == true))
	{
		return (false);
	}

	return (true);
}

bool LancemateSquadOrders::GetLeaderAlignment(int& alignment)
{
	MWObject* leader = GetLeader();

	if ((leader != 0) &&
		(leader != &(m_CombatAI.GetSelf())))
	{
		alignment = leader->GetAlignment();
		return (true);
	}

	return (false);
}

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

Lancemate::Lancemate()
	: m_LastLeaderFireTime(0)
	, m_LastPickTargetTime(0)
	, m_TargetToAvoid(-2)
	, m_TargetToAvoidTime(0)
{
}

Lancemate::~Lancemate()
{
	{for (std::vector<CombatAI*>::iterator i = m_CombatAIsWithSquadOrders.begin();
		  i != m_CombatAIsWithSquadOrders.end();
		  ++i)
	{
		if ((*i)->GetSquadOrders() != 0)
		{
			#pragma warning(disable:4239)
			(*i)->SetSquadOrders(Stuff::Auto_Ptr<SquadOrders>(0));
			#pragma warning(default:4239)
		}
	}}
}

ID Lancemate::GetID() const
{
	return (GROUPAI_LANCEMATE);
}

void Lancemate::Update(Group& group, CombatAI& combat_ai)
{
	if ((combat_ai.GetSquadOrders() != 0) &&
		(combat_ai.GetSquadOrders()->ShouldRunScript() == true))
	{
		return;
	}

	inherited::Update(group,combat_ai);

	if ((combat_ai.IsDestroyed() == true) &&
		(combat_ai.GetSquadOrders() != 0))
	{
		NotifyMemberRemoved(group,combat_ai.GetSelf());
	}

	Adept::Entity* leader = GetLeader(group);

	if ((leader != 0) &&
		(leader->IsDerivedFrom(Mech::DefaultData) == true))
	{
		Mech* mech = Cast_Object(Mech*,leader);
		m_AudioManager.Update(group,*mech);
	}
}

void Lancemate::NotifyShot(Group& group, Adept::ObjectID id)
{
	inherited::NotifyShot(group,id);
}

void Lancemate::NotifyShotFired(Group& group,
								const Stuff::Line3D& line,
								MWObject& at_who,
								MWObject& shooter)
{
	inherited::NotifyShotFired(group,line,at_who,shooter);

	Verify(&at_who != &shooter);
	Verify(std::find(shooter.GetGroups().begin(),shooter.GetGroups().end(),group.GetID()) != shooter.GetGroups().end());

	if (GetObjectIndex(shooter,group) != 0)
	{
		return;
	}

	Verify(std::find(at_who.GetGroups().begin(),at_who.GetGroups().end(),group.GetID()) != at_who.GetGroups().end());

	if ((m_LastLeaderFireTime < gos_GetElapsedTime()) &&
		(LinePenetrates(line,(Stuff::Point3D)(at_who.GetLocalToWorld()),max_too_close_to_leaders_fire_distance) == true))
	{
		m_LastLeaderFireTime = gos_GetElapsedTime();
		Stuff::Line3D* new_line = new Stuff::Line3D;
		*new_line = line;
		m_LeaderFireLine.Assimilate(new_line);
	}

	if (m_LastPickTargetTime < gos_GetElapsedTime())
	{
		m_LastPickTargetTime = gos_GetElapsedTime();
		Propagate_NotifyShotFired(group);
	}
}

void Lancemate::Propagate_NotifyShotFired(Group& group)
{
	Verify(group.GetElements().size() > 0);

//	CombatAI* leader_combatai = GetLeaderCombatAI(GetLeader(group));
	// TODO: get leader's target -- this will be a challenge since the "leader" can be the player

	typedef std::vector<MWObject*> member_list;

	member_list members;
	group.GetMembers(members);

	{for (member_list::iterator i = members.begin();
		  i != members.end();
		  ++i)
	{
		if (((*i)->GetAI() != 0) &&
			((*i)->GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true))
		{
			CombatAI* combat_ai = Cast_Object(CombatAI*,(*i)->GetAI());
			Check_Object(combat_ai);

			if (combat_ai->GetSquadOrders() != 0)
			{
				combat_ai->GetSquadOrders()->NotifyShotFired();
			}
		}
	}}
}

Stuff::Time Lancemate::GetLastLeaderFireTime() const
{
	return (m_LastLeaderFireTime);
}

Stuff::Line3D* Lancemate::GetLeaderFireLine() const
{
	return (m_LeaderFireLine.GetPointer());
}

void Lancemate::NotifyMemberAdded(Group& group, MWObject& who)
{
	if ((who.GetAI() != 0) &&
		(who.IsPlayerVehicle() == false) &&
		(who.IsDestroyed() == false) &&
		(who.GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true))
	{
		CombatAI* combat_ai = Cast_Object(CombatAI*,who.GetAI());
		#pragma warning(disable:4239)
		LancemateSquadOrders* new_squad_orders = new LancemateSquadOrders(*combat_ai,*this,group,m_AudioManager);
		combat_ai->SetSquadOrders(Stuff::Auto_Ptr<SquadOrders>(new_squad_orders));

		if (std::find(m_CombatAIsWithSquadOrders.begin(),m_CombatAIsWithSquadOrders.end(),combat_ai) == m_CombatAIsWithSquadOrders.end())
		{
			m_CombatAIsWithSquadOrders.push_back(combat_ai);
		}

		Adept::Entity* leader = GetLeader(group);

		if ((leader != 0) &&
			(leader->IsDerivedFrom(MWObject::DefaultData) == true))
		{
			new_squad_orders->IssueCommand(LancemateCommands::CreateLancemateCommand(LancemateCommands::LANCEMATE_DEFAULT,*combat_ai,*(Cast_Object(MWObject*,leader)),m_AudioManager));
		}
		#pragma warning(default:4239)
	}
}

void Lancemate::NotifyMemberRemoved(Group& group, MWObject& who)
{
	Verify(who.GetAI() != 0);
	Verify(who.GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true);

	if ((who.GetAI() != 0) &&
		(who.GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true))
	{
		CombatAI* combat_ai = Cast_Object(CombatAI*,who.GetAI());
		#pragma warning(disable:4239)
		combat_ai->SetSquadOrders(Stuff::Auto_Ptr<SquadOrders>(0));
		#pragma warning(default:4239)

		std::vector<CombatAI*>::iterator found = std::find(m_CombatAIsWithSquadOrders.begin(),m_CombatAIsWithSquadOrders.end(),combat_ai);

		if (found != m_CombatAIsWithSquadOrders.end())
		{
			m_CombatAIsWithSquadOrders.erase(found);
		}
	}
}

void Lancemate::SetEntityToIgnore(Adept::ObjectID objectid)
{
	SetTargetToAvoid(objectid);
}

Adept::ObjectID Lancemate::GetTargetToAvoid() const
{
	if (m_TargetToAvoidTime + target_to_avoid_refresh_time > gos_GetElapsedTime())
	{
		return (m_TargetToAvoid);
	}

	return (-2);
}

void Lancemate::SetTargetToAvoid(Adept::ObjectID id)
{
	m_TargetToAvoid = id;
	m_TargetToAvoidTime = gos_GetElapsedTime();
}

void Lancemate::NotifyMechDestroyed(MechWarrior4::Group& group,
									const Adept::ReplicatorID& inflicting_id,
									const Adept::ReplicatorID& victim_id)
{
	Connection	*connection = Network::GetInstance()->GetConnection(victim_id.connectionID);
	if (connection == 0)
	{
		return;
	}

	Replicator* rep = connection->FindReplicator(victim_id);
	if (rep == 0)
	{
		return;
	}

	Entity *ent = Cast_Object(Entity*,rep);
	if (ent->IsDerivedFrom(Mech::DefaultData) == false)
	{
		return;
	}

	Mech* mech = Cast_Object(Mech*,ent);

	if ((mech->GetAI() == 0) ||
		(mech->IsPlayerVehicle() == true))
	{
		return;
	}

	std::vector<MWObject*> lancemates;
	MW4AI::Groups::GetLancemates(lancemates,false);

	if (std::find(lancemates.begin(),lancemates.end(),mech) != lancemates.end())
	{
		m_AudioManager.PlayAudio(LancemateAudio::AUDIO_EJECT,mech->GetAI()->GetTalkerSuffix());
	}
}
