Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.1 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "NextBotChasePath.h"
  8. #include "tier1/fmtstr.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. //----------------------------------------------------------------------------------------------
  12. /**
  13. * Try to cutoff our chase subject
  14. */
  15. Vector ChasePath::PredictSubjectPosition( INextBot *bot, CBaseEntity *subject ) const
  16. {
  17. ILocomotion *mover = bot->GetLocomotionInterface();
  18. const Vector &subjectPos = subject->GetAbsOrigin();
  19. Vector to = subjectPos - bot->GetPosition();
  20. to.z = 0.0f;
  21. float flRangeSq = to.LengthSqr();
  22. // don't lead if subject is very far away
  23. float flLeadRadiusSq = GetLeadRadius();
  24. flLeadRadiusSq *= flLeadRadiusSq;
  25. if ( flRangeSq > flLeadRadiusSq )
  26. return subjectPos;
  27. // Normalize in place
  28. float range = sqrt( flRangeSq );
  29. to /= ( range + 0.0001f ); // avoid divide by zero
  30. // estimate time to reach subject, assuming maximum speed
  31. float leadTime = 0.5f + ( range / ( mover->GetRunSpeed() + 0.0001f ) );
  32. // estimate amount to lead the subject
  33. Vector lead = leadTime * subject->GetAbsVelocity();
  34. lead.z = 0.0f;
  35. if ( DotProduct( to, lead ) < 0.0f )
  36. {
  37. // the subject is moving towards us - only pay attention
  38. // to his perpendicular velocity for leading
  39. Vector2D to2D = to.AsVector2D();
  40. to2D.NormalizeInPlace();
  41. Vector2D perp( -to2D.y, to2D.x );
  42. float enemyGroundSpeed = lead.x * perp.x + lead.y * perp.y;
  43. lead.x = enemyGroundSpeed * perp.x;
  44. lead.y = enemyGroundSpeed * perp.y;
  45. }
  46. // compute our desired destination
  47. Vector pathTarget = subjectPos + lead;
  48. // validate this destination
  49. // don't lead through walls
  50. if ( lead.LengthSqr() > 36.0f )
  51. {
  52. float fraction;
  53. if ( !mover->IsPotentiallyTraversable( subjectPos, pathTarget, ILocomotion::IMMEDIATELY, &fraction ) )
  54. {
  55. // tried to lead through an unwalkable area - clip to walkable space
  56. pathTarget = subjectPos + fraction * ( pathTarget - subjectPos );
  57. }
  58. }
  59. // don't lead over cliffs
  60. CNavArea *leadArea = NULL;
  61. #ifdef NEED_GPGLOBALS_SERVERCOUNT_TO_DO_THIS
  62. CBaseCombatCharacter *pBCC = subject->MyCombatCharacterPointer();
  63. if ( pBCC && CloseEnough( pathTarget, subjectPos, 3.0 ) )
  64. {
  65. pathTarget = subjectPos;
  66. leadArea = pBCC->GetLastKnownArea(); // can return null?
  67. }
  68. else
  69. {
  70. struct CacheEntry_t
  71. {
  72. CacheEntry_t() : pArea(NULL) {}
  73. Vector target;
  74. CNavArea *pArea;
  75. };
  76. static int iServer;
  77. static CacheEntry_t cache[4];
  78. static int iNext;
  79. int i;
  80. bool bFound = false;
  81. if ( iServer != gpGlobals->serverCount )
  82. {
  83. for ( i = 0; i < ARRAYSIZE(cache); i++ )
  84. {
  85. cache[i].pArea = NULL;
  86. }
  87. iServer = gpGlobals->serverCount;
  88. }
  89. else
  90. {
  91. for ( i = 0; i < ARRAYSIZE(cache); i++ )
  92. {
  93. if ( cache[i].pArea && CloseEnough( cache[i].target, pathTarget, 2.0 ) )
  94. {
  95. pathTarget = cache[i].target;
  96. leadArea = cache[i].pArea;
  97. bFound = true;
  98. break;
  99. }
  100. }
  101. }
  102. if ( !bFound )
  103. {
  104. leadArea = TheNavMesh->GetNearestNavArea( pathTarget );
  105. if ( leadArea )
  106. {
  107. cache[iNext].target = pathTarget;
  108. cache[iNext].pArea = leadArea;
  109. iNext = ( iNext + 1 ) % ARRAYSIZE( cache );
  110. }
  111. }
  112. }
  113. #else
  114. leadArea = TheNavMesh->GetNearestNavArea( pathTarget );
  115. #endif
  116. if ( !leadArea || leadArea->GetZ( pathTarget.x, pathTarget.y ) < pathTarget.z - mover->GetMaxJumpHeight() )
  117. {
  118. // would fall off a cliff
  119. return subjectPos;
  120. }
  121. /** This needs more thought - it is preventing bots from using dropdowns
  122. if ( mover->HasPotentialGap( subjectPos, pathTarget, &fraction ) )
  123. {
  124. // tried to lead over a cliff - clip to safe region
  125. pathTarget = subjectPos + fraction * ( pathTarget - subjectPos );
  126. }
  127. */
  128. return pathTarget;
  129. }
  130. // if the victim is a player, poke them so they know they're being chased
  131. void DirectChasePath::NotifyVictim( INextBot *me, CBaseEntity *victim )
  132. {
  133. CBaseCombatCharacter *pBCCVictim = ToBaseCombatCharacter( victim );
  134. if ( !pBCCVictim )
  135. return;
  136. pBCCVictim->OnPursuedBy( me );
  137. }