//===========================================================================// // File: MechAI.cpp // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 07/13/99 AHF Created file // //---------------------------------------------------------------------------// // Copyright (C) 1999, Microsoft // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include "MW4Headers.hpp" #include "Adept\AdeptHeaders.hpp" #include "mw4.hpp" #include "planeai.hpp" #include "Adept\Interface.hpp" #include "Adept\Application.hpp" #include "Adept\player.hpp" #include "Adept\map.hpp" #include "torso.hpp" #include "Airplane.hpp" #include "helicopter.hpp" #include "dropship.hpp" #include #include #include "cheap_move.hpp" #include "aiutils.hpp" #include "dropship.hpp" #pragma warning (push) #include #pragma warning (pop) const int OBSTACLELOOKAHEAD = 50; const Stuff::Time FLYBY_TIME = 1.0; // 1 second fly by const Stuff::Scalar flyby_distance = 200.0f; using namespace MechWarrior4; using namespace MW4AI; extern __int64 tMoverAITime,tDynamicObstacleTime; //############################################################################# //############################### AI ############################## //############################################################################# PlaneAI::ClassData* PlaneAI::DefaultData = NULL; const Receiver::MessageEntry PlaneAI::MessageEntries[]= { MESSAGE_ENTRY(PlaneAI, DebugText), }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void PlaneAI::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( PlaneAIClassID, "MechWarrior4::PlaneAI", CombatAI::DefaultData, ELEMENTS(MessageEntries), MessageEntries, (Entity::Factory)Make, (Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage, ExecutionStateEngine::DefaultData, (Entity::GameModel::Factory)GameModel::ConstructGameModel, NULL, (Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify, (Entity::GameModel::ModelWrite)GameModel::WriteToText, (Entity::GameModel::ModelSave)GameModel::SaveGameModel ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void PlaneAI::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PlaneAI* PlaneAI::Make(CreateMessage *message,ReplicatorID *base_id) { Check_Object(message); AutoHeap local_heap (g_AIHeap); PlaneAI *new_entity = new PlaneAI(DefaultData, message, base_id, NULL); Check_Object(new_entity); new_entity->SyncMatrices(true); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PlaneAI::PlaneAI(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element) : CombatAI(class_data, message, base_id, element), m_ContinuousFlybySwingPoint(-1,-1,-1) { AutoHeap local_heap (g_AIHeap); Check_Pointer(this); Check_Object(message); m_InMove = 0; m_InMoveDir = 1; m_AirLockedRects.reserve (20); m_ObstacleAvoidPoint = Point3D (-1,-1,-1); m_AttackRun = false; m_Aborting = false; m_FinishedFlyBy = false; if (Network::GetInstance()->AmIServer()) { Entity *ent = getEntity (); Check_Object(ent); Verify (ent->IsDerivedFrom (Airplane::DefaultData)); m_Airplane = Cast_Object (Airplane *,ent); // if (!m_Airplane->IsDerivedFrom (Dropship::DefaultData)) m_Airplane->TurnOn (); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PlaneAI::~PlaneAI() { } bool PlaneAI::PointIsOK(const Stuff::Point3D& point) const { Check_Object (this); Check_Object (MW4AI::g_MissionGraph); Check_Object (m_Vehicle); return true; } void PlaneAI::ClearPathLock (bool quitting,bool fromram) { Check_Object (this); __int64 TempTime=GetCycles(); stlport::vector::iterator iter2; Verify (m_AirLockedRects.size () == 0); for (iter2 = m_AirLockedRects.begin ();iter2 != m_AirLockedRects.end ();iter2++) { g_AirRect4DHash->RemoveRect (*iter2); g_AirRect4DHash->VerifyRectGone (*iter2); delete *iter2; } m_AirLockedRects.clear (); MoverAI::ClearPathLock (quitting,fromram); tDynamicObstacleTime+=GetCycles()-TempTime; } bool PlaneAI::PathLock (bool secondrun,bool fromram) { Check_Object (this); if (!m_Vehicle) return false; __int64 TempTime=GetCycles(); Check_Object (m_Vehicle); Check_Object (m_Vehicle->GetGameModel ()); Airplane *airplane; airplane = Cast_Object (Airplane *,m_Vehicle); if (airplane->OnGround ()) { MoverAI::PathLock (secondrun,fromram); } tDynamicObstacleTime+=GetCycles()-TempTime; return false; } void PlaneAI::NotifyNoPath(void) { inherited::NotifyNoPath (); } void PlaneAI::NotifyReachedDestination(void) { inherited::NotifyReachedDestination (); } void PlaneAI::NotifyCollided(Entity *who) { inherited::NotifyCollided (who); } bool PlaneAI::AddLockRect (Rect4D *newrect,bool carecollide,Stuff::Time till) { delete newrect; return false; #if 0 Check_Object (this); Rect4D *crect; Point3D loc; Check_Pointer (g_AirRect4DHash); Check_Pointer (newrect); if ((!carecollide) || (!m_AttackRun)) { g_AirRect4DHash->AddRect (newrect); m_AirLockedRects.push_back (newrect); return false; } crect = g_AirRect4DHash->Collide (*newrect); if (crect) { Verify (m_Vehicle); loc = (Point3D) m_Vehicle->GetLocalToWorld (); UnitVector3D p1(m_Vehicle->worldSpaceVelocity.linearMotion),p2; Point3D extra,otherloc; Scalar dot = 5.0f; if ((crect->who) && (crect->who->IsDerivedFrom (AI::DefaultData))) { AI* tempai; tempai = (AI*) crect->who; if (tempai->getEntity ()) { otherloc = (Point3D) tempai->getEntity ()->GetLocalToWorld (); extra.Subtract (otherloc,loc); if (Small_Enough (extra.GetLength ())) dot = 0.0f; else { p2 = extra; dot = (p1.x*p2.x) + (p1.z*p2.z); } } } if (dot > 0) { HandleObstacle (crect); delete newrect; return true; } else { g_AirRect4DHash->AddRect (newrect); m_AirLockedRects.push_back (newrect); } } else { g_AirRect4DHash->AddRect (newrect); m_AirLockedRects.push_back (newrect); } return false; #endif } bool PlaneAI::Avoiding (PlaneAI *who) { stlport::vector::iterator iter; for (iter = m_AvoidList.begin ();iter != m_AvoidList.end ();iter++) if (iter->who == who) return true; return false; } void PlaneAI::AddAvoid (PlaneAI *who) { AutoHeap local_heap (g_AIHeap); stlport::vector::iterator iter; for (iter = m_AvoidList.begin ();iter != m_AvoidList.end ();iter++) if (iter->who == who) break; if (iter == m_AvoidList.end ()) { m_AvoidList.push_back (AvoidData (who,gos_GetElapsedTime ())); } else { iter->active = gos_GetElapsedTime (); } } void PlaneAI::CleanAvoidList (void) { Stuff::Time current; stlport::vector::iterator iter; AutoHeap local_heap (g_AIHeap); current = gos_GetElapsedTime (); iter = m_AvoidList.begin (); while (iter != m_AvoidList.end ()) { if ((iter->active - current) >= 2.0) iter = m_AvoidList.erase (iter); else iter++; } } void PlaneAI::RemoveAvoid (PlaneAI *who) { stlport::vector::iterator iter; AutoHeap local_heap (g_AIHeap); for (iter = m_AvoidList.begin ();iter != m_AvoidList.end ();iter++) if (iter->who == who) break; if (iter != m_AvoidList.end ()) { m_AvoidList.erase (iter); } } void PlaneAI::KillAvoidList (void) { AutoHeap local_heap (g_AIHeap); m_AvoidList.clear (); } Point3D PlaneAI::FindAvoidPoint (Rect4D *rect) { Point3D head; Check_Object (rect->who); Verify (rect->who->IsDerivedFrom (PlaneAI::DefaultData)); Check_Object (m_Vehicle); PlaneAI *plane = Cast_Object (PlaneAI *,rect->who); Check_Object (plane->m_Vehicle); head.x = ((rect->right - rect->left) / 2.0f) + rect->left; head.z = ((rect->bottom - rect->top) / 2.0f) + rect->top; head.y = 0; Scalar rad1aa,rad2aa; CollisionVolume *col1,*col2; col1 = m_Vehicle->GetHierarchicalVolume (); if (!col1) col1 = m_Vehicle->GetSolidVolume (); if (!col1) rad1aa = 15.0f; else rad1aa = col1->m_localSpaceBounds.sphereRadius; col2 = plane->m_Vehicle->GetHierarchicalVolume (); if (!col2) col2 = plane->m_Vehicle->GetSolidVolume (); if (!col2) rad2aa = 15.0f; else rad2aa = col2->m_localSpaceBounds.sphereRadius; rad1aa *= 1.2f; rad2aa *= 1.2f; Vector3D dir; if (Small_Enough (rect->velocity.linearMotion.GetLengthSquared ())) { dir.x = 1; dir.y = 0; dir.z = 0; } else dir.Normalize (rect->velocity.linearMotion); dir *= (rad1aa+rad2aa); head -= dir; return head; } void PlaneAI::AttackRunDone (void) { m_AttackRun = false; m_Aborting = false; m_FinishedFlyBy = true; } void PlaneAI::EndAttackRun (void) { if (m_Aborting) return; m_EndStart = gos_GetElapsedTime (); m_Aborting = true; } void PlaneAI::AbortAttackRun (void) { Check_Object (m_Airplane); m_EndStart = gos_GetElapsedTime ()-FLYBY_TIME; m_Aborting = true; m_Airplane->m_Attacking = false; } bool PlaneAI::ValidAttackPoint (const Point3D& point) { Check_Object (m_Vehicle); Point3D endpt,extra; Point3D loc (m_Vehicle->GetLocalToWorld ()); endpt.Subtract (point,loc); endpt.y = 0; extra.Normalize (endpt); Check_Object (m_Vehicle->GetGameModel ()); const Vehicle__GameModel *model = static_cast (m_Vehicle->GetGameModel ()) ; extra *= ((float) (FLYBY_TIME * (double) model->maxSpeed)); extra += point; return g_MissionGraph->AirQuickCheck ((int) loc.x,(int) loc.z,(int) extra.x,(int) extra.z); } bool PlaneAI::FlyBy (const Point3D& point) { if (m_FinishedFlyBy) return true; if ((m_Aborting) || (m_AttackRun)) return false; return (!StartAttackRun (point)); } void PlaneAI::ContinuousFlyBy(const Point3D& point) { if (m_ContinuousFlybySwingPoint != Point3D(-1,-1,-1)) { FlyBy(point); } if ((m_FinishedFlyBy == true) || ((m_CurMove == 0) && (m_OrderSpeed == 0))) { if ((m_ContinuousFlybySwingPoint == Point3D(-1,-1,-1)) || (m_CurMove == 0)) { Stuff::Point3D& p = m_ContinuousFlybySwingPoint; p = point; switch (gos_rand() & 0x03) { case 0: { p.x += flyby_distance; p.z += flyby_distance; break; } case 1: { p.x -= flyby_distance; p.z += flyby_distance; break; } case 2: { p.x += flyby_distance; p.z -= flyby_distance; break; } case 3: { p.x -= flyby_distance; p.z -= flyby_distance; break; } } Clamp(p.x,MW4AI::MinX,MW4AI::MaxX); Clamp(p.z,MW4AI::MinZ,MW4AI::MaxZ); ClearMoveOrder(); orderMoveToLocPoint(p,1000,true,false); return; } Point3D my_pos(vehicle->GetLocalToWorld()); m_ContinuousFlybySwingPoint.y = my_pos.y; if ((m_CurMove == 0) || (m_CurMove->Done() == true) || (GetApproximateLength(m_ContinuousFlybySwingPoint,my_pos) < 50)) { ClearMoveOrder(); m_ContinuousFlybySwingPoint = Point3D(-1,-1,-1); m_FinishedFlyBy = false; } } } bool PlaneAI::StartAttackRun (const Point3D& attackpoint) { Verify (m_Vehicle); m_FinishedFlyBy = false; if ((m_Aborting) || (m_AttackRun)) { return false; } if (!ValidAttackPoint (attackpoint)) { return false; } ClearMoveOrder (); Scalar height; BYTE material; Point3D loc (m_Vehicle->GetLocalToWorld ()); height = GetMapY (attackpoint.x,attackpoint.z,m_Vehicle,material,loc.y); m_AttackPoint = attackpoint; if (attackpoint.y < (height+g_MaxAirHeight)) { m_AttackPoint.y = height + g_MaxAirHeight; } m_AttackRun = true; m_Aborting = false; m_OrderSpeed = 1.0f; m_Airplane->m_Attacking = true; m_Airplane->m_AttackAltitude = g_MaxAirHeight; return true; } void PlaneAI::HandleObstacle (MW4AI::Rect4D *p1,Adept::Entity *p2) { if (p1) // potential collision { Verify (m_AttackRun); Verify (p1->who); Verify (p1->who->IsDerivedFrom (PlaneAI::DefaultData)); PlaneAI *plane = Cast_Object (PlaneAI *,p1->who); Point3D loc(m_Airplane->GetLocalToWorld ()),otherloc (plane->m_Airplane->GetLocalToWorld ()); if (otherloc.y < loc.y) // only avoid plane lower than us since we are decending { AbortAttackRun (); } #if 0 Point3D head; if (!plane->Avoiding (this)) { head = FindAvoidPoint (p1); CommandEntry *com; com = CreateTurnToCommand (head,false); Check_Pointer (com); ExecuteCommand (com); AddAvoid (plane); } #endif } else if (p2) { STOP (("plane ran into another object")); } else { STOP (("somehow a plane went up a slope that was too steep")); } } void PlaneAI::PathDone (Stuff::Time till,bool failed) { Verify (m_Vehicle); NotifyReachedDestination (); if ((m_CurMove->MoveType () == MOVE_PATROL) || (m_CurMove->MoveType () == MOVE_SEMIPATROL)) { CPatrolData *tempmove; tempmove = (CPatrolData *) m_CurMove; switch (tempmove->PatrolType ()) { case PATROL_SINGLE: m_CurMove->Done (true); break; case PATROL_LOOP: m_InMove = 0; m_InMoveDir = 1; break; case PATROL_SEESAW: m_InMoveDir *= -1; m_InMove += m_InMoveDir; break; } } else m_CurMove->Done (true); if (m_Vehicle->IsDerivedFrom (Helicopter::DefaultData)) { CommandEntry *com; com = CreateSetSpeedCommand (0); Check_Pointer (com); ExecuteCommand (com); } } void PlaneAI::ExecuteMoveHead (Stuff::Time till) { if (m_NewMoveHead != m_MoveHead) { m_MoveHead = m_NewMoveHead; m_NeedHeadingAdjust = true; } if (m_NewOutVec != m_OutVec) { m_OutVec = m_NewOutVec; } if (m_NewSpeedRequest != m_SpeedRequest) { m_SpeedRequest = m_NewSpeedRequest; } } void PlaneAI::ExecutePath (Stuff::Time till) { Verify (m_Vehicle); if ((!m_CurMove) && (!(((m_Aborting) || (m_AttackRun))))) return; Stuff::Scalar dist,move,yvalue; CommandEntry *com; Point3D head(0,0,0),destloc,extra; Point3D loc (m_Vehicle->GetLocalToWorld ()); const Vehicle__GameModel *model = static_cast (m_Vehicle->GetGameModel ()) ; Stuff::Scalar speed; speed = m_Vehicle->speedDemand; yvalue = loc.y; loc.y = 0; if (m_AttackRun) { if (!ValidAttackPoint (m_AttackPoint)) { AbortAttackRun (); } destloc = m_AttackPoint; } else { destloc = m_CurMove->Location (m_InMove); } destloc.y = 0; extra.Subtract (destloc,loc); dist = extra.GetLengthSquared (); move = (float) (speed * model->maxSpeed); // use one second buffer move *= move; if ((dist<25.0) || (dist < move)) { if (m_AttackRun) { EndAttackRun (); } else if ((m_InMoveDir > 0) && (m_CurMove->LocationSize () > (m_InMove+m_InMoveDir))) { m_InMove+=m_InMoveDir; } else if ((m_InMoveDir < 0) && ((m_InMove+m_InMoveDir) > 0)) { m_InMove+= m_InMoveDir; } else { PathDone (till); } } if (m_AttackRun) { head = m_AttackPoint; } else { head = m_CurMove->Location (m_InMove); } if (m_Aborting) { BYTE material; Scalar height; if ((m_EndStart+FLYBY_TIME) <= gos_GetElapsedTime ()) { Check_Object (m_Airplane); m_Airplane->m_Attacking = false; } height = GetMapY (loc.x,loc.z,m_Vehicle,material,yvalue); if (yvalue >= (height + m_Airplane->flyingAltitude)) { AttackRunDone (); } else { m_NewMoveHead = Point3D (-1,-1,-1); com = CreateKillTurnToCommand (); Check_Pointer (com); ExecuteCommand (com); return; } } else { m_NewMoveHead = Point3D (-1,-1,-1); com = CreateTurnToCommand (head,false); Check_Pointer (com); ExecuteCommand (com); } m_NewSpeedRequest = m_OrderSpeed; com = CreateSetSpeedCommand (m_OrderSpeed); Check_Pointer (com); ExecuteCommand (com); } void PlaneAI::ExecuteMoveCommand (Stuff::Time till) { Verify (m_Vehicle); if (!m_CurMove) return; if (m_CurMove->Done ()) return; if (!m_CurMove->Executing ()) { m_CurMove->Executing (true); m_OrderSpeed = m_CurMove->Speed (); } else { m_OrderSpeed = m_CurMove->Speed (); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void PlaneAI::PreCollisionExecute(Time till) { Check_Object(this); AI_LOGIC("Pre-Collision::Plane AI"); // //--------------------------------- // Verify that this always executes //--------------------------------- // Check_Object(executionState); Verify(executionState->GetState() != ExecutionStateEngine::NeverExecuteState); m_Deactive = false; CleanAvoidList (); CombatAI::PreCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void PlaneAI::DebugTextMessageHandler(Adept::ReceiverDataMessageOf *message) { Check_Object(this); Check_Object(message); Verify(message->messageID == DebugTextMessageID); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void PlaneAI::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } void PlaneAI::TurnOn (void) { } void PlaneAI::TurnOff (void) { m_Deactive = false; } void PlaneAI::ExecuteMoveCommand (MW4AI::CommandEntry *command) { Verify (m_Vehicle); Check_Pointer (command); ClearMoveOrder (); m_CurMove = (CMoveData *) command->DataPtr (); Stuff::Point3D temp; temp = (Stuff::Point3D) m_Vehicle->GetLocalToWorld (); m_CurMove->CreatePlaneRequests (temp); m_OrderSpeed = m_CurMove->Speed (); m_InMove = 0; m_InMoveDir = 1; Check_Pointer (m_CurMove); } void PlaneAI::hellteleport (void) { Check_Object(m_Vehicle); Verify(m_Vehicle->IsDerivedFrom(Airplane::DefaultData)); Airplane *flyer = Cast_Object(Airplane *, m_Vehicle); flyer->executionState->RequestState(Airplane::ExecutionStateEngine::NeverExecuteState); } bool PlaneAI::orderTakeOff (int distance) { Check_Object(m_Vehicle); Verify(m_Vehicle->IsDerivedFrom(Airplane::DefaultData)); Airplane *flyer = Cast_Object(Airplane *, m_Vehicle); if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::NeverExecuteState) flyer->TakeOff((Scalar)distance); else if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::AlwaysExecuteState) flyer->TakeOff((Scalar)distance); else if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::TaxiState) flyer->TakeOff((Scalar)distance); else if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::FlyingMotionState) return true; return false; } bool PlaneAI::orderLand (void) { Check_Object(m_Vehicle); Verify(m_Vehicle->IsDerivedFrom(Helicopter::DefaultData)); ClearMoveOrder (); Helicopter *flyer = Cast_Object(Helicopter *, m_Vehicle); if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::NeverExecuteState) return true; else if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::AlwaysExecuteState) return true; else if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::TaxiState) return true; else if(flyer->executionState->GetState() == Airplane::ExecutionStateEngine::FlyingMotionState) flyer->Land(); return false; } bool PlaneAI::orderDoorOpen(void) { Check_Object(m_Vehicle); Verify(m_Vehicle->IsDerivedFrom(Dropship::DefaultData)); ClearMoveOrder (); Dropship *drop = Cast_Object(Dropship *, m_Vehicle); return drop->orderDoorOpen(); } bool PlaneAI::orderDoorClose(void) { Check_Object(m_Vehicle); Verify(m_Vehicle->IsDerivedFrom(Dropship::DefaultData)); ClearMoveOrder (); Dropship *drop = Cast_Object(Dropship *, m_Vehicle); return drop->orderDoorClose(); } void PlaneAI::orderMoveLookOut (void) { if (!m_Active) return; gosREPORT (false,"Cannot give a lookout order to a plane"); } void PlaneAI::orderMoveSit (bool shutdown) { if (!m_Active) return; gosREPORT (false,"Cannot give a sit order to a plane"); }