Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

654 lines
16 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. // AI Navigation areas
  8. // Author: Michael S. Booth ([email protected]), January 2003
  9. #include "cbase.h"
  10. #include "nav_mesh.h"
  11. #include "nav_node.h"
  12. #include "nav_pathfind.h"
  13. #include "nav_colors.h"
  14. #ifdef TERROR
  15. #include "TerrorShared.h"
  16. #endif
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. extern ConVar nav_area_bgcolor;
  20. unsigned int CNavLadder::m_nextID = 1;
  21. //--------------------------------------------------------------------------------------------------------------
  22. /**
  23. * Shift the nav area
  24. */
  25. void CNavLadder::Shift( const Vector &shift )
  26. {
  27. m_top += shift;
  28. m_bottom += shift;
  29. }
  30. //--------------------------------------------------------------------------------------------------------------
  31. void CNavLadder::CompressIDs( void )
  32. {
  33. m_nextID = 1;
  34. if ( TheNavMesh )
  35. {
  36. for ( int i=0; i<TheNavMesh->GetLadders().Count(); ++i )
  37. {
  38. CNavLadder *ladder = TheNavMesh->GetLadders()[i];
  39. ladder->m_id = m_nextID++;
  40. }
  41. }
  42. }
  43. //--------------------------------------------------------------------------------------------------------------
  44. CNavArea ** CNavLadder::GetConnection( LadderConnectionType dir )
  45. {
  46. switch ( dir )
  47. {
  48. case LADDER_TOP_FORWARD:
  49. return &m_topForwardArea;
  50. case LADDER_TOP_LEFT:
  51. return &m_topLeftArea;
  52. case LADDER_TOP_RIGHT:
  53. return &m_topRightArea;
  54. case LADDER_TOP_BEHIND:
  55. return &m_topBehindArea;
  56. case LADDER_BOTTOM:
  57. return &m_bottomArea;
  58. }
  59. return NULL;
  60. }
  61. //--------------------------------------------------------------------------------------------------------------
  62. void CNavLadder::OnSplit( CNavArea *original, CNavArea *alpha, CNavArea *beta )
  63. {
  64. for ( int con=0; con<NUM_LADDER_CONNECTIONS; ++con )
  65. {
  66. CNavArea ** areaConnection = GetConnection( (LadderConnectionType)con );
  67. if ( areaConnection && *areaConnection == original )
  68. {
  69. float alphaDistance = alpha->GetDistanceSquaredToPoint( m_top );
  70. float betaDistance = beta->GetDistanceSquaredToPoint( m_top );
  71. if ( alphaDistance < betaDistance )
  72. {
  73. *areaConnection = alpha;
  74. }
  75. else
  76. {
  77. *areaConnection = beta;
  78. }
  79. }
  80. }
  81. }
  82. //--------------------------------------------------------------------------------------------------------------
  83. /**
  84. * Connect this ladder to given area
  85. */
  86. void CNavLadder::ConnectTo( CNavArea *area )
  87. {
  88. float center = (m_top.z + m_bottom.z) * 0.5f;
  89. if (area->GetCenter().z > center)
  90. {
  91. // connect to top
  92. NavDirType dir;
  93. Vector dirVector = area->GetCenter() - m_top;
  94. if ( fabs( dirVector.x ) > fabs( dirVector.y ) )
  95. {
  96. if ( dirVector.x > 0.0f ) // east
  97. {
  98. dir = EAST;
  99. }
  100. else // west
  101. {
  102. dir = WEST;
  103. }
  104. }
  105. else
  106. {
  107. if ( dirVector.y > 0.0f ) // south
  108. {
  109. dir = SOUTH;
  110. }
  111. else // north
  112. {
  113. dir = NORTH;
  114. }
  115. }
  116. if ( m_dir == dir )
  117. {
  118. m_topBehindArea = area;
  119. }
  120. else if ( OppositeDirection( m_dir ) == dir )
  121. {
  122. m_topForwardArea = area;
  123. }
  124. else if ( DirectionLeft( m_dir ) == dir )
  125. {
  126. m_topLeftArea = area;
  127. }
  128. else
  129. {
  130. m_topRightArea = area;
  131. }
  132. }
  133. else
  134. {
  135. // connect to bottom
  136. m_bottomArea = area;
  137. }
  138. }
  139. //--------------------------------------------------------------------------------------------------------------
  140. /**
  141. * Destructor
  142. */
  143. CNavLadder::~CNavLadder()
  144. {
  145. // tell the other areas we are going away
  146. FOR_EACH_VEC( TheNavAreas, it )
  147. {
  148. CNavArea *area = TheNavAreas[ it ];
  149. area->OnDestroyNotify( this );
  150. }
  151. }
  152. //--------------------------------------------------------------------------------------------------------------
  153. /**
  154. * invoked when given area is going away
  155. */
  156. void CNavLadder::OnDestroyNotify( CNavArea *dead )
  157. {
  158. Disconnect( dead );
  159. }
  160. //--------------------------------------------------------------------------------------------------------------
  161. /**
  162. * Disconnect this ladder from given area
  163. */
  164. void CNavLadder::Disconnect( CNavArea *area )
  165. {
  166. if ( m_topForwardArea == area )
  167. {
  168. m_topForwardArea = NULL;
  169. }
  170. else if ( m_topLeftArea == area )
  171. {
  172. m_topLeftArea = NULL;
  173. }
  174. else if ( m_topRightArea == area )
  175. {
  176. m_topRightArea = NULL;
  177. }
  178. else if ( m_topBehindArea == area )
  179. {
  180. m_topBehindArea = NULL;
  181. }
  182. else if ( m_bottomArea == area )
  183. {
  184. m_bottomArea = NULL;
  185. }
  186. }
  187. //--------------------------------------------------------------------------------------------------------------
  188. /**
  189. * returns true if given area is connected in given direction
  190. */
  191. bool CNavLadder::IsConnected( const CNavArea *area, LadderDirectionType dir ) const
  192. {
  193. if ( dir == LADDER_DOWN )
  194. {
  195. return area == m_bottomArea;
  196. }
  197. else if ( dir == LADDER_UP )
  198. {
  199. return ( area == m_topForwardArea ||
  200. area == m_topLeftArea ||
  201. area == m_topRightArea ||
  202. area == m_topBehindArea );
  203. }
  204. else
  205. {
  206. return ( area == m_bottomArea ||
  207. area == m_topForwardArea ||
  208. area == m_topLeftArea ||
  209. area == m_topRightArea ||
  210. area == m_topBehindArea );
  211. }
  212. }
  213. //--------------------------------------------------------------------------------------------------------------
  214. void CNavLadder::SetDir( NavDirType dir )
  215. {
  216. m_dir = dir;
  217. m_normal.Init();
  218. AddDirectionVector( &m_normal, m_dir, 1.0f ); // worst-case, we have the NavDirType as a normal
  219. Vector from = (m_top + m_bottom) * 0.5f + m_normal * 5.0f;
  220. Vector to = from - m_normal * 32.0f;
  221. trace_t result;
  222. #ifdef TERROR
  223. // TERROR: use the MASK_ZOMBIESOLID_BRUSHONLY contents, since that's what zombies use
  224. UTIL_TraceLine( from, to, MASK_ZOMBIESOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  225. #else
  226. UTIL_TraceLine( from, to, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  227. #endif
  228. if (result.fraction != 1.0f)
  229. {
  230. bool climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0;
  231. if ( !climbableSurface )
  232. {
  233. climbableSurface = (result.contents & CONTENTS_LADDER) != 0;
  234. }
  235. if ( climbableSurface )
  236. {
  237. m_normal = result.plane.normal;
  238. }
  239. }
  240. }
  241. //--------------------------------------------------------------------------------------------------------------
  242. void CNavLadder::DrawLadder( void ) const
  243. {
  244. CBasePlayer *player = UTIL_GetListenServerHost();
  245. if (player == NULL)
  246. return;
  247. Vector dir;
  248. const Vector &eye = player->EyePosition();
  249. AngleVectors( player->EyeAngles() + player->GetViewPunchAngle(), &dir );
  250. float dx = eye.x - m_bottom.x;
  251. float dy = eye.y - m_bottom.y;
  252. Vector2D eyeDir( dx, dy );
  253. eyeDir.NormalizeInPlace();
  254. bool isSelected = ( this == TheNavMesh->GetSelectedLadder() );
  255. bool isMarked = ( this == TheNavMesh->GetMarkedLadder() );
  256. bool isFront = DotProduct2D( eyeDir, GetNormal().AsVector2D() ) > 0;
  257. if ( TheNavMesh->IsEditMode( CNavMesh::PLACE_PAINTING ) )
  258. {
  259. isSelected = isMarked = false;
  260. isFront = true;
  261. }
  262. // Highlight ladder entity ------------------------------------------------
  263. CBaseEntity *ladderEntity = m_ladderEntity.Get();
  264. if ( ladderEntity )
  265. {
  266. ladderEntity->DrawAbsBoxOverlay();
  267. }
  268. // Draw 'ladder' lines ----------------------------------------------------
  269. NavEditColor ladderColor = NavNormalColor;
  270. if ( isFront )
  271. {
  272. if ( isMarked )
  273. {
  274. ladderColor = NavMarkedColor;
  275. }
  276. else if ( isSelected )
  277. {
  278. ladderColor = NavSelectedColor;
  279. }
  280. else
  281. {
  282. ladderColor = NavSamePlaceColor;
  283. }
  284. }
  285. else if ( isMarked )
  286. {
  287. ladderColor = NavMarkedColor;
  288. }
  289. else if ( isSelected )
  290. {
  291. ladderColor = NavSelectedColor;
  292. }
  293. Vector right(0, 0, 0), up( 0, 0, 0 );
  294. VectorVectors( GetNormal(), right, up );
  295. if ( up.z <= 0.0f )
  296. {
  297. AssertMsg( false, "A nav ladder has an invalid normal" );
  298. up.Init( 0, 0, 1 );
  299. }
  300. right *= m_width * 0.5f;
  301. Vector bottomLeft = m_bottom - right;
  302. Vector bottomRight = m_bottom + right;
  303. Vector topLeft = m_top - right;
  304. Vector topRight = m_top + right;
  305. int bgcolor[4];
  306. if ( 4 == sscanf( nav_area_bgcolor.GetString(), "%d %d %d %d", &(bgcolor[0]), &(bgcolor[1]), &(bgcolor[2]), &(bgcolor[3]) ) )
  307. {
  308. for ( int i=0; i<4; ++i )
  309. bgcolor[i] = clamp( bgcolor[i], 0, 255 );
  310. if ( bgcolor[3] > 0 )
  311. {
  312. Vector offset( 0, 0, 0 );
  313. AddDirectionVector( &offset, OppositeDirection( m_dir ), 1 );
  314. NDebugOverlay::Triangle( topLeft+offset, topRight+offset, bottomRight+offset, bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3], true, 0.15f );
  315. NDebugOverlay::Triangle( bottomRight+offset, bottomLeft+offset, topLeft+offset, bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3], true, 0.15f );
  316. }
  317. }
  318. NavDrawLine( topLeft, bottomLeft, ladderColor );
  319. NavDrawLine( topRight, bottomRight, ladderColor );
  320. while ( bottomRight.z < topRight.z )
  321. {
  322. NavDrawLine( bottomRight, bottomLeft, ladderColor );
  323. bottomRight += up * (GenerationStepSize/2);
  324. bottomLeft += up * (GenerationStepSize/2);
  325. }
  326. // Draw connector lines ---------------------------------------------------
  327. if ( !TheNavMesh->IsEditMode( CNavMesh::PLACE_PAINTING ) )
  328. {
  329. Vector bottom = m_bottom;
  330. Vector top = m_top;
  331. NavDrawLine( top, bottom, NavConnectedTwoWaysColor );
  332. if (m_bottomArea)
  333. {
  334. float offset = GenerationStepSize;
  335. const Vector& areaBottom = m_bottomArea->GetCenter();
  336. // don't draw the bottom connection too high if the ladder is very short
  337. if ( top.z - bottom.z < GenerationStepSize * 1.5f )
  338. offset = 0.0f;
  339. // don't draw the bottom connection too high if the ladder is high above the area
  340. if ( bottom.z - areaBottom.z > GenerationStepSize * 1.5f )
  341. offset = 0.0f;
  342. NavDrawLine( bottom + Vector( 0, 0, offset ), areaBottom, ((m_bottomArea->IsConnected( this, LADDER_UP ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
  343. }
  344. if (m_topForwardArea)
  345. NavDrawLine( top, m_topForwardArea->GetCenter(), ((m_topForwardArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
  346. if (m_topLeftArea)
  347. NavDrawLine( top, m_topLeftArea->GetCenter(), ((m_topLeftArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
  348. if (m_topRightArea)
  349. NavDrawLine( top, m_topRightArea->GetCenter(), ((m_topRightArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
  350. if (m_topBehindArea)
  351. NavDrawLine( top, m_topBehindArea->GetCenter(), ((m_topBehindArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
  352. }
  353. }
  354. //--------------------------------------------------------------------------------------------------------------
  355. void CNavLadder::DrawConnectedAreas( void )
  356. {
  357. CUtlVector< CNavArea * > areas;
  358. if ( m_topForwardArea )
  359. areas.AddToTail( m_topForwardArea );
  360. if ( m_topLeftArea )
  361. areas.AddToTail( m_topLeftArea );
  362. if ( m_topRightArea )
  363. areas.AddToTail( m_topRightArea );
  364. if ( m_topBehindArea )
  365. areas.AddToTail( m_topBehindArea );
  366. if ( m_bottomArea )
  367. areas.AddToTail( m_bottomArea );
  368. for ( int i=0; i<areas.Count(); ++i )
  369. {
  370. CNavArea *adj = areas[i];
  371. adj->Draw();
  372. if ( !TheNavMesh->IsEditMode( CNavMesh::PLACE_PAINTING ) )
  373. {
  374. adj->DrawHidingSpots();
  375. }
  376. }
  377. }
  378. //--------------------------------------------------------------------------------------------------------------
  379. /**
  380. * invoked when a game round restarts
  381. */
  382. void CNavLadder::OnRoundRestart( void )
  383. {
  384. FindLadderEntity();
  385. }
  386. //--------------------------------------------------------------------------------------------------------------
  387. void CNavLadder::FindLadderEntity( void )
  388. {
  389. m_ladderEntity = gEntList.FindEntityByClassnameNearest( "func_simpleladder", (m_top + m_bottom) * 0.5f, HalfHumanWidth );
  390. }
  391. //--------------------------------------------------------------------------------------------------------------
  392. /**
  393. * Save a navigation ladder to the opened binary stream
  394. */
  395. void CNavLadder::Save( CUtlBuffer &fileBuffer, unsigned int version ) const
  396. {
  397. // save ID
  398. fileBuffer.PutUnsignedInt( m_id );
  399. // save extent of ladder
  400. fileBuffer.PutFloat( m_width );
  401. // save top endpoint of ladder
  402. fileBuffer.PutFloat( m_top.x );
  403. fileBuffer.PutFloat( m_top.y );
  404. fileBuffer.PutFloat( m_top.z );
  405. // save bottom endpoint of ladder
  406. fileBuffer.PutFloat( m_bottom.x );
  407. fileBuffer.PutFloat( m_bottom.y );
  408. fileBuffer.PutFloat( m_bottom.z );
  409. // save ladder length
  410. fileBuffer.PutFloat( m_length );
  411. // save direction
  412. fileBuffer.PutUnsignedInt( m_dir );
  413. // save IDs of connecting areas
  414. unsigned int id;
  415. id = ( m_topForwardArea ) ? m_topForwardArea->GetID() : 0;
  416. fileBuffer.PutUnsignedInt( id );
  417. id = ( m_topLeftArea ) ? m_topLeftArea->GetID() : 0;
  418. fileBuffer.PutUnsignedInt( id );
  419. id = ( m_topRightArea ) ? m_topRightArea->GetID() : 0;
  420. fileBuffer.PutUnsignedInt( id );
  421. id = ( m_topBehindArea ) ? m_topBehindArea->GetID() : 0;
  422. fileBuffer.PutUnsignedInt( id );
  423. id = ( m_bottomArea ) ? m_bottomArea->GetID() : 0;
  424. fileBuffer.PutUnsignedInt( id );
  425. }
  426. //--------------------------------------------------------------------------------------------------------------
  427. /**
  428. * Load a navigation ladder from the opened binary stream
  429. */
  430. void CNavLadder::Load( CUtlBuffer &fileBuffer, unsigned int version )
  431. {
  432. // load ID
  433. m_id = fileBuffer.GetUnsignedInt();
  434. // update nextID to avoid collisions
  435. if (m_id >= m_nextID)
  436. m_nextID = m_id+1;
  437. // load extent of ladder
  438. m_width = fileBuffer.GetFloat();
  439. // load top endpoint of ladder
  440. m_top.x = fileBuffer.GetFloat();
  441. m_top.y = fileBuffer.GetFloat();
  442. m_top.z = fileBuffer.GetFloat();
  443. // load bottom endpoint of ladder
  444. m_bottom.x = fileBuffer.GetFloat();
  445. m_bottom.y = fileBuffer.GetFloat();
  446. m_bottom.z = fileBuffer.GetFloat();
  447. // load ladder length
  448. m_length = fileBuffer.GetFloat();
  449. // load direction
  450. m_dir = (NavDirType)fileBuffer.GetUnsignedInt();
  451. SetDir( m_dir ); // regenerate the surface normal
  452. // load dangling status
  453. if ( version == 6 )
  454. {
  455. bool m_isDangling;
  456. fileBuffer.Get( &m_isDangling, sizeof(m_isDangling) );
  457. }
  458. // load IDs of connecting areas
  459. unsigned int id;
  460. id = fileBuffer.GetUnsignedInt();
  461. m_topForwardArea = TheNavMesh->GetNavAreaByID( id );
  462. id = fileBuffer.GetUnsignedInt();
  463. m_topLeftArea = TheNavMesh->GetNavAreaByID( id );
  464. id = fileBuffer.GetUnsignedInt();
  465. m_topRightArea = TheNavMesh->GetNavAreaByID( id );
  466. id = fileBuffer.GetUnsignedInt();
  467. m_topBehindArea = TheNavMesh->GetNavAreaByID( id );
  468. id = fileBuffer.GetUnsignedInt();
  469. m_bottomArea = TheNavMesh->GetNavAreaByID( id );
  470. if ( !m_bottomArea )
  471. {
  472. DevMsg( "ERROR: Unconnected ladder #%d bottom at ( %g, %g, %g )\n", m_id, m_bottom.x, m_bottom.y, m_bottom.z );
  473. DevWarning( "nav_unmark; nav_mark ladder %d; nav_warp_to_mark\n", m_id );
  474. }
  475. else if (!m_topForwardArea && !m_topLeftArea && !m_topRightArea) // can't include behind area, since it is not used when going up a ladder
  476. {
  477. DevMsg( "ERROR: Unconnected ladder #%d top at ( %g, %g, %g )\n", m_id, m_top.x, m_top.y, m_top.z );
  478. DevWarning( "nav_unmark; nav_mark ladder %d; nav_warp_to_mark\n", m_id );
  479. }
  480. FindLadderEntity();
  481. }
  482. //--------------------------------------------------------------------------------------------------------------
  483. /**
  484. * Functor returns true if ladder is free, or false if someone is on the ladder
  485. */
  486. class IsLadderFreeFunctor
  487. {
  488. public:
  489. IsLadderFreeFunctor( const CNavLadder *ladder, const CBasePlayer *ignore )
  490. {
  491. m_ladder = ladder;
  492. m_ignore = ignore;
  493. }
  494. bool operator() ( CBasePlayer *player )
  495. {
  496. if (player == m_ignore)
  497. return true;
  498. if (!player->IsOnLadder())
  499. return true;
  500. // player is on a ladder - is it this one?
  501. const Vector &feet = player->GetAbsOrigin();
  502. if (feet.z > m_ladder->m_top.z + HalfHumanHeight)
  503. return true;
  504. if (feet.z + HumanHeight < m_ladder->m_bottom.z - HalfHumanHeight)
  505. return true;
  506. Vector2D away( m_ladder->m_bottom.x - feet.x, m_ladder->m_bottom.y - feet.y );
  507. const float onLadderRange = 50.0f;
  508. return away.IsLengthGreaterThan( onLadderRange );
  509. }
  510. const CNavLadder *m_ladder;
  511. const CBasePlayer *m_ignore;
  512. };
  513. //--------------------------------------------------------------------------------------------------------------
  514. /**
  515. * Return true if someone is on this ladder
  516. */
  517. bool CNavLadder::IsInUse( const CBasePlayer *ignore ) const
  518. {
  519. IsLadderFreeFunctor isLadderFree( this, ignore );
  520. return !ForEachPlayer( isLadderFree );
  521. }
  522. //--------------------------------------------------------------------------------------------------------------
  523. Vector CNavLadder::GetPosAtHeight( float height ) const
  524. {
  525. if ( height < m_bottom.z )
  526. {
  527. return m_bottom;
  528. }
  529. if ( height > m_top.z )
  530. {
  531. return m_top;
  532. }
  533. if ( m_top.z == m_bottom.z )
  534. {
  535. return m_top;
  536. }
  537. float percent = ( height - m_bottom.z ) / ( m_top.z - m_bottom.z );
  538. return m_top * percent + m_bottom * ( 1.0f - percent );
  539. }
  540. //--------------------------------------------------------------------------------------------------------------