//========= Copyright 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 "cs_nav_node.h" #include "nav_colors.h" #include "nav_mesh.h" NavDirType Opposite[ NUM_DIRECTIONS ] = { SOUTH, WEST, NORTH, EAST }; CSNavNode *CSNavNode::m_list = NULL; unsigned int CSNavNode::m_listLength = 0; unsigned int CSNavNode::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 */ CSNavNode::CSNavNode( const Vector &pos, const Vector &normal, CSNavNode *parent ) { m_pos = pos; m_normal = normal; m_id = m_nextID++; int i; for( i=0; iTestArea( 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; iGetGroundHeight( 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 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 CSNavNode::ConnectTo( CSNavNode *node, NavDirType dir ) { m_to[ dir ] = node; } //-------------------------------------------------------------------------------------------------------------- /** * Return node at given position. * @todo Need a hash table to make this lookup fast */ CSNavNode *CSNavNode::GetNode( const Vector &pos ) { const float tolerance = 0.45f * GenerationStepSize; // 1.0f for( CSNavNode *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 CSNavNode::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 CSNavNode::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; }