2015-06-30 12:46:07 +03:00
/*
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* In addition , as a special exception , the author gives permission to
* link the code of this program with the Half - Life Game Engine ( " HL
* Engine " ) and Modified Game Libraries ( " MODs " ) developed by Valve,
* L . L . C ( " Valve " ) . You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve . If you modify this file , you may extend this exception
* to your version of the file , but you are not obligated to do so . If
* you do not wish to do so , delete this exception statement from your
* version .
*
2015-09-16 23:19:21 +03:00
*/
2015-06-30 12:46:07 +03:00
# pragma once
2015-09-16 23:19:21 +03:00
2015-06-30 12:46:07 +03:00
// STL uses exceptions, but we are not compiling with them - ignore warning
# pragma warning(disable : 4530)
class CNavPath
{
public :
2016-02-04 03:18:26 +03:00
CNavPath ( ) { m_segmentCount = 0 ; }
2015-06-30 12:46:07 +03:00
struct PathSegment
{
2017-07-01 23:40:10 +03:00
CNavArea * area ; // the area along the path
2015-06-30 12:46:07 +03:00
NavTraverseType how ; // how to enter this area from the previous one
2017-07-01 23:40:10 +03:00
Vector pos ; // our movement goal position at this point in the path
2015-06-30 12:46:07 +03:00
const CNavLadder * ladder ; // if "how" refers to a ladder, this is it
} ;
2017-10-12 17:50:56 +03:00
const PathSegment * operator [ ] ( int i ) { return ( i > = 0 & & i < m_segmentCount ) ? & m_path [ i ] : nullptr ; }
2016-02-04 03:18:26 +03:00
int GetSegmentCount ( ) const { return m_segmentCount ; }
2017-10-12 17:50:56 +03:00
const Vector & GetEndpoint ( ) const { return m_path [ m_segmentCount - 1 ] . pos ; }
2015-06-30 12:46:07 +03:00
2017-07-01 23:40:10 +03:00
bool IsAtEnd ( const Vector & pos ) const ; // return true if position is at the end of the path
float GetLength ( ) const ; // return length of path from start to finish
2016-02-23 02:13:52 +03:00
bool GetPointAlongPath ( float distAlong , Vector * pointOnPath ) const ; // return point a given distance along the path - if distance is out of path bounds, point is clamped to start/end
2015-06-30 12:46:07 +03:00
2016-02-04 03:18:26 +03:00
// return the node index closest to the given distance along the path without going over - returns (-1) if error
2015-06-30 12:46:07 +03:00
int GetSegmentIndexAlongPath ( float distAlong ) const ;
2016-02-04 03:18:26 +03:00
bool IsValid ( ) const { return ( m_segmentCount > 0 ) ; }
void Invalidate ( ) { m_segmentCount = 0 ; }
2015-06-30 12:46:07 +03:00
// draw the path for debugging
2016-02-04 03:18:26 +03:00
void Draw ( ) ;
2015-06-30 12:46:07 +03:00
2016-02-04 03:18:26 +03:00
// compute closest point on path to given point
2016-02-23 02:13:52 +03:00
bool FindClosestPointOnPath ( const Vector * worldPos , int startIndex , int endIndex , Vector * close ) const ;
2015-06-30 12:46:07 +03:00
2016-02-04 03:18:26 +03:00
void Optimize ( ) ;
2015-06-30 12:46:07 +03:00
// Compute shortest path from 'start' to 'goal' via A* algorithm
2015-12-09 01:39:54 +03:00
template < typename CostFunctor >
2015-06-30 12:46:07 +03:00
bool Compute ( const Vector * start , const Vector * goal , CostFunctor & costFunc )
{
Invalidate ( ) ;
2017-10-12 17:50:56 +03:00
if ( ! start | | ! goal )
2015-06-30 12:46:07 +03:00
return false ;
CNavArea * startArea = TheNavAreaGrid . GetNearestNavArea ( start ) ;
2017-10-12 17:50:56 +03:00
if ( ! startArea )
2015-06-30 12:46:07 +03:00
return false ;
CNavArea * goalArea = TheNavAreaGrid . GetNavArea ( goal ) ;
// if we are already in the goal area, build trivial path
if ( startArea = = goalArea )
{
BuildTrivialPath ( start , goal ) ;
return true ;
}
// make sure path end position is on the ground
Vector pathEndPosition = * goal ;
if ( goalArea )
pathEndPosition . z = goalArea - > GetZ ( & pathEndPosition ) ;
else
GetGroundHeight ( & pathEndPosition , & pathEndPosition . z ) ;
// Compute shortest path to goal
CNavArea * closestArea ;
bool pathToGoalExists = NavAreaBuildPath ( startArea , goalArea , goal , costFunc , & closestArea ) ;
CNavArea * effectiveGoalArea = ( pathToGoalExists ) ? goalArea : closestArea ;
// Build path by following parent links
int count = 0 ;
CNavArea * area ;
for ( area = effectiveGoalArea ; area ; area = area - > GetParent ( ) )
2017-10-12 17:50:56 +03:00
count + + ;
2015-06-30 12:46:07 +03:00
// save room for endpoint
if ( count > MAX_PATH_SEGMENTS - 1 )
count = MAX_PATH_SEGMENTS - 1 ;
if ( count = = 0 )
return false ;
if ( count = = 1 )
{
BuildTrivialPath ( start , goal ) ;
return true ;
}
m_segmentCount = count ;
for ( area = effectiveGoalArea ; count & & area ; area = area - > GetParent ( ) )
{
2017-10-12 17:50:56 +03:00
count - - ;
m_path [ count ] . area = area ;
m_path [ count ] . how = area - > GetParentHow ( ) ;
2015-06-30 12:46:07 +03:00
}
// compute path positions
if ( ComputePathPositions ( ) = = false )
{
Invalidate ( ) ;
return false ;
}
// append path end position
2017-10-12 17:50:56 +03:00
m_path [ m_segmentCount ] . area = effectiveGoalArea ;
m_path [ m_segmentCount ] . pos = pathEndPosition ;
m_path [ m_segmentCount ] . ladder = nullptr ;
m_path [ m_segmentCount ] . how = NUM_TRAVERSE_TYPES ;
m_segmentCount + + ;
2015-06-30 12:46:07 +03:00
return true ;
}
2017-07-01 23:40:10 +03:00
2015-06-30 12:46:07 +03:00
private :
enum { MAX_PATH_SEGMENTS = 256 } ;
2017-10-12 17:50:56 +03:00
PathSegment m_path [ MAX_PATH_SEGMENTS ] ;
2015-06-30 12:46:07 +03:00
int m_segmentCount ;
2017-07-01 23:40:10 +03:00
bool ComputePathPositions ( ) ; // determine actual path positions
2016-02-04 03:18:26 +03:00
bool BuildTrivialPath ( const Vector * start , const Vector * goal ) ; // utility function for when start and goal are in the same area
2017-11-22 20:27:55 +03:00
int FindNextOccludedNode ( int anchor ) ; // used by Optimize()
2016-02-04 03:18:26 +03:00
} ;
2015-06-30 12:46:07 +03:00
// Monitor improv movement and determine if it becomes stuck
class CStuckMonitor
{
public :
2016-02-04 03:18:26 +03:00
CStuckMonitor ( ) ;
2015-06-30 12:46:07 +03:00
2016-02-04 03:18:26 +03:00
void Reset ( ) ;
2015-06-30 12:46:07 +03:00
void Update ( CImprov * improv ) ;
2016-02-04 03:18:26 +03:00
2017-10-12 17:50:56 +03:00
bool IsStuck ( ) const { return m_isStuck ; }
2016-02-04 03:18:26 +03:00
float GetDuration ( ) const { return m_isStuck ? m_stuckTimer . GetElapsedTime ( ) : 0.0f ; }
2015-06-30 12:46:07 +03:00
2015-12-09 01:39:54 +03:00
private :
2017-07-01 23:40:10 +03:00
bool m_isStuck ; // if true, we are stuck
Vector m_stuckSpot ; // the location where we became stuck
2015-06-30 12:46:07 +03:00
IntervalTimer m_stuckTimer ; // how long we have been stuck
enum { MAX_VEL_SAMPLES = 5 } ;
2017-10-12 17:50:56 +03:00
float m_avgVel [ MAX_VEL_SAMPLES ] ;
2015-06-30 12:46:07 +03:00
int m_avgVelIndex ;
int m_avgVelCount ;
Vector m_lastCentroid ;
float m_lastTime ;
2016-02-04 03:18:26 +03:00
} ;
2015-06-30 12:46:07 +03:00
// The CNavPathFollower class implements path following behavior
class CNavPathFollower
{
public :
2016-02-04 03:18:26 +03:00
CNavPathFollower ( ) ;
2015-06-30 12:46:07 +03:00
2016-02-04 03:18:26 +03:00
void SetImprov ( CImprov * improv ) { m_improv = improv ; }
void SetPath ( CNavPath * path ) { m_path = path ; }
void Reset ( ) ;
2015-06-30 12:46:07 +03:00
2017-07-01 23:40:10 +03:00
void Update ( float deltaT , bool avoidObstacles = true ) ; // move improv along path
void Debug ( bool status ) { m_isDebug = status ; } // turn debugging on/off
2015-06-30 12:46:07 +03:00
2017-07-01 23:40:10 +03:00
bool IsStuck ( ) const { return m_stuckMonitor . IsStuck ( ) ; } // return true if improv is stuck
2016-02-04 03:18:26 +03:00
void ResetStuck ( ) { m_stuckMonitor . Reset ( ) ; }
float GetStuckDuration ( ) const { return m_stuckMonitor . GetDuration ( ) ; } // return how long we've been stuck
2015-06-30 12:46:07 +03:00
2016-02-04 03:18:26 +03:00
void FeelerReflexAdjustment ( Vector * goalPosition , float height = - 1.0f ) ; // adjust goal position if "feelers" are touched
2015-06-30 12:46:07 +03:00
private :
2017-10-12 17:50:56 +03:00
int FindOurPositionOnPath ( Vector * close , bool local ) const ; // return the closest point to our current position on current path
2015-06-30 12:46:07 +03:00
int FindPathPoint ( float aheadRange , Vector * point , int * prevIndex ) ; // compute a point a fixed distance ahead along our path.
2016-02-04 03:18:26 +03:00
CImprov * m_improv ; // who is doing the path following
CNavPath * m_path ; // the path being followed
int m_segmentIndex ; // the point on the path the improv is moving towards
int m_behindIndex ; // index of the node on the path just behind us
Vector m_goal ; // last computed follow goal
2015-06-30 12:46:07 +03:00
bool m_isLadderStarted ;
bool m_isDebug ;
CStuckMonitor m_stuckMonitor ;
2016-02-04 03:18:26 +03:00
} ;