Skip to content

Commit 64654f2

Browse files
committed
Bots - Defend/cap capture zones, follow ghost player
* Bots will now react to ghost plays, opting to try to either defend the cap zones if enemy is ghoster, or going towards capping cap zone if friendly is ghoster * Also have some logic where if it's nearby a ghoster player, the bot will instead follow them, although prefer to go to cap zones if it's nearer * Bump up vision for bots when there's an enemy ghoster player * fixes #1263
1 parent 006174a commit 64654f2

File tree

4 files changed

+137
-12
lines changed

4 files changed

+137
-12
lines changed

src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "bot/behavior/neo_bot_attack.h"
77
#include "bot/behavior/neo_bot_seek_and_destroy.h"
88
#include "nav_mesh.h"
9+
#include "neo_ghost_cap_point.h"
910

1011
extern ConVar neo_bot_path_lookahead_range;
1112
extern ConVar neo_bot_offense_must_push_time;
@@ -369,21 +370,114 @@ void CNEOBotSeekAndDestroy::RecomputeSeekPath( CNEOBot *me )
369370
#endif
370371

371372
if (NEORules()->GhostExists())
372-
{ // If the ghost exists, go to the ghost
373-
m_vGoalPos = NEORules()->GetGhostPos();
374-
constexpr int DISTANCE_CONSIDERED_ARRIVED_SQUARED = 5000;
375-
if (m_vGoalPos.DistToSqr(me->GetAbsOrigin()) < DISTANCE_CONSIDERED_ARRIVED_SQUARED)
373+
{
374+
const Vector vGhostPos = NEORules()->GetGhostPos();
375+
const int iGhosterPlayer = NEORules()->GetGhosterPlayer();
376+
377+
bool bGoToGoalPos = true;
378+
bool bGetCloserToGhoster = false;
379+
bool bQuickToGoalPos = false;
380+
381+
if (iGhosterPlayer > 0)
376382
{
377-
constexpr float RECHECK_TIME = 30.f;
378-
m_repathTimer.Start(RECHECK_TIME);
379-
m_bGoingToTargetEntity = false;
380-
return;
383+
const int iMyTeam = me->GetTeamNumber();
384+
const int iGhosterTeam = NEORules()->GetGhosterTeam();
385+
386+
// If there's a player playing ghost, turn toward cap zones that's
387+
// closest to the ghoster player
388+
Vector vrTargetCapPos;
389+
int iMinCapGhostLength = INT_MAX;
390+
391+
// Enemy team is carrying the ghost - try to defend the cap zone
392+
// You or friendly team is carrying the ghost - go towards the cap point
393+
const int iTargetCapTeam = (iGhosterTeam == iMyTeam) ? iMyTeam : iGhosterTeam;
394+
395+
for (int i = 0; i < NEORules()->m_pGhostCaps.Count(); i++)
396+
{
397+
auto pGhostCap = dynamic_cast<CNEOGhostCapturePoint *>(
398+
UTIL_EntityByIndex(NEORules()->m_pGhostCaps[i]));
399+
if (!pGhostCap)
400+
{
401+
continue;
402+
}
403+
404+
const Vector vCapPos = pGhostCap->GetAbsOrigin();
405+
const Vector vGhostCapDist = vGhostPos - vCapPos;
406+
const int iGhostCapLength = static_cast<int>(vGhostCapDist.Length());
407+
const int iCapTeam = pGhostCap->owningTeamAlternate();
408+
409+
if (iCapTeam == iTargetCapTeam && iGhostCapLength < iMinCapGhostLength)
410+
{
411+
vrTargetCapPos = vCapPos;
412+
iMinCapGhostLength = iGhostCapLength;
413+
}
414+
}
415+
416+
if (!me->IsCarryingGhost())
417+
{
418+
// If a ghoster player carrying and nearby, get close to them
419+
const float flGhosterMeters = METERS_PER_INCH * me->GetAbsOrigin().DistTo(vGhostPos);
420+
const float flMinCapMeters = METERS_PER_INCH * iMinCapGhostLength;
421+
static const constexpr float FL_NEARBY_FOLLOW_METERS = 26.0f;
422+
static const constexpr float FL_NEARBY_CAPZONE_METERS = 18.0f;
423+
const bool bGhosterNearby = flGhosterMeters < FL_NEARBY_FOLLOW_METERS;
424+
const bool bCapzoneNearby = flMinCapMeters < FL_NEARBY_CAPZONE_METERS;
425+
// But a nearby capzone overrides a nearby ghoster
426+
bGetCloserToGhoster = !bCapzoneNearby && bGhosterNearby && flMinCapMeters > flGhosterMeters;
427+
}
428+
429+
if (bGetCloserToGhoster)
430+
{
431+
m_vGoalPos = vGhostPos;
432+
bQuickToGoalPos = true;
433+
}
434+
else
435+
{
436+
// iMinCapGhostLength == INT_MAX should never happen, just disable going to target
437+
Assert(iMinCapGhostLength < INT_MAX);
438+
bGoToGoalPos = (iMinCapGhostLength < INT_MAX);
439+
440+
m_vGoalPos = vrTargetCapPos;
441+
bQuickToGoalPos = (iGhosterTeam != iMyTeam);
442+
}
381443
}
382-
m_bGoingToTargetEntity = true;
383-
CNEOBotPathCost cost(me, SAFEST_ROUTE);
384-
if (m_path.Compute(me, m_vGoalPos, cost, 0.0f, true, true) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH)
444+
else
385445
{
386-
return;
446+
// If the ghost exists, go to the ghost
447+
m_vGoalPos = vGhostPos;
448+
// NEO TODO (nullsystem): More sophisticated on handling non-ghost playing scenario,
449+
// although it kind of already prefer hunting down players when they're in view, but
450+
// just going towards ghost isn't something that always happens in general.
451+
}
452+
453+
if (bGoToGoalPos)
454+
{
455+
if (bGetCloserToGhoster)
456+
{
457+
constexpr int DISTANCE_CONSIDERED_ASSISTING_SQUARED = 50000;
458+
if (m_vGoalPos.DistToSqr(me->GetAbsOrigin()) < DISTANCE_CONSIDERED_ASSISTING_SQUARED)
459+
{
460+
// Don't stop targeting entity even when near enough
461+
return;
462+
}
463+
}
464+
else
465+
{
466+
constexpr int DISTANCE_CONSIDERED_ARRIVED_SQUARED = 10000;
467+
if (m_vGoalPos.DistToSqr(me->GetAbsOrigin()) < DISTANCE_CONSIDERED_ARRIVED_SQUARED)
468+
{
469+
constexpr float RECHECK_TIME = 30.f;
470+
m_repathTimer.Start(RECHECK_TIME);
471+
m_bGoingToTargetEntity = false;
472+
return;
473+
}
474+
}
475+
m_bGoingToTargetEntity = true;
476+
CNEOBotPathCost cost(me, bQuickToGoalPos ? FASTEST_ROUTE : SAFEST_ROUTE);
477+
if (m_path.Compute(me, m_vGoalPos, cost, 0.0f, true, true) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH)
478+
{
479+
return;
480+
}
387481
}
388482
}
389483

src/game/server/neo/bot/neo_bot_vision.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ bool CNEOBotVision::IsIgnored( CBaseEntity* subject ) const
106106
return false;
107107
}
108108

109+
const int iGhosterPlayer = NEORules()->GetGhosterPlayer();
110+
if (iGhosterPlayer > 0)
111+
{
112+
auto *pNEOPlayer = dynamic_cast<CNEO_Player *>(subject);
113+
if (pNEOPlayer && pNEOPlayer->IsCarryingGhost())
114+
{
115+
// don't ignore ghoster
116+
return false;
117+
}
118+
}
119+
109120
if ( subject->IsEffectActive( EF_NODRAW ) )
110121
{
111122
return true;
@@ -148,3 +159,19 @@ float CNEOBotVision::GetMaxVisionRange( void ) const
148159
// long range, particularly for snipers
149160
return 6000.0f;
150161
}
162+
163+
bool CNEOBotVision::IsInFieldOfView( CBaseEntity *subject ) const
164+
{
165+
// Ghoster is always in FOV of everyone
166+
const int iGhosterPlayer = NEORules()->GetGhosterPlayer();
167+
if (iGhosterPlayer > 0)
168+
{
169+
auto *pNEOPlayer = dynamic_cast<CNEO_Player *>(subject);
170+
if (pNEOPlayer && pNEOPlayer->IsCarryingGhost())
171+
{
172+
return true;
173+
}
174+
}
175+
176+
return IVision::IsInFieldOfView(subject);
177+
}

src/game/server/neo/bot/neo_bot_vision.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class CNEOBotVision : public IVision
2323
virtual float GetMaxVisionRange( void ) const override; // return maximum distance vision can reach
2424
virtual float GetMinRecognizeTime( void ) const override; // return VISUAL reaction time
2525

26+
bool IsInFieldOfView( CBaseEntity *subject ) const override;
27+
2628
private:
2729
CUtlVector< CHandle< CBaseCombatCharacter > > m_potentiallyVisibleNPCVector;
2830
CountdownTimer m_potentiallyVisibleUpdateTimer;

src/game/shared/neo/neo_gamerules.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class NEOViewVectors : public HL2MPViewVectors
8787
class CNEOGhostCapturePoint;
8888
class CNEO_Player;
8989
class CWeaponGhost;
90+
class CNEOBotSeekAndDestroy;
9091

9192
extern ConVar sv_neo_mirror_teamdamage_multiplier;
9293
extern ConVar sv_neo_mirror_teamdamage_duration;
@@ -411,6 +412,7 @@ class CNEORules : public CHL2MPRules, public CGameEventListener
411412
void SpawnTheGhost(const Vector *origin = nullptr);
412413
void SelectTheVIP();
413414

415+
friend class CNEOBotSeekAndDestroy;
414416
CUtlVector<int> m_pGhostCaps;
415417
CWeaponGhost *m_pGhost = nullptr;
416418
CNEO_Player *m_pVIP = nullptr;

0 commit comments

Comments
 (0)