Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions src/game/server/NextBot/Path/NextBotChasePath.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ class ChasePath : public PathFollower

private:
void RefreshPath( INextBot *bot, CBaseEntity *subject, const IPathCost &cost, Vector *pPredictedSubjectPos );
#ifdef NEO
bool HasReachableArea( void ) const;
bool IsLastLegWalkable( INextBot *bot ) const;
void BackOff( INextBot *bot, CBaseEntity *subject );

bool m_isPartialPath = false; // following a path the search could not complete
#endif

CountdownTimer m_failTimer; // throttle re-pathing if last path attempt failed
CountdownTimer m_throttleTimer; // require a minimum time between re-paths
Expand Down Expand Up @@ -103,9 +110,81 @@ inline void ChasePath::Update( INextBot *bot, CBaseEntity *subject, const IPathC
// maintain the path to the subject
RefreshPath( bot, subject, cost, pPredictedSubjectPos );

#ifdef NEO
// A partial path ends in a straight leg from the last area the search reached to the subject,
// through whatever stopped the search. Walk it only as far as that area goes, then wait there.
const bool onLastLeg = subject && IsValid() && m_isPartialPath && GetCurrentGoal() == LastSegment();
if ( onLastLeg && !IsLastLegWalkable( bot ) )
{
bot->OnMoveToFailure( this, FAIL_NO_PATH_EXISTS );
BackOff( bot, subject );
Invalidate();
return;
}
#endif

// move along the path towards the subject
PathFollower::Update( bot );

#ifdef NEO
// the follower dropped the path on the leg (fell off, stuck): wait instead of rebuilding it next tick
if ( onLastLeg && !IsValid() )
{
BackOff( bot, subject );
}
#endif
}


#ifdef NEO
//----------------------------------------------------------------------------------------------
/**
* True if a partial path crosses an area before its last leg. Without one the bot already stands
* in the last area the search reached, and the path is only the unreachable leg.
*/
inline bool ChasePath::HasReachableArea( void ) const
{
return PriorSegment( LastSegment() ) != FirstSegment();
}


//----------------------------------------------------------------------------------------------
/**
* True while the bot on the last leg of a partial path still has the last area the search reached
* just ahead of it, so it stops at that area's edge instead of walking into what stopped the search
*/
inline bool ChasePath::IsLastLegWalkable( INextBot *bot ) const
{
const CNavArea *lastArea = LastSegment()->area;
if ( !lastArea || bot->GetLocomotionInterface()->IsStuck() )
{
return false;
}

// three quarters of a hull width, so the bot stops before its body crosses the edge
const float edgeLookAhead = 24.0f;

Vector toEnd = LastSegment()->pos - bot->GetPosition();
toEnd.z = 0.0f;
toEnd.NormalizeInPlace();

return lastArea->IsOverlapping( bot->GetPosition() + edgeLookAhead * toEnd );
}


//----------------------------------------------------------------------------------------------
// Seconds of re-path back-off per unit of range to the subject,
// the rate RefreshPath's failure branch uses
static constexpr float CHASE_RETRY_SECONDS_PER_UNIT = 0.005f;

/**
* Throttle the next repath by range to the subject, as a failed path computation does
*/
inline void ChasePath::BackOff( INextBot *bot, CBaseEntity *subject )
{
m_failTimer.Start( CHASE_RETRY_SECONDS_PER_UNIT * ( bot->GetRangeTo( subject ) ) );
}
#endif


//----------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -218,6 +297,17 @@ inline void ChasePath::RefreshPath( INextBot *bot, CBaseEntity *subject, const I
isPath = Compute( bot, pathTarget, cost, GetMaxPathLength() );
}

#ifdef NEO
// Even if the chase path only has a partial path, get as close to the target as possible
const bool isCompletePath = isPath;
if ( !isPath && IsValid() )
{
isPath = HasReachableArea();
}

m_isPartialPath = isPath && !isCompletePath;
#endif

if ( isPath )
{
if ( bot->IsDebugging( NEXTBOT_PATH ) )
Expand Down
Loading