summaryrefslogtreecommitdiff
path: root/game/server/tf/bot/behavior/scenario/capture_point
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/bot/behavior/scenario/capture_point
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/tf/bot/behavior/scenario/capture_point')
-rw-r--r--game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.cpp231
-rw-r--r--game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.h36
-rw-r--r--game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.cpp442
-rw-r--r--game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.h48
-rw-r--r--game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.cpp247
-rw-r--r--game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h40
6 files changed, 1044 insertions, 0 deletions
diff --git a/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.cpp b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.cpp
new file mode 100644
index 0000000..6d8648b
--- /dev/null
+++ b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.cpp
@@ -0,0 +1,231 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// tf_bot_capture_point.cpp
+// Move to and try to capture the next point
+// Michael Booth, February 2009
+
+#include "cbase.h"
+#include "nav_mesh.h"
+#include "tf_player.h"
+#include "tf_gamerules.h"
+#include "team_control_point_master.h"
+#include "trigger_area_capture.h"
+#include "bot/tf_bot.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_capture_point.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_defend_point.h"
+#include "bot/behavior/tf_bot_seek_and_destroy.h"
+
+
+extern ConVar tf_bot_path_lookahead_range;
+ConVar tf_bot_offense_must_push_time( "tf_bot_offense_must_push_time", "120", FCVAR_CHEAT, "If timer is less than this, bots will push hard to cap" );
+
+ConVar tf_bot_capture_seek_and_destroy_min_duration( "tf_bot_capture_seek_and_destroy_min_duration", "15", FCVAR_CHEAT, "If a capturing bot decides to go hunting, this is the min duration he will hunt for before reconsidering" );
+ConVar tf_bot_capture_seek_and_destroy_max_duration( "tf_bot_capture_seek_and_destroy_max_duration", "30", FCVAR_CHEAT, "If a capturing bot decides to go hunting, this is the max duration he will hunt for before reconsidering" );
+
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotCapturePoint::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
+{
+ VPROF_BUDGET( "CTFBotCapturePoint::OnStart", "NextBot" );
+
+ m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
+ m_path.Invalidate();
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotCapturePoint::Update( CTFBot *me, float interval )
+{
+ if ( TFGameRules()->InSetup() )
+ {
+ // wait until the gates open, then path
+ m_path.Invalidate();
+ m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
+
+ return Continue();
+ }
+
+ CTeamControlPoint *point = me->GetMyControlPoint();
+
+ if ( point == NULL )
+ {
+ const float roamTime = 10.0f;
+ return SuspendFor( new CTFBotSeekAndDestroy( roamTime ), "Seek and destroy until a point becomes available" );
+ }
+
+ if ( point->GetTeamNumber() == me->GetTeamNumber() )
+ {
+ return ChangeTo( new CTFBotDefendPoint, "We need to defend our point(s)" );
+ }
+
+ const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
+ if ( threat && threat->IsVisibleRecently() )
+ {
+ // prepare to fight
+ me->EquipBestWeaponForThreat( threat );
+ }
+
+ bool isPushingToCapture = ( me->IsPointBeingCaptured( point ) && !me->IsInCombat() ) || // a friend is capturing
+ me->IsCapturingPoint() || // we're capturing
+ // me->m_Shared.InCond( TF_COND_INVULNERABLE ) || // we're ubered
+ TFGameRules()->InOvertime() || // the game is in overtime
+ me->GetTimeLeftToCapture() < tf_bot_offense_must_push_time.GetFloat() || // nearly out of tim
+ TFGameRules()->IsInTraining() || // teach newbies to capture
+ me->IsNearPoint( point );
+
+
+ // if we see an enemy at a good combat range, stop and engage them unless we're running out of time
+ if ( !isPushingToCapture )
+ {
+ if ( threat && threat->IsVisibleRecently() )
+ {
+ return SuspendFor( new CTFBotSeekAndDestroy( RandomFloat( tf_bot_capture_seek_and_destroy_min_duration.GetFloat(), tf_bot_capture_seek_and_destroy_max_duration.GetFloat() ) ), "Too early to capture - hunting" );
+ }
+ }
+
+
+ if ( me->IsCapturingPoint() )
+ {
+ // move around on the point while we capture
+ const CUtlVector< CTFNavArea * > *controlPointAreas = TheTFNavMesh()->GetControlPointAreas( point->GetPointIndex() );
+ if ( controlPointAreas )
+ {
+ if ( controlPointAreas->Count() == 0 )
+ {
+ Assert( controlPointAreas->Count() );
+ Continue(); // this control point has no nav areas for bot to move around
+ }
+
+ // move to a random spot on this control point
+ if ( m_repathTimer.IsElapsed() )
+ {
+ m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
+
+ int which = RandomInt( 0, controlPointAreas->Count() - 1 );
+ CTFNavArea *goalArea = controlPointAreas->Element( which );
+ if ( goalArea )
+ {
+ CTFBotPathCost cost( me, DEFAULT_ROUTE );
+ m_path.Compute( me, goalArea->GetRandomPoint(), cost );
+ }
+ }
+
+ m_path.Update( me );
+ }
+ }
+ else
+ {
+ // move toward the point, periodically repathing to account for changing situation
+ if ( m_repathTimer.IsElapsed() )
+ {
+ VPROF_BUDGET( "CTFBotCapturePoint::Update( repath )", "NextBot" );
+
+ CTFBotPathCost cost( me, SAFEST_ROUTE );
+ m_path.Compute( me, point->GetAbsOrigin(), cost );
+ m_repathTimer.Start( RandomFloat( 2.0f, 3.0f ) );
+ }
+
+ if ( TFGameRules()->IsInTraining() && !me->IsAnyPointBeingCaptured() )
+ {
+ // stop short of capturing until the human trainee starts it
+ if ( m_path.GetLength() < 1000.0f )
+ {
+ // hold here and yell at player to get on the point
+ me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_GO );
+
+ return Continue();
+ }
+ }
+
+ // move towards next capture point
+ m_path.Update( me );
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotCapturePoint::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
+{
+ m_repathTimer.Invalidate();
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotCapturePoint::OnStuck( CTFBot *me )
+{
+ m_repathTimer.Invalidate();
+ me->GetLocomotionInterface()->ClearStuckStatus();
+
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotCapturePoint::OnMoveToSuccess( CTFBot *me, const Path *path )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotCapturePoint::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
+{
+ m_repathTimer.Invalidate();
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotCapturePoint::OnTerritoryContested( CTFBot *me, int territoryID )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotCapturePoint::OnTerritoryCaptured( CTFBot *me, int territoryID )
+{
+ // we got it, move on
+ m_repathTimer.Invalidate();
+
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotCapturePoint::OnTerritoryLost( CTFBot *me, int territoryID )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+QueryResultType CTFBotCapturePoint::ShouldRetreat( const INextBot *bot ) const
+{
+ CTFBot *me = (CTFBot *)bot->GetEntity();
+
+ // if we're running out of time, we have to go for it
+ if ( me->GetTimeLeftToCapture() < tf_bot_offense_must_push_time.GetFloat() )
+ return ANSWER_NO;
+
+ return ANSWER_UNDEFINED;
+}
+
+
+//---------------------------------------------------------------------------------------------
+QueryResultType CTFBotCapturePoint::ShouldHurry( const INextBot *bot ) const
+{
+ CTFBot *me = (CTFBot *)bot->GetEntity();
+
+ // if we're running out of time, we have to go for it
+ if ( me->GetTimeLeftToCapture() < tf_bot_offense_must_push_time.GetFloat() )
+ return ANSWER_YES;
+
+ return ANSWER_UNDEFINED;
+}
+
diff --git a/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.h b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.h
new file mode 100644
index 0000000..af80217
--- /dev/null
+++ b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_capture_point.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// tf_bot_capture_point.h
+// Move to and try to capture the next point
+// Michael Booth, February 2009
+
+#ifndef TF_BOT_CAPTURE_POINT_H
+#define TF_BOT_CAPTURE_POINT_H
+
+#include "Path/NextBotPathFollow.h"
+
+class CTFBotCapturePoint : public Action< CTFBot >
+{
+public:
+ 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 > OnMoveToSuccess( CTFBot *me, const Path *path );
+ virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
+
+ virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
+ virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
+ virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
+
+ 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 const char *GetName( void ) const { return "CapturePoint"; };
+
+private:
+ PathFollower m_path;
+ CountdownTimer m_repathTimer;
+};
+
+#endif // TF_BOT_CAPTURE_POINT_H
diff --git a/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.cpp b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.cpp
new file mode 100644
index 0000000..20c090b
--- /dev/null
+++ b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.cpp
@@ -0,0 +1,442 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// tf_bot_defend_point.h
+// Move to and defend current point from capture
+// Michael Booth, February 2009
+
+#include "cbase.h"
+#include "nav_mesh/tf_nav_mesh.h"
+#include "tf_player.h"
+#include "tf_gamerules.h"
+#include "team_control_point_master.h"
+#include "trigger_area_capture.h"
+#include "bot/tf_bot.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_defend_point.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_capture_point.h"
+#include "bot/behavior/medic/tf_bot_medic_heal.h"
+#include "bot/behavior/tf_bot_attack.h"
+#include "bot/behavior/tf_bot_seek_and_destroy.h"
+#include "bot/behavior/engineer/tf_bot_engineer_build.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h"
+#include "bot/behavior/sniper/tf_bot_sniper_attack.h"
+#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h"
+
+
+extern ConVar tf_bot_path_lookahead_range;
+extern ConVar tf_bot_min_setup_gate_defend_range;
+extern ConVar tf_bot_max_setup_gate_defend_range;
+extern ConVar tf_bot_min_setup_gate_sniper_defend_range;
+extern ConVar tf_bot_offense_must_push_time;
+
+ConVar tf_bot_defense_must_defend_time( "tf_bot_defense_must_defend_time", "300", FCVAR_CHEAT, "If timer is less than this, bots will stay near point and guard" );
+ConVar tf_bot_max_point_defend_range( "tf_bot_max_point_defend_range", "1250", FCVAR_CHEAT, "How far (in travel distance) from the point defending bots will take up positions" );
+ConVar tf_bot_defense_debug( "tf_bot_defense_debug", "0", FCVAR_CHEAT );
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotDefendPoint::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
+{
+ m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
+
+ m_defenseArea = NULL;
+
+ // higher skilled bots prefer to seek and destroy until the time is almost up
+ static float roamChance[ CTFBot::NUM_DIFFICULTY_LEVELS ] = { 10.0f, 50.0f, 75.0f, 90.0f };
+ m_isAllowedToRoam = ( RandomFloat( 0.0f, 100.0f ) < roamChance[ (int)clamp( me->GetDifficulty(), CTFBot::EASY, CTFBot::EXPERT ) ] );
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+/**
+ * Return true if we're in immediate danger of losing the point
+ */
+bool CTFBotDefendPoint::IsPointThreatened( CTFBot *me )
+{
+ CTeamControlPoint *point = me->GetMyControlPoint();
+
+ if ( point == NULL )
+ return false;
+
+ if ( point->LastContestedAt() > 0.0f && ( gpGlobals->curtime - point->LastContestedAt() ) < 5.0f )
+ {
+ // the point is, or was very recently, contested
+ return true;
+ }
+
+ // if we just lost a point, we should fall back and stand on the next point to defend against a rush
+ if ( me->WasPointJustLost() )
+ {
+ return true;
+ }
+
+/*
+ // if an enemy is closer to the point than we are, head them off
+ // TODO: Compare time to reach, not distance
+ const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
+ if ( threat )
+ {
+ const float tolerance = 100.0f;
+
+ float themRange = ( threat->GetLastKnownPosition() - point->GetAbsOrigin() ).Length();
+ float myRange = ( me->GetAbsOrigin() - point->GetAbsOrigin() ).Length();
+ if ( myRange + tolerance > themRange )
+ return true;
+ }
+*/
+
+ return false;
+}
+
+
+//---------------------------------------------------------------------------------------------
+// Are we smart enough to get on the point to block the cap
+bool CTFBotDefendPoint::WillBlockCapture( CTFBot *me ) const
+{
+ if ( TFGameRules()->IsInTraining() )
+ return false;
+
+ if ( me->IsDifficulty( CTFBot::EASY ) )
+ return false;
+
+ if ( me->IsDifficulty( CTFBot::NORMAL ) )
+ {
+ // 50% chance of blocking cap
+ return me->TransientlyConsistentRandomValue() > 0.5f;
+ }
+
+ return true;
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotDefendPoint::Update( CTFBot *me, float interval )
+{
+ // King of the Hill logic
+ CTeamControlPointMaster *master = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
+ if ( master && master->GetNumPoints() == 1 )
+ {
+ // if we don't own the only point, switch to capture behavior
+ CTeamControlPoint *point = master->GetControlPoint( 0 );
+ if ( point && point->GetOwner() != me->GetTeamNumber() )
+ {
+ return ChangeTo( new CTFBotCapturePoint, "We need to capture the point!" );
+ }
+ }
+
+ CTeamControlPoint *point = me->GetMyControlPoint();
+
+ if ( point == NULL )
+ {
+ const float roamTime = 10.0f;
+ return SuspendFor( new CTFBotSeekAndDestroy( roamTime ), "Seek and destroy until a point becomes available" );
+ }
+
+ if ( point->GetTeamNumber() != me->GetTeamNumber() )
+ {
+ return ChangeTo( new CTFBotCapturePoint, "We need to capture our point(s)" );
+ }
+
+ // if point in is danger - get ON the point!
+ // Don't do this in training to keep things easy for the new trainee
+ if ( IsPointThreatened( me ) && WillBlockCapture( me ) )
+ {
+ // point is being captured - get on it!
+ return SuspendFor( new CTFBotDefendPointBlockCapture, "Moving to block point capture!" );
+ }
+
+ // point is safe for the moment
+
+ // if I'm uber'd, go get 'em!
+ if ( me->m_Shared.InCond( TF_COND_INVULNERABLE ) )
+ {
+ const float uberChargeTime = 6.0;
+ return SuspendFor( new CTFBotSeekAndDestroy( uberChargeTime ), "Attacking because I'm uber'd!" );
+ }
+
+ if ( point && point->IsLocked() )
+ {
+ return SuspendFor( new CTFBotSeekAndDestroy, "Seek and destroy until the point unlocks" );
+ }
+
+ if ( m_isAllowedToRoam && me->GetTimeLeftToCapture() > tf_bot_defense_must_defend_time.GetFloat() )
+ {
+ return SuspendFor( new CTFBotSeekAndDestroy( 15.0f ), "Seek and destroy - we have lots of time" );
+ }
+
+ if ( TFGameRules()->InSetup() )
+ {
+ // don't lose patience during setup time
+ m_idleTimer.Reset();
+ }
+
+ // if we see an enemy as we have a melee weapon equipped, chase them down
+ const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
+
+ me->EquipBestWeaponForThreat( threat );
+
+ if ( threat && threat->IsVisibleRecently() )
+ {
+ // we're aware of an enemy
+ m_idleTimer.Reset();
+
+ if ( me->IsPlayerClass( TF_CLASS_PYRO ) )
+ {
+ // go get 'em
+ return SuspendFor( new CTFBotSeekAndDestroy( 15.0f ), "Going after an enemy" );
+ }
+
+ CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon();
+ if ( myWeapon && ( myWeapon->IsMeleeWeapon() || myWeapon->IsWeapon( TF_WEAPON_FLAMETHROWER ) ) )
+ {
+ // TODO: Check if threat is visible and if not, move to last known position
+ CTFBotPathCost cost( me, me->IsPlayerClass( TF_CLASS_PYRO ) ? SAFEST_ROUTE : FASTEST_ROUTE );
+ m_chasePath.Update( me, threat->GetEntity(), cost );
+
+ return Continue();
+ }
+ }
+
+ // choose where we'll defend from
+ if ( m_defenseArea == NULL || m_idleTimer.IsElapsed() )
+ {
+ m_defenseArea = SelectAreaToDefendFrom( me );
+ }
+
+ if ( m_defenseArea )
+ {
+ if ( me->GetLastKnownArea() == m_defenseArea )
+ {
+ // at our defense position
+ if ( CTFBotPrepareStickybombTrap::IsPossible( me ) )
+ {
+ return SuspendFor( new CTFBotPrepareStickybombTrap, "Laying sticky bombs!" );
+ }
+ }
+ else
+ {
+ // move to our desired defense position, repathing periodically to account for changing situation
+ VPROF_BUDGET( "CTFBotDefendPoint::Update( repath )", "NextBot" );
+
+ if ( m_repathTimer.IsElapsed() )
+ {
+ m_repathTimer.Start( RandomFloat( 2.0f, 3.0f ) );
+
+ CTFBotPathCost cost( me, DEFAULT_ROUTE );
+ m_path.Compute( me, m_defenseArea->GetCenter(), cost );
+ }
+
+ m_path.Update( me );
+
+ // we're not idle while we're moving to our defend position
+ m_idleTimer.Reset();
+ }
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotDefendPoint::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
+{
+ // may have lost point - recheck
+ me->ClearMyControlPoint();
+ m_repathTimer.Invalidate();
+ m_path.Invalidate();
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPoint::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPoint::OnStuck( CTFBot *me )
+{
+ m_path.Invalidate();
+ m_defenseArea = SelectAreaToDefendFrom( me );
+ me->GetLocomotionInterface()->ClearStuckStatus();
+
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPoint::OnMoveToSuccess( CTFBot *me, const Path *path )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPoint::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
+{
+ m_path.Invalidate();
+ m_defenseArea = SelectAreaToDefendFrom( me );
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPoint::OnTerritoryContested( CTFBot *me, int territoryID )
+{
+ // handled in the Update() loop
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPoint::OnTerritoryCaptured( CTFBot *me, int territoryID )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPoint::OnTerritoryLost( CTFBot *me, int territoryID )
+{
+ // we lost it, fall back to next point
+ me->ClearMyControlPoint();
+ m_defenseArea = SelectAreaToDefendFrom( me );
+ m_repathTimer.Invalidate();
+ m_path.Invalidate();
+
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+class CSelectDefenseAreaForPoint : public ISearchSurroundingAreasFunctor
+{
+public:
+ CSelectDefenseAreaForPoint( CTFNavArea *pointArea, int myTeam, CUtlVector< CTFNavArea * > *areaVector )
+ {
+ m_pointArea = pointArea;
+ m_myTeam = myTeam;
+
+ // don't select areas that are beyond the point
+ m_incursionFlowLimit = pointArea->GetIncursionDistance( m_myTeam ) + 250.0f;
+
+ m_areaVector = areaVector;
+ m_areaVector->RemoveAll();
+ }
+
+ virtual bool operator() ( CNavArea *baseArea, CNavArea *priorArea, float travelDistanceSoFar )
+ {
+ CTFNavArea *area = (CTFNavArea *)baseArea;
+
+ if ( !TFGameRules()->IsInKothMode() )
+ {
+ // don't select areas that are beyond the point
+ if ( area->GetIncursionDistance( m_myTeam ) > m_incursionFlowLimit )
+ return true;
+ }
+
+ if ( area->IsPotentiallyVisible( m_pointArea ) )
+ {
+ // a bit of a hack here to avoid bots choosing to defend in bottom of ravine at stage 3 of dustbowl
+ const float tooLow = 220.0f;
+ if ( m_pointArea->GetCenter().z - area->GetCenter().z < tooLow )
+ {
+ // valid defense position
+ m_areaVector->AddToTail( area );
+ }
+ }
+
+ return true;
+ }
+
+ virtual bool ShouldSearch( CNavArea *adjArea, CNavArea *currentArea, float travelDistanceSoFar )
+ {
+ if ( adjArea->IsBlocked( TFGameRules()->IsInKothMode() ? TEAM_ANY : m_myTeam ) )
+ {
+ return false;
+ }
+
+ if ( travelDistanceSoFar > tf_bot_max_point_defend_range.GetFloat() )
+ {
+ // too far away
+ return false;
+ }
+
+ const float maxHeightChange = 65.0f;
+ float deltaZ = currentArea->ComputeAdjacentConnectionHeightChange( adjArea );
+ return ( fabs( deltaZ ) < maxHeightChange );
+ }
+
+ CTFNavArea *m_pointArea;
+ CUtlVector< CTFNavArea * > *m_areaVector;
+ float m_incursionFlowLimit;
+ int m_myTeam;
+};
+
+
+//---------------------------------------------------------------------------------------------
+/**
+ * Select the area where we will guard the point from
+ */
+CTFNavArea *CTFBotDefendPoint::SelectAreaToDefendFrom( CTFBot *me )
+{
+ VPROF_BUDGET( "CTFBotDefendPoint::SelectAreaToDefendFrom", "NextBot" );
+
+ CTeamControlPoint *point = me->GetMyControlPoint();
+ if ( !point )
+ {
+ return NULL;
+ }
+
+ // decide where we will defend from
+ CUtlVector< CTFNavArea * > defenseAreas;
+
+/*
+ if ( !TFGameRules()->IsInKothMode() &&
+ point->GetTeamCapPercentage( me->GetTeamNumber() ) <= 0.0f && // point is currently safe
+ ( ObjectiveResource()->GetPreviousPointForPoint( point->GetPointIndex(), me->GetTeamNumber(), 0 ) < 0 || // this is the first cap point
+ me->IsPlayerClass( TF_CLASS_PYRO ) ) ) // pyros are skirmishers
+ {
+ if ( TheTFNavMesh()->GetSetupGateDefenseAreas() )
+ {
+ defenseAreas = *TheTFNavMesh()->GetSetupGateDefenseAreas();
+ }
+ }
+*/
+
+ if ( defenseAreas.Count() == 0 )
+ {
+ CTFNavArea *pointArea = TheTFNavMesh()->GetControlPointCenterArea( point->GetPointIndex() );
+ if ( pointArea )
+ {
+ // search outwards from the point along walkable areas (not drop downs) to make sure we can get back to the point quickly
+ CSelectDefenseAreaForPoint defenseScan( pointArea, me->GetTeamNumber(), &defenseAreas );
+ SearchSurroundingAreas( pointArea, defenseScan );
+ }
+ }
+
+ // select a specific area from the potential defense set
+ if ( defenseAreas.Count() == 0 )
+ {
+ return NULL;
+ }
+
+ // how long will we wait if we don't see any action
+ m_idleTimer.Start( RandomFloat( 10.0f, 20.0f ) );
+
+ if ( tf_bot_defense_debug.GetBool() )
+ {
+ for( int i=0; i<defenseAreas.Count(); ++i )
+ {
+ defenseAreas[i]->DrawFilled( 0, 200, 200, 999.9f );
+ }
+ }
+
+ // select one of the defense areas
+ int which = RandomInt( 0, defenseAreas.Count()-1 );
+ return defenseAreas[ which ];
+}
+
diff --git a/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.h b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.h
new file mode 100644
index 0000000..c68a55c
--- /dev/null
+++ b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point.h
@@ -0,0 +1,48 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// tf_bot_defend_point.h
+// Move to and defend current point from capture
+// Michael Booth, February 2009
+
+#ifndef TF_BOT_DEFEND_POINT_H
+#define TF_BOT_DEFEND_POINT_H
+
+#include "Path/NextBotPathFollow.h"
+#include "Path/NextBotChasePath.h"
+
+class CTFBotDefendPoint : public Action< CTFBot >
+{
+public:
+ 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 > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL );
+
+ virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
+ virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
+ virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
+
+ virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
+ virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
+ virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
+
+ virtual const char *GetName( void ) const { return "DefendPoint"; };
+
+private:
+ PathFollower m_path; // for moving to a defense position
+ ChasePath m_chasePath; // for chasing enemies
+
+ CountdownTimer m_repathTimer;
+ CountdownTimer m_lookAroundTimer;
+ CountdownTimer m_idleTimer;
+
+ CTFNavArea *m_defenseArea;
+ CTFNavArea *SelectAreaToDefendFrom( CTFBot *me );
+
+ bool IsPointThreatened( CTFBot *me );
+ bool WillBlockCapture( CTFBot *me ) const;
+ bool m_isAllowedToRoam;
+};
+
+
+#endif // TF_BOT_DEFEND_POINT_H
diff --git a/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.cpp b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.cpp
new file mode 100644
index 0000000..6e5bbf7
--- /dev/null
+++ b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.cpp
@@ -0,0 +1,247 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// tf_bot_defend_point_block_capture.h
+// Move to and defend current point from capture
+// Michael Booth, February 2009
+
+#include "cbase.h"
+#include "nav_mesh/tf_nav_mesh.h"
+#include "tf_player.h"
+#include "tf_gamerules.h"
+#include "trigger_area_capture.h"
+#include "bot/tf_bot.h"
+#include "bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h"
+#include "bot/behavior/medic/tf_bot_medic_heal.h"
+#include "bot/behavior/tf_bot_attack.h"
+#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h"
+
+
+extern ConVar tf_bot_path_lookahead_range;
+
+ConVar tf_bot_defend_owned_point_percent( "tf_bot_defend_owned_point_percent", "0.5", FCVAR_CHEAT, "Stay on the contested point we own until enemy cap percent falls below this" );
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotDefendPointBlockCapture::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
+{
+ m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
+
+ m_point = me->GetMyControlPoint();
+ if ( m_point == NULL )
+ {
+ return Done( "Point is NULL" );
+ }
+
+ m_defenseArea = static_cast< CTFNavArea * >( TheTFNavMesh()->GetNearestNavArea( m_point->GetAbsOrigin() ) );
+ if ( m_defenseArea == NULL )
+ {
+ return Done( "Can't find nav area on point" );
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+bool CTFBotDefendPointBlockCapture::IsPointSafe( CTFBot *me )
+{
+ // if a point was just captured, defend this point for awhile
+ if ( me->WasPointJustLost() )
+ {
+ return false;
+ }
+
+ if ( m_point == NULL )
+ {
+ return true;
+ }
+
+ if ( m_point->GetTeamCapPercentage( me->GetTeamNumber() ) < tf_bot_defend_owned_point_percent.GetFloat() )
+ {
+ // we're not in complete control of this point yet
+ return false;
+ }
+
+ // is point is being contested, or was just being contested, its not safe
+ if ( m_point->HasBeenContested() && ( gpGlobals->curtime - m_point->LastContestedAt() ) < 5.0f )
+ {
+ return false;
+ }
+
+ // if we still see a near threat, stay put
+ const CKnownEntity *knownThreat = me->GetVisionInterface()->GetPrimaryKnownThreat();
+ if ( knownThreat )
+ {
+ const float dangerRange = 500.0f;
+ if ( ( knownThreat->GetLastKnownPosition() - m_point->GetAbsOrigin() ).IsLengthLessThan( dangerRange ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotDefendPointBlockCapture::Update( CTFBot *me, float interval )
+{
+ // if point is safe, we can move back to our defense positions
+ if ( IsPointSafe( me ) )
+ {
+ return Done( "Point is safe again" );
+ }
+
+ if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
+ {
+ // medics look ridiculous rushing to the point - they need to heal
+ return SuspendFor( new CTFBotMedicHeal );
+ }
+
+ const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
+ me->EquipBestWeaponForThreat( threat );
+
+ Extent pointExtent;
+ pointExtent.Init( m_point );
+
+ bool isStandingOnThePoint = pointExtent.Contains( me->GetAbsOrigin() );
+
+ const CUtlVector< CTFNavArea * > *controlPointAreas = TheTFNavMesh()->GetControlPointAreas( m_point->GetPointIndex() );
+ if ( controlPointAreas )
+ {
+ for( int i=0; i<controlPointAreas->Count(); ++i )
+ {
+ if ( me->GetLastKnownArea() && me->GetLastKnownArea()->GetID() == controlPointAreas->Element(i)->GetID() )
+ {
+ isStandingOnThePoint = true;
+ }
+ }
+ }
+
+ if ( isStandingOnThePoint && CTFBotPrepareStickybombTrap::IsPossible( me ) )
+ {
+ return SuspendFor( new CTFBotPrepareStickybombTrap, "Placing stickies for defense" );
+ }
+
+ if ( controlPointAreas )
+ {
+ // move to a random spot on this control point
+ if ( m_repathTimer.IsElapsed() )
+ {
+ m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
+
+ float totalArea = 0.0f;
+ int i;
+ for( i=0; i<controlPointAreas->Count(); ++i )
+ {
+ CTFNavArea *area = controlPointAreas->Element(i);
+ totalArea += area->GetSizeX() * area->GetSizeY();
+ }
+
+ float which = RandomFloat( 0.0f, totalArea - 1.0f );
+ CTFNavArea *goalArea = NULL;
+ for( i=0; i<controlPointAreas->Count(); ++i )
+ {
+ CTFNavArea *area = controlPointAreas->Element(i);
+ which -= area->GetSizeX() * area->GetSizeY();
+ if ( which <= 0.0f )
+ {
+ goalArea = area;
+ break;
+ }
+ }
+
+ if ( goalArea )
+ {
+ CTFBotPathCost cost( me, DEFAULT_ROUTE );
+ m_path.Compute( me, goalArea->GetRandomPoint(), cost );
+ }
+ }
+
+ m_path.Update( me );
+ }
+ else if ( !isStandingOnThePoint )
+ {
+ // get on the point!
+ if ( m_repathTimer.IsElapsed() )
+ {
+ m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
+
+ CTFBotPathCost cost( me, DEFAULT_ROUTE );
+ m_path.Compute( me, ( pointExtent.lo + pointExtent.hi )/2.0f, cost );
+ }
+
+ m_path.Update( me );
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CTFBot > CTFBotDefendPointBlockCapture::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
+{
+ m_path.Invalidate();
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnStuck( CTFBot *me )
+{
+ m_path.Invalidate();
+ me->GetLocomotionInterface()->ClearStuckStatus();
+
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnMoveToSuccess( CTFBot *me, const Path *path )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
+{
+ m_path.Invalidate();
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnTerritoryContested( CTFBot *me, int territoryID )
+{
+ return TryToSustain();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnTerritoryCaptured( CTFBot *me, int territoryID )
+{
+ return TryContinue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnTerritoryLost( CTFBot *me, int territoryID )
+{
+ // we lost it, fall back
+ return TryDone( RESULT_CRITICAL, "Lost the point" );
+}
+
+
+//---------------------------------------------------------------------------------------------
+QueryResultType CTFBotDefendPointBlockCapture::ShouldHurry( const INextBot *me ) const
+{
+ // hurry up and get on the point!
+ return ANSWER_YES;
+}
+
+
+//---------------------------------------------------------------------------------------------
+QueryResultType CTFBotDefendPointBlockCapture::ShouldRetreat( const INextBot *me ) const
+{
+ // get on the point!
+ return ANSWER_NO;
+}
diff --git a/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h
new file mode 100644
index 0000000..55ab7af
--- /dev/null
+++ b/game/server/tf/bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// tf_bot_defend_point_block_capture.h
+// Move to and defend current point from capture
+// Michael Booth, February 2009
+
+#ifndef TF_BOT_DEFEND_POINT_BLOCK_CAPTURE_H
+#define TF_BOT_DEFEND_POINT_BLOCK_CAPTURE_H
+
+
+class CTFBotDefendPointBlockCapture : public Action< CTFBot >
+{
+public:
+ 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 > OnMoveToSuccess( CTFBot *me, const Path *path );
+ virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
+
+ virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
+ virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
+ virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
+
+ virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
+ virtual QueryResultType ShouldRetreat( const INextBot *me ) const;
+
+ virtual const char *GetName( void ) const { return "BlockCapture"; };
+
+private:
+ PathFollower m_path;
+ CountdownTimer m_repathTimer;
+ CTeamControlPoint *m_point;
+ CTFNavArea *m_defenseArea;
+
+ bool IsPointSafe( CTFBot *me );
+};
+
+
+#endif // TF_BOT_DEFEND_POINT_BLOCK_CAPTURE_H