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.
166 lines
4.1 KiB
166 lines
4.1 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
|
|
#include "NextBotChasePath.h" |
|
#include "tier1/fmtstr.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
//---------------------------------------------------------------------------------------------- |
|
/** |
|
* Try to cutoff our chase subject |
|
*/ |
|
Vector ChasePath::PredictSubjectPosition( INextBot *bot, CBaseEntity *subject ) const |
|
{ |
|
ILocomotion *mover = bot->GetLocomotionInterface(); |
|
|
|
const Vector &subjectPos = subject->GetAbsOrigin(); |
|
|
|
Vector to = subjectPos - bot->GetPosition(); |
|
to.z = 0.0f; |
|
float flRangeSq = to.LengthSqr(); |
|
|
|
// don't lead if subject is very far away |
|
float flLeadRadiusSq = GetLeadRadius(); |
|
flLeadRadiusSq *= flLeadRadiusSq; |
|
if ( flRangeSq > flLeadRadiusSq ) |
|
return subjectPos; |
|
|
|
// Normalize in place |
|
float range = sqrt( flRangeSq ); |
|
to /= ( range + 0.0001f ); // avoid divide by zero |
|
|
|
// estimate time to reach subject, assuming maximum speed |
|
float leadTime = 0.5f + ( range / ( mover->GetRunSpeed() + 0.0001f ) ); |
|
|
|
// estimate amount to lead the subject |
|
Vector lead = leadTime * subject->GetAbsVelocity(); |
|
lead.z = 0.0f; |
|
|
|
if ( DotProduct( to, lead ) < 0.0f ) |
|
{ |
|
// the subject is moving towards us - only pay attention |
|
// to his perpendicular velocity for leading |
|
Vector2D to2D = to.AsVector2D(); |
|
to2D.NormalizeInPlace(); |
|
|
|
Vector2D perp( -to2D.y, to2D.x ); |
|
|
|
float enemyGroundSpeed = lead.x * perp.x + lead.y * perp.y; |
|
|
|
lead.x = enemyGroundSpeed * perp.x; |
|
lead.y = enemyGroundSpeed * perp.y; |
|
} |
|
|
|
// compute our desired destination |
|
Vector pathTarget = subjectPos + lead; |
|
|
|
// validate this destination |
|
|
|
// don't lead through walls |
|
if ( lead.LengthSqr() > 36.0f ) |
|
{ |
|
float fraction; |
|
if ( !mover->IsPotentiallyTraversable( subjectPos, pathTarget, ILocomotion::IMMEDIATELY, &fraction ) ) |
|
{ |
|
// tried to lead through an unwalkable area - clip to walkable space |
|
pathTarget = subjectPos + fraction * ( pathTarget - subjectPos ); |
|
} |
|
} |
|
|
|
// don't lead over cliffs |
|
CNavArea *leadArea = NULL; |
|
|
|
#ifdef NEED_GPGLOBALS_SERVERCOUNT_TO_DO_THIS |
|
CBaseCombatCharacter *pBCC = subject->MyCombatCharacterPointer(); |
|
if ( pBCC && CloseEnough( pathTarget, subjectPos, 3.0 ) ) |
|
{ |
|
pathTarget = subjectPos; |
|
leadArea = pBCC->GetLastKnownArea(); // can return null? |
|
} |
|
else |
|
{ |
|
struct CacheEntry_t |
|
{ |
|
CacheEntry_t() : pArea(NULL) {} |
|
Vector target; |
|
CNavArea *pArea; |
|
}; |
|
|
|
static int iServer; |
|
static CacheEntry_t cache[4]; |
|
static int iNext; |
|
int i; |
|
|
|
bool bFound = false; |
|
if ( iServer != gpGlobals->serverCount ) |
|
{ |
|
for ( i = 0; i < ARRAYSIZE(cache); i++ ) |
|
{ |
|
cache[i].pArea = NULL; |
|
} |
|
iServer = gpGlobals->serverCount; |
|
} |
|
else |
|
{ |
|
for ( i = 0; i < ARRAYSIZE(cache); i++ ) |
|
{ |
|
if ( cache[i].pArea && CloseEnough( cache[i].target, pathTarget, 2.0 ) ) |
|
{ |
|
pathTarget = cache[i].target; |
|
leadArea = cache[i].pArea; |
|
bFound = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if ( !bFound ) |
|
{ |
|
leadArea = TheNavMesh->GetNearestNavArea( pathTarget ); |
|
if ( leadArea ) |
|
{ |
|
cache[iNext].target = pathTarget; |
|
cache[iNext].pArea = leadArea; |
|
iNext = ( iNext + 1 ) % ARRAYSIZE( cache ); |
|
} |
|
} |
|
} |
|
#else |
|
leadArea = TheNavMesh->GetNearestNavArea( pathTarget ); |
|
#endif |
|
|
|
|
|
if ( !leadArea || leadArea->GetZ( pathTarget.x, pathTarget.y ) < pathTarget.z - mover->GetMaxJumpHeight() ) |
|
{ |
|
// would fall off a cliff |
|
return subjectPos; |
|
} |
|
|
|
/** This needs more thought - it is preventing bots from using dropdowns |
|
if ( mover->HasPotentialGap( subjectPos, pathTarget, &fraction ) ) |
|
{ |
|
// tried to lead over a cliff - clip to safe region |
|
pathTarget = subjectPos + fraction * ( pathTarget - subjectPos ); |
|
} |
|
*/ |
|
|
|
return pathTarget; |
|
} |
|
|
|
// if the victim is a player, poke them so they know they're being chased |
|
void DirectChasePath::NotifyVictim( INextBot *me, CBaseEntity *victim ) |
|
{ |
|
CBaseCombatCharacter *pBCCVictim = ToBaseCombatCharacter( victim ); |
|
if ( !pBCCVictim ) |
|
return; |
|
|
|
pBCCVictim->OnPursuedBy( me ); |
|
} |