From 26018157ace44e6f093c287dabfeeeb501b0ddfd Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Sat, 7 Mar 2026 09:34:27 -0700 Subject: [PATCH 1/2] Lone bots deploy detpack ambush at ghost --- src/game/server/CMakeLists.txt | 5 + .../neo/bot/behavior/neo_bot_ctg_capture.cpp | 26 +++ .../bot/behavior/neo_bot_ctg_lone_wolf.cpp | 27 ++- .../neo/bot/behavior/neo_bot_ctg_lone_wolf.h | 2 +- .../behavior/neo_bot_ctg_lone_wolf_ambush.cpp | 13 +- .../behavior/neo_bot_ctg_lone_wolf_ambush.h | 1 + .../behavior/neo_bot_ctg_lone_wolf_seek.cpp | 24 +- .../bot/behavior/neo_bot_ctg_lone_wolf_seek.h | 1 + .../bot/behavior/neo_bot_detpack_deploy.cpp | 218 ++++++++++++++++++ .../neo/bot/behavior/neo_bot_detpack_deploy.h | 35 +++ .../bot/behavior/neo_bot_detpack_trigger.cpp | 82 +++++++ .../bot/behavior/neo_bot_detpack_trigger.h | 26 +++ .../bot/behavior/neo_bot_tactical_monitor.cpp | 126 ++++++++++ .../bot/behavior/neo_bot_tactical_monitor.h | 2 + src/game/server/neo/bot/neo_bot.cpp | 77 +++++++ src/game/server/neo/bot/neo_bot.h | 1 + src/game/server/neo/neo_player.cpp | 5 + src/game/shared/neo/neo_gamerules.h | 1 + .../shared/neo/weapons/weapon_detpack.cpp | 7 +- src/game/shared/neo/weapons/weapon_detpack.h | 6 + 20 files changed, 668 insertions(+), 17 deletions(-) create mode 100644 src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.cpp create mode 100644 src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.h create mode 100644 src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.cpp create mode 100644 src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.h diff --git a/src/game/server/CMakeLists.txt b/src/game/server/CMakeLists.txt index 5f9434ff9a..fd002b63f9 100644 --- a/src/game/server/CMakeLists.txt +++ b/src/game/server/CMakeLists.txt @@ -1589,6 +1589,11 @@ target_sources_grouped( neo/bot/behavior/neo_bot_ctg_seek.cpp neo/bot/behavior/neo_bot_ctg_seek.h neo/bot/behavior/neo_bot_dead.h + neo/bot/behavior/neo_bot_detpack_deploy.cpp + neo/bot/behavior/neo_bot_detpack_deploy.h + neo/bot/behavior/neo_bot_detpack_trigger.cpp + neo/bot/behavior/neo_bot_detpack_trigger.h + neo/bot/behavior/neo_bot_grenade_dispatch.cpp neo/bot/behavior/neo_bot_grenade_dispatch.h neo/bot/behavior/neo_bot_grenade_throw.h neo/bot/behavior/neo_bot_grenade_throw_frag.h diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp index d6ee9d542a..b0647673bc 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp @@ -3,6 +3,7 @@ #include "bot/behavior/neo_bot_ctg_lone_wolf_seek.h" #include "bot/behavior/neo_bot_seek_weapon.h" #include "bot/neo_bot_path_compute.h" +#include "neo_detpack.h" #include "weapon_ghost.h" @@ -68,6 +69,31 @@ ActionResult CNEOBotCtgCapture::Update( CNEOBot *me, float interval ) m_captureAttemptTimer.Start( 3.0f ); } + // Check if there is a detpack that risks exploding if I pick up the ghost + // NEO Jank: It may be more proper to check for line of sight, + // but triggering an entity search at the last moment may involve fewer overall calculations + // with the lampshade explanation as the bot being able to notice the detpack along the path + // even if we didn't check constantly along the same path + CBaseEntity *pEnts[256]; + int numEnts = UTIL_EntitiesInSphere( pEnts, 256, me->GetAbsOrigin(), NEO_DETPACK_DAMAGE_RADIUS, 0 ); + bool bDetpackNear = false; + for ( int i = 0; i < numEnts; ++i ) + { + if ( pEnts[i] && FClassnameIs( pEnts[i], "neo_deployed_detpack" ) ) + { + bDetpackNear = true; + break; + } + } + + if ( bDetpackNear ) + { + // NEO JANK: Putting the bot into seek mode will have it search the map for enemies for the rest of the round + // but for now this could be fine as it may indicate an entrenched enemy + // or a friendly that is setting up an ambush, where either scenario indicates ghost capture is too dangerous + return ChangeTo( new CNEOBotCtgLoneWolfSeek(), "Found detpack: skipping ghost capture to search for enemies" ); + } + CBaseCombatWeapon *pPrimary = me->Weapon_GetSlot( 0 ); if ( pPrimary ) { diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp index 060824007a..c9a4483326 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp @@ -5,6 +5,7 @@ #include "bot/behavior/neo_bot_ctg_lone_wolf.h" #include "bot/behavior/neo_bot_ctg_lone_wolf_ambush.h" #include "bot/behavior/neo_bot_ctg_lone_wolf_seek.h" +#include "bot/behavior/neo_bot_detpack_deploy.h" #include "bot/neo_bot_path_compute.h" #include "neo_gamerules.h" #include "neo_ghost_cap_point.h" @@ -46,17 +47,21 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::Update( CNEOBot *me, float interval me->ReloadIfLowClip(true); // force reload true } - const CWeaponDetpack *pDetpackWeapon = assert_cast( me->Weapon_OwnsThisType( "weapon_remotedet" ) ); + CWeaponDetpack *const pDetpackWeapon = assert_cast( me->Weapon_OwnsThisType( "weapon_remotedet" ) ); if ( pDetpackWeapon && pDetpackWeapon->m_bThisDetpackHasBeenThrown && !pDetpackWeapon->m_bRemoteHasBeenTriggered ) { return ChangeTo( new CNEOBotCtgLoneWolfAmbush(), "Detpack deployed, transitioning to ambush" ); } - const CNavArea *ghostArea = TheNavMesh->GetNearestNavArea( NEORules()->GetGhostPos() ); - const CNavArea *myArea = me->GetLastKnownArea(); + CNavArea *const ghostArea = TheNavMesh->GetNearestNavArea( NEORules()->GetGhostPos() ); + CNavArea *const myArea = me->GetLastKnownArea(); if ( ghostArea && myArea && ghostArea->IsPotentiallyVisible( myArea ) ) { + if ( pDetpackWeapon && !pDetpackWeapon->m_bThisDetpackHasBeenThrown && NEORules()->m_pGhost ) + { + return ChangeTo( new CNEOBotDetpackDeploy( NEORules()->GetGhostPos(), new CNEOBotCtgLoneWolfAmbush() ), "Moving to plant detpack" ); + } return ChangeTo( new CNEOBotCtgLoneWolfAmbush(), "Waiting in ambush near ghost" ); } @@ -87,7 +92,7 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::ConsiderGhostInterception( CNEOBot * return SuspendFor( new CNEOBotAttack, "Attacking the ghost carrier!" ); } - const Vector& vecInterceptGoal = NEORules()->GetGhostPos(); + Vector vecInterceptGoal = NEORules()->GetGhostPos(); if ( vecInterceptGoal != CNEO_Player::VECTOR_INVALID_WAYPOINT ) { if ( !m_repathTimer.HasStarted() || m_repathTimer.IsElapsed() ) @@ -123,7 +128,7 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::ConsiderGhostVisualCheck( CNEOBot *m } // Move to ghost's location to gain visual contact - const Vector& vecAcquireGoal = NEORules()->GetGhostPos(); + Vector vecAcquireGoal = NEORules()->GetGhostPos(); if ( vecAcquireGoal != CNEO_Player::VECTOR_INVALID_WAYPOINT ) { if ( !m_repathTimer.HasStarted() || m_repathTimer.IsElapsed() ) @@ -165,18 +170,16 @@ EventDesiredResult< CNEOBot > CNEOBotCtgLoneWolf::OnStuck( CNEOBot *me ) //--------------------------------------------------------------------------------------------- -Vector CNEOBotCtgLoneWolf::GetNearestEnemyCapPoint( CNEOBot *me ) const +Vector CNEOBotCtgLoneWolf::GetNearestEnemyCapPoint( CNEOBot *me ) { if ( !me ) - { return CNEO_Player::VECTOR_INVALID_WAYPOINT; - } const int iEnemyTeam = NEORules()->GetOpposingTeam( me->GetTeamNumber() ); if ( NEORules()->m_pGhostCaps.Count() > 0 ) { - const Vector* pBestPos = nullptr; + Vector bestPos = CNEO_Player::VECTOR_INVALID_WAYPOINT; float flNearestSq = FLT_MAX; for ( int i = 0; i < NEORules()->m_pGhostCaps.Count(); ++i ) { @@ -193,11 +196,11 @@ Vector CNEOBotCtgLoneWolf::GetNearestEnemyCapPoint( CNEOBot *me ) const if ( distSq < flNearestSq ) { flNearestSq = distSq; - pBestPos = &pCapPoint->GetAbsOrigin(); - } + bestPos = pCapPoint->GetAbsOrigin(); } } - return pBestPos ? *pBestPos : CNEO_Player::VECTOR_INVALID_WAYPOINT; + } + return bestPos; } return CNEO_Player::VECTOR_INVALID_WAYPOINT; diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h index a38f6e3371..e6629e7977 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h @@ -21,7 +21,7 @@ class CNEOBotCtgLoneWolf : public Action< CNEOBot > virtual ActionResult< CNEOBot > ConsiderGhostInterception( CNEOBot *me, const CBaseCombatCharacter *pGhostOwner = nullptr ); virtual ActionResult< CNEOBot > ConsiderGhostVisualCheck( CNEOBot *me ); - Vector GetNearestEnemyCapPoint( CNEOBot *me ) const; + Vector GetNearestEnemyCapPoint( CNEOBot *me ); CountdownTimer m_repathTimer; PathFollower m_path; diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp index 2264071ea7..d206f68c7e 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp @@ -254,7 +254,18 @@ bool CNEOBotCtgLoneWolfAmbush::Is1v1( CNEOBot *me ) // I entered this function because my teammates are dead if ( !m_1v1Timer.HasStarted() || m_1v1Timer.IsElapsed() ) { - int iAliveEnemyCount = g_Teams[GetEnemyTeam(me->GetTeamNumber())]->GetAliveMembers(); + int iAliveEnemyCount = 0; + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CNEO_Player *pPlayer = ToNEOPlayer( UTIL_PlayerByIndex( i ) ); + if ( pPlayer && pPlayer->IsAlive() && !me->InSameTeam( pPlayer ) ) + { + if ( ++iAliveEnemyCount > 1 ) + { + break; + } + } + } m_bIs1v1 = ( iAliveEnemyCount == 1 ); m_1v1Timer.Start( 2.0f ); } diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h index cb19d2e339..5394f2c82a 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h @@ -3,6 +3,7 @@ #include "bot/neo_bot.h" #include "bot/behavior/neo_bot_ctg_lone_wolf.h" +class CWeaponDetpack; //-------------------------------------------------------------------------------------------------------- class CNEOBotCtgLoneWolfAmbush : public CNEOBotCtgLoneWolf { diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp index b5f40b25e9..07b9092c38 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp @@ -158,7 +158,27 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolfSeek::Update( CNEOBot *me, float inter } } - const Vector& currentGhostPos = NEORules()->GetGhostPos(); + Vector vecSoundPos = me->GetAudibleEnemySoundPos(); + if ( vecSoundPos != CNEO_Player::VECTOR_INVALID_WAYPOINT ) + { + // Don't veer path for sound if waypoint is not that far off + if ( m_vecSearchWaypoint.DistToSqr( vecSoundPos ) > Square( 200.0f ) ) + { + m_vecSearchWaypoint = vecSoundPos; + m_path.Invalidate(); + m_repathTimer.Invalidate(); // path to sound next tick + + CNavArea *soundArea = TheNavMesh->GetNearestNavArea( vecSoundPos ); + if ( soundArea ) + { + m_iExplorationTargetId = (int)soundArea->GetID(); + // Mark sound area as not explored + m_exploredAreaIds.Remove( m_iExplorationTargetId ); + } + } + } + + const Vector currentGhostPos = NEORules()->GetGhostPos(); if ( !m_pCachedGhostArea || currentGhostPos.DistToSqr( m_vecLastGhostPos ) > Square( 64.0f ) ) { CNavArea *pLastGhostArea = m_pCachedGhostArea; @@ -217,7 +237,7 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolfSeek::Update( CNEOBot *me, float inter if ( search.m_candidateAreas.IsEmpty() ) { // Track already explored areas around the ghost - auto searchFromVisible = [&search]( CNavArea *visibleArea ) -> bool + auto searchFromVisible = [&]( CNavArea *visibleArea ) -> bool { if ( search.m_candidateAreas.Count() >= CSearchForUnexplored::CANDIDATE_LIMIT || search.m_iAreaCount >= search.m_iAreaLimit ) { diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h index f03fa44910..afe7683131 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h @@ -5,6 +5,7 @@ #include "utlmap.h" #include +class CWeaponDetpack; class CNEOIgnoredWeaponsCache; //-------------------------------------------------------------------------------------------------------- diff --git a/src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.cpp b/src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.cpp new file mode 100644 index 0000000000..65bb4ae17c --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.cpp @@ -0,0 +1,218 @@ +#include "cbase.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_ctg_lone_wolf_seek.h" +#include "bot/behavior/neo_bot_detpack_deploy.h" +#include "neo_detpack.h" +#include "weapon_detpack.h" + +//--------------------------------------------------------------------------------------------- +CNEOBotDetpackDeploy::CNEOBotDetpackDeploy( const Vector &targetPos, Action< CNEOBot > *nextAction ) + : m_targetPos( targetPos ), m_nextAction( nextAction ), m_flDeployDistSq( 0.0f ) +{ + m_bPushedWeapon = false; + m_losTimer.Invalidate(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotDetpackDeploy::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) +{ + m_hDetpackWeapon = assert_cast< CWeaponDetpack* >( me->Weapon_OwnsThisType( "weapon_remotedet" ) ); + if ( !m_hDetpackWeapon ) + { + if (m_nextAction != nullptr) + { + return ChangeTo( m_nextAction, "No detpack weapon, transitioning to next action" ); + } + return Done( "No detpack weapon found" ); + } + + me->PushRequiredWeapon( m_hDetpackWeapon ); + m_bPushedWeapon = true; + + // Ignore enemy to prevent weapon handling interference with detpack deployment + me->StopLookingAroundForEnemies(); + me->SetAttribute( CNEOBot::IGNORE_ENEMIES ); + + m_expiryTimer.Start( 10.0f ); + m_repathTimer.Invalidate(); + + m_flDeployDistSq = Square( MAX( 100.0f, CWeaponDetpack::GetArmingTime() * me->GetNormSpeed() ) ); + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotDetpackDeploy::Update( CNEOBot *me, float interval ) +{ + if ( !m_hDetpackWeapon ) + { + if ( m_nextAction ) + { + return ChangeTo( m_nextAction, "No detpack weapon, transitioning to next action" ); + } + return Done( "No detpack weapon" ); + } + + if ( m_expiryTimer.IsElapsed() ) + { + if ( m_nextAction ) + { + return ChangeTo( m_nextAction, "Detpack deploy timer expired, transitioning to next action" ); + } + return Done( "Detpack deploy timer expired" ); + } + + if ( m_hDetpackWeapon->m_bThisDetpackHasBeenThrown ) + { + if ( m_nextAction ) + { + return ChangeTo( m_nextAction, "Detpack deployed, transitioning to next action" ); + } + return Done( "Detpack deployed" ); + } + + const CKnownEntity* threat = me->GetVisionInterface()->GetPrimaryKnownThreat( true ); + if ( threat && threat->GetEntity() ) + { + if (m_nextAction != nullptr) + { + return ChangeTo( m_nextAction, "Interrupting detpack deploy to let next action handle enemy" ); + } + return ChangeTo( new CNEOBotAttack(), "Engaging enemy encountered while deploying detpack" ); + } + + float flDistToTargetSq = me->GetAbsOrigin().DistToSqr( m_targetPos ); + if ( flDistToTargetSq < m_flDeployDistSq ) + { + if ( me->GetActiveWeapon() == m_hDetpackWeapon ) + { + if ( !m_losTimer.HasStarted() || m_losTimer.IsElapsed() ) + { + if ( me->GetVisionInterface()->IsLineOfSightClear( m_targetPos ) ) + { + CBaseEntity *pEnts[256]; + int numEnts = UTIL_EntitiesInSphere( pEnts, 256, me->GetAbsOrigin(), NEO_DETPACK_DAMAGE_RADIUS, 0 ); + bool bDetpackNear = false; + for ( int i = 0; i < numEnts; ++i ) + { + if ( pEnts[i] && FClassnameIs( pEnts[i], "neo_deployed_detpack" ) ) + { + bDetpackNear = true; + break; + } + } + + if ( bDetpackNear ) + { + if ( m_nextAction ) + { + return ChangeTo( m_nextAction, "Skipping detpack deploy: in blast radius of another detpack" ); + } + return Done( "Aborting detpack deploy: in blast radius of another detpack" ); + } + + me->PressFireButton(); + + if ( flDistToTargetSq < Square( 64.0f ) ) + { + m_path.Invalidate(); + m_repathTimer.Start( 10.0f ); + } + } + + m_losTimer.Start( RandomFloat( 0.2f, 0.4f ) ); + } + } + } + + if ( !m_path.IsValid() ) + { + if ( !m_repathTimer.HasStarted() || m_repathTimer.IsElapsed() ) + { + CNEOBotPathCompute( me, m_path, m_targetPos, FASTEST_ROUTE ); + m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) ); + } + } + else + { + m_path.Update( me ); + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +void CNEOBotDetpackDeploy::OnEnd( CNEOBot *me, Action< CNEOBot > *nextAction ) +{ + if ( m_bPushedWeapon ) + { + me->PopRequiredWeapon(); + m_bPushedWeapon = false; + } + me->StartLookingAroundForEnemies(); + me->ClearAttribute( CNEOBot::IGNORE_ENEMIES ); + + if ( m_hDetpackWeapon && m_hDetpackWeapon->m_bThisDetpackHasBeenThrown ) + { + me->EquipBestWeaponForThreat( me->GetVisionInterface()->GetPrimaryKnownThreat( true ) ); + } +} + + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotDetpackDeploy::OnSuspend( CNEOBot *me, Action *interruptingAction ) +{ + if (m_nextAction != nullptr) + { + return ChangeTo( m_nextAction, "Detpack deploy suspend cancelled, transitioning to next action" ); + } + + return Done( "Detpack deploy suspended, situation will likely become stale." ); + // OnEnd will get called after Done +} + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotDetpackDeploy::OnResume( CNEOBot *me, Action *interruptingAction ) +{ + if (m_nextAction != nullptr) + { + return ChangeTo( m_nextAction, "Detpack deploy resume cancelled, transitioning to next action" ); + } + + return Done( "Detpack deploy resumed, situation is likely stale." ); + // OnEnd will get called after Done +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotDetpackDeploy::OnStuck( CNEOBot *me ) +{ + if (m_nextAction != nullptr) + { + return TryChangeTo( m_nextAction, RESULT_CRITICAL, "Detpack deploy stuck, transitioning to next action" ); + } + + return TryDone( RESULT_CRITICAL, "Detpack deploy stuck, situation will likely become stale." ); + // OnEnd will get called after Done +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotDetpackDeploy::OnMoveToSuccess( CNEOBot *me, const Path *path ) +{ + return TryContinue(); +} + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CNEOBot > CNEOBotDetpackDeploy::OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) +{ + if (m_nextAction != nullptr) + { + return TryChangeTo( m_nextAction, RESULT_CRITICAL, "Detpack deploy move to failure, transitioning to next action" ); + } + + return TryDone( RESULT_CRITICAL, "Detpack deploy move to failure, situation will likely become stale." ); + // OnEnd will get called after Done +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.h b/src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.h new file mode 100644 index 0000000000..3f0c91ba42 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_detpack_deploy.h @@ -0,0 +1,35 @@ +#pragma once + +#include "bot/neo_bot.h" + +class CWeaponDetpack; + +//-------------------------------------------------------------------------------------------------------- +class CNEOBotDetpackDeploy : public Action< CNEOBot > +{ +public: + CNEOBotDetpackDeploy( const Vector &targetPos, Action< CNEOBot > *nextAction = nullptr ); + + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) override; + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ) override; + virtual ActionResult< CNEOBot > OnSuspend( CNEOBot *me, Action< CNEOBot > *interruptingAction ) override; + virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ) override; + virtual void OnEnd( CNEOBot *me, Action< CNEOBot > *nextAction ) override; + + 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 override { return "DetpackDeploy"; } + +private: + bool m_bPushedWeapon; + float m_flDeployDistSq; + Action< CNEOBot > *m_nextAction; + CHandle< CWeaponDetpack > m_hDetpackWeapon; + CountdownTimer m_expiryTimer; + CountdownTimer m_losTimer; + CountdownTimer m_repathTimer; + PathFollower m_path; + Vector m_targetPos; +}; diff --git a/src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.cpp b/src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.cpp new file mode 100644 index 0000000000..1ebb6173d4 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.cpp @@ -0,0 +1,82 @@ +#include "cbase.h" +#include "bot/neo_bot.h" +#include "bot/behavior/neo_bot_detpack_trigger.h" +#include "weapon_detpack.h" + +//--------------------------------------------------------------------------------------------- +CNEOBotDetpackTrigger::CNEOBotDetpackTrigger( void ) +{ + m_hDetpackWeapon = nullptr; + m_bPushedWeapon = false; +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotDetpackTrigger::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) +{ + m_hDetpackWeapon = assert_cast( me->Weapon_OwnsThisType( "weapon_remotedet" ) ); + if ( m_hDetpackWeapon ) + { + me->PushRequiredWeapon( m_hDetpackWeapon ); + m_bPushedWeapon = true; + } + else + { + return Done( "No detpack weapon found" ); + } + + // Ignore enemy to prevent weapon handling interference with detpack trigger + me->StopLookingAroundForEnemies(); + me->SetAttribute( CNEOBot::IGNORE_ENEMIES ); + + m_expiryTimer.Start( 5.0f ); + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotDetpackTrigger::Update( CNEOBot *me, float interval ) +{ + if ( m_expiryTimer.IsElapsed() ) + { + return Done( "Detpack trigger timer expired" ); + } + + if ( !m_hDetpackWeapon || !m_hDetpackWeapon->m_bThisDetpackHasBeenThrown || m_hDetpackWeapon->m_bRemoteHasBeenTriggered ) + { + return Done( "Detpack triggered or invalid" ); + } + + if ( me->GetActiveWeapon() == m_hDetpackWeapon && gpGlobals->curtime >= m_hDetpackWeapon->m_flNextPrimaryAttack ) + { + me->PressFireButton(); + } + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +void CNEOBotDetpackTrigger::OnEnd( CNEOBot *me, Action< CNEOBot > *nextAction ) +{ + // Restore looking and weapon handling behaviors + if ( m_bPushedWeapon ) + { + me->PopRequiredWeapon(); + m_bPushedWeapon = false; + } + me->StartLookingAroundForEnemies(); + me->ClearAttribute( CNEOBot::IGNORE_ENEMIES ); +} + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotDetpackTrigger::OnSuspend( CNEOBot *me, Action *interruptingAction ) +{ + return Done( "OnSuspend: Cancel out of detpack trigger behavior, situation will likely become stale." ); + // OnEnd will get called after Done +} + +//--------------------------------------------------------------------------------------------- +ActionResult CNEOBotDetpackTrigger::OnResume( CNEOBot *me, Action *interruptingAction ) +{ + return Done( "OnResume: Cancel out of detpack trigger behavior, situation is likely stale." ); + // OnEnd will get called after Done +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.h b/src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.h new file mode 100644 index 0000000000..ebd1b6abb2 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_detpack_trigger.h @@ -0,0 +1,26 @@ +#pragma once + +#include "bot/neo_bot.h" + +class CWeaponDetpack; + +//----------------------------------------------------------------------------- +class CNEOBotDetpackTrigger : public Action< CNEOBot > +{ +public: + CNEOBotDetpackTrigger( void ); + + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) override; + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ) override; + virtual void OnEnd( CNEOBot *me, Action< CNEOBot > *nextAction ) override; + + virtual ActionResult OnSuspend( CNEOBot *me, Action *interruptingAction ) override; + virtual ActionResult OnResume( CNEOBot *me, Action *interruptingAction ) override; + + virtual const char *GetName( void ) const override { return "DetpackTrigger"; } + +private: + bool m_bPushedWeapon; + CHandle< CWeaponDetpack > m_hDetpackWeapon; + CountdownTimer m_expiryTimer; +}; diff --git a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp index 78e8be610a..cded9861b5 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp @@ -8,6 +8,7 @@ #include "bot/neo_bot.h" #include "bot/neo_bot_manager.h" +#include "bot/behavior/neo_bot_detpack_trigger.h" #include "bot/behavior/neo_bot_tactical_monitor.h" #include "bot/behavior/neo_bot_scenario_monitor.h" @@ -26,6 +27,9 @@ #include "bot/behavior/nav_entities/neo_bot_nav_ent_move_to.h" #include "bot/behavior/nav_entities/neo_bot_nav_ent_wait.h" #include "neo/neo_player_shared.h" +#include "neo_detpack.h" +#include "weapon_detpack.h" +#include "weapon_ghost.h" #include "nav_mesh.h" ConVar neo_bot_force_jump( "neo_bot_force_jump", "0", FCVAR_CHEAT, "Force bots to continuously jump" ); @@ -85,6 +89,122 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::OnStart( CNEOBot *me, Action< CN } +//----------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotTacticalMonitor::MonitorArmedDetpack( CNEOBot *me ) +{ + if ( !m_detpackCheckTimer.IsElapsed() ) + { + return Continue(); + } + m_detpackCheckTimer.Start( 0.2f ); + + CWeaponDetpack *pDetWeapon = assert_cast( me->Weapon_OwnsThisType( "weapon_remotedet" ) ); + if ( !pDetWeapon || !pDetWeapon->m_bThisDetpackHasBeenThrown || pDetWeapon->m_bRemoteHasBeenTriggered ) + { + return Continue(); + } + + CBaseEntity *pDetpackEnt = pDetWeapon->GetDetpackEntity(); + if ( !pDetpackEnt ) + { + return Continue(); + } + + const Vector vecDetpackPos = pDetpackEnt->GetAbsOrigin(); + + // Check if I am too close to the detpack + if ( me->GetAbsOrigin().DistToSqr( vecDetpackPos ) <= Square( NEO_DETPACK_DAMAGE_RADIUS ) ) + { + return Continue(); + } + + float flThresholdMultiplier; + switch ( me->GetDifficulty() ) + { + case CNEOBot::EASY: + flThresholdMultiplier = 1.05f; + break; + case CNEOBot::NORMAL: + flThresholdMultiplier = 0.95f; + break; + case CNEOBot::HARD: + flThresholdMultiplier = 0.85f; + break; + case CNEOBot::EXPERT: + flThresholdMultiplier = 0.75f; + break; + default: + flThresholdMultiplier = 0.95f; + break; + } + + const float flMaxRadiusSq = Square( NEO_DETPACK_DAMAGE_RADIUS * flThresholdMultiplier ); + bool bShouldDetonate = false; + + // Check if any known threat or teammate is in range + CUtlVector< CKnownEntity > knownVector; + me->GetVisionInterface()->CollectKnownEntities( &knownVector ); + bool bIsTeamplay = NEORules()->IsTeamplay(); + + for ( int i = 0; i < knownVector.Count(); ++i ) + { + if ( knownVector[i].IsObsolete() ) + { + continue; + } + + CBaseEntity *pEntity = knownVector[i].GetEntity(); + if ( !pEntity ) + { + continue; + } + + if ( vecDetpackPos.DistToSqr( knownVector[i].GetLastKnownPosition() ) <= flMaxRadiusSq ) + { + if ( bIsTeamplay && me->InSameTeam( pEntity ) ) + { + // Teammate in blast radius + return Continue(); + } + + // Enemy in range. + bShouldDetonate = true; + } + } + + // Check if ghost carrier is in range + if ( !bShouldDetonate ) + { + if ( CWeaponGhost *pGhost = NEORules()->m_pGhost ) + { + CBaseCombatCharacter *pGhostOwner = pGhost->GetOwner(); + if ( pGhostOwner && !me->InSameTeam( pGhostOwner ) ) + { + if ( vecDetpackPos.DistToSqr( pGhostOwner->GetAbsOrigin() ) <= flMaxRadiusSq ) + { + bShouldDetonate = true; + } + } + } + } + + if ( !bShouldDetonate ) + { + if ( me->GetAudibleEnemySoundPos( vecDetpackPos, flMaxRadiusSq ) != CNEO_Player::VECTOR_INVALID_WAYPOINT ) + { + bShouldDetonate = true; + } + } + + if ( bShouldDetonate ) + { + return SuspendFor( new CNEOBotDetpackTrigger(), "Triggering detpack!" ); + } + + return Continue(); +} + + #ifndef NEO // NEO TODO (Adam) Monitor the remote detpack //----------------------------------------------------------------------------------------- void CNEOBotTacticalMonitor::MonitorArmedStickyBombs( CNEOBot *me ) @@ -311,6 +431,12 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter } #endif + ActionResult< CNEOBot > detpackResult = MonitorArmedDetpack( me ); + if ( detpackResult.IsRequestingChange() ) + { + return detpackResult; + } + #if 0 // NEO TODO (Adam) detonate remote detpacks // detonate sticky bomb traps when victims are near MonitorArmedStickyBombs( me ); diff --git a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.h b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.h index 83634acfcf..0cfe08a0d2 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.h +++ b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.h @@ -33,6 +33,8 @@ class CNEOBotTacticalMonitor : public Action< CNEOBot > CountdownTimer m_attentionTimer; std::unique_ptr m_pIgnoredWeapons; + CountdownTimer m_detpackCheckTimer; + ActionResult< CNEOBot > MonitorArmedDetpack(CNEOBot *me); #if 0 CountdownTimer m_stickyBombCheckTimer; void MonitorArmedStickyBombs(CNEOBot* me); diff --git a/src/game/server/neo/bot/neo_bot.cpp b/src/game/server/neo/bot/neo_bot.cpp index 1931b34153..dbd883e128 100644 --- a/src/game/server/neo/bot/neo_bot.cpp +++ b/src/game/server/neo/bot/neo_bot.cpp @@ -21,6 +21,8 @@ #include "neo_weapon_loadout.h" #include "behavior/neo_bot_behavior.h" #include "neo_crosshair.h" +#include "recipientfilter.h" +#include "soundent.h" ConVar neo_bot_notice_gunfire_range("neo_bot_notice_gunfire_range", "3000", FCVAR_GAMEDLL); ConVar neo_bot_notice_quiet_gunfire_range("neo_bot_notice_quiet_gunfire_range", "500", FCVAR_GAMEDLL); @@ -2939,5 +2941,80 @@ QueryResultType CNEOBotBehavior::ShouldAim(const CNEOBot *me, const bool bWepHas return result; } +//--------------------------------------------------------------------------------------------- +Vector CNEOBot::GetAudibleEnemySoundPos(const Vector& vecReferencePos, float flMaxRangeSq) const +{ + CSound *pSound = nullptr; + for ( int iSound = CSoundEnt::ActiveList(); iSound != SOUNDLIST_EMPTY; iSound = pSound->NextSound() ) + { + pSound = CSoundEnt::SoundPointerForIndex( iSound ); + if ( !pSound ) + { + break; + } + + // If a reference position and range are provided, check distance first + if ( vecReferencePos != CNEO_Player::VECTOR_INVALID_WAYPOINT && flMaxRangeSq > 0.0f ) + { + if ( pSound->GetSoundOrigin().DistToSqr( vecReferencePos ) > flMaxRangeSq ) + { + continue; + } + } + + if ( ( pSound->SoundType() & ( SOUND_COMBAT | SOUND_PLAYER ) ) == 0 ) + { + continue; + } + + CBaseEntity *pOwner = pSound->m_hOwner.Get(); + + // Ignore non-player sounds and sounds I was responsible for + if ( !pOwner || !pOwner->IsPlayer() || pOwner == GetEntity() ) + { + continue; + } + + // Only care about sounds from the enemy + if ( InSameTeam( pOwner ) ) + { + continue; + } + + // Check if I can hear the sound + bool bCanHearEnemy = false; + CPASFilter soundFilter( pSound->GetSoundOrigin() ); + for ( int i = 0; i < soundFilter.GetRecipientCount(); ++i ) + { + if ( soundFilter.GetRecipientIndex( i ) == entindex() ) + { + bCanHearEnemy = true; + break; + } + } + + if ( !bCanHearEnemy ) + { + // Check if I can hear the shooter + CPASFilter shooterFilter( pOwner->GetAbsOrigin() ); + for ( int i = 0; i < shooterFilter.GetRecipientCount(); ++i ) + { + if ( shooterFilter.GetRecipientIndex( i ) == entindex() ) + { + bCanHearEnemy = true; + break; + } + } + } + + if ( bCanHearEnemy ) + { + return pSound->GetSoundOrigin(); + } + } + + return CNEO_Player::VECTOR_INVALID_WAYPOINT; +} + diff --git a/src/game/server/neo/bot/neo_bot.h b/src/game/server/neo/bot/neo_bot.h index eef55c4c83..b88feac775 100644 --- a/src/game/server/neo/bot/neo_bot.h +++ b/src/game/server/neo/bot/neo_bot.h @@ -172,6 +172,7 @@ class CNEOBot : public NextBotPlayer< CNEO_Player >, public CGameEventListener bool IsQuietWeapon(CNEOBaseCombatWeapon* weapon) const; // return true if given weapon doesn't make much sound when used (ie: spy knife, etc) bool IsEnvironmentNoisy(void) const; // return true if there are/have been loud noises (ie: non-quiet weapons) nearby very recently + Vector GetAudibleEnemySoundPos(const Vector& vecReferencePos = CNEO_Player::VECTOR_INVALID_WAYPOINT, float flMaxRangeSq = -1.0f) const; bool IsEnemy(const CBaseEntity* them) const OVERRIDE; diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 268ac07c44..d957849f94 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -1293,6 +1293,9 @@ void CNEO_Player::PlayCloakSound(bool removeLocalPlayer) // effect lasts 0.5 seconds, but allow 200-300ms leeway with GetFogObscuredRatio cache window m_botThermOpticCamoDisruptedTimer.Start(0.2f); } + + // For bots to notice cloak sound + CSoundEnt::InsertSound(SOUND_COMBAT, GetAbsOrigin(), 600, 0.2, this); } void CNEO_Player::SetCloakState(bool state) @@ -2949,6 +2952,8 @@ void CNEO_Player::PickupObject( CBaseEntity *pObject, void CNEO_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) { + // For bots to hear footsteps + CSoundEnt::InsertSound(SOUND_PLAYER, GetAbsOrigin(), 150, 0.1, this); BaseClass::PlayStepSound(vecOrigin, psurface, fvol, force); } diff --git a/src/game/shared/neo/neo_gamerules.h b/src/game/shared/neo/neo_gamerules.h index 7d3045be32..af67c76868 100644 --- a/src/game/shared/neo/neo_gamerules.h +++ b/src/game/shared/neo/neo_gamerules.h @@ -86,6 +86,7 @@ class CNEO_Player; class CWeaponGhost; class CNEOBotCtgLoneWolf; class CNEOBotCtgLoneWolfAmbush; +class CNEOBotCtgLoneWolfDetpack; class CNEOBotCtgLoneWolfSeek; class CNEOBotSeekAndDestroy; diff --git a/src/game/shared/neo/weapons/weapon_detpack.cpp b/src/game/shared/neo/weapons/weapon_detpack.cpp index 8228525a9e..510cdc1ef7 100644 --- a/src/game/shared/neo/weapons/weapon_detpack.cpp +++ b/src/game/shared/neo/weapons/weapon_detpack.cpp @@ -19,6 +19,7 @@ #include "te_effect_dispatch.h" #include "grenade_frag.h" #include "eventqueue.h" +#include "soundent.h" #endif #include "effect_dispatch_data.h" @@ -251,7 +252,7 @@ void CWeaponDetpack::ItemPostFrame(void) pOwner->DoAnimationEvent(PLAYERANIMEVENT_ATTACK_PRIMARY); // NEO NOTE (Rain): Why 0.9? Because we want the explosion to occur after 2.666... seconds of detpack arming, - // plus 1.333... seconds of the trigger, for a total of 4 seconds delay. And we just happen to need 0.9 seconds + // plus ~1.333... seconds of the trigger, for a total of 4 seconds delay. And we just happen to need 0.9 seconds // here to reach that. There's probably some nicer way to arrive at these values, but that's the explanation // for this magic value. m_flNextPrimaryAttack = gpGlobals->curtime + 0.9; @@ -304,6 +305,10 @@ void CWeaponDetpack::TossDetpack(CBasePlayer* pPlayer) { Assert(false); } + + // Notify bots after pressing keypad since reacting immediately to press fire is unforgiving + // especially since the sound clip has some silence at the beginning + CSoundEnt::InsertSound( SOUND_COMBAT, pPlayer->GetAbsOrigin(), 256, 0.2, pPlayer, SOUNDENT_CHANNEL_WEAPON ); #endif if (GetOwner()->IsPlayer()) // NEO NOTE (Adam) if else taken from CBaseCombatWeapon::Equip, this must be what was fixing the viewmodel previously after dropping and picking up the detremote { diff --git a/src/game/shared/neo/weapons/weapon_detpack.h b/src/game/shared/neo/weapons/weapon_detpack.h index 0a7938c6ce..e46d6442d6 100644 --- a/src/game/shared/neo/weapons/weapon_detpack.h +++ b/src/game/shared/neo/weapons/weapon_detpack.h @@ -53,6 +53,12 @@ class CWeaponDetpack : public CNEOBaseProjectile virtual float GetSpeedScale(void) const OVERRIDE { return 0.85f; } +#ifdef GAME_DLL + CNEODeployedDetpack* GetDetpackEntity() const { return m_pDetpack; } +#endif + + static float GetArmingTime() { return 2.66f; } // see Rain's comment in DecrementAmmo + bool CanDrop(void) OVERRIDE; virtual bool CanAim() final { return false; } virtual bool CanPerformSecondaryAttack() const override final { return false; } From f38efaeabdeaa449f1a49e41a475d2654a7341b2 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Mon, 13 Jul 2026 23:15:58 -0600 Subject: [PATCH 2/2] Excise changes not related to just arming detpack --- .../neo/bot/behavior/neo_bot_ctg_capture.cpp | 26 ------- .../bot/behavior/neo_bot_ctg_lone_wolf.cpp | 22 +++--- .../neo/bot/behavior/neo_bot_ctg_lone_wolf.h | 2 +- .../behavior/neo_bot_ctg_lone_wolf_ambush.cpp | 13 +--- .../behavior/neo_bot_ctg_lone_wolf_ambush.h | 1 - .../behavior/neo_bot_ctg_lone_wolf_seek.cpp | 24 +----- .../bot/behavior/neo_bot_ctg_lone_wolf_seek.h | 1 - .../bot/behavior/neo_bot_tactical_monitor.cpp | 8 -- src/game/server/neo/bot/neo_bot.cpp | 77 ------------------- src/game/server/neo/bot/neo_bot.h | 1 - src/game/server/neo/neo_player.cpp | 5 -- 11 files changed, 16 insertions(+), 164 deletions(-) diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp index b0647673bc..d6ee9d542a 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_capture.cpp @@ -3,7 +3,6 @@ #include "bot/behavior/neo_bot_ctg_lone_wolf_seek.h" #include "bot/behavior/neo_bot_seek_weapon.h" #include "bot/neo_bot_path_compute.h" -#include "neo_detpack.h" #include "weapon_ghost.h" @@ -69,31 +68,6 @@ ActionResult CNEOBotCtgCapture::Update( CNEOBot *me, float interval ) m_captureAttemptTimer.Start( 3.0f ); } - // Check if there is a detpack that risks exploding if I pick up the ghost - // NEO Jank: It may be more proper to check for line of sight, - // but triggering an entity search at the last moment may involve fewer overall calculations - // with the lampshade explanation as the bot being able to notice the detpack along the path - // even if we didn't check constantly along the same path - CBaseEntity *pEnts[256]; - int numEnts = UTIL_EntitiesInSphere( pEnts, 256, me->GetAbsOrigin(), NEO_DETPACK_DAMAGE_RADIUS, 0 ); - bool bDetpackNear = false; - for ( int i = 0; i < numEnts; ++i ) - { - if ( pEnts[i] && FClassnameIs( pEnts[i], "neo_deployed_detpack" ) ) - { - bDetpackNear = true; - break; - } - } - - if ( bDetpackNear ) - { - // NEO JANK: Putting the bot into seek mode will have it search the map for enemies for the rest of the round - // but for now this could be fine as it may indicate an entrenched enemy - // or a friendly that is setting up an ambush, where either scenario indicates ghost capture is too dangerous - return ChangeTo( new CNEOBotCtgLoneWolfSeek(), "Found detpack: skipping ghost capture to search for enemies" ); - } - CBaseCombatWeapon *pPrimary = me->Weapon_GetSlot( 0 ); if ( pPrimary ) { diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp index c9a4483326..5a2556b015 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp @@ -47,15 +47,15 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::Update( CNEOBot *me, float interval me->ReloadIfLowClip(true); // force reload true } - CWeaponDetpack *const pDetpackWeapon = assert_cast( me->Weapon_OwnsThisType( "weapon_remotedet" ) ); + const CWeaponDetpack *pDetpackWeapon = assert_cast( me->Weapon_OwnsThisType( "weapon_remotedet" ) ); if ( pDetpackWeapon && pDetpackWeapon->m_bThisDetpackHasBeenThrown && !pDetpackWeapon->m_bRemoteHasBeenTriggered ) { return ChangeTo( new CNEOBotCtgLoneWolfAmbush(), "Detpack deployed, transitioning to ambush" ); } - CNavArea *const ghostArea = TheNavMesh->GetNearestNavArea( NEORules()->GetGhostPos() ); - CNavArea *const myArea = me->GetLastKnownArea(); + const CNavArea *ghostArea = TheNavMesh->GetNearestNavArea( NEORules()->GetGhostPos() ); + const CNavArea *myArea = me->GetLastKnownArea(); if ( ghostArea && myArea && ghostArea->IsPotentiallyVisible( myArea ) ) { if ( pDetpackWeapon && !pDetpackWeapon->m_bThisDetpackHasBeenThrown && NEORules()->m_pGhost ) @@ -92,7 +92,7 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::ConsiderGhostInterception( CNEOBot * return SuspendFor( new CNEOBotAttack, "Attacking the ghost carrier!" ); } - Vector vecInterceptGoal = NEORules()->GetGhostPos(); + const Vector& vecInterceptGoal = NEORules()->GetGhostPos(); if ( vecInterceptGoal != CNEO_Player::VECTOR_INVALID_WAYPOINT ) { if ( !m_repathTimer.HasStarted() || m_repathTimer.IsElapsed() ) @@ -128,7 +128,7 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::ConsiderGhostVisualCheck( CNEOBot *m } // Move to ghost's location to gain visual contact - Vector vecAcquireGoal = NEORules()->GetGhostPos(); + const Vector& vecAcquireGoal = NEORules()->GetGhostPos(); if ( vecAcquireGoal != CNEO_Player::VECTOR_INVALID_WAYPOINT ) { if ( !m_repathTimer.HasStarted() || m_repathTimer.IsElapsed() ) @@ -170,16 +170,18 @@ EventDesiredResult< CNEOBot > CNEOBotCtgLoneWolf::OnStuck( CNEOBot *me ) //--------------------------------------------------------------------------------------------- -Vector CNEOBotCtgLoneWolf::GetNearestEnemyCapPoint( CNEOBot *me ) +Vector CNEOBotCtgLoneWolf::GetNearestEnemyCapPoint( CNEOBot *me ) const { if ( !me ) + { return CNEO_Player::VECTOR_INVALID_WAYPOINT; + } const int iEnemyTeam = NEORules()->GetOpposingTeam( me->GetTeamNumber() ); if ( NEORules()->m_pGhostCaps.Count() > 0 ) { - Vector bestPos = CNEO_Player::VECTOR_INVALID_WAYPOINT; + const Vector* pBestPos = nullptr; float flNearestSq = FLT_MAX; for ( int i = 0; i < NEORules()->m_pGhostCaps.Count(); ++i ) { @@ -196,11 +198,11 @@ Vector CNEOBotCtgLoneWolf::GetNearestEnemyCapPoint( CNEOBot *me ) if ( distSq < flNearestSq ) { flNearestSq = distSq; - bestPos = pCapPoint->GetAbsOrigin(); + pBestPos = &pCapPoint->GetAbsOrigin(); + } } } - } - return bestPos; + return pBestPos ? *pBestPos : CNEO_Player::VECTOR_INVALID_WAYPOINT; } return CNEO_Player::VECTOR_INVALID_WAYPOINT; diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h index e6629e7977..a38f6e3371 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.h @@ -21,7 +21,7 @@ class CNEOBotCtgLoneWolf : public Action< CNEOBot > virtual ActionResult< CNEOBot > ConsiderGhostInterception( CNEOBot *me, const CBaseCombatCharacter *pGhostOwner = nullptr ); virtual ActionResult< CNEOBot > ConsiderGhostVisualCheck( CNEOBot *me ); - Vector GetNearestEnemyCapPoint( CNEOBot *me ); + Vector GetNearestEnemyCapPoint( CNEOBot *me ) const; CountdownTimer m_repathTimer; PathFollower m_path; diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp index d206f68c7e..2264071ea7 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.cpp @@ -254,18 +254,7 @@ bool CNEOBotCtgLoneWolfAmbush::Is1v1( CNEOBot *me ) // I entered this function because my teammates are dead if ( !m_1v1Timer.HasStarted() || m_1v1Timer.IsElapsed() ) { - int iAliveEnemyCount = 0; - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CNEO_Player *pPlayer = ToNEOPlayer( UTIL_PlayerByIndex( i ) ); - if ( pPlayer && pPlayer->IsAlive() && !me->InSameTeam( pPlayer ) ) - { - if ( ++iAliveEnemyCount > 1 ) - { - break; - } - } - } + int iAliveEnemyCount = g_Teams[GetEnemyTeam(me->GetTeamNumber())]->GetAliveMembers(); m_bIs1v1 = ( iAliveEnemyCount == 1 ); m_1v1Timer.Start( 2.0f ); } diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h index 5394f2c82a..cb19d2e339 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_ambush.h @@ -3,7 +3,6 @@ #include "bot/neo_bot.h" #include "bot/behavior/neo_bot_ctg_lone_wolf.h" -class CWeaponDetpack; //-------------------------------------------------------------------------------------------------------- class CNEOBotCtgLoneWolfAmbush : public CNEOBotCtgLoneWolf { diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp index 07b9092c38..b5f40b25e9 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.cpp @@ -158,27 +158,7 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolfSeek::Update( CNEOBot *me, float inter } } - Vector vecSoundPos = me->GetAudibleEnemySoundPos(); - if ( vecSoundPos != CNEO_Player::VECTOR_INVALID_WAYPOINT ) - { - // Don't veer path for sound if waypoint is not that far off - if ( m_vecSearchWaypoint.DistToSqr( vecSoundPos ) > Square( 200.0f ) ) - { - m_vecSearchWaypoint = vecSoundPos; - m_path.Invalidate(); - m_repathTimer.Invalidate(); // path to sound next tick - - CNavArea *soundArea = TheNavMesh->GetNearestNavArea( vecSoundPos ); - if ( soundArea ) - { - m_iExplorationTargetId = (int)soundArea->GetID(); - // Mark sound area as not explored - m_exploredAreaIds.Remove( m_iExplorationTargetId ); - } - } - } - - const Vector currentGhostPos = NEORules()->GetGhostPos(); + const Vector& currentGhostPos = NEORules()->GetGhostPos(); if ( !m_pCachedGhostArea || currentGhostPos.DistToSqr( m_vecLastGhostPos ) > Square( 64.0f ) ) { CNavArea *pLastGhostArea = m_pCachedGhostArea; @@ -237,7 +217,7 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolfSeek::Update( CNEOBot *me, float inter if ( search.m_candidateAreas.IsEmpty() ) { // Track already explored areas around the ghost - auto searchFromVisible = [&]( CNavArea *visibleArea ) -> bool + auto searchFromVisible = [&search]( CNavArea *visibleArea ) -> bool { if ( search.m_candidateAreas.Count() >= CSearchForUnexplored::CANDIDATE_LIMIT || search.m_iAreaCount >= search.m_iAreaLimit ) { diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h index afe7683131..f03fa44910 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf_seek.h @@ -5,7 +5,6 @@ #include "utlmap.h" #include -class CWeaponDetpack; class CNEOIgnoredWeaponsCache; //-------------------------------------------------------------------------------------------------------- diff --git a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp index cded9861b5..f7556f5d96 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp @@ -188,14 +188,6 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::MonitorArmedDetpack( CNEOBot *me } } - if ( !bShouldDetonate ) - { - if ( me->GetAudibleEnemySoundPos( vecDetpackPos, flMaxRadiusSq ) != CNEO_Player::VECTOR_INVALID_WAYPOINT ) - { - bShouldDetonate = true; - } - } - if ( bShouldDetonate ) { return SuspendFor( new CNEOBotDetpackTrigger(), "Triggering detpack!" ); diff --git a/src/game/server/neo/bot/neo_bot.cpp b/src/game/server/neo/bot/neo_bot.cpp index dbd883e128..1931b34153 100644 --- a/src/game/server/neo/bot/neo_bot.cpp +++ b/src/game/server/neo/bot/neo_bot.cpp @@ -21,8 +21,6 @@ #include "neo_weapon_loadout.h" #include "behavior/neo_bot_behavior.h" #include "neo_crosshair.h" -#include "recipientfilter.h" -#include "soundent.h" ConVar neo_bot_notice_gunfire_range("neo_bot_notice_gunfire_range", "3000", FCVAR_GAMEDLL); ConVar neo_bot_notice_quiet_gunfire_range("neo_bot_notice_quiet_gunfire_range", "500", FCVAR_GAMEDLL); @@ -2941,80 +2939,5 @@ QueryResultType CNEOBotBehavior::ShouldAim(const CNEOBot *me, const bool bWepHas return result; } -//--------------------------------------------------------------------------------------------- -Vector CNEOBot::GetAudibleEnemySoundPos(const Vector& vecReferencePos, float flMaxRangeSq) const -{ - CSound *pSound = nullptr; - for ( int iSound = CSoundEnt::ActiveList(); iSound != SOUNDLIST_EMPTY; iSound = pSound->NextSound() ) - { - pSound = CSoundEnt::SoundPointerForIndex( iSound ); - if ( !pSound ) - { - break; - } - - // If a reference position and range are provided, check distance first - if ( vecReferencePos != CNEO_Player::VECTOR_INVALID_WAYPOINT && flMaxRangeSq > 0.0f ) - { - if ( pSound->GetSoundOrigin().DistToSqr( vecReferencePos ) > flMaxRangeSq ) - { - continue; - } - } - - if ( ( pSound->SoundType() & ( SOUND_COMBAT | SOUND_PLAYER ) ) == 0 ) - { - continue; - } - - CBaseEntity *pOwner = pSound->m_hOwner.Get(); - - // Ignore non-player sounds and sounds I was responsible for - if ( !pOwner || !pOwner->IsPlayer() || pOwner == GetEntity() ) - { - continue; - } - - // Only care about sounds from the enemy - if ( InSameTeam( pOwner ) ) - { - continue; - } - - // Check if I can hear the sound - bool bCanHearEnemy = false; - CPASFilter soundFilter( pSound->GetSoundOrigin() ); - for ( int i = 0; i < soundFilter.GetRecipientCount(); ++i ) - { - if ( soundFilter.GetRecipientIndex( i ) == entindex() ) - { - bCanHearEnemy = true; - break; - } - } - - if ( !bCanHearEnemy ) - { - // Check if I can hear the shooter - CPASFilter shooterFilter( pOwner->GetAbsOrigin() ); - for ( int i = 0; i < shooterFilter.GetRecipientCount(); ++i ) - { - if ( shooterFilter.GetRecipientIndex( i ) == entindex() ) - { - bCanHearEnemy = true; - break; - } - } - } - - if ( bCanHearEnemy ) - { - return pSound->GetSoundOrigin(); - } - } - - return CNEO_Player::VECTOR_INVALID_WAYPOINT; -} - diff --git a/src/game/server/neo/bot/neo_bot.h b/src/game/server/neo/bot/neo_bot.h index b88feac775..eef55c4c83 100644 --- a/src/game/server/neo/bot/neo_bot.h +++ b/src/game/server/neo/bot/neo_bot.h @@ -172,7 +172,6 @@ class CNEOBot : public NextBotPlayer< CNEO_Player >, public CGameEventListener bool IsQuietWeapon(CNEOBaseCombatWeapon* weapon) const; // return true if given weapon doesn't make much sound when used (ie: spy knife, etc) bool IsEnvironmentNoisy(void) const; // return true if there are/have been loud noises (ie: non-quiet weapons) nearby very recently - Vector GetAudibleEnemySoundPos(const Vector& vecReferencePos = CNEO_Player::VECTOR_INVALID_WAYPOINT, float flMaxRangeSq = -1.0f) const; bool IsEnemy(const CBaseEntity* them) const OVERRIDE; diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index d957849f94..268ac07c44 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -1293,9 +1293,6 @@ void CNEO_Player::PlayCloakSound(bool removeLocalPlayer) // effect lasts 0.5 seconds, but allow 200-300ms leeway with GetFogObscuredRatio cache window m_botThermOpticCamoDisruptedTimer.Start(0.2f); } - - // For bots to notice cloak sound - CSoundEnt::InsertSound(SOUND_COMBAT, GetAbsOrigin(), 600, 0.2, this); } void CNEO_Player::SetCloakState(bool state) @@ -2952,8 +2949,6 @@ void CNEO_Player::PickupObject( CBaseEntity *pObject, void CNEO_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) { - // For bots to hear footsteps - CSoundEnt::InsertSound(SOUND_PLAYER, GetAbsOrigin(), 150, 0.1, this); BaseClass::PlayStepSound(vecOrigin, psurface, fvol, force); }