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

3314 lines
96 KiB

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