mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
//========= 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 );
|
||||
}
|
||||
Reference in New Issue
Block a user