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
150 changes: 150 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_attack.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "cbase.h"
#include "neo_player.h"
#include "neo_gamerules.h"
#include "neo_smokelineofsightblocker.h"
#include "team_control_point_master.h"
#include "bot/neo_bot.h"
#include "bot/behavior/neo_bot_attack.h"
Expand All @@ -17,18 +18,118 @@ ConVar neo_bot_aggressive( "neo_bot_aggressive", "0", FCVAR_NONE );
//---------------------------------------------------------------------------------------------
CNEOBotAttack::CNEOBotAttack( void ) : m_chasePath( ChasePath::LEAD_SUBJECT )
{
m_attackCoverArea = nullptr;
}


//---------------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotAttack::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction )
{
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
m_attackCoverTimer.Invalidate();

return Continue();
}


//---------------------------------------------------------------------------------------------
// for finding cover closer to our threat
class CSearchForAttackCover : public ISearchSurroundingAreasFunctor
{
public:
CSearchForAttackCover( CNEOBot *me, const CKnownEntity *threat ) : m_me( me ), m_threat( threat )
{
m_attackCoverArea = nullptr;
m_threatArea = threat->GetLastKnownArea();
m_distToThreatSq = ( threat->GetLastKnownPosition() - me->GetAbsOrigin() ).LengthSqr();
}

virtual bool operator() ( CNavArea *baseArea, CNavArea *priorArea, float travelDistanceSoFar )
{
// return true to keep searching, false when suitable cover is found
CNavArea *area = static_cast<CNavArea *>(baseArea);

if ( !m_threatArea )
{
return false; // threat area is unknown
}

constexpr float distanceThresholdRatio = 1.2f;
float candidateDistFromMeSq = ( m_me->GetAbsOrigin() - area->GetCenter() ).LengthSqr();
if ( candidateDistFromMeSq > m_distToThreatSq * distanceThresholdRatio )
{
return true; // not close enough to justify rerouting
}

if ( area->IsPotentiallyVisible( m_threatArea ) )
{
// Even if area does not have hard cover, see if Support can use smoke concealment
if ( m_me->GetClass() == NEO_CLASS_SUPPORT )
{
CNEO_Player *pThreatPlayer = ToNEOPlayer( m_threat->GetEntity() );
if ( pThreatPlayer && ( pThreatPlayer->GetClass() != NEO_CLASS_SUPPORT ) )
{
ScopedSmokeLOS smokeScope( false );

Vector vecThreatEye = m_threat->GetLastKnownPosition() + pThreatPlayer->GetViewOffset();
Vector vecCandidateArea = area->GetCenter() + m_me->GetViewOffset();

trace_t tr;
CTraceFilterSimple filter( m_threat->GetEntity(), COLLISION_GROUP_NONE );
UTIL_TraceLine( vecThreatEye, vecCandidateArea, MASK_BLOCKLOS, &filter, &tr );

if ( tr.fraction < 1.0f )
{
m_attackCoverArea = area;
return false; // found smoke as concealment
}
}
}

return true; // not cover
}

// found hard cover
m_attackCoverArea = area;
return false; // found suitable cover
}

virtual bool ShouldSearch( CNavArea *adjArea, CNavArea *currentArea, float travelDistanceSoFar )
{
if ( travelDistanceSoFar > 1000.0f )
{
return false;
}

// For considering areas off to the side of current area
constexpr float distanceThresholdRatio = 0.9f;

// The adjacent area to search should not be farther from the threat
float adjThreatDistance = ( m_threatArea->GetCenter() - adjArea->GetCenter() ).LengthSqr();
float curThreatDistance = ( m_threatArea->GetCenter() - currentArea->GetCenter() ).LengthSqr();
if ( adjThreatDistance * distanceThresholdRatio > curThreatDistance )
{
return false; // Candidate adjacent area veers farther from threat
}

// The adjacent area to search should not be beyond the threat
if ( adjThreatDistance > m_distToThreatSq )
{
return false; // Candidate adjacent area is beyond threat distance
}

// Don't consider areas that require jumping when engaging enemy
return ( currentArea->ComputeAdjacentConnectionHeightChange( adjArea ) < m_me->GetLocomotionInterface()->GetStepHeight() );
}

CNEOBot *m_me;
const CKnownEntity *m_threat;
CNavArea *m_attackCoverArea;
const CNavArea *m_threatArea; // reference point of the threat
float m_distToThreatSq; // the bot's current distance to the threat
};


//---------------------------------------------------------------------------------------------
// head aiming and weapon firing is handled elsewhere - we just need to get into position to fight
ActionResult< CNEOBot > CNEOBotAttack::Update( CNEOBot *me, float interval )
Expand Down Expand Up @@ -90,6 +191,55 @@ ActionResult< CNEOBot > CNEOBotAttack::Update( CNEOBot *me, float interval )
}
}

// Look for opportunities to leap frog from cover to cover
if ( m_attackCoverTimer.IsElapsed() )
{
m_attackCoverTimer.Start( 5.0f );

CSearchForAttackCover search( me, threat );
SearchSurroundingAreas( me->GetLastKnownArea(), search );

if ( search.m_attackCoverArea )
{
m_attackCoverArea = search.m_attackCoverArea;
m_path.Invalidate(); // recompute path
m_chasePath.Invalidate();
}
}

if ( m_attackCoverArea )
{
if ( me->GetLastKnownArea() == m_attackCoverArea )
{
// Immediately look for new cover
m_attackCoverArea = nullptr;
m_attackCoverTimer.Invalidate();
}
else
{
if ( !m_path.IsValid() )
{
if ( !CNEOBotPathCompute( me, m_path, m_attackCoverArea->GetCenter(), DEFAULT_ROUTE ) )
{
// If no valid path, fallback to chasing threat
m_attackCoverArea = nullptr;
m_path.Invalidate();
}
}

if ( m_attackCoverArea )
{
m_path.Update( me );
return Continue();
}
}
}
else
{
m_path.Invalidate();
}

// Fallback when there is no advancing cover
if ( isUsingCloseRangeWeapon )
{
CNEOBotPathUpdateChase( me, m_chasePath, threat->GetEntity(), FASTEST_ROUTE );
Expand Down
2 changes: 2 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_attack.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class CNEOBotAttack : public Action< CNEOBot >
private:
PathFollower m_path;
ChasePath m_chasePath;
CountdownTimer m_attackCoverTimer;
CountdownTimer m_grenadeThrowCooldownTimer;
CountdownTimer m_repathTimer;
CNavArea *m_attackCoverArea;
};
16 changes: 13 additions & 3 deletions src/game/server/neo/bot/behavior/neo_bot_ctg_escort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include "neo_ghost_cap_point.h"
#include "weapons/weapon_ghost.h"

extern ConVar sv_neo_grenade_blast_radius;

//---------------------------------------------------------------------------------------------
CNEOBotCtgEscort::CNEOBotCtgEscort( void ) :
m_role( ROLE_SCREEN ),
Expand Down Expand Up @@ -86,6 +88,12 @@ ActionResult< CNEOBot > CNEOBotCtgEscort::Update( CNEOBot *me, float interval )
{
m_lostSightOfCarrierTimer.Invalidate();

const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(true);
if ( threat && threat->GetEntity() && threat->GetEntity()->IsAlive() )
{
return SuspendFor( new CNEOBotAttack, "Breaking away from ghoster to engage threat" );
}

if ( !m_bHasGoal )
{
// Asymmetric defense: No goal cap zone, so defend the carrier.
Expand Down Expand Up @@ -131,10 +139,13 @@ ActionResult< CNEOBot > CNEOBotCtgEscort::Update( CNEOBot *me, float interval )
}
else
{
const float flSafeRadius = sv_neo_grenade_blast_radius.GetFloat();
const float flSafeRadiusSq = flSafeRadius * flSafeRadius;

if ( m_role == ROLE_BODYGUARD )
{
// Dont crowd the carrier
if ( me->GetAbsOrigin().DistToSqr( pGhostCarrier->GetAbsOrigin() ) < ( 100.0f * 100.0f ) )
if ( me->GetAbsOrigin().DistToSqr( pGhostCarrier->GetAbsOrigin() ) < flSafeRadiusSq )
{
m_path.Invalidate();
m_chasePath.Invalidate();
Expand All @@ -160,8 +171,7 @@ ActionResult< CNEOBot > CNEOBotCtgEscort::Update( CNEOBot *me, float interval )

// No active threats to carrier
float flDistToCarrierSq = me->GetAbsOrigin().DistToSqr( pGhostCarrier->GetAbsOrigin() );
constexpr float regroupDistanceSq = 300.0f * 300.0f;
if ( flDistToCarrierSq > regroupDistanceSq )
if ( flDistToCarrierSq > flSafeRadiusSq )
{
// Regroup
CNEOBotPathUpdateChase( me, m_chasePath, pGhostCarrier, SAFEST_ROUTE );
Expand Down
12 changes: 7 additions & 5 deletions src/game/server/neo/bot/behavior/neo_bot_jgr_escort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
#include "bot/neo_bot.h"
#include "bot/behavior/neo_bot_attack.h"
#include "bot/behavior/neo_bot_jgr_escort.h"
#include "bot/behavior/neo_bot_retreat_to_cover.h"
#include "bot/neo_bot_path_compute.h"
#include "neo_gamerules.h"

extern ConVar sv_neo_grenade_blast_radius;

//---------------------------------------------------------------------------------------------
CNEOBotJgrEscort::CNEOBotJgrEscort( void )
{
Expand Down Expand Up @@ -63,17 +64,18 @@ ActionResult< CNEOBot > CNEOBotJgrEscort::Update( CNEOBot *me, float interval )

// Check for own threats
const CKnownEntity *myThreat = me->GetVisionInterface()->GetPrimaryKnownThreat();
if ( myThreat )
if ( myThreat && myThreat->GetEntity() && myThreat->GetEntity()->IsAlive() )
{
return SuspendFor( new CNEOBotRetreatToCover, "Retreating to let Juggernaut get the kill" );
return SuspendFor( new CNEOBotAttack, "Breaking away from Juggernaut to engage threat" );
}

// Just follow the Juggernaut
// If too close, stop moving to avoid crowding
constexpr float flMinFollowDistSq = 200.0f * 200.0f;
float flDistSq = me->GetAbsOrigin().DistToSqr( pJuggernaut->GetAbsOrigin() );
float flSafeRadius = sv_neo_grenade_blast_radius.GetFloat();
float flSafeRadiusSq = flSafeRadius * flSafeRadius;

if ( flDistSq < flMinFollowDistSq )
if ( flDistSq < flSafeRadiusSq )
{
// Too close, stop moving
if ( m_chasePath.IsValid() )
Expand Down
Loading