From 685893e2881e2c78e4cf28f8852ed75ed1609748 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Mon, 2 Mar 2026 00:48:06 -0700 Subject: [PATCH 1/3] Refactor ReloadIfLowClip --- .../neo/bot/behavior/neo_bot_jgr_capture.cpp | 1 + .../bot/behavior/neo_bot_retreat_to_cover.cpp | 6 +- .../bot/behavior/neo_bot_tactical_monitor.cpp | 6 ++ src/game/server/neo/bot/neo_bot.cpp | 67 +++++++++++++------ 4 files changed, 60 insertions(+), 20 deletions(-) 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 a582fc298d..b9847b82ab 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 @@ -35,6 +35,7 @@ ActionResult CNEOBotJgrCapture::OnStart( CNEOBot *me, Action * // Ignore enemies while capturing juggernaut me->StopLookingAroundForEnemies(); + me->ReloadIfLowClip(); // might as well as we're preoccupied return Continue(); } diff --git a/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp b/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp index ae1ea157db..cf95cb193a 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp @@ -204,7 +204,11 @@ ActionResult< CNEOBot > CNEOBotRetreatToCover::Update( CNEOBot *me, float interv { const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat( true ); - if ( threat && threat->GetEntity() && threat->GetEntity()->IsPlayer() ) + if (!threat) + { + me->ReloadIfLowClip(); + } + else if ( threat->GetEntity() && threat->GetEntity()->IsPlayer() ) { CNEO_Player *pThreatPlayer = ToNEOPlayer( threat->GetEntity() ); if ( pThreatPlayer && pThreatPlayer->IsCarryingGhost() ) 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 7f9d1a5fa1..a1229e7f1d 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 @@ -435,6 +435,12 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter AvoidBumpingFriends( me ); } + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( !threat ) + { + me->ReloadIfLowClip(); + } + me->UpdateDelayedThreatNotices(); return Continue(); diff --git a/src/game/server/neo/bot/neo_bot.cpp b/src/game/server/neo/bot/neo_bot.cpp index 58714c8820..f75a7bb62e 100644 --- a/src/game/server/neo/bot/neo_bot.cpp +++ b/src/game/server/neo/bot/neo_bot.cpp @@ -1600,37 +1600,66 @@ void CNEOBot::EquipBestWeaponForThreat(const CKnownEntity* threat, const bool bN void CNEOBot::ReloadIfLowClip(void) { CNEOBaseCombatWeapon* myWeapon = static_cast(GetActiveWeapon()); - if (myWeapon && myWeapon->GetPrimaryAmmoCount() > 0) + + if (!myWeapon) + { + return; + } + + if (myWeapon->GetPrimaryAmmoCount() <= 0) + { + return; + } + + if (myWeapon->Clip1() >= myWeapon->GetMaxClip1()) + { + return; + } + + if ((myWeapon->GetNeoWepBits() & NEO_WEP_BALC)) + { + return; + } + else if ((myWeapon->GetNeoWepBits() & NEO_WEP_SMAC)) + { + return; + } + else if ((myWeapon->GetNeoWepBits() & NEO_WEP_SUPA7)) { - bool shouldReload = false; - // SUPA7 reload doesn't discard ammo - if ((myWeapon->GetNeoWepBits() & NEO_WEP_SUPA7) && (myWeapon->Clip1() < myWeapon->GetMaxClip1())) + // Consider loading slug + if ( (myWeapon->m_iSecondaryAmmoCount > 0) && (myWeapon->Clip1() == myWeapon->GetMaxClip1() - 1)) { - shouldReload = true; + ReleaseFireButton(); + PressAltFireButton(); + return; // attempt to load slug } - else + // SUPA7 reload doesn't discard ammo, continue + } + else if (myWeapon->Clip1() > 0) + { + auto* pPlayer = ToNEOPlayer(this); + if ( pPlayer->GetTimeSinceWeaponFired() < 3.0f) { - int maxClip = myWeapon->GetMaxClip1(); - bool isBarrage = IsBarrageAndReloadWeapon(myWeapon); + return; // still in the middle of a fight + } - int baseThreshold = isBarrage ? (maxClip / 3) : (maxClip / 2); + int maxClip = myWeapon->GetMaxClip1(); + bool isBarrage = IsBarrageAndReloadWeapon(myWeapon); - float aggressionFactor = 1.0f - HealthFraction(); + int baseThreshold = isBarrage ? (maxClip / 3) : (maxClip / 2); - float dynamicThreshold = baseThreshold + aggressionFactor * (maxClip - baseThreshold); + float aggressionFactor = 1.0f - HealthFraction(); - if (myWeapon->Clip1() < static_cast(dynamicThreshold)) - { - shouldReload = true; - } - } + float dynamicThreshold = baseThreshold + aggressionFactor * (maxClip - baseThreshold); - if (shouldReload) + if (myWeapon->Clip1() > static_cast(dynamicThreshold)) { - ReleaseFireButton(); - PressReloadButton(); + return; // reloads drop ammo, still have enough in clip } } + + ReleaseFireButton(); + PressReloadButton(); } From acc03d7df820db8f2e1111c5339bb6bffb66034c Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Mon, 2 Mar 2026 22:32:42 -0700 Subject: [PATCH 2/3] Consolidate reload logic into ReloadIfLowClip --- .../server/neo/bot/behavior/neo_bot_attack.cpp | 3 +-- .../neo/bot/behavior/neo_bot_behavior.cpp | 9 +++------ .../neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp | 9 +++------ src/game/server/neo/bot/neo_bot.cpp | 17 ++++++++++++----- src/game/server/neo/bot/neo_bot.h | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/game/server/neo/bot/behavior/neo_bot_attack.cpp b/src/game/server/neo/bot/behavior/neo_bot_attack.cpp index 7a1f4005a3..b9a3f463e0 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_attack.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_attack.cpp @@ -71,8 +71,7 @@ ActionResult< CNEOBot > CNEOBotAttack::Update( CNEOBot *me, float interval ) // SUPA7 reload can be interrupted so proactively reload if (myWeapon && (myWeapon->GetNeoWepBits() & NEO_WEP_SUPA7) && (myWeapon->Clip1() < myWeapon->GetMaxClip1())) { - me->ReleaseFireButton(); - me->PressReloadButton(); + me->ReloadIfLowClip(true); } if ( threat->IsVisibleRecently() ) 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 244ac6835f..3a9ea7e19a 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_behavior.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_behavior.cpp @@ -622,10 +622,9 @@ void CNEOBotMainAction::FireWeaponAtEnemy( CNEOBot *me ) { if ( myWeapon->Clip1() <= 0 ) { - me->ReleaseFireButton(); me->EnableCloak(3.0f); m_isWaitingForFullReload = true; - me->PressReloadButton(); + me->ReloadIfLowClip(true); } if ( m_isWaitingForFullReload ) @@ -670,8 +669,7 @@ void CNEOBotMainAction::FireWeaponAtEnemy( CNEOBot *me ) if (myWeapon->Clip1() <= 0) { - me->ReleaseFireButton(); - me->PressReloadButton(); + me->ReloadIfLowClip(true); m_isWaitingForFullReload = true; } @@ -849,8 +847,7 @@ void CNEOBotMainAction::FireWeaponAtEnemy( CNEOBot *me ) } else { - me->ReleaseFireButton(); - me->PressReloadButton(); + me->ReloadIfLowClip(true); m_isWaitingForFullReload = true; } return; 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 c561f50326..cca00ee16b 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,13 +47,10 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::Update( CNEOBot *me, float interval const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat( true ); CBaseCombatWeapon *pWeapon = me->GetActiveWeapon(); - if ( !threat && pWeapon && pWeapon->UsesClipsForAmmo1() ) + if ( !threat && pWeapon ) { - if ( pWeapon->Clip1() < pWeapon->GetMaxClip1() && me->GetAmmoCount( pWeapon->GetPrimaryAmmoType() ) > 0 ) - { - // Aggressively reload due to lack of backup - me->PressReloadButton(); - } + // Aggressively reload due to lack of backup + me->ReloadIfLowClip(true); // force reload true } // We dropped the ghost to hunt a threat. diff --git a/src/game/server/neo/bot/neo_bot.cpp b/src/game/server/neo/bot/neo_bot.cpp index f75a7bb62e..6ed90e8776 100644 --- a/src/game/server/neo/bot/neo_bot.cpp +++ b/src/game/server/neo/bot/neo_bot.cpp @@ -1597,7 +1597,7 @@ void CNEOBot::EquipBestWeaponForThreat(const CKnownEntity* threat, const bool bN //----------------------------------------------------------------------------------------------------- // Reload the active weapon if it makes sense for the situation -void CNEOBot::ReloadIfLowClip(void) +void CNEOBot::ReloadIfLowClip(bool bForceReload) { CNEOBaseCombatWeapon* myWeapon = static_cast(GetActiveWeapon()); @@ -1616,15 +1616,22 @@ void CNEOBot::ReloadIfLowClip(void) return; } - if ((myWeapon->GetNeoWepBits() & NEO_WEP_BALC)) + if (!(myWeapon->GetNeoWepBits() & NEO_WEP_FIREARM)) { return; } - else if ((myWeapon->GetNeoWepBits() & NEO_WEP_SMAC)) + + if (myWeapon->GetNeoWepBits() & NEO_WEP_BALC) + { + return; + } + + if (myWeapon->GetNeoWepBits() & NEO_WEP_SMAC) { return; } - else if ((myWeapon->GetNeoWepBits() & NEO_WEP_SUPA7)) + + if (myWeapon->GetNeoWepBits() & NEO_WEP_SUPA7) { // Consider loading slug if ( (myWeapon->m_iSecondaryAmmoCount > 0) && (myWeapon->Clip1() == myWeapon->GetMaxClip1() - 1)) @@ -1652,7 +1659,7 @@ void CNEOBot::ReloadIfLowClip(void) float dynamicThreshold = baseThreshold + aggressionFactor * (maxClip - baseThreshold); - if (myWeapon->Clip1() > static_cast(dynamicThreshold)) + if (!bForceReload && myWeapon->Clip1() > static_cast(dynamicThreshold)) { return; // reloads drop ammo, still have enough in clip } diff --git a/src/game/server/neo/bot/neo_bot.h b/src/game/server/neo/bot/neo_bot.h index 190ecdcb9c..01f83b0e31 100644 --- a/src/game/server/neo/bot/neo_bot.h +++ b/src/game/server/neo/bot/neo_bot.h @@ -156,7 +156,7 @@ class CNEOBot : public NextBotPlayer< CNEO_Player >, public CGameEventListener bool EquipRequiredWeapon(void); // if we're required to equip a specific weapon, do it. void EquipBestWeaponForThreat(const CKnownEntity* threat, const bool bNotPrimary = false); // equip the best weapon we have to attack the given threat - void ReloadIfLowClip(void); + void ReloadIfLowClip(bool bForceReload = false); void PushRequiredWeapon(CNEOBaseCombatWeapon* weapon); // force us to equip and use this weapon until popped off the required stack void PopRequiredWeapon(void); // pop top required weapon off of stack and discard From 08222649c487f3aba5f7a645a153329acbb7c37b Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Mon, 2 Mar 2026 22:50:47 -0700 Subject: [PATCH 3/3] Check if firearm in IsLineOfFirePenetrationClear --- src/game/server/neo/bot/neo_bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/server/neo/bot/neo_bot.cpp b/src/game/server/neo/bot/neo_bot.cpp index 6ed90e8776..f3f336e57a 100644 --- a/src/game/server/neo/bot/neo_bot.cpp +++ b/src/game/server/neo/bot/neo_bot.cpp @@ -1817,7 +1817,7 @@ bool CNEOBot::IsLineOfFirePenetrationClear(const trace_t &tr, const Vector &from // Only bother with fire penetration in short distance auto *neoWeapon = static_cast(GetActiveWeapon()); - if (!neoWeapon) + if (!neoWeapon || !(neoWeapon->GetNeoWepBits() & NEO_WEP_FIREARM)) { return false; }