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
10 changes: 10 additions & 0 deletions src/game/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,16 @@ target_sources_grouped(
neo/bot/behavior/neo_bot_behavior.h
neo/bot/behavior/neo_bot_dead.cpp
neo/bot/behavior/neo_bot_dead.h
neo/bot/behavior/neo_bot_jgr_capture.cpp
neo/bot/behavior/neo_bot_jgr_capture.h
neo/bot/behavior/neo_bot_jgr_enemy.cpp
neo/bot/behavior/neo_bot_jgr_enemy.h
neo/bot/behavior/neo_bot_jgr_escort.cpp
neo/bot/behavior/neo_bot_jgr_escort.h
neo/bot/behavior/neo_bot_jgr_juggernaut.cpp
neo/bot/behavior/neo_bot_jgr_juggernaut.h
neo/bot/behavior/neo_bot_jgr_seek.cpp
neo/bot/behavior/neo_bot_jgr_seek.h
neo/bot/behavior/neo_bot_melee_attack.cpp
neo/bot/behavior/neo_bot_melee_attack.h
neo/bot/behavior/neo_bot_move_to_vantage_point.cpp
Expand Down
1 change: 1 addition & 0 deletions src/game/server/neo/bot/behavior/neo_bot_behavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,7 @@ void CNEOBotMainAction::FireWeaponAtEnemy( CNEOBot *me )
{
// Minimum viable firing BALC
// TODO: Proper heat management for higher difficulty bots
me->ReleaseWalkButton(); // NEO Jank: this actually cancels sprint
me->PressFireButton(GetFireDurationByDifficulty(me));
return;
}
Expand Down
151 changes: 151 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_jgr_capture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#include "cbase.h"
#include "bot/behavior/neo_bot_jgr_capture.h"
#include "bot/behavior/neo_bot_retreat_to_cover.h"
#include "bot/neo_bot_path_compute.h"
#include "neo_gamerules.h"
#include "neo_juggernaut.h"

//---------------------------------------------------------------------------------------------
CNEOBotJgrCapture::CNEOBotJgrCapture( CNEO_Juggernaut *pObjective )
{
m_hObjective = pObjective;
}

//---------------------------------------------------------------------------------------------
ActionResult<CNEOBot> CNEOBotJgrCapture::OnStart( CNEOBot *me, Action<CNEOBot> *priorAction )
{
m_useAttemptTimer.Invalidate();
m_path.Invalidate();
m_repathTimer.Invalidate();

if ( !m_hObjective )
{
return Done( "No capture target specified." );
}

if ( NEORules()->GetGameType() != NEO_GAME_TYPE_JGR )
{
return Done( "Not in JGR game mode" );
}

if ( !FStrEq( m_hObjective->GetClassname(), "neo_juggernaut" ) )
{
return Done( "Capture target is not the juggernaut" );
}

// Ignore enemies while capturing juggernaut
me->StopLookingAroundForEnemies();
return Continue();
}

//---------------------------------------------------------------------------------------------
void CNEOBotJgrCapture::OnEnd( CNEOBot *me, Action<CNEOBot> *nextAction )
{
me->ReleaseUseButton();
me->StartLookingAroundForEnemies();
}

//---------------------------------------------------------------------------------------------
ActionResult<CNEOBot> CNEOBotJgrCapture::OnSuspend( CNEOBot *me, Action<CNEOBot> *interruptingAction )
{
// CNEOBotTacticalMonitor -> CNEOBotRetreatToCover will handle reacting to enemies if under fire
// Debatably, maybe bots should just ignore enemies, but that would require a change to CNEOBotTacticalMonitor
// Also it might be more fun for humans if they can interrupt bots from taking the Juggernaut.
me->ReleaseUseButton();
me->StartLookingAroundForEnemies();
return Continue();
}

//---------------------------------------------------------------------------------------------
ActionResult<CNEOBot> CNEOBotJgrCapture::OnResume( CNEOBot *me, Action<CNEOBot> *interruptingAction )
{
me->StopLookingAroundForEnemies();
return Continue();
}

//---------------------------------------------------------------------------------------------
ActionResult<CNEOBot> CNEOBotJgrCapture::Update( CNEOBot *me, float interval )
{
if ( me->IsDead() )
{
return Done( "I am dead" );
}

if ( !m_hObjective )
{
return Done( "Capture target gone" );
}

if ( me->GetClass() == NEO_CLASS_JUGGERNAUT )
{
return Done( "Became the Juggernaut" );
}

const int iJuggernautPlayer = NEORules()->GetJuggernautPlayer();
if ( iJuggernautPlayer > 0 )
{
CNEO_Player *pJuggernautPlayer = ToNEOPlayer( UTIL_PlayerByIndex( iJuggernautPlayer ) );
if ( pJuggernautPlayer && pJuggernautPlayer != me )
{
return Done( "Juggernaut captured by someone else" );
}
}

if ( me->GetAbsOrigin().DistToSqr( m_hObjective->GetAbsOrigin() ) < CNEO_Juggernaut::GetUseDistanceSquared() )
{
CNEO_Juggernaut *pJuggernaut = m_hObjective.Get();
if ( NEORules()->IsJuggernautLocked() || (pJuggernaut && pJuggernaut->m_bLocked) )
{
Assert( NEORules()->IsJuggernautLocked() == (pJuggernaut && pJuggernaut->m_bLocked) );
me->ReleaseUseButton();
return SuspendFor( new CNEOBotRetreatToCover( 2.0f ), "Juggernaut is locked, taking cover to wait for it to unlock" );
}

// Stop moving while using
m_path.Invalidate();
me->ReleaseForwardButton();
me->ReleaseBackwardButton();
me->ReleaseLeftButton();
me->ReleaseRightButton();

const Vector vecObjectiveCenter = m_hObjective->WorldSpaceCenter();
Vector vecToTargetDir = vecObjectiveCenter - me->EyePosition();
vecToTargetDir.NormalizeInPlace();

// Ensure we are facing the target before attempting to use
Vector vecEyeDirection;
me->EyeVectors( &vecEyeDirection );
const float flDot = vecEyeDirection.Dot( vecToTargetDir );
const bool bIsFacing = flDot > 0.9f;

me->GetBodyInterface()->AimHeadTowards( vecObjectiveCenter, IBody::CRITICAL, 0.1f, NULL, "Looking at Juggernaut objective to use" );

if ( m_useAttemptTimer.HasStarted() )
{
if ( m_useAttemptTimer.IsElapsed() )
{
return Done( "Use timer elapsed, failed to capture" );
}
}
else if ( bIsFacing && me->GetBodyInterface()->IsHeadAimingOnTarget() )
{
m_useAttemptTimer.Start( CNEO_Juggernaut::GetUseDuration() + 1.0f );
me->PressUseButton( CNEO_Juggernaut::GetUseDuration() + 1.0f );
}
}
else
{
// Objective is farther than we can reach for cpature
if ( m_repathTimer.IsElapsed() )
{
m_repathTimer.Start( RandomFloat( 1.0f, 5.0f ) );
if ( !CNEOBotPathCompute( me, m_path, m_hObjective->GetAbsOrigin(), FASTEST_ROUTE ) )
{
return Done( "Unable to find a path to the Juggernaut objective" );
}
}
m_path.Update( me );
}

return Continue();
}
29 changes: 29 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_jgr_capture.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef NEO_BOT_CAPTURE_JGR_H
#define NEO_BOT_CAPTURE_JGR_H

#include "NextBotBehavior.h"
#include "bot/neo_bot.h"

//----------------------------------------------------------------------------------------------------------------
class CNEOBotJgrCapture : public Action<CNEOBot>
{
public:
CNEOBotJgrCapture( CNEO_Juggernaut *pObjective );
virtual ~CNEOBotJgrCapture() { }

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

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

private:
CHandle<CNEO_Juggernaut> m_hObjective;
CountdownTimer m_useAttemptTimer;
PathFollower m_path;
CountdownTimer m_repathTimer;
};

#endif // NEO_BOT_CAPTURE_JGR_H
79 changes: 79 additions & 0 deletions src/game/server/neo/bot/behavior/neo_bot_jgr_enemy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "cbase.h"
#include "neo_player.h"
#include "bot/neo_bot.h"
#include "bot/behavior/neo_bot_jgr_enemy.h"
#include "bot/neo_bot_path_compute.h"
#include "neo_gamerules.h"
#include "bot/behavior/neo_bot_attack.h"

//---------------------------------------------------------------------------------------------
CNEOBotJgrEnemy::CNEOBotJgrEnemy( void )
{
}

//---------------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotJgrEnemy::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction )
{
return Continue();
}

//---------------------------------------------------------------------------------------------
ActionResult< CNEOBot > CNEOBotJgrEnemy::Update( CNEOBot *me, float interval )
{
const int iJuggernautPlayer = NEORules()->GetJuggernautPlayer();
if ( iJuggernautPlayer <= 0 )
{
return Done( "No juggernaut player" );
}

CNEO_Player* pJuggernaut = ToNEOPlayer( UTIL_PlayerByIndex( iJuggernautPlayer ) );
if ( !pJuggernaut )
{
return Done( "Juggernaut is invalid" );
}

if ( !pJuggernaut->IsAlive() )
{
return Done( "Juggernaut is dead" );
}

if ( pJuggernaut->GetTeamNumber() == me->GetTeamNumber() )
{
return Done( "Juggernaut is friendly" );
}

const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
if ( threat && !threat->IsObsolete() && me->GetIntentionInterface()->ShouldAttack( me, threat ) )
{
return SuspendFor( new CNEOBotAttack, "Attacking threat" );
}

// Chase the Juggernaut
CNEOBotPathUpdateChase( me, m_chasePath, pJuggernaut, DEFAULT_ROUTE );

return Continue();
}

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

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

//---------------------------------------------------------------------------------------------
EventDesiredResult< CNEOBot > CNEOBotJgrEnemy::OnMoveToSuccess( CNEOBot *me, const Path *path )
{
return TryContinue();
}

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

#include "bot/neo_bot.h"
#include "Path/NextBotChasePath.h"

//--------------------------------------------------------------------------------------------------------
class CNEOBotJgrEnemy : public Action< CNEOBot >
{
public:
CNEOBotJgrEnemy( 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 "JgrEnemy"; }

private:
ChasePath m_chasePath;
};

#endif // NEO_BOT_JGR_ENEMY_H
Loading