//========= Copyright © 1996-2005, 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
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, NULL );
	}
	else
	{
		m_pCurrentPathTarget = NULL;
	}

	// Restore destination path
	if ( m_strDestPathName != NULL_STRING )
	{
		m_pDestPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strDestPathName, NULL );
	}
	else
	{
		m_pDestPathTarget = NULL;
	}

	// Restore last path
	if ( m_strLastPathName != NULL_STRING )
	{
		m_pLastPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strLastPathName, NULL );
	}
	else
	{
		m_pLastPathTarget = NULL;
	}

	// Restore target nearest path
	if ( m_strTargetNearestPathName != NULL_STRING )
	{
		m_pTargetNearestPath = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strTargetNearestPathName, NULL );
	}
	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 )
{
	m_bLeading = bEnable;
	if ( m_bLeading )
	{
		m_bPatrolling = false;
	}
}

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 a player in a vehicle
	CBaseEntity *pTargetEnt = GetTrackPatherTargetEnt();
	CBasePlayer *pPlayer = ToBasePlayer( pTargetEnt );
	CBaseEntity *pVehicle = NULL;
	if ( pPlayer && pPlayer->IsInAVehicle() )
	{
		pVehicle = pPlayer->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 );
		}
	}
}


//------------------------------------------------------------------------------
// 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;

	CBasePlayer *pPlayer = ToBasePlayer( pTargetEnt );
	CBaseEntity *pVehicle = NULL;
	if ( pPlayer && pPlayer->IsInAVehicle() )
	{
		pVehicle = pPlayer->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() );
			}

			// 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.
	if ( ShouldUseFixedPatrolLogic() )
	{
		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;
}


//-----------------------------------------------------------------------------
// 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() );
		return;
	}

	MoveToClosestTrackPoint( pTrack );
}

void CAI_TrackPather::SetTrack( string_t strTrackName )
{
	// Find our specified target
	CBaseEntity *pGoalEnt = gEntList.FindEntityByName( NULL, strTrackName, NULL );
	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, NULL );
	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() );
	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;
}
