Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/game/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,7 @@ set(UNITY_SOURCE_NEO_BOT
neo/bot/neo_bot_body.cpp
neo/bot/neo_bot_locomotion.cpp
neo/bot/neo_bot_manager.cpp
neo/bot/neo_bot_memory_sound_combat.cpp
neo/bot/neo_bot_profile.cpp
neo/bot/neo_bot_squad.cpp
neo/bot/neo_bot_vision.cpp
Expand Down Expand Up @@ -1580,6 +1581,7 @@ target_sources_grouped(
neo/bot/neo_bot_contextual_query_interface.h
neo/bot/neo_bot_locomotion.h
neo/bot/neo_bot_manager.h
neo/bot/neo_bot_memory_sound_combat.h
neo/bot/neo_bot_profile.h
neo/bot/neo_bot_squad.h
neo/bot/neo_bot_vision.h
Expand Down
3 changes: 3 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_ctg_seek.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ class CNEOBotCtgSeek : public CNEOBotSeekAndDestroy

protected:
virtual void RecomputeSeekPath( CNEOBot *me ) override;

// Only detour to combat sounds that are roughly on the way to the ghost
virtual bool IsSeekGoalAnObjective() const override { return true; }
};
20 changes: 4 additions & 16 deletions src/game/server/neo/bot/behavior/neo_bot_jgr_juggernaut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,13 @@ void CNEOBotJgrJuggernaut::RecomputeSeekPath( CNEOBot *me )
m_bGoingToTargetEntity = false;
m_vGoalPos = vec3_origin;

// Listen for gunfights
const Vector& vGunfireLocation = SearchGunfireLocation(me);
if (vGunfireLocation != vec3_invalid)
// Listen for combat sounds
if ( TryPathToCombatSound( me ) )
{
m_vGoalPos = vGunfireLocation;
m_bGoingToTargetEntity = false;

if (CNEOBotPathCompute(me, m_path, m_vGoalPos, DEFAULT_ROUTE) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH)
{
return;
}
else
{
// NEO Jank: Sound is unreachable so wait for it clear from the sound list
m_soundSearchTimer.Start( 3.0f );
}
return;
}

// If unaware of gunfights, patrol spawn points
// If unaware of combat noises, patrol spawn points
if (m_jgrSpawns.Count() > 0)
{
CBaseEntity* pTargetSpawn = m_jgrSpawns[ RandomInt(0, m_jgrSpawns.Count() - 1) ];
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/neo/bot/behavior/neo_bot_jgr_seek.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//---------------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotJgrSeek::Update( CNEOBot *me, float interval )
{
m_bInvestigateGunfire = false; // Focus on capturing JGR
m_bListenForCombatSounds = false; // Focus on capturing JGR

if (NEORules()->GetGameType() != NEO_GAME_TYPE_JGR)
{
Expand Down
189 changes: 83 additions & 106 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 @@ -9,9 +9,9 @@
#include "bot/behavior/neo_bot_ctg_seek.h"
#include "bot/behavior/neo_bot_ctg_enemy.h"
#include "bot/behavior/neo_bot_jgr_seek.h"
#include "bot/neo_bot_memory_sound_combat.h"
#include "bot/neo_bot_path_compute.h"
#include "nav_mesh.h"
#include "soundent.h"

extern ConVar neo_bot_path_lookahead_range;
extern ConVar neo_bot_offense_must_push_time;
Expand All @@ -20,106 +20,114 @@ extern ConVar neo_bot_defense_must_defend_time;
ConVar neo_bot_debug_seek_and_destroy( "neo_bot_debug_seek_and_destroy", "0", FCVAR_CHEAT );
ConVar neo_bot_disable_seek_and_destroy( "neo_bot_disable_seek_and_destroy", "0", FCVAR_CHEAT );

ConVar sv_neo_bot_seek_and_destroy_combat_sound_commit_time( "sv_neo_bot_seek_and_destroy_combat_sound_commit_time", "3.0", FCVAR_CHEAT,
"Having picked a combat sound, travel to it for this long instead of re-picking.", true, 0, false, 0 );
ConVar sv_neo_bot_seek_and_destroy_combat_sound_detour_ratio( "sv_neo_bot_seek_and_destroy_combat_sound_detour_ratio", "1.5", FCVAR_CHEAT,
"When racing for an objective, divert only if going via the combat sound costs at most this multiple of direct distance.", true, 0, false, 0 );
ConVar sv_neo_bot_seek_and_destroy_combat_sound_arrive_range( "sv_neo_bot_seek_and_destroy_combat_sound_arrive_range", "200.0", FCVAR_CHEAT,
"Combat sound close enough to count as investigated.", true, 0, false, 0 );


//---------------------------------------------------------------------------------------------
CSound* CNEOBotSeekAndDestroy::SearchGunfireSounds(CNEOBot* me, const Vector* currentGoalPos)
// Is the bot inside the potentially-audible set of a sound at vSoundPos?
static bool BotInSoundPAS( CNEOBot *me, const Vector &vSoundPos )
{
CSound* pClosestSound = nullptr;
float flClosestDistSqr = FLT_MAX;
const Vector& vecMyOrigin = me->GetAbsOrigin();

float flGoalDistSqr = FLT_MAX;
if (currentGoalPos && *currentGoalPos != vec3_invalid)
{
flGoalDistSqr = vecMyOrigin.DistToSqr(*currentGoalPos);
}

CSound* pSound = nullptr;
for (int iSound = CSoundEnt::ActiveList(); iSound != SOUNDLIST_EMPTY; iSound = pSound->NextSound())
CPASFilter filter( vSoundPos );
for ( int i = 0; i < filter.GetRecipientCount(); ++i )
{
pSound = CSoundEnt::SoundPointerForIndex(iSound);
if (!pSound)
if ( filter.GetRecipientIndex( i ) == me->entindex() )
{
break;
return true;
}
}

if (!(pSound->SoundType() & SOUND_COMBAT))
{
continue;
}
return false;
}

CBaseEntity *pOwner = pSound->m_hOwner.Get();

// Ignore non-player sounds and sounds we were responsible for
if (!pOwner || !pOwner->IsPlayer() || pOwner == me)
{
continue;
}
//---------------------------------------------------------------------------------------------
// Returns true if m_path now leads to a combat sound the bot heard
bool CNEOBotSeekAndDestroy::TryPathToCombatSound( CNEOBot *me )
{
if ( !m_bListenForCombatSounds )
{
return false;
}

// NEO Jank: prevent bots from crowding teammates in teamplay
if (NEORules()->IsTeamplay() && me->InSameTeam(pOwner))
{
continue;
}
const float flArriveRange = sv_neo_bot_seek_and_destroy_combat_sound_arrive_range.GetFloat();
const float flArriveSqr = flArriveRange * flArriveRange;
const Vector vGoalBefore = m_vGoalPos;

// Stick with the sound already picked until arrival or the commit time runs out
const bool bCommitted = m_combatSoundCommitTimer.HasStarted() && !m_combatSoundCommitTimer.IsElapsed()
&& me->GetAbsOrigin().DistToSqr( m_vCombatSoundSpot ) > flArriveSqr;

// Search for the closest gunfire sounds
float distSqr = vecMyOrigin.DistToSqr(pSound->GetSoundOrigin());
if ( bCommitted && m_path.IsValid() && m_vGoalPos == m_vCombatSoundSpot )
{
return false;
}

// Only consider sounds that are closer than the current goal
if (distSqr >= flGoalDistSqr)
if ( !bCommitted )
{
if ( m_soundSearchTimer.HasStarted() && !m_soundSearchTimer.IsElapsed() )
{
continue;
return false;
}
m_soundSearchTimer.Start( 0.25f );

if (distSqr >= flClosestDistSqr)
Vector vFight;
if ( !NEOMemorySoundCombat::FindNearestFight( me->GetAbsOrigin(), me->GetTeamNumber(), me->entindex(), vFight )
|| !BotInSoundPAS( me, vFight ) )
{
continue;
return false;
}

bool bInPAS = false;
CPASFilter filter(pSound->GetSoundOrigin());
for (int i = 0; i < filter.GetRecipientCount(); ++i)
if ( vGoalBefore != vec3_origin )
{
if (filter.GetRecipientIndex(i) == me->entindex())
// Already heading there
if ( vGoalBefore.DistToSqr( vFight ) <= flArriveSqr )
{
bInPAS = true;
break;
return false;
}
}

if (!bInPAS)
{
continue;
// An objective runner only takes the detour if the sound is roughly on the way
if ( IsSeekGoalAnObjective() )
{
const float flDirect = me->GetAbsOrigin().DistTo( vGoalBefore );
const float flVia = me->GetAbsOrigin().DistTo( vFight ) + vFight.DistTo( vGoalBefore );
if ( flVia > flDirect * sv_neo_bot_seek_and_destroy_combat_sound_detour_ratio.GetFloat() )
{
return false;
}
}
}

flClosestDistSqr = distSqr;
pClosestSound = pSound;
m_vCombatSoundSpot = vFight;
m_combatSoundCommitTimer.Start( sv_neo_bot_seek_and_destroy_combat_sound_commit_time.GetFloat() );
}

return pClosestSound;
}
if ( CNEOBotPathCompute( me, m_path, m_vCombatSoundSpot, DEFAULT_ROUTE )
&& m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH )
{
m_vGoalPos = m_vCombatSoundSpot;
m_bGoingToTargetEntity = false;
return true;
}

//---------------------------------------------------------------------------------------------
const Vector& CNEOBotSeekAndDestroy::SearchGunfireLocation(CNEOBot* me, const Vector* currentGoalPos)
{
CSound* pBestSound = SearchGunfireSounds(me, currentGoalPos);
if (pBestSound)
// NEO Jank: the combat sound is unreachable, so give up on it and stop listening for a few seconds
m_combatSoundCommitTimer.Invalidate();
m_soundSearchTimer.Start( 3.0f );

if ( vGoalBefore != vec3_origin )
{
if (currentGoalPos && *currentGoalPos != vec3_invalid)
{
// Only change goal if recent gunfire is radically different than where I was going
constexpr float flThresholdSqr = 200.0f * 200.0f;
if (currentGoalPos->DistToSqr(pBestSound->GetSoundOrigin()) <= flThresholdSqr)
{
return vec3_invalid;
}
}
return pBestSound->GetSoundOrigin();
m_vGoalPos = vGoalBefore;
CNEOBotPathCompute( me, m_path, m_vGoalPos, DEFAULT_ROUTE );
}

return vec3_invalid;
return false;
}


//---------------------------------------------------------------------------------------------
CNEOBotSeekAndDestroy::CNEOBotSeekAndDestroy( float duration )
{
Expand Down Expand Up @@ -367,26 +375,10 @@ ActionResult< CNEOBot > CNEOBotSeekAndDestroy::UpdateCommon( CNEOBot *me, float
}
}
}
else if ( m_bInvestigateGunfire && (!m_soundSearchTimer.HasStarted() || m_soundSearchTimer.IsElapsed()) )
// Listen for combat sounds on the way to wherever we were going
else if ( TryPathToCombatSound( me ) )
{
m_soundSearchTimer.Start( 0.25f );

const Vector& vGunfireLocation = SearchGunfireLocation(me, &m_vGoalPos);
if (vGunfireLocation != vec3_invalid)
{
m_vGoalPos = vGunfireLocation;
m_bGoingToTargetEntity = false;

if (CNEOBotPathCompute(me, m_path, m_vGoalPos, DEFAULT_ROUTE))
{
m_repathTimer.Start( 45.0f );
}
else
{
// NEO Jank: Sound is unreachable so wait for it clear from the sound list
m_soundSearchTimer.Start( 3.0f );
}
}
m_repathTimer.Start( 45.0f );
}

return Continue();
Expand Down Expand Up @@ -591,25 +583,10 @@ void CNEOBotSeekAndDestroy::RecomputeSeekPath( CNEOBot *me )
}
#endif

// Listen for gunfights
if (m_bInvestigateGunfire)
// Listen for combat sounds
if ( TryPathToCombatSound( me ) )
{
const Vector& vGunfireLocation = SearchGunfireLocation(me);
if (vGunfireLocation != vec3_invalid)
{
m_vGoalPos = vGunfireLocation;
m_bGoingToTargetEntity = false;

if (CNEOBotPathCompute(me, m_path, m_vGoalPos, DEFAULT_ROUTE) && m_path.IsValid() && m_path.GetResult() == Path::COMPLETE_PATH)
{
return;
}
else
{
// NEO Jank: Sound is unreachable so wait for it clear from the sound list
m_soundSearchTimer.Start( 3.0f );
}
}
return;
}

// Fallback and roam random spawn points if we have all weapons.
Expand Down
10 changes: 5 additions & 5 deletions src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

#include "Path/NextBotChasePath.h"

class CSound;

//
// Roam around the map attacking enemies
//
Expand Down Expand Up @@ -37,17 +35,19 @@ class CNEOBotSeekAndDestroy : public Action< CNEOBot >

virtual void RecomputeSeekPath( CNEOBot *me );

static CSound* SearchGunfireSounds(CNEOBot* me, const Vector* currentGoalPos = nullptr);
static const Vector& SearchGunfireLocation(CNEOBot* me, const Vector* currentGoalPos = nullptr);
bool TryPathToCombatSound( CNEOBot *me );
virtual bool IsSeekGoalAnObjective() const { return false; }

PathFollower m_path;
CountdownTimer m_repathTimer;
CountdownTimer m_repathFailTimer;
CountdownTimer m_soundSearchTimer;
CountdownTimer m_combatSoundCommitTimer;
Vector m_vCombatSoundSpot = vec3_origin;
CountdownTimer m_itemStolenTimer;
EHANDLE m_hTargetEntity;
bool m_bGoingToTargetEntity = false;
bool m_bInvestigateGunfire = true;
bool m_bListenForCombatSounds = true;
Vector m_vGoalPos = vec3_origin;
bool m_bTimerElapsed = false;
bool m_bOverrideApproach = false;
Expand Down
4 changes: 4 additions & 0 deletions src/game/server/neo/bot/neo_bot_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "team.h"
#include "neo_bot.h"
#include "neo_gamerules.h"
#include "neo_bot_memory_sound_combat.h"
#include "neo_bot_path_reservation.h"


Expand Down Expand Up @@ -139,6 +140,7 @@ void CNEOBotManager::OnMapLoaded( void )
ClearStuckBotData();

CNEOBotPathReservations()->Clear();
NEOMemorySoundCombat::Reset();
}


Expand All @@ -147,6 +149,8 @@ void CNEOBotManager::Update()
{
MaintainBotQuota();

NEOMemorySoundCombat::Update();

DrawStuckBotData();

NextBotManager::Update();
Expand Down
Loading