Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

335 lines
9.0 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
// tf_bot_spy_infiltrate.cpp
// Move into position behind enemy lines and wait for victims
// Michael Booth, June 2010
#include "cbase.h"
#include "tf_player.h"
#include "tf_obj_sentrygun.h"
#include "bot/tf_bot.h"
#include "bot/behavior/spy/tf_bot_spy_infiltrate.h"
#include "bot/behavior/spy/tf_bot_spy_sap.h"
#include "bot/behavior/spy/tf_bot_spy_attack.h"
#include "bot/behavior/tf_bot_retreat_to_cover.h"
#include "nav_mesh.h"
extern ConVar tf_bot_path_lookahead_range;
ConVar tf_bot_debug_spy( "tf_bot_debug_spy", "0", FCVAR_CHEAT );
//---------------------------------------------------------------------------------------------
ActionResult< CTFBot > CTFBotSpyInfiltrate::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
{
m_hideArea = NULL;
m_hasEnteredCombatZone = false;
return Continue();
}
//---------------------------------------------------------------------------------------------
ActionResult< CTFBot > CTFBotSpyInfiltrate::Update( CTFBot *me, float interval )
{
// switch to our pistol
CBaseCombatWeapon *myGun = me->Weapon_GetSlot( TF_WPN_TYPE_PRIMARY );
if ( myGun )
{
me->Weapon_Switch( myGun );
}
CTFNavArea *myArea = me->GetLastKnownArea();
if ( !myArea )
{
return Continue();
}
bool isInMySpawn = myArea->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE | TF_NAV_SPAWN_ROOM_RED );
if ( myArea->HasAttributeTF( TF_NAV_SPAWN_ROOM_EXIT ) )
{
// don't count exits so we cloak as we leave
isInMySpawn = false;
}
// cloak when we first enter an area of active combat
if ( !me->m_Shared.IsStealthed() &&
!isInMySpawn &&
myArea->IsInCombat() &&
!m_hasEnteredCombatZone )
{
m_hasEnteredCombatZone = true;
me->PressAltFireButton();
}
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
if ( threat && threat->GetEntity() && threat->GetEntity()->IsBaseObject() )
{
CBaseObject *enemyObject = (CBaseObject *)threat->GetEntity();
if ( !enemyObject->HasSapper() && me->IsEnemy( enemyObject ) )
{
return SuspendFor( new CTFBotSpySap( enemyObject ), "Sapping an enemy object" );
}
}
if ( me->GetEnemySentry() && !me->GetEnemySentry()->HasSapper() )
{
return SuspendFor( new CTFBotSpySap( me->GetEnemySentry() ), "Sapping a Sentry" );
}
if ( !m_hideArea && m_findHidingSpotTimer.IsElapsed() )
{
FindHidingSpot( me );
m_findHidingSpotTimer.Start( 3.0f );
}
if ( !TFGameRules()->InSetup() )
{
// go after victims we've gotten behind
if ( threat && threat->GetTimeSinceLastKnown() < 3.0f )
{
CTFPlayer *victim = ToTFPlayer( threat->GetEntity() );
if ( victim )
{
CTFNavArea *victimArea = (CTFNavArea *)victim->GetLastKnownArea();
if ( victimArea )
{
int victimTeam = victim->GetTeamNumber();
if ( victimArea->GetIncursionDistance( victimTeam ) > myArea->GetIncursionDistance( victimTeam ) )
{
if ( me->m_Shared.IsStealthed() )
{
return SuspendFor( new CTFBotRetreatToCover( new CTFBotSpyAttack( victim ) ), "Hiding to decloak before going after a backstab victim" );
}
else
{
return SuspendFor( new CTFBotSpyAttack( victim ), "Going after a backstab victim" );
}
}
}
}
}
}
if ( m_hideArea )
{
if ( tf_bot_debug_spy.GetBool() )
{
m_hideArea->DrawFilled( 255, 255, 0, 255, NDEBUG_PERSIST_TILL_NEXT_SERVER );
}
if ( myArea == m_hideArea )
{
// stay hidden during setup time
if ( TFGameRules()->InSetup() )
{
m_waitTimer.Start( RandomFloat( 0.0f, 5.0f ) );
}
else
{
// wait in our hiding spot for a bit, then try another
if ( !m_waitTimer.HasStarted() )
{
m_waitTimer.Start( RandomFloat( 5.0f, 10.0f ) );
}
else if ( m_waitTimer.IsElapsed() )
{
// time to find a new hiding spot
m_hideArea = NULL;
}
}
}
else
{
// move to our ambush position
if ( m_repathTimer.IsElapsed() )
{
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
// we may not be able to path to our hiding spot, but get as close as we can
// (dropdown mid spawn in cp_gorge)
CTFBotPathCost cost( me, SAFEST_ROUTE );
m_path.Compute( me, m_hideArea->GetCenter(), cost );
}
m_path.Update( me );
m_waitTimer.Invalidate();
}
}
return Continue();
}
//---------------------------------------------------------------------------------------------
void CTFBotSpyInfiltrate::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
{
}
//---------------------------------------------------------------------------------------------
ActionResult< CTFBot > CTFBotSpyInfiltrate::OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction )
{
return Continue();
}
//---------------------------------------------------------------------------------------------
ActionResult< CTFBot > CTFBotSpyInfiltrate::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
{
m_repathTimer.Invalidate();
m_hideArea = NULL;
return Continue();
}
//---------------------------------------------------------------------------------------------
bool CTFBotSpyInfiltrate::FindHidingSpot( CTFBot *me )
{
m_hideArea = NULL;
if ( me->GetAliveDuration() < 5.0f && TFGameRules()->InSetup() )
{
// wait a bit until the nav mesh has updated itself
return false;
}
int myTeam = me->GetTeamNumber();
const CUtlVector< CTFNavArea * > *enemySpawnExitVector = TheTFNavMesh()->GetSpawnRoomExitAreas( GetEnemyTeam( myTeam ) );
#ifdef TF_RAID_MODE
if ( TFGameRules()->IsRaidMode() )
{
// for now, just lurk where we are
return false;
}
#endif
if ( !enemySpawnExitVector || enemySpawnExitVector->Count() == 0 )
{
if ( tf_bot_debug_spy.GetBool() )
{
DevMsg( "%3.2f: No enemy spawn room exit areas found\n", gpGlobals->curtime );
}
return false;
}
// find nearby place to hide hear enemy spawn exit(s)
CUtlVector< CNavArea * > nearbyAreaVector;
const float nearbyHideRange = 2500.0f;
for( int x=0; x<enemySpawnExitVector->Count(); ++x )
{
CTFNavArea *enemySpawnExitArea = enemySpawnExitVector->Element( x );
CUtlVector< CNavArea * > nearbyThisExitAreaVector;
CollectSurroundingAreas( &nearbyThisExitAreaVector, enemySpawnExitArea, nearbyHideRange, me->GetLocomotionInterface()->GetStepHeight(), me->GetLocomotionInterface()->GetStepHeight() );
// concat vectors (assuming N^2 unique search would cost more than ripping through some duplicates)
nearbyAreaVector.AddVectorToTail( nearbyThisExitAreaVector );
}
// find area not visible to any enemy spawn exits
CUtlVector< CTFNavArea * > hideAreaVector;
int i;
for( i=0; i<nearbyAreaVector.Count(); ++i )
{
CTFNavArea *area = (CTFNavArea *)nearbyAreaVector[i];
if ( !me->GetLocomotionInterface()->IsAreaTraversable( area ) )
continue;
bool isHidden = true;
for( int j=0; j<enemySpawnExitVector->Count(); ++j )
{
if ( area->IsPotentiallyVisible( enemySpawnExitVector->Element(j) ) )
{
isHidden = false;
break;
}
}
if ( isHidden )
{
hideAreaVector.AddToTail( area );
}
}
if ( hideAreaVector.Count() == 0 )
{
if ( tf_bot_debug_spy.GetBool() )
{
DevMsg( "%3.2f: Can't find any non-visible hiding areas, trying for anything near the spawn exit...\n", gpGlobals->curtime );
}
for( i=0; i<nearbyAreaVector.Count(); ++i )
{
CTFNavArea *area = (CTFNavArea *)nearbyAreaVector[i];
if ( !me->GetLocomotionInterface()->IsAreaTraversable( area ) )
continue;
hideAreaVector.AddToTail( area );
}
}
if ( hideAreaVector.Count() == 0 )
{
if ( tf_bot_debug_spy.GetBool() )
{
DevMsg( "%3.2f: Can't find any areas near the enemy spawn exit - just heading to the enemy spawn and hoping...\n", gpGlobals->curtime );
}
m_hideArea = enemySpawnExitVector->Element( RandomInt( 0, enemySpawnExitVector->Count()-1 ) );
return false;
}
// pick a specific hiding spot
m_hideArea = hideAreaVector[ RandomInt( 0, hideAreaVector.Count()-1 ) ];
return true;
}
//---------------------------------------------------------------------------------------------
EventDesiredResult< CTFBot > CTFBotSpyInfiltrate::OnStuck( CTFBot *me )
{
m_hideArea = NULL;
m_findHidingSpotTimer.Invalidate();
return TryContinue( RESULT_TRY );
}
//---------------------------------------------------------------------------------------------
EventDesiredResult< CTFBot > CTFBotSpyInfiltrate::OnTerritoryCaptured( CTFBot *me, int territoryID )
{
// enemy spawn likely changed - find new hiding spot after internal data has updated
m_hideArea = NULL;
m_findHidingSpotTimer.Start( 5.0f );
return TryContinue( RESULT_TRY );
}
//---------------------------------------------------------------------------------------------
EventDesiredResult< CTFBot > CTFBotSpyInfiltrate::OnTerritoryLost( CTFBot *me, int territoryID )
{
// enemy spawn likely changed - find new hiding spot after internal data has updated
m_hideArea = NULL;
m_findHidingSpotTimer.Start( 5.0f );
return TryContinue( RESULT_TRY );
}
//---------------------------------------------------------------------------------------------
QueryResultType CTFBotSpyInfiltrate::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
{
return ANSWER_NO;
}