diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/nav_entities.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/nav_entities.cpp')
| -rw-r--r-- | mp/src/game/server/nav_entities.cpp | 1418 |
1 files changed, 709 insertions, 709 deletions
diff --git a/mp/src/game/server/nav_entities.cpp b/mp/src/game/server/nav_entities.cpp index ef9366cc..83a7dd83 100644 --- a/mp/src/game/server/nav_entities.cpp +++ b/mp/src/game/server/nav_entities.cpp @@ -1,709 +1,709 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-// nav_entities.cpp
-// AI Navigation entities
-// 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"
-#include "fmtstr.h"
-#include "props_shared.h"
-#include "func_breakablesurf.h"
-
-#ifdef TERROR
-#include "func_elevator.h"
-#include "AmbientLight.h"
-#endif
-
-#ifdef TF_DLL
-#include "tf_player.h"
-#include "bot/tf_bot.h"
-#endif
-
-#include "Color.h"
-#include "collisionutils.h"
-#include "functorutils.h"
-#include "team.h"
-#include "nav_entities.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-//--------------------------------------------------------------------------------------------------------
-//--------------------------------------------------------------------------------------------------------
-BEGIN_DATADESC( CFuncNavCost )
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
- DEFINE_KEYFIELD( m_iszTags, FIELD_STRING, "tags" ),
- DEFINE_KEYFIELD( m_team, FIELD_INTEGER, "team" ),
- DEFINE_KEYFIELD( m_isDisabled, FIELD_BOOLEAN, "start_disabled" ),
-
- DEFINE_THINKFUNC( CostThink ),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( func_nav_avoid, CFuncNavAvoid );
-LINK_ENTITY_TO_CLASS( func_nav_prefer, CFuncNavPrefer );
-
-CUtlVector< CHandle< CFuncNavCost > > CFuncNavCost::gm_masterCostVector;
-CountdownTimer CFuncNavCost::gm_dirtyTimer;
-
-#define UPDATE_DIRTY_TIME 0.2f
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavCost::Spawn( void )
-{
- BaseClass::Spawn();
-
- gm_masterCostVector.AddToTail( this );
- gm_dirtyTimer.Start( UPDATE_DIRTY_TIME );
-
- SetSolid( SOLID_BSP );
- AddSolidFlags( FSOLID_NOT_SOLID );
-
- SetMoveType( MOVETYPE_NONE );
- SetModel( STRING( GetModelName() ) );
- AddEffects( EF_NODRAW );
- SetCollisionGroup( COLLISION_GROUP_NONE );
-
- VPhysicsInitShadow( false, false );
-
- SetThink( &CFuncNavCost::CostThink );
- SetNextThink( gpGlobals->curtime + UPDATE_DIRTY_TIME );
-
- m_tags.RemoveAll();
-
- const char *tags = STRING( m_iszTags );
-
- // chop space-delimited string into individual tokens
- if ( tags )
- {
- char *buffer = new char [ strlen( tags ) + 1 ];
- Q_strcpy( buffer, tags );
-
- for( char *token = strtok( buffer, " " ); token; token = strtok( NULL, " " ) )
- {
- m_tags.AddToTail( CFmtStr( "%s", token ) );
- }
-
- delete [] buffer;
- }
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavCost::UpdateOnRemove( void )
-{
- gm_masterCostVector.FindAndFastRemove( this );
- BaseClass::UpdateOnRemove();
-
- gm_dirtyTimer.Start( UPDATE_DIRTY_TIME );
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavCost::InputEnable( inputdata_t &inputdata )
-{
- m_isDisabled = false;
- gm_dirtyTimer.Start( UPDATE_DIRTY_TIME );
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavCost::InputDisable( inputdata_t &inputdata )
-{
- m_isDisabled = true;
- gm_dirtyTimer.Start( UPDATE_DIRTY_TIME );
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavCost::CostThink( void )
-{
- SetNextThink( gpGlobals->curtime + UPDATE_DIRTY_TIME );
-
- if ( gm_dirtyTimer.HasStarted() && gm_dirtyTimer.IsElapsed() )
- {
- // one or more avoid entities have changed - update nav decoration
- gm_dirtyTimer.Invalidate();
-
- UpdateAllNavCostDecoration();
- }
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-bool CFuncNavCost::HasTag( const char *groupname ) const
-{
- for( int i=0; i<m_tags.Count(); ++i )
- {
- if ( FStrEq( m_tags[i], groupname ) )
- {
- return true;
- }
- }
-
- return false;
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-// Return true if this cost applies to the given actor
-bool CFuncNavCost::IsApplicableTo( CBaseCombatCharacter *who ) const
-{
- if ( !who )
- {
- return false;
- }
-
- if ( m_team > 0 )
- {
- if ( who->GetTeamNumber() != m_team )
- {
- return false;
- }
- }
-
-#ifdef TF_DLL
- // TODO: Make group comparison efficient and move to base combat character
- CTFBot *bot = ToTFBot( who );
- if ( bot )
- {
- if ( bot->HasTheFlag() )
- {
- if ( HasTag( "bomb_carrier" ) )
- {
- return true;
- }
-
- // check custom bomb_carrier tags for this bot
- for( int i=0; i<m_tags.Count(); ++i )
- {
- const char* pszTag = m_tags[i];
- if ( V_stristr( pszTag, "bomb_carrier" ) )
- {
- if ( bot->HasTag( pszTag ) )
- {
- return true;
- }
- }
- }
-
- // the bomb carrier only pays attention to bomb_carrier costs
- return false;
- }
-
- if ( bot->HasMission( CTFBot::MISSION_DESTROY_SENTRIES ) )
- {
- if ( HasTag( "mission_sentry_buster" ) )
- {
- return true;
- }
- }
-
- if ( bot->HasMission( CTFBot::MISSION_SNIPER ) )
- {
- if ( HasTag( "mission_sniper" ) )
- {
- return true;
- }
- }
-
- if ( bot->HasMission( CTFBot::MISSION_SPY ) )
- {
- if ( HasTag( "mission_spy" ) )
- {
- return true;
- }
- }
-
- if ( bot->HasMission( CTFBot::MISSION_REPROGRAMMED ) )
- {
- return false;
- }
-
- if ( !bot->IsOnAnyMission() )
- {
- if ( HasTag( "common" ) )
- {
- return true;
- }
- }
-
- if ( HasTag( bot->GetPlayerClass()->GetName() ) )
- {
- return true;
- }
-
- // check custom tags for this bot
- for( int i=0; i<m_tags.Count(); ++i )
- {
- if ( bot->HasTag( m_tags[i] ) )
- {
- return true;
- }
- }
-
- // this cost doesn't apply to me
- return false;
- }
-#endif
-
- return false;
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-// Reevaluate all func_nav_cost entities and update the nav decoration accordingly.
-// This is required to handle overlapping func_nav_cost entities.
-void CFuncNavCost::UpdateAllNavCostDecoration( void )
-{
- int i, j;
-
- // first, clear all avoid decoration from the mesh
- for( i=0; i<TheNavAreas.Count(); ++i )
- {
- TheNavAreas[i]->ClearAllNavCostEntities();
- }
-
- // now, mark all areas with active cost entities overlapping them
- for( i=0; i<gm_masterCostVector.Count(); ++i )
- {
- CFuncNavCost *cost = gm_masterCostVector[i];
-
- if ( !cost || !cost->IsEnabled() )
- {
- continue;
- }
-
- Extent extent;
- extent.Init( cost );
-
- CUtlVector< CNavArea * > overlapVector;
- TheNavMesh->CollectAreasOverlappingExtent( extent, &overlapVector );
-
- Ray_t ray;
- trace_t tr;
- ICollideable *pCollide = cost->CollisionProp();
-
- for( j=0; j<overlapVector.Count(); ++j )
- {
- ray.Init( overlapVector[j]->GetCenter(), overlapVector[j]->GetCenter() );
-
- enginetrace->ClipRayToCollideable( ray, MASK_ALL, pCollide, &tr );
-
- if ( tr.startsolid )
- {
- overlapVector[j]->AddFuncNavCostEntity( cost );
- }
- }
- }
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-//--------------------------------------------------------------------------------------------------------
-// Return pathfind cost multiplier for the given actor
-float CFuncNavAvoid::GetCostMultiplier( CBaseCombatCharacter *who ) const
-{
- if ( IsApplicableTo( who ) )
- {
- return 25.0f;
- }
-
- return 1.0f;
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-//--------------------------------------------------------------------------------------------------------
-// Return pathfind cost multiplier for the given actor
-float CFuncNavPrefer::GetCostMultiplier( CBaseCombatCharacter *who ) const
-{
- if ( IsApplicableTo( who ) )
- {
- return 0.04f; // 1/25th
- }
-
- return 1.0f;
-}
-
-
-
-//--------------------------------------------------------------------------------------------------------
-//--------------------------------------------------------------------------------------------------------
-BEGIN_DATADESC( CFuncNavBlocker )
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "BlockNav", InputBlockNav ),
- DEFINE_INPUTFUNC( FIELD_VOID, "UnblockNav", InputUnblockNav ),
- DEFINE_KEYFIELD( m_blockedTeamNumber, FIELD_INTEGER, "teamToBlock" ),
- DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
-
-END_DATADESC()
-
-
-LINK_ENTITY_TO_CLASS( func_nav_blocker, CFuncNavBlocker );
-
-
-CUtlLinkedList<CFuncNavBlocker *> CFuncNavBlocker::gm_NavBlockers;
-
-//-----------------------------------------------------------------------------------------------------
-int CFuncNavBlocker::DrawDebugTextOverlays( void )
-{
- int offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- CFmtStr str;
-
- // FIRST_GAME_TEAM skips TEAM_SPECTATOR and TEAM_UNASSIGNED, so we can print
- // useful team names in a non-game-specific fashion.
- for ( int i=FIRST_GAME_TEAM; i<FIRST_GAME_TEAM + MAX_NAV_TEAMS; ++i )
- {
- if ( IsBlockingNav( i ) )
- {
- CTeam *team = GetGlobalTeam( i );
- if ( team )
- {
- EntityText( offset++, str.sprintf( "blocking team %s", team->GetName() ), 0 );
- }
- else
- {
- EntityText( offset++, str.sprintf( "blocking team %d", i ), 0 );
- }
- }
- }
-
- NavAreaCollector collector( true );
- Extent extent;
- extent.Init( this );
- TheNavMesh->ForAllAreasOverlappingExtent( collector, extent );
-
- for ( int i=0; i<collector.m_area.Count(); ++i )
- {
- CNavArea *area = collector.m_area[i];
- Extent areaExtent;
- area->GetExtent( &areaExtent );
- debugoverlay->AddBoxOverlay( vec3_origin, areaExtent.lo, areaExtent.hi, vec3_angle, 0, 255, 0, 10, NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
- }
-
- return offset;
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavBlocker::UpdateBlocked()
-{
- NavAreaCollector collector( true );
- Extent extent;
- extent.Init( this );
- TheNavMesh->ForAllAreasOverlappingExtent( collector, extent );
-
- for ( int i=0; i<collector.m_area.Count(); ++i )
- {
- CNavArea *area = collector.m_area[i];
- area->UpdateBlocked( true );
- }
-
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-// Forces nav areas to unblock when the nav blocker is deleted (round restart) so flow can compute properly
-void CFuncNavBlocker::UpdateOnRemove( void )
-{
- UnblockNav();
-
- gm_NavBlockers.FindAndRemove( this );
-
- BaseClass::UpdateOnRemove();
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavBlocker::Spawn( void )
-{
- gm_NavBlockers.AddToTail( this );
-
- if ( !m_blockedTeamNumber )
- m_blockedTeamNumber = TEAM_ANY;
-
- SetMoveType( MOVETYPE_NONE );
- SetModel( STRING( GetModelName() ) );
- AddEffects( EF_NODRAW );
- SetCollisionGroup( COLLISION_GROUP_NONE );
- SetSolid( SOLID_NONE );
- AddSolidFlags( FSOLID_NOT_SOLID );
- CollisionProp()->WorldSpaceAABB( &m_CachedMins, &m_CachedMaxs );
-
- if ( m_bDisabled )
- {
- UnblockNav();
- }
- else
- {
- BlockNav();
- }
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavBlocker::InputBlockNav( inputdata_t &inputdata )
-{
- BlockNav();
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavBlocker::InputUnblockNav( inputdata_t &inputdata )
-{
- UnblockNav();
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavBlocker::BlockNav( void )
-{
- if ( m_blockedTeamNumber == TEAM_ANY )
- {
- for ( int i=0; i<MAX_NAV_TEAMS; ++i )
- {
- m_isBlockingNav[ i ] = true;
- }
- }
- else
- {
- int teamNumber = m_blockedTeamNumber % MAX_NAV_TEAMS;
- m_isBlockingNav[ teamNumber ] = true;
- }
-
- Extent extent;
- extent.Init( this );
- TheNavMesh->ForAllAreasOverlappingExtent( *this, extent );
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavBlocker::UnblockNav( void )
-{
- if ( m_blockedTeamNumber == TEAM_ANY )
- {
- for ( int i=0; i<MAX_NAV_TEAMS; ++i )
- {
- m_isBlockingNav[ i ] = false;
- }
- }
- else
- {
- int teamNumber = m_blockedTeamNumber % MAX_NAV_TEAMS;
- m_isBlockingNav[ teamNumber ] = false;
- }
-
- UpdateBlocked();
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-// functor that blocks areas in our extent
-bool CFuncNavBlocker::operator()( CNavArea *area )
-{
- area->MarkAsBlocked( m_blockedTeamNumber, this );
- return true;
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-bool CFuncNavBlocker::CalculateBlocked( bool *pResultByTeam, const Vector &vecMins, const Vector &vecMaxs )
-{
- int nTeamsBlocked = 0;
- int i;
- bool bBlocked = false;
- for ( i=0; i<MAX_NAV_TEAMS; ++i )
- {
- pResultByTeam[i] = false;
- }
-
- FOR_EACH_LL( gm_NavBlockers, iBlocker )
- {
- CFuncNavBlocker *pBlocker = gm_NavBlockers[iBlocker];
- bool bIsIntersecting = false;
-
- for ( i=0; i<MAX_NAV_TEAMS; ++i )
- {
- if ( pBlocker->m_isBlockingNav[i] )
- {
- if ( !pResultByTeam[i] )
- {
- if ( bIsIntersecting || ( bIsIntersecting = IsBoxIntersectingBox( pBlocker->m_CachedMins, pBlocker->m_CachedMaxs, vecMins, vecMaxs ) ) != false )
- {
- bBlocked = true;
- pResultByTeam[i] = true;
- nTeamsBlocked++;
- }
- else
- {
- continue;
- }
- }
- }
- }
-
- if ( nTeamsBlocked == MAX_NAV_TEAMS )
- {
- break;
- }
- }
- return bBlocked;
-}
-
-
-//-----------------------------------------------------------------------------------------------------
-/**
- * An entity that can obstruct nav areas. This is meant for semi-transient areas that obstruct
- * pathfinding but can be ignored for longer-term queries like computing L4D flow distances and
- * escape routes.
- */
-class CFuncNavObstruction : public CBaseEntity, public INavAvoidanceObstacle
-{
- DECLARE_DATADESC();
- DECLARE_CLASS( CFuncNavObstruction, CBaseEntity );
-
-public:
- void Spawn();
- virtual void UpdateOnRemove( void );
-
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-
- virtual bool IsPotentiallyAbleToObstructNavAreas( void ) const { return true; } // could we at some future time obstruct nav?
- virtual float GetNavObstructionHeight( void ) const { return JumpCrouchHeight; } // height at which to obstruct nav areas
- virtual bool CanObstructNavAreas( void ) const { return !m_bDisabled; } // can we obstruct nav right this instant?
- virtual CBaseEntity *GetObstructingEntity( void ) { return this; }
- virtual void OnNavMeshLoaded( void )
- {
- if ( !m_bDisabled )
- {
- ObstructNavAreas();
- }
- }
-
- int DrawDebugTextOverlays( void );
-
- bool operator()( CNavArea *area ); // functor that obstructs areas in our extent
-
-private:
-
- void ObstructNavAreas( void );
- bool m_bDisabled;
-};
-
-
-
-//--------------------------------------------------------------------------------------------------------
-BEGIN_DATADESC( CFuncNavObstruction )
- DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
-END_DATADESC()
-
-
-LINK_ENTITY_TO_CLASS( func_nav_avoidance_obstacle, CFuncNavObstruction );
-
-
-//-----------------------------------------------------------------------------------------------------
-int CFuncNavObstruction::DrawDebugTextOverlays( void )
-{
- int offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- if ( CanObstructNavAreas() )
- {
- EntityText( offset++, "Obstructing nav", NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
- else
- {
- EntityText( offset++, "Not obstructing nav", NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
- }
-
- return offset;
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavObstruction::UpdateOnRemove( void )
-{
- TheNavMesh->UnregisterAvoidanceObstacle( this );
-
- BaseClass::UpdateOnRemove();
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavObstruction::Spawn( void )
-{
- SetMoveType( MOVETYPE_NONE );
- SetModel( STRING( GetModelName() ) );
- AddEffects( EF_NODRAW );
- SetCollisionGroup( COLLISION_GROUP_NONE );
- SetSolid( SOLID_NONE );
- AddSolidFlags( FSOLID_NOT_SOLID );
-
- if ( !m_bDisabled )
- {
- ObstructNavAreas();
- TheNavMesh->RegisterAvoidanceObstacle( this );
- }
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavObstruction::InputEnable( inputdata_t &inputdata )
-{
- m_bDisabled = false;
- ObstructNavAreas();
- TheNavMesh->RegisterAvoidanceObstacle( this );
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavObstruction::InputDisable( inputdata_t &inputdata )
-{
- m_bDisabled = true;
- TheNavMesh->UnregisterAvoidanceObstacle( this );
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-void CFuncNavObstruction::ObstructNavAreas( void )
-{
- Extent extent;
- extent.Init( this );
- TheNavMesh->ForAllAreasOverlappingExtent( *this, extent );
-}
-
-
-//--------------------------------------------------------------------------------------------------------
-// functor that blocks areas in our extent
-bool CFuncNavObstruction::operator()( CNavArea *area )
-{
- area->MarkObstacleToAvoid( GetNavObstructionHeight() );
- return true;
-}
-
-
-//--------------------------------------------------------------------------------------------------------------
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// nav_entities.cpp +// AI Navigation entities +// 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" +#include "fmtstr.h" +#include "props_shared.h" +#include "func_breakablesurf.h" + +#ifdef TERROR +#include "func_elevator.h" +#include "AmbientLight.h" +#endif + +#ifdef TF_DLL +#include "tf_player.h" +#include "bot/tf_bot.h" +#endif + +#include "Color.h" +#include "collisionutils.h" +#include "functorutils.h" +#include "team.h" +#include "nav_entities.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//-------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +BEGIN_DATADESC( CFuncNavCost ) + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_KEYFIELD( m_iszTags, FIELD_STRING, "tags" ), + DEFINE_KEYFIELD( m_team, FIELD_INTEGER, "team" ), + DEFINE_KEYFIELD( m_isDisabled, FIELD_BOOLEAN, "start_disabled" ), + + DEFINE_THINKFUNC( CostThink ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( func_nav_avoid, CFuncNavAvoid ); +LINK_ENTITY_TO_CLASS( func_nav_prefer, CFuncNavPrefer ); + +CUtlVector< CHandle< CFuncNavCost > > CFuncNavCost::gm_masterCostVector; +CountdownTimer CFuncNavCost::gm_dirtyTimer; + +#define UPDATE_DIRTY_TIME 0.2f + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavCost::Spawn( void ) +{ + BaseClass::Spawn(); + + gm_masterCostVector.AddToTail( this ); + gm_dirtyTimer.Start( UPDATE_DIRTY_TIME ); + + SetSolid( SOLID_BSP ); + AddSolidFlags( FSOLID_NOT_SOLID ); + + SetMoveType( MOVETYPE_NONE ); + SetModel( STRING( GetModelName() ) ); + AddEffects( EF_NODRAW ); + SetCollisionGroup( COLLISION_GROUP_NONE ); + + VPhysicsInitShadow( false, false ); + + SetThink( &CFuncNavCost::CostThink ); + SetNextThink( gpGlobals->curtime + UPDATE_DIRTY_TIME ); + + m_tags.RemoveAll(); + + const char *tags = STRING( m_iszTags ); + + // chop space-delimited string into individual tokens + if ( tags ) + { + char *buffer = new char [ strlen( tags ) + 1 ]; + Q_strcpy( buffer, tags ); + + for( char *token = strtok( buffer, " " ); token; token = strtok( NULL, " " ) ) + { + m_tags.AddToTail( CFmtStr( "%s", token ) ); + } + + delete [] buffer; + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavCost::UpdateOnRemove( void ) +{ + gm_masterCostVector.FindAndFastRemove( this ); + BaseClass::UpdateOnRemove(); + + gm_dirtyTimer.Start( UPDATE_DIRTY_TIME ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavCost::InputEnable( inputdata_t &inputdata ) +{ + m_isDisabled = false; + gm_dirtyTimer.Start( UPDATE_DIRTY_TIME ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavCost::InputDisable( inputdata_t &inputdata ) +{ + m_isDisabled = true; + gm_dirtyTimer.Start( UPDATE_DIRTY_TIME ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavCost::CostThink( void ) +{ + SetNextThink( gpGlobals->curtime + UPDATE_DIRTY_TIME ); + + if ( gm_dirtyTimer.HasStarted() && gm_dirtyTimer.IsElapsed() ) + { + // one or more avoid entities have changed - update nav decoration + gm_dirtyTimer.Invalidate(); + + UpdateAllNavCostDecoration(); + } +} + + +//-------------------------------------------------------------------------------------------------------- +bool CFuncNavCost::HasTag( const char *groupname ) const +{ + for( int i=0; i<m_tags.Count(); ++i ) + { + if ( FStrEq( m_tags[i], groupname ) ) + { + return true; + } + } + + return false; +} + + +//-------------------------------------------------------------------------------------------------------- +// Return true if this cost applies to the given actor +bool CFuncNavCost::IsApplicableTo( CBaseCombatCharacter *who ) const +{ + if ( !who ) + { + return false; + } + + if ( m_team > 0 ) + { + if ( who->GetTeamNumber() != m_team ) + { + return false; + } + } + +#ifdef TF_DLL + // TODO: Make group comparison efficient and move to base combat character + CTFBot *bot = ToTFBot( who ); + if ( bot ) + { + if ( bot->HasTheFlag() ) + { + if ( HasTag( "bomb_carrier" ) ) + { + return true; + } + + // check custom bomb_carrier tags for this bot + for( int i=0; i<m_tags.Count(); ++i ) + { + const char* pszTag = m_tags[i]; + if ( V_stristr( pszTag, "bomb_carrier" ) ) + { + if ( bot->HasTag( pszTag ) ) + { + return true; + } + } + } + + // the bomb carrier only pays attention to bomb_carrier costs + return false; + } + + if ( bot->HasMission( CTFBot::MISSION_DESTROY_SENTRIES ) ) + { + if ( HasTag( "mission_sentry_buster" ) ) + { + return true; + } + } + + if ( bot->HasMission( CTFBot::MISSION_SNIPER ) ) + { + if ( HasTag( "mission_sniper" ) ) + { + return true; + } + } + + if ( bot->HasMission( CTFBot::MISSION_SPY ) ) + { + if ( HasTag( "mission_spy" ) ) + { + return true; + } + } + + if ( bot->HasMission( CTFBot::MISSION_REPROGRAMMED ) ) + { + return false; + } + + if ( !bot->IsOnAnyMission() ) + { + if ( HasTag( "common" ) ) + { + return true; + } + } + + if ( HasTag( bot->GetPlayerClass()->GetName() ) ) + { + return true; + } + + // check custom tags for this bot + for( int i=0; i<m_tags.Count(); ++i ) + { + if ( bot->HasTag( m_tags[i] ) ) + { + return true; + } + } + + // this cost doesn't apply to me + return false; + } +#endif + + return false; +} + + +//-------------------------------------------------------------------------------------------------------- +// Reevaluate all func_nav_cost entities and update the nav decoration accordingly. +// This is required to handle overlapping func_nav_cost entities. +void CFuncNavCost::UpdateAllNavCostDecoration( void ) +{ + int i, j; + + // first, clear all avoid decoration from the mesh + for( i=0; i<TheNavAreas.Count(); ++i ) + { + TheNavAreas[i]->ClearAllNavCostEntities(); + } + + // now, mark all areas with active cost entities overlapping them + for( i=0; i<gm_masterCostVector.Count(); ++i ) + { + CFuncNavCost *cost = gm_masterCostVector[i]; + + if ( !cost || !cost->IsEnabled() ) + { + continue; + } + + Extent extent; + extent.Init( cost ); + + CUtlVector< CNavArea * > overlapVector; + TheNavMesh->CollectAreasOverlappingExtent( extent, &overlapVector ); + + Ray_t ray; + trace_t tr; + ICollideable *pCollide = cost->CollisionProp(); + + for( j=0; j<overlapVector.Count(); ++j ) + { + ray.Init( overlapVector[j]->GetCenter(), overlapVector[j]->GetCenter() ); + + enginetrace->ClipRayToCollideable( ray, MASK_ALL, pCollide, &tr ); + + if ( tr.startsolid ) + { + overlapVector[j]->AddFuncNavCostEntity( cost ); + } + } + } +} + + +//-------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +// Return pathfind cost multiplier for the given actor +float CFuncNavAvoid::GetCostMultiplier( CBaseCombatCharacter *who ) const +{ + if ( IsApplicableTo( who ) ) + { + return 25.0f; + } + + return 1.0f; +} + + +//-------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +// Return pathfind cost multiplier for the given actor +float CFuncNavPrefer::GetCostMultiplier( CBaseCombatCharacter *who ) const +{ + if ( IsApplicableTo( who ) ) + { + return 0.04f; // 1/25th + } + + return 1.0f; +} + + + +//-------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +BEGIN_DATADESC( CFuncNavBlocker ) + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "BlockNav", InputBlockNav ), + DEFINE_INPUTFUNC( FIELD_VOID, "UnblockNav", InputUnblockNav ), + DEFINE_KEYFIELD( m_blockedTeamNumber, FIELD_INTEGER, "teamToBlock" ), + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( func_nav_blocker, CFuncNavBlocker ); + + +CUtlLinkedList<CFuncNavBlocker *> CFuncNavBlocker::gm_NavBlockers; + +//----------------------------------------------------------------------------------------------------- +int CFuncNavBlocker::DrawDebugTextOverlays( void ) +{ + int offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + CFmtStr str; + + // FIRST_GAME_TEAM skips TEAM_SPECTATOR and TEAM_UNASSIGNED, so we can print + // useful team names in a non-game-specific fashion. + for ( int i=FIRST_GAME_TEAM; i<FIRST_GAME_TEAM + MAX_NAV_TEAMS; ++i ) + { + if ( IsBlockingNav( i ) ) + { + CTeam *team = GetGlobalTeam( i ); + if ( team ) + { + EntityText( offset++, str.sprintf( "blocking team %s", team->GetName() ), 0 ); + } + else + { + EntityText( offset++, str.sprintf( "blocking team %d", i ), 0 ); + } + } + } + + NavAreaCollector collector( true ); + Extent extent; + extent.Init( this ); + TheNavMesh->ForAllAreasOverlappingExtent( collector, extent ); + + for ( int i=0; i<collector.m_area.Count(); ++i ) + { + CNavArea *area = collector.m_area[i]; + Extent areaExtent; + area->GetExtent( &areaExtent ); + debugoverlay->AddBoxOverlay( vec3_origin, areaExtent.lo, areaExtent.hi, vec3_angle, 0, 255, 0, 10, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + } + + return offset; +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavBlocker::UpdateBlocked() +{ + NavAreaCollector collector( true ); + Extent extent; + extent.Init( this ); + TheNavMesh->ForAllAreasOverlappingExtent( collector, extent ); + + for ( int i=0; i<collector.m_area.Count(); ++i ) + { + CNavArea *area = collector.m_area[i]; + area->UpdateBlocked( true ); + } + +} + + +//-------------------------------------------------------------------------------------------------------- +// Forces nav areas to unblock when the nav blocker is deleted (round restart) so flow can compute properly +void CFuncNavBlocker::UpdateOnRemove( void ) +{ + UnblockNav(); + + gm_NavBlockers.FindAndRemove( this ); + + BaseClass::UpdateOnRemove(); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavBlocker::Spawn( void ) +{ + gm_NavBlockers.AddToTail( this ); + + if ( !m_blockedTeamNumber ) + m_blockedTeamNumber = TEAM_ANY; + + SetMoveType( MOVETYPE_NONE ); + SetModel( STRING( GetModelName() ) ); + AddEffects( EF_NODRAW ); + SetCollisionGroup( COLLISION_GROUP_NONE ); + SetSolid( SOLID_NONE ); + AddSolidFlags( FSOLID_NOT_SOLID ); + CollisionProp()->WorldSpaceAABB( &m_CachedMins, &m_CachedMaxs ); + + if ( m_bDisabled ) + { + UnblockNav(); + } + else + { + BlockNav(); + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavBlocker::InputBlockNav( inputdata_t &inputdata ) +{ + BlockNav(); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavBlocker::InputUnblockNav( inputdata_t &inputdata ) +{ + UnblockNav(); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavBlocker::BlockNav( void ) +{ + if ( m_blockedTeamNumber == TEAM_ANY ) + { + for ( int i=0; i<MAX_NAV_TEAMS; ++i ) + { + m_isBlockingNav[ i ] = true; + } + } + else + { + int teamNumber = m_blockedTeamNumber % MAX_NAV_TEAMS; + m_isBlockingNav[ teamNumber ] = true; + } + + Extent extent; + extent.Init( this ); + TheNavMesh->ForAllAreasOverlappingExtent( *this, extent ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavBlocker::UnblockNav( void ) +{ + if ( m_blockedTeamNumber == TEAM_ANY ) + { + for ( int i=0; i<MAX_NAV_TEAMS; ++i ) + { + m_isBlockingNav[ i ] = false; + } + } + else + { + int teamNumber = m_blockedTeamNumber % MAX_NAV_TEAMS; + m_isBlockingNav[ teamNumber ] = false; + } + + UpdateBlocked(); +} + + +//-------------------------------------------------------------------------------------------------------- +// functor that blocks areas in our extent +bool CFuncNavBlocker::operator()( CNavArea *area ) +{ + area->MarkAsBlocked( m_blockedTeamNumber, this ); + return true; +} + + +//-------------------------------------------------------------------------------------------------------- +bool CFuncNavBlocker::CalculateBlocked( bool *pResultByTeam, const Vector &vecMins, const Vector &vecMaxs ) +{ + int nTeamsBlocked = 0; + int i; + bool bBlocked = false; + for ( i=0; i<MAX_NAV_TEAMS; ++i ) + { + pResultByTeam[i] = false; + } + + FOR_EACH_LL( gm_NavBlockers, iBlocker ) + { + CFuncNavBlocker *pBlocker = gm_NavBlockers[iBlocker]; + bool bIsIntersecting = false; + + for ( i=0; i<MAX_NAV_TEAMS; ++i ) + { + if ( pBlocker->m_isBlockingNav[i] ) + { + if ( !pResultByTeam[i] ) + { + if ( bIsIntersecting || ( bIsIntersecting = IsBoxIntersectingBox( pBlocker->m_CachedMins, pBlocker->m_CachedMaxs, vecMins, vecMaxs ) ) != false ) + { + bBlocked = true; + pResultByTeam[i] = true; + nTeamsBlocked++; + } + else + { + continue; + } + } + } + } + + if ( nTeamsBlocked == MAX_NAV_TEAMS ) + { + break; + } + } + return bBlocked; +} + + +//----------------------------------------------------------------------------------------------------- +/** + * An entity that can obstruct nav areas. This is meant for semi-transient areas that obstruct + * pathfinding but can be ignored for longer-term queries like computing L4D flow distances and + * escape routes. + */ +class CFuncNavObstruction : public CBaseEntity, public INavAvoidanceObstacle +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CFuncNavObstruction, CBaseEntity ); + +public: + void Spawn(); + virtual void UpdateOnRemove( void ); + + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + virtual bool IsPotentiallyAbleToObstructNavAreas( void ) const { return true; } // could we at some future time obstruct nav? + virtual float GetNavObstructionHeight( void ) const { return JumpCrouchHeight; } // height at which to obstruct nav areas + virtual bool CanObstructNavAreas( void ) const { return !m_bDisabled; } // can we obstruct nav right this instant? + virtual CBaseEntity *GetObstructingEntity( void ) { return this; } + virtual void OnNavMeshLoaded( void ) + { + if ( !m_bDisabled ) + { + ObstructNavAreas(); + } + } + + int DrawDebugTextOverlays( void ); + + bool operator()( CNavArea *area ); // functor that obstructs areas in our extent + +private: + + void ObstructNavAreas( void ); + bool m_bDisabled; +}; + + + +//-------------------------------------------------------------------------------------------------------- +BEGIN_DATADESC( CFuncNavObstruction ) + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( func_nav_avoidance_obstacle, CFuncNavObstruction ); + + +//----------------------------------------------------------------------------------------------------- +int CFuncNavObstruction::DrawDebugTextOverlays( void ) +{ + int offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + if ( CanObstructNavAreas() ) + { + EntityText( offset++, "Obstructing nav", NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + else + { + EntityText( offset++, "Not obstructing nav", NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + } + + return offset; +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavObstruction::UpdateOnRemove( void ) +{ + TheNavMesh->UnregisterAvoidanceObstacle( this ); + + BaseClass::UpdateOnRemove(); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavObstruction::Spawn( void ) +{ + SetMoveType( MOVETYPE_NONE ); + SetModel( STRING( GetModelName() ) ); + AddEffects( EF_NODRAW ); + SetCollisionGroup( COLLISION_GROUP_NONE ); + SetSolid( SOLID_NONE ); + AddSolidFlags( FSOLID_NOT_SOLID ); + + if ( !m_bDisabled ) + { + ObstructNavAreas(); + TheNavMesh->RegisterAvoidanceObstacle( this ); + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavObstruction::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; + ObstructNavAreas(); + TheNavMesh->RegisterAvoidanceObstacle( this ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavObstruction::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; + TheNavMesh->UnregisterAvoidanceObstacle( this ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CFuncNavObstruction::ObstructNavAreas( void ) +{ + Extent extent; + extent.Init( this ); + TheNavMesh->ForAllAreasOverlappingExtent( *this, extent ); +} + + +//-------------------------------------------------------------------------------------------------------- +// functor that blocks areas in our extent +bool CFuncNavObstruction::operator()( CNavArea *area ) +{ + area->MarkObstacleToAvoid( GetNavObstructionHeight() ); + return true; +} + + +//-------------------------------------------------------------------------------------------------------------- |