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

3918 lines
96 KiB

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