#include "MW4Headers.hpp" #include "AI_Lancemate.hpp" #include "Group.hpp" #include "CombatAI.hpp" #include #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 #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& command); void NotifyShotFired(); void NotifyFriendlyFire(Adept::Entity& who); bool CanExecuteCommands() const; Stuff::Auto_Ptr 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 m_Command; int m_FriendlyFireFrustration; Stuff::Scalar m_LastTimeNoticedFriendlyFire; Stuff::Scalar m_LastTimeFrustrationChanged; bool m_TurnedAgainstPlayer; }; }; Stuff::Auto_Ptr 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& 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::iterator i = m_CombatAIsWithSquadOrders.begin(); i != m_CombatAIsWithSquadOrders.end(); ++i) { if ((*i)->GetSquadOrders() != 0) { #pragma warning(disable:4239) (*i)->SetSquadOrders(Stuff::Auto_Ptr(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 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(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(0)); #pragma warning(default:4239) std::vector::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 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()); } }