mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2025-09-19 20:16:10 +08:00
Added most recent version of unmodified HL2 SDK for Episode 1 engine
This commit is contained in:
383
dlls/nav_node.cpp
Normal file
383
dlls/nav_node.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// nav_node.cpp
|
||||
// AI Navigation Nodes
|
||||
// Author: Michael S. Booth (mike@turtlerockstudios.com), January 2003
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_node.h"
|
||||
#include "nav_colors.h"
|
||||
#include "nav_mesh.h"
|
||||
|
||||
NavDirType Opposite[ NUM_DIRECTIONS ] = { SOUTH, WEST, NORTH, EAST };
|
||||
|
||||
CNavNode *CNavNode::m_list = NULL;
|
||||
unsigned int CNavNode::m_listLength = 0;
|
||||
unsigned int CNavNode::m_nextID = 1;
|
||||
|
||||
ConVar nav_show_nodes( "nav_show_nodes", "0" );
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
class LookAtTarget
|
||||
{
|
||||
public:
|
||||
LookAtTarget( const Vector &target )
|
||||
{
|
||||
m_target = target;
|
||||
}
|
||||
|
||||
bool operator()( CBasePlayer *player )
|
||||
{
|
||||
QAngle angles;
|
||||
Vector to = m_target - player->GetAbsOrigin();
|
||||
VectorAngles( to, angles );
|
||||
|
||||
player->SetLocalAngles( angles );
|
||||
player->SnapEyeAngles( angles );
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Vector m_target;
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
CNavNode::CNavNode( const Vector &pos, const Vector &normal, CNavNode *parent )
|
||||
{
|
||||
m_pos = pos;
|
||||
m_normal = normal;
|
||||
|
||||
m_id = m_nextID++;
|
||||
|
||||
int i;
|
||||
for( i=0; i<NUM_DIRECTIONS; ++i )
|
||||
{
|
||||
m_to[ i ] = NULL;
|
||||
}
|
||||
|
||||
for ( i=0; i<NUM_CORNERS; ++i )
|
||||
{
|
||||
m_crouch[ i ] = false;
|
||||
}
|
||||
|
||||
m_visited = 0;
|
||||
m_parent = parent;
|
||||
|
||||
m_next = m_list;
|
||||
m_list = this;
|
||||
m_listLength++;
|
||||
|
||||
m_isCovered = false;
|
||||
m_area = NULL;
|
||||
|
||||
m_attributeFlags = 0;
|
||||
|
||||
if ( nav_show_nodes.GetBool() )
|
||||
{
|
||||
NDebugOverlay::Cross3D( m_pos, 10.0f, 128, 128, 128, true, 10.0f );
|
||||
NDebugOverlay::Cross3D( m_pos, 10.0f, 255, 255, 255, false, 10.0f );
|
||||
|
||||
LookAtTarget lookAt( m_pos );
|
||||
ForEachPlayer( lookAt );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
#if DEBUG_NAV_NODES
|
||||
ConVar nav_show_node_id( "nav_show_node_id", "0" );
|
||||
ConVar nav_test_node( "nav_test_node", "0" );
|
||||
ConVar nav_test_node_crouch( "nav_test_node_crouch", "0" );
|
||||
ConVar nav_test_node_crouch_dir( "nav_test_node_crouch_dir", "4" );
|
||||
#endif // DEBUG_NAV_NODES
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
void CNavNode::Draw( void )
|
||||
{
|
||||
#if DEBUG_NAV_NODES
|
||||
if ( !nav_show_nodes.GetBool() )
|
||||
return;
|
||||
|
||||
int r = 0, g = 0, b = 0;
|
||||
|
||||
if ( m_isCovered )
|
||||
{
|
||||
if ( GetAttributes() & NAV_MESH_CROUCH )
|
||||
{
|
||||
b = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = 255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetAttributes() & NAV_MESH_CROUCH )
|
||||
{
|
||||
b = 255;
|
||||
}
|
||||
g = 255;
|
||||
}
|
||||
|
||||
NDebugOverlay::Cross3D( m_pos, 2, r, g, b, true, 0.1f );
|
||||
|
||||
if ( (!m_isCovered && nav_show_node_id.GetBool()) || (m_isCovered && nav_show_node_id.GetInt() < 0) )
|
||||
{
|
||||
char text[16];
|
||||
Q_snprintf( text, sizeof( text ), "%d", m_id );
|
||||
NDebugOverlay::Text( m_pos, text, true, 0.1f );
|
||||
}
|
||||
|
||||
if ( (unsigned int)(nav_test_node.GetInt()) == m_id )
|
||||
{
|
||||
TheNavMesh->TestArea( this, 1, 1 );
|
||||
nav_test_node.SetValue( 0 );
|
||||
}
|
||||
|
||||
if ( (unsigned int)(nav_test_node_crouch.GetInt()) == m_id )
|
||||
{
|
||||
CheckCrouch();
|
||||
nav_test_node_crouch.SetValue( 0 );
|
||||
}
|
||||
|
||||
if ( GetAttributes() & NAV_MESH_CROUCH )
|
||||
{
|
||||
int i;
|
||||
for( i=0; i<NUM_CORNERS; i++ )
|
||||
{
|
||||
if ( m_crouch[i] )
|
||||
{
|
||||
Vector2D dir;
|
||||
CornerToVector2D( (NavCornerType)i, &dir );
|
||||
|
||||
const float scale = 3.0f;
|
||||
Vector scaled( dir.x * scale, dir.y * scale, 0 );
|
||||
|
||||
NDebugOverlay::HorzArrow( m_pos, m_pos + scaled, 0.5, 0, 0, 255, 255, true, 0.1f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DEBUG_NAV_NODES
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
void CNavNode::CheckCrouch( void )
|
||||
{
|
||||
CTraceFilterWalkableEntities filter( NULL, COLLISION_GROUP_PLAYER_MOVEMENT, WALK_THRU_EVERYTHING );
|
||||
trace_t tr;
|
||||
|
||||
// Trace downward from duck height to find the max floor height for the node's surroundings
|
||||
Vector mins( -HalfHumanWidth, -HalfHumanWidth, 0 );
|
||||
Vector maxs( HalfHumanWidth, HalfHumanWidth, 0 );
|
||||
Vector start( m_pos.x, m_pos.y, m_pos.z + VEC_DUCK_HULL_MAX.z - 0.1f );
|
||||
UTIL_TraceHull(
|
||||
start,
|
||||
m_pos,
|
||||
mins,
|
||||
maxs,
|
||||
MASK_PLAYERSOLID_BRUSHONLY,
|
||||
&filter,
|
||||
&tr );
|
||||
|
||||
Vector groundPos = tr.endpos;
|
||||
|
||||
if ( tr.startsolid && !tr.allsolid )
|
||||
{
|
||||
// Try going down out of the solid and re-check for the floor height
|
||||
start.z -= tr.endpos.z - 0.1f;
|
||||
|
||||
UTIL_TraceHull(
|
||||
start,
|
||||
m_pos,
|
||||
mins,
|
||||
maxs,
|
||||
MASK_PLAYERSOLID_BRUSHONLY,
|
||||
&filter,
|
||||
&tr );
|
||||
|
||||
groundPos = tr.endpos;
|
||||
}
|
||||
|
||||
if ( tr.startsolid )
|
||||
{
|
||||
// we don't even have duck height clear. try a simple check to find floor height.
|
||||
float x, y;
|
||||
|
||||
// Find the highest floor z - for a player to stand in this area, we need a full
|
||||
// VEC_HULL_MAX.z of clearance above this height at all points.
|
||||
float maxFloorZ = m_pos.z;
|
||||
for( y = -HalfHumanWidth; y <= HalfHumanWidth + 0.1f; y += HalfHumanWidth )
|
||||
{
|
||||
for( x = -HalfHumanWidth; x <= HalfHumanWidth + 0.1f; x += HalfHumanWidth )
|
||||
{
|
||||
float floorZ;
|
||||
if ( TheNavMesh->GetGroundHeight( m_pos, &floorZ ) )
|
||||
{
|
||||
maxFloorZ = max( maxFloorZ, floorZ + 0.1f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groundPos.Init( m_pos.x, m_pos.y, maxFloorZ );
|
||||
}
|
||||
|
||||
// For each direction, trace upwards from our best ground height to VEC_HULL_MAX.z to see if we have standing room.
|
||||
for ( int i=0; i<NUM_CORNERS; ++i )
|
||||
{
|
||||
#if DEBUG_NAV_NODES
|
||||
if ( nav_test_node_crouch_dir.GetInt() != NUM_CORNERS && i != nav_test_node_crouch_dir.GetInt() )
|
||||
continue;
|
||||
#endif // DEBUG_NAV_NODES
|
||||
|
||||
NavCornerType corner = (NavCornerType)i;
|
||||
Vector2D cornerVec;
|
||||
CornerToVector2D( corner, &cornerVec );
|
||||
|
||||
Vector actualGroundPos = groundPos; // we might need to adjust this if the tracehull failed above and we fell back to m_pos.z
|
||||
|
||||
// Build a mins/maxs pair for the HumanWidth x HalfHumanWidth box facing the appropriate direction
|
||||
mins.Init();
|
||||
maxs.Init( cornerVec.x * HalfHumanWidth, cornerVec.y * HalfHumanWidth, 0 );
|
||||
|
||||
// now make sure that mins is smaller than maxs
|
||||
for ( int j=0; j<3; ++j )
|
||||
{
|
||||
if ( mins[j] > maxs[j] )
|
||||
{
|
||||
float tmp = mins[j];
|
||||
mins[j] = maxs[j];
|
||||
maxs[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
UTIL_TraceHull(
|
||||
actualGroundPos + Vector( 0, 0, 0.1f ),
|
||||
actualGroundPos + Vector( 0, 0, VEC_HULL_MAX.z - 0.2f ),
|
||||
mins,
|
||||
maxs,
|
||||
MASK_PLAYERSOLID_BRUSHONLY,
|
||||
&filter,
|
||||
&tr );
|
||||
actualGroundPos.z += tr.fractionleftsolid * VEC_HULL_MAX.z;
|
||||
float maxHeight = actualGroundPos.z + VEC_DUCK_HULL_MAX.z;
|
||||
for ( ; tr.startsolid && actualGroundPos.z <= maxHeight; actualGroundPos.z += 1.0f )
|
||||
{
|
||||
// In case we didn't find a good ground pos above, we could start in the ground. Move us up some.
|
||||
UTIL_TraceHull(
|
||||
actualGroundPos + Vector( 0, 0, 0.1f ),
|
||||
actualGroundPos + Vector( 0, 0, VEC_HULL_MAX.z - 0.2f ),
|
||||
mins,
|
||||
maxs,
|
||||
MASK_PLAYERSOLID_BRUSHONLY,
|
||||
&filter,
|
||||
&tr );
|
||||
}
|
||||
if (tr.startsolid || tr.fraction != 1.0f)
|
||||
{
|
||||
SetAttributes( NAV_MESH_CROUCH );
|
||||
m_crouch[corner] = true;
|
||||
}
|
||||
|
||||
#if DEBUG_NAV_NODES
|
||||
if ( nav_show_nodes.GetBool() )
|
||||
{
|
||||
if ( nav_test_node_crouch_dir.GetInt() == i || nav_test_node_crouch_dir.GetInt() == NUM_CORNERS )
|
||||
{
|
||||
if ( tr.startsolid )
|
||||
{
|
||||
NDebugOverlay::Box( actualGroundPos, mins, maxs+Vector( 0, 0, VEC_HULL_MAX.z), 255, 0, 0, 10, 20.0f );
|
||||
}
|
||||
else if ( m_crouch[corner] )
|
||||
{
|
||||
NDebugOverlay::Box( actualGroundPos, mins, maxs+Vector( 0, 0, VEC_HULL_MAX.z), 0, 0, 255, 10, 20.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
NDebugOverlay::Box( actualGroundPos, mins, maxs+Vector( 0, 0, VEC_HULL_MAX.z), 0, 255, 0, 10, 10.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_NAV_NODES
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Create a connection FROM this node TO the given node, in the given direction
|
||||
*/
|
||||
void CNavNode::ConnectTo( CNavNode *node, NavDirType dir )
|
||||
{
|
||||
m_to[ dir ] = node;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Return node at given position.
|
||||
* @todo Need a hash table to make this lookup fast
|
||||
*/
|
||||
CNavNode *CNavNode::GetNode( const Vector &pos )
|
||||
{
|
||||
const float tolerance = 0.45f * GenerationStepSize; // 1.0f
|
||||
|
||||
for( CNavNode *node = m_list; node; node = node->m_next )
|
||||
{
|
||||
float dx = fabs( node->m_pos.x - pos.x );
|
||||
float dy = fabs( node->m_pos.y - pos.y );
|
||||
float dz = fabs( node->m_pos.z - pos.z );
|
||||
|
||||
if (dx < tolerance && dy < tolerance && dz < tolerance)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Return true if this node is bidirectionally linked to
|
||||
* another node in the given direction
|
||||
*/
|
||||
BOOL CNavNode::IsBiLinked( NavDirType dir ) const
|
||||
{
|
||||
if (m_to[ dir ] && m_to[ dir ]->m_to[ Opposite[dir] ] == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Return true if this node is the NW corner of a quad of nodes
|
||||
* that are all bidirectionally linked.
|
||||
*/
|
||||
BOOL CNavNode::IsClosedCell( void ) const
|
||||
{
|
||||
if (IsBiLinked( SOUTH ) &&
|
||||
IsBiLinked( EAST ) &&
|
||||
m_to[ EAST ]->IsBiLinked( SOUTH ) &&
|
||||
m_to[ SOUTH ]->IsBiLinked( EAST ) &&
|
||||
m_to[ EAST ]->m_to[ SOUTH ] == m_to[ SOUTH ]->m_to[ EAST ])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user