You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
7.5 KiB
231 lines
7.5 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// tf_bot_capture_point.cpp |
|
// Move to and try to capture the next point |
|
// Michael Booth, February 2009 |
|
|
|
#include "cbase.h" |
|
#include "nav_mesh.h" |
|
#include "tf_player.h" |
|
#include "tf_gamerules.h" |
|
#include "team_control_point_master.h" |
|
#include "trigger_area_capture.h" |
|
#include "bot/tf_bot.h" |
|
#include "bot/behavior/scenario/capture_point/tf_bot_capture_point.h" |
|
#include "bot/behavior/scenario/capture_point/tf_bot_defend_point.h" |
|
#include "bot/behavior/tf_bot_seek_and_destroy.h" |
|
|
|
|
|
extern ConVar tf_bot_path_lookahead_range; |
|
ConVar tf_bot_offense_must_push_time( "tf_bot_offense_must_push_time", "120", FCVAR_CHEAT, "If timer is less than this, bots will push hard to cap" ); |
|
|
|
ConVar tf_bot_capture_seek_and_destroy_min_duration( "tf_bot_capture_seek_and_destroy_min_duration", "15", FCVAR_CHEAT, "If a capturing bot decides to go hunting, this is the min duration he will hunt for before reconsidering" ); |
|
ConVar tf_bot_capture_seek_and_destroy_max_duration( "tf_bot_capture_seek_and_destroy_max_duration", "30", FCVAR_CHEAT, "If a capturing bot decides to go hunting, this is the max duration he will hunt for before reconsidering" ); |
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
ActionResult< CTFBot > CTFBotCapturePoint::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) |
|
{ |
|
VPROF_BUDGET( "CTFBotCapturePoint::OnStart", "NextBot" ); |
|
|
|
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() ); |
|
m_path.Invalidate(); |
|
|
|
return Continue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
ActionResult< CTFBot > CTFBotCapturePoint::Update( CTFBot *me, float interval ) |
|
{ |
|
if ( TFGameRules()->InSetup() ) |
|
{ |
|
// wait until the gates open, then path |
|
m_path.Invalidate(); |
|
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) ); |
|
|
|
return Continue(); |
|
} |
|
|
|
CTeamControlPoint *point = me->GetMyControlPoint(); |
|
|
|
if ( point == NULL ) |
|
{ |
|
const float roamTime = 10.0f; |
|
return SuspendFor( new CTFBotSeekAndDestroy( roamTime ), "Seek and destroy until a point becomes available" ); |
|
} |
|
|
|
if ( point->GetTeamNumber() == me->GetTeamNumber() ) |
|
{ |
|
return ChangeTo( new CTFBotDefendPoint, "We need to defend our point(s)" ); |
|
} |
|
|
|
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); |
|
if ( threat && threat->IsVisibleRecently() ) |
|
{ |
|
// prepare to fight |
|
me->EquipBestWeaponForThreat( threat ); |
|
} |
|
|
|
bool isPushingToCapture = ( me->IsPointBeingCaptured( point ) && !me->IsInCombat() ) || // a friend is capturing |
|
me->IsCapturingPoint() || // we're capturing |
|
// me->m_Shared.InCond( TF_COND_INVULNERABLE ) || // we're ubered |
|
TFGameRules()->InOvertime() || // the game is in overtime |
|
me->GetTimeLeftToCapture() < tf_bot_offense_must_push_time.GetFloat() || // nearly out of tim |
|
TFGameRules()->IsInTraining() || // teach newbies to capture |
|
me->IsNearPoint( point ); |
|
|
|
|
|
// if we see an enemy at a good combat range, stop and engage them unless we're running out of time |
|
if ( !isPushingToCapture ) |
|
{ |
|
if ( threat && threat->IsVisibleRecently() ) |
|
{ |
|
return SuspendFor( new CTFBotSeekAndDestroy( RandomFloat( tf_bot_capture_seek_and_destroy_min_duration.GetFloat(), tf_bot_capture_seek_and_destroy_max_duration.GetFloat() ) ), "Too early to capture - hunting" ); |
|
} |
|
} |
|
|
|
|
|
if ( me->IsCapturingPoint() ) |
|
{ |
|
// move around on the point while we capture |
|
const CUtlVector< CTFNavArea * > *controlPointAreas = TheTFNavMesh()->GetControlPointAreas( point->GetPointIndex() ); |
|
if ( controlPointAreas ) |
|
{ |
|
if ( controlPointAreas->Count() == 0 ) |
|
{ |
|
Assert( controlPointAreas->Count() ); |
|
Continue(); // this control point has no nav areas for bot to move around |
|
} |
|
|
|
// move to a random spot on this control point |
|
if ( m_repathTimer.IsElapsed() ) |
|
{ |
|
m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) ); |
|
|
|
int which = RandomInt( 0, controlPointAreas->Count() - 1 ); |
|
CTFNavArea *goalArea = controlPointAreas->Element( which ); |
|
if ( goalArea ) |
|
{ |
|
CTFBotPathCost cost( me, DEFAULT_ROUTE ); |
|
m_path.Compute( me, goalArea->GetRandomPoint(), cost ); |
|
} |
|
} |
|
|
|
m_path.Update( me ); |
|
} |
|
} |
|
else |
|
{ |
|
// move toward the point, periodically repathing to account for changing situation |
|
if ( m_repathTimer.IsElapsed() ) |
|
{ |
|
VPROF_BUDGET( "CTFBotCapturePoint::Update( repath )", "NextBot" ); |
|
|
|
CTFBotPathCost cost( me, SAFEST_ROUTE ); |
|
m_path.Compute( me, point->GetAbsOrigin(), cost ); |
|
m_repathTimer.Start( RandomFloat( 2.0f, 3.0f ) ); |
|
} |
|
|
|
if ( TFGameRules()->IsInTraining() && !me->IsAnyPointBeingCaptured() ) |
|
{ |
|
// stop short of capturing until the human trainee starts it |
|
if ( m_path.GetLength() < 1000.0f ) |
|
{ |
|
// hold here and yell at player to get on the point |
|
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_GO ); |
|
|
|
return Continue(); |
|
} |
|
} |
|
|
|
// move towards next capture point |
|
m_path.Update( me ); |
|
} |
|
|
|
return Continue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
ActionResult< CTFBot > CTFBotCapturePoint::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction ) |
|
{ |
|
m_repathTimer.Invalidate(); |
|
return Continue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
EventDesiredResult< CTFBot > CTFBotCapturePoint::OnStuck( CTFBot *me ) |
|
{ |
|
m_repathTimer.Invalidate(); |
|
me->GetLocomotionInterface()->ClearStuckStatus(); |
|
|
|
return TryContinue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
EventDesiredResult< CTFBot > CTFBotCapturePoint::OnMoveToSuccess( CTFBot *me, const Path *path ) |
|
{ |
|
return TryContinue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
EventDesiredResult< CTFBot > CTFBotCapturePoint::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason ) |
|
{ |
|
m_repathTimer.Invalidate(); |
|
return TryContinue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
EventDesiredResult< CTFBot > CTFBotCapturePoint::OnTerritoryContested( CTFBot *me, int territoryID ) |
|
{ |
|
return TryContinue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
EventDesiredResult< CTFBot > CTFBotCapturePoint::OnTerritoryCaptured( CTFBot *me, int territoryID ) |
|
{ |
|
// we got it, move on |
|
m_repathTimer.Invalidate(); |
|
|
|
return TryContinue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
EventDesiredResult< CTFBot > CTFBotCapturePoint::OnTerritoryLost( CTFBot *me, int territoryID ) |
|
{ |
|
return TryContinue(); |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
QueryResultType CTFBotCapturePoint::ShouldRetreat( const INextBot *bot ) const |
|
{ |
|
CTFBot *me = (CTFBot *)bot->GetEntity(); |
|
|
|
// if we're running out of time, we have to go for it |
|
if ( me->GetTimeLeftToCapture() < tf_bot_offense_must_push_time.GetFloat() ) |
|
return ANSWER_NO; |
|
|
|
return ANSWER_UNDEFINED; |
|
} |
|
|
|
|
|
//--------------------------------------------------------------------------------------------- |
|
QueryResultType CTFBotCapturePoint::ShouldHurry( const INextBot *bot ) const |
|
{ |
|
CTFBot *me = (CTFBot *)bot->GetEntity(); |
|
|
|
// if we're running out of time, we have to go for it |
|
if ( me->GetTimeLeftToCapture() < tf_bot_offense_must_push_time.GetFloat() ) |
|
return ANSWER_YES; |
|
|
|
return ANSWER_UNDEFINED; |
|
} |
|
|
|
|