#include "MW4Headers.hpp" #include "AI_LancemateCommands.hpp" #include "CombatAI.hpp" #include "MWMission.hpp" #include "AI_FindObject.hpp" #include "AI_Groups.hpp" #include "VehicleInterface.hpp" #include "Flag.hpp" #include "Field_Base.hpp" #include "AI_Damage.hpp" #include "NavPoint.hpp" #include #include #include #include "lancemate.hpp" #include #pragma warning(push) #include #pragma warning(pop) using namespace MechWarrior4; using namespace MW4AI; using namespace MW4AI::LancemateCommands; using namespace MW4AI::LancemateAudio; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Adept::Entity* AttackTargetIfOK(CombatAI& combat_ai, Adept::Entity* target, LancemateAudio::AudioManager& audio_manager,bool bPlayAudio = true); MWObject* MWObjectForID(Adept::ObjectID id) { Adept::Entity* entity = NameTable::GetInstance()->FindData(id); if ((entity == 0) || (entity->IsDestroyed() == true) || (entity->IsDerivedFrom(MWObject::DefaultData) == false)) { return (0); } return (Cast_Object(MWObject*,entity)); } inline MWObject* GetLeader(Group& group) { return (MWObjectForID(group.GetElements()[0])); } inline MWObject* GetLeader(MWObject& mwobject) { Group& group = Groups::GetGroup(*(mwobject.GetGroups().begin())); return (GetLeader(group)); } int GetMemberIndex(Group& group, CombatAI& combat_ai) { int index = index_of(group.GetElements().begin(),group.GetElements().end(),combat_ai.GetSelf().objectID); Verify(index < group.GetElements().size()); // if this fails, the lancemate does not belong to the group return (index); } void EnterFormationAtIndex(CombatAI& combat_ai, int index, Adept::Entity* who_to_follow) { int offset_x(0); int offset_y(0); if (combat_ai.Crouching() == true) { combat_ai.Crouch(); } switch (index) { case 0: { return; } case 1: { offset_x = 30; offset_y = -30; break; } case 2: { offset_x = -30; offset_y = -30; break; } case 3: { offset_x = 0; offset_y = -60; break; } default: { Verify(!"Lancemate::OrderFormation() not yet designed for groups of more than 4 units."); break; } } combat_ai.orderMoveFollow(who_to_follow,offset_x,offset_y,1000); } void EnterFormation(CombatAI& combat_ai, MWObject& leader) { if (!&leader || (leader.objectID == combat_ai.GetSelf().objectID)) { return; } combat_ai.Reset(true); MWObject::GroupList::iterator shared_group = std::find_first_of(leader.GetGroups().begin(), leader.GetGroups().end(), combat_ai.GetSelf().GetGroups().begin(), combat_ai.GetSelf().GetGroups().end()); if (shared_group == leader.GetGroups().end()) { // jcem if (combat_ai.GetSelf().GetGroups().size()) { shared_group = combat_ai.GetSelf().GetGroups().begin(); } else { return; } } int index = GetMemberIndex(Groups::GetGroup(*shared_group),combat_ai); if (index == 0) { index = 3; // return... } EnterFormationAtIndex(combat_ai,index,&leader); } enum FaceForward_Result { DOING_SOMETHING, FACING, UN_CROUCHING }; FaceForward_Result FaceForwardIfInFormation(CombatAI& combat_ai, MWObject& leader) { if ((combat_ai.MoveDone() == true) && (combat_ai.GetAttackState() == CombatAI::NOT_ATTACKING)) { if (leader.IsDerivedFrom(Vehicle::DefaultData) == true) { Vehicle* vehicle = Cast_Object(Vehicle*,&leader); if ((vehicle->currentSpeedKPH > 10.0f) || (GetLengthSquared((Stuff::Point3D)combat_ai.GetSelf().GetLocalToWorld(),(Stuff::Point3D)leader.GetLocalToWorld()) > 80.0f * 80.0f)) { EnterFormation(combat_ai,leader); return (DOING_SOMETHING); } } Stuff::UnitVector3D unit_forward; leader.GetLocalToWorld().GetLocalForwardInWorld(&unit_forward); Stuff::Point3D forward(unit_forward); Stuff::UnitVector3D my_unit_forward; combat_ai.GetSelf().GetLocalToWorld().GetLocalForwardInWorld(&my_unit_forward); Stuff::Point3D my_forward(my_unit_forward); Vector3D cross_product; cross_product.Cross(forward,my_forward); if ((cross_product.GetLengthSquared() > (0.25f * 0.25f)) && (combat_ai.Crouching() == true)) { combat_ai.Crouch(); return (UN_CROUCHING); } forward *= 100.0f; forward += (Stuff::Point3D)combat_ai.GetSelf().GetLocalToWorld(); combat_ai.FacePointOrLookOut(forward); return (FACING); } return (DOING_SOMETHING); } void EnsureActive(CombatAI& combat_ai, bool stop_holding_fire = true) { combat_ai.Reset(true); if (stop_holding_fire == true) { combat_ai.SetHoldingFire(false); } if (combat_ai.CanAct() == false) { combat_ai.Startup(); } if (combat_ai.Crouching() == true) { combat_ai.Crouch(); } } Adept::Entity* AttackTargetIfOK(CombatAI& combat_ai, Adept::Entity* target, LancemateAudio::AudioManager& audio_manager,bool bPlayAudio/* = true*/) { if ((target == 0) || (target->IsDestroyed() == true)) { return (0); } if (target->GetAlignment() == combat_ai.GetSelf().GetAlignment()) { if (bPlayAudio) audio_manager.PlayAudio(AUDIO_NO,combat_ai); return (0); } if (target->IsDerivedFrom(MWObject::DefaultData) == true) { MWObject* mwobject = Cast_Object(MWObject*,target); if (mwobject->GetTargetDesirability() == MWObject::desirability_never) { if (bPlayAudio) audio_manager.PlayAudio(AUDIO_NO,combat_ai); return (0); } } combat_ai.Reset(); combat_ai.Target(target); if (combat_ai.Target() == 0) { if (bPlayAudio) audio_manager.PlayAudio(AUDIO_NO,combat_ai); return (0); } combat_ai.OrderAttack(); return (target); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LancemateCommand::LancemateCommand(CombatAI& combat_ai, MWObject& leader, LancemateAudio::AudioManager& audio_manager, Adept::Entity* pTarget2/* = NULL*/) : m_CombatAI(combat_ai), m_AudioManager(audio_manager) { m_Leader.Add(&leader); if (pTarget2) m_pTarget2.Add(pTarget2); Check_Object(&combat_ai); Check_Object(&leader); } LancemateCommand::~LancemateCommand() { } bool LancemateCommand::OnLeash() const { return (true); } Stuff::Point3D LancemateCommand::GetLeashPoint() const { if (m_pTarget2.GetCurrent()) return ((Stuff::Point3D)m_pTarget2.GetCurrent()->GetLocalToWorld()); if (m_Leader.GetCurrent()) return ((Stuff::Point3D)m_Leader.GetCurrent()->GetLocalToWorld()); return Stuff::Point3D(0.0, 0.0, 0.0); } Stuff::Scalar LancemateCommand::GetLeashRadius() const { return (UserConstants::Instance()->Get(UserConstants::max_lancemate_distance_from_leader)); } bool LancemateCommand::CanDistract() const { return (false); } bool LancemateCommand::CanDeviateFromPath() const { return (false); } MWObject* LancemateCommand::FindSomeoneToAttack(const Adept::ObjectID* except_who, FindObject::Criteria criteria, Stuff::Scalar radius) const { if ((m_CombatAI.GetSelf().GetSensor() == 0) || (m_CombatAI.GetHoldingFire() == true)) { return (0); } Adept::ObjectID id = FindObject::Find(m_CombatAI.GetSelf(), FindObject::FA_ENEMY, FindObject::FT_DEFAULT, criteria, FindObject::FF_WHO_SHOT + FindObject::FF_SEEPLAYER, radius, except_who); if (id <= 0) { if (except_who != 0) { id = FindObject::Find(m_CombatAI.GetSelf(), FindObject::FA_ENEMY, FindObject::FT_DEFAULT, criteria, FindObject::FF_WHO_SHOT, radius, 0); if (id <= 0) { return (0); } } else { return (0); } } Adept::Entity* entity = NameTable::GetInstance()->FindData(id); if ((entity != 0) && (entity->IsDestroyed() == false) && (entity->IsDerivedFrom(MWObject::DefaultData) == true)) { return (Cast_Object(MWObject*,entity)); } return (0); } void LancemateCommand::NotifyNoPath() { } namespace MW4AI { namespace LancemateCommands { class AttackPlayersTarget : public LancemateCommand { DERIVED_LancemateCommand(AttackPlayersTarget); virtual Stuff::Point3D GetLeashPoint() const; private: SlotOf m_Target; }; class DefendPlayersTarget : public LancemateCommand { DERIVED_LancemateCommand(DefendPlayersTarget); virtual Stuff::Point3D GetLeashPoint() const; private: SlotOf m_Target; }; class FormOnMe : public LancemateCommand { DERIVED_LancemateCommand(FormOnMe); virtual bool CanDistract() const; virtual void NotifyNoPath(); private: bool m_GivingUp; }; class HoldFire : public LancemateCommand { DERIVED_LancemateCommand(HoldFire); }; class GoToMyNavPoint : public LancemateCommand { DERIVED_LancemateCommand(GoToMyNavPoint); virtual Stuff::Point3D GetLeashPoint() const; virtual void NotifyNoPath(); private: Stuff::Point3D m_NavPos; bool m_PlayedReachedSound; bool m_GivingUp; }; class Stop : public LancemateCommand { DERIVED_LancemateCommand(Stop); }; class Shutdown : public LancemateCommand { DERIVED_LancemateCommand(Shutdown); private: bool m_ShuttingDown; }; class AttackNearestThreat : public LancemateCommand { DERIVED_LancemateCommand(AttackNearestThreat); virtual Stuff::Point3D GetLeashPoint() const; private: SlotOf m_Target; }; // not yet implemented ... class RepairAtNearestRepairBay : public LancemateCommand { DERIVED_LancemateCommand(RepairAtNearestRepairBay); virtual Stuff::Point3D GetLeashPoint() const; virtual ~RepairAtNearestRepairBay(); virtual void NotifyNoPath(); virtual bool CanInterrupt() const; private: void StartMoving(); bool Reset(); private: Adept::ObjectID m_MFB; Stuff::Point3D m_LastMovePoint; Stuff::Point3D m_MovePoints[2]; int m_NumRetries; bool m_IssuedSetMoveHead; }; }; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Auto_Ptr LancemateCommands::CreateLancemateCommand(ID command, CombatAI& combat_ai, MWObject& leader, LancemateAudio::AudioManager& audio_manager, Adept::Entity* pTarget2/* = NULL*/) { Verify(command > LANCEMATE_FIRST); Verify(command < LANCEMATE_LAST); switch (command) { case LANCEMATE_DEFAULT: { return (new Default(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_ATTACKPLAYERSTARGET: { return (new AttackPlayersTarget(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_DEFENDPLAYERSTARGET: { return (new DefendPlayersTarget(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_FORMONME: { return (new FormOnMe(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_HOLDFIRE: { return (new HoldFire(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_GOTOMYNAVPOINT: { return (new GoToMyNavPoint(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_STOP: { return (new Stop(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_SHUTDOWN: { return (new Shutdown(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_ATTACKNEARESTTHREAT: { return (new AttackNearestThreat(combat_ai,leader,audio_manager,pTarget2)); } case LANCEMATE_REPAIRATNEARESTREPAIRBAY: { return (new RepairAtNearestRepairBay(combat_ai,leader,audio_manager,pTarget2)); } } return (0); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool Default::Finished() const { return (false); } void Default::Init() { MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); if (m_CombatAI.GetSelf().IsPlayerVehicle() == true) { return; } m_LastTarget.Remove(); m_LastTimeNotWaiting = (Stuff::Scalar)gos_GetElapsedTime(); EnterFormation(m_CombatAI,*pLeader); m_CombatAI.SetAutoTargeting(true); } void Default::Update() { MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); if (!pLeader) return; if (m_CombatAI.GetSelf().IsPlayerVehicle() == true) { return; } if (m_CombatAI.GetTimeTargetedByFriendly() > 0.5f) { EnterFormation(m_CombatAI,*pLeader); } else { if (m_LastTarget.GetCurrent() != m_CombatAI.Target()) { if ((m_CombatAI.Target() == 0) || (m_CombatAI.Target()->IsDestroyed() == true)) { EnterFormation(m_CombatAI,*pLeader); } } } m_LastTarget.Remove(); if (m_CombatAI.Target()) m_LastTarget.Add(m_CombatAI.Target()); switch (FaceForwardIfInFormation(m_CombatAI,*pLeader)) { case DOING_SOMETHING: { m_LastTimeNotWaiting = (Stuff::Scalar)gos_GetElapsedTime(); break; } case UN_CROUCHING: { m_LastTimeNotWaiting = (Stuff::Scalar)gos_GetElapsedTime() - 8.0f; break; } case FACING: { Stuff::Scalar wait_time = 20.0f; wait_time += (Stuff::Scalar)(m_CombatAI.GetSelf().objectID % 0x08); if ((m_LastTimeNotWaiting + wait_time < gos_GetElapsedTime()) && (m_CombatAI.Crouching() == false)) { m_CombatAI.Crouch(); } break; } } } bool Default::CanDistract() const { return (true); } bool Default::CanDeviateFromPath() const { return (true); } void Default::NotifyNoPath() { m_CombatAI.Reset(); Init(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool AttackPlayersTarget::Finished() const { Adept::Entity* pTarget = m_Target.GetCurrent(); if (pTarget == 0) { return (true); } Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); if (pTarget->IsDestroyed() == true) { if (!pTarget2) m_AudioManager.PlayAudio(LancemateAudio::AUDIO_TARGETDESTROYED,m_CombatAI); return (true); } return (false); } void AttackPlayersTarget::Init() { Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); if (!pTarget2) // jcem m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); EnsureActive(m_CombatAI); m_CombatAI.SetAutoTargeting(false); Adept::Entity* pTarget = NULL; m_Target.Remove(); if (!pTarget2) { // jcem if (pLeader->IsDerivedFrom(Vehicle::DefaultData) == true) { Vehicle* leader_as_vehicle = Cast_Object(Vehicle*,pLeader); if ((leader_as_vehicle->vehicleInterface != 0) && (leader_as_vehicle->vehicleInterface->m_interfaceTarget.GetCurrent() != 0)) { Adept::Entity* entity = leader_as_vehicle->vehicleInterface->m_interfaceTarget.GetCurrent(); if (entity->IsDerivedFrom(MWObject::DefaultData) == true) { MWObject* mwobject = Cast_Object(MWObject*,entity); if ((m_CombatAI.GetAttackState() != CombatAI::ATTACKING) || (m_CombatAI.Target() != mwobject)) { pTarget = mwobject; } } } } if ((pTarget == 0) && (pLeader->GetAI() != 0) && (pLeader->GetAI()->IsDerivedFrom(CombatAI::DefaultData) == false)) { CombatAI* combat_ai = Cast_Object(CombatAI*,pLeader->GetAI()); Check_Object(combat_ai); pTarget = combat_ai->Target(); } } else { // jcem if ((m_CombatAI.GetAttackState() != CombatAI::ATTACKING) || (m_CombatAI.Target() != pTarget2)) { pTarget = pTarget2; } } pTarget = AttackTargetIfOK(m_CombatAI,pTarget,m_AudioManager,m_pTarget2.GetCurrent() ? false: true); if (pTarget) m_Target.Add(pTarget); } void AttackPlayersTarget::Update() { } Stuff::Point3D AttackPlayersTarget::GetLeashPoint() const { Adept::Entity* pTarget = m_Target.GetCurrent(); if ((pTarget == 0) || (pTarget->IsDestroyed() == true)) { return (LancemateCommand::GetLeashPoint()); } return ((Stuff::Point3D)pTarget->GetLocalToWorld()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool DefendPlayersTarget::Finished() const { Adept::Entity* pTarget = m_Target.GetCurrent(); if ((pTarget == 0) || (pTarget->IsDestroyed() == true)) { return (true); } return (false); } void DefendPlayersTarget::Init() { Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); EnsureActive(m_CombatAI); m_CombatAI.SetAutoTargeting(false); Adept::Entity* pTarget = NULL; m_Target.Remove(); if (!pTarget2) { // jcem if (pLeader->IsDerivedFrom(Vehicle::DefaultData) == true) { Vehicle* leader_as_vehicle = Cast_Object(Vehicle*,pLeader); if ((leader_as_vehicle->vehicleInterface != 0) && (leader_as_vehicle->vehicleInterface->m_interfaceTarget.GetCurrent() != 0)) { Adept::Entity* entity = leader_as_vehicle->vehicleInterface->m_interfaceTarget.GetCurrent(); if (entity->IsDerivedFrom(MWObject::DefaultData) == true) { MWObject* mwobject = Cast_Object(MWObject*,entity); if ((m_CombatAI.GetAttackState() != CombatAI::ATTACKING) || (m_CombatAI.GetCurrentTactic() != Tactics::TACTIC_DEFEND) || (m_CombatAI.Target() != mwobject)) { if (mwobject->GetAlignment() != m_CombatAI.GetSelf().GetAlignment()) { m_AudioManager.PlayAudio(AUDIO_NO,m_CombatAI); return; } else { pTarget = mwobject; } } } } } if ((pTarget == 0) && (pLeader->GetAI() != 0) && (pLeader->GetAI()->IsDerivedFrom(CombatAI::DefaultData) == false)) { CombatAI* combat_ai = Cast_Object(CombatAI*,pLeader->GetAI()); Check_Object(combat_ai); if (combat_ai->Target()->GetAlignment() == m_CombatAI.GetSelf().GetAlignment()) { pTarget = combat_ai->Target(); } } } else { // jcem if ((m_CombatAI.GetAttackState() != CombatAI::ATTACKING) || (m_CombatAI.GetCurrentTactic() != Tactics::TACTIC_DEFEND) || (m_CombatAI.Target() != pTarget2)) { if (pTarget2->GetAlignment() != m_CombatAI.GetSelf().GetAlignment()) { return; } else { pTarget = pTarget2; } } } if ((pTarget != 0) && (pTarget->IsDestroyed() == true)) { pTarget = 0; } if (pTarget != 0) { m_Target.Add(pTarget); m_CombatAI.Reset(); m_CombatAI.Target(pTarget); m_CombatAI.OrderAttackTactic(Tactics::TACTIC_DEFEND); if (!pTarget2) m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); } } void DefendPlayersTarget::Update() { } Stuff::Point3D DefendPlayersTarget::GetLeashPoint() const { Adept::Entity* pTarget = m_Target.GetCurrent(); if ((pTarget == 0) || (pTarget->IsDestroyed() == true)) { return (LancemateCommand::GetLeashPoint()); } return ((Stuff::Point3D)pTarget->GetLocalToWorld()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool FormOnMe::Finished() const { if (m_CombatAI.MoveDone() == true) { Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); if (pTarget2) { if (GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),(Stuff::Point3D)pTarget2->GetLocalToWorld()) < 80.0f * 80.0f) return (true); } else if (pLeader) { if (GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),(Stuff::Point3D)pLeader->GetLocalToWorld()) < 80.0f * 80.0f) return (true); } } return (m_GivingUp); } bool FormOnMe::CanDistract() const { if (m_CombatAI.MoveDone() == true) { Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); if (pTarget2) { if (GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),(Stuff::Point3D)pTarget2->GetLocalToWorld()) < 80.0f * 80.0f) return (true); } else { if (GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),(Stuff::Point3D)pLeader->GetLocalToWorld()) < 80.0f * 80.0f) return (true); } } return (false); } void FormOnMe::Init() { Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); if (!pTarget2) m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); EnsureActive(m_CombatAI,false); m_CombatAI.SetAutoTargeting(true); m_GivingUp = false; if (pTarget2) EnterFormation(m_CombatAI,*(MWObject*)pTarget2); else EnterFormation(m_CombatAI,*pLeader); } void FormOnMe::Update() { Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); if (pTarget2) FaceForwardIfInFormation(m_CombatAI,*(MWObject*)pTarget2); else FaceForwardIfInFormation(m_CombatAI,*pLeader); } void FormOnMe::NotifyNoPath() { m_GivingUp = true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool HoldFire::Finished() const { return (false); } void HoldFire::Init() { m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); if (m_CombatAI.GetHoldingFire() == true) { m_CombatAI.SetHoldingFire(false); } else { m_CombatAI.SetHoldingFire(true); } } void HoldFire::Update() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool GoToMyNavPoint::Finished() const { return (m_GivingUp); } void GoToMyNavPoint::Init() { MechWarrior4::MWObject* pLeader = m_Leader.GetCurrent(); m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); EnsureActive(m_CombatAI,false); m_CombatAI.SetAutoTargeting(true); m_PlayedReachedSound = false; m_GivingUp = false; if (m_CombatAI.GetSelf().IsDerivedFrom(Vehicle::DefaultData) == false) { m_NavPos = LancemateCommand::GetLeashPoint(); return; } Vehicle* leader_as_vehicle = Cast_Object(Vehicle*,pLeader); if ((leader_as_vehicle->vehicleInterface == 0) || (leader_as_vehicle->vehicleInterface->m_selectedNavPoint.GetCurrent() == 0)) { m_NavPos = LancemateCommand::GetLeashPoint(); return; } m_NavPos = (Stuff::Point3D)(leader_as_vehicle->vehicleInterface->m_selectedNavPoint.GetCurrent()->GetLocalToWorld()); m_CombatAI.Reset(); m_CombatAI.MoveToPoint(m_NavPos); } void GoToMyNavPoint::Update() { if ((m_PlayedReachedSound == false) && (GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),m_NavPos) < 150.0f * 150.0f)) { m_PlayedReachedSound = true; m_AudioManager.PlayAudio(LancemateAudio::AUDIO_ATNAV,m_CombatAI); } } Stuff::Point3D GoToMyNavPoint::GetLeashPoint() const { return (m_NavPos); } void GoToMyNavPoint::NotifyNoPath() { m_GivingUp = true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool Stop::Finished() const { return (false); } void Stop::Init() { m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); m_CombatAI.Reset(); m_CombatAI.SetAutoTargeting(true); } void Stop::Update() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool Shutdown::Finished() const { if (m_ShuttingDown == true) { if (m_CombatAI.CanAct() == false) { return (true); } } else { if (m_CombatAI.CanAct() == true) { return (true); } } return (false); } void Shutdown::Init() { m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); if (m_CombatAI.Crouching() == true) { m_CombatAI.Crouch(); } if (m_CombatAI.CanAct() == true) { m_ShuttingDown = true; m_CombatAI.Shutdown(); } else { m_ShuttingDown = false; m_CombatAI.Startup(); } } void Shutdown::Update() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool AttackNearestThreat::Finished() const { Adept::Entity* pTarget = m_Target.GetCurrent(); if ((pTarget == 0) || (pTarget->IsDestroyed() == true)) { return (true); } return (false); } Stuff::Point3D AttackNearestThreat::GetLeashPoint() const { Adept::Entity* pTarget = m_Target.GetCurrent(); if ((pTarget == 0) || (pTarget->IsDestroyed() == true)) { return (LancemateCommand::GetLeashPoint()); } return ((Stuff::Point3D)pTarget->GetLocalToWorld()); } void AttackNearestThreat::Init() { Adept::Entity* pTarget2 = m_pTarget2.GetCurrent(); m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); EnsureActive(m_CombatAI); m_CombatAI.SetAutoTargeting(true); m_Target.Remove(); Update(); MWObject* nearest_enemy = FindSomeoneToAttack(0,FindObject::FC_NEAREST); Adept::Entity* pTarget = AttackTargetIfOK(m_CombatAI,nearest_enemy,m_AudioManager,pTarget2 ? false: true); if (pTarget) m_Target.Add(pTarget); } void AttackNearestThreat::Update() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // template T* FindByID(Adept::ObjectID id, Adept::Entity::ClassData* classdata) { if (id == -2) { return (0); } Adept::Entity* entity = NameTable::GetInstance()->FindData(id); if ((entity == 0) || (entity->IsDestroyed() == true) || (entity->IsDerivedFrom(classdata) == false)) { return (0); } return (Cast_Object(T*,entity)); } Adept::ObjectID FindNearestFieldBase(const Stuff::Point3D& pos, Stuff::ChainOf& chain) { Stuff::ChainIteratorOf i(&chain); MFB* t = 0; Stuff::Scalar least_distance = 0; MFB* best = 0; while ((t = i.ReadAndNext()) != 0) { if (t->IsDerivedFrom(FieldBase::DefaultData) == true) { Stuff::Scalar distance(GetLengthSquared(pos,(Stuff::Point3D)t->GetLocalToWorld())); if ((least_distance == 0) || (distance < least_distance)) { best = t; least_distance = distance; } } } if (best == 0) { return (-2); } return (best->objectID); } Stuff::Point3D GetPointInFrontOfObject(Adept::Entity& entity, bool front, Stuff::Scalar collision_volume_radius_multiplier) { const Stuff::LinearMatrix4D& matrix = entity.GetLocalToWorld(); Stuff::UnitVector3D unit_vector; if (front == true) { matrix.GetLocalForwardInWorld(&unit_vector); } else { matrix.GetLocalBackwardInWorld(&unit_vector); } CollisionVolume* cv = entity.GetSolidVolume(); Stuff::Point3D rv(matrix); if (cv == 0) { return (rv); } Stuff::Scalar radius = cv->m_worldSpaceBounds.sphereRadius * collision_volume_radius_multiplier; Stuff::Point3D add(unit_vector); add *= radius; rv += add; return (rv); } bool RepairAtNearestRepairBay::Finished() const { if (m_NumRetries > 3) { return (true); } if (m_CombatAI.ExitedFieldBaseRecently() == true) { return (true); } MFB* mfb = FindByID(m_MFB,MFB::DefaultData); if (mfb == 0) { return (true); } return (false); } bool RepairAtNearestRepairBay::Reset() { EnsureActive(m_CombatAI,false); m_LastMovePoint = Stuff::Point3D(0,0,0); m_MovePoints[0] = m_LastMovePoint; m_MovePoints[1] = m_LastMovePoint; m_MFB = FindNearestFieldBase((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),*(MFB::s_MFBs)); MFB* mfb = FindByID(m_MFB,MFB::DefaultData); if ((mfb && (mfb->m_CurrentMechUsing != &m_CombatAI) && (mfb->m_CurrentMechUsing != NULL)) || (MW4AI::Damage::CanRepair(m_CombatAI.GetSelf()) == false)) { return (false); } StartMoving(); return (true); } void RepairAtNearestRepairBay::Init() { Verify(MFB::s_MFBs != 0); m_AudioManager.PlayAudio(AUDIO_OK,m_CombatAI); m_NumRetries = 0; m_IssuedSetMoveHead = false; if (Reset() == false) { m_AudioManager.PlayAudio(LancemateAudio::AUDIO_NO,m_CombatAI); m_NumRetries = 4; } } void RepairAtNearestRepairBay::StartMoving() { MFB* mfb = FindByID(m_MFB,MFB::DefaultData); if (mfb == 0) { return; } mfb->m_CurrentMechUsing = &m_CombatAI; Stuff::Point3D forward = GetPointInFrontOfObject(*mfb,true,1.9f); Stuff::Point3D backward = GetPointInFrontOfObject(*mfb,false,1.9f); if (GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),forward) < GetLengthSquared((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),backward)) { m_MovePoints[0] = forward; m_MovePoints[1] = GetPointInFrontOfObject(*mfb,true,0.5f); } else { m_MovePoints[0] = backward; m_MovePoints[1] = GetPointInFrontOfObject(*mfb,false,0.5f); } m_LastMovePoint = m_MovePoints[0]; m_CombatAI.Reset(); m_CombatAI.orderMoveToLocPoint(m_LastMovePoint,60,true,false); } void RepairAtNearestRepairBay::Update() { MFB* mfb = FindByID(m_MFB,MFB::DefaultData); if (mfb == 0) { m_MFB = FindNearestFieldBase((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),*(MFB::s_MFBs)); mfb = FindByID(m_MFB,MFB::DefaultData); if (mfb != 0) { StartMoving(); } } if (mfb != 0) { if ((m_CombatAI.CanAct() == true) && (m_CombatAI.MovedWithin(4.0f) == false)) { ++m_NumRetries; if (m_NumRetries > 3) { m_AudioManager.PlayAudio(AUDIO_NO,m_CombatAI); } else { m_CombatAI.m_LastTimeMoved = (Stuff::Scalar)gos_GetElapsedTime(); if (m_NumRetries > 2) { NotifyNoPath(); } else { Reset(); } } } else { if (m_LastMovePoint == m_MovePoints[0]) { Stuff::Point3D pos(m_CombatAI.GetSelf().GetLocalToWorld()); Stuff::Point3D dest = m_LastMovePoint; if (m_CombatAI.getCurPath() != 0) { dest = m_CombatAI.getCurPath()->End(); } if (GetLengthSquared(pos,dest) < 20 * 20) { m_LastMovePoint = m_MovePoints[1]; m_CombatAI.ClearMoveOrder (); m_CombatAI.Reset(); m_CombatAI.SetRamTarget(mfb); Check_Object (m_CombatAI.getVehicle()->GetGameModel ()); const Vehicle__GameModel *model = static_cast (m_CombatAI.getVehicle()->GetGameModel ()) ; Scalar normalspeed = ((float) ConvertKPHtoMPS(30)) / ((float) model->maxSpeed); Scalar endspeed = ((float) ConvertKPHtoMPS(30)) / ((float) model->maxSpeed); m_CombatAI.SetMoveHead (m_LastMovePoint,normalspeed,Point3D (-1,-1,-1),endspeed); m_IssuedSetMoveHead = true; // m_CombatAI.orderMoveToLocPoint(m_LastMovePoint,600,true,false); } } } } } Stuff::Point3D RepairAtNearestRepairBay::GetLeashPoint() const { MFB* mfb = FindByID(m_MFB,MFB::DefaultData); if (mfb != 0) { return ((Stuff::Point3D)mfb->GetLocalToWorld()); } return (LancemateCommand::GetLeashPoint()); } RepairAtNearestRepairBay::~RepairAtNearestRepairBay() { MFB* mfb = FindByID(m_MFB,MFB::DefaultData); if ((mfb != 0) && (mfb->m_CurrentMechUsing == &m_CombatAI)) { mfb->m_CurrentMechUsing = 0; } } void RepairAtNearestRepairBay::NotifyNoPath() { MFB* mfb = FindByID(m_MFB,MFB::DefaultData); if (mfb == 0) { Reset(); return; } mfb->m_CurrentMechUsing = &m_CombatAI; Stuff::Point3D forward = GetPointInFrontOfObject(*mfb,true,1.9f); Stuff::Point3D backward = GetPointInFrontOfObject(*mfb,false,1.9f); if (m_MovePoints[0] == backward) { m_MovePoints[0] = forward; m_MovePoints[1] = GetPointInFrontOfObject(*mfb,true,0.5f); } else { m_MovePoints[0] = backward; m_MovePoints[1] = GetPointInFrontOfObject(*mfb,false,0.5f); } m_LastMovePoint = m_MovePoints[0]; m_CombatAI.Reset(); m_CombatAI.orderMoveToLocPoint(m_LastMovePoint,600,true,false); } bool RepairAtNearestRepairBay::CanInterrupt() const { if (m_IssuedSetMoveHead == true) { return (false); } return ((m_LastMovePoint == m_MovePoints[0]) || (m_NumRetries > 0)); } void StandardLancemateSound(const Stuff::MString& prefix, Stuff::MString& s) { s = "VO\\Generic\\"; s += prefix; s += (gos_rand() & 0x01) ? "1" : "2"; s += "_Ian.wav{handle}"; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ExecuteForAllLancemates(): executes a LancemateFunction functor for a given list of lancemates // void ExecuteForAllLancemates(const lancemate_list& lancemates, LancemateCommands::ID command_id) { Verify(NameTable::GetInstance() != 0); MWObject* leader = 0; {for (lancemate_list::const_iterator i = lancemates.begin(); i != lancemates.end(); ++i) { MWObject* mwobject = MWObjectForID(*i); if (mwobject != 0) { Check_Object(mwobject); MWObject* l = GetLeader(*mwobject); if (l != 0) { Check_Object(l); leader = l; break; } } }} if (leader == 0) { return; } bool notified(false); {for (lancemate_list::const_iterator i = lancemates.begin(); i != lancemates.end(); ++i) { MWObject* mwobject = MWObjectForID(*i); if ((mwobject != 0) && (mwobject != leader) && (mwobject->GetAI() != 0) && (mwobject->GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true)) { if ((mwobject->GetAI() == 0) || (mwobject->GetAI()->IsDerivedFrom(CombatAI::DefaultData) == false)) { continue; } CombatAI* m_CombatAI = Cast_Object(CombatAI*,mwobject->GetAI()); if ((m_CombatAI->GetSquadOrders() != 0) && (m_CombatAI->GetSquadOrders()->CanExecuteCommands() == true)) { if (notified == false) { notified = true; Stuff::MString s(""); switch (command_id) { case LANCEMATE_ATTACKPLAYERSTARGET: StandardLancemateSound("Attack",s); break; case LANCEMATE_DEFENDPLAYERSTARGET: StandardLancemateSound("Defend",s); break; case LANCEMATE_FORMONME: StandardLancemateSound("Form",s); break; case LANCEMATE_HOLDFIRE: StandardLancemateSound("Hold",s); break; case LANCEMATE_GOTOMYNAVPOINT: StandardLancemateSound("Goto",s); break; case LANCEMATE_STOP: StandardLancemateSound("Stop",s); break; case LANCEMATE_SHUTDOWN: StandardLancemateSound("Shutdown",s); break; case LANCEMATE_ATTACKNEARESTTHREAT: StandardLancemateSound("AttackNear",s); break; case LANCEMATE_REPAIRATNEARESTREPAIRBAY: StandardLancemateSound("Repair",s); break; } if (s.GetLength() > 0) { AudioCommand *command = AudioCommand::Create(AudioRenderer::VOType, s, 1.0f, 1.0f); if (command) { Check_Object(command); command->Play(); } } } Stuff::Auto_Ptr lancemate_command(m_CombatAI->GetSquadOrders()->CreateCommand(command_id)); // Stuff::Auto_Ptr lancemate_command(CreateLancemateCommand(command_id,*m_CombatAI,*leader,m_CombatAI->GetSquadOrders()->GetAudioManager())); if (lancemate_command.GetPointer() != 0) { m_CombatAI->GetSquadOrders()->IssueCommand(lancemate_command); } } } }} } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Execute(): the core, public execution function // void LancemateCommands::Execute(ID command, const lancemate_list& lancemates) { Verify(command > LANCEMATE_FIRST); Verify(command < LANCEMATE_LAST); gos_PushCurrentHeap(Heap); ExecuteForAllLancemates(lancemates,command); gos_PopCurrentHeap(); } void LancemateCommands::ExecuteForAllLancemates2(Mech* paMechs[], int nMechs, LancemateCommands::ID command_id, MWObject* pLeader, MWObject* pTarget) { Verify(command_id > LANCEMATE_FIRST); Verify(command_id < LANCEMATE_LAST); Verify(NameTable::GetInstance() != 0); gos_PushCurrentHeap(Heap); for (int i = 0; i < nMechs; ++i) { MWObject* mwobject = paMechs[i]; if ((mwobject != 0) && (mwobject != pLeader)) { AI* pAI = mwobject->GetAI(); if ((pAI != 0) && (pAI->IsDerivedFrom(CombatAI::DefaultData) == true)) { CombatAI* pCombatAI = Cast_Object(CombatAI*,pAI); if ((pCombatAI->GetSquadOrders() != 0) && (pCombatAI->GetSquadOrders()->CanExecuteCommands() == true)) { Stuff::Auto_Ptr lancemate_command(pCombatAI->GetSquadOrders()->CreateCommand(command_id,pTarget)); // Stuff::Auto_Ptr lancemate_command(CreateLancemateCommand(command_id,m_CombatAI,*leader,m_CombatAI.GetSquadOrders()->GetAudioManager())); if (lancemate_command.GetPointer() != 0) { pCombatAI->GetSquadOrders()->IssueCommand(lancemate_command); } } } } } gos_PopCurrentHeap(); }