aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/ai_trackpather.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/ai_trackpather.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/ai_trackpather.cpp')
-rw-r--r--mp/src/game/server/ai_trackpather.cpp3392
1 files changed, 1696 insertions, 1696 deletions
diff --git a/mp/src/game/server/ai_trackpather.cpp b/mp/src/game/server/ai_trackpather.cpp
index bbffe510..18596c9a 100644
--- a/mp/src/game/server/ai_trackpather.cpp
+++ b/mp/src/game/server/ai_trackpather.cpp
@@ -1,1696 +1,1696 @@
-//========= 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;
-}
+//========= 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;
+}