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
2 changes: 2 additions & 0 deletions src/game/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,8 @@ target_sources_grouped(
neo/bot/behavior/neo_bot_scenario_monitor.h
neo/bot/behavior/neo_bot_seek_and_destroy.cpp
neo/bot/behavior/neo_bot_seek_and_destroy.h
neo/bot/behavior/neo_bot_seek_weapon.cpp
neo/bot/behavior/neo_bot_seek_weapon.h
neo/bot/behavior/neo_bot_tactical_monitor.cpp
neo/bot/behavior/neo_bot_tactical_monitor.h
neo/bot/behavior/nav_entities/neo_bot_nav_ent_destroy_entity.cpp
Expand Down
148 changes: 148 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_seek_weapon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include "cbase.h"
#include "neo_player.h"
#include "bot/neo_bot.h"
#include "bot/behavior/neo_bot_seek_weapon.h"
#include "bot/neo_bot_path_compute.h"

//---------------------------------------------------------------------------------------------
CBaseEntity *FindNearestPrimaryWeapon( const Vector &searchOrigin, bool bShortCircuit )
{
constexpr float flSearchRadius = 1000.0f;
CBaseEntity *pClosestWeapon = nullptr;
float flClosestDistSq = FLT_MAX;

// Iterate through all available weapons, looking for the nearest primary
CBaseEntity *pEntity = nullptr;
while ( ( pEntity = gEntList.FindEntityByClassnameWithin( pEntity, "weapon_*", searchOrigin, flSearchRadius ) ) )
{
CBaseCombatWeapon *pWeapon = pEntity->MyCombatWeaponPointer();
if ( pWeapon && !pWeapon->GetOwner() && pWeapon->HasAnyAmmo() && pWeapon->GetSlot() == 0 )
{
// Prioritize getting any primary class weapon that is NOT the ghost
if ( FStrEq( pEntity->GetClassname(), "weapon_ghost" ) )
{
continue;
}

if ( bShortCircuit )
{
return pEntity;
}

float flDistSq = searchOrigin.DistToSqr( pEntity->GetAbsOrigin() );
if ( flDistSq < flClosestDistSq )
{
flClosestDistSq = flDistSq;
pClosestWeapon = pEntity;
}
}
}

return pClosestWeapon;
}

//---------------------------------------------------------------------------------------------
CNEOBotSeekWeapon::CNEOBotSeekWeapon( void )
{
m_hTargetWeapon = nullptr;
}

//---------------------------------------------------------------------------------------------
CBaseEntity *CNEOBotSeekWeapon::FindAndPathToWeapon( CNEOBot *me )
{
CBaseEntity *pClosestWeapon = FindNearestPrimaryWeapon( me->GetAbsOrigin() );

if (pClosestWeapon)
{
m_hTargetWeapon = pClosestWeapon;
CNEOBotPathCompute(me, m_path, m_hTargetWeapon->GetAbsOrigin(), FASTEST_ROUTE);
}
else
{
// no weapon found
m_hTargetWeapon = nullptr;
m_path.Invalidate();

}

return pClosestWeapon;
}

//---------------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotSeekWeapon::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction )
{
CBaseCombatWeapon *pPrimary = me->Weapon_GetSlot(0);
if ( pPrimary )
{
if ( pPrimary->HasAnyAmmo() )
{
// so we don't exit this behavior still equipped with secondary
me->Weapon_Switch(pPrimary);
}
return Done("Already have a primary weapon");
}

if ( !FindAndPathToWeapon(me) )
{
return Done("No replacement primary found");
}

// found a replacement, go towards it next frame
return Continue();
}

//---------------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotSeekWeapon::Update( CNEOBot *me, float interval )
{
CBaseCombatWeapon *pPrimary = me->Weapon_GetSlot(0);
if ( pPrimary )
{
if ( pPrimary->HasAnyAmmo() )
{
// so we don't exit this behavior still equipped with secondary
me->Weapon_Switch(pPrimary);
}
return Done("Acquired a primary weapon");
}

if (!m_hTargetWeapon)
{
return Done("No weapon to seek");
}

if (!m_path.IsValid())
{
return Done("Path to weapon is invalid");
}

m_path.Update(me);

return Continue();
}

//---------------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotSeekWeapon::OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction )
{
return OnStart(me, interruptingAction);
}

//---------------------------------------------------------------------------------------------
EventDesiredResult< CNEOBot > CNEOBotSeekWeapon::OnStuck( CNEOBot *me )
{
FindAndPathToWeapon(me);
return TryContinue();
}

//---------------------------------------------------------------------------------------------
EventDesiredResult< CNEOBot > CNEOBotSeekWeapon::OnMoveToSuccess( CNEOBot *me, const Path *path )
{
// a weapon should have been picked up
return TryDone();
}

//---------------------------------------------------------------------------------------------
EventDesiredResult< CNEOBot > CNEOBotSeekWeapon::OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason )
{
FindAndPathToWeapon(me);
return TryContinue();
}
32 changes: 32 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_seek_weapon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef NEO_BOT_SEEK_WEAPON_H
#define NEO_BOT_SEEK_WEAPON_H

#include "bot/neo_bot.h"

// General utility that can be used to see if it makes sense to transition into this behavior
CBaseEntity *FindNearestPrimaryWeapon( const Vector &searchOrigin, bool bShortCircuit = false );

//--------------------------------------------------------------------------------------------------------
class CNEOBotSeekWeapon : public Action< CNEOBot >
{
public:
CNEOBotSeekWeapon( void );

virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) override;
virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ) override;
virtual ActionResult< CNEOBot > OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ) override;

virtual EventDesiredResult< CNEOBot > OnStuck( CNEOBot *me ) override;
virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ) override;
virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) override;

virtual const char *GetName( void ) const override { return "SeekWeapon"; }

private:
PathFollower m_path;
CHandle<CBaseEntity> m_hTargetWeapon;

CBaseEntity *FindAndPathToWeapon( CNEOBot *me );
};

#endif // NEO_BOT_SEEK_WEAPON_H
33 changes: 33 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "bot/behavior/neo_bot_scenario_monitor.h"

#include "bot/behavior/neo_bot_seek_and_destroy.h"
#include "bot/behavior/neo_bot_seek_weapon.h"
#include "bot/behavior/neo_bot_retreat_to_cover.h"
#include "bot/behavior/neo_bot_retreat_from_grenade.h"
#if 0 // NEO TODO (Adam) Fix picking up weapons, search for dropped weapons to pick up ammo
Expand Down Expand Up @@ -299,6 +300,12 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter
}
}

ActionResult< CNEOBot > scavengeResult = ScavengeForPrimaryWeapon( me );
if ( scavengeResult.IsRequestingChange() )
{
return scavengeResult;
}

#if 0 // NEO TODO (Adam) search for dropped weapons to resupply ammunition
bool isAvailable = ( me->GetIntentionInterface()->ShouldHurry( me ) != ANSWER_YES );

Expand Down Expand Up @@ -328,6 +335,32 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter
}


//-----------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotTacticalMonitor::ScavengeForPrimaryWeapon( CNEOBot *me )
{
if ( me->Weapon_GetSlot( 0 ) )
{
return Continue();
}

if ( !m_maintainTimer.IsElapsed() )
{
return Continue();
}
m_maintainTimer.Start( RandomFloat( 1.0f, 3.0f ) );

// Look for any one valid primary weapon, then dispatch into behavior for more optimal search
// true parameter: short-circuit the search if any valid primary weapon is found
// We just want to sanity check if there's a valid weapon before suspending into the dedicated behavior
if ( FindNearestPrimaryWeapon( me->GetAbsOrigin(), true ) )
{
return SuspendFor( new CNEOBotSeekWeapon, "Scavenging for a primary weapon" );
}

return Continue();
}


//-----------------------------------------------------------------------------------------
EventDesiredResult< CNEOBot > CNEOBotTacticalMonitor::OnOtherKilled( CNEOBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info )
{
Expand Down
2 changes: 2 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class CNEOBotTacticalMonitor : public Action< CNEOBot >
void MonitorArmedStickyBombs(CNEOBot* me);
#endif

ActionResult< CNEOBot > ScavengeForPrimaryWeapon(CNEOBot* me);

void AvoidBumpingEnemies(CNEOBot* me);
void ReconConsiderSuperJump(CNEOBot *me);
ActionResult< CNEOBot > WatchForGrenades(CNEOBot* me);
Expand Down