Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
908 lines
22 KiB
C++
908 lines
22 KiB
C++
#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());
|
|
}
|
|
}
|