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

473 lines
12 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// NavMesh.cpp
// Implementation of Navigation Mesh interface
// Author: Michael S. Booth, 2003-2004
#include "cbase.h"
#include "filesystem.h"
#include "cs_nav_mesh.h"
#include "cs_nav_node.h"
#include "cs_nav_area.h"
#include "cs_nav_pathfind.h"
#include "cs_shareddefs.h"
#include "cs_gamerules.h"
#include "fmtstr.h"
#include "utlbuffer.h"
#include "bot_util.h"
#include "tier0/vprof.h"
#include "cs_bot_manager.h"
#ifndef CLIENT_DLL
#include "mapinfo.h"
#endif
class CFuncBlockDMSpawns : public CFuncBrush
{
DECLARE_CLASS( CFuncBlockDMSpawns, CFuncBrush );
void Spawn( void )
{
SetSolid( SOLID_BSP );
AddSolidFlags( FSOLID_NOT_SOLID );
SetModel( STRING( GetModelName() ) ); // set size and link into world
extern ConVar showtriggers;
if ( !showtriggers.GetInt() )
{
AddEffects( EF_NODRAW );
}
}
};
LINK_ENTITY_TO_CLASS( func_block_dm_spawns, CFuncBlockDMSpawns );
extern ConVar mp_randomspawn;
ConVar mp_guardian_target_site( "mp_guardian_target_site", "-1", FCVAR_RELEASE | FCVAR_GAMEDLL, "If set to the index of a bombsite, will cause random spawns to be only created near that site." );
//--------------------------------------------------------------------------------------------------------------
CSNavMesh::CSNavMesh( void )
{
m_desiredChickenCount = 0;
}
//--------------------------------------------------------------------------------------------------------------
CSNavMesh::~CSNavMesh()
{
}
CNavArea * CSNavMesh::CreateArea( void ) const
{
return new CCSNavArea;
}
//-------------------------------------------------------------------------
void CSNavMesh::BeginCustomAnalysis( bool bIncremental )
{
}
//-------------------------------------------------------------------------
// invoked when custom analysis step is complete
void CSNavMesh::PostCustomAnalysis( void )
{
}
//-------------------------------------------------------------------------
void CSNavMesh::EndCustomAnalysis()
{
}
//-------------------------------------------------------------------------
/**
* Returns sub-version number of data format used by derived classes
*/
unsigned int CSNavMesh::GetSubVersionNumber( void ) const
{
// 1: initial implementation - added ApproachArea data
return 1;
}
//-------------------------------------------------------------------------
/**
* Store custom mesh data for derived classes
*/
void CSNavMesh::SaveCustomData( CUtlBuffer &fileBuffer ) const
{
}
//-------------------------------------------------------------------------
/**
* Load custom mesh data for derived classes
*/
void CSNavMesh::LoadCustomData( CUtlBuffer &fileBuffer, unsigned int subVersion )
{
}
//--------------------------------------------------------------------------------------------------------------
/**
* Reset the Navigation Mesh to initial values
*/
void CSNavMesh::Reset( void )
{
m_refreshChickenTimer.Start( 5.0f );
m_refreshDMSpawnTimer.Start( 1.0f );
CNavMesh::Reset();
}
//--------------------------------------------------------------------------------------------------------------
/**
* Zero player counts in all areas
*/
void CSNavMesh::ClearPlayerCounts( void )
{
FOR_EACH_VEC( TheNavAreas, it )
{
CCSNavArea *area = (CCSNavArea*)TheNavAreas[ it ];
area->ClearPlayerCount();
}
}
//--------------------------------------------------------------------------------------------------------------
// Keep desired number of chickens alive in map
void CSNavMesh::MaintainChickenPopulation( void )
{
if ( m_desiredChickenCount > 0 && m_refreshChickenTimer.IsElapsed() )
{
m_refreshChickenTimer.Start( RandomFloat( 10.0f, 20.0f ) );
int actualCount = 0;
for( int i=0; i<m_chickenVector.Count(); ++i )
{
if ( m_chickenVector[i] != NULL )
{
++actualCount;
}
}
if ( actualCount < m_desiredChickenCount )
{
int need = m_desiredChickenCount - actualCount;
for( int k=0; k<need; ++k )
{
// find a good spot to spawn a chicken
CBaseEntity *chicken = NULL;
for( int attempts=0; attempts<10; ++attempts )
{
int which = RandomInt( 0, TheNavAreas.Count()-1 );
CNavArea *testArea = TheNavAreas[ which ];
const float tooSmall = 50.0f;
if ( testArea && testArea->GetSizeX() > tooSmall && testArea->GetSizeY() > tooSmall )
{
if ( !UTIL_IsVisibleToTeam( testArea->GetCenter(), TEAM_CT ) &&
!UTIL_IsVisibleToTeam( testArea->GetCenter(), TEAM_TERRORIST ) )
{
// don't spawn a chicken on top of another chicken
int n;
for( n=0; n<m_chickenVector.Count(); ++n )
{
if ( m_chickenVector[n] == NULL )
continue;
const float tooClose = 50.0f;
Vector between = m_chickenVector[n]->GetAbsOrigin() - testArea->GetCenter();
if ( between.IsLengthLessThan( tooClose ) )
break;
}
if ( n >= m_chickenVector.Count() )
{
// found a good spot - spawn a chicken here
chicken = CreateEntityByName( "chicken" );
if ( chicken )
{
chicken->SetAbsOrigin( testArea->GetCenter() );
DispatchSpawn( chicken );
m_chickenVector.AddToTail( chicken );
}
break;
}
}
}
}
if ( !chicken )
{
// couldn't spawn a chicken - try again later
return;
}
}
}
}
}
//--------------------------------------------------------------------------------------------------------------
void CSNavMesh::Update( void )
{
MaintainChickenPopulation();
MaintainDMSpawnPopulation();
CNavMesh::Update();
}
NavErrorType CSNavMesh::Load( void )
{
return CNavMesh::Load();
}
bool CSNavMesh::Save( void ) const
{
return CNavMesh::Save();
}
NavErrorType CSNavMesh::PostLoad( unsigned int version )
{
if ( CSGameRules()->IsPlayingGunGameDeathmatch() )
m_desiredChickenCount = 10;
else
m_desiredChickenCount = g_pMapInfo ? g_pMapInfo->m_iPetPopulation : 0;
m_chickenVector.RemoveAll();
if ( CSGameRules() && CSGameRules()->IsPlayingCooperativeGametype() )
{
// in Guardian mode, remove all of the mapper placed DM spawn points
CBaseEntity *pEntity = NULL;
while ( ( pEntity = gEntList.FindEntityByClassname( pEntity, "info_deathmatch_spawn" ) ) != NULL )
{
UTIL_Remove( pEntity );
}
}
ResetDMSpawns();
return CNavMesh::PostLoad(version);
}
CON_COMMAND( dm_reset_spawns, "" )
{
( dynamic_cast< CSNavMesh* >( TheNavMesh ) )->ResetDMSpawns();
DevMsg( "Manually resetting deathmatch spawns.\n");
}
void CSNavMesh::ResetDMSpawns( void )
{
FOR_EACH_VEC( m_DMSpawnVector, i )
{
UTIL_Remove( m_DMSpawnVector[i] );
}
m_desiredDMSpawns = 50;
m_consecutiveFailedAttempts = 0;
m_DMSpawnVector.RemoveAll();
MaintainDMSpawnPopulation();
}
void CSNavMesh::MaintainDMSpawnPopulation( void )
{
if ( mp_randomspawn.GetInt() == 0 )
return;
bool bDisableAutoGeneratedDMSpawns = ( g_pMapInfo ? g_pMapInfo->m_bDisableAutoGeneratedDMSpawns : false );
// HACK: Force this to run in guardian, even if the map explicitly placed dm spawns
if ( bDisableAutoGeneratedDMSpawns && !CSGameRules()->IsPlayingCoopGuardian() )
return;
if ( m_desiredDMSpawns > 0 && m_refreshDMSpawnTimer.IsElapsed() )
{
m_refreshDMSpawnTimer.Start( 1.0f );
int actualCount = 0;
for( int i=0; i<m_DMSpawnVector.Count(); ++i )
{
if ( m_DMSpawnVector[i] != NULL )
{
++actualCount;
}
}
// If the map isn't large enough to support as many spawns as m_desiredDMSpawns then shrink m_desiredDMSpawns to whatever we can get.
if ( m_consecutiveFailedAttempts >= 100 )
{
m_desiredDMSpawns = actualCount;
m_consecutiveFailedAttempts = 0;
Warning( "Giving up attempts to make more random spawn points. Current count: %i.\n", actualCount );
}
if ( actualCount < m_desiredDMSpawns )
{
// float calctime = Plat_FloatTime(); MEASURE PERF 1/2
int need = m_desiredDMSpawns - actualCount;
int spawnsToTry = max( 10, need );
for( int k=0; k < spawnsToTry; k++ )
{
// find a good spot to spawn a Deathmatch spawnpoint
CBaseEntity *DMSpawn = NULL;
m_consecutiveFailedAttempts++;
for( int attempts = 0; attempts < 10; attempts++ )
{
CNavArea *testArea = NULL;
if ( mp_guardian_target_site.GetInt() >= 0 )
{
const CCSBotManager::Zone *zone = TheCSBots()->GetZone( mp_guardian_target_site.GetInt() );
if ( zone )
{
testArea = zone->m_area[ RandomInt( 0, zone->m_areaCount - 1 ) ];
}
}
else
{
testArea = TheNavAreas[ RandomInt( 0, TheNavAreas.Count() - 1 ) ];
}
const float tooSmall = ( mp_guardian_target_site.GetInt() >= 0 ) ? 35.0f : 100.0f;
if ( testArea && testArea->GetSizeX() > tooSmall && testArea->GetSizeY() > tooSmall )
{
bool bBadNavArea = false;
// don't spawn a DMSpawn too close to another DMSpawn
for( int n = 0; n < m_DMSpawnVector.Count(); n++ )
{
if ( m_DMSpawnVector[n] == NULL )
continue;
float tooClose = ( mp_guardian_target_site.GetInt() >= 0 ) ? 50.0f : 300.0f;
Vector between = m_DMSpawnVector[n]->GetAbsOrigin() - testArea->GetCenter();
if ( between.IsLengthLessThan( tooClose ) )
{
bBadNavArea = true;
break;
}
}
if ( bBadNavArea )
continue;
if ( IsSpawnBlockedByTrigger( testArea->GetCenter() + Vector( 0, 0, 16 ) ) )
continue;
// check that we can path from the nav area to a ct spawner to confirm it isn't orphaned.
CBaseEntity *CTSpawn = gEntList.FindEntityByClassname( NULL, "info_player_counterterrorist" );
if ( CTSpawn )
{
CNavArea *CTSpawnArea = GetNearestNavArea( CTSpawn->GetAbsOrigin() );
ShortestPathCost cost;
bool bNotOrphaned = NavAreaBuildPath( testArea, CTSpawnArea, NULL, cost );
if ( !bNotOrphaned )
{
const char* szTSpawnEntName = "info_player_terrorist";
if ( CSGameRules()->IsPlayingCoopMission() )
szTSpawnEntName = "info_enemy_terrorist_spawn";
// double check that we can path from the nav area to a t spawner to confirm it isn't orphaned.
CBaseEntity *TSpawn = gEntList.FindEntityByClassname( NULL, szTSpawnEntName );
if ( TSpawn )
{
CNavArea *TSpawnArea = GetNearestNavArea( TSpawn->GetAbsOrigin() );
ShortestPathCost cost2;
bNotOrphaned = NavAreaBuildPath( testArea, TSpawnArea, NULL, cost2 );
if ( !bNotOrphaned )
continue;
}
}
}
// found a good spot - spawn a DMSpawn here
DMSpawn = CreateEntityByName( "info_deathmatch_spawn" );
if ( DMSpawn )
{
DMSpawn->SetAbsOrigin( testArea->GetCenter() + Vector( 0, 0, 16 ));
DispatchSpawn( DMSpawn );
m_DMSpawnVector.AddToTail( DMSpawn );
m_consecutiveFailedAttempts = 0;
// Msg( " %f :CREATED DM SPAWN %i.\n", gpGlobals->curtime, m_DMSpawnVector.Count() );
}
break;
}
}
}
// MEASURE PERF 2/2
// calctime = Plat_FloatTime() - calctime;
// Msg( "DM SPAWN SEARCH CALC TIME: %f\n", calctime );
}
}
}
bool CSNavMesh::IsSpawnBlockedByTrigger( Vector pos )
{
CBaseEntity *list[32];
Ray_t ray;
ray.Init( pos, pos + Vector( 1,1,1 ) );
int nCount = AllEdictsAlongRay( list, 1024, ray, 0 );
for ( int i = 0; i < nCount; i++ )
{
if ( FClassnameIs( list[i], "func_block_dm_spawns" ) )
return true;
}
return false;
}
int CSNavMesh::AllEdictsAlongRay( CBaseEntity **pList, int listMax, const Ray_t &ray, int flagMask )
{
CFlaggedEntitiesEnum rayEnum( pList, listMax, flagMask );
::partition->EnumerateElementsAlongRay( PARTITION_ENGINE_NON_STATIC_EDICTS, ray, false, &rayEnum );
return rayEnum.GetCount();
}