diff options
Diffstat (limited to 'mp/src/game/server/nav_ladder.cpp')
| -rw-r--r-- | mp/src/game/server/nav_ladder.cpp | 654 |
1 files changed, 654 insertions, 0 deletions
diff --git a/mp/src/game/server/nav_ladder.cpp b/mp/src/game/server/nav_ladder.cpp new file mode 100644 index 00000000..4526c20d --- /dev/null +++ b/mp/src/game/server/nav_ladder.cpp @@ -0,0 +1,654 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+// AI Navigation areas
+// Author: Michael S. Booth ([email protected]), January 2003
+
+#include "cbase.h"
+
+#include "nav_mesh.h"
+#include "nav_node.h"
+#include "nav_pathfind.h"
+#include "nav_colors.h"
+#ifdef TERROR
+#include "TerrorShared.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern ConVar nav_area_bgcolor;
+
+unsigned int CNavLadder::m_nextID = 1;
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Shift the nav area
+ */
+void CNavLadder::Shift( const Vector &shift )
+{
+ m_top += shift;
+ m_bottom += shift;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CNavLadder::CompressIDs( void )
+{
+ m_nextID = 1;
+
+ if ( TheNavMesh )
+ {
+ for ( int i=0; i<TheNavMesh->GetLadders().Count(); ++i )
+ {
+ CNavLadder *ladder = TheNavMesh->GetLadders()[i];
+ ladder->m_id = m_nextID++;
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+CNavArea ** CNavLadder::GetConnection( LadderConnectionType dir )
+{
+ switch ( dir )
+ {
+ case LADDER_TOP_FORWARD:
+ return &m_topForwardArea;
+ case LADDER_TOP_LEFT:
+ return &m_topLeftArea;
+ case LADDER_TOP_RIGHT:
+ return &m_topRightArea;
+ case LADDER_TOP_BEHIND:
+ return &m_topBehindArea;
+ case LADDER_BOTTOM:
+ return &m_bottomArea;
+ }
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CNavLadder::OnSplit( CNavArea *original, CNavArea *alpha, CNavArea *beta )
+{
+ for ( int con=0; con<NUM_LADDER_CONNECTIONS; ++con )
+ {
+ CNavArea ** areaConnection = GetConnection( (LadderConnectionType)con );
+
+ if ( areaConnection && *areaConnection == original )
+ {
+ float alphaDistance = alpha->GetDistanceSquaredToPoint( m_top );
+ float betaDistance = beta->GetDistanceSquaredToPoint( m_top );
+
+ if ( alphaDistance < betaDistance )
+ {
+ *areaConnection = alpha;
+ }
+ else
+ {
+ *areaConnection = beta;
+ }
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Connect this ladder to given area
+ */
+void CNavLadder::ConnectTo( CNavArea *area )
+{
+ float center = (m_top.z + m_bottom.z) * 0.5f;
+
+ if (area->GetCenter().z > center)
+ {
+ // connect to top
+ NavDirType dir;
+
+ Vector dirVector = area->GetCenter() - m_top;
+ if ( fabs( dirVector.x ) > fabs( dirVector.y ) )
+ {
+ if ( dirVector.x > 0.0f ) // east
+ {
+ dir = EAST;
+ }
+ else // west
+ {
+ dir = WEST;
+ }
+ }
+ else
+ {
+ if ( dirVector.y > 0.0f ) // south
+ {
+ dir = SOUTH;
+ }
+ else // north
+ {
+ dir = NORTH;
+ }
+ }
+
+ if ( m_dir == dir )
+ {
+ m_topBehindArea = area;
+ }
+ else if ( OppositeDirection( m_dir ) == dir )
+ {
+ m_topForwardArea = area;
+ }
+ else if ( DirectionLeft( m_dir ) == dir )
+ {
+ m_topLeftArea = area;
+ }
+ else
+ {
+ m_topRightArea = area;
+ }
+ }
+ else
+ {
+ // connect to bottom
+ m_bottomArea = area;
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Destructor
+ */
+CNavLadder::~CNavLadder()
+{
+ // tell the other areas we are going away
+ FOR_EACH_VEC( TheNavAreas, it )
+ {
+ CNavArea *area = TheNavAreas[ it ];
+
+ area->OnDestroyNotify( this );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * invoked when given area is going away
+ */
+void CNavLadder::OnDestroyNotify( CNavArea *dead )
+{
+ Disconnect( dead );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Disconnect this ladder from given area
+ */
+void CNavLadder::Disconnect( CNavArea *area )
+{
+ if ( m_topForwardArea == area )
+ {
+ m_topForwardArea = NULL;
+ }
+ else if ( m_topLeftArea == area )
+ {
+ m_topLeftArea = NULL;
+ }
+ else if ( m_topRightArea == area )
+ {
+ m_topRightArea = NULL;
+ }
+ else if ( m_topBehindArea == area )
+ {
+ m_topBehindArea = NULL;
+ }
+ else if ( m_bottomArea == area )
+ {
+ m_bottomArea = NULL;
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * returns true if given area is connected in given direction
+ */
+bool CNavLadder::IsConnected( const CNavArea *area, LadderDirectionType dir ) const
+{
+ if ( dir == LADDER_DOWN )
+ {
+ return area == m_bottomArea;
+ }
+ else if ( dir == LADDER_UP )
+ {
+ return ( area == m_topForwardArea ||
+ area == m_topLeftArea ||
+ area == m_topRightArea ||
+ area == m_topBehindArea );
+ }
+ else
+ {
+ return ( area == m_bottomArea ||
+ area == m_topForwardArea ||
+ area == m_topLeftArea ||
+ area == m_topRightArea ||
+ area == m_topBehindArea );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CNavLadder::SetDir( NavDirType dir )
+{
+ m_dir = dir;
+
+ m_normal.Init();
+ AddDirectionVector( &m_normal, m_dir, 1.0f ); // worst-case, we have the NavDirType as a normal
+
+ Vector from = (m_top + m_bottom) * 0.5f + m_normal * 5.0f;
+ Vector to = from - m_normal * 32.0f;
+
+ trace_t result;
+#ifdef TERROR
+ // TERROR: use the MASK_ZOMBIESOLID_BRUSHONLY contents, since that's what zombies use
+ UTIL_TraceLine( from, to, MASK_ZOMBIESOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
+#else
+ UTIL_TraceLine( from, to, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &result );
+#endif
+
+ if (result.fraction != 1.0f)
+ {
+ bool climbableSurface = physprops->GetSurfaceData( result.surface.surfaceProps )->game.climbable != 0;
+ if ( !climbableSurface )
+ {
+ climbableSurface = (result.contents & CONTENTS_LADDER) != 0;
+ }
+ if ( climbableSurface )
+ {
+ m_normal = result.plane.normal;
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CNavLadder::DrawLadder( void ) const
+{
+ CBasePlayer *player = UTIL_GetListenServerHost();
+ if (player == NULL)
+ return;
+
+ Vector dir;
+ const Vector &eye = player->EyePosition();
+ AngleVectors( player->EyeAngles() + player->GetPunchAngle(), &dir );
+
+ float dx = eye.x - m_bottom.x;
+ float dy = eye.y - m_bottom.y;
+
+ Vector2D eyeDir( dx, dy );
+ eyeDir.NormalizeInPlace();
+ bool isSelected = ( this == TheNavMesh->GetSelectedLadder() );
+ bool isMarked = ( this == TheNavMesh->GetMarkedLadder() );
+ bool isFront = DotProduct2D( eyeDir, GetNormal().AsVector2D() ) > 0;
+
+ if ( TheNavMesh->IsEditMode( CNavMesh::PLACE_PAINTING ) )
+ {
+ isSelected = isMarked = false;
+ isFront = true;
+ }
+
+ // Highlight ladder entity ------------------------------------------------
+ CBaseEntity *ladderEntity = m_ladderEntity.Get();
+ if ( ladderEntity )
+ {
+ ladderEntity->DrawAbsBoxOverlay();
+ }
+
+ // Draw 'ladder' lines ----------------------------------------------------
+ NavEditColor ladderColor = NavNormalColor;
+ if ( isFront )
+ {
+ if ( isMarked )
+ {
+ ladderColor = NavMarkedColor;
+ }
+ else if ( isSelected )
+ {
+ ladderColor = NavSelectedColor;
+ }
+ else
+ {
+ ladderColor = NavSamePlaceColor;
+ }
+ }
+ else if ( isMarked )
+ {
+ ladderColor = NavMarkedColor;
+ }
+ else if ( isSelected )
+ {
+ ladderColor = NavSelectedColor;
+ }
+
+ Vector right(0, 0, 0), up( 0, 0, 0 );
+ VectorVectors( GetNormal(), right, up );
+ if ( up.z <= 0.0f )
+ {
+ AssertMsg( false, "A nav ladder has an invalid normal" );
+ up.Init( 0, 0, 1 );
+ }
+
+ right *= m_width * 0.5f;
+
+ Vector bottomLeft = m_bottom - right;
+ Vector bottomRight = m_bottom + right;
+ Vector topLeft = m_top - right;
+ Vector topRight = m_top + right;
+
+ int bgcolor[4];
+ if ( 4 == sscanf( nav_area_bgcolor.GetString(), "%d %d %d %d", &(bgcolor[0]), &(bgcolor[1]), &(bgcolor[2]), &(bgcolor[3]) ) )
+ {
+ for ( int i=0; i<4; ++i )
+ bgcolor[i] = clamp( bgcolor[i], 0, 255 );
+
+ if ( bgcolor[3] > 0 )
+ {
+ Vector offset( 0, 0, 0 );
+ AddDirectionVector( &offset, OppositeDirection( m_dir ), 1 );
+ NDebugOverlay::Triangle( topLeft+offset, topRight+offset, bottomRight+offset, bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3], true, 0.15f );
+ NDebugOverlay::Triangle( bottomRight+offset, bottomLeft+offset, topLeft+offset, bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3], true, 0.15f );
+ }
+ }
+
+ NavDrawLine( topLeft, bottomLeft, ladderColor );
+ NavDrawLine( topRight, bottomRight, ladderColor );
+
+ while ( bottomRight.z < topRight.z )
+ {
+ NavDrawLine( bottomRight, bottomLeft, ladderColor );
+ bottomRight += up * (GenerationStepSize/2);
+ bottomLeft += up * (GenerationStepSize/2);
+ }
+
+ // Draw connector lines ---------------------------------------------------
+ if ( !TheNavMesh->IsEditMode( CNavMesh::PLACE_PAINTING ) )
+ {
+ Vector bottom = m_bottom;
+ Vector top = m_top;
+
+ NavDrawLine( top, bottom, NavConnectedTwoWaysColor );
+
+ if (m_bottomArea)
+ {
+ float offset = GenerationStepSize;
+ const Vector& areaBottom = m_bottomArea->GetCenter();
+
+ // don't draw the bottom connection too high if the ladder is very short
+ if ( top.z - bottom.z < GenerationStepSize * 1.5f )
+ offset = 0.0f;
+
+ // don't draw the bottom connection too high if the ladder is high above the area
+ if ( bottom.z - areaBottom.z > GenerationStepSize * 1.5f )
+ offset = 0.0f;
+
+ NavDrawLine( bottom + Vector( 0, 0, offset ), areaBottom, ((m_bottomArea->IsConnected( this, LADDER_UP ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
+ }
+
+ if (m_topForwardArea)
+ NavDrawLine( top, m_topForwardArea->GetCenter(), ((m_topForwardArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
+
+ if (m_topLeftArea)
+ NavDrawLine( top, m_topLeftArea->GetCenter(), ((m_topLeftArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
+
+ if (m_topRightArea)
+ NavDrawLine( top, m_topRightArea->GetCenter(), ((m_topRightArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
+
+ if (m_topBehindArea)
+ NavDrawLine( top, m_topBehindArea->GetCenter(), ((m_topBehindArea->IsConnected( this, LADDER_DOWN ))?NavConnectedTwoWaysColor:NavConnectedOneWayColor) );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CNavLadder::DrawConnectedAreas( void )
+{
+ CUtlVector< CNavArea * > areas;
+ if ( m_topForwardArea )
+ areas.AddToTail( m_topForwardArea );
+ if ( m_topLeftArea )
+ areas.AddToTail( m_topLeftArea );
+ if ( m_topRightArea )
+ areas.AddToTail( m_topRightArea );
+ if ( m_topBehindArea )
+ areas.AddToTail( m_topBehindArea );
+ if ( m_bottomArea )
+ areas.AddToTail( m_bottomArea );
+
+ for ( int i=0; i<areas.Count(); ++i )
+ {
+ CNavArea *adj = areas[i];
+
+ adj->Draw();
+
+ if ( !TheNavMesh->IsEditMode( CNavMesh::PLACE_PAINTING ) )
+ {
+ adj->DrawHidingSpots();
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * invoked when a game round restarts
+ */
+void CNavLadder::OnRoundRestart( void )
+{
+ FindLadderEntity();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CNavLadder::FindLadderEntity( void )
+{
+ m_ladderEntity = gEntList.FindEntityByClassnameNearest( "func_simpleladder", (m_top + m_bottom) * 0.5f, HalfHumanWidth );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Save a navigation ladder to the opened binary stream
+ */
+void CNavLadder::Save( CUtlBuffer &fileBuffer, unsigned int version ) const
+{
+ // save ID
+ fileBuffer.PutUnsignedInt( m_id );
+
+ // save extent of ladder
+ fileBuffer.PutFloat( m_width );
+
+ // save top endpoint of ladder
+ fileBuffer.PutFloat( m_top.x );
+ fileBuffer.PutFloat( m_top.y );
+ fileBuffer.PutFloat( m_top.z );
+
+ // save bottom endpoint of ladder
+ fileBuffer.PutFloat( m_bottom.x );
+ fileBuffer.PutFloat( m_bottom.y );
+ fileBuffer.PutFloat( m_bottom.z );
+
+ // save ladder length
+ fileBuffer.PutFloat( m_length );
+
+ // save direction
+ fileBuffer.PutUnsignedInt( m_dir );
+
+ // save IDs of connecting areas
+ unsigned int id;
+ id = ( m_topForwardArea ) ? m_topForwardArea->GetID() : 0;
+ fileBuffer.PutUnsignedInt( id );
+
+ id = ( m_topLeftArea ) ? m_topLeftArea->GetID() : 0;
+ fileBuffer.PutUnsignedInt( id );
+
+ id = ( m_topRightArea ) ? m_topRightArea->GetID() : 0;
+ fileBuffer.PutUnsignedInt( id );
+
+ id = ( m_topBehindArea ) ? m_topBehindArea->GetID() : 0;
+ fileBuffer.PutUnsignedInt( id );
+
+ id = ( m_bottomArea ) ? m_bottomArea->GetID() : 0;
+ fileBuffer.PutUnsignedInt( id );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Load a navigation ladder from the opened binary stream
+ */
+void CNavLadder::Load( CUtlBuffer &fileBuffer, unsigned int version )
+{
+ // load ID
+ m_id = fileBuffer.GetUnsignedInt();
+
+ // update nextID to avoid collisions
+ if (m_id >= m_nextID)
+ m_nextID = m_id+1;
+
+ // load extent of ladder
+ m_width = fileBuffer.GetFloat();
+
+ // load top endpoint of ladder
+ m_top.x = fileBuffer.GetFloat();
+ m_top.y = fileBuffer.GetFloat();
+ m_top.z = fileBuffer.GetFloat();
+
+ // load bottom endpoint of ladder
+ m_bottom.x = fileBuffer.GetFloat();
+ m_bottom.y = fileBuffer.GetFloat();
+ m_bottom.z = fileBuffer.GetFloat();
+
+ // load ladder length
+ m_length = fileBuffer.GetFloat();
+
+ // load direction
+ m_dir = (NavDirType)fileBuffer.GetUnsignedInt();
+ SetDir( m_dir ); // regenerate the surface normal
+
+ // load dangling status
+ if ( version == 6 )
+ {
+ bool m_isDangling;
+ fileBuffer.Get( &m_isDangling, sizeof(m_isDangling) );
+ }
+
+ // load IDs of connecting areas
+ unsigned int id;
+ id = fileBuffer.GetUnsignedInt();
+ m_topForwardArea = TheNavMesh->GetNavAreaByID( id );
+
+ id = fileBuffer.GetUnsignedInt();
+ m_topLeftArea = TheNavMesh->GetNavAreaByID( id );
+
+ id = fileBuffer.GetUnsignedInt();
+ m_topRightArea = TheNavMesh->GetNavAreaByID( id );
+
+ id = fileBuffer.GetUnsignedInt();
+ m_topBehindArea = TheNavMesh->GetNavAreaByID( id );
+
+ id = fileBuffer.GetUnsignedInt();
+ m_bottomArea = TheNavMesh->GetNavAreaByID( id );
+ if ( !m_bottomArea )
+ {
+ DevMsg( "ERROR: Unconnected ladder #%d bottom at ( %g, %g, %g )\n", m_id, m_bottom.x, m_bottom.y, m_bottom.z );
+ DevWarning( "nav_unmark; nav_mark ladder %d; nav_warp_to_mark\n", m_id );
+ }
+ else if (!m_topForwardArea && !m_topLeftArea && !m_topRightArea) // can't include behind area, since it is not used when going up a ladder
+ {
+ DevMsg( "ERROR: Unconnected ladder #%d top at ( %g, %g, %g )\n", m_id, m_top.x, m_top.y, m_top.z );
+ DevWarning( "nav_unmark; nav_mark ladder %d; nav_warp_to_mark\n", m_id );
+ }
+
+ FindLadderEntity();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Functor returns true if ladder is free, or false if someone is on the ladder
+ */
+class IsLadderFreeFunctor
+{
+public:
+ IsLadderFreeFunctor( const CNavLadder *ladder, const CBasePlayer *ignore )
+ {
+ m_ladder = ladder;
+ m_ignore = ignore;
+ }
+
+ bool operator() ( CBasePlayer *player )
+ {
+ if (player == m_ignore)
+ return true;
+
+ if (!player->IsOnLadder())
+ return true;
+
+ // player is on a ladder - is it this one?
+ const Vector &feet = player->GetAbsOrigin();
+
+ if (feet.z > m_ladder->m_top.z + HalfHumanHeight)
+ return true;
+
+ if (feet.z + HumanHeight < m_ladder->m_bottom.z - HalfHumanHeight)
+ return true;
+
+ Vector2D away( m_ladder->m_bottom.x - feet.x, m_ladder->m_bottom.y - feet.y );
+ const float onLadderRange = 50.0f;
+ return away.IsLengthGreaterThan( onLadderRange );
+ }
+
+ const CNavLadder *m_ladder;
+ const CBasePlayer *m_ignore;
+};
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if someone is on this ladder
+ */
+bool CNavLadder::IsInUse( const CBasePlayer *ignore ) const
+{
+ IsLadderFreeFunctor isLadderFree( this, ignore );
+ return !ForEachPlayer( isLadderFree );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+Vector CNavLadder::GetPosAtHeight( float height ) const
+{
+ if ( height < m_bottom.z )
+ {
+ return m_bottom;
+ }
+
+ if ( height > m_top.z )
+ {
+ return m_top;
+ }
+
+ if ( m_top.z == m_bottom.z )
+ {
+ return m_top;
+ }
+
+ float percent = ( height - m_bottom.z ) / ( m_top.z - m_bottom.z );
+
+ return m_top * percent + m_bottom * ( 1.0f - percent );
+}
+
+//--------------------------------------------------------------------------------------------------------------
|