mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-15 13:16:58 +00:00
1
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_nav_area.h
|
||||
// TF specific nav area
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#ifndef TF_NAV_AREA_H
|
||||
#define TF_NAV_AREA_H
|
||||
|
||||
#include "nav_area.h"
|
||||
#include "tf_shareddefs.h"
|
||||
|
||||
enum TFNavAttributeType
|
||||
{
|
||||
TF_NAV_INVALID = 0x00000000,
|
||||
|
||||
// Also look for NAV_MESH_NAV_BLOCKER (w/ nav_debug_blocked ConVar).
|
||||
TF_NAV_BLOCKED = 0x00000001, // blocked for some TF-specific reason
|
||||
TF_NAV_SPAWN_ROOM_RED = 0x00000002,
|
||||
TF_NAV_SPAWN_ROOM_BLUE = 0x00000004,
|
||||
TF_NAV_SPAWN_ROOM_EXIT = 0x00000008,
|
||||
TF_NAV_HAS_AMMO = 0x00000010,
|
||||
TF_NAV_HAS_HEALTH = 0x00000020,
|
||||
TF_NAV_CONTROL_POINT = 0x00000040,
|
||||
|
||||
TF_NAV_BLUE_SENTRY_DANGER = 0x00000080, // sentry can potentially fire upon enemies in this area
|
||||
TF_NAV_RED_SENTRY_DANGER = 0x00000100,
|
||||
|
||||
TF_NAV_BLUE_SETUP_GATE = 0x00000800, // this area is blocked until the setup period is over
|
||||
TF_NAV_RED_SETUP_GATE = 0x00001000, // this area is blocked until the setup period is over
|
||||
TF_NAV_BLOCKED_AFTER_POINT_CAPTURE = 0x00002000, // this area becomes blocked after the first point is capped
|
||||
TF_NAV_BLOCKED_UNTIL_POINT_CAPTURE = 0x00004000, // this area is blocked until the first point is capped, then is unblocked
|
||||
TF_NAV_BLUE_ONE_WAY_DOOR = 0x00008000,
|
||||
TF_NAV_RED_ONE_WAY_DOOR = 0x00010000,
|
||||
|
||||
TF_NAV_WITH_SECOND_POINT = 0x00020000, // modifier for BLOCKED_*_POINT_CAPTURE
|
||||
TF_NAV_WITH_THIRD_POINT = 0x00040000, // modifier for BLOCKED_*_POINT_CAPTURE
|
||||
TF_NAV_WITH_FOURTH_POINT = 0x00080000, // modifier for BLOCKED_*_POINT_CAPTURE
|
||||
TF_NAV_WITH_FIFTH_POINT = 0x00100000, // modifier for BLOCKED_*_POINT_CAPTURE
|
||||
|
||||
TF_NAV_SNIPER_SPOT = 0x00200000, // this is a good place for a sniper to lurk
|
||||
TF_NAV_SENTRY_SPOT = 0x00400000, // this is a good place to build a sentry
|
||||
|
||||
TF_NAV_ESCAPE_ROUTE = 0x00800000, // for Raid mode
|
||||
TF_NAV_ESCAPE_ROUTE_VISIBLE = 0x01000000, // all areas that have visibility to the escape route
|
||||
|
||||
TF_NAV_NO_SPAWNING = 0x02000000, // don't spawn bots in this area
|
||||
|
||||
TF_NAV_RESCUE_CLOSET = 0x04000000, // for respawning friends in Raid mode
|
||||
|
||||
TF_NAV_BOMB_CAN_DROP_HERE = 0x08000000, // the bomb can be dropped here and reached by the invaders in MvM
|
||||
|
||||
TF_NAV_DOOR_NEVER_BLOCKS = 0x10000000,
|
||||
TF_NAV_DOOR_ALWAYS_BLOCKS = 0x20000000,
|
||||
|
||||
TF_NAV_UNBLOCKABLE = 0x40000000, // this area cannot be blocked
|
||||
|
||||
// save/load these manually set flags, and don't clear them between rounds
|
||||
TF_NAV_PERSISTENT_ATTRIBUTES = TF_NAV_SNIPER_SPOT | TF_NAV_SENTRY_SPOT | TF_NAV_NO_SPAWNING | TF_NAV_BLUE_SETUP_GATE | TF_NAV_RED_SETUP_GATE | TF_NAV_BLOCKED_AFTER_POINT_CAPTURE | TF_NAV_BLOCKED_UNTIL_POINT_CAPTURE | TF_NAV_BLUE_ONE_WAY_DOOR | TF_NAV_RED_ONE_WAY_DOOR | TF_NAV_DOOR_NEVER_BLOCKS | TF_NAV_DOOR_ALWAYS_BLOCKS | TF_NAV_UNBLOCKABLE | TF_NAV_WITH_SECOND_POINT | TF_NAV_WITH_THIRD_POINT | TF_NAV_WITH_FOURTH_POINT | TF_NAV_WITH_FIFTH_POINT | TF_NAV_RESCUE_CLOSET
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CTFNavArea : public CNavArea
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFNavArea, CNavArea );
|
||||
|
||||
CTFNavArea( void );
|
||||
|
||||
virtual void OnServerActivate( void ); // (EXTEND) invoked when map is initially loaded
|
||||
virtual void OnRoundRestart( void ); // (EXTEND) invoked for each area when the round restarts
|
||||
|
||||
virtual void CustomAnalysis( bool isIncremental = false ); // for game-specific analysis
|
||||
virtual void Draw( void ) const; // draw area for debugging & editing
|
||||
|
||||
virtual void UpdateBlocked( bool force = false, int teamID = TEAM_ANY ) { } // we'll handle managing blocked status directly
|
||||
virtual bool IsBlocked( int teamID, bool ignoreNavBlockers = false ) const;
|
||||
|
||||
virtual void Save( CUtlBuffer &fileBuffer, unsigned int version ) const; // (EXTEND)
|
||||
virtual NavErrorType Load( CUtlBuffer &fileBuffer, unsigned int version, unsigned int subVersion ); // (EXTEND)
|
||||
|
||||
float GetIncursionDistance( int team ) const; // return travel distance from the team's active spawn room to this area, -1 for invalid
|
||||
CTFNavArea *GetNextIncursionArea( int team ) const; // return adjacent area with largest increase in incursion distance
|
||||
bool IsReachableByTeam( int team ) const; // return true if the given team can reach this area
|
||||
void CollectPriorIncursionAreas( int team, CUtlVector< CTFNavArea * > *priorVector ); // populate 'priorVector' with a collection of adjacent areas that have a lower incursion distance that this area
|
||||
void CollectNextIncursionAreas( int team, CUtlVector< CTFNavArea * > *priorVector ); // populate 'priorVector' with a collection of adjacent areas that have a higher incursion distance that this area
|
||||
|
||||
const CUtlVector< CTFNavArea * > &GetEnemyInvasionAreaVector( int myTeam ) const; // given OUR team index, return list of areas the enemy is invading from
|
||||
bool IsAwayFromInvasionAreas( int myTeam, float safetyRange = 1000.0f ) const; // return true if this area is at least safetyRange units away from all invasion areas
|
||||
|
||||
void ComputeInvasionAreaVectors( void );
|
||||
void SetInvasionSearchMarker( unsigned int marker );
|
||||
bool IsInvasionSearchMarked( unsigned int marker ) const;
|
||||
|
||||
void SetAttributeTF( int flags );
|
||||
void ClearAttributeTF( int flags );
|
||||
bool HasAttributeTF( int flags ) const;
|
||||
|
||||
void AddPotentiallyVisibleActor( CBaseCombatCharacter *who );
|
||||
void RemovePotentiallyVisibleActor( CBaseCombatCharacter *who );
|
||||
void ClearAllPotentiallyVisibleActors( void );
|
||||
bool IsPotentiallyVisibleToActor( CBaseCombatCharacter *who ) const; // return true if the given actor has potential visibility to this area
|
||||
|
||||
virtual bool IsPotentiallyVisibleToTeam( int team ) const; // return true if any portion of this area is visible to anyone on the given team (very fast)
|
||||
|
||||
class IForEachPotentiallyVisibleActor
|
||||
{
|
||||
public:
|
||||
virtual bool Inspect( CBaseCombatCharacter *who ) = 0;
|
||||
};
|
||||
bool ForEachPotentiallyVisibleActor( IForEachPotentiallyVisibleActor &func, int team = TEAM_ANY );
|
||||
|
||||
void OnCombat( void ); // invoked when combat happens in/near this area
|
||||
bool IsInCombat( void ) const; // return true if this area has seen combat recently
|
||||
float GetCombatIntensity( void ) const; // 1 = in active combat, 0 = quiet
|
||||
|
||||
static void MakeNewTFMarker( void );
|
||||
static void ResetTFMarker( void );
|
||||
bool IsTFMarked( void ) const;
|
||||
void TFMark( void );
|
||||
|
||||
// Raid mode -------------------------------------------------
|
||||
void AddToWanderCount( int count );
|
||||
void SetWanderCount( int count );
|
||||
int GetWanderCount( void ) const;
|
||||
|
||||
bool IsValidForWanderingPopulation( void ) const;
|
||||
// Raid mode -------------------------------------------------
|
||||
|
||||
// Distance for MvM bomb delivery
|
||||
float GetTravelDistanceToBombTarget( void ) const;
|
||||
|
||||
private:
|
||||
friend class CTFNavMesh;
|
||||
|
||||
float m_distanceFromSpawnRoom[ TF_TEAM_COUNT ];
|
||||
CUtlVector< CTFNavArea * > m_invasionAreaVector[ TF_TEAM_COUNT ]; // use our team as index to get list of areas the enemy is invading from
|
||||
unsigned int m_invasionSearchMarker;
|
||||
|
||||
unsigned int m_attributeFlags;
|
||||
|
||||
CUtlVector< CHandle< CBaseCombatCharacter > > m_potentiallyVisibleActor[ TF_TEAM_COUNT ];
|
||||
|
||||
float m_combatIntensity;
|
||||
IntervalTimer m_combatTimer;
|
||||
|
||||
static unsigned int m_masterTFMark;
|
||||
unsigned int m_TFMark; // this area's mark
|
||||
|
||||
// Raid mode -------------------------------------------------
|
||||
int m_wanderCount; // how many wandering defenders to populate here
|
||||
// Raid mode -------------------------------------------------
|
||||
|
||||
float m_distanceToBombTarget;
|
||||
};
|
||||
|
||||
|
||||
inline float CTFNavArea::GetTravelDistanceToBombTarget( void ) const
|
||||
{
|
||||
return m_distanceToBombTarget;
|
||||
}
|
||||
|
||||
inline void CTFNavArea::AddToWanderCount( int count )
|
||||
{
|
||||
m_wanderCount += count;
|
||||
}
|
||||
|
||||
inline void CTFNavArea::SetWanderCount( int count )
|
||||
{
|
||||
m_wanderCount = count;
|
||||
}
|
||||
|
||||
inline int CTFNavArea::GetWanderCount( void ) const
|
||||
{
|
||||
return m_wanderCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline bool CTFNavArea::IsPotentiallyVisibleToActor( CBaseCombatCharacter *who ) const
|
||||
{
|
||||
if ( who == NULL )
|
||||
return false;
|
||||
|
||||
int team = who->GetTeamNumber();
|
||||
if ( team < 0 || team >= TF_TEAM_COUNT )
|
||||
return false;
|
||||
|
||||
return m_potentiallyVisibleActor[ team ].Find( who ) != m_potentiallyVisibleActor[ team ].InvalidIndex();
|
||||
}
|
||||
|
||||
|
||||
inline bool CTFNavArea::IsPotentiallyVisibleToTeam( int team ) const
|
||||
{
|
||||
return team >= 0 && team < TF_TEAM_COUNT && m_potentiallyVisibleActor[ team ].Count() > 0;
|
||||
}
|
||||
|
||||
|
||||
inline bool CTFNavArea::ForEachPotentiallyVisibleActor( CTFNavArea::IForEachPotentiallyVisibleActor &func, int team )
|
||||
{
|
||||
if ( team == TEAM_ANY )
|
||||
{
|
||||
for( int t=0; t<TF_TEAM_COUNT; ++t )
|
||||
{
|
||||
for( int i=0; i<m_potentiallyVisibleActor[ t ].Count(); ++i )
|
||||
{
|
||||
CBaseCombatCharacter *who = m_potentiallyVisibleActor[ t ][ i ];
|
||||
|
||||
if ( who && func.Inspect( who ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( team >= 0 && team < TF_TEAM_COUNT )
|
||||
{
|
||||
for( int i=0; i<m_potentiallyVisibleActor[ team ].Count(); ++i )
|
||||
{
|
||||
CBaseCombatCharacter *who = m_potentiallyVisibleActor[ team ][ i ];
|
||||
|
||||
if ( who && func.Inspect( who ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void CTFNavArea::RemovePotentiallyVisibleActor( CBaseCombatCharacter *who )
|
||||
{
|
||||
for( int i=0; i<TF_TEAM_COUNT; ++i )
|
||||
m_potentiallyVisibleActor[i].FindAndFastRemove( who );
|
||||
}
|
||||
|
||||
inline void CTFNavArea::ClearAllPotentiallyVisibleActors( void )
|
||||
{
|
||||
for( int i=0; i<TF_TEAM_COUNT; ++i )
|
||||
m_potentiallyVisibleActor[i].RemoveAll();
|
||||
}
|
||||
|
||||
inline float CTFNavArea::GetIncursionDistance( int team ) const
|
||||
{
|
||||
if ( team < 0 || team >= TF_TEAM_COUNT )
|
||||
{
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
return m_distanceFromSpawnRoom[ team ];
|
||||
}
|
||||
|
||||
inline bool CTFNavArea::IsReachableByTeam( int team ) const
|
||||
{
|
||||
if ( team < 0 || team >= TF_TEAM_COUNT )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_distanceFromSpawnRoom[ team ] >= 0.0f;
|
||||
}
|
||||
|
||||
inline const CUtlVector< CTFNavArea * > &CTFNavArea::GetEnemyInvasionAreaVector( int myTeam ) const
|
||||
{
|
||||
if ( myTeam < 0 || myTeam >= TF_TEAM_COUNT )
|
||||
{
|
||||
myTeam = 0.0f;
|
||||
}
|
||||
|
||||
return m_invasionAreaVector[ myTeam ];
|
||||
}
|
||||
|
||||
inline void CTFNavArea::SetInvasionSearchMarker( unsigned int marker )
|
||||
{
|
||||
m_invasionSearchMarker = marker;
|
||||
}
|
||||
|
||||
inline bool CTFNavArea::IsInvasionSearchMarked( unsigned int marker ) const
|
||||
{
|
||||
return marker == m_invasionSearchMarker;
|
||||
}
|
||||
|
||||
inline void CTFNavArea::SetAttributeTF( int flags )
|
||||
{
|
||||
m_attributeFlags |= flags;
|
||||
}
|
||||
|
||||
inline void CTFNavArea::ClearAttributeTF( int flags )
|
||||
{
|
||||
m_attributeFlags &= ~flags;
|
||||
}
|
||||
|
||||
inline bool CTFNavArea::HasAttributeTF( int flags ) const
|
||||
{
|
||||
return ( m_attributeFlags & flags ) ? true : false;
|
||||
}
|
||||
|
||||
#endif // TF_NAV_AREA_H
|
||||
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements nav interface entity. Used by maps to do various things
|
||||
// with the nav mesh
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_nav_mesh.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
class CPointNavInterface : public CPointEntity
|
||||
{
|
||||
DECLARE_CLASS( CPointNavInterface, CPointEntity );
|
||||
|
||||
public:
|
||||
|
||||
// Input handlers
|
||||
void RecomputeBlockers(inputdata_t &inputdata);
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CPointNavInterface )
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "RecomputeBlockers", RecomputeBlockers ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_point_nav_interface, CPointNavInterface );
|
||||
|
||||
|
||||
void CPointNavInterface::RecomputeBlockers( inputdata_t &inputdata )
|
||||
{
|
||||
CTFNavMesh* pTFNavMesh = dynamic_cast<CTFNavMesh*>( TheNavMesh );
|
||||
Assert( pTFNavMesh );
|
||||
if( pTFNavMesh )
|
||||
{
|
||||
pTFNavMesh->ScheduleRecomputationOfInternalData( CTFNavMesh::MAP_LOGIC );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,235 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_nav_mesh.h
|
||||
// TF specific nav mesh
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#ifndef TF_NAV_MESH_H
|
||||
#define TF_NAV_MESH_H
|
||||
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_nav_area.h"
|
||||
#include "tf_obj_teleporter.h"
|
||||
|
||||
#define TF_PLAYER_JUMP_HEIGHT 45.0f // non crouch-jumping
|
||||
|
||||
class CBaseObject;
|
||||
class CObjectTeleporter;
|
||||
class CTFPlayer;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// General purpose collector class for ForAllArea-style functor methods
|
||||
class CTFAreaCollector
|
||||
{
|
||||
public:
|
||||
bool operator() ( CNavArea *area )
|
||||
{
|
||||
m_vector.AddToTail( (CTFNavArea *)area );
|
||||
return true;
|
||||
}
|
||||
|
||||
CUtlVector< CTFNavArea * > m_vector;
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class CTFNavMesh : public CNavMesh
|
||||
{
|
||||
public:
|
||||
CTFNavMesh( void );
|
||||
|
||||
virtual CTFNavArea *CreateArea( void ) const; // CNavArea factory
|
||||
|
||||
virtual void Update( void ); // invoked on each game frame
|
||||
|
||||
virtual unsigned int GetSubVersionNumber( void ) const; // returns sub-version number of data format used by derived classes
|
||||
virtual void SaveCustomData( CUtlBuffer &fileBuffer ) const; // store custom mesh data for derived classes
|
||||
virtual void LoadCustomData( CUtlBuffer &fileBuffer, unsigned int subVersion ); // load custom mesh data for derived classes
|
||||
|
||||
virtual void OnServerActivate( void ); // (EXTEND) invoked when server loads a new map
|
||||
virtual void OnRoundRestart( void ); // invoked when a game round restarts
|
||||
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
|
||||
/**
|
||||
* Return true if nav mesh can be trusted for all climbing/jumping decisions because game environment is fairly simple.
|
||||
* Authoritative meshes mean path followers can skip CPU intesive realtime scanning of unpredictable geometry.
|
||||
*/
|
||||
virtual bool IsAuthoritative( void ) const { return true; } // TF2 has nice clean environments
|
||||
|
||||
virtual unsigned int GetGenerationTraceMask( void ) const; // return the mask used by traces when generating the mesh
|
||||
|
||||
void OnObjectChanged();
|
||||
bool IsSentryGunHere( CTFNavArea *area ) const; // return true if a Sentry Gun has been built in the given area
|
||||
|
||||
void CollectBuiltObjects( CUtlVector< CBaseObject * > *collectionVector, int team = TEAM_ANY ); // fill given vector will all objects on the given team
|
||||
|
||||
struct BallisticLaunchInfo
|
||||
{
|
||||
Vector m_launchSpot; // where to stand
|
||||
float m_aimYaw; // how to aim
|
||||
float m_aimPitch; // how to aim
|
||||
float m_chargeTime; // how long to charge weapon
|
||||
};
|
||||
|
||||
// populate the given vector with ways to launch grenades to hit the given building
|
||||
void CollectBallisticAttackInfo( CBaseObject *building, CUtlVector< BallisticLaunchInfo > *infoVector ) const;
|
||||
|
||||
void ResetMeshAttributes( bool bScheduleRecomputation );
|
||||
|
||||
void CollectControlPointAreas( void );
|
||||
|
||||
void DecorateMesh( void );
|
||||
void DecorateMeshTacticalHints( void );
|
||||
void RemoveAllMeshDecoration( void );
|
||||
|
||||
// populate the given "ambushVector" with good areas to lurk in ambush for the invading enemy team
|
||||
void CollectAmbushAreas( CUtlVector< CTFNavArea * > *ambushVector, CTFNavArea *startArea, int teamToAmbush, float searchRadius, float incursionTolerance = 300.0f ) const;
|
||||
|
||||
// populate the given vector with areas that are just outside of the given team's spawn room(s)
|
||||
void CollectSpawnRoomThresholdAreas( CUtlVector< CTFNavArea * > *spawnExitAreaVector, int team ) const;
|
||||
|
||||
// populate the given vector with areas that have a bomb travel distance within the given range
|
||||
void CollectAreaWithinBombTravelRange( CUtlVector< CTFNavArea * > *spawnExitAreaVector, float minTravel, float maxTravel ) const;
|
||||
|
||||
const CUtlVector< CTFNavArea * > *GetSetupGateDefenseAreas( void ) const; // return vector of areas that are good for defending enemies coming out of the blue setup gates
|
||||
const CUtlVector< CTFNavArea * > *GetControlPointAreas( int pointIndex ) const; // return vector of areas overlapping the given control point
|
||||
CTFNavArea *GetControlPointCenterArea( int pointIndex ) const; // return area overlapping the center of the given control point
|
||||
const CUtlVector< CTFNavArea * > *GetSpawnRoomAreas( int team ) const; // return vector of areas within the given team spawn room(s)
|
||||
const CUtlVector< CTFNavArea * > *GetSpawnRoomExitAreas( int team ) const; // return vector of areas where the given team exits their spawn room(s)
|
||||
|
||||
enum RecomputeReasonType
|
||||
{
|
||||
RESET,
|
||||
SETUP_FINISHED,
|
||||
POINT_CAPTURED,
|
||||
POINT_UNLOCKED,
|
||||
BLOCKED_STATUS_CHANGED,
|
||||
MAP_LOGIC
|
||||
};
|
||||
void ScheduleRecomputationOfInternalData( RecomputeReasonType reason, int whichPoint );
|
||||
|
||||
|
||||
protected:
|
||||
virtual void BeginCustomAnalysis( bool bIncremental );
|
||||
virtual void PostCustomAnalysis( void ); // invoked when custom analysis step is complete
|
||||
virtual void EndCustomAnalysis();
|
||||
|
||||
private:
|
||||
void ComputeIncursionDistances( void ); // recompute travel distance from each team's spawn room for each nav area
|
||||
void ComputeIncursionDistances( CTFNavArea *spawnArea, int team );
|
||||
void ComputeInvasionAreas( void );
|
||||
void ComputeLegalBombDropAreas( void );
|
||||
void ComputeBombTargetDistance();
|
||||
|
||||
void UpdateDebugDisplay( void ) const;
|
||||
|
||||
void OnBlockedAreasChanged( void );
|
||||
|
||||
void ComputeBlockedAreas( void );
|
||||
|
||||
CountdownTimer m_recomputeInternalDataTimer; // if started, when counts down recompute internal data to give various map logic time to complete
|
||||
RecomputeReasonType m_recomputeReason;
|
||||
int m_recomputeReasonWhichPoint;
|
||||
void RecomputeInternalData( void );
|
||||
|
||||
// Array of areas with sentry danger attributes set.
|
||||
CUtlVector< CTFNavArea * > m_sentryAreas;
|
||||
|
||||
CUtlVector< CTFNavArea * > m_setupGateDefenseAreaVector;
|
||||
|
||||
CUtlVector< CTFNavArea * > m_controlPointAreaVector[ MAX_CONTROL_POINTS ];
|
||||
CTFNavArea *m_controlPointCenterAreaVector[ MAX_CONTROL_POINTS ];
|
||||
|
||||
CUtlVector< CTFNavArea * > m_redSpawnRoomAreaVector;
|
||||
CUtlVector< CTFNavArea * > m_blueSpawnRoomAreaVector;
|
||||
|
||||
CUtlVector< CTFNavArea * > m_redSpawnRoomExitAreaVector;
|
||||
CUtlVector< CTFNavArea * > m_blueSpawnRoomExitAreaVector;
|
||||
void CollectAndMarkSpawnRoomExits( CTFNavArea *area, CUtlVector< CTFNavArea * > *exitAreaVector );
|
||||
|
||||
CountdownTimer m_watchCartTimer;
|
||||
|
||||
int m_priorBotCount;
|
||||
};
|
||||
|
||||
|
||||
inline void CTFNavMesh::ScheduleRecomputationOfInternalData( CTFNavMesh::RecomputeReasonType reason, int whichPoint = 0 )
|
||||
{
|
||||
m_recomputeInternalDataTimer.Start( 2.0f );
|
||||
m_recomputeReason = reason;
|
||||
m_recomputeReasonWhichPoint = whichPoint;
|
||||
}
|
||||
|
||||
|
||||
inline const CUtlVector< CTFNavArea * > *CTFNavMesh::GetSpawnRoomAreas( int team ) const
|
||||
{
|
||||
if ( team == TF_TEAM_RED )
|
||||
{
|
||||
return &m_redSpawnRoomAreaVector;
|
||||
}
|
||||
|
||||
if ( team == TF_TEAM_BLUE )
|
||||
{
|
||||
return &m_blueSpawnRoomAreaVector;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline const CUtlVector< CTFNavArea * > *CTFNavMesh::GetSpawnRoomExitAreas( int team ) const
|
||||
{
|
||||
if ( team == TF_TEAM_RED )
|
||||
{
|
||||
return &m_redSpawnRoomExitAreaVector;
|
||||
}
|
||||
|
||||
if ( team == TF_TEAM_BLUE )
|
||||
{
|
||||
return &m_blueSpawnRoomExitAreaVector;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline const CUtlVector< CTFNavArea * > *CTFNavMesh::GetControlPointAreas( int pointIndex ) const
|
||||
{
|
||||
if ( pointIndex < 0 || pointIndex >= MAX_CONTROL_POINTS )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &m_controlPointAreaVector[ pointIndex ];
|
||||
}
|
||||
|
||||
inline CTFNavArea *CTFNavMesh::GetControlPointCenterArea( int pointIndex ) const
|
||||
{
|
||||
if ( pointIndex < 0 || pointIndex >= MAX_CONTROL_POINTS )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_controlPointCenterAreaVector[ pointIndex ];
|
||||
}
|
||||
|
||||
inline const CUtlVector< CTFNavArea * > *CTFNavMesh::GetSetupGateDefenseAreas( void ) const
|
||||
{
|
||||
return &m_setupGateDefenseAreaVector;
|
||||
}
|
||||
|
||||
|
||||
inline unsigned int CTFNavMesh::GetGenerationTraceMask( void ) const
|
||||
{
|
||||
return MASK_PLAYERSOLID_BRUSHONLY;
|
||||
}
|
||||
|
||||
|
||||
inline CTFNavMesh *TheTFNavMesh( void )
|
||||
{
|
||||
return reinterpret_cast< CTFNavMesh * >( TheNavMesh );
|
||||
}
|
||||
|
||||
|
||||
extern TFNavAttributeType NameToTFAttribute( const char *name );
|
||||
extern const char *TFAttributeToName( TFNavAttributeType attribute );
|
||||
|
||||
#endif // TF_NAV_MESH_H
|
||||
@@ -0,0 +1,306 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_nav_mesh_edit.cpp
|
||||
// TF specific nav mesh editing
|
||||
// Michael Booth, May 2009
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_nav_mesh.h"
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
class CTFAttributeClearer
|
||||
{
|
||||
public:
|
||||
CTFAttributeClearer( TFNavAttributeType attribute )
|
||||
{
|
||||
m_attribute = attribute;
|
||||
}
|
||||
|
||||
bool operator() ( CNavArea *baseArea )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)baseArea;
|
||||
|
||||
area->ClearAttributeTF( m_attribute );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TFNavAttributeType m_attribute;
|
||||
};
|
||||
|
||||
void TF_EditClearAllAttributes( void )
|
||||
{
|
||||
CTFAttributeClearer clear( (TFNavAttributeType)0xFFFFFFFF );
|
||||
TheNavMesh->ForAllSelectedAreas( clear );
|
||||
TheNavMesh->ClearSelectedSet();
|
||||
}
|
||||
static ConCommand ClearAllAttributes( "tf_wipe_attributes", TF_EditClearAllAttributes, "Clear all TF-specific attributes of selected area.", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
struct AttributeLookup
|
||||
{
|
||||
const char *name;
|
||||
TFNavAttributeType attribute;
|
||||
};
|
||||
|
||||
static AttributeLookup s_TFAttributeTable[] =
|
||||
{
|
||||
{ "BLUE_SETUP_GATE", TF_NAV_BLUE_SETUP_GATE },
|
||||
{ "RED_SETUP_GATE", TF_NAV_RED_SETUP_GATE },
|
||||
{ "BLOCKED_AFTER_POINT_CAPTURE", TF_NAV_BLOCKED_AFTER_POINT_CAPTURE },
|
||||
{ "BLOCKED_UNTIL_POINT_CAPTURE", TF_NAV_BLOCKED_UNTIL_POINT_CAPTURE },
|
||||
{ "BLUE_ONE_WAY_DOOR", TF_NAV_BLUE_ONE_WAY_DOOR },
|
||||
{ "RED_ONE_WAY_DOOR", TF_NAV_RED_ONE_WAY_DOOR },
|
||||
|
||||
{ "SNIPER_SPOT", TF_NAV_SNIPER_SPOT },
|
||||
{ "SENTRY_SPOT", TF_NAV_SENTRY_SPOT },
|
||||
{ "NO_SPAWNING", TF_NAV_NO_SPAWNING },
|
||||
{ "RESCUE_CLOSET", TF_NAV_RESCUE_CLOSET },
|
||||
|
||||
{ "DOOR_ALWAYS_BLOCKS", TF_NAV_DOOR_ALWAYS_BLOCKS },
|
||||
{ "DOOR_NEVER_BLOCKS", TF_NAV_DOOR_NEVER_BLOCKS },
|
||||
{ "UNBLOCKABLE", TF_NAV_UNBLOCKABLE },
|
||||
|
||||
{ "WITH_SECOND_POINT", TF_NAV_WITH_SECOND_POINT },
|
||||
{ "WITH_THIRD_POINT", TF_NAV_WITH_THIRD_POINT },
|
||||
{ "WITH_FOURTH_POINT", TF_NAV_WITH_FOURTH_POINT },
|
||||
{ "WITH_FIFTH_POINT", TF_NAV_WITH_FIFTH_POINT },
|
||||
|
||||
{ NULL, TF_NAV_INVALID }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Can be used with any command that takes an attribute as its 2nd argument
|
||||
*/
|
||||
static int AttributeAutocomplete( const char *input, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
|
||||
{
|
||||
if ( Q_strlen( input ) >= COMMAND_COMPLETION_ITEM_LENGTH )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char command[ COMMAND_COMPLETION_ITEM_LENGTH+1 ];
|
||||
Q_strncpy( command, input, sizeof( command ) );
|
||||
|
||||
// skip to start of argument
|
||||
char *partialArg = Q_strrchr( command, ' ' );
|
||||
if ( partialArg == NULL )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// chop command from partial argument
|
||||
*partialArg = '\000';
|
||||
++partialArg;
|
||||
|
||||
int partialArgLength = Q_strlen( partialArg );
|
||||
|
||||
int count = 0;
|
||||
for( unsigned int i=0; s_TFAttributeTable[i].name && count < COMMAND_COMPLETION_MAXITEMS; ++i )
|
||||
{
|
||||
if ( !Q_strnicmp( s_TFAttributeTable[i].name, partialArg, partialArgLength ) )
|
||||
{
|
||||
// Add to the autocomplete array
|
||||
Q_snprintf( commands[ count++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", command, s_TFAttributeTable[i].name );
|
||||
}
|
||||
}
|
||||
|
||||
/* all of these are deprecated
|
||||
for( unsigned int i=0; TheNavAttributeTable[i].name && count < COMMAND_COMPLETION_MAXITEMS; ++i )
|
||||
{
|
||||
if ( !Q_strnicmp( TheNavAttributeTable[i].name, partialArg, partialArgLength ) )
|
||||
{
|
||||
// Add to the autocomplete array
|
||||
Q_snprintf( commands[ count++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", command, TheNavAttributeTable[i].name );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
TFNavAttributeType NameToTFAttribute( const char *name )
|
||||
{
|
||||
for( unsigned int i=0; s_TFAttributeTable[i].name; ++i )
|
||||
{
|
||||
if ( !Q_stricmp( s_TFAttributeTable[i].name, name ) )
|
||||
{
|
||||
return s_TFAttributeTable[i].attribute;
|
||||
}
|
||||
}
|
||||
|
||||
return TF_NAV_INVALID;
|
||||
}
|
||||
|
||||
|
||||
const char *TFAttributeToName( TFNavAttributeType attribute )
|
||||
{
|
||||
for( unsigned int i=0; s_TFAttributeTable[i].name; ++i )
|
||||
{
|
||||
if ( s_TFAttributeTable[i].attribute == attribute )
|
||||
{
|
||||
return s_TFAttributeTable[i].name;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void TF_EditClearAttribute( const CCommand &args )
|
||||
{
|
||||
if ( args.ArgC() < 2 )
|
||||
{
|
||||
Msg( "Usage: %s <attribute1> [attribute2...]\n", args[0] );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 1; i < args.ArgC(); ++i )
|
||||
{
|
||||
TFNavAttributeType spawnAttribute = NameToTFAttribute( args[i] );
|
||||
NavAttributeType navAttribute = NameToNavAttribute( args[i] );
|
||||
|
||||
if ( spawnAttribute != TF_NAV_INVALID )
|
||||
{
|
||||
CTFAttributeClearer clear( spawnAttribute );
|
||||
TheNavMesh->ForAllSelectedAreas( clear );
|
||||
}
|
||||
else if ( navAttribute != NAV_MESH_INVALID )
|
||||
{
|
||||
NavAttributeClearer clear( navAttribute );
|
||||
TheNavMesh->ForAllSelectedAreas( clear );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Unknown attribute '%s'", args[i] );
|
||||
}
|
||||
}
|
||||
|
||||
TheNavMesh->ClearSelectedSet();
|
||||
}
|
||||
static ConCommand ClearAttributeTF( "tf_clear_attribute", TF_EditClearAttribute, "Remove given attribute from all areas in the selected set.", FCVAR_CHEAT, AttributeAutocomplete );
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
class CTFAttributeToggler
|
||||
{
|
||||
public:
|
||||
CTFAttributeToggler( TFNavAttributeType attribute )
|
||||
{
|
||||
m_attribute = attribute;
|
||||
}
|
||||
|
||||
bool operator() ( CNavArea *baseArea )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)baseArea;
|
||||
|
||||
// only toggle if dealing with a single selected area
|
||||
if ( TheNavMesh->IsSelectedSetEmpty() && area->HasAttributeTF( m_attribute ) )
|
||||
{
|
||||
area->ClearAttributeTF( m_attribute );
|
||||
}
|
||||
else
|
||||
{
|
||||
area->SetAttributeTF( m_attribute );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TFNavAttributeType m_attribute;
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void TF_EditMarkAttribute( const CCommand &args )
|
||||
{
|
||||
if ( args.ArgC() < 2 )
|
||||
{
|
||||
Msg( "Usage: %s <attribute> [attribute2...]\n", args[0] );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 1; i < args.ArgC(); ++i )
|
||||
{
|
||||
TFNavAttributeType spawnAttribute = NameToTFAttribute( args[i] );
|
||||
NavAttributeType navAttribute = NameToNavAttribute( args[i] );
|
||||
|
||||
if ( spawnAttribute != TF_NAV_INVALID )
|
||||
{
|
||||
CTFAttributeToggler toggle( spawnAttribute );
|
||||
TheNavMesh->ForAllSelectedAreas( toggle );
|
||||
}
|
||||
else if ( navAttribute != NAV_MESH_INVALID )
|
||||
{
|
||||
NavAttributeToggler clear( navAttribute );
|
||||
TheNavMesh->ForAllSelectedAreas( clear );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Unknown attribute '%s'", args[i] );
|
||||
}
|
||||
}
|
||||
|
||||
TheNavMesh->ClearSelectedSet();
|
||||
}
|
||||
static ConCommand MarkAttribute( "tf_mark", TF_EditMarkAttribute, "Set attribute of selected area.", FCVAR_CHEAT, AttributeAutocomplete );
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void TF_EditSelectWithAttribute( const CCommand &args )
|
||||
{
|
||||
TheNavMesh->ClearSelectedSet();
|
||||
|
||||
if ( args.ArgC() != 2 )
|
||||
{
|
||||
Msg( "Usage: %s <attribute>\n", args[0] );
|
||||
return;
|
||||
}
|
||||
|
||||
TFNavAttributeType spawnAttribute = NameToTFAttribute( args[1] );
|
||||
|
||||
int count = 0;
|
||||
|
||||
if ( spawnAttribute != TF_NAV_INVALID )
|
||||
{
|
||||
FOR_EACH_VEC( TheNavAreas, it )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)TheNavAreas[ it ];
|
||||
|
||||
if ( area->HasAttributeTF( spawnAttribute ) )
|
||||
{
|
||||
TheNavMesh->AddToSelectedSet( area );
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NavAttributeType navAttribute = NameToNavAttribute( args[1] );
|
||||
if ( navAttribute != NAV_MESH_INVALID )
|
||||
{
|
||||
FOR_EACH_VEC( TheNavAreas, it )
|
||||
{
|
||||
CNavArea *area = TheNavAreas[ it ];
|
||||
|
||||
if ( area->GetAttributes() & navAttribute )
|
||||
{
|
||||
TheNavMesh->AddToSelectedSet( area );
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Unknown attribute '%s'", args[1] );
|
||||
}
|
||||
}
|
||||
|
||||
Msg( "%d areas added to selection\n", count );
|
||||
}
|
||||
static ConCommand SelectWithAttribute( "tf_select_with_attribute", TF_EditSelectWithAttribute, "Selects areas with the given attribute.", FCVAR_CHEAT, AttributeAutocomplete );
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_nav_mesh_edit.h
|
||||
// TF specific nav mesh editing
|
||||
// Michael Booth, May 2009
|
||||
|
||||
#ifndef TF_NAV_MESH_EDIT_H
|
||||
#define TF_NAV_MESH_EDIT_H
|
||||
|
||||
|
||||
|
||||
#endif // TF_NAV_MESH_EDIT_H
|
||||
@@ -0,0 +1,158 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_path_follower.cpp
|
||||
// Simplified path following for TF2
|
||||
// Author: Michael Booth, November 2010
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "NextBotManager.h"
|
||||
#include "tf_path_follower.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
CTFPathFollower::CTFPathFollower( void )
|
||||
{
|
||||
m_goal = NULL;
|
||||
m_minLookAheadRange = 300.0f;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
CTFPathFollower::~CTFPathFollower()
|
||||
{
|
||||
// allow bots to detach pointer to me
|
||||
CUtlVector< INextBot * > botVector;
|
||||
TheNextBots().CollectAllBots( &botVector );
|
||||
|
||||
for( int i=0; i<botVector.Count(); ++i )
|
||||
{
|
||||
botVector[i]->NotifyPathDestruction( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* When the path is invalidated, the follower is also reset
|
||||
*/
|
||||
void CTFPathFollower::Invalidate( void )
|
||||
{
|
||||
// extend
|
||||
Path::Invalidate();
|
||||
|
||||
m_goal = NULL;
|
||||
MoveCursorToStart();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Invoked when the path is (re)computed (path is valid at the time of this call)
|
||||
*/
|
||||
void CTFPathFollower::OnPathChanged( INextBot *bot, Path::ResultType result )
|
||||
{
|
||||
// start from the beginning
|
||||
m_goal = FirstSegment();
|
||||
MoveCursorToStart();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Move mover along path
|
||||
*/
|
||||
void CTFPathFollower::Update( INextBot *bot )
|
||||
{
|
||||
VPROF_BUDGET( "CTFPathFollower::Update", "NextBot" );
|
||||
ILocomotion *mover = bot->GetLocomotionInterface();
|
||||
|
||||
// track most recent path followed
|
||||
bot->SetCurrentPath( this );
|
||||
|
||||
if ( !IsValid() || m_goal == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we've reached the end of the path
|
||||
const float nearRange = 25.0f;
|
||||
if ( mover->IsOnGround() && ( GetEndPosition() - mover->GetFeet() ).AsVector2D().IsLengthLessThan( nearRange ) )
|
||||
{
|
||||
// the end of the path has been reached
|
||||
mover->GetBot()->OnMoveToSuccess( this );
|
||||
|
||||
if ( bot->IsDebugging( NEXTBOT_PATH ) )
|
||||
{
|
||||
DevMsg( "CTFPathFollower: OnMoveToSuccess\n" );
|
||||
}
|
||||
|
||||
// don't invalidate if OnMoveToSuccess just recomputed a new path
|
||||
if ( GetAge() > 0.0f )
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// move along the path
|
||||
MoveCursorToClosestPosition( mover->GetFeet(), SEEK_AHEAD );
|
||||
float myCursorPosition = GetCursorPosition();
|
||||
const Path::Data &data = GetCursorData();
|
||||
|
||||
if ( !data.segmentPrior )
|
||||
{
|
||||
// this shouldn't happen
|
||||
mover->GetBot()->OnMoveToFailure( this, FAIL_STUCK );
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
// set goal to be just ahead of wherever we happen to be on the path
|
||||
m_goal = NextSegment( data.segmentPrior );
|
||||
|
||||
if ( !m_goal )
|
||||
{
|
||||
m_goal = data.segmentPrior;
|
||||
}
|
||||
|
||||
// find actual move-to position farther down the path
|
||||
Vector moveToPos = m_goal->pos;
|
||||
|
||||
// follow point farther down the path to smooth out our movement
|
||||
for( float ahead = m_minLookAheadRange; ahead > 0.0f; ahead -= 50.0f )
|
||||
{
|
||||
MoveCursor( myCursorPosition, PATH_ABSOLUTE_DISTANCE );
|
||||
MoveCursor( ahead, PATH_RELATIVE_DISTANCE );
|
||||
|
||||
// get path data at this lookahead point
|
||||
const Path::Data &data = GetCursorData();
|
||||
|
||||
if ( mover->IsPotentiallyTraversable( mover->GetFeet(), data.pos ) )
|
||||
{
|
||||
moveToPos = data.pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// move bot along path
|
||||
mover->FaceTowards( moveToPos );
|
||||
mover->Approach( moveToPos );
|
||||
|
||||
// debug display
|
||||
if ( bot->IsDebugging( NEXTBOT_PATH ) )
|
||||
{
|
||||
Path::Draw();
|
||||
|
||||
NDebugOverlay::Cross3D( moveToPos, 5.0f, 150, 150, 255, true, 0.1f );
|
||||
NDebugOverlay::Line( bot->GetEntity()->WorldSpaceCenter(), moveToPos, 255, 255, 0, true, 0.1f );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_path_follower.h
|
||||
// Simplified path following for TF2
|
||||
// Author: Michael Booth, November 2010
|
||||
|
||||
#ifndef TF_PATH_FOLLOWER_H
|
||||
#define TF_PATH_FOLLOWER_H
|
||||
|
||||
#include "nav_mesh.h"
|
||||
#include "nav_pathfind.h"
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class INextBot;
|
||||
class ILocomotion;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* This is a simplified path follower that doesn't care about ladders, climbing, hindrances, etc.
|
||||
*/
|
||||
class CTFPathFollower : public PathFollower
|
||||
{
|
||||
public:
|
||||
CTFPathFollower( void );
|
||||
virtual ~CTFPathFollower();
|
||||
|
||||
virtual void Invalidate( void ); // (EXTEND) cause the path to become invalid
|
||||
virtual void OnPathChanged( INextBot *bot, Path::ResultType result ); // invoked when the path is (re)computed (path is valid at the time of this call)
|
||||
|
||||
virtual void Update( INextBot *bot ); // move bot along path
|
||||
|
||||
virtual const Path::Segment *GetCurrentGoal( void ) const; // return current goal along the path we are trying to reach
|
||||
|
||||
virtual void SetMinLookAheadDistance( float value ); // minimum range movement goal must be along path
|
||||
|
||||
private:
|
||||
const Path::Segment *m_goal; // our current goal along the path
|
||||
float m_minLookAheadRange;
|
||||
|
||||
// bool CheckProgress( INextBot *bot );
|
||||
// bool IsAtGoal( INextBot *bot ) const; // return true if reached current path goal
|
||||
};
|
||||
|
||||
|
||||
inline const Path::Segment *CTFPathFollower::GetCurrentGoal( void ) const
|
||||
{
|
||||
return m_goal;
|
||||
}
|
||||
|
||||
|
||||
inline void CTFPathFollower::SetMinLookAheadDistance( float value )
|
||||
{
|
||||
m_minLookAheadRange = value;
|
||||
}
|
||||
|
||||
#endif // TF_PATH_FOLLOWER_H
|
||||
|
||||
|
||||
Reference in New Issue
Block a user