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.

3081 lines
90 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // NavMesh.cpp
  9. // Implementation of Navigation Mesh interface
  10. // Author: Michael S. Booth, 2003-2004
  11. #include "cbase.h"
  12. #include "filesystem.h"
  13. #include "nav_mesh.h"
  14. #include "nav_node.h"
  15. #include "fmtstr.h"
  16. #include "utlbuffer.h"
  17. #include "tier0/vprof.h"
  18. //#include "shared_util.h"
  19. #ifdef TERROR
  20. #include "func_simpleladder.h"
  21. #endif
  22. #include "functorutils.h"
  23. // NOTE: This has to be the last file included!
  24. #include "tier0/memdbgon.h"
  25. #define DrawLine( from, to, duration, red, green, blue ) NDebugOverlay::Line( from, to, red, green, blue, true, 0.1f )
  26. /**
  27. * The singleton for accessing the navigation mesh
  28. */
  29. CNavMesh *TheNavMesh = NULL;
  30. ConVar nav_edit( "nav_edit", "0", FCVAR_GAMEDLL | FCVAR_CHEAT, "Set to one to interactively edit the Navigation Mesh. Set to zero to leave edit mode." );
  31. #ifdef CSTRIKE_DLL
  32. #define NAV_QUICKSAVE_DEFAULT "0" // the CSBots need this data
  33. #else
  34. #define NAV_QUICKSAVE_DEFAULT "1"
  35. #endif
  36. ConVar nav_quicksave( "nav_quicksave", NAV_QUICKSAVE_DEFAULT, FCVAR_GAMEDLL | FCVAR_CHEAT, "Set to one to skip the time consuming phases of the analysis. Useful for data collection and testing." );
  37. ConVar nav_show_approach_points( "nav_show_approach_points", "0", FCVAR_GAMEDLL | FCVAR_CHEAT, "Show Approach Points in the Navigation Mesh." );
  38. ConVar nav_show_danger( "nav_show_danger", "0", FCVAR_GAMEDLL | FCVAR_CHEAT, "Show current 'danger' levels." );
  39. ConVar nav_show_player_counts( "nav_show_player_counts", "0", FCVAR_GAMEDLL | FCVAR_CHEAT, "Show current player counts in each area." );
  40. ConVar nav_max_vis_delta_list_length( "nav_max_vis_delta_list_length", "64", FCVAR_CHEAT );
  41. extern ConVar nav_show_potentially_visible;
  42. int g_DebugPathfindCounter = 0;
  43. bool FindGroundForNode( Vector *pos, Vector *normal );
  44. //--------------------------------------------------------------------------------------------------------------
  45. CNavMesh::CNavMesh( void )
  46. {
  47. m_spawnName = NULL;
  48. m_gridCellSize = 300.0f;
  49. m_editMode = NORMAL;
  50. m_bQuitWhenFinished = false;
  51. m_hostThreadModeRestoreValue = 0;
  52. m_placeCount = 0;
  53. m_placeName = NULL;
  54. LoadPlaceDatabase();
  55. ListenForGameEvent( "round_start" );
  56. ListenForGameEvent( "round_start_pre_entity" );
  57. ListenForGameEvent( "break_prop" );
  58. ListenForGameEvent( "break_breakable" );
  59. ListenForGameEvent( "teamplay_round_start" );
  60. Reset();
  61. }
  62. //--------------------------------------------------------------------------------------------------------------
  63. CNavMesh::~CNavMesh()
  64. {
  65. if (m_spawnName)
  66. delete [] m_spawnName;
  67. // !!!!bug!!! why does this crash in linux on server exit
  68. for( unsigned int i=0; i<m_placeCount; ++i )
  69. {
  70. delete [] m_placeName[i];
  71. }
  72. }
  73. //--------------------------------------------------------------------------------------------------------------
  74. /**
  75. * Reset the Navigation Mesh to initial values
  76. */
  77. void CNavMesh::Reset( void )
  78. {
  79. DestroyNavigationMesh();
  80. m_generationMode = GENERATE_NONE;
  81. m_currentNode = NULL;
  82. ClearWalkableSeeds();
  83. m_isAnalyzed = false;
  84. m_isOutOfDate = false;
  85. m_isEditing = false;
  86. m_navPlace = UNDEFINED_PLACE;
  87. m_markedArea = NULL;
  88. m_selectedArea = NULL;
  89. m_bQuitWhenFinished = false;
  90. m_editMode = NORMAL;
  91. m_lastSelectedArea = NULL;
  92. m_isPlacePainting = false;
  93. m_climbableSurface = false;
  94. m_markedLadder = NULL;
  95. m_selectedLadder = NULL;
  96. m_updateBlockedAreasTimer.Invalidate();
  97. if (m_spawnName)
  98. {
  99. delete [] m_spawnName;
  100. }
  101. m_spawnName = NULL;
  102. m_walkableSeeds.RemoveAll();
  103. }
  104. //--------------------------------------------------------------------------------------------------------------
  105. CNavArea *CNavMesh::GetMarkedArea( void ) const
  106. {
  107. if ( m_markedArea )
  108. {
  109. return m_markedArea;
  110. }
  111. if ( m_selectedSet.Count() == 1 )
  112. {
  113. return m_selectedSet[0];
  114. }
  115. return NULL;
  116. }
  117. //--------------------------------------------------------------------------------------------------------------
  118. /**
  119. * Free all resources of the mesh and reset it to empty state
  120. */
  121. void CNavMesh::DestroyNavigationMesh( bool incremental )
  122. {
  123. m_blockedAreas.RemoveAll();
  124. m_avoidanceObstacleAreas.RemoveAll();
  125. m_transientAreas.RemoveAll();
  126. if ( !incremental )
  127. {
  128. // destroy all areas
  129. CNavArea::m_isReset = true;
  130. // tell players to forget about the areas
  131. FOR_EACH_VEC( TheNavAreas, it )
  132. {
  133. EditDestroyNotification notification( TheNavAreas[it] );
  134. ForEachActor( notification );
  135. }
  136. // remove each element of the list and delete them
  137. FOR_EACH_VEC( TheNavAreas, it )
  138. {
  139. DestroyArea( TheNavAreas[ it ] );
  140. }
  141. TheNavAreas.RemoveAll();
  142. CNavArea::m_isReset = false;
  143. // destroy ladder representations
  144. DestroyLadders();
  145. }
  146. else
  147. {
  148. FOR_EACH_VEC( TheNavAreas, it )
  149. {
  150. TheNavAreas[ it ]->ResetNodes();
  151. }
  152. }
  153. // destroy all hiding spots
  154. DestroyHidingSpots();
  155. // destroy navigation nodes created during map generation
  156. CNavNode::CleanupGeneration();
  157. if ( !incremental )
  158. {
  159. // destroy the grid
  160. m_grid.RemoveAll();
  161. m_gridSizeX = 0;
  162. m_gridSizeY = 0;
  163. }
  164. // clear the hash table
  165. for( int i=0; i<HASH_TABLE_SIZE; ++i )
  166. {
  167. m_hashTable[i] = NULL;
  168. }
  169. if ( !incremental )
  170. {
  171. m_areaCount = 0;
  172. }
  173. if ( !incremental )
  174. {
  175. // Reset the next area and ladder IDs to 1
  176. CNavArea::CompressIDs();
  177. CNavLadder::CompressIDs();
  178. }
  179. SetEditMode( NORMAL );
  180. m_markedArea = NULL;
  181. m_selectedArea = NULL;
  182. m_lastSelectedArea = NULL;
  183. m_climbableSurface = false;
  184. m_markedLadder = NULL;
  185. m_selectedLadder = NULL;
  186. if ( !incremental )
  187. {
  188. m_isLoaded = false;
  189. }
  190. }
  191. //--------------------------------------------------------------------------------------------------------------
  192. /**
  193. * Invoked on each game frame
  194. */
  195. void CNavMesh::Update( void )
  196. {
  197. VPROF( "CNavMesh::Update" );
  198. if (IsGenerating())
  199. {
  200. UpdateGeneration( 0.03 );
  201. return; // don't bother trying to draw stuff while we're generating
  202. }
  203. // Test all of the areas for blocked status
  204. if ( m_updateBlockedAreasTimer.HasStarted() && m_updateBlockedAreasTimer.IsElapsed() )
  205. {
  206. TestAllAreasForBlockedStatus();
  207. m_updateBlockedAreasTimer.Invalidate();
  208. }
  209. UpdateBlockedAreas();
  210. UpdateAvoidanceObstacleAreas();
  211. if (nav_edit.GetBool())
  212. {
  213. if (m_isEditing == false)
  214. {
  215. OnEditModeStart();
  216. m_isEditing = true;
  217. }
  218. DrawEditMode();
  219. }
  220. else
  221. {
  222. if (m_isEditing)
  223. {
  224. OnEditModeEnd();
  225. m_isEditing = false;
  226. }
  227. }
  228. if (nav_show_danger.GetBool())
  229. {
  230. DrawDanger();
  231. }
  232. if (nav_show_player_counts.GetBool())
  233. {
  234. DrawPlayerCounts();
  235. }
  236. if ( nav_show_potentially_visible.GetBool() )
  237. {
  238. CBasePlayer *player = UTIL_GetListenServerHost();
  239. if ( player && player->GetLastKnownArea() )
  240. {
  241. CNavArea *eyepointArea = player->GetLastKnownArea();
  242. if ( eyepointArea )
  243. {
  244. FOR_EACH_VEC( TheNavAreas, it )
  245. {
  246. CNavArea *area = TheNavAreas[it];
  247. if ( eyepointArea->IsCompletelyVisible( area ) )
  248. {
  249. area->DrawFilled( 100, 100, 200, 255 );
  250. }
  251. else if ( eyepointArea->IsPotentiallyVisible( area ) && nav_show_potentially_visible.GetInt() == 1 )
  252. {
  253. area->DrawFilled( 100, 200, 100, 255 );
  254. }
  255. }
  256. }
  257. }
  258. }
  259. // draw any walkable seeds that have been marked
  260. for ( int it=0; it < m_walkableSeeds.Count(); ++it )
  261. {
  262. WalkableSeedSpot spot = m_walkableSeeds[ it ];
  263. const float height = 50.0f;
  264. const float width = 25.0f;
  265. DrawLine( spot.pos, spot.pos + height * spot.normal, 3, 255, 0, 255 );
  266. DrawLine( spot.pos + Vector( width, 0, 0 ), spot.pos + height * spot.normal, 3, 255, 0, 255 );
  267. DrawLine( spot.pos + Vector( -width, 0, 0 ), spot.pos + height * spot.normal, 3, 255, 0, 255 );
  268. DrawLine( spot.pos + Vector( 0, width, 0 ), spot.pos + height * spot.normal, 3, 255, 0, 255 );
  269. DrawLine( spot.pos + Vector( 0, -width, 0 ), spot.pos + height * spot.normal, 3, 255, 0, 255 );
  270. }
  271. }
  272. //--------------------------------------------------------------------------------------------------------------
  273. /**
  274. * Check all nav areas inside the breakable's extent to see if players would now fall through
  275. */
  276. class CheckAreasOverlappingBreakable
  277. {
  278. public:
  279. CheckAreasOverlappingBreakable( CBaseEntity *breakable )
  280. {
  281. m_breakable = breakable;
  282. ICollideable *collideable = breakable->GetCollideable();
  283. collideable->WorldSpaceSurroundingBounds( &m_breakableExtent.lo, &m_breakableExtent.hi );
  284. const float expand = 10.0f;
  285. m_breakableExtent.lo += Vector( -expand, -expand, -expand );
  286. m_breakableExtent.hi += Vector( expand, expand, expand );
  287. }
  288. bool operator() ( CNavArea *area )
  289. {
  290. if ( area->IsOverlapping( m_breakableExtent ) )
  291. {
  292. // area overlaps the breakable
  293. area->CheckFloor( m_breakable );
  294. }
  295. return true;
  296. }
  297. private:
  298. Extent m_breakableExtent;
  299. CBaseEntity *m_breakable;
  300. };
  301. //--------------------------------------------------------------------------------------------------------------
  302. class NavRoundRestart
  303. {
  304. public:
  305. bool operator()( CNavArea *area )
  306. {
  307. area->OnRoundRestart();
  308. return true;
  309. }
  310. bool operator()( CNavLadder *ladder )
  311. {
  312. ladder->OnRoundRestart();
  313. return true;
  314. }
  315. };
  316. //--------------------------------------------------------------------------------------------------------------
  317. /**
  318. * Invoked when the round restarts
  319. */
  320. void CNavMesh::FireGameEvent( IGameEvent *gameEvent )
  321. {
  322. VPROF_BUDGET( "CNavMesh::FireGameEvent", VPROF_BUDGETGROUP_NPCS );
  323. if ( FStrEq( gameEvent->GetName(), "break_prop" ) || FStrEq( gameEvent->GetName(), "break_breakable" ) )
  324. {
  325. CheckAreasOverlappingBreakable collector( UTIL_EntityByIndex( gameEvent->GetInt( "entindex" ) ) );
  326. ForAllAreas( collector );
  327. }
  328. if ( FStrEq( gameEvent->GetName(), "round_start" ) || FStrEq( gameEvent->GetName(), "teamplay_round_start" ) )
  329. {
  330. OnRoundRestart();
  331. NavRoundRestart restart;
  332. ForAllAreas( restart );
  333. ForAllLadders( restart );
  334. }
  335. else if ( FStrEq( gameEvent->GetName(), "round_start_pre_entity" ) )
  336. {
  337. OnRoundRestartPreEntity();
  338. FOR_EACH_VEC( TheNavAreas, it )
  339. {
  340. CNavArea *area = TheNavAreas[ it ];
  341. area->OnRoundRestartPreEntity();
  342. }
  343. }
  344. }
  345. //--------------------------------------------------------------------------------------------------------------
  346. /**
  347. * Allocate the grid and define its extents
  348. */
  349. void CNavMesh::AllocateGrid( float minX, float maxX, float minY, float maxY )
  350. {
  351. m_grid.RemoveAll();
  352. m_minX = minX;
  353. m_minY = minY;
  354. m_gridSizeX = (int)((maxX - minX) / m_gridCellSize) + 1;
  355. m_gridSizeY = (int)((maxY - minY) / m_gridCellSize) + 1;
  356. m_grid.SetCount( m_gridSizeX * m_gridSizeY );
  357. }
  358. //--------------------------------------------------------------------------------------------------------------
  359. /**
  360. * Add an area to the mesh
  361. */
  362. void CNavMesh::AddNavArea( CNavArea *area )
  363. {
  364. if ( !m_grid.Count() )
  365. {
  366. // If we somehow have no grid (manually creating a nav area without loading or generating a mesh), don't crash
  367. AllocateGrid( 0, 0, 0, 0 );
  368. }
  369. // add to grid
  370. int loX = WorldToGridX( area->GetCorner( NORTH_WEST ).x );
  371. int loY = WorldToGridY( area->GetCorner( NORTH_WEST ).y );
  372. int hiX = WorldToGridX( area->GetCorner( SOUTH_EAST ).x );
  373. int hiY = WorldToGridY( area->GetCorner( SOUTH_EAST ).y );
  374. for( int y = loY; y <= hiY; ++y )
  375. {
  376. for( int x = loX; x <= hiX; ++x )
  377. {
  378. m_grid[ x + y*m_gridSizeX ].AddToTail( const_cast<CNavArea *>( area ) );
  379. }
  380. }
  381. // add to hash table
  382. int key = ComputeHashKey( area->GetID() );
  383. if (m_hashTable[key])
  384. {
  385. // add to head of list in this slot
  386. area->m_prevHash = NULL;
  387. area->m_nextHash = m_hashTable[key];
  388. m_hashTable[key]->m_prevHash = area;
  389. m_hashTable[key] = area;
  390. }
  391. else
  392. {
  393. // first entry in this slot
  394. m_hashTable[key] = area;
  395. area->m_nextHash = NULL;
  396. area->m_prevHash = NULL;
  397. }
  398. if ( area->GetAttributes() & NAV_MESH_TRANSIENT )
  399. {
  400. m_transientAreas.AddToTail( area );
  401. }
  402. ++m_areaCount;
  403. }
  404. //--------------------------------------------------------------------------------------------------------------
  405. /**
  406. * Remove an area from the mesh
  407. */
  408. void CNavMesh::RemoveNavArea( CNavArea *area )
  409. {
  410. // add to grid
  411. int loX = WorldToGridX( area->GetCorner( NORTH_WEST ).x );
  412. int loY = WorldToGridY( area->GetCorner( NORTH_WEST ).y );
  413. int hiX = WorldToGridX( area->GetCorner( SOUTH_EAST ).x );
  414. int hiY = WorldToGridY( area->GetCorner( SOUTH_EAST ).y );
  415. for( int y = loY; y <= hiY; ++y )
  416. {
  417. for( int x = loX; x <= hiX; ++x )
  418. {
  419. m_grid[ x + y*m_gridSizeX ].FindAndRemove( area );
  420. }
  421. }
  422. // remove from hash table
  423. int key = ComputeHashKey( area->GetID() );
  424. if (area->m_prevHash)
  425. {
  426. area->m_prevHash->m_nextHash = area->m_nextHash;
  427. }
  428. else
  429. {
  430. // area was at start of list
  431. m_hashTable[key] = area->m_nextHash;
  432. if (m_hashTable[key])
  433. {
  434. m_hashTable[key]->m_prevHash = NULL;
  435. }
  436. }
  437. if (area->m_nextHash)
  438. {
  439. area->m_nextHash->m_prevHash = area->m_prevHash;
  440. }
  441. if ( area->GetAttributes() & NAV_MESH_TRANSIENT )
  442. {
  443. BuildTransientAreaList();
  444. }
  445. m_avoidanceObstacleAreas.FindAndRemove( area );
  446. m_blockedAreas.FindAndRemove( area );
  447. --m_areaCount;
  448. }
  449. //--------------------------------------------------------------------------------------------------------------
  450. /**
  451. * Invoked when server loads a new map
  452. */
  453. void CNavMesh::OnServerActivate( void )
  454. {
  455. FOR_EACH_VEC( TheNavAreas, pit )
  456. {
  457. CNavArea *area = TheNavAreas[ pit ];
  458. area->OnServerActivate();
  459. }
  460. }
  461. //--------------------------------------------------------------------------------------------------------------
  462. //--------------------------------------------------------------------------------------------------------------
  463. /**
  464. * Test all areas for blocked status
  465. */
  466. void CNavMesh::TestAllAreasForBlockedStatus( void )
  467. {
  468. FOR_EACH_VEC( TheNavAreas, pit )
  469. {
  470. CNavArea *area = TheNavAreas[ pit ];
  471. area->UpdateBlocked( true );
  472. }
  473. }
  474. //--------------------------------------------------------------------------------------------------------------
  475. /**
  476. * Invoked when a game round restarts
  477. */
  478. void CNavMesh::OnRoundRestart( void )
  479. {
  480. m_updateBlockedAreasTimer.Start( 0.1 );
  481. FOR_EACH_VEC( TheNavAreas, pit )
  482. {
  483. CNavArea *area = TheNavAreas[ pit ];
  484. area->UpdateBlocked( true );
  485. }
  486. }
  487. //--------------------------------------------------------------------------------------------------------------
  488. /**
  489. * Invoked when a game round restarts, but before entities are deleted and recreated
  490. */
  491. void CNavMesh::OnRoundRestartPreEntity( void )
  492. {
  493. }
  494. //--------------------------------------------------------------------------------------------------------------
  495. void CNavMesh::BuildTransientAreaList( void )
  496. {
  497. m_transientAreas.RemoveAll();
  498. FOR_EACH_VEC( TheNavAreas, it )
  499. {
  500. CNavArea *area = TheNavAreas[ it ];
  501. if ( area->GetAttributes() & NAV_MESH_TRANSIENT )
  502. {
  503. m_transientAreas.AddToTail( area );
  504. }
  505. }
  506. }
  507. //--------------------------------------------------------------------------------------------------------------
  508. inline void CNavMesh::GridToWorld( int gridX, int gridY, Vector *pos ) const
  509. {
  510. gridX = clamp( gridX, 0, m_gridSizeX-1 );
  511. gridY = clamp( gridY, 0, m_gridSizeY-1 );
  512. pos->x = m_minX + gridX * m_gridCellSize;
  513. pos->y = m_minY + gridY * m_gridCellSize;
  514. }
  515. //--------------------------------------------------------------------------------------------------------------
  516. /**
  517. * Given a position, return the nav area that IsOverlapping and is *immediately* beneath it
  518. */
  519. CNavArea *CNavMesh::GetNavArea( const Vector &pos, float beneathLimit, bool checkLOS ) const
  520. {
  521. VPROF_BUDGET( "CNavMesh::GetNavArea", "NextBot" );
  522. if ( !m_grid.Count() )
  523. return NULL;
  524. // get list in cell that contains position
  525. int x = WorldToGridX( pos.x );
  526. int y = WorldToGridY( pos.y );
  527. NavAreaVector *areaVector = &m_grid[ x + y*m_gridSizeX ];
  528. // search cell list to find correct area
  529. CNavArea *use = NULL;
  530. float useZ = -99999999.9f;
  531. Vector testPos = pos + Vector( 0, 0, 5 );
  532. FOR_EACH_VEC( (*areaVector), it )
  533. {
  534. CNavArea *area = (*areaVector)[ it ];
  535. // check if position is within 2D boundaries of this area
  536. if (area->IsOverlapping( testPos ))
  537. {
  538. // project position onto area to get Z
  539. float z = area->GetZ( testPos );
  540. // if area is above us, skip it
  541. if (z > testPos.z)
  542. continue;
  543. // if area is too far below us, skip it
  544. if (z < pos.z - beneathLimit)
  545. continue;
  546. // if area is higher than the one we have, use this instead
  547. if (z > useZ)
  548. {
  549. bool isUsable = true;
  550. if ( checkLOS )
  551. {
  552. trace_t result;
  553. Vector groundPos( pos.x, pos.y, z + 0.1f );
  554. UTIL_TraceLine( pos, groundPos, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  555. if ( result.DidHit() && fabs( result.endpos.z - groundPos.z ) > StepHeight )
  556. {
  557. isUsable = false;
  558. }
  559. }
  560. if ( isUsable )
  561. {
  562. use = area;
  563. useZ = z;
  564. }
  565. }
  566. }
  567. }
  568. return use;
  569. }
  570. //----------------------------------------------------------------------------
  571. // Given a position, return the nav area that IsOverlapping and is *immediately* beneath it
  572. //----------------------------------------------------------------------------
  573. CNavArea *CNavMesh::GetNavArea( CBaseEntity *pEntity, int nFlags, float flBeneathLimit ) const
  574. {
  575. VPROF( "CNavMesh::GetNavArea [ent]" );
  576. if ( !m_grid.Count() )
  577. return NULL;
  578. Vector testPos = pEntity->GetAbsOrigin();
  579. float flStepHeight = 1e-3;
  580. CBaseCombatCharacter *pBCC = pEntity->MyCombatCharacterPointer();
  581. if ( pBCC )
  582. {
  583. // Check if we're still in the last area
  584. CNavArea *pLastNavArea = pBCC->GetLastKnownArea();
  585. if ( pLastNavArea && pLastNavArea->IsOverlapping( testPos ) )
  586. {
  587. float flZ = pLastNavArea->GetZ( testPos );
  588. if ( ( flZ <= testPos.z + StepHeight ) && ( flZ >= testPos.z - StepHeight ) )
  589. return pLastNavArea;
  590. }
  591. flStepHeight = StepHeight;
  592. }
  593. // get list in cell that contains position
  594. int x = WorldToGridX( testPos.x );
  595. int y = WorldToGridY( testPos.y );
  596. NavAreaVector *areaVector = &m_grid[ x + y*m_gridSizeX ];
  597. // search cell list to find correct area
  598. CNavArea *use = NULL;
  599. float useZ = -99999999.9f;
  600. bool bSkipBlockedAreas = ( ( nFlags & GETNAVAREA_ALLOW_BLOCKED_AREAS ) == 0 );
  601. FOR_EACH_VEC( (*areaVector), it )
  602. {
  603. CNavArea *pArea = (*areaVector)[ it ];
  604. // check if position is within 2D boundaries of this area
  605. if ( !pArea->IsOverlapping( testPos ) )
  606. continue;
  607. // don't consider blocked areas
  608. if ( bSkipBlockedAreas && pArea->IsBlocked( TEAM_ANY ) )
  609. continue;
  610. // project position onto area to get Z
  611. float z = pArea->GetZ( testPos );
  612. // if area is above us, skip it
  613. if ( z > testPos.z + flStepHeight )
  614. continue;
  615. // if area is too far below us, skip it
  616. if ( z < testPos.z - flBeneathLimit )
  617. continue;
  618. // if area is lower than the one we have, skip it
  619. if ( z <= useZ )
  620. continue;
  621. use = pArea;
  622. useZ = z;
  623. }
  624. // Check LOS if necessary
  625. if ( use && ( nFlags && GETNAVAREA_CHECK_LOS ) && ( useZ < testPos.z - flStepHeight ) )
  626. {
  627. // trace directly down to see if it's below us and unobstructed
  628. trace_t result;
  629. UTIL_TraceLine( testPos, Vector( testPos.x, testPos.y, useZ ), MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  630. if ( ( result.fraction != 1.0f ) && ( fabs( result.endpos.z - useZ ) > flStepHeight ) )
  631. return NULL;
  632. }
  633. return use;
  634. }
  635. //--------------------------------------------------------------------------------------------------------------
  636. /**
  637. * Given a position in the world, return the nav area that is closest
  638. * and at the same height, or beneath it.
  639. * Used to find initial area if we start off of the mesh.
  640. * @todo Make sure area is not on the other side of the wall from goal.
  641. */
  642. CNavArea *CNavMesh::GetNearestNavArea( const Vector &pos, bool anyZ, float maxDist, bool checkLOS, bool checkGround ) const
  643. {
  644. SNPROF("GetNearestNavArea");
  645. VPROF_BUDGET( "CNavMesh::GetNearestNavArea", "NextBot" );
  646. if ( !m_grid.Count() )
  647. return NULL;
  648. CNavArea *close = NULL;
  649. float closeDistSq = maxDist * maxDist;
  650. // quick check
  651. if ( !checkLOS && !checkGround )
  652. {
  653. close = GetNavArea( pos );
  654. if ( close )
  655. {
  656. return close;
  657. }
  658. }
  659. // ensure source position is well behaved
  660. Vector source;
  661. source.x = pos.x;
  662. source.y = pos.y;
  663. if ( GetGroundHeight( pos, &source.z ) == false )
  664. {
  665. if ( !checkGround )
  666. {
  667. source.z = pos.z;
  668. }
  669. else
  670. {
  671. return NULL;
  672. }
  673. }
  674. source.z += HalfHumanHeight;
  675. // find closest nav area
  676. // use a unique marker for this method, so it can be used within a SearchSurroundingArea() call
  677. static unsigned int searchMarker = RandomInt(0, 1024*1024 );
  678. ++searchMarker;
  679. if ( searchMarker == 0 )
  680. {
  681. ++searchMarker;
  682. }
  683. // get list in cell that contains position
  684. int originX = WorldToGridX( pos.x );
  685. int originY = WorldToGridY( pos.y );
  686. int shiftLimit = ceil(maxDist / m_gridCellSize);
  687. //
  688. // Search in increasing rings out from origin, starting with cell
  689. // that contains the given position.
  690. // Once we find a close area, we must check one more step out in
  691. // case our position is just against the edge of the cell boundary
  692. // and an area in an adjacent cell is actually closer.
  693. //
  694. for( int shift=0; shift <= shiftLimit; ++shift )
  695. {
  696. for( int x = originX - shift; x <= originX + shift; ++x )
  697. {
  698. if ( x < 0 || x >= m_gridSizeX )
  699. continue;
  700. for( int y = originY - shift; y <= originY + shift; ++y )
  701. {
  702. if ( y < 0 || y >= m_gridSizeY )
  703. continue;
  704. // only check these areas if we're on the outer edge of our spiral
  705. if ( x > originX - shift &&
  706. x < originX + shift &&
  707. y > originY - shift &&
  708. y < originY + shift )
  709. continue;
  710. NavAreaVector *areaVector = &m_grid[ x + y*m_gridSizeX ];
  711. // find closest area in this cell
  712. FOR_EACH_VEC( (*areaVector), it )
  713. {
  714. CNavArea *area = (*areaVector)[ it ];
  715. // skip if we've already visited this area
  716. if ( area->m_nearNavSearchMarker == searchMarker )
  717. continue;
  718. // don't consider blocked areas
  719. if ( area->IsBlocked( TEAM_ANY ) )
  720. continue;
  721. // mark as visited
  722. area->m_nearNavSearchMarker = searchMarker;
  723. Vector areaPos;
  724. area->GetClosestPointOnArea( source, &areaPos );
  725. // TERROR: Using the original pos for distance calculations. Since it's a pure 3D distance,
  726. // with no Z restrictions or LOS checks, this should work for passing in bot foot positions.
  727. // This needs to be ported back to CS:S.
  728. float distSq = ( areaPos - pos ).LengthSqr();
  729. // keep the closest area
  730. if ( distSq >= closeDistSq )
  731. continue;
  732. // check LOS to area
  733. // REMOVED: If we do this for !anyZ, it's likely we wont have LOS and will enumerate every area in the mesh
  734. // It is still good to do this in some isolated cases, however
  735. if ( checkLOS )
  736. {
  737. trace_t result;
  738. // make sure 'pos' is not embedded in the world
  739. Vector safePos;
  740. UTIL_TraceLine( pos, pos + Vector( 0, 0, StepHeight ), MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  741. if ( result.startsolid )
  742. {
  743. // it was embedded - move it out
  744. safePos = result.endpos + Vector( 0, 0, 1.0f );
  745. }
  746. else
  747. {
  748. safePos = pos;
  749. }
  750. // Don't bother tracing from the nav area up to safePos.z if it's within StepHeight of the area, since areas can be embedded in the ground a bit
  751. float heightDelta = fabs(areaPos.z - safePos.z);
  752. if ( heightDelta > StepHeight )
  753. {
  754. // trace to the height of the original point
  755. UTIL_TraceLine( areaPos + Vector( 0, 0, StepHeight ), Vector( areaPos.x, areaPos.y, safePos.z ), MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  756. if ( result.fraction != 1.0f )
  757. {
  758. continue;
  759. }
  760. }
  761. // trace to the original point's height above the area
  762. UTIL_TraceLine( safePos, Vector( areaPos.x, areaPos.y, safePos.z + StepHeight ), MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  763. if ( result.fraction != 1.0f )
  764. {
  765. continue;
  766. }
  767. }
  768. closeDistSq = distSq;
  769. close = area;
  770. // look one more step outwards
  771. shiftLimit = shift+1;
  772. }
  773. }
  774. }
  775. }
  776. return close;
  777. }
  778. //----------------------------------------------------------------------------
  779. // Given a position in the world, return the nav area that is closest
  780. // and at the same height, or beneath it.
  781. // Used to find initial area if we start off of the mesh.
  782. // @todo Make sure area is not on the other side of the wall from goal.
  783. //----------------------------------------------------------------------------
  784. CNavArea *CNavMesh::GetNearestNavArea( CBaseEntity *pEntity, int nFlags, float maxDist ) const
  785. {
  786. VPROF( "CNavMesh::GetNearestNavArea [ent]" );
  787. SNPROF("CNavMesh::GetNearestNavArea [ent]");
  788. if ( !m_grid.Count() )
  789. return NULL;
  790. // quick check
  791. CNavArea *pClose = GetNavArea( pEntity, nFlags );
  792. if ( pClose )
  793. return pClose;
  794. bool bCheckLOS = ( nFlags & GETNAVAREA_CHECK_LOS ) != 0;
  795. bool bCheckGround = ( nFlags & GETNAVAREA_CHECK_GROUND ) != 0;
  796. return GetNearestNavArea( pEntity->GetAbsOrigin(), false, maxDist, bCheckLOS, bCheckGround );
  797. }
  798. //--------------------------------------------------------------------------------------------------------------
  799. /**
  800. * Given an ID, return the associated area
  801. */
  802. CNavArea *CNavMesh::GetNavAreaByID( unsigned int id ) const
  803. {
  804. if (id == 0)
  805. return NULL;
  806. int key = ComputeHashKey( id );
  807. for( CNavArea *area = m_hashTable[key]; area; area = area->m_nextHash )
  808. {
  809. if (area->GetID() == id)
  810. {
  811. return area;
  812. }
  813. }
  814. return NULL;
  815. }
  816. //--------------------------------------------------------------------------------------------------------------
  817. /**
  818. * Given an ID, return the associated ladder
  819. */
  820. CNavLadder *CNavMesh::GetLadderByID( unsigned int id ) const
  821. {
  822. if (id == 0)
  823. return NULL;
  824. for ( int i=0; i<m_ladders.Count(); ++i )
  825. {
  826. CNavLadder *ladder = m_ladders[i];
  827. if ( ladder->GetID() == id )
  828. {
  829. return ladder;
  830. }
  831. }
  832. return NULL;
  833. }
  834. //--------------------------------------------------------------------------------------------------------------
  835. /**
  836. * Return radio chatter place for given coordinate
  837. */
  838. unsigned int CNavMesh::GetPlace( const Vector &pos ) const
  839. {
  840. CNavArea *area = GetNearestNavArea( pos, true );
  841. if (area)
  842. {
  843. return area->GetPlace();
  844. }
  845. return UNDEFINED_PLACE;
  846. }
  847. //--------------------------------------------------------------------------------------------------------------
  848. /**
  849. * Load the place names from a file
  850. */
  851. void CNavMesh::LoadPlaceDatabase( void )
  852. {
  853. m_placeCount = 0;
  854. #ifdef TERROR
  855. // TODO: LoadPlaceDatabase happens during the constructor, so we can't override it!
  856. // Population.txt holds all the info we need for place names in Left4Dead, so let's not
  857. // make Phil edit yet another text file.
  858. KeyValues *populationData = new KeyValues( "population" );
  859. if ( populationData->LoadFromFile( filesystem, "scripts/population.txt" ) )
  860. {
  861. CUtlVector< char * > placeNames;
  862. for ( KeyValues *key = populationData->GetFirstTrueSubKey(); key != NULL; key = key->GetNextTrueSubKey() )
  863. {
  864. if ( FStrEq( key->GetName(), "default" ) ) // default population is the undefined place
  865. continue;
  866. placeNames.AddToTail( CloneString( key->GetName() ) );
  867. }
  868. m_placeCount = placeNames.Count();
  869. // allocate place name array
  870. m_placeName = new char * [ m_placeCount ];
  871. for ( unsigned int i=0; i<m_placeCount; ++i )
  872. {
  873. m_placeName[i] = placeNames[i];
  874. }
  875. populationData->deleteThis();
  876. return;
  877. }
  878. populationData->deleteThis();
  879. #endif
  880. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  881. filesystem->ReadFile("NavPlace.db", "GAME", buf);
  882. if (!buf.Size())
  883. return;
  884. const int maxNameLength = 128;
  885. char buffer[ maxNameLength ];
  886. CUtlVector<char*> placeNames;
  887. // count the number of places
  888. while( true )
  889. {
  890. buf.GetLine( buffer, maxNameLength );
  891. if ( !buf.IsValid() )
  892. break;
  893. int len = V_strlen( buffer );
  894. if ( len >= 2 )
  895. {
  896. if ( buffer[len-1] == '\n' || buffer[len-1] == '\r' )
  897. buffer[len-1] = 0;
  898. if ( buffer[len-2] == '\r' )
  899. buffer[len-2] = 0;
  900. char *pName = new char[ len + 1 ];
  901. V_strncpy( pName, buffer, len+1 );
  902. placeNames.AddToTail( pName );
  903. }
  904. }
  905. // allocate place name array
  906. m_placeCount = placeNames.Count();
  907. m_placeName = new char * [ m_placeCount ];
  908. for ( unsigned int i=0; i < m_placeCount; i++ )
  909. {
  910. m_placeName[i] = placeNames[i];
  911. }
  912. }
  913. //--------------------------------------------------------------------------------------------------------------
  914. /**
  915. * Given a place, return its name.
  916. * Reserve zero as invalid.
  917. */
  918. const char *CNavMesh::PlaceToName( Place place ) const
  919. {
  920. if (place >= 1 && place <= m_placeCount)
  921. return m_placeName[ (int)place - 1 ];
  922. return NULL;
  923. }
  924. //--------------------------------------------------------------------------------------------------------------
  925. /**
  926. * Given a place name, return a place ID or zero if no place is defined
  927. * Reserve zero as invalid.
  928. */
  929. Place CNavMesh::NameToPlace( const char *name ) const
  930. {
  931. for( unsigned int i=0; i<m_placeCount; ++i )
  932. {
  933. if (FStrEq( m_placeName[i], name ))
  934. return i+1;
  935. }
  936. return UNDEFINED_PLACE;
  937. }
  938. //--------------------------------------------------------------------------------------------------------------
  939. /**
  940. * Given the first part of a place name, return a place ID or zero if no place is defined, or the partial match is ambiguous
  941. */
  942. Place CNavMesh::PartialNameToPlace( const char *name ) const
  943. {
  944. Place found = UNDEFINED_PLACE;
  945. bool isAmbiguous = false;
  946. for( unsigned int i=0; i<m_placeCount; ++i )
  947. {
  948. if (!strnicmp( m_placeName[i], name, strlen( name ) ))
  949. {
  950. // check for exact match in case of subsets of other strings
  951. if (!stricmp( m_placeName[i], name ))
  952. {
  953. found = NameToPlace( m_placeName[i] );
  954. isAmbiguous = false;
  955. break;
  956. }
  957. if (found != UNDEFINED_PLACE)
  958. {
  959. isAmbiguous = true;
  960. }
  961. else
  962. {
  963. found = NameToPlace( m_placeName[i] );
  964. }
  965. }
  966. }
  967. if (isAmbiguous)
  968. return UNDEFINED_PLACE;
  969. return found;
  970. }
  971. //--------------------------------------------------------------------------------------------------------------
  972. /**
  973. * Given a partial place name, fill in possible place names for ConCommand autocomplete
  974. */
  975. int CNavMesh::PlaceNameAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  976. {
  977. int numMatches = 0;
  978. partial += Q_strlen( "nav_use_place " );
  979. int partialLength = Q_strlen( partial );
  980. for( unsigned int i=0; i<m_placeCount; ++i )
  981. {
  982. if ( !Q_strnicmp( m_placeName[i], partial, partialLength ) )
  983. {
  984. // Add the place name to the autocomplete array
  985. Q_snprintf( commands[ numMatches++ ], COMMAND_COMPLETION_ITEM_LENGTH, "nav_use_place %s", m_placeName[i] );
  986. // Make sure we don't try to return too many place names
  987. if ( numMatches == COMMAND_COMPLETION_MAXITEMS )
  988. return numMatches;
  989. }
  990. }
  991. return numMatches;
  992. }
  993. //--------------------------------------------------------------------------------------------------------------
  994. typedef const char * SortStringType;
  995. int StringSort (const SortStringType *s1, const SortStringType *s2)
  996. {
  997. return strcmp( *s1, *s2 );
  998. }
  999. //--------------------------------------------------------------------------------------------------------------
  1000. /**
  1001. * Output a list of names to the console
  1002. */
  1003. void CNavMesh::PrintAllPlaces( void ) const
  1004. {
  1005. if (m_placeCount == 0)
  1006. {
  1007. Msg( "There are no entries in the Place database.\n" );
  1008. return;
  1009. }
  1010. unsigned int i;
  1011. CUtlVector< SortStringType > placeNames;
  1012. for ( i=0; i<m_placeCount; ++i )
  1013. {
  1014. placeNames.AddToTail( m_placeName[i] );
  1015. }
  1016. placeNames.Sort( StringSort );
  1017. for( i=0; i<(unsigned int)placeNames.Count(); ++i )
  1018. {
  1019. if (NameToPlace( placeNames[i] ) == GetNavPlace())
  1020. Msg( "--> %-26s", placeNames[i] );
  1021. else
  1022. Msg( "%-30s", placeNames[i] );
  1023. if ((i+1) % 3 == 0)
  1024. Msg( "\n" );
  1025. }
  1026. Msg( "\n" );
  1027. }
  1028. class CTraceFilterGroundEntities : public CTraceFilterWalkableEntities
  1029. {
  1030. typedef CTraceFilterWalkableEntities BaseClass;
  1031. public:
  1032. CTraceFilterGroundEntities( const IHandleEntity *passentity, int collisionGroup, unsigned int flags )
  1033. : BaseClass( passentity, collisionGroup, flags )
  1034. {
  1035. }
  1036. virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  1037. {
  1038. CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  1039. if ( FClassnameIs( pEntity, "prop_door" ) ||
  1040. FClassnameIs( pEntity, "prop_door_rotating" ) ||
  1041. FClassnameIs( pEntity, "func_breakable" ) )
  1042. {
  1043. return false;
  1044. }
  1045. return BaseClass::ShouldHitEntity( pServerEntity, contentsMask );
  1046. }
  1047. };
  1048. bool CNavMesh::GetGroundHeight( const Vector &pos, float *height, Vector *normal ) const
  1049. {
  1050. Vector from( pos.x, pos.y, pos.z + HalfHumanHeight + 1e-3 );
  1051. return GetSimpleGroundHeight( from, height, normal );
  1052. }
  1053. //--------------------------------------------------------------------------------------------------------------
  1054. /**
  1055. * Return the "simple" ground height below this point in "height".
  1056. * This function is much faster, but less tolerant. Make sure the give position is "well behaved".
  1057. * Return false if position is invalid (outside of map, in a solid area, etc).
  1058. */
  1059. bool CNavMesh::GetSimpleGroundHeight( const Vector &pos, float *height, Vector *normal ) const
  1060. {
  1061. Vector to;
  1062. to.x = pos.x;
  1063. to.y = pos.y;
  1064. to.z = pos.z - 9999.9f;
  1065. trace_t result;
  1066. UTIL_TraceLine( pos, to, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  1067. if (result.startsolid)
  1068. return false;
  1069. *height = result.endpos.z;
  1070. if (normal)
  1071. *normal = result.plane.normal;
  1072. return true;
  1073. }
  1074. //--------------------------------------------------------------------------------------------------------------
  1075. /**
  1076. * Show danger levels for debugging
  1077. */
  1078. void CNavMesh::DrawDanger( void ) const
  1079. {
  1080. FOR_EACH_VEC( TheNavAreas, it )
  1081. {
  1082. CNavArea *area = TheNavAreas[ it ];
  1083. Vector center = area->GetCenter();
  1084. Vector top;
  1085. center.z = area->GetZ( center );
  1086. float danger = area->GetDanger( 0 );
  1087. if (danger > 0.1f)
  1088. {
  1089. top.x = center.x;
  1090. top.y = center.y;
  1091. top.z = center.z + 10.0f * danger;
  1092. DrawLine( center, top, 3, 255, 0, 0 );
  1093. }
  1094. danger = area->GetDanger( 1 );
  1095. if (danger > 0.1f)
  1096. {
  1097. top.x = center.x;
  1098. top.y = center.y;
  1099. top.z = center.z + 10.0f * danger;
  1100. DrawLine( center, top, 3, 0, 0, 255 );
  1101. }
  1102. }
  1103. }
  1104. //--------------------------------------------------------------------------------------------------------------
  1105. /**
  1106. * Show current player counts for debugging.
  1107. * NOTE: Assumes two teams.
  1108. */
  1109. void CNavMesh::DrawPlayerCounts( void ) const
  1110. {
  1111. CFmtStr msg;
  1112. FOR_EACH_VEC( TheNavAreas, it )
  1113. {
  1114. CNavArea *area = TheNavAreas[ it ];
  1115. if (area->GetPlayerCount() > 0)
  1116. {
  1117. NDebugOverlay::Text( area->GetCenter(), msg.sprintf( "%d (%d/%d)", area->GetPlayerCount(), area->GetPlayerCount(1), area->GetPlayerCount(2) ), false, 0.1f );
  1118. }
  1119. }
  1120. }
  1121. //--------------------------------------------------------------------------------------------------------------
  1122. /**
  1123. * Increase the danger of nav areas containing and near the given position
  1124. */
  1125. void CNavMesh::IncreaseDangerNearby( int teamID, float amount, CNavArea *startArea, const Vector &pos, float maxRadius, float dangerLimit )
  1126. {
  1127. if (startArea == NULL)
  1128. return;
  1129. CNavArea::MakeNewMarker();
  1130. CNavArea::ClearSearchLists();
  1131. startArea->AddToOpenList();
  1132. startArea->SetTotalCost( 0.0f );
  1133. startArea->Mark();
  1134. float finalDanger = amount;
  1135. if ( dangerLimit > 0.0f && startArea->GetDanger( teamID ) + finalDanger > dangerLimit )
  1136. {
  1137. // clamp danger to given limit
  1138. finalDanger = dangerLimit - startArea->GetDanger( teamID );
  1139. }
  1140. startArea->IncreaseDanger( teamID, finalDanger );
  1141. while( !CNavArea::IsOpenListEmpty() )
  1142. {
  1143. // get next area to check
  1144. CNavArea *area = CNavArea::PopOpenList();
  1145. // explore adjacent areas
  1146. for( int dir=0; dir<NUM_DIRECTIONS; ++dir )
  1147. {
  1148. int count = area->GetAdjacentCount( (NavDirType)dir );
  1149. for( int i=0; i<count; ++i )
  1150. {
  1151. CNavArea *adjArea = area->GetAdjacentArea( (NavDirType)dir, i );
  1152. if (!adjArea->IsMarked())
  1153. {
  1154. // compute distance from danger source
  1155. float cost = (adjArea->GetCenter() - pos).Length();
  1156. if (cost <= maxRadius)
  1157. {
  1158. adjArea->AddToOpenList();
  1159. adjArea->SetTotalCost( cost );
  1160. adjArea->Mark();
  1161. finalDanger = amount * cost/maxRadius;
  1162. if ( dangerLimit > 0.0f && adjArea->GetDanger( teamID ) + finalDanger > dangerLimit )
  1163. {
  1164. // clamp danger to given limit
  1165. finalDanger = dangerLimit - adjArea->GetDanger( teamID );
  1166. }
  1167. adjArea->IncreaseDanger( teamID, finalDanger );
  1168. }
  1169. }
  1170. }
  1171. }
  1172. }
  1173. }
  1174. //--------------------------------------------------------------------------------------------------------------
  1175. void CommandNavRemoveJumpAreas( void )
  1176. {
  1177. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1178. return;
  1179. TheNavMesh->CommandNavRemoveJumpAreas();
  1180. }
  1181. static ConCommand nav_remove_jump_areas( "nav_remove_jump_areas", CommandNavRemoveJumpAreas, "Removes legacy jump areas, replacing them with connections.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1182. //--------------------------------------------------------------------------------------------------------------
  1183. void CommandNavDelete( void )
  1184. {
  1185. if ( !UTIL_IsCommandIssuedByServerAdmin() || !nav_edit.GetBool() )
  1186. return;
  1187. TheNavMesh->CommandNavDelete();
  1188. }
  1189. static ConCommand nav_delete( "nav_delete", CommandNavDelete, "Deletes the currently highlighted Area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1190. //--------------------------------------------------------------------------------------------------------------
  1191. void CommandNavDeleteMarked( void )
  1192. {
  1193. if ( !UTIL_IsCommandIssuedByServerAdmin() || !nav_edit.GetBool() )
  1194. return;
  1195. TheNavMesh->CommandNavDeleteMarked();
  1196. }
  1197. static ConCommand nav_delete_marked( "nav_delete_marked", CommandNavDeleteMarked, "Deletes the currently marked Area (if any).", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1198. //--------------------------------------------------------------------------------------------------------------
  1199. CON_COMMAND_F( nav_flood_select, "Selects the current Area and all Areas connected to it, recursively. To clear a selection, use this command again.", FCVAR_GAMEDLL | FCVAR_CHEAT )
  1200. {
  1201. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1202. return;
  1203. TheNavMesh->CommandNavFloodSelect( args );
  1204. }
  1205. //--------------------------------------------------------------------------------------------------------------
  1206. void CommandNavToggleSelectedSet( void )
  1207. {
  1208. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1209. return;
  1210. TheNavMesh->CommandNavToggleSelectedSet();
  1211. }
  1212. static ConCommand nav_toggle_selected_set( "nav_toggle_selected_set", CommandNavToggleSelectedSet, "Toggles all areas into/out of the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1213. //--------------------------------------------------------------------------------------------------------------
  1214. void CommandNavStoreSelectedSet( void )
  1215. {
  1216. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1217. return;
  1218. TheNavMesh->CommandNavStoreSelectedSet();
  1219. }
  1220. static ConCommand nav_store_selected_set( "nav_store_selected_set", CommandNavStoreSelectedSet, "Stores the current selected set for later retrieval.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1221. //--------------------------------------------------------------------------------------------------------------
  1222. void CommandNavRecallSelectedSet( void )
  1223. {
  1224. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1225. return;
  1226. TheNavMesh->CommandNavRecallSelectedSet();
  1227. }
  1228. static ConCommand nav_recall_selected_set( "nav_recall_selected_set", CommandNavRecallSelectedSet, "Re-selects the stored selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1229. //--------------------------------------------------------------------------------------------------------------
  1230. void CommandNavAddToSelectedSet( void )
  1231. {
  1232. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1233. return;
  1234. TheNavMesh->CommandNavAddToSelectedSet();
  1235. }
  1236. static ConCommand nav_add_to_selected_set( "nav_add_to_selected_set", CommandNavAddToSelectedSet, "Add current area to the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1237. //--------------------------------------------------------------------------------------------------------------
  1238. CON_COMMAND_F( nav_add_to_selected_set_by_id, "Add specified area id to the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT )
  1239. {
  1240. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1241. return;
  1242. TheNavMesh->CommandNavAddToSelectedSetByID( args );
  1243. }
  1244. //--------------------------------------------------------------------------------------------------------------
  1245. void CommandNavRemoveFromSelectedSet( void )
  1246. {
  1247. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1248. return;
  1249. TheNavMesh->CommandNavRemoveFromSelectedSet();
  1250. }
  1251. static ConCommand nav_remove_from_selected_set( "nav_remove_from_selected_set", CommandNavRemoveFromSelectedSet, "Remove current area from the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1252. //--------------------------------------------------------------------------------------------------------------
  1253. void CommandNavToggleInSelectedSet( void )
  1254. {
  1255. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1256. return;
  1257. TheNavMesh->CommandNavToggleInSelectedSet();
  1258. }
  1259. static ConCommand nav_toggle_in_selected_set( "nav_toggle_in_selected_set", CommandNavToggleInSelectedSet, "Remove current area from the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1260. //--------------------------------------------------------------------------------------------------------------
  1261. void CommandNavClearSelectedSet( void )
  1262. {
  1263. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1264. return;
  1265. TheNavMesh->CommandNavClearSelectedSet();
  1266. }
  1267. static ConCommand nav_clear_selected_set( "nav_clear_selected_set", CommandNavClearSelectedSet, "Clear the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1268. //--------------------------------------------------------------------------------------------------------------
  1269. void CommandNavBeginSelecting( void )
  1270. {
  1271. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1272. return;
  1273. TheNavMesh->CommandNavBeginSelecting();
  1274. }
  1275. static ConCommand nav_begin_selecting( "nav_begin_selecting", CommandNavBeginSelecting, "Start continuously adding to the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1276. //--------------------------------------------------------------------------------------------------------------
  1277. void CommandNavEndSelecting( void )
  1278. {
  1279. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1280. return;
  1281. TheNavMesh->CommandNavEndSelecting();
  1282. }
  1283. static ConCommand nav_end_selecting( "nav_end_selecting", CommandNavEndSelecting, "Stop continuously adding to the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1284. //--------------------------------------------------------------------------------------------------------------
  1285. void CommandNavBeginDragSelecting( void )
  1286. {
  1287. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1288. return;
  1289. TheNavMesh->CommandNavBeginDragSelecting();
  1290. }
  1291. static ConCommand nav_begin_drag_selecting( "nav_begin_drag_selecting", CommandNavBeginDragSelecting, "Start dragging a selection area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1292. //--------------------------------------------------------------------------------------------------------------
  1293. void CommandNavEndDragSelecting( void )
  1294. {
  1295. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1296. return;
  1297. TheNavMesh->CommandNavEndDragSelecting();
  1298. }
  1299. static ConCommand nav_end_drag_selecting( "nav_end_drag_selecting", CommandNavEndDragSelecting, "Stop dragging a selection area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1300. //--------------------------------------------------------------------------------------------------------------
  1301. void CommandNavBeginDragDeselecting( void )
  1302. {
  1303. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1304. return;
  1305. TheNavMesh->CommandNavBeginDragDeselecting();
  1306. }
  1307. static ConCommand nav_begin_drag_deselecting( "nav_begin_drag_deselecting", CommandNavBeginDragDeselecting, "Start dragging a selection area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1308. //--------------------------------------------------------------------------------------------------------------
  1309. void CommandNavEndDragDeselecting( void )
  1310. {
  1311. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1312. return;
  1313. TheNavMesh->CommandNavEndDragDeselecting();
  1314. }
  1315. static ConCommand nav_end_drag_deselecting( "nav_end_drag_deselecting", CommandNavEndDragDeselecting, "Stop dragging a selection area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1316. //--------------------------------------------------------------------------------------------------------------
  1317. void CommandNavRaiseDragVolumeMax( void )
  1318. {
  1319. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1320. return;
  1321. TheNavMesh->CommandNavRaiseDragVolumeMax();
  1322. }
  1323. static ConCommand nav_raise_drag_volume_max( "nav_raise_drag_volume_max", CommandNavRaiseDragVolumeMax, "Raise the top of the drag select volume.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1324. //--------------------------------------------------------------------------------------------------------------
  1325. void CommandNavLowerDragVolumeMax( void )
  1326. {
  1327. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1328. return;
  1329. TheNavMesh->CommandNavLowerDragVolumeMax();
  1330. }
  1331. static ConCommand nav_lower_drag_volume_max( "nav_lower_drag_volume_max", CommandNavLowerDragVolumeMax, "Lower the top of the drag select volume.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1332. //--------------------------------------------------------------------------------------------------------------
  1333. void CommandNavRaiseDragVolumeMin( void )
  1334. {
  1335. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1336. return;
  1337. TheNavMesh->CommandNavRaiseDragVolumeMin();
  1338. }
  1339. static ConCommand nav_raise_drag_volume_min( "nav_raise_drag_volume_min", CommandNavRaiseDragVolumeMin, "Raise the bottom of the drag select volume.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1340. //--------------------------------------------------------------------------------------------------------------
  1341. void CommandNavLowerDragVolumeMin( void )
  1342. {
  1343. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1344. return;
  1345. TheNavMesh->CommandNavLowerDragVolumeMin();
  1346. }
  1347. static ConCommand nav_lower_drag_volume_min( "nav_lower_drag_volume_min", CommandNavLowerDragVolumeMin, "Lower the bottom of the drag select volume.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1348. //--------------------------------------------------------------------------------------------------------------
  1349. void CommandNavToggleSelecting( void )
  1350. {
  1351. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1352. return;
  1353. TheNavMesh->CommandNavToggleSelecting();
  1354. }
  1355. static ConCommand nav_toggle_selecting( "nav_toggle_selecting", CommandNavToggleSelecting, "Start or stop continuously adding to the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1356. //--------------------------------------------------------------------------------------------------------------
  1357. void CommandNavBeginDeselecting( void )
  1358. {
  1359. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1360. return;
  1361. TheNavMesh->CommandNavBeginDeselecting();
  1362. }
  1363. static ConCommand nav_begin_deselecting( "nav_begin_deselecting", CommandNavBeginDeselecting, "Start continuously removing from the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1364. //--------------------------------------------------------------------------------------------------------------
  1365. void CommandNavEndDeselecting( void )
  1366. {
  1367. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1368. return;
  1369. TheNavMesh->CommandNavEndDeselecting();
  1370. }
  1371. static ConCommand nav_end_deselecting( "nav_end_deselecting", CommandNavEndDeselecting, "Stop continuously removing from the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1372. //--------------------------------------------------------------------------------------------------------------
  1373. void CommandNavToggleDeselecting( void )
  1374. {
  1375. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1376. return;
  1377. TheNavMesh->CommandNavToggleDeselecting();
  1378. }
  1379. static ConCommand nav_toggle_deselecting( "nav_toggle_deselecting", CommandNavToggleDeselecting, "Start or stop continuously removing from the selected set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1380. //--------------------------------------------------------------------------------------------------------------
  1381. CON_COMMAND_F( nav_select_half_space, "Selects any areas that intersect the given half-space.", FCVAR_GAMEDLL | FCVAR_CHEAT )
  1382. {
  1383. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1384. return;
  1385. TheNavMesh->CommandNavSelectHalfSpace( args );
  1386. }
  1387. //--------------------------------------------------------------------------------------------------------------
  1388. void CommandNavBeginShiftXY( void )
  1389. {
  1390. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1391. return;
  1392. TheNavMesh->CommandNavBeginShiftXY();
  1393. }
  1394. static ConCommand nav_begin_shift_xy( "nav_begin_shift_xy", CommandNavBeginShiftXY, "Begin shifting the Selected Set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1395. //--------------------------------------------------------------------------------------------------------------
  1396. void CommandNavEndShiftXY( void )
  1397. {
  1398. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1399. return;
  1400. TheNavMesh->CommandNavEndShiftXY();
  1401. }
  1402. static ConCommand nav_end_shift_xy( "nav_end_shift_xy", CommandNavEndShiftXY, "Finish shifting the Selected Set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1403. //--------------------------------------------------------------------------------------------------------------
  1404. void CommandNavSelectInvalidAreas( void )
  1405. {
  1406. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1407. return;
  1408. TheNavMesh->CommandNavSelectInvalidAreas();
  1409. }
  1410. static ConCommand nav_select_invalid_areas( "nav_select_invalid_areas", CommandNavSelectInvalidAreas, "Adds all invalid areas to the Selected Set.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1411. //--------------------------------------------------------------------------------------------------------------
  1412. CON_COMMAND_F( nav_select_blocked_areas, "Adds all blocked areas to the selected set", FCVAR_CHEAT )
  1413. {
  1414. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1415. return;
  1416. TheNavMesh->CommandNavSelectBlockedAreas();
  1417. }
  1418. //--------------------------------------------------------------------------------------------------------------
  1419. CON_COMMAND_F( nav_select_obstructed_areas, "Adds all obstructed areas to the selected set", FCVAR_CHEAT )
  1420. {
  1421. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1422. return;
  1423. TheNavMesh->CommandNavSelectObstructedAreas();
  1424. }
  1425. //--------------------------------------------------------------------------------------------------------------
  1426. CON_COMMAND_F( nav_select_damaging_areas, "Adds all damaging areas to the selected set", FCVAR_CHEAT )
  1427. {
  1428. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1429. return;
  1430. TheNavMesh->CommandNavSelectDamagingAreas();
  1431. }
  1432. //--------------------------------------------------------------------------------------------------------------
  1433. CON_COMMAND_F( nav_select_stairs, "Adds all stairway areas to the selected set", FCVAR_CHEAT )
  1434. {
  1435. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1436. return;
  1437. TheNavMesh->CommandNavSelectStairs();
  1438. }
  1439. //--------------------------------------------------------------------------------------------------------------
  1440. void CommandNavSplit( void )
  1441. {
  1442. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1443. return;
  1444. TheNavMesh->CommandNavSplit();
  1445. }
  1446. static ConCommand nav_split( "nav_split", CommandNavSplit, "To split an Area into two, align the split line using your cursor and invoke the split command.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1447. //--------------------------------------------------------------------------------------------------------------
  1448. void CommandNavMakeSniperSpots( void )
  1449. {
  1450. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1451. return;
  1452. TheNavMesh->CommandNavMakeSniperSpots();
  1453. }
  1454. static ConCommand nav_make_sniper_spots( "nav_make_sniper_spots", CommandNavMakeSniperSpots, "Chops the marked area into disconnected sub-areas suitable for sniper spots.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1455. //--------------------------------------------------------------------------------------------------------------
  1456. void CommandNavMerge( void )
  1457. {
  1458. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1459. return;
  1460. TheNavMesh->CommandNavMerge();
  1461. }
  1462. static ConCommand nav_merge( "nav_merge", CommandNavMerge, "To merge two Areas into one, mark the first Area, highlight the second by pointing your cursor at it, and invoke the merge command.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1463. //--------------------------------------------------------------------------------------------------------------
  1464. void CommandNavMark( const CCommand &args )
  1465. {
  1466. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1467. return;
  1468. TheNavMesh->CommandNavMark( args );
  1469. }
  1470. static ConCommand nav_mark( "nav_mark", CommandNavMark, "Marks the Area or Ladder under the cursor for manipulation by subsequent editing commands.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1471. //--------------------------------------------------------------------------------------------------------------
  1472. void CommandNavUnmark( void )
  1473. {
  1474. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1475. return;
  1476. TheNavMesh->CommandNavUnmark();
  1477. }
  1478. static ConCommand nav_unmark( "nav_unmark", CommandNavUnmark, "Clears the marked Area or Ladder.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1479. //--------------------------------------------------------------------------------------------------------------
  1480. void CommandNavBeginArea( void )
  1481. {
  1482. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1483. return;
  1484. TheNavMesh->CommandNavBeginArea();
  1485. }
  1486. static ConCommand nav_begin_area( "nav_begin_area", CommandNavBeginArea, "Defines a corner of a new Area or Ladder. To complete the Area or Ladder, drag the opposite corner to the desired location and issue a 'nav_end_area' command.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1487. //--------------------------------------------------------------------------------------------------------------
  1488. void CommandNavEndArea( void )
  1489. {
  1490. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1491. return;
  1492. TheNavMesh->CommandNavEndArea();
  1493. }
  1494. static ConCommand nav_end_area( "nav_end_area", CommandNavEndArea, "Defines the second corner of a new Area or Ladder and creates it.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1495. //--------------------------------------------------------------------------------------------------------------
  1496. void CommandNavConnect( void )
  1497. {
  1498. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1499. return;
  1500. TheNavMesh->CommandNavConnect();
  1501. }
  1502. static ConCommand nav_connect( "nav_connect", CommandNavConnect, "To connect two Areas, mark the first Area, highlight the second Area, then invoke the connect command. Note that this creates a ONE-WAY connection from the first to the second Area. To make a two-way connection, also connect the second area to the first.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1503. //--------------------------------------------------------------------------------------------------------------
  1504. void CommandNavDisconnect( void )
  1505. {
  1506. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1507. return;
  1508. TheNavMesh->CommandNavDisconnect();
  1509. }
  1510. static ConCommand nav_disconnect( "nav_disconnect", CommandNavDisconnect, "To disconnect two Areas, mark an Area, highlight a second Area, then invoke the disconnect command. This will remove all connections between the two Areas.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1511. //--------------------------------------------------------------------------------------------------------------
  1512. void CommandNavSplice( void )
  1513. {
  1514. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1515. return;
  1516. TheNavMesh->CommandNavSplice();
  1517. }
  1518. static ConCommand nav_splice( "nav_splice", CommandNavSplice, "To splice, mark an area, highlight a second area, then invoke the splice command to create a new, connected area between them.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1519. //--------------------------------------------------------------------------------------------------------------
  1520. void CommandNavCrouch( void )
  1521. {
  1522. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1523. return;
  1524. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_CROUCH );
  1525. }
  1526. static ConCommand nav_crouch( "nav_crouch", CommandNavCrouch, "Toggles the 'must crouch in this area' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1527. //--------------------------------------------------------------------------------------------------------------
  1528. void CommandNavPrecise( void )
  1529. {
  1530. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1531. return;
  1532. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_PRECISE );
  1533. }
  1534. static ConCommand nav_precise( "nav_precise", CommandNavPrecise, "Toggles the 'dont avoid obstacles' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1535. //--------------------------------------------------------------------------------------------------------------
  1536. void CommandNavJump( void )
  1537. {
  1538. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1539. return;
  1540. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_JUMP );
  1541. }
  1542. static ConCommand nav_jump( "nav_jump", CommandNavJump, "Toggles the 'traverse this area by jumping' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1543. //--------------------------------------------------------------------------------------------------------------
  1544. void CommandNavNoJump( void )
  1545. {
  1546. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1547. return;
  1548. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_NO_JUMP );
  1549. }
  1550. static ConCommand nav_no_jump( "nav_no_jump", CommandNavNoJump, "Toggles the 'dont jump in this area' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1551. //--------------------------------------------------------------------------------------------------------------
  1552. void CommandNavStop( void )
  1553. {
  1554. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1555. return;
  1556. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_STOP );
  1557. }
  1558. static ConCommand nav_stop( "nav_stop", CommandNavStop, "Toggles the 'must stop when entering this area' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1559. //--------------------------------------------------------------------------------------------------------------
  1560. void CommandNavWalk( void )
  1561. {
  1562. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1563. return;
  1564. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_WALK );
  1565. }
  1566. static ConCommand nav_walk( "nav_walk", CommandNavWalk, "Toggles the 'traverse this area by walking' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1567. //--------------------------------------------------------------------------------------------------------------
  1568. void CommandNavRun( void )
  1569. {
  1570. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1571. return;
  1572. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_RUN );
  1573. }
  1574. static ConCommand nav_run( "nav_run", CommandNavRun, "Toggles the 'traverse this area by running' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1575. //--------------------------------------------------------------------------------------------------------------
  1576. void CommandNavAvoid( void )
  1577. {
  1578. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1579. return;
  1580. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_AVOID );
  1581. }
  1582. static ConCommand nav_avoid( "nav_avoid", CommandNavAvoid, "Toggles the 'avoid this area when possible' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1583. //--------------------------------------------------------------------------------------------------------------
  1584. void CommandNavTransient( void )
  1585. {
  1586. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1587. return;
  1588. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_TRANSIENT );
  1589. }
  1590. static ConCommand nav_transient( "nav_transient", CommandNavTransient, "Toggles the 'area is transient and may become blocked' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1591. //--------------------------------------------------------------------------------------------------------------
  1592. void CommandNavDontHide( void )
  1593. {
  1594. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1595. return;
  1596. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_DONT_HIDE );
  1597. }
  1598. static ConCommand nav_dont_hide( "nav_dont_hide", CommandNavDontHide, "Toggles the 'area is not suitable for hiding spots' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1599. //--------------------------------------------------------------------------------------------------------------
  1600. void CommandNavStand( void )
  1601. {
  1602. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1603. return;
  1604. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_STAND );
  1605. }
  1606. static ConCommand nav_stand( "nav_stand", CommandNavStand, "Toggles the 'stand while hiding' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1607. //--------------------------------------------------------------------------------------------------------------
  1608. void CommandNavNoHostages( void )
  1609. {
  1610. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1611. return;
  1612. TheNavMesh->CommandNavToggleAttribute( NAV_MESH_NO_HOSTAGES );
  1613. }
  1614. static ConCommand nav_no_hostages( "nav_no_hostages", CommandNavNoHostages, "Toggles the 'hostages cannot use this area' flag used by the AI system.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1615. //--------------------------------------------------------------------------------------------------------------
  1616. void CommandNavStrip( void )
  1617. {
  1618. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1619. return;
  1620. TheNavMesh->StripNavigationAreas();
  1621. }
  1622. static ConCommand nav_strip( "nav_strip", CommandNavStrip, "Strips all Hiding Spots, Approach Points, and Encounter Spots from the current Area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1623. //--------------------------------------------------------------------------------------------------------------
  1624. void CommandNavSave( void )
  1625. {
  1626. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1627. return;
  1628. if (TheNavMesh->Save())
  1629. {
  1630. Msg( "Navigation map '%s' saved.\n", TheNavMesh->GetFilename() );
  1631. }
  1632. else
  1633. {
  1634. const char *filename = TheNavMesh->GetFilename();
  1635. Msg( "ERROR: Cannot save navigation map '%s'.\n", (filename) ? filename : "(null)" );
  1636. }
  1637. }
  1638. static ConCommand nav_save( "nav_save", CommandNavSave, "Saves the current Navigation Mesh to disk.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1639. //--------------------------------------------------------------------------------------------------------------
  1640. void CommandNavLoad( void )
  1641. {
  1642. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1643. return;
  1644. if (TheNavMesh->Load() != NAV_OK)
  1645. {
  1646. Msg( "ERROR: Navigation Mesh load failed.\n" );
  1647. }
  1648. }
  1649. static ConCommand nav_load( "nav_load", CommandNavLoad, "Loads the Navigation Mesh for the current map.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1650. //--------------------------------------------------------------------------------------------------------------
  1651. static int PlaceNameAutocompleteCallback( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  1652. {
  1653. return TheNavMesh->PlaceNameAutocomplete( partial, commands );
  1654. }
  1655. //--------------------------------------------------------------------------------------------------------------
  1656. void CommandNavUsePlace( const CCommand &args )
  1657. {
  1658. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1659. return;
  1660. if (args.ArgC() == 1)
  1661. {
  1662. // no arguments = list all available places
  1663. TheNavMesh->PrintAllPlaces();
  1664. }
  1665. else
  1666. {
  1667. // single argument = set current place
  1668. Place place = TheNavMesh->PartialNameToPlace( args[ 1 ] );
  1669. if (place == UNDEFINED_PLACE)
  1670. {
  1671. Msg( "Ambiguous\n" );
  1672. }
  1673. else
  1674. {
  1675. Msg( "Current place set to '%s'\n", TheNavMesh->PlaceToName( place ) );
  1676. TheNavMesh->SetNavPlace( place );
  1677. }
  1678. }
  1679. }
  1680. static ConCommand nav_use_place( "nav_use_place", CommandNavUsePlace, "If used without arguments, all available Places will be listed. If a Place argument is given, the current Place is set.", FCVAR_GAMEDLL | FCVAR_CHEAT, PlaceNameAutocompleteCallback );
  1681. //--------------------------------------------------------------------------------------------------------------
  1682. void CommandNavPlaceReplace( const CCommand &args )
  1683. {
  1684. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1685. return;
  1686. if (args.ArgC() != 3)
  1687. {
  1688. // no arguments
  1689. Msg( "Usage: nav_place_replace <OldPlace> <NewPlace>\n" );
  1690. }
  1691. else
  1692. {
  1693. // two arguments - replace the first place with the second
  1694. Place oldPlace = TheNavMesh->PartialNameToPlace( args[ 1 ] );
  1695. Place newPlace = TheNavMesh->PartialNameToPlace( args[ 2 ] );
  1696. if ( oldPlace == UNDEFINED_PLACE || newPlace == UNDEFINED_PLACE )
  1697. {
  1698. Msg( "Ambiguous\n" );
  1699. }
  1700. else
  1701. {
  1702. FOR_EACH_VEC( TheNavAreas, it )
  1703. {
  1704. CNavArea *area = TheNavAreas[it];
  1705. if ( area->GetPlace() == oldPlace )
  1706. {
  1707. area->SetPlace( newPlace );
  1708. }
  1709. }
  1710. }
  1711. }
  1712. }
  1713. static ConCommand nav_place_replace( "nav_place_replace", CommandNavPlaceReplace, "Replaces all instances of the first place with the second place.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1714. //--------------------------------------------------------------------------------------------------------------
  1715. void CommandNavPlaceList( void )
  1716. {
  1717. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1718. return;
  1719. CUtlVector< Place > placeDirectory;
  1720. FOR_EACH_VEC( TheNavAreas, nit )
  1721. {
  1722. CNavArea *area = TheNavAreas[ nit ];
  1723. Place place = area->GetPlace();
  1724. if ( place )
  1725. {
  1726. if ( !placeDirectory.HasElement( place ) )
  1727. {
  1728. placeDirectory.AddToTail( place );
  1729. }
  1730. }
  1731. }
  1732. Msg( "Map uses %d place names:\n", placeDirectory.Count() );
  1733. for ( int i=0; i<placeDirectory.Count(); ++i )
  1734. {
  1735. Msg( " %s\n", TheNavMesh->PlaceToName( placeDirectory[i] ) );
  1736. }
  1737. }
  1738. static ConCommand nav_place_list( "nav_place_list", CommandNavPlaceList, "Lists all place names used in the map.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1739. //--------------------------------------------------------------------------------------------------------------
  1740. void CommandNavTogglePlaceMode( void )
  1741. {
  1742. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1743. return;
  1744. TheNavMesh->CommandNavTogglePlaceMode();
  1745. }
  1746. static ConCommand nav_toggle_place_mode( "nav_toggle_place_mode", CommandNavTogglePlaceMode, "Toggle the editor into and out of Place mode. Place mode allows labelling of Area with Place names.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1747. //--------------------------------------------------------------------------------------------------------------
  1748. void CommandNavSetPlaceMode( const CCommand &args )
  1749. {
  1750. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1751. return;
  1752. bool on = true;
  1753. if ( args.ArgC() == 2 )
  1754. {
  1755. on = (atoi( args[ 1 ] ) != 0);
  1756. }
  1757. if ( on != TheNavMesh->IsEditMode( CNavMesh::PLACE_PAINTING ) )
  1758. {
  1759. TheNavMesh->CommandNavTogglePlaceMode();
  1760. }
  1761. }
  1762. static ConCommand nav_set_place_mode( "nav_set_place_mode", CommandNavSetPlaceMode, "Sets the editor into or out of Place mode. Place mode allows labelling of Area with Place names.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1763. //--------------------------------------------------------------------------------------------------------------
  1764. void CommandNavPlaceFloodFill( void )
  1765. {
  1766. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1767. return;
  1768. TheNavMesh->CommandNavPlaceFloodFill();
  1769. }
  1770. static ConCommand nav_place_floodfill( "nav_place_floodfill", CommandNavPlaceFloodFill, "Sets the Place of the Area under the cursor to the curent Place, and 'flood-fills' the Place to all adjacent Areas. Flood-filling stops when it hits an Area with the same Place, or a different Place than that of the initial Area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1771. //--------------------------------------------------------------------------------------------------------------
  1772. void CommandNavPlaceSet( void )
  1773. {
  1774. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1775. return;
  1776. TheNavMesh->CommandNavPlaceSet();
  1777. }
  1778. static ConCommand nav_place_set( "nav_place_set", CommandNavPlaceSet, "Sets the Place of all selected areas to the current Place.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1779. //--------------------------------------------------------------------------------------------------------------
  1780. void CommandNavPlacePick( void )
  1781. {
  1782. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1783. return;
  1784. TheNavMesh->CommandNavPlacePick();
  1785. }
  1786. static ConCommand nav_place_pick( "nav_place_pick", CommandNavPlacePick, "Sets the current Place to the Place of the Area under the cursor.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1787. //--------------------------------------------------------------------------------------------------------------
  1788. void CommandNavTogglePlacePainting( void )
  1789. {
  1790. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1791. return;
  1792. TheNavMesh->CommandNavTogglePlacePainting();
  1793. }
  1794. static ConCommand nav_toggle_place_painting( "nav_toggle_place_painting", CommandNavTogglePlacePainting, "Toggles Place Painting mode. When Place Painting, pointing at an Area will 'paint' it with the current Place.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1795. //--------------------------------------------------------------------------------------------------------------
  1796. void CommandNavMarkUnnamed( void )
  1797. {
  1798. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1799. return;
  1800. TheNavMesh->CommandNavMarkUnnamed();
  1801. }
  1802. static ConCommand nav_mark_unnamed( "nav_mark_unnamed", CommandNavMarkUnnamed, "Mark an Area with no Place name. Useful for finding stray areas missed when Place Painting.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1803. //--------------------------------------------------------------------------------------------------------------
  1804. void CommandNavCornerSelect( void )
  1805. {
  1806. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1807. return;
  1808. TheNavMesh->CommandNavCornerSelect();
  1809. }
  1810. static ConCommand nav_corner_select( "nav_corner_select", CommandNavCornerSelect, "Select a corner of the currently marked Area. Use multiple times to access all four corners.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1811. //--------------------------------------------------------------------------------------------------------------
  1812. CON_COMMAND_F( nav_corner_raise, "Raise the selected corner of the currently marked Area.", FCVAR_GAMEDLL | FCVAR_CHEAT )
  1813. {
  1814. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1815. return;
  1816. TheNavMesh->CommandNavCornerRaise( args );
  1817. }
  1818. //--------------------------------------------------------------------------------------------------------------
  1819. CON_COMMAND_F( nav_corner_lower, "Lower the selected corner of the currently marked Area.", FCVAR_GAMEDLL | FCVAR_CHEAT )
  1820. {
  1821. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1822. return;
  1823. TheNavMesh->CommandNavCornerLower( args );
  1824. }
  1825. //--------------------------------------------------------------------------------------------------------------
  1826. CON_COMMAND_F( nav_corner_place_on_ground, "Places the selected corner of the currently marked Area on the ground.", FCVAR_GAMEDLL | FCVAR_CHEAT )
  1827. {
  1828. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1829. return;
  1830. TheNavMesh->CommandNavCornerPlaceOnGround( args );
  1831. }
  1832. //--------------------------------------------------------------------------------------------------------------
  1833. void CommandNavWarpToMark( void )
  1834. {
  1835. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1836. return;
  1837. TheNavMesh->CommandNavWarpToMark();
  1838. }
  1839. static ConCommand nav_warp_to_mark( "nav_warp_to_mark", CommandNavWarpToMark, "Warps the player to the marked area.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1840. //--------------------------------------------------------------------------------------------------------------
  1841. void CommandNavLadderFlip( void )
  1842. {
  1843. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1844. return;
  1845. TheNavMesh->CommandNavLadderFlip();
  1846. }
  1847. static ConCommand nav_ladder_flip( "nav_ladder_flip", CommandNavLadderFlip, "Flips the selected ladder's direction.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1848. //--------------------------------------------------------------------------------------------------------------
  1849. void CommandNavGenerate( void )
  1850. {
  1851. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1852. return;
  1853. TheNavMesh->BeginGeneration();
  1854. }
  1855. static ConCommand nav_generate( "nav_generate", CommandNavGenerate, "Generate a Navigation Mesh for the current map and save it to disk.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1856. //--------------------------------------------------------------------------------------------------------------
  1857. void CommandNavGenerateIncremental( void )
  1858. {
  1859. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1860. return;
  1861. TheNavMesh->BeginGeneration( INCREMENTAL_GENERATION );
  1862. }
  1863. static ConCommand nav_generate_incremental( "nav_generate_incremental", CommandNavGenerateIncremental, "Generate a Navigation Mesh for the current map and save it to disk.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1864. //--------------------------------------------------------------------------------------------------------------
  1865. void CommandNavAnalyze( void )
  1866. {
  1867. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1868. return;
  1869. if ( nav_edit.GetBool() )
  1870. {
  1871. TheNavMesh->BeginAnalysis();
  1872. }
  1873. }
  1874. static ConCommand nav_analyze( "nav_analyze", CommandNavAnalyze, "Re-analyze the current Navigation Mesh and save it to disk.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1875. //--------------------------------------------------------------------------------------------------------------
  1876. void CommandNavAnalyzeScripted( const CCommand &args )
  1877. {
  1878. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1879. return;
  1880. const char *pszCmd = NULL;
  1881. int count = args.ArgC();
  1882. if ( count > 0 )
  1883. {
  1884. pszCmd = args[1];
  1885. }
  1886. bool bForceAnalyze = pszCmd && !Q_stricmp( pszCmd, "force" );
  1887. if ( TheNavMesh->IsAnalyzed() && !bForceAnalyze )
  1888. {
  1889. engine->ServerCommand( "quit\n" );
  1890. return;
  1891. }
  1892. if ( nav_edit.GetBool() )
  1893. {
  1894. TheNavMesh->BeginAnalysis( true );
  1895. }
  1896. }
  1897. static ConCommand nav_analyze_scripted( "nav_analyze_scripted", CommandNavAnalyzeScripted, "commandline hook to run a nav_analyze and then quit.", FCVAR_GAMEDLL | FCVAR_CHEAT | FCVAR_HIDDEN );
  1898. //--------------------------------------------------------------------------------------------------------------
  1899. void CommandNavMarkWalkable( void )
  1900. {
  1901. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1902. return;
  1903. TheNavMesh->CommandNavMarkWalkable();
  1904. }
  1905. //--------------------------------------------------------------------------------------------------------
  1906. void CNavMesh::CommandNavMarkWalkable( void )
  1907. {
  1908. Vector pos;
  1909. if (nav_edit.GetBool())
  1910. {
  1911. // we are in edit mode, use the edit cursor's location
  1912. pos = GetEditCursorPosition();
  1913. }
  1914. else
  1915. {
  1916. // we are not in edit mode, use the position of the local player
  1917. CBasePlayer *player = UTIL_GetListenServerHost();
  1918. if (player == NULL)
  1919. {
  1920. Msg( "ERROR: No local player!\n" );
  1921. return;
  1922. }
  1923. pos = player->GetAbsOrigin();
  1924. }
  1925. // snap position to the sampling grid
  1926. pos.x = SnapToGrid( pos.x, true );
  1927. pos.y = SnapToGrid( pos.y, true );
  1928. Vector normal;
  1929. if ( !FindGroundForNode( &pos, &normal ) )
  1930. {
  1931. Msg( "ERROR: Invalid ground position.\n" );
  1932. return;
  1933. }
  1934. AddWalkableSeed( pos, normal );
  1935. Msg( "Walkable position marked.\n" );
  1936. }
  1937. static ConCommand nav_mark_walkable( "nav_mark_walkable", CommandNavMarkWalkable, "Mark the current location as a walkable position. These positions are used as seed locations when sampling the map to generate a Navigation Mesh.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1938. //--------------------------------------------------------------------------------------------------------------
  1939. void CommandNavClearWalkableMarks( void )
  1940. {
  1941. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1942. return;
  1943. TheNavMesh->ClearWalkableSeeds();
  1944. }
  1945. static ConCommand nav_clear_walkable_marks( "nav_clear_walkable_marks", CommandNavClearWalkableMarks, "Erase any previously placed walkable positions.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1946. //--------------------------------------------------------------------------------------------------------------
  1947. void CommandNavCompressID( void )
  1948. {
  1949. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1950. return;
  1951. CNavArea::CompressIDs();
  1952. CNavLadder::CompressIDs();
  1953. }
  1954. static ConCommand nav_compress_id( "nav_compress_id", CommandNavCompressID, "Re-orders area and ladder ID's so they are continuous.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1955. //--------------------------------------------------------------------------------------------------------------
  1956. #ifdef TERROR
  1957. void CommandNavShowLadderBounds( void )
  1958. {
  1959. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1960. return;
  1961. CFuncSimpleLadder *ladder = NULL;
  1962. while( (ladder = dynamic_cast< CFuncSimpleLadder * >(gEntList.FindEntityByClassname( ladder, "func_simpleladder" ))) != NULL )
  1963. {
  1964. Vector mins, maxs;
  1965. ladder->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
  1966. ladder->m_debugOverlays |= OVERLAY_TEXT_BIT | OVERLAY_ABSBOX_BIT;
  1967. NDebugOverlay::Box( vec3_origin, mins, maxs, 0, 255, 0, 0, 600 );
  1968. }
  1969. }
  1970. static ConCommand nav_show_ladder_bounds( "nav_show_ladder_bounds", CommandNavShowLadderBounds, "Draws the bounding boxes of all func_ladders in the map.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1971. #endif
  1972. //--------------------------------------------------------------------------------------------------------------
  1973. void CommandNavBuildLadder( void )
  1974. {
  1975. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1976. return;
  1977. TheNavMesh->CommandNavBuildLadder();
  1978. }
  1979. static ConCommand nav_build_ladder( "nav_build_ladder", CommandNavBuildLadder, "Attempts to build a nav ladder on the climbable surface under the cursor.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1980. //--------------------------------------------------------------------------------------------------------
  1981. void NavEditClearAllAttributes( void )
  1982. {
  1983. NavAttributeClearer clear( (NavAttributeType)0xFFFF );
  1984. TheNavMesh->ForAllSelectedAreas( clear );
  1985. TheNavMesh->ClearSelectedSet();
  1986. }
  1987. static ConCommand ClearAllNavAttributes( "wipe_nav_attributes", NavEditClearAllAttributes, "Clear all nav attributes of selected area.", FCVAR_CHEAT );
  1988. //--------------------------------------------------------------------------------------------------------
  1989. bool NavAttributeToggler::operator() ( CNavArea *area )
  1990. {
  1991. // only toggle if dealing with a single selected area
  1992. if ( TheNavMesh->IsSelectedSetEmpty() && (area->GetAttributes() & m_attribute) != 0 )
  1993. {
  1994. area->SetAttributes( area->GetAttributes() & (~m_attribute) );
  1995. }
  1996. else
  1997. {
  1998. area->SetAttributes( area->GetAttributes() | m_attribute );
  1999. }
  2000. return true;
  2001. }
  2002. //--------------------------------------------------------------------------------------------------------
  2003. NavAttributeLookup TheNavAttributeTable[] =
  2004. {
  2005. { "CROUCH", NAV_MESH_CROUCH },
  2006. { "JUMP", NAV_MESH_JUMP },
  2007. { "PRECISE", NAV_MESH_PRECISE },
  2008. { "NO_JUMP", NAV_MESH_NO_JUMP },
  2009. { "STOP", NAV_MESH_STOP },
  2010. { "RUN", NAV_MESH_RUN },
  2011. { "WALK", NAV_MESH_WALK },
  2012. { "AVOID", NAV_MESH_AVOID },
  2013. { "TRANSIENT", NAV_MESH_TRANSIENT },
  2014. { "DONT_HIDE", NAV_MESH_DONT_HIDE },
  2015. { "STAND", NAV_MESH_STAND },
  2016. { "NO_HOSTAGES", NAV_MESH_NO_HOSTAGES },
  2017. { "STAIRS", NAV_MESH_STAIRS },
  2018. { "NO_MERGE", NAV_MESH_NO_MERGE },
  2019. { "OBSTACLE_TOP", NAV_MESH_OBSTACLE_TOP },
  2020. { "CLIFF", NAV_MESH_CLIFF },
  2021. #ifdef TERROR
  2022. { "PLAYERCLIP", (NavAttributeType)CNavArea::NAV_PLAYERCLIP },
  2023. { "BREAKABLEWALL", (NavAttributeType)CNavArea::NAV_BREAKABLEWALL },
  2024. #endif
  2025. { NULL, NAV_MESH_INVALID }
  2026. };
  2027. /**
  2028. * Can be used with any command that takes an attribute as its 2nd argument
  2029. */
  2030. static int NavAttributeAutocomplete( const char *input, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  2031. {
  2032. if ( Q_strlen( input ) >= COMMAND_COMPLETION_ITEM_LENGTH )
  2033. {
  2034. return 0;
  2035. }
  2036. char command[ COMMAND_COMPLETION_ITEM_LENGTH+1 ];
  2037. Q_strncpy( command, input, sizeof( command ) );
  2038. // skip to start of argument
  2039. char *partialArg = Q_strrchr( command, ' ' );
  2040. if ( partialArg == NULL )
  2041. {
  2042. return 0;
  2043. }
  2044. // chop command from partial argument
  2045. *partialArg = '\000';
  2046. ++partialArg;
  2047. int partialArgLength = Q_strlen( partialArg );
  2048. int count = 0;
  2049. for( unsigned int i=0; TheNavAttributeTable[i].name && count < COMMAND_COMPLETION_MAXITEMS; ++i )
  2050. {
  2051. if ( !Q_strnicmp( TheNavAttributeTable[i].name, partialArg, partialArgLength ) )
  2052. {
  2053. // Add to the autocomplete array
  2054. Q_snprintf( commands[ count++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", command, TheNavAttributeTable[i].name );
  2055. }
  2056. }
  2057. return count;
  2058. }
  2059. //--------------------------------------------------------------------------------------------------------
  2060. NavAttributeType NameToNavAttribute( const char *name )
  2061. {
  2062. for( unsigned int i=0; TheNavAttributeTable[i].name; ++i )
  2063. {
  2064. if ( !Q_stricmp( TheNavAttributeTable[i].name, name ) )
  2065. {
  2066. return TheNavAttributeTable[i].attribute;
  2067. }
  2068. }
  2069. return (NavAttributeType)0;
  2070. }
  2071. //--------------------------------------------------------------------------------------------------------
  2072. void NavEditClearAttribute( const CCommand &args )
  2073. {
  2074. if ( args.ArgC() != 2 )
  2075. {
  2076. Msg( "Usage: %s <attribute>\n", args[0] );
  2077. return;
  2078. }
  2079. NavAttributeType attribute = NameToNavAttribute( args[1] );
  2080. if ( attribute != 0 )
  2081. {
  2082. NavAttributeClearer clear( attribute );
  2083. TheNavMesh->ForAllSelectedAreas( clear );
  2084. TheNavMesh->ClearSelectedSet();
  2085. return;
  2086. }
  2087. Msg( "Unknown attribute '%s'", args[1] );
  2088. }
  2089. static ConCommand NavClearAttribute( "nav_clear_attribute", NavEditClearAttribute, "Remove given nav attribute from all areas in the selected set.", FCVAR_CHEAT, NavAttributeAutocomplete );
  2090. //--------------------------------------------------------------------------------------------------------
  2091. void NavEditMarkAttribute( const CCommand &args )
  2092. {
  2093. if ( args.ArgC() != 2 )
  2094. {
  2095. Msg( "Usage: %s <attribute>\n", args[0] );
  2096. return;
  2097. }
  2098. NavAttributeType attribute = NameToNavAttribute( args[1] );
  2099. if ( attribute != 0 )
  2100. {
  2101. NavAttributeSetter setter( attribute );
  2102. TheNavMesh->ForAllSelectedAreas( setter );
  2103. TheNavMesh->ClearSelectedSet();
  2104. return;
  2105. }
  2106. Msg( "Unknown attribute '%s'", args[1] );
  2107. }
  2108. static ConCommand NavMarkAttribute( "nav_mark_attribute", NavEditMarkAttribute, "Set nav attribute for all areas in the selected set.", FCVAR_CHEAT, NavAttributeAutocomplete );
  2109. /* IN PROGRESS:
  2110. //--------------------------------------------------------------------------------------------------------------
  2111. void CommandNavPickArea( void )
  2112. {
  2113. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  2114. return;
  2115. TheNavMesh->CommandNavPickArea();
  2116. }
  2117. static ConCommand nav_pick_area( "nav_pick_area", CommandNavPickArea, "Marks an area (and corner) based on the surface under the cursor.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  2118. //--------------------------------------------------------------------------------------------------------------
  2119. void CommandNavResizeHorizontal( void )
  2120. {
  2121. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  2122. return;
  2123. TheNavMesh->CommandNavResizeHorizontal();
  2124. }
  2125. static ConCommand nav_resize_horizontal( "nav_resize_horizontal", CommandNavResizeHorizontal, "TODO", FCVAR_GAMEDLL | FCVAR_CHEAT );
  2126. //--------------------------------------------------------------------------------------------------------------
  2127. void CommandNavResizeVertical( void )
  2128. {
  2129. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  2130. return;
  2131. TheNavMesh->CommandNavResizeVertical();
  2132. }
  2133. static ConCommand nav_resize_vertical( "nav_resize_vertical", CommandNavResizeVertical, "TODO", FCVAR_GAMEDLL | FCVAR_CHEAT );
  2134. //--------------------------------------------------------------------------------------------------------------
  2135. void CommandNavResizeEnd( void )
  2136. {
  2137. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  2138. return;
  2139. TheNavMesh->CommandNavResizeEnd();
  2140. }
  2141. static ConCommand nav_resize_end( "nav_resize_end", CommandNavResizeEnd, "TODO", FCVAR_GAMEDLL | FCVAR_CHEAT );
  2142. */
  2143. //--------------------------------------------------------------------------------------------------------------
  2144. /**
  2145. * Destroy ladder representations
  2146. */
  2147. void CNavMesh::DestroyLadders( void )
  2148. {
  2149. for ( int i=0; i<m_ladders.Count(); ++i )
  2150. {
  2151. OnEditDestroyNotify( m_ladders[i] );
  2152. delete m_ladders[i];
  2153. }
  2154. m_ladders.RemoveAll();
  2155. m_markedLadder = NULL;
  2156. m_selectedLadder = NULL;
  2157. }
  2158. //--------------------------------------------------------------------------------------------------------------
  2159. /**
  2160. * Strip the "analyzed" data out of all navigation areas
  2161. */
  2162. void CNavMesh::StripNavigationAreas( void )
  2163. {
  2164. FOR_EACH_VEC( TheNavAreas, it )
  2165. {
  2166. CNavArea *area = TheNavAreas[ it ];
  2167. area->Strip();
  2168. }
  2169. m_isAnalyzed = false;
  2170. }
  2171. //--------------------------------------------------------------------------------------------------------------
  2172. HidingSpotVector TheHidingSpots;
  2173. unsigned int HidingSpot::m_nextID = 1;
  2174. unsigned int HidingSpot::m_masterMarker = 0;
  2175. //--------------------------------------------------------------------------------------------------------------
  2176. /**
  2177. * Hiding Spot factory
  2178. */
  2179. HidingSpot *CNavMesh::CreateHidingSpot( void ) const
  2180. {
  2181. return new HidingSpot;
  2182. }
  2183. //--------------------------------------------------------------------------------------------------------------
  2184. void CNavMesh::DestroyHidingSpots( void )
  2185. {
  2186. // remove all hiding spot references from the nav areas
  2187. FOR_EACH_VEC( TheNavAreas, it )
  2188. {
  2189. CNavArea *area = TheNavAreas[ it ];
  2190. area->m_hidingSpots.RemoveAll();
  2191. }
  2192. HidingSpot::m_nextID = 0;
  2193. // free all the HidingSpots
  2194. FOR_EACH_VEC( TheHidingSpots, hit )
  2195. {
  2196. delete TheHidingSpots[ hit ];
  2197. }
  2198. TheHidingSpots.RemoveAll();
  2199. }
  2200. //--------------------------------------------------------------------------------------------------------------
  2201. //--------------------------------------------------------------------------------------------------------------
  2202. /**
  2203. * Construct a Hiding Spot. Assign a unique ID which may be overwritten if loaded.
  2204. */
  2205. HidingSpot::HidingSpot( void )
  2206. {
  2207. m_pos = Vector( 0, 0, 0 );
  2208. m_id = m_nextID++;
  2209. m_flags = 0;
  2210. m_area = NULL;
  2211. m_bIsSaved = true;
  2212. TheHidingSpots.AddToTail( this );
  2213. }
  2214. //--------------------------------------------------------------------------------------------------------------
  2215. void HidingSpot::Save( CUtlBuffer &fileBuffer, unsigned int version ) const
  2216. {
  2217. if ( !m_bIsSaved )
  2218. return;
  2219. fileBuffer.PutUnsignedInt( m_id );
  2220. fileBuffer.PutFloat( m_pos.x );
  2221. fileBuffer.PutFloat( m_pos.y );
  2222. fileBuffer.PutFloat( m_pos.z );
  2223. fileBuffer.PutUnsignedChar( m_flags );
  2224. }
  2225. //--------------------------------------------------------------------------------------------------------------
  2226. void HidingSpot::Load( CUtlBuffer &fileBuffer, unsigned int version )
  2227. {
  2228. m_id = fileBuffer.GetUnsignedInt();
  2229. m_pos.x = fileBuffer.GetFloat();
  2230. m_pos.y = fileBuffer.GetFloat();
  2231. m_pos.z = fileBuffer.GetFloat();
  2232. m_flags = fileBuffer.GetUnsignedChar();
  2233. // update next ID to avoid ID collisions by later spots
  2234. if (m_id >= m_nextID)
  2235. m_nextID = m_id+1;
  2236. }
  2237. //--------------------------------------------------------------------------------------------------------------
  2238. /**
  2239. * Hiding Spot post-load processing
  2240. */
  2241. NavErrorType HidingSpot::PostLoad( void )
  2242. {
  2243. // set our area
  2244. m_area = TheNavMesh->GetNavArea( m_pos + Vector( 0, 0, HalfHumanHeight ) );
  2245. if ( !m_area )
  2246. {
  2247. DevWarning( "A Hiding Spot is off of the Nav Mesh at setpos %.0f %.0f %.0f\n", m_pos.x, m_pos.y, m_pos.z );
  2248. AssertMsg(false, "This will cause crashes in the Bot Navigation code!");
  2249. }
  2250. return NAV_OK;
  2251. }
  2252. //--------------------------------------------------------------------------------------------------------------
  2253. /**
  2254. * Given a HidingSpot ID, return the associated HidingSpot
  2255. */
  2256. HidingSpot *GetHidingSpotByID( unsigned int id )
  2257. {
  2258. FOR_EACH_VEC( TheHidingSpots, it )
  2259. {
  2260. HidingSpot *spot = TheHidingSpots[ it ];
  2261. if (spot->GetID() == id)
  2262. return spot;
  2263. }
  2264. return NULL;
  2265. }
  2266. //--------------------------------------------------------------------------------------------------------
  2267. // invoked when the area becomes blocked
  2268. void CNavMesh::OnAreaBlocked( CNavArea *area )
  2269. {
  2270. if ( !m_blockedAreas.HasElement( area ) )
  2271. {
  2272. m_blockedAreas.AddToTail( area );
  2273. }
  2274. }
  2275. //--------------------------------------------------------------------------------------------------------
  2276. // invoked when the area becomes un-blocked
  2277. void CNavMesh::OnAreaUnblocked( CNavArea *area )
  2278. {
  2279. m_blockedAreas.FindAndRemove( area );
  2280. }
  2281. //--------------------------------------------------------------------------------------------------------
  2282. void CNavMesh::UpdateBlockedAreas( void )
  2283. {
  2284. VPROF( "CNavMesh::UpdateBlockedAreas" );
  2285. for ( int i=0; i<m_blockedAreas.Count(); ++i )
  2286. {
  2287. CNavArea *area = m_blockedAreas[i];
  2288. area->UpdateBlocked();
  2289. }
  2290. }
  2291. //--------------------------------------------------------------------------------------------------------
  2292. void CNavMesh::RegisterAvoidanceObstacle( INavAvoidanceObstacle *obstruction )
  2293. {
  2294. m_avoidanceObstacles.FindAndFastRemove( obstruction );
  2295. m_avoidanceObstacles.AddToTail( obstruction );
  2296. }
  2297. //--------------------------------------------------------------------------------------------------------
  2298. void CNavMesh::UnregisterAvoidanceObstacle( INavAvoidanceObstacle *obstruction )
  2299. {
  2300. m_avoidanceObstacles.FindAndFastRemove( obstruction );
  2301. }
  2302. //--------------------------------------------------------------------------------------------------------
  2303. // invoked when the area becomes blocked
  2304. void CNavMesh::OnAvoidanceObstacleEnteredArea( CNavArea *area )
  2305. {
  2306. if ( !m_avoidanceObstacleAreas.HasElement( area ) )
  2307. {
  2308. m_avoidanceObstacleAreas.AddToTail( area );
  2309. }
  2310. }
  2311. //--------------------------------------------------------------------------------------------------------
  2312. // invoked when the area becomes un-blocked
  2313. void CNavMesh::OnAvoidanceObstacleLeftArea( CNavArea *area )
  2314. {
  2315. m_avoidanceObstacleAreas.FindAndRemove( area );
  2316. }
  2317. //--------------------------------------------------------------------------------------------------------
  2318. void CNavMesh::UpdateAvoidanceObstacleAreas( void )
  2319. {
  2320. VPROF( "CNavMesh::UpdateAvoidanceObstacleAreas" );
  2321. for ( int i=0; i<m_avoidanceObstacleAreas.Count(); ++i )
  2322. {
  2323. CNavArea *area = m_avoidanceObstacleAreas[i];
  2324. area->UpdateAvoidanceObstacles();
  2325. }
  2326. }
  2327. extern CUtlHash< NavVisPair_t, CVisPairHashFuncs, CVisPairHashFuncs > *g_pNavVisPairHash;
  2328. //--------------------------------------------------------------------------------------------------------
  2329. void CNavMesh::BeginVisibilityComputations( void )
  2330. {
  2331. if ( !g_pNavVisPairHash )
  2332. {
  2333. g_pNavVisPairHash = new CUtlHash< NavVisPair_t, CVisPairHashFuncs, CVisPairHashFuncs >( 16*1024 );
  2334. }
  2335. else
  2336. {
  2337. g_pNavVisPairHash->RemoveAll();
  2338. }
  2339. FOR_EACH_VEC( TheNavAreas, it )
  2340. {
  2341. CNavArea *area = TheNavAreas[ it ];
  2342. area->ResetPotentiallyVisibleAreas();
  2343. }
  2344. }
  2345. //--------------------------------------------------------------------------------------------------------
  2346. /**
  2347. * Invoked when custom analysis step is complete
  2348. */
  2349. void CNavMesh::EndVisibilityComputations( void )
  2350. {
  2351. g_pNavVisPairHash->RemoveAll();
  2352. int avgVisLength = 0;
  2353. int maxVisLength = 0;
  2354. int minVisLength = 999999999;
  2355. // Optimize visibility storage of nav mesh by doing a kind of run-length encoding.
  2356. // Pick an "anchor" area and compare adjacent areas visibility lists to it. If the delta is
  2357. // small, point back to the anchor and just store the delta.
  2358. FOR_EACH_VEC( TheNavAreas, it )
  2359. {
  2360. CNavArea *area = (CNavArea *)TheNavAreas[ it ];
  2361. int visLength = area->m_potentiallyVisibleAreas.Count();
  2362. avgVisLength += visLength;
  2363. if ( visLength < minVisLength )
  2364. {
  2365. minVisLength = visLength;
  2366. }
  2367. if ( visLength > maxVisLength )
  2368. {
  2369. maxVisLength = visLength;
  2370. }
  2371. if ( area->m_isInheritedFrom )
  2372. {
  2373. // another area is inheriting from our vis data, we can't inherit
  2374. continue;
  2375. }
  2376. // find adjacent area with the smallest change from our visibility list
  2377. CNavArea::CAreaBindInfoArray bestDelta;
  2378. CNavArea *anchor = NULL;
  2379. for( int dir = NORTH; dir < NUM_DIRECTIONS; ++dir )
  2380. {
  2381. int count = area->GetAdjacentCount( (NavDirType)dir );
  2382. for( int i=0; i<count; ++i )
  2383. {
  2384. CNavArea *adjArea = (CNavArea *)area->GetAdjacentArea( (NavDirType)dir, i );
  2385. // do not inherit from an area that is inheriting - use its ultimate source
  2386. if ( adjArea->m_inheritVisibilityFrom.area != NULL )
  2387. {
  2388. adjArea = adjArea->m_inheritVisibilityFrom.area;
  2389. if ( adjArea == area )
  2390. continue; // don't try to inherit visibility from ourselves
  2391. }
  2392. const CNavArea::CAreaBindInfoArray &delta = area->ComputeVisibilityDelta( adjArea );
  2393. // keep the smallest delta
  2394. if ( !anchor || ( anchor && delta.Count() < bestDelta.Count() ) )
  2395. {
  2396. bestDelta = delta;
  2397. anchor = adjArea;
  2398. Assert( anchor != area );
  2399. }
  2400. }
  2401. }
  2402. // if best delta is small enough, inherit our data from this anchor
  2403. if ( anchor && bestDelta.Count() <= nav_max_vis_delta_list_length.GetInt() && anchor != area )
  2404. {
  2405. // inherit from anchor area's visibility list
  2406. area->m_inheritVisibilityFrom.area = anchor;
  2407. area->m_potentiallyVisibleAreas = bestDelta;
  2408. // mark inherited-from area so it doesn't later try to inherit
  2409. anchor->m_isInheritedFrom = true;
  2410. }
  2411. else
  2412. {
  2413. // retain full list of visible areas
  2414. area->m_inheritVisibilityFrom.area = NULL;
  2415. }
  2416. }
  2417. if ( TheNavAreas.Count() )
  2418. {
  2419. avgVisLength /= TheNavAreas.Count();
  2420. }
  2421. Msg( "NavMesh Visibility List Lengths: min = %d, avg = %d, max = %d\n", minVisLength, avgVisLength, maxVisLength );
  2422. }