diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/nav_mesh/tf_nav_area.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/tf/nav_mesh/tf_nav_area.cpp')
| -rw-r--r-- | game/server/tf/nav_mesh/tf_nav_area.cpp | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/game/server/tf/nav_mesh/tf_nav_area.cpp b/game/server/tf/nav_mesh/tf_nav_area.cpp new file mode 100644 index 0000000..2ca96a2 --- /dev/null +++ b/game/server/tf/nav_mesh/tf_nav_area.cpp @@ -0,0 +1,537 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_nav_area.h +// TF specific nav area +// Michael Booth, February 2009 + +#include "cbase.h" +#include "tf_nav_mesh.h" +#include "tf_nav_area.h" +#include "tf_gamerules.h" +#include "bot/tf_bot.h" +#include "nav_pathfind.h" + +ConVar tf_nav_show_incursion_distance( "tf_nav_show_incursion_distance", "0", FCVAR_CHEAT, "Display travel distances from current spawn room (1=red, 2=blue)" ); +ConVar tf_nav_show_bomb_target_distance( "tf_nav_show_bomb_target_distance", "0", FCVAR_CHEAT, "Display travel distances to bomb target (MvM mode)" ); +ConVar tf_nav_show_turf_ownership( "tf_nav_show_turf_ownership", "0", FCVAR_CHEAT, "Color nav area by smallest incursion distance" ); + +ConVar tf_nav_in_combat_duration( "tf_nav_in_combat_duration", "30", FCVAR_CHEAT, "How long after gunfire occurs is this area still considered to be 'in combat'" ); + +ConVar tf_nav_combat_build_rate( "tf_nav_combat_build_rate", "0.05", FCVAR_CHEAT, "Gunfire/second increase (combat caps at 1.0)" ); +ConVar tf_nav_combat_decay_rate( "tf_nav_combat_decay_rate", "0.022", FCVAR_CHEAT, "Decay/second toward zero" ); + +ConVar tf_show_sniper_areas( "tf_show_sniper_areas", "0", FCVAR_CHEAT ); +ConVar tf_show_sniper_areas_safety_range( "tf_show_sniper_areas_safety_range", "1000", FCVAR_CHEAT ); + +ConVar tf_show_incursion_range( "tf_show_incursion_range", "0", FCVAR_CHEAT, "1 = red, 2 = blue" ); +ConVar tf_show_incursion_range_min( "tf_show_incursion_range_min", "0", FCVAR_CHEAT, "Highlight areas with incursion distances between min and max cvar values" ); +ConVar tf_show_incursion_range_max( "tf_show_incursion_range_max", "0", FCVAR_CHEAT, "Highlight areas with incursion distances between min and max cvar values" ); + + +//------------------------------------------------------------------------------------------------ +CTFNavArea::CTFNavArea( void ) +{ + m_attributeFlags = 0; + m_wanderCount = 0; + m_combatIntensity = 0.0f; + m_distanceToBombTarget = 0.0f; + m_TFMark = 0; + m_invasionSearchMarker = (unsigned int)-1; +} + + +//------------------------------------------------------------------------------------------------ +/** + * (EXTEND) invoked when map is initially loaded + */ +void CTFNavArea::OnServerActivate( void ) +{ + BaseClass::OnServerActivate(); + + ClearAllPotentiallyVisibleActors(); +} + +//------------------------------------------------------------------------------------------------ +/** + * (EXTEND) invoked for each area when the round restarts + */ +void CTFNavArea::OnRoundRestart( void ) +{ + BaseClass::OnRoundRestart(); + + ClearAllPotentiallyVisibleActors(); + + m_combatIntensity = 0.0f; +} + +//------------------------------------------------------------------------------------------------ +/** + * For game-specific analysis + */ +void CTFNavArea::CustomAnalysis( bool isIncremental ) +{ + +} + + +//------------------------------------------------------------------------------------------------ +/** + * Draw area for debugging & editing + */ +void CTFNavArea::Draw( void ) const +{ + CNavArea::Draw(); + +#ifdef TF_RAID_MODE + if ( TFGameRules()->IsRaidMode() && m_wanderCount > 0 ) + { + NDebugOverlay::Text( GetCenter(), UTIL_VarArgs( "%d", m_wanderCount ), false, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } +#endif // TF_RAID_MODE + + if ( tf_nav_show_incursion_distance.GetBool() ) + { + NDebugOverlay::Text( GetCenter(), UTIL_VarArgs( "R:%3.1f B:%3.1f", GetIncursionDistance( TF_TEAM_RED ), GetIncursionDistance( TF_TEAM_BLUE ) ), false, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + + if ( tf_nav_show_bomb_target_distance.GetBool() ) + { + NDebugOverlay::Text( GetCenter(), UTIL_VarArgs( "%3.1f", GetTravelDistanceToBombTarget() ), false, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + + if ( tf_show_sniper_areas.GetBool() ) + { + bool redSniper = IsAwayFromInvasionAreas( TF_TEAM_RED, tf_show_sniper_areas_safety_range.GetFloat() ); + bool blueSniper = IsAwayFromInvasionAreas( TF_TEAM_BLUE, tf_show_sniper_areas_safety_range.GetFloat() ); + + if ( blueSniper ) + { + if ( redSniper ) + { + // both teams like this spot? + DrawFilled( 255, 0, 255, 255, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + else + { + // blue sniper area + DrawFilled( 0, 0, 255, 255, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + } + else if ( redSniper ) + { + // red sniper area + DrawFilled( 255, 0, 0, 255, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + } + + int rangeTeam = tf_show_incursion_range.GetInt(); + if ( rangeTeam > 0 ) + { + rangeTeam += ( TF_TEAM_RED - 1); + + float range = GetIncursionDistance( rangeTeam ); + if ( range >= tf_show_incursion_range_min.GetFloat() && range <= tf_show_incursion_range_max.GetFloat() ) + { + DrawFilled( 0, 255, 0, 255, NDEBUG_PERSIST_TILL_NEXT_SERVER ); + } + } +} + + +//------------------------------------------------------------------------------------------------ +/** + * Return adjacent area with largest increase in incursion distance + */ +CTFNavArea *CTFNavArea::GetNextIncursionArea( int team ) const +{ + CTFNavArea *nextIncursionArea = NULL; + float nextIncursionDistance = GetIncursionDistance( team ); + + for( int dir=0; dir<NUM_DIRECTIONS; ++dir ) + { + const NavConnectVector *adjVector = GetAdjacentAreas( (NavDirType)dir ); + FOR_EACH_VEC( (*adjVector), bit ) + { + CTFNavArea *adjArea = static_cast< CTFNavArea * >( (*adjVector)[ bit ].area ); + + if ( adjArea->GetIncursionDistance( team ) > nextIncursionDistance ) + { + nextIncursionArea = adjArea; + nextIncursionDistance = adjArea->GetIncursionDistance( team ); + } + } + } + + return nextIncursionArea; +} + + +//----------------------------------------------------------------------------- +// Populate 'priorVector' with a collection of adjacent areas that have a lower incursion distance that this area +void CTFNavArea::CollectPriorIncursionAreas( int team, CUtlVector< CTFNavArea * > *priorVector ) +{ + float myIncursionDistance = GetIncursionDistance( team ); + + priorVector->RemoveAll(); + + for( int dir=0; dir<NUM_DIRECTIONS; ++dir ) + { + const NavConnectVector *adjVector = GetAdjacentAreas( (NavDirType)dir ); + FOR_EACH_VEC( (*adjVector), bit ) + { + CTFNavArea *adjArea = static_cast< CTFNavArea * >( (*adjVector)[ bit ].area ); + + if ( adjArea->GetIncursionDistance( team ) < myIncursionDistance ) + { + priorVector->AddToTail( adjArea ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Populate 'priorVector' with a collection of adjacent areas that have a higher incursion distance that this area +void CTFNavArea::CollectNextIncursionAreas( int team, CUtlVector< CTFNavArea * > *priorVector ) +{ + float myIncursionDistance = GetIncursionDistance( team ); + + priorVector->RemoveAll(); + + for( int dir=0; dir<NUM_DIRECTIONS; ++dir ) + { + const NavConnectVector *adjVector = GetAdjacentAreas( (NavDirType)dir ); + FOR_EACH_VEC( (*adjVector), bit ) + { + CTFNavArea *adjArea = static_cast< CTFNavArea * >( (*adjVector)[ bit ].area ); + + if ( adjArea->GetIncursionDistance( team ) > myIncursionDistance ) + { + priorVector->AddToTail( adjArea ); + } + } + } +} + + +//----------------------------------------------------------------------------- +/** + * Return true if this area is at least safetyRange units away from all invasion areas + */ +bool CTFNavArea::IsAwayFromInvasionAreas( int myTeam, float safetyRange ) const +{ + const CUtlVector< CTFNavArea * > &invasionVector = GetEnemyInvasionAreaVector( myTeam ); + FOR_EACH_VEC( invasionVector, vit ) + { + CTFNavArea *invasionArea = invasionVector[ vit ]; + + if ( ( invasionArea->GetCenter() - GetCenter() ).IsLengthLessThan( safetyRange ) ) + { + // too close to incoming enemy route to snipe + return false; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +class MarkVisibleSet +{ +public: + MarkVisibleSet( unsigned int marker ) + { + m_marker = marker; + } + + bool operator() ( CNavArea *baseArea ) + { + CTFNavArea *area = static_cast< CTFNavArea * >( baseArea ); + area->SetInvasionSearchMarker( m_marker ); + return true; + } + + unsigned int m_marker; +}; + + +//----------------------------------------------------------------------------- +class CollectInvasionAreas +{ +public: + CollectInvasionAreas( unsigned int marker, CTFNavArea *homeArea, CUtlVector< CTFNavArea * > *redInvasionAreaVector, CUtlVector< CTFNavArea * > *blueInvasionAreaVector ) + { + m_homeArea = homeArea; + m_visibleMarker = marker; + m_redInvasionAreaVector = redInvasionAreaVector; + m_blueInvasionAreaVector = blueInvasionAreaVector; + } + + void FilterArea( CTFNavArea *area, CTFNavArea *adjArea ) + { + if ( adjArea->IsInvasionSearchMarked( m_visibleMarker ) ) + { + // also in PVS - can't be invasion area + return; + } + + const float behindTolerance = 100.0; + + // adjacent area is not in PVS, test if adjacent area not penetrated as far, if so it is an invasion area + if ( area->GetIncursionDistance( TF_TEAM_BLUE ) > adjArea->GetIncursionDistance( TF_TEAM_BLUE ) ) + { + if ( area->GetIncursionDistance( TF_TEAM_BLUE ) > m_homeArea->GetIncursionDistance( TF_TEAM_BLUE ) + behindTolerance ) + { + // this area is farther "in" than we are - don't search further + return; + } + + m_redInvasionAreaVector->AddToTail( adjArea ); + } + + if ( area->GetIncursionDistance( TF_TEAM_RED ) > adjArea->GetIncursionDistance( TF_TEAM_RED ) ) + { + if ( area->GetIncursionDistance( TF_TEAM_RED ) > m_homeArea->GetIncursionDistance( TF_TEAM_RED ) + behindTolerance ) + { + // this area is farther "in" than we are - don't search further + return; + } + + m_blueInvasionAreaVector->AddToTail( adjArea ); + } + } + + bool operator() ( CNavArea *baseArea ) + { + CTFNavArea *area = static_cast< CTFNavArea * >( baseArea ); + + // explore adjacent floor areas + int dir; + for( dir=0; dir<NUM_DIRECTIONS; ++dir ) + { + int count = area->GetAdjacentCount( (NavDirType)dir ); + for( int i=0; i<count; ++i ) + { + CTFNavArea *adjArea = static_cast< CTFNavArea * >( area->GetAdjacentArea( (NavDirType)dir, i ) ); + + FilterArea( area, adjArea ); + } + } + + // include areas that connect TO this area via a one-way link, since the enemy is coming TO us + for( dir=0; dir<NUM_DIRECTIONS; ++dir ) + { + const NavConnectVector *list = area->GetIncomingConnections( (NavDirType)dir ); + + FOR_EACH_VEC( (*list), it ) + { + NavConnect connect = (*list)[ it ]; + + FilterArea( area, static_cast< CTFNavArea * >( connect.area ) ); + } + } + + return true; + } + + CTFNavArea *m_homeArea; + CUtlVector< CTFNavArea * > *m_redInvasionAreaVector; + CUtlVector< CTFNavArea * > *m_blueInvasionAreaVector; + unsigned int m_visibleMarker; +}; + + +//------------------------------------------------------------------------------------------------ +/** + * Find invasion areas where enemies enter from + */ +void CTFNavArea::ComputeInvasionAreaVectors( void ) +{ + static unsigned int searchMarker = RandomInt( 0, 1024*1024 ); + + for( int i=0; i<TF_TEAM_COUNT; ++i ) + { + m_invasionAreaVector[ i ].RemoveAll(); + } + + ++searchMarker; + + // mark all potentially visible areas for quick testing during the search + MarkVisibleSet marker( searchMarker ); + ForAllCompletelyVisibleAreas( marker ); + + // search boundary of potentially visible area set for area pairs where + // the area in the PVS has a higher incursion distance than an adjacent + // area outside of the PVS - an invasion area + + CollectInvasionAreas collector( searchMarker, this, &m_invasionAreaVector[ TF_TEAM_RED ], &m_invasionAreaVector[ TF_TEAM_BLUE ] ); + ForAllCompletelyVisibleAreas( collector ); +} + + +//------------------------------------------------------------------------------------------------ +bool CTFNavArea::IsBlocked( int teamID, bool ignoreNavBlockers ) const +{ + if ( HasAttributeTF( TF_NAV_UNBLOCKABLE ) ) + return false; + + if ( HasAttributeTF( TF_NAV_BLOCKED ) ) + return true; + + // temporary fix: + if ( teamID == TF_TEAM_RED && HasAttributeTF( TF_NAV_BLUE_ONE_WAY_DOOR ) ) + return true; + + if ( teamID == TF_TEAM_BLUE && HasAttributeTF( TF_NAV_RED_ONE_WAY_DOOR ) ) + return true; + + return CNavArea::IsBlocked( teamID, ignoreNavBlockers ); +} + + +//------------------------------------------------------------------------------------------------ +void CTFNavArea::Save( CUtlBuffer &fileBuffer, unsigned int version ) const +{ + CNavArea::Save( fileBuffer, version ); + + // save attribute flags + unsigned int attributes = m_attributeFlags & TF_NAV_PERSISTENT_ATTRIBUTES; + fileBuffer.PutUnsignedInt( attributes ); +} + + +//------------------------------------------------------------------------------------------------ +NavErrorType CTFNavArea::Load( CUtlBuffer &fileBuffer, unsigned int version, unsigned int subVersion ) +{ + // load base class data + CNavArea::Load( fileBuffer, version, subVersion ); + + if ( subVersion > TheNavMesh->GetSubVersionNumber() ) + { + Warning( "Unknown NavArea sub-version number\n" ); + return NAV_INVALID_FILE; + } + else if ( subVersion <= 1 ) + { + // no data + m_attributeFlags = 0; + return NAV_OK; + } + + m_attributeFlags = fileBuffer.GetUnsignedInt(); + if ( !fileBuffer.IsValid() ) + { + Warning( "Can't read TF-specific attributes\n" ); + return NAV_INVALID_FILE; + } + + return NAV_OK; +} + + +//-------------------------------------------------------------------------------------------------------- +unsigned int CTFNavArea::m_masterTFMark = 1; + + +//-------------------------------------------------------------------------------------------------------- +void CTFNavArea::MakeNewTFMarker( void ) +{ + ++m_masterTFMark; +} + + +//-------------------------------------------------------------------------------------------------------- +void CTFNavArea::ResetTFMarker( void ) +{ + m_masterTFMark = 1; +} + + +//-------------------------------------------------------------------------------------------------------- +bool CTFNavArea::IsTFMarked( void ) const +{ + return ( m_TFMark == m_masterTFMark ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CTFNavArea::TFMark( void ) +{ + m_TFMark = m_masterTFMark; +} + + +//-------------------------------------------------------------------------------------------------------- +bool CTFNavArea::IsValidForWanderingPopulation( void ) const +{ + if ( HasAttributeTF( TF_NAV_BLOCKED | TF_NAV_SPAWN_ROOM_RED | TF_NAV_SPAWN_ROOM_BLUE | TF_NAV_NO_SPAWNING | TF_NAV_RESCUE_CLOSET ) ) + return false; + + return true; +} + + +//-------------------------------------------------------------------------------------------------------- +void CTFNavArea::AddPotentiallyVisibleActor( CBaseCombatCharacter *who ) +{ + if ( who == NULL ) + { + return; + } + + int team = who->GetTeamNumber(); + if ( team < 0 || team >= TF_TEAM_COUNT ) + return; + + CTFBot *bot = ToTFBot( who ); + if ( bot && bot->HasAttribute( CTFBot::IS_NPC ) ) + return; + + if ( m_potentiallyVisibleActor[ team ].Find( who ) == m_potentiallyVisibleActor[ team ].InvalidIndex() ) + { + m_potentiallyVisibleActor[ team ].AddToTail( who ); + } +} + + + +//-------------------------------------------------------------------------------------------------------- +float CTFNavArea::GetCombatIntensity( void ) const +{ + if ( !m_combatTimer.HasStarted() ) + { + return 0.0f; + } + + float actualIntensity = m_combatIntensity - m_combatTimer.GetElapsedTime() * tf_nav_combat_decay_rate.GetFloat(); + + if ( actualIntensity < 0.0f ) + { + actualIntensity = 0.0f; + } + + return actualIntensity; +} + + +//-------------------------------------------------------------------------------------------------------- +// Invoked when combat happens in/near this area +void CTFNavArea::OnCombat( void ) +{ + m_combatIntensity += tf_nav_combat_build_rate.GetFloat(); + if ( m_combatIntensity > 1.0f ) + { + m_combatIntensity = 1.0f; + } + + m_combatTimer.Start(); +} + + +//-------------------------------------------------------------------------------------------------------- +bool CTFNavArea::IsInCombat( void ) const +{ + return GetCombatIntensity() > 0.01f; +} + + |