Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 108 additions & 12 deletions src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "bot/behavior/neo_bot_attack.h"
#include "bot/behavior/neo_bot_seek_and_destroy.h"
#include "nav_mesh.h"
#include "neo_ghost_cap_point.h"

extern ConVar neo_bot_path_lookahead_range;
extern ConVar neo_bot_offense_must_push_time;
Expand Down Expand Up @@ -369,21 +370,116 @@ void CNEOBotSeekAndDestroy::RecomputeSeekPath( CNEOBot *me )
#endif

if (NEORules()->GhostExists())
{ // If the ghost exists, go to the ghost
m_vGoalPos = NEORules()->GetGhostPos();
constexpr int DISTANCE_CONSIDERED_ARRIVED_SQUARED = 5000;
if (m_vGoalPos.DistToSqr(me->GetAbsOrigin()) < DISTANCE_CONSIDERED_ARRIVED_SQUARED)
{
const Vector vGhostPos = NEORules()->GetGhostPos();
const int iGhosterPlayer = NEORules()->GetGhosterPlayer();

const int iMyTeam = me->GetTeamNumber();
const int iGhosterTeam = NEORules()->GetGhosterTeam();

bool bGoToGoalPos = true;
bool bGetCloserToGhoster = false;
bool bQuickToGoalPos = false;

if (iGhosterPlayer > 0)
{
constexpr float RECHECK_TIME = 30.f;
m_repathTimer.Start(RECHECK_TIME);
m_bGoingToTargetEntity = false;
return;
const int iTargetCapTeam = (iGhosterTeam == iMyTeam) ? iMyTeam : iGhosterTeam;

// If there's a player playing ghost, turn toward cap zones that's
// closest to the ghoster player
Vector vrTargetCapPos;
int iMinCapGhostLength = INT_MAX;

// Enemy team is carrying the ghost - try to defend the cap zone
// You or friendly team is carrying the ghost - go towards the cap point

for (int i = 0; i < NEORules()->m_pGhostCaps.Count(); i++)
{
auto pGhostCap = dynamic_cast<CNEOGhostCapturePoint *>(
UTIL_EntityByIndex(NEORules()->m_pGhostCaps[i]));
if (!pGhostCap)
{
continue;
}

const Vector vCapPos = pGhostCap->GetAbsOrigin();
const Vector vGhostCapDist = vGhostPos - vCapPos;
const int iGhostCapLength = static_cast<int>(vGhostCapDist.Length());
const int iCapTeam = pGhostCap->owningTeamAlternate();

if (iCapTeam == iTargetCapTeam && iGhostCapLength < iMinCapGhostLength)
{
vrTargetCapPos = vCapPos;
iMinCapGhostLength = iGhostCapLength;
}
}

if (!me->IsCarryingGhost())
{
// If a ghoster player carrying and nearby, get close to them
// Friendly - get closer and assists, enemy - get closer and attack
const float flGhosterMeters = METERS_PER_INCH * me->GetAbsOrigin().DistTo(vGhostPos);
const float flMinCapMeters = METERS_PER_INCH * iMinCapGhostLength;
static const constexpr float FL_NEARBY_FOLLOW_METERS = 26.0f;
static const constexpr float FL_NEARBY_CAPZONE_METERS = 18.0f;
const bool bGhosterNearby = flGhosterMeters < FL_NEARBY_FOLLOW_METERS;
const bool bCapzoneNearby = flMinCapMeters < FL_NEARBY_CAPZONE_METERS;
// But a nearby capzone overrides a nearby ghoster
bGetCloserToGhoster = !bCapzoneNearby && bGhosterNearby && flMinCapMeters > flGhosterMeters;
}

if (bGetCloserToGhoster)
{
m_vGoalPos = vGhostPos;
bQuickToGoalPos = true;
}
else
{
// iMinCapGhostLength == INT_MAX should never happen, just disable going to target
Assert(iMinCapGhostLength < INT_MAX);
bGoToGoalPos = (iMinCapGhostLength < INT_MAX);

m_vGoalPos = vrTargetCapPos;
bQuickToGoalPos = (iGhosterTeam != iMyTeam);
}
}
m_bGoingToTargetEntity = true;
CNEOBotPathCost cost(me, SAFEST_ROUTE);
if (m_path.Compute(me, m_vGoalPos, cost, 0.0f, true, true) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH)
else
{
return;
// If the ghost exists, go to the ghost
m_vGoalPos = vGhostPos;
// NEO TODO (nullsystem): More sophisticated on handling non-ghost playing scenario,
// although it kind of already prefer hunting down players when they're in view, but
// just going towards ghost isn't something that always happens in general.
}

if (bGoToGoalPos)
{
if (bGetCloserToGhoster)
{
const int iDistSqrConsidered = (iGhosterTeam == iMyTeam) ? 50000 : 5000;
if (m_vGoalPos.DistToSqr(me->GetAbsOrigin()) < iDistSqrConsidered)
{
// Don't stop targeting entity even when near enough
return;
}
}
else
{
constexpr int DISTANCE_CONSIDERED_ARRIVED_SQUARED = 10000;
if (m_vGoalPos.DistToSqr(me->GetAbsOrigin()) < DISTANCE_CONSIDERED_ARRIVED_SQUARED)
{
constexpr float RECHECK_TIME = 30.f;
m_repathTimer.Start(RECHECK_TIME);
m_bGoingToTargetEntity = false;
return;
}
}
m_bGoingToTargetEntity = true;
CNEOBotPathCost cost(me, bQuickToGoalPos ? FASTEST_ROUTE : SAFEST_ROUTE);
if (m_path.Compute(me, m_vGoalPos, cost, 0.0f, true, true) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH)
{
return;
}
}
}

Expand Down
27 changes: 27 additions & 0 deletions src/game/server/neo/bot/neo_bot_vision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ bool CNEOBotVision::IsIgnored( CBaseEntity* subject ) const
return false;
}

const int iGhosterPlayer = NEORules()->GetGhosterPlayer();
if (iGhosterPlayer > 0)
{
auto *pNEOPlayer = dynamic_cast<CNEO_Player *>(subject);
if (pNEOPlayer && pNEOPlayer->IsCarryingGhost())
{
// don't ignore ghoster
return false;
}
}

if ( subject->IsEffectActive( EF_NODRAW ) )
{
return true;
Expand Down Expand Up @@ -148,3 +159,19 @@ float CNEOBotVision::GetMaxVisionRange( void ) const
// long range, particularly for snipers
return 6000.0f;
}

bool CNEOBotVision::IsInFieldOfView( CBaseEntity *subject ) const
{
// Ghoster is always in FOV of everyone
const int iGhosterPlayer = NEORules()->GetGhosterPlayer();
if (iGhosterPlayer > 0)
{
auto *pNEOPlayer = dynamic_cast<CNEO_Player *>(subject);
if (pNEOPlayer && pNEOPlayer->IsCarryingGhost())
{
return true;
}
}

return IVision::IsInFieldOfView(subject);
}
2 changes: 2 additions & 0 deletions src/game/server/neo/bot/neo_bot_vision.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class CNEOBotVision : public IVision
virtual float GetMaxVisionRange( void ) const override; // return maximum distance vision can reach
virtual float GetMinRecognizeTime( void ) const override; // return VISUAL reaction time

bool IsInFieldOfView( CBaseEntity *subject ) const override;

private:
CUtlVector< CHandle< CBaseCombatCharacter > > m_potentiallyVisibleNPCVector;
CountdownTimer m_potentiallyVisibleUpdateTimer;
Expand Down
2 changes: 2 additions & 0 deletions src/game/shared/neo/neo_gamerules.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class NEOViewVectors : public HL2MPViewVectors
class CNEOGhostCapturePoint;
class CNEO_Player;
class CWeaponGhost;
class CNEOBotSeekAndDestroy;

extern ConVar sv_neo_mirror_teamdamage_multiplier;
extern ConVar sv_neo_mirror_teamdamage_duration;
Expand Down Expand Up @@ -395,6 +396,7 @@ class CNEORules : public CHL2MPRules, public CGameEventListener
void SpawnTheGhost(const Vector *origin = nullptr);
void SelectTheVIP();

friend class CNEOBotSeekAndDestroy;
CUtlVector<int> m_pGhostCaps;
CWeaponGhost *m_pGhost = nullptr;
CNEO_Player *m_pVIP = nullptr;
Expand Down
Loading