From 0bbb7ab0d1963d95af46257e6ffe0853d98eefc2 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Sun, 27 Sep 2026 19:30:00 -0600 Subject: [PATCH 1/6] Bots avoid neo_npc_targetsystem via nav PVS --- .../neo_bot_retreat_from_hazard_area.cpp | 8 ++ .../neo/bot/neo_bot_path_reservation.cpp | 45 +++++++++ .../server/neo/bot/neo_bot_path_reservation.h | 3 + src/game/server/neo/neo_npc_targetsystem.cpp | 93 +++++++++++++++++++ src/game/server/neo/neo_npc_targetsystem.h | 6 ++ 5 files changed, 155 insertions(+) diff --git a/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp b/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp index 99057cbbfa..ee049264cd 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp @@ -135,6 +135,14 @@ ActionResult CNEOBotRetreatFromHazardArea::Update(CNEOBot *me, float in CNEOBotPathCompute(me, m_path, m_safeArea->GetCenter(), RETREAT_ROUTE); } + // Possibly don't want to cloak with grenade/smoke hazards yet + // because in such situations either the threat noticed you already + // or because they didn't notice you yet, you shouldn't announce your position + if (CNEOBotPathReservations()->IsAreaNpcTurretHazard(myArea->GetID(), me)) + { + me->EnableCloak(3.0f); + } + m_path.Update(me); return Continue(); } diff --git a/src/game/server/neo/bot/neo_bot_path_reservation.cpp b/src/game/server/neo/bot/neo_bot_path_reservation.cpp index 77b9173413..423ca6282e 100644 --- a/src/game/server/neo/bot/neo_bot_path_reservation.cpp +++ b/src/game/server/neo/bot/neo_bot_path_reservation.cpp @@ -340,6 +340,7 @@ void CNEOBotPathReservationSystem::AddDeadlyHazard(int navAreaID, float expireTi blank.hazardExpireTime = expireTime; blank.smokeExpireTime = 0.0f; blank.smokePropagateTime = 0.0f; + blank.npcTurretExpireTime = 0.0f; index = m_HazardAreas[teamID].Insert(navAreaID, blank); } else @@ -414,6 +415,7 @@ void CNEOBotPathReservationSystem::AddSmokeHazard(int navAreaID, float expireTim blank.hazardExpireTime = 0.0f; blank.smokeExpireTime = expireTime; blank.smokePropagateTime = 0.0f; + blank.npcTurretExpireTime = 0.0f; index = m_HazardAreas[teamID].Insert(navAreaID, blank); } else @@ -444,6 +446,49 @@ void CNEOBotPathReservationSystem::AddSmokeHazard(int navAreaID, float expireTim } } +//------------------------------------------------------------------------------------------------- +// Area tagged by neo_npc_targetsystem turret, whose motion vision +// cannot see slow cloaked players, so bots escaping it should cloak +void CNEOBotPathReservationSystem::AddNpcTurretHazard(int navAreaID, float expireTime, int teamID, bool propagatePVS) +{ + AddDeadlyHazard(navAreaID, expireTime, teamID, propagatePVS); + + if ( (teamID < 0) || (teamID >= TEAM__TOTAL) ) + { + return; + } + + int index = m_HazardAreas[teamID].Find(navAreaID); + if ( m_HazardAreas[teamID].IsValidIndex(index) ) + { + // Inform the bots in the specific target area to turn on cloak + m_HazardAreas[teamID][index].npcTurretExpireTime = expireTime; + } +} + +//------------------------------------------------------------------------------------------------- +bool CNEOBotPathReservationSystem::IsAreaNpcTurretHazard(int navAreaID, const CNEOBot *me) const +{ + if ( !neo_bot_path_reservation_avoid_penalty_enable.GetBool() || !me ) + { + return false; + } + + int teamID = me->GetTeamNumber(); + if ( (teamID < 0) || (teamID >= TEAM__TOTAL) ) + { + return false; + } + + int index = m_HazardAreas[teamID].Find(navAreaID); + if ( !m_HazardAreas[teamID].IsValidIndex(index) ) + { + return false; + } + + return m_HazardAreas[teamID][index].npcTurretExpireTime > gpGlobals->curtime; +} + //------------------------------------------------------------------------------------------------- float CNEOBotPathReservationSystem::GetAreaHazardousTime(int navAreaID, const CNEOBot *me) const { diff --git a/src/game/server/neo/bot/neo_bot_path_reservation.h b/src/game/server/neo/bot/neo_bot_path_reservation.h index b6a411e606..d0ae6539fe 100644 --- a/src/game/server/neo/bot/neo_bot_path_reservation.h +++ b/src/game/server/neo/bot/neo_bot_path_reservation.h @@ -40,6 +40,7 @@ struct HazardInfo float smokeExpireTime; // when the smoke hazard risk expires float hazardExpireTime; // when a general deadly hazard risk expires float smokePropagateTime; // expiry this area has already propagated to its visible set + float npcTurretExpireTime; // when a neo_npc_targetsystem hazard expires }; struct BotReservedAreas_t @@ -89,6 +90,8 @@ class CNEOBotPathReservationSystem void AddDeadlyHazard(int navAreaID, float expireTime, int teamID, bool propagatePVS = false); void AddFragHazard(int navAreaID, float expireTime, int teamID); void AddSmokeHazard(int navAreaID, float expireTime, int teamID, bool propagatePVS = true); + void AddNpcTurretHazard(int navAreaID, float expireTime, int teamID, bool propagatePVS = true); + bool IsAreaNpcTurretHazard(int navAreaID, const CNEOBot *me) const; float GetAreaHazardousTime(int navAreaID, const CNEOBot *me) const; bool IsAreaHazardous(int navAreaID, const CNEOBot *me) const; bool IsAreaDeadlyHazard(int navAreaID, const CNEOBot *me) const; diff --git a/src/game/server/neo/neo_npc_targetsystem.cpp b/src/game/server/neo/neo_npc_targetsystem.cpp index 0923545928..81fc301471 100644 --- a/src/game/server/neo/neo_npc_targetsystem.cpp +++ b/src/game/server/neo/neo_npc_targetsystem.cpp @@ -1,11 +1,19 @@ #include "neo_npc_targetsystem.h" #include "neo_player.h" #include "ammodef.h" +#include "nav_mesh.h" +#include "NextBotManager.h" +#include "bot/neo_bot_path_reservation.h" #include "tier0/memdbgon.h" #define CLOAKED_VELOCITY_THRESHOLD 32400 // 180 horizontal velocity. Slightly under sprint/wigglerun speed +// Bot hazard timing +static constexpr float BOT_HAZARD_REACTION_TIME = 0.25f; +static constexpr float BOT_HAZARD_INTERVAL = BOT_HAZARD_REACTION_TIME; +static constexpr float BOT_HAZARD_DURATION = 2.0f; + LINK_ENTITY_TO_CLASS(neo_npc_targetsystem, CNEO_NPCTargetSystem); BEGIN_DATADESC(CNEO_NPCTargetSystem) @@ -185,6 +193,7 @@ void CNEO_NPCTargetSystem::Think() { m_OnSpotted.FireOutput(pBestTarget, this); m_bTargetAcquired = true; + m_flNextHazardTime = gpGlobals->curtime + BOT_HAZARD_REACTION_TIME; } if (bMiddleIgnore && !m_bMiddleIgnoreActive) @@ -234,6 +243,8 @@ void CNEO_NPCTargetSystem::Think() m_bMiddleIgnoreActive = false; } + PublishBotHazards(pBestTarget); + // Optional - damage the target if (m_hDamageSource && pBestTarget && (iNewZone == ZONE_MIDDLE)) { @@ -260,6 +271,88 @@ void CNEO_NPCTargetSystem::Think() SetNextThink(gpGlobals->curtime + 0.05f); } +//----------------------------------------------------------------------------- +// Bot hazard publishing - Bots cannot fight this entity. +// Hazards: its own area and all adjacent areas for every team (run-over), +// and its targeted player's area once acquired. +//----------------------------------------------------------------------------- + +void CNEO_NPCTargetSystem::PublishBotHazards(CBasePlayer *pTarget) +{ + if (!TheNavMesh->IsLoaded() || TheNextBots().GetNextBotCount() == 0) + { + return; + } + + if (gpGlobals->curtime < m_flNextHazardTime) + { + return; + } + + m_flNextHazardTime = gpGlobals->curtime + BOT_HAZARD_INTERVAL; + const float flExpireTime = gpGlobals->curtime + BOT_HAZARD_DURATION; + + // A team is targeted if a living member passes the filter; on ntre_rogue_ctg + // the filter swaps on ghost pickup so the carrier's team is left out + bool bTeamTargeted[TEAM__TOTAL] = {}; + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer || !pPlayer->IsAlive() || pPlayer->GetTeamNumber() < FIRST_GAME_TEAM) + { + continue; + } + + if (!m_pFilter || m_pFilter->PassesFilter(this, pPlayer)) + { + bTeamTargeted[pPlayer->GetTeamNumber()] = true; + } + } + + // Mark areas PVS to the tank as do not hang out in my view + CNavArea *pOwnArea = TheNavMesh->GetNearestNavArea(GetAbsOrigin()); + if (pOwnArea) + { + for (int iTeam = FIRST_GAME_TEAM; iTeam < TEAM__TOTAL; ++iTeam) + { + CNEOBotPathReservations()->AddDeadlyHazard(pOwnArea->GetID(), flExpireTime, iTeam); + + if (bTeamTargeted[iTeam]) + { + AddVisibleHazard(pOwnArea, iTeam, flExpireTime); + } + } + } + + if (pTarget) + { + CNavArea *pTargetArea = TheNavMesh->GetNearestNavArea(pTarget->GetAbsOrigin()); + if (pTargetArea) + { + // Don't propagate the PVS for the targeted bot location to avoid marking the areas around the corner + AddVisibleHazard(pTargetArea, pTarget->GetTeamNumber(), flExpireTime, false); + } + } +} + +void CNEO_NPCTargetSystem::AddVisibleHazard(CNavArea *pArea, int iTeam, float flExpireTime, bool bPropagatePVS) +{ + const bool bMotionVision = m_bMotionVision; + auto addHazard = [flExpireTime, iTeam, bMotionVision, bPropagatePVS](CNavArea *pVisible) + { + if (bMotionVision) + { + CNEOBotPathReservations()->AddNpcTurretHazard(pVisible->GetID(), flExpireTime, iTeam, bPropagatePVS); + } + else + { + CNEOBotPathReservations()->AddDeadlyHazard(pVisible->GetID(), flExpireTime, iTeam, bPropagatePVS); + } + return true; + }; + addHazard(pArea); +} + bool CNEO_NPCTargetSystem::CanSee(CBaseEntity *pEntity) { if (m_pFilter && m_pFilter->PassesFilter(this, pEntity)) diff --git a/src/game/server/neo/neo_npc_targetsystem.h b/src/game/server/neo/neo_npc_targetsystem.h index 620f6ad597..9b3085c65c 100644 --- a/src/game/server/neo/neo_npc_targetsystem.h +++ b/src/game/server/neo/neo_npc_targetsystem.h @@ -2,6 +2,8 @@ #include "cbase.h" #include "filters.h" +class CNavArea; + class CNEO_NPCTargetSystem : public CBaseEntity { public: @@ -45,6 +47,9 @@ class CNEO_NPCTargetSystem : public CBaseEntity bool CanSee(CBaseEntity *pEntity); private: + void PublishBotHazards(CBasePlayer *pTarget); + void AddVisibleHazard(CNavArea *pArea, int iTeam, float flExpireTime, bool bPropagatePVS = true); + enum TargetZone_e { ZONE_NONE = 0, @@ -58,4 +63,5 @@ class CNEO_NPCTargetSystem : public CBaseEntity bool m_bMiddleIgnoreActive = false; float m_flNextFireTime = 0; CBasePlayer *m_pLastBestTarget = nullptr; + float m_flNextHazardTime = 0; }; \ No newline at end of file From 14cc8a2bf6eb6a39d47da8d053283841aefaca4f Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Fri, 16 Oct 2026 21:05:08 -0600 Subject: [PATCH 2/6] Walk while cloaked when retreating from the tank The tank's motion vision ignores a cloaked player only under 180 u/s. A Recon walks at 200 and a sprinting bot is faster still, so cloak alone spent charge without hiding the bot. Release run and hold sneak once the cloak is on, as CNEOBotCommandFollow does, which keeps every class under the threshold (walk speed 150 or less). --- .../neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp b/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp index ee049264cd..9764b46865 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_retreat_from_hazard_area.cpp @@ -141,6 +141,12 @@ ActionResult CNEOBotRetreatFromHazardArea::Update(CNEOBot *me, float in if (CNEOBotPathReservations()->IsAreaNpcTurretHazard(myArea->GetID(), me)) { me->EnableCloak(3.0f); + if (me->IsCloakEnabled()) + { + // The tank's motion vision only ignores cloaked players under 180 u/s + me->ReleaseRunButton(); + me->PressSneakButton(0.3f); + } } m_path.Update(me); From 951a6afc88d0e11aa948f3e68f5fb5ab3fcf5aeb Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Fri, 16 Oct 2026 21:05:08 -0600 Subject: [PATCH 3/6] Simplify the tank's hazard publishing Mark the tank's own area once per team: a targeted team gets the visible-area hazard, which already includes the deadly marking, and other teams get the deadly marking alone. Replace a one-shot lambda left from an earlier functor with a plain if/else, and fix the header comment, which described adjacent-area marking that was never done. No behavior change. --- src/game/server/neo/neo_npc_targetsystem.cpp | 30 +++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/game/server/neo/neo_npc_targetsystem.cpp b/src/game/server/neo/neo_npc_targetsystem.cpp index 81fc301471..7970197868 100644 --- a/src/game/server/neo/neo_npc_targetsystem.cpp +++ b/src/game/server/neo/neo_npc_targetsystem.cpp @@ -273,8 +273,8 @@ void CNEO_NPCTargetSystem::Think() //----------------------------------------------------------------------------- // Bot hazard publishing - Bots cannot fight this entity. -// Hazards: its own area and all adjacent areas for every team (run-over), -// and its targeted player's area once acquired. +// Hazards: its own area for every team (run-over), the areas it can see +// for targeted teams, and its targeted player's area once acquired. //----------------------------------------------------------------------------- void CNEO_NPCTargetSystem::PublishBotHazards(CBasePlayer *pTarget) @@ -315,12 +315,14 @@ void CNEO_NPCTargetSystem::PublishBotHazards(CBasePlayer *pTarget) { for (int iTeam = FIRST_GAME_TEAM; iTeam < TEAM__TOTAL; ++iTeam) { - CNEOBotPathReservations()->AddDeadlyHazard(pOwnArea->GetID(), flExpireTime, iTeam); - if (bTeamTargeted[iTeam]) { AddVisibleHazard(pOwnArea, iTeam, flExpireTime); } + else + { + CNEOBotPathReservations()->AddDeadlyHazard(pOwnArea->GetID(), flExpireTime, iTeam); + } } } @@ -337,20 +339,14 @@ void CNEO_NPCTargetSystem::PublishBotHazards(CBasePlayer *pTarget) void CNEO_NPCTargetSystem::AddVisibleHazard(CNavArea *pArea, int iTeam, float flExpireTime, bool bPropagatePVS) { - const bool bMotionVision = m_bMotionVision; - auto addHazard = [flExpireTime, iTeam, bMotionVision, bPropagatePVS](CNavArea *pVisible) + if (m_bMotionVision) { - if (bMotionVision) - { - CNEOBotPathReservations()->AddNpcTurretHazard(pVisible->GetID(), flExpireTime, iTeam, bPropagatePVS); - } - else - { - CNEOBotPathReservations()->AddDeadlyHazard(pVisible->GetID(), flExpireTime, iTeam, bPropagatePVS); - } - return true; - }; - addHazard(pArea); + CNEOBotPathReservations()->AddNpcTurretHazard(pArea->GetID(), flExpireTime, iTeam, bPropagatePVS); + } + else + { + CNEOBotPathReservations()->AddDeadlyHazard(pArea->GetID(), flExpireTime, iTeam, bPropagatePVS); + } } bool CNEO_NPCTargetSystem::CanSee(CBaseEntity *pEntity) From 40a8064c2f3b2f059014b82ba3df225424a7d258 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Fri, 16 Oct 2026 22:24:13 -0600 Subject: [PATCH 4/6] Cloak in every area the tank can see The turret mark that tells bots to cloak was set only on the tank's own area and its target's, while the tank's deadly mark spread to every area it can see. A bot elsewhere in its sight retreated without cloaking. The mark now spreads to the same visible areas; teams the tank does not target still get only the run-over mark on its own area, which gives no reason to cloak. --- .../neo/bot/neo_bot_path_reservation.cpp | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/game/server/neo/bot/neo_bot_path_reservation.cpp b/src/game/server/neo/bot/neo_bot_path_reservation.cpp index 423ca6282e..58eb567119 100644 --- a/src/game/server/neo/bot/neo_bot_path_reservation.cpp +++ b/src/game/server/neo/bot/neo_bot_path_reservation.cpp @@ -446,12 +446,31 @@ void CNEOBotPathReservationSystem::AddSmokeHazard(int navAreaID, float expireTim } } +//------------------------------------------------------------------------------------------------- +// Functor to propagate a neo_npc_targetsystem turret hazard to PVS-adjacent areas +struct CNEOFunctorPropagatePVSNpcTurretHazard +{ + CNEOFunctorPropagatePVSNpcTurretHazard(float expireTime, int teamID) + : m_expireTime(expireTime), m_teamID(teamID) + { + } + + bool operator()(CNavArea *area) + { + CNEOBotPathReservations()->AddNpcTurretHazard(area->GetID(), m_expireTime, m_teamID, false); + return true; + } + + float m_expireTime; + int m_teamID; +}; + //------------------------------------------------------------------------------------------------- // Area tagged by neo_npc_targetsystem turret, whose motion vision // cannot see slow cloaked players, so bots escaping it should cloak void CNEOBotPathReservationSystem::AddNpcTurretHazard(int navAreaID, float expireTime, int teamID, bool propagatePVS) { - AddDeadlyHazard(navAreaID, expireTime, teamID, propagatePVS); + AddDeadlyHazard(navAreaID, expireTime, teamID); if ( (teamID < 0) || (teamID >= TEAM__TOTAL) ) { @@ -461,9 +480,20 @@ void CNEOBotPathReservationSystem::AddNpcTurretHazard(int navAreaID, float expir int index = m_HazardAreas[teamID].Find(navAreaID); if ( m_HazardAreas[teamID].IsValidIndex(index) ) { - // Inform the bots in the specific target area to turn on cloak + // Inform the bots in every area the turret can see to turn on cloak m_HazardAreas[teamID][index].npcTurretExpireTime = expireTime; } + + if ( propagatePVS ) + { + CNavArea *area = TheNavMesh->GetNavAreaByID(navAreaID); + if (area) + { + // Same visible set as a propagated deadly hazard + CNEOFunctorPropagatePVSNpcTurretHazard propagate(expireTime, teamID); + area->ForAllCompletelyVisibleAreas(propagate); + } + } } //------------------------------------------------------------------------------------------------- From 5c246295e83ae027c433f003826a240a4b2746b0 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Fri, 16 Oct 2026 22:14:23 -0600 Subject: [PATCH 5/6] Cloak past the tank when in a hurry A bot in a hurry for the objective skips the hazard retreat, so it never reached the retreat's cloak near the tank. In the tank's line of fire it now cloaks and keeps going; a cloaked Assault holds off sprinting to stay under the 180 u/s the tank's motion vision ignores, while a Recon keeps its pace and Support or the carrier cannot cloak. --- .../bot/behavior/neo_bot_tactical_monitor.cpp | 31 +++++++++++++++++++ .../bot/behavior/neo_bot_tactical_monitor.h | 1 + 2 files changed, 32 insertions(+) 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 a75c482872..dd3bd90559 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 @@ -316,6 +316,33 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::WatchForLadders( CNEOBot *me ) } +//----------------------------------------------------------------------------------------- +// A bot in a hurry does not retreat from the tank, so it cloaks in the tank's line of fire and keeps going: +// the tank's motion vision ignores cloaked players under 180 u/s +void CNEOBotTacticalMonitor::CloakPastNpcTurret( CNEOBot *me ) +{ + if ( me->m_hCommandingPlayer.Get() ) + { + return; + } + + CNavArea *myArea = me->GetLastKnownArea(); + if ( !myArea || !CNEOBotPathReservations()->IsAreaNpcTurretHazard( myArea->GetID(), me ) ) + { + return; + } + + me->EnableCloak( 3.0f ); + + // An Assault stays under that speed unless it sprints, while a Recon is over it anyway and keeps its pace + if ( me->IsCloakEnabled() && ( me->GetClass() == NEO_CLASS_ASSAULT ) ) + { + // the child behaviors moved first this update, so this cancels their run press + me->ReleaseRunButton(); + } +} + + //----------------------------------------------------------------------------------------- ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float interval ) { @@ -394,6 +421,10 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter return scavengeResult; } } + else + { + CloakPastNpcTurret( me ); + } #if 0 // NEO TODO (Adam) search for dropped weapons to resupply ammunition bool isAvailable = ( me->GetIntentionInterface()->ShouldHurry( me ) != ANSWER_YES ); 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 017c6a15c3..7a00cad38d 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 @@ -41,4 +41,5 @@ class CNEOBotTacticalMonitor : public Action< CNEOBot > void AvoidBumpingFriends(CNEOBot* me); ActionResult< CNEOBot > WatchForLadders(CNEOBot* me); + void CloakPastNpcTurret(CNEOBot* me); }; From 1ab1e19efb3f592f29b1fa4bfd9ebd244a1009c3 Mon Sep 17 00:00:00 2001 From: Alan Shen Date: Fri, 16 Oct 2026 23:21:13 -0600 Subject: [PATCH 6/6] Let hurrying bots cross the tank's sight at a cost A bot chasing on DEFAULT_ROUTE treated every area the tank can see as impassable, so a hurrying interceptor stalled when no detour existed. The turret mark is no longer also written as a deadly mark, so the hurry penalty for hazards that are not deadly (#2260) now covers it: in a hurry a bot crosses the tank's sight at a large path cost and still detours when a detour exists. Grenades and the tank's own area (run over) stay deadly, and every other hazard check counts the turret mark as before. --- .../neo/bot/neo_bot_path_reservation.cpp | 27 +++++++++++++++---- src/game/server/neo/neo_npc_targetsystem.cpp | 7 +++-- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/game/server/neo/bot/neo_bot_path_reservation.cpp b/src/game/server/neo/bot/neo_bot_path_reservation.cpp index 58eb567119..b68b1bed47 100644 --- a/src/game/server/neo/bot/neo_bot_path_reservation.cpp +++ b/src/game/server/neo/bot/neo_bot_path_reservation.cpp @@ -470,20 +470,31 @@ struct CNEOFunctorPropagatePVSNpcTurretHazard // cannot see slow cloaked players, so bots escaping it should cloak void CNEOBotPathReservationSystem::AddNpcTurretHazard(int navAreaID, float expireTime, int teamID, bool propagatePVS) { - AddDeadlyHazard(navAreaID, expireTime, teamID); + if ( !neo_bot_path_reservation_avoid_penalty_enable.GetBool() ) + { + return; + } if ( (teamID < 0) || (teamID >= TEAM__TOTAL) ) { return; } + // Kept apart from deadly hazards, so a bot in a hurry can accept the turret's risk int index = m_HazardAreas[teamID].Find(navAreaID); - if ( m_HazardAreas[teamID].IsValidIndex(index) ) + if ( !m_HazardAreas[teamID].IsValidIndex(index) ) { - // Inform the bots in every area the turret can see to turn on cloak - m_HazardAreas[teamID][index].npcTurretExpireTime = expireTime; + HazardInfo blank; + blank.hazardExpireTime = 0.0f; + blank.smokeExpireTime = 0.0f; + blank.smokePropagateTime = 0.0f; + blank.npcTurretExpireTime = 0.0f; + index = m_HazardAreas[teamID].Insert(navAreaID, blank); } + // Inform the bots in every area the turret can see to turn on cloak + m_HazardAreas[teamID][index].npcTurretExpireTime = expireTime; + if ( propagatePVS ) { CNavArea *area = TheNavMesh->GetNavAreaByID(navAreaID); @@ -551,6 +562,12 @@ float CNEOBotPathReservationSystem::GetAreaHazardousTime(int navAreaID, const CN return hazardTimeLeft; } + auto npcTurretTimeLeft = h.npcTurretExpireTime - gpGlobals->curtime; + if (npcTurretTimeLeft > 0) + { + return npcTurretTimeLeft; + } + // Support class can see through smoke if (me->GetClass() != NEO_CLASS_SUPPORT) { @@ -572,7 +589,7 @@ bool CNEOBotPathReservationSystem::IsAreaHazardous(int navAreaID, const CNEOBot } //------------------------------------------------------------------------------------------------- -// Unlike IsAreaHazardous, smoke does not count: only marks that can kill, such as a frag grenade's blast area +// Unlike IsAreaHazardous, smoke and the turret's sightline do not count: only marks that can kill, such as a frag grenade's blast area bool CNEOBotPathReservationSystem::IsAreaDeadlyHazard(int navAreaID, const CNEOBot *me) const { if (!neo_bot_path_reservation_avoid_penalty_enable.GetBool()) diff --git a/src/game/server/neo/neo_npc_targetsystem.cpp b/src/game/server/neo/neo_npc_targetsystem.cpp index 7970197868..c49480651d 100644 --- a/src/game/server/neo/neo_npc_targetsystem.cpp +++ b/src/game/server/neo/neo_npc_targetsystem.cpp @@ -315,14 +315,13 @@ void CNEO_NPCTargetSystem::PublishBotHazards(CBasePlayer *pTarget) { for (int iTeam = FIRST_GAME_TEAM; iTeam < TEAM__TOTAL; ++iTeam) { + // Being run over is deadly even for a bot in a hurry, unlike the turret's sightline + CNEOBotPathReservations()->AddDeadlyHazard(pOwnArea->GetID(), flExpireTime, iTeam); + if (bTeamTargeted[iTeam]) { AddVisibleHazard(pOwnArea, iTeam, flExpireTime); } - else - { - CNEOBotPathReservations()->AddDeadlyHazard(pOwnArea->GetID(), flExpireTime, iTeam); - } } }