diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/ai_trackpather.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/ai_trackpather.cpp')
| -rw-r--r-- | mp/src/game/server/ai_trackpather.cpp | 1696 |
1 files changed, 1696 insertions, 0 deletions
diff --git a/mp/src/game/server/ai_trackpather.cpp b/mp/src/game/server/ai_trackpather.cpp new file mode 100644 index 00000000..bbffe510 --- /dev/null +++ b/mp/src/game/server/ai_trackpather.cpp @@ -0,0 +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;
+}
|