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