Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 113 additions & 28 deletions src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
#include "cbase.h"
#include "bot/behavior/neo_bot_jgr_capture.h"
#include "bot/behavior/neo_bot_retreat_to_cover.h"
#include "bot/behavior/neo_bot_attack.h"
#include "bot/neo_bot_path_compute.h"
#include "neo_gamerules.h"
#include "neo_juggernaut.h"

static const float JGR_CAPTURE_FACING_DOT = 0.9f;
static const float JGR_CAPTURE_LOCKED_RETREAT_TIME = 2.0f;
static const float JGR_CAPTURE_MAX_REPATH_DELAY = 5.0f;
static const float JGR_CAPTURE_MIN_REPATH_DELAY = 1.0f;
static const float JGR_CAPTURE_REPOSITION_RATIO = 0.7f;
static const float JGR_CAPTURE_TIMER_BUFFER = 1.0f;

//---------------------------------------------------------------------------------------------
void CNEOBotJgrCapture::StopMoving( CNEOBot *me )
{
m_path.Invalidate();
me->ReleaseForwardButton();
me->ReleaseBackwardButton();
me->ReleaseLeftButton();
me->ReleaseRightButton();
}

//---------------------------------------------------------------------------------------------
CNEOBotJgrCapture::CNEOBotJgrCapture( CNEO_Juggernaut *pObjective )
{
Expand All @@ -17,6 +35,7 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::OnStart( CNEOBot *me, Action<CNEOBot> *
m_useAttemptTimer.Invalidate();
m_path.Invalidate();
m_repathTimer.Invalidate();
m_bStrafeRight = ( RandomInt( 0, 1 ) == 1 );

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

// Ignore enemies while capturing juggernaut
me->StopLookingAroundForEnemies();
me->SetAttribute( CNEOBot::IGNORE_ENEMIES );
me->ReloadIfLowClip(); // might as well as we're preoccupied
return Continue();
}
Expand All @@ -44,24 +64,21 @@ void CNEOBotJgrCapture::OnEnd( CNEOBot *me, Action<CNEOBot> *nextAction )
{
me->ReleaseUseButton();
me->StartLookingAroundForEnemies();
me->ClearAttribute( CNEOBot::IGNORE_ENEMIES );
}

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

//---------------------------------------------------------------------------------------------
ActionResult<CNEOBot> CNEOBotJgrCapture::OnResume( CNEOBot *me, Action<CNEOBot> *interruptingAction )
{
me->StopLookingAroundForEnemies();
return Continue();
// Situation around juggernaut is possibly stale, reevaluate
return Done( "OnResume: Reevaluating capture situation" );
}

//---------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -92,6 +109,29 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::Update( CNEOBot *me, float interval )
}
}

CBasePlayer *pActivatingPlayer = m_hObjective->GetActivatingPlayer();
if ( pActivatingPlayer )
{
if ( !me->InSameTeam( pActivatingPlayer ) )
{
return SuspendFor( new CNEOBotAttack, "Attacking enemy capturing the juggernaut" );
}
else if ( pActivatingPlayer != me )
{
if ( me->GetVisionInterface()->GetPrimaryKnownThreat() )
{
return SuspendFor( new CNEOBotAttack, "Defending teammate capturing the juggernaut" );
}

// Look away from the juggernaut to watch for threats
CNEOBotJgrCapture::LookAwayFrom( me, m_hObjective );

me->ReleaseUseButton();
m_useAttemptTimer.Invalidate();
return Continue();
}
}

if ( me->GetAbsOrigin().DistToSqr( m_hObjective->GetAbsOrigin() ) < CNEO_Juggernaut::GetUseDistanceSquared() )
{
if ( NEORules()->IsJuggernautLocked() )
Expand All @@ -101,47 +141,76 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::Update( CNEOBot *me, float interval )
Assert( NEORules()->IsJuggernautLocked() == (pJuggernaut && pJuggernaut->m_bLocked) );
#endif
me->ReleaseUseButton();
return SuspendFor( new CNEOBotRetreatToCover( 2.0f ), "Juggernaut is locked, taking cover to wait for it to unlock" );
m_useAttemptTimer.Invalidate();

// Look away from the juggernaut while it's locked to watch for threats
CNEOBotJgrCapture::LookAwayFrom( me, m_hObjective );
}

// Stop moving while using
m_path.Invalidate();
me->ReleaseForwardButton();
me->ReleaseBackwardButton();
me->ReleaseLeftButton();
me->ReleaseRightButton();

const Vector vecObjectiveCenter = m_hObjective->WorldSpaceCenter();
me->GetBodyInterface()->AimHeadTowards( vecObjectiveCenter, IBody::MANDATORY, 0.1f, nullptr, "Focusing on Juggernaut objective" );

// Check if we are facing juggernaut and have a clear line of sight
Vector vecToTargetDir = vecObjectiveCenter - me->EyePosition();
vecToTargetDir.NormalizeInPlace();

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

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

if ( m_useAttemptTimer.HasStarted() )
if ( trace.m_pEnt == m_hObjective )
{
if ( m_useAttemptTimer.IsElapsed() )
if ( bIsFacing && me->GetBodyInterface()->IsHeadAimingOnTarget() )
{
return Done( "Use timer elapsed, failed to capture" );
if ( !m_useAttemptTimer.HasStarted() )
{
m_useAttemptTimer.Start( CNEO_Juggernaut::GetUseDuration() + JGR_CAPTURE_TIMER_BUFFER );
}

me->PressUseButton();
StopMoving( me );

if ( m_useAttemptTimer.IsElapsed() )
{
return Done( "Activation attempt expired" );
}
}
}
else if ( bIsFacing && me->GetBodyInterface()->IsHeadAimingOnTarget() )
else
{
m_useAttemptTimer.Start( CNEO_Juggernaut::GetUseDuration() + 1.0f );
me->PressUseButton( CNEO_Juggernaut::GetUseDuration() + 1.0f );
// Trace blocked (usually by teammate), reposition to get a better angle
me->ReleaseUseButton();
m_useAttemptTimer.Invalidate();
m_path.Invalidate();

const float flRepositionDistSqr = JGR_CAPTURE_REPOSITION_RATIO * CNEO_Juggernaut::GetUseDistanceSquared();
if ( me->GetAbsOrigin().DistToSqr( m_hObjective->GetAbsOrigin() ) > flRepositionDistSqr )
{
me->PressForwardButton( 0.2f );
}

if ( m_bStrafeRight )
{
me->PressRightButton( 1.0f );
me->ReleaseLeftButton();
}
else
{
me->PressLeftButton( 1.0f );
me->ReleaseRightButton();
}
}
}
else
{
// Objective is farther than we can reach for cpature
if ( m_repathTimer.IsElapsed() )
// Objective is farther than we can reach for capture
if ( !m_repathTimer.HasStarted() || m_repathTimer.IsElapsed() )
{
m_repathTimer.Start( RandomFloat( 1.0f, 5.0f ) );
m_repathTimer.Start( RandomFloat( JGR_CAPTURE_MIN_REPATH_DELAY, JGR_CAPTURE_MAX_REPATH_DELAY ) );
if ( !CNEOBotPathCompute( me, m_path, m_hObjective->GetAbsOrigin(), FASTEST_ROUTE ) )
{
return Done( "Unable to find a path to the Juggernaut objective" );
Expand All @@ -152,3 +221,19 @@ ActionResult<CNEOBot> CNEOBotJgrCapture::Update( CNEOBot *me, float interval )

return Continue();
}

//---------------------------------------------------------------------------------------------
void CNEOBotJgrCapture::LookAwayFrom( CNEOBot *me, CBaseEntity *pTarget )
{
if ( !pTarget )
{
return;
}

Vector vecAwayFromTarget = me->GetAbsOrigin() - pTarget->GetAbsOrigin();
Vector2D vecLookDir2D = vecAwayFromTarget.AsVector2D();
vecLookDir2D.NormalizeInPlace();

const Vector vecLookPos = me->EyePosition() + Vector( vecLookDir2D.x, vecLookDir2D.y, 0.0f ) * 500.0f;
me->GetBodyInterface()->AimHeadTowards( vecLookPos, IBody::IMPORTANT, 0.1f, nullptr, "Facing away from target to watch for threats" );
}
5 changes: 5 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ class CNEOBotJgrCapture : public Action<CNEOBot>
virtual ActionResult<CNEOBot> OnSuspend( CNEOBot *me, Action<CNEOBot> *interruptingAction ) override;
virtual ActionResult<CNEOBot> OnResume( CNEOBot *me, Action<CNEOBot> *interruptingAction ) override;

static void LookAwayFrom( CNEOBot *me, CBaseEntity *pTarget );

private:
void StopMoving( CNEOBot *me );

bool m_bStrafeRight = false;
CHandle<CNEO_Juggernaut> m_hObjective;
CountdownTimer m_useAttemptTimer;
PathFollower m_path;
Expand Down
31 changes: 30 additions & 1 deletion src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "neo_gamerules.h"
#include "bot/neo_bot.h"
#include "bot/neo_bot_path_compute.h"
#include "bot/behavior/neo_bot_attack.h"
#include "bot/behavior/neo_bot_jgr_seek.h"
#include "bot/behavior/neo_bot_jgr_escort.h"
#include "bot/behavior/neo_bot_jgr_enemy.h"
Expand Down Expand Up @@ -65,7 +66,24 @@ ActionResult< CNEOBot > CNEOBotJgrSeek::Update( CNEOBot *me, float interval )

if ( FStrEq( classname, "neo_juggernaut" ) )
{
return SuspendFor( new CNEOBotJgrCapture( static_cast<CNEO_Juggernaut*>(m_hTargetEntity.Get()) ), "Capturing Juggernaut" );
CNEO_Juggernaut *pJgr = static_cast<CNEO_Juggernaut*>( m_hTargetEntity.Get() );
if ( pJgr )
{
CBasePlayer *pActivatingPlayer = pJgr->GetActivatingPlayer();
if ( NEORules()->IsJuggernautLocked()
|| ( pActivatingPlayer && me->InSameTeam( pActivatingPlayer ) && pActivatingPlayer != me ) )
{
CNEOBotJgrCapture::LookAwayFrom( me, pJgr );
m_path.Invalidate(); // wait at juggernaut
if ( me->GetVisionInterface()->GetPrimaryKnownThreat() )
{
return SuspendFor( new CNEOBotAttack, "Intercepting enemies near juggernaut" );
}
return Continue();
}
}

return SuspendFor( new CNEOBotJgrCapture( pJgr ), "Capturing Juggernaut" );
}
}
}
Expand Down Expand Up @@ -98,6 +116,17 @@ void CNEOBotJgrSeek::RecomputeSeekPath( CNEOBot *me )
CBaseEntity* pJuggernaut = gEntList.FindEntityByClassname(NULL, "neo_juggernaut");
if (pJuggernaut)
{
const float useRangeSq = CNEO_Juggernaut::GetUseDistanceSquared() * 0.8f;
if ( me->GetAbsOrigin().DistToSqr( pJuggernaut->GetAbsOrigin() ) < useRangeSq )
{
// We're already at the goal, no need to path.
m_vGoalPos = pJuggernaut->WorldSpaceCenter();
m_bGoingToTargetEntity = true;
m_hTargetEntity = pJuggernaut;
m_path.Invalidate();
return;
}

m_vGoalPos = pJuggernaut->WorldSpaceCenter();
m_bGoingToTargetEntity = true;
m_hTargetEntity = pJuggernaut;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter
#endif

CNEO_Player* pBotPlayer = ToNEOPlayer( me->GetEntity() );
if ( pBotPlayer && !(pBotPlayer->m_nButtons & (IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT)) )
if ( pBotPlayer && !(pBotPlayer->m_nButtons & (IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT | IN_USE)) )
{
AvoidBumpingFriends( me );
}
Expand Down
2 changes: 2 additions & 0 deletions src/game/shared/neo/neo_juggernaut.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class CNEO_Juggernaut : public CBaseAnimating
virtual int ObjectCaps(void) { return BaseClass::ObjectCaps() | FCAP_ONOFF_USE; }
virtual int UpdateTransmitState() override;

CNEO_Player* GetActivatingPlayer() const { return m_hHoldingPlayer.Get(); }
bool IsBeingActivated() const { return m_bIsHolding; }
const bool IsBeingActivatedByLosingTeam();

bool m_bPostDeath = false;
Expand Down
Loading