mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-14 20:56:56 +00:00
1
This commit is contained in:
@@ -0,0 +1,412 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_attack.cpp
|
||||
// Backstab or pistol, as appropriate
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_attack.h"
|
||||
#include "bot/behavior/tf_bot_retreat_to_cover.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_sap.h"
|
||||
|
||||
#include "nav_mesh.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
ConVar tf_bot_spy_knife_range( "tf_bot_spy_knife_range", "300", FCVAR_CHEAT, "If threat is closer than this, prefer our knife" );
|
||||
ConVar tf_bot_spy_change_target_range_threshold( "tf_bot_spy_change_target_range_threshold", "300", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotSpyAttack::CTFBotSpyAttack( CTFPlayer *victim ) : m_path( ChasePath::LEAD_SUBJECT )
|
||||
{
|
||||
m_victim = victim;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyAttack::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
m_isCoverBlown = false;
|
||||
|
||||
if ( m_victim.Get() )
|
||||
{
|
||||
me->GetVisionInterface()->AddKnownEntity( m_victim );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyAttack::Update( CTFBot *me, float interval )
|
||||
{
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetKnown( m_victim );
|
||||
|
||||
// opportunistically attack closer threat if they are much closer to us than our existing threat
|
||||
const CKnownEntity *closestThreat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
|
||||
if ( !threat )
|
||||
{
|
||||
threat = closestThreat;
|
||||
m_isCoverBlown = false;
|
||||
if ( closestThreat )
|
||||
{
|
||||
m_victim = ToTFPlayer( closestThreat->GetEntity() );
|
||||
}
|
||||
}
|
||||
else if ( closestThreat &&
|
||||
closestThreat->GetEntity() &&
|
||||
closestThreat != threat )
|
||||
{
|
||||
float rangeToCurrentThreat = me->GetRangeTo( threat->GetLastKnownPosition() );
|
||||
float rangeToNewThreat = me->GetRangeTo( closestThreat->GetLastKnownPosition() );
|
||||
|
||||
if ( rangeToCurrentThreat - rangeToNewThreat > tf_bot_spy_change_target_range_threshold.GetFloat() )
|
||||
{
|
||||
if ( closestThreat->GetEntity()->IsPlayer() )
|
||||
{
|
||||
threat = closestThreat;
|
||||
m_victim = ToTFPlayer( closestThreat->GetEntity() );
|
||||
m_isCoverBlown = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !threat || threat->IsObsolete() )
|
||||
{
|
||||
return Done( "No threat" );
|
||||
}
|
||||
|
||||
|
||||
CBaseObject *sapTarget = me->GetNearestKnownSappableTarget();
|
||||
if ( sapTarget && me->IsEntityBetweenTargetAndSelf( sapTarget, threat->GetEntity() ) )
|
||||
{
|
||||
return ChangeTo( new CTFBotSpySap( sapTarget ), "Opportunistically sapping an enemy object between my victim and I" );
|
||||
}
|
||||
|
||||
if ( me->IsAnyEnemySentryAbleToAttackMe() )
|
||||
{
|
||||
m_isCoverBlown = true;
|
||||
|
||||
CBaseCombatWeapon *myGun = me->Weapon_GetWeaponByType( TF_WPN_TYPE_PRIMARY );
|
||||
me->Weapon_Switch( myGun );
|
||||
|
||||
return ChangeTo( new CTFBotRetreatToCover, "Escaping sentry fire!" );
|
||||
}
|
||||
|
||||
CTFPlayer *playerThreat = ToTFPlayer( threat->GetEntity() );
|
||||
if ( !playerThreat )
|
||||
{
|
||||
return Done( "Current 'threat' is not a player or a building?" );
|
||||
}
|
||||
|
||||
// remember who we are attacking (in case we changed our minds)
|
||||
m_victim = playerThreat;
|
||||
|
||||
// uncloak so we can attack
|
||||
if ( me->m_Shared.IsStealthed() && m_decloakTimer.IsElapsed() )
|
||||
{
|
||||
me->PressAltFireButton();
|
||||
m_decloakTimer.Start( 1.0f );
|
||||
}
|
||||
|
||||
bool isKnifeFight = false;
|
||||
|
||||
if ( me->m_Shared.InCond( TF_COND_DISGUISED ) ||
|
||||
me->m_Shared.InCond( TF_COND_DISGUISING ) ||
|
||||
me->m_Shared.IsStealthed() )
|
||||
{
|
||||
isKnifeFight = true;
|
||||
}
|
||||
|
||||
Vector playerThreatForward;
|
||||
playerThreat->EyeVectors( &playerThreatForward );
|
||||
|
||||
Vector toPlayerThreat = playerThreat->GetAbsOrigin() - me->GetAbsOrigin();
|
||||
float threatRange = toPlayerThreat.NormalizeInPlace();
|
||||
|
||||
float behindTolerance = 0.0f;
|
||||
|
||||
switch( me->GetDifficulty() )
|
||||
{
|
||||
case CTFBot::EASY: behindTolerance = 0.9f; break;
|
||||
case CTFBot::NORMAL: behindTolerance = 0.7071f; break;
|
||||
case CTFBot::HARD: behindTolerance = 0.2f; break;
|
||||
case CTFBot::EXPERT: behindTolerance = 0.0f; break;
|
||||
}
|
||||
|
||||
if ( TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
behindTolerance = 0.7071f;
|
||||
}
|
||||
|
||||
bool isBehindVictim = DotProduct( playerThreatForward, toPlayerThreat ) > behindTolerance;
|
||||
|
||||
// easy Spies always think they're in position to backstab
|
||||
if ( me->GetDifficulty() == CTFBot::EASY )
|
||||
{
|
||||
isBehindVictim = true;
|
||||
}
|
||||
|
||||
if ( threatRange < tf_bot_spy_knife_range.GetFloat() )
|
||||
{
|
||||
isKnifeFight = true;
|
||||
}
|
||||
else if ( threat->IsVisibleInFOVNow() && isBehindVictim )
|
||||
{
|
||||
// they are facing away from us - go for the backstab
|
||||
isKnifeFight = true;
|
||||
}
|
||||
|
||||
// does my threat know I'm a Spy?
|
||||
if ( me->IsThreatAimingTowardMe( playerThreat, 0.99f ) && me->GetTimeSinceLastInjury( GetEnemyTeam( me->GetTeamNumber() ) ) < 1.0f )
|
||||
{
|
||||
m_isCoverBlown |= ( playerThreat->GetTimeSinceWeaponFired() < 0.25f );
|
||||
}
|
||||
|
||||
if ( m_isCoverBlown ||
|
||||
me->m_Shared.InCond( TF_COND_BURNING ) ||
|
||||
me->m_Shared.InCond( TF_COND_URINE ) ||
|
||||
me->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ||
|
||||
me->m_Shared.InCond( TF_COND_BLEEDING ) )
|
||||
{
|
||||
isKnifeFight = false;
|
||||
}
|
||||
|
||||
CBaseCombatWeapon *myGun = me->Weapon_GetWeaponByType( isKnifeFight ? TF_WPN_TYPE_MELEE : TF_WPN_TYPE_PRIMARY );
|
||||
me->Weapon_Switch( myGun );
|
||||
|
||||
CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
|
||||
bool isMovingTowardVictim = true;
|
||||
|
||||
if ( myWeapon && myWeapon->IsMeleeWeapon() )
|
||||
{
|
||||
if ( threat->IsVisibleInFOVNow() )
|
||||
{
|
||||
const float circleStrafeRange = 250.0f;
|
||||
|
||||
if ( threatRange < circleStrafeRange )
|
||||
{
|
||||
// we're close - aim our stab attack
|
||||
me->GetBodyInterface()->AimHeadTowards( playerThreat, IBody::MANDATORY, 0.1f, NULL, "Aiming my stab!" );
|
||||
|
||||
if ( !isBehindVictim )
|
||||
{
|
||||
// circle around our victim to get behind them
|
||||
Vector myForward;
|
||||
me->EyeVectors( &myForward );
|
||||
|
||||
Vector cross;
|
||||
CrossProduct( playerThreatForward, myForward, cross );
|
||||
|
||||
if ( cross.z < 0.0f )
|
||||
{
|
||||
me->PressRightButton();
|
||||
}
|
||||
else
|
||||
{
|
||||
me->PressLeftButton();
|
||||
}
|
||||
|
||||
// don't continue to close in if we're already very close so we don't bump them and give ourselves away
|
||||
if ( threatRange < 100.0f )
|
||||
{
|
||||
isMovingTowardVictim = false;
|
||||
}
|
||||
}
|
||||
else if ( TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
if ( m_chuckleTimer.IsElapsed() )
|
||||
{
|
||||
m_chuckleTimer.Start( 1.0f );
|
||||
me->EmitSound( "Spy.MVM_Chuckle" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( threatRange < me->GetDesiredAttackRange() )
|
||||
{
|
||||
// if we're still disguised, go for the backstab
|
||||
if ( me->m_Shared.InCond( TF_COND_DISGUISED ) )
|
||||
{
|
||||
if ( isBehindVictim || m_isCoverBlown )
|
||||
{
|
||||
// we're behind them (or they're onto us) - backstab!
|
||||
me->PressFireButton();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're exposed - stab! stab! stab!
|
||||
me->PressFireButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// aim our pistol
|
||||
me->GetBodyInterface()->AimHeadTowards( playerThreat, IBody::MANDATORY, 0.1f, NULL, "Aiming my pistol" );
|
||||
}
|
||||
|
||||
if ( isMovingTowardVictim )
|
||||
{
|
||||
// pursue the threat. if not visible, go to the last known position
|
||||
if ( !threat->IsVisibleRecently() ||
|
||||
me->IsRangeGreaterThan( threat->GetEntity()->GetAbsOrigin(), me->GetDesiredAttackRange() ) ||
|
||||
!me->IsLineOfFireClear( threat->GetEntity()->EyePosition() ) )
|
||||
{
|
||||
// if we're at the threat's last known position and he's still not visible, we lost him
|
||||
if ( !threat->IsVisibleRecently() )
|
||||
{
|
||||
if ( me->IsRangeLessThan( threat->GetLastKnownPosition(), 20.0f ) )
|
||||
{
|
||||
me->GetVisionInterface()->ForgetEntity( threat->GetEntity() );
|
||||
return Done( "I lost my target!" );
|
||||
}
|
||||
}
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Update( me, threat->GetEntity(), cost );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyAttack::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_victim = NULL;
|
||||
m_path.Invalidate();
|
||||
m_isCoverBlown = false;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotSpyAttack::OnStuck( CTFBot *me )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotSpyAttack::OnInjured( CTFBot *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( me->IsEnemy( info.GetAttacker() ) )
|
||||
{
|
||||
if ( !me->m_Shared.InCond( TF_COND_DISGUISED ) )
|
||||
{
|
||||
// hurt by an enemy and exposed as a spy - flee!
|
||||
m_isCoverBlown = true;
|
||||
|
||||
CBaseCombatWeapon *myGun = me->Weapon_GetWeaponByType( TF_WPN_TYPE_PRIMARY );
|
||||
me->Weapon_Switch( myGun );
|
||||
|
||||
return TryChangeTo( new CTFBotRetreatToCover, RESULT_IMPORTANT, "Time to get out of here!" );
|
||||
}
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotSpyAttack::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result )
|
||||
{
|
||||
if ( me->IsEnemy( other ) && other->MyCombatCharacterPointer() )
|
||||
{
|
||||
if ( other->MyCombatCharacterPointer()->IsLookingTowards( me ) )
|
||||
{
|
||||
m_isCoverBlown = true;
|
||||
}
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyAttack::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyAttack::ShouldHurry( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyAttack::ShouldAttack( const INextBot *meBot, const CKnownEntity *them ) const
|
||||
{
|
||||
CTFBot *me = ToTFBot( meBot->GetEntity() );
|
||||
|
||||
if ( m_isCoverBlown ||
|
||||
me->m_Shared.InCond( TF_COND_BURNING ) ||
|
||||
me->m_Shared.InCond( TF_COND_URINE ) ||
|
||||
me->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ||
|
||||
me->m_Shared.InCond( TF_COND_BLEEDING ) )
|
||||
{
|
||||
// our cover is blown anyway
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Use this to signal the enemy we are focusing on, so we dont avoid them
|
||||
QueryResultType CTFBotSpyAttack::IsHindrance( const INextBot *me, CBaseEntity *blocker ) const
|
||||
{
|
||||
if ( blocker != IS_ANY_HINDRANCE_POSSIBLE )
|
||||
{
|
||||
if ( blocker && m_victim.Get() && blocker->entindex() == m_victim->entindex() )
|
||||
{
|
||||
// don't avoid this guy
|
||||
return ANSWER_NO;
|
||||
}
|
||||
}
|
||||
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Return the more dangerous of the two threats to 'subject', or NULL if we have no opinion
|
||||
const CKnownEntity * CTFBotSpyAttack::SelectMoreDangerousThreat( const INextBot *meBot,
|
||||
const CBaseCombatCharacter *subject,
|
||||
const CKnownEntity *threat1,
|
||||
const CKnownEntity *threat2 ) const
|
||||
{
|
||||
CTFBot *me = ToTFBot( meBot->GetEntity() );
|
||||
|
||||
if ( me->IsSelf( subject ) )
|
||||
{
|
||||
CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
if ( myWeapon && myWeapon->IsMeleeWeapon() )
|
||||
{
|
||||
// attack the closest victim with my knife
|
||||
if ( me->GetRangeSquaredTo( threat1->GetEntity() ) < me->GetRangeSquaredTo( threat2->GetEntity() ) )
|
||||
{
|
||||
return threat1;
|
||||
}
|
||||
|
||||
return threat2;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_attack.h
|
||||
// Backstab or pistol, as appropriate
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#ifndef TF_BOT_SPY_ATTACK_H
|
||||
#define TF_BOT_SPY_ATTACK_H
|
||||
|
||||
#include "Path/NextBotChasePath.h"
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
class CTFBotSpyAttack : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotSpyAttack( CTFPlayer *victim );
|
||||
virtual ~CTFBotSpyAttack() { }
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnInjured( CTFBot *me, const CTakeDamageInfo &info );
|
||||
virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL );
|
||||
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
virtual QueryResultType ShouldAttack( const INextBot *meBot, const CKnownEntity *them ) const;
|
||||
virtual QueryResultType IsHindrance( const INextBot *me, CBaseEntity *blocker ) const; // use this to signal the enemy we are focusing on, so we dont avoid them
|
||||
|
||||
virtual const CKnownEntity * SelectMoreDangerousThreat( const INextBot *me,
|
||||
const CBaseCombatCharacter *subject,
|
||||
const CKnownEntity *threat1,
|
||||
const CKnownEntity *threat2 ) const; // return the more dangerous of the two threats to 'subject', or NULL if we have no opinion
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpyAttack"; };
|
||||
|
||||
private:
|
||||
CHandle< CTFPlayer > m_victim;
|
||||
ChasePath m_path;
|
||||
bool m_isCoverBlown;
|
||||
CountdownTimer m_chuckleTimer;
|
||||
CountdownTimer m_decloakTimer;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_SPY_ATTACK_H
|
||||
@@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_backstab.cpp
|
||||
// Chase behind a victim and backstab them
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_backstab.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyBackstab::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyBackstab::Update( CTFBot *me, float interval )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyBackstab::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_backstab.h
|
||||
// Chase behind a victim and backstab them
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#ifndef TF_BOT_SPY_BACKSTAB_H
|
||||
#define TF_BOT_SPY_BACKSTAB_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotSpyBackstab : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpyBackstab"; };
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // TF_BOT_SPY_BACKSTAB_H
|
||||
@@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_escape.cpp
|
||||
// Flee!
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_escape.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyEscape::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyEscape::Update( CTFBot *me, float interval )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyEscape::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_escape.h
|
||||
// Flee!
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#ifndef TF_BOT_SPY_ESCAPE_H
|
||||
#define TF_BOT_SPY_ESCAPE_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotSpyEscape : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpyEscape"; };
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // TF_BOT_SPY_ESCAPE_H
|
||||
@@ -0,0 +1,236 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_hide.cpp
|
||||
// Move to a hiding spot
|
||||
// Michael Booth, September 2011
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_hide.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_lurk.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_attack.h"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotSpyHide::CTFBotSpyHide( CTFPlayer *victim )
|
||||
{
|
||||
m_initialVictim = victim;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyHide::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_hidingSpot = NULL;
|
||||
m_findTimer.Invalidate();
|
||||
m_isAtGoal = false;
|
||||
|
||||
CTFNavArea *myArea = me->GetLastKnownArea();
|
||||
|
||||
int enemyTeam = GetEnemyTeam( me->GetTeamNumber() );
|
||||
|
||||
m_incursionThreshold = myArea ? myArea->GetIncursionDistance( enemyTeam ) : FLT_MAX;
|
||||
if ( m_incursionThreshold < 0.0f )
|
||||
{
|
||||
m_incursionThreshold = FLT_MAX;
|
||||
}
|
||||
|
||||
m_talkTimer.Start( RandomFloat( 5.0f, 10.0f ) );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyHide::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( m_initialVictim != NULL && !me->GetVisionInterface()->IsIgnored( m_initialVictim ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotSpyAttack( m_initialVictim ), "Going after our initial victim" );
|
||||
}
|
||||
|
||||
// go after victims we've gotten behind
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->GetTimeSinceLastKnown() < 3.0f )
|
||||
{
|
||||
CTFPlayer *victim = ToTFPlayer( threat->GetEntity() );
|
||||
if ( victim )
|
||||
{
|
||||
const float attackRange = 750.0f;
|
||||
if ( me->IsRangeLessThan( victim, attackRange ) )
|
||||
{
|
||||
if ( !victim->IsLookingTowards( me ) || victim->IsFiringWeapon() )
|
||||
{
|
||||
return SuspendFor( new CTFBotSpyAttack( victim ), "Opportunistic attack or self defense!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_talkTimer.IsElapsed() )
|
||||
{
|
||||
m_talkTimer.Start( RandomFloat( 5.0f, 10.0f ) );
|
||||
me->EmitSound( "Spy.TeaseVictim" );
|
||||
}
|
||||
|
||||
if ( m_isAtGoal )
|
||||
{
|
||||
// Quiet everyone! We are hiding now!
|
||||
CTFNavArea *myArea = me->GetLastKnownArea();
|
||||
if ( myArea )
|
||||
{
|
||||
int enemyTeam = GetEnemyTeam( me->GetTeamNumber() );
|
||||
|
||||
m_incursionThreshold = myArea->GetIncursionDistance( enemyTeam );
|
||||
}
|
||||
|
||||
return SuspendFor( new CTFBotSpyLurk, "Reached hiding spot - lurking" );
|
||||
}
|
||||
|
||||
if ( m_hidingSpot == NULL && m_findTimer.IsElapsed() )
|
||||
{
|
||||
FindHidingSpot( me );
|
||||
}
|
||||
|
||||
// move to our hiding spot
|
||||
m_path.Update( me );
|
||||
|
||||
// path following may invalidate our hiding spot (OnMoveToFailure())
|
||||
if ( m_hidingSpot == NULL )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 0.3f, 0.5f ) );
|
||||
|
||||
CTFBotPathCost cost( me, SAFEST_ROUTE );
|
||||
m_path.Compute( me, m_hidingSpot->GetPosition(), cost );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyHide::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_hidingSpot = NULL;
|
||||
m_isAtGoal = false;
|
||||
m_initialVictim = NULL;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotSpyHide::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
m_isAtGoal = true;
|
||||
|
||||
return TryContinue( RESULT_CRITICAL );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotSpyHide::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
m_hidingSpot = NULL;
|
||||
m_isAtGoal = false;
|
||||
|
||||
return TryContinue( RESULT_IMPORTANT );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyHide::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
struct IncursionEntry_t
|
||||
{
|
||||
int team;
|
||||
CTFNavArea *area;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class SpyHideIncursionDistanceLess
|
||||
{
|
||||
public:
|
||||
bool Less( const IncursionEntry_t &src1, const IncursionEntry_t &src2, void *pCtx )
|
||||
{
|
||||
return src1.area->GetIncursionDistance( src1.team ) < src2.area->GetIncursionDistance( src2.team );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CTFBotSpyHide::FindHidingSpot( CTFBot *me )
|
||||
{
|
||||
CTFNavArea *myArea = me->GetLastKnownArea();
|
||||
if ( !myArea )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hidingSpot = NULL;
|
||||
|
||||
// find a spot to hide
|
||||
const float maxRange = 3500.0f;
|
||||
CUtlVector< CNavArea * > nearbyVector;
|
||||
CollectSurroundingAreas( &nearbyVector, me->GetLastKnownArea(), maxRange,
|
||||
500.0f, 500.0f );
|
||||
|
||||
CUtlSortVector< IncursionEntry_t, SpyHideIncursionDistanceLess > hidingSpotVector;
|
||||
|
||||
float maxIncursion = m_incursionThreshold + 1000.0f;
|
||||
|
||||
int enemyTeam = GetEnemyTeam( me->GetTeamNumber() );
|
||||
|
||||
// if we are standing in an area the defenders can't reach, don't limit
|
||||
if ( myArea->GetIncursionDistance( enemyTeam ) < 0.0f )
|
||||
{
|
||||
maxIncursion = 9999999;
|
||||
}
|
||||
|
||||
for( int i=0; i<nearbyVector.Count(); ++i )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)nearbyVector[i];
|
||||
|
||||
if ( area->GetHidingSpots()->Count() <= 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( area->GetIncursionDistance( enemyTeam ) < 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// keep pushing inwards towards defender's spawn
|
||||
if ( area->GetIncursionDistance( enemyTeam ) > maxIncursion )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IncursionEntry_t entry = { enemyTeam, area };
|
||||
hidingSpotVector.Insert( entry );
|
||||
}
|
||||
|
||||
if ( hidingSpotVector.Count() <= 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// penetrate as far as we can
|
||||
int which = RandomInt( 0, hidingSpotVector.Count()/2 );
|
||||
CTFNavArea *whichArea = hidingSpotVector[ which ].area;
|
||||
|
||||
const HidingSpotVector *hidingSpots = whichArea->GetHidingSpots();
|
||||
|
||||
m_hidingSpot = hidingSpots->Element( RandomInt( 0, hidingSpots->Count()-1 ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_hide.h
|
||||
// Move to a hiding spot
|
||||
// Michael Booth, September 2011
|
||||
|
||||
#ifndef TF_BOT_SPY_HIDE
|
||||
#define TF_BOT_SPY_HIDE
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotSpyHide : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotSpyHide( CTFPlayer *victim = NULL );
|
||||
virtual ~CTFBotSpyHide() { }
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpyHide"; };
|
||||
|
||||
private:
|
||||
CHandle< CTFPlayer > m_initialVictim;
|
||||
|
||||
HidingSpot *m_hidingSpot;
|
||||
bool FindHidingSpot( CTFBot *me );
|
||||
CountdownTimer m_findTimer;
|
||||
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
bool m_isAtGoal;
|
||||
|
||||
float m_incursionThreshold;
|
||||
|
||||
CountdownTimer m_talkTimer;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_SPY_HIDE
|
||||
@@ -0,0 +1,335 @@
|
||||
//========= 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;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_infiltrate.h
|
||||
// Move into position behind enemy lines and wait for victims
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#ifndef TF_BOT_SPY_INFILTRATE_H
|
||||
#define TF_BOT_SPY_INFILTRATE_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotSpyInfiltrate : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
virtual ActionResult< CTFBot > OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpyInfiltrate"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_repathTimer;
|
||||
PathFollower m_path;
|
||||
|
||||
CTFNavArea *m_hideArea;
|
||||
bool FindHidingSpot( CTFBot *me );
|
||||
CountdownTimer m_findHidingSpotTimer;
|
||||
|
||||
CountdownTimer m_waitTimer;
|
||||
|
||||
bool m_hasEnteredCombatZone;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_SPY_INFILTRATE_H
|
||||
@@ -0,0 +1,160 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_leave_spawn_room.cpp
|
||||
// Assume the enemy is watching our spawn - escape it
|
||||
// Michael Booth, September 2011
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_leave_spawn_room.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_hide.h"
|
||||
|
||||
|
||||
extern bool IsSpaceToSpawnHere( const Vector &where );
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool TeleportNearVictim( CTFBot *me, CTFPlayer *victim, int attempt )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotSpyLeaveSpawnRoom::TeleportNearVictim", "NextBot" );
|
||||
|
||||
if ( !victim )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CUtlVector< CTFNavArea * > ambushVector; // vector of hidden but near-to-victim areas
|
||||
|
||||
if ( !victim->GetLastKnownArea() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const float maxSurroundTravelRange = 6000.0f;
|
||||
|
||||
float surroundTravelRange = 1500.0f + 500.0f * attempt;
|
||||
if ( surroundTravelRange > maxSurroundTravelRange )
|
||||
{
|
||||
surroundTravelRange = maxSurroundTravelRange;
|
||||
}
|
||||
|
||||
CUtlVector< CNavArea * > areaVector;
|
||||
|
||||
// collect walkable areas surrounding this victim
|
||||
CollectSurroundingAreas( &areaVector, victim->GetLastKnownArea(), surroundTravelRange, StepHeight, StepHeight );
|
||||
|
||||
// keep subset that isn't visible to the victim's team
|
||||
for( int j=0; j<areaVector.Count(); ++j )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)areaVector[j];
|
||||
|
||||
if ( !area->IsValidForWanderingPopulation() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( area->IsPotentiallyVisibleToTeam( victim->GetTeamNumber() ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ambushVector.AddToTail( area );
|
||||
}
|
||||
|
||||
if ( ambushVector.Count() == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxTries = MIN( 10, ambushVector.Count() );
|
||||
|
||||
for( int retry=0; retry<maxTries; ++retry )
|
||||
{
|
||||
int which = RandomInt( 0, ambushVector.Count()-1 );
|
||||
Vector where = ambushVector[ which ]->GetCenter() + Vector( 0, 0, StepHeight );
|
||||
|
||||
if ( IsSpaceToSpawnHere( where ) )
|
||||
{
|
||||
me->Teleport( &where, &vec3_angle, &vec3_origin );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyLeaveSpawnRoom::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
// disguise as enemy team
|
||||
me->DisguiseAsMemberOfEnemyTeam();
|
||||
|
||||
// cloak
|
||||
me->PressAltFireButton();
|
||||
|
||||
// wait a few moments to guarantee a minimum time between announcing Spies and their attack
|
||||
m_waitTimer.Start( 2.0f + RandomFloat( 0.0f, 1.0f ) );
|
||||
|
||||
m_attempt = 0;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyLeaveSpawnRoom::Update( CTFBot *me, float interval )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotSpyLeaveSpawnRoom::Update", "NextBot" );
|
||||
|
||||
if ( m_waitTimer.IsElapsed() )
|
||||
{
|
||||
CTFPlayer *victim = NULL;
|
||||
|
||||
CUtlVector< CTFPlayer * > enemyVector;
|
||||
CollectPlayers( &enemyVector, GetEnemyTeam( me->GetTeamNumber() ), COLLECT_ONLY_LIVING_PLAYERS );
|
||||
|
||||
// randomly shuffle our enemies
|
||||
CUtlVector< CTFPlayer * > shuffleVector;
|
||||
shuffleVector = enemyVector;
|
||||
int n = shuffleVector.Count();
|
||||
while( n > 1 )
|
||||
{
|
||||
int k = RandomInt( 0, n-1 );
|
||||
n--;
|
||||
|
||||
CTFPlayer *tmp = shuffleVector[n];
|
||||
shuffleVector[n] = shuffleVector[k];
|
||||
shuffleVector[k] = tmp;
|
||||
}
|
||||
|
||||
for( int i=0; i<shuffleVector.Count(); ++i )
|
||||
{
|
||||
if ( TeleportNearVictim( me, shuffleVector[i], m_attempt ) )
|
||||
{
|
||||
victim = shuffleVector[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a victim, try again in a bit
|
||||
if ( !victim )
|
||||
{
|
||||
m_waitTimer.Start( 1.0f );
|
||||
|
||||
++m_attempt;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
return ChangeTo( new CTFBotSpyHide( victim ), "Hiding!" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyLeaveSpawnRoom::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_leave_spawn_room.h
|
||||
// Assume the enemy is watching our spawn - escape it
|
||||
// Michael Booth, September 2011
|
||||
|
||||
#ifndef TF_BOT_LEAVE_SPAWN_ROOM_H
|
||||
#define TF_BOT_LEAVE_SPAWN_ROOM_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotSpyLeaveSpawnRoom : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpyLeaveSpawnRoom"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_waitTimer;
|
||||
int m_attempt;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_LEAVE_SPAWN_ROOM_H
|
||||
@@ -0,0 +1,91 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_lurk.cpp
|
||||
// Wait for victims
|
||||
// Michael Booth, September 2011
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_lurk.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 "bot/behavior/spy/tf_bot_spy_sap.h"
|
||||
|
||||
#include "nav_mesh.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
extern ConVar tf_bot_debug_spy;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyLurk::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
// cloak
|
||||
if ( !me->m_Shared.IsStealthed() )
|
||||
{
|
||||
me->PressAltFireButton();
|
||||
}
|
||||
|
||||
// disguise as the enemy team
|
||||
me->DisguiseAsMemberOfEnemyTeam();
|
||||
|
||||
m_lurkTimer.Start( RandomFloat( 3.0f, 5.0f ) );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpyLurk::Update( CTFBot *me, float interval )
|
||||
{
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->GetEntity() )
|
||||
{
|
||||
CBaseObject *enemyObject = dynamic_cast< CBaseObject * >( threat->GetEntity() );
|
||||
if ( enemyObject && !enemyObject->HasSapper() && me->IsEnemy( enemyObject ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotSpySap( enemyObject ), "Sapping an enemy object" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( me->GetEnemySentry() != NULL && !me->GetEnemySentry()->HasSapper() )
|
||||
{
|
||||
return SuspendFor( new CTFBotSpySap( me->GetEnemySentry() ), "Sapping a Sentry" );
|
||||
}
|
||||
|
||||
if ( m_lurkTimer.IsElapsed() )
|
||||
{
|
||||
return Done( "Lost patience with my hiding spot" );
|
||||
}
|
||||
|
||||
CTFNavArea *myArea = me->GetLastKnownArea();
|
||||
|
||||
if ( !myArea )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// go after victims we've gotten behind
|
||||
if ( threat && threat->GetTimeSinceLastKnown() < 3.0f )
|
||||
{
|
||||
CTFPlayer *victim = ToTFPlayer( threat->GetEntity() );
|
||||
if ( victim )
|
||||
{
|
||||
if ( !victim->IsLookingTowards( me ) )
|
||||
{
|
||||
return ChangeTo( new CTFBotSpyAttack( victim ), "Going after a backstab victim" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpyLurk::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_lurk.h
|
||||
// Wait for victims
|
||||
// Michael Booth, September 2011
|
||||
|
||||
#ifndef TF_BOT_SPY_LURK_H
|
||||
#define TF_BOT_SPY_LURK_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotSpyLurk : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpyLurk"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_lurkTimer;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_SPY_LURK_H
|
||||
@@ -0,0 +1,250 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_sap.cpp
|
||||
// Sap nearby enemy buildings
|
||||
// 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_sap.h"
|
||||
#include "bot/behavior/tf_bot_approach_object.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_attack.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
extern ConVar tf_bot_debug_spy;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotSpySap::CTFBotSpySap( CBaseObject *sapTarget )
|
||||
{
|
||||
m_sapTarget = sapTarget;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpySap::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
me->StopLookingAroundForEnemies();
|
||||
|
||||
// uncloak so we can sap
|
||||
if ( me->m_Shared.IsStealthed() )
|
||||
{
|
||||
me->PressAltFireButton();
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpySap::Update( CTFBot *me, float interval )
|
||||
{
|
||||
CBaseObject *newSapTarget = me->GetNearestKnownSappableTarget();
|
||||
|
||||
if ( newSapTarget )
|
||||
{
|
||||
m_sapTarget = newSapTarget;
|
||||
}
|
||||
|
||||
if ( m_sapTarget == NULL )
|
||||
{
|
||||
return Done( "Sap target gone" );
|
||||
}
|
||||
|
||||
CTFPlayer *victim = NULL;
|
||||
|
||||
CUtlVector< CKnownEntity > knownVector;
|
||||
me->GetVisionInterface()->CollectKnownEntities( &knownVector );
|
||||
|
||||
for( int i=0; i<knownVector.Count(); ++i )
|
||||
{
|
||||
CTFPlayer *playerThreat = ToTFPlayer( knownVector[i].GetEntity() );
|
||||
if ( playerThreat && me->IsEnemy( playerThreat ) )
|
||||
{
|
||||
victim = playerThreat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// opportunistic backstab if engineer is between me and my sap target
|
||||
if ( victim && victim->IsPlayerClass( TF_CLASS_ENGINEER ) )
|
||||
{
|
||||
const float nearbyRange = 150.0f;
|
||||
if ( m_sapTarget->GetOwner() == victim && me->IsRangeLessThan( victim, nearbyRange ) )
|
||||
{
|
||||
if ( me->IsEntityBetweenTargetAndSelf( victim, m_sapTarget ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotSpyAttack( victim ), "Backstabbing the engineer before I sap his buildings" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float sapRange = 40.0f;
|
||||
|
||||
if ( me->IsRangeLessThan( m_sapTarget, 2.0f * sapRange ) )
|
||||
{
|
||||
// switch to our sapper and spam it
|
||||
CBaseCombatWeapon *mySapper = me->Weapon_GetWeaponByType( TF_WPN_TYPE_BUILDING );
|
||||
if ( !mySapper )
|
||||
{
|
||||
return Done( "I have no sapper" );
|
||||
}
|
||||
|
||||
me->Weapon_Switch( mySapper );
|
||||
|
||||
// uncloak
|
||||
if ( me->m_Shared.IsStealthed() )
|
||||
{
|
||||
me->PressAltFireButton();
|
||||
}
|
||||
|
||||
// sap our target
|
||||
me->GetBodyInterface()->AimHeadTowards( m_sapTarget, IBody::MANDATORY, 0.1f, NULL, "Aiming my sapper" );
|
||||
|
||||
me->PressFireButton();
|
||||
}
|
||||
|
||||
if ( me->IsRangeGreaterThan( m_sapTarget, sapRange ) )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
if ( m_path.Compute( me, m_sapTarget, cost ) == false )
|
||||
{
|
||||
return Done( "No path to sap target!" );
|
||||
}
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// if our target is sapped, look for other nearby buildings to sap
|
||||
if ( m_sapTarget->HasSapper() )
|
||||
{
|
||||
CBaseObject *nextTarget = me->GetNearestKnownSappableTarget();
|
||||
if ( nextTarget )
|
||||
{
|
||||
m_sapTarget = nextTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
// everything is sapped - explicitly attack nearby enemy Engineers
|
||||
if ( victim && victim->IsPlayerClass( TF_CLASS_ENGINEER ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotSpyAttack( victim ), "Attacking an engineer" );
|
||||
}
|
||||
|
||||
return Done( "All targets sapped" );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotSpySap::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
me->StartLookingAroundForEnemies();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpySap::OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
me->StartLookingAroundForEnemies();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSpySap::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
me->StopLookingAroundForEnemies();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotSpySap::OnStuck( CTFBot *me )
|
||||
{
|
||||
return TryDone( RESULT_CRITICAL, "I'm stuck, probably on a sapped building that hasn't exploded yet" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpySap::ShouldAttack( const INextBot *meBot, const CKnownEntity *them ) const
|
||||
{
|
||||
CTFBot *me = ToTFBot( meBot->GetEntity() );
|
||||
|
||||
if ( m_sapTarget && !m_sapTarget->HasSapper() )
|
||||
{
|
||||
// mission not accomplished
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
if ( !me->m_Shared.InCond( TF_COND_DISGUISED ) &&
|
||||
!me->m_Shared.InCond( TF_COND_DISGUISING ) &&
|
||||
!me->m_Shared.IsStealthed() )
|
||||
{
|
||||
// our cover is blown!
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
// if we've sapped, attack
|
||||
return AreAllDangerousSentriesSapped( me ) ? ANSWER_YES : ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Don't avoid enemies when we're going in for the sap
|
||||
QueryResultType CTFBotSpySap::IsHindrance( const INextBot *me, CBaseEntity *blocker ) const
|
||||
{
|
||||
if ( m_sapTarget.Get() && me->IsRangeLessThan( m_sapTarget, 300.0f ) )
|
||||
{
|
||||
// we're almost to our sap target - don't avoid anyone
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
// avoid everyone while we move to our sap target
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotSpySap::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CTFBotSpySap::AreAllDangerousSentriesSapped( CTFBot *me ) const
|
||||
{
|
||||
CUtlVector< CKnownEntity > knownVector;
|
||||
me->GetVisionInterface()->CollectKnownEntities( &knownVector );
|
||||
|
||||
for( int i=0; i<knownVector.Count(); ++i )
|
||||
{
|
||||
CBaseObject *enemyObject = dynamic_cast< CBaseObject * >( knownVector[i].GetEntity() );
|
||||
if ( enemyObject && enemyObject->ObjectType() == OBJ_SENTRYGUN && !enemyObject->HasSapper() && me->IsEnemy( enemyObject ) )
|
||||
{
|
||||
// this is an active enemy sentry, are we in range and line of fire?
|
||||
if ( me->IsRangeLessThan( enemyObject, SENTRY_MAX_RANGE ) && me->IsLineOfFireClear( enemyObject ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_spy_sap.h
|
||||
// Sap nearby enemy buildings
|
||||
// Michael Booth, June 2010
|
||||
|
||||
#ifndef TF_BOT_SPY_SAP_H
|
||||
#define TF_BOT_SPY_SAP_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotSpySap : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotSpySap( CBaseObject *sapTarget );
|
||||
virtual ~CTFBotSpySap() { }
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual ActionResult< CTFBot > OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
virtual QueryResultType IsHindrance( const INextBot *me, CBaseEntity *blocker ) const; // use this to signal the enemy we are focusing on, so we dont avoid them
|
||||
|
||||
virtual const char *GetName( void ) const { return "SpySap"; };
|
||||
|
||||
private:
|
||||
CHandle< CBaseObject > m_sapTarget;
|
||||
|
||||
CountdownTimer m_repathTimer;
|
||||
PathFollower m_path;
|
||||
|
||||
CBaseObject *GetNearestKnownSappableTarget( CTFBot *me );
|
||||
bool AreAllDangerousSentriesSapped( CTFBot *me ) const;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_SPY_SAP_H
|
||||
Reference in New Issue
Block a user