Counter Strike : Global Offensive Source Code
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.

383 lines
9.3 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_node.cpp
  9. // AI Navigation Nodes
  10. // Author: Michael S. Booth ([email protected]), January 2003
  11. #include "cbase.h"
  12. #include "cs_nav_node.h"
  13. #include "nav_colors.h"
  14. #include "nav_mesh.h"
  15. NavDirType Opposite[ NUM_DIRECTIONS ] = { SOUTH, WEST, NORTH, EAST };
  16. CSNavNode *CSNavNode::m_list = NULL;
  17. unsigned int CSNavNode::m_listLength = 0;
  18. unsigned int CSNavNode::m_nextID = 1;
  19. ConVar nav_show_nodes( "nav_show_nodes", "0" );
  20. //--------------------------------------------------------------------------------------------------------------
  21. class LookAtTarget
  22. {
  23. public:
  24. LookAtTarget( const Vector &target )
  25. {
  26. m_target = target;
  27. }
  28. bool operator()( CBasePlayer *player )
  29. {
  30. QAngle angles;
  31. Vector to = m_target - player->GetAbsOrigin();
  32. VectorAngles( to, angles );
  33. player->SetLocalAngles( angles );
  34. player->SnapEyeAngles( angles );
  35. return true;
  36. }
  37. private:
  38. Vector m_target;
  39. };
  40. //--------------------------------------------------------------------------------------------------------------
  41. /**
  42. * Constructor
  43. */
  44. CSNavNode::CSNavNode( const Vector &pos, const Vector &normal, CSNavNode *parent )
  45. {
  46. m_pos = pos;
  47. m_normal = normal;
  48. m_id = m_nextID++;
  49. int i;
  50. for( i=0; i<NUM_DIRECTIONS; ++i )
  51. {
  52. m_to[ i ] = NULL;
  53. }
  54. for ( i=0; i<NUM_CORNERS; ++i )
  55. {
  56. m_crouch[ i ] = false;
  57. }
  58. m_visited = 0;
  59. m_parent = parent;
  60. m_next = m_list;
  61. m_list = this;
  62. m_listLength++;
  63. m_isCovered = false;
  64. m_area = NULL;
  65. m_attributeFlags = 0;
  66. if ( nav_show_nodes.GetBool() )
  67. {
  68. NDebugOverlay::Cross3D( m_pos, 10.0f, 128, 128, 128, true, 10.0f );
  69. NDebugOverlay::Cross3D( m_pos, 10.0f, 255, 255, 255, false, 10.0f );
  70. LookAtTarget lookAt( m_pos );
  71. ForEachPlayer( lookAt );
  72. }
  73. }
  74. //--------------------------------------------------------------------------------------------------------------
  75. #if DEBUG_NAV_NODES
  76. ConVar nav_show_node_id( "nav_show_node_id", "0" );
  77. ConVar nav_test_node( "nav_test_node", "0" );
  78. ConVar nav_test_node_crouch( "nav_test_node_crouch", "0" );
  79. ConVar nav_test_node_crouch_dir( "nav_test_node_crouch_dir", "4" );
  80. #endif // DEBUG_NAV_NODES
  81. //--------------------------------------------------------------------------------------------------------------
  82. void CSNavNode::Draw( void )
  83. {
  84. #if DEBUG_NAV_NODES
  85. if ( !nav_show_nodes.GetBool() )
  86. return;
  87. int r = 0, g = 0, b = 0;
  88. if ( m_isCovered )
  89. {
  90. if ( GetAttributes() & NAV_MESH_CROUCH )
  91. {
  92. b = 255;
  93. }
  94. else
  95. {
  96. r = 255;
  97. }
  98. }
  99. else
  100. {
  101. if ( GetAttributes() & NAV_MESH_CROUCH )
  102. {
  103. b = 255;
  104. }
  105. g = 255;
  106. }
  107. NDebugOverlay::Cross3D( m_pos, 2, r, g, b, true, 0.1f );
  108. if ( (!m_isCovered && nav_show_node_id.GetBool()) || (m_isCovered && nav_show_node_id.GetInt() < 0) )
  109. {
  110. char text[16];
  111. Q_snprintf( text, sizeof( text ), "%d", m_id );
  112. NDebugOverlay::Text( m_pos, text, true, 0.1f );
  113. }
  114. if ( (unsigned int)(nav_test_node.GetInt()) == m_id )
  115. {
  116. TheNavMesh->TestArea( this, 1, 1 );
  117. nav_test_node.SetValue( 0 );
  118. }
  119. if ( (unsigned int)(nav_test_node_crouch.GetInt()) == m_id )
  120. {
  121. CheckCrouch();
  122. nav_test_node_crouch.SetValue( 0 );
  123. }
  124. if ( GetAttributes() & NAV_MESH_CROUCH )
  125. {
  126. int i;
  127. for( i=0; i<NUM_CORNERS; i++ )
  128. {
  129. if ( m_crouch[i] )
  130. {
  131. Vector2D dir;
  132. CornerToVector2D( (NavCornerType)i, &dir );
  133. const float scale = 3.0f;
  134. Vector scaled( dir.x * scale, dir.y * scale, 0 );
  135. NDebugOverlay::HorzArrow( m_pos, m_pos + scaled, 0.5, 0, 0, 255, 255, true, 0.1f );
  136. }
  137. }
  138. }
  139. #endif // DEBUG_NAV_NODES
  140. }
  141. //--------------------------------------------------------------------------------------------------------------
  142. void CSNavNode::CheckCrouch( void )
  143. {
  144. CTraceFilterWalkableEntities filter( NULL, COLLISION_GROUP_PLAYER_MOVEMENT, WALK_THRU_EVERYTHING );
  145. trace_t tr;
  146. // Trace downward from duck height to find the max floor height for the node's surroundings
  147. Vector mins( -HalfHumanWidth, -HalfHumanWidth, 0 );
  148. Vector maxs( HalfHumanWidth, HalfHumanWidth, 0 );
  149. Vector start( m_pos.x, m_pos.y, m_pos.z + VEC_DUCK_HULL_MAX.z - 0.1f );
  150. UTIL_TraceHull(
  151. start,
  152. m_pos,
  153. mins,
  154. maxs,
  155. MASK_PLAYERSOLID_BRUSHONLY,
  156. &filter,
  157. &tr );
  158. Vector groundPos = tr.endpos;
  159. if ( tr.startsolid && !tr.allsolid )
  160. {
  161. // Try going down out of the solid and re-check for the floor height
  162. start.z -= tr.endpos.z - 0.1f;
  163. UTIL_TraceHull(
  164. start,
  165. m_pos,
  166. mins,
  167. maxs,
  168. MASK_PLAYERSOLID_BRUSHONLY,
  169. &filter,
  170. &tr );
  171. groundPos = tr.endpos;
  172. }
  173. if ( tr.startsolid )
  174. {
  175. // we don't even have duck height clear. try a simple check to find floor height.
  176. float x, y;
  177. // Find the highest floor z - for a player to stand in this area, we need a full
  178. // VEC_HULL_MAX.z of clearance above this height at all points.
  179. float maxFloorZ = m_pos.z;
  180. for( y = -HalfHumanWidth; y <= HalfHumanWidth + 0.1f; y += HalfHumanWidth )
  181. {
  182. for( x = -HalfHumanWidth; x <= HalfHumanWidth + 0.1f; x += HalfHumanWidth )
  183. {
  184. float floorZ;
  185. if ( TheNavMesh->GetGroundHeight( m_pos, &floorZ ) )
  186. {
  187. maxFloorZ = MAX( maxFloorZ, floorZ + 0.1f );
  188. }
  189. }
  190. }
  191. groundPos.Init( m_pos.x, m_pos.y, maxFloorZ );
  192. }
  193. // For each direction, trace upwards from our best ground height to VEC_HULL_MAX.z to see if we have standing room.
  194. for ( int i=0; i<NUM_CORNERS; ++i )
  195. {
  196. #if DEBUG_NAV_NODES
  197. if ( nav_test_node_crouch_dir.GetInt() != NUM_CORNERS && i != nav_test_node_crouch_dir.GetInt() )
  198. continue;
  199. #endif // DEBUG_NAV_NODES
  200. NavCornerType corner = (NavCornerType)i;
  201. Vector2D cornerVec;
  202. CornerToVector2D( corner, &cornerVec );
  203. Vector actualGroundPos = groundPos; // we might need to adjust this if the tracehull failed above and we fell back to m_pos.z
  204. // Build a mins/maxs pair for the HumanWidth x HalfHumanWidth box facing the appropriate direction
  205. mins.Init();
  206. maxs.Init( cornerVec.x * HalfHumanWidth, cornerVec.y * HalfHumanWidth, 0 );
  207. // now make sure that mins is smaller than maxs
  208. for ( int j=0; j<3; ++j )
  209. {
  210. if ( mins[j] > maxs[j] )
  211. {
  212. float tmp = mins[j];
  213. mins[j] = maxs[j];
  214. maxs[j] = tmp;
  215. }
  216. }
  217. UTIL_TraceHull(
  218. actualGroundPos + Vector( 0, 0, 0.1f ),
  219. actualGroundPos + Vector( 0, 0, VEC_HULL_MAX.z - 0.2f ),
  220. mins,
  221. maxs,
  222. MASK_PLAYERSOLID_BRUSHONLY,
  223. &filter,
  224. &tr );
  225. actualGroundPos.z += tr.fractionleftsolid * VEC_HULL_MAX.z;
  226. float maxHeight = actualGroundPos.z + VEC_DUCK_HULL_MAX.z;
  227. for ( ; tr.startsolid && actualGroundPos.z <= maxHeight; actualGroundPos.z += 1.0f )
  228. {
  229. // In case we didn't find a good ground pos above, we could start in the ground. Move us up some.
  230. UTIL_TraceHull(
  231. actualGroundPos + Vector( 0, 0, 0.1f ),
  232. actualGroundPos + Vector( 0, 0, VEC_HULL_MAX.z - 0.2f ),
  233. mins,
  234. maxs,
  235. MASK_PLAYERSOLID_BRUSHONLY,
  236. &filter,
  237. &tr );
  238. }
  239. if (tr.startsolid || tr.fraction != 1.0f)
  240. {
  241. SetAttributes( NAV_MESH_CROUCH );
  242. m_crouch[corner] = true;
  243. }
  244. #if DEBUG_NAV_NODES
  245. if ( nav_show_nodes.GetBool() )
  246. {
  247. if ( nav_test_node_crouch_dir.GetInt() == i || nav_test_node_crouch_dir.GetInt() == NUM_CORNERS )
  248. {
  249. if ( tr.startsolid )
  250. {
  251. NDebugOverlay::Box( actualGroundPos, mins, maxs+Vector( 0, 0, VEC_HULL_MAX.z), 255, 0, 0, 10, 20.0f );
  252. }
  253. else if ( m_crouch[corner] )
  254. {
  255. NDebugOverlay::Box( actualGroundPos, mins, maxs+Vector( 0, 0, VEC_HULL_MAX.z), 0, 0, 255, 10, 20.0f );
  256. }
  257. else
  258. {
  259. NDebugOverlay::Box( actualGroundPos, mins, maxs+Vector( 0, 0, VEC_HULL_MAX.z), 0, 255, 0, 10, 10.0f );
  260. }
  261. }
  262. }
  263. #endif // DEBUG_NAV_NODES
  264. }
  265. }
  266. //--------------------------------------------------------------------------------------------------------------
  267. /**
  268. * Create a connection FROM this node TO the given node, in the given direction
  269. */
  270. void CSNavNode::ConnectTo( CSNavNode *node, NavDirType dir )
  271. {
  272. m_to[ dir ] = node;
  273. }
  274. //--------------------------------------------------------------------------------------------------------------
  275. /**
  276. * Return node at given position.
  277. * @todo Need a hash table to make this lookup fast
  278. */
  279. CSNavNode *CSNavNode::GetNode( const Vector &pos )
  280. {
  281. const float tolerance = 0.45f * GenerationStepSize; // 1.0f
  282. for( CSNavNode *node = m_list; node; node = node->m_next )
  283. {
  284. float dx = fabs( node->m_pos.x - pos.x );
  285. float dy = fabs( node->m_pos.y - pos.y );
  286. float dz = fabs( node->m_pos.z - pos.z );
  287. if (dx < tolerance && dy < tolerance && dz < tolerance)
  288. return node;
  289. }
  290. return NULL;
  291. }
  292. //--------------------------------------------------------------------------------------------------------------
  293. /**
  294. * Return true if this node is bidirectionally linked to
  295. * another node in the given direction
  296. */
  297. BOOL CSNavNode::IsBiLinked( NavDirType dir ) const
  298. {
  299. if (m_to[ dir ] && m_to[ dir ]->m_to[ Opposite[dir] ] == this)
  300. {
  301. return true;
  302. }
  303. return false;
  304. }
  305. //--------------------------------------------------------------------------------------------------------------
  306. /**
  307. * Return true if this node is the NW corner of a quad of nodes
  308. * that are all bidirectionally linked.
  309. */
  310. BOOL CSNavNode::IsClosedCell( void ) const
  311. {
  312. if (IsBiLinked( SOUTH ) &&
  313. IsBiLinked( EAST ) &&
  314. m_to[ EAST ]->IsBiLinked( SOUTH ) &&
  315. m_to[ SOUTH ]->IsBiLinked( EAST ) &&
  316. m_to[ EAST ]->m_to[ SOUTH ] == m_to[ SOUTH ]->m_to[ EAST ])
  317. {
  318. return true;
  319. }
  320. return false;
  321. }