mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-29 17:43:01 +00:00
1697 lines
53 KiB
C++
1697 lines
53 KiB
C++
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
|
||
|
#include "trains.h"
|
||
|
#include "ai_trackpather.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
#define TRACKPATHER_DEBUG_LEADING 1
|
||
|
#define TRACKPATHER_DEBUG_PATH 2
|
||
|
#define TRACKPATHER_DEBUG_TRACKS 3
|
||
|
ConVar g_debug_trackpather( "g_debug_trackpather", "0", FCVAR_CHEAT );
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
BEGIN_DATADESC( CAI_TrackPather )
|
||
|
DEFINE_FIELD( m_vecDesiredPosition, FIELD_POSITION_VECTOR ),
|
||
|
DEFINE_FIELD( m_vecGoalOrientation, FIELD_VECTOR ),
|
||
|
|
||
|
DEFINE_FIELD( m_pCurrentPathTarget, FIELD_CLASSPTR ),
|
||
|
DEFINE_FIELD( m_pDestPathTarget, FIELD_CLASSPTR ),
|
||
|
DEFINE_FIELD( m_pLastPathTarget, FIELD_CLASSPTR ),
|
||
|
DEFINE_FIELD( m_pTargetNearestPath, FIELD_CLASSPTR ),
|
||
|
|
||
|
DEFINE_FIELD( m_strCurrentPathName, FIELD_STRING ),
|
||
|
DEFINE_FIELD( m_strDestPathName, FIELD_STRING ),
|
||
|
DEFINE_FIELD( m_strLastPathName, FIELD_STRING ),
|
||
|
DEFINE_FIELD( m_strTargetNearestPathName, FIELD_STRING ),
|
||
|
|
||
|
DEFINE_FIELD( m_vecLastGoalCheckPosition, FIELD_POSITION_VECTOR ),
|
||
|
DEFINE_FIELD( m_flEnemyPathUpdateTime, FIELD_TIME ),
|
||
|
DEFINE_FIELD( m_bForcedMove, FIELD_BOOLEAN ),
|
||
|
DEFINE_FIELD( m_bPatrolling, FIELD_BOOLEAN ),
|
||
|
DEFINE_FIELD( m_bPatrolBreakable, FIELD_BOOLEAN ),
|
||
|
DEFINE_FIELD( m_bLeading, FIELD_BOOLEAN ),
|
||
|
|
||
|
// Derived class pathing data
|
||
|
DEFINE_FIELD( m_flTargetDistanceThreshold, FIELD_FLOAT ),
|
||
|
DEFINE_FIELD( m_flAvoidDistance, FIELD_FLOAT ),
|
||
|
|
||
|
DEFINE_FIELD( m_flTargetTolerance, FIELD_FLOAT ),
|
||
|
DEFINE_FIELD( m_vecSegmentStartPoint, FIELD_POSITION_VECTOR ),
|
||
|
DEFINE_FIELD( m_vecSegmentStartSplinePoint, FIELD_POSITION_VECTOR ),
|
||
|
DEFINE_FIELD( m_bMovingForward, FIELD_BOOLEAN ),
|
||
|
DEFINE_FIELD( m_bChooseFarthestPoint, FIELD_BOOLEAN ),
|
||
|
DEFINE_FIELD( m_flFarthestPathDist, FIELD_FLOAT ),
|
||
|
DEFINE_FIELD( m_flPathMaxSpeed, FIELD_FLOAT ),
|
||
|
DEFINE_FIELD( m_flTargetDistFromPath, FIELD_FLOAT ),
|
||
|
DEFINE_FIELD( m_flLeadDistance, FIELD_FLOAT ),
|
||
|
DEFINE_FIELD( m_vecTargetPathDir, FIELD_VECTOR ),
|
||
|
DEFINE_FIELD( m_vecTargetPathPoint, FIELD_POSITION_VECTOR ),
|
||
|
|
||
|
DEFINE_FIELD( m_nPauseState, FIELD_INTEGER ),
|
||
|
|
||
|
// Inputs
|
||
|
DEFINE_INPUTFUNC( FIELD_STRING, "SetTrack", InputSetTrack ),
|
||
|
DEFINE_INPUTFUNC( FIELD_STRING, "FlyToSpecificTrackViaPath", InputFlyToPathTrack ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrol", InputStartPatrol ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "StopPatrol", InputStopPatrol ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "StartBreakableMovement", InputStartBreakableMovement ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "StopBreakableMovement", InputStopBreakableMovement ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "ChooseFarthestPathPoint", InputChooseFarthestPathPoint ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "ChooseNearestPathPoint", InputChooseNearestPathPoint ),
|
||
|
DEFINE_INPUTFUNC( FIELD_INTEGER,"InputStartLeading", InputStartLeading ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "InputStopLeading", InputStopLeading ),
|
||
|
|
||
|
// Obsolete, for backwards compatibility
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrolBreakable", InputStartPatrolBreakable ),
|
||
|
DEFINE_INPUTFUNC( FIELD_STRING, "FlyToPathTrack", InputFlyToPathTrack ),
|
||
|
END_DATADESC()
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Initialize pathing data
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InitPathingData( float flTrackArrivalTolerance, float flTargetDistance, float flAvoidDistance )
|
||
|
{
|
||
|
m_flTargetTolerance = flTrackArrivalTolerance;
|
||
|
m_flTargetDistanceThreshold = flTargetDistance;
|
||
|
m_flAvoidDistance = flAvoidDistance;
|
||
|
|
||
|
m_pCurrentPathTarget = NULL;
|
||
|
m_pDestPathTarget = NULL;
|
||
|
m_pLastPathTarget = NULL;
|
||
|
m_pTargetNearestPath = NULL;
|
||
|
m_bLeading = false;
|
||
|
|
||
|
m_flEnemyPathUpdateTime = gpGlobals->curtime;
|
||
|
m_bForcedMove = false;
|
||
|
m_bPatrolling = false;
|
||
|
m_bPatrolBreakable = false;
|
||
|
m_flLeadDistance = 0.0f;
|
||
|
m_bMovingForward = true;
|
||
|
m_vecSegmentStartPoint = m_vecSegmentStartSplinePoint = m_vecDesiredPosition = GetAbsOrigin();
|
||
|
m_bChooseFarthestPoint = true;
|
||
|
m_flFarthestPathDist = 1e10;
|
||
|
m_flPathMaxSpeed = 0;
|
||
|
m_nPauseState = PAUSE_NO_PAUSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::OnRestore( void )
|
||
|
{
|
||
|
BaseClass::OnRestore();
|
||
|
|
||
|
// Restore current path
|
||
|
if ( m_strCurrentPathName != NULL_STRING )
|
||
|
{
|
||
|
m_pCurrentPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strCurrentPathName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pCurrentPathTarget = NULL;
|
||
|
}
|
||
|
|
||
|
// Restore destination path
|
||
|
if ( m_strDestPathName != NULL_STRING )
|
||
|
{
|
||
|
m_pDestPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strDestPathName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pDestPathTarget = NULL;
|
||
|
}
|
||
|
|
||
|
// Restore last path
|
||
|
if ( m_strLastPathName != NULL_STRING )
|
||
|
{
|
||
|
m_pLastPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strLastPathName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pLastPathTarget = NULL;
|
||
|
}
|
||
|
|
||
|
// Restore target nearest path
|
||
|
if ( m_strTargetNearestPathName != NULL_STRING )
|
||
|
{
|
||
|
m_pTargetNearestPath = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strTargetNearestPathName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pTargetNearestPath = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::OnSave( IEntitySaveUtils *pUtils )
|
||
|
{
|
||
|
BaseClass::OnSave( pUtils );
|
||
|
|
||
|
// Stash all the paths into strings for restoration later
|
||
|
m_strCurrentPathName = ( m_pCurrentPathTarget != NULL ) ? m_pCurrentPathTarget->GetEntityName() : NULL_STRING;
|
||
|
m_strDestPathName = ( m_pDestPathTarget != NULL ) ? m_pDestPathTarget->GetEntityName() : NULL_STRING;
|
||
|
m_strLastPathName = ( m_pLastPathTarget != NULL ) ? m_pLastPathTarget->GetEntityName() : NULL_STRING;
|
||
|
m_strTargetNearestPathName = ( m_pTargetNearestPath != NULL ) ? m_pTargetNearestPath->GetEntityName() : NULL_STRING;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Leading distance
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::EnableLeading( bool bEnable )
|
||
|
{
|
||
|
bool bWasLeading = m_bLeading;
|
||
|
m_bLeading = bEnable;
|
||
|
if ( m_bLeading )
|
||
|
{
|
||
|
m_bPatrolling = false;
|
||
|
}
|
||
|
else if ( bWasLeading )
|
||
|
{
|
||
|
|
||
|
// Going from leading to not leading. Refresh the desired position
|
||
|
// to prevent us from hovering around our old, no longer valid lead position.
|
||
|
if ( m_pCurrentPathTarget )
|
||
|
{
|
||
|
SetDesiredPosition( m_pCurrentPathTarget->GetAbsOrigin() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CAI_TrackPather::SetLeadingDistance( float flLeadDistance )
|
||
|
{
|
||
|
m_flLeadDistance = flLeadDistance;
|
||
|
}
|
||
|
|
||
|
float CAI_TrackPather::GetLeadingDistance( ) const
|
||
|
{
|
||
|
return m_flLeadDistance;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Returns the next path along our current path
|
||
|
//-----------------------------------------------------------------------------
|
||
|
inline CPathTrack *CAI_TrackPather::NextAlongCurrentPath( CPathTrack *pPath ) const
|
||
|
{
|
||
|
return CPathTrack::ValidPath( m_bMovingForward ? pPath->GetNext() : pPath->GetPrevious() );
|
||
|
}
|
||
|
|
||
|
inline CPathTrack *CAI_TrackPather::PreviousAlongCurrentPath( CPathTrack *pPath ) const
|
||
|
{
|
||
|
return CPathTrack::ValidPath( m_bMovingForward ? pPath->GetPrevious() : pPath->GetNext() );
|
||
|
}
|
||
|
|
||
|
inline CPathTrack *CAI_TrackPather::AdjustForMovementDirection( CPathTrack *pPath ) const
|
||
|
{
|
||
|
if ( !m_bMovingForward && CPathTrack::ValidPath( pPath->GetPrevious( ) ) )
|
||
|
{
|
||
|
pPath = CPathTrack::ValidPath( pPath->GetPrevious() );
|
||
|
}
|
||
|
return pPath;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Enemy visibility check
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBaseEntity *CAI_TrackPather::FindTrackBlocker( const Vector &vecViewPoint, const Vector &vecTargetPos )
|
||
|
{
|
||
|
trace_t tr;
|
||
|
AI_TraceHull( vecViewPoint, vecTargetPos, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
||
|
return (tr.fraction != 1.0f) ? tr.m_pEnt : NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : &targetPos -
|
||
|
// Output : CBaseEntity
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CPathTrack *CAI_TrackPather::BestPointOnPath( CPathTrack *pPath, const Vector &targetPos, float flAvoidRadius, bool visible, bool bFarthestPoint )
|
||
|
{
|
||
|
// Find the node nearest to the destination path target if a path is not specified
|
||
|
if ( pPath == NULL )
|
||
|
{
|
||
|
pPath = m_pDestPathTarget;
|
||
|
}
|
||
|
|
||
|
// If the path node we're trying to use is not valid, then we're done.
|
||
|
if ( CPathTrack::ValidPath( pPath ) == NULL )
|
||
|
{
|
||
|
//FIXME: Implement
|
||
|
Assert(0);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Our target may be in a vehicle
|
||
|
CBaseEntity *pVehicle = NULL;
|
||
|
CBaseEntity *pTargetEnt = GetTrackPatherTargetEnt();
|
||
|
if ( pTargetEnt != NULL )
|
||
|
{
|
||
|
CBaseCombatCharacter *pCCTarget = pTargetEnt->MyCombatCharacterPointer();
|
||
|
if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() )
|
||
|
{
|
||
|
pVehicle = pCCTarget->GetVehicleEntity();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Faster math...
|
||
|
flAvoidRadius *= flAvoidRadius;
|
||
|
|
||
|
// Find the nearest node to the target (going forward)
|
||
|
CPathTrack *pNearestPath = NULL;
|
||
|
float flNearestDist = bFarthestPoint ? 0 : 999999999;
|
||
|
float flPathDist;
|
||
|
|
||
|
float flFarthestDistSqr = ( m_flFarthestPathDist - 2.0f * m_flTargetDistanceThreshold );
|
||
|
flFarthestDistSqr *= flFarthestDistSqr;
|
||
|
|
||
|
// NOTE: Gotta do it this crazy way because paths can be one-way.
|
||
|
for ( int i = 0; i < 2; ++i )
|
||
|
{
|
||
|
int loopCheck = 0;
|
||
|
CPathTrack *pTravPath = pPath;
|
||
|
CPathTrack *pNextPath;
|
||
|
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath, loopCheck++ )
|
||
|
{
|
||
|
// Circular loop checking
|
||
|
if ( pTravPath->HasBeenVisited() )
|
||
|
break;
|
||
|
|
||
|
pTravPath->Visit();
|
||
|
|
||
|
pNextPath = (i == 0) ? pTravPath->GetPrevious() : pTravPath->GetNext();
|
||
|
|
||
|
// Find the distance between this test point and our goal point
|
||
|
flPathDist = ( pTravPath->GetAbsOrigin() - targetPos ).LengthSqr();
|
||
|
|
||
|
// See if it's closer and it's also not within our avoidance radius
|
||
|
if ( bFarthestPoint )
|
||
|
{
|
||
|
if ( ( flPathDist <= flNearestDist ) && ( flNearestDist <= flFarthestDistSqr ) )
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( flPathDist >= flNearestDist )
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Don't choose points that are within the avoid radius
|
||
|
if ( flAvoidRadius && ( pTravPath->GetAbsOrigin() - targetPos ).Length2DSqr() <= flAvoidRadius )
|
||
|
continue;
|
||
|
|
||
|
if ( visible )
|
||
|
{
|
||
|
// If it has to be visible, run those checks
|
||
|
CBaseEntity *pBlocker = FindTrackBlocker( pTravPath->GetAbsOrigin(), targetPos );
|
||
|
|
||
|
// Check to see if we've hit the target, or the player's vehicle if it's a player in a vehicle
|
||
|
bool bHitTarget = ( pTargetEnt && ( pTargetEnt == pBlocker ) ) ||
|
||
|
( pVehicle && ( pVehicle == pBlocker ) );
|
||
|
|
||
|
// If we hit something, and it wasn't the target or his vehicle, then no dice
|
||
|
// If we hit the target and forced move was set, *still* no dice
|
||
|
if ( (pBlocker != NULL) && ( !bHitTarget || m_bForcedMove ) )
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
pNearestPath = pTravPath;
|
||
|
flNearestDist = flPathDist;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pNearestPath;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Compute a point n units along a path
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CPathTrack *CAI_TrackPather::ComputeLeadingPointAlongPath( const Vector &vecStartPoint,
|
||
|
CPathTrack *pFirstTrack, float flDistance, Vector *pTarget )
|
||
|
{
|
||
|
bool bMovingForward = (flDistance > 0.0f);
|
||
|
flDistance = fabs(flDistance);
|
||
|
|
||
|
CPathTrack *pTravPath = pFirstTrack;
|
||
|
if ( (!bMovingForward) && pFirstTrack->GetPrevious() )
|
||
|
{
|
||
|
pTravPath = pFirstTrack->GetPrevious();
|
||
|
}
|
||
|
|
||
|
*pTarget = vecStartPoint;
|
||
|
CPathTrack *pNextPath;
|
||
|
|
||
|
// No circular loop checking needed; eventually, it'll run out of distance
|
||
|
for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath )
|
||
|
{
|
||
|
pNextPath = bMovingForward ? pTravPath->GetNext() : pTravPath->GetPrevious();
|
||
|
|
||
|
// Find the distance between this test point and our goal point
|
||
|
float flPathDist = pTarget->DistTo( pTravPath->GetAbsOrigin() );
|
||
|
|
||
|
// Find the distance between this test point and our goal point
|
||
|
if ( flPathDist <= flDistance )
|
||
|
{
|
||
|
flDistance -= flPathDist;
|
||
|
*pTarget = pTravPath->GetAbsOrigin();
|
||
|
if ( !CPathTrack::ValidPath(pNextPath) )
|
||
|
return bMovingForward ? pTravPath : pTravPath->GetNext();
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ComputeClosestPoint( *pTarget, flDistance, pTravPath->GetAbsOrigin(), pTarget );
|
||
|
return bMovingForward ? pTravPath : pTravPath->GetNext();
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Compute the distance to a particular point on the path
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::ComputeDistanceAlongPathToPoint( CPathTrack *pStartTrack,
|
||
|
CPathTrack *pDestTrack, const Vector &vecDestPosition, bool bMovingForward )
|
||
|
{
|
||
|
float flTotalDist = 0.0f;
|
||
|
|
||
|
Vector vecPoint;
|
||
|
ClosestPointToCurrentPath( &vecPoint );
|
||
|
|
||
|
CPathTrack *pTravPath = pStartTrack;
|
||
|
CPathTrack *pNextPath, *pTestPath;
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath )
|
||
|
{
|
||
|
// Circular loop checking
|
||
|
if ( pTravPath->HasBeenVisited() )
|
||
|
break;
|
||
|
|
||
|
// Mark it as being visited.
|
||
|
pTravPath->Visit();
|
||
|
|
||
|
pNextPath = bMovingForward ? pTravPath->GetNext() : pTravPath->GetPrevious();
|
||
|
pTestPath = pTravPath;
|
||
|
Assert( pTestPath );
|
||
|
|
||
|
if ( pTravPath == pDestTrack )
|
||
|
{
|
||
|
Vector vecDelta;
|
||
|
Vector vecPathDelta;
|
||
|
VectorSubtract( vecDestPosition, vecPoint, vecDelta );
|
||
|
ComputePathDirection( pTravPath, &vecPathDelta );
|
||
|
float flDot = DotProduct( vecDelta, vecPathDelta );
|
||
|
flTotalDist += (flDot > 0.0f ? 1.0f : -1.0f) * vecDelta.Length2D();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// NOTE: This would be made more accurate if we did the path direction check here too.
|
||
|
// The starting vecPoint is sometimes *not* within the bounds of the line segment.
|
||
|
|
||
|
// Find the distance between this test point and our goal point
|
||
|
flTotalDist += (bMovingForward ? 1.0f : -1.0f) * vecPoint.AsVector2D().DistTo( pTestPath->GetAbsOrigin().AsVector2D() );
|
||
|
vecPoint = pTestPath->GetAbsOrigin();
|
||
|
}
|
||
|
|
||
|
return flTotalDist;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Track debugging info
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::VisualizeDebugInfo( const Vector &vecNearestPoint, const Vector &vecTarget )
|
||
|
{
|
||
|
if ( g_debug_trackpather.GetInt() == TRACKPATHER_DEBUG_PATH )
|
||
|
{
|
||
|
NDebugOverlay::Line( m_vecSegmentStartPoint, vecTarget, 0, 0, 255, true, 0.1f );
|
||
|
NDebugOverlay::Cross3D( vecNearestPoint, -Vector(16,16,16), Vector(16,16,16), 255, 0, 0, true, 0.1f );
|
||
|
NDebugOverlay::Cross3D( m_pCurrentPathTarget->GetAbsOrigin(), -Vector(16,16,16), Vector(16,16,16), 0, 255, 0, true, 0.1f );
|
||
|
NDebugOverlay::Cross3D( m_vecDesiredPosition, -Vector(16,16,16), Vector(16,16,16), 0, 0, 255, true, 0.1f );
|
||
|
NDebugOverlay::Cross3D( m_pDestPathTarget->GetAbsOrigin(), -Vector(16,16,16), Vector(16,16,16), 255, 255, 255, true, 0.1f );
|
||
|
|
||
|
if ( m_pTargetNearestPath )
|
||
|
{
|
||
|
NDebugOverlay::Cross3D( m_pTargetNearestPath->GetAbsOrigin(), -Vector(24,24,24), Vector(24,24,24), 255, 0, 255, true, 0.1f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( g_debug_trackpather.GetInt() == TRACKPATHER_DEBUG_TRACKS )
|
||
|
{
|
||
|
if ( m_pCurrentPathTarget )
|
||
|
{
|
||
|
CPathTrack *pPathTrack = m_pCurrentPathTarget;
|
||
|
for ( ; CPathTrack::ValidPath( pPathTrack ); pPathTrack = pPathTrack->GetNext() )
|
||
|
{
|
||
|
NDebugOverlay::Box( pPathTrack->GetAbsOrigin(), -Vector(2,2,2), Vector(2,2,2), 0,255, 0, 8, 0.1 );
|
||
|
if ( CPathTrack::ValidPath( pPathTrack->GetNext() ) )
|
||
|
{
|
||
|
NDebugOverlay::Line( pPathTrack->GetAbsOrigin(), pPathTrack->GetNext()->GetAbsOrigin(), 0,255,0, true, 0.1 );
|
||
|
}
|
||
|
|
||
|
if ( pPathTrack->GetNext() == m_pCurrentPathTarget )
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Does this path track have LOS to the target?
|
||
|
//------------------------------------------------------------------------------
|
||
|
bool CAI_TrackPather::HasLOSToTarget( CPathTrack *pTrack )
|
||
|
{
|
||
|
CBaseEntity *pTargetEnt = GetTrackPatherTargetEnt();
|
||
|
if ( !pTargetEnt )
|
||
|
return true;
|
||
|
|
||
|
Vector targetPos;
|
||
|
if ( !GetTrackPatherTarget( &targetPos ) )
|
||
|
return true;
|
||
|
|
||
|
// Translate driver into vehicle for testing
|
||
|
CBaseEntity *pVehicle = NULL;
|
||
|
CBaseCombatCharacter *pCCTarget = pTargetEnt->MyCombatCharacterPointer();
|
||
|
if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() )
|
||
|
{
|
||
|
pVehicle = pCCTarget->GetVehicleEntity();
|
||
|
}
|
||
|
|
||
|
// If it has to be visible, run those checks
|
||
|
CBaseEntity *pBlocker = FindTrackBlocker( pTrack->GetAbsOrigin(), targetPos );
|
||
|
|
||
|
// Check to see if we've hit the target, or the player's vehicle if it's a player in a vehicle
|
||
|
bool bHitTarget = ( pTargetEnt && ( pTargetEnt == pBlocker ) ) ||
|
||
|
( pVehicle && ( pVehicle == pBlocker ) );
|
||
|
|
||
|
return (pBlocker == NULL) || bHitTarget;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Moves to the track
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::UpdateCurrentTarget()
|
||
|
{
|
||
|
// Find the point along the line that we're closest to.
|
||
|
const Vector &vecTarget = m_pCurrentPathTarget->GetAbsOrigin();
|
||
|
Vector vecPoint;
|
||
|
float t = ClosestPointToCurrentPath( &vecPoint );
|
||
|
if ( (t < 1.0f) && ( vecPoint.DistToSqr( vecTarget ) > m_flTargetTolerance * m_flTargetTolerance ) )
|
||
|
goto visualizeDebugInfo;
|
||
|
|
||
|
// Forced move is gone as soon as we've reached the first point on our path
|
||
|
if ( m_bLeading )
|
||
|
{
|
||
|
m_bForcedMove = false;
|
||
|
}
|
||
|
|
||
|
// Trip our "path_track reached" output
|
||
|
if ( m_pCurrentPathTarget != m_pLastPathTarget )
|
||
|
{
|
||
|
// Get the path's specified max speed
|
||
|
m_flPathMaxSpeed = m_pCurrentPathTarget->m_flSpeed;
|
||
|
|
||
|
variant_t emptyVariant;
|
||
|
m_pCurrentPathTarget->AcceptInput( "InPass", this, this, emptyVariant, 0 );
|
||
|
m_pLastPathTarget = m_pCurrentPathTarget;
|
||
|
}
|
||
|
|
||
|
if ( m_nPauseState == PAUSED_AT_POSITION )
|
||
|
return;
|
||
|
|
||
|
if ( m_nPauseState == PAUSE_AT_NEXT_LOS_POSITION )
|
||
|
{
|
||
|
if ( HasLOSToTarget(m_pCurrentPathTarget) )
|
||
|
{
|
||
|
m_nPauseState = PAUSED_AT_POSITION;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update our dest path target, if appropriate...
|
||
|
if ( m_pCurrentPathTarget == m_pDestPathTarget )
|
||
|
{
|
||
|
m_bForcedMove = false;
|
||
|
SelectNewDestTarget();
|
||
|
}
|
||
|
|
||
|
// Did SelectNewDestTarget give us a new point to move to?
|
||
|
if ( m_pCurrentPathTarget != m_pDestPathTarget )
|
||
|
{
|
||
|
// Update to the next path, if there is one...
|
||
|
m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget );
|
||
|
if ( !m_pCurrentPathTarget )
|
||
|
{
|
||
|
m_pCurrentPathTarget = m_pLastPathTarget;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We're at rest (no patrolling behavior), which means we're moving forward now.
|
||
|
m_bMovingForward = true;
|
||
|
}
|
||
|
|
||
|
SetDesiredPosition( m_pCurrentPathTarget->GetAbsOrigin() );
|
||
|
m_vecSegmentStartSplinePoint = m_vecSegmentStartPoint;
|
||
|
m_vecSegmentStartPoint = m_pLastPathTarget->GetAbsOrigin();
|
||
|
|
||
|
visualizeDebugInfo:
|
||
|
VisualizeDebugInfo( vecPoint, vecTarget );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// NOTE: All code below is used exclusively for leading/trailing behavior
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Compute the distance to the leading position
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::ComputeDistanceToLeadingPosition()
|
||
|
{
|
||
|
return ComputeDistanceAlongPathToPoint( m_pCurrentPathTarget, m_pDestPathTarget, GetDesiredPosition(), m_bMovingForward );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Compute the distance to the *target* position
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::ComputeDistanceToTargetPosition()
|
||
|
{
|
||
|
Assert( m_pTargetNearestPath );
|
||
|
|
||
|
CPathTrack *pDest = m_bMovingForward ? m_pTargetNearestPath.Get() : m_pTargetNearestPath->GetPrevious();
|
||
|
if ( !pDest )
|
||
|
{
|
||
|
pDest = m_pTargetNearestPath;
|
||
|
}
|
||
|
bool bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pDest );
|
||
|
|
||
|
CPathTrack *pStart = m_pCurrentPathTarget;
|
||
|
if ( bMovingForward != m_bMovingForward )
|
||
|
{
|
||
|
if (bMovingForward)
|
||
|
{
|
||
|
if ( pStart->GetNext() )
|
||
|
{
|
||
|
pStart = pStart->GetNext();
|
||
|
}
|
||
|
if ( pDest->GetNext() )
|
||
|
{
|
||
|
pDest = pDest->GetNext();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( pStart->GetPrevious() )
|
||
|
{
|
||
|
pStart = pStart->GetPrevious();
|
||
|
}
|
||
|
if ( pDest->GetPrevious() )
|
||
|
{
|
||
|
pDest = pDest->GetPrevious();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ComputeDistanceAlongPathToPoint( pStart, pDest, m_vecTargetPathPoint, bMovingForward );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Compute a path direction
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::ComputePathDirection( CPathTrack *pPath, Vector *pVecPathDir )
|
||
|
{
|
||
|
if ( pPath->GetPrevious() )
|
||
|
{
|
||
|
VectorSubtract( pPath->GetAbsOrigin(), pPath->GetPrevious()->GetAbsOrigin(), *pVecPathDir );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( pPath->GetNext() )
|
||
|
{
|
||
|
VectorSubtract( pPath->GetNext()->GetAbsOrigin(), pPath->GetAbsOrigin(), *pVecPathDir );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pVecPathDir->Init( 1, 0, 0 );
|
||
|
}
|
||
|
}
|
||
|
VectorNormalize( *pVecPathDir );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// What's the current path direction?
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::CurrentPathDirection( Vector *pVecPathDir )
|
||
|
{
|
||
|
if ( m_pCurrentPathTarget )
|
||
|
{
|
||
|
ComputePathDirection( m_pCurrentPathTarget, pVecPathDir );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pVecPathDir->Init( 0, 0, 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Compute a point n units along the current path from our current position
|
||
|
// (but don't pass the desired target point)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::ComputePointAlongCurrentPath( float flDistance, float flPerpDist, Vector *pTarget )
|
||
|
{
|
||
|
Vector vecPathDir;
|
||
|
Vector vecStartPoint;
|
||
|
ClosestPointToCurrentPath( &vecStartPoint );
|
||
|
*pTarget = vecStartPoint;
|
||
|
|
||
|
if ( flDistance != 0.0f )
|
||
|
{
|
||
|
Vector vecPrevPoint = vecStartPoint;
|
||
|
CPathTrack *pTravPath = m_pCurrentPathTarget;
|
||
|
CPathTrack *pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget );
|
||
|
for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = NextAlongCurrentPath( pTravPath ) )
|
||
|
{
|
||
|
if ( pTravPath == pAdjustedDest )
|
||
|
{
|
||
|
ComputePathDirection( pTravPath, &vecPathDir );
|
||
|
|
||
|
float flPathDist = pTarget->DistTo( GetDesiredPosition() );
|
||
|
if ( flDistance > flPathDist )
|
||
|
{
|
||
|
*pTarget = GetDesiredPosition();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ComputeClosestPoint( *pTarget, flDistance, GetDesiredPosition(), pTarget );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Find the distance between this test point and our goal point
|
||
|
float flPathDist = pTarget->DistTo( pTravPath->GetAbsOrigin() );
|
||
|
|
||
|
// Find the distance between this test point and our goal point
|
||
|
if ( flPathDist <= flDistance )
|
||
|
{
|
||
|
flDistance -= flPathDist;
|
||
|
*pTarget = pTravPath->GetAbsOrigin();
|
||
|
|
||
|
// FIXME: Reduce the distance further based on the angle between this segment + the next
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ComputePathDirection( pTravPath, &vecPathDir );
|
||
|
ComputeClosestPoint( *pTarget, flDistance, pTravPath->GetAbsOrigin(), pTarget );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VectorSubtract( m_pCurrentPathTarget->GetAbsOrigin(), m_vecSegmentStartPoint, vecPathDir );
|
||
|
VectorNormalize( vecPathDir );
|
||
|
}
|
||
|
|
||
|
// Add in the horizontal component
|
||
|
ComputePointFromPerpDistance( *pTarget, vecPathDir, flPerpDist, pTarget );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Methods to find a signed perp distance from the track
|
||
|
// and to compute a point off the path based on the signed perp distance
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::ComputePerpDistanceFromPath( const Vector &vecPointOnPath, const Vector &vecPathDir, const Vector &vecPointOffPath )
|
||
|
{
|
||
|
// Make it be a signed distance of the target from the path
|
||
|
// Positive means on the right side, negative means on the left side
|
||
|
Vector vecAcross, vecDelta;
|
||
|
CrossProduct( vecPathDir, Vector( 0, 0, 1 ), vecAcross );
|
||
|
VectorSubtract( vecPointOffPath, vecPointOnPath, vecDelta );
|
||
|
VectorMA( vecDelta, -DotProduct( vecPathDir, vecDelta ), vecPathDir, vecDelta );
|
||
|
|
||
|
float flDistanceFromPath = vecDelta.Length2D();
|
||
|
if ( DotProduct2D( vecAcross.AsVector2D(), vecDelta.AsVector2D() ) < 0.0f )
|
||
|
{
|
||
|
flDistanceFromPath *= -1.0f;
|
||
|
}
|
||
|
|
||
|
return flDistanceFromPath;
|
||
|
}
|
||
|
|
||
|
void CAI_TrackPather::ComputePointFromPerpDistance( const Vector &vecPointOnPath, const Vector &vecPathDir, float flPerpDist, Vector *pResult )
|
||
|
{
|
||
|
Vector vecAcross;
|
||
|
CrossProduct( vecPathDir, Vector( 0, 0, 1 ), vecAcross );
|
||
|
VectorMA( vecPointOnPath, flPerpDist, vecAcross, *pResult );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Finds the closest point on the path, returns a signed perpendicular distance
|
||
|
// where negative means on the left side of the path (when travelled from prev to next)
|
||
|
// and positive means on the right side
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CPathTrack *CAI_TrackPather::FindClosestPointOnPath( CPathTrack *pPath,
|
||
|
const Vector &targetPos, Vector *pVecClosestPoint, Vector *pVecPathDir, float *pDistanceFromPath )
|
||
|
{
|
||
|
// Find the node nearest to the destination path target if a path is not specified
|
||
|
if ( pPath == NULL )
|
||
|
{
|
||
|
pPath = m_pDestPathTarget;
|
||
|
}
|
||
|
|
||
|
// If the path node we're trying to use is not valid, then we're done.
|
||
|
if ( CPathTrack::ValidPath( pPath ) == NULL )
|
||
|
{
|
||
|
//FIXME: Implement
|
||
|
Assert(0);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Find the nearest node to the target (going forward)
|
||
|
CPathTrack *pNearestPath = NULL;
|
||
|
float flNearestDist2D = 999999999;
|
||
|
float flNearestDist = 999999999;
|
||
|
float flPathDist, flPathDist2D;
|
||
|
|
||
|
// NOTE: Gotta do it this crazy way because paths can be one-way.
|
||
|
Vector vecNearestPoint;
|
||
|
Vector vecNearestPathSegment;
|
||
|
for ( int i = 0; i < 2; ++i )
|
||
|
{
|
||
|
int loopCheck = 0;
|
||
|
CPathTrack *pTravPath = pPath;
|
||
|
CPathTrack *pNextPath;
|
||
|
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath, loopCheck++ )
|
||
|
{
|
||
|
// Circular loop checking
|
||
|
if ( pTravPath->HasBeenVisited() )
|
||
|
break;
|
||
|
|
||
|
// Mark it as being visited.
|
||
|
pTravPath->Visit();
|
||
|
|
||
|
pNextPath = (i == 0) ? pTravPath->GetPrevious() : pTravPath->GetNext();
|
||
|
|
||
|
// No alt paths allowed in leading mode.
|
||
|
if ( pTravPath->m_paltpath )
|
||
|
{
|
||
|
Warning( "%s: Alternative paths in path_track not allowed when using the leading behavior!\n", GetEntityName().ToCStr() );
|
||
|
}
|
||
|
|
||
|
// Need line segments
|
||
|
if ( !CPathTrack::ValidPath(pNextPath) )
|
||
|
break;
|
||
|
|
||
|
// Find the closest point on the line segment on the path
|
||
|
Vector vecClosest;
|
||
|
CalcClosestPointOnLineSegment( targetPos, pTravPath->GetAbsOrigin(), pNextPath->GetAbsOrigin(), vecClosest );
|
||
|
|
||
|
// Find the distance between this test point and our goal point
|
||
|
flPathDist2D = vecClosest.AsVector2D().DistToSqr( targetPos.AsVector2D() );
|
||
|
if ( flPathDist2D > flNearestDist2D )
|
||
|
continue;
|
||
|
|
||
|
flPathDist = vecClosest.z - targetPos.z;
|
||
|
flPathDist *= flPathDist;
|
||
|
flPathDist += flPathDist2D;
|
||
|
if (( flPathDist2D == flNearestDist2D ) && ( flPathDist >= flNearestDist ))
|
||
|
continue;
|
||
|
|
||
|
pNearestPath = (i == 0) ? pTravPath : pNextPath;
|
||
|
flNearestDist2D = flPathDist2D;
|
||
|
flNearestDist = flPathDist;
|
||
|
vecNearestPoint = vecClosest;
|
||
|
VectorSubtract( pNextPath->GetAbsOrigin(), pTravPath->GetAbsOrigin(), vecNearestPathSegment );
|
||
|
if ( i == 0 )
|
||
|
{
|
||
|
vecNearestPathSegment *= -1.0f;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VectorNormalize( vecNearestPathSegment );
|
||
|
*pDistanceFromPath = ComputePerpDistanceFromPath( vecNearestPoint, vecNearestPathSegment, targetPos );
|
||
|
*pVecClosestPoint = vecNearestPoint;
|
||
|
*pVecPathDir = vecNearestPathSegment;
|
||
|
return pNearestPath;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Breakable paths?
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputStartBreakableMovement( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_bPatrolBreakable = true;
|
||
|
}
|
||
|
|
||
|
void CAI_TrackPather::InputStopBreakableMovement( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_bPatrolBreakable = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : &inputdata -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputStartPatrol( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_bPatrolling = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputStopPatrol( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_bPatrolling = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : &inputdata -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputStartPatrolBreakable( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_bPatrolBreakable = true;
|
||
|
m_bPatrolling = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Leading behaviors
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputStartLeading( inputdata_t &inputdata )
|
||
|
{
|
||
|
EnableLeading( true );
|
||
|
SetLeadingDistance( inputdata.value.Int() );
|
||
|
}
|
||
|
|
||
|
void CAI_TrackPather::InputStopLeading( inputdata_t &inputdata )
|
||
|
{
|
||
|
EnableLeading( false );
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Selects a new destination target
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::SelectNewDestTarget()
|
||
|
{
|
||
|
if ( !m_bPatrolling )
|
||
|
return;
|
||
|
|
||
|
// NOTE: This version is bugged, but I didn't want to make the fix
|
||
|
// here for fear of breaking a lot of maps late in the day.
|
||
|
// So, only the chopper does the "right" thing.
|
||
|
#ifdef HL2_EPISODIC
|
||
|
// Episodic uses the fixed logic for all trackpathers
|
||
|
if ( 1 )
|
||
|
#else
|
||
|
if ( ShouldUseFixedPatrolLogic() )
|
||
|
#endif
|
||
|
{
|
||
|
CPathTrack *pOldDest = m_pDestPathTarget;
|
||
|
|
||
|
// Only switch polarity of movement if we're at the *end* of the path
|
||
|
// This is really useful for initial conditions of patrolling
|
||
|
// NOTE: We've got to do some extra work for circular paths
|
||
|
bool bIsCircular = false;
|
||
|
{
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
CPathTrack *pTravPath = m_pDestPathTarget;
|
||
|
while( CPathTrack::ValidPath( pTravPath ) )
|
||
|
{
|
||
|
// Circular loop checking
|
||
|
if ( pTravPath->HasBeenVisited() )
|
||
|
{
|
||
|
bIsCircular = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pTravPath->Visit();
|
||
|
pTravPath = NextAlongCurrentPath( pTravPath );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( bIsCircular || (NextAlongCurrentPath( m_pDestPathTarget ) == NULL) )
|
||
|
{
|
||
|
m_bMovingForward = !m_bMovingForward;
|
||
|
}
|
||
|
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
|
||
|
while ( true )
|
||
|
{
|
||
|
CPathTrack *pNextTrack = NextAlongCurrentPath( m_pDestPathTarget );
|
||
|
if ( !pNextTrack || (pNextTrack == pOldDest) || pNextTrack->HasBeenVisited() )
|
||
|
break;
|
||
|
|
||
|
pNextTrack->Visit();
|
||
|
m_pDestPathTarget = pNextTrack;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CPathTrack *pOldDest = m_pDestPathTarget;
|
||
|
|
||
|
// For patrolling, switch the polarity of movement
|
||
|
m_bMovingForward = !m_bMovingForward;
|
||
|
|
||
|
int loopCount = 0;
|
||
|
while ( true )
|
||
|
{
|
||
|
CPathTrack *pNextTrack = NextAlongCurrentPath( m_pDestPathTarget );
|
||
|
if ( !pNextTrack )
|
||
|
break;
|
||
|
if ( ++loopCount > 1024 )
|
||
|
{
|
||
|
DevMsg(1,"WARNING: Looping path for %s\n", GetDebugName() );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
m_pDestPathTarget = pNextTrack;
|
||
|
}
|
||
|
|
||
|
if ( m_pDestPathTarget == pOldDest )
|
||
|
{
|
||
|
// This can occur if we move to the first point on the path
|
||
|
SelectNewDestTarget();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Moves to the track
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::UpdateCurrentTargetLeading()
|
||
|
{
|
||
|
bool bRestingAtDest = false;
|
||
|
CPathTrack *pAdjustedDest;
|
||
|
|
||
|
// Find the point along the line that we're closest to.
|
||
|
const Vector &vecTarget = m_pCurrentPathTarget->GetAbsOrigin();
|
||
|
Vector vecPoint;
|
||
|
float t = ClosestPointToCurrentPath( &vecPoint );
|
||
|
if ( (t < 1.0f) && ( vecPoint.DistToSqr( vecTarget ) > m_flTargetTolerance * m_flTargetTolerance ) )
|
||
|
goto visualizeDebugInfo;
|
||
|
|
||
|
// Trip our "path_track reached" output
|
||
|
if ( m_pCurrentPathTarget != m_pLastPathTarget )
|
||
|
{
|
||
|
// Get the path's specified max speed
|
||
|
m_flPathMaxSpeed = m_pCurrentPathTarget->m_flSpeed;
|
||
|
|
||
|
variant_t emptyVariant;
|
||
|
m_pCurrentPathTarget->AcceptInput( "InPass", this, this, emptyVariant, 0 );
|
||
|
m_pLastPathTarget = m_pCurrentPathTarget;
|
||
|
}
|
||
|
|
||
|
// NOTE: CurrentPathTarget doesn't mean the same thing as dest path target!
|
||
|
// It's the "next"most when moving forward + "prev"most when moving backward
|
||
|
// Must do the tests in the same space
|
||
|
pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget );
|
||
|
|
||
|
// Update our dest path target, if appropriate...
|
||
|
if ( m_pCurrentPathTarget == pAdjustedDest )
|
||
|
{
|
||
|
m_bForcedMove = false;
|
||
|
SelectNewDestTarget();
|
||
|
|
||
|
// NOTE: Must do this again since SelectNewDestTarget may change m_pDestPathTarget
|
||
|
pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget );
|
||
|
}
|
||
|
|
||
|
if ( m_pCurrentPathTarget != pAdjustedDest )
|
||
|
{
|
||
|
// Update to the next path, if there is one...
|
||
|
m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget );
|
||
|
if ( !m_pCurrentPathTarget )
|
||
|
{
|
||
|
m_pCurrentPathTarget = m_pLastPathTarget;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// NOTE: Have to do this here because the NextAlongCurrentPath call above
|
||
|
// could make m_pCurrentPathTarget == m_pDestPathTarget.
|
||
|
// In this case, we're at rest (no patrolling behavior)
|
||
|
bRestingAtDest = true;
|
||
|
}
|
||
|
|
||
|
if ( bRestingAtDest )
|
||
|
{
|
||
|
// NOTE: Must use current path target, instead of dest
|
||
|
// to get the PreviousAlongCurrentPath working correctly
|
||
|
CPathTrack *pSegmentStart = PreviousAlongCurrentPath( m_pCurrentPathTarget );
|
||
|
if ( !pSegmentStart )
|
||
|
{
|
||
|
pSegmentStart = m_pCurrentPathTarget;
|
||
|
}
|
||
|
m_vecSegmentStartPoint = pSegmentStart->GetAbsOrigin();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_vecSegmentStartPoint = m_pLastPathTarget->GetAbsOrigin();
|
||
|
}
|
||
|
|
||
|
visualizeDebugInfo:
|
||
|
VisualizeDebugInfo( vecPoint, vecTarget );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::UpdateTargetPositionLeading( void )
|
||
|
{
|
||
|
Vector targetPos;
|
||
|
if ( !GetTrackPatherTarget( &targetPos ) )
|
||
|
return;
|
||
|
|
||
|
// NOTE: FindClosestPointOnPath *always* returns the point on the "far",
|
||
|
// end of the line segment containing the closest point (namely the 'next'
|
||
|
// track, as opposed to the 'prev' track)
|
||
|
Vector vecClosestPoint, vecPathDir;
|
||
|
float flTargetDistanceFromPath;
|
||
|
CPathTrack *pNextPath = FindClosestPointOnPath( m_pCurrentPathTarget,
|
||
|
targetPos, &vecClosestPoint, &vecPathDir, &flTargetDistanceFromPath );
|
||
|
|
||
|
// This means that a valid path could not be found to our target!
|
||
|
if ( CPathTrack::ValidPath( pNextPath ) == NULL )
|
||
|
return;
|
||
|
|
||
|
// NDebugOverlay::Cross3D( vecClosestPoint, -Vector(24,24,24), Vector(24,24,24), 0, 255, 255, true, 0.1f );
|
||
|
// NDebugOverlay::Cross3D( pNextPath->GetAbsOrigin(), -Vector(24,24,24), Vector(24,24,24), 255, 255, 0, true, 0.1f );
|
||
|
|
||
|
// Here's how far we are from the path
|
||
|
m_flTargetDistFromPath = flTargetDistanceFromPath;
|
||
|
m_vecTargetPathDir = vecPathDir;
|
||
|
|
||
|
// Here's info about where the target is along the path
|
||
|
m_vecTargetPathPoint = vecClosestPoint;
|
||
|
m_pTargetNearestPath = pNextPath;
|
||
|
|
||
|
// Find the best position to be on our path
|
||
|
// NOTE: This will *also* return a path track on the "far" end of the line segment
|
||
|
// containing the leading position, namely the "next" end of the segment as opposed
|
||
|
// to the "prev" end of the segment.
|
||
|
CPathTrack *pDest = ComputeLeadingPointAlongPath( vecClosestPoint, pNextPath, m_flLeadDistance, &targetPos );
|
||
|
SetDesiredPosition( targetPos );
|
||
|
|
||
|
// We only want to switch movement directions when absolutely necessary
|
||
|
// so convert dest into a more appropriate value based on the current movement direction
|
||
|
if ( pDest != m_pDestPathTarget )
|
||
|
{
|
||
|
// NOTE: This is really tricky + subtle
|
||
|
// For leading, we don't want to ever change direction when the current target == the
|
||
|
// adjusted destination target. Namely, if we're going forward, both dest + curr
|
||
|
// mean the "next"most node so we can compare them directly against eath other.
|
||
|
// If we're moving backward, dest means "next"most, but curr means "prev"most.
|
||
|
// We first have to adjust the dest to mean "prev"most, and then do the comparison.
|
||
|
// If the adjusted dest == curr, then maintain direction. Otherwise, use the forward along path test.
|
||
|
bool bMovingForward = m_bMovingForward;
|
||
|
CPathTrack *pAdjustedDest = AdjustForMovementDirection( pDest );
|
||
|
if ( m_pCurrentPathTarget != pAdjustedDest )
|
||
|
{
|
||
|
bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pAdjustedDest );
|
||
|
}
|
||
|
|
||
|
if ( bMovingForward != m_bMovingForward )
|
||
|
{
|
||
|
// As a result of the tricky note above, this should never occur
|
||
|
Assert( pAdjustedDest != m_pCurrentPathTarget );
|
||
|
|
||
|
// Oops! Need to reverse direction
|
||
|
m_bMovingForward = bMovingForward;
|
||
|
m_vecSegmentStartPoint = m_pCurrentPathTarget->GetAbsOrigin();
|
||
|
m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget );
|
||
|
}
|
||
|
m_pDestPathTarget = pDest;
|
||
|
}
|
||
|
|
||
|
// NDebugOverlay::Cross3D( m_pCurrentPathTarget->GetAbsOrigin(), -Vector(36,36,36), Vector(36,36,36), 255, 0, 0, true, 0.1f );
|
||
|
// NDebugOverlay::Cross3D( m_pDestPathTarget->GetAbsOrigin(), -Vector(48,48,48), Vector(48,48,48), 0, 255, 0, true, 0.1f );
|
||
|
// NDebugOverlay::Cross3D( targetPos, -Vector(36,36,36), Vector(36,36,36), 0, 0, 255, true, 0.1f );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::UpdateTargetPosition( void )
|
||
|
{
|
||
|
// Don't update our target if we're being told to go somewhere
|
||
|
if ( m_bForcedMove && !m_bPatrolBreakable )
|
||
|
return;
|
||
|
|
||
|
// Don't update our target if we're patrolling
|
||
|
if ( m_bPatrolling )
|
||
|
{
|
||
|
// If we have an enemy, and our patrol is breakable, stop patrolling
|
||
|
if ( !m_bPatrolBreakable || !GetEnemy() )
|
||
|
return;
|
||
|
|
||
|
m_bPatrolling = false;
|
||
|
}
|
||
|
|
||
|
Vector targetPos;
|
||
|
if ( !GetTrackPatherTarget( &targetPos ) )
|
||
|
return;
|
||
|
|
||
|
// Not time to update again
|
||
|
if ( m_flEnemyPathUpdateTime > gpGlobals->curtime )
|
||
|
return;
|
||
|
|
||
|
// See if the target has moved enough to make us recheck
|
||
|
float flDistSqr = ( targetPos - m_vecLastGoalCheckPosition ).LengthSqr();
|
||
|
if ( flDistSqr < m_flTargetDistanceThreshold * m_flTargetDistanceThreshold )
|
||
|
return;
|
||
|
|
||
|
// Find the best position to be on our path
|
||
|
CPathTrack *pDest = BestPointOnPath( m_pCurrentPathTarget, targetPos, m_flAvoidDistance, true, m_bChooseFarthestPoint );
|
||
|
|
||
|
if ( CPathTrack::ValidPath( pDest ) == NULL )
|
||
|
{
|
||
|
// This means that a valid path could not be found to our target!
|
||
|
// Assert(0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( pDest != m_pDestPathTarget )
|
||
|
{
|
||
|
// This is our new destination
|
||
|
bool bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pDest );
|
||
|
if ( bMovingForward != m_bMovingForward )
|
||
|
{
|
||
|
// Oops! Need to reverse direction
|
||
|
m_bMovingForward = bMovingForward;
|
||
|
if ( pDest != m_pCurrentPathTarget )
|
||
|
{
|
||
|
SetupNewCurrentTarget( NextAlongCurrentPath( m_pCurrentPathTarget ) );
|
||
|
}
|
||
|
}
|
||
|
m_pDestPathTarget = pDest;
|
||
|
}
|
||
|
|
||
|
// Keep this goal point for comparisons later
|
||
|
m_vecLastGoalCheckPosition = targetPos;
|
||
|
|
||
|
// Only do this on set intervals
|
||
|
m_flEnemyPathUpdateTime = gpGlobals->curtime + 1.0f;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Returns the direction of the path at the closest point to the target
|
||
|
//------------------------------------------------------------------------------
|
||
|
const Vector &CAI_TrackPather::TargetPathDirection() const
|
||
|
{
|
||
|
return m_vecTargetPathDir;
|
||
|
}
|
||
|
|
||
|
const Vector &CAI_TrackPather::TargetPathAcrossDirection() const
|
||
|
{
|
||
|
static Vector s_Result;
|
||
|
CrossProduct( m_vecTargetPathDir, Vector( 0, 0, 1 ), s_Result );
|
||
|
return s_Result;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Returns the speed of the target relative to the path
|
||
|
//------------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::TargetSpeedAlongPath() const
|
||
|
{
|
||
|
if ( !GetEnemy() || !IsLeading() )
|
||
|
return 0.0f;
|
||
|
|
||
|
Vector vecSmoothedVelocity = GetEnemy()->GetSmoothedVelocity();
|
||
|
return DotProduct( vecSmoothedVelocity, TargetPathDirection() );
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Returns the speed of the target *across* the path
|
||
|
//------------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::TargetSpeedAcrossPath() const
|
||
|
{
|
||
|
if ( !GetEnemy() || !IsLeading() )
|
||
|
return 0.0f;
|
||
|
|
||
|
Vector vecSmoothedVelocity = GetEnemy()->GetSmoothedVelocity();
|
||
|
return DotProduct( vecSmoothedVelocity, TargetPathAcrossDirection() );
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Returns the max distance we can be from the path
|
||
|
//------------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::MaxDistanceFromCurrentPath() const
|
||
|
{
|
||
|
if ( !IsLeading() || !m_pCurrentPathTarget )
|
||
|
return 0.0f;
|
||
|
|
||
|
CPathTrack *pPrevPath = PreviousAlongCurrentPath( m_pCurrentPathTarget );
|
||
|
if ( !pPrevPath )
|
||
|
{
|
||
|
pPrevPath = m_pCurrentPathTarget;
|
||
|
}
|
||
|
|
||
|
// NOTE: Can't use m_vecSegmentStartPoint because we don't have a radius defined for it
|
||
|
float t;
|
||
|
Vector vecTemp;
|
||
|
CalcClosestPointOnLine( GetAbsOrigin(), pPrevPath->GetAbsOrigin(),
|
||
|
m_pCurrentPathTarget->GetAbsOrigin(), vecTemp, &t );
|
||
|
t = clamp( t, 0.0f, 1.0f );
|
||
|
float flRadius = (1.0f - t) * pPrevPath->GetRadius() + t * m_pCurrentPathTarget->GetRadius();
|
||
|
return flRadius;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Purpose : A different version of the track pather which is more explicit about
|
||
|
// the meaning of dest, current, and prev path points
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::UpdateTrackNavigation( void )
|
||
|
{
|
||
|
// No target? Use the string specified. We have no spawn method (sucky!!) so this is how that works
|
||
|
if ( ( CPathTrack::ValidPath( m_pDestPathTarget ) == NULL ) && ( m_target != NULL_STRING ) )
|
||
|
{
|
||
|
FlyToPathTrack( m_target );
|
||
|
m_target = NULL_STRING;
|
||
|
}
|
||
|
|
||
|
if ( !IsLeading() )
|
||
|
{
|
||
|
if ( !m_pCurrentPathTarget )
|
||
|
return;
|
||
|
|
||
|
// Updates our destination node if we're tracking something
|
||
|
UpdateTargetPosition();
|
||
|
|
||
|
// Move along our path towards our current destination
|
||
|
UpdateCurrentTarget();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Updates our destination position if we're leading something
|
||
|
UpdateTargetPositionLeading();
|
||
|
|
||
|
// Move along our path towards our current destination
|
||
|
UpdateCurrentTargetLeading();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Sets the farthest path distance
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::SetFarthestPathDist( float flMaxPathDist )
|
||
|
{
|
||
|
m_flFarthestPathDist = flMaxPathDist;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Sets up a new current path target
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::SetupNewCurrentTarget( CPathTrack *pTrack )
|
||
|
{
|
||
|
Assert( pTrack );
|
||
|
m_vecSegmentStartPoint = GetAbsOrigin();
|
||
|
VectorMA( m_vecSegmentStartPoint, -2.0f, GetAbsVelocity(), m_vecSegmentStartSplinePoint );
|
||
|
m_pCurrentPathTarget = pTrack;
|
||
|
SetDesiredPosition( m_pCurrentPathTarget->GetAbsOrigin() );
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Moves to an explicit track point
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::MoveToTrackPoint( CPathTrack *pTrack )
|
||
|
{
|
||
|
if ( IsOnSameTrack( pTrack, m_pDestPathTarget ) )
|
||
|
{
|
||
|
// The track must be valid
|
||
|
if ( CPathTrack::ValidPath( pTrack ) == NULL )
|
||
|
return;
|
||
|
|
||
|
m_pDestPathTarget = pTrack;
|
||
|
m_bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pTrack );
|
||
|
m_bForcedMove = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CPathTrack *pClosestTrack = BestPointOnPath( pTrack, WorldSpaceCenter(), 0.0f, false, false );
|
||
|
|
||
|
// The track must be valid
|
||
|
if ( CPathTrack::ValidPath( pClosestTrack ) == NULL )
|
||
|
return;
|
||
|
|
||
|
SetupNewCurrentTarget( pClosestTrack );
|
||
|
m_pDestPathTarget = pTrack;
|
||
|
m_bMovingForward = IsForwardAlongPath( pClosestTrack, pTrack );
|
||
|
m_bForcedMove = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Moves to the closest track point
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::MoveToClosestTrackPoint( CPathTrack *pTrack )
|
||
|
{
|
||
|
if ( IsOnSameTrack( pTrack, m_pDestPathTarget ) )
|
||
|
return;
|
||
|
|
||
|
CPathTrack *pClosestTrack = BestPointOnPath( pTrack, WorldSpaceCenter(), 0.0f, false, false );
|
||
|
|
||
|
// The track must be valid
|
||
|
if ( CPathTrack::ValidPath( pClosestTrack ) == NULL )
|
||
|
return;
|
||
|
|
||
|
SetupNewCurrentTarget( pClosestTrack );
|
||
|
m_pDestPathTarget = pClosestTrack;
|
||
|
m_bMovingForward = true;
|
||
|
|
||
|
// Force us to switch tracks if we're leading
|
||
|
if ( IsLeading() )
|
||
|
{
|
||
|
m_bForcedMove = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Are the two path tracks connected?
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CAI_TrackPather::IsOnSameTrack( CPathTrack *pPath1, CPathTrack *pPath2 ) const
|
||
|
{
|
||
|
if ( pPath1 == pPath2 )
|
||
|
return true;
|
||
|
|
||
|
{
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
CPathTrack *pTravPath = pPath1->GetPrevious();
|
||
|
while( CPathTrack::ValidPath( pTravPath ) && (pTravPath != pPath1) )
|
||
|
{
|
||
|
// Circular loop checking
|
||
|
if ( pTravPath->HasBeenVisited() )
|
||
|
break;
|
||
|
|
||
|
pTravPath->Visit();
|
||
|
|
||
|
if ( pTravPath == pPath2 )
|
||
|
return true;
|
||
|
|
||
|
pTravPath = pTravPath->GetPrevious();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
CPathTrack *pTravPath = pPath1->GetNext();
|
||
|
while( CPathTrack::ValidPath( pTravPath ) && (pTravPath != pPath1) )
|
||
|
{
|
||
|
// Circular loop checking
|
||
|
if ( pTravPath->HasBeenVisited() )
|
||
|
break;
|
||
|
|
||
|
pTravPath->Visit();
|
||
|
|
||
|
if ( pTravPath == pPath2 )
|
||
|
return true;
|
||
|
|
||
|
pTravPath = pTravPath->GetNext();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Deal with teleportation
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::Teleported()
|
||
|
{
|
||
|
// This updates the paths so they are reasonable
|
||
|
CPathTrack *pClosestTrack = BestPointOnPath( GetDestPathTarget(), WorldSpaceCenter(), 0.0f, false, false );
|
||
|
m_pDestPathTarget = NULL;
|
||
|
MoveToClosestTrackPoint( pClosestTrack );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Returns distance along path to target, returns FLT_MAX if there's no path
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::ComputePathDistance( CPathTrack *pPath, CPathTrack *pDest, bool bForward ) const
|
||
|
{
|
||
|
float flDist = 0.0f;
|
||
|
CPathTrack *pLast = pPath;
|
||
|
|
||
|
BEGIN_PATH_TRACK_ITERATION();
|
||
|
while ( CPathTrack::ValidPath( pPath ) )
|
||
|
{
|
||
|
// Ciruclar loop checking
|
||
|
if ( pPath->HasBeenVisited() )
|
||
|
return FLT_MAX;
|
||
|
|
||
|
pPath->Visit();
|
||
|
|
||
|
flDist += pLast->GetAbsOrigin().DistTo( pPath->GetAbsOrigin() );
|
||
|
|
||
|
if ( pDest == pPath )
|
||
|
return flDist;
|
||
|
|
||
|
pLast = pPath;
|
||
|
pPath = bForward ? pPath->GetNext() : pPath->GetPrevious();
|
||
|
}
|
||
|
|
||
|
return FLT_MAX;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Is pPathTest in "front" of pPath on the same path? (Namely, does GetNext() get us there?)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CAI_TrackPather::IsForwardAlongPath( CPathTrack *pPath, CPathTrack *pPathTest ) const
|
||
|
{
|
||
|
// Also, in the case of looping paths, we want to return the shortest path
|
||
|
float flForwardDist = ComputePathDistance( pPath, pPathTest, true );
|
||
|
float flReverseDist = ComputePathDistance( pPath, pPathTest, false );
|
||
|
|
||
|
Assert( ( flForwardDist != FLT_MAX ) || ( flReverseDist != FLT_MAX ) );
|
||
|
return ( flForwardDist <= flReverseDist );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Computes distance + nearest point from the current path..
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CAI_TrackPather::ClosestPointToCurrentPath( Vector *pVecPoint ) const
|
||
|
{
|
||
|
if (!m_pCurrentPathTarget)
|
||
|
{
|
||
|
*pVecPoint = GetAbsOrigin();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
float t;
|
||
|
CalcClosestPointOnLine( GetAbsOrigin(), m_vecSegmentStartPoint,
|
||
|
m_pCurrentPathTarget->GetAbsOrigin(), *pVecPoint, &t );
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Computes a "path" velocity at a particular point along the current path
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::ComputePathTangent( float t, Vector *pVecTangent ) const
|
||
|
{
|
||
|
CPathTrack *pNextTrack = NextAlongCurrentPath(m_pCurrentPathTarget);
|
||
|
if ( !pNextTrack )
|
||
|
{
|
||
|
pNextTrack = m_pCurrentPathTarget;
|
||
|
}
|
||
|
|
||
|
t = clamp( t, 0.0f, 1.0f );
|
||
|
pVecTangent->Init(0,0,0);
|
||
|
Catmull_Rom_Spline_Tangent( m_vecSegmentStartSplinePoint, m_vecSegmentStartPoint,
|
||
|
m_pCurrentPathTarget->GetAbsOrigin(), pNextTrack->GetAbsOrigin(), t, *pVecTangent );
|
||
|
VectorNormalize( *pVecTangent );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Computes the *normalized* velocity at which the helicopter should approach the final point
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::ComputeNormalizedDestVelocity( Vector *pVecVelocity ) const
|
||
|
{
|
||
|
if ( m_nPauseState != PAUSE_NO_PAUSE )
|
||
|
{
|
||
|
pVecVelocity->Init(0,0,0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CPathTrack *pNextTrack = NextAlongCurrentPath(m_pCurrentPathTarget);
|
||
|
if ( !pNextTrack )
|
||
|
{
|
||
|
pNextTrack = m_pCurrentPathTarget;
|
||
|
}
|
||
|
|
||
|
if ( ( pNextTrack == m_pCurrentPathTarget ) || ( m_pCurrentPathTarget == m_pDestPathTarget ) )
|
||
|
{
|
||
|
pVecVelocity->Init(0,0,0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VectorSubtract( pNextTrack->GetAbsOrigin(), m_pCurrentPathTarget->GetAbsOrigin(), *pVecVelocity );
|
||
|
VectorNormalize( *pVecVelocity );
|
||
|
|
||
|
// Slow it down if we're approaching a sharp corner
|
||
|
Vector vecDelta;
|
||
|
VectorSubtract( m_pCurrentPathTarget->GetAbsOrigin(), m_vecSegmentStartPoint, vecDelta );
|
||
|
VectorNormalize( vecDelta );
|
||
|
float flDot = DotProduct( *pVecVelocity, vecDelta );
|
||
|
*pVecVelocity *= clamp( flDot, 0.0f, 1.0f );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : &inputdata -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::SetTrack( CBaseEntity *pGoalEnt )
|
||
|
{
|
||
|
// Ignore this input if we're *already* on that path.
|
||
|
CPathTrack *pTrack = dynamic_cast<CPathTrack *>(pGoalEnt);
|
||
|
if ( !pTrack )
|
||
|
{
|
||
|
DevWarning( "%s: Specified entity '%s' must be a path_track!\n", pGoalEnt->GetClassname(), pGoalEnt->GetEntityName().ToCStr() );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
MoveToClosestTrackPoint( pTrack );
|
||
|
}
|
||
|
|
||
|
void CAI_TrackPather::SetTrack( string_t strTrackName )
|
||
|
{
|
||
|
// Find our specified target
|
||
|
CBaseEntity *pGoalEnt = gEntList.FindEntityByName( NULL, strTrackName );
|
||
|
if ( pGoalEnt == NULL )
|
||
|
{
|
||
|
DevWarning( "%s: Could not find path_track '%s'!\n", GetClassname(), STRING( strTrackName ) );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetTrack( pGoalEnt );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : &inputdata -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputSetTrack( inputdata_t &inputdata )
|
||
|
{
|
||
|
string_t strTrackName = MAKE_STRING( inputdata.value.String() );
|
||
|
SetTrack( MAKE_STRING( inputdata.value.String() ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : strTrackName -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::FlyToPathTrack( string_t strTrackName )
|
||
|
{
|
||
|
CBaseEntity *pGoalEnt = gEntList.FindEntityByName( NULL, strTrackName );
|
||
|
if ( pGoalEnt == NULL )
|
||
|
{
|
||
|
DevWarning( "%s: Could not find path_track '%s'!\n", GetClassname(), STRING( strTrackName ) );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Ignore this input if we're *already* on that path.
|
||
|
CPathTrack *pTrack = dynamic_cast<CPathTrack *>(pGoalEnt);
|
||
|
if ( !pTrack )
|
||
|
{
|
||
|
DevWarning( "%s: Specified entity '%s' must be a path_track!\n", GetClassname(), STRING( strTrackName ) );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Find our specified target
|
||
|
MoveToTrackPoint( pTrack );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : &inputdata -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputFlyToPathTrack( inputdata_t &inputdata )
|
||
|
{
|
||
|
// Find our specified target
|
||
|
string_t strTrackName = MAKE_STRING( inputdata.value.String() );
|
||
|
m_nPauseState = PAUSE_NO_PAUSE;
|
||
|
FlyToPathTrack( strTrackName );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Changes the mode used to determine which path point to move to
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAI_TrackPather::InputChooseFarthestPathPoint( inputdata_t &inputdata )
|
||
|
{
|
||
|
UseFarthestPathPoint( true );
|
||
|
}
|
||
|
|
||
|
void CAI_TrackPather::InputChooseNearestPathPoint( inputdata_t &inputdata )
|
||
|
{
|
||
|
UseFarthestPathPoint( false );
|
||
|
}
|
||
|
|
||
|
void CAI_TrackPather::UseFarthestPathPoint( bool useFarthest )
|
||
|
{
|
||
|
m_bChooseFarthestPoint = useFarthest;
|
||
|
}
|