From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/nav_edit.cpp | 7928 +++++++++++++++++++-------------------- 1 file changed, 3964 insertions(+), 3964 deletions(-) (limited to 'mp/src/game/server/nav_edit.cpp') diff --git a/mp/src/game/server/nav_edit.cpp b/mp/src/game/server/nav_edit.cpp index 512d5dda..4113438d 100644 --- a/mp/src/game/server/nav_edit.cpp +++ b/mp/src/game/server/nav_edit.cpp @@ -1,3964 +1,3964 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -// nav_edit.cpp -// Implementation of Navigation Mesh edit mode -// Author: Michael Booth, 2003-2004 - -#include "cbase.h" -#include "nav_mesh.h" -#include "nav_pathfind.h" -#include "nav_node.h" -#include "nav_colors.h" -#include "Color.h" -#include "tier0/vprof.h" -#include "collisionutils.h" -#include "world.h" -#include "functorutils.h" -#include "team.h" -#ifdef TERROR -#include "TerrorShared.h" -#endif - -#ifdef DOTA_SERVER_DLL -#include "dota_npc_base.h" -#include "dota_player.h" -#endif - -// NOTE: This has to be the last file included! -#include "tier0/memdbgon.h" - - -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" ); -ConVar nav_snap_to_grid( "nav_snap_to_grid", "0", FCVAR_CHEAT, "Snap to the nav generation grid when creating new nav areas" ); -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." ); -#ifdef DEBUG -ConVar nav_draw_limit( "nav_draw_limit", "50", FCVAR_CHEAT, "The maximum number of areas to draw in edit mode" ); -#else -ConVar nav_draw_limit( "nav_draw_limit", "500", FCVAR_CHEAT, "The maximum number of areas to draw in edit mode" ); -#endif -ConVar nav_solid_props( "nav_solid_props", "0", FCVAR_CHEAT, "Make props solid to nav generation/editing" ); -ConVar nav_create_area_at_feet( "nav_create_area_at_feet", "0", FCVAR_CHEAT, "Anchor nav_begin_area Z to editing player's feet" ); - -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" ); -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" ); - -extern void GetNavUIEditVectors( Vector *pos, Vector *forward ); - -Color s_dragSelectionSetAddColor( 100, 255, 100, 96 ); -Color s_dragSelectionSetDeleteColor( 255, 100, 100, 96 ); - -#if DEBUG_NAV_NODES -extern ConVar nav_show_nodes; -#endif // DEBUG_NAV_NODES - - -//-------------------------------------------------------------------------------------------------------------- -int GetGridSize( bool forceGrid = false ) -{ - if ( TheNavMesh->IsGenerating() ) - { - return (int)GenerationStepSize; - } - - int snapVal = nav_snap_to_grid.GetInt(); - if ( forceGrid && !snapVal ) - { - snapVal = 1; - } - - if ( snapVal == 0 ) - { - return 0; - } - - int scale = (int)GenerationStepSize; - switch ( snapVal ) - { - case 3: - scale = 1; - break; - case 2: - scale = 5; - break; - case 1: - default: - break; - } - - return scale; -} - - -//-------------------------------------------------------------------------------------------------------------- -Vector CNavMesh::SnapToGrid( const Vector& in, bool snapX, bool snapY, bool forceGrid ) const -{ - int scale = GetGridSize( forceGrid ); - if ( !scale ) - { - return in; - } - - Vector out( in ); - - if ( snapX ) - { - out.x = RoundToUnits( in.x, scale ); - } - - if ( snapY ) - { - out.y = RoundToUnits( in.y, scale ); - } - - return out; -} - - -//-------------------------------------------------------------------------------------------------------------- -float CNavMesh::SnapToGrid( float x, bool forceGrid ) const -{ - int scale = GetGridSize( forceGrid ); - if ( !scale ) - { - return x; - } - - x = RoundToUnits( x, scale ); - return x; -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::GetEditVectors( Vector *pos, Vector *forward ) -{ - if ( !pos || !forward ) - { - return; - } - - CBasePlayer *player = UTIL_GetListenServerHost(); - if ( !player ) - { - return; - } - - //DOTA places the edit cursor where the 2D cursor is located. -#ifdef DOTA_SERVER_DLL - CDOTAPlayer *pDOTAPlayer = ToDOTAPlayer( player ); - - if ( pDOTAPlayer && pDOTAPlayer->GetMoveType() != MOVETYPE_NOCLIP ) - { - Vector dir = pDOTAPlayer->GetCrosshairTracePos() - player->EyePosition(); - VectorNormalize( dir ); - - *forward = dir; - } - else - { - AngleVectors( player->EyeAngles() + player->GetPunchAngle(), forward ); - } -#else - Vector dir; - AngleVectors( player->EyeAngles() + player->GetPunchAngle(), forward ); -#endif - - *pos = player->EyePosition(); - -#ifdef SERVER_USES_VGUI -// GetNavUIEditVectors( pos, forward ); -#endif // SERVER_USES_VGUI -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Change the edit mode - */ -void CNavMesh::SetEditMode( EditModeType mode ) -{ - m_markedLadder = NULL; - m_markedArea = NULL; - m_markedCorner = NUM_CORNERS; - - m_editMode = mode; - - m_isContinuouslySelecting = false; - m_isContinuouslyDeselecting = false; - m_bIsDragDeselecting = false; -} - - -//-------------------------------------------------------------------------------------------------------------- -bool CNavMesh::FindNavAreaOrLadderAlongRay( const Vector &start, const Vector &end, CNavArea **bestArea, CNavLadder **bestLadder, CNavArea *ignore ) -{ - if ( !m_grid.Count() ) - return false; - - Ray_t ray; - ray.Init( start, end, vec3_origin, vec3_origin ); - - *bestArea = NULL; - *bestLadder = NULL; - - float bestDist = 1.0f; // 0..1 fraction - - for ( int i=0; iGetNormal(), right, up ); - right *= ladder->m_width * 0.5f; - left = -right; - - Vector c1 = ladder->m_top + right; - Vector c2 = ladder->m_top + left; - Vector c3 = ladder->m_bottom + right; - Vector c4 = ladder->m_bottom + left; - float dist = IntersectRayWithTriangle( ray, c1, c2, c4, false ); - if ( dist > 0 && dist < bestDist ) - { - *bestLadder = ladder; - bestDist = dist; - } - - dist = IntersectRayWithTriangle( ray, c1, c4, c3, false ); - if ( dist > 0 && dist < bestDist ) - { - *bestLadder = ladder; - bestDist = dist; - } - } - - Extent extent; - extent.lo = extent.hi = start; - extent.Encompass( end ); - - int loX = WorldToGridX( extent.lo.x ); - int loY = WorldToGridY( extent.lo.y ); - int hiX = WorldToGridX( extent.hi.x ); - int hiY = WorldToGridY( extent.hi.y ); - - for( int y = loY; y <= hiY; ++y ) - { - for( int x = loX; x <= hiX; ++x ) - { - NavAreaVector &areaGrid = m_grid[ x + y*m_gridSizeX ]; - - FOR_EACH_VEC( areaGrid, it ) - { - CNavArea *area = areaGrid[ it ]; - if ( area == ignore ) - continue; - - Vector nw = area->m_nwCorner; - Vector se = area->m_seCorner; - Vector ne, sw; - ne.x = se.x; - ne.y = nw.y; - ne.z = area->m_neZ; - sw.x = nw.x; - sw.y = se.y; - sw.z = area->m_swZ; - - float dist = IntersectRayWithTriangle( ray, nw, ne, se, false ); - if ( dist > 0 && dist < bestDist ) - { - *bestArea = area; - bestDist = dist; - } - - dist = IntersectRayWithTriangle( ray, se, sw, nw, false ); - if ( dist > 0 && dist < bestDist ) - { - *bestArea = area; - bestDist = dist; - } - } - } - } - - if ( *bestArea ) - { - *bestLadder = NULL; - } - - return bestDist < 1.0f; -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Convenience function to find the nav area a player is looking at, for editing commands - */ -bool CNavMesh::FindActiveNavArea( void ) -{ - VPROF( "CNavMesh::FindActiveNavArea" ); - - m_splitAlongX = false; - m_splitEdge = 0.0f; - m_selectedArea = NULL; - m_climbableSurface = false; - m_selectedLadder = NULL; - - CBasePlayer *player = UTIL_GetListenServerHost(); - if ( player == NULL ) - return false; - - Vector from, dir; - GetEditVectors( &from, &dir ); - - float maxRange = 2000.0f; // 500 - bool isClippingRayAtFeet = false; - if ( nav_create_area_at_feet.GetBool() ) - { - if ( dir.z < 0 ) - { - float eyeHeight = player->GetViewOffset().z; - if ( eyeHeight != 0.0f ) - { - float rayHeight = -dir.z * maxRange; - maxRange = maxRange * eyeHeight / rayHeight; - isClippingRayAtFeet = true; - } - } - } - - Vector to = from + maxRange * dir; - - trace_t result; - CTraceFilterWalkableEntities filter( NULL, COLLISION_GROUP_NONE, WALK_THRU_EVERYTHING ); - UTIL_TraceLine( from, to, (nav_solid_props.GetBool()) ? MASK_NPCSOLID : MASK_NPCSOLID_BRUSHONLY, &filter, &result ); - - if (result.fraction != 1.0f) - { - if ( !IsEditMode( CREATING_AREA ) ) - { - m_climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0; - if ( !m_climbableSurface ) - { - m_climbableSurface = (result.contents & CONTENTS_LADDER) != 0; - } - m_surfaceNormal = result.plane.normal; - - if ( m_climbableSurface ) - { - // check if we're on the same plane as the original point when we're building a ladder - if ( IsEditMode( CREATING_LADDER ) ) - { - if ( m_surfaceNormal != m_ladderNormal ) - { - m_climbableSurface = false; - } - } - - if ( m_surfaceNormal.z > 0.9f ) - { - m_climbableSurface = false; // don't try to build ladders on flat ground - } - } - } - - if ( ( m_climbableSurface && !IsEditMode( CREATING_LADDER ) ) || !IsEditMode( CREATING_AREA ) ) - { - float closestDistSqr = 200.0f * 200.0f; - - for ( int i=0; im_bottom; - Vector absMax = ladder->m_top; - - Vector left( 0, 0, 0), right(0, 0, 0), up( 0, 0, 0); - VectorVectors( ladder->GetNormal(), right, up ); - right *= ladder->m_width * 0.5f; - left = -right; - - absMin.x += MIN( left.x, right.x ); - absMin.y += MIN( left.y, right.y ); - - absMax.x += MAX( left.x, right.x ); - absMax.y += MAX( left.y, right.y ); - - Extent e; - e.lo = absMin + Vector( -5, -5, -5 ); - e.hi = absMax + Vector( 5, 5, 5 ); - - if ( e.Contains( m_editCursorPos ) ) - { - m_selectedLadder = ladder; - break; - } - - if ( !m_climbableSurface ) - continue; - - Vector p1 = (ladder->m_bottom + ladder->m_top)/2; - Vector p2 = m_editCursorPos; - float distSqr = p1.DistToSqr( p2 ); - - if ( distSqr < closestDistSqr ) - { - m_selectedLadder = ladder; - closestDistSqr = distSqr; - } - } - } - - m_editCursorPos = result.endpos; - - // find the area the player is pointing at - if ( !m_climbableSurface && !m_selectedLadder ) - { - // Try to clip our trace to nav areas - FindNavAreaOrLadderAlongRay( result.startpos, result.endpos + 100.0f * dir, &m_selectedArea, &m_selectedLadder ); // extend a few units into the ground - - // Failing that, get the closest area to the endpoint - if ( !m_selectedArea && !m_selectedLadder ) - { - m_selectedArea = TheNavMesh->GetNearestNavArea( result.endpos, false, 500.0f ); - } - } - - if ( m_selectedArea ) - { - float yaw = player->EyeAngles().y; - while( yaw > 360.0f ) - yaw -= 360.0f; - - while( yaw < 0.0f ) - yaw += 360.0f; - - if ((yaw < 45.0f || yaw > 315.0f) || (yaw > 135.0f && yaw < 225.0f)) - { - m_splitEdge = SnapToGrid( result.endpos.y, true ); - m_splitAlongX = true; - } - else - { - m_splitEdge = SnapToGrid( result.endpos.x, true ); - m_splitAlongX = false; - } - } - - if ( !m_climbableSurface && !IsEditMode( CREATING_LADDER ) ) - { - m_editCursorPos = SnapToGrid( m_editCursorPos ); - } - - return true; - } - else if ( isClippingRayAtFeet ) - { - m_editCursorPos = SnapToGrid( result.endpos ); - } - - if ( IsEditMode( CREATING_LADDER ) || IsEditMode( CREATING_AREA ) ) - return false; - - // We started solid. Look for areas in front of us. - FindNavAreaOrLadderAlongRay( from, to, &m_selectedArea, &m_selectedLadder ); - - return (m_selectedArea != NULL || m_selectedLadder != NULL || isClippingRayAtFeet); -} - - -//-------------------------------------------------------------------------------------------------------------- -bool CNavMesh::FindLadderCorners( Vector *corner1, Vector *corner2, Vector *corner3 ) -{ - if ( !corner1 || !corner2 || !corner3 ) - return false; - - Vector ladderRight, ladderUp; - VectorVectors( m_ladderNormal, ladderRight, ladderUp ); - - Vector from, dir; - GetEditVectors( &from, &dir ); - - const float maxDist = 100000.0f; - - Ray_t ray; - ray.Init( from, from + dir * maxDist, vec3_origin, vec3_origin ); - - *corner1 = m_ladderAnchor + ladderUp * maxDist + ladderRight * maxDist; - *corner2 = m_ladderAnchor + ladderUp * maxDist - ladderRight * maxDist; - *corner3 = m_ladderAnchor - ladderUp * maxDist - ladderRight * maxDist; - float dist = IntersectRayWithTriangle( ray, *corner1, *corner2, *corner3, false ); - if ( dist < 0 ) - { - *corner2 = m_ladderAnchor - ladderUp * maxDist + ladderRight * maxDist; - dist = IntersectRayWithTriangle( ray, *corner1, *corner2, *corner3, false ); - } - - *corner3 = m_editCursorPos; - if ( dist > 0 && dist < maxDist ) - { - *corner3 = from + dir * dist * maxDist; - - float vertDistance = corner3->z - m_ladderAnchor.z; - float val = vertDistance / ladderUp.z; - - *corner1 = m_ladderAnchor + val * ladderUp; - *corner2 = *corner3 - val * ladderUp; - - return true; - } - - return false; -} - - -//-------------------------------------------------------------------------------------------------------------- -bool CheckForClimbableSurface( const Vector &start, const Vector &end ) -{ - trace_t result; - UTIL_TraceLine( start, end, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result ); - - bool climbableSurface = false; - if (result.fraction != 1.0f) - { - climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0; - if ( !climbableSurface ) - { - climbableSurface = (result.contents & CONTENTS_LADDER) != 0; - } - } - - return climbableSurface; -} - - -//-------------------------------------------------------------------------------------------------------------- -void StepAlongClimbableSurface( Vector &pos, const Vector &increment, const Vector &probe ) -{ - while ( CheckForClimbableSurface( pos + increment - probe, pos + increment + probe ) ) - { - pos += increment; - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavBuildLadder( void ) -{ - if ( !IsEditMode( NORMAL ) || !m_climbableSurface ) - { - return; - } - - // We've got a ladder at m_editCursorPos, with a normal of m_surfaceNormal - Vector right, up; - VectorVectors( -m_surfaceNormal, right, up ); - - m_ladderNormal = m_surfaceNormal; - - Vector startPos = m_editCursorPos; - - Vector leftEdge = startPos; - Vector rightEdge = startPos; - - // trace to the sides to find the width - Vector probe = m_surfaceNormal * -HalfHumanWidth; - const float StepSize = 1.0f; - StepAlongClimbableSurface( leftEdge, right * -StepSize, probe ); - StepAlongClimbableSurface( rightEdge, right * StepSize, probe ); - - Vector topEdge = (leftEdge + rightEdge) * 0.5f; - Vector bottomEdge = topEdge; - StepAlongClimbableSurface( topEdge, up * StepSize, probe ); - StepAlongClimbableSurface( bottomEdge, up * -StepSize, probe ); - - Vector top = (leftEdge + rightEdge) * 0.5f; - top.z = topEdge.z; - - Vector bottom = top; - bottom.z = bottomEdge.z; - - CreateLadder( topEdge, bottomEdge, leftEdge.DistTo( rightEdge ), m_ladderNormal.AsVector2D(), 0.0f ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Flood fills all areas with current place - */ -class PlaceFloodFillFunctor -{ -public: - PlaceFloodFillFunctor( CNavArea *area ) - { - m_initialPlace = area->GetPlace(); - } - - bool operator() ( CNavArea *area ) - { - if (area->GetPlace() != m_initialPlace) - return false; - - area->SetPlace( TheNavMesh->GetNavPlace() ); - - return true; - } - -private: - unsigned int m_initialPlace; -}; - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Called when edit mode has just been enabled - */ -void CNavMesh::OnEditModeStart( void ) -{ - ClearSelectedSet(); - m_isContinuouslySelecting = false; - m_isContinuouslyDeselecting = false; -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Called when edit mode has just been disabled - */ -void CNavMesh::OnEditModeEnd( void ) -{ -} - - -//-------------------------------------------------------------------------------------------------------------- -class DrawSelectedSet -{ -public: - DrawSelectedSet( const Vector &shift ) - { - m_count = 0; - m_shift = shift; - } - - bool operator() ( CNavArea *area ) - { - if (TheNavMesh->IsInSelectedSet( area )) - { - area->DrawSelectedSet( m_shift ); - ++m_count; - } - - return (m_count < nav_draw_limit.GetInt()); - } - - int m_count; - Vector m_shift; -}; - - -//-------------------------------------------------------------------------------------------------------- -class AddToDragSet -{ -public: - AddToDragSet( Extent &area, int zMin, int zMax, bool bDragDeselecting ) - { - m_nTolerance = 1; - m_dragArea = area; - m_zMin = zMin - m_nTolerance; - m_zMax = zMax + m_nTolerance; - m_bDragDeselecting = bDragDeselecting; - } - - bool operator() ( CNavArea *area ) - { - bool bShouldBeInSelectedSet = m_bDragDeselecting; - if ( ( TheNavMesh->IsInSelectedSet( area ) == bShouldBeInSelectedSet ) && area->IsOverlapping( m_dragArea ) && area->GetCenter().z >= m_zMin && area->GetCenter().z <= m_zMax ) - { - TheNavMesh->AddToDragSelectionSet( area ); - } - return true; - } - - Extent m_dragArea; - int m_zMin; - int m_zMax; - int m_nTolerance; - bool m_bDragDeselecting; -}; - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::UpdateDragSelectionSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - Extent dragArea; - int xmin = MIN( m_anchor.x, m_editCursorPos.x ); - int xmax = MAX( m_anchor.x, m_editCursorPos.x ); - int ymin = MIN( m_anchor.y, m_editCursorPos.y ); - int ymax = MAX( m_anchor.y, m_editCursorPos.y ); - - dragArea.lo = Vector( xmin, ymin, m_anchor.z ); - dragArea.hi = Vector( xmax, ymax, m_anchor.z ); - - ClearDragSelectionSet(); - - AddToDragSet add( dragArea, m_anchor.z - m_nDragSelectionVolumeZMin, m_anchor.z + m_nDragSelectionVolumeZMax, m_bIsDragDeselecting ); - ForAllAreas( add ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Draw navigation areas and edit them - * @todo Clean the whole edit system up - its structure is legacy from peculiarities in GoldSrc. - */ -ConVar nav_show_compass( "nav_show_compass", "0", FCVAR_CHEAT ); -void CNavMesh::DrawEditMode( void ) -{ - VPROF( "CNavMesh::DrawEditMode" ); - - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( IsGenerating() ) - return; - - // TODO: remove this when host_thread_mode 1 stops breaking NDEBUG_PERSIST_TILL_NEXT_SERVER overlays - static ConVarRef host_thread_mode( "host_thread_mode" ); - host_thread_mode.SetValue( 0 ); - - const float maxRange = 1000.0f; // 500 - -#if DEBUG_NAV_NODES - if ( nav_show_nodes.GetBool() ) - { - for ( CNavNode *node = CNavNode::GetFirst(); node != NULL; node = node->GetNext() ) - { - if ( m_editCursorPos.DistToSqr( *node->GetPosition() ) < 150*150 ) - { - node->Draw(); - } - } - } -#endif // DEBUG_NAV_NODES - - Vector from, dir; - GetEditVectors( &from, &dir ); - - Vector to = from + maxRange * dir; - - /* IN_PROGRESS: - if ( !IsEditMode( PLACE_PAINTING ) && nav_snap_to_grid.GetBool() ) - { - Vector center = SnapToGrid( m_editCursorPos ); - - const int GridCount = 3; - const int GridArraySize = GridCount * 2 + 1; - const int GridSize = GetGridSize(); - - // fill in an array of heights for the grid - Vector pos[GridArraySize][GridArraySize]; - int x, y; - for ( x=0; xDrawDragSelectionSet( dragSelectionColor ); - } - } - else if ( IsEditMode( CREATING_LADDER ) ) - { - Vector corner1, corner2, corner3; - if ( FindLadderCorners( &corner1, &corner2, &corner3 ) ) - { - NavEditColor color = NavCreationColor; - if ( !m_climbableSurface ) - { - color = NavInvalidCreationColor; - } - - NavDrawLine( m_ladderAnchor, corner1, color ); - NavDrawLine( corner1, corner3, color ); - NavDrawLine( corner3, corner2, color ); - NavDrawLine( corner2, m_ladderAnchor, color ); - } - } - - if ( m_selectedLadder ) - { - m_lastSelectedArea = NULL; - - // if ladder changed, print its ID - if (m_selectedLadder != m_lastSelectedLadder || nav_show_area_info.GetBool()) - { - m_lastSelectedLadder = m_selectedLadder; - - // print ladder info - char buffer[80]; - - CBaseEntity *ladderEntity = m_selectedLadder->GetLadderEntity(); - if ( ladderEntity ) - { - V_snprintf( buffer, sizeof( buffer ), "Ladder #%d (Team %s)\n", m_selectedLadder->GetID(), GetGlobalTeam( ladderEntity->GetTeamNumber() )->GetName() ); - } - else - { - V_snprintf( buffer, sizeof( buffer ), "Ladder #%d\n", m_selectedLadder->GetID() ); - } - NDebugOverlay::ScreenText( 0.5, 0.53, buffer, 255, 255, 0, 128, NDEBUG_PERSIST_TILL_NEXT_SERVER ); - } - - // draw the ladder we are pointing at and all connected areas - m_selectedLadder->DrawLadder(); - m_selectedLadder->DrawConnectedAreas(); - } - - if ( m_markedLadder && !IsEditMode( PLACE_PAINTING ) ) - { - // draw the "marked" ladder - m_markedLadder->DrawLadder(); - } - - if ( m_markedArea && !IsEditMode( PLACE_PAINTING ) ) - { - // draw the "marked" area - m_markedArea->Draw(); - } - - // find the area the player is pointing at - if (m_selectedArea) - { - m_lastSelectedLadder = NULL; - - // if area changed, print its ID - if ( m_selectedArea != m_lastSelectedArea ) - { - m_showAreaInfoTimer.Start( nav_show_area_info.GetFloat() ); - m_lastSelectedArea = m_selectedArea; - } - - if (m_showAreaInfoTimer.HasStarted() && !m_showAreaInfoTimer.IsElapsed() ) - { - char buffer[80]; - char attrib[80]; - char locName[80]; - - if (m_selectedArea->GetPlace()) - { - const char *name = TheNavMesh->PlaceToName( m_selectedArea->GetPlace() ); - if (name) - V_strcpy_safe( locName, name ); - else - V_strcpy_safe( locName, "ERROR" ); - } - else - { - locName[0] = '\000'; - } - - if (IsEditMode( PLACE_PAINTING )) - { - attrib[0] = '\000'; - } - else - { - attrib[0] = 0; - int attributes = m_selectedArea->GetAttributes(); - if ( attributes & NAV_MESH_CROUCH ) Q_strncat( attrib, "CROUCH ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_JUMP ) Q_strncat( attrib, "JUMP ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_PRECISE ) Q_strncat( attrib, "PRECISE ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_NO_JUMP ) Q_strncat( attrib, "NO_JUMP ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_STOP ) Q_strncat( attrib, "STOP ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_RUN ) Q_strncat( attrib, "RUN ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_WALK ) Q_strncat( attrib, "WALK ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_AVOID ) Q_strncat( attrib, "AVOID ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_TRANSIENT ) Q_strncat( attrib, "TRANSIENT ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_DONT_HIDE ) Q_strncat( attrib, "DONT_HIDE ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_STAND ) Q_strncat( attrib, "STAND ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_NO_HOSTAGES )Q_strncat( attrib, "NO HOSTAGES ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_STAIRS ) Q_strncat( attrib, "STAIRS ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_OBSTACLE_TOP ) Q_strncat( attrib, "OBSTACLE ", sizeof( attrib ), -1 ); - if ( attributes & NAV_MESH_CLIFF ) Q_strncat( attrib, "CLIFF ", sizeof( attrib ), -1 ); -#ifdef TERROR - if ( attributes & TerrorNavArea::NAV_PLAYERCLIP ) Q_strncat( attrib, "PLAYERCLIP ", sizeof( attrib ), -1 ); - if ( attributes & TerrorNavArea::NAV_BREAKABLEWALL ) Q_strncat( attrib, "BREAKABLEWALL ", sizeof( attrib ), -1 ); - if ( m_selectedArea->IsBlocked( TEAM_SURVIVOR ) ) Q_strncat( attrib, "BLOCKED_SURVIVOR ", sizeof( attrib ), -1 ); - if ( m_selectedArea->IsBlocked( TEAM_ZOMBIE ) ) Q_strncat( attrib, "BLOCKED_ZOMBIE ", sizeof( attrib ), -1 ); -#else - if ( m_selectedArea->IsBlocked( TEAM_ANY ) ) Q_strncat( attrib, "BLOCKED ", sizeof( attrib ), -1 ); -#endif - if ( m_selectedArea->HasAvoidanceObstacle() ) Q_strncat( attrib, "OBSTRUCTED ", sizeof( attrib ), -1 ); - if ( m_selectedArea->IsDamaging() ) Q_strncat( attrib, "DAMAGING ", sizeof( attrib ), -1 ); - if ( m_selectedArea->IsUnderwater() ) Q_strncat( attrib, "UNDERWATER ", sizeof( attrib ), -1 ); - - int connected = 0; - connected += m_selectedArea->GetAdjacentCount( NORTH ); - connected += m_selectedArea->GetAdjacentCount( SOUTH ); - connected += m_selectedArea->GetAdjacentCount( EAST ); - connected += m_selectedArea->GetAdjacentCount( WEST ); - Q_strncat( attrib, UTIL_VarArgs( "%d Connections ", connected ), sizeof( attrib ), -1 ); - } - - Q_snprintf( buffer, sizeof( buffer ), "Area #%d %s %s\n", m_selectedArea->GetID(), locName, attrib ); - NDebugOverlay::ScreenText( 0.5, 0.53, buffer, 255, 255, 0, 128, NDEBUG_PERSIST_TILL_NEXT_SERVER ); - - // do "place painting" - if ( m_isPlacePainting ) - { - if (m_selectedArea->GetPlace() != TheNavMesh->GetNavPlace()) - { - m_selectedArea->SetPlace( TheNavMesh->GetNavPlace() ); - player->EmitSound( "Bot.EditSwitchOn" ); - } - } - } - - - // do continuous selecting into selected set - if ( m_isContinuouslySelecting ) - { - AddToSelectedSet( m_selectedArea ); - } - else if ( m_isContinuouslyDeselecting ) - { - // do continuous de-selecting into selected set - RemoveFromSelectedSet( m_selectedArea ); - } - - - if (IsEditMode( PLACE_PAINTING )) - { - m_selectedArea->DrawConnectedAreas(); - } - else // normal editing mode - { - // draw split line - Extent extent; - m_selectedArea->GetExtent( &extent ); - - float yaw = player->EyeAngles().y; - while( yaw > 360.0f ) - yaw -= 360.0f; - - while( yaw < 0.0f ) - yaw += 360.0f; - - if (m_splitAlongX) - { - from.x = extent.lo.x; - from.y = m_splitEdge; - from.z = m_selectedArea->GetZ( from ); - - to.x = extent.hi.x; - to.y = m_splitEdge; - to.z = m_selectedArea->GetZ( to ); - } - else - { - from.x = m_splitEdge; - from.y = extent.lo.y; - from.z = m_selectedArea->GetZ( from ); - - to.x = m_splitEdge; - to.y = extent.hi.y; - to.z = m_selectedArea->GetZ( to ); - } - - NavDrawLine( from, to, NavSplitLineColor ); - - // draw the area we are pointing at and all connected areas - m_selectedArea->DrawConnectedAreas(); - } - } - - // render the selected set - if (!IsSelectedSetEmpty()) - { - Vector shift( 0, 0, 0 ); - - if (IsEditMode( SHIFTING_XY )) - { - shift = m_editCursorPos - m_anchor; - shift.z = 0.0f; - } - - DrawSelectedSet draw( shift ); - - // if the selected set is small, just blast it out - if (m_selectedSet.Count() < nav_draw_limit.GetInt()) - { - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - - draw( area ); - } - } - else - { - // draw the part nearest the player - CNavArea *nearest = NULL; - float nearRange = 9999999999.9f; - - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - - float range = (player->GetAbsOrigin() - area->GetCenter()).LengthSqr(); - if (range < nearRange) - { - nearRange = range; - nearest = area; - } - } - - SearchSurroundingAreas( nearest, nearest->GetCenter(), draw, -1, INCLUDE_INCOMING_CONNECTIONS | INCLUDE_BLOCKED_AREAS ); - } - } - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::SetMarkedLadder( CNavLadder *ladder ) -{ - m_markedLadder = ladder; - m_markedArea = NULL; - m_markedCorner = NUM_CORNERS; -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::SetMarkedArea( CNavArea *area ) -{ - m_markedLadder = NULL; - m_markedArea = area; - m_markedCorner = NUM_CORNERS; -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavDelete( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - if (IsSelectedSetEmpty()) - { - // the old way - CNavArea *markedArea = GetMarkedArea(); - CNavLadder *markedLadder = GetMarkedLadder(); - FindActiveNavArea(); - - if( markedArea ) - { - player->EmitSound( "EDIT_DELETE" ); - TheNavAreas.FindAndRemove( markedArea ); - TheNavMesh->OnEditDestroyNotify( markedArea ); - TheNavMesh->DestroyArea( markedArea ); - } - else if( markedLadder ) - { - player->EmitSound( "EDIT_DELETE" ); - m_ladders.FindAndRemove( markedLadder ); - OnEditDestroyNotify( markedLadder ); - delete markedLadder; - } - else if ( m_selectedArea ) - { - player->EmitSound( "EDIT_DELETE" ); - TheNavAreas.FindAndRemove( m_selectedArea ); - CNavArea *deadArea = m_selectedArea; - OnEditDestroyNotify( deadArea ); - TheNavMesh->DestroyArea( deadArea ); - } - else if ( m_selectedLadder ) - { - player->EmitSound( "EDIT_DELETE" ); - m_ladders.FindAndRemove( m_selectedLadder ); - CNavLadder *deadLadder = m_selectedLadder; - OnEditDestroyNotify( deadLadder ); - delete deadLadder; - } - } - else - { - // delete all areas in the selected set - player->EmitSound( "EDIT_DELETE" ); - - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - - TheNavAreas.FindAndRemove( area ); - - OnEditDestroyNotify( area ); - - TheNavMesh->DestroyArea( area ); - } - - Msg( "Deleted %d areas\n", m_selectedSet.Count() ); - - ClearSelectedSet(); - } - - StripNavigationAreas(); - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -class SelectCollector -{ -public: - SelectCollector( void ) - { - m_count = 0; - } - - bool operator() ( CNavArea *area ) - { - // already selected areas terminate flood select - if (TheNavMesh->IsInSelectedSet( area )) - return false; - - TheNavMesh->AddToSelectedSet( area ); - ++m_count; - - return true; - } - - int m_count; -}; - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavDeleteMarked( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - CNavArea *markedArea = GetMarkedArea(); - if( markedArea ) - { - player->EmitSound( "EDIT_DELETE" ); - TheNavMesh->OnEditDestroyNotify( markedArea ); - TheNavAreas.FindAndRemove( markedArea ); - TheNavMesh->DestroyArea( markedArea ); - } - - CNavLadder *markedLadder = GetMarkedLadder(); - if( markedLadder ) - { - player->EmitSound( "EDIT_DELETE" ); - m_ladders.FindAndRemove( markedLadder ); - delete markedLadder; - } - - StripNavigationAreas(); - - ClearSelectedSet(); - - SetMarkedArea( NULL ); // unmark the mark area - SetMarkedLadder( NULL ); // unmark the mark ladder - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Select the current nav area and all recursively connected areas - */ -void CNavMesh::CommandNavFloodSelect( const CCommand &args ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - FindActiveNavArea(); - - CNavArea *start = m_selectedArea; - if ( !start ) - { - start = m_markedArea; - } - - if ( start ) - { - player->EmitSound( "EDIT_DELETE" ); - - int connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS; - if ( args.ArgC() == 2 && FStrEq( "out", args[1] ) ) - { - connections = INCLUDE_BLOCKED_AREAS; - } - if ( args.ArgC() == 2 && FStrEq( "in", args[1] ) ) - { - connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS | EXCLUDE_OUTGOING_CONNECTIONS; - } - - // collect all areas connected to this area - SelectCollector collector; - SearchSurroundingAreas( start, start->GetCenter(), collector, -1, connections ); - - Msg( "Selected %d areas.\n", collector.m_count ); - } - - SetMarkedArea( NULL ); // unmark the mark area -} - - -//-------------------------------------------------------------------------------------------------------------- -/** -* Toggles all areas into/out of the selected set -*/ -void CNavMesh::CommandNavToggleSelectedSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - player->EmitSound( "EDIT_DELETE" ); - - NavAreaVector notInSelectedSet; - - // Build a list of all areas not in the selected set - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[it]; - if ( !IsInSelectedSet( area ) ) - { - notInSelectedSet.AddToTail( area ); - } - } - - // Clear out the selected set - ClearSelectedSet(); - - // Add areas back into the selected set - FOR_EACH_VEC( notInSelectedSet, nit ) - { - CNavArea *area = notInSelectedSet[nit]; - AddToSelectedSet( area ); - } - - Msg( "Selected %d areas.\n", notInSelectedSet.Count() ); - - SetMarkedArea( NULL ); // unmark the mark area -} - - -//-------------------------------------------------------------------------------------------------------------- -/** -* Saves the current selected set for later retrieval. -*/ -void CNavMesh::CommandNavStoreSelectedSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - player->EmitSound( "EDIT_DELETE" ); - - m_storedSelectedSet.RemoveAll(); - FOR_EACH_VEC( m_selectedSet, it ) - { - m_storedSelectedSet.AddToTail( m_selectedSet[it]->GetID() ); - } -} - - - -//-------------------------------------------------------------------------------------------------------------- -/** -* Restores an older selected set. -*/ -void CNavMesh::CommandNavRecallSelectedSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - player->EmitSound( "EDIT_DELETE" ); - - ClearSelectedSet(); - - for ( int i=0; iEmitSound( "EDIT_MARK.Enable" ); - } -} - -//-------------------------------------------------------------------------------------------------------------- -/** -* Add area ID to selected set -*/ -void CNavMesh::CommandNavAddToSelectedSetByID( const CCommand &args ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) || args.ArgC() < 2 ) - return; - - int id = atoi( args[1] ); - CNavArea *area = GetNavAreaByID( id ); - if ( area ) - { - AddToSelectedSet( area ); - player->EmitSound( "EDIT_MARK.Enable" ); - Msg( "Added area %d. ( to go there: setpos %f %f %f )\n", id, area->GetCenter().x, area->GetCenter().y, area->GetCenter().z + 5 ); - } - else - { - Msg( "No area with id %d\n", id ); - } -} - -//-------------------------------------------------------------------------------------------------------------- -/** - * Remove current area from selected set - */ -void CNavMesh::CommandNavRemoveFromSelectedSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - RemoveFromSelectedSet( m_selectedArea ); - player->EmitSound( "EDIT_MARK.Disable" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** -* Add/remove current area from selected set -*/ -void CNavMesh::CommandNavToggleInSelectedSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (IsInSelectedSet( m_selectedArea )) - { - RemoveFromSelectedSet( m_selectedArea ); - } - else - { - AddToSelectedSet( m_selectedArea ); - } - player->EmitSound( "EDIT_MARK.Disable" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Clear the selected set to empty - */ -void CNavMesh::CommandNavClearSelectedSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - ClearSelectedSet(); - player->EmitSound( "EDIT_MARK.Disable" ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Start continuously selecting areas into the selected set - */ -void CNavMesh::CommandNavBeginSelecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - m_isContinuouslySelecting = true; - m_isContinuouslyDeselecting = false; - - player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Stop continuously selecting areas into the selected set - */ -void CNavMesh::CommandNavEndSelecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - m_isContinuouslySelecting = false; - m_isContinuouslyDeselecting = false; - - player->EmitSound( "EDIT_END_AREA.Creating" ); -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavBeginDragSelecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) && !IsEditMode( DRAG_SELECTING ) ) - return; - - FindActiveNavArea(); - - if ( IsEditMode( DRAG_SELECTING ) ) - { - ClearDragSelectionSet(); - SetEditMode( NORMAL ); - player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); - } - else - { - player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); - - SetEditMode( DRAG_SELECTING ); - - // m_anchor starting corner - m_anchor = m_editCursorPos; - m_nDragSelectionVolumeZMax = nav_drag_selection_volume_zmax_offset.GetInt(); - m_nDragSelectionVolumeZMin = nav_drag_selection_volume_zmin_offset.GetInt(); - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavEndDragSelecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( IsEditMode( DRAG_SELECTING ) ) - { - // Transfer drag selected areas to the selected set - FOR_EACH_VEC( m_dragSelectionSet, it ) - { - AddToSelectedSet( m_dragSelectionSet[it] ); - } - SetEditMode( NORMAL ); - } - else - { - player->EmitSound( "EDIT_END_AREA.NotCreating" ); - } - - ClearDragSelectionSet(); - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavBeginDragDeselecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) && !IsEditMode( DRAG_SELECTING ) ) - return; - - FindActiveNavArea(); - - if ( IsEditMode( DRAG_SELECTING ) ) - { - ClearDragSelectionSet(); - SetEditMode( NORMAL ); - player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); - } - else - { - player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); - - SetEditMode( DRAG_SELECTING ); - m_bIsDragDeselecting = true; - - // m_anchor starting corner - m_anchor = m_editCursorPos; - m_nDragSelectionVolumeZMax = nav_drag_selection_volume_zmax_offset.GetInt(); - m_nDragSelectionVolumeZMin = nav_drag_selection_volume_zmin_offset.GetInt(); - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavEndDragDeselecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( IsEditMode( DRAG_SELECTING ) ) - { - // Remove drag selected areas from the selected set - FOR_EACH_VEC( m_dragSelectionSet, it ) - { - RemoveFromSelectedSet( m_dragSelectionSet[it] ); - } - SetEditMode( NORMAL ); - } - else - { - player->EmitSound( "EDIT_END_AREA.NotCreating" ); - } - - ClearDragSelectionSet(); - m_bIsDragDeselecting = false; - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavRaiseDragVolumeMax( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - m_nDragSelectionVolumeZMax += 32; - nav_drag_selection_volume_zmax_offset.SetValue( m_nDragSelectionVolumeZMax ); -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavLowerDragVolumeMax( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - m_nDragSelectionVolumeZMax = MAX( 0, m_nDragSelectionVolumeZMax - 32 ); - nav_drag_selection_volume_zmax_offset.SetValue( m_nDragSelectionVolumeZMax ); -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavRaiseDragVolumeMin( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - m_nDragSelectionVolumeZMin = MAX( 0, m_nDragSelectionVolumeZMin - 32 ); - nav_drag_selection_volume_zmin_offset.SetValue( m_nDragSelectionVolumeZMin ); -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavLowerDragVolumeMin( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - m_nDragSelectionVolumeZMin += 32; - nav_drag_selection_volume_zmin_offset.SetValue( m_nDragSelectionVolumeZMin ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Start/stop continuously selecting areas into the selected set - */ -void CNavMesh::CommandNavToggleSelecting( bool playSound ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - m_isContinuouslySelecting = !m_isContinuouslySelecting; - m_isContinuouslyDeselecting = false; - - if ( playSound ) - { - player->EmitSound( "EDIT_END_AREA.Creating" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Start continuously de-selecting areas from the selected set - */ -void CNavMesh::CommandNavBeginDeselecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - m_isContinuouslyDeselecting = true; - m_isContinuouslySelecting = false; - - player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Stop continuously de-selecting areas from the selected set - */ -void CNavMesh::CommandNavEndDeselecting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - m_isContinuouslyDeselecting = false; - m_isContinuouslySelecting = false; - - player->EmitSound( "EDIT_END_AREA.Creating" ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Start/stop continuously de-selecting areas from the selected set - */ -void CNavMesh::CommandNavToggleDeselecting( bool playSound ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - m_isContinuouslyDeselecting = !m_isContinuouslyDeselecting; - m_isContinuouslySelecting = false; - - if ( playSound ) - { - player->EmitSound( "EDIT_END_AREA.Creating" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Selects all areas that intersect the half-space - * Arguments: "+z 100", or "-x 30", etc. - */ -void CNavMesh::CommandNavSelectHalfSpace( const CCommand &args ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - if ( args.ArgC() != 3 ) - { - Warning( "Error: <+X|-X|+Y|-Y|+Z|-Z> \n" ); - return; - } - - enum HalfSpaceType - { - PLUS_X, MINUS_X, - PLUS_Y, MINUS_Y, - PLUS_Z, MINUS_Z, - } - halfSpace = PLUS_X; - - if (FStrEq( "+x", args[1] )) - { - halfSpace = PLUS_X; - } - else if (FStrEq( "-x", args[1] )) - { - halfSpace = MINUS_X; - } - else if (FStrEq( "+y", args[1] )) - { - halfSpace = PLUS_Y; - } - else if (FStrEq( "-y", args[1] )) - { - halfSpace = MINUS_Y; - } - else if (FStrEq( "+z", args[1] )) - { - halfSpace = PLUS_Z; - } - else if (FStrEq( "-z", args[1] )) - { - halfSpace = MINUS_Z; - } - - float value = atof( args[2] ); - - Extent extent; - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[it]; - area->GetExtent( &extent ); - - switch( halfSpace ) - { - case PLUS_X: - if (extent.lo.x < value && extent.hi.x < value) - { - continue; - } - break; - - case PLUS_Y: - if (extent.lo.y < value && extent.hi.y < value) - { - continue; - } - break; - - case PLUS_Z: - if (extent.lo.z < value && extent.hi.z < value) - { - continue; - } - break; - - case MINUS_X: - if (extent.lo.x > value && extent.hi.x > value) - { - continue; - } - break; - - case MINUS_Y: - if (extent.lo.y > value && extent.hi.y > value) - { - continue; - } - break; - - case MINUS_Z: - if (extent.lo.z > value && extent.hi.z > value) - { - continue; - } - break; - } - - // toggle membership - if ( IsInSelectedSet( area ) ) - { - RemoveFromSelectedSet( area ); - } - else - { - AddToSelectedSet( area ); - } - } - - player->EmitSound( "EDIT_DELETE" ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Begin shifting selected set in the XY plane - */ -void CNavMesh::CommandNavBeginShiftXY( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if (GetEditMode() == SHIFTING_XY) - { - SetEditMode( NORMAL ); - player->EmitSound( "EDIT_END_AREA.Creating" ); - return; - } - else - { - SetEditMode( SHIFTING_XY ); - player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); - } - - // m_anchor starting corner - m_anchor = m_editCursorPos; -} - - -//-------------------------------------------------------------------------------------------------------- -/** - * Shift a set of areas, and all connected ladders - */ -class ShiftSet -{ -public: - ShiftSet( const Vector &shift ) - { - m_shift = shift; - } - - bool operator()( CNavArea *area ) - { - area->Shift( m_shift ); - - const NavLadderConnectVector *ladders = area->GetLadders( CNavLadder::LADDER_UP ); - int it; - for( it = 0; it < ladders->Count(); ++it ) - { - CNavLadder *ladder = (*ladders)[ it ].ladder; - if ( !m_ladders.HasElement( ladder ) ) - { - ladder->Shift( m_shift ); - m_ladders.AddToTail( ladder ); - } - } - - ladders = area->GetLadders( CNavLadder::LADDER_DOWN ); - for( it = 0; it < ladders->Count(); ++it ) - { - CNavLadder *ladder = (*ladders)[ it ].ladder; - if ( !m_ladders.HasElement( ladder ) ) - { - ladder->Shift( m_shift ); - m_ladders.AddToTail( ladder ); - } - } - - return true; - } - -private: - CUtlVector< CNavLadder * > m_ladders; - Vector m_shift; -}; - - -//-------------------------------------------------------------------------------------------------------------- -/** - * End shifting selected set in the XY plane - */ -void CNavMesh::CommandNavEndShiftXY( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - SetEditMode( NORMAL ); - - Vector shiftAmount = m_editCursorPos - m_anchor; - shiftAmount.z = 0.0f; - - ShiftSet shift( shiftAmount ); - - // update the position of all areas in the selected set - TheNavMesh->ForAllSelectedAreas( shift ); - - player->EmitSound( "EDIT_END_AREA.Creating" ); -} - - -//-------------------------------------------------------------------------------------------------------- -CON_COMMAND_F( nav_shift, "Shifts the selected areas by the specified amount", FCVAR_CHEAT ) -{ - if ( !UTIL_IsCommandIssuedByServerAdmin() ) - return; - - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - TheNavMesh->SetEditMode( CNavMesh::NORMAL ); - - Vector shiftAmount( vec3_origin ); - if ( args.ArgC() > 1 ) - { - shiftAmount.x = atoi( args[1] ); - - if ( args.ArgC() > 2 ) - { - shiftAmount.y = atoi( args[2] ); - - if ( args.ArgC() > 3 ) - { - shiftAmount.z = atoi( args[3] ); - } - } - } - - ShiftSet shift( shiftAmount ); - - // update the position of all areas in the selected set - TheNavMesh->ForAllSelectedAreas( shift ); - - player->EmitSound( "EDIT_END_AREA.Creating" ); -} - - -//-------------------------------------------------------------------------------------------------------- -void CommandNavCenterInWorld( void ) -{ - if ( !UTIL_IsCommandIssuedByServerAdmin() ) - return; - - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - TheNavMesh->SetEditMode( CNavMesh::NORMAL ); - - // Build the nav mesh's extent - Extent navExtent; - bool first = true; - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[it]; - if ( first ) - { - area->GetExtent( &navExtent ); - first = false; - } - else - { - navExtent.Encompass( area->GetCorner( NORTH_WEST ) ); - navExtent.Encompass( area->GetCorner( NORTH_EAST ) ); - navExtent.Encompass( area->GetCorner( SOUTH_WEST ) ); - navExtent.Encompass( area->GetCorner( SOUTH_EAST ) ); - } - } - - // Get the world's extent - CWorld *world = dynamic_cast< CWorld * >( CBaseEntity::Instance( INDEXENT( 0 ) ) ); - if ( !world ) - return; - - Extent worldExtent; - world->GetWorldBounds( worldExtent.lo, worldExtent.hi ); - - // Compute the difference, and shift in XY - Vector navCenter = ( navExtent.lo + navExtent.hi ) * 0.5f; - Vector worldCenter = ( worldExtent.lo + worldExtent.hi ) * 0.5f; - Vector shift = worldCenter - navCenter; - shift.z = 0.0f; - - // update the position of all areas - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - - area->Shift( shift ); - } - - player->EmitSound( "EDIT_END_AREA.Creating" ); - - Msg( "Shifting mesh by %f,%f\n", shift.x, shift.y ); -} -ConCommand nav_world_center( "nav_world_center", CommandNavCenterInWorld, "Centers the nav mesh in the world", FCVAR_CHEAT ); - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Add invalid areas to selected set - */ -void CNavMesh::CommandNavSelectInvalidAreas( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - ClearSelectedSet(); - - Extent areaExtent; - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - - if ( area ) - { - area->GetExtent( &areaExtent ); - for ( float x = areaExtent.lo.x; x + GenerationStepSize <= areaExtent.hi.x; x += GenerationStepSize ) - { - for ( float y = areaExtent.lo.y; y + GenerationStepSize <= areaExtent.hi.y; y += GenerationStepSize ) - { - float nw = area->GetZ( x, y ); - float ne = area->GetZ( x + GenerationStepSize, y ); - float sw = area->GetZ( x, y + GenerationStepSize ); - float se = area->GetZ( x + GenerationStepSize, y + GenerationStepSize ); - - if ( !IsHeightDifferenceValid( nw, ne, sw, se ) || - !IsHeightDifferenceValid( ne, nw, sw, se ) || - !IsHeightDifferenceValid( sw, ne, nw, se ) || - !IsHeightDifferenceValid( se, ne, sw, nw ) ) - { - AddToSelectedSet( area ); - } - } - } - } - } - - Msg( "Selected %d areas.\n", m_selectedSet.Count() ); - - if ( m_selectedSet.Count() ) - { - player->EmitSound( "EDIT_MARK.Enable" ); - } - else - { - player->EmitSound( "EDIT_MARK.Disable" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Add blocked areas to selected set - */ -void CNavMesh::CommandNavSelectBlockedAreas( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - ClearSelectedSet(); - - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - - if ( area && area->IsBlocked( TEAM_ANY ) ) - { - AddToSelectedSet( area ); - } - } - - Msg( "Selected %d areas.\n", m_selectedSet.Count() ); - - if ( m_selectedSet.Count() ) - { - player->EmitSound( "EDIT_MARK.Enable" ); - } - else - { - player->EmitSound( "EDIT_MARK.Disable" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Add obstructed areas to selected set - */ -void CNavMesh::CommandNavSelectObstructedAreas( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - ClearSelectedSet(); - - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - - if ( area && area->HasAvoidanceObstacle() ) - { - AddToSelectedSet( area ); - } - } - - Msg( "Selected %d areas.\n", m_selectedSet.Count() ); - - if ( m_selectedSet.Count() ) - { - player->EmitSound( "EDIT_MARK.Enable" ); - } - else - { - player->EmitSound( "EDIT_MARK.Disable" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Add damaging areas to selected set - */ -void CNavMesh::CommandNavSelectDamagingAreas( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - ClearSelectedSet(); - - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - - if ( area && area->IsDamaging() ) - { - AddToSelectedSet( area ); - } - } - - Msg( "Selected %d areas.\n", m_selectedSet.Count() ); - - if ( m_selectedSet.Count() ) - { - player->EmitSound( "EDIT_MARK.Enable" ); - } - else - { - player->EmitSound( "EDIT_MARK.Disable" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Adds stairs areas to the selected set - */ -void CNavMesh::CommandNavSelectStairs( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if ( player == NULL ) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - ClearSelectedSet(); - - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - - if ( area && area->HasAttributes( NAV_MESH_STAIRS ) ) - { - AddToSelectedSet( area ); - } - } - - Msg( "Selected %d areas.\n", m_selectedSet.Count() ); - - if ( m_selectedSet.Count() ) - { - player->EmitSound( "EDIT_MARK.Enable" ); - } - else - { - player->EmitSound( "EDIT_MARK.Disable" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -// Adds areas not connected to mesh to the selected set -void CNavMesh::CommandNavSelectOrphans( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) - return; - - FindActiveNavArea(); - - CNavArea *start = m_selectedArea; - if ( !start ) - { - start = m_markedArea; - } - - if ( start ) - { - player->EmitSound( "EDIT_DELETE" ); - - int connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS; - - // collect all areas connected to this area - SelectCollector collector; - SearchSurroundingAreas( start, start->GetCenter(), collector, -1, connections ); - - // toggle the selected set to reveal the orphans - CommandNavToggleSelectedSet(); - } - - SetMarkedArea( NULL ); // unmark the mark area - -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavSplit( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (m_selectedArea->SplitEdit( m_splitAlongX, m_splitEdge )) - player->EmitSound( "EDIT_SPLIT.MarkedArea" ); - else - player->EmitSound( "EDIT_SPLIT.NoMarkedArea" ); - } - - StripNavigationAreas(); - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -bool MakeSniperSpots( CNavArea *area ) -{ - if ( !area ) - return false; - - bool splitAlongX; - float splitEdge; - - const float minSplitSize = 2.0f; // ensure the first split is larger than this - - float sizeX = area->GetSizeX(); - float sizeY = area->GetSizeY(); - - if ( sizeX > GenerationStepSize && sizeX > sizeY ) - { - splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).x, GenerationStepSize ); - if ( splitEdge < area->GetCorner( NORTH_WEST ).x + minSplitSize ) - splitEdge += GenerationStepSize; - splitAlongX = false; - } - else if ( sizeY > GenerationStepSize && sizeY > sizeX ) - { - splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).y, GenerationStepSize ); - if ( splitEdge < area->GetCorner( NORTH_WEST ).y + minSplitSize ) - splitEdge += GenerationStepSize; - splitAlongX = true; - } - else - { - return false; - } - - CNavArea *first, *second; - if ( !area->SplitEdit( splitAlongX, splitEdge, &first, &second ) ) - { - return false; - } - - first->Disconnect( second ); - second->Disconnect( first ); - - MakeSniperSpots( first ); - MakeSniperSpots( second ); - - return true; -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavMakeSniperSpots( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - // recursively split the area - if ( MakeSniperSpots( m_selectedArea ) ) - { - player->EmitSound( "EDIT_SPLIT.MarkedArea" ); - } - else - { - player->EmitSound( "EDIT_SPLIT.NoMarkedArea" ); - } - } - else - { - player->EmitSound( "EDIT_SPLIT.NoMarkedArea" ); - } - - StripNavigationAreas(); - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavMerge( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - CNavArea *other = m_markedArea; - if ( !m_markedArea && m_selectedSet.Count() == 1 ) - { - other = m_selectedSet[0]; - } - - if ( other && other != m_selectedArea ) - { - if ( m_selectedArea->MergeEdit( other ) ) - player->EmitSound( "EDIT_MERGE.Enable" ); - else - player->EmitSound( "EDIT_MERGE.Disable" ); - } - else - { - Msg( "To merge, mark an area, highlight a second area, then invoke the merge command" ); - player->EmitSound( "EDIT_MERGE.Disable" ); - } - } - - StripNavigationAreas(); - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection - ClearSelectedSet(); -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavMark( const CCommand &args ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - if (!IsSelectedSetEmpty()) - { - // add or remove areas from the selected set - if (IsInSelectedSet( m_selectedArea )) - { - // remove from set - player->EmitSound( "EDIT_MARK.Disable" ); - RemoveFromSelectedSet( m_selectedArea ); - } - else - { - // add to set - player->EmitSound( "EDIT_MARK.Enable" ); - AddToSelectedSet( m_selectedArea ); - } - return; - } - - FindActiveNavArea(); - - if ( m_markedArea || m_markedLadder ) - { - // Unmark area or ladder - player->EmitSound( "EDIT_MARK.Enable" ); - Msg("Area unmarked.\n"); - SetMarkedArea( NULL ); - } - else if ( args.ArgC() > 1 ) - { - if ( FStrEq( args[1], "ladder" ) ) - { - if ( args.ArgC() > 2 ) - { - const char *ladderIDNameToMark = args[2]; - if ( ladderIDNameToMark ) - { - unsigned int ladderIDToMark = atoi( ladderIDNameToMark ); - if ( ladderIDToMark != 0 ) - { - CNavLadder *ladder = TheNavMesh->GetLadderByID( ladderIDToMark ); - if ( ladder ) - { - player->EmitSound( "EDIT_MARK.Disable" ); - SetMarkedLadder( ladder ); - - int connected = 0; - connected += m_markedLadder->m_topForwardArea != NULL; - connected += m_markedLadder->m_topLeftArea != NULL; - connected += m_markedLadder->m_topRightArea != NULL; - connected += m_markedLadder->m_topBehindArea != NULL; - connected += m_markedLadder->m_bottomArea != NULL; - - Msg( "Marked Ladder is connected to %d Areas\n", connected ); - } - } - } - } - } - else - { - const char *areaIDNameToMark = args[1]; - if( areaIDNameToMark != NULL ) - { - unsigned int areaIDToMark = atoi(areaIDNameToMark); - if( areaIDToMark != 0 ) - { - CNavArea *areaToMark = NULL; - FOR_EACH_VEC( TheNavAreas, nit ) - { - if( TheNavAreas[nit]->GetID() == areaIDToMark ) - { - areaToMark = TheNavAreas[nit]; - break; - } - } - if( areaToMark ) - { - player->EmitSound( "EDIT_MARK.Disable" ); - SetMarkedArea( areaToMark ); - - int connected = 0; - connected += GetMarkedArea()->GetAdjacentCount( NORTH ); - connected += GetMarkedArea()->GetAdjacentCount( SOUTH ); - connected += GetMarkedArea()->GetAdjacentCount( EAST ); - connected += GetMarkedArea()->GetAdjacentCount( WEST ); - - Msg( "Marked Area is connected to %d other Areas\n", connected ); - } - } - } - } - } - else if ( m_selectedArea ) - { - // Mark an area - player->EmitSound( "EDIT_MARK.Disable" ); - SetMarkedArea( m_selectedArea ); - - int connected = 0; - connected += GetMarkedArea()->GetAdjacentCount( NORTH ); - connected += GetMarkedArea()->GetAdjacentCount( SOUTH ); - connected += GetMarkedArea()->GetAdjacentCount( EAST ); - connected += GetMarkedArea()->GetAdjacentCount( WEST ); - - Msg( "Marked Area is connected to %d other Areas\n", connected ); - } - else if ( m_selectedLadder ) - { - // Mark a ladder - player->EmitSound( "EDIT_MARK.Disable" ); - SetMarkedLadder( m_selectedLadder ); - - int connected = 0; - connected += m_markedLadder->m_topForwardArea != NULL; - connected += m_markedLadder->m_topLeftArea != NULL; - connected += m_markedLadder->m_topRightArea != NULL; - connected += m_markedLadder->m_topBehindArea != NULL; - connected += m_markedLadder->m_bottomArea != NULL; - - Msg( "Marked Ladder is connected to %d Areas\n", connected ); - } - - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavUnmark( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - player->EmitSound( "EDIT_MARK.Enable" ); - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavBeginArea( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !(IsEditMode( CREATING_AREA ) || IsEditMode( CREATING_LADDER ) || IsEditMode( NORMAL )) ) - { - player->EmitSound( "EDIT_END_AREA.NotCreating" ); - return; - } - - FindActiveNavArea(); - - if ( IsEditMode( CREATING_AREA ) ) - { - SetEditMode( NORMAL ); - player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); - } - else if ( IsEditMode( CREATING_LADDER ) ) - { - SetEditMode( NORMAL ); - player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); - } - else if ( m_climbableSurface ) - { - player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); - - SetEditMode( CREATING_LADDER ); - - // m_ladderAnchor starting corner - m_ladderAnchor = m_editCursorPos; - m_ladderNormal = m_surfaceNormal; - } - else - { - player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); - - SetEditMode( CREATING_AREA ); - - // m_anchor starting corner - m_anchor = m_editCursorPos; - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavEndArea( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !(IsEditMode( CREATING_AREA ) || IsEditMode( CREATING_LADDER ) || IsEditMode( NORMAL )) ) - { - player->EmitSound( "EDIT_END_AREA.NotCreating" ); - return; - } - - if ( IsEditMode( CREATING_AREA ) ) - { - SetEditMode( NORMAL ); - - // create the new nav area - Vector endPos = m_editCursorPos; - endPos.z = m_anchor.z; - - // We're a manually-created area, so let's look around to see what's nearby - CNavArea *nearby = GetMarkedArea(); - if ( !nearby ) - { - nearby = TheNavMesh->GetNearestNavArea( m_editCursorPos + Vector( 0, 0, HalfHumanHeight ), false, 10000.0f, true ); - } - if ( !nearby ) - { - nearby = TheNavMesh->GetNearestNavArea( endPos + Vector( 0, 0, HalfHumanHeight ), false, 10000.0f, true ); - } - if ( !nearby ) - { - nearby = TheNavMesh->GetNearestNavArea( m_editCursorPos ); - } - if ( !nearby ) - { - nearby = TheNavMesh->GetNearestNavArea( endPos ); - } - - CNavArea *newArea = CreateArea(); - if (newArea == NULL) - { - Warning( "NavEndArea: Out of memory\n" ); - player->EmitSound( "EDIT_END_AREA.NotCreating" ); - return; - } - - newArea->Build( m_anchor, endPos ); - - if ( nearby ) - { - newArea->InheritAttributes( nearby ); // inherit from the nearby area - } - - TheNavAreas.AddToTail( newArea ); - TheNavMesh->AddNavArea( newArea ); - player->EmitSound( "EDIT_END_AREA.Creating" ); - - if ( nav_create_place_on_ground.GetBool() ) - { - newArea->PlaceOnGround( NUM_CORNERS ); - } - - // if we have a marked area, inter-connect the two - if (GetMarkedArea()) - { - Extent extent; - GetMarkedArea()->GetExtent( &extent ); - - if (m_anchor.x > extent.hi.x && m_editCursorPos.x > extent.hi.x) - { - GetMarkedArea()->ConnectTo( newArea, EAST ); - newArea->ConnectTo( GetMarkedArea(), WEST ); - } - else if (m_anchor.x < extent.lo.x && m_editCursorPos.x < extent.lo.x) - { - GetMarkedArea()->ConnectTo( newArea, WEST ); - newArea->ConnectTo( GetMarkedArea(), EAST ); - } - else if (m_anchor.y > extent.hi.y && m_editCursorPos.y > extent.hi.y) - { - GetMarkedArea()->ConnectTo( newArea, SOUTH ); - newArea->ConnectTo( GetMarkedArea(), NORTH ); - } - else if (m_anchor.y < extent.lo.y && m_editCursorPos.y < extent.lo.y) - { - GetMarkedArea()->ConnectTo( newArea, NORTH ); - newArea->ConnectTo( GetMarkedArea(), SOUTH ); - } - - // propogate marked area to new area - SetMarkedArea( newArea ); - } - - TheNavMesh->OnEditCreateNotify( newArea ); - } - else if ( IsEditMode( CREATING_LADDER ) ) - { - SetEditMode( NORMAL ); - - player->EmitSound( "EDIT_END_AREA.Creating" ); - - Vector corner1, corner2, corner3; - if ( m_climbableSurface && FindLadderCorners( &corner1, &corner2, &corner3 ) ) - { - // m_ladderAnchor and corner2 are at the same Z, and corner1 & corner3 share Z. - Vector top = (m_ladderAnchor + corner2) * 0.5f; - Vector bottom = (corner1 + corner3) * 0.5f; - if ( top.z < bottom.z ) - { - Vector tmp = top; - top = bottom; - bottom = tmp; - } - - float width = m_ladderAnchor.DistTo( corner2 ); - Vector2D ladderDir = m_surfaceNormal.AsVector2D(); - - CreateLadder( top, bottom, width, ladderDir, HumanHeight ); - } - else - { - player->EmitSound( "EDIT_END_AREA.NotCreating" ); - } - } - else - { - player->EmitSound( "EDIT_END_AREA.NotCreating" ); - } - - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavConnect( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - Vector center; - float halfWidth; - if ( m_selectedSet.Count() > 1 ) - { - bool bValid = true; - for ( int i = 1; i < m_selectedSet.Count(); ++i ) - { - // Make sure all connections are valid - CNavArea *first = m_selectedSet[0]; - CNavArea *second = m_selectedSet[i]; - - NavDirType dir = second->ComputeLargestPortal( first, ¢er, &halfWidth ); - if (dir == NUM_DIRECTIONS) - { - player->EmitSound( "EDIT_CONNECT.AllDirections" ); - bValid = false; - break; - } - - dir = first->ComputeLargestPortal( second, ¢er, &halfWidth ); - if (dir == NUM_DIRECTIONS) - { - player->EmitSound( "EDIT_CONNECT.AllDirections" ); - bValid = false; - break; - } - } - - if ( bValid ) - { - for ( int i = 1; i < m_selectedSet.Count(); ++i ) - { - CNavArea *first = m_selectedSet[0]; - CNavArea *second = m_selectedSet[i]; - - NavDirType dir = second->ComputeLargestPortal( first, ¢er, &halfWidth ); - second->ConnectTo( first, dir ); - - dir = first->ComputeLargestPortal( second, ¢er, &halfWidth ); - first->ConnectTo( second, dir ); - player->EmitSound( "EDIT_CONNECT.Added" ); - } - } - } - else if ( m_selectedArea ) - { - if ( m_markedLadder ) - { - m_markedLadder->ConnectTo( m_selectedArea ); - player->EmitSound( "EDIT_CONNECT.Added" ); - } - else if ( m_markedArea ) - { - NavDirType dir = GetMarkedArea()->ComputeLargestPortal( m_selectedArea, ¢er, &halfWidth ); - if (dir == NUM_DIRECTIONS) - { - player->EmitSound( "EDIT_CONNECT.AllDirections" ); - } - else - { - m_markedArea->ConnectTo( m_selectedArea, dir ); - player->EmitSound( "EDIT_CONNECT.Added" ); - } - } - else - { - if ( m_selectedSet.Count() == 1 ) - { - CNavArea *area = m_selectedSet[0]; - NavDirType dir = area->ComputeLargestPortal( m_selectedArea, ¢er, &halfWidth ); - if (dir == NUM_DIRECTIONS) - { - player->EmitSound( "EDIT_CONNECT.AllDirections" ); - } - else - { - area->ConnectTo( m_selectedArea, dir ); - player->EmitSound( "EDIT_CONNECT.Added" ); - } - } - else - { - 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." ); - player->EmitSound( "EDIT_CONNECT.AllDirections" ); - } - } - } - else if ( m_selectedLadder ) - { - if ( m_markedArea ) - { - m_markedArea->ConnectTo( m_selectedLadder ); - player->EmitSound( "EDIT_CONNECT.Added" ); - } - else - { - 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." ); - player->EmitSound( "EDIT_CONNECT.AllDirections" ); - } - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection - ClearSelectedSet(); -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavDisconnect( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedSet.Count() > 1 ) - { - bool bValid = true; - for ( int i = 1; i < m_selectedSet.Count(); ++i ) - { - // 2 areas are selected, so connect them bi-directionally - CNavArea *first = m_selectedSet[0]; - CNavArea *second = m_selectedSet[i]; - if ( !first->IsConnected( second, NUM_DIRECTIONS ) && !second->IsConnected( first, NUM_DIRECTIONS ) ) - { - player->EmitSound( "EDIT_CONNECT.AllDirections" ); - bValid = false; - break; - } - } - - if ( bValid ) - { - for ( int i = 1; i < m_selectedSet.Count(); ++i ) - { - // 2 areas are selected, so connect them bi-directionally - CNavArea *first = m_selectedSet[0]; - CNavArea *second = m_selectedSet[i]; - first->Disconnect( second ); - second->Disconnect( first ); - } - player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); - } - } - else if ( m_selectedArea ) - { - if ( m_markedArea ) - { - m_markedArea->Disconnect( m_selectedArea ); - m_selectedArea->Disconnect( m_markedArea ); - player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); - } - else if ( m_selectedSet.Count() == 1 ) - { - m_selectedSet[0]->Disconnect( m_selectedArea ); - m_selectedArea->Disconnect( m_selectedSet[0] ); - player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); - } - else - { - if ( m_markedLadder ) - { - m_markedLadder->Disconnect( m_selectedArea ); - m_selectedArea->Disconnect( m_markedLadder ); - player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); - } - else - { - 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." ); - player->EmitSound( "EDIT_DISCONNECT.NoMarkedArea" ); - } - } - } - else if ( m_selectedLadder ) - { - if ( m_markedArea ) - { - m_markedArea->Disconnect( m_selectedLadder ); - m_selectedLadder->Disconnect( m_markedArea ); - player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); - } - if ( m_selectedSet.Count() == 1 ) - { - m_selectedSet[0]->Disconnect( m_selectedLadder ); - m_selectedLadder->Disconnect( m_selectedSet[0] ); - player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); - } - else - { - 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." ); - player->EmitSound( "EDIT_DISCONNECT.NoMarkedArea" ); - } - } - - ClearSelectedSet(); - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -// Disconnect all outgoing one-way connects from each area in the selected set -void CNavMesh::CommandNavDisconnectOutgoingOneWays( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if ( !player ) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - if ( m_selectedSet.Count() == 0 ) - { - FindActiveNavArea(); - - if ( !m_selectedArea ) - { - return; - } - - m_selectedSet.AddToTail( m_selectedArea ); - } - - for ( int i = 0; i < m_selectedSet.Count(); ++i ) - { - CNavArea *area = m_selectedSet[i]; - - CUtlVector< CNavArea * > adjVector; - area->CollectAdjacentAreas( &adjVector ); - - for( int j=0; jIsConnected( area, NUM_DIRECTIONS ) ) - { - // no connect back - this is a one-way connection - area->Disconnect( adj ); - } - } - } - player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); - - ClearSelectedSet(); - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavSplice( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (GetMarkedArea()) - { - if (m_selectedArea->SpliceEdit( GetMarkedArea() )) - player->EmitSound( "EDIT_SPLICE.MarkedArea" ); - else - player->EmitSound( "EDIT_SPLICE.NoMarkedArea" ); - } - else - { - Msg( "To splice, mark an area, highlight a second area, then invoke the splice command to create an area between them" ); - player->EmitSound( "EDIT_SPLICE.NoMarkedArea" ); - } - } - - SetMarkedArea( NULL ); // unmark the mark area - ClearSelectedSet(); - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Toggle an attribute on given area - */ -void CNavMesh::DoToggleAttribute( CNavArea *area, NavAttributeType attribute ) -{ - area->SetAttributes( area->GetAttributes() ^ attribute ); - - // keep a list of all "transient" nav areas - if ( attribute == NAV_MESH_TRANSIENT ) - { - if ( area->GetAttributes() & NAV_MESH_TRANSIENT ) - { - m_transientAreas.AddToTail( area ); - } - else - { - m_transientAreas.FindAndRemove( area ); - } - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavToggleAttribute( NavAttributeType attribute ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - if ( IsSelectedSetEmpty() ) - { - // the old way - FindActiveNavArea(); - - if ( m_selectedArea ) - { - player->EmitSound( "EDIT.ToggleAttribute" ); - DoToggleAttribute( m_selectedArea, attribute ); - } - } - else - { - // toggle the attribute in all areas in the selected set - player->EmitSound( "EDIT.ToggleAttribute" ); - - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - - DoToggleAttribute( area, attribute ); - } - - Msg( "Changed attribute in %d areas\n", m_selectedSet.Count() ); - - ClearSelectedSet(); - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavTogglePlaceMode( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( IsEditMode( PLACE_PAINTING ) ) - { - SetEditMode( NORMAL ); - } - else - { - SetEditMode( PLACE_PAINTING ); - } - - player->EmitSound( "EDIT_TOGGLE_PLACE_MODE" ); - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavPlaceFloodFill( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( PLACE_PAINTING ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - PlaceFloodFillFunctor pff( m_selectedArea ); - SearchSurroundingAreas( m_selectedArea, m_selectedArea->GetCenter(), pff ); - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavPlaceSet( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( PLACE_PAINTING ) ) - return; - - if ( !IsSelectedSetEmpty() ) - { - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - area->SetPlace( TheNavMesh->GetNavPlace() ); - } - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavPlacePick( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( PLACE_PAINTING ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - player->EmitSound( "EDIT_PLACE_PICK" ); - TheNavMesh->SetNavPlace( m_selectedArea->GetPlace() ); - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavTogglePlacePainting( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( PLACE_PAINTING ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (m_isPlacePainting) - { - m_isPlacePainting = false; - player->EmitSound( "Bot.EditSwitchOff" ); - } - else - { - m_isPlacePainting = true; - - player->EmitSound( "Bot.EditSwitchOn" ); - - // paint the initial area - m_selectedArea->SetPlace( TheNavMesh->GetNavPlace() ); - } - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavMarkUnnamed( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (GetMarkedArea()) - { - player->EmitSound( "EDIT_MARK_UNNAMED.Enable" ); - SetMarkedArea( NULL ); - } - else - { - SetMarkedArea( NULL ); - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - - if ( area->GetPlace() == 0 ) - { - SetMarkedArea( area ); - break; - } - } - if ( !GetMarkedArea() ) - { - player->EmitSound( "EDIT_MARK_UNNAMED.NoMarkedArea" ); - } - else - { - player->EmitSound( "EDIT_MARK_UNNAMED.MarkedArea" ); - - int connected = 0; - connected += GetMarkedArea()->GetAdjacentCount( NORTH ); - connected += GetMarkedArea()->GetAdjacentCount( SOUTH ); - connected += GetMarkedArea()->GetAdjacentCount( EAST ); - connected += GetMarkedArea()->GetAdjacentCount( WEST ); - - int totalUnnamedAreas = 0; - FOR_EACH_VEC( TheNavAreas, it ) - { - CNavArea *area = TheNavAreas[ it ]; - if ( area->GetPlace() == 0 ) - { - ++totalUnnamedAreas; - } - } - - Msg( "Marked Area is connected to %d other Areas - there are %d total unnamed areas\n", connected, totalUnnamedAreas ); - } - } - } - - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavCornerSelect( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (GetMarkedArea()) - { - int corner = (m_markedCorner + 1) % (NUM_CORNERS + 1); - m_markedCorner = (NavCornerType)corner; - player->EmitSound( "EDIT_SELECT_CORNER.MarkedArea" ); - } - else - { - player->EmitSound( "EDIT_SELECT_CORNER.NoMarkedArea" ); - } - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavCornerRaise( const CCommand &args ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - int amount = 1; - if ( args.ArgC() > 1 ) - { - amount = atoi( args[1] ); - } - - if (IsSelectedSetEmpty()) - { - // the old way - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (GetMarkedArea()) - { - GetMarkedArea()->RaiseCorner( m_markedCorner, amount ); - player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); - } - else - { - player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" ); - } - } - } - else - { - // raise all areas in the selected set - player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); - - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - - area->RaiseCorner( NUM_CORNERS, amount, false ); - } - - Msg( "Raised %d areas\n", m_selectedSet.Count() ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavCornerLower( const CCommand &args ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - int amount = -1; - if ( args.ArgC() > 1 ) - { - amount = -atoi( args[1] ); - } - - if (IsSelectedSetEmpty()) - { - // the old way - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if (GetMarkedArea()) - { - GetMarkedArea()->RaiseCorner( m_markedCorner, amount ); - player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); - } - else - { - player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" ); - } - } - } - else - { - // raise all areas in the selected set - player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); - - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - - area->RaiseCorner( NUM_CORNERS, amount, false ); - } - - Msg( "Lowered %d areas\n", m_selectedSet.Count() ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavCornerPlaceOnGround( const CCommand &args ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - float inset = 0.0f; - if ( args.ArgC() == 2 ) - { - inset = atof(args[1]); - } - - if (IsSelectedSetEmpty()) - { - // the old way - FindActiveNavArea(); - - if ( m_selectedArea ) - { - if ( m_markedArea ) - { - m_markedArea->PlaceOnGround( m_markedCorner, inset ); - } - else - { - m_selectedArea->PlaceOnGround( NUM_CORNERS, inset ); - } - player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); - } - else - { - player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" ); - } - } - else - { - // snap all areas in the selected set to the ground - player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); - - FOR_EACH_VEC( m_selectedSet, it ) - { - CNavArea *area = m_selectedSet[ it ]; - - area->PlaceOnGround( NUM_CORNERS, inset ); - } - - Msg( "Placed %d areas on the ground\n", m_selectedSet.Count() ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavWarpToMark( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - CNavArea *targetArea = GetMarkedArea(); - if ( !targetArea && !IsSelectedSetEmpty() ) - { - targetArea = m_selectedSet[0]; - } - - if ( targetArea ) - { - Vector origin = targetArea->GetCenter() + Vector( 0, 0, 0.75f * HumanHeight ); - QAngle angles = player->GetAbsAngles(); - - if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING ) - { - UTIL_SetOrigin( player, origin ); - player->EmitSound( "EDIT_WARP_TO_MARK" ); - } - else - { - player->Teleport( &origin, &angles, &vec3_origin ); - player->EmitSound( "EDIT_WARP_TO_MARK" ); - } - } - else if ( GetMarkedLadder() ) - { - CNavLadder *ladder = GetMarkedLadder(); - - QAngle angles = player->GetAbsAngles(); - Vector origin = (ladder->m_top + ladder->m_bottom)/2; - origin.x += ladder->GetNormal().x * GenerationStepSize; - origin.y += ladder->GetNormal().y * GenerationStepSize; - - if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING ) - { - UTIL_SetOrigin( player, origin ); - player->EmitSound( "EDIT_WARP_TO_MARK" ); - } - else - { - player->Teleport( &origin, &angles, &vec3_origin ); - player->EmitSound( "EDIT_WARP_TO_MARK" ); - } - } - else - { - player->EmitSound( "EDIT_WARP_TO_MARK" ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -void CNavMesh::CommandNavLadderFlip( void ) -{ - CBasePlayer *player = UTIL_GetListenServerHost(); - if (player == NULL) - return; - - if ( !IsEditMode( NORMAL ) ) - return; - - FindActiveNavArea(); - - if ( m_selectedLadder ) - { - CNavArea *area; - - // flip direction - player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); - m_selectedLadder->SetDir( OppositeDirection( m_selectedLadder->GetDir() ) ); - - // and reverse ladder's area pointers - area = m_selectedLadder->m_topBehindArea; - m_selectedLadder->m_topBehindArea = m_selectedLadder->m_topForwardArea; - m_selectedLadder->m_topForwardArea = area; - - area = m_selectedLadder->m_topRightArea; - m_selectedLadder->m_topRightArea = m_selectedLadder->m_topLeftArea; - m_selectedLadder->m_topLeftArea = area; - } - - SetMarkedArea( NULL ); // unmark the mark area - m_markedCorner = NUM_CORNERS; // clear the corner selection -} - - -//-------------------------------------------------------------------------------------------------------------- -class RadiusSelect -{ - Vector m_origin; - float m_radiusSquared; - int m_selected; - -public: - RadiusSelect( const Vector &origin, float radius ) - { - m_origin = origin; - m_radiusSquared = radius * radius; - m_selected = 0; - } - - bool operator()( CNavArea *area ) - { - if ( TheNavMesh->IsInSelectedSet( area ) ) - return true; - - Vector close; - area->GetClosestPointOnArea( m_origin, &close ); - if ( close.DistToSqr( m_origin ) < m_radiusSquared ) - { - TheNavMesh->AddToSelectedSet( area ); - ++m_selected; - } - - return true; - } - - int GetNumSelected( void ) const - { - return m_selected; - } -}; - - -//-------------------------------------------------------------------------------------------------------------- -CON_COMMAND_F( nav_select_radius, "Adds all areas in a radius to the selection set", FCVAR_CHEAT ) -{ - if ( !UTIL_IsCommandIssuedByServerAdmin() || engine->IsDedicatedServer() ) - return; - - if ( args.ArgC() < 2 ) - { - Msg( "Needs a radius\n" ); - return; - } - - float radius = atof( args[ 1 ] ); - CBasePlayer *host = UTIL_GetListenServerHost(); - if ( !host ) - return; - - RadiusSelect select( host->GetAbsOrigin(), radius ); - TheNavMesh->ForAllAreas( select ); - - Msg( "%d areas added to selection\n", select.GetNumSelected() ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Add area to the currently selected set - */ -void CNavMesh::AddToSelectedSet( CNavArea *area ) -{ - if ( !area ) - return; - - // make sure area is not already in list - if (m_selectedSet.Find( area ) != m_selectedSet.InvalidIndex()) - return; - - m_selectedSet.AddToTail( area ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Remove area from the currently selected set - */ -void CNavMesh::RemoveFromSelectedSet( CNavArea *area ) -{ - m_selectedSet.FindAndRemove( area ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Add area to the drag selection set - */ -void CNavMesh::AddToDragSelectionSet( CNavArea *area ) -{ - if ( !area ) - return; - - // make sure area is not already in list - if (m_dragSelectionSet.Find( area ) != m_dragSelectionSet.InvalidIndex()) - return; - - m_dragSelectionSet.AddToTail( area ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Remove area from the drag selection set - */ -void CNavMesh::RemoveFromDragSelectionSet( CNavArea *area ) -{ - m_dragSelectionSet.FindAndRemove( area ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Clear the currently selected set to empty - */ -void CNavMesh::ClearDragSelectionSet( void ) -{ - m_dragSelectionSet.RemoveAll(); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Clear the currently selected set to empty - */ -void CNavMesh::ClearSelectedSet( void ) -{ - m_selectedSet.RemoveAll(); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Return true if the selected set is empty - */ -bool CNavMesh::IsSelectedSetEmpty( void ) const -{ - return (m_selectedSet.Count() == 0); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Return size of the selected set - */ -int CNavMesh::GetSelecteSetSize( void ) const -{ - return m_selectedSet.Count(); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Return the selected set - */ -const NavAreaVector &CNavMesh::GetSelectedSet( void ) const -{ - return m_selectedSet; -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Return true if the given area is in the selected set - */ -bool CNavMesh::IsInSelectedSet( const CNavArea *area ) const -{ - FOR_EACH_VEC( m_selectedSet, it ) - { - const CNavArea *setArea = m_selectedSet[ it ]; - - if (setArea == area) - return true; - } - - return false; -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Invoked when given area has just been added to the mesh in edit mode - */ -void CNavMesh::OnEditCreateNotify( CNavArea *newArea ) -{ - FOR_EACH_VEC( TheNavAreas, it ) - { - TheNavAreas[ it ]->OnEditCreateNotify( newArea ); - } -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Invoked when given area has just been deleted from the mesh in edit mode - */ -void CNavMesh::OnEditDestroyNotify( CNavArea *deadArea ) -{ - // clean up any edit hooks - m_markedArea = NULL; - m_selectedArea = NULL; - m_lastSelectedArea = NULL; - m_selectedLadder = NULL; - m_lastSelectedLadder = NULL; - m_markedLadder = NULL; - - m_avoidanceObstacleAreas.FindAndRemove( deadArea ); - m_blockedAreas.FindAndRemove( deadArea ); - - FOR_EACH_VEC( TheNavAreas, it ) - { - TheNavAreas[ it ]->OnEditDestroyNotify( deadArea ); - } - - EditDestroyNotification notification( deadArea ); - ForEachActor( notification ); -} - - -//-------------------------------------------------------------------------------------------------------------- -/** - * Invoked when given ladder has just been deleted from the mesh in edit mode - * @TODO: Implement me - */ -void CNavMesh::OnEditDestroyNotify( CNavLadder *deadLadder ) -{ -} - - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// nav_edit.cpp +// Implementation of Navigation Mesh edit mode +// Author: Michael Booth, 2003-2004 + +#include "cbase.h" +#include "nav_mesh.h" +#include "nav_pathfind.h" +#include "nav_node.h" +#include "nav_colors.h" +#include "Color.h" +#include "tier0/vprof.h" +#include "collisionutils.h" +#include "world.h" +#include "functorutils.h" +#include "team.h" +#ifdef TERROR +#include "TerrorShared.h" +#endif + +#ifdef DOTA_SERVER_DLL +#include "dota_npc_base.h" +#include "dota_player.h" +#endif + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +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" ); +ConVar nav_snap_to_grid( "nav_snap_to_grid", "0", FCVAR_CHEAT, "Snap to the nav generation grid when creating new nav areas" ); +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." ); +#ifdef DEBUG +ConVar nav_draw_limit( "nav_draw_limit", "50", FCVAR_CHEAT, "The maximum number of areas to draw in edit mode" ); +#else +ConVar nav_draw_limit( "nav_draw_limit", "500", FCVAR_CHEAT, "The maximum number of areas to draw in edit mode" ); +#endif +ConVar nav_solid_props( "nav_solid_props", "0", FCVAR_CHEAT, "Make props solid to nav generation/editing" ); +ConVar nav_create_area_at_feet( "nav_create_area_at_feet", "0", FCVAR_CHEAT, "Anchor nav_begin_area Z to editing player's feet" ); + +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" ); +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" ); + +extern void GetNavUIEditVectors( Vector *pos, Vector *forward ); + +Color s_dragSelectionSetAddColor( 100, 255, 100, 96 ); +Color s_dragSelectionSetDeleteColor( 255, 100, 100, 96 ); + +#if DEBUG_NAV_NODES +extern ConVar nav_show_nodes; +#endif // DEBUG_NAV_NODES + + +//-------------------------------------------------------------------------------------------------------------- +int GetGridSize( bool forceGrid = false ) +{ + if ( TheNavMesh->IsGenerating() ) + { + return (int)GenerationStepSize; + } + + int snapVal = nav_snap_to_grid.GetInt(); + if ( forceGrid && !snapVal ) + { + snapVal = 1; + } + + if ( snapVal == 0 ) + { + return 0; + } + + int scale = (int)GenerationStepSize; + switch ( snapVal ) + { + case 3: + scale = 1; + break; + case 2: + scale = 5; + break; + case 1: + default: + break; + } + + return scale; +} + + +//-------------------------------------------------------------------------------------------------------------- +Vector CNavMesh::SnapToGrid( const Vector& in, bool snapX, bool snapY, bool forceGrid ) const +{ + int scale = GetGridSize( forceGrid ); + if ( !scale ) + { + return in; + } + + Vector out( in ); + + if ( snapX ) + { + out.x = RoundToUnits( in.x, scale ); + } + + if ( snapY ) + { + out.y = RoundToUnits( in.y, scale ); + } + + return out; +} + + +//-------------------------------------------------------------------------------------------------------------- +float CNavMesh::SnapToGrid( float x, bool forceGrid ) const +{ + int scale = GetGridSize( forceGrid ); + if ( !scale ) + { + return x; + } + + x = RoundToUnits( x, scale ); + return x; +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::GetEditVectors( Vector *pos, Vector *forward ) +{ + if ( !pos || !forward ) + { + return; + } + + CBasePlayer *player = UTIL_GetListenServerHost(); + if ( !player ) + { + return; + } + + //DOTA places the edit cursor where the 2D cursor is located. +#ifdef DOTA_SERVER_DLL + CDOTAPlayer *pDOTAPlayer = ToDOTAPlayer( player ); + + if ( pDOTAPlayer && pDOTAPlayer->GetMoveType() != MOVETYPE_NOCLIP ) + { + Vector dir = pDOTAPlayer->GetCrosshairTracePos() - player->EyePosition(); + VectorNormalize( dir ); + + *forward = dir; + } + else + { + AngleVectors( player->EyeAngles() + player->GetPunchAngle(), forward ); + } +#else + Vector dir; + AngleVectors( player->EyeAngles() + player->GetPunchAngle(), forward ); +#endif + + *pos = player->EyePosition(); + +#ifdef SERVER_USES_VGUI +// GetNavUIEditVectors( pos, forward ); +#endif // SERVER_USES_VGUI +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Change the edit mode + */ +void CNavMesh::SetEditMode( EditModeType mode ) +{ + m_markedLadder = NULL; + m_markedArea = NULL; + m_markedCorner = NUM_CORNERS; + + m_editMode = mode; + + m_isContinuouslySelecting = false; + m_isContinuouslyDeselecting = false; + m_bIsDragDeselecting = false; +} + + +//-------------------------------------------------------------------------------------------------------------- +bool CNavMesh::FindNavAreaOrLadderAlongRay( const Vector &start, const Vector &end, CNavArea **bestArea, CNavLadder **bestLadder, CNavArea *ignore ) +{ + if ( !m_grid.Count() ) + return false; + + Ray_t ray; + ray.Init( start, end, vec3_origin, vec3_origin ); + + *bestArea = NULL; + *bestLadder = NULL; + + float bestDist = 1.0f; // 0..1 fraction + + for ( int i=0; iGetNormal(), right, up ); + right *= ladder->m_width * 0.5f; + left = -right; + + Vector c1 = ladder->m_top + right; + Vector c2 = ladder->m_top + left; + Vector c3 = ladder->m_bottom + right; + Vector c4 = ladder->m_bottom + left; + float dist = IntersectRayWithTriangle( ray, c1, c2, c4, false ); + if ( dist > 0 && dist < bestDist ) + { + *bestLadder = ladder; + bestDist = dist; + } + + dist = IntersectRayWithTriangle( ray, c1, c4, c3, false ); + if ( dist > 0 && dist < bestDist ) + { + *bestLadder = ladder; + bestDist = dist; + } + } + + Extent extent; + extent.lo = extent.hi = start; + extent.Encompass( end ); + + int loX = WorldToGridX( extent.lo.x ); + int loY = WorldToGridY( extent.lo.y ); + int hiX = WorldToGridX( extent.hi.x ); + int hiY = WorldToGridY( extent.hi.y ); + + for( int y = loY; y <= hiY; ++y ) + { + for( int x = loX; x <= hiX; ++x ) + { + NavAreaVector &areaGrid = m_grid[ x + y*m_gridSizeX ]; + + FOR_EACH_VEC( areaGrid, it ) + { + CNavArea *area = areaGrid[ it ]; + if ( area == ignore ) + continue; + + Vector nw = area->m_nwCorner; + Vector se = area->m_seCorner; + Vector ne, sw; + ne.x = se.x; + ne.y = nw.y; + ne.z = area->m_neZ; + sw.x = nw.x; + sw.y = se.y; + sw.z = area->m_swZ; + + float dist = IntersectRayWithTriangle( ray, nw, ne, se, false ); + if ( dist > 0 && dist < bestDist ) + { + *bestArea = area; + bestDist = dist; + } + + dist = IntersectRayWithTriangle( ray, se, sw, nw, false ); + if ( dist > 0 && dist < bestDist ) + { + *bestArea = area; + bestDist = dist; + } + } + } + } + + if ( *bestArea ) + { + *bestLadder = NULL; + } + + return bestDist < 1.0f; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Convenience function to find the nav area a player is looking at, for editing commands + */ +bool CNavMesh::FindActiveNavArea( void ) +{ + VPROF( "CNavMesh::FindActiveNavArea" ); + + m_splitAlongX = false; + m_splitEdge = 0.0f; + m_selectedArea = NULL; + m_climbableSurface = false; + m_selectedLadder = NULL; + + CBasePlayer *player = UTIL_GetListenServerHost(); + if ( player == NULL ) + return false; + + Vector from, dir; + GetEditVectors( &from, &dir ); + + float maxRange = 2000.0f; // 500 + bool isClippingRayAtFeet = false; + if ( nav_create_area_at_feet.GetBool() ) + { + if ( dir.z < 0 ) + { + float eyeHeight = player->GetViewOffset().z; + if ( eyeHeight != 0.0f ) + { + float rayHeight = -dir.z * maxRange; + maxRange = maxRange * eyeHeight / rayHeight; + isClippingRayAtFeet = true; + } + } + } + + Vector to = from + maxRange * dir; + + trace_t result; + CTraceFilterWalkableEntities filter( NULL, COLLISION_GROUP_NONE, WALK_THRU_EVERYTHING ); + UTIL_TraceLine( from, to, (nav_solid_props.GetBool()) ? MASK_NPCSOLID : MASK_NPCSOLID_BRUSHONLY, &filter, &result ); + + if (result.fraction != 1.0f) + { + if ( !IsEditMode( CREATING_AREA ) ) + { + m_climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0; + if ( !m_climbableSurface ) + { + m_climbableSurface = (result.contents & CONTENTS_LADDER) != 0; + } + m_surfaceNormal = result.plane.normal; + + if ( m_climbableSurface ) + { + // check if we're on the same plane as the original point when we're building a ladder + if ( IsEditMode( CREATING_LADDER ) ) + { + if ( m_surfaceNormal != m_ladderNormal ) + { + m_climbableSurface = false; + } + } + + if ( m_surfaceNormal.z > 0.9f ) + { + m_climbableSurface = false; // don't try to build ladders on flat ground + } + } + } + + if ( ( m_climbableSurface && !IsEditMode( CREATING_LADDER ) ) || !IsEditMode( CREATING_AREA ) ) + { + float closestDistSqr = 200.0f * 200.0f; + + for ( int i=0; im_bottom; + Vector absMax = ladder->m_top; + + Vector left( 0, 0, 0), right(0, 0, 0), up( 0, 0, 0); + VectorVectors( ladder->GetNormal(), right, up ); + right *= ladder->m_width * 0.5f; + left = -right; + + absMin.x += MIN( left.x, right.x ); + absMin.y += MIN( left.y, right.y ); + + absMax.x += MAX( left.x, right.x ); + absMax.y += MAX( left.y, right.y ); + + Extent e; + e.lo = absMin + Vector( -5, -5, -5 ); + e.hi = absMax + Vector( 5, 5, 5 ); + + if ( e.Contains( m_editCursorPos ) ) + { + m_selectedLadder = ladder; + break; + } + + if ( !m_climbableSurface ) + continue; + + Vector p1 = (ladder->m_bottom + ladder->m_top)/2; + Vector p2 = m_editCursorPos; + float distSqr = p1.DistToSqr( p2 ); + + if ( distSqr < closestDistSqr ) + { + m_selectedLadder = ladder; + closestDistSqr = distSqr; + } + } + } + + m_editCursorPos = result.endpos; + + // find the area the player is pointing at + if ( !m_climbableSurface && !m_selectedLadder ) + { + // Try to clip our trace to nav areas + FindNavAreaOrLadderAlongRay( result.startpos, result.endpos + 100.0f * dir, &m_selectedArea, &m_selectedLadder ); // extend a few units into the ground + + // Failing that, get the closest area to the endpoint + if ( !m_selectedArea && !m_selectedLadder ) + { + m_selectedArea = TheNavMesh->GetNearestNavArea( result.endpos, false, 500.0f ); + } + } + + if ( m_selectedArea ) + { + float yaw = player->EyeAngles().y; + while( yaw > 360.0f ) + yaw -= 360.0f; + + while( yaw < 0.0f ) + yaw += 360.0f; + + if ((yaw < 45.0f || yaw > 315.0f) || (yaw > 135.0f && yaw < 225.0f)) + { + m_splitEdge = SnapToGrid( result.endpos.y, true ); + m_splitAlongX = true; + } + else + { + m_splitEdge = SnapToGrid( result.endpos.x, true ); + m_splitAlongX = false; + } + } + + if ( !m_climbableSurface && !IsEditMode( CREATING_LADDER ) ) + { + m_editCursorPos = SnapToGrid( m_editCursorPos ); + } + + return true; + } + else if ( isClippingRayAtFeet ) + { + m_editCursorPos = SnapToGrid( result.endpos ); + } + + if ( IsEditMode( CREATING_LADDER ) || IsEditMode( CREATING_AREA ) ) + return false; + + // We started solid. Look for areas in front of us. + FindNavAreaOrLadderAlongRay( from, to, &m_selectedArea, &m_selectedLadder ); + + return (m_selectedArea != NULL || m_selectedLadder != NULL || isClippingRayAtFeet); +} + + +//-------------------------------------------------------------------------------------------------------------- +bool CNavMesh::FindLadderCorners( Vector *corner1, Vector *corner2, Vector *corner3 ) +{ + if ( !corner1 || !corner2 || !corner3 ) + return false; + + Vector ladderRight, ladderUp; + VectorVectors( m_ladderNormal, ladderRight, ladderUp ); + + Vector from, dir; + GetEditVectors( &from, &dir ); + + const float maxDist = 100000.0f; + + Ray_t ray; + ray.Init( from, from + dir * maxDist, vec3_origin, vec3_origin ); + + *corner1 = m_ladderAnchor + ladderUp * maxDist + ladderRight * maxDist; + *corner2 = m_ladderAnchor + ladderUp * maxDist - ladderRight * maxDist; + *corner3 = m_ladderAnchor - ladderUp * maxDist - ladderRight * maxDist; + float dist = IntersectRayWithTriangle( ray, *corner1, *corner2, *corner3, false ); + if ( dist < 0 ) + { + *corner2 = m_ladderAnchor - ladderUp * maxDist + ladderRight * maxDist; + dist = IntersectRayWithTriangle( ray, *corner1, *corner2, *corner3, false ); + } + + *corner3 = m_editCursorPos; + if ( dist > 0 && dist < maxDist ) + { + *corner3 = from + dir * dist * maxDist; + + float vertDistance = corner3->z - m_ladderAnchor.z; + float val = vertDistance / ladderUp.z; + + *corner1 = m_ladderAnchor + val * ladderUp; + *corner2 = *corner3 - val * ladderUp; + + return true; + } + + return false; +} + + +//-------------------------------------------------------------------------------------------------------------- +bool CheckForClimbableSurface( const Vector &start, const Vector &end ) +{ + trace_t result; + UTIL_TraceLine( start, end, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result ); + + bool climbableSurface = false; + if (result.fraction != 1.0f) + { + climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0; + if ( !climbableSurface ) + { + climbableSurface = (result.contents & CONTENTS_LADDER) != 0; + } + } + + return climbableSurface; +} + + +//-------------------------------------------------------------------------------------------------------------- +void StepAlongClimbableSurface( Vector &pos, const Vector &increment, const Vector &probe ) +{ + while ( CheckForClimbableSurface( pos + increment - probe, pos + increment + probe ) ) + { + pos += increment; + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavBuildLadder( void ) +{ + if ( !IsEditMode( NORMAL ) || !m_climbableSurface ) + { + return; + } + + // We've got a ladder at m_editCursorPos, with a normal of m_surfaceNormal + Vector right, up; + VectorVectors( -m_surfaceNormal, right, up ); + + m_ladderNormal = m_surfaceNormal; + + Vector startPos = m_editCursorPos; + + Vector leftEdge = startPos; + Vector rightEdge = startPos; + + // trace to the sides to find the width + Vector probe = m_surfaceNormal * -HalfHumanWidth; + const float StepSize = 1.0f; + StepAlongClimbableSurface( leftEdge, right * -StepSize, probe ); + StepAlongClimbableSurface( rightEdge, right * StepSize, probe ); + + Vector topEdge = (leftEdge + rightEdge) * 0.5f; + Vector bottomEdge = topEdge; + StepAlongClimbableSurface( topEdge, up * StepSize, probe ); + StepAlongClimbableSurface( bottomEdge, up * -StepSize, probe ); + + Vector top = (leftEdge + rightEdge) * 0.5f; + top.z = topEdge.z; + + Vector bottom = top; + bottom.z = bottomEdge.z; + + CreateLadder( topEdge, bottomEdge, leftEdge.DistTo( rightEdge ), m_ladderNormal.AsVector2D(), 0.0f ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Flood fills all areas with current place + */ +class PlaceFloodFillFunctor +{ +public: + PlaceFloodFillFunctor( CNavArea *area ) + { + m_initialPlace = area->GetPlace(); + } + + bool operator() ( CNavArea *area ) + { + if (area->GetPlace() != m_initialPlace) + return false; + + area->SetPlace( TheNavMesh->GetNavPlace() ); + + return true; + } + +private: + unsigned int m_initialPlace; +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Called when edit mode has just been enabled + */ +void CNavMesh::OnEditModeStart( void ) +{ + ClearSelectedSet(); + m_isContinuouslySelecting = false; + m_isContinuouslyDeselecting = false; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Called when edit mode has just been disabled + */ +void CNavMesh::OnEditModeEnd( void ) +{ +} + + +//-------------------------------------------------------------------------------------------------------------- +class DrawSelectedSet +{ +public: + DrawSelectedSet( const Vector &shift ) + { + m_count = 0; + m_shift = shift; + } + + bool operator() ( CNavArea *area ) + { + if (TheNavMesh->IsInSelectedSet( area )) + { + area->DrawSelectedSet( m_shift ); + ++m_count; + } + + return (m_count < nav_draw_limit.GetInt()); + } + + int m_count; + Vector m_shift; +}; + + +//-------------------------------------------------------------------------------------------------------- +class AddToDragSet +{ +public: + AddToDragSet( Extent &area, int zMin, int zMax, bool bDragDeselecting ) + { + m_nTolerance = 1; + m_dragArea = area; + m_zMin = zMin - m_nTolerance; + m_zMax = zMax + m_nTolerance; + m_bDragDeselecting = bDragDeselecting; + } + + bool operator() ( CNavArea *area ) + { + bool bShouldBeInSelectedSet = m_bDragDeselecting; + if ( ( TheNavMesh->IsInSelectedSet( area ) == bShouldBeInSelectedSet ) && area->IsOverlapping( m_dragArea ) && area->GetCenter().z >= m_zMin && area->GetCenter().z <= m_zMax ) + { + TheNavMesh->AddToDragSelectionSet( area ); + } + return true; + } + + Extent m_dragArea; + int m_zMin; + int m_zMax; + int m_nTolerance; + bool m_bDragDeselecting; +}; + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::UpdateDragSelectionSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + Extent dragArea; + int xmin = MIN( m_anchor.x, m_editCursorPos.x ); + int xmax = MAX( m_anchor.x, m_editCursorPos.x ); + int ymin = MIN( m_anchor.y, m_editCursorPos.y ); + int ymax = MAX( m_anchor.y, m_editCursorPos.y ); + + dragArea.lo = Vector( xmin, ymin, m_anchor.z ); + dragArea.hi = Vector( xmax, ymax, m_anchor.z ); + + ClearDragSelectionSet(); + + AddToDragSet add( dragArea, m_anchor.z - m_nDragSelectionVolumeZMin, m_anchor.z + m_nDragSelectionVolumeZMax, m_bIsDragDeselecting ); + ForAllAreas( add ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Draw navigation areas and edit them + * @todo Clean the whole edit system up - its structure is legacy from peculiarities in GoldSrc. + */ +ConVar nav_show_compass( "nav_show_compass", "0", FCVAR_CHEAT ); +void CNavMesh::DrawEditMode( void ) +{ + VPROF( "CNavMesh::DrawEditMode" ); + + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( IsGenerating() ) + return; + + // TODO: remove this when host_thread_mode 1 stops breaking NDEBUG_PERSIST_TILL_NEXT_SERVER overlays + static ConVarRef host_thread_mode( "host_thread_mode" ); + host_thread_mode.SetValue( 0 ); + + const float maxRange = 1000.0f; // 500 + +#if DEBUG_NAV_NODES + if ( nav_show_nodes.GetBool() ) + { + for ( CNavNode *node = CNavNode::GetFirst(); node != NULL; node = node->GetNext() ) + { + if ( m_editCursorPos.DistToSqr( *node->GetPosition() ) < 150*150 ) + { + node->Draw(); + } + } + } +#endif // DEBUG_NAV_NODES + + Vector from, dir; + GetEditVectors( &from, &dir ); + + Vector to = from + maxRange * dir; + + /* IN_PROGRESS: + if ( !IsEditMode( PLACE_PAINTING ) && nav_snap_to_grid.GetBool() ) + { + Vector center = SnapToGrid( m_editCursorPos ); + + const int GridCount = 3; + const int GridArraySize = GridCount * 2 + 1; + const int GridSize = GetGridSize(); + + // fill in an array of heights for the grid + Vector pos[GridArraySize][GridArraySize]; + int x, y; + for ( x=0; xDrawDragSelectionSet( dragSelectionColor ); + } + } + else if ( IsEditMode( CREATING_LADDER ) ) + { + Vector corner1, corner2, corner3; + if ( FindLadderCorners( &corner1, &corner2, &corner3 ) ) + { + NavEditColor color = NavCreationColor; + if ( !m_climbableSurface ) + { + color = NavInvalidCreationColor; + } + + NavDrawLine( m_ladderAnchor, corner1, color ); + NavDrawLine( corner1, corner3, color ); + NavDrawLine( corner3, corner2, color ); + NavDrawLine( corner2, m_ladderAnchor, color ); + } + } + + if ( m_selectedLadder ) + { + m_lastSelectedArea = NULL; + + // if ladder changed, print its ID + if (m_selectedLadder != m_lastSelectedLadder || nav_show_area_info.GetBool()) + { + m_lastSelectedLadder = m_selectedLadder; + + // print ladder info + char buffer[80]; + + CBaseEntity *ladderEntity = m_selectedLadder->GetLadderEntity(); + if ( ladderEntity ) + { + V_snprintf( buffer, sizeof( buffer ), "Ladder #%d (Team %s)\n", m_selectedLadder->GetID(), GetGlobalTeam( ladderEntity->GetTeamNumber() )->GetName() ); + } + else + { + V_snprintf( buffer, sizeof( buffer ), "Ladder #%d\n", m_selectedLadder->GetID() ); + } + NDebugOverlay::ScreenText( 0.5, 0.53, buffer, 255, 255, 0, 128, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + + // draw the ladder we are pointing at and all connected areas + m_selectedLadder->DrawLadder(); + m_selectedLadder->DrawConnectedAreas(); + } + + if ( m_markedLadder && !IsEditMode( PLACE_PAINTING ) ) + { + // draw the "marked" ladder + m_markedLadder->DrawLadder(); + } + + if ( m_markedArea && !IsEditMode( PLACE_PAINTING ) ) + { + // draw the "marked" area + m_markedArea->Draw(); + } + + // find the area the player is pointing at + if (m_selectedArea) + { + m_lastSelectedLadder = NULL; + + // if area changed, print its ID + if ( m_selectedArea != m_lastSelectedArea ) + { + m_showAreaInfoTimer.Start( nav_show_area_info.GetFloat() ); + m_lastSelectedArea = m_selectedArea; + } + + if (m_showAreaInfoTimer.HasStarted() && !m_showAreaInfoTimer.IsElapsed() ) + { + char buffer[80]; + char attrib[80]; + char locName[80]; + + if (m_selectedArea->GetPlace()) + { + const char *name = TheNavMesh->PlaceToName( m_selectedArea->GetPlace() ); + if (name) + V_strcpy_safe( locName, name ); + else + V_strcpy_safe( locName, "ERROR" ); + } + else + { + locName[0] = '\000'; + } + + if (IsEditMode( PLACE_PAINTING )) + { + attrib[0] = '\000'; + } + else + { + attrib[0] = 0; + int attributes = m_selectedArea->GetAttributes(); + if ( attributes & NAV_MESH_CROUCH ) Q_strncat( attrib, "CROUCH ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_JUMP ) Q_strncat( attrib, "JUMP ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_PRECISE ) Q_strncat( attrib, "PRECISE ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_NO_JUMP ) Q_strncat( attrib, "NO_JUMP ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_STOP ) Q_strncat( attrib, "STOP ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_RUN ) Q_strncat( attrib, "RUN ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_WALK ) Q_strncat( attrib, "WALK ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_AVOID ) Q_strncat( attrib, "AVOID ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_TRANSIENT ) Q_strncat( attrib, "TRANSIENT ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_DONT_HIDE ) Q_strncat( attrib, "DONT_HIDE ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_STAND ) Q_strncat( attrib, "STAND ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_NO_HOSTAGES )Q_strncat( attrib, "NO HOSTAGES ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_STAIRS ) Q_strncat( attrib, "STAIRS ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_OBSTACLE_TOP ) Q_strncat( attrib, "OBSTACLE ", sizeof( attrib ), -1 ); + if ( attributes & NAV_MESH_CLIFF ) Q_strncat( attrib, "CLIFF ", sizeof( attrib ), -1 ); +#ifdef TERROR + if ( attributes & TerrorNavArea::NAV_PLAYERCLIP ) Q_strncat( attrib, "PLAYERCLIP ", sizeof( attrib ), -1 ); + if ( attributes & TerrorNavArea::NAV_BREAKABLEWALL ) Q_strncat( attrib, "BREAKABLEWALL ", sizeof( attrib ), -1 ); + if ( m_selectedArea->IsBlocked( TEAM_SURVIVOR ) ) Q_strncat( attrib, "BLOCKED_SURVIVOR ", sizeof( attrib ), -1 ); + if ( m_selectedArea->IsBlocked( TEAM_ZOMBIE ) ) Q_strncat( attrib, "BLOCKED_ZOMBIE ", sizeof( attrib ), -1 ); +#else + if ( m_selectedArea->IsBlocked( TEAM_ANY ) ) Q_strncat( attrib, "BLOCKED ", sizeof( attrib ), -1 ); +#endif + if ( m_selectedArea->HasAvoidanceObstacle() ) Q_strncat( attrib, "OBSTRUCTED ", sizeof( attrib ), -1 ); + if ( m_selectedArea->IsDamaging() ) Q_strncat( attrib, "DAMAGING ", sizeof( attrib ), -1 ); + if ( m_selectedArea->IsUnderwater() ) Q_strncat( attrib, "UNDERWATER ", sizeof( attrib ), -1 ); + + int connected = 0; + connected += m_selectedArea->GetAdjacentCount( NORTH ); + connected += m_selectedArea->GetAdjacentCount( SOUTH ); + connected += m_selectedArea->GetAdjacentCount( EAST ); + connected += m_selectedArea->GetAdjacentCount( WEST ); + Q_strncat( attrib, UTIL_VarArgs( "%d Connections ", connected ), sizeof( attrib ), -1 ); + } + + Q_snprintf( buffer, sizeof( buffer ), "Area #%d %s %s\n", m_selectedArea->GetID(), locName, attrib ); + NDebugOverlay::ScreenText( 0.5, 0.53, buffer, 255, 255, 0, 128, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + + // do "place painting" + if ( m_isPlacePainting ) + { + if (m_selectedArea->GetPlace() != TheNavMesh->GetNavPlace()) + { + m_selectedArea->SetPlace( TheNavMesh->GetNavPlace() ); + player->EmitSound( "Bot.EditSwitchOn" ); + } + } + } + + + // do continuous selecting into selected set + if ( m_isContinuouslySelecting ) + { + AddToSelectedSet( m_selectedArea ); + } + else if ( m_isContinuouslyDeselecting ) + { + // do continuous de-selecting into selected set + RemoveFromSelectedSet( m_selectedArea ); + } + + + if (IsEditMode( PLACE_PAINTING )) + { + m_selectedArea->DrawConnectedAreas(); + } + else // normal editing mode + { + // draw split line + Extent extent; + m_selectedArea->GetExtent( &extent ); + + float yaw = player->EyeAngles().y; + while( yaw > 360.0f ) + yaw -= 360.0f; + + while( yaw < 0.0f ) + yaw += 360.0f; + + if (m_splitAlongX) + { + from.x = extent.lo.x; + from.y = m_splitEdge; + from.z = m_selectedArea->GetZ( from ); + + to.x = extent.hi.x; + to.y = m_splitEdge; + to.z = m_selectedArea->GetZ( to ); + } + else + { + from.x = m_splitEdge; + from.y = extent.lo.y; + from.z = m_selectedArea->GetZ( from ); + + to.x = m_splitEdge; + to.y = extent.hi.y; + to.z = m_selectedArea->GetZ( to ); + } + + NavDrawLine( from, to, NavSplitLineColor ); + + // draw the area we are pointing at and all connected areas + m_selectedArea->DrawConnectedAreas(); + } + } + + // render the selected set + if (!IsSelectedSetEmpty()) + { + Vector shift( 0, 0, 0 ); + + if (IsEditMode( SHIFTING_XY )) + { + shift = m_editCursorPos - m_anchor; + shift.z = 0.0f; + } + + DrawSelectedSet draw( shift ); + + // if the selected set is small, just blast it out + if (m_selectedSet.Count() < nav_draw_limit.GetInt()) + { + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + + draw( area ); + } + } + else + { + // draw the part nearest the player + CNavArea *nearest = NULL; + float nearRange = 9999999999.9f; + + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + + float range = (player->GetAbsOrigin() - area->GetCenter()).LengthSqr(); + if (range < nearRange) + { + nearRange = range; + nearest = area; + } + } + + SearchSurroundingAreas( nearest, nearest->GetCenter(), draw, -1, INCLUDE_INCOMING_CONNECTIONS | INCLUDE_BLOCKED_AREAS ); + } + } + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::SetMarkedLadder( CNavLadder *ladder ) +{ + m_markedLadder = ladder; + m_markedArea = NULL; + m_markedCorner = NUM_CORNERS; +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::SetMarkedArea( CNavArea *area ) +{ + m_markedLadder = NULL; + m_markedArea = area; + m_markedCorner = NUM_CORNERS; +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavDelete( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + if (IsSelectedSetEmpty()) + { + // the old way + CNavArea *markedArea = GetMarkedArea(); + CNavLadder *markedLadder = GetMarkedLadder(); + FindActiveNavArea(); + + if( markedArea ) + { + player->EmitSound( "EDIT_DELETE" ); + TheNavAreas.FindAndRemove( markedArea ); + TheNavMesh->OnEditDestroyNotify( markedArea ); + TheNavMesh->DestroyArea( markedArea ); + } + else if( markedLadder ) + { + player->EmitSound( "EDIT_DELETE" ); + m_ladders.FindAndRemove( markedLadder ); + OnEditDestroyNotify( markedLadder ); + delete markedLadder; + } + else if ( m_selectedArea ) + { + player->EmitSound( "EDIT_DELETE" ); + TheNavAreas.FindAndRemove( m_selectedArea ); + CNavArea *deadArea = m_selectedArea; + OnEditDestroyNotify( deadArea ); + TheNavMesh->DestroyArea( deadArea ); + } + else if ( m_selectedLadder ) + { + player->EmitSound( "EDIT_DELETE" ); + m_ladders.FindAndRemove( m_selectedLadder ); + CNavLadder *deadLadder = m_selectedLadder; + OnEditDestroyNotify( deadLadder ); + delete deadLadder; + } + } + else + { + // delete all areas in the selected set + player->EmitSound( "EDIT_DELETE" ); + + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + + TheNavAreas.FindAndRemove( area ); + + OnEditDestroyNotify( area ); + + TheNavMesh->DestroyArea( area ); + } + + Msg( "Deleted %d areas\n", m_selectedSet.Count() ); + + ClearSelectedSet(); + } + + StripNavigationAreas(); + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +class SelectCollector +{ +public: + SelectCollector( void ) + { + m_count = 0; + } + + bool operator() ( CNavArea *area ) + { + // already selected areas terminate flood select + if (TheNavMesh->IsInSelectedSet( area )) + return false; + + TheNavMesh->AddToSelectedSet( area ); + ++m_count; + + return true; + } + + int m_count; +}; + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavDeleteMarked( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + CNavArea *markedArea = GetMarkedArea(); + if( markedArea ) + { + player->EmitSound( "EDIT_DELETE" ); + TheNavMesh->OnEditDestroyNotify( markedArea ); + TheNavAreas.FindAndRemove( markedArea ); + TheNavMesh->DestroyArea( markedArea ); + } + + CNavLadder *markedLadder = GetMarkedLadder(); + if( markedLadder ) + { + player->EmitSound( "EDIT_DELETE" ); + m_ladders.FindAndRemove( markedLadder ); + delete markedLadder; + } + + StripNavigationAreas(); + + ClearSelectedSet(); + + SetMarkedArea( NULL ); // unmark the mark area + SetMarkedLadder( NULL ); // unmark the mark ladder + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Select the current nav area and all recursively connected areas + */ +void CNavMesh::CommandNavFloodSelect( const CCommand &args ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + FindActiveNavArea(); + + CNavArea *start = m_selectedArea; + if ( !start ) + { + start = m_markedArea; + } + + if ( start ) + { + player->EmitSound( "EDIT_DELETE" ); + + int connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS; + if ( args.ArgC() == 2 && FStrEq( "out", args[1] ) ) + { + connections = INCLUDE_BLOCKED_AREAS; + } + if ( args.ArgC() == 2 && FStrEq( "in", args[1] ) ) + { + connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS | EXCLUDE_OUTGOING_CONNECTIONS; + } + + // collect all areas connected to this area + SelectCollector collector; + SearchSurroundingAreas( start, start->GetCenter(), collector, -1, connections ); + + Msg( "Selected %d areas.\n", collector.m_count ); + } + + SetMarkedArea( NULL ); // unmark the mark area +} + + +//-------------------------------------------------------------------------------------------------------------- +/** +* Toggles all areas into/out of the selected set +*/ +void CNavMesh::CommandNavToggleSelectedSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + player->EmitSound( "EDIT_DELETE" ); + + NavAreaVector notInSelectedSet; + + // Build a list of all areas not in the selected set + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[it]; + if ( !IsInSelectedSet( area ) ) + { + notInSelectedSet.AddToTail( area ); + } + } + + // Clear out the selected set + ClearSelectedSet(); + + // Add areas back into the selected set + FOR_EACH_VEC( notInSelectedSet, nit ) + { + CNavArea *area = notInSelectedSet[nit]; + AddToSelectedSet( area ); + } + + Msg( "Selected %d areas.\n", notInSelectedSet.Count() ); + + SetMarkedArea( NULL ); // unmark the mark area +} + + +//-------------------------------------------------------------------------------------------------------------- +/** +* Saves the current selected set for later retrieval. +*/ +void CNavMesh::CommandNavStoreSelectedSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + player->EmitSound( "EDIT_DELETE" ); + + m_storedSelectedSet.RemoveAll(); + FOR_EACH_VEC( m_selectedSet, it ) + { + m_storedSelectedSet.AddToTail( m_selectedSet[it]->GetID() ); + } +} + + + +//-------------------------------------------------------------------------------------------------------------- +/** +* Restores an older selected set. +*/ +void CNavMesh::CommandNavRecallSelectedSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + player->EmitSound( "EDIT_DELETE" ); + + ClearSelectedSet(); + + for ( int i=0; iEmitSound( "EDIT_MARK.Enable" ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +/** +* Add area ID to selected set +*/ +void CNavMesh::CommandNavAddToSelectedSetByID( const CCommand &args ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) || args.ArgC() < 2 ) + return; + + int id = atoi( args[1] ); + CNavArea *area = GetNavAreaByID( id ); + if ( area ) + { + AddToSelectedSet( area ); + player->EmitSound( "EDIT_MARK.Enable" ); + Msg( "Added area %d. ( to go there: setpos %f %f %f )\n", id, area->GetCenter().x, area->GetCenter().y, area->GetCenter().z + 5 ); + } + else + { + Msg( "No area with id %d\n", id ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Remove current area from selected set + */ +void CNavMesh::CommandNavRemoveFromSelectedSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + RemoveFromSelectedSet( m_selectedArea ); + player->EmitSound( "EDIT_MARK.Disable" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** +* Add/remove current area from selected set +*/ +void CNavMesh::CommandNavToggleInSelectedSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (IsInSelectedSet( m_selectedArea )) + { + RemoveFromSelectedSet( m_selectedArea ); + } + else + { + AddToSelectedSet( m_selectedArea ); + } + player->EmitSound( "EDIT_MARK.Disable" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Clear the selected set to empty + */ +void CNavMesh::CommandNavClearSelectedSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + ClearSelectedSet(); + player->EmitSound( "EDIT_MARK.Disable" ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Start continuously selecting areas into the selected set + */ +void CNavMesh::CommandNavBeginSelecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + m_isContinuouslySelecting = true; + m_isContinuouslyDeselecting = false; + + player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Stop continuously selecting areas into the selected set + */ +void CNavMesh::CommandNavEndSelecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + m_isContinuouslySelecting = false; + m_isContinuouslyDeselecting = false; + + player->EmitSound( "EDIT_END_AREA.Creating" ); +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavBeginDragSelecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) && !IsEditMode( DRAG_SELECTING ) ) + return; + + FindActiveNavArea(); + + if ( IsEditMode( DRAG_SELECTING ) ) + { + ClearDragSelectionSet(); + SetEditMode( NORMAL ); + player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); + } + else + { + player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); + + SetEditMode( DRAG_SELECTING ); + + // m_anchor starting corner + m_anchor = m_editCursorPos; + m_nDragSelectionVolumeZMax = nav_drag_selection_volume_zmax_offset.GetInt(); + m_nDragSelectionVolumeZMin = nav_drag_selection_volume_zmin_offset.GetInt(); + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavEndDragSelecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( IsEditMode( DRAG_SELECTING ) ) + { + // Transfer drag selected areas to the selected set + FOR_EACH_VEC( m_dragSelectionSet, it ) + { + AddToSelectedSet( m_dragSelectionSet[it] ); + } + SetEditMode( NORMAL ); + } + else + { + player->EmitSound( "EDIT_END_AREA.NotCreating" ); + } + + ClearDragSelectionSet(); + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavBeginDragDeselecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) && !IsEditMode( DRAG_SELECTING ) ) + return; + + FindActiveNavArea(); + + if ( IsEditMode( DRAG_SELECTING ) ) + { + ClearDragSelectionSet(); + SetEditMode( NORMAL ); + player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); + } + else + { + player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); + + SetEditMode( DRAG_SELECTING ); + m_bIsDragDeselecting = true; + + // m_anchor starting corner + m_anchor = m_editCursorPos; + m_nDragSelectionVolumeZMax = nav_drag_selection_volume_zmax_offset.GetInt(); + m_nDragSelectionVolumeZMin = nav_drag_selection_volume_zmin_offset.GetInt(); + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavEndDragDeselecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( IsEditMode( DRAG_SELECTING ) ) + { + // Remove drag selected areas from the selected set + FOR_EACH_VEC( m_dragSelectionSet, it ) + { + RemoveFromSelectedSet( m_dragSelectionSet[it] ); + } + SetEditMode( NORMAL ); + } + else + { + player->EmitSound( "EDIT_END_AREA.NotCreating" ); + } + + ClearDragSelectionSet(); + m_bIsDragDeselecting = false; + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavRaiseDragVolumeMax( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + m_nDragSelectionVolumeZMax += 32; + nav_drag_selection_volume_zmax_offset.SetValue( m_nDragSelectionVolumeZMax ); +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavLowerDragVolumeMax( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + m_nDragSelectionVolumeZMax = MAX( 0, m_nDragSelectionVolumeZMax - 32 ); + nav_drag_selection_volume_zmax_offset.SetValue( m_nDragSelectionVolumeZMax ); +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavRaiseDragVolumeMin( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + m_nDragSelectionVolumeZMin = MAX( 0, m_nDragSelectionVolumeZMin - 32 ); + nav_drag_selection_volume_zmin_offset.SetValue( m_nDragSelectionVolumeZMin ); +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavLowerDragVolumeMin( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + m_nDragSelectionVolumeZMin += 32; + nav_drag_selection_volume_zmin_offset.SetValue( m_nDragSelectionVolumeZMin ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Start/stop continuously selecting areas into the selected set + */ +void CNavMesh::CommandNavToggleSelecting( bool playSound ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + m_isContinuouslySelecting = !m_isContinuouslySelecting; + m_isContinuouslyDeselecting = false; + + if ( playSound ) + { + player->EmitSound( "EDIT_END_AREA.Creating" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Start continuously de-selecting areas from the selected set + */ +void CNavMesh::CommandNavBeginDeselecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + m_isContinuouslyDeselecting = true; + m_isContinuouslySelecting = false; + + player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Stop continuously de-selecting areas from the selected set + */ +void CNavMesh::CommandNavEndDeselecting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + m_isContinuouslyDeselecting = false; + m_isContinuouslySelecting = false; + + player->EmitSound( "EDIT_END_AREA.Creating" ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Start/stop continuously de-selecting areas from the selected set + */ +void CNavMesh::CommandNavToggleDeselecting( bool playSound ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + m_isContinuouslyDeselecting = !m_isContinuouslyDeselecting; + m_isContinuouslySelecting = false; + + if ( playSound ) + { + player->EmitSound( "EDIT_END_AREA.Creating" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Selects all areas that intersect the half-space + * Arguments: "+z 100", or "-x 30", etc. + */ +void CNavMesh::CommandNavSelectHalfSpace( const CCommand &args ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + if ( args.ArgC() != 3 ) + { + Warning( "Error: <+X|-X|+Y|-Y|+Z|-Z> \n" ); + return; + } + + enum HalfSpaceType + { + PLUS_X, MINUS_X, + PLUS_Y, MINUS_Y, + PLUS_Z, MINUS_Z, + } + halfSpace = PLUS_X; + + if (FStrEq( "+x", args[1] )) + { + halfSpace = PLUS_X; + } + else if (FStrEq( "-x", args[1] )) + { + halfSpace = MINUS_X; + } + else if (FStrEq( "+y", args[1] )) + { + halfSpace = PLUS_Y; + } + else if (FStrEq( "-y", args[1] )) + { + halfSpace = MINUS_Y; + } + else if (FStrEq( "+z", args[1] )) + { + halfSpace = PLUS_Z; + } + else if (FStrEq( "-z", args[1] )) + { + halfSpace = MINUS_Z; + } + + float value = atof( args[2] ); + + Extent extent; + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[it]; + area->GetExtent( &extent ); + + switch( halfSpace ) + { + case PLUS_X: + if (extent.lo.x < value && extent.hi.x < value) + { + continue; + } + break; + + case PLUS_Y: + if (extent.lo.y < value && extent.hi.y < value) + { + continue; + } + break; + + case PLUS_Z: + if (extent.lo.z < value && extent.hi.z < value) + { + continue; + } + break; + + case MINUS_X: + if (extent.lo.x > value && extent.hi.x > value) + { + continue; + } + break; + + case MINUS_Y: + if (extent.lo.y > value && extent.hi.y > value) + { + continue; + } + break; + + case MINUS_Z: + if (extent.lo.z > value && extent.hi.z > value) + { + continue; + } + break; + } + + // toggle membership + if ( IsInSelectedSet( area ) ) + { + RemoveFromSelectedSet( area ); + } + else + { + AddToSelectedSet( area ); + } + } + + player->EmitSound( "EDIT_DELETE" ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Begin shifting selected set in the XY plane + */ +void CNavMesh::CommandNavBeginShiftXY( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if (GetEditMode() == SHIFTING_XY) + { + SetEditMode( NORMAL ); + player->EmitSound( "EDIT_END_AREA.Creating" ); + return; + } + else + { + SetEditMode( SHIFTING_XY ); + player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); + } + + // m_anchor starting corner + m_anchor = m_editCursorPos; +} + + +//-------------------------------------------------------------------------------------------------------- +/** + * Shift a set of areas, and all connected ladders + */ +class ShiftSet +{ +public: + ShiftSet( const Vector &shift ) + { + m_shift = shift; + } + + bool operator()( CNavArea *area ) + { + area->Shift( m_shift ); + + const NavLadderConnectVector *ladders = area->GetLadders( CNavLadder::LADDER_UP ); + int it; + for( it = 0; it < ladders->Count(); ++it ) + { + CNavLadder *ladder = (*ladders)[ it ].ladder; + if ( !m_ladders.HasElement( ladder ) ) + { + ladder->Shift( m_shift ); + m_ladders.AddToTail( ladder ); + } + } + + ladders = area->GetLadders( CNavLadder::LADDER_DOWN ); + for( it = 0; it < ladders->Count(); ++it ) + { + CNavLadder *ladder = (*ladders)[ it ].ladder; + if ( !m_ladders.HasElement( ladder ) ) + { + ladder->Shift( m_shift ); + m_ladders.AddToTail( ladder ); + } + } + + return true; + } + +private: + CUtlVector< CNavLadder * > m_ladders; + Vector m_shift; +}; + + +//-------------------------------------------------------------------------------------------------------------- +/** + * End shifting selected set in the XY plane + */ +void CNavMesh::CommandNavEndShiftXY( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + SetEditMode( NORMAL ); + + Vector shiftAmount = m_editCursorPos - m_anchor; + shiftAmount.z = 0.0f; + + ShiftSet shift( shiftAmount ); + + // update the position of all areas in the selected set + TheNavMesh->ForAllSelectedAreas( shift ); + + player->EmitSound( "EDIT_END_AREA.Creating" ); +} + + +//-------------------------------------------------------------------------------------------------------- +CON_COMMAND_F( nav_shift, "Shifts the selected areas by the specified amount", FCVAR_CHEAT ) +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + TheNavMesh->SetEditMode( CNavMesh::NORMAL ); + + Vector shiftAmount( vec3_origin ); + if ( args.ArgC() > 1 ) + { + shiftAmount.x = atoi( args[1] ); + + if ( args.ArgC() > 2 ) + { + shiftAmount.y = atoi( args[2] ); + + if ( args.ArgC() > 3 ) + { + shiftAmount.z = atoi( args[3] ); + } + } + } + + ShiftSet shift( shiftAmount ); + + // update the position of all areas in the selected set + TheNavMesh->ForAllSelectedAreas( shift ); + + player->EmitSound( "EDIT_END_AREA.Creating" ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CommandNavCenterInWorld( void ) +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + TheNavMesh->SetEditMode( CNavMesh::NORMAL ); + + // Build the nav mesh's extent + Extent navExtent; + bool first = true; + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[it]; + if ( first ) + { + area->GetExtent( &navExtent ); + first = false; + } + else + { + navExtent.Encompass( area->GetCorner( NORTH_WEST ) ); + navExtent.Encompass( area->GetCorner( NORTH_EAST ) ); + navExtent.Encompass( area->GetCorner( SOUTH_WEST ) ); + navExtent.Encompass( area->GetCorner( SOUTH_EAST ) ); + } + } + + // Get the world's extent + CWorld *world = dynamic_cast< CWorld * >( CBaseEntity::Instance( INDEXENT( 0 ) ) ); + if ( !world ) + return; + + Extent worldExtent; + world->GetWorldBounds( worldExtent.lo, worldExtent.hi ); + + // Compute the difference, and shift in XY + Vector navCenter = ( navExtent.lo + navExtent.hi ) * 0.5f; + Vector worldCenter = ( worldExtent.lo + worldExtent.hi ) * 0.5f; + Vector shift = worldCenter - navCenter; + shift.z = 0.0f; + + // update the position of all areas + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + + area->Shift( shift ); + } + + player->EmitSound( "EDIT_END_AREA.Creating" ); + + Msg( "Shifting mesh by %f,%f\n", shift.x, shift.y ); +} +ConCommand nav_world_center( "nav_world_center", CommandNavCenterInWorld, "Centers the nav mesh in the world", FCVAR_CHEAT ); + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Add invalid areas to selected set + */ +void CNavMesh::CommandNavSelectInvalidAreas( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + ClearSelectedSet(); + + Extent areaExtent; + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + + if ( area ) + { + area->GetExtent( &areaExtent ); + for ( float x = areaExtent.lo.x; x + GenerationStepSize <= areaExtent.hi.x; x += GenerationStepSize ) + { + for ( float y = areaExtent.lo.y; y + GenerationStepSize <= areaExtent.hi.y; y += GenerationStepSize ) + { + float nw = area->GetZ( x, y ); + float ne = area->GetZ( x + GenerationStepSize, y ); + float sw = area->GetZ( x, y + GenerationStepSize ); + float se = area->GetZ( x + GenerationStepSize, y + GenerationStepSize ); + + if ( !IsHeightDifferenceValid( nw, ne, sw, se ) || + !IsHeightDifferenceValid( ne, nw, sw, se ) || + !IsHeightDifferenceValid( sw, ne, nw, se ) || + !IsHeightDifferenceValid( se, ne, sw, nw ) ) + { + AddToSelectedSet( area ); + } + } + } + } + } + + Msg( "Selected %d areas.\n", m_selectedSet.Count() ); + + if ( m_selectedSet.Count() ) + { + player->EmitSound( "EDIT_MARK.Enable" ); + } + else + { + player->EmitSound( "EDIT_MARK.Disable" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Add blocked areas to selected set + */ +void CNavMesh::CommandNavSelectBlockedAreas( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + ClearSelectedSet(); + + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + + if ( area && area->IsBlocked( TEAM_ANY ) ) + { + AddToSelectedSet( area ); + } + } + + Msg( "Selected %d areas.\n", m_selectedSet.Count() ); + + if ( m_selectedSet.Count() ) + { + player->EmitSound( "EDIT_MARK.Enable" ); + } + else + { + player->EmitSound( "EDIT_MARK.Disable" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Add obstructed areas to selected set + */ +void CNavMesh::CommandNavSelectObstructedAreas( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + ClearSelectedSet(); + + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + + if ( area && area->HasAvoidanceObstacle() ) + { + AddToSelectedSet( area ); + } + } + + Msg( "Selected %d areas.\n", m_selectedSet.Count() ); + + if ( m_selectedSet.Count() ) + { + player->EmitSound( "EDIT_MARK.Enable" ); + } + else + { + player->EmitSound( "EDIT_MARK.Disable" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Add damaging areas to selected set + */ +void CNavMesh::CommandNavSelectDamagingAreas( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + ClearSelectedSet(); + + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + + if ( area && area->IsDamaging() ) + { + AddToSelectedSet( area ); + } + } + + Msg( "Selected %d areas.\n", m_selectedSet.Count() ); + + if ( m_selectedSet.Count() ) + { + player->EmitSound( "EDIT_MARK.Enable" ); + } + else + { + player->EmitSound( "EDIT_MARK.Disable" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Adds stairs areas to the selected set + */ +void CNavMesh::CommandNavSelectStairs( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if ( player == NULL ) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + ClearSelectedSet(); + + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + + if ( area && area->HasAttributes( NAV_MESH_STAIRS ) ) + { + AddToSelectedSet( area ); + } + } + + Msg( "Selected %d areas.\n", m_selectedSet.Count() ); + + if ( m_selectedSet.Count() ) + { + player->EmitSound( "EDIT_MARK.Enable" ); + } + else + { + player->EmitSound( "EDIT_MARK.Disable" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +// Adds areas not connected to mesh to the selected set +void CNavMesh::CommandNavSelectOrphans( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) && !IsEditMode( PLACE_PAINTING ) ) + return; + + FindActiveNavArea(); + + CNavArea *start = m_selectedArea; + if ( !start ) + { + start = m_markedArea; + } + + if ( start ) + { + player->EmitSound( "EDIT_DELETE" ); + + int connections = INCLUDE_BLOCKED_AREAS | INCLUDE_INCOMING_CONNECTIONS; + + // collect all areas connected to this area + SelectCollector collector; + SearchSurroundingAreas( start, start->GetCenter(), collector, -1, connections ); + + // toggle the selected set to reveal the orphans + CommandNavToggleSelectedSet(); + } + + SetMarkedArea( NULL ); // unmark the mark area + +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavSplit( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (m_selectedArea->SplitEdit( m_splitAlongX, m_splitEdge )) + player->EmitSound( "EDIT_SPLIT.MarkedArea" ); + else + player->EmitSound( "EDIT_SPLIT.NoMarkedArea" ); + } + + StripNavigationAreas(); + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +bool MakeSniperSpots( CNavArea *area ) +{ + if ( !area ) + return false; + + bool splitAlongX; + float splitEdge; + + const float minSplitSize = 2.0f; // ensure the first split is larger than this + + float sizeX = area->GetSizeX(); + float sizeY = area->GetSizeY(); + + if ( sizeX > GenerationStepSize && sizeX > sizeY ) + { + splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).x, GenerationStepSize ); + if ( splitEdge < area->GetCorner( NORTH_WEST ).x + minSplitSize ) + splitEdge += GenerationStepSize; + splitAlongX = false; + } + else if ( sizeY > GenerationStepSize && sizeY > sizeX ) + { + splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).y, GenerationStepSize ); + if ( splitEdge < area->GetCorner( NORTH_WEST ).y + minSplitSize ) + splitEdge += GenerationStepSize; + splitAlongX = true; + } + else + { + return false; + } + + CNavArea *first, *second; + if ( !area->SplitEdit( splitAlongX, splitEdge, &first, &second ) ) + { + return false; + } + + first->Disconnect( second ); + second->Disconnect( first ); + + MakeSniperSpots( first ); + MakeSniperSpots( second ); + + return true; +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavMakeSniperSpots( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + // recursively split the area + if ( MakeSniperSpots( m_selectedArea ) ) + { + player->EmitSound( "EDIT_SPLIT.MarkedArea" ); + } + else + { + player->EmitSound( "EDIT_SPLIT.NoMarkedArea" ); + } + } + else + { + player->EmitSound( "EDIT_SPLIT.NoMarkedArea" ); + } + + StripNavigationAreas(); + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavMerge( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + CNavArea *other = m_markedArea; + if ( !m_markedArea && m_selectedSet.Count() == 1 ) + { + other = m_selectedSet[0]; + } + + if ( other && other != m_selectedArea ) + { + if ( m_selectedArea->MergeEdit( other ) ) + player->EmitSound( "EDIT_MERGE.Enable" ); + else + player->EmitSound( "EDIT_MERGE.Disable" ); + } + else + { + Msg( "To merge, mark an area, highlight a second area, then invoke the merge command" ); + player->EmitSound( "EDIT_MERGE.Disable" ); + } + } + + StripNavigationAreas(); + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection + ClearSelectedSet(); +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavMark( const CCommand &args ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + if (!IsSelectedSetEmpty()) + { + // add or remove areas from the selected set + if (IsInSelectedSet( m_selectedArea )) + { + // remove from set + player->EmitSound( "EDIT_MARK.Disable" ); + RemoveFromSelectedSet( m_selectedArea ); + } + else + { + // add to set + player->EmitSound( "EDIT_MARK.Enable" ); + AddToSelectedSet( m_selectedArea ); + } + return; + } + + FindActiveNavArea(); + + if ( m_markedArea || m_markedLadder ) + { + // Unmark area or ladder + player->EmitSound( "EDIT_MARK.Enable" ); + Msg("Area unmarked.\n"); + SetMarkedArea( NULL ); + } + else if ( args.ArgC() > 1 ) + { + if ( FStrEq( args[1], "ladder" ) ) + { + if ( args.ArgC() > 2 ) + { + const char *ladderIDNameToMark = args[2]; + if ( ladderIDNameToMark ) + { + unsigned int ladderIDToMark = atoi( ladderIDNameToMark ); + if ( ladderIDToMark != 0 ) + { + CNavLadder *ladder = TheNavMesh->GetLadderByID( ladderIDToMark ); + if ( ladder ) + { + player->EmitSound( "EDIT_MARK.Disable" ); + SetMarkedLadder( ladder ); + + int connected = 0; + connected += m_markedLadder->m_topForwardArea != NULL; + connected += m_markedLadder->m_topLeftArea != NULL; + connected += m_markedLadder->m_topRightArea != NULL; + connected += m_markedLadder->m_topBehindArea != NULL; + connected += m_markedLadder->m_bottomArea != NULL; + + Msg( "Marked Ladder is connected to %d Areas\n", connected ); + } + } + } + } + } + else + { + const char *areaIDNameToMark = args[1]; + if( areaIDNameToMark != NULL ) + { + unsigned int areaIDToMark = atoi(areaIDNameToMark); + if( areaIDToMark != 0 ) + { + CNavArea *areaToMark = NULL; + FOR_EACH_VEC( TheNavAreas, nit ) + { + if( TheNavAreas[nit]->GetID() == areaIDToMark ) + { + areaToMark = TheNavAreas[nit]; + break; + } + } + if( areaToMark ) + { + player->EmitSound( "EDIT_MARK.Disable" ); + SetMarkedArea( areaToMark ); + + int connected = 0; + connected += GetMarkedArea()->GetAdjacentCount( NORTH ); + connected += GetMarkedArea()->GetAdjacentCount( SOUTH ); + connected += GetMarkedArea()->GetAdjacentCount( EAST ); + connected += GetMarkedArea()->GetAdjacentCount( WEST ); + + Msg( "Marked Area is connected to %d other Areas\n", connected ); + } + } + } + } + } + else if ( m_selectedArea ) + { + // Mark an area + player->EmitSound( "EDIT_MARK.Disable" ); + SetMarkedArea( m_selectedArea ); + + int connected = 0; + connected += GetMarkedArea()->GetAdjacentCount( NORTH ); + connected += GetMarkedArea()->GetAdjacentCount( SOUTH ); + connected += GetMarkedArea()->GetAdjacentCount( EAST ); + connected += GetMarkedArea()->GetAdjacentCount( WEST ); + + Msg( "Marked Area is connected to %d other Areas\n", connected ); + } + else if ( m_selectedLadder ) + { + // Mark a ladder + player->EmitSound( "EDIT_MARK.Disable" ); + SetMarkedLadder( m_selectedLadder ); + + int connected = 0; + connected += m_markedLadder->m_topForwardArea != NULL; + connected += m_markedLadder->m_topLeftArea != NULL; + connected += m_markedLadder->m_topRightArea != NULL; + connected += m_markedLadder->m_topBehindArea != NULL; + connected += m_markedLadder->m_bottomArea != NULL; + + Msg( "Marked Ladder is connected to %d Areas\n", connected ); + } + + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavUnmark( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + player->EmitSound( "EDIT_MARK.Enable" ); + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavBeginArea( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !(IsEditMode( CREATING_AREA ) || IsEditMode( CREATING_LADDER ) || IsEditMode( NORMAL )) ) + { + player->EmitSound( "EDIT_END_AREA.NotCreating" ); + return; + } + + FindActiveNavArea(); + + if ( IsEditMode( CREATING_AREA ) ) + { + SetEditMode( NORMAL ); + player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); + } + else if ( IsEditMode( CREATING_LADDER ) ) + { + SetEditMode( NORMAL ); + player->EmitSound( "EDIT_BEGIN_AREA.Creating" ); + } + else if ( m_climbableSurface ) + { + player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); + + SetEditMode( CREATING_LADDER ); + + // m_ladderAnchor starting corner + m_ladderAnchor = m_editCursorPos; + m_ladderNormal = m_surfaceNormal; + } + else + { + player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" ); + + SetEditMode( CREATING_AREA ); + + // m_anchor starting corner + m_anchor = m_editCursorPos; + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavEndArea( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !(IsEditMode( CREATING_AREA ) || IsEditMode( CREATING_LADDER ) || IsEditMode( NORMAL )) ) + { + player->EmitSound( "EDIT_END_AREA.NotCreating" ); + return; + } + + if ( IsEditMode( CREATING_AREA ) ) + { + SetEditMode( NORMAL ); + + // create the new nav area + Vector endPos = m_editCursorPos; + endPos.z = m_anchor.z; + + // We're a manually-created area, so let's look around to see what's nearby + CNavArea *nearby = GetMarkedArea(); + if ( !nearby ) + { + nearby = TheNavMesh->GetNearestNavArea( m_editCursorPos + Vector( 0, 0, HalfHumanHeight ), false, 10000.0f, true ); + } + if ( !nearby ) + { + nearby = TheNavMesh->GetNearestNavArea( endPos + Vector( 0, 0, HalfHumanHeight ), false, 10000.0f, true ); + } + if ( !nearby ) + { + nearby = TheNavMesh->GetNearestNavArea( m_editCursorPos ); + } + if ( !nearby ) + { + nearby = TheNavMesh->GetNearestNavArea( endPos ); + } + + CNavArea *newArea = CreateArea(); + if (newArea == NULL) + { + Warning( "NavEndArea: Out of memory\n" ); + player->EmitSound( "EDIT_END_AREA.NotCreating" ); + return; + } + + newArea->Build( m_anchor, endPos ); + + if ( nearby ) + { + newArea->InheritAttributes( nearby ); // inherit from the nearby area + } + + TheNavAreas.AddToTail( newArea ); + TheNavMesh->AddNavArea( newArea ); + player->EmitSound( "EDIT_END_AREA.Creating" ); + + if ( nav_create_place_on_ground.GetBool() ) + { + newArea->PlaceOnGround( NUM_CORNERS ); + } + + // if we have a marked area, inter-connect the two + if (GetMarkedArea()) + { + Extent extent; + GetMarkedArea()->GetExtent( &extent ); + + if (m_anchor.x > extent.hi.x && m_editCursorPos.x > extent.hi.x) + { + GetMarkedArea()->ConnectTo( newArea, EAST ); + newArea->ConnectTo( GetMarkedArea(), WEST ); + } + else if (m_anchor.x < extent.lo.x && m_editCursorPos.x < extent.lo.x) + { + GetMarkedArea()->ConnectTo( newArea, WEST ); + newArea->ConnectTo( GetMarkedArea(), EAST ); + } + else if (m_anchor.y > extent.hi.y && m_editCursorPos.y > extent.hi.y) + { + GetMarkedArea()->ConnectTo( newArea, SOUTH ); + newArea->ConnectTo( GetMarkedArea(), NORTH ); + } + else if (m_anchor.y < extent.lo.y && m_editCursorPos.y < extent.lo.y) + { + GetMarkedArea()->ConnectTo( newArea, NORTH ); + newArea->ConnectTo( GetMarkedArea(), SOUTH ); + } + + // propogate marked area to new area + SetMarkedArea( newArea ); + } + + TheNavMesh->OnEditCreateNotify( newArea ); + } + else if ( IsEditMode( CREATING_LADDER ) ) + { + SetEditMode( NORMAL ); + + player->EmitSound( "EDIT_END_AREA.Creating" ); + + Vector corner1, corner2, corner3; + if ( m_climbableSurface && FindLadderCorners( &corner1, &corner2, &corner3 ) ) + { + // m_ladderAnchor and corner2 are at the same Z, and corner1 & corner3 share Z. + Vector top = (m_ladderAnchor + corner2) * 0.5f; + Vector bottom = (corner1 + corner3) * 0.5f; + if ( top.z < bottom.z ) + { + Vector tmp = top; + top = bottom; + bottom = tmp; + } + + float width = m_ladderAnchor.DistTo( corner2 ); + Vector2D ladderDir = m_surfaceNormal.AsVector2D(); + + CreateLadder( top, bottom, width, ladderDir, HumanHeight ); + } + else + { + player->EmitSound( "EDIT_END_AREA.NotCreating" ); + } + } + else + { + player->EmitSound( "EDIT_END_AREA.NotCreating" ); + } + + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavConnect( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + Vector center; + float halfWidth; + if ( m_selectedSet.Count() > 1 ) + { + bool bValid = true; + for ( int i = 1; i < m_selectedSet.Count(); ++i ) + { + // Make sure all connections are valid + CNavArea *first = m_selectedSet[0]; + CNavArea *second = m_selectedSet[i]; + + NavDirType dir = second->ComputeLargestPortal( first, ¢er, &halfWidth ); + if (dir == NUM_DIRECTIONS) + { + player->EmitSound( "EDIT_CONNECT.AllDirections" ); + bValid = false; + break; + } + + dir = first->ComputeLargestPortal( second, ¢er, &halfWidth ); + if (dir == NUM_DIRECTIONS) + { + player->EmitSound( "EDIT_CONNECT.AllDirections" ); + bValid = false; + break; + } + } + + if ( bValid ) + { + for ( int i = 1; i < m_selectedSet.Count(); ++i ) + { + CNavArea *first = m_selectedSet[0]; + CNavArea *second = m_selectedSet[i]; + + NavDirType dir = second->ComputeLargestPortal( first, ¢er, &halfWidth ); + second->ConnectTo( first, dir ); + + dir = first->ComputeLargestPortal( second, ¢er, &halfWidth ); + first->ConnectTo( second, dir ); + player->EmitSound( "EDIT_CONNECT.Added" ); + } + } + } + else if ( m_selectedArea ) + { + if ( m_markedLadder ) + { + m_markedLadder->ConnectTo( m_selectedArea ); + player->EmitSound( "EDIT_CONNECT.Added" ); + } + else if ( m_markedArea ) + { + NavDirType dir = GetMarkedArea()->ComputeLargestPortal( m_selectedArea, ¢er, &halfWidth ); + if (dir == NUM_DIRECTIONS) + { + player->EmitSound( "EDIT_CONNECT.AllDirections" ); + } + else + { + m_markedArea->ConnectTo( m_selectedArea, dir ); + player->EmitSound( "EDIT_CONNECT.Added" ); + } + } + else + { + if ( m_selectedSet.Count() == 1 ) + { + CNavArea *area = m_selectedSet[0]; + NavDirType dir = area->ComputeLargestPortal( m_selectedArea, ¢er, &halfWidth ); + if (dir == NUM_DIRECTIONS) + { + player->EmitSound( "EDIT_CONNECT.AllDirections" ); + } + else + { + area->ConnectTo( m_selectedArea, dir ); + player->EmitSound( "EDIT_CONNECT.Added" ); + } + } + else + { + 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." ); + player->EmitSound( "EDIT_CONNECT.AllDirections" ); + } + } + } + else if ( m_selectedLadder ) + { + if ( m_markedArea ) + { + m_markedArea->ConnectTo( m_selectedLadder ); + player->EmitSound( "EDIT_CONNECT.Added" ); + } + else + { + 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." ); + player->EmitSound( "EDIT_CONNECT.AllDirections" ); + } + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection + ClearSelectedSet(); +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavDisconnect( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedSet.Count() > 1 ) + { + bool bValid = true; + for ( int i = 1; i < m_selectedSet.Count(); ++i ) + { + // 2 areas are selected, so connect them bi-directionally + CNavArea *first = m_selectedSet[0]; + CNavArea *second = m_selectedSet[i]; + if ( !first->IsConnected( second, NUM_DIRECTIONS ) && !second->IsConnected( first, NUM_DIRECTIONS ) ) + { + player->EmitSound( "EDIT_CONNECT.AllDirections" ); + bValid = false; + break; + } + } + + if ( bValid ) + { + for ( int i = 1; i < m_selectedSet.Count(); ++i ) + { + // 2 areas are selected, so connect them bi-directionally + CNavArea *first = m_selectedSet[0]; + CNavArea *second = m_selectedSet[i]; + first->Disconnect( second ); + second->Disconnect( first ); + } + player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); + } + } + else if ( m_selectedArea ) + { + if ( m_markedArea ) + { + m_markedArea->Disconnect( m_selectedArea ); + m_selectedArea->Disconnect( m_markedArea ); + player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); + } + else if ( m_selectedSet.Count() == 1 ) + { + m_selectedSet[0]->Disconnect( m_selectedArea ); + m_selectedArea->Disconnect( m_selectedSet[0] ); + player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); + } + else + { + if ( m_markedLadder ) + { + m_markedLadder->Disconnect( m_selectedArea ); + m_selectedArea->Disconnect( m_markedLadder ); + player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); + } + else + { + 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." ); + player->EmitSound( "EDIT_DISCONNECT.NoMarkedArea" ); + } + } + } + else if ( m_selectedLadder ) + { + if ( m_markedArea ) + { + m_markedArea->Disconnect( m_selectedLadder ); + m_selectedLadder->Disconnect( m_markedArea ); + player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); + } + if ( m_selectedSet.Count() == 1 ) + { + m_selectedSet[0]->Disconnect( m_selectedLadder ); + m_selectedLadder->Disconnect( m_selectedSet[0] ); + player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); + } + else + { + 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." ); + player->EmitSound( "EDIT_DISCONNECT.NoMarkedArea" ); + } + } + + ClearSelectedSet(); + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +// Disconnect all outgoing one-way connects from each area in the selected set +void CNavMesh::CommandNavDisconnectOutgoingOneWays( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if ( !player ) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + if ( m_selectedSet.Count() == 0 ) + { + FindActiveNavArea(); + + if ( !m_selectedArea ) + { + return; + } + + m_selectedSet.AddToTail( m_selectedArea ); + } + + for ( int i = 0; i < m_selectedSet.Count(); ++i ) + { + CNavArea *area = m_selectedSet[i]; + + CUtlVector< CNavArea * > adjVector; + area->CollectAdjacentAreas( &adjVector ); + + for( int j=0; jIsConnected( area, NUM_DIRECTIONS ) ) + { + // no connect back - this is a one-way connection + area->Disconnect( adj ); + } + } + } + player->EmitSound( "EDIT_DISCONNECT.MarkedArea" ); + + ClearSelectedSet(); + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavSplice( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (GetMarkedArea()) + { + if (m_selectedArea->SpliceEdit( GetMarkedArea() )) + player->EmitSound( "EDIT_SPLICE.MarkedArea" ); + else + player->EmitSound( "EDIT_SPLICE.NoMarkedArea" ); + } + else + { + Msg( "To splice, mark an area, highlight a second area, then invoke the splice command to create an area between them" ); + player->EmitSound( "EDIT_SPLICE.NoMarkedArea" ); + } + } + + SetMarkedArea( NULL ); // unmark the mark area + ClearSelectedSet(); + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Toggle an attribute on given area + */ +void CNavMesh::DoToggleAttribute( CNavArea *area, NavAttributeType attribute ) +{ + area->SetAttributes( area->GetAttributes() ^ attribute ); + + // keep a list of all "transient" nav areas + if ( attribute == NAV_MESH_TRANSIENT ) + { + if ( area->GetAttributes() & NAV_MESH_TRANSIENT ) + { + m_transientAreas.AddToTail( area ); + } + else + { + m_transientAreas.FindAndRemove( area ); + } + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavToggleAttribute( NavAttributeType attribute ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + if ( IsSelectedSetEmpty() ) + { + // the old way + FindActiveNavArea(); + + if ( m_selectedArea ) + { + player->EmitSound( "EDIT.ToggleAttribute" ); + DoToggleAttribute( m_selectedArea, attribute ); + } + } + else + { + // toggle the attribute in all areas in the selected set + player->EmitSound( "EDIT.ToggleAttribute" ); + + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + + DoToggleAttribute( area, attribute ); + } + + Msg( "Changed attribute in %d areas\n", m_selectedSet.Count() ); + + ClearSelectedSet(); + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavTogglePlaceMode( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( IsEditMode( PLACE_PAINTING ) ) + { + SetEditMode( NORMAL ); + } + else + { + SetEditMode( PLACE_PAINTING ); + } + + player->EmitSound( "EDIT_TOGGLE_PLACE_MODE" ); + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavPlaceFloodFill( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( PLACE_PAINTING ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + PlaceFloodFillFunctor pff( m_selectedArea ); + SearchSurroundingAreas( m_selectedArea, m_selectedArea->GetCenter(), pff ); + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavPlaceSet( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( PLACE_PAINTING ) ) + return; + + if ( !IsSelectedSetEmpty() ) + { + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + area->SetPlace( TheNavMesh->GetNavPlace() ); + } + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavPlacePick( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( PLACE_PAINTING ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + player->EmitSound( "EDIT_PLACE_PICK" ); + TheNavMesh->SetNavPlace( m_selectedArea->GetPlace() ); + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavTogglePlacePainting( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( PLACE_PAINTING ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (m_isPlacePainting) + { + m_isPlacePainting = false; + player->EmitSound( "Bot.EditSwitchOff" ); + } + else + { + m_isPlacePainting = true; + + player->EmitSound( "Bot.EditSwitchOn" ); + + // paint the initial area + m_selectedArea->SetPlace( TheNavMesh->GetNavPlace() ); + } + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavMarkUnnamed( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (GetMarkedArea()) + { + player->EmitSound( "EDIT_MARK_UNNAMED.Enable" ); + SetMarkedArea( NULL ); + } + else + { + SetMarkedArea( NULL ); + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + + if ( area->GetPlace() == 0 ) + { + SetMarkedArea( area ); + break; + } + } + if ( !GetMarkedArea() ) + { + player->EmitSound( "EDIT_MARK_UNNAMED.NoMarkedArea" ); + } + else + { + player->EmitSound( "EDIT_MARK_UNNAMED.MarkedArea" ); + + int connected = 0; + connected += GetMarkedArea()->GetAdjacentCount( NORTH ); + connected += GetMarkedArea()->GetAdjacentCount( SOUTH ); + connected += GetMarkedArea()->GetAdjacentCount( EAST ); + connected += GetMarkedArea()->GetAdjacentCount( WEST ); + + int totalUnnamedAreas = 0; + FOR_EACH_VEC( TheNavAreas, it ) + { + CNavArea *area = TheNavAreas[ it ]; + if ( area->GetPlace() == 0 ) + { + ++totalUnnamedAreas; + } + } + + Msg( "Marked Area is connected to %d other Areas - there are %d total unnamed areas\n", connected, totalUnnamedAreas ); + } + } + } + + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavCornerSelect( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (GetMarkedArea()) + { + int corner = (m_markedCorner + 1) % (NUM_CORNERS + 1); + m_markedCorner = (NavCornerType)corner; + player->EmitSound( "EDIT_SELECT_CORNER.MarkedArea" ); + } + else + { + player->EmitSound( "EDIT_SELECT_CORNER.NoMarkedArea" ); + } + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavCornerRaise( const CCommand &args ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + int amount = 1; + if ( args.ArgC() > 1 ) + { + amount = atoi( args[1] ); + } + + if (IsSelectedSetEmpty()) + { + // the old way + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (GetMarkedArea()) + { + GetMarkedArea()->RaiseCorner( m_markedCorner, amount ); + player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); + } + else + { + player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" ); + } + } + } + else + { + // raise all areas in the selected set + player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); + + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + + area->RaiseCorner( NUM_CORNERS, amount, false ); + } + + Msg( "Raised %d areas\n", m_selectedSet.Count() ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavCornerLower( const CCommand &args ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + int amount = -1; + if ( args.ArgC() > 1 ) + { + amount = -atoi( args[1] ); + } + + if (IsSelectedSetEmpty()) + { + // the old way + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if (GetMarkedArea()) + { + GetMarkedArea()->RaiseCorner( m_markedCorner, amount ); + player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); + } + else + { + player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" ); + } + } + } + else + { + // raise all areas in the selected set + player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); + + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + + area->RaiseCorner( NUM_CORNERS, amount, false ); + } + + Msg( "Lowered %d areas\n", m_selectedSet.Count() ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavCornerPlaceOnGround( const CCommand &args ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + float inset = 0.0f; + if ( args.ArgC() == 2 ) + { + inset = atof(args[1]); + } + + if (IsSelectedSetEmpty()) + { + // the old way + FindActiveNavArea(); + + if ( m_selectedArea ) + { + if ( m_markedArea ) + { + m_markedArea->PlaceOnGround( m_markedCorner, inset ); + } + else + { + m_selectedArea->PlaceOnGround( NUM_CORNERS, inset ); + } + player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); + } + else + { + player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" ); + } + } + else + { + // snap all areas in the selected set to the ground + player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); + + FOR_EACH_VEC( m_selectedSet, it ) + { + CNavArea *area = m_selectedSet[ it ]; + + area->PlaceOnGround( NUM_CORNERS, inset ); + } + + Msg( "Placed %d areas on the ground\n", m_selectedSet.Count() ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavWarpToMark( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + CNavArea *targetArea = GetMarkedArea(); + if ( !targetArea && !IsSelectedSetEmpty() ) + { + targetArea = m_selectedSet[0]; + } + + if ( targetArea ) + { + Vector origin = targetArea->GetCenter() + Vector( 0, 0, 0.75f * HumanHeight ); + QAngle angles = player->GetAbsAngles(); + + if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING ) + { + UTIL_SetOrigin( player, origin ); + player->EmitSound( "EDIT_WARP_TO_MARK" ); + } + else + { + player->Teleport( &origin, &angles, &vec3_origin ); + player->EmitSound( "EDIT_WARP_TO_MARK" ); + } + } + else if ( GetMarkedLadder() ) + { + CNavLadder *ladder = GetMarkedLadder(); + + QAngle angles = player->GetAbsAngles(); + Vector origin = (ladder->m_top + ladder->m_bottom)/2; + origin.x += ladder->GetNormal().x * GenerationStepSize; + origin.y += ladder->GetNormal().y * GenerationStepSize; + + if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING ) + { + UTIL_SetOrigin( player, origin ); + player->EmitSound( "EDIT_WARP_TO_MARK" ); + } + else + { + player->Teleport( &origin, &angles, &vec3_origin ); + player->EmitSound( "EDIT_WARP_TO_MARK" ); + } + } + else + { + player->EmitSound( "EDIT_WARP_TO_MARK" ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavLadderFlip( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if (player == NULL) + return; + + if ( !IsEditMode( NORMAL ) ) + return; + + FindActiveNavArea(); + + if ( m_selectedLadder ) + { + CNavArea *area; + + // flip direction + player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" ); + m_selectedLadder->SetDir( OppositeDirection( m_selectedLadder->GetDir() ) ); + + // and reverse ladder's area pointers + area = m_selectedLadder->m_topBehindArea; + m_selectedLadder->m_topBehindArea = m_selectedLadder->m_topForwardArea; + m_selectedLadder->m_topForwardArea = area; + + area = m_selectedLadder->m_topRightArea; + m_selectedLadder->m_topRightArea = m_selectedLadder->m_topLeftArea; + m_selectedLadder->m_topLeftArea = area; + } + + SetMarkedArea( NULL ); // unmark the mark area + m_markedCorner = NUM_CORNERS; // clear the corner selection +} + + +//-------------------------------------------------------------------------------------------------------------- +class RadiusSelect +{ + Vector m_origin; + float m_radiusSquared; + int m_selected; + +public: + RadiusSelect( const Vector &origin, float radius ) + { + m_origin = origin; + m_radiusSquared = radius * radius; + m_selected = 0; + } + + bool operator()( CNavArea *area ) + { + if ( TheNavMesh->IsInSelectedSet( area ) ) + return true; + + Vector close; + area->GetClosestPointOnArea( m_origin, &close ); + if ( close.DistToSqr( m_origin ) < m_radiusSquared ) + { + TheNavMesh->AddToSelectedSet( area ); + ++m_selected; + } + + return true; + } + + int GetNumSelected( void ) const + { + return m_selected; + } +}; + + +//-------------------------------------------------------------------------------------------------------------- +CON_COMMAND_F( nav_select_radius, "Adds all areas in a radius to the selection set", FCVAR_CHEAT ) +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() || engine->IsDedicatedServer() ) + return; + + if ( args.ArgC() < 2 ) + { + Msg( "Needs a radius\n" ); + return; + } + + float radius = atof( args[ 1 ] ); + CBasePlayer *host = UTIL_GetListenServerHost(); + if ( !host ) + return; + + RadiusSelect select( host->GetAbsOrigin(), radius ); + TheNavMesh->ForAllAreas( select ); + + Msg( "%d areas added to selection\n", select.GetNumSelected() ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Add area to the currently selected set + */ +void CNavMesh::AddToSelectedSet( CNavArea *area ) +{ + if ( !area ) + return; + + // make sure area is not already in list + if (m_selectedSet.Find( area ) != m_selectedSet.InvalidIndex()) + return; + + m_selectedSet.AddToTail( area ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Remove area from the currently selected set + */ +void CNavMesh::RemoveFromSelectedSet( CNavArea *area ) +{ + m_selectedSet.FindAndRemove( area ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Add area to the drag selection set + */ +void CNavMesh::AddToDragSelectionSet( CNavArea *area ) +{ + if ( !area ) + return; + + // make sure area is not already in list + if (m_dragSelectionSet.Find( area ) != m_dragSelectionSet.InvalidIndex()) + return; + + m_dragSelectionSet.AddToTail( area ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Remove area from the drag selection set + */ +void CNavMesh::RemoveFromDragSelectionSet( CNavArea *area ) +{ + m_dragSelectionSet.FindAndRemove( area ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Clear the currently selected set to empty + */ +void CNavMesh::ClearDragSelectionSet( void ) +{ + m_dragSelectionSet.RemoveAll(); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Clear the currently selected set to empty + */ +void CNavMesh::ClearSelectedSet( void ) +{ + m_selectedSet.RemoveAll(); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return true if the selected set is empty + */ +bool CNavMesh::IsSelectedSetEmpty( void ) const +{ + return (m_selectedSet.Count() == 0); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return size of the selected set + */ +int CNavMesh::GetSelecteSetSize( void ) const +{ + return m_selectedSet.Count(); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return the selected set + */ +const NavAreaVector &CNavMesh::GetSelectedSet( void ) const +{ + return m_selectedSet; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return true if the given area is in the selected set + */ +bool CNavMesh::IsInSelectedSet( const CNavArea *area ) const +{ + FOR_EACH_VEC( m_selectedSet, it ) + { + const CNavArea *setArea = m_selectedSet[ it ]; + + if (setArea == area) + return true; + } + + return false; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Invoked when given area has just been added to the mesh in edit mode + */ +void CNavMesh::OnEditCreateNotify( CNavArea *newArea ) +{ + FOR_EACH_VEC( TheNavAreas, it ) + { + TheNavAreas[ it ]->OnEditCreateNotify( newArea ); + } +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Invoked when given area has just been deleted from the mesh in edit mode + */ +void CNavMesh::OnEditDestroyNotify( CNavArea *deadArea ) +{ + // clean up any edit hooks + m_markedArea = NULL; + m_selectedArea = NULL; + m_lastSelectedArea = NULL; + m_selectedLadder = NULL; + m_lastSelectedLadder = NULL; + m_markedLadder = NULL; + + m_avoidanceObstacleAreas.FindAndRemove( deadArea ); + m_blockedAreas.FindAndRemove( deadArea ); + + FOR_EACH_VEC( TheNavAreas, it ) + { + TheNavAreas[ it ]->OnEditDestroyNotify( deadArea ); + } + + EditDestroyNotification notification( deadArea ); + ForEachActor( notification ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Invoked when given ladder has just been deleted from the mesh in edit mode + * @TODO: Implement me + */ +void CNavMesh::OnEditDestroyNotify( CNavLadder *deadLadder ) +{ +} + + + + -- cgit v1.2.3