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
7 changes: 3 additions & 4 deletions src/game/server/neo/bot/behavior/neo_bot_attack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class CSearchForAttackCover : public ISearchSurroundingAreasFunctor
m_threatArea = threat->GetLastKnownArea();
m_goalArea = goalArea ? goalArea : m_threatArea; // prioritize movement towards input goal area or threat
m_myDistToGoalSq = m_goalArea ? ( m_goalArea->GetCenter() - m_me->GetAbsOrigin() ).LengthSqr() : 0;
m_onStuckPenalty = neo_bot_path_reservation_onstuck_penalty.GetFloat();
}

bool IsBetterCandidate( CNavArea *area, float avoidPenalty ) const
Expand Down Expand Up @@ -126,20 +125,21 @@ class CSearchForAttackCover : public ISearchSurroundingAreasFunctor
}

// Skip areas that are hazardous or where bots get stuck
const AreaPenalty_t penalty = CNEOBotPathReservations()->GetAreaPenalty( area->GetID() );
if ( neo_bot_path_reservation_enable.GetBool() )
{
int navAreaId = area->GetID();
if (CNEOBotPathReservations()->IsAreaHazardous(navAreaId, m_me))
{
return true;
}
if (CNEOBotPathReservations()->GetAreaAvoidPenalty(navAreaId) >= m_onStuckPenalty)
if (penalty.IsStuckProne())
{
return true;
}
}

float avoidPenalty = CNEOBotPathReservations()->GetAreaAvoidPenalty( area->GetID() );
float avoidPenalty = penalty.flStuck;
if ( !IsBetterCandidate( area, avoidPenalty ) )
{
return true; // the cover candidate we already have is at least as good
Expand Down Expand Up @@ -232,7 +232,6 @@ class CSearchForAttackCover : public ISearchSurroundingAreasFunctor
const CNavArea *m_myArea; // reference point of myself
const CNavArea *m_threatArea; // reference point of the threat
float m_myDistToGoalSq; // the bot's current distance to the threat
float m_onStuckPenalty; // cache onstuck penalty
};


Expand Down
4 changes: 2 additions & 2 deletions src/game/server/neo/bot/behavior/neo_bot_behavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,15 @@ EventDesiredResult<CNEOBot> CNEOBotMainAction::OnKilled( CNEOBot *me, const CTak
// Intended to add some variance to pathing for similar starting scenarios
if ( const CNavArea *navArea = me->GetLastKnownArea() )
{
CNEOBotPathReservations()->IncrementAreaAvoidPenalty( navArea->GetID(), neo_bot_path_reservation_killed_penalty.GetFloat() );
CNEOBotPathReservations()->IncrementAreaDeathPenalty( navArea->GetID(), neo_bot_path_reservation_killed_penalty.GetFloat() );
}
else
{
// Fallback if GetLastKnownArea is null, try finding nearest nav area
CNavArea *nearestArea = TheNavMesh->GetNearestNavArea( me->GetAbsOrigin() );
if ( nearestArea )
{
CNEOBotPathReservations()->IncrementAreaAvoidPenalty( nearestArea->GetID(), neo_bot_path_reservation_killed_penalty.GetFloat() );
CNEOBotPathReservations()->IncrementAreaDeathPenalty( nearestArea->GetID(), neo_bot_path_reservation_killed_penalty.GetFloat() );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ class CSearchForCoverFromGrenade : public ISearchSurroundingAreasFunctor
{
m_me = me;
m_grenade = grenade;
m_onStuckPenalty = neo_bot_path_reservation_onstuck_penalty.GetFloat();
m_coverDist = CNEOBotRetreatFromGrenade::GetGrenadeCoverDistance();
m_blastRadius = sv_neo_grenade_blast_radius.GetFloat();

Expand Down Expand Up @@ -207,14 +206,15 @@ class CSearchForCoverFromGrenade : public ISearchSurroundingAreasFunctor
}

// Skip areas that are hazardous or where bots get stuck
const AreaPenalty_t penalty = CNEOBotPathReservations()->GetAreaPenalty( area->GetID() );
if ( neo_bot_path_reservation_enable.GetBool() )
{
int navAreaId = area->GetID();
if (CNEOBotPathReservations()->IsAreaHazardous(navAreaId, m_me))
{
return true;
}
if (CNEOBotPathReservations()->GetAreaAvoidPenalty(navAreaId) >= m_onStuckPenalty)
if (penalty.IsStuckProne())
{
return true;
}
Expand All @@ -236,6 +236,7 @@ class CSearchForCoverFromGrenade : public ISearchSurroundingAreasFunctor
CoverCandidate candidate;
candidate.area = area;
candidate.cost = travelDistanceSoFar + kCoverEnemyExposureCost * CountThreatsExposingArea( m_me, area );
candidate.cost += penalty.flStuck; // where bots got stuck, as in path cost
m_coverAreaVector.AddToTail( candidate );

return true;
Expand Down Expand Up @@ -278,7 +279,6 @@ class CSearchForCoverFromGrenade : public ISearchSurroundingAreasFunctor

CNEOBot *m_me;
CBaseEntity *m_grenade;
float m_onStuckPenalty;
float m_coverDist;
float m_blastRadius;
Vector m_vecDangerStart;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ class CSearchForSafeArea : public ISearchSurroundingAreasFunctor
public:
CSearchForSafeArea(CNEOBot *me)
: m_me(me)
, m_onStuckPenalty(neo_bot_path_reservation_onstuck_penalty.GetFloat())
{
}

virtual bool operator()(CNavArea *baseArea, CNavArea *priorArea, float travelDistanceSoFar)
{
int id = baseArea->GetID();
const AreaPenalty_t penalty = CNEOBotPathReservations()->GetAreaPenalty(id);
float candidateHazardTime = CNEOBotPathReservations()->GetAreaHazardousTime(id, m_me);
if (candidateHazardTime > 0)
{
Expand All @@ -27,10 +27,14 @@ class CSearchForSafeArea : public ISearchSurroundingAreasFunctor
baseArea->DrawFilled(255, 0, 0, MIN(255, candidateHazardTime), candidateHazardTime);
}
}
else if (CNEOBotPathReservations()->GetAreaAvoidPenalty(id) < m_onStuckPenalty)
else if (penalty.flStuck <= 0.0f)
{
m_safeAreas.AddToTail(baseArea);
}
else if (!penalty.IsStuckProne())
{
m_stuckOnceAreas.AddToTail(baseArea); // a fallback when no area free of stucks is found
}

if (m_safeAreas.Count() >= MAX_NON_HAZARD_AREA_CANDIDATES)
{
Expand All @@ -47,10 +51,10 @@ class CSearchForSafeArea : public ISearchSurroundingAreasFunctor
}

CUtlVector<CNavArea *> m_safeAreas;
CUtlVector<CNavArea *> m_stuckOnceAreas;

private:
CNEOBot *m_me;
float m_onStuckPenalty;
};

CNEOBotRetreatFromHazardArea::CNEOBotRetreatFromHazardArea()
Expand All @@ -68,14 +72,15 @@ CNavArea *CNEOBotRetreatFromHazardArea::FindSafeArea(CNEOBot *me)
CSearchForSafeArea search(me);
SearchSurroundingAreas(start, search);

if (search.m_safeAreas.Count() == 0)
const CUtlVector<CNavArea *> &candidates = search.m_safeAreas.Count() ? search.m_safeAreas : search.m_stuckOnceAreas;
if (candidates.Count() == 0)
{
return nullptr;
}

int last = MIN(MAX_NON_HAZARD_AREA_CANDIDATES, search.m_safeAreas.Count());
int last = MIN(MAX_NON_HAZARD_AREA_CANDIDATES, candidates.Count());
int which = RandomInt(0, last - 1);
return search.m_safeAreas[which];
return candidates[which];
}

ActionResult<CNEOBot> CNEOBotRetreatFromHazardArea::OnStart(CNEOBot *me, Action<CNEOBot> *priorAction)
Expand Down
21 changes: 15 additions & 6 deletions src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,16 @@ int CountThreatsExposingArea( CNEOBot *me, CNavArea *area )
}


// A cover area where a bot got stuck this round counts as exposed to this many more threats
static constexpr int NEO_BOT_STUCK_AREA_EXPOSURE = 1;

// collect nearby areas that provide cover from our known threats
class CSearchForCover : public ISearchSurroundingAreasFunctor
{
public:
CSearchForCover( CNEOBot *me )
{
m_me = me;
m_onStuckPenalty = neo_bot_path_reservation_onstuck_penalty.GetFloat();
m_minExposureCount = 9999;

if ( neo_bot_debug_retreat_to_cover.GetBool() )
Expand All @@ -127,14 +129,15 @@ class CSearchForCover : public ISearchSurroundingAreasFunctor
CNavArea *area = (CNavArea *)baseArea;

// Skip areas that are hazardous or where bots get stuck
const AreaPenalty_t penalty = CNEOBotPathReservations()->GetAreaPenalty( area->GetID() );
if ( neo_bot_path_reservation_enable.GetBool() )
{
int navAreaId = area->GetID();
if (CNEOBotPathReservations()->IsAreaHazardous(navAreaId, m_me))
{
return true;
}
if (CNEOBotPathReservations()->GetAreaAvoidPenalty(navAreaId) >= m_onStuckPenalty)
if (penalty.IsStuckProne())
{
return true;
}
Expand All @@ -143,14 +146,21 @@ class CSearchForCover : public ISearchSurroundingAreasFunctor
CTestAreaAgainstThreats test( m_me, area );
m_me->GetVisionInterface()->ForEachKnownEntity( test );

if ( test.m_exposedThreatCount <= m_minExposureCount )
// an area where a bot got stuck this round counts as exposed to one more threat
int exposure = test.m_exposedThreatCount;
if ( penalty.flStuck > 0.0f )
{
exposure += NEO_BOT_STUCK_AREA_EXPOSURE;
}

if ( exposure <= m_minExposureCount )
{
// this area is at least as good as already found cover
if ( test.m_exposedThreatCount < m_minExposureCount )
if ( exposure < m_minExposureCount )
{
// this area is better than already found cover - throw out list and start over
m_coverAreaVector.RemoveAll();
m_minExposureCount = test.m_exposedThreatCount;
m_minExposureCount = exposure;
}

m_coverAreaVector.AddToTail( area );
Expand Down Expand Up @@ -181,7 +191,6 @@ class CSearchForCover : public ISearchSurroundingAreasFunctor
CNEOBot *m_me;
CUtlVector< CNavArea * > m_coverAreaVector;
int m_minExposureCount;
float m_onStuckPenalty;
};


Expand Down
2 changes: 1 addition & 1 deletion src/game/server/neo/bot/neo_bot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3062,7 +3062,7 @@ CNEOBotIntention::~CNEOBotIntention()

static void CNEOBotApplyOnStuckAreaPenalty( CNEOBot *me )
{
// NEO Jank: For the current match, all bots share where they get stuck.
// NEO Jank: For the current round, all bots share where they get stuck.
// The reasoning is that bots on either team will get stuck in their respective half of the map
// so the overall fairness may balance out for both teams sharing common sticking points.
if ( const CNavArea *navArea = me->GetLastKnownArea() )
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/neo/bot/neo_bot_path_cost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ float CNEOBotPathCost::operator()(CNavArea* baseArea, CNavArea* fromArea, const
// New path reservation related cost adjustments
if ( !m_bIgnoreReservations && (m_routeType != FASTEST_ROUTE) )
{
cost += CNEOBotPathReservations()->GetAreaAvoidPenalty(area->GetID());
cost += CNEOBotPathReservations()->GetAreaPenalty(area->GetID()).GetPathCost();

if ( NEORules()->IsTeamplay() )
{
Expand Down
60 changes: 47 additions & 13 deletions src/game/server/neo/bot/neo_bot_path_reservation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ void CNEOBotPathReservationSystem::ReleaseAllAreas(CNEOBot *bot)
void CNEOBotPathReservationSystem::Clear()
{
ClearRound();
m_AreaAvoidPenalties.RemoveAll();
m_AreaPenalties.RemoveAll();
}

//--------------------------------------------------------------------------------------------------------------
Expand All @@ -207,6 +207,12 @@ void CNEOBotPathReservationSystem::ClearRound()
m_HazardAreas[team].RemoveAll();
}
m_BotReservedAreas.RemoveAll();

// a new round respawns everyone, so where bots got stuck starts over
FOR_EACH_MAP_FAST( m_AreaPenalties, i )
{
m_AreaPenalties[i].flStuck = 0.0f;
}
}

//-------------------------------------------------------------------------------------------------
Expand All @@ -233,36 +239,64 @@ int CNEOBotPathReservationSystem::GetPredictedFriendlyPathCount( int areaID, int
}

//-------------------------------------------------------------------------------------------------
void CNEOBotPathReservationSystem::IncrementAreaAvoidPenalty(unsigned int navAreaID, float penaltyAmount)
AreaPenalty_t *CNEOBotPathReservationSystem::FindOrAddAreaPenalty(unsigned int navAreaID)
{
if ( !neo_bot_path_reservation_avoid_penalty_enable.GetBool() )
{
return;
return nullptr;
}

unsigned short index = m_AreaAvoidPenalties.Find(navAreaID);
if (index == m_AreaAvoidPenalties.InvalidIndex())
unsigned short index = m_AreaPenalties.Find(navAreaID);
if (index == m_AreaPenalties.InvalidIndex())
{
index = m_AreaAvoidPenalties.Insert(navAreaID, 0.0f);
index = m_AreaPenalties.Insert(navAreaID, AreaPenalty_t());
}
return &m_AreaPenalties[index];
}

m_AreaAvoidPenalties[index] += penaltyAmount;
//-------------------------------------------------------------------------------------------------
void CNEOBotPathReservationSystem::IncrementAreaAvoidPenalty(unsigned int navAreaID, float penaltyAmount)
{
if ( AreaPenalty_t *pPenalty = FindOrAddAreaPenalty(navAreaID) )
{
pPenalty->flStuck += penaltyAmount;
}
}

//-------------------------------------------------------------------------------------------------
float CNEOBotPathReservationSystem::GetAreaAvoidPenalty(unsigned int navAreaID) const
void CNEOBotPathReservationSystem::IncrementAreaDeathPenalty(unsigned int navAreaID, float penaltyAmount)
{
if ( AreaPenalty_t *pPenalty = FindOrAddAreaPenalty(navAreaID) )
{
pPenalty->flDeath += penaltyAmount;
}
}

//-------------------------------------------------------------------------------------------------
AreaPenalty_t CNEOBotPathReservationSystem::GetAreaPenalty(unsigned int navAreaID) const
{
if ( !neo_bot_path_reservation_avoid_penalty_enable.GetBool() )
{
return 0.0f;
return AreaPenalty_t();
}

unsigned short index = m_AreaAvoidPenalties.Find(navAreaID);
if (index != m_AreaAvoidPenalties.InvalidIndex())
unsigned short index = m_AreaPenalties.Find(navAreaID);
if (index != m_AreaPenalties.InvalidIndex())
{
return m_AreaAvoidPenalties[index];
return m_AreaPenalties[index];
}
return 0.0f;
return AreaPenalty_t();
}

// Cover and retreat searches skip an area once bots got stuck in it this many times in the round;
// an area with fewer stucks is only a cost to them, since one stuck can be a crowd or a pushed prop
static constexpr int NEO_BOT_STUCK_PRONE_AREA_COUNT = 2;

//-------------------------------------------------------------------------------------------------
bool AreaPenalty_t::IsStuckProne() const
{
const float flStuckPronePenalty = NEO_BOT_STUCK_PRONE_AREA_COUNT * neo_bot_path_reservation_onstuck_penalty.GetFloat();
return flStuck >= flStuckPronePenalty;
}

//-------------------------------------------------------------------------------------------------
Expand Down
21 changes: 18 additions & 3 deletions src/game/server/neo/bot/neo_bot_path_reservation.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ struct AreaReservation_t
AreaReservation_t& operator=(const AreaReservation_t& src) { claims = src.claims; return *this; }
};

// What bots learned about a nav area this match, kept as path cost penalties
struct AreaPenalty_t
{
// where bots got stuck this round: a path cost, and to cover and retreat searches a cost, then a filter
float flStuck = 0.0f;
// where bots died: only a path cost, since an area where bots died can still be good cover
float flDeath = 0.0f;

float GetPathCost() const { return flStuck + flDeath; }
bool IsStuckProne() const;
};

struct HazardInfo
{
float smokeExpireTime; // when the smoke hazard risk expires
Expand Down Expand Up @@ -54,7 +66,7 @@ class CNEOBotPathReservationSystem

CNEOBotPathReservationSystem()
: m_BotReservedAreas(DefLessFunc(int))
, m_AreaAvoidPenalties(DefLessFunc(unsigned int))
, m_AreaPenalties(DefLessFunc(unsigned int))
{
for (int i = 0; i < TEAM__TOTAL; ++i)
{
Expand All @@ -71,7 +83,8 @@ class CNEOBotPathReservationSystem
int GetPredictedFriendlyPathCount( int areaID, int teamID, const CNEOBot *excluding = NULL ) const;

void IncrementAreaAvoidPenalty(unsigned int navAreaID, float penaltyAmount);
float GetAreaAvoidPenalty(unsigned int navAreaID) const;
void IncrementAreaDeathPenalty(unsigned int navAreaID, float penaltyAmount);
AreaPenalty_t GetAreaPenalty(unsigned int navAreaID) const;

void AddDeadlyHazard(int navAreaID, float expireTime, int teamID, bool propagatePVS = false);
void AddFragHazard(int navAreaID, float expireTime, int teamID);
Expand All @@ -84,7 +97,9 @@ class CNEOBotPathReservationSystem

CUtlMap<int, AreaReservation_t> m_Reservations[TEAM__TOTAL]; // keyed by nav area ID
CUtlMap<int, BotReservedAreas_t> m_BotReservedAreas; // keyed by bot entindex
CUtlMap<unsigned int, float> m_AreaAvoidPenalties;
AreaPenalty_t *FindOrAddAreaPenalty(unsigned int navAreaID);

CUtlMap<unsigned int, AreaPenalty_t> m_AreaPenalties; // keyed by nav area ID
CUtlMap<int, HazardInfo> m_HazardAreas[TEAM__TOTAL];
};

Expand Down
Loading