Skip to content

Commit 2057419

Browse files
committed
Improve bot JGR capture reliability
1 parent 8cfdc9f commit 2057419

5 files changed

Lines changed: 151 additions & 30 deletions

File tree

src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp

Lines changed: 113 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
#include "cbase.h"
22
#include "bot/behavior/neo_bot_jgr_capture.h"
33
#include "bot/behavior/neo_bot_retreat_to_cover.h"
4+
#include "bot/behavior/neo_bot_attack.h"
45
#include "bot/neo_bot_path_compute.h"
56
#include "neo_gamerules.h"
67
#include "neo_juggernaut.h"
78

9+
static const float JGR_CAPTURE_FACING_DOT = 0.9f;
10+
static const float JGR_CAPTURE_LOCKED_RETREAT_TIME = 2.0f;
11+
static const float JGR_CAPTURE_MAX_REPATH_DELAY = 5.0f;
12+
static const float JGR_CAPTURE_MIN_REPATH_DELAY = 1.0f;
13+
static const float JGR_CAPTURE_REPOSITION_RATIO = 0.7f;
14+
static const float JGR_CAPTURE_TIMER_BUFFER = 1.0f;
15+
16+
//---------------------------------------------------------------------------------------------
17+
void CNEOBotJgrCapture::StopMoving( CNEOBot *me )
18+
{
19+
m_path.Invalidate();
20+
me->ReleaseForwardButton();
21+
me->ReleaseBackwardButton();
22+
me->ReleaseLeftButton();
23+
me->ReleaseRightButton();
24+
}
25+
826
//---------------------------------------------------------------------------------------------
927
CNEOBotJgrCapture::CNEOBotJgrCapture( CNEO_Juggernaut *pObjective )
1028
{
@@ -17,6 +35,7 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::OnStart( CNEOBot *me, Action<CNEOBot> *
1735
m_useAttemptTimer.Invalidate();
1836
m_path.Invalidate();
1937
m_repathTimer.Invalidate();
38+
m_bStrafeRight = ( RandomInt( 0, 1 ) == 1 );
2039

2140
if ( !m_hObjective )
2241
{
@@ -35,6 +54,7 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::OnStart( CNEOBot *me, Action<CNEOBot> *
3554

3655
// Ignore enemies while capturing juggernaut
3756
me->StopLookingAroundForEnemies();
57+
me->SetAttribute( CNEOBot::IGNORE_ENEMIES );
3858
me->ReloadIfLowClip(); // might as well as we're preoccupied
3959
return Continue();
4060
}
@@ -44,24 +64,21 @@ void CNEOBotJgrCapture::OnEnd( CNEOBot *me, Action<CNEOBot> *nextAction )
4464
{
4565
me->ReleaseUseButton();
4666
me->StartLookingAroundForEnemies();
67+
me->ClearAttribute( CNEOBot::IGNORE_ENEMIES );
4768
}
4869

4970
//---------------------------------------------------------------------------------------------
5071
ActionResult<CNEOBot> CNEOBotJgrCapture::OnSuspend( CNEOBot *me, Action<CNEOBot> *interruptingAction )
5172
{
52-
// CNEOBotTacticalMonitor -> CNEOBotRetreatToCover will handle reacting to enemies if under fire
53-
// Debatably, maybe bots should just ignore enemies, but that would require a change to CNEOBotTacticalMonitor
54-
// Also it might be more fun for humans if they can interrupt bots from taking the Juggernaut.
55-
me->ReleaseUseButton();
56-
me->StartLookingAroundForEnemies();
57-
return Continue();
73+
// Situation around juggernaut is possibly stale, reevaluate
74+
return Done( "OnSuspend: Reevaluating capture situation" );
5875
}
5976

6077
//---------------------------------------------------------------------------------------------
6178
ActionResult<CNEOBot> CNEOBotJgrCapture::OnResume( CNEOBot *me, Action<CNEOBot> *interruptingAction )
6279
{
63-
me->StopLookingAroundForEnemies();
64-
return Continue();
80+
// Situation around juggernaut is possibly stale, reevaluate
81+
return Done( "OnResume: Reevaluating capture situation" );
6582
}
6683

6784
//---------------------------------------------------------------------------------------------
@@ -92,6 +109,29 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::Update( CNEOBot *me, float interval )
92109
}
93110
}
94111

112+
CBasePlayer *pActivatingPlayer = m_hObjective->GetActivatingPlayer();
113+
if ( pActivatingPlayer )
114+
{
115+
if ( !me->InSameTeam( pActivatingPlayer ) )
116+
{
117+
return SuspendFor( new CNEOBotAttack, "Attacking enemy capturing the juggernaut" );
118+
}
119+
else if ( pActivatingPlayer != me )
120+
{
121+
if ( me->GetVisionInterface()->GetPrimaryKnownThreat() )
122+
{
123+
return SuspendFor( new CNEOBotAttack, "Defending teammate capturing the juggernaut" );
124+
}
125+
126+
// Look away from the juggernaut to watch for threats
127+
CNEOBotJgrCapture::LookAwayFrom( me, m_hObjective );
128+
129+
me->ReleaseUseButton();
130+
m_useAttemptTimer.Invalidate();
131+
return Continue();
132+
}
133+
}
134+
95135
if ( me->GetAbsOrigin().DistToSqr( m_hObjective->GetAbsOrigin() ) < CNEO_Juggernaut::GetUseDistanceSquared() )
96136
{
97137
if ( NEORules()->IsJuggernautLocked() )
@@ -101,47 +141,76 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::Update( CNEOBot *me, float interval )
101141
Assert( NEORules()->IsJuggernautLocked() == (pJuggernaut && pJuggernaut->m_bLocked) );
102142
#endif
103143
me->ReleaseUseButton();
104-
return SuspendFor( new CNEOBotRetreatToCover( 2.0f ), "Juggernaut is locked, taking cover to wait for it to unlock" );
144+
m_useAttemptTimer.Invalidate();
145+
146+
// Look away from the juggernaut while it's locked to watch for threats
147+
CNEOBotJgrCapture::LookAwayFrom( me, m_hObjective );
105148
}
106-
107-
// Stop moving while using
108-
m_path.Invalidate();
109-
me->ReleaseForwardButton();
110-
me->ReleaseBackwardButton();
111-
me->ReleaseLeftButton();
112-
me->ReleaseRightButton();
113149

114150
const Vector vecObjectiveCenter = m_hObjective->WorldSpaceCenter();
151+
me->GetBodyInterface()->AimHeadTowards( vecObjectiveCenter, IBody::MANDATORY, 0.1f, nullptr, "Focusing on Juggernaut objective" );
152+
153+
// Check if we are facing juggernaut and have a clear line of sight
115154
Vector vecToTargetDir = vecObjectiveCenter - me->EyePosition();
116155
vecToTargetDir.NormalizeInPlace();
117156

118-
// Ensure we are facing the target before attempting to use
119157
Vector vecEyeDirection;
120158
me->EyeVectors( &vecEyeDirection );
121159
const float flDot = vecEyeDirection.Dot( vecToTargetDir );
122-
const bool bIsFacing = flDot > 0.9f;
160+
const bool bIsFacing = flDot > JGR_CAPTURE_FACING_DOT;
123161

124-
me->GetBodyInterface()->AimHeadTowards( vecObjectiveCenter, IBody::CRITICAL, 0.1f, NULL, "Looking at Juggernaut objective to use" );
162+
trace_t trace;
163+
UTIL_TraceLine( me->EyePosition(), vecObjectiveCenter, MASK_PLAYERSOLID, me, COLLISION_GROUP_NONE, &trace );
125164

126-
if ( m_useAttemptTimer.HasStarted() )
165+
if ( trace.m_pEnt == m_hObjective )
127166
{
128-
if ( m_useAttemptTimer.IsElapsed() )
167+
if ( bIsFacing && me->GetBodyInterface()->IsHeadAimingOnTarget() )
129168
{
130-
return Done( "Use timer elapsed, failed to capture" );
169+
if ( !m_useAttemptTimer.HasStarted() )
170+
{
171+
m_useAttemptTimer.Start( CNEO_Juggernaut::GetUseDuration() + JGR_CAPTURE_TIMER_BUFFER );
172+
}
173+
174+
me->PressUseButton();
175+
StopMoving( me );
176+
177+
if ( m_useAttemptTimer.IsElapsed() )
178+
{
179+
return Done( "Activation attempt expired" );
180+
}
131181
}
132182
}
133-
else if ( bIsFacing && me->GetBodyInterface()->IsHeadAimingOnTarget() )
183+
else
134184
{
135-
m_useAttemptTimer.Start( CNEO_Juggernaut::GetUseDuration() + 1.0f );
136-
me->PressUseButton( CNEO_Juggernaut::GetUseDuration() + 1.0f );
185+
// Trace blocked (usually by teammate), reposition to get a better angle
186+
me->ReleaseUseButton();
187+
m_useAttemptTimer.Invalidate();
188+
m_path.Invalidate();
189+
190+
const float flRepositionDistSqr = JGR_CAPTURE_REPOSITION_RATIO * CNEO_Juggernaut::GetUseDistanceSquared();
191+
if ( me->GetAbsOrigin().DistToSqr( m_hObjective->GetAbsOrigin() ) > flRepositionDistSqr )
192+
{
193+
me->PressForwardButton( 0.2f );
194+
}
195+
196+
if ( m_bStrafeRight )
197+
{
198+
me->PressRightButton( 1.0f );
199+
me->ReleaseLeftButton();
200+
}
201+
else
202+
{
203+
me->PressLeftButton( 1.0f );
204+
me->ReleaseRightButton();
205+
}
137206
}
138207
}
139208
else
140209
{
141-
// Objective is farther than we can reach for cpature
142-
if ( m_repathTimer.IsElapsed() )
210+
// Objective is farther than we can reach for capture
211+
if ( !m_repathTimer.HasStarted() || m_repathTimer.IsElapsed() )
143212
{
144-
m_repathTimer.Start( RandomFloat( 1.0f, 5.0f ) );
213+
m_repathTimer.Start( RandomFloat( JGR_CAPTURE_MIN_REPATH_DELAY, JGR_CAPTURE_MAX_REPATH_DELAY ) );
145214
if ( !CNEOBotPathCompute( me, m_path, m_hObjective->GetAbsOrigin(), FASTEST_ROUTE ) )
146215
{
147216
return Done( "Unable to find a path to the Juggernaut objective" );
@@ -152,3 +221,19 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::Update( CNEOBot *me, float interval )
152221

153222
return Continue();
154223
}
224+
225+
//---------------------------------------------------------------------------------------------
226+
void CNEOBotJgrCapture::LookAwayFrom( CNEOBot *me, CBaseEntity *pTarget )
227+
{
228+
if ( !pTarget )
229+
{
230+
return;
231+
}
232+
233+
Vector vecAwayFromTarget = me->GetAbsOrigin() - pTarget->GetAbsOrigin();
234+
Vector2D vecLookDir2D = vecAwayFromTarget.AsVector2D();
235+
vecLookDir2D.NormalizeInPlace();
236+
237+
const Vector vecLookPos = me->EyePosition() + Vector( vecLookDir2D.x, vecLookDir2D.y, 0.0f ) * 500.0f;
238+
me->GetBodyInterface()->AimHeadTowards( vecLookPos, IBody::IMPORTANT, 0.1f, nullptr, "Facing away from target to watch for threats" );
239+
}

src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ class CNEOBotJgrCapture : public Action<CNEOBot>
1919
virtual ActionResult<CNEOBot> OnSuspend( CNEOBot *me, Action<CNEOBot> *interruptingAction ) override;
2020
virtual ActionResult<CNEOBot> OnResume( CNEOBot *me, Action<CNEOBot> *interruptingAction ) override;
2121

22+
static void LookAwayFrom( CNEOBot *me, CBaseEntity *pTarget );
23+
2224
private:
25+
void StopMoving( CNEOBot *me );
26+
27+
bool m_bStrafeRight = false;
2328
CHandle<CNEO_Juggernaut> m_hObjective;
2429
CountdownTimer m_useAttemptTimer;
2530
PathFollower m_path;

src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "neo_gamerules.h"
44
#include "bot/neo_bot.h"
55
#include "bot/neo_bot_path_compute.h"
6+
#include "bot/behavior/neo_bot_attack.h"
67
#include "bot/behavior/neo_bot_jgr_seek.h"
78
#include "bot/behavior/neo_bot_jgr_escort.h"
89
#include "bot/behavior/neo_bot_jgr_enemy.h"
@@ -65,7 +66,24 @@ ActionResult< CNEOBot > CNEOBotJgrSeek::Update( CNEOBot *me, float interval )
6566

6667
if ( FStrEq( classname, "neo_juggernaut" ) )
6768
{
68-
return SuspendFor( new CNEOBotJgrCapture( static_cast<CNEO_Juggernaut*>(m_hTargetEntity.Get()) ), "Capturing Juggernaut" );
69+
CNEO_Juggernaut *pJgr = static_cast<CNEO_Juggernaut*>( m_hTargetEntity.Get() );
70+
if ( pJgr )
71+
{
72+
CBasePlayer *pActivatingPlayer = pJgr->GetActivatingPlayer();
73+
if ( NEORules()->IsJuggernautLocked()
74+
|| ( pActivatingPlayer && me->InSameTeam( pActivatingPlayer ) && pActivatingPlayer != me ) )
75+
{
76+
CNEOBotJgrCapture::LookAwayFrom( me, pJgr );
77+
m_path.Invalidate(); // wait at juggernaut
78+
if ( me->GetVisionInterface()->GetPrimaryKnownThreat() )
79+
{
80+
return SuspendFor( new CNEOBotAttack, "Intercepting enemies near juggernaut" );
81+
}
82+
return Continue();
83+
}
84+
}
85+
86+
return SuspendFor( new CNEOBotJgrCapture( pJgr ), "Capturing Juggernaut" );
6987
}
7088
}
7189
}
@@ -98,6 +116,17 @@ void CNEOBotJgrSeek::RecomputeSeekPath( CNEOBot *me )
98116
CBaseEntity* pJuggernaut = gEntList.FindEntityByClassname(NULL, "neo_juggernaut");
99117
if (pJuggernaut)
100118
{
119+
const float useRangeSq = CNEO_Juggernaut::GetUseDistanceSquared() * 0.8f;
120+
if ( me->GetAbsOrigin().DistToSqr( pJuggernaut->GetAbsOrigin() ) < useRangeSq )
121+
{
122+
// We're already at the goal, no need to path.
123+
m_vGoalPos = pJuggernaut->WorldSpaceCenter();
124+
m_bGoingToTargetEntity = true;
125+
m_hTargetEntity = pJuggernaut;
126+
m_path.Invalidate();
127+
return;
128+
}
129+
101130
m_vGoalPos = pJuggernaut->WorldSpaceCenter();
102131
m_bGoingToTargetEntity = true;
103132
m_hTargetEntity = pJuggernaut;

src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter
430430
#endif
431431

432432
CNEO_Player* pBotPlayer = ToNEOPlayer( me->GetEntity() );
433-
if ( pBotPlayer && !(pBotPlayer->m_nButtons & (IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT)) )
433+
if ( pBotPlayer && !(pBotPlayer->m_nButtons & (IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT | IN_USE)) )
434434
{
435435
AvoidBumpingFriends( me );
436436
}

src/game/shared/neo/neo_juggernaut.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class CNEO_Juggernaut : public CBaseAnimating
3333
virtual int ObjectCaps(void) { return BaseClass::ObjectCaps() | FCAP_ONOFF_USE; }
3434
virtual int UpdateTransmitState() override;
3535

36+
CNEO_Player* GetActivatingPlayer() const { return m_hHoldingPlayer.Get(); }
37+
bool IsBeingActivated() const { return m_bIsHolding; }
3638
const bool IsBeingActivatedByLosingTeam();
3739

3840
bool m_bPostDeath = false;

0 commit comments

Comments
 (0)