From 6e474b2a747c591b6f409d1a095f3cc102500225 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Sat, 27 Dec 2025 14:47:47 -0800 Subject: [PATCH 1/2] JGR mode behavior dispatcher for bots --- src/game/server/CMakeLists.txt | 10 + .../neo/bot/behavior/neo_bot_jgr_capture.cpp | 140 ++++++++++++ .../neo/bot/behavior/neo_bot_jgr_capture.h | 29 +++ .../neo/bot/behavior/neo_bot_jgr_enemy.cpp | 79 +++++++ .../neo/bot/behavior/neo_bot_jgr_enemy.h | 27 +++ .../neo/bot/behavior/neo_bot_jgr_escort.cpp | 113 ++++++++++ .../neo/bot/behavior/neo_bot_jgr_escort.h | 27 +++ .../bot/behavior/neo_bot_jgr_juggernaut.cpp | 205 ++++++++++++++++++ .../neo/bot/behavior/neo_bot_jgr_juggernaut.h | 30 +++ .../neo/bot/behavior/neo_bot_jgr_seek.cpp | 112 ++++++++++ .../neo/bot/behavior/neo_bot_jgr_seek.h | 21 ++ .../bot/behavior/neo_bot_seek_and_destroy.cpp | 17 ++ .../bot/behavior/neo_bot_seek_and_destroy.h | 8 +- src/game/shared/neo/neo_juggernaut.cpp | 10 + src/game/shared/neo/neo_juggernaut.h | 3 + 15 files changed, 828 insertions(+), 3 deletions(-) create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.cpp create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_escort.cpp create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp create mode 100644 src/game/server/neo/bot/behavior/neo_bot_jgr_seek.h diff --git a/src/game/server/CMakeLists.txt b/src/game/server/CMakeLists.txt index d58937b7ba..a6302f3791 100644 --- a/src/game/server/CMakeLists.txt +++ b/src/game/server/CMakeLists.txt @@ -1438,6 +1438,16 @@ target_sources_grouped( neo/bot/behavior/neo_bot_behavior.h neo/bot/behavior/neo_bot_dead.cpp neo/bot/behavior/neo_bot_dead.h + neo/bot/behavior/neo_bot_jgr_capture.cpp + neo/bot/behavior/neo_bot_jgr_capture.h + neo/bot/behavior/neo_bot_jgr_escort.cpp + neo/bot/behavior/neo_bot_jgr_escort.h + neo/bot/behavior/neo_bot_jgr_enemy.cpp + neo/bot/behavior/neo_bot_jgr_enemy.h + neo/bot/behavior/neo_bot_jgr_juggernaut.cpp + neo/bot/behavior/neo_bot_jgr_juggernaut.h + neo/bot/behavior/neo_bot_jgr_seek.cpp + neo/bot/behavior/neo_bot_jgr_seek.h neo/bot/behavior/neo_bot_melee_attack.cpp neo/bot/behavior/neo_bot_melee_attack.h neo/bot/behavior/neo_bot_move_to_vantage_point.cpp diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp new file mode 100644 index 0000000000..14ff9dddaa --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp @@ -0,0 +1,140 @@ +#include "cbase.h" +#include "bot/behavior/neo_bot_jgr_capture.h" +#include "bot/behavior/neo_bot_retreat_to_cover.h" +#include "bot/neo_bot_path_compute.h" +#include "neo_gamerules.h" +#include "neo_juggernaut.h" + +//--------------------------------------------------------------------------------------------- +CNEOBotJgrCapture::CNEOBotJgrCapture( CBaseEntity *pObjective ) +{ + m_hObjective = pObjective; +} + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotJgrCapture::OnStart( CNEOBot *me, Action *priorAction ) +{ + m_useAttemptTimer.Invalidate(); + m_path.Invalidate(); + m_repathTimer.Invalidate(); + + if ( !m_hObjective ) + { + return Done( "No capture target specified." ); + } + + if ( NEORules()->GetGameType() != NEO_GAME_TYPE_JGR ) + { + return Done( "Not in JGR game mode" ); + } + + if ( !FStrEq( m_hObjective->GetClassname(), "neo_juggernaut" ) ) + { + return Done( "Capture target is not the juggernaut" ); + } + + // Ignore enemies while capturing juggernaut + me->StopLookingAroundForEnemies(); + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +void CNEOBotJgrCapture::OnEnd( CNEOBot *me, Action *nextAction ) +{ + me->ReleaseUseButton(); + me->StartLookingAroundForEnemies(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotJgrCapture::OnSuspend( CNEOBot *me, Action *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(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotJgrCapture::OnResume( CNEOBot *me, Action *interruptingAction ) +{ + me->StopLookingAroundForEnemies(); + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotJgrCapture::Update( CNEOBot *me, float interval ) +{ + if ( me->IsDead() ) + { + return Done( "I am dead" ); + } + + if ( !m_hObjective ) + { + return Done( "Capture target gone" ); + } + + if ( me->GetClass() == NEO_CLASS_JUGGERNAUT ) + { + return Done( "Became the Juggernaut" ); + } + + if ( me->GetAbsOrigin().DistToSqr( m_hObjective->GetAbsOrigin() ) < CNEO_Juggernaut::GetUseDistanceSquared() ) + { + CNEO_Juggernaut *pJuggernaut = static_cast(m_hObjective.Get()); + if ( NEORules()->IsJuggernautLocked() || (pJuggernaut && pJuggernaut->m_bLocked) ) + { + me->ReleaseUseButton(); + return SuspendFor( new CNEOBotRetreatToCover( 2.0f ), "Juggernaut is locked, taking cover to wait for it to unlock" ); + } + + // Stop moving while using + m_path.Invalidate(); + me->ReleaseForwardButton(); + me->ReleaseBackwardButton(); + me->ReleaseLeftButton(); + me->ReleaseRightButton(); + + const Vector vecToTarget = m_hObjective->WorldSpaceCenter() - me->EyePosition(); + Vector vecToTargetDir = vecToTarget; + 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; + + me->GetBodyInterface()->AimHeadTowards( m_hObjective->WorldSpaceCenter(), IBody::CRITICAL, 0.1f, NULL, "Looking at Juggernaut objective to use" ); + + if ( m_useAttemptTimer.HasStarted() ) + { + if ( m_useAttemptTimer.IsElapsed() ) + { + return Done( "Use timer elapsed, failed to capture" ); + } + } + else if ( me->GetBodyInterface()->IsHeadAimingOnTarget() && bIsFacing ) + { + m_useAttemptTimer.Start( CNEO_Juggernaut::GetUseDuration() + 1.0f ); + me->PressUseButton( CNEO_Juggernaut::GetUseDuration() + 1.0f ); + } + } + else + { + // Objective is farther than we can reach for cpature + if ( m_repathTimer.IsElapsed() ) + { + m_repathTimer.Start( RandomFloat( 1.0f, 5.0f ) ); + if ( !CNEOBotPathCompute( me, m_path, m_hObjective->GetAbsOrigin(), FASTEST_ROUTE ) ) + { + return Done( "Unable to find a path to the Juggernaut objective" ); + } + } + m_path.Update( me ); + } + + return Continue(); +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h new file mode 100644 index 0000000000..d1fe7fe8f3 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h @@ -0,0 +1,29 @@ +#ifndef NEO_BOT_CAPTURE_JGR_H +#define NEO_BOT_CAPTURE_JGR_H + +#include "NextBotBehavior.h" +#include "bot/neo_bot.h" + +//---------------------------------------------------------------------------------------------------------------- +class CNEOBotJgrCapture : public Action +{ +public: + CNEOBotJgrCapture( CBaseEntity *pObjective ); + virtual ~CNEOBotJgrCapture() { } + + virtual const char *GetName() const override { return "jgrCapture"; } + + virtual ActionResult OnStart( CNEOBot *me, Action *priorAction ) override; + virtual ActionResult Update( CNEOBot *me, float interval ) override; + virtual void OnEnd( CNEOBot *me, Action *nextAction ) override; + virtual ActionResult OnSuspend( CNEOBot *me, Action *interruptingAction ) override; + virtual ActionResult OnResume( CNEOBot *me, Action *interruptingAction ) override; + +private: + CHandle m_hObjective; + CountdownTimer m_useAttemptTimer; + PathFollower m_path; + CountdownTimer m_repathTimer; +}; + +#endif // NEO_BOT_CAPTURE_JGR_H diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.cpp new file mode 100644 index 0000000000..38c1e0ef44 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.cpp @@ -0,0 +1,79 @@ +#include "cbase.h" +#include "neo_player.h" +#include "bot/neo_bot.h" +#include "bot/behavior/neo_bot_jgr_enemy.h" +#include "bot/neo_bot_path_compute.h" +#include "neo_gamerules.h" +#include "bot/behavior/neo_bot_attack.h" + +//--------------------------------------------------------------------------------------------- +CNEOBotJgrEnemy::CNEOBotJgrEnemy( void ) +{ +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrEnemy::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) +{ + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrEnemy::Update( CNEOBot *me, float interval ) +{ + const int iJuggernautPlayer = NEORules()->GetJuggernautPlayer(); + if ( iJuggernautPlayer <= 0 ) + { + return Done( "No juggernaut player" ); + } + + CNEO_Player* pJuggernaut = ToNEOPlayer( UTIL_PlayerByIndex( iJuggernautPlayer ) ); + if ( !pJuggernaut ) + { + return Done( "Juggernaut is invalid" ); + } + + if ( !pJuggernaut->IsAlive() ) + { + return Done( "Juggernaut is dead" ); + } + + if ( pJuggernaut->GetTeamNumber() == me->GetTeamNumber() ) + { + return Done( "Juggernaut is friendly" ); + } + + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat && !threat->IsObsolete() && me->GetIntentionInterface()->ShouldAttack( me, threat ) ) + { + return SuspendFor( new CNEOBotAttack, "Attacking threat" ); + } + + // Chase the Juggernaut + CNEOBotPathUpdateChase( me, m_chasePath, pJuggernaut, DEFAULT_ROUTE ); + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrEnemy::OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ) +{ + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotJgrEnemy::OnStuck( CNEOBot *me ) +{ + return TryContinue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotJgrEnemy::OnMoveToSuccess( CNEOBot *me, const Path *path ) +{ + return TryContinue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotJgrEnemy::OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) +{ + return TryContinue(); +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h new file mode 100644 index 0000000000..1d5e6ce0e3 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h @@ -0,0 +1,27 @@ +#ifndef NEO_BOT_JGR_ENEMY_H +#define NEO_BOT_JGR_ENEMY_H + +#include "bot/neo_bot.h" +#include "Path/NextBotChasePath.h" + +//-------------------------------------------------------------------------------------------------------- +class CNEOBotJgrEnemy : public Action< CNEOBot > +{ +public: + CNEOBotJgrEnemy( void ); + + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ); + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ); + virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ); + + virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ); + virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ); + virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ); + + virtual const char *GetName( void ) const { return "JgrEnemy"; } + +private: + ChasePath m_chasePath; +}; + +#endif // NEO_BOT_JGR_ENEMY_H diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.cpp new file mode 100644 index 0000000000..d01d86dd94 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.cpp @@ -0,0 +1,113 @@ +#include "cbase.h" +#include "neo_player.h" +#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" + +//--------------------------------------------------------------------------------------------- +CNEOBotJgrEscort::CNEOBotJgrEscort( void ) +{ +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrEscort::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) +{ + m_chasePath.Invalidate(); + return Continue(); +} + + + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrEscort::Update( CNEOBot *me, float interval ) +{ + const int iJuggernautPlayer = NEORules()->GetJuggernautPlayer(); + if ( iJuggernautPlayer <= 0 ) + { + return Done( "No juggernaut player" ); + } + + CNEO_Player* pJuggernaut = ToNEOPlayer( UTIL_PlayerByIndex( iJuggernautPlayer ) ); + if ( !pJuggernaut ) + { + return Done( "Juggernaut is invalid" ); + } + + if ( !pJuggernaut->IsAlive() ) + { + return Done( "Juggernaut is dead" ); + } + + if ( pJuggernaut->GetTeamNumber() != me->GetTeamNumber() ) + { + return Done( "Juggernaut is not friendly" ); + } + + // Check if we can assist the Juggernaut (if they are a bot) + CNEOBot *pBotJuggernaut = ToNEOBot( pJuggernaut ); + if ( pBotJuggernaut ) + { + const CKnownEntity *juggernautThreat = pBotJuggernaut->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( juggernautThreat ) + { + // Check if the threat has a clear shot at our friend + if ( me->IsLineOfFireClear( juggernautThreat->GetLastKnownPosition(), pJuggernaut, CNEOBot::LINE_OF_FIRE_FLAGS_DEFAULT ) ) + { + return SuspendFor( new CNEOBotAttack, "Assisting Juggernaut with their threat" ); + } + } + } + + // Check for own threats + const CKnownEntity *myThreat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( myThreat ) + { + return SuspendFor( new CNEOBotRetreatToCover, "Retreating to let Juggernaut get the kill" ); + } + + // 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() ); + + if ( flDistSq < flMinFollowDistSq ) + { + // Too close, stop moving + if ( m_chasePath.IsValid() ) + { + m_chasePath.Invalidate(); + } + return Continue(); + } + + CNEOBotPathUpdateChase( me, m_chasePath, pJuggernaut, DEFAULT_ROUTE ); + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrEscort::OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ) +{ + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotJgrEscort::OnStuck( CNEOBot *me ) +{ + return TryContinue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotJgrEscort::OnMoveToSuccess( CNEOBot *me, const Path *path ) +{ + return TryContinue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotJgrEscort::OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) +{ + return TryContinue(); +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h new file mode 100644 index 0000000000..b891b3671c --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h @@ -0,0 +1,27 @@ +#ifndef NEO_BOT_JGR_ESCORT_H +#define NEO_BOT_JGR_ESCORT_H + +#include "bot/neo_bot.h" +#include "Path/NextBotChasePath.h" + +//-------------------------------------------------------------------------------------------------------- +class CNEOBotJgrEscort : public Action< CNEOBot > +{ +public: + CNEOBotJgrEscort( void ); + + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ); + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ); + virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ); + + virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ); + virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ); + virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ); + + virtual const char *GetName( void ) const { return "jgrEscort"; } + +private: + ChasePath m_chasePath; +}; + +#endif // NEO_BOT_JGR_ESCORT_H diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp new file mode 100644 index 0000000000..2298a1e3d0 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp @@ -0,0 +1,205 @@ +#include "cbase.h" +#include "neo_player.h" +#include "neo_gamerules.h" +#include "bot/neo_bot.h" +#include "bot/neo_bot_path_compute.h" +#include "bot/behavior/neo_bot_jgr_juggernaut.h" + +//--------------------------------------------------------------------------------------------- +static CSound* SearchGunfireSounds(CNEOBot* me) +{ + CSound* pClosestSound = NULL; + float flClosestDistSqr = FLT_MAX; + const Vector& vecMyOrigin = me->GetAbsOrigin(); + + int iSound = CSoundEnt::ActiveList(); + while (iSound != SOUNDLIST_EMPTY) + { + CSound* pSound = CSoundEnt::SoundPointerForIndex(iSound); + if (!pSound) + break; + + if ((pSound->SoundType() & (SOUND_COMBAT | SOUND_BULLET_IMPACT)) && pSound->ValidateOwner()) + { + // Don't listen to sounds I was responsible for + if (pSound->m_hOwner.Get() == me->GetEntity()) + { + iSound = pSound->NextSound(); + continue; + } + + // Search for the closest gunfire sounds + float distSqr = (pSound->GetSoundOrigin() - vecMyOrigin).LengthSqr(); + if (distSqr < flClosestDistSqr) + { + flClosestDistSqr = distSqr; + pClosestSound = pSound; + } + } + + iSound = pSound->NextSound(); + } + + return pClosestSound; +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrJuggernaut::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) +{ + m_jgrSpawns.RemoveAll(); + + CBaseEntity* pSearch = NULL; + while ((pSearch = gEntList.FindEntityByClassname(pSearch, "neo_juggernautspawnpoint")) != NULL) + { + m_jgrSpawns.AddToTail(pSearch); + } + + m_stuckTimer.Invalidate(); + m_bStuckCrouchPhase = false; + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrJuggernaut::Update( CNEOBot *me, float interval ) +{ + ActionResult< CNEOBot > result = UpdateCommon( me, interval ); + if ( result.IsRequestingChange() || result.IsDone() ) + return result; + + // Listen for gunfire + if ( !m_soundSearchTimer.HasStarted() || m_soundSearchTimer.IsElapsed() ) + { + m_soundSearchTimer.Start( 0.25f ); + + CSound* pBestSound = SearchGunfireSounds(me); + if (pBestSound) + { + // Only change goal if recent gunfire is radically different than where I was going + constexpr float flThresholdSqr = 200.0f * 200.0f; + if (m_vGoalPos.DistToSqr(pBestSound->GetSoundOrigin()) > flThresholdSqr) + { + m_vGoalPos = pBestSound->GetSoundOrigin(); + // gunfire is not an entity target + m_bGoingToTargetEntity = false; + + if (CNEOBotPathCompute(me, m_path, m_vGoalPos, FASTEST_ROUTE)) + { + return Continue(); + } + } + } + } + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +void CNEOBotJgrJuggernaut::RecomputeSeekPath( CNEOBot *me ) +{ + m_hTargetEntity = NULL; + m_bGoingToTargetEntity = false; + m_vGoalPos = vec3_origin; + + // Listen for gunfights + CSound* pBestSound = SearchGunfireSounds(me); + if (pBestSound) + { + m_vGoalPos = pBestSound->GetSoundOrigin(); + m_bGoingToTargetEntity = false; + + if (CNEOBotPathCompute(me, m_path, m_vGoalPos, FASTEST_ROUTE) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH) + { + return; + } + } + + // If unaware of gunfights, patrol spawn points + if (m_jgrSpawns.Count() > 0) + { + CBaseEntity* pTargetSpawn = m_jgrSpawns[ RandomInt(0, m_jgrSpawns.Count() - 1) ]; + if (pTargetSpawn) + { + m_vGoalPos = pTargetSpawn->GetAbsOrigin(); + m_bGoingToTargetEntity = false; + + if (CNEOBotPathCompute(me, m_path, m_vGoalPos, DEFAULT_ROUTE) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH) + { + return; + } + } + } + + // Fallback to base behavior + CNEOBotSeekAndDestroy::RecomputeSeekPath( me ); +} + + +//--------------------------------------------------------------------------------------------- +// Adds a periodic crouch attempt on top of CNEOBotMainAction::OnStuck for height clearance +EventDesiredResult< CNEOBot > CNEOBotJgrJuggernaut::OnStuck( CNEOBot *me ) +{ + UTIL_LogPrintf( "\"%s<%i><%s>\" stuck (position \"%3.2f %3.2f %3.2f\") (duration \"%3.2f\") ", + me->GetNeoPlayerName(), + me->GetUserID(), + me->GetNetworkIDString(), + me->GetAbsOrigin().x, me->GetAbsOrigin().y, me->GetAbsOrigin().z, + me->GetLocomotionInterface()->GetStuckDuration() ); + + const PathFollower *path = me->GetCurrentPath(); + if ( path && path->GetCurrentGoal() ) + { + UTIL_LogPrintf( " path_goal ( \"%3.2f %3.2f %3.2f\" )\n", + path->GetCurrentGoal()->pos.x, + path->GetCurrentGoal()->pos.y, + path->GetCurrentGoal()->pos.z ); + } + else + { + UTIL_LogPrintf( " path_goal ( \"NULL\" )\n" ); + } + + if ( !m_stuckTimer.HasStarted() || m_stuckTimer.IsElapsed() ) + { + m_stuckTimer.Start( 1.0f ); + m_bStuckCrouchPhase = !m_bStuckCrouchPhase; + } + + // Try to crouch under doorways/gates + if ( m_bStuckCrouchPhase ) + { + me->PressCrouchButton(); + } + // ... in addition to usual OnStuck behavior + else + { + me->GetLocomotionInterface()->Jump(); + } + + if ( RandomInt( 0, 1 ) ) + { + me->PressLeftButton(); + } + else + { + me->PressRightButton(); + } + + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +QueryResultType CNEOBotJgrJuggernaut::ShouldWalk( const CNEOBot *me, const QueryResultType qShouldAimQuery ) const +{ + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( threat && threat->GetEntity() ) + { + // BALC weapon doesn't have an aim mode, so need to walk to allow shooting + // Sometimes my primary threat is not the one I am aiming at so just walk to be ready to fire + return ANSWER_YES; + } + + return ANSWER_UNDEFINED; +} + diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h new file mode 100644 index 0000000000..6cf0285e65 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h @@ -0,0 +1,30 @@ +#ifndef NEO_BOT_JGR_JUGGERNAUT_H +#define NEO_BOT_JGR_JUGGERNAUT_H +#pragma once + +#include "bot/behavior/neo_bot_seek_and_destroy.h" + +class CNEOBotJgrJuggernaut : public CNEOBotSeekAndDestroy +{ +public: + CNEOBotJgrJuggernaut( float duration = -1.0f ) : CNEOBotSeekAndDestroy( duration ), m_bStuckCrouchPhase( false ) {} + + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ); + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ); + virtual const char *GetName( void ) const { return "jgrJuggernaut"; } + + virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ); + + virtual QueryResultType ShouldWalk( const CNEOBot *me, const QueryResultType qShouldAimQuery ) const; + +protected: + virtual void RecomputeSeekPath( CNEOBot *me ); + + CountdownTimer m_stuckTimer; + bool m_bStuckCrouchPhase; + +private: + CUtlVector> m_jgrSpawns; + CountdownTimer m_soundSearchTimer; +}; +#endif // NEO_BOT_JGR_JUGGERNAUT_H diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp new file mode 100644 index 0000000000..05d21403a6 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp @@ -0,0 +1,112 @@ +#include "cbase.h" +#include "neo_player.h" +#include "neo_gamerules.h" +#include "bot/neo_bot.h" +#include "bot/neo_bot_path_compute.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" +#include "bot/behavior/neo_bot_jgr_capture.h" +#include "bot/behavior/neo_bot_jgr_juggernaut.h" + + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotJgrSeek::Update( CNEOBot *me, float interval ) +{ + if (NEORules()->GetGameType() != NEO_GAME_TYPE_JGR) + { + return Done( "Game mode is no longer JGR" ); + } + + if ( me->GetClass() != NEO_CLASS_JUGGERNAUT ) + { + int iJuggernautPlayer = NEORules()->GetJuggernautPlayer(); + if (iJuggernautPlayer > 0) + { + CNEO_Player* pJuggernaut = ToNEOPlayer(UTIL_PlayerByIndex(iJuggernautPlayer)); + if (pJuggernaut && pJuggernaut != me && pJuggernaut->IsAlive()) + { + if (pJuggernaut->GetTeamNumber() == me->GetTeamNumber()) + { + return SuspendFor(new CNEOBotJgrEscort, "Escorting the Juggernaut"); + } + else + { + return SuspendFor(new CNEOBotJgrEnemy, "Attacking the Juggernaut"); + } + } + } + } + + ActionResult< CNEOBot > result = UpdateCommon( me, interval ); + if ( result.IsRequestingChange() || result.IsDone() ) + return result; + + if ( me->GetClass() == NEO_CLASS_JUGGERNAUT ) + { + return SuspendFor( new CNEOBotJgrJuggernaut, "I am the Juggernaut now" ); + } + + // Juggernaut objective capture logic + if (m_bGoingToTargetEntity && m_hTargetEntity) + { + const float useRangeSq = 100.0f * 100.0f; + if ( me->GetAbsOrigin().DistToSqr( m_hTargetEntity->GetAbsOrigin() ) < useRangeSq ) + { + if ( !m_hTargetEntity->IsPlayer() ) + { + if ( me->IsLineOfSightClear( m_hTargetEntity, CBaseCombatCharacter::IGNORE_ACTORS ) ) + { + const char *classname = m_hTargetEntity->GetClassname(); + + if ( FStrEq( classname, "neo_juggernaut" ) ) + { + return SuspendFor( new CNEOBotJgrCapture( m_hTargetEntity ), "Capturing Juggernaut" ); + } + } + } + } + } + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +void CNEOBotJgrSeek::RecomputeSeekPath( CNEOBot *me ) +{ + if (NEORules()->GetGameType() != NEO_GAME_TYPE_JGR) + { + CNEOBotSeekAndDestroy::RecomputeSeekPath( me ); + return; + } + + m_hTargetEntity = NULL; + m_bGoingToTargetEntity = false; + m_vGoalPos = vec3_origin; + + if (NEORules()->JuggernautItemExists()) + { + const int iJuggernautPlayer = NEORules()->GetJuggernautPlayer(); + + // If it's on the ground (no owner) + if (iJuggernautPlayer <= 0) + { + CBaseEntity* pJuggernaut = gEntList.FindEntityByClassname(NULL, "neo_juggernaut"); + if (pJuggernaut) + { + m_vGoalPos = pJuggernaut->WorldSpaceCenter(); + m_bGoingToTargetEntity = true; + m_hTargetEntity = pJuggernaut; + + if (CNEOBotPathCompute(me, m_path, m_vGoalPos, FASTEST_ROUTE) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH) + { + return; + } + } + } + + } + + // Fallback to base behavior (roaming spawn points) + CNEOBotSeekAndDestroy::RecomputeSeekPath( me ); +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.h new file mode 100644 index 0000000000..a8ede5723b --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.h @@ -0,0 +1,21 @@ +#ifndef NEO_BOT_JGR_SEEK_H +#define NEO_BOT_JGR_SEEK_H +#pragma once + +#include "bot/behavior/neo_bot_seek_and_destroy.h" + +// +// JGR game mode behavior dispatcher +// +class CNEOBotJgrSeek : public CNEOBotSeekAndDestroy +{ +public: + CNEOBotJgrSeek( float duration = -1.0f ) : CNEOBotSeekAndDestroy( duration ) { } + + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ); + virtual const char *GetName( void ) const { return "jgrSeek"; }; + +protected: + virtual void RecomputeSeekPath( CNEOBot *me ); +}; +#endif // NEO_BOT_JGR_SEEK_H diff --git a/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp b/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp index b67c4cefca..8094c5ec3b 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp @@ -5,6 +5,7 @@ #include "bot/neo_bot.h" #include "bot/behavior/neo_bot_attack.h" #include "bot/behavior/neo_bot_seek_and_destroy.h" +#include "bot/behavior/neo_bot_jgr_seek.h" #include "bot/neo_bot_path_compute.h" #include "nav_mesh.h" #include "neo_ghost_cap_point.h" @@ -51,6 +52,22 @@ ActionResult< CNEOBot > CNEOBotSeekAndDestroy::OnStart( CNEOBot *me, Action< CNE //--------------------------------------------------------------------------------------------- ActionResult< CNEOBot > CNEOBotSeekAndDestroy::Update( CNEOBot *me, float interval ) +{ + ActionResult< CNEOBot > result = UpdateCommon( me, interval ); + if ( result.IsRequestingChange() || result.IsDone() ) + return result; + + // Check for Game Type Specific behaviors and suspend for them + if (NEORules()->GetGameType() == NEO_GAME_TYPE_JGR) + { + return SuspendFor( new CNEOBotJgrSeek, "Switching to Juggernaut-related Seek and Destroy" ); + } + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotSeekAndDestroy::UpdateCommon( CNEOBot *me, float interval ) { if ( m_giveUpTimer.HasStarted() && m_giveUpTimer.IsElapsed() ) { diff --git a/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.h b/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.h index 39950bfa70..a905221535 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.h +++ b/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.h @@ -31,7 +31,11 @@ class CNEOBotSeekAndDestroy : public Action< CNEOBot > virtual const char *GetName( void ) const { return "SeekAndDestroy"; }; -private: +protected: + virtual ActionResult< CNEOBot > UpdateCommon( CNEOBot *me, float interval ); + + virtual void RecomputeSeekPath( CNEOBot *me ); + PathFollower m_path; CountdownTimer m_repathTimer; CountdownTimer m_itemStolenTimer; @@ -39,8 +43,6 @@ class CNEOBotSeekAndDestroy : public Action< CNEOBot > bool m_bGoingToTargetEntity = false; Vector m_vGoalPos = vec3_origin; bool m_bTimerElapsed = false; - void RecomputeSeekPath( CNEOBot *me ); - bool m_bOverrideApproach = false; Vector m_vOverrideApproach = vec3_origin; diff --git a/src/game/shared/neo/neo_juggernaut.cpp b/src/game/shared/neo/neo_juggernaut.cpp index c3591fe81e..70d9168411 100644 --- a/src/game/shared/neo/neo_juggernaut.cpp +++ b/src/game/shared/neo/neo_juggernaut.cpp @@ -15,6 +15,16 @@ LINK_ENTITY_TO_CLASS(neo_juggernaut, CNEO_Juggernaut); +float CNEO_Juggernaut::GetUseDuration() +{ + return USE_DURATION; +} + +float CNEO_Juggernaut::GetUseDistanceSquared() +{ + return USE_DISTANCE_SQUARED; +} + #ifdef GAME_DLL IMPLEMENT_SERVERCLASS_ST(CNEO_Juggernaut, DT_NEO_Juggernaut) SendPropBool(SENDINFO(m_bLocked)), diff --git a/src/game/shared/neo/neo_juggernaut.h b/src/game/shared/neo/neo_juggernaut.h index fe921a5091..c9b38e291f 100644 --- a/src/game/shared/neo/neo_juggernaut.h +++ b/src/game/shared/neo/neo_juggernaut.h @@ -15,6 +15,9 @@ class CNEO_Juggernaut : public CBaseAnimating { public: DECLARE_CLASS(CNEO_Juggernaut, CBaseAnimating); + + static float GetUseDuration(); + static float GetUseDistanceSquared(); #ifdef GAME_DLL virtual ~CNEO_Juggernaut(); DECLARE_SERVERCLASS(); From 58e0f52373ce1b07381bc586fdd70905d2a67cb0 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Sat, 3 Jan 2026 22:28:36 -0700 Subject: [PATCH 2/2] Code Review Feedback --- src/game/server/CMakeLists.txt | 4 ++-- .../neo/bot/behavior/neo_bot_behavior.cpp | 1 + .../neo/bot/behavior/neo_bot_jgr_capture.cpp | 23 ++++++++++++++----- .../neo/bot/behavior/neo_bot_jgr_capture.h | 4 ++-- .../neo/bot/behavior/neo_bot_jgr_enemy.h | 14 +++++------ .../neo/bot/behavior/neo_bot_jgr_escort.h | 14 +++++------ .../bot/behavior/neo_bot_jgr_juggernaut.cpp | 15 ------------ .../neo/bot/behavior/neo_bot_jgr_juggernaut.h | 12 ++++------ .../neo/bot/behavior/neo_bot_jgr_seek.cpp | 6 +++-- 9 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/game/server/CMakeLists.txt b/src/game/server/CMakeLists.txt index a6302f3791..d1c0d03487 100644 --- a/src/game/server/CMakeLists.txt +++ b/src/game/server/CMakeLists.txt @@ -1440,10 +1440,10 @@ target_sources_grouped( neo/bot/behavior/neo_bot_dead.h neo/bot/behavior/neo_bot_jgr_capture.cpp neo/bot/behavior/neo_bot_jgr_capture.h - neo/bot/behavior/neo_bot_jgr_escort.cpp - neo/bot/behavior/neo_bot_jgr_escort.h neo/bot/behavior/neo_bot_jgr_enemy.cpp neo/bot/behavior/neo_bot_jgr_enemy.h + neo/bot/behavior/neo_bot_jgr_escort.cpp + neo/bot/behavior/neo_bot_jgr_escort.h neo/bot/behavior/neo_bot_jgr_juggernaut.cpp neo/bot/behavior/neo_bot_jgr_juggernaut.h neo/bot/behavior/neo_bot_jgr_seek.cpp diff --git a/src/game/server/neo/bot/behavior/neo_bot_behavior.cpp b/src/game/server/neo/bot/behavior/neo_bot_behavior.cpp index 8725e2f91f..40336fdbd7 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_behavior.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_behavior.cpp @@ -777,6 +777,7 @@ void CNEOBotMainAction::FireWeaponAtEnemy( CNEOBot *me ) { // Minimum viable firing BALC // TODO: Proper heat management for higher difficulty bots + me->ReleaseWalkButton(); // NEO Jank: this actually cancels sprint me->PressFireButton(GetFireDurationByDifficulty(me)); return; } diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp index 14ff9dddaa..8280e1c06b 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp @@ -6,7 +6,7 @@ #include "neo_juggernaut.h" //--------------------------------------------------------------------------------------------- -CNEOBotJgrCapture::CNEOBotJgrCapture( CBaseEntity *pObjective ) +CNEOBotJgrCapture::CNEOBotJgrCapture( CNEO_Juggernaut *pObjective ) { m_hObjective = pObjective; } @@ -81,11 +81,22 @@ ActionResult CNEOBotJgrCapture::Update( CNEOBot *me, float interval ) return Done( "Became the Juggernaut" ); } + const int iJuggernautPlayer = NEORules()->GetJuggernautPlayer(); + if ( iJuggernautPlayer > 0 ) + { + CNEO_Player *pJuggernautPlayer = ToNEOPlayer( UTIL_PlayerByIndex( iJuggernautPlayer ) ); + if ( pJuggernautPlayer && pJuggernautPlayer != me ) + { + return Done( "Juggernaut captured by someone else" ); + } + } + if ( me->GetAbsOrigin().DistToSqr( m_hObjective->GetAbsOrigin() ) < CNEO_Juggernaut::GetUseDistanceSquared() ) { - CNEO_Juggernaut *pJuggernaut = static_cast(m_hObjective.Get()); + CNEO_Juggernaut *pJuggernaut = m_hObjective.Get(); if ( NEORules()->IsJuggernautLocked() || (pJuggernaut && pJuggernaut->m_bLocked) ) { + Assert( NEORules()->IsJuggernautLocked() == (pJuggernaut && pJuggernaut->m_bLocked) ); me->ReleaseUseButton(); return SuspendFor( new CNEOBotRetreatToCover( 2.0f ), "Juggernaut is locked, taking cover to wait for it to unlock" ); } @@ -97,8 +108,8 @@ ActionResult CNEOBotJgrCapture::Update( CNEOBot *me, float interval ) me->ReleaseLeftButton(); me->ReleaseRightButton(); - const Vector vecToTarget = m_hObjective->WorldSpaceCenter() - me->EyePosition(); - Vector vecToTargetDir = vecToTarget; + const Vector vecObjectiveCenter = m_hObjective->WorldSpaceCenter(); + Vector vecToTargetDir = vecObjectiveCenter - me->EyePosition(); vecToTargetDir.NormalizeInPlace(); // Ensure we are facing the target before attempting to use @@ -107,7 +118,7 @@ ActionResult CNEOBotJgrCapture::Update( CNEOBot *me, float interval ) const float flDot = vecEyeDirection.Dot( vecToTargetDir ); const bool bIsFacing = flDot > 0.9f; - me->GetBodyInterface()->AimHeadTowards( m_hObjective->WorldSpaceCenter(), IBody::CRITICAL, 0.1f, NULL, "Looking at Juggernaut objective to use" ); + me->GetBodyInterface()->AimHeadTowards( vecObjectiveCenter, IBody::CRITICAL, 0.1f, NULL, "Looking at Juggernaut objective to use" ); if ( m_useAttemptTimer.HasStarted() ) { @@ -116,7 +127,7 @@ ActionResult CNEOBotJgrCapture::Update( CNEOBot *me, float interval ) return Done( "Use timer elapsed, failed to capture" ); } } - else if ( me->GetBodyInterface()->IsHeadAimingOnTarget() && bIsFacing ) + else if ( bIsFacing && me->GetBodyInterface()->IsHeadAimingOnTarget() ) { m_useAttemptTimer.Start( CNEO_Juggernaut::GetUseDuration() + 1.0f ); me->PressUseButton( CNEO_Juggernaut::GetUseDuration() + 1.0f ); diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h index d1fe7fe8f3..984697a65a 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h @@ -8,7 +8,7 @@ class CNEOBotJgrCapture : public Action { public: - CNEOBotJgrCapture( CBaseEntity *pObjective ); + CNEOBotJgrCapture( CNEO_Juggernaut *pObjective ); virtual ~CNEOBotJgrCapture() { } virtual const char *GetName() const override { return "jgrCapture"; } @@ -20,7 +20,7 @@ class CNEOBotJgrCapture : public Action virtual ActionResult OnResume( CNEOBot *me, Action *interruptingAction ) override; private: - CHandle m_hObjective; + CHandle m_hObjective; CountdownTimer m_useAttemptTimer; PathFollower m_path; CountdownTimer m_repathTimer; diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h index 1d5e6ce0e3..f5f8f11f83 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.h @@ -10,15 +10,15 @@ class CNEOBotJgrEnemy : public Action< CNEOBot > public: CNEOBotJgrEnemy( void ); - virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ); - virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ); - virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ); + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) override; + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ) override; + virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ) override; - virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ); - virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ); - virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ); + virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ) override; + virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ) override; + virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) override; - virtual const char *GetName( void ) const { return "JgrEnemy"; } + virtual const char *GetName( void ) const override { return "JgrEnemy"; } private: ChasePath m_chasePath; diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h index b891b3671c..8a05b44e8f 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_escort.h @@ -10,15 +10,15 @@ class CNEOBotJgrEscort : public Action< CNEOBot > public: CNEOBotJgrEscort( void ); - virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ); - virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ); - virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ); + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) override; + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ) override; + virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ) override; - virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ); - virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ); - virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ); + virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ) override; + virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ) override; + virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) override; - virtual const char *GetName( void ) const { return "jgrEscort"; } + virtual const char *GetName( void ) const override { return "jgrEscort"; } private: ChasePath m_chasePath; diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp index 2298a1e3d0..2ea54f6120 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp @@ -188,18 +188,3 @@ EventDesiredResult< CNEOBot > CNEOBotJgrJuggernaut::OnStuck( CNEOBot *me ) return TryContinue(); } - -//--------------------------------------------------------------------------------------------- -QueryResultType CNEOBotJgrJuggernaut::ShouldWalk( const CNEOBot *me, const QueryResultType qShouldAimQuery ) const -{ - const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); - if ( threat && threat->GetEntity() ) - { - // BALC weapon doesn't have an aim mode, so need to walk to allow shooting - // Sometimes my primary threat is not the one I am aiming at so just walk to be ready to fire - return ANSWER_YES; - } - - return ANSWER_UNDEFINED; -} - diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h index 6cf0285e65..bfc1e1532c 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.h @@ -9,16 +9,14 @@ class CNEOBotJgrJuggernaut : public CNEOBotSeekAndDestroy public: CNEOBotJgrJuggernaut( float duration = -1.0f ) : CNEOBotSeekAndDestroy( duration ), m_bStuckCrouchPhase( false ) {} - virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ); - virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ); - virtual const char *GetName( void ) const { return "jgrJuggernaut"; } + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) override; + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ) override; + virtual const char *GetName( void ) const override { return "jgrJuggernaut"; } - virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ); - - virtual QueryResultType ShouldWalk( const CNEOBot *me, const QueryResultType qShouldAimQuery ) const; + virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ) override; protected: - virtual void RecomputeSeekPath( CNEOBot *me ); + virtual void RecomputeSeekPath( CNEOBot *me ) override; CountdownTimer m_stuckTimer; bool m_bStuckCrouchPhase; diff --git a/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp b/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp index 05d21403a6..33f89244af 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp @@ -8,6 +8,7 @@ #include "bot/behavior/neo_bot_jgr_enemy.h" #include "bot/behavior/neo_bot_jgr_capture.h" #include "bot/behavior/neo_bot_jgr_juggernaut.h" +#include "neo_juggernaut.h" //--------------------------------------------------------------------------------------------- @@ -24,6 +25,7 @@ ActionResult< CNEOBot > CNEOBotJgrSeek::Update( CNEOBot *me, float interval ) if (iJuggernautPlayer > 0) { CNEO_Player* pJuggernaut = ToNEOPlayer(UTIL_PlayerByIndex(iJuggernautPlayer)); + Assert( pJuggernaut != me ); if (pJuggernaut && pJuggernaut != me && pJuggernaut->IsAlive()) { if (pJuggernaut->GetTeamNumber() == me->GetTeamNumber()) @@ -50,7 +52,7 @@ ActionResult< CNEOBot > CNEOBotJgrSeek::Update( CNEOBot *me, float interval ) // Juggernaut objective capture logic if (m_bGoingToTargetEntity && m_hTargetEntity) { - const float useRangeSq = 100.0f * 100.0f; + const float useRangeSq = CNEO_Juggernaut::GetUseDistanceSquared() * 0.8f; if ( me->GetAbsOrigin().DistToSqr( m_hTargetEntity->GetAbsOrigin() ) < useRangeSq ) { if ( !m_hTargetEntity->IsPlayer() ) @@ -61,7 +63,7 @@ ActionResult< CNEOBot > CNEOBotJgrSeek::Update( CNEOBot *me, float interval ) if ( FStrEq( classname, "neo_juggernaut" ) ) { - return SuspendFor( new CNEOBotJgrCapture( m_hTargetEntity ), "Capturing Juggernaut" ); + return SuspendFor( new CNEOBotJgrCapture( static_cast(m_hTargetEntity.Get()) ), "Capturing Juggernaut" ); } } }