mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2024-12-23 01:59:43 +08:00
2106 lines
58 KiB
C++
2106 lines
58 KiB
C++
//========= Copyright © 1996-2005, 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"
|
|
|
|
ConVar nav_show_area_info( "nav_show_area_info", "0.5", FCVAR_GAMEDLL, "Duration in seconds to show nav area ID and attributes while editing" );
|
|
ConVar nav_snap_to_grid( "nav_snap_to_grid", "0", FCVAR_GAMEDLL, "Snap to the nav generation grid when creating new nav areas" );
|
|
ConVar nav_create_place_on_ground( "nav_create_place_on_ground", "0", FCVAR_GAMEDLL, "If true, nav areas will be placed flush with the ground when created by hand." );
|
|
|
|
#if DEBUG_NAV_NODES
|
|
extern ConVar nav_show_nodes;
|
|
#endif // DEBUG_NAV_NODES
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void EditNav_Precache(void *pUser)
|
|
{
|
|
CBaseEntity::PrecacheScriptSound( "Bot.EditSwitchOn" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_TOGGLE_PLACE_MODE" );
|
|
CBaseEntity::PrecacheScriptSound( "Bot.EditSwitchOff" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_PLACE_PICK" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_DELETE" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT.ToggleAttribute" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_SPLIT.MarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_SPLIT.NoMarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MERGE.Enable" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MERGE.Disable" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MARK.Enable" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MARK.Disable" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MARK_UNNAMED.Enable" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MARK_UNNAMED.NoMarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MARK_UNNAMED.MarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_CONNECT.AllDirections" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_CONNECT.Added" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_DISCONNECT.MarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_DISCONNECT.NoMarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_SPLICE.MarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_SPLICE.NoMarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_SELECT_CORNER.MarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_SELECT_CORNER.NoMarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MOVE_CORNER.MarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_MOVE_CORNER.NoMarkedArea" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_BEGIN_AREA.Creating" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_BEGIN_AREA.NotCreating" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_END_AREA.Creating" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_END_AREA.NotCreating" );
|
|
CBaseEntity::PrecacheScriptSound( "EDIT_WARP_TO_MARK" );
|
|
}
|
|
|
|
#ifdef CSTRIKE_DLL
|
|
PRECACHE_REGISTER_FN( EditNav_Precache );
|
|
#endif
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
inline float round( float val, float unit )
|
|
{
|
|
val = val + ((val < 0.0f) ? -unit*0.5f : unit*0.5f);
|
|
return (float)( unit * ( ((int)val) / (int)unit ) );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
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 = round( in.x, scale );
|
|
}
|
|
|
|
if ( snapY )
|
|
{
|
|
out.y = round( in.y, scale );
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
float CNavMesh::SnapToGrid( float x, bool forceGrid ) const
|
|
{
|
|
int scale = GetGridSize( forceGrid );
|
|
if ( !scale )
|
|
{
|
|
return x;
|
|
}
|
|
|
|
x = round( x, scale );
|
|
return x;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::GetEditVectors( Vector *pos, Vector *forward )
|
|
{
|
|
if ( !pos || !forward )
|
|
{
|
|
return;
|
|
}
|
|
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if ( !player )
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector dir;
|
|
AngleVectors( player->EyeAngles() + player->GetPunchAngle(), forward );
|
|
|
|
*pos = player->EyePosition();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* Convenience function to find the nav area a player is looking at, for editing commands
|
|
*/
|
|
bool CNavMesh::GetActiveNavArea( void )
|
|
{
|
|
VPROF( "CNavMesh::GetActiveNavArea" );
|
|
|
|
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;
|
|
|
|
const float maxRange = 1000.0f; // 500
|
|
|
|
Vector from, dir;
|
|
GetEditVectors( &from, &dir );
|
|
|
|
Vector to = from + maxRange * dir;
|
|
|
|
trace_t result;
|
|
UTIL_TraceLine( from, to, MASK_PLAYERSOLID_BRUSHONLY, player, COLLISION_GROUP_NONE, &result );
|
|
|
|
if (result.fraction != 1.0f)
|
|
{
|
|
if ( m_navEditMode != NAV_EDIT_CREATE )
|
|
{
|
|
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 ( m_isCreatingLadder )
|
|
{
|
|
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 && !m_isCreatingLadder ) || m_navEditMode != NAV_EDIT_CREATE )
|
|
{
|
|
float closestDistSqr = 200.0f * 200.0f;
|
|
|
|
FOR_EACH_LL( m_ladderList, it )
|
|
{
|
|
CNavLadder *ladder = m_ladderList[ it ];
|
|
|
|
Vector absMin = ladder->m_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
|
|
if ( m_grid )
|
|
{
|
|
Vector start = result.startpos;
|
|
Vector end = result.endpos + 10.0f * dir; // extend a few units into the ground
|
|
Ray_t ray;
|
|
ray.Init( start, end, vec3_origin, vec3_origin );
|
|
|
|
float bestDist = 1.0f;
|
|
|
|
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 );
|
|
|
|
//int navAreasTouched = 0;
|
|
//int gridBucketsTouched = 0;
|
|
for( int y = loY; y <= hiY; ++y )
|
|
{
|
|
for( int x = loX; x <= hiX; ++x )
|
|
{
|
|
NavAreaList &areaGrid = m_grid[ x + y*m_gridSizeX ];
|
|
//++gridBucketsTouched;
|
|
|
|
FOR_EACH_LL( areaGrid, it )
|
|
{
|
|
//++navAreasTouched;
|
|
|
|
CNavArea *area = areaGrid[ it ];
|
|
|
|
Vector nw = area->m_extent.lo;
|
|
Vector se = area->m_extent.hi;
|
|
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 )
|
|
{
|
|
m_selectedArea = area;
|
|
bestDist = dist;
|
|
}
|
|
|
|
dist = IntersectRayWithTriangle( ray, se, sw, nw, false );
|
|
if ( dist > 0 && dist < bestDist )
|
|
{
|
|
m_selectedArea = area;
|
|
bestDist = dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//engine->Con_NPrintf( 20, "%d areas queried in %d grid buckets, instead of %d areas", navAreasTouched, gridBucketsTouched, TheNavAreaList.Count() );
|
|
|
|
// Failing that, get the closest area to the endpoint
|
|
if ( !m_selectedArea )
|
|
{
|
|
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 && !m_isCreatingLadder )
|
|
{
|
|
m_editCursorPos = SnapToGrid( m_editCursorPos );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
bool CheckForClimbableSurface( const Vector &start, const Vector &end )
|
|
{
|
|
trace_t result;
|
|
UTIL_TraceLine( start, end, MASK_PLAYERSOLID_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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || !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 );
|
|
|
|
Vector startPos = m_editCursorPos;
|
|
|
|
Vector leftEdge = startPos;
|
|
Vector rightEdge = startPos;
|
|
Vector topEdge = startPos;
|
|
Vector bottomEdge = 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 );
|
|
StepAlongClimbableSurface( topEdge, up * StepSize, probe );
|
|
StepAlongClimbableSurface( bottomEdge, up * -StepSize, probe );
|
|
|
|
Vector2D ladderDir = m_surfaceNormal.AsVector2D();
|
|
|
|
trace_t result;
|
|
CNavLadder *ladder = new CNavLadder;
|
|
|
|
// compute top & bottom of ladder
|
|
ladder->m_top = topEdge;
|
|
ladder->m_bottom = bottomEdge;
|
|
ladder->m_width = leftEdge.DistTo( rightEdge );
|
|
if ( fabs( ladderDir.x ) > fabs( ladderDir.y ) )
|
|
{
|
|
if ( ladderDir.x > 0.0f )
|
|
{
|
|
ladder->SetDir( EAST );
|
|
}
|
|
else
|
|
{
|
|
ladder->SetDir( WEST );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ladderDir.y > 0.0f )
|
|
{
|
|
ladder->SetDir( SOUTH );
|
|
}
|
|
else
|
|
{
|
|
ladder->SetDir( NORTH );
|
|
}
|
|
}
|
|
|
|
// adjust top and bottom of ladder to make sure they are reachable
|
|
// (cs_office has a crate right in front of the base of a ladder)
|
|
Vector along = ladder->m_top - ladder->m_bottom;
|
|
float length = along.NormalizeInPlace();
|
|
Vector on, out;
|
|
const float minLadderClearance = 32.0f;
|
|
|
|
// adjust bottom to bypass blockages
|
|
const float inc = 10.0f;
|
|
float t;
|
|
for( t = 0.0f; t <= length; t += inc )
|
|
{
|
|
on = ladder->m_bottom + t * along;
|
|
|
|
out = on + ladder->GetNormal() * minLadderClearance;
|
|
|
|
UTIL_TraceLine( on, out, MASK_PLAYERSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
|
|
|
|
if (result.fraction == 1.0f && !result.startsolid)
|
|
{
|
|
// found viable ladder bottom
|
|
ladder->m_bottom = on;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// adjust top to bypass blockages
|
|
for( t = 0.0f; t <= length; t += inc )
|
|
{
|
|
on = ladder->m_top - t * along;
|
|
|
|
out = on + ladder->GetNormal() * minLadderClearance;
|
|
|
|
UTIL_TraceLine( on, out, MASK_PLAYERSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
|
|
|
|
if (result.fraction == 1.0f && !result.startsolid)
|
|
{
|
|
// found viable ladder top
|
|
ladder->m_top = on;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ladder->m_length = (ladder->m_top - ladder->m_bottom).Length();
|
|
|
|
ladder->SetDir( ladder->GetDir() ); // now that we've adjusted the top and bottom, re-check the normal
|
|
|
|
ladder->m_bottomArea = NULL;
|
|
ladder->m_topForwardArea = NULL;
|
|
ladder->m_topLeftArea = NULL;
|
|
ladder->m_topRightArea = NULL;
|
|
ladder->m_topBehindArea = NULL;
|
|
ladder->ConnectGeneratedLadder();
|
|
|
|
// add ladder to global list
|
|
m_ladderList.AddToTail( ladder );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* 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 )
|
|
{
|
|
/*
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
const Vector &eye = player->EyePosition();
|
|
Vector forward, right, up;
|
|
AngleVectors( player->EyeAngles(), &forward, &right, &up );
|
|
|
|
// darken the world so the edit lines show up clearly
|
|
const float screenHalfSize = 1000.0f;
|
|
const float screenRange = 50.0f;
|
|
Vector upLeft = eye + screenRange * forward + screenHalfSize * (up - right);
|
|
Vector upRight = eye + screenRange * forward + screenHalfSize * (up + right);
|
|
Vector downLeft = eye + screenRange * forward - screenHalfSize * (up + right);
|
|
Vector downRight = eye + screenRange * forward + screenHalfSize * (right - up);
|
|
|
|
const int alpha = 200;
|
|
NDebugOverlay::Triangle( downLeft, upLeft, upRight, 0, 0, 0, alpha, true, 999999.9f );
|
|
NDebugOverlay::Triangle( downLeft, upRight, downRight, 0, 0, 0, alpha, true, 999999.9f );
|
|
*/
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* Called when edit mode has just been disabled
|
|
*/
|
|
void CNavMesh::OnEditModeEnd( void )
|
|
{
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* Draw navigation areas and edit them
|
|
* @todo Clean the whole edit system up - its structure is legacy from peculiarities in GoldSrc.
|
|
*/
|
|
void CNavMesh::DrawEditMode( void )
|
|
{
|
|
VPROF( "CNavMesh::DrawEditMode" );
|
|
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
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
|
|
|
|
// draw approach points for marked area
|
|
if (nav_show_approach_points.GetBool() && GetMarkedArea())
|
|
{
|
|
Vector ap;
|
|
float halfWidth;
|
|
for( int i=0; i<GetMarkedArea()->GetApproachInfoCount(); ++i )
|
|
{
|
|
const CNavArea::ApproachInfo *info = GetMarkedArea()->GetApproachInfo( i );
|
|
|
|
// compute approach point
|
|
if (info->hereToNextHow <= GO_WEST)
|
|
{
|
|
info->here.area->ComputePortal( info->next.area, (NavDirType)info->hereToNextHow, &ap, &halfWidth );
|
|
ap.z = info->next.area->GetZ( ap );
|
|
}
|
|
else
|
|
{
|
|
// use the area's center as an approach point
|
|
ap = info->here.area->GetCenter();
|
|
}
|
|
|
|
NavDrawLine( ap + Vector( 0, 0, 50 ), ap + Vector( 10, 0, 0 ), NavApproachPointColor );
|
|
NavDrawLine( ap + Vector( 0, 0, 50 ), ap + Vector( -10, 0, 0 ), NavApproachPointColor );
|
|
NavDrawLine( ap + Vector( 0, 0, 50 ), ap + Vector( 0, 10, 0 ), NavApproachPointColor );
|
|
NavDrawLine( ap + Vector( 0, 0, 50 ), ap + Vector( 0, -10, 0 ), NavApproachPointColor );
|
|
}
|
|
}
|
|
|
|
Vector from, dir;
|
|
GetEditVectors( &from, &dir );
|
|
|
|
Vector to = from + maxRange * dir;
|
|
|
|
/* IN_PROGRESS:
|
|
if ( m_navEditMode != NAV_EDIT_PLACE && 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; x<GridArraySize; ++x )
|
|
{
|
|
for ( y=0; y<GridArraySize; ++y )
|
|
{
|
|
pos[x][y] = center;
|
|
pos[x][y].x += (x-GridCount) * GridSize;
|
|
pos[x][y].y += (y-GridCount) * GridSize;
|
|
pos[x][y].z += 36.0f;
|
|
|
|
GetGroundHeight( pos[x][y], &pos[x][y].z );
|
|
}
|
|
}
|
|
|
|
for ( x=1; x<GridArraySize; ++x )
|
|
{
|
|
for ( y=1; y<GridArraySize; ++y )
|
|
{
|
|
NavDrawLine( pos[x-1][y-1], pos[x-1][y], NavGridColor );
|
|
NavDrawLine( pos[x-1][y-1], pos[x][y-1], NavGridColor );
|
|
|
|
if ( x == GridArraySize-1 )
|
|
{
|
|
NavDrawLine( pos[x][y-1], pos[x][y], NavGridColor );
|
|
}
|
|
|
|
if ( y == GridArraySize-1 )
|
|
{
|
|
NavDrawLine( pos[x-1][y], pos[x][y], NavGridColor );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
if ( GetActiveNavArea() )
|
|
{
|
|
// draw cursor
|
|
float cursorSize = 10.0f;
|
|
|
|
if ( m_climbableSurface )
|
|
{
|
|
NDebugOverlay::Cross3D( m_editCursorPos, cursorSize, 0, 255, 0, true, 0.1f );
|
|
}
|
|
else
|
|
{
|
|
NavDrawLine( m_editCursorPos + Vector( 0, 0, cursorSize ), m_editCursorPos, NavCursorColor );
|
|
NavDrawLine( m_editCursorPos + Vector( cursorSize, 0, 0 ), m_editCursorPos + Vector( -cursorSize, 0, 0 ), NavCursorColor );
|
|
NavDrawLine( m_editCursorPos + Vector( 0, cursorSize, 0 ), m_editCursorPos + Vector( 0, -cursorSize, 0 ), NavCursorColor );
|
|
}
|
|
|
|
// show drag rectangle when creating areas and ladders
|
|
if ( m_navEditMode == NAV_EDIT_CREATE )
|
|
{
|
|
float z = m_anchor.z + 2.0f;
|
|
NavDrawLine( Vector( m_editCursorPos.x, m_editCursorPos.y, z ), Vector( m_anchor.x, m_editCursorPos.y, z ), NavCreationColor );
|
|
NavDrawLine( Vector( m_anchor.x, m_anchor.y, z ), Vector( m_anchor.x, m_editCursorPos.y, z ), NavCreationColor );
|
|
NavDrawLine( Vector( m_anchor.x, m_anchor.y, z ), Vector( m_editCursorPos.x, m_anchor.y, z ), NavCreationColor );
|
|
NavDrawLine( Vector( m_editCursorPos.x, m_editCursorPos.y, z ), Vector( m_editCursorPos.x, m_anchor.y, z ), NavCreationColor );
|
|
}
|
|
else if ( m_isCreatingLadder )
|
|
{
|
|
NavDrawLine( m_editCursorPos, Vector( m_editCursorPos.x, m_editCursorPos.y, m_ladderAnchor.z ), NavCreationColor );
|
|
NavDrawLine( Vector( m_editCursorPos.x, m_editCursorPos.y, m_ladderAnchor.z ), m_ladderAnchor, NavCreationColor );
|
|
NavDrawLine( m_ladderAnchor, Vector( m_ladderAnchor.x, m_ladderAnchor.y, m_editCursorPos.z ), NavCreationColor );
|
|
NavDrawLine( Vector( m_ladderAnchor.x, m_ladderAnchor.y, m_editCursorPos.z ), m_editCursorPos, NavCreationColor );
|
|
}
|
|
|
|
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];
|
|
|
|
Q_snprintf( buffer, sizeof( buffer ), "Ladder #%d\n", m_selectedLadder->GetID() );
|
|
NDebugOverlay::ScreenText( 0.5, 0.53, buffer, 255, 255, 0, 128, nav_show_area_info.GetBool() ? 0.1 : 0.5 );
|
|
}
|
|
|
|
// draw the ladder we are pointing at and all connected areas
|
|
m_selectedLadder->DrawLadder();
|
|
m_selectedLadder->DrawConnectedAreas();
|
|
}
|
|
|
|
if ( m_markedLadder && m_navEditMode != NAV_EDIT_PLACE )
|
|
{
|
|
// draw the "marked" ladder
|
|
m_markedLadder->DrawLadder();
|
|
}
|
|
|
|
if ( m_markedArea && m_navEditMode != NAV_EDIT_PLACE )
|
|
{
|
|
// 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)
|
|
strcpy( locName, name );
|
|
else
|
|
strcpy( locName, "ERROR" );
|
|
}
|
|
else
|
|
{
|
|
locName[0] = '\000';
|
|
}
|
|
|
|
if (m_navEditMode == NAV_EDIT_PLACE)
|
|
{
|
|
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 ( m_selectedArea->IsBlocked() ) Q_strncat( attrib, "BLOCKED ", sizeof( attrib ), -1 );
|
|
if ( m_selectedArea->IsUnderwater() ) Q_strncat( attrib, "UNDERWATER ", 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, 0.1 );
|
|
|
|
// do "place painting"
|
|
if (m_isPlacePainting)
|
|
{
|
|
if (m_selectedArea->GetPlace() != TheNavMesh->GetNavPlace())
|
|
{
|
|
m_selectedArea->SetPlace( TheNavMesh->GetNavPlace() );
|
|
player->EmitSound( "Bot.EditSwitchOn" );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_navEditMode == NAV_EDIT_PLACE)
|
|
{
|
|
m_selectedArea->DrawConnectedAreas();
|
|
|
|
}
|
|
else // normal editing mode
|
|
{
|
|
// draw split line
|
|
const Extent &extent = m_selectedArea->GetExtent();
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::SetEditMode( bool isPlaceMode )
|
|
{
|
|
if ( isPlaceMode )
|
|
{
|
|
m_markedLadder = NULL;
|
|
m_markedArea = NULL;
|
|
m_markedCorner = NUM_CORNERS;
|
|
}
|
|
|
|
m_navEditMode = (isPlaceMode)?NAV_EDIT_PLACE:NAV_EDIT_NORMAL;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::SetPlacePaintingMode( bool painting )
|
|
{
|
|
if ( m_navEditMode == NAV_EDIT_PLACE )
|
|
{
|
|
m_markedLadder = NULL;
|
|
m_markedArea = NULL;
|
|
m_markedCorner = NUM_CORNERS;
|
|
|
|
m_isPlacePainting = painting;
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
player->EmitSound( "EDIT_DELETE" );
|
|
TheNavAreaList.FindAndRemove( m_selectedArea );
|
|
delete m_selectedArea;
|
|
}
|
|
else if ( m_selectedLadder )
|
|
{
|
|
player->EmitSound( "EDIT_DELETE" );
|
|
m_ladderList.FindAndRemove( m_selectedLadder );
|
|
delete m_selectedLadder;
|
|
}
|
|
|
|
StripNavigationAreas();
|
|
|
|
SetMarkedArea( NULL ); // unmark the mark area
|
|
m_markedCorner = NUM_CORNERS; // clear the corner selection
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavDeleteMarked( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
CNavArea *markedArea = GetMarkedArea();
|
|
if( markedArea )
|
|
{
|
|
player->EmitSound( "EDIT_DELETE" );
|
|
TheNavAreaList.FindAndRemove( markedArea );
|
|
delete markedArea;
|
|
}
|
|
|
|
StripNavigationAreas();
|
|
|
|
SetMarkedArea( NULL ); // unmark the mark area
|
|
m_markedCorner = NUM_CORNERS; // clear the corner selection
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavSplit( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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 = round( area->GetExtent().lo.x, GenerationStepSize );
|
|
if ( splitEdge < area->GetExtent().lo.x + minSplitSize )
|
|
splitEdge += GenerationStepSize;
|
|
splitAlongX = false;
|
|
}
|
|
else if ( sizeY > GenerationStepSize && sizeY > sizeX )
|
|
{
|
|
splitEdge = round( area->GetExtent().lo.y, GenerationStepSize );
|
|
if ( splitEdge < area->GetExtent().lo.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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
if ( m_markedArea && m_markedArea != m_selectedArea )
|
|
{
|
|
if ( m_selectedArea->MergeEdit( GetMarkedArea() ) )
|
|
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
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavMark( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_markedArea || m_markedLadder )
|
|
{
|
|
// Unmark area or ladder
|
|
player->EmitSound( "EDIT_MARK.Enable" );
|
|
Msg("Area unmarked.\n");
|
|
SetMarkedArea( NULL );
|
|
}
|
|
else if ( engine->Cmd_Argc() > 1 )
|
|
{
|
|
const char *areaIDNameToMark = engine->Cmd_Argv(1);
|
|
if( areaIDNameToMark != NULL )
|
|
{
|
|
unsigned int areaIDToMark = atoi(areaIDNameToMark);
|
|
if( areaIDToMark != 0 )
|
|
{
|
|
CNavArea *areaToMark = NULL;
|
|
FOR_EACH_LL( TheNavAreaList, nit )
|
|
{
|
|
if( TheNavAreaList[nit]->GetID() == areaIDToMark )
|
|
{
|
|
areaToMark = TheNavAreaList[nit];
|
|
break;
|
|
}
|
|
}
|
|
if( areaToMark )
|
|
{
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
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 ( m_navEditMode == NAV_EDIT_PLACE )
|
|
{
|
|
player->EmitSound( "EDIT_END_AREA.NotCreating" );
|
|
return;
|
|
}
|
|
|
|
GetActiveNavArea();
|
|
|
|
if (m_navEditMode == NAV_EDIT_CREATE)
|
|
{
|
|
m_navEditMode = NAV_EDIT_NORMAL;
|
|
player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
|
|
}
|
|
else if ( m_isCreatingLadder )
|
|
{
|
|
m_isCreatingLadder = false;
|
|
player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
|
|
}
|
|
else if ( m_climbableSurface )
|
|
{
|
|
player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
|
|
m_isCreatingLadder = true;
|
|
|
|
// m_ladderAnchor starting corner
|
|
m_ladderAnchor = m_editCursorPos;
|
|
m_ladderNormal = m_surfaceNormal;
|
|
}
|
|
else
|
|
{
|
|
player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
|
|
m_navEditMode = NAV_EDIT_CREATE;
|
|
|
|
// 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 ( m_navEditMode == NAV_EDIT_PLACE )
|
|
{
|
|
player->EmitSound( "EDIT_END_AREA.NotCreating" );
|
|
return;
|
|
}
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE )
|
|
{
|
|
// create the new nav area
|
|
Vector endPos = m_editCursorPos;
|
|
endPos.z = m_anchor.z;
|
|
CNavArea *newArea = new CNavArea( m_anchor, endPos );
|
|
TheNavAreaList.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())
|
|
{
|
|
const Extent &extent = GetMarkedArea()->GetExtent();
|
|
|
|
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 );
|
|
}
|
|
|
|
m_navEditMode = NAV_EDIT_NORMAL;
|
|
}
|
|
else if ( m_isCreatingLadder )
|
|
{
|
|
player->EmitSound( "EDIT_END_AREA.Creating" );
|
|
|
|
// the two points defining the ladder are m_ladderAnchor and m_editCursorPos. The normal is m_ladderNormal.
|
|
Vector mins, maxs;
|
|
|
|
mins.x = MIN( m_ladderAnchor.x, m_editCursorPos.x );
|
|
mins.y = MIN( m_ladderAnchor.y, m_editCursorPos.y );
|
|
mins.z = MIN( m_ladderAnchor.z, m_editCursorPos.z );
|
|
|
|
maxs.x = MAX( m_ladderAnchor.x, m_editCursorPos.x );
|
|
maxs.y = MAX( m_ladderAnchor.y, m_editCursorPos.y );
|
|
maxs.z = MAX( m_ladderAnchor.z, m_editCursorPos.z );
|
|
|
|
Vector2D ladderDir = m_ladderNormal.AsVector2D();
|
|
CreateLadder( mins, maxs, &ladderDir );
|
|
|
|
m_isCreatingLadder = false;
|
|
}
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
if ( m_markedLadder )
|
|
{
|
|
m_markedLadder->ConnectTo( m_selectedArea );
|
|
player->EmitSound( "EDIT_CONNECT.Added" );
|
|
}
|
|
else if ( m_markedArea )
|
|
{
|
|
NavDirType dir = GetMarkedArea()->ComputeDirection( &m_editCursorPos );
|
|
if (dir == NUM_DIRECTIONS)
|
|
{
|
|
player->EmitSound( "EDIT_CONNECT.AllDirections" );
|
|
}
|
|
else
|
|
{
|
|
m_markedArea->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
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavDisconnect( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
if ( m_markedArea )
|
|
{
|
|
m_markedArea->Disconnect( m_selectedArea );
|
|
m_selectedArea->Disconnect( m_markedArea );
|
|
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" );
|
|
}
|
|
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" );
|
|
}
|
|
}
|
|
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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
|
|
m_markedCorner = NUM_CORNERS; // clear the corner selection
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavToggleAttribute( NavAttributeType attribute )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
player->EmitSound( "EDIT.ToggleAttribute" );
|
|
m_selectedArea->SetAttributes( m_selectedArea->GetAttributes() ^ attribute );
|
|
|
|
if ( attribute == NAV_MESH_TRANSIENT )
|
|
{
|
|
if ( m_selectedArea->GetAttributes() & NAV_MESH_TRANSIENT )
|
|
{
|
|
m_transientAreas.AddToTail( m_selectedArea );
|
|
}
|
|
else
|
|
{
|
|
m_transientAreas.FindAndRemove( m_selectedArea );
|
|
}
|
|
}
|
|
}
|
|
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder )
|
|
return;
|
|
|
|
player->EmitSound( "EDIT_TOGGLE_PLACE_MODE" );
|
|
m_navEditMode = (m_navEditMode == NAV_EDIT_PLACE) ? NAV_EDIT_NORMAL : NAV_EDIT_PLACE;
|
|
|
|
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 ( m_navEditMode != NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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::CommandNavPlacePick( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode != NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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 ( m_navEditMode != NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
if (GetMarkedArea())
|
|
{
|
|
player->EmitSound( "EDIT_MARK_UNNAMED.Enable" );
|
|
SetMarkedArea( NULL );
|
|
}
|
|
else
|
|
{
|
|
SetMarkedArea( NULL );
|
|
FOR_EACH_LL( TheNavAreaList, it )
|
|
{
|
|
CNavArea *area = TheNavAreaList[ 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_LL( TheNavAreaList, it )
|
|
{
|
|
CNavArea *area = TheNavAreaList[ 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::CommandNavPickArea( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
if ( m_markedLadder )
|
|
{
|
|
// Unmark ladder
|
|
player->EmitSound( "EDIT_MARK.Enable" );
|
|
Msg("Ladder unmarked.\n");
|
|
SetMarkedArea( NULL );
|
|
m_markedCorner = NUM_CORNERS; // clear the corner selection
|
|
return;
|
|
}
|
|
else if ( m_selectedArea )
|
|
{
|
|
NavCornerType bestCorner = m_selectedArea->GetCornerUnderCursor();
|
|
|
|
if ( m_markedCorner == bestCorner && m_selectedArea == m_markedArea )
|
|
{
|
|
// Unmark area
|
|
player->EmitSound( "EDIT_MARK.Enable" );
|
|
Msg("Area unmarked.\n");
|
|
SetMarkedArea( NULL );
|
|
m_markedCorner = NUM_CORNERS; // clear the corner selection
|
|
return;
|
|
}
|
|
|
|
|
|
// Mark an area
|
|
SetMarkedArea( m_selectedArea );
|
|
m_markedCorner = bestCorner;
|
|
|
|
int connected = 0;
|
|
connected += GetMarkedArea()->GetAdjacentCount( NORTH );
|
|
connected += GetMarkedArea()->GetAdjacentCount( SOUTH );
|
|
connected += GetMarkedArea()->GetAdjacentCount( EAST );
|
|
connected += GetMarkedArea()->GetAdjacentCount( WEST );
|
|
|
|
player->EmitSound( "EDIT_MARK.Disable" );
|
|
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::CommandNavResizeHorizontal( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( !m_markedArea )
|
|
{
|
|
CommandNavPickArea(); // try to grab an area first
|
|
}
|
|
|
|
if ( !m_markedArea )
|
|
{
|
|
player->EmitSound( "EDIT_END_AREA.Creating" );
|
|
return;
|
|
}
|
|
|
|
if ( m_navEditMode == NAV_EDIT_RESIZE_HORIZONTAL )
|
|
{
|
|
m_navEditMode = NAV_EDIT_NORMAL;
|
|
player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
|
|
return;
|
|
}
|
|
|
|
if ( m_navEditMode != NAV_EDIT_NORMAL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_navEditMode = NAV_EDIT_RESIZE_HORIZONTAL;
|
|
player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
|
|
m_anchor = m_editCursorPos;
|
|
if ( nav_snap_to_grid.GetBool() )
|
|
{
|
|
m_anchor = SnapToGrid( m_anchor );
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavResizeVertical( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( !m_markedArea )
|
|
{
|
|
player->EmitSound( "EDIT_END_AREA.Creating" );
|
|
return;
|
|
}
|
|
|
|
if ( m_navEditMode == NAV_EDIT_RESIZE_VERTICAL )
|
|
{
|
|
m_navEditMode = NAV_EDIT_NORMAL;
|
|
player->EmitSound( "EDIT_BEGIN_AREA.Creating" );
|
|
return;
|
|
}
|
|
|
|
if ( m_navEditMode != NAV_EDIT_NORMAL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_navEditMode = NAV_EDIT_RESIZE_VERTICAL;
|
|
player->EmitSound( "EDIT_BEGIN_AREA.NotCreating" );
|
|
m_anchor = m_editCursorPos;
|
|
if ( nav_snap_to_grid.GetBool() )
|
|
{
|
|
m_anchor = SnapToGrid( m_anchor );
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavResizeEnd( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode != NAV_EDIT_RESIZE_HORIZONTAL && m_navEditMode != NAV_EDIT_RESIZE_VERTICAL )
|
|
{
|
|
player->EmitSound( "EDIT_END_AREA.Creating" );
|
|
return;
|
|
}
|
|
|
|
if ( !m_markedArea )
|
|
{
|
|
player->EmitSound( "EDIT_END_AREA.Creating" );
|
|
m_navEditMode = NAV_EDIT_NORMAL;
|
|
return;
|
|
}
|
|
|
|
Vector cornerPos = ( m_markedCorner == NUM_CORNERS ) ? m_markedArea->GetCenter() : m_markedArea->GetCorner( m_markedCorner );
|
|
|
|
if ( m_navEditMode == NAV_EDIT_RESIZE_HORIZONTAL )
|
|
{
|
|
// find the new pos
|
|
Vector from, dir;
|
|
GetEditVectors( &from, &dir );
|
|
|
|
const float dist = 10000.0f;
|
|
float t = IntersectRayWithAAPlane( from, from + dir * dist, 2, 1.0f, cornerPos.z );
|
|
|
|
if ( t > 0 && t < 1 )
|
|
{
|
|
Vector newPos = from + dir * dist * t;
|
|
if ( nav_snap_to_grid.GetBool() )
|
|
{
|
|
newPos = SnapToGrid( newPos );
|
|
}
|
|
m_markedArea->SetCorner( m_markedCorner, newPos );
|
|
}
|
|
}
|
|
player->EmitSound( "EDIT_END_AREA.NotCreating" );
|
|
m_navEditMode = NAV_EDIT_NORMAL;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavCornerSelect( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
if (GetMarkedArea())
|
|
{
|
|
GetMarkedArea()->RaiseCorner( m_markedCorner, 1 );
|
|
player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
|
|
}
|
|
else
|
|
{
|
|
player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavCornerLower( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
if (GetMarkedArea())
|
|
{
|
|
GetMarkedArea()->RaiseCorner( m_markedCorner, -1 );
|
|
player->EmitSound( "EDIT_MOVE_CORNER.MarkedArea" );
|
|
}
|
|
else
|
|
{
|
|
player->EmitSound( "EDIT_MOVE_CORNER.NoMarkedArea" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavCornerPlaceOnGround( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
if ( m_selectedArea )
|
|
{
|
|
float inset = 0.0f;
|
|
if ( engine->Cmd_Argc() == 2 )
|
|
inset = atof(engine->Cmd_Argv(1));
|
|
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" );
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
void CNavMesh::CommandNavWarpToMark( void )
|
|
{
|
|
CBasePlayer *player = UTIL_GetListenServerHost();
|
|
if (player == NULL)
|
|
return;
|
|
|
|
if ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
if (GetMarkedArea())
|
|
{
|
|
if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING )
|
|
{
|
|
Vector origin = GetMarkedArea()->GetCenter() + Vector( 0, 0, 0.75f * HumanHeight );
|
|
UTIL_SetOrigin( player, origin );
|
|
}
|
|
else
|
|
{
|
|
player->EmitSound( "EDIT_WARP_TO_MARK" );
|
|
}
|
|
}
|
|
else if (GetMarkedLadder())
|
|
{
|
|
CNavLadder *ladder = GetMarkedLadder();
|
|
if ( ( player->IsDead() || player->IsObserver() ) && player->GetObserverMode() == OBS_MODE_ROAMING )
|
|
{
|
|
Vector origin = (ladder->m_top + ladder->m_bottom)/2;
|
|
origin.x += ladder->GetNormal().x * GenerationStepSize;
|
|
origin.y += ladder->GetNormal().y * GenerationStepSize;
|
|
UTIL_SetOrigin( player, origin );
|
|
}
|
|
else
|
|
{
|
|
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 ( m_navEditMode == NAV_EDIT_CREATE || m_isCreatingLadder || m_navEditMode == NAV_EDIT_PLACE )
|
|
return;
|
|
|
|
GetActiveNavArea();
|
|
|
|
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
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|