Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

683 lines
18 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "ai_link.h"
#include "ai_navtype.h"
#include "ai_waypoint.h"
#include "ai_pathfinder.h"
#include "ai_navgoaltype.h"
#include "ai_routedist.h"
#include "ai_route.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_SIMPLE_DATADESC(CAI_Path)
// m_Waypoints (reconsititute on load)
DEFINE_FIELD( m_goalTolerance, FIELD_FLOAT ),
DEFINE_CUSTOM_FIELD( m_activity, ActivityDataOps() ),
DEFINE_FIELD( m_target, FIELD_EHANDLE ),
DEFINE_FIELD( m_sequence, FIELD_INTEGER ),
DEFINE_FIELD( m_vecTargetOffset, FIELD_VECTOR ),
DEFINE_FIELD( m_waypointTolerance, FIELD_FLOAT ),
DEFINE_CUSTOM_FIELD( m_arrivalActivity, ActivityDataOps() ),
DEFINE_FIELD( m_arrivalSequence, FIELD_INTEGER ),
// m_iLastNodeReached
DEFINE_FIELD( m_bGoalPosSet, FIELD_BOOLEAN ),
DEFINE_FIELD( m_goalPos, FIELD_POSITION_VECTOR),
DEFINE_FIELD( m_bGoalTypeSet, FIELD_BOOLEAN ),
DEFINE_FIELD( m_goalType, FIELD_INTEGER ),
DEFINE_FIELD( m_goalFlags, FIELD_INTEGER ),
DEFINE_FIELD( m_routeStartTime, FIELD_TIME ),
DEFINE_FIELD( m_goalDirection, FIELD_VECTOR ),
DEFINE_FIELD( m_goalDirectionTarget, FIELD_EHANDLE ),
DEFINE_FIELD( m_goalSpeed, FIELD_FLOAT ),
DEFINE_FIELD( m_goalSpeedTarget, FIELD_EHANDLE ),
DEFINE_FIELD( m_goalStoppingDistance, FIELD_FLOAT ),
END_DATADESC()
//-----------------------------------------------------------------------------
AI_Waypoint_t CAI_Path::gm_InvalidWaypoint( Vector(0,0,0), 0, NAV_NONE, 0, 0 );
//-----------------------------------------------------------------------------
void CAI_Path::SetWaypoints(AI_Waypoint_t* route, bool fSetGoalFromLast)
{
m_Waypoints.Set(route);
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
if ( pLast )
{
pLast->flPathDistGoal = -1;
if ( fSetGoalFromLast )
{
if ( pLast )
{
m_bGoalPosSet = false;
pLast->ModifyFlags( bits_WP_TO_GOAL, true );
SetGoalPosition(pLast->GetPos());
}
}
}
AssertRouteValid( m_Waypoints.GetFirst() );
}
//-----------------------------------------------------------------------------
void CAI_Path::PrependWaypoints( AI_Waypoint_t *pWaypoints )
{
m_Waypoints.PrependWaypoints( pWaypoints );
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
pLast->flPathDistGoal = -1;
AssertRouteValid( m_Waypoints.GetFirst() );
}
//-----------------------------------------------------------------------------
void CAI_Path::PrependWaypoint( const Vector &newPoint, Navigation_t navType, unsigned waypointFlags )
{
m_Waypoints.PrependWaypoint( newPoint, navType, waypointFlags );
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
pLast->flPathDistGoal = -1;
AssertRouteValid( m_Waypoints.GetFirst() );
}
//-----------------------------------------------------------------------------
float CAI_Path::GetPathLength()
{
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
if ( pLast && pLast->flPathDistGoal == -1 )
{
ComputeRouteGoalDistances( pLast );
}
AI_Waypoint_t *pCurrent = GetCurWaypoint();
return ( ( pCurrent ) ? pCurrent->flPathDistGoal : 0 );
}
//-----------------------------------------------------------------------------
float CAI_Path::GetPathDistanceToGoal( const Vector &startPos )
{
AI_Waypoint_t *pCurrent = GetCurWaypoint();
if ( pCurrent )
{
return ( GetPathLength() + ComputePathDistance(pCurrent->NavType(), startPos, pCurrent->GetPos()) );
}
return 0;
}
//-----------------------------------------------------------------------------
Activity CAI_Path::SetMovementActivity(Activity activity)
{
Assert( activity != ACT_RESET && activity != ACT_INVALID );
//Msg("Set movement to %s\n", ActivityList_NameForIndex(activity) );
m_sequence = ACT_INVALID;
return (m_activity = activity);
}
//-----------------------------------------------------------------------------
Activity CAI_Path::GetArrivalActivity( ) const
{
if ( !m_Waypoints.IsEmpty() )
{
return m_arrivalActivity;
}
return ACT_INVALID;
}
//-----------------------------------------------------------------------------
void CAI_Path::SetArrivalActivity(Activity activity)
{
m_arrivalActivity = activity;
m_arrivalSequence = ACT_INVALID;
}
//-----------------------------------------------------------------------------
int CAI_Path::GetArrivalSequence( ) const
{
if ( !m_Waypoints.IsEmpty() )
{
return m_arrivalSequence;
}
return ACT_INVALID;
}
//-----------------------------------------------------------------------------
void CAI_Path::SetArrivalSequence( int sequence )
{
m_arrivalSequence = sequence;
}
//-----------------------------------------------------------------------------
void CAI_Path::SetGoalDirection( const Vector &goalDirection )
{
m_goalDirectionTarget = NULL;
m_goalDirection = goalDirection;
VectorNormalize( m_goalDirection );
/*
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
if ( pLast )
{
NDebugOverlay::Box( pLast->vecLocation, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0,0,255, 0, 2.0 );
NDebugOverlay::Line( pLast->vecLocation, pLast->vecLocation + m_goalDirection * 32, 0,0,255, true, 2.0 );
}
*/
}
//-----------------------------------------------------------------------------
void CAI_Path::SetGoalDirection( CBaseEntity *pTarget )
{
m_goalDirectionTarget = pTarget;
if (pTarget)
{
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
if ( pLast )
{
m_goalDirection = pTarget->GetAbsOrigin() - pLast->vecLocation;
VectorNormalize( m_goalDirection );
/*
NDebugOverlay::Box( pLast->vecLocation, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0,0,255, 0, 2.0 );
NDebugOverlay::Line( pLast->vecLocation, pLast->vecLocation + m_goalDirection * 32, 0,0,255, true, 2.0 );
*/
}
}
}
//-----------------------------------------------------------------------------
Vector CAI_Path::GetGoalDirection( const Vector &startPos )
{
if (m_goalDirectionTarget)
{
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
if ( pLast )
{
AI_Waypoint_t *pPrev = pLast->GetPrev();
if (pPrev)
{
Vector goalDirection = m_goalDirectionTarget->GetAbsOrigin() - pPrev->vecLocation;
VectorNormalize( goalDirection );
return goalDirection;
}
else
{
Vector goalDirection = m_goalDirectionTarget->GetAbsOrigin() - startPos;
VectorNormalize( goalDirection );
return goalDirection;
}
}
}
else if (m_goalDirection == vec3_origin)
{
// Assert(0); // comment out the default directions in SetGoal() to find test cases for missing initialization
AI_Waypoint_t *pLast = m_Waypoints.GetLast();
if ( pLast )
{
AI_Waypoint_t *pPrev = pLast->GetPrev();
if (pPrev)
{
Vector goalDirection = pLast->vecLocation - pPrev->vecLocation;
VectorNormalize( goalDirection );
return goalDirection;
}
else
{
Vector goalDirection =pLast->vecLocation - startPos;
VectorNormalize( goalDirection );
return goalDirection;
}
}
}
return m_goalDirection;
}
//-----------------------------------------------------------------------------
void CAI_Path::SetGoalSpeed( float flSpeed )
{
m_goalSpeed = flSpeed;
}
//-----------------------------------------------------------------------------
void CAI_Path::SetGoalSpeed( CBaseEntity *pTarget )
{
m_goalSpeedTarget = pTarget;
}
//-----------------------------------------------------------------------------
float CAI_Path::GetGoalSpeed( const Vector &startPos )
{
if (m_goalSpeedTarget)
{
Vector goalDirection = GetGoalDirection( startPos );
Vector targetVelocity = m_goalSpeedTarget->GetSmoothedVelocity();
float dot = DotProduct( goalDirection, targetVelocity );
dot = MAX( 0.0f, dot );
// return a relative impact speed of m_goalSpeed
if (m_goalSpeed > 0.0)
{
return dot + m_goalSpeed;
}
return dot;
}
return m_goalSpeed;
}
//-----------------------------------------------------------------------------
void CAI_Path::SetGoalStoppingDistance( float flDistance )
{
m_goalStoppingDistance = flDistance;
}
//-----------------------------------------------------------------------------
float CAI_Path::GetGoalStoppingDistance( ) const
{
return m_goalStoppingDistance;
}
//-----------------------------------------------------------------------------
const Vector &CAI_Path::CurWaypointPos() const
{
if ( GetCurWaypoint() )
return GetCurWaypoint()->GetPos();
AssertMsg(0, "Invalid call to CurWaypointPos()");
return gm_InvalidWaypoint.GetPos();
}
//-----------------------------------------------------------------------------
const Vector &CAI_Path::NextWaypointPos() const
{
if ( GetCurWaypoint() && GetCurWaypoint()->GetNext())
return GetCurWaypoint()->GetNext()->GetPos();
static Vector invalid( 0, 0, 0 );
AssertMsg(0, "Invalid call to NextWaypointPos()");
return gm_InvalidWaypoint.GetPos();
}
//-----------------------------------------------------------------------------
float CAI_Path::CurWaypointYaw() const
{
return GetCurWaypoint()->flYaw;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_Path::SetGoalPosition(const Vector &goalPos)
{
#ifdef _DEBUG
// Make sure goal position isn't set more than once
if (m_bGoalPosSet == true)
{
DevMsg( "GetCurWaypoint Goal Position Set Twice!\n");
}
#endif
m_bGoalPosSet = true;
VectorAdd( goalPos, m_vecTargetOffset, m_goalPos );
}
//-----------------------------------------------------------------------------
// Purpose: Sets last node as goal and goal position
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_Path::SetLastNodeAsGoal(bool bReset)
{
#ifdef _DEBUG
// Make sure goal position isn't set more than once
if (!bReset && m_bGoalPosSet == true)
{
DevMsg( "GetCurWaypoint Goal Position Set Twice!\n");
}
#endif
// Find the last node
if (GetCurWaypoint())
{
AI_Waypoint_t* waypoint = GetCurWaypoint();
while (waypoint)
{
if (!waypoint->GetNext())
{
m_goalPos = waypoint->GetPos();
m_bGoalPosSet = true;
waypoint->ModifyFlags( bits_WP_TO_GOAL, true );
return;
}
waypoint = waypoint->GetNext();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Explicitly change the goal position w/o check
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_Path::ResetGoalPosition(const Vector &goalPos)
{
m_bGoalPosSet = true;
VectorAdd( goalPos, m_vecTargetOffset, m_goalPos );
}
//-----------------------------------------------------------------------------
// Returns the *base* goal position (without the offset applied)
//-----------------------------------------------------------------------------
const Vector& CAI_Path::BaseGoalPosition() const
{
#ifdef _DEBUG
// Make sure goal position was set
if (m_bGoalPosSet == false)
{
DevMsg( "GetCurWaypoint Goal Position Never Set!\n");
}
#endif
// FIXME: A little risky; store the base if this becomes a problem
static Vector vecResult;
VectorSubtract( m_goalPos, m_vecTargetOffset, vecResult );
return vecResult;
}
//-----------------------------------------------------------------------------
// Returns the *actual* goal position (with the offset applied)
//-----------------------------------------------------------------------------
const Vector & CAI_Path::ActualGoalPosition(void) const
{
#ifdef _DEBUG
// Make sure goal position was set
if (m_bGoalPosSet == false)
{
DevMsg( "GetCurWaypoint Goal Position Never Set!\n");
}
#endif
return m_goalPos;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_Path::SetGoalType(GoalType_t goalType)
{
#ifdef _DEBUG
// Make sure goal position isn't set more than once
if (m_goalType != GOALTYPE_NONE && goalType != GOALTYPE_NONE )
{
DevMsg( "GetCurWaypoint Goal Type Set Twice!\n");
}
#endif
if (m_goalType != GOALTYPE_NONE)
{
m_routeStartTime = gpGlobals->curtime;
m_bGoalTypeSet = true;
}
else
m_bGoalTypeSet = false;
m_goalType = goalType;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
GoalType_t CAI_Path::GoalType(void) const
{
return m_goalType;
}
//-----------------------------------------------------------------------------
void CAI_Path::Advance( void )
{
if ( CurWaypointIsGoal() )
return;
// -------------------------------------------------------
// If I have another waypoint advance my path
// -------------------------------------------------------
if (GetCurWaypoint()->GetNext())
{
AI_Waypoint_t *pNext = GetCurWaypoint()->GetNext();
// If waypoint was a node take note of it
if (GetCurWaypoint()->Flags() & bits_WP_TO_NODE)
{
m_iLastNodeReached = GetCurWaypoint()->iNodeID;
}
delete GetCurWaypoint();
SetWaypoints(pNext);
return;
}
// -------------------------------------------------
// This is an error catch that should *not* happen
// It means a route was created with no goal
// -------------------------------------------------
else
{
DevMsg( "!!ERROR!! Force end of route with no goal!\n");
GetCurWaypoint()->ModifyFlags( bits_WP_TO_GOAL, true );
}
AssertRouteValid( m_Waypoints.GetFirst() );
}
//-----------------------------------------------------------------------------
// Purpose: Clears the route and resets all its fields to default values
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_Path::Clear( void )
{
m_Waypoints.RemoveAll();
m_goalType = GOALTYPE_NONE; // Type of goal
m_goalPos = vec3_origin; // Our ultimate goal position
m_bGoalPosSet = false; // Was goal position set
m_bGoalTypeSet = false; // Was goal position set
m_goalFlags = false;
m_vecTargetOffset = vec3_origin;
m_routeStartTime = FLT_MAX;
m_goalTolerance = 0.0; // How close do we need to get to the goal
// FIXME: split m_goalTolerance into m_buildTolerance and m_moveTolerance, let them be seperatly controllable.
m_activity = ACT_INVALID;
m_sequence = ACT_INVALID;
m_target = NULL;
m_arrivalActivity = ACT_INVALID;
m_arrivalSequence = ACT_INVALID;
m_goalDirectionTarget = NULL;
m_goalDirection = vec3_origin;
m_goalSpeedTarget = NULL;
m_goalSpeed = -1.0f; // init to an invalid speed
m_goalStoppingDistance = 0.0; // How close to we want to get to the goal
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
Navigation_t CAI_Path::CurWaypointNavType() const
{
if (!GetCurWaypoint())
{
return NAV_NONE;
}
else
{
return GetCurWaypoint()->NavType();
}
}
int CAI_Path::CurWaypointFlags() const
{
if (!GetCurWaypoint())
{
return 0;
}
else
{
return GetCurWaypoint()->Flags();
}
}
//-----------------------------------------------------------------------------
// Purpose: Get the goal's flags
// Output : unsigned
//-----------------------------------------------------------------------------
unsigned CAI_Path::GoalFlags( void ) const
{
return m_goalFlags;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if current waypoint is my goal
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CAI_Path::CurWaypointIsGoal( void ) const
{
// Assert( GetCurWaypoint() );
if( !GetCurWaypoint() )
return false;
if ( GetCurWaypoint()->Flags() & bits_WP_TO_GOAL )
{
#ifdef _DEBUG
if (GetCurWaypoint()->GetNext())
{
DevMsg( "!!ERROR!! Goal is not last waypoint!\n");
}
if ((GetCurWaypoint()->GetPos() - m_goalPos).Length() > 0.1)
{
DevMsg( "!!ERROR!! Last waypoint isn't in goal position!\n");
}
#endif
return true;
}
if ( GetCurWaypoint()->Flags() & bits_WP_TO_PATHCORNER )
{
// UNDONE: Refresh here or somewhere else?
}
#ifdef _DEBUG
if (!GetCurWaypoint()->GetNext())
{
DevMsg( "!!ERROR!! GetCurWaypoint has no goal!\n");
}
#endif
return false;
}
//-----------------------------------------------------------------------------
// Computes the goal distance for each waypoint along the route
//-----------------------------------------------------------------------------
void CAI_Path::ComputeRouteGoalDistances(AI_Waypoint_t *pGoalWaypoint)
{
// The goal distance is the distance from any waypoint to the goal waypoint
// Backup through the list and calculate distance to goal
AI_Waypoint_t *pPrev;
AI_Waypoint_t *pCurWaypoint = pGoalWaypoint;
pCurWaypoint->flPathDistGoal = 0;
while (pCurWaypoint->GetPrev())
{
pPrev = pCurWaypoint->GetPrev();
float flWaypointDist = ComputePathDistance(pCurWaypoint->NavType(), pPrev->GetPos(), pCurWaypoint->GetPos());
pPrev->flPathDistGoal = pCurWaypoint->flPathDistGoal + flWaypointDist;
pCurWaypoint = pPrev;
}
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_Path::CAI_Path()
{
m_goalType = GOALTYPE_NONE; // Type of goal
m_goalPos = vec3_origin; // Our ultimate goal position
m_goalTolerance = 0.0; // How close do we need to get to the goal
m_activity = ACT_INVALID; // The activity to use during motion
m_sequence = ACT_INVALID;
m_target = NULL;
m_goalFlags = 0;
m_routeStartTime = FLT_MAX;
m_arrivalActivity = ACT_INVALID;
m_arrivalSequence = ACT_INVALID;
m_iLastNodeReached = NO_NODE;
m_waypointTolerance = DEF_WAYPOINT_TOLERANCE;
}
CAI_Path::~CAI_Path()
{
DeleteAll( GetCurWaypoint() );
}