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.

3964 lines
97 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_edit.cpp
  9. // Implementation of Navigation Mesh edit mode
  10. // Author: Michael Booth, 2003-2004
  11. #include "cbase.h"
  12. #include "nav_mesh.h"
  13. #include "nav_pathfind.h"
  14. #include "nav_node.h"
  15. #include "nav_colors.h"
  16. #include "Color.h"
  17. #include "tier0/vprof.h"
  18. #include "collisionutils.h"
  19. #include "world.h"
  20. #include "functorutils.h"
  21. #include "team.h"
  22. #ifdef TERROR
  23. #include "TerrorShared.h"
  24. #endif
  25. #ifdef DOTA_SERVER_DLL
  26. #include "dota_npc_base.h"
  27. #include "dota_player.h"
  28. #endif
  29. // NOTE: This has to be the last file included!
  30. #include "tier0/memdbgon.h"
  31. ConVar nav_show_area_info( "nav_show_area_info", "0.5", FCVAR_CHEAT, "Duration in seconds to show nav area ID and attributes while editing" );
  32. ConVar nav_snap_to_grid( "nav_snap_to_grid", "0", FCVAR_CHEAT, "Snap to the nav generation grid when creating new nav areas" );
  33. ConVar nav_create_place_on_ground( "nav_create_place_on_ground", "0", FCVAR_CHEAT, "If true, nav areas will be placed flush with the ground when created by hand." );
  34. #ifdef DEBUG
  35. ConVar nav_draw_limit( "nav_draw_limit", "50", FCVAR_CHEAT, "The maximum number of areas to draw in edit mode" );
  36. #else
  37. ConVar nav_draw_limit( "nav_draw_limit", "500", FCVAR_CHEAT, "The maximum number of areas to draw in edit mode" );
  38. #endif
  39. ConVar nav_solid_props( "nav_solid_props", "0", FCVAR_CHEAT, "Make props solid to nav generation/editing" );
  40. ConVar nav_create_area_at_feet( "nav_create_area_at_feet", "0", FCVAR_CHEAT, "Anchor nav_begin_area Z to editing player's feet" );
  41. ConVar nav_drag_selection_volume_zmax_offset( "nav_drag_selection_volume_zmax_offset", "32", FCVAR_REPLICATED, "The offset of the nav drag volume top from center" );
  42. ConVar nav_drag_selection_volume_zmin_offset( "nav_drag_selection_volume_zmin_offset", "32", FCVAR_REPLICATED, "The offset of the nav drag volume bottom from center" );
  43. extern void GetNavUIEditVectors( Vector *pos, Vector *forward );
  44. Color s_dragSelectionSetAddColor( 100, 255, 100, 96 );
  45. Color s_dragSelectionSetDeleteColor( 255, 100, 100, 96 );
  46. #if DEBUG_NAV_NODES
  47. extern ConVar nav_show_nodes;
  48. #endif // DEBUG_NAV_NODES
  49. //--------------------------------------------------------------------------------------------------------------
  50. int GetGridSize( bool forceGrid = false )
  51. {
  52. if ( TheNavMesh->IsGenerating() )
  53. {
  54. return (int)GenerationStepSize;
  55. }
  56. int snapVal = nav_snap_to_grid.GetInt();
  57. if ( forceGrid && !snapVal )
  58. {
  59. snapVal = 1;
  60. }
  61. if ( snapVal == 0 )
  62. {
  63. return 0;
  64. }
  65. int scale = (int)GenerationStepSize;
  66. switch ( snapVal )
  67. {
  68. case 3:
  69. scale = 1;
  70. break;
  71. case 2:
  72. scale = 5;
  73. break;
  74. case 1:
  75. default:
  76. break;
  77. }
  78. return scale;
  79. }
  80. //--------------------------------------------------------------------------------------------------------------
  81. Vector CNavMesh::SnapToGrid( const Vector& in, bool snapX, bool snapY, bool forceGrid ) const
  82. {
  83. int scale = GetGridSize( forceGrid );
  84. if ( !scale )
  85. {
  86. return in;
  87. }
  88. Vector out( in );
  89. if ( snapX )
  90. {
  91. out.x = RoundToUnits( in.x, scale );
  92. }
  93. if ( snapY )
  94. {
  95. out.y = RoundToUnits( in.y, scale );
  96. }
  97. return out;
  98. }
  99. //--------------------------------------------------------------------------------------------------------------
  100. float CNavMesh::SnapToGrid( float x, bool forceGrid ) const
  101. {
  102. int scale = GetGridSize( forceGrid );
  103. if ( !scale )
  104. {
  105. return x;
  106. }
  107. x = RoundToUnits( x, scale );
  108. return x;
  109. }
  110. //--------------------------------------------------------------------------------------------------------------
  111. void CNavMesh::GetEditVectors( Vector *pos, Vector *forward )
  112. {
  113. if ( !pos || !forward )
  114. {
  115. return;
  116. }
  117. CBasePlayer *player = UTIL_GetListenServerHost();
  118. if ( !player )
  119. {
  120. return;
  121. }
  122. //DOTA places the edit cursor where the 2D cursor is located.
  123. #ifdef DOTA_SERVER_DLL
  124. CDOTAPlayer *pDOTAPlayer = ToDOTAPlayer( player );
  125. if ( pDOTAPlayer && pDOTAPlayer->GetMoveType() != MOVETYPE_NOCLIP )
  126. {
  127. Vector dir = pDOTAPlayer->GetCrosshairTracePos() - player->EyePosition();
  128. VectorNormalize( dir );
  129. *forward = dir;
  130. }
  131. else
  132. {
  133. AngleVectors( player->EyeAngles() + player->GetPunchAngle(), forward );
  134. }
  135. #else
  136. Vector dir;
  137. AngleVectors( player->EyeAngles() + player->GetPunchAngle(), forward );
  138. #endif
  139. *pos = player->EyePosition();
  140. #ifdef SERVER_USES_VGUI
  141. // GetNavUIEditVectors( pos, forward );
  142. #endif // SERVER_USES_VGUI
  143. }
  144. //--------------------------------------------------------------------------------------------------------------
  145. /**
  146. * Change the edit mode
  147. */
  148. void CNavMesh::SetEditMode( EditModeType mode )
  149. {
  150. m_markedLadder = NULL;
  151. m_markedArea = NULL;
  152. m_markedCorner = NUM_CORNERS;
  153. m_editMode = mode;
  154. m_isContinuouslySelecting = false;
  155. m_isContinuouslyDeselecting = false;
  156. m_bIsDragDeselecting = false;
  157. }
  158. //--------------------------------------------------------------------------------------------------------------
  159. bool CNavMesh::FindNavAreaOrLadderAlongRay( const Vector &start, const Vector &end, CNavArea **bestArea, CNavLadder **bestLadder, CNavArea *ignore )
  160. {
  161. if ( !m_grid.Count() )
  162. return false;
  163. Ray_t ray;
  164. ray.Init( start, end, vec3_origin, vec3_origin );
  165. *bestArea = NULL;
  166. *bestLadder = NULL;
  167. float bestDist = 1.0f; // 0..1 fraction
  168. for ( int i=0; i<m_ladders.Count(); ++i )
  169. {
  170. CNavLadder *ladder = m_ladders[i];
  171. Vector left( 0, 0, 0), right(0, 0, 0), up( 0, 0, 0);
  172. VectorVectors( ladder->GetNormal(), right, up );
  173. right *= ladder->m_width * 0.5f;
  174. left = -right;
  175. Vector c1 = ladder->m_top + right;
  176. Vector c2 = ladder->m_top + left;
  177. Vector c3 = ladder->m_bottom + right;
  178. Vector c4 = ladder->m_bottom + left;
  179. float dist = IntersectRayWithTriangle( ray, c1, c2, c4, false );
  180. if ( dist > 0 && dist < bestDist )
  181. {
  182. *bestLadder = ladder;
  183. bestDist = dist;
  184. }
  185. dist = IntersectRayWithTriangle( ray, c1, c4, c3, false );
  186. if ( dist > 0 && dist < bestDist )
  187. {
  188. *bestLadder = ladder;
  189. bestDist = dist;
  190. }
  191. }
  192. Extent extent;
  193. extent.lo = extent.hi = start;
  194. extent.Encompass( end );
  195. int loX = WorldToGridX( extent.lo.x );
  196. int loY = WorldToGridY( extent.lo.y );
  197. int hiX = WorldToGridX( extent.hi.x );
  198. int hiY = WorldToGridY( extent.hi.y );
  199. for( int y = loY; y <= hiY; ++y )
  200. {
  201. for( int x = loX; x <= hiX; ++x )
  202. {
  203. NavAreaVector &areaGrid = m_grid[ x + y*m_gridSizeX ];
  204. FOR_EACH_VEC( areaGrid, it )
  205. {
  206. CNavArea *area = areaGrid[ it ];
  207. if ( area == ignore )
  208. continue;
  209. Vector nw = area->m_nwCorner;
  210. Vector se = area->m_seCorner;
  211. Vector ne, sw;
  212. ne.x = se.x;
  213. ne.y = nw.y;
  214. ne.z = area->m_neZ;
  215. sw.x = nw.x;
  216. sw.y = se.y;
  217. sw.z = area->m_swZ;
  218. float dist = IntersectRayWithTriangle( ray, nw, ne, se, false );
  219. if ( dist > 0 && dist < bestDist )
  220. {
  221. *bestArea = area;
  222. bestDist = dist;
  223. }
  224. dist = IntersectRayWithTriangle( ray, se, sw, nw, false );
  225. if ( dist > 0 && dist < bestDist )
  226. {
  227. *bestArea = area;
  228. bestDist = dist;
  229. }
  230. }
  231. }
  232. }
  233. if ( *bestArea )
  234. {
  235. *bestLadder = NULL;
  236. }
  237. return bestDist < 1.0f;
  238. }
  239. //--------------------------------------------------------------------------------------------------------------
  240. /**
  241. * Convenience function to find the nav area a player is looking at, for editing commands
  242. */
  243. bool CNavMesh::FindActiveNavArea( void )
  244. {
  245. VPROF( "CNavMesh::FindActiveNavArea" );
  246. m_splitAlongX = false;
  247. m_splitEdge = 0.0f;
  248. m_selectedArea = NULL;
  249. m_climbableSurface = false;
  250. m_selectedLadder = NULL;
  251. CBasePlayer *player = UTIL_GetListenServerHost();
  252. if ( player == NULL )
  253. return false;
  254. Vector from, dir;
  255. GetEditVectors( &from, &dir );
  256. float maxRange = 2000.0f; // 500
  257. bool isClippingRayAtFeet = false;
  258. if ( nav_create_area_at_feet.GetBool() )
  259. {
  260. if ( dir.z < 0 )
  261. {
  262. float eyeHeight = player->GetViewOffset().z;
  263. if ( eyeHeight != 0.0f )
  264. {
  265. float rayHeight = -dir.z * maxRange;
  266. maxRange = maxRange * eyeHeight / rayHeight;
  267. isClippingRayAtFeet = true;
  268. }
  269. }
  270. }
  271. Vector to = from + maxRange * dir;
  272. trace_t result;
  273. CTraceFilterWalkableEntities filter( NULL, COLLISION_GROUP_NONE, WALK_THRU_EVERYTHING );
  274. UTIL_TraceLine( from, to, (nav_solid_props.GetBool()) ? MASK_NPCSOLID : MASK_NPCSOLID_BRUSHONLY, &filter, &result );
  275. if (result.fraction != 1.0f)
  276. {
  277. if ( !IsEditMode( CREATING_AREA ) )
  278. {
  279. m_climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0;
  280. if ( !m_climbableSurface )
  281. {
  282. m_climbableSurface = (result.contents & CONTENTS_LADDER) != 0;
  283. }
  284. m_surfaceNormal = result.plane.normal;
  285. if ( m_climbableSurface )
  286. {
  287. // check if we're on the same plane as the original point when we're building a ladder
  288. if ( IsEditMode( CREATING_LADDER ) )
  289. {
  290. if ( m_surfaceNormal != m_ladderNormal )
  291. {
  292. m_climbableSurface = false;
  293. }
  294. }
  295. if ( m_surfaceNormal.z > 0.9f )
  296. {
  297. m_climbableSurface = false; // don't try to build ladders on flat ground
  298. }
  299. }
  300. }
  301. if ( ( m_climbableSurface && !IsEditMode( CREATING_LADDER ) ) || !IsEditMode( CREATING_AREA ) )
  302. {
  303. float closestDistSqr = 200.0f * 200.0f;
  304. for ( int i=0; i<m_ladders.Count(); ++i )
  305. {
  306. CNavLadder *ladder = m_ladders[i];
  307. Vector absMin = ladder->m_bottom;
  308. Vector absMax = ladder->m_top;
  309. Vector left( 0, 0, 0), right(0, 0, 0), up( 0, 0, 0);
  310. VectorVectors( ladder->GetNormal(), right, up );
  311. right *= ladder->m_width * 0.5f;
  312. left = -right;
  313. absMin.x += MIN( left.x, right.x );
  314. absMin.y += MIN( left.y, right.y );
  315. absMax.x += MAX( left.x, right.x );
  316. absMax.y += MAX( left.y, right.y );
  317. Extent e;
  318. e.lo = absMin + Vector( -5, -5, -5 );
  319. e.hi = absMax + Vector( 5, 5, 5 );
  320. if ( e.Contains( m_editCursorPos ) )
  321. {
  322. m_selectedLadder = ladder;
  323. break;
  324. }
  325. if ( !m_climbableSurface )
  326. continue;
  327. Vector p1 = (ladder->m_bottom + ladder->m_top)/2;
  328. Vector p2 = m_editCursorPos;
  329. float distSqr = p1.DistToSqr( p2 );
  330. if ( distSqr < closestDistSqr )
  331. {
  332. m_selectedLadder = ladder;
  333. closestDistSqr = distSqr;
  334. }
  335. }
  336. }
  337. m_editCursorPos = result.endpos;
  338. // find the area the player is pointing at
  339. if ( !m_climbableSurface && !m_selectedLadder )
  340. {
  341. // Try to clip our trace to nav areas
  342. FindNavAreaOrLadderAlongRay( result.startpos, result.endpos + 100.0f * dir, &m_selectedArea, &m_selectedLadder ); // extend a few units into the ground
  343. // Failing that, get the closest area to the endpoint
  344. if ( !m_selectedArea && !m_selectedLadder )
  345. {
  346. m_selectedArea = TheNavMesh->GetNearestNavArea( result.endpos, false, 500.0f );
  347. }
  348. }
  349. if ( m_selectedArea )
  350. {
  351. float yaw = player->EyeAngles().y;
  352. while( yaw > 360.0f )
  353. yaw -= 360.0f;
  354. while( yaw < 0.0f )
  355. yaw += 360.0f;
  356. if ((yaw < 45.0f || yaw > 315.0f) || (yaw > 135.0f && yaw < 225.0f))
  357. {
  358. m_splitEdge = SnapToGrid( result.endpos.y, true );
  359. m_splitAlongX = true;
  360. }
  361. else
  362. {
  363. m_splitEdge = SnapToGrid( result.endpos.x, true );
  364. m_splitAlongX = false;
  365. }
  366. }
  367. if ( !m_climbableSurface && !IsEditMode( CREATING_LADDER ) )
  368. {
  369. m_editCursorPos = SnapToGrid( m_editCursorPos );
  370. }
  371. return true;
  372. }
  373. else if ( isClippingRayAtFeet )
  374. {
  375. m_editCursorPos = SnapToGrid( result.endpos );
  376. }
  377. if ( IsEditMode( CREATING_LADDER ) || IsEditMode( CREATING_AREA ) )
  378. return false;
  379. // We started solid. Look for areas in front of us.
  380. FindNavAreaOrLadderAlongRay( from, to, &m_selectedArea, &m_selectedLadder );
  381. return (m_selectedArea != NULL || m_selectedLadder != NULL || isClippingRayAtFeet);
  382. }
  383. //--------------------------------------------------------------------------------------------------------------
  384. bool CNavMesh::FindLadderCorners( Vector *corner1, Vector *corner2, Vector *corner3 )
  385. {
  386. if ( !corner1 || !corner2 || !corner3 )
  387. return false;
  388. Vector ladderRight, ladderUp;
  389. VectorVectors( m_ladderNormal, ladderRight, ladderUp );
  390. Vector from, dir;
  391. GetEditVectors( &from, &dir );
  392. const float maxDist = 100000.0f;
  393. Ray_t ray;
  394. ray.Init( from, from + dir * maxDist, vec3_origin, vec3_origin );
  395. *corner1 = m_ladderAnchor + ladderUp * maxDist + ladderRight * maxDist;
  396. *corner2 = m_ladderAnchor + ladderUp * maxDist - ladderRight * maxDist;
  397. *corner3 = m_ladderAnchor - ladderUp * maxDist - ladderRight * maxDist;
  398. float dist = IntersectRayWithTriangle( ray, *corner1, *corner2, *corner3, false );
  399. if ( dist < 0 )
  400. {
  401. *corner2 = m_ladderAnchor - ladderUp * maxDist + ladderRight * maxDist;
  402. dist = IntersectRayWithTriangle( ray, *corner1, *corner2, *corner3, false );
  403. }
  404. *corner3 = m_editCursorPos;
  405. if ( dist > 0 && dist < maxDist )
  406. {
  407. *corner3 = from + dir * dist * maxDist;
  408. float vertDistance = corner3->z - m_ladderAnchor.z;
  409. float val = vertDistance / ladderUp.z;
  410. *corner1 = m_ladderAnchor + val * ladderUp;
  411. *corner2 = *corner3 - val * ladderUp;
  412. return true;
  413. }
  414. return false;
  415. }
  416. //--------------------------------------------------------------------------------------------------------------
  417. bool CheckForClimbableSurface( const Vector &start, const Vector &end )
  418. {
  419. trace_t result;
  420. UTIL_TraceLine( start, end, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
  421. bool climbableSurface = false;
  422. if (result.fraction != 1.0f)
  423. {
  424. climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0;
  425. if ( !climbableSurface )
  426. {
  427. climbableSurface = (result.contents & CONTENTS_LADDER) != 0;
  428. }
  429. }
  430. return climbableSurface;
  431. }
  432. //--------------------------------------------------------------------------------------------------------------
  433. void StepAlongClimbableSurface( Vector &pos, const Vector &increment, const Vector &probe )
  434. {
  435. while ( CheckForClimbableSurface( pos + increment - probe, pos + increment + probe ) )
  436. {
  437. pos += increment;
  438. }
  439. }
  440. //--------------------------------------------------------------------------------------------------------------
  441. void CNavMesh::CommandNavBuildLadder( void )
  442. {
  443. if ( !IsEditMode( NORMAL ) || !m_climbableSurface )
  444. {
  445. return;
  446. }
  447. // We've got a ladder at m_editCursorPos, with a normal of m_surfaceNormal
  448. Vector right, up;
  449. VectorVectors( -m_surfaceNormal, right, up );
  450. m_ladderNormal = m_surfaceNormal;
  451. Vector startPos = m_editCursorPos;
  452. Vector leftEdge = startPos;
  453. Vector rightEdge = startPos;
  454. // trace to the sides to find the width
  455. Vector probe = m_surfaceNormal * -HalfHumanWidth;
  456. const float StepSize = 1.0f;
  457. StepAlongClimbableSurface( leftEdge, right * -StepSize, probe );
  458. StepAlongClimbableSurface( rightEdge, right * StepSize, probe );
  459. Vector topEdge = (leftEdge + rightEdge) * 0.5f;
  460. Vector bottomEdge = topEdge;
  461. StepAlongClimbableSurface( topEdge, up * StepSize, probe );
  462. StepAlongClimbableSurface( bottomEdge, up * -StepSize, probe );
  463. Vector top = (leftEdge + rightEdge) * 0.5f;
  464. top.z = topEdge.z;
  465. Vector bottom = top;
  466. bottom.z = bottomEdge.z;
  467. CreateLadder( topEdge, bottomEdge, leftEdge.DistTo( rightEdge ), m_ladderNormal.AsVector2D(), 0.0f );
  468. }
  469. //--------------------------------------------------------------------------------------------------------------
  470. /**
  471. * Flood fills all areas with current place
  472. */
  473. class PlaceFloodFillFunctor
  474. {
  475. public:
  476. PlaceFloodFillFunctor( CNavArea *area )
  477. {
  478. m_initialPlace = area->GetPlace();
  479. }
  480. bool operator() ( CNavArea *area )
  481. {
  482. if (area->GetPlace() != m_initialPlace)
  483. return false;
  484. area->SetPlace( TheNavMesh->GetNavPlace() );
  485. return true;
  486. }
  487. private:
  488. unsigned int m_initialPlace;
  489. };
  490. //--------------------------------------------------------------------------------------------------------------
  491. /**
  492. * Called when edit mode has just been enabled
  493. */
  494. void CNavMesh::OnEditModeStart( void )
  495. {
  496. ClearSelectedSet();
  497. m_isContinuouslySelecting = false;
  498. m_isContinuouslyDeselecting = false;
  499. }
  500. //--------------------------------------------------------------------------------------------------------------
  501. /**
  502. * Called when edit mode has just been disabled
  503. */
  504. void CNavMesh::OnEditModeEnd( void )
  505. {
  506. }
  507. //--------------------------------------------------------------------------------------------------------------
  508. class DrawSelectedSet
  509. {
  510. public:
  511. DrawSelectedSet( const Vector &shift )
  512. {
  513. m_count = 0;
  514. m_shift = shift;
  515. }
  516. bool operator() ( CNavArea *area )
  517. {
  518. if (TheNavMesh->IsInSelectedSet( area ))
  519. {
  520. area->DrawSelectedSet( m_shift );
  521. ++m_count;
  522. }
  523. return (m_count < nav_draw_limit.GetInt());
  524. }
  525. int m_count;
  526. Vector m_shift;
  527. };
  528. //--------------------------------------------------------------------------------------------------------
  529. class AddToDragSet
  530. {
  531. public:
  532. AddToDragSet( Extent &area, int zMin, int zMax, bool bDragDeselecting )
  533. {
  534. m_nTolerance = 1;
  535. m_dragArea = area;
  536. m_zMin = zMin - m_nTolerance;
  537. m_zMax = zMax + m_nTolerance;
  538. m_bDragDeselecting = bDragDeselecting;
  539. }
  540. bool operator() ( CNavArea *area )
  541. {
  542. bool bShouldBeInSelectedSet = m_bDragDeselecting;
  543. if ( ( TheNavMesh->IsInSelectedSet( area ) == bShouldBeInSelectedSet ) && area->IsOverlapping( m_dragArea ) && area->GetCenter().z >= m_zMin && area->GetCenter().z <= m_zMax )
  544. {
  545. TheNavMesh->AddToDragSelectionSet( area );
  546. }
  547. return true;
  548. }
  549. Extent m_dragArea;
  550. int m_zMin;
  551. int m_zMax;
  552. int m_nTolerance;
  553. bool m_bDragDeselecting;
  554. };
  555. //--------------------------------------------------------------------------------------------------------------
  556. void CNavMesh::UpdateDragSelectionSet( void )
  557. {
  558. CBasePlayer *player = UTIL_GetListenServerHost();
  559. if (player == NULL)
  560. return;
  561. Extent dragArea;
  562. int xmin = MIN( m_anchor.x, m_editCursorPos.x );
  563. int xmax = MAX( m_anchor.x, m_editCursorPos.x );
  564. int ymin = MIN( m_anchor.y, m_editCursorPos.y );
  565. int ymax = MAX( m_anchor.y, m_editCursorPos.y );
  566. dragArea.lo = Vector( xmin, ymin, m_anchor.z );
  567. dragArea.hi = Vector( xmax, ymax, m_anchor.z );
  568. ClearDragSelectionSet();
  569. AddToDragSet add( dragArea, m_anchor.z - m_nDragSelectionVolumeZMin, m_anchor.z + m_nDragSelectionVolumeZMax, m_bIsDragDeselecting );
  570. ForAllAreas( add );
  571. }
  572. //--------------------------------------------------------------------------------------------------------------
  573. /**
  574. * Draw navigation areas and edit them
  575. * @todo Clean the whole edit system up - its structure is legacy from peculiarities in GoldSrc.
  576. */
  577. ConVar nav_show_compass( "nav_show_compass", "0", FCVAR_CHEAT );
  578. void CNavMesh::DrawEditMode( void )
  579. {
  580. VPROF( "CNavMesh::DrawEditMode" );
  581. CBasePlayer *player = UTIL_GetListenServerHost();
  582. if (player == NULL)
  583. return;
  584. if ( IsGenerating() )
  585. return;
  586. // TODO: remove this when host_thread_mode 1 stops breaking NDEBUG_PERSIST_TILL_NEXT_SERVER overlays
  587. static ConVarRef host_thread_mode( "host_thread_mode" );
  588. host_thread_mode.SetValue( 0 );
  589. const float maxRange = 1000.0f; // 500
  590. #if DEBUG_NAV_NODES
  591. if ( nav_show_nodes.GetBool() )
  592. {
  593. for ( CNavNode *node = CNavNode::GetFirst(); node != NULL; node = node->GetNext() )
  594. {
  595. if ( m_editCursorPos.DistToSqr( *node->GetPosition() ) < 150*150 )
  596. {
  597. node->Draw();
  598. }
  599. }
  600. }
  601. #endif // DEBUG_NAV_NODES
  602. Vector from, dir;
  603. GetEditVectors( &from, &dir );
  604. Vector to = from + maxRange * dir;
  605. /* IN_PROGRESS:
  606. if ( !IsEditMode( PLACE_PAINTING ) && nav_snap_to_grid.GetBool() )
  607. {
  608. Vector center = SnapToGrid( m_editCursorPos );
  609. const int GridCount = 3;
  610. const int GridArraySize = GridCount * 2 + 1;
  611. const int GridSize = GetGridSize();
  612. // fill in an array of heights for the grid
  613. Vector pos[GridArraySize][GridArraySize];
  614. int x, y;
  615. for ( x=0; x<GridArraySize; ++x )
  616. {
  617. for ( y=0; y<GridArraySize; ++y )
  618. {
  619. pos[x][y] = center;
  620. pos[x][y].x += (x-GridCount) * GridSize;
  621. pos[x][y].y += (y-GridCount) * GridSize;
  622. pos[x][y].z += 36.0f;
  623. GetGroundHeight( pos[x][y], &pos[x][y].z );
  624. }
  625. }
  626. for ( x=1; x<GridArraySize; ++x )
  627. {
  628. for ( y=1; y<GridArraySize; ++y )
  629. {
  630. NavDrawLine( pos[x-1][y-1], pos[x-1][y], NavGridColor );
  631. NavDrawLine( pos[x-1][y-1], pos[x][y-1], NavGridColor );
  632. if ( x == GridArraySize-1 )
  633. {
  634. NavDrawLine( pos[x][y-1], pos[x][y], NavGridColor );
  635. }
  636. if ( y == GridArraySize-1 )
  637. {
  638. NavDrawLine( pos[x-1][y], pos[x][y], NavGridColor );
  639. }
  640. }
  641. }
  642. }
  643. */
  644. if ( FindActiveNavArea() || m_markedArea || m_markedLadder || !IsSelectedSetEmpty() || IsEditMode( CREATING_AREA ) || IsEditMode( CREATING_LADDER ) )
  645. {
  646. // draw cursor
  647. float cursorSize = 10.0f;
  648. if ( m_climbableSurface )
  649. {
  650. NDebugOverlay::Cross3D( m_editCursorPos, cursorSize, 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  651. }
  652. else
  653. {
  654. NavDrawLine( m_editCursorPos + Vector( 0, 0, cursorSize ), m_editCursorPos, NavCursorColor );
  655. NavDrawLine( m_editCursorPos + Vector( cursorSize, 0, 0 ), m_editCursorPos + Vector( -cursorSize, 0, 0 ), NavCursorColor );
  656. NavDrawLine( m_editCursorPos + Vector( 0, cursorSize, 0 ), m_editCursorPos + Vector( 0, -cursorSize, 0 ), NavCursorColor );
  657. if ( nav_show_compass.GetBool() )
  658. {
  659. const float offset = cursorSize * 1.5f;
  660. Vector pos;
  661. pos = m_editCursorPos;
  662. AddDirectionVector( &pos, NORTH, offset );
  663. NDebugOverlay::Text( pos, "N", false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  664. pos = m_editCursorPos;
  665. AddDirectionVector( &pos, SOUTH, offset );
  666. NDebugOverlay::Text( pos, "S", false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  667. pos = m_editCursorPos;
  668. AddDirectionVector( &pos, EAST, offset );
  669. NDebugOverlay::Text( pos, "E", false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  670. pos = m_editCursorPos;
  671. AddDirectionVector( &pos, WEST, offset );
  672. NDebugOverlay::Text( pos, "W", false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  673. }
  674. }
  675. // show drag rectangle when creating areas and ladders
  676. if ( IsEditMode( CREATING_AREA ) )
  677. {
  678. float z = m_anchor.z + 2.0f;
  679. NavDrawLine( Vector( m_editCursorPos.x, m_editCursorPos.y, z ), Vector( m_anchor.x, m_editCursorPos.y, z ), NavCreationColor );
  680. NavDrawLine( Vector( m_anchor.x, m_anchor.y, z ), Vector( m_anchor.x, m_editCursorPos.y, z ), NavCreationColor );
  681. NavDrawLine( Vector( m_anchor.x, m_anchor.y, z ), Vector( m_editCursorPos.x, m_anchor.y, z ), NavCreationColor );
  682. NavDrawLine( Vector( m_editCursorPos.x, m_editCursorPos.y, z ), Vector( m_editCursorPos.x, m_anchor.y, z ), NavCreationColor );
  683. }
  684. else if ( IsEditMode( DRAG_SELECTING ) )
  685. {
  686. float z1 = m_anchor.z + m_nDragSelectionVolumeZMax;
  687. float z2 = m_anchor.z - m_nDragSelectionVolumeZMin;
  688. // Draw the drag select volume
  689. Vector vMin( m_anchor.x, m_anchor.y, z1 );
  690. Vector vMax( m_editCursorPos.x, m_editCursorPos.y, z2 );
  691. NavDrawVolume( vMin, vMax, m_anchor.z, NavDragSelectionColor );
  692. UpdateDragSelectionSet();
  693. Color dragSelectionColor = m_bIsDragDeselecting ? s_dragSelectionSetDeleteColor : s_dragSelectionSetAddColor;
  694. // Draw the drag selection set
  695. FOR_EACH_VEC( m_dragSelectionSet, it )
  696. {
  697. CNavArea *area = m_dragSelectionSet[ it ];
  698. area->DrawDragSelectionSet( dragSelectionColor );
  699. }
  700. }
  701. else if ( IsEditMode( CREATING_LADDER ) )
  702. {
  703. Vector corner1, corner2, corner3;
  704. if ( FindLadderCorners( &corner1, &corner2, &corner3 ) )
  705. {
  706. NavEditColor color = NavCreationColor;
  707. if ( !m_climbableSurface )
  708. {
  709. color = NavInvalidCreationColor;
  710. }
  711. NavDrawLine( m_ladderAnchor, corner1, color );
  712. NavDrawLine( corner1, corner3, color );
  713. NavDrawLine( corner3, corner2, color );
  714. NavDrawLine( corner2, m_ladderAnchor, color );
  715. }
  716. }
  717. if ( m_selectedLadder )
  718. {
  719. m_lastSelectedArea = NULL;
  720. // if ladder changed, print its ID
  721. if (m_selectedLadder != m_lastSelectedLadder || nav_show_area_info.GetBool())
  722. {
  723. m_lastSelectedLadder = m_selectedLadder;
  724. // print ladder info
  725. char buffer[80];
  726. CBaseEntity *ladderEntity = m_selectedLadder->GetLadderEntity();
  727. if ( ladderEntity )
  728. {
  729. V_snprintf( buffer, sizeof( buffer ), "Ladder #%d (Team %s)\n", m_selectedLadder->GetID(), GetGlobalTeam( ladderEntity->GetTeamNumber() )->GetName() );
  730. }
  731. else
  732. {
  733. V_snprintf( buffer, sizeof( buffer ), "Ladder #%d\n", m_selectedLadder->GetID() );
  734. }
  735. NDebugOverlay::ScreenText( 0.5, 0.53, buffer, 255, 255, 0, 128, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  736. }
  737. // draw the ladder we are pointing at and all connected areas
  738. m_selectedLadder->DrawLadder();
  739. m_selectedLadder->DrawConnectedAreas();
  740. }
  741. if ( m_markedLadder && !IsEditMode( PLACE_PAINTING ) )
  742. {
  743. // draw the "marked" ladder
  744. m_markedLadder->DrawLadder();
  745. }
  746. if ( m_markedArea && !IsEditMode( PLACE_PAINTING ) )
  747. {
  748. // draw the "marked" area
  749. m_markedArea->Draw();
  750. }
  751. // find the area the player is pointing at
  752. if (m_selectedArea)
  753. {
  754. m_lastSelectedLadder = NULL;
  755. // if area changed, print its ID
  756. if ( m_selectedArea != m_lastSelectedArea )
  757. {
  758. m_showAreaInfoTimer.Start( nav_show_area_info.GetFloat() );
  759. m_lastSelectedArea = m_selectedArea;
  760. }
  761. if (m_showAreaInfoTimer.HasStarted() && !m_showAreaInfoTimer.IsElapsed() )
  762. {
  763. char buffer[80];
  764. char attrib[80];
  765. char locName[80];
  766. if (m_selectedArea->GetPlace())
  767. {
  768. const char *name = TheNavMesh->PlaceToName( m_selectedArea->GetPlace() );
  769. if (name)
  770. V_strcpy_safe( locName, name );
  771. else
  772. V_strcpy_safe( locName, "ERROR" );
  773. }
  774. else
  775. {
  776. locName[0] = '\000';
  777. }
  778. if (IsEditMode( PLACE_PAINTING ))
  779. {
  780. attrib[0] = '\000';
  781. }
  782. else
  783. {
  784. attrib[0] = 0;
  785. int attributes = m_selectedArea->GetAttributes();
  786. if ( attributes & NAV_MESH_CROUCH ) Q_strncat( attrib, "CROUCH ", sizeof( attrib ), -1 );
  787. if ( attributes & NAV_MESH_JUMP ) Q_strncat( attrib, "JUMP ", sizeof( attrib ), -1 );
  788. if ( attributes & NAV_MESH_PRECISE ) Q_strncat( attrib, "PRECISE ", sizeof( attrib ), -1 );
  789. if ( attributes & NAV_MESH_NO_JUMP ) Q_strncat( attrib, "NO_JUMP ", sizeof( attrib ), -1 );
  790. if ( attributes & NAV_MESH_STOP ) Q_strncat( attrib, "STOP ", sizeof( attrib ), -1 );
  791. if ( attributes & NAV_MESH_RUN ) Q_strncat( attrib, "RUN ", sizeof( attrib ), -1 );
  792. if ( attributes & NAV_MESH_WALK ) Q_strncat( attrib, "WALK ", sizeof( attrib ), -1 );
  793. if ( attributes & NAV_MESH_AVOID ) Q_strncat( attrib, "AVOID ", sizeof( attrib ), -1 );
  794. if ( attributes & NAV_MESH_TRANSIENT ) Q_strncat( attrib, "TRANSIENT ", sizeof( attrib ), -1 );
  795. if ( attributes & NAV_MESH_DONT_HIDE ) Q_strncat( attrib, "DONT_HIDE ", sizeof( attrib ), -1 );
  796. if ( attributes & NAV_MESH_STAND ) Q_strncat( attrib, "STAND ", sizeof( attrib ), -1 );
  797. if ( attributes & NAV_MESH_NO_HOSTAGES )Q_strncat( attrib, "NO HOSTAGES ", sizeof( attrib ), -1 );
  798. if ( attributes & NAV_MESH_STAIRS ) Q_strncat( attrib, "STAIRS ", sizeof( attrib ), -1 );
  799. if ( attributes & NAV_MESH_OBSTACLE_TOP ) Q_strncat( attrib, "OBSTACLE ", sizeof( attrib ), -1 );
  800. if ( attributes & NAV_MESH_CLIFF ) Q_strncat( attrib, "CLIFF ", sizeof( attrib ), -1 );
  801. #ifdef TERROR
  802. if ( attributes & TerrorNavArea::NAV_PLAYERCLIP ) Q_strncat( attrib, "PLAYERCLIP ", sizeof( attrib ), -1 );
  803. if ( attributes & TerrorNavArea::NAV_BREAKABLEWALL ) Q_strncat( attrib, "BREAKABLEWALL ", sizeof( attrib ), -1 );
  804. if ( m_selectedArea->IsBlocked( TEAM_SURVIVOR ) ) Q_strncat( attrib, "BLOCKED_SURVIVOR ", sizeof( attrib ), -1 );
  805. if ( m_selectedArea->IsBlocked( TEAM_ZOMBIE ) ) Q_strncat( attrib, "BLOCKED_ZOMBIE ", sizeof( attrib ), -1 );
  806. #else
  807. if ( m_selectedArea->IsBlocked( TEAM_ANY ) ) Q_strncat( attrib, "BLOCKED ", sizeof( attrib ), -1 );
  808. #endif
  809. if ( m_selectedArea->HasAvoidanceObstacle() ) Q_strncat( attrib, "OBSTRUCTED ", sizeof( attrib ), -1 );
  810. if ( m_selectedArea->IsDamaging() ) Q_strncat( attrib, "DAMAGING ", sizeof( attrib ), -1 );
  811. if ( m_selectedArea->IsUnderwater() ) Q_strncat( attrib, "UNDERWATER ", sizeof( attrib ), -1 );
  812. int connected = 0;
  813. connected += m_selectedArea->GetAdjacentCount( NORTH );
  814. connected += m_selectedArea->GetAdjacentCount( SOUTH );
  815. connected += m_selectedArea->GetAdjacentCount( EAST );
  816. connected += m_selectedArea->GetAdjacentCount( WEST );
  817. Q_strncat( attrib, UTIL_VarArgs( "%d Connections ", connected ), sizeof( attrib ), -1 );
  818. }
  819. Q_snprintf( buffer, sizeof( buffer ), "Area #%d %s %s\n", m_selectedArea->GetID(), locName, attrib );
  820. NDebugOverlay::ScreenText( 0.5, 0.53, buffer, 255, 255, 0, 128, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  821. // do "place painting"
  822. if ( m_isPlacePainting )
  823. {
  824. if (m_selectedArea->GetPlace() != TheNavMesh->GetNavPlace())
  825. {
  826. m_selectedArea->SetPlace( TheNavMesh->GetNavPlace() );
  827. player->EmitSound( "Bot.EditSwitchOn" );
  828. }
  829. }
  830. }
  831. // do continuous selecting into selected set
  832. if ( m_isContinuouslySelecting )
  833. {
  834. AddToSelectedSet( m_selectedArea );
  835. }
  836. else if ( m_isContinuouslyDeselecting )
  837. {
  838. // do continuous de-selecting into selected set
  839. RemoveFromSelectedSet( m_selectedArea );
  840. }
  841. if (IsEditMode( PLACE_PAINTING ))
  842. {
  843. m_selectedArea->DrawConnectedAreas();
  844. }
  845. else // normal editing mode
  846. {
  847. // draw split line
  848. Extent extent;
  849. m_selectedArea->GetExtent( &extent );
  850. float yaw = player->EyeAngles().y;
  851. while( yaw > 360.0f )
  852. yaw -= 360.0f;
  853. while( yaw < 0.0f )
  854. yaw += 360.0f;
  855. if (m_splitAlongX)
  856. {
  857. from.x = extent.lo.x;
  858. from.y = m_splitEdge;
  859. from.z = m_selectedArea->GetZ( from );
  860. to.x = extent.hi.x;
  861. to.y = m_splitEdge;
  862. to.z = m_selectedArea->GetZ( to );
  863. }
  864. else
  865. {
  866. from.x = m_splitEdge;
  867. from.y = extent.lo.y;
  868. from.z = m_selectedArea->GetZ( from );
  869. to.x = m_splitEdge;
  870. to.y = extent.hi.y;
  871. to.z = m_selectedArea->GetZ( to );
  872. }
  873. NavDrawLine( from, to, NavSplitLineColor );
  874. // draw the area we are pointing at and all connected areas
  875. m_selectedArea->DrawConnectedAreas();
  876. }
  877. }
  878. // render the selected set
  879. if (!IsSelectedSetEmpty())
  880. {
  881. Vector shift( 0, 0, 0 );
  882. if (IsEditMode( SHIFTING_XY ))
  883. {
  884. shift = m_editCursorPos - m_anchor;
  885. shift.z = 0.0f;
  886. }
  887. DrawSelectedSet draw( shift );
  888. // if the selected set is small, just blast it out
  889. if (m_selectedSet.Count() < nav_draw_limit.GetInt())
  890. {
  891. FOR_EACH_VEC( m_selectedSet, it )
  892. {
  893. CNavArea *area = m_selectedSet[ it ];
  894. draw( area );
  895. }
  896. }
  897. else
  898. {
  899. // draw the part nearest the player
  900. CNavArea *nearest = NULL;
  901. float nearRange = 9999999999.9f;
  902. FOR_EACH_VEC( m_selectedSet, it )
  903. {
  904. CNavArea *area = m_selectedSet[ it ];
  905. float range = (player->GetAbsOrigin() - area->GetCenter()).LengthSqr();
  906. if (range < nearRange)
  907. {
  908. nearRange = range;
  909. nearest = area;
  910. }
  911. }
  912. SearchSurroundingAreas( nearest, nearest->GetCenter(), draw, -1, INCLUDE_INCOMING_CONNECTIONS | INCLUDE_BLOCKED_AREAS );
  913. }
  914. }
  915. }
  916. }
  917. //--------------------------------------------------------------------------------------------------------------
  918. void CNavMesh::SetMarkedLadder( CNavLadder *ladder )
  919. {
  920. m_markedLadder = ladder;
  921. m_markedArea = NULL;
  922. m_markedCorner = NUM_CORNERS;
  923. }
  924. //--------------------------------------------------------------------------------------------------------------
  925. void CNavMesh::SetMarkedArea( CNavArea *area )
  926. {
  927. m_markedLadder = NULL;
  928. m_markedArea = area;
  929. m_markedCorner = NUM_CORNERS;
  930. }
  931. //--------------------------------------------------------------------------------------------------------------
  932. void CNavMesh::CommandNavDelete( void )
  933. {
  934. CBasePlayer *player = UTIL_GetListenServerHost();
  935. if (player == NULL)
  936. return;
  937. if ( !IsEditMode( NORMAL ) )
  938. return;
  939. if (IsSelectedSetEmpty())
  940. {
  941. // the old way
  942. CNavArea *markedArea = GetMarkedArea();
  943. CNavLadder *markedLadder = GetMarkedLadder();
  944. FindActiveNavArea();
  945. if( markedArea )
  946. {
  947. player->EmitSound( "EDIT_DELETE" );
  948. TheNavAreas.FindAndRemove( markedArea );
  949. TheNavMesh->OnEditDestroyNotify( markedArea );
  950. TheNavMesh->DestroyArea( markedArea );
  951. }
  952. else if( markedLadder )
  953. {
  954. player->EmitSound( "EDIT_DELETE" );
  955. m_ladders.FindAndRemove( markedLadder );
  956. OnEditDestroyNotify( markedLadder );
  957. delete markedLadder;
  958. }
  959. else if ( m_selectedArea )
  960. {
  961. player->EmitSound( "EDIT_DELETE" );
  962. TheNavAreas.FindAndRemove( m_selectedArea );
  963. CNavArea *deadArea = m_selectedArea;
  964. OnEditDestroyNotify( deadArea );
  965. TheNavMesh->DestroyArea( deadArea );
  966. }
  967. else if ( m_selectedLadder )
  968. {
  969. player->EmitSound( "EDIT_DELETE" );
  970. m_ladders.FindAndRemove( m_selectedLadder );
  971. CNavLadder *deadLadder = m_selectedLadder;
  972. OnEditDestroyNotify( deadLadder );
  973. delete deadLadder;
  974. }
  975. }
  976. else
  977. {
  978. // delete all areas in the selected set
  979. player->EmitSound( "EDIT_DELETE" );
  980. FOR_EACH_VEC( m_selectedSet, it )
  981. {
  982. CNavArea *area = m_selectedSet[ it ];
  983. TheNavAreas.FindAndRemove( area );
  984. OnEditDestroyNotify( area );
  985. TheNavMesh->DestroyArea( area );
  986. }
  987. Msg( "Deleted %d areas\n", m_selectedSet.Count() );
  988. ClearSelectedSet();
  989. }
  990. StripNavigationAreas();
  991. SetMarkedArea( NULL ); // unmark the mark area
  992. m_markedCorner = NUM_CORNERS; // clear the corner selection
  993. }
  994. //--------------------------------------------------------------------------------------------------------------
  995. class SelectCollector
  996. {
  997. public:
  998. SelectCollector( void )
  999. {
  1000. m_count = 0;
  1001. }
  1002. bool operator() ( CNavArea *area )
  1003. {
  1004. // already selected areas terminate flood select
  1005. if (TheNavMesh->IsInSelectedSet( area ))
  1006. return false;
  1007. TheNavMesh->AddToSelectedSet( area );
  1008. ++m_count;
  1009. return true;
  1010. }
  1011. int m_count;
  1012. };
  1013. //--------------------------------------------------------------------------------------------------------------
  1014. void CNavMesh::CommandNavDeleteMarked( void )
  1015. {
  1016. CBasePlayer *player = UTIL_GetListenServerHost();
  1017. if (player == NULL)
  1018. return;
  1019. if ( !IsEditMode( NORMAL ) )
  1020. return;
  1021. CNavArea *markedArea = GetMarkedArea();
  1022. if( markedArea )
  1023. {
  1024. player->EmitSound( "EDIT_DELETE" );
  1025. TheNavMesh->OnEditDestroyNotify( markedArea );
  1026. TheNavAreas.FindAndRemove( markedArea );
  1027. TheNavMesh->DestroyArea( markedArea );
  1028. }
  1029. CNavLadder *markedLadder = GetMarkedLadder();
  1030. if( markedLadder )
  1031. {
  1032. player->EmitSound( "EDIT_DELETE" );
  1033. m_ladders.FindAndRemove( markedLadder );
  1034. delete markedLadder;
  1035. }
  1036. StripNavigationAreas();
  1037. ClearSelectedSet();
  1038. SetMarkedArea( NULL ); // unmark the mark area
  1039. SetMarkedLadder( NULL ); // unmark the mark ladder
  1040. m_markedCorner = NUM_CORNERS; // clear the corner selection
  1041. }
  1042. //--------------------------------------------------------------------------------------------------------------
  1043. /**
  1044. * Select the current nav area and all recursively connected areas
  1045. */
  1046. void CNavMesh::CommandNavFloodSelect( const CCommand &args )
  1047. {
  1048. CBasePlayer *player = UTIL_GetListenServerHost();
  1049. if (player == NULL)
  1050. return;
  1051. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1052. return;
  1053. FindActiveNavArea();
  1054. CNavArea *start = m_selectedArea;
  1055. if ( !start )
  1056. {
  1057. start = m_markedArea;
  1058. }
  1059. if ( start )
  1060. {
  1061. player->EmitSound( "EDIT_DELETE" );
  1062. int connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS;
  1063. if ( args.ArgC() == 2 && FStrEq( "out", args[1] ) )
  1064. {
  1065. connections = INCLUDE_BLOCKED_AREAS;
  1066. }
  1067. if ( args.ArgC() == 2 && FStrEq( "in", args[1] ) )
  1068. {
  1069. connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS | EXCLUDE_OUTGOING_CONNECTIONS;
  1070. }
  1071. // collect all areas connected to this area
  1072. SelectCollector collector;
  1073. SearchSurroundingAreas( start, start->GetCenter(), collector, -1, connections );
  1074. Msg( "Selected %d areas.\n", collector.m_count );
  1075. }
  1076. SetMarkedArea( NULL ); // unmark the mark area
  1077. }
  1078. //--------------------------------------------------------------------------------------------------------------
  1079. /**
  1080. * Toggles all areas into/out of the selected set
  1081. */
  1082. void CNavMesh::CommandNavToggleSelectedSet( void )
  1083. {
  1084. CBasePlayer *player = UTIL_GetListenServerHost();
  1085. if (player == NULL)
  1086. return;
  1087. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1088. return;
  1089. player->EmitSound( "EDIT_DELETE" );
  1090. NavAreaVector notInSelectedSet;
  1091. // Build a list of all areas not in the selected set
  1092. FOR_EACH_VEC( TheNavAreas, it )
  1093. {
  1094. CNavArea *area = TheNavAreas[it];
  1095. if ( !IsInSelectedSet( area ) )
  1096. {
  1097. notInSelectedSet.AddToTail( area );
  1098. }
  1099. }
  1100. // Clear out the selected set
  1101. ClearSelectedSet();
  1102. // Add areas back into the selected set
  1103. FOR_EACH_VEC( notInSelectedSet, nit )
  1104. {
  1105. CNavArea *area = notInSelectedSet[nit];
  1106. AddToSelectedSet( area );
  1107. }
  1108. Msg( "Selected %d areas.\n", notInSelectedSet.Count() );
  1109. SetMarkedArea( NULL ); // unmark the mark area
  1110. }
  1111. //--------------------------------------------------------------------------------------------------------------
  1112. /**
  1113. * Saves the current selected set for later retrieval.
  1114. */
  1115. void CNavMesh::CommandNavStoreSelectedSet( void )
  1116. {
  1117. CBasePlayer *player = UTIL_GetListenServerHost();
  1118. if (player == NULL)
  1119. return;
  1120. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1121. return;
  1122. player->EmitSound( "EDIT_DELETE" );
  1123. m_storedSelectedSet.RemoveAll();
  1124. FOR_EACH_VEC( m_selectedSet, it )
  1125. {
  1126. m_storedSelectedSet.AddToTail( m_selectedSet[it]->GetID() );
  1127. }
  1128. }
  1129. //--------------------------------------------------------------------------------------------------------------
  1130. /**
  1131. * Restores an older selected set.
  1132. */
  1133. void CNavMesh::CommandNavRecallSelectedSet( void )
  1134. {
  1135. CBasePlayer *player = UTIL_GetListenServerHost();
  1136. if (player == NULL)
  1137. return;
  1138. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1139. return;
  1140. player->EmitSound( "EDIT_DELETE" );
  1141. ClearSelectedSet();
  1142. for ( int i=0; i<m_storedSelectedSet.Count(); ++i )
  1143. {
  1144. CNavArea *area = GetNavAreaByID( m_storedSelectedSet[i] );
  1145. if ( area )
  1146. {
  1147. AddToSelectedSet( area );
  1148. }
  1149. }
  1150. Msg( "Selected %d areas.\n", m_selectedSet.Count() );
  1151. }
  1152. //--------------------------------------------------------------------------------------------------------------
  1153. /**
  1154. * Add current area to selected set
  1155. */
  1156. void CNavMesh::CommandNavAddToSelectedSet( void )
  1157. {
  1158. CBasePlayer *player = UTIL_GetListenServerHost();
  1159. if (player == NULL)
  1160. return;
  1161. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1162. return;
  1163. FindActiveNavArea();
  1164. if ( m_selectedArea )
  1165. {
  1166. AddToSelectedSet( m_selectedArea );
  1167. player->EmitSound( "EDIT_MARK.Enable" );
  1168. }
  1169. }
  1170. //--------------------------------------------------------------------------------------------------------------
  1171. /**
  1172. * Add area ID to selected set
  1173. */
  1174. void CNavMesh::CommandNavAddToSelectedSetByID( const CCommand &args )
  1175. {
  1176. CBasePlayer *player = UTIL_GetListenServerHost();
  1177. if (player == NULL)
  1178. return;
  1179. if ( ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) || args.ArgC() < 2 )
  1180. return;
  1181. int id = atoi( args[1] );
  1182. CNavArea *area = GetNavAreaByID( id );
  1183. if ( area )
  1184. {
  1185. AddToSelectedSet( area );
  1186. player->EmitSound( "EDIT_MARK.Enable" );
  1187. Msg( "Added area %d. ( to go there: setpos %f %f %f )\n", id, area->GetCenter().x, area->GetCenter().y, area->GetCenter().z + 5 );
  1188. }
  1189. else
  1190. {
  1191. Msg( "No area with id %d\n", id );
  1192. }
  1193. }
  1194. //--------------------------------------------------------------------------------------------------------------
  1195. /**
  1196. * Remove current area from selected set
  1197. */
  1198. void CNavMesh::CommandNavRemoveFromSelectedSet( void )
  1199. {
  1200. CBasePlayer *player = UTIL_GetListenServerHost();
  1201. if (player == NULL)
  1202. return;
  1203. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1204. return;
  1205. FindActiveNavArea();
  1206. if ( m_selectedArea )
  1207. {
  1208. RemoveFromSelectedSet( m_selectedArea );
  1209. player->EmitSound( "EDIT_MARK.Disable" );
  1210. }
  1211. }
  1212. //--------------------------------------------------------------------------------------------------------------
  1213. /**
  1214. * Add/remove current area from selected set
  1215. */
  1216. void CNavMesh::CommandNavToggleInSelectedSet( void )
  1217. {
  1218. CBasePlayer *player = UTIL_GetListenServerHost();
  1219. if (player == NULL)
  1220. return;
  1221. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1222. return;
  1223. FindActiveNavArea();
  1224. if ( m_selectedArea )
  1225. {
  1226. if (IsInSelectedSet( m_selectedArea ))
  1227. {
  1228. RemoveFromSelectedSet( m_selectedArea );
  1229. }
  1230. else
  1231. {
  1232. AddToSelectedSet( m_selectedArea );
  1233. }
  1234. player->EmitSound( "EDIT_MARK.Disable" );
  1235. }
  1236. }
  1237. //--------------------------------------------------------------------------------------------------------------
  1238. /**
  1239. * Clear the selected set to empty
  1240. */
  1241. void CNavMesh::CommandNavClearSelectedSet( void )
  1242. {
  1243. CBasePlayer *player = UTIL_GetListenServerHost();
  1244. if (player == NULL)
  1245. return;
  1246. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1247. return;
  1248. ClearSelectedSet();
  1249. player->EmitSound( "EDIT_MARK.Disable" );
  1250. }
  1251. //--------------------------------------------------------------------------------------------------------------
  1252. /**
  1253. * Start continuously selecting areas into the selected set
  1254. */
  1255. void CNavMesh::CommandNavBeginSelecting( void )
  1256. {
  1257. CBasePlayer *player = UTIL_GetListenServerHost();
  1258. if (player == NULL)
  1259. return;
  1260. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1261. return;
  1262. m_isContinuouslySelecting = true;
  1263. m_isContinuouslyDeselecting = false;
  1264. player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
  1265. }
  1266. //--------------------------------------------------------------------------------------------------------------
  1267. /**
  1268. * Stop continuously selecting areas into the selected set
  1269. */
  1270. void CNavMesh::CommandNavEndSelecting( void )
  1271. {
  1272. CBasePlayer *player = UTIL_GetListenServerHost();
  1273. if (player == NULL)
  1274. return;
  1275. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1276. return;
  1277. m_isContinuouslySelecting = false;
  1278. m_isContinuouslyDeselecting = false;
  1279. player->EmitSound( "EDIT_END_AREA.Creating" );
  1280. }
  1281. //--------------------------------------------------------------------------------------------------------------
  1282. void CNavMesh::CommandNavBeginDragSelecting( void )
  1283. {
  1284. CBasePlayer *player = UTIL_GetListenServerHost();
  1285. if (player == NULL)
  1286. return;
  1287. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) && !IsEditMode( DRAG_SELECTING ) )
  1288. return;
  1289. FindActiveNavArea();
  1290. if ( IsEditMode( DRAG_SELECTING ) )
  1291. {
  1292. ClearDragSelectionSet();
  1293. SetEditMode( NORMAL );
  1294. player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
  1295. }
  1296. else
  1297. {
  1298. player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
  1299. SetEditMode( DRAG_SELECTING );
  1300. // m_anchor starting corner
  1301. m_anchor = m_editCursorPos;
  1302. m_nDragSelectionVolumeZMax = nav_drag_selection_volume_zmax_offset.GetInt();
  1303. m_nDragSelectionVolumeZMin = nav_drag_selection_volume_zmin_offset.GetInt();
  1304. }
  1305. SetMarkedArea( NULL ); // unmark the mark area
  1306. m_markedCorner = NUM_CORNERS; // clear the corner selection
  1307. }
  1308. //--------------------------------------------------------------------------------------------------------------
  1309. void CNavMesh::CommandNavEndDragSelecting( void )
  1310. {
  1311. CBasePlayer *player = UTIL_GetListenServerHost();
  1312. if (player == NULL)
  1313. return;
  1314. if ( IsEditMode( DRAG_SELECTING ) )
  1315. {
  1316. // Transfer drag selected areas to the selected set
  1317. FOR_EACH_VEC( m_dragSelectionSet, it )
  1318. {
  1319. AddToSelectedSet( m_dragSelectionSet[it] );
  1320. }
  1321. SetEditMode( NORMAL );
  1322. }
  1323. else
  1324. {
  1325. player->EmitSound( "EDIT_END_AREA.NotCreating" );
  1326. }
  1327. ClearDragSelectionSet();
  1328. m_markedCorner = NUM_CORNERS; // clear the corner selection
  1329. }
  1330. //--------------------------------------------------------------------------------------------------------------
  1331. void CNavMesh::CommandNavBeginDragDeselecting( void )
  1332. {
  1333. CBasePlayer *player = UTIL_GetListenServerHost();
  1334. if (player == NULL)
  1335. return;
  1336. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) && !IsEditMode( DRAG_SELECTING ) )
  1337. return;
  1338. FindActiveNavArea();
  1339. if ( IsEditMode( DRAG_SELECTING ) )
  1340. {
  1341. ClearDragSelectionSet();
  1342. SetEditMode( NORMAL );
  1343. player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
  1344. }
  1345. else
  1346. {
  1347. player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
  1348. SetEditMode( DRAG_SELECTING );
  1349. m_bIsDragDeselecting = true;
  1350. // m_anchor starting corner
  1351. m_anchor = m_editCursorPos;
  1352. m_nDragSelectionVolumeZMax = nav_drag_selection_volume_zmax_offset.GetInt();
  1353. m_nDragSelectionVolumeZMin = nav_drag_selection_volume_zmin_offset.GetInt();
  1354. }
  1355. SetMarkedArea( NULL ); // unmark the mark area
  1356. m_markedCorner = NUM_CORNERS; // clear the corner selection
  1357. }
  1358. //--------------------------------------------------------------------------------------------------------------
  1359. void CNavMesh::CommandNavEndDragDeselecting( void )
  1360. {
  1361. CBasePlayer *player = UTIL_GetListenServerHost();
  1362. if (player == NULL)
  1363. return;
  1364. if ( IsEditMode( DRAG_SELECTING ) )
  1365. {
  1366. // Remove drag selected areas from the selected set
  1367. FOR_EACH_VEC( m_dragSelectionSet, it )
  1368. {
  1369. RemoveFromSelectedSet( m_dragSelectionSet[it] );
  1370. }
  1371. SetEditMode( NORMAL );
  1372. }
  1373. else
  1374. {
  1375. player->EmitSound( "EDIT_END_AREA.NotCreating" );
  1376. }
  1377. ClearDragSelectionSet();
  1378. m_bIsDragDeselecting = false;
  1379. m_markedCorner = NUM_CORNERS; // clear the corner selection
  1380. }
  1381. //--------------------------------------------------------------------------------------------------------------
  1382. void CNavMesh::CommandNavRaiseDragVolumeMax( void )
  1383. {
  1384. CBasePlayer *player = UTIL_GetListenServerHost();
  1385. if (player == NULL)
  1386. return;
  1387. m_nDragSelectionVolumeZMax += 32;
  1388. nav_drag_selection_volume_zmax_offset.SetValue( m_nDragSelectionVolumeZMax );
  1389. }
  1390. //--------------------------------------------------------------------------------------------------------------
  1391. void CNavMesh::CommandNavLowerDragVolumeMax( void )
  1392. {
  1393. CBasePlayer *player = UTIL_GetListenServerHost();
  1394. if (player == NULL)
  1395. return;
  1396. m_nDragSelectionVolumeZMax = MAX( 0, m_nDragSelectionVolumeZMax - 32 );
  1397. nav_drag_selection_volume_zmax_offset.SetValue( m_nDragSelectionVolumeZMax );
  1398. }
  1399. //--------------------------------------------------------------------------------------------------------------
  1400. void CNavMesh::CommandNavRaiseDragVolumeMin( void )
  1401. {
  1402. CBasePlayer *player = UTIL_GetListenServerHost();
  1403. if (player == NULL)
  1404. return;
  1405. m_nDragSelectionVolumeZMin = MAX( 0, m_nDragSelectionVolumeZMin - 32 );
  1406. nav_drag_selection_volume_zmin_offset.SetValue( m_nDragSelectionVolumeZMin );
  1407. }
  1408. //--------------------------------------------------------------------------------------------------------------
  1409. void CNavMesh::CommandNavLowerDragVolumeMin( void )
  1410. {
  1411. CBasePlayer *player = UTIL_GetListenServerHost();
  1412. if (player == NULL)
  1413. return;
  1414. m_nDragSelectionVolumeZMin += 32;
  1415. nav_drag_selection_volume_zmin_offset.SetValue( m_nDragSelectionVolumeZMin );
  1416. }
  1417. //--------------------------------------------------------------------------------------------------------------
  1418. /**
  1419. * Start/stop continuously selecting areas into the selected set
  1420. */
  1421. void CNavMesh::CommandNavToggleSelecting( bool playSound )
  1422. {
  1423. CBasePlayer *player = UTIL_GetListenServerHost();
  1424. if (player == NULL)
  1425. return;
  1426. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1427. return;
  1428. m_isContinuouslySelecting = !m_isContinuouslySelecting;
  1429. m_isContinuouslyDeselecting = false;
  1430. if ( playSound )
  1431. {
  1432. player->EmitSound( "EDIT_END_AREA.Creating" );
  1433. }
  1434. }
  1435. //--------------------------------------------------------------------------------------------------------------
  1436. /**
  1437. * Start continuously de-selecting areas from the selected set
  1438. */
  1439. void CNavMesh::CommandNavBeginDeselecting( void )
  1440. {
  1441. CBasePlayer *player = UTIL_GetListenServerHost();
  1442. if (player == NULL)
  1443. return;
  1444. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1445. return;
  1446. m_isContinuouslyDeselecting = true;
  1447. m_isContinuouslySelecting = false;
  1448. player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
  1449. }
  1450. //--------------------------------------------------------------------------------------------------------------
  1451. /**
  1452. * Stop continuously de-selecting areas from the selected set
  1453. */
  1454. void CNavMesh::CommandNavEndDeselecting( void )
  1455. {
  1456. CBasePlayer *player = UTIL_GetListenServerHost();
  1457. if (player == NULL)
  1458. return;
  1459. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1460. return;
  1461. m_isContinuouslyDeselecting = false;
  1462. m_isContinuouslySelecting = false;
  1463. player->EmitSound( "EDIT_END_AREA.Creating" );
  1464. }
  1465. //--------------------------------------------------------------------------------------------------------------
  1466. /**
  1467. * Start/stop continuously de-selecting areas from the selected set
  1468. */
  1469. void CNavMesh::CommandNavToggleDeselecting( bool playSound )
  1470. {
  1471. CBasePlayer *player = UTIL_GetListenServerHost();
  1472. if (player == NULL)
  1473. return;
  1474. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1475. return;
  1476. m_isContinuouslyDeselecting = !m_isContinuouslyDeselecting;
  1477. m_isContinuouslySelecting = false;
  1478. if ( playSound )
  1479. {
  1480. player->EmitSound( "EDIT_END_AREA.Creating" );
  1481. }
  1482. }
  1483. //--------------------------------------------------------------------------------------------------------------
  1484. /**
  1485. * Selects all areas that intersect the half-space
  1486. * Arguments: "+z 100", or "-x 30", etc.
  1487. */
  1488. void CNavMesh::CommandNavSelectHalfSpace( const CCommand &args )
  1489. {
  1490. CBasePlayer *player = UTIL_GetListenServerHost();
  1491. if (player == NULL)
  1492. return;
  1493. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1494. return;
  1495. if ( args.ArgC() != 3 )
  1496. {
  1497. Warning( "Error: <+X|-X|+Y|-Y|+Z|-Z> <value>\n" );
  1498. return;
  1499. }
  1500. enum HalfSpaceType
  1501. {
  1502. PLUS_X, MINUS_X,
  1503. PLUS_Y, MINUS_Y,
  1504. PLUS_Z, MINUS_Z,
  1505. }
  1506. halfSpace = PLUS_X;
  1507. if (FStrEq( "+x", args[1] ))
  1508. {
  1509. halfSpace = PLUS_X;
  1510. }
  1511. else if (FStrEq( "-x", args[1] ))
  1512. {
  1513. halfSpace = MINUS_X;
  1514. }
  1515. else if (FStrEq( "+y", args[1] ))
  1516. {
  1517. halfSpace = PLUS_Y;
  1518. }
  1519. else if (FStrEq( "-y", args[1] ))
  1520. {
  1521. halfSpace = MINUS_Y;
  1522. }
  1523. else if (FStrEq( "+z", args[1] ))
  1524. {
  1525. halfSpace = PLUS_Z;
  1526. }
  1527. else if (FStrEq( "-z", args[1] ))
  1528. {
  1529. halfSpace = MINUS_Z;
  1530. }
  1531. float value = atof( args[2] );
  1532. Extent extent;
  1533. FOR_EACH_VEC( TheNavAreas, it )
  1534. {
  1535. CNavArea *area = TheNavAreas[it];
  1536. area->GetExtent( &extent );
  1537. switch( halfSpace )
  1538. {
  1539. case PLUS_X:
  1540. if (extent.lo.x < value && extent.hi.x < value)
  1541. {
  1542. continue;
  1543. }
  1544. break;
  1545. case PLUS_Y:
  1546. if (extent.lo.y < value && extent.hi.y < value)
  1547. {
  1548. continue;
  1549. }
  1550. break;
  1551. case PLUS_Z:
  1552. if (extent.lo.z < value && extent.hi.z < value)
  1553. {
  1554. continue;
  1555. }
  1556. break;
  1557. case MINUS_X:
  1558. if (extent.lo.x > value && extent.hi.x > value)
  1559. {
  1560. continue;
  1561. }
  1562. break;
  1563. case MINUS_Y:
  1564. if (extent.lo.y > value && extent.hi.y > value)
  1565. {
  1566. continue;
  1567. }
  1568. break;
  1569. case MINUS_Z:
  1570. if (extent.lo.z > value && extent.hi.z > value)
  1571. {
  1572. continue;
  1573. }
  1574. break;
  1575. }
  1576. // toggle membership
  1577. if ( IsInSelectedSet( area ) )
  1578. {
  1579. RemoveFromSelectedSet( area );
  1580. }
  1581. else
  1582. {
  1583. AddToSelectedSet( area );
  1584. }
  1585. }
  1586. player->EmitSound( "EDIT_DELETE" );
  1587. }
  1588. //--------------------------------------------------------------------------------------------------------------
  1589. /**
  1590. * Begin shifting selected set in the XY plane
  1591. */
  1592. void CNavMesh::CommandNavBeginShiftXY( void )
  1593. {
  1594. CBasePlayer *player = UTIL_GetListenServerHost();
  1595. if (player == NULL)
  1596. return;
  1597. if (GetEditMode() == SHIFTING_XY)
  1598. {
  1599. SetEditMode( NORMAL );
  1600. player->EmitSound( "EDIT_END_AREA.Creating" );
  1601. return;
  1602. }
  1603. else
  1604. {
  1605. SetEditMode( SHIFTING_XY );
  1606. player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
  1607. }
  1608. // m_anchor starting corner
  1609. m_anchor = m_editCursorPos;
  1610. }
  1611. //--------------------------------------------------------------------------------------------------------
  1612. /**
  1613. * Shift a set of areas, and all connected ladders
  1614. */
  1615. class ShiftSet
  1616. {
  1617. public:
  1618. ShiftSet( const Vector &shift )
  1619. {
  1620. m_shift = shift;
  1621. }
  1622. bool operator()( CNavArea *area )
  1623. {
  1624. area->Shift( m_shift );
  1625. const NavLadderConnectVector *ladders = area->GetLadders( CNavLadder::LADDER_UP );
  1626. int it;
  1627. for( it = 0; it < ladders->Count(); ++it )
  1628. {
  1629. CNavLadder *ladder = (*ladders)[ it ].ladder;
  1630. if ( !m_ladders.HasElement( ladder ) )
  1631. {
  1632. ladder->Shift( m_shift );
  1633. m_ladders.AddToTail( ladder );
  1634. }
  1635. }
  1636. ladders = area->GetLadders( CNavLadder::LADDER_DOWN );
  1637. for( it = 0; it < ladders->Count(); ++it )
  1638. {
  1639. CNavLadder *ladder = (*ladders)[ it ].ladder;
  1640. if ( !m_ladders.HasElement( ladder ) )
  1641. {
  1642. ladder->Shift( m_shift );
  1643. m_ladders.AddToTail( ladder );
  1644. }
  1645. }
  1646. return true;
  1647. }
  1648. private:
  1649. CUtlVector< CNavLadder * > m_ladders;
  1650. Vector m_shift;
  1651. };
  1652. //--------------------------------------------------------------------------------------------------------------
  1653. /**
  1654. * End shifting selected set in the XY plane
  1655. */
  1656. void CNavMesh::CommandNavEndShiftXY( void )
  1657. {
  1658. CBasePlayer *player = UTIL_GetListenServerHost();
  1659. if (player == NULL)
  1660. return;
  1661. SetEditMode( NORMAL );
  1662. Vector shiftAmount = m_editCursorPos - m_anchor;
  1663. shiftAmount.z = 0.0f;
  1664. ShiftSet shift( shiftAmount );
  1665. // update the position of all areas in the selected set
  1666. TheNavMesh->ForAllSelectedAreas( shift );
  1667. player->EmitSound( "EDIT_END_AREA.Creating" );
  1668. }
  1669. //--------------------------------------------------------------------------------------------------------
  1670. CON_COMMAND_F( nav_shift, "Shifts the selected areas by the specified amount", FCVAR_CHEAT )
  1671. {
  1672. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1673. return;
  1674. CBasePlayer *player = UTIL_GetListenServerHost();
  1675. if (player == NULL)
  1676. return;
  1677. TheNavMesh->SetEditMode( CNavMesh::NORMAL );
  1678. Vector shiftAmount( vec3_origin );
  1679. if ( args.ArgC() > 1 )
  1680. {
  1681. shiftAmount.x = atoi( args[1] );
  1682. if ( args.ArgC() > 2 )
  1683. {
  1684. shiftAmount.y = atoi( args[2] );
  1685. if ( args.ArgC() > 3 )
  1686. {
  1687. shiftAmount.z = atoi( args[3] );
  1688. }
  1689. }
  1690. }
  1691. ShiftSet shift( shiftAmount );
  1692. // update the position of all areas in the selected set
  1693. TheNavMesh->ForAllSelectedAreas( shift );
  1694. player->EmitSound( "EDIT_END_AREA.Creating" );
  1695. }
  1696. //--------------------------------------------------------------------------------------------------------
  1697. void CommandNavCenterInWorld( void )
  1698. {
  1699. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1700. return;
  1701. CBasePlayer *player = UTIL_GetListenServerHost();
  1702. if (player == NULL)
  1703. return;
  1704. TheNavMesh->SetEditMode( CNavMesh::NORMAL );
  1705. // Build the nav mesh's extent
  1706. Extent navExtent;
  1707. bool first = true;
  1708. FOR_EACH_VEC( TheNavAreas, it )
  1709. {
  1710. CNavArea *area = TheNavAreas[it];
  1711. if ( first )
  1712. {
  1713. area->GetExtent( &navExtent );
  1714. first = false;
  1715. }
  1716. else
  1717. {
  1718. navExtent.Encompass( area->GetCorner( NORTH_WEST ) );
  1719. navExtent.Encompass( area->GetCorner( NORTH_EAST ) );
  1720. navExtent.Encompass( area->GetCorner( SOUTH_WEST ) );
  1721. navExtent.Encompass( area->GetCorner( SOUTH_EAST ) );
  1722. }
  1723. }
  1724. // Get the world's extent
  1725. CWorld *world = dynamic_cast< CWorld * >( CBaseEntity::Instance( INDEXENT( 0 ) ) );
  1726. if ( !world )
  1727. return;
  1728. Extent worldExtent;
  1729. world->GetWorldBounds( worldExtent.lo, worldExtent.hi );
  1730. // Compute the difference, and shift in XY
  1731. Vector navCenter = ( navExtent.lo + navExtent.hi ) * 0.5f;
  1732. Vector worldCenter = ( worldExtent.lo + worldExtent.hi ) * 0.5f;
  1733. Vector shift = worldCenter - navCenter;
  1734. shift.z = 0.0f;
  1735. // update the position of all areas
  1736. FOR_EACH_VEC( TheNavAreas, it )
  1737. {
  1738. CNavArea *area = TheNavAreas[ it ];
  1739. area->Shift( shift );
  1740. }
  1741. player->EmitSound( "EDIT_END_AREA.Creating" );
  1742. Msg( "Shifting mesh by %f,%f\n", shift.x, shift.y );
  1743. }
  1744. ConCommand nav_world_center( "nav_world_center", CommandNavCenterInWorld, "Centers the nav mesh in the world", FCVAR_CHEAT );
  1745. //--------------------------------------------------------------------------------------------------------------
  1746. /**
  1747. * Add invalid areas to selected set
  1748. */
  1749. void CNavMesh::CommandNavSelectInvalidAreas( void )
  1750. {
  1751. CBasePlayer *player = UTIL_GetListenServerHost();
  1752. if (player == NULL)
  1753. return;
  1754. if ( !IsEditMode( NORMAL ) )
  1755. return;
  1756. ClearSelectedSet();
  1757. Extent areaExtent;
  1758. FOR_EACH_VEC( TheNavAreas, it )
  1759. {
  1760. CNavArea *area = TheNavAreas[ it ];
  1761. if ( area )
  1762. {
  1763. area->GetExtent( &areaExtent );
  1764. for ( float x = areaExtent.lo.x; x + GenerationStepSize <= areaExtent.hi.x; x += GenerationStepSize )
  1765. {
  1766. for ( float y = areaExtent.lo.y; y + GenerationStepSize <= areaExtent.hi.y; y += GenerationStepSize )
  1767. {
  1768. float nw = area->GetZ( x, y );
  1769. float ne = area->GetZ( x + GenerationStepSize, y );
  1770. float sw = area->GetZ( x, y + GenerationStepSize );
  1771. float se = area->GetZ( x + GenerationStepSize, y + GenerationStepSize );
  1772. if ( !IsHeightDifferenceValid( nw, ne, sw, se ) ||
  1773. !IsHeightDifferenceValid( ne, nw, sw, se ) ||
  1774. !IsHeightDifferenceValid( sw, ne, nw, se ) ||
  1775. !IsHeightDifferenceValid( se, ne, sw, nw ) )
  1776. {
  1777. AddToSelectedSet( area );
  1778. }
  1779. }
  1780. }
  1781. }
  1782. }
  1783. Msg( "Selected %d areas.\n", m_selectedSet.Count() );
  1784. if ( m_selectedSet.Count() )
  1785. {
  1786. player->EmitSound( "EDIT_MARK.Enable" );
  1787. }
  1788. else
  1789. {
  1790. player->EmitSound( "EDIT_MARK.Disable" );
  1791. }
  1792. }
  1793. //--------------------------------------------------------------------------------------------------------------
  1794. /**
  1795. * Add blocked areas to selected set
  1796. */
  1797. void CNavMesh::CommandNavSelectBlockedAreas( void )
  1798. {
  1799. CBasePlayer *player = UTIL_GetListenServerHost();
  1800. if (player == NULL)
  1801. return;
  1802. if ( !IsEditMode( NORMAL ) )
  1803. return;
  1804. ClearSelectedSet();
  1805. FOR_EACH_VEC( TheNavAreas, it )
  1806. {
  1807. CNavArea *area = TheNavAreas[ it ];
  1808. if ( area && area->IsBlocked( TEAM_ANY ) )
  1809. {
  1810. AddToSelectedSet( area );
  1811. }
  1812. }
  1813. Msg( "Selected %d areas.\n", m_selectedSet.Count() );
  1814. if ( m_selectedSet.Count() )
  1815. {
  1816. player->EmitSound( "EDIT_MARK.Enable" );
  1817. }
  1818. else
  1819. {
  1820. player->EmitSound( "EDIT_MARK.Disable" );
  1821. }
  1822. }
  1823. //--------------------------------------------------------------------------------------------------------------
  1824. /**
  1825. * Add obstructed areas to selected set
  1826. */
  1827. void CNavMesh::CommandNavSelectObstructedAreas( void )
  1828. {
  1829. CBasePlayer *player = UTIL_GetListenServerHost();
  1830. if (player == NULL)
  1831. return;
  1832. if ( !IsEditMode( NORMAL ) )
  1833. return;
  1834. ClearSelectedSet();
  1835. FOR_EACH_VEC( TheNavAreas, it )
  1836. {
  1837. CNavArea *area = TheNavAreas[ it ];
  1838. if ( area && area->HasAvoidanceObstacle() )
  1839. {
  1840. AddToSelectedSet( area );
  1841. }
  1842. }
  1843. Msg( "Selected %d areas.\n", m_selectedSet.Count() );
  1844. if ( m_selectedSet.Count() )
  1845. {
  1846. player->EmitSound( "EDIT_MARK.Enable" );
  1847. }
  1848. else
  1849. {
  1850. player->EmitSound( "EDIT_MARK.Disable" );
  1851. }
  1852. }
  1853. //--------------------------------------------------------------------------------------------------------------
  1854. /**
  1855. * Add damaging areas to selected set
  1856. */
  1857. void CNavMesh::CommandNavSelectDamagingAreas( void )
  1858. {
  1859. CBasePlayer *player = UTIL_GetListenServerHost();
  1860. if (player == NULL)
  1861. return;
  1862. if ( !IsEditMode( NORMAL ) )
  1863. return;
  1864. ClearSelectedSet();
  1865. FOR_EACH_VEC( TheNavAreas, it )
  1866. {
  1867. CNavArea *area = TheNavAreas[ it ];
  1868. if ( area && area->IsDamaging() )
  1869. {
  1870. AddToSelectedSet( area );
  1871. }
  1872. }
  1873. Msg( "Selected %d areas.\n", m_selectedSet.Count() );
  1874. if ( m_selectedSet.Count() )
  1875. {
  1876. player->EmitSound( "EDIT_MARK.Enable" );
  1877. }
  1878. else
  1879. {
  1880. player->EmitSound( "EDIT_MARK.Disable" );
  1881. }
  1882. }
  1883. //--------------------------------------------------------------------------------------------------------------
  1884. /**
  1885. * Adds stairs areas to the selected set
  1886. */
  1887. void CNavMesh::CommandNavSelectStairs( void )
  1888. {
  1889. CBasePlayer *player = UTIL_GetListenServerHost();
  1890. if ( player == NULL )
  1891. return;
  1892. if ( !IsEditMode( NORMAL ) )
  1893. return;
  1894. ClearSelectedSet();
  1895. FOR_EACH_VEC( TheNavAreas, it )
  1896. {
  1897. CNavArea *area = TheNavAreas[ it ];
  1898. if ( area && area->HasAttributes( NAV_MESH_STAIRS ) )
  1899. {
  1900. AddToSelectedSet( area );
  1901. }
  1902. }
  1903. Msg( "Selected %d areas.\n", m_selectedSet.Count() );
  1904. if ( m_selectedSet.Count() )
  1905. {
  1906. player->EmitSound( "EDIT_MARK.Enable" );
  1907. }
  1908. else
  1909. {
  1910. player->EmitSound( "EDIT_MARK.Disable" );
  1911. }
  1912. }
  1913. //--------------------------------------------------------------------------------------------------------------
  1914. // Adds areas not connected to mesh to the selected set
  1915. void CNavMesh::CommandNavSelectOrphans( void )
  1916. {
  1917. CBasePlayer *player = UTIL_GetListenServerHost();
  1918. if (player == NULL)
  1919. return;
  1920. if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) )
  1921. return;
  1922. FindActiveNavArea();
  1923. CNavArea *start = m_selectedArea;
  1924. if ( !start )
  1925. {
  1926. start = m_markedArea;
  1927. }
  1928. if ( start )
  1929. {
  1930. player->EmitSound( "EDIT_DELETE" );
  1931. int connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS;
  1932. // collect all areas connected to this area
  1933. SelectCollector collector;
  1934. SearchSurroundingAreas( start, start->GetCenter(), collector, -1, connections );
  1935. // toggle the selected set to reveal the orphans
  1936. CommandNavToggleSelectedSet();
  1937. }
  1938. SetMarkedArea( NULL ); // unmark the mark area
  1939. }
  1940. //--------------------------------------------------------------------------------------------------------------
  1941. void CNavMesh::CommandNavSplit( void )
  1942. {
  1943. CBasePlayer *player = UTIL_GetListenServerHost();
  1944. if (player == NULL)
  1945. return;
  1946. if ( !IsEditMode( NORMAL ) )
  1947. return;
  1948. FindActiveNavArea();
  1949. if ( m_selectedArea )
  1950. {
  1951. if (m_selectedArea->SplitEdit( m_splitAlongX, m_splitEdge ))
  1952. player->EmitSound( "EDIT_SPLIT.MarkedArea" );
  1953. else
  1954. player->EmitSound( "EDIT_SPLIT.NoMarkedArea" );
  1955. }
  1956. StripNavigationAreas();
  1957. SetMarkedArea( NULL ); // unmark the mark area
  1958. m_markedCorner = NUM_CORNERS; // clear the corner selection
  1959. }
  1960. //--------------------------------------------------------------------------------------------------------------
  1961. bool MakeSniperSpots( CNavArea *area )
  1962. {
  1963. if ( !area )
  1964. return false;
  1965. bool splitAlongX;
  1966. float splitEdge;
  1967. const float minSplitSize = 2.0f; // ensure the first split is larger than this
  1968. float sizeX = area->GetSizeX();
  1969. float sizeY = area->GetSizeY();
  1970. if ( sizeX > GenerationStepSize && sizeX > sizeY )
  1971. {
  1972. splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).x, GenerationStepSize );
  1973. if ( splitEdge < area->GetCorner( NORTH_WEST ).x + minSplitSize )
  1974. splitEdge += GenerationStepSize;
  1975. splitAlongX = false;
  1976. }
  1977. else if ( sizeY > GenerationStepSize && sizeY > sizeX )
  1978. {
  1979. splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).y, GenerationStepSize );
  1980. if ( splitEdge < area->GetCorner( NORTH_WEST ).y + minSplitSize )
  1981. splitEdge += GenerationStepSize;
  1982. splitAlongX = true;
  1983. }
  1984. else
  1985. {
  1986. return false;
  1987. }
  1988. CNavArea *first, *second;
  1989. if ( !area->SplitEdit( splitAlongX, splitEdge, &first, &second ) )
  1990. {
  1991. return false;
  1992. }
  1993. first->Disconnect( second );
  1994. second->Disconnect( first );
  1995. MakeSniperSpots( first );
  1996. MakeSniperSpots( second );
  1997. return true;
  1998. }
  1999. //--------------------------------------------------------------------------------------------------------------
  2000. void CNavMesh::CommandNavMakeSniperSpots( void )
  2001. {
  2002. CBasePlayer *player = UTIL_GetListenServerHost();
  2003. if (player == NULL)
  2004. return;
  2005. if ( !IsEditMode( NORMAL ) )
  2006. return;
  2007. FindActiveNavArea();
  2008. if ( m_selectedArea )
  2009. {
  2010. // recursively split the area
  2011. if ( MakeSniperSpots( m_selectedArea ) )
  2012. {
  2013. player->EmitSound( "EDIT_SPLIT.MarkedArea" );
  2014. }
  2015. else
  2016. {
  2017. player->EmitSound( "EDIT_SPLIT.NoMarkedArea" );
  2018. }
  2019. }
  2020. else
  2021. {
  2022. player->EmitSound( "EDIT_SPLIT.NoMarkedArea" );
  2023. }
  2024. StripNavigationAreas();
  2025. SetMarkedArea( NULL ); // unmark the mark area
  2026. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2027. }
  2028. //--------------------------------------------------------------------------------------------------------------
  2029. void CNavMesh::CommandNavMerge( void )
  2030. {
  2031. CBasePlayer *player = UTIL_GetListenServerHost();
  2032. if (player == NULL)
  2033. return;
  2034. if ( !IsEditMode( NORMAL ) )
  2035. return;
  2036. FindActiveNavArea();
  2037. if ( m_selectedArea )
  2038. {
  2039. CNavArea *other = m_markedArea;
  2040. if ( !m_markedArea && m_selectedSet.Count() == 1 )
  2041. {
  2042. other = m_selectedSet[0];
  2043. }
  2044. if ( other && other != m_selectedArea )
  2045. {
  2046. if ( m_selectedArea->MergeEdit( other ) )
  2047. player->EmitSound( "EDIT_MERGE.Enable" );
  2048. else
  2049. player->EmitSound( "EDIT_MERGE.Disable" );
  2050. }
  2051. else
  2052. {
  2053. Msg( "To merge, mark an area, highlight a second area, then invoke the merge command" );
  2054. player->EmitSound( "EDIT_MERGE.Disable" );
  2055. }
  2056. }
  2057. StripNavigationAreas();
  2058. SetMarkedArea( NULL ); // unmark the mark area
  2059. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2060. ClearSelectedSet();
  2061. }
  2062. //--------------------------------------------------------------------------------------------------------------
  2063. void CNavMesh::CommandNavMark( const CCommand &args )
  2064. {
  2065. CBasePlayer *player = UTIL_GetListenServerHost();
  2066. if (player == NULL)
  2067. return;
  2068. if ( !IsEditMode( NORMAL ) )
  2069. return;
  2070. if (!IsSelectedSetEmpty())
  2071. {
  2072. // add or remove areas from the selected set
  2073. if (IsInSelectedSet( m_selectedArea ))
  2074. {
  2075. // remove from set
  2076. player->EmitSound( "EDIT_MARK.Disable" );
  2077. RemoveFromSelectedSet( m_selectedArea );
  2078. }
  2079. else
  2080. {
  2081. // add to set
  2082. player->EmitSound( "EDIT_MARK.Enable" );
  2083. AddToSelectedSet( m_selectedArea );
  2084. }
  2085. return;
  2086. }
  2087. FindActiveNavArea();
  2088. if ( m_markedArea || m_markedLadder )
  2089. {
  2090. // Unmark area or ladder
  2091. player->EmitSound( "EDIT_MARK.Enable" );
  2092. Msg("Area unmarked.\n");
  2093. SetMarkedArea( NULL );
  2094. }
  2095. else if ( args.ArgC() > 1 )
  2096. {
  2097. if ( FStrEq( args[1], "ladder" ) )
  2098. {
  2099. if ( args.ArgC() > 2 )
  2100. {
  2101. const char *ladderIDNameToMark = args[2];
  2102. if ( ladderIDNameToMark )
  2103. {
  2104. unsigned int ladderIDToMark = atoi( ladderIDNameToMark );
  2105. if ( ladderIDToMark != 0 )
  2106. {
  2107. CNavLadder *ladder = TheNavMesh->GetLadderByID( ladderIDToMark );
  2108. if ( ladder )
  2109. {
  2110. player->EmitSound( "EDIT_MARK.Disable" );
  2111. SetMarkedLadder( ladder );
  2112. int connected = 0;
  2113. connected += m_markedLadder->m_topForwardArea != NULL;
  2114. connected += m_markedLadder->m_topLeftArea != NULL;
  2115. connected += m_markedLadder->m_topRightArea != NULL;
  2116. connected += m_markedLadder->m_topBehindArea != NULL;
  2117. connected += m_markedLadder->m_bottomArea != NULL;
  2118. Msg( "Marked Ladder is connected to %d Areas\n", connected );
  2119. }
  2120. }
  2121. }
  2122. }
  2123. }
  2124. else
  2125. {
  2126. const char *areaIDNameToMark = args[1];
  2127. if( areaIDNameToMark != NULL )
  2128. {
  2129. unsigned int areaIDToMark = atoi(areaIDNameToMark);
  2130. if( areaIDToMark != 0 )
  2131. {
  2132. CNavArea *areaToMark = NULL;
  2133. FOR_EACH_VEC( TheNavAreas, nit )
  2134. {
  2135. if( TheNavAreas[nit]->GetID() == areaIDToMark )
  2136. {
  2137. areaToMark = TheNavAreas[nit];
  2138. break;
  2139. }
  2140. }
  2141. if( areaToMark )
  2142. {
  2143. player->EmitSound( "EDIT_MARK.Disable" );
  2144. SetMarkedArea( areaToMark );
  2145. int connected = 0;
  2146. connected += GetMarkedArea()->GetAdjacentCount( NORTH );
  2147. connected += GetMarkedArea()->GetAdjacentCount( SOUTH );
  2148. connected += GetMarkedArea()->GetAdjacentCount( EAST );
  2149. connected += GetMarkedArea()->GetAdjacentCount( WEST );
  2150. Msg( "Marked Area is connected to %d other Areas\n", connected );
  2151. }
  2152. }
  2153. }
  2154. }
  2155. }
  2156. else if ( m_selectedArea )
  2157. {
  2158. // Mark an area
  2159. player->EmitSound( "EDIT_MARK.Disable" );
  2160. SetMarkedArea( m_selectedArea );
  2161. int connected = 0;
  2162. connected += GetMarkedArea()->GetAdjacentCount( NORTH );
  2163. connected += GetMarkedArea()->GetAdjacentCount( SOUTH );
  2164. connected += GetMarkedArea()->GetAdjacentCount( EAST );
  2165. connected += GetMarkedArea()->GetAdjacentCount( WEST );
  2166. Msg( "Marked Area is connected to %d other Areas\n", connected );
  2167. }
  2168. else if ( m_selectedLadder )
  2169. {
  2170. // Mark a ladder
  2171. player->EmitSound( "EDIT_MARK.Disable" );
  2172. SetMarkedLadder( m_selectedLadder );
  2173. int connected = 0;
  2174. connected += m_markedLadder->m_topForwardArea != NULL;
  2175. connected += m_markedLadder->m_topLeftArea != NULL;
  2176. connected += m_markedLadder->m_topRightArea != NULL;
  2177. connected += m_markedLadder->m_topBehindArea != NULL;
  2178. connected += m_markedLadder->m_bottomArea != NULL;
  2179. Msg( "Marked Ladder is connected to %d Areas\n", connected );
  2180. }
  2181. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2182. }
  2183. //--------------------------------------------------------------------------------------------------------------
  2184. void CNavMesh::CommandNavUnmark( void )
  2185. {
  2186. CBasePlayer *player = UTIL_GetListenServerHost();
  2187. if (player == NULL)
  2188. return;
  2189. if ( !IsEditMode( NORMAL ) )
  2190. return;
  2191. player->EmitSound( "EDIT_MARK.Enable" );
  2192. SetMarkedArea( NULL ); // unmark the mark area
  2193. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2194. }
  2195. //--------------------------------------------------------------------------------------------------------------
  2196. void CNavMesh::CommandNavBeginArea( void )
  2197. {
  2198. CBasePlayer *player = UTIL_GetListenServerHost();
  2199. if (player == NULL)
  2200. return;
  2201. if ( !(IsEditMode( CREATING_AREA ) || IsEditMode( CREATING_LADDER ) || IsEditMode( NORMAL )) )
  2202. {
  2203. player->EmitSound( "EDIT_END_AREA.NotCreating" );
  2204. return;
  2205. }
  2206. FindActiveNavArea();
  2207. if ( IsEditMode( CREATING_AREA ) )
  2208. {
  2209. SetEditMode( NORMAL );
  2210. player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
  2211. }
  2212. else if ( IsEditMode( CREATING_LADDER ) )
  2213. {
  2214. SetEditMode( NORMAL );
  2215. player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
  2216. }
  2217. else if ( m_climbableSurface )
  2218. {
  2219. player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
  2220. SetEditMode( CREATING_LADDER );
  2221. // m_ladderAnchor starting corner
  2222. m_ladderAnchor = m_editCursorPos;
  2223. m_ladderNormal = m_surfaceNormal;
  2224. }
  2225. else
  2226. {
  2227. player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
  2228. SetEditMode( CREATING_AREA );
  2229. // m_anchor starting corner
  2230. m_anchor = m_editCursorPos;
  2231. }
  2232. SetMarkedArea( NULL ); // unmark the mark area
  2233. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2234. }
  2235. //--------------------------------------------------------------------------------------------------------------
  2236. void CNavMesh::CommandNavEndArea( void )
  2237. {
  2238. CBasePlayer *player = UTIL_GetListenServerHost();
  2239. if (player == NULL)
  2240. return;
  2241. if ( !(IsEditMode( CREATING_AREA ) || IsEditMode( CREATING_LADDER ) || IsEditMode( NORMAL )) )
  2242. {
  2243. player->EmitSound( "EDIT_END_AREA.NotCreating" );
  2244. return;
  2245. }
  2246. if ( IsEditMode( CREATING_AREA ) )
  2247. {
  2248. SetEditMode( NORMAL );
  2249. // create the new nav area
  2250. Vector endPos = m_editCursorPos;
  2251. endPos.z = m_anchor.z;
  2252. // We're a manually-created area, so let's look around to see what's nearby
  2253. CNavArea *nearby = GetMarkedArea();
  2254. if ( !nearby )
  2255. {
  2256. nearby = TheNavMesh->GetNearestNavArea( m_editCursorPos + Vector( 0, 0, HalfHumanHeight ), false, 10000.0f, true );
  2257. }
  2258. if ( !nearby )
  2259. {
  2260. nearby = TheNavMesh->GetNearestNavArea( endPos + Vector( 0, 0, HalfHumanHeight ), false, 10000.0f, true );
  2261. }
  2262. if ( !nearby )
  2263. {
  2264. nearby = TheNavMesh->GetNearestNavArea( m_editCursorPos );
  2265. }
  2266. if ( !nearby )
  2267. {
  2268. nearby = TheNavMesh->GetNearestNavArea( endPos );
  2269. }
  2270. CNavArea *newArea = CreateArea();
  2271. if (newArea == NULL)
  2272. {
  2273. Warning( "NavEndArea: Out of memory\n" );
  2274. player->EmitSound( "EDIT_END_AREA.NotCreating" );
  2275. return;
  2276. }
  2277. newArea->Build( m_anchor, endPos );
  2278. if ( nearby )
  2279. {
  2280. newArea->InheritAttributes( nearby ); // inherit from the nearby area
  2281. }
  2282. TheNavAreas.AddToTail( newArea );
  2283. TheNavMesh->AddNavArea( newArea );
  2284. player->EmitSound( "EDIT_END_AREA.Creating" );
  2285. if ( nav_create_place_on_ground.GetBool() )
  2286. {
  2287. newArea->PlaceOnGround( NUM_CORNERS );
  2288. }
  2289. // if we have a marked area, inter-connect the two
  2290. if (GetMarkedArea())
  2291. {
  2292. Extent extent;
  2293. GetMarkedArea()->GetExtent( &extent );
  2294. if (m_anchor.x > extent.hi.x && m_editCursorPos.x > extent.hi.x)
  2295. {
  2296. GetMarkedArea()->ConnectTo( newArea, EAST );
  2297. newArea->ConnectTo( GetMarkedArea(), WEST );
  2298. }
  2299. else if (m_anchor.x < extent.lo.x && m_editCursorPos.x < extent.lo.x)
  2300. {
  2301. GetMarkedArea()->ConnectTo( newArea, WEST );
  2302. newArea->ConnectTo( GetMarkedArea(), EAST );
  2303. }
  2304. else if (m_anchor.y > extent.hi.y && m_editCursorPos.y > extent.hi.y)
  2305. {
  2306. GetMarkedArea()->ConnectTo( newArea, SOUTH );
  2307. newArea->ConnectTo( GetMarkedArea(), NORTH );
  2308. }
  2309. else if (m_anchor.y < extent.lo.y && m_editCursorPos.y < extent.lo.y)
  2310. {
  2311. GetMarkedArea()->ConnectTo( newArea, NORTH );
  2312. newArea->ConnectTo( GetMarkedArea(), SOUTH );
  2313. }
  2314. // propogate marked area to new area
  2315. SetMarkedArea( newArea );
  2316. }
  2317. TheNavMesh->OnEditCreateNotify( newArea );
  2318. }
  2319. else if ( IsEditMode( CREATING_LADDER ) )
  2320. {
  2321. SetEditMode( NORMAL );
  2322. player->EmitSound( "EDIT_END_AREA.Creating" );
  2323. Vector corner1, corner2, corner3;
  2324. if ( m_climbableSurface && FindLadderCorners( &corner1, &corner2, &corner3 ) )
  2325. {
  2326. // m_ladderAnchor and corner2 are at the same Z, and corner1 & corner3 share Z.
  2327. Vector top = (m_ladderAnchor + corner2) * 0.5f;
  2328. Vector bottom = (corner1 + corner3) * 0.5f;
  2329. if ( top.z < bottom.z )
  2330. {
  2331. Vector tmp = top;
  2332. top = bottom;
  2333. bottom = tmp;
  2334. }
  2335. float width = m_ladderAnchor.DistTo( corner2 );
  2336. Vector2D ladderDir = m_surfaceNormal.AsVector2D();
  2337. CreateLadder( top, bottom, width, ladderDir, HumanHeight );
  2338. }
  2339. else
  2340. {
  2341. player->EmitSound( "EDIT_END_AREA.NotCreating" );
  2342. }
  2343. }
  2344. else
  2345. {
  2346. player->EmitSound( "EDIT_END_AREA.NotCreating" );
  2347. }
  2348. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2349. }
  2350. //--------------------------------------------------------------------------------------------------------------
  2351. void CNavMesh::CommandNavConnect( void )
  2352. {
  2353. CBasePlayer *player = UTIL_GetListenServerHost();
  2354. if (player == NULL)
  2355. return;
  2356. if ( !IsEditMode( NORMAL ) )
  2357. return;
  2358. FindActiveNavArea();
  2359. Vector center;
  2360. float halfWidth;
  2361. if ( m_selectedSet.Count() > 1 )
  2362. {
  2363. bool bValid = true;
  2364. for ( int i = 1; i < m_selectedSet.Count(); ++i )
  2365. {
  2366. // Make sure all connections are valid
  2367. CNavArea *first = m_selectedSet[0];
  2368. CNavArea *second = m_selectedSet[i];
  2369. NavDirType dir = second->ComputeLargestPortal( first, &center, &halfWidth );
  2370. if (dir == NUM_DIRECTIONS)
  2371. {
  2372. player->EmitSound( "EDIT_CONNECT.AllDirections" );
  2373. bValid = false;
  2374. break;
  2375. }
  2376. dir = first->ComputeLargestPortal( second, &center, &halfWidth );
  2377. if (dir == NUM_DIRECTIONS)
  2378. {
  2379. player->EmitSound( "EDIT_CONNECT.AllDirections" );
  2380. bValid = false;
  2381. break;
  2382. }
  2383. }
  2384. if ( bValid )
  2385. {
  2386. for ( int i = 1; i < m_selectedSet.Count(); ++i )
  2387. {
  2388. CNavArea *first = m_selectedSet[0];
  2389. CNavArea *second = m_selectedSet[i];
  2390. NavDirType dir = second->ComputeLargestPortal( first, &center, &halfWidth );
  2391. second->ConnectTo( first, dir );
  2392. dir = first->ComputeLargestPortal( second, &center, &halfWidth );
  2393. first->ConnectTo( second, dir );
  2394. player->EmitSound( "EDIT_CONNECT.Added" );
  2395. }
  2396. }
  2397. }
  2398. else if ( m_selectedArea )
  2399. {
  2400. if ( m_markedLadder )
  2401. {
  2402. m_markedLadder->ConnectTo( m_selectedArea );
  2403. player->EmitSound( "EDIT_CONNECT.Added" );
  2404. }
  2405. else if ( m_markedArea )
  2406. {
  2407. NavDirType dir = GetMarkedArea()->ComputeLargestPortal( m_selectedArea, &center, &halfWidth );
  2408. if (dir == NUM_DIRECTIONS)
  2409. {
  2410. player->EmitSound( "EDIT_CONNECT.AllDirections" );
  2411. }
  2412. else
  2413. {
  2414. m_markedArea->ConnectTo( m_selectedArea, dir );
  2415. player->EmitSound( "EDIT_CONNECT.Added" );
  2416. }
  2417. }
  2418. else
  2419. {
  2420. if ( m_selectedSet.Count() == 1 )
  2421. {
  2422. CNavArea *area = m_selectedSet[0];
  2423. NavDirType dir = area->ComputeLargestPortal( m_selectedArea, &center, &halfWidth );
  2424. if (dir == NUM_DIRECTIONS)
  2425. {
  2426. player->EmitSound( "EDIT_CONNECT.AllDirections" );
  2427. }
  2428. else
  2429. {
  2430. area->ConnectTo( m_selectedArea, dir );
  2431. player->EmitSound( "EDIT_CONNECT.Added" );
  2432. }
  2433. }
  2434. else
  2435. {
  2436. Msg( "To connect areas, mark an area, highlight a second area, then invoke the connect command. Make sure the cursor is directly north, south, east, or west of the marked area." );
  2437. player->EmitSound( "EDIT_CONNECT.AllDirections" );
  2438. }
  2439. }
  2440. }
  2441. else if ( m_selectedLadder )
  2442. {
  2443. if ( m_markedArea )
  2444. {
  2445. m_markedArea->ConnectTo( m_selectedLadder );
  2446. player->EmitSound( "EDIT_CONNECT.Added" );
  2447. }
  2448. else
  2449. {
  2450. Msg( "To connect areas, mark an area, highlight a second area, then invoke the connect command. Make sure the cursor is directly north, south, east, or west of the marked area." );
  2451. player->EmitSound( "EDIT_CONNECT.AllDirections" );
  2452. }
  2453. }
  2454. SetMarkedArea( NULL ); // unmark the mark area
  2455. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2456. ClearSelectedSet();
  2457. }
  2458. //--------------------------------------------------------------------------------------------------------------
  2459. void CNavMesh::CommandNavDisconnect( void )
  2460. {
  2461. CBasePlayer *player = UTIL_GetListenServerHost();
  2462. if (player == NULL)
  2463. return;
  2464. if ( !IsEditMode( NORMAL ) )
  2465. return;
  2466. FindActiveNavArea();
  2467. if ( m_selectedSet.Count() > 1 )
  2468. {
  2469. bool bValid = true;
  2470. for ( int i = 1; i < m_selectedSet.Count(); ++i )
  2471. {
  2472. // 2 areas are selected, so connect them bi-directionally
  2473. CNavArea *first = m_selectedSet[0];
  2474. CNavArea *second = m_selectedSet[i];
  2475. if ( !first->IsConnected( second, NUM_DIRECTIONS ) && !second->IsConnected( first, NUM_DIRECTIONS ) )
  2476. {
  2477. player->EmitSound( "EDIT_CONNECT.AllDirections" );
  2478. bValid = false;
  2479. break;
  2480. }
  2481. }
  2482. if ( bValid )
  2483. {
  2484. for ( int i = 1; i < m_selectedSet.Count(); ++i )
  2485. {
  2486. // 2 areas are selected, so connect them bi-directionally
  2487. CNavArea *first = m_selectedSet[0];
  2488. CNavArea *second = m_selectedSet[i];
  2489. first->Disconnect( second );
  2490. second->Disconnect( first );
  2491. }
  2492. player->EmitSound( "EDIT_DISCONNECT.MarkedArea" );
  2493. }
  2494. }
  2495. else if ( m_selectedArea )
  2496. {
  2497. if ( m_markedArea )
  2498. {
  2499. m_markedArea->Disconnect( m_selectedArea );
  2500. m_selectedArea->Disconnect( m_markedArea );
  2501. player->EmitSound( "EDIT_DISCONNECT.MarkedArea" );
  2502. }
  2503. else if ( m_selectedSet.Count() == 1 )
  2504. {
  2505. m_selectedSet[0]->Disconnect( m_selectedArea );
  2506. m_selectedArea->Disconnect( m_selectedSet[0] );
  2507. player->EmitSound( "EDIT_DISCONNECT.MarkedArea" );
  2508. }
  2509. else
  2510. {
  2511. if ( m_markedLadder )
  2512. {
  2513. m_markedLadder->Disconnect( m_selectedArea );
  2514. m_selectedArea->Disconnect( m_markedLadder );
  2515. player->EmitSound( "EDIT_DISCONNECT.MarkedArea" );
  2516. }
  2517. else
  2518. {
  2519. Msg( "To disconnect areas, mark an area, highlight a second area, then invoke the disconnect command. This will remove all connections between the two areas." );
  2520. player->EmitSound( "EDIT_DISCONNECT.NoMarkedArea" );
  2521. }
  2522. }
  2523. }
  2524. else if ( m_selectedLadder )
  2525. {
  2526. if ( m_markedArea )
  2527. {
  2528. m_markedArea->Disconnect( m_selectedLadder );
  2529. m_selectedLadder->Disconnect( m_markedArea );
  2530. player->EmitSound( "EDIT_DISCONNECT.MarkedArea" );
  2531. }
  2532. if ( m_selectedSet.Count() == 1 )
  2533. {
  2534. m_selectedSet[0]->Disconnect( m_selectedLadder );
  2535. m_selectedLadder->Disconnect( m_selectedSet[0] );
  2536. player->EmitSound( "EDIT_DISCONNECT.MarkedArea" );
  2537. }
  2538. else
  2539. {
  2540. Msg( "To disconnect areas, mark an area, highlight a second area, then invoke the disconnect command. This will remove all connections between the two areas." );
  2541. player->EmitSound( "EDIT_DISCONNECT.NoMarkedArea" );
  2542. }
  2543. }
  2544. ClearSelectedSet();
  2545. SetMarkedArea( NULL ); // unmark the mark area
  2546. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2547. }
  2548. //--------------------------------------------------------------------------------------------------------------
  2549. // Disconnect all outgoing one-way connects from each area in the selected set
  2550. void CNavMesh::CommandNavDisconnectOutgoingOneWays( void )
  2551. {
  2552. CBasePlayer *player = UTIL_GetListenServerHost();
  2553. if ( !player )
  2554. return;
  2555. if ( !IsEditMode( NORMAL ) )
  2556. return;
  2557. if ( m_selectedSet.Count() == 0 )
  2558. {
  2559. FindActiveNavArea();
  2560. if ( !m_selectedArea )
  2561. {
  2562. return;
  2563. }
  2564. m_selectedSet.AddToTail( m_selectedArea );
  2565. }
  2566. for ( int i = 0; i < m_selectedSet.Count(); ++i )
  2567. {
  2568. CNavArea *area = m_selectedSet[i];
  2569. CUtlVector< CNavArea * > adjVector;
  2570. area->CollectAdjacentAreas( &adjVector );
  2571. for( int j=0; j<adjVector.Count(); ++j )
  2572. {
  2573. CNavArea *adj = adjVector[j];
  2574. if ( !adj->IsConnected( area, NUM_DIRECTIONS ) )
  2575. {
  2576. // no connect back - this is a one-way connection
  2577. area->Disconnect( adj );
  2578. }
  2579. }
  2580. }
  2581. player->EmitSound( "EDIT_DISCONNECT.MarkedArea" );
  2582. ClearSelectedSet();
  2583. SetMarkedArea( NULL ); // unmark the mark area
  2584. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2585. }
  2586. //--------------------------------------------------------------------------------------------------------------
  2587. void CNavMesh::CommandNavSplice( void )
  2588. {
  2589. CBasePlayer *player = UTIL_GetListenServerHost();
  2590. if (player == NULL)
  2591. return;
  2592. if ( !IsEditMode( NORMAL ) )
  2593. return;
  2594. FindActiveNavArea();
  2595. if ( m_selectedArea )
  2596. {
  2597. if (GetMarkedArea())
  2598. {
  2599. if (m_selectedArea->SpliceEdit( GetMarkedArea() ))
  2600. player->EmitSound( "EDIT_SPLICE.MarkedArea" );
  2601. else
  2602. player->EmitSound( "EDIT_SPLICE.NoMarkedArea" );
  2603. }
  2604. else
  2605. {
  2606. Msg( "To splice, mark an area, highlight a second area, then invoke the splice command to create an area between them" );
  2607. player->EmitSound( "EDIT_SPLICE.NoMarkedArea" );
  2608. }
  2609. }
  2610. SetMarkedArea( NULL ); // unmark the mark area
  2611. ClearSelectedSet();
  2612. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2613. }
  2614. //--------------------------------------------------------------------------------------------------------------
  2615. /**
  2616. * Toggle an attribute on given area
  2617. */
  2618. void CNavMesh::DoToggleAttribute( CNavArea *area, NavAttributeType attribute )
  2619. {
  2620. area->SetAttributes( area->GetAttributes() ^ attribute );
  2621. // keep a list of all "transient" nav areas
  2622. if ( attribute == NAV_MESH_TRANSIENT )
  2623. {
  2624. if ( area->GetAttributes() & NAV_MESH_TRANSIENT )
  2625. {
  2626. m_transientAreas.AddToTail( area );
  2627. }
  2628. else
  2629. {
  2630. m_transientAreas.FindAndRemove( area );
  2631. }
  2632. }
  2633. }
  2634. //--------------------------------------------------------------------------------------------------------------
  2635. void CNavMesh::CommandNavToggleAttribute( NavAttributeType attribute )
  2636. {
  2637. CBasePlayer *player = UTIL_GetListenServerHost();
  2638. if (player == NULL)
  2639. return;
  2640. if ( !IsEditMode( NORMAL ) )
  2641. return;
  2642. if ( IsSelectedSetEmpty() )
  2643. {
  2644. // the old way
  2645. FindActiveNavArea();
  2646. if ( m_selectedArea )
  2647. {
  2648. player->EmitSound( "EDIT.ToggleAttribute" );
  2649. DoToggleAttribute( m_selectedArea, attribute );
  2650. }
  2651. }
  2652. else
  2653. {
  2654. // toggle the attribute in all areas in the selected set
  2655. player->EmitSound( "EDIT.ToggleAttribute" );
  2656. FOR_EACH_VEC( m_selectedSet, it )
  2657. {
  2658. CNavArea *area = m_selectedSet[ it ];
  2659. DoToggleAttribute( area, attribute );
  2660. }
  2661. Msg( "Changed attribute in %d areas\n", m_selectedSet.Count() );
  2662. ClearSelectedSet();
  2663. }
  2664. SetMarkedArea( NULL ); // unmark the mark area
  2665. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2666. }
  2667. //--------------------------------------------------------------------------------------------------------------
  2668. void CNavMesh::CommandNavTogglePlaceMode( void )
  2669. {
  2670. CBasePlayer *player = UTIL_GetListenServerHost();
  2671. if (player == NULL)
  2672. return;
  2673. if ( IsEditMode( PLACE_PAINTING ) )
  2674. {
  2675. SetEditMode( NORMAL );
  2676. }
  2677. else
  2678. {
  2679. SetEditMode( PLACE_PAINTING );
  2680. }
  2681. player->EmitSound( "EDIT_TOGGLE_PLACE_MODE" );
  2682. SetMarkedArea( NULL ); // unmark the mark area
  2683. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2684. }
  2685. //--------------------------------------------------------------------------------------------------------------
  2686. void CNavMesh::CommandNavPlaceFloodFill( void )
  2687. {
  2688. CBasePlayer *player = UTIL_GetListenServerHost();
  2689. if (player == NULL)
  2690. return;
  2691. if ( !IsEditMode( PLACE_PAINTING ) )
  2692. return;
  2693. FindActiveNavArea();
  2694. if ( m_selectedArea )
  2695. {
  2696. PlaceFloodFillFunctor pff( m_selectedArea );
  2697. SearchSurroundingAreas( m_selectedArea, m_selectedArea->GetCenter(), pff );
  2698. }
  2699. SetMarkedArea( NULL ); // unmark the mark area
  2700. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2701. }
  2702. //--------------------------------------------------------------------------------------------------------------
  2703. void CNavMesh::CommandNavPlaceSet( void )
  2704. {
  2705. CBasePlayer *player = UTIL_GetListenServerHost();
  2706. if (player == NULL)
  2707. return;
  2708. if ( !IsEditMode( PLACE_PAINTING ) )
  2709. return;
  2710. if ( !IsSelectedSetEmpty() )
  2711. {
  2712. FOR_EACH_VEC( m_selectedSet, it )
  2713. {
  2714. CNavArea *area = m_selectedSet[ it ];
  2715. area->SetPlace( TheNavMesh->GetNavPlace() );
  2716. }
  2717. }
  2718. }
  2719. //--------------------------------------------------------------------------------------------------------------
  2720. void CNavMesh::CommandNavPlacePick( void )
  2721. {
  2722. CBasePlayer *player = UTIL_GetListenServerHost();
  2723. if (player == NULL)
  2724. return;
  2725. if ( !IsEditMode( PLACE_PAINTING ) )
  2726. return;
  2727. FindActiveNavArea();
  2728. if ( m_selectedArea )
  2729. {
  2730. player->EmitSound( "EDIT_PLACE_PICK" );
  2731. TheNavMesh->SetNavPlace( m_selectedArea->GetPlace() );
  2732. }
  2733. SetMarkedArea( NULL ); // unmark the mark area
  2734. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2735. }
  2736. //--------------------------------------------------------------------------------------------------------------
  2737. void CNavMesh::CommandNavTogglePlacePainting( void )
  2738. {
  2739. CBasePlayer *player = UTIL_GetListenServerHost();
  2740. if (player == NULL)
  2741. return;
  2742. if ( !IsEditMode( PLACE_PAINTING ) )
  2743. return;
  2744. FindActiveNavArea();
  2745. if ( m_selectedArea )
  2746. {
  2747. if (m_isPlacePainting)
  2748. {
  2749. m_isPlacePainting = false;
  2750. player->EmitSound( "Bot.EditSwitchOff" );
  2751. }
  2752. else
  2753. {
  2754. m_isPlacePainting = true;
  2755. player->EmitSound( "Bot.EditSwitchOn" );
  2756. // paint the initial area
  2757. m_selectedArea->SetPlace( TheNavMesh->GetNavPlace() );
  2758. }
  2759. }
  2760. SetMarkedArea( NULL ); // unmark the mark area
  2761. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2762. }
  2763. //--------------------------------------------------------------------------------------------------------------
  2764. void CNavMesh::CommandNavMarkUnnamed( void )
  2765. {
  2766. CBasePlayer *player = UTIL_GetListenServerHost();
  2767. if (player == NULL)
  2768. return;
  2769. if ( !IsEditMode( NORMAL ) )
  2770. return;
  2771. FindActiveNavArea();
  2772. if ( m_selectedArea )
  2773. {
  2774. if (GetMarkedArea())
  2775. {
  2776. player->EmitSound( "EDIT_MARK_UNNAMED.Enable" );
  2777. SetMarkedArea( NULL );
  2778. }
  2779. else
  2780. {
  2781. SetMarkedArea( NULL );
  2782. FOR_EACH_VEC( TheNavAreas, it )
  2783. {
  2784. CNavArea *area = TheNavAreas[ it ];
  2785. if ( area->GetPlace() == 0 )
  2786. {
  2787. SetMarkedArea( area );
  2788. break;
  2789. }
  2790. }
  2791. if ( !GetMarkedArea() )
  2792. {
  2793. player->EmitSound( "EDIT_MARK_UNNAMED.NoMarkedArea" );
  2794. }
  2795. else
  2796. {
  2797. player->EmitSound( "EDIT_MARK_UNNAMED.MarkedArea" );
  2798. int connected = 0;
  2799. connected += GetMarkedArea()->GetAdjacentCount( NORTH );
  2800. connected += GetMarkedArea()->GetAdjacentCount( SOUTH );
  2801. connected += GetMarkedArea()->GetAdjacentCount( EAST );
  2802. connected += GetMarkedArea()->GetAdjacentCount( WEST );
  2803. int totalUnnamedAreas = 0;
  2804. FOR_EACH_VEC( TheNavAreas, it )
  2805. {
  2806. CNavArea *area = TheNavAreas[ it ];
  2807. if ( area->GetPlace() == 0 )
  2808. {
  2809. ++totalUnnamedAreas;
  2810. }
  2811. }
  2812. Msg( "Marked Area is connected to %d other Areas - there are %d total unnamed areas\n", connected, totalUnnamedAreas );
  2813. }
  2814. }
  2815. }
  2816. m_markedCorner = NUM_CORNERS; // clear the corner selection
  2817. }
  2818. //--------------------------------------------------------------------------------------------------------------
  2819. void CNavMesh::CommandNavCornerSelect( void )
  2820. {
  2821. CBasePlayer *player = UTIL_GetListenServerHost();
  2822. if (player == NULL)
  2823. return;
  2824. if ( !IsEditMode( NORMAL ) )
  2825. return;
  2826. FindActiveNavArea();
  2827. if ( m_selectedArea )
  2828. {
  2829. if (GetMarkedArea())
  2830. {
  2831. int corner = (m_markedCorner + 1) % (NUM_CORNERS + 1);
  2832. m_markedCorner = (NavCornerType)corner;
  2833. player->EmitSound( "EDIT_SELECT_CORNER.MarkedArea" );
  2834. }
  2835. else
  2836. {
  2837. player->EmitSound( "EDIT_SELECT_CORNER.NoMarkedArea" );
  2838. }
  2839. }
  2840. }
  2841. //--------------------------------------------------------------------------------------------------------------
  2842. void CNavMesh::CommandNavCornerRaise( const CCommand &args )
  2843. {
  2844. CBasePlayer *player = UTIL_GetListenServerHost();
  2845. if (player == NULL)
  2846. return;
  2847. if ( !IsEditMode( NORMAL ) )
  2848. return;
  2849. int amount = 1;
  2850. if ( args.ArgC() > 1 )
  2851. {
  2852. amount = atoi( args[1] );
  2853. }
  2854. if (IsSelectedSetEmpty())
  2855. {
  2856. // the old way
  2857. FindActiveNavArea();
  2858. if ( m_selectedArea )
  2859. {
  2860. if (GetMarkedArea())
  2861. {
  2862. GetMarkedArea()->RaiseCorner( m_markedCorner, amount );
  2863. player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
  2864. }
  2865. else
  2866. {
  2867. player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" );
  2868. }
  2869. }
  2870. }
  2871. else
  2872. {
  2873. // raise all areas in the selected set
  2874. player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
  2875. FOR_EACH_VEC( m_selectedSet, it )
  2876. {
  2877. CNavArea *area = m_selectedSet[ it ];
  2878. area->RaiseCorner( NUM_CORNERS, amount, false );
  2879. }
  2880. Msg( "Raised %d areas\n", m_selectedSet.Count() );
  2881. }
  2882. }
  2883. //--------------------------------------------------------------------------------------------------------------
  2884. void CNavMesh::CommandNavCornerLower( const CCommand &args )
  2885. {
  2886. CBasePlayer *player = UTIL_GetListenServerHost();
  2887. if (player == NULL)
  2888. return;
  2889. if ( !IsEditMode( NORMAL ) )
  2890. return;
  2891. int amount = -1;
  2892. if ( args.ArgC() > 1 )
  2893. {
  2894. amount = -atoi( args[1] );
  2895. }
  2896. if (IsSelectedSetEmpty())
  2897. {
  2898. // the old way
  2899. FindActiveNavArea();
  2900. if ( m_selectedArea )
  2901. {
  2902. if (GetMarkedArea())
  2903. {
  2904. GetMarkedArea()->RaiseCorner( m_markedCorner, amount );
  2905. player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
  2906. }
  2907. else
  2908. {
  2909. player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" );
  2910. }
  2911. }
  2912. }
  2913. else
  2914. {
  2915. // raise all areas in the selected set
  2916. player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
  2917. FOR_EACH_VEC( m_selectedSet, it )
  2918. {
  2919. CNavArea *area = m_selectedSet[ it ];
  2920. area->RaiseCorner( NUM_CORNERS, amount, false );
  2921. }
  2922. Msg( "Lowered %d areas\n", m_selectedSet.Count() );
  2923. }
  2924. }
  2925. //--------------------------------------------------------------------------------------------------------------
  2926. void CNavMesh::CommandNavCornerPlaceOnGround( const CCommand &args )
  2927. {
  2928. CBasePlayer *player = UTIL_GetListenServerHost();
  2929. if (player == NULL)
  2930. return;
  2931. if ( !IsEditMode( NORMAL ) )
  2932. return;
  2933. float inset = 0.0f;
  2934. if ( args.ArgC() == 2 )
  2935. {
  2936. inset = atof(args[1]);
  2937. }
  2938. if (IsSelectedSetEmpty())
  2939. {
  2940. // the old way
  2941. FindActiveNavArea();
  2942. if ( m_selectedArea )
  2943. {
  2944. if ( m_markedArea )
  2945. {
  2946. m_markedArea->PlaceOnGround( m_markedCorner, inset );
  2947. }
  2948. else
  2949. {
  2950. m_selectedArea->PlaceOnGround( NUM_CORNERS, inset );
  2951. }
  2952. player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
  2953. }
  2954. else
  2955. {
  2956. player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" );
  2957. }
  2958. }
  2959. else
  2960. {
  2961. // snap all areas in the selected set to the ground
  2962. player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
  2963. FOR_EACH_VEC( m_selectedSet, it )
  2964. {
  2965. CNavArea *area = m_selectedSet[ it ];
  2966. area->PlaceOnGround( NUM_CORNERS, inset );
  2967. }
  2968. Msg( "Placed %d areas on the ground\n", m_selectedSet.Count() );
  2969. }
  2970. }
  2971. //--------------------------------------------------------------------------------------------------------------
  2972. void CNavMesh::CommandNavWarpToMark( void )
  2973. {
  2974. CBasePlayer *player = UTIL_GetListenServerHost();
  2975. if (player == NULL)
  2976. return;
  2977. if ( !IsEditMode( NORMAL ) )
  2978. return;
  2979. CNavArea *targetArea = GetMarkedArea();
  2980. if ( !targetArea && !IsSelectedSetEmpty() )
  2981. {
  2982. targetArea = m_selectedSet[0];
  2983. }
  2984. if ( targetArea )
  2985. {
  2986. Vector origin = targetArea->GetCenter() + Vector( 0, 0, 0.75f * HumanHeight );
  2987. QAngle angles = player->GetAbsAngles();
  2988. if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING )
  2989. {
  2990. UTIL_SetOrigin( player, origin );
  2991. player->EmitSound( "EDIT_WARP_TO_MARK" );
  2992. }
  2993. else
  2994. {
  2995. player->Teleport( &origin, &angles, &vec3_origin );
  2996. player->EmitSound( "EDIT_WARP_TO_MARK" );
  2997. }
  2998. }
  2999. else if ( GetMarkedLadder() )
  3000. {
  3001. CNavLadder *ladder = GetMarkedLadder();
  3002. QAngle angles = player->GetAbsAngles();
  3003. Vector origin = (ladder->m_top + ladder->m_bottom)/2;
  3004. origin.x += ladder->GetNormal().x * GenerationStepSize;
  3005. origin.y += ladder->GetNormal().y * GenerationStepSize;
  3006. if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING )
  3007. {
  3008. UTIL_SetOrigin( player, origin );
  3009. player->EmitSound( "EDIT_WARP_TO_MARK" );
  3010. }
  3011. else
  3012. {
  3013. player->Teleport( &origin, &angles, &vec3_origin );
  3014. player->EmitSound( "EDIT_WARP_TO_MARK" );
  3015. }
  3016. }
  3017. else
  3018. {
  3019. player->EmitSound( "EDIT_WARP_TO_MARK" );
  3020. }
  3021. }
  3022. //--------------------------------------------------------------------------------------------------------------
  3023. void CNavMesh::CommandNavLadderFlip( void )
  3024. {
  3025. CBasePlayer *player = UTIL_GetListenServerHost();
  3026. if (player == NULL)
  3027. return;
  3028. if ( !IsEditMode( NORMAL ) )
  3029. return;
  3030. FindActiveNavArea();
  3031. if ( m_selectedLadder )
  3032. {
  3033. CNavArea *area;
  3034. // flip direction
  3035. player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
  3036. m_selectedLadder->SetDir( OppositeDirection( m_selectedLadder->GetDir() ) );
  3037. // and reverse ladder's area pointers
  3038. area = m_selectedLadder->m_topBehindArea;
  3039. m_selectedLadder->m_topBehindArea = m_selectedLadder->m_topForwardArea;
  3040. m_selectedLadder->m_topForwardArea = area;
  3041. area = m_selectedLadder->m_topRightArea;
  3042. m_selectedLadder->m_topRightArea = m_selectedLadder->m_topLeftArea;
  3043. m_selectedLadder->m_topLeftArea = area;
  3044. }
  3045. SetMarkedArea( NULL ); // unmark the mark area
  3046. m_markedCorner = NUM_CORNERS; // clear the corner selection
  3047. }
  3048. //--------------------------------------------------------------------------------------------------------------
  3049. class RadiusSelect
  3050. {
  3051. Vector m_origin;
  3052. float m_radiusSquared;
  3053. int m_selected;
  3054. public:
  3055. RadiusSelect( const Vector &origin, float radius )
  3056. {
  3057. m_origin = origin;
  3058. m_radiusSquared = radius * radius;
  3059. m_selected = 0;
  3060. }
  3061. bool operator()( CNavArea *area )
  3062. {
  3063. if ( TheNavMesh->IsInSelectedSet( area ) )
  3064. return true;
  3065. Vector close;
  3066. area->GetClosestPointOnArea( m_origin, &close );
  3067. if ( close.DistToSqr( m_origin ) < m_radiusSquared )
  3068. {
  3069. TheNavMesh->AddToSelectedSet( area );
  3070. ++m_selected;
  3071. }
  3072. return true;
  3073. }
  3074. int GetNumSelected( void ) const
  3075. {
  3076. return m_selected;
  3077. }
  3078. };
  3079. //--------------------------------------------------------------------------------------------------------------
  3080. CON_COMMAND_F( nav_select_radius, "Adds all areas in a radius to the selection set", FCVAR_CHEAT )
  3081. {
  3082. if ( !UTIL_IsCommandIssuedByServerAdmin() || engine->IsDedicatedServer() )
  3083. return;
  3084. if ( args.ArgC() < 2 )
  3085. {
  3086. Msg( "Needs a radius\n" );
  3087. return;
  3088. }
  3089. float radius = atof( args[ 1 ] );
  3090. CBasePlayer *host = UTIL_GetListenServerHost();
  3091. if ( !host )
  3092. return;
  3093. RadiusSelect select( host->GetAbsOrigin(), radius );
  3094. TheNavMesh->ForAllAreas( select );
  3095. Msg( "%d areas added to selection\n", select.GetNumSelected() );
  3096. }
  3097. //--------------------------------------------------------------------------------------------------------------
  3098. /**
  3099. * Add area to the currently selected set
  3100. */
  3101. void CNavMesh::AddToSelectedSet( CNavArea *area )
  3102. {
  3103. if ( !area )
  3104. return;
  3105. // make sure area is not already in list
  3106. if (m_selectedSet.Find( area ) != m_selectedSet.InvalidIndex())
  3107. return;
  3108. m_selectedSet.AddToTail( area );
  3109. }
  3110. //--------------------------------------------------------------------------------------------------------------
  3111. /**
  3112. * Remove area from the currently selected set
  3113. */
  3114. void CNavMesh::RemoveFromSelectedSet( CNavArea *area )
  3115. {
  3116. m_selectedSet.FindAndRemove( area );
  3117. }
  3118. //--------------------------------------------------------------------------------------------------------------
  3119. /**
  3120. * Add area to the drag selection set
  3121. */
  3122. void CNavMesh::AddToDragSelectionSet( CNavArea *area )
  3123. {
  3124. if ( !area )
  3125. return;
  3126. // make sure area is not already in list
  3127. if (m_dragSelectionSet.Find( area ) != m_dragSelectionSet.InvalidIndex())
  3128. return;
  3129. m_dragSelectionSet.AddToTail( area );
  3130. }
  3131. //--------------------------------------------------------------------------------------------------------------
  3132. /**
  3133. * Remove area from the drag selection set
  3134. */
  3135. void CNavMesh::RemoveFromDragSelectionSet( CNavArea *area )
  3136. {
  3137. m_dragSelectionSet.FindAndRemove( area );
  3138. }
  3139. //--------------------------------------------------------------------------------------------------------------
  3140. /**
  3141. * Clear the currently selected set to empty
  3142. */
  3143. void CNavMesh::ClearDragSelectionSet( void )
  3144. {
  3145. m_dragSelectionSet.RemoveAll();
  3146. }
  3147. //--------------------------------------------------------------------------------------------------------------
  3148. /**
  3149. * Clear the currently selected set to empty
  3150. */
  3151. void CNavMesh::ClearSelectedSet( void )
  3152. {
  3153. m_selectedSet.RemoveAll();
  3154. }
  3155. //--------------------------------------------------------------------------------------------------------------
  3156. /**
  3157. * Return true if the selected set is empty
  3158. */
  3159. bool CNavMesh::IsSelectedSetEmpty( void ) const
  3160. {
  3161. return (m_selectedSet.Count() == 0);
  3162. }
  3163. //--------------------------------------------------------------------------------------------------------------
  3164. /**
  3165. * Return size of the selected set
  3166. */
  3167. int CNavMesh::GetSelecteSetSize( void ) const
  3168. {
  3169. return m_selectedSet.Count();
  3170. }
  3171. //--------------------------------------------------------------------------------------------------------------
  3172. /**
  3173. * Return the selected set
  3174. */
  3175. const NavAreaVector &CNavMesh::GetSelectedSet( void ) const
  3176. {
  3177. return m_selectedSet;
  3178. }
  3179. //--------------------------------------------------------------------------------------------------------------
  3180. /**
  3181. * Return true if the given area is in the selected set
  3182. */
  3183. bool CNavMesh::IsInSelectedSet( const CNavArea *area ) const
  3184. {
  3185. FOR_EACH_VEC( m_selectedSet, it )
  3186. {
  3187. const CNavArea *setArea = m_selectedSet[ it ];
  3188. if (setArea == area)
  3189. return true;
  3190. }
  3191. return false;
  3192. }
  3193. //--------------------------------------------------------------------------------------------------------------
  3194. /**
  3195. * Invoked when given area has just been added to the mesh in edit mode
  3196. */
  3197. void CNavMesh::OnEditCreateNotify( CNavArea *newArea )
  3198. {
  3199. FOR_EACH_VEC( TheNavAreas, it )
  3200. {
  3201. TheNavAreas[ it ]->OnEditCreateNotify( newArea );
  3202. }
  3203. }
  3204. //--------------------------------------------------------------------------------------------------------------
  3205. /**
  3206. * Invoked when given area has just been deleted from the mesh in edit mode
  3207. */
  3208. void CNavMesh::OnEditDestroyNotify( CNavArea *deadArea )
  3209. {
  3210. // clean up any edit hooks
  3211. m_markedArea = NULL;
  3212. m_selectedArea = NULL;
  3213. m_lastSelectedArea = NULL;
  3214. m_selectedLadder = NULL;
  3215. m_lastSelectedLadder = NULL;
  3216. m_markedLadder = NULL;
  3217. m_avoidanceObstacleAreas.FindAndRemove( deadArea );
  3218. m_blockedAreas.FindAndRemove( deadArea );
  3219. FOR_EACH_VEC( TheNavAreas, it )
  3220. {
  3221. TheNavAreas[ it ]->OnEditDestroyNotify( deadArea );
  3222. }
  3223. EditDestroyNotification notification( deadArea );
  3224. ForEachActor( notification );
  3225. }
  3226. //--------------------------------------------------------------------------------------------------------------
  3227. /**
  3228. * Invoked when given ladder has just been deleted from the mesh in edit mode
  3229. * @TODO: Implement me
  3230. */
  3231. void CNavMesh::OnEditDestroyNotify( CNavLadder *deadLadder )
  3232. {
  3233. }